From aa62599d13e1e48e57e4a255c7816f72a43f5144 Mon Sep 17 00:00:00 2001
From: lawvs <18554747+lawvs@users.noreply.github.com>
Date: Wed, 24 Aug 2022 18:54:30 +0800
Subject: [PATCH 1/5] refactor: kanban block render
---
.../src/render-block/RenderKanbanBlock.tsx | 62 ++++++++++++++-----
1 file changed, 47 insertions(+), 15 deletions(-)
diff --git a/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx b/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx
index 54f9e2276e..9eb01dbde4 100644
--- a/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx
+++ b/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx
@@ -25,32 +25,64 @@ const OneLevelBlockRender = ({ blockId }: RenderBlockProps) => {
);
};
-export const KanbanBlockRender = ({ blockId }: RenderBlockProps) => {
+export const KanbanParentBlockRender = ({
+ blockId,
+ active,
+}: RenderBlockProps & { active?: boolean }) => {
+ return (
+
+
+
+ );
+};
+
+const KanbanChildrenRender = ({
+ blockId,
+ activeBlock,
+}: RenderBlockProps & { activeBlock?: string | null }) => {
const { block } = useBlock(blockId);
if (!block) {
- return (
-
-
-
- );
+ return null;
}
return (
-
{block?.childrenIds.map(childId => (
-
+
-
+
))}
);
};
-const StyledBorder = styled('div')({
- border: '1px solid #E0E6EB',
- borderRadius: '5px',
- margin: '4px',
- padding: '0 4px',
-});
+export const KanbanBlockRender = ({
+ blockId,
+ activeBlock,
+}: RenderBlockProps & { activeBlock?: string | null }) => {
+ return (
+
+
+
+
+ );
+};
+
+const BlockBorder = styled('div')<{ active?: boolean }>(
+ ({ theme, active }) => ({
+ borderRadius: '5px',
+ padding: '0 4px',
+ border: `1px solid ${
+ active ? theme.affine.palette.primary : 'transparent'
+ }`,
+ })
+);
+
+const ChildBorder = styled(BlockBorder)(({ active, theme }) => ({
+ border: `1px solid ${active ? theme.affine.palette.primary : '#E0E6EB'}`,
+ margin: '4px 0',
+}));
From 876a8755769fbef03806525bf0fb046982519573 Mon Sep 17 00:00:00 2001
From: lawvs <18554747+lawvs@users.noreply.github.com>
Date: Wed, 24 Aug 2022 18:56:19 +0800
Subject: [PATCH 2/5] feat: add kanban card editable border
---
.../blocks/group/scene-kanban/CardItem.tsx | 34 ++++++++-----------
libs/components/editor-core/src/index.ts | 7 +---
.../editor-core/src/kanban/Context.tsx | 10 ++----
.../editor-core/src/render-block/index.ts | 5 +--
4 files changed, 22 insertions(+), 34 deletions(-)
diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx
index 8f79a6c208..e50dccf826 100644
--- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx
+++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx
@@ -1,6 +1,6 @@
import {
+ KanbanBlockRender,
KanbanCard,
- useBlockRender,
useEditor,
useKanban,
} from '@toeverything/components/editor-core';
@@ -14,9 +14,9 @@ import { useFlag } from '@toeverything/datasource/feature-flags';
import { useState, type MouseEvent } from 'react';
import { useRefPage } from './RefPage';
-const CardContent = styled('div')({
+const CardContent = styled('div')(({ theme }) => ({
margin: '20px',
-});
+}));
const CardActions = styled('div')({
cursor: 'pointer',
@@ -82,42 +82,38 @@ const Overlay = styled('div')({
},
});
-export const CardItem = ({
- id,
- block,
-}: {
- id: KanbanCard['id'];
- block: KanbanCard['block'];
-}) => {
+export const CardItem = ({ block }: { block: KanbanCard['block'] }) => {
const { addSubItem } = useKanban();
const { openSubPage } = useRefPage();
- const [editable, setEditable] = useState(false);
+ const [editableBlock, setEditableBlock] = useState(null);
const showKanbanRefPageFlag = useFlag('ShowKanbanRefPage', false);
const { editor } = useEditor();
- const { BlockRender } = useBlockRender();
const onAddItem = async () => {
- setEditable(true);
- await addSubItem(block);
+ const newItem = await addSubItem(block);
+ setEditableBlock(newItem.id);
};
const onClickCard = async () => {
- openSubPage(id);
+ openSubPage(block.id);
};
const onClickPen = (e: MouseEvent) => {
e.stopPropagation();
- setEditable(true);
+ setEditableBlock(block.id);
editor.selectionManager.activeNodeByNodeId(block.id);
};
return (
- setEditable(false)}>
+ setEditableBlock(null)}>
-
+
- {showKanbanRefPageFlag && !editable && (
+ {showKanbanRefPageFlag && !editableBlock && (
diff --git a/libs/components/editor-core/src/index.ts b/libs/components/editor-core/src/index.ts
index 6059350bc9..3b793b0a6f 100644
--- a/libs/components/editor-core/src/index.ts
+++ b/libs/components/editor-core/src/index.ts
@@ -6,11 +6,6 @@ export * from './kanban';
export * from './kanban/types';
export * from './recast-block';
export * from './recast-block/types';
-export {
- BlockRenderProvider,
- RenderBlockChildren,
- useBlockRender,
- withTreeViewChildren,
-} from './render-block';
+export * from './render-block';
export { MIN_PAGE_WIDTH, RenderRoot } from './RenderRoot';
export * from './utils';
diff --git a/libs/components/editor-core/src/kanban/Context.tsx b/libs/components/editor-core/src/kanban/Context.tsx
index d0fc58ed0a..12cc0db2f1 100644
--- a/libs/components/editor-core/src/kanban/Context.tsx
+++ b/libs/components/editor-core/src/kanban/Context.tsx
@@ -10,8 +10,6 @@ import {
RecastMetaProperty,
RecastPropertyId,
} from '../recast-block/types';
-import { BlockRenderProvider } from '../render-block';
-import { KanbanBlockRender } from '../render-block/RenderKanbanBlock';
import { useInitKanbanEffect, useRecastKanban } from './kanban';
import { KanbanGroup } from './types';
@@ -56,11 +54,9 @@ export const KanbanProvider = ({
};
return (
-
-
- {children}
-
-
+
+ {children}
+
);
};
diff --git a/libs/components/editor-core/src/render-block/index.ts b/libs/components/editor-core/src/render-block/index.ts
index 0d5c809c4c..5d3c6d122e 100644
--- a/libs/components/editor-core/src/render-block/index.ts
+++ b/libs/components/editor-core/src/render-block/index.ts
@@ -1,4 +1,5 @@
export { BlockRenderProvider, useBlockRender } from './Context';
-export * from './RenderBlock';
-export * from './RenderBlockChildren';
+export { NullBlockRender, RenderBlock } from './RenderBlock';
+export { RenderBlockChildren } from './RenderBlockChildren';
+export { KanbanBlockRender } from './RenderKanbanBlock';
export { withTreeViewChildren } from './WithTreeViewChildren';
From 797652d66c27a08614f341826081ffa0c05a886e Mon Sep 17 00:00:00 2001
From: lawvs <18554747+lawvs@users.noreply.github.com>
Date: Wed, 24 Aug 2022 18:57:46 +0800
Subject: [PATCH 3/5] chore: remove redundant kanban item id
---
.../src/blocks/group/scene-kanban/CardContext.tsx | 12 ++++++------
.../drag-overlay/renderContainerDragOverlay.tsx | 5 ++---
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardContext.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardContext.tsx
index 13fcaa45e3..74afbe087d 100644
--- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardContext.tsx
+++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardContext.tsx
@@ -1,12 +1,12 @@
-import { useCallback } from 'react';
-import { CardItem } from './CardItem';
-import { styled } from '@toeverything/components/ui';
-import { useKanban } from '@toeverything/components/editor-core';
-import { CardItemPanelWrapper } from './dndable/wrapper/CardItemPanelWrapper';
import type {
KanbanCard,
KanbanGroup,
} from '@toeverything/components/editor-core';
+import { useKanban } from '@toeverything/components/editor-core';
+import { styled } from '@toeverything/components/ui';
+import { useCallback } from 'react';
+import { CardItem } from './CardItem';
+import { CardItemPanelWrapper } from './dndable/wrapper/CardItemPanelWrapper';
const AddCardWrapper = styled('div')({
display: 'flex',
@@ -48,7 +48,7 @@ export const CardContext = (props: Props) => {
item={item}
active={activeId === id}
>
-
+
);
diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/dndable/drag-overlay/renderContainerDragOverlay.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/dndable/drag-overlay/renderContainerDragOverlay.tsx
index 957ee8599f..b2b1382e5c 100644
--- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/dndable/drag-overlay/renderContainerDragOverlay.tsx
+++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/dndable/drag-overlay/renderContainerDragOverlay.tsx
@@ -1,7 +1,6 @@
-import { CardItemWrapper } from '../wrapper/CardItemWrapper';
import { CardItem } from '../../CardItem';
-import type { KanbanCard } from '@toeverything/components/editor-core';
import type { DndableItems } from '../type';
+import { CardItemWrapper } from '../wrapper/CardItemWrapper';
export function renderContainerDragOverlay({
containerId,
@@ -18,7 +17,7 @@ export function renderContainerDragOverlay({
return (
}
+ card={}
index={index}
/>
);
From d98e44bfc9c1b631e803e0d89d8a74db51821ab6 Mon Sep 17 00:00:00 2001
From: lawvs <18554747+lawvs@users.noreply.github.com>
Date: Wed, 24 Aug 2022 18:58:53 +0800
Subject: [PATCH 4/5] chore: clean code
---
.../src/blocks/group/scene-kanban/CardItem.tsx | 8 +++-----
.../src/block-pendant/BlockPendantProvider.tsx | 6 +++---
libs/components/editor-core/src/editor/views/base-view.ts | 4 ++--
libs/components/editor-core/src/kanban/kanban.ts | 1 +
libs/components/editor-core/src/recast-block/group.ts | 2 +-
libs/components/editor-core/src/recast-block/property.ts | 4 ++--
6 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx
index e50dccf826..b527711217 100644
--- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx
+++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx
@@ -10,13 +10,12 @@ import {
MuiClickAwayListener,
styled,
} from '@toeverything/components/ui';
-import { useFlag } from '@toeverything/datasource/feature-flags';
import { useState, type MouseEvent } from 'react';
import { useRefPage } from './RefPage';
-const CardContent = styled('div')(({ theme }) => ({
+const CardContent = styled('div')({
margin: '20px',
-}));
+});
const CardActions = styled('div')({
cursor: 'pointer',
@@ -86,7 +85,6 @@ export const CardItem = ({ block }: { block: KanbanCard['block'] }) => {
const { addSubItem } = useKanban();
const { openSubPage } = useRefPage();
const [editableBlock, setEditableBlock] = useState(null);
- const showKanbanRefPageFlag = useFlag('ShowKanbanRefPage', false);
const { editor } = useEditor();
const onAddItem = async () => {
@@ -113,7 +111,7 @@ export const CardItem = ({ block }: { block: KanbanCard['block'] }) => {
activeBlock={editableBlock}
/>
- {showKanbanRefPageFlag && !editableBlock && (
+ {!editableBlock && (
diff --git a/libs/components/editor-core/src/block-pendant/BlockPendantProvider.tsx b/libs/components/editor-core/src/block-pendant/BlockPendantProvider.tsx
index 6d099d8ae4..52d397b1c2 100644
--- a/libs/components/editor-core/src/block-pendant/BlockPendantProvider.tsx
+++ b/libs/components/editor-core/src/block-pendant/BlockPendantProvider.tsx
@@ -1,10 +1,10 @@
-import type { PropsWithChildren } from 'react';
import { styled } from '@toeverything/components/ui';
+import type { PropsWithChildren } from 'react';
+import { useRef } from 'react';
import type { AsyncBlock } from '../editor';
+import { getRecastItemValue, useRecastBlockMeta } from '../recast-block';
import { PendantPopover } from './pendant-popover';
import { PendantRender } from './pendant-render';
-import { useRef } from 'react';
-import { getRecastItemValue, useRecastBlockMeta } from '../recast-block';
/**
* @deprecated
*/
diff --git a/libs/components/editor-core/src/editor/views/base-view.ts b/libs/components/editor-core/src/editor/views/base-view.ts
index ff6ab06098..d4867f2b59 100644
--- a/libs/components/editor-core/src/editor/views/base-view.ts
+++ b/libs/components/editor-core/src/editor/views/base-view.ts
@@ -1,15 +1,15 @@
-import { ComponentType, ReactElement } from 'react';
import type { Column } from '@toeverything/datasource/db-service';
import {
ArrayOperation,
BlockDecoration,
MapOperation,
} from '@toeverything/datasource/jwt';
+import type { ComponentType, ReactElement } from 'react';
import type { EventData } from '../block';
import { AsyncBlock } from '../block';
+import { HTML2BlockResult } from '../clipboard';
import type { Editor } from '../editor';
import { SelectBlock } from '../selection';
-import { HTML2BlockResult } from '../clipboard';
export interface CreateView {
block: AsyncBlock;
editor: Editor;
diff --git a/libs/components/editor-core/src/kanban/kanban.ts b/libs/components/editor-core/src/kanban/kanban.ts
index 218476ac44..9beb2a9cd6 100644
--- a/libs/components/editor-core/src/kanban/kanban.ts
+++ b/libs/components/editor-core/src/kanban/kanban.ts
@@ -358,6 +358,7 @@ export const useKanban = () => {
}
card.append(newBlock);
editor.selectionManager.activeNodeByNodeId(newBlock.id);
+ return newBlock;
},
[editor]
);
diff --git a/libs/components/editor-core/src/recast-block/group.ts b/libs/components/editor-core/src/recast-block/group.ts
index 9879ba43df..23dc0c6873 100644
--- a/libs/components/editor-core/src/recast-block/group.ts
+++ b/libs/components/editor-core/src/recast-block/group.ts
@@ -1,7 +1,7 @@
import { Protocol } from '@toeverything/datasource/db-service';
import type { AsyncBlock, BlockEditor } from '../editor';
-import type { RecastBlock } from '.';
import { cloneRecastMetaTo, mergeRecastMeta } from './property';
+import type { RecastBlock } from './types';
const mergeGroupProperties = async (...groups: RecastBlock[]) => {
const [headGroup, ...restGroups] = groups;
diff --git a/libs/components/editor-core/src/recast-block/property.ts b/libs/components/editor-core/src/recast-block/property.ts
index 3676a1777d..d27b10ef58 100644
--- a/libs/components/editor-core/src/recast-block/property.ts
+++ b/libs/components/editor-core/src/recast-block/property.ts
@@ -2,6 +2,7 @@ import { nanoid } from 'nanoid';
import { useCallback } from 'react';
import { AsyncBlock } from '../editor';
import { useRecastBlock } from './Context';
+import { getHistory, removeHistory, setHistory } from './history';
import type { RecastBlock, RecastItem, StatusProperty } from './types';
import {
META_PROPERTIES_KEY,
@@ -15,7 +16,6 @@ import {
SelectProperty,
TABLE_VALUES_KEY,
} from './types';
-import { getHistory, removeHistory, setHistory } from './history';
/**
* Generate a unique id for a property
@@ -275,7 +275,7 @@ const isSelectLikeProperty = (
metaProperty?: RecastMetaProperty
): metaProperty is SelectProperty | MultiSelectProperty | StatusProperty => {
return (
- metaProperty &&
+ !!metaProperty &&
(metaProperty.type === PropertyType.Status ||
metaProperty.type === PropertyType.Select ||
metaProperty.type === PropertyType.MultiSelect)
From d6009e5e483da7cb29575b2eb42f59963691fc85 Mon Sep 17 00:00:00 2001
From: lawvs <18554747+lawvs@users.noreply.github.com>
Date: Wed, 24 Aug 2022 20:39:45 +0800
Subject: [PATCH 5/5] feat: add kanban todo progress
---
.../src/render-block/RenderKanbanBlock.tsx | 81 ++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)
diff --git a/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx b/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx
index 9eb01dbde4..f9b532f6b3 100644
--- a/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx
+++ b/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx
@@ -1,4 +1,7 @@
import { styled } from '@toeverything/components/ui';
+import { Protocol } from '@toeverything/datasource/db-service';
+import { useEffect, useState } from 'react';
+import { AsyncBlock } from '../editor';
import { useBlock } from '../hooks';
import { BlockRenderProvider } from './Context';
import { NullBlockRender, RenderBlock, RenderBlockProps } from './RenderBlock';
@@ -36,18 +39,73 @@ export const KanbanParentBlockRender = ({
);
};
+const useBlockProgress = (block?: AsyncBlock) => {
+ const [progress, setProgress] = useState(1);
+
+ useEffect(() => {
+ if (!block) {
+ return;
+ }
+ const updateProgress = async () => {
+ const children = await block.children();
+ const todoChildren = children.filter(
+ child => child.type === Protocol.Block.Type.todo
+ );
+ const checkedTodoChildren = todoChildren.filter(
+ child => child.getProperty('checked')?.value === true
+ );
+ setProgress(checkedTodoChildren.length / todoChildren.length);
+ };
+
+ let childrenQueue: (() => void)[] = [];
+ const childrenUnobserve = () => {
+ childrenQueue.forEach(fn => fn());
+ childrenQueue = [];
+ };
+
+ const observeChildren = async () => {
+ const children = await block.children();
+
+ childrenUnobserve();
+ children.forEach(child => {
+ const unobserve = child.onUpdate(() => {
+ updateProgress();
+ });
+ childrenQueue.push(unobserve);
+ });
+ };
+
+ observeChildren();
+ updateProgress();
+
+ const unobserve = block.onUpdate(() => {
+ observeChildren();
+ updateProgress();
+ });
+
+ return () => {
+ unobserve();
+ childrenUnobserve();
+ };
+ }, [block]);
+
+ return progress;
+};
+
const KanbanChildrenRender = ({
blockId,
activeBlock,
}: RenderBlockProps & { activeBlock?: string | null }) => {
const { block } = useBlock(blockId);
+ const progress = useBlockProgress(block);
- if (!block) {
+ if (!block || !block?.childrenIds.length) {
return null;
}
return (
+
{block?.childrenIds.map(childId => (
@@ -82,6 +140,27 @@ const BlockBorder = styled('div')<{ active?: boolean }>(
})
);
+const ProgressBar = styled('div')<{ progress?: number }>(
+ ({ progress = 1 }) => ({
+ height: '3px',
+ width: '100%',
+ background: '#CFE5FF',
+ borderRadius: '5px',
+ overflow: 'hidden',
+ margin: '12px 0',
+
+ '::after': {
+ content: '""',
+ position: 'relative',
+ display: 'flex',
+ background: '#60A5FA',
+ height: '100%',
+ width: `${(progress * 100).toFixed(2)}%`,
+ transition: 'ease 0.5s all',
+ },
+ })
+);
+
const ChildBorder = styled(BlockBorder)(({ active, theme }) => ({
border: `1px solid ${active ? theme.affine.palette.primary : '#E0E6EB'}`,
margin: '4px 0',