From eb02e62a0e0b17e58104bfa5c3353837fc1e17d5 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Wed, 24 Aug 2022 01:20:52 +0800 Subject: [PATCH 1/4] feat: new useBlockRender API --- .../editor-core/src/render-block/Context.tsx | 32 ++++ .../src/render-block/RenderBlock.tsx | 9 +- .../src/render-block/RenderBlockChildren.tsx | 35 +++- .../src/render-block/RenderKanbanBlock.tsx | 56 ++++++ .../src/render-block/WithTreeViewChildren.tsx | 181 ++++++++++++++++++ .../editor-core/src/render-block/index.ts | 2 + 6 files changed, 307 insertions(+), 8 deletions(-) create mode 100644 libs/components/editor-core/src/render-block/Context.tsx create mode 100644 libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx create mode 100644 libs/components/editor-core/src/render-block/WithTreeViewChildren.tsx diff --git a/libs/components/editor-core/src/render-block/Context.tsx b/libs/components/editor-core/src/render-block/Context.tsx new file mode 100644 index 0000000000..8a1b68465c --- /dev/null +++ b/libs/components/editor-core/src/render-block/Context.tsx @@ -0,0 +1,32 @@ +import { genErrorObj } from '@toeverything/utils'; +import { createContext, PropsWithChildren, useContext } from 'react'; +import { RenderBlockProps } from './RenderBlock'; + +type BlockRenderProps = { + blockRender: (args: RenderBlockProps) => JSX.Element | null; +}; + +export const BlockRenderContext = createContext( + genErrorObj( + 'Failed to get BlockChildrenContext! The context only can use under the "render-root"' + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any +); + +export const BlockRenderProvider = ({ + blockRender, + children, +}: PropsWithChildren) => { + return ( + + {children} + + ); +}; + +export const useBlockRender = () => { + const { blockRender } = useContext(BlockRenderContext); + return { + BlockRender: blockRender, + }; +}; diff --git a/libs/components/editor-core/src/render-block/RenderBlock.tsx b/libs/components/editor-core/src/render-block/RenderBlock.tsx index 0a1531a709..1dc945e870 100644 --- a/libs/components/editor-core/src/render-block/RenderBlock.tsx +++ b/libs/components/editor-core/src/render-block/RenderBlock.tsx @@ -4,7 +4,12 @@ import { useCallback, useMemo } from 'react'; import { useEditor } from '../Contexts'; import { useBlock } from '../hooks'; -interface RenderBlockProps { +/** + * Render nothing + */ +export const NullBlockRender = (): null => null; + +export interface RenderBlockProps { blockId: string; hasContainer?: boolean; } @@ -29,7 +34,7 @@ export function RenderBlock({ if (block?.type) { return editor.getView(block.type).View; } - return () => null; + return (): null => null; }, [editor, block?.type]); if (!block) { diff --git a/libs/components/editor-core/src/render-block/RenderBlockChildren.tsx b/libs/components/editor-core/src/render-block/RenderBlockChildren.tsx index 4c87ccc8e8..d79d0cd002 100644 --- a/libs/components/editor-core/src/render-block/RenderBlockChildren.tsx +++ b/libs/components/editor-core/src/render-block/RenderBlockChildren.tsx @@ -1,16 +1,39 @@ +import { styled } from '@toeverything/components/ui'; import type { AsyncBlock } from '../editor'; -import { RenderBlock } from './RenderBlock'; +import { useBlockRender } from './Context'; +import { NullBlockRender } from './RenderBlock'; -interface RenderChildrenProps { +export interface RenderChildrenProps { block: AsyncBlock; + indent?: boolean; } -export const RenderBlockChildren = ({ block }: RenderChildrenProps) => { +export const RenderBlockChildren = ({ + block, + indent = true, +}: RenderChildrenProps) => { + const { BlockRender } = useBlockRender(); + if (BlockRender === NullBlockRender) { + return null; + } + return block.childrenIds.length ? ( - <> + {block.childrenIds.map(childId => { - return ; + return ; })} - + ) : null; }; + +/** + * Indent rendering child nodes + */ +const StyledIdentWrapper = styled('div')<{ indent?: boolean }>( + ({ indent }) => ({ + display: 'flex', + flexDirection: 'column', + // TODO: marginLeft should use theme provided by styled + ...(indent && { marginLeft: '30px' }), + }) +); diff --git a/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx b/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx new file mode 100644 index 0000000000..54f9e2276e --- /dev/null +++ b/libs/components/editor-core/src/render-block/RenderKanbanBlock.tsx @@ -0,0 +1,56 @@ +import { styled } from '@toeverything/components/ui'; +import { useBlock } from '../hooks'; +import { BlockRenderProvider } from './Context'; +import { NullBlockRender, RenderBlock, RenderBlockProps } from './RenderBlock'; + +/** + * Render block without children. + */ +const BlockWithoutChildrenRender = ({ blockId }: RenderBlockProps) => { + return ( + + + + ); +}; + +/** + * Render a block, but only one level of children. + */ +const OneLevelBlockRender = ({ blockId }: RenderBlockProps) => { + return ( + + + + ); +}; + +export const KanbanBlockRender = ({ blockId }: RenderBlockProps) => { + const { block } = useBlock(blockId); + + if (!block) { + return ( + + + + ); + } + + return ( + + + {block?.childrenIds.map(childId => ( + + + + ))} + + ); +}; + +const StyledBorder = styled('div')({ + border: '1px solid #E0E6EB', + borderRadius: '5px', + margin: '4px', + padding: '0 4px', +}); diff --git a/libs/components/editor-core/src/render-block/WithTreeViewChildren.tsx b/libs/components/editor-core/src/render-block/WithTreeViewChildren.tsx new file mode 100644 index 0000000000..8c3642ef6d --- /dev/null +++ b/libs/components/editor-core/src/render-block/WithTreeViewChildren.tsx @@ -0,0 +1,181 @@ +import { styled } from '@toeverything/components/ui'; +import type { + ComponentPropsWithoutRef, + ComponentPropsWithRef, + CSSProperties, + ReactElement, +} from 'react'; +import { forwardRef } from 'react'; +import { CreateView } from '../editor'; +import { useBlockRender } from './Context'; +import { NullBlockRender } from './RenderBlock'; + +type WithChildrenConfig = { + indent: CSSProperties['marginLeft']; +}; + +const defaultConfig: WithChildrenConfig = { + indent: '30px', +}; + +const TreeView = forwardRef< + HTMLDivElement, + { lastItem?: boolean } & ComponentPropsWithRef<'div'> +>(({ lastItem = false, children, onClick, ...restProps }, ref) => { + return ( + + + + + {lastItem && } + + {/* maybe need a child wrapper */} + {children} + + ); +}); + +const CollapsedNode = forwardRef< + HTMLDivElement, + ComponentPropsWithoutRef<'div'> +>((props, ref) => { + return ( + + ··· + + ); +}); + +/** + * Indent rendering child nodes + */ +export const withTreeViewChildren = ( + creator: (props: CreateView) => ReactElement, + customConfig: Partial = {} +) => { + const config = { + ...defaultConfig, + ...customConfig, + }; + + return (props: CreateView) => { + const { block } = props; + const { BlockRender } = useBlockRender(); + const collapsed = block.getProperty('collapsed')?.value; + const childrenIds = block.childrenIds; + const showChildren = + !collapsed && + childrenIds.length > 0 && + BlockRender !== NullBlockRender; + + const handleCollapse = () => { + block.setProperty('collapsed', { value: true }); + }; + + const handleExpand = () => { + block.setProperty('collapsed', { value: false }); + }; + + return ( + <> + {creator(props)} + + {collapsed && ( + + )} + {showChildren && + childrenIds.map((childId, idx) => { + return ( + + + + ); + })} + + ); + }; +}; + +const TREE_COLOR = '#D5DFE6'; +// adjust left and right margins of the the tree line +const TREE_LINE_LEFT_OFFSET = '-16px'; +// determine the position of the horizontal line by the type of the item +const TREE_LINE_TOP_OFFSET = '20px'; // '50%' +const TREE_LINE_WIDTH = '12px'; + +const TreeWrapper = styled('div')({ + position: 'relative', + display: 'flex', +}); + +const StyledTreeView = styled('div')({ + position: 'absolute', + left: TREE_LINE_LEFT_OFFSET, + height: '100%', +}); + +const Line = styled('div')({ + position: 'absolute', + cursor: 'pointer', + backgroundColor: TREE_COLOR, + // somehow tldraw would override this + boxSizing: 'content-box!important' as any, + // See [Can I add background color only for padding?](https://stackoverflow.com/questions/14628601/can-i-add-background-color-only-for-padding) + backgroundClip: 'content-box', + backgroundOrigin: 'content-box', + // Increase click hot spot + padding: '10px', +}); + +const VerticalLine = styled(Line)<{ last: boolean }>(({ last }) => ({ + width: '1px', + height: last ? TREE_LINE_TOP_OFFSET : '100%', + paddingTop: 0, + paddingBottom: 0, + transform: 'translate(-50%, 0)', + + opacity: last ? 0 : 'unset', +})); + +const HorizontalLine = styled(Line)<{ last: boolean }>(({ last }) => ({ + width: TREE_LINE_WIDTH, + height: '1px', + paddingLeft: 0, + paddingRight: 0, + top: TREE_LINE_TOP_OFFSET, + transform: 'translate(0, -50%)', + opacity: last ? 0 : 'unset', +})); + +const Collapsed = styled('div')({ + cursor: 'pointer', + display: 'inline-block', + color: '#98ACBD', + padding: '8px', +}); + +const LastItemRadius = styled('div')({ + boxSizing: 'content-box', + position: 'absolute', + left: '-0.5px', + top: 0, + height: TREE_LINE_TOP_OFFSET, + bottom: '50%', + width: TREE_LINE_WIDTH, + borderWidth: '1px', + borderStyle: 'solid', + borderLeftColor: TREE_COLOR, + borderBottomColor: TREE_COLOR, + borderTop: 'none', + borderRight: 'none', + borderRadius: '0 0 0 3px', + pointerEvents: 'none', +}); diff --git a/libs/components/editor-core/src/render-block/index.ts b/libs/components/editor-core/src/render-block/index.ts index 820bac8b57..0d5c809c4c 100644 --- a/libs/components/editor-core/src/render-block/index.ts +++ b/libs/components/editor-core/src/render-block/index.ts @@ -1,2 +1,4 @@ +export { BlockRenderProvider, useBlockRender } from './Context'; export * from './RenderBlock'; export * from './RenderBlockChildren'; +export { withTreeViewChildren } from './WithTreeViewChildren'; From dd24711f21ef6097c99edade1a40202b23802374 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Wed, 24 Aug 2022 01:25:25 +0800 Subject: [PATCH 2/4] refactor: update editor --- libs/components/affine-editor/src/Editor.tsx | 5 +- libs/components/editor-core/src/Contexts.tsx | 28 +++++-- .../components/editor-core/src/RenderRoot.tsx | 82 +++++++++---------- libs/components/editor-core/src/index.ts | 27 +++--- .../editor-core/src/kanban/Context.tsx | 10 ++- 5 files changed, 81 insertions(+), 71 deletions(-) diff --git a/libs/components/affine-editor/src/Editor.tsx b/libs/components/affine-editor/src/Editor.tsx index c280bea984..f1edb2ce74 100644 --- a/libs/components/affine-editor/src/Editor.tsx +++ b/libs/components/affine-editor/src/Editor.tsx @@ -1,7 +1,6 @@ import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'; import { - RenderBlock, RenderRoot, type BlockEditor, } from '@toeverything/components/editor-core'; @@ -88,9 +87,7 @@ export const AffineEditor = forwardRef( editor={editor} editorElement={AffineEditor as any} scrollBlank={scrollBlank} - > - - + /> ); } ); diff --git a/libs/components/editor-core/src/Contexts.tsx b/libs/components/editor-core/src/Contexts.tsx index 418fe0e8b4..0b7c7d2270 100644 --- a/libs/components/editor-core/src/Contexts.tsx +++ b/libs/components/editor-core/src/Contexts.tsx @@ -1,22 +1,34 @@ -import { createContext, useContext } from 'react'; -import type { BlockEditor, AsyncBlock } from './editor'; import { genErrorObj } from '@toeverything/utils'; +import { createContext, PropsWithChildren, useContext } from 'react'; +import type { AsyncBlock, BlockEditor } from './editor'; -const RootContext = createContext<{ +type EditorProps = { editor: BlockEditor; // TODO: Temporary fix, dependencies in the new architecture are bottom-up, editors do not need to be passed down from the top editorElement: () => JSX.Element; -}>( +}; + +const EditorContext = createContext( genErrorObj( - 'Failed to get context! The context only can use under the "render-root"' + 'Failed to get EditorContext! The context only can use under the "render-root"' // eslint-disable-next-line @typescript-eslint/no-explicit-any ) as any ); -export const EditorProvider = RootContext.Provider; - export const useEditor = () => { - return useContext(RootContext); + return useContext(EditorContext); +}; + +export const EditorProvider = ({ + editor, + editorElement, + children, +}: PropsWithChildren) => { + return ( + + {children} + + ); }; /** diff --git a/libs/components/editor-core/src/RenderRoot.tsx b/libs/components/editor-core/src/RenderRoot.tsx index cbafd3ea75..c1f1dd0d49 100644 --- a/libs/components/editor-core/src/RenderRoot.tsx +++ b/libs/components/editor-core/src/RenderRoot.tsx @@ -4,12 +4,12 @@ import { services, type ReturnUnobserve, } from '@toeverything/datasource/db-service'; -import type { PropsWithChildren } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { EditorProvider } from './Contexts'; import type { BlockEditor } from './editor'; import { useIsOnDrag } from './hooks'; import { addNewGroup, appendNewGroup } from './recast-block'; +import { BlockRenderProvider, RenderBlock } from './render-block'; import { SelectionRect, SelectionRef } from './Selection'; interface RenderRootProps { @@ -24,11 +24,7 @@ interface RenderRootProps { const MAX_PAGE_WIDTH = 5000; export const MIN_PAGE_WIDTH = 1480; -export const RenderRoot = ({ - editor, - editorElement, - children, -}: PropsWithChildren) => { +export const RenderRoot = ({ editor, editorElement }: RenderRootProps) => { const selectionRef = useRef(null); const triggeredBySelect = useRef(false); const [pageWidth, setPageWidth] = useState(MIN_PAGE_WIDTH); @@ -158,39 +154,43 @@ export const RenderRoot = ({ }; return ( - - { - if (ref != null && ref !== editor.container) { - editor.container = ref; - editor.getHooks().render(); - } - }} - onMouseMove={onMouseMove} - onMouseDown={onMouseDown} - onMouseUp={onMouseUp} - onMouseLeave={onMouseLeave} - onMouseOut={onMouseOut} - onContextMenu={onContextmenu} - onKeyDown={onKeyDown} - onKeyDownCapture={onKeyDownCapture} - onKeyUp={onKeyUp} - onDragOver={onDragOver} - onDragLeave={onDragLeave} - onDragOverCapture={onDragOverCapture} - onDragEnd={onDragEnd} - onDrop={onDrop} - isOnDrag={isOnDrag} - > - - {children} - - {/** TODO: remove selectionManager insert */} - {editor && } - {editor.isEdgeless ? null : } - {patchedNodes} - + + + { + if (ref != null && ref !== editor.container) { + editor.container = ref; + editor.getHooks().render(); + } + }} + onMouseMove={onMouseMove} + onMouseDown={onMouseDown} + onMouseUp={onMouseUp} + onMouseLeave={onMouseLeave} + onMouseOut={onMouseOut} + onContextMenu={onContextmenu} + onKeyDown={onKeyDown} + onKeyDownCapture={onKeyDownCapture} + onKeyUp={onKeyUp} + onDragOver={onDragOver} + onDragLeave={onDragLeave} + onDragOverCapture={onDragOverCapture} + onDragEnd={onDragEnd} + onDrop={onDrop} + isOnDrag={isOnDrag} + > + + + + {/** TODO: remove selectionManager insert */} + {editor && ( + + )} + {editor.isEdgeless ? null : } + {patchedNodes} + + ); }; @@ -251,7 +251,7 @@ function ScrollBlank({ editor }: { editor: BlockEditor }) { ); return ( - - {children} - + + + {children} + + ); }; From a4dc7bf127d2c2905527919dc01fd06e9324534e Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Wed, 24 Aug 2022 01:35:04 +0800 Subject: [PATCH 3/4] refactor: update block --- .../src/blocks/bullet/BulletView.tsx | 27 +- .../editor-blocks/src/blocks/bullet/index.ts | 14 +- .../src/blocks/grid-item/GridItemRender.tsx | 5 +- .../editor-blocks/src/blocks/grid/Grid.tsx | 17 +- .../src/blocks/group/ScenePage.tsx | 2 +- .../blocks/group/scene-kanban/CardItem.tsx | 5 +- .../src/blocks/numbered/NumberedView.tsx | 17 +- .../src/blocks/numbered/index.ts | 11 +- .../src/blocks/text/TextView.tsx | 5 +- .../src/blocks/todo/TodoView.tsx | 62 +++-- .../editor-blocks/src/blocks/todo/index.ts | 16 +- .../BlockContainer/BlockContainer.tsx | 2 +- .../IndentWrapper/IndentWrapper.tsx | 17 -- .../src/components/IndentWrapper/index.ts | 1 - .../source-view/format-url/youtube.ts | 1 + .../src/utils/WithTreeViewChildren.tsx | 236 ------------------ .../src/render-block/RenderBlock.tsx | 1 + 17 files changed, 96 insertions(+), 343 deletions(-) delete mode 100644 libs/components/editor-blocks/src/components/IndentWrapper/IndentWrapper.tsx delete mode 100644 libs/components/editor-blocks/src/components/IndentWrapper/index.ts delete mode 100644 libs/components/editor-blocks/src/utils/WithTreeViewChildren.tsx diff --git a/libs/components/editor-blocks/src/blocks/bullet/BulletView.tsx b/libs/components/editor-blocks/src/blocks/bullet/BulletView.tsx index 9f77eda6cb..b2f76e46f9 100644 --- a/libs/components/editor-blocks/src/blocks/bullet/BulletView.tsx +++ b/libs/components/editor-blocks/src/blocks/bullet/BulletView.tsx @@ -1,29 +1,28 @@ import type { TextProps } from '@toeverything/components/common'; import { ContentColumnValue, - services, Protocol, + services, } from '@toeverything/datasource/db-service'; import { type CreateView } from '@toeverything/framework/virgo'; import { useEffect, useRef, useState } from 'react'; +import { + BlockPendantProvider, + RenderBlockChildren, + supportChildren, + useOnSelect, +} from '@toeverything/components/editor-core'; +import { styled } from '@toeverything/components/ui'; +import { BlockContainer } from '../../components/BlockContainer'; +import { List } from '../../components/style-container'; import { TextManage, type ExtendedTextUtils, } from '../../components/text-manage'; import { tabBlock } from '../../utils/indent'; +import { BulletIcon, getChildrenType, NumberType } from './data'; import { BulletBlock, BulletProperties } from './types'; -import { - supportChildren, - RenderBlockChildren, - useOnSelect, - BlockPendantProvider, -} from '@toeverything/components/editor-core'; -import { List } from '../../components/style-container'; -import { getChildrenType, BulletIcon, NumberType } from './data'; -import { IndentWrapper } from '../../components/IndentWrapper'; -import { BlockContainer } from '../../components/BlockContainer'; -import { styled } from '@toeverything/components/ui'; export const defaultBulletProps: BulletProperties = { text: { value: [{ text: '' }] }, @@ -208,9 +207,7 @@ export const BulletView = ({ block, editor }: CreateView) => { - - - + ); }; diff --git a/libs/components/editor-blocks/src/blocks/bullet/index.ts b/libs/components/editor-blocks/src/blocks/bullet/index.ts index 087afb3056..761aab03a1 100644 --- a/libs/components/editor-blocks/src/blocks/bullet/index.ts +++ b/libs/components/editor-blocks/src/blocks/bullet/index.ts @@ -1,18 +1,16 @@ +import { + DefaultColumnsValue, + Protocol, +} from '@toeverything/datasource/db-service'; import { AsyncBlock, BaseView, - CreateView, + getTextHtml, getTextProperties, SelectBlock, - getTextHtml, } from '@toeverything/framework/virgo'; -import { - Protocol, - DefaultColumnsValue, -} from '@toeverything/datasource/db-service'; // import { withTreeViewChildren } from '../../utils/with-tree-view-children'; -import { defaultBulletProps, BulletView } from './BulletView'; -import { IndentWrapper } from '../../components/IndentWrapper'; +import { BulletView, defaultBulletProps } from './BulletView'; export class BulletBlock extends BaseView { public type = Protocol.Block.Type.bullet; diff --git a/libs/components/editor-blocks/src/blocks/grid-item/GridItemRender.tsx b/libs/components/editor-blocks/src/blocks/grid-item/GridItemRender.tsx index 3a598b3145..1dece91699 100644 --- a/libs/components/editor-blocks/src/blocks/grid-item/GridItemRender.tsx +++ b/libs/components/editor-blocks/src/blocks/grid-item/GridItemRender.tsx @@ -1,4 +1,4 @@ -import { RenderBlock } from '@toeverything/components/editor-core'; +import { useBlockRender } from '@toeverything/components/editor-core'; import { ChildrenView, CreateView } from '@toeverything/framework/virgo'; export const GridItemRender = function ( @@ -6,10 +6,11 @@ export const GridItemRender = function ( ) { const GridItem = function (props: CreateView) { const { block } = props; + const { BlockRender } = useBlockRender(); const children = ( <> {block.childrenIds.map(id => { - return ; + return ; })} ); diff --git a/libs/components/editor-blocks/src/blocks/grid/Grid.tsx b/libs/components/editor-blocks/src/blocks/grid/Grid.tsx index 4ecf47f3b5..4b5bfd49b3 100644 --- a/libs/components/editor-blocks/src/blocks/grid/Grid.tsx +++ b/libs/components/editor-blocks/src/blocks/grid/Grid.tsx @@ -1,16 +1,16 @@ -import { RenderBlock } from '@toeverything/components/editor-core'; -import { CreateView } from '@toeverything/framework/virgo'; -import React, { useEffect, useRef, useState } from 'react'; -import { GridHandle } from './GirdHandle'; +import { useBlockRender } from '@toeverything/components/editor-core'; import { styled } from '@toeverything/components/ui'; +import { Protocol } from '@toeverything/datasource/db-service'; +import { CreateView } from '@toeverything/framework/virgo'; +import { debounce, domToRect, Point } from '@toeverything/utils'; +import clsx from 'clsx'; +import React, { useEffect, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import { GRID_ITEM_CLASS_NAME, GRID_ITEM_CONTENT_CLASS_NAME, } from '../grid-item/GridItem'; -import { debounce, domToRect, Point } from '@toeverything/utils'; -import clsx from 'clsx'; -import { Protocol } from '@toeverything/datasource/db-service'; +import { GridHandle } from './GirdHandle'; const DB_UPDATE_DELAY = 50; const GRID_ON_DRAG_CLASS = 'grid-layout-on-drag'; @@ -31,6 +31,7 @@ export const Grid = function (props: CreateView) { const originalLeftWidth = useRef(gridItemMinWidth); const originalRightWidth = useRef(gridItemMinWidth); const [alertHandleId, setAlertHandleId] = useState(null); + const { BlockRender } = useBlockRender(); const getLeftRightGridItemDomByIndex = (index: number) => { const gridItems = Array.from(gridContainerRef.current?.children).filter( @@ -226,7 +227,7 @@ export const Grid = function (props: CreateView) { key={id} className={GRID_ITEM_CLASS_NAME} > - + handleDragGrid(event, i)} editor={editor} diff --git a/libs/components/editor-blocks/src/blocks/group/ScenePage.tsx b/libs/components/editor-blocks/src/blocks/group/ScenePage.tsx index 6145bec114..739927626d 100644 --- a/libs/components/editor-blocks/src/blocks/group/ScenePage.tsx +++ b/libs/components/editor-blocks/src/blocks/group/ScenePage.tsx @@ -2,5 +2,5 @@ import { RenderBlockChildren } from '@toeverything/components/editor-core'; import type { CreateView } from '@toeverything/framework/virgo'; export const ScenePage = ({ block }: CreateView) => { - return ; + return ; }; 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 837772bed2..8f79a6c208 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 { KanbanCard, - RenderBlock, + useBlockRender, useEditor, useKanban, } from '@toeverything/components/editor-core'; @@ -94,6 +94,7 @@ export const CardItem = ({ const [editable, setEditable] = useState(false); const showKanbanRefPageFlag = useFlag('ShowKanbanRefPage', false); const { editor } = useEditor(); + const { BlockRender } = useBlockRender(); const onAddItem = async () => { setEditable(true); @@ -114,7 +115,7 @@ export const CardItem = ({ setEditable(false)}> - + {showKanbanRefPageFlag && !editable && ( diff --git a/libs/components/editor-blocks/src/blocks/numbered/NumberedView.tsx b/libs/components/editor-blocks/src/blocks/numbered/NumberedView.tsx index 38dac6fc75..74bcfab504 100644 --- a/libs/components/editor-blocks/src/blocks/numbered/NumberedView.tsx +++ b/libs/components/editor-blocks/src/blocks/numbered/NumberedView.tsx @@ -1,8 +1,8 @@ import { TextProps } from '@toeverything/components/common'; import { ContentColumnValue, - services, Protocol, + services, } from '@toeverything/datasource/db-service'; import { type CreateView } from '@toeverything/framework/virgo'; import { useEffect, useRef, useState } from 'react'; @@ -11,18 +11,17 @@ import { type ExtendedTextUtils, } from '../../components/text-manage'; import { tabBlock } from '../../utils/indent'; -import { IndentWrapper } from '../../components/IndentWrapper'; import type { Numbered, NumberedAsyncBlock } from './types'; -import { getChildrenType, getNumber } from './data'; import { - supportChildren, - RenderBlockChildren, - useOnSelect, BlockPendantProvider, + RenderBlockChildren, + supportChildren, + useOnSelect, } from '@toeverything/components/editor-core'; -import { List } from '../../components/style-container'; import { BlockContainer } from '../../components/BlockContainer'; +import { List } from '../../components/style-container'; +import { getChildrenType, getNumber } from './data'; export const defaultTodoProps: Numbered = { text: { value: [{ text: '' }] }, @@ -204,9 +203,7 @@ export const NumberedView = ({ block, editor }: CreateView) => { - - - + ); }; diff --git a/libs/components/editor-blocks/src/blocks/numbered/index.ts b/libs/components/editor-blocks/src/blocks/numbered/index.ts index b8b3c3dc0a..92187be81b 100644 --- a/libs/components/editor-blocks/src/blocks/numbered/index.ts +++ b/libs/components/editor-blocks/src/blocks/numbered/index.ts @@ -1,17 +1,16 @@ +import { + DefaultColumnsValue, + Protocol, +} from '@toeverything/datasource/db-service'; import { AsyncBlock, BaseView, + getTextHtml, getTextProperties, SelectBlock, - getTextHtml, } from '@toeverything/framework/virgo'; -import { - Protocol, - DefaultColumnsValue, -} from '@toeverything/datasource/db-service'; // import { withTreeViewChildren } from '../../utils/with-tree-view-children'; import { defaultTodoProps, NumberedView } from './NumberedView'; -import { IndentWrapper } from '../../components/IndentWrapper'; export class NumberedBlock extends BaseView { public type = Protocol.Block.Type.numbered; diff --git a/libs/components/editor-blocks/src/blocks/text/TextView.tsx b/libs/components/editor-blocks/src/blocks/text/TextView.tsx index 5edf231217..fcb06238e2 100644 --- a/libs/components/editor-blocks/src/blocks/text/TextView.tsx +++ b/libs/components/editor-blocks/src/blocks/text/TextView.tsx @@ -12,7 +12,6 @@ import { styled } from '@toeverything/components/ui'; import { Protocol } from '@toeverything/datasource/db-service'; import { CreateView } from '@toeverything/framework/virgo'; import { BlockContainer } from '../../components/BlockContainer'; -import { IndentWrapper } from '../../components/IndentWrapper'; import { TextManage } from '../../components/text-manage'; import { dedentBlock, tabBlock } from '../../utils/indent'; interface CreateTextView extends CreateView { @@ -255,9 +254,7 @@ export const TextView = ({ handleTab={onTab} /> - - - + ); }; diff --git a/libs/components/editor-blocks/src/blocks/todo/TodoView.tsx b/libs/components/editor-blocks/src/blocks/todo/TodoView.tsx index 8982f4695e..9bf24a37ec 100644 --- a/libs/components/editor-blocks/src/blocks/todo/TodoView.tsx +++ b/libs/components/editor-blocks/src/blocks/todo/TodoView.tsx @@ -1,11 +1,17 @@ import { TextProps } from '@toeverything/components/common'; +import { + AsyncBlock, + BlockPendantProvider, + CreateView, + useOnSelect, +} from '@toeverything/components/editor-core'; import { styled } from '@toeverything/components/ui'; import { ContentColumnValue, Protocol, } from '@toeverything/datasource/db-service'; -import { AsyncBlock, type CreateView } from '@toeverything/framework/virgo'; -import { useRef } from 'react'; +import { useRef, useState } from 'react'; +import { BlockContainer } from '../../components/BlockContainer'; import { TextManage, type ExtendedTextUtils, @@ -36,6 +42,10 @@ const todoIsEmpty = (contentValue: ContentColumnValue): boolean => { export const TodoView = ({ block, editor }: CreateView) => { const properties = { ...defaultTodoProps, ...block.getProperties() }; const text_ref = useRef(null); + const [isSelect, setIsSelect] = useState(false); + useOnSelect(block.id, (isSelect: boolean) => { + setIsSelect(isSelect); + }); const turn_into_text_block = async () => { // Convert to text block @@ -121,28 +131,34 @@ export const TodoView = ({ block, editor }: CreateView) => { }; return ( - -
- -
+ + + +
+ +
-
- -
-
+
+ +
+
+ + ); }; diff --git a/libs/components/editor-blocks/src/blocks/todo/index.ts b/libs/components/editor-blocks/src/blocks/todo/index.ts index c6580009d0..78f317103a 100644 --- a/libs/components/editor-blocks/src/blocks/todo/index.ts +++ b/libs/components/editor-blocks/src/blocks/todo/index.ts @@ -1,18 +1,16 @@ import { - BaseView, - getTextProperties, AsyncBlock, - SelectBlock, + BaseView, getTextHtml, -} from '@toeverything/framework/virgo'; -// import type { CreateView } from '@toeverything/framework/virgo'; + getTextProperties, + SelectBlock, + withTreeViewChildren, +} from '@toeverything/components/editor-core'; import { - Protocol, DefaultColumnsValue, + Protocol, } from '@toeverything/datasource/db-service'; -// import { withTreeViewChildren } from '../../utils/with-tree-view-children'; -import { withTreeViewChildren } from '../../utils/WithTreeViewChildren'; -import { TodoView, defaultTodoProps } from './TodoView'; +import { defaultTodoProps, TodoView } from './TodoView'; import type { TodoAsyncBlock } from './types'; export class TodoBlock extends BaseView { diff --git a/libs/components/editor-blocks/src/components/BlockContainer/BlockContainer.tsx b/libs/components/editor-blocks/src/components/BlockContainer/BlockContainer.tsx index 00da92a813..4c043ce163 100644 --- a/libs/components/editor-blocks/src/components/BlockContainer/BlockContainer.tsx +++ b/libs/components/editor-blocks/src/components/BlockContainer/BlockContainer.tsx @@ -23,7 +23,7 @@ export const BlockContainer = function ({ ); }; -export const Container = styled('div')<{ selected: boolean }>( +export const Container = styled('div')<{ selected?: boolean }>( ({ selected, theme }) => ({ backgroundColor: selected ? theme.affine.palette.textSelected : '', marginBottom: '2px', diff --git a/libs/components/editor-blocks/src/components/IndentWrapper/IndentWrapper.tsx b/libs/components/editor-blocks/src/components/IndentWrapper/IndentWrapper.tsx deleted file mode 100644 index 93aa9d34a1..0000000000 --- a/libs/components/editor-blocks/src/components/IndentWrapper/IndentWrapper.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { PropsWithChildren } from 'react'; -import { ChildrenView } from '@toeverything/framework/virgo'; -import { styled } from '@toeverything/components/ui'; - -/** - * Indent rendering child nodes - */ -export const IndentWrapper = (props: PropsWithChildren) => { - return {props.children}; -}; - -const StyledIdentWrapper = styled('div')({ - display: 'flex', - flexDirection: 'column', - // TODO: marginLeft should use theme provided by styled - marginLeft: '30px', -}); diff --git a/libs/components/editor-blocks/src/components/IndentWrapper/index.ts b/libs/components/editor-blocks/src/components/IndentWrapper/index.ts deleted file mode 100644 index e4234a916d..0000000000 --- a/libs/components/editor-blocks/src/components/IndentWrapper/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './IndentWrapper'; diff --git a/libs/components/editor-blocks/src/components/source-view/format-url/youtube.ts b/libs/components/editor-blocks/src/components/source-view/format-url/youtube.ts index 56f165a515..7cd540b12e 100644 --- a/libs/components/editor-blocks/src/components/source-view/format-url/youtube.ts +++ b/libs/components/editor-blocks/src/components/source-view/format-url/youtube.ts @@ -1,4 +1,5 @@ export const isYoutubeUrl = (url?: string): boolean => { + if (!url) return false; const allowedHosts = ['www.youtu.be', 'www.youtube.com']; const host = new URL(url).host; return allowedHosts.includes(host); diff --git a/libs/components/editor-blocks/src/utils/WithTreeViewChildren.tsx b/libs/components/editor-blocks/src/utils/WithTreeViewChildren.tsx deleted file mode 100644 index e7e497f2ef..0000000000 --- a/libs/components/editor-blocks/src/utils/WithTreeViewChildren.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import { - BlockPendantProvider, - CreateView, - RenderBlock, - useCurrentView, - useOnSelect, -} from '@toeverything/components/editor-core'; -import { styled } from '@toeverything/components/ui'; -import type { - ComponentPropsWithoutRef, - ComponentPropsWithRef, - CSSProperties, - ReactElement, -} from 'react'; -import { forwardRef, useState } from 'react'; -import { SCENE_CONFIG } from '../blocks/group/config'; -import { BlockContainer } from '../components/BlockContainer'; - -type WithChildrenConfig = { - indent: CSSProperties['marginLeft']; -}; - -const defaultConfig: WithChildrenConfig = { - indent: '30px', -}; - -const TreeView = forwardRef< - HTMLDivElement, - { lastItem?: boolean } & ComponentPropsWithRef<'div'> ->(({ lastItem = false, children, onClick, ...restProps }, ref) => { - return ( - - - - - {lastItem && } - - {/* maybe need a child wrapper */} - {children} - - ); -}); - -interface ChildrenViewProp { - childrenIds: string[]; - handleCollapse: () => void; - indent?: string | number; -} - -const ChildrenView = ({ - childrenIds, - handleCollapse, - indent, -}: ChildrenViewProp) => { - const [currentView] = useCurrentView(); - const isKanbanScene = currentView.type === SCENE_CONFIG.KANBAN; - - return ( - - {childrenIds.map((childId, idx) => { - if (isKanbanScene) { - return ( - - - - ); - } - - return ( - - - - ); - })} - - ); -}; - -const CollapsedNode = forwardRef< - HTMLDivElement, - ComponentPropsWithoutRef<'div'> ->((props, ref) => { - return ( - - ··· - - ); -}); - -/** - * Indent rendering child nodes - */ -export const withTreeViewChildren = ( - creator: (props: CreateView) => ReactElement, - customConfig: Partial = {} -) => { - const config = { - ...defaultConfig, - ...customConfig, - }; - - return (props: CreateView) => { - const { block, editor } = props; - const collapsed = block.getProperty('collapsed')?.value; - const childrenIds = block.childrenIds; - const showChildren = !collapsed && childrenIds.length > 0; - - const [isSelect, setIsSelect] = useState(false); - useOnSelect(block.id, (isSelect: boolean) => { - setIsSelect(isSelect); - }); - const handleCollapse = () => { - block.setProperty('collapsed', { value: true }); - }; - - const handleExpand = () => { - block.setProperty('collapsed', { value: false }); - }; - - return ( - - -
{creator(props)}
-
- - {collapsed && ( - - )} - {showChildren && ( - - )} -
- ); - }; -}; - -const Wrapper = styled('div')({ display: 'flex', flexDirection: 'column' }); - -const Children = Wrapper; - -const TREE_COLOR = '#D5DFE6'; -// adjust left and right margins of the the tree line -const TREE_LINE_LEFT_OFFSET = '-16px'; -// determine the position of the horizontal line by the type of the item -const TREE_LINE_TOP_OFFSET = '20px'; // '50%' -const TREE_LINE_WIDTH = '12px'; - -const TreeWrapper = styled('div')({ - position: 'relative', -}); - -const StyledTreeView = styled('div')({ - position: 'absolute', - left: TREE_LINE_LEFT_OFFSET, - height: '100%', -}); - -const Line = styled('div')({ - position: 'absolute', - cursor: 'pointer', - backgroundColor: TREE_COLOR, - // somehow tldraw would override this - boxSizing: 'content-box!important' as any, - // See [Can I add background color only for padding?](https://stackoverflow.com/questions/14628601/can-i-add-background-color-only-for-padding) - backgroundClip: 'content-box', - backgroundOrigin: 'content-box', - // Increase click hot spot - padding: '10px', -}); - -const VerticalLine = styled(Line)<{ last: boolean }>(({ last }) => ({ - width: '1px', - height: last ? TREE_LINE_TOP_OFFSET : '100%', - paddingTop: 0, - paddingBottom: 0, - transform: 'translate(-50%, 0)', - - opacity: last ? 0 : 'unset', -})); - -const HorizontalLine = styled(Line)<{ last: boolean }>(({ last }) => ({ - width: TREE_LINE_WIDTH, - height: '1px', - paddingLeft: 0, - paddingRight: 0, - top: TREE_LINE_TOP_OFFSET, - transform: 'translate(0, -50%)', - opacity: last ? 0 : 'unset', -})); - -const Collapsed = styled('div')({ - cursor: 'pointer', - display: 'inline-block', - color: '#98ACBD', - padding: '8px', -}); - -const LastItemRadius = styled('div')({ - boxSizing: 'content-box', - position: 'absolute', - left: '-0.5px', - top: 0, - height: TREE_LINE_TOP_OFFSET, - bottom: '50%', - width: TREE_LINE_WIDTH, - borderWidth: '1px', - borderStyle: 'solid', - borderLeftColor: TREE_COLOR, - borderBottomColor: TREE_COLOR, - borderTop: 'none', - borderRight: 'none', - borderRadius: '0 0 0 3px', - pointerEvents: 'none', -}); - -const StyledBorder = styled('div')({ - border: '1px solid #E0E6EB', - borderRadius: '5px', - margin: '4px', -}); diff --git a/libs/components/editor-core/src/render-block/RenderBlock.tsx b/libs/components/editor-core/src/render-block/RenderBlock.tsx index 1dc945e870..c843274166 100644 --- a/libs/components/editor-core/src/render-block/RenderBlock.tsx +++ b/libs/components/editor-core/src/render-block/RenderBlock.tsx @@ -69,4 +69,5 @@ export function RenderBlock({ const BlockContainer = styled('div')(({ theme }) => ({ fontSize: theme.typography.body1.fontSize, + flex: 1, })); From 123091c1aaa37bfd80da7c891542a9f33cde8d8f Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Wed, 24 Aug 2022 02:07:37 +0800 Subject: [PATCH 4/4] chore: add warn comment --- libs/components/editor-core/src/render-block/Context.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/components/editor-core/src/render-block/Context.tsx b/libs/components/editor-core/src/render-block/Context.tsx index 8a1b68465c..90c89eec0e 100644 --- a/libs/components/editor-core/src/render-block/Context.tsx +++ b/libs/components/editor-core/src/render-block/Context.tsx @@ -13,6 +13,9 @@ export const BlockRenderContext = createContext( ) as any ); +/** + * CAUTION! DO NOT PROVIDE A DYNAMIC BLOCK RENDER! + */ export const BlockRenderProvider = ({ blockRender, children,