From e8f390a0d26e60b5e01177b43f2ed31dbf5cba11 Mon Sep 17 00:00:00 2001
From: DiamondThree
Date: Tue, 2 Aug 2022 16:17:10 +0800
Subject: [PATCH 01/91] fix iframe will take away the focus
---
.../src/components/source-view/SourceView.tsx | 80 +++++++++++++++----
.../editor-core/src/recast-block/view.ts | 23 +++++-
2 files changed, 85 insertions(+), 18 deletions(-)
diff --git a/libs/components/editor-blocks/src/components/source-view/SourceView.tsx b/libs/components/editor-blocks/src/components/source-view/SourceView.tsx
index 3835199d3f..2c117e97fe 100644
--- a/libs/components/editor-blocks/src/components/source-view/SourceView.tsx
+++ b/libs/components/editor-blocks/src/components/source-view/SourceView.tsx
@@ -1,6 +1,11 @@
-import type { AsyncBlock } from '@toeverything/components/editor-core';
+import {
+ AsyncBlock,
+ useCurrentView,
+ useLazyIframe,
+} from '@toeverything/components/editor-core';
import { styled } from '@toeverything/components/ui';
-import type { FC } from 'react';
+import { FC, useRef } from 'react';
+import { SCENE_CONFIG } from '../../blocks/group/config';
import { BlockPreview } from './BlockView';
import { formatUrl } from './format-url';
@@ -15,7 +20,18 @@ export interface Props {
}
const getHost = (url: string) => new URL(url).host;
-
+const MouseMaskContainer = styled('div')({
+ position: 'absolute',
+ zIndex: 1,
+ top: '0px',
+ left: '0px',
+ right: '0px',
+ bottom: '0px',
+ backgroundColor: 'transparent',
+ '&:hover': {
+ pointerEvents: 'none',
+ },
+});
const LinkContainer = styled('div')<{
isSelected: boolean;
}>(({ theme, isSelected }) => {
@@ -38,12 +54,28 @@ const LinkContainer = styled('div')<{
},
};
});
-
+const _getLinkStyle = (scene: string) => {
+ switch (scene) {
+ case SCENE_CONFIG.PAGE:
+ return {
+ width: '420px',
+ height: '198px',
+ };
+ default:
+ return {
+ width: '252px',
+ height: '126px',
+ };
+ }
+};
const SourceViewContainer = styled('div')<{
isSelected: boolean;
-}>(({ theme, isSelected }) => {
+ scene: string;
+}>(({ theme, isSelected, scene }) => {
return {
+ ..._getLinkStyle(scene),
overflow: 'hidden',
+ position: 'relative',
borderRadius: theme.affine.shape.borderRadius,
background: isSelected ? 'rgba(152, 172, 189, 0.1)' : 'transparent',
padding: '8px',
@@ -52,32 +84,46 @@ const SourceViewContainer = styled('div')<{
height: '100%',
border: '1px solid #EAEEF2',
borderRadius: theme.affine.shape.borderRadius,
+ userSelect: 'none',
},
};
});
-
+const IframeContainer = styled('div')<{ show: boolean }>(({ show }) => {
+ return {
+ height: '100%',
+ display: show ? 'block' : 'none',
+ };
+});
export const SourceView: FC = props => {
const { link, isSelected, block, editorElement } = props;
const src = formatUrl(link);
- const openTabOnBrowser = () => {
- window.open(link, '_blank');
- };
+
+ const iframeContainer = useRef(null);
+ let iframeShow = useLazyIframe(src, 3000, iframeContainer);
+ const [currentView] = useCurrentView();
+ const { type } = currentView;
if (src?.startsWith('http')) {
return (
- e.preventDefault()}
- onClick={openTabOnBrowser}
- >
- {getHost(src)}
- {src}
-
+
+
+
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ }}
+ show={iframeShow}
+ ref={iframeContainer}
+ >
+
+
);
} else if (src?.startsWith('affine')) {
return (
{
);
return [currentView, setCurrentView] as const;
};
+export const useLazyIframe = (
+ link: string,
+ timers: number,
+ container: MutableRefObject
+) => {
+ const [iframeShow, setIframeShow] = useState(false);
+ useEffect(() => {
+ const iframe = document.createElement('iframe');
+ iframe.src = link;
+ iframe.onload = () => {
+ setTimeout(() => {
+ setIframeShow(true);
+ }, timers);
+ };
+ container.current.appendChild(iframe);
+ return () => {
+ iframe.remove();
+ };
+ }, [link, container]);
+ return iframeShow;
+};
export const useRecastView = () => {
const recastBlock = useRecastBlock();
const recastViews =
From 6f340adf243e470cfba04916ec1ddc2780fde3d5 Mon Sep 17 00:00:00 2001
From: DiamondThree
Date: Tue, 2 Aug 2022 16:59:59 +0800
Subject: [PATCH 02/91] fix iframe will take away the focus
---
libs/components/editor-core/src/recast-block/view.ts | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/libs/components/editor-core/src/recast-block/view.ts b/libs/components/editor-core/src/recast-block/view.ts
index 4386c83918..89b32488ea 100644
--- a/libs/components/editor-core/src/recast-block/view.ts
+++ b/libs/components/editor-core/src/recast-block/view.ts
@@ -61,10 +61,15 @@ export const useLazyIframe = (
iframe.src = link;
iframe.onload = () => {
setTimeout(() => {
+ // Prevent iframe from scrolling parent container
+ // TODO W3C https://github.com/w3c/csswg-drafts/issues/7134
+ // https://forum.figma.com/t/prevent-figmas-embed-code-from-automatically-scrolling-to-it-on-page-load/26029/6
setIframeShow(true);
}, timers);
};
- container.current.appendChild(iframe);
+ if (container?.current) {
+ container.current.appendChild(iframe);
+ }
return () => {
iframe.remove();
};
From 96ea531b4ed508ff6cf4a9ab0431b47b0983a2de Mon Sep 17 00:00:00 2001
From: DiamondThree
Date: Tue, 2 Aug 2022 17:03:01 +0800
Subject: [PATCH 03/91] fix iframe will take away the focus
---
libs/components/editor-core/src/recast-block/view.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/components/editor-core/src/recast-block/view.ts b/libs/components/editor-core/src/recast-block/view.ts
index 89b32488ea..09e4644977 100644
--- a/libs/components/editor-core/src/recast-block/view.ts
+++ b/libs/components/editor-core/src/recast-block/view.ts
@@ -53,7 +53,7 @@ export const useCurrentView = () => {
export const useLazyIframe = (
link: string,
timers: number,
- container: MutableRefObject
+ container: MutableRefObject
) => {
const [iframeShow, setIframeShow] = useState(false);
useEffect(() => {
From f18f51ba7cf245f3cb3042540ba5f0237bdb4767 Mon Sep 17 00:00:00 2001
From: QiShaoXuan
Date: Tue, 2 Aug 2022 19:10:51 +0800
Subject: [PATCH 04/91] feat: support group by status in kanban mode, resolved
#40
---
.../group/scene-kanban/CardContainer.tsx | 1 +
.../pendant-modify-panel/Information.tsx | 8 ++--
.../pendant-modify-panel/Select.tsx | 4 +-
.../CreatePendantPanel.tsx | 15 ++++---
.../editor-core/src/block-pendant/utils.ts | 24 ++++++++----
.../components/editor-core/src/kanban/atom.ts | 39 +++++++++++++++----
.../editor-core/src/kanban/group.ts | 1 +
.../editor-core/src/kanban/kanban.ts | 8 ++--
.../editor-core/src/kanban/types.ts | 5 ++-
.../editor-core/src/recast-block/property.ts | 12 +++---
10 files changed, 76 insertions(+), 41 deletions(-)
diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardContainer.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardContainer.tsx
index 3616c0d206..cd71b72666 100644
--- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardContainer.tsx
+++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardContainer.tsx
@@ -41,6 +41,7 @@ const getKanbanColor = (
return DEFAULT_COLOR;
}
if (
+ group.type === PropertyType.Status ||
group.type === PropertyType.Select ||
group.type === PropertyType.MultiSelect ||
group.type === DEFAULT_GROUP_ID
diff --git a/libs/components/editor-core/src/block-pendant/pendant-modify-panel/Information.tsx b/libs/components/editor-core/src/block-pendant/pendant-modify-panel/Information.tsx
index afd5bc5525..a8929f9e6b 100644
--- a/libs/components/editor-core/src/block-pendant/pendant-modify-panel/Information.tsx
+++ b/libs/components/editor-core/src/block-pendant/pendant-modify-panel/Information.tsx
@@ -4,7 +4,7 @@ import { ModifyPanelContentProps } from './types';
import { StyledDivider, StyledPopoverSubTitle } from '../StyledComponent';
import { BasicSelect } from './Select';
import { InformationProperty, InformationValue } from '../../recast-block';
-import { genInitialOptions, getPendantIconsConfigByName } from '../utils';
+import { generateInitialOptions, getPendantIconsConfigByName } from '../utils';
export default (props: ModifyPanelContentProps) => {
const { onPropertyChange, onValueChange, initialValue, property } = props;
@@ -38,7 +38,7 @@ export default (props: ModifyPanelContentProps) => {
}}
initialOptions={
propProperty?.emailOptions ||
- genInitialOptions(
+ generateInitialOptions(
property?.type,
getPendantIconsConfigByName('Email')
)
@@ -66,7 +66,7 @@ export default (props: ModifyPanelContentProps) => {
}}
initialOptions={
propProperty?.phoneOptions ||
- genInitialOptions(
+ generateInitialOptions(
property?.type,
getPendantIconsConfigByName('Phone')
)
@@ -94,7 +94,7 @@ export default (props: ModifyPanelContentProps) => {
}}
initialOptions={
propProperty?.locationOptions ||
- genInitialOptions(
+ generateInitialOptions(
property?.type,
getPendantIconsConfigByName('Location')
)
diff --git a/libs/components/editor-core/src/block-pendant/pendant-modify-panel/Select.tsx b/libs/components/editor-core/src/block-pendant/pendant-modify-panel/Select.tsx
index 6d0d1f26f9..b016437ac8 100644
--- a/libs/components/editor-core/src/block-pendant/pendant-modify-panel/Select.tsx
+++ b/libs/components/editor-core/src/block-pendant/pendant-modify-panel/Select.tsx
@@ -21,7 +21,7 @@ import {
} from '@toeverything/components/ui';
import { HighLightIconInput } from './IconInput';
import { PendantConfig, IconNames, OptionIdType, OptionType } from '../types';
-import { genBasicOption } from '../utils';
+import { generateBasicOption } from '../utils';
type OptionItemType = {
option: OptionType;
@@ -66,7 +66,7 @@ export const BasicSelect = ({
const [selectIds, setSelectIds] = useState(initialValue);
const insertOption = (insertId: OptionIdType) => {
- const newOption = genBasicOption({
+ const newOption = generateBasicOption({
index: options.length + 1,
iconConfig,
});
diff --git a/libs/components/editor-core/src/block-pendant/pendant-operation-panel/CreatePendantPanel.tsx b/libs/components/editor-core/src/block-pendant/pendant-operation-panel/CreatePendantPanel.tsx
index b9d29cebe7..fa7d50bfba 100644
--- a/libs/components/editor-core/src/block-pendant/pendant-operation-panel/CreatePendantPanel.tsx
+++ b/libs/components/editor-core/src/block-pendant/pendant-operation-panel/CreatePendantPanel.tsx
@@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react';
-import { nanoid } from 'nanoid';
import { Input, Option, Select, Tooltip } from '@toeverything/components/ui';
import { HelpCenterIcon } from '@toeverything/components/icons';
import { AsyncBlock } from '../../editor';
@@ -15,13 +14,13 @@ import {
StyledPopoverSubTitle,
StyledPopoverWrapper,
} from '../StyledComponent';
-import { genInitialOptions, getPendantConfigByType } from '../utils';
+import {
+ generateRandomFieldName,
+ generateInitialOptions,
+ getPendantConfigByType,
+} from '../utils';
import { useOnCreateSure } from './hooks';
-const upperFirst = (str: string) => {
- return `${str[0].toUpperCase()}${str.slice(1)}`;
-};
-
export const CreatePendantPanel = ({
block,
onSure,
@@ -35,7 +34,7 @@ export const CreatePendantPanel = ({
useEffect(() => {
selectedOption &&
- setFieldName(upperFirst(`${selectedOption.type}#${nanoid(4)}`));
+ setFieldName(generateRandomFieldName(selectedOption.type));
}, [selectedOption]);
return (
@@ -93,7 +92,7 @@ export const CreatePendantPanel = ({
{
return tempOptions.findIndex((o: OptionType) => o.id === id);
})
- .filter(index => index != -1);
+ .filter(index => index !== -1);
selectedId = selectedIndex.map((index: number) => {
return options[index].id;
});
@@ -130,7 +131,7 @@ export const getPendantIconsConfigByName = (
return pendantConfig[pendantName];
};
-export const genBasicOption = ({
+export const generateBasicOption = ({
index,
iconConfig,
name = '',
@@ -159,22 +160,22 @@ export const genBasicOption = ({
/**
* Status Pendant is a Select Pendant built-in some options
* **/
-export const genInitialOptions = (
+export const generateInitialOptions = (
type: PendantTypes,
iconConfig: PendantConfig
) => {
if (type === PendantTypes.Status) {
return [
- genBasicOption({ index: 0, iconConfig, name: 'No Started' }),
- genBasicOption({
+ generateBasicOption({ index: 0, iconConfig, name: 'No Started' }),
+ generateBasicOption({
index: 1,
iconConfig,
name: 'In Progress',
}),
- genBasicOption({ index: 2, iconConfig, name: 'Complete' }),
+ generateBasicOption({ index: 2, iconConfig, name: 'Complete' }),
];
}
- return [genBasicOption({ index: 0, iconConfig })];
+ return [generateBasicOption({ index: 0, iconConfig })];
};
export const checkPendantForm = (
@@ -222,3 +223,10 @@ export const checkPendantForm = (
return { passed: true, message: 'Check passed !' };
};
+
+const upperFirst = (str: string) => {
+ return `${str[0].toUpperCase()}${str.slice(1)}`;
+};
+
+export const generateRandomFieldName = (type: PendantTypes) =>
+ upperFirst(`${type}#${nanoid(4)}`);
diff --git a/libs/components/editor-core/src/kanban/atom.ts b/libs/components/editor-core/src/kanban/atom.ts
index b903e2d92f..238690863c 100644
--- a/libs/components/editor-core/src/kanban/atom.ts
+++ b/libs/components/editor-core/src/kanban/atom.ts
@@ -10,6 +10,12 @@ import {
} from '../recast-block/types';
import type { DefaultGroup, KanbanGroup } from './types';
import { DEFAULT_GROUP_ID } from './types';
+import {
+ generateInitialOptions,
+ generateRandomFieldName,
+ getPendantIconsConfigByName,
+} from '../block-pendant/utils';
+import { SelectOption } from '../recast-block';
/**
* - If the `groupBy` is `SelectProperty` or `MultiSelectProperty`, return `(Multi)SelectProperty.options`.
@@ -23,6 +29,7 @@ export const getGroupOptions = async (
return [];
}
switch (groupBy.type) {
+ case PropertyType.Status:
case PropertyType.Select:
case PropertyType.MultiSelect: {
return groupBy.options.map(option => ({
@@ -57,6 +64,9 @@ const isValueBelongOption = (
case PropertyType.MultiSelect: {
return propertyValue.value.some(i => i === option.id);
}
+ case PropertyType.Status: {
+ return propertyValue.value === option.id;
+ }
// case PropertyType.Text: {
// TOTODO:DO support this type
// }
@@ -107,6 +117,7 @@ export const moveCardToGroup = async (
success = await removeValue(groupById);
return false;
}
+
switch (group.type) {
case PropertyType.Select: {
success = await setValue({
@@ -116,6 +127,14 @@ export const moveCardToGroup = async (
});
break;
}
+ case PropertyType.Status: {
+ success = await setValue({
+ id: groupById,
+ type: group.type,
+ value: group.id,
+ });
+ break;
+ }
case PropertyType.MultiSelect: {
success = await setValue({
id: groupById,
@@ -194,14 +213,18 @@ export const genDefaultGroup = (groupBy: RecastMetaProperty): DefaultGroup => ({
items: [],
});
-export const DEFAULT_GROUP_BY_PROPERTY = {
- name: 'Status',
- options: [
- { name: 'No Started', color: '#E53535', background: '#FFCECE' },
- { name: 'In Progress', color: '#A77F1A', background: '#FFF5AB' },
- { name: 'Complete', color: '#3C8867', background: '#C5FBE0' },
- ],
-};
+export const generateDefaultGroupByProperty = (): {
+ name: string;
+ options: Omit[];
+ type: PropertyType.Status;
+} => ({
+ name: generateRandomFieldName(PropertyType.Status),
+ type: PropertyType.Status,
+ options: generateInitialOptions(
+ PropertyType.Status,
+ getPendantIconsConfigByName(PropertyType.Status)
+ ),
+});
/**
* Unwrap blocks from the grid recursively.
diff --git a/libs/components/editor-core/src/kanban/group.ts b/libs/components/editor-core/src/kanban/group.ts
index 0297946d89..e857a6865a 100644
--- a/libs/components/editor-core/src/kanban/group.ts
+++ b/libs/components/editor-core/src/kanban/group.ts
@@ -7,6 +7,7 @@ export const useKanbanGroup = (groupBy: RecastMetaProperty) => {
const { updateSelect } = useSelectProperty();
switch (groupBy.type) {
+ case PropertyType.Status:
case PropertyType.MultiSelect:
case PropertyType.Select: {
const {
diff --git a/libs/components/editor-core/src/kanban/kanban.ts b/libs/components/editor-core/src/kanban/kanban.ts
index 675e75db3e..21564b6b2e 100644
--- a/libs/components/editor-core/src/kanban/kanban.ts
+++ b/libs/components/editor-core/src/kanban/kanban.ts
@@ -18,8 +18,8 @@ import {
import { supportChildren } from '../utils';
import {
calcCardGroup,
- DEFAULT_GROUP_BY_PROPERTY,
genDefaultGroup,
+ generateDefaultGroupByProperty,
getCardGroup,
getGroupOptions,
moveCardToAfter,
@@ -48,6 +48,7 @@ export const useRecastKanbanGroupBy = () => {
// Add other type groupBy support
const supportedGroupBy = getProperties().filter(
prop =>
+ prop.type === PropertyType.Status ||
prop.type === PropertyType.Select ||
prop.type === PropertyType.MultiSelect
);
@@ -88,7 +89,8 @@ export const useRecastKanbanGroupBy = () => {
// TODO: support other property type
if (
groupByProperty.type !== PropertyType.Select &&
- groupByProperty.type !== PropertyType.MultiSelect
+ groupByProperty.type !== PropertyType.MultiSelect &&
+ groupByProperty.type !== PropertyType.Status
) {
console.warn('Not support groupBy type', groupByProperty);
@@ -134,7 +136,7 @@ export const useInitKanbanEffect = ():
}
// 3. no group by, no properties
// create a new property and set it as group by
- const prop = await createSelect(DEFAULT_GROUP_BY_PROPERTY);
+ const prop = await createSelect(generateDefaultGroupByProperty());
await setGroupBy(prop.id);
};
diff --git a/libs/components/editor-core/src/kanban/types.ts b/libs/components/editor-core/src/kanban/types.ts
index c34371f2d8..926b274c77 100644
--- a/libs/components/editor-core/src/kanban/types.ts
+++ b/libs/components/editor-core/src/kanban/types.ts
@@ -46,7 +46,10 @@ export type DefaultGroup = KanbanGroupBase & {
type SelectGroup = KanbanGroupBase &
SelectOption & {
- type: PropertyType.Select | PropertyType.MultiSelect;
+ type:
+ | PropertyType.Select
+ | PropertyType.MultiSelect
+ | PropertyType.Status;
};
type TextGroup = KanbanGroupBase & {
diff --git a/libs/components/editor-core/src/recast-block/property.ts b/libs/components/editor-core/src/recast-block/property.ts
index e8f0104269..1452bba849 100644
--- a/libs/components/editor-core/src/recast-block/property.ts
+++ b/libs/components/editor-core/src/recast-block/property.ts
@@ -257,14 +257,12 @@ export const getRecastItemValue = (block: RecastItem | AsyncBlock) => {
const isSelectLikeProperty = (
metaProperty?: RecastMetaProperty
): metaProperty is SelectProperty | MultiSelectProperty => {
- if (
+ return !(
!metaProperty ||
- (metaProperty.type !== PropertyType.Select &&
+ (metaProperty.type !== PropertyType.Status &&
+ metaProperty.type !== PropertyType.Select &&
metaProperty.type !== PropertyType.MultiSelect)
- ) {
- return false;
- }
- return true;
+ );
};
/**
@@ -312,7 +310,7 @@ export const useSelectProperty = () => {
};
const updateSelect = (
- selectProperty: SelectProperty | MultiSelectProperty
+ selectProperty: StatusProperty | SelectProperty | MultiSelectProperty
) => {
// if (typeof selectProperty === 'string') {
// const maybeSelectProperty = getProperty(selectProperty);
From dd4360fb7988202bd88ae81c85c03fca0a811336 Mon Sep 17 00:00:00 2001
From: QiShaoXuan
Date: Tue, 2 Aug 2022 19:44:04 +0800
Subject: [PATCH 05/91] fix: The status history in kanban mode cannot be synced
to text mode, fixed #43
---
.../src/block-pendant/use-pendant.ts | 37 ++------------
.../editor-core/src/block-pendant/utils.ts | 45 +++++++++++++++--
.../components/editor-core/src/kanban/atom.ts | 49 ++++++++-----------
.../editor-core/src/kanban/kanban.ts | 21 ++++++--
4 files changed, 84 insertions(+), 68 deletions(-)
diff --git a/libs/components/editor-core/src/block-pendant/use-pendant.ts b/libs/components/editor-core/src/block-pendant/use-pendant.ts
index 55c8aa054e..da66906db2 100644
--- a/libs/components/editor-core/src/block-pendant/use-pendant.ts
+++ b/libs/components/editor-core/src/block-pendant/use-pendant.ts
@@ -1,41 +1,10 @@
-import { removePropertyValueRecord, setPendantHistory } from './utils';
+import { getPendantController } from './utils';
import { AsyncBlock } from '../editor';
-import {
- getRecastItemValue,
- RecastMetaProperty,
- useRecastBlock,
-} from '../recast-block';
+import { useRecastBlock } from '../recast-block';
export const usePendant = (block: AsyncBlock) => {
// const { getProperties, removeProperty } = useRecastBlockMeta();
const recastBlock = useRecastBlock();
- const { getValue, setValue, removeValue } = getRecastItemValue(block);
- // const { updateSelect } = useSelectProperty();
- const setPendant = async (property: RecastMetaProperty, newValue: any) => {
- const nv = {
- id: property.id,
- type: property.type,
- value: newValue,
- };
- await setValue(nv);
- setPendantHistory({
- recastBlockId: recastBlock.id,
- blockId: block.id,
- propertyId: property.id,
- });
- };
-
- const removePendant = async (property: RecastMetaProperty) => {
- await removeValue(property.id);
- removePropertyValueRecord({
- recastBlockId: block.id,
- propertyId: property.id,
- });
- };
-
- return {
- setPendant,
- removePendant,
- };
+ return getPendantController(recastBlock, block);
};
diff --git a/libs/components/editor-core/src/block-pendant/utils.ts b/libs/components/editor-core/src/block-pendant/utils.ts
index a7cd6f3fc9..b55e0a9175 100644
--- a/libs/components/editor-core/src/block-pendant/utils.ts
+++ b/libs/components/editor-core/src/block-pendant/utils.ts
@@ -1,13 +1,16 @@
import {
+ getRecastItemValue,
PropertyType,
- RecastBlockValue,
+ RecastBlock,
+ RecastItem,
+ RecastMetaProperty,
RecastPropertyId,
SelectOption,
} from '../recast-block';
-import { OptionIdType, OptionType } from './types';
+import { OptionIdType, OptionType, PendantConfig, PendantTypes } from './types';
import { pendantConfig } from './config';
-import { PendantConfig, PendantTypes } from './types';
import { nanoid } from 'nanoid';
+import { AsyncBlock } from '../editor';
type Props = {
recastBlockId: string;
blockId: string;
@@ -81,6 +84,42 @@ export const removePendantHistory = ({
localStorage.setItem(LOCAL_STORAGE_NAME, JSON.stringify(data));
};
+export const getPendantController = (
+ recastBlock: RecastBlock,
+ block: AsyncBlock | RecastItem
+) => {
+ const { setValue, removeValue } = getRecastItemValue(block);
+
+ const setPendant = async (property: RecastMetaProperty, newValue: any) => {
+ const nv = {
+ id: property.id,
+ type: property.type,
+ value: newValue,
+ };
+
+ setPendantHistory({
+ recastBlockId: recastBlock.id,
+ blockId: block.id,
+ propertyId: property.id,
+ });
+
+ return await setValue(nv);
+ };
+
+ const removePendant = async (property: RecastMetaProperty) => {
+ removePendantHistory({
+ recastBlockId: block.id,
+ propertyId: property.id,
+ });
+ return await removeValue(property.id);
+ };
+
+ return {
+ setPendant,
+ removePendant,
+ };
+};
+
/**
* In select pendant panel, use mock options instead of use `createSelect` when add or delete option
* so the option`s id is index number, not `SelectOptionId`
diff --git a/libs/components/editor-core/src/kanban/atom.ts b/libs/components/editor-core/src/kanban/atom.ts
index 238690863c..5bb3e652a6 100644
--- a/libs/components/editor-core/src/kanban/atom.ts
+++ b/libs/components/editor-core/src/kanban/atom.ts
@@ -6,7 +6,6 @@ import {
PropertyType,
RecastBlockValue,
RecastMetaProperty,
- RecastPropertyId,
} from '../recast-block/types';
import type { DefaultGroup, KanbanGroup } from './types';
import { DEFAULT_GROUP_ID } from './types';
@@ -14,6 +13,7 @@ import {
generateInitialOptions,
generateRandomFieldName,
getPendantIconsConfigByName,
+ getPendantController,
} from '../block-pendant/utils';
import { SelectOption } from '../recast-block';
@@ -106,49 +106,42 @@ export const calcCardGroup = (
/**
* Set group value for the card block
*/
-export const moveCardToGroup = async (
- groupById: RecastPropertyId,
- cardBlock: RecastItem,
- group: KanbanGroup
-) => {
- const { setValue, removeValue } = getRecastItemValue(cardBlock);
+export const moveCardToGroup = async ({
+ groupBy,
+ cardBlock,
+ group,
+ recastBlock,
+}: {
+ groupBy: RecastMetaProperty;
+ cardBlock: RecastItem;
+ group: KanbanGroup;
+ recastBlock: RecastBlock;
+}) => {
+ const { setPendant, removePendant } = getPendantController(
+ recastBlock,
+ cardBlock
+ );
let success = false;
if (group.id === DEFAULT_GROUP_ID) {
- success = await removeValue(groupById);
+ success = await removePendant(groupBy);
return false;
}
switch (group.type) {
case PropertyType.Select: {
- success = await setValue({
- id: groupById,
- type: group.type,
- value: group.id,
- });
+ success = await setPendant(groupBy, group.id);
break;
}
case PropertyType.Status: {
- success = await setValue({
- id: groupById,
- type: group.type,
- value: group.id,
- });
+ success = await setPendant(groupBy, group.id);
break;
}
case PropertyType.MultiSelect: {
- success = await setValue({
- id: groupById,
- type: group.type,
- value: [group.id],
- });
+ success = await setPendant(groupBy, [group.id]);
break;
}
case PropertyType.Text: {
- success = await setValue({
- id: groupById,
- type: group.type,
- value: group.id,
- });
+ success = await setPendant(groupBy, group.id);
break;
}
default:
diff --git a/libs/components/editor-core/src/kanban/kanban.ts b/libs/components/editor-core/src/kanban/kanban.ts
index 21564b6b2e..ff33b17f1d 100644
--- a/libs/components/editor-core/src/kanban/kanban.ts
+++ b/libs/components/editor-core/src/kanban/kanban.ts
@@ -199,7 +199,12 @@ export const useRecastKanban = () => {
beforeBlock: string | null,
afterBlock: string | null
) => {
- await moveCardToGroup(groupBy.id, child, kanbanMap[id]);
+ await moveCardToGroup({
+ groupBy,
+ cardBlock: child,
+ group: kanbanMap[id],
+ recastBlock,
+ });
if (beforeBlock) {
const block = await editor.getBlockById(
beforeBlock
@@ -288,7 +293,12 @@ export const useKanban = () => {
);
if (isChangedGroup) {
// 1.2 Move to the target group
- await moveCardToGroup(groupBy.id, targetCard, targetGroup);
+ await moveCardToGroup({
+ groupBy,
+ cardBlock: targetCard,
+ group: targetGroup,
+ recastBlock,
+ });
}
// 2. Reorder the card
@@ -326,7 +336,12 @@ export const useKanban = () => {
}
recastBlock.append(newBlock);
const newCard = newBlock as unknown as RecastItem;
- await moveCardToGroup(groupBy.id, newCard, group);
+ await moveCardToGroup({
+ groupBy,
+ cardBlock: newCard,
+ group,
+ recastBlock,
+ });
},
[editor, groupBy.id, recastBlock]
);
From 2d1266ac1c4bdc5561a4c18bb9247cac484388fc Mon Sep 17 00:00:00 2001
From: tzhangchi
Date: Wed, 3 Aug 2022 10:57:18 +0800
Subject: [PATCH 06/91] docs(contributorsrc): update contributorsrc
---
.all-contributorsrc | 246 +++++++++---------
README.md | 24 +-
.../src/placeholder/PlaceholderPanel.tsx | 1 +
3 files changed, 136 insertions(+), 135 deletions(-)
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 0340f41c92..aadd5a7f93 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1,125 +1,125 @@
{
- "projectName": "Ligo-Virgo",
- "projectOwner": "toeverything",
- "repoType": "github",
- "repoHost": "https://github.com",
- "files": [
- "README.md"
- ],
- "imageSize": 100,
- "commit": false,
- "commitConvention": "angular",
- "contributorsPerLine": 7,
- "contributors": [
- {
- "login": "darkskygit",
- "name": "DarkSky",
- "avatar_url": "https://avatars.githubusercontent.com/u/25152247?v=4",
- "profile": "https://darksky.eu.org/",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "tzhangchi",
- "name": "Chi Zhang",
- "avatar_url": "https://avatars.githubusercontent.com/u/5910926?v=4",
- "profile": "https://zhangchi.blog.csdn.net/",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "alt1o",
- "name": "alt1o",
- "avatar_url": "https://avatars.githubusercontent.com/u/21084335?v=4",
- "profile": "https://github.com/alt1o",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "DiamondThree",
- "name": "Diamond",
- "avatar_url": "https://avatars.githubusercontent.com/u/24630517?v=4",
- "profile": "https://github.com/DiamondThree",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "lawvs",
- "name": "Whitewater",
- "avatar_url": "https://avatars.githubusercontent.com/u/18554747?v=4",
- "profile": "https://lawvs.github.io/profile/",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "zuoxiaodong0815",
- "name": "zuoxiaodong0815",
- "avatar_url": "https://avatars.githubusercontent.com/u/53252747?v=4",
- "profile": "https://github.com/zuoxiaodong0815",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "SaikaSakura",
- "name": "SaikaSakura",
- "avatar_url": "https://avatars.githubusercontent.com/u/11530942?v=4",
- "profile": "https://github.com/SaikaSakura",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "QiShaoXuan",
- "name": "Qi",
- "avatar_url": "https://avatars.githubusercontent.com/u/22772830?v=4",
- "profile": "https://github.com/QiShaoXuan",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "tuluffy",
- "name": "tuluffy",
- "avatar_url": "https://avatars.githubusercontent.com/u/26808339?v=4",
- "profile": "https://tuluffy.github.io/angular.github.io/",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "Austaras",
- "name": "Austaras",
- "avatar_url": "https://avatars.githubusercontent.com/u/15013925?v=4",
- "profile": "https://shockwave.me/",
- "contributions": [
- "code",
- "doc"
- ]
- },
- {
- "login": "uptonking",
- "name": "Jin Yao",
- "avatar_url": "https://avatars.githubusercontent.com/u/11391549?v=4",
- "profile": "https://github.com/uptonking?tab=repositories&type=source",
- "contributions": [
- "code",
- "doc"
- ]
- }
- ]
+ "projectName": "Ligo-Virgo",
+ "projectOwner": "toeverything",
+ "repoType": "github",
+ "repoHost": "https://github.com",
+ "files": [
+ "README.md"
+ ],
+ "imageSize": 100,
+ "commit": false,
+ "commitConvention": "angular",
+ "contributorsPerLine": 7,
+ "contributors": [
+ {
+ "login": "darkskygit",
+ "name": "DarkSky",
+ "avatar_url": "https://avatars.githubusercontent.com/u/25152247?v=4",
+ "profile": "https://darksky.eu.org/",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "tzhangchi",
+ "name": "Chi Zhang",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5910926?v=4",
+ "profile": "http://zhangchi.page/",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "alt1o",
+ "name": "wang xinglong",
+ "avatar_url": "https://avatars.githubusercontent.com/u/21084335?v=4",
+ "profile": "https://github.com/alt1o",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "DiamondThree",
+ "name": "DiamondThree",
+ "avatar_url": "https://avatars.githubusercontent.com/u/24630517?v=4",
+ "profile": "https://github.com/DiamondThree",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "lawvs",
+ "name": "Whitewater",
+ "avatar_url": "https://avatars.githubusercontent.com/u/18554747?v=4",
+ "profile": "https://lawvs.github.io/profile/",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "zuoxiaodong0815",
+ "name": "xiaodong zuo",
+ "avatar_url": "https://avatars.githubusercontent.com/u/53252747?v=4",
+ "profile": "https://github.com/zuoxiaodong0815",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "SaikaSakura",
+ "name": "MingLIang Wang",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11530942?v=4",
+ "profile": "https://github.com/SaikaSakura",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "QiShaoXuan",
+ "name": "Qi",
+ "avatar_url": "https://avatars.githubusercontent.com/u/22772830?v=4",
+ "profile": "https://github.com/QiShaoXuan",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "mitsuhatu",
+ "name": "mitsuhatu",
+ "avatar_url": "https://avatars.githubusercontent.com/u/110213079?v=4",
+ "profile": "https://github.com/mitsuhatu",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "Austaras",
+ "name": "Austaras",
+ "avatar_url": "https://avatars.githubusercontent.com/u/15013925?v=4",
+ "profile": "https://shockwave.me/",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ },
+ {
+ "login": "uptonking",
+ "name": "Jin Yao",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11391549?v=4",
+ "profile": "https://github.com/uptonking?tab=repositories&type=source",
+ "contributions": [
+ "code",
+ "doc"
+ ]
+ }
+ ]
}
diff --git a/README.md b/README.md
index 0fbcf5bf80..4a140fd197 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ See https://github.com/all-contributors/all-contributors/issues/361#issuecomment
-->
-[all-contributors-badge]: https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square
+[](#contributors-)
@@ -151,19 +151,19 @@ For help, discussion about best practices, or any other conversation that would
diff --git a/libs/components/editor-plugins/src/placeholder/PlaceholderPanel.tsx b/libs/components/editor-plugins/src/placeholder/PlaceholderPanel.tsx
index 429336c976..847804bd63 100644
--- a/libs/components/editor-plugins/src/placeholder/PlaceholderPanel.tsx
+++ b/libs/components/editor-plugins/src/placeholder/PlaceholderPanel.tsx
@@ -141,6 +141,7 @@ export const PlaceholderPanel = (props: PlaceholderPanelProps) => {
setOpen(false);
props.onClickTips();
};
+ //@ts-ignore lint rule change result this code lint error, add ingore
const templateList: Array =
TemplateFactory.defaultTemplateList;
const handleNewFromTemplate = async (template: TemplateMeta) => {
From f1774c630c41e82eef2dcde3216f7cbfed250174 Mon Sep 17 00:00:00 2001
From: tzhangchi
Date: Wed, 3 Aug 2022 11:02:24 +0800
Subject: [PATCH 07/91] docs(readme): remove unused contributors info
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 4a140fd197..fa964da17b 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ See https://github.com/all-contributors/all-contributors/issues/361#issuecomment
-->
-[](#contributors-)
+[all-contributors-badge]: https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square
From 928a90942a408d42d5d1a16cc774bd7a32420a93 Mon Sep 17 00:00:00 2001
From: DiamondThree
Date: Wed, 3 Aug 2022 11:04:02 +0800
Subject: [PATCH 08/91] fix clickaway error in page
---
.../src/menu/command-menu/Menu.tsx | 42 ++++++++++---------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/libs/components/editor-plugins/src/menu/command-menu/Menu.tsx b/libs/components/editor-plugins/src/menu/command-menu/Menu.tsx
index 3ab140f3bc..1cd22e48e2 100644
--- a/libs/components/editor-plugins/src/menu/command-menu/Menu.tsx
+++ b/libs/components/editor-plugins/src/menu/command-menu/Menu.tsx
@@ -237,25 +237,29 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
onKeyUpCapture={handle_keyup}
ref={commandMenuContentRef}
>
-
-
-
-
-
+ {show ? (
+
+
+
+
+
+ ) : (
+ <>>
+ )}
);
};
From 727c7428890bf855634973bbe386b4d71521ef96 Mon Sep 17 00:00:00 2001
From: QiShaoXuan
Date: Wed, 3 Aug 2022 11:14:52 +0800
Subject: [PATCH 09/91] feat: modify the type of animation that appears for the
pendant Add button
---
.../src/block-pendant/pendant-render/PandentRender.tsx | 7 +++----
libs/components/ui/src/mui.ts | 6 ++++++
2 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/libs/components/editor-core/src/block-pendant/pendant-render/PandentRender.tsx b/libs/components/editor-core/src/block-pendant/pendant-render/PandentRender.tsx
index 8b6c9bb53f..76ed0d4e32 100644
--- a/libs/components/editor-core/src/block-pendant/pendant-render/PandentRender.tsx
+++ b/libs/components/editor-core/src/block-pendant/pendant-render/PandentRender.tsx
@@ -1,5 +1,5 @@
import {
- MuiZoom,
+ MuiFade,
Popover,
PopperHandler,
styled,
@@ -100,16 +100,15 @@ export const PendantRender = ({ block }: { block: AsyncBlock }) => {
);
})}
{hasAddBtn ? (
-
+
-
+
) : null}
);
diff --git a/libs/components/ui/src/mui.ts b/libs/components/ui/src/mui.ts
index 94025c5f5b..b0658ad526 100644
--- a/libs/components/ui/src/mui.ts
+++ b/libs/components/ui/src/mui.ts
@@ -51,6 +51,7 @@ import {
tooltipClasses,
Typography,
Zoom,
+ Fade,
} from '@mui/material';
export { alpha } from '@mui/system';
@@ -233,6 +234,11 @@ export const MuiInput = Input;
*/
export const MuiZoom = Zoom;
+/**
+ * @deprecated It is not recommended to use Mui directly, because the design will not refer to Mui's interaction logic.
+ */
+export const MuiFade = Fade;
+
/**
* @deprecated It is not recommended to use Mui directly, because the design will not refer to Mui's interaction logic.
*/
From fe9a4470fceee1f235567732e86485963db54b65 Mon Sep 17 00:00:00 2001
From: QiShaoXuan
Date: Wed, 3 Aug 2022 11:18:00 +0800
Subject: [PATCH 10/91] fix: fix mui select throw wraning when pendant select
change
---
.../pendant-operation-panel/CreatePendantPanel.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/components/editor-core/src/block-pendant/pendant-operation-panel/CreatePendantPanel.tsx b/libs/components/editor-core/src/block-pendant/pendant-operation-panel/CreatePendantPanel.tsx
index fa7d50bfba..b09f23ed35 100644
--- a/libs/components/editor-core/src/block-pendant/pendant-operation-panel/CreatePendantPanel.tsx
+++ b/libs/components/editor-core/src/block-pendant/pendant-operation-panel/CreatePendantPanel.tsx
@@ -44,7 +44,7 @@ export const CreatePendantPanel = ({
-
+
# Stay Up-to-Date
From 249eb1746cb03b29c5d7a7ce57554ce86801c473 Mon Sep 17 00:00:00 2001
From: alt0
Date: Wed, 3 Aug 2022 16:26:54 +0800
Subject: [PATCH 12/91] fix: update readme
---
README.md | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index eca6135618..771a6c2249 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,8 @@ See https://github.com/all-contributors/all-contributors/issues/361#issuecomment
- [Plan your task](#plan-your-task)
- [Sort your knowledge](#sort-your-knowledge)
- [Create your story](#create-your-story)
-- [Getting Started with development](#getting-started-with-development)
+- [Documentation](#documentation)
+ - [Getting Started with development](#getting-started-with-development)
- [Roadmap](#roadmap)
- [Releases](#releases)
- [Feature requests](#feature-requests)
@@ -60,6 +61,7 @@ See https://github.com/all-contributors/all-contributors/issues/361#issuecomment
- [The Philosophy of AFFiNE](#the-philosophy-of-affine)
- [Community](#community)
- [Contributors](#contributors)
+- [Acknowledgments](#acknowledgments)
- [License](#license)
## Shape your page
@@ -80,9 +82,13 @@ We want your data always to be yours, and we don't want to make any sacrifice to
Collaboration isn't only necessary for teams -- you may take and insert pics on your phone, then edit them on your desktop, and share them with your collaborators.
Affine is fully built with web technologies so that consistency and accessibility are always guaranteed on Mac, Windows and Linux. The local file system support will be available when version 0.0.1beta is released.
-# Getting Started with development
+# Documentation
-Please view the [documentation](https://affine.gitbook.io/affine/) in Contribute-to-AFFiNE/Software-Contributions/Environment-setup.
+Please view the [documentation](https://affine.gitbook.io/affine/)
+
+## Getting Started with development
+
+Please view the path Contribute-to-AFFiNE/Software-Contributions/Quick-Start in documentation.
# Roadmap
@@ -172,6 +178,10 @@ For help, discussion about best practices, or any other conversation that would
+# Acknowledgments
+
+Thanks a lot to the community for providing such powerful and simple libraries as [slatejs](https://github.com/ianstormtaylor/slate), [tldraw](https://github.com/tldraw/tldraw), [yjs](https://github.com/yjs/yjs), etc., so that we can focus more on the implementation of the business logic, and we hope that in the future our projects will provide a more easy-to-use knowledge base for everyone.
+
# License
AFFiNE is distributed under the terms of MIT license.
From 28f744e57c4b6928d150d1db7cf5277a72be9ce6 Mon Sep 17 00:00:00 2001
From: SaikaSakura
Date: Tue, 2 Aug 2022 16:57:00 +0800
Subject: [PATCH 13/91] feat: grid optimise
---
.../src/blocks/grid/GirdHandle.tsx | 32 ++++--
.../editor-blocks/src/blocks/grid/Grid.tsx | 99 +++++++++++++------
.../src/editor/drag-drop/drag-drop.ts | 12 +++
.../editor-core/src/editor/editor.ts | 29 +++++-
4 files changed, 131 insertions(+), 41 deletions(-)
diff --git a/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx b/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx
index e6464860c5..3ac519f3c5 100644
--- a/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx
+++ b/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx
@@ -12,6 +12,8 @@ type GridHandleProps = {
blockId: string;
enabledAddItem: boolean;
draggable: boolean;
+ alertHandleId: string;
+ onMouseEnter?: React.MouseEventHandler;
};
export const GridHandle: FC = function ({
@@ -21,6 +23,8 @@ export const GridHandle: FC = function ({
onDrag,
onMouseDown,
draggable,
+ alertHandleId,
+ onMouseEnter,
}) {
const [isMouseDown, setIsMouseDown] = useState(false);
const handleMouseDown: React.MouseEventHandler = e => {
@@ -44,16 +48,17 @@ export const GridHandle: FC = function ({
editor.selectionManager.setActivatedNodeId(textBlock.id);
}
};
+
+ const handleMouseEnter: React.MouseEventHandler = e => {
+ onMouseEnter && onMouseEnter(e);
+ };
+
return (
{enabledAddItem ? (
= function ({
);
};
-const GridHandleContainer = styled('div')(({ theme }) => ({
+const GridHandleContainer = styled('div')<{
+ isMouseDown: boolean;
+ isAlert: boolean;
+}>(({ theme, isMouseDown, isAlert }) => ({
position: 'relative',
width: '10px',
flexGrow: '0',
@@ -78,11 +86,17 @@ const GridHandleContainer = styled('div')(({ theme }) => ({
borderRadius: '1px',
backgroundClip: 'content-box',
' &:hover': {
- backgroundColor: theme.affine.palette.primary,
+ backgroundColor: isAlert
+ ? 'red !important'
+ : theme.affine.palette.primary,
[`.${GRID_ADD_HANDLE_NAME}`]: {
display: 'block',
},
},
+ ...(isMouseDown &&
+ (isAlert
+ ? { backgroundColor: 'red' }
+ : { backgroundColor: theme.affine.palette.primary })),
}));
const AddGridHandle = styled('div')(({ theme }) => ({
diff --git a/libs/components/editor-blocks/src/blocks/grid/Grid.tsx b/libs/components/editor-blocks/src/blocks/grid/Grid.tsx
index 77a83ff599..bc27ec4ac6 100644
--- a/libs/components/editor-blocks/src/blocks/grid/Grid.tsx
+++ b/libs/components/editor-blocks/src/blocks/grid/Grid.tsx
@@ -31,6 +31,7 @@ export const Grid: FC = function (props) {
const gridItemCountRef = useRef();
const originalLeftWidth = useRef(GRID_ITEM_MIN_WIDTH);
const originalRightWidth = useRef(GRID_ITEM_MIN_WIDTH);
+ const [alertHandleId, setAlertHandleId] = useState(null);
const getLeftRightGridItemDomByIndex = (index: number) => {
const gridItems = Array.from(gridContainerRef.current?.children).filter(
@@ -117,7 +118,7 @@ export const Grid: FC = function (props) {
itemDom.style.width = width;
};
- const handleDragGrid = (e: MouseEvent, index: number) => {
+ const handleDragGrid = async (e: MouseEvent, index: number) => {
setIsOnDrag(true);
window.getSelection().removeAllRanges();
if (!isSetMouseUp.current) {
@@ -165,39 +166,47 @@ export const Grid: FC = function (props) {
setItemWidth(leftGrid, newLeft);
setItemWidth(rightGrid, newRight);
updateDbWidth(leftBlockId, newLeft, rightBlockId, newRight);
+ [leftBlockId, rightBlockId].forEach(async blockId => {
+ if (await checkGridItemHasOverflow(blockId)) {
+ setAlertHandleId(leftBlockId);
+ } else {
+ setAlertHandleId(null);
+ }
+ });
}
}
};
- const children = (
- <>
- {block.childrenIds.map((id, i) => {
- return (
-
-
- handleDragGrid(event, i)}
- editor={editor}
- onMouseDown={event => handleMouseDown(event, i)}
- blockId={id}
- enabledAddItem={
- block.childrenIds.length < MAX_ITEM_COUNT
- }
- draggable={i !== block.childrenIds.length - 1}
- />
-
- );
- })}
- >
- );
+ const checkGridItemHasOverflow = async (blockId: string) => {
+ let isOverflow = false;
+ const block = await editor.getBlockById(blockId);
+ if (block) {
+ const blockDom = block.dom;
+ if (blockDom) {
+ block.dom.style.overflow = 'scroll';
+ if (block.dom.clientWidth !== block.dom.scrollWidth) {
+ isOverflow = true;
+ }
+ blockDom.style.overflow = 'visible';
+ }
+ }
+ return isOverflow;
+ };
+
+ const handleHandleMouseEnter = (
+ e: React.MouseEvent,
+ index: number
+ ) => {
+ const leftBlockId = block.childrenIds[index];
+ const rightBlockId = block.childrenIds[index + 1];
+ [leftBlockId, rightBlockId].forEach(async blockId => {
+ if (await checkGridItemHasOverflow(blockId)) {
+ setAlertHandleId(leftBlockId);
+ } else {
+ setAlertHandleId(null);
+ }
+ });
+ };
return (
<>
@@ -206,7 +215,35 @@ export const Grid: FC = function (props) {
ref={gridContainerRef}
isOnDrag={isOnDrag}
>
- {children}
+ {block.childrenIds.map((id, i) => {
+ return (
+
+
+ handleDragGrid(event, i)}
+ editor={editor}
+ onMouseDown={event => handleMouseDown(event, i)}
+ blockId={id}
+ enabledAddItem={
+ block.childrenIds.length < MAX_ITEM_COUNT
+ }
+ onMouseEnter={event =>
+ handleHandleMouseEnter(event, i)
+ }
+ alertHandleId={alertHandleId}
+ draggable={i !== block.childrenIds.length - 1}
+ />
+
+ );
+ })}
{isOnDrag
? ReactDOM.createPortal(, window.document.body)
diff --git a/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts b/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts
index d3160d56f5..35426c73de 100644
--- a/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts
+++ b/libs/components/editor-core/src/editor/drag-drop/drag-drop.ts
@@ -12,6 +12,7 @@ enum DragType {
}
const DRAG_STATE_CHANGE_EVENT_KEY = 'dragStateChange';
+const MAX_GRID_BLOCK_FLOOR = 3;
export class DragDropManager {
private _editor: Editor;
private _enabled: boolean;
@@ -231,6 +232,17 @@ export class DragDropManager {
if (!(await this._canBeDrop(event))) {
direction = BlockDropPlacement.none;
}
+ if (
+ direction === BlockDropPlacement.left ||
+ direction === BlockDropPlacement.right
+ ) {
+ const path = await this._editor.getBlockPath(blockId);
+ const gridBlocks = path.filter(block => block.type === 'grid');
+ // limit grid block floor counts
+ if (gridBlocks.length >= MAX_GRID_BLOCK_FLOOR) {
+ direction = BlockDropPlacement.none;
+ }
+ }
this._setBlockDragDirection(direction);
return direction;
}
diff --git a/libs/components/editor-core/src/editor/editor.ts b/libs/components/editor-core/src/editor/editor.ts
index 73e7f6f7d5..8750a566c7 100644
--- a/libs/components/editor-core/src/editor/editor.ts
+++ b/libs/components/editor-core/src/editor/editor.ts
@@ -340,7 +340,20 @@ export class Editor implements Virgo {
const rootBlockId = this.getRootBlockId();
const rootBlock = await this.getBlockById(rootBlockId);
const blockList: Array = rootBlock ? [rootBlock] : [];
- const children = (await rootBlock?.children()) || [];
+ return [...blockList, ...(await this.getOffspring(rootBlockId))];
+ }
+
+ /**
+ *
+ * get all offspring of block
+ * @param {string} id
+ * @return {*}
+ * @memberof Editor
+ */
+ async getOffspring(id: string) {
+ const block = await this.getBlockById(id);
+ const blockList: Array = [];
+ const children = (await block?.children()) || [];
for (const block of children) {
if (!block) {
continue;
@@ -379,6 +392,20 @@ export class Editor implements Virgo {
return lastBlock;
}
+ async getBlockPath(id: string) {
+ const block = await this.getBlockById(id);
+ if (!block) {
+ return [];
+ }
+ const path = [block];
+ let parent = await block.parent();
+ while (parent) {
+ path.unshift(parent);
+ parent = await parent.parent();
+ }
+ return path;
+ }
+
async getBlockByPoint(point: Point) {
const blockList = await this.getBlockList();
From 307032239346bbbad07278c23587ad5e6eba4c79 Mon Sep 17 00:00:00 2001
From: SaikaSakura
Date: Tue, 2 Aug 2022 17:27:06 +0800
Subject: [PATCH 14/91] feat: change left menu icon style
---
.../src/menu/group-menu/DragItem.tsx | 20 +++++++++++--------
.../src/menu/left-menu/LeftMenuDraggable.tsx | 12 +++++++----
2 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx b/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx
index a323ca1961..35ee3d9683 100644
--- a/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx
+++ b/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx
@@ -4,7 +4,7 @@ import { HandleParentIcon } from '@toeverything/components/icons';
import { styled } from '@toeverything/components/ui';
import { Point } from '@toeverything/utils';
-export const ICON_WIDTH = 24;
+export const ICON_WIDTH = 16;
type DragItemProps = {
isShow: boolean;
@@ -30,17 +30,21 @@ export const DragItem = function ({
);
};
-const StyledDiv = styled('div')({
+const StyledDiv = styled('div')(theme => ({
padding: '0',
- display: 'inlineFlex',
+ display: 'inline-flex',
width: `${ICON_WIDTH}px`,
- height: `${ICON_WIDTH}px`,
+ height: '20px',
cursor: 'grab',
- ':hover': {
- backgroundColor: '#edeef0',
- borderRadius: '4px',
+ '& svg': {
+ fontSize: '20px',
+ marginLeft: '-2px',
},
-});
+ ':hover': {
+ backgroundColor: '#F5F7F8',
+ borderRadius: '3.75px',
+ },
+}));
const StyledButton = styled('div')({
padding: '0',
diff --git a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx
index 602c08aacb..693c29c9b4 100644
--- a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx
+++ b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx
@@ -239,11 +239,15 @@ const Draggable = styled(Button)({
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'transparent',
- width: '24px',
- height: '24px',
+ width: '16px',
+ height: '20px',
+ '& svg': {
+ fontSize: '20px',
+ marginLeft: '-2px',
+ },
':hover': {
- backgroundColor: '#edeef0',
- borderRadius: '4px',
+ backgroundColor: '#F5F7F8',
+ borderRadius: '3.75px',
},
});
From e813b4a8d9157afedacd06c3959602ef59a67f56 Mon Sep 17 00:00:00 2001
From: SaikaSakura
Date: Wed, 3 Aug 2022 14:38:27 +0800
Subject: [PATCH 15/91] feat: fix code review
---
libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx | 4 +---
.../editor-plugins/src/menu/group-menu/DragItem.tsx | 4 ++--
.../editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx | 2 +-
3 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx b/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx
index 3ac519f3c5..687cc28735 100644
--- a/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx
+++ b/libs/components/editor-blocks/src/blocks/grid/GirdHandle.tsx
@@ -86,9 +86,7 @@ const GridHandleContainer = styled('div')<{
borderRadius: '1px',
backgroundClip: 'content-box',
' &:hover': {
- backgroundColor: isAlert
- ? 'red !important'
- : theme.affine.palette.primary,
+ backgroundColor: isAlert ? 'red' : theme.affine.palette.primary,
[`.${GRID_ADD_HANDLE_NAME}`]: {
display: 'block',
},
diff --git a/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx b/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx
index 35ee3d9683..73ab1fde1c 100644
--- a/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx
+++ b/libs/components/editor-plugins/src/menu/group-menu/DragItem.tsx
@@ -30,7 +30,7 @@ export const DragItem = function ({
);
};
-const StyledDiv = styled('div')(theme => ({
+const StyledDiv = styled('div')(({ theme }) => ({
padding: '0',
display: 'inline-flex',
width: `${ICON_WIDTH}px`,
@@ -42,7 +42,7 @@ const StyledDiv = styled('div')(theme => ({
},
':hover': {
backgroundColor: '#F5F7F8',
- borderRadius: '3.75px',
+ borderRadius: '4px',
},
}));
diff --git a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx
index 693c29c9b4..297193971c 100644
--- a/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx
+++ b/libs/components/editor-plugins/src/menu/left-menu/LeftMenuDraggable.tsx
@@ -247,7 +247,7 @@ const Draggable = styled(Button)({
},
':hover': {
backgroundColor: '#F5F7F8',
- borderRadius: '3.75px',
+ borderRadius: '4px',
},
});
From 67238b8da8ddb010e5f6b78ddc34f10302b27779 Mon Sep 17 00:00:00 2001
From: lawvs <18554747+lawvs@users.noreply.github.com>
Date: Wed, 3 Aug 2022 16:38:47 +0800
Subject: [PATCH 16/91] fix: check error
---
.../src/placeholder/PlaceholderPanel.tsx | 56 +++++++++----------
.../templates/template-factory.ts | 6 +-
2 files changed, 29 insertions(+), 33 deletions(-)
diff --git a/libs/components/editor-plugins/src/placeholder/PlaceholderPanel.tsx b/libs/components/editor-plugins/src/placeholder/PlaceholderPanel.tsx
index 429336c976..89b66256f7 100644
--- a/libs/components/editor-plugins/src/placeholder/PlaceholderPanel.tsx
+++ b/libs/components/editor-plugins/src/placeholder/PlaceholderPanel.tsx
@@ -141,8 +141,7 @@ export const PlaceholderPanel = (props: PlaceholderPanelProps) => {
setOpen(false);
props.onClickTips();
};
- const templateList: Array =
- TemplateFactory.defaultTemplateList;
+ const templateList = TemplateFactory.defaultTemplateList;
const handleNewFromTemplate = async (template: TemplateMeta) => {
const pageId = await editor.getRootBlockId();
const newPage = await services.api.editorBlock.getBlock(
@@ -162,33 +161,30 @@ export const PlaceholderPanel = (props: PlaceholderPanelProps) => {
setOpen(false);
};
return (
- <>
-
-
- Press Enter to continue with an empty page, or pick a
- template
-
-
- {templateList.map((template, index) => {
- return (
- {
- handleNewFromTemplate(template);
- }}
- >
- {template.name}
-
- );
- })}
-
-
- >
+
+
+ Press Enter to continue with an empty page, or pick a template
+
+
+ {templateList.map((template, index) => {
+ return (
+ {
+ handleNewFromTemplate(template);
+ }}
+ >
+ {template.name}
+
+ );
+ })}
+
+
);
};
diff --git a/libs/datasource/db-service/src/services/editor-block/templates/template-factory.ts b/libs/datasource/db-service/src/services/editor-block/templates/template-factory.ts
index 953cf0e6de..88a8d08bbe 100644
--- a/libs/datasource/db-service/src/services/editor-block/templates/template-factory.ts
+++ b/libs/datasource/db-service/src/services/editor-block/templates/template-factory.ts
@@ -13,7 +13,7 @@ const groupTemplateMap = {
grid: gridTemplate,
} as GroupTemplateMap;
-const defaultTemplateList = [
+const defaultTemplateList: Array = [
{
name: 'New From Quick Start',
groupKeys: ['todolist'],
@@ -22,10 +22,10 @@ const defaultTemplateList = [
{ name: 'New From Blog', groupKeys: ['blog'] },
{ name: ' New Todolist', groupKeys: ['todolist'] },
{ name: ' New Empty Page', groupKeys: ['empty'] },
-] as const;
+];
const TemplateFactory = {
- defaultTemplateList: defaultTemplateList,
+ defaultTemplateList,
generatePageTemplateByGroupKeys(props: TemplateMeta): Template {
const newTitle = props.name || 'Get Started with AFFiNE';
const keys: GroupTemplateKeys[] = props.groupKeys || [];
From 57422cbd7872bed059cd0913531814c7fb4322f8 Mon Sep 17 00:00:00 2001
From: mitsuha
Date: Wed, 3 Aug 2022 17:18:40 +0800
Subject: [PATCH 17/91] opti: 1.adjust page-tree header;
---
apps/ligo-virgo/src/assets/images/logo.svg | 4 +
.../src/pages/workspace/docs/Page.tsx | 7 +-
.../workspace/docs/components/tabs/Tabs.tsx | 61 +++++++++
.../workspace/docs/components/tabs/index.ts | 1 +
.../pages/workspace/docs/workspace-name.tsx | 125 +++++++++++-------
5 files changed, 147 insertions(+), 51 deletions(-)
create mode 100644 apps/ligo-virgo/src/assets/images/logo.svg
create mode 100644 apps/ligo-virgo/src/pages/workspace/docs/components/tabs/Tabs.tsx
create mode 100644 apps/ligo-virgo/src/pages/workspace/docs/components/tabs/index.ts
diff --git a/apps/ligo-virgo/src/assets/images/logo.svg b/apps/ligo-virgo/src/assets/images/logo.svg
new file mode 100644
index 0000000000..2180acf189
--- /dev/null
+++ b/apps/ligo-virgo/src/assets/images/logo.svg
@@ -0,0 +1,4 @@
+
diff --git a/apps/ligo-virgo/src/pages/workspace/docs/Page.tsx b/apps/ligo-virgo/src/pages/workspace/docs/Page.tsx
index b2ebc5f7dc..556ca606a7 100644
--- a/apps/ligo-virgo/src/pages/workspace/docs/Page.tsx
+++ b/apps/ligo-virgo/src/pages/workspace/docs/Page.tsx
@@ -31,7 +31,7 @@ import { WorkspaceName } from './workspace-name';
import { CollapsiblePageTree } from './collapsible-page-tree';
import { useFlag } from '@toeverything/datasource/feature-flags';
import { type BlockEditor } from '@toeverything/components/editor-core';
-
+import { Tabs } from './components/tabs';
type PageProps = {
workspace: string;
};
@@ -80,7 +80,9 @@ export function Page(props: PageProps) {
onMouseLeave={() => setSpaceSidebarVisible(false)}
>
-
+
+
+
{dailyNotesFlag && (
@@ -219,4 +221,5 @@ const WorkspaceSidebar = styled('div')(({ hidden }) => ({
const WorkspaceSidebarContent = styled('div')({
flex: 'auto',
overflow: 'hidden auto',
+ marginTop: '18px',
});
diff --git a/apps/ligo-virgo/src/pages/workspace/docs/components/tabs/Tabs.tsx b/apps/ligo-virgo/src/pages/workspace/docs/components/tabs/Tabs.tsx
new file mode 100644
index 0000000000..3722c5e162
--- /dev/null
+++ b/apps/ligo-virgo/src/pages/workspace/docs/components/tabs/Tabs.tsx
@@ -0,0 +1,61 @@
+import { useState } from 'react';
+import { MuiDivider as Divider, styled } from '@toeverything/components/ui';
+import type { ValueOf } from '@toeverything/utils';
+
+const StyledTabs = styled('div')({
+ width: '100%',
+ height: '12px',
+ marginTop: '12px',
+ display: 'flex',
+ alignItems: 'center',
+ cursor: 'pointer',
+});
+
+const StyledDivider = styled(Divider)<{ isActive?: boolean }>(
+ ({ isActive }) => {
+ return {
+ flex: 1,
+ backgroundColor: isActive ? '#3E6FDB' : '#ECF1FB',
+ borderWidth: '2px',
+ };
+ }
+);
+
+const TAB_TITLE = {
+ PAGES: 'pages',
+ GALLERY: 'gallery',
+ TOC: 'toc',
+} as const;
+
+const TabMap = new Map
([
+ ['PAGES', 'pages'],
+ ['GALLERY', 'gallery'],
+ ['TOC', 'toc'],
+]);
+
+type TabKey = keyof typeof TAB_TITLE;
+type TabValue = ValueOf;
+
+const Tabs = () => {
+ const [activeTab, setActiveTab] = useState(TAB_TITLE.PAGES);
+
+ const onClick = (v: TabValue) => {
+ setActiveTab(v);
+ };
+
+ return (
+
+ {[...TabMap.entries()].map(([k, v]) => {
+ return (
+ onClick(v)}
+ />
+ );
+ })}
+
+ );
+};
+
+export { Tabs };
diff --git a/apps/ligo-virgo/src/pages/workspace/docs/components/tabs/index.ts b/apps/ligo-virgo/src/pages/workspace/docs/components/tabs/index.ts
new file mode 100644
index 0000000000..941e6baba5
--- /dev/null
+++ b/apps/ligo-virgo/src/pages/workspace/docs/components/tabs/index.ts
@@ -0,0 +1 @@
+export { Tabs } from './Tabs';
diff --git a/apps/ligo-virgo/src/pages/workspace/docs/workspace-name.tsx b/apps/ligo-virgo/src/pages/workspace/docs/workspace-name.tsx
index 24d69b659a..35b52ee264 100644
--- a/apps/ligo-virgo/src/pages/workspace/docs/workspace-name.tsx
+++ b/apps/ligo-virgo/src/pages/workspace/docs/workspace-name.tsx
@@ -1,27 +1,29 @@
import {
- MuiButton as Button,
- Switch,
styled,
MuiOutlinedInput as OutlinedInput,
} from '@toeverything/components/ui';
-import { LogoIcon } from '@toeverything/components/icons';
+import { PinIcon } from '@toeverything/components/icons';
+import logo from '../../../assets/images/logo.svg';
import {
useUserAndSpaces,
useShowSpaceSidebar,
} from '@toeverything/datasource/state';
-import { useCallback, useEffect, useRef, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
+import type { CSSProperties } from 'react';
import { services } from '@toeverything/datasource/db-service';
const WorkspaceContainer = styled('div')({
display: 'flex',
- alignItems: 'center',
- minHeight: 60,
- padding: '12px 0px',
+ flexDirection: 'column',
+ paddingBottom: '12px',
color: '#566B7D',
});
const LeftContainer = styled('div')({
flex: 'auto',
display: 'flex',
+ height: '52px',
+ alignItems: 'center',
+ margin: '0 12px',
});
const LogoContainer = styled('div')({
@@ -32,20 +34,40 @@ const LogoContainer = styled('div')({
minWidth: 24,
});
-const StyledLogoIcon = styled(LogoIcon)(({ theme }) => {
- return {
- color: theme.affine.palette.primary,
- width: '16px !important',
- height: '16px !important',
- };
+export const LogoImg = ({ style = {} }: { style?: CSSProperties }) => {
+ return
;
+};
+
+const StyledPin = styled('div')({
+ display: 'flex',
+ justifyContent: 'end',
+ alignItems: 'center',
+});
+
+const StyledWorkspace = styled('div')({
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'space-between',
+ marginLeft: '12px',
+ paddingLeft: '12px',
+});
+
+const StyledWorkspaceDesc = styled('div')({
+ fontSize: '12px',
+ color: '#98ACBD',
+ height: '18px',
+
+ display: 'flex',
+ alignItems: 'center',
});
const WorkspaceNameContainer = styled('div')({
display: 'flex',
alignItems: 'center',
flex: 'auto',
- width: '100px',
- marginRight: '10px',
+ width: '165px',
+ height: '34px',
input: {
padding: '5px 10px',
},
@@ -58,21 +80,15 @@ const WorkspaceNameContainer = styled('div')({
});
const WorkspaceReNameContainer = styled('div')({
- marginRight: '10px',
+ height: '34px',
+ display: 'flex',
+ alignItems: 'center',
+
input: {
padding: '5px 10px',
},
});
-const ToggleDisplayContainer = styled('div')({
- display: 'flex',
- alignItems: 'center',
- fontSize: 12,
- color: '#3E6FDB',
- padding: 6,
- minWidth: 64,
-});
-
export const WorkspaceName = () => {
const { currentSpaceId } = useUserAndSpaces();
const { fixedDisplay, toggleSpaceSidebar } = useShowSpaceSidebar();
@@ -139,35 +155,46 @@ export const WorkspaceName = () => {
return (
+
+
+
-
+
- {inRename ? (
-
- setInRename(false)}
- />
-
- ) : (
-
- setInRename(true)}>
- {workspaceName || workspaceId}
-
-
- )}
+
+ {inRename ? (
+
+ setInRename(false)}
+ />
+
+ ) : (
+
+ setInRename(true)}>
+ {workspaceName || workspaceId}
+
+
+ )}
+
+
+ To shape, Not to Adapt.
+
+
-
-
-
);
};
From cb7b16c7b34cd275e41b1a7930cf10856f66dfac Mon Sep 17 00:00:00 2001
From: alt0
Date: Wed, 3 Aug 2022 17:28:21 +0800
Subject: [PATCH 18/91] feat: support set frame background color
---
.../components/command-panel/CommandPanel.tsx | 8 ++
.../command-panel/FrameFillColorConfig.tsx | 89 +++++++++++++++++++
.../command-panel/utils/use-config.ts | 12 +++
.../{frame-util.tsx => FrameUtil.tsx} | 60 ++++---------
.../src/frame-util/components/Frame.tsx | 54 +++++++++++
.../src/frame-util/components/draw-frame.tsx | 42 ---------
.../board-shapes/src/frame-util/index.ts | 2 +-
.../src/select-tool/select-tool.ts | 1 +
8 files changed, 182 insertions(+), 86 deletions(-)
create mode 100644 libs/components/board-draw/src/components/command-panel/FrameFillColorConfig.tsx
rename libs/components/board-shapes/src/frame-util/{frame-util.tsx => FrameUtil.tsx} (67%)
create mode 100644 libs/components/board-shapes/src/frame-util/components/Frame.tsx
delete mode 100644 libs/components/board-shapes/src/frame-util/components/draw-frame.tsx
diff --git a/libs/components/board-draw/src/components/command-panel/CommandPanel.tsx b/libs/components/board-draw/src/components/command-panel/CommandPanel.tsx
index f1f6961e5a..1e73889fb2 100644
--- a/libs/components/board-draw/src/components/command-panel/CommandPanel.tsx
+++ b/libs/components/board-draw/src/components/command-panel/CommandPanel.tsx
@@ -11,6 +11,7 @@ import { StrokeLineStyleConfig } from './stroke-line-style-config';
import { Group, UnGroup } from './GroupOperation';
import { DeleteShapes } from './DeleteOperation';
import { Lock, Unlock } from './LockOperation';
+import { FrameFillColorConfig } from './FrameFillColorConfig';
export const CommandPanel: FC<{ app: TldrawApp }> = ({ app }) => {
const state = app.useStore();
@@ -51,6 +52,13 @@ export const CommandPanel: FC<{ app: TldrawApp }> = ({ app }) => {
shapes={config.fill.selectedShapes}
/>
) : null,
+ frameFill: config.frameFill.selectedShapes.length ? (
+
+ ) : null,
font: config.font.selectedShapes.length ? (
{
+ const counted = countBy(shapes, shape => shape.style.fill);
+ const max = maxBy(Object.entries(counted), ([c, n]) => n);
+ return max[0];
+};
+
+export const FrameFillColorConfig: FC = ({
+ app,
+ shapes,
+}) => {
+ const theme = useTheme();
+ const setFillColor = (color: ColorType) => {
+ app.style(
+ { fill: color, isFilled: color !== 'none' },
+ getShapeIds(shapes)
+ );
+ };
+
+ const iconColor = _getIconRenderColor(shapes);
+
+ return (
+
+ }
+ >
+
+
+ {iconColor === 'none' ? (
+
+ ) : (
+
+ )}
+
+
+
+ );
+};
diff --git a/libs/components/board-draw/src/components/command-panel/utils/use-config.ts b/libs/components/board-draw/src/components/command-panel/utils/use-config.ts
index 05b08b46c6..947cfbd29c 100644
--- a/libs/components/board-draw/src/components/command-panel/utils/use-config.ts
+++ b/libs/components/board-draw/src/components/command-panel/utils/use-config.ts
@@ -7,6 +7,7 @@ interface Config {
type:
| 'stroke'
| 'fill'
+ | 'frameFill'
| 'font'
| 'group'
| 'ungroup'
@@ -22,6 +23,10 @@ const _createInitConfig = (): Record => {
type: 'fill',
selectedShapes: [],
},
+ frameFill: {
+ type: 'frameFill',
+ selectedShapes: [],
+ },
stroke: {
type: 'stroke',
selectedShapes: [],
@@ -91,6 +96,10 @@ const _isSupportFont = (shape: TDShape): boolean => {
].some(type => type === shape.type);
};
+const _isSupportFrameFill = (shape: TDShape): boolean => {
+ return shape.type === TDShapeType.Frame;
+};
+
export const useConfig = (app: TldrawApp): Record => {
const state = app.useStore();
const selectedShapes = TLDR.get_selected_shapes(state, app.currentPageId);
@@ -105,6 +114,9 @@ export const useConfig = (app: TldrawApp): Record => {
if (_isSupportFont(cur)) {
acc.font.selectedShapes.push(cur);
}
+ if (_isSupportFrameFill(cur)) {
+ acc.frameFill.selectedShapes.push(cur);
+ }
return acc;
},
_createInitConfig()
diff --git a/libs/components/board-shapes/src/frame-util/frame-util.tsx b/libs/components/board-shapes/src/frame-util/FrameUtil.tsx
similarity index 67%
rename from libs/components/board-shapes/src/frame-util/frame-util.tsx
rename to libs/components/board-shapes/src/frame-util/FrameUtil.tsx
index f8db4d09ff..47fc778da7 100644
--- a/libs/components/board-shapes/src/frame-util/frame-util.tsx
+++ b/libs/components/board-shapes/src/frame-util/FrameUtil.tsx
@@ -1,12 +1,9 @@
-import * as React from 'react';
+/* eslint-disable no-restricted-syntax */
import { Utils, SVGContainer } from '@tldraw/core';
import {
FrameShape,
- DashStyle,
TDShapeType,
TDMeta,
- GHOSTED_OPACITY,
- LABEL_POINT,
} from '@toeverything/components/board-types';
import { TDShapeUtil } from '../TDShapeUtil';
import {
@@ -14,14 +11,13 @@ import {
getShapeStyle,
getBoundsRectangle,
transformRectangle,
- getFontStyle,
transformSingleRectangle,
} from '../shared';
-import { DrawFrame } from './components/draw-frame';
+import { Frame } from './components/Frame';
import { styled } from '@toeverything/components/ui';
type T = FrameShape;
-type E = HTMLDivElement;
+type E = SVGSVGElement;
export class FrameUtil extends TDShapeUtil {
type = TDShapeType.Frame as const;
@@ -56,10 +52,7 @@ export class FrameUtil extends TDShapeUtil {
(
{
shape,
- isEditing,
- isBinding,
isSelected,
- isGhost,
meta,
bounds,
events,
@@ -70,21 +63,20 @@ export class FrameUtil extends TDShapeUtil {
) => {
const { id, size, style } = shape;
return (
-
-
-
-
-
+
+
+
);
}
);
@@ -121,27 +113,9 @@ export class FrameUtil extends TDShapeUtil {
override transform = transformRectangle;
override transformSingle = transformSingleRectangle;
-
- override hitTestPoint = (shape: T, point: number[]): boolean => {
- return false;
- };
-
- override hitTestLineSegment = (
- shape: T,
- A: number[],
- B: number[]
- ): boolean => {
- return false;
- };
}
const FullWrapper = styled('div')({
width: '100%',
height: '100%',
- '.tl-fill-hitarea': {
- fill: '#F7F9FF',
- },
- '.tl-stroke-hitarea': {
- fill: '#F7F9FF',
- },
});
diff --git a/libs/components/board-shapes/src/frame-util/components/Frame.tsx b/libs/components/board-shapes/src/frame-util/components/Frame.tsx
new file mode 100644
index 0000000000..57a6fa878a
--- /dev/null
+++ b/libs/components/board-shapes/src/frame-util/components/Frame.tsx
@@ -0,0 +1,54 @@
+import * as React from 'react';
+import { BINDING_DISTANCE } from '@toeverything/components/board-types';
+import type { ShapeStyles } from '@toeverything/components/board-types';
+import { getShapeStyle } from '../../shared';
+
+interface RectangleSvgProps {
+ id: string;
+ style: ShapeStyles;
+ isSelected: boolean;
+ size: number[];
+ isDarkMode: boolean;
+}
+
+export const Frame = React.memo(function DashedRectangle({
+ id,
+ style,
+ size,
+ isSelected,
+ isDarkMode,
+}: RectangleSvgProps) {
+ const { strokeWidth, fill } = getShapeStyle(style, isDarkMode);
+
+ const _fill = fill && fill !== 'none' ? fill : '#F7F9FF';
+
+ const sw = 1 + strokeWidth * 1.618;
+
+ const w = Math.max(0, size[0] - sw / 2);
+ const h = Math.max(0, size[1] - sw / 2);
+
+ return (
+ <>
+
+
+ >
+ );
+});
diff --git a/libs/components/board-shapes/src/frame-util/components/draw-frame.tsx b/libs/components/board-shapes/src/frame-util/components/draw-frame.tsx
deleted file mode 100644
index 4074f76b15..0000000000
--- a/libs/components/board-shapes/src/frame-util/components/draw-frame.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import * as React from 'react';
-import { BINDING_DISTANCE } from '@toeverything/components/board-types';
-import type { ShapeStyles } from '@toeverything/components/board-types';
-import { getShapeStyle } from '../../shared';
-
-interface RectangleSvgProps {
- id: string;
- style: ShapeStyles;
- isSelected: boolean;
- size: number[];
- isDarkMode: boolean;
-}
-
-export const DrawFrame = React.memo(function DashedRectangle({
- id,
- style,
- size,
- isSelected,
- isDarkMode,
-}: RectangleSvgProps) {
- const { stroke, strokeWidth, fill } = getShapeStyle(style, isDarkMode);
-
- const sw = 1 + strokeWidth * 1.618;
-
- const w = Math.max(0, size[0] - sw / 2);
- const h = Math.max(0, size[1] - sw / 2);
-
- return (
-
- );
-});
diff --git a/libs/components/board-shapes/src/frame-util/index.ts b/libs/components/board-shapes/src/frame-util/index.ts
index bda0014cda..5401a5fd24 100644
--- a/libs/components/board-shapes/src/frame-util/index.ts
+++ b/libs/components/board-shapes/src/frame-util/index.ts
@@ -1 +1 @@
-export * from './frame-util';
+export * from './FrameUtil';
diff --git a/libs/components/board-tools/src/select-tool/select-tool.ts b/libs/components/board-tools/src/select-tool/select-tool.ts
index 3c96d0fa56..c4883f4a22 100644
--- a/libs/components/board-tools/src/select-tool/select-tool.ts
+++ b/libs/components/board-tools/src/select-tool/select-tool.ts
@@ -284,6 +284,7 @@ export class SelectTool extends BaseTool {
override onPointerMove: TLPointerEventHandler = (info, e) => {
const { originPoint, currentPoint } = this.app;
+ console.log('this.status', this.status);
switch (this.status) {
case Status.PointingBoundsHandle: {
From 017db7d7f10b2cd7f59ea728436f16e82aec3ba0 Mon Sep 17 00:00:00 2001
From: DarkSky
Date: Wed, 3 Aug 2022 17:28:19 +0800
Subject: [PATCH 19/91] feat: venus style adjust
---
apps/venus/src/app/index.tsx | 132 ++++++++++++++++--------
apps/venus/src/assets/collaboration.png | Bin 1721901 -> 1092345 bytes
apps/venus/src/assets/page.png | Bin 1055959 -> 1089437 bytes
apps/venus/src/assets/shape.png | Bin 1240661 -> 958124 bytes
apps/venus/src/assets/task.png | Bin 1222509 -> 587828 bytes
5 files changed, 90 insertions(+), 42 deletions(-)
diff --git a/apps/venus/src/app/index.tsx b/apps/venus/src/app/index.tsx
index 8e17108f13..8bb50308c0 100644
--- a/apps/venus/src/app/index.tsx
+++ b/apps/venus/src/app/index.tsx
@@ -20,12 +20,12 @@ const DiscordIcon = (props: any) => {
width="71"
height="55"
viewBox="0 0 71 55"
- fill="none"
+ fill="currentcolor"
>
@@ -515,10 +515,12 @@ export function App() {
justifyContent: 'left',
textAlign: 'left',
transition: 'all .5s',
- // boxShadow: '2px 2px 40px #08f2',
- // ':hover': {
- // boxShadow: '2px 2px 40px #08f4',
- // },
+ transform: 'scale(0.98)',
+ boxShadow: '2px 2px 40px #0002',
+ ':hover': {
+ transform: 'scale(1)',
+ boxShadow: '2px 2px 40px #0004',
+ },
}}
>
-
+ window.open(
+ 'https://github.com/toeverything/AFFiNE/'
+ )
+ }
>
GitHub
-
+
-
+ window.open('https://www.reddit.com/r/Affine/')
+ }
>
Reddit
-
+
-
+ window.open('https://t.me/affineworkos')
+ }
>
Telegram
-
+
-
+ window.open('https://discord.gg/yz6tGVsf5p')
+ }
>
@@ -883,12 +927,16 @@ export function App() {
}}
>
Discord
-
+
diff --git a/apps/venus/src/assets/collaboration.png b/apps/venus/src/assets/collaboration.png
index 5d56d61581336890b43de39643cc23045a7ee4a6..279d968cb56143e0d840707fccf7ad256f22da02 100644
GIT binary patch
literal 1092345
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 |