From f18f51ba7cf245f3cb3042540ba5f0237bdb4767 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Tue, 2 Aug 2022 19:10:51 +0800 Subject: [PATCH 1/9] 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 2/9] 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 727c7428890bf855634973bbe386b4d71521ef96 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 3 Aug 2022 11:14:52 +0800 Subject: [PATCH 3/9] 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 4/9] 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 = ({