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 cb313d14ee..f7f05b37d4 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 @@ -25,7 +25,7 @@ const AddCard = ({ group }: { group: KanbanGroup }) => { const { addCard } = useKanban(); const handleClick = useCallback(async () => { await addCard(group); - }, [addCard]); + }, [addCard, group]); 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 f26374e4c3..76220d7ff0 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,11 @@ import type { KanbanCard } from '@toeverything/components/editor-core'; -import { RenderBlock, useKanban } from '@toeverything/components/editor-core'; +import { + RenderBlock, + useKanban, + useRefPage, +} from '@toeverything/components/editor-core'; import { styled } from '@toeverything/components/ui'; +import { useFlag } from '@toeverything/datasource/feature-flags'; const CardContent = styled('div')({ margin: '20px', @@ -58,18 +63,24 @@ export const CardItem = ({ block: KanbanCard['block']; }) => { const { addSubItem } = useKanban(); + const { openSubPage } = useRefPage(); + const showKanbanRefPageFlag = useFlag('ShowKanbanRefPage', false); const onAddItem = async () => { await addSubItem(block); }; + const onClickCard = async () => { + showKanbanRefPageFlag && openSubPage(id); + }; + return ( - + - Add item + Add a sub-block ); diff --git a/libs/components/editor-core/src/contexts.tsx b/libs/components/editor-core/src/Contexts.tsx similarity index 70% rename from libs/components/editor-core/src/contexts.tsx rename to libs/components/editor-core/src/Contexts.tsx index cdd4dbf305..418fe0e8b4 100644 --- a/libs/components/editor-core/src/contexts.tsx +++ b/libs/components/editor-core/src/Contexts.tsx @@ -1,9 +1,8 @@ import { createContext, useContext } from 'react'; import type { BlockEditor, AsyncBlock } from './editor'; -import type { Column } from '@toeverything/datasource/db-service'; import { genErrorObj } from '@toeverything/utils'; -export const RootContext = createContext<{ +const RootContext = createContext<{ 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; @@ -14,6 +13,8 @@ export const RootContext = createContext<{ ) as any ); +export const EditorProvider = RootContext.Provider; + export const useEditor = () => { return useContext(RootContext); }; @@ -22,16 +23,3 @@ export const useEditor = () => { * @deprecated */ export const BlockContext = createContext(null as any); - -/** - * Context of column information - * - * @deprecated - */ -export const ColumnsContext = createContext<{ - fromId: string; - columns: Column[]; -}>({ - fromId: '', - columns: [], -}); diff --git a/libs/components/editor-core/src/RenderRoot.tsx b/libs/components/editor-core/src/RenderRoot.tsx index 1d08db0b16..e4534b813d 100644 --- a/libs/components/editor-core/src/RenderRoot.tsx +++ b/libs/components/editor-core/src/RenderRoot.tsx @@ -2,7 +2,7 @@ import type { BlockEditor } from './editor'; import { styled, usePatchNodes } from '@toeverything/components/ui'; import type { FC, PropsWithChildren } from 'react'; import React, { useEffect, useRef, useState, useCallback } from 'react'; -import { RootContext } from './contexts'; +import { EditorProvider } from './Contexts'; import { SelectionRect, SelectionRef } from './Selection'; import { Protocol, @@ -151,7 +151,7 @@ export const RenderRoot: FC> = ({ }; return ( - + { @@ -183,7 +183,7 @@ export const RenderRoot: FC> = ({ {editor.isWhiteboard ? null : } {patchedNodes} - + ); }; diff --git a/libs/components/editor-core/src/hooks.ts b/libs/components/editor-core/src/hooks.ts index d7d1a00cef..f0af8a0f87 100644 --- a/libs/components/editor-core/src/hooks.ts +++ b/libs/components/editor-core/src/hooks.ts @@ -1,3 +1,6 @@ +import { noop, Point } from '@toeverything/utils'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useEditor } from './Contexts'; import { AsyncBlock, BlockEditor, @@ -5,9 +8,6 @@ import { SelectionInfo, SelectionSettingsMap, } from './editor'; -import { noop, Point } from '@toeverything/utils'; -import { useCallback, useContext, useEffect, useRef, useState } from 'react'; -import { RootContext } from './contexts'; function useRequestReRender() { const [, setUpdateCounter] = useState(0); @@ -56,7 +56,7 @@ function useRequestReRender() { export const useBlock = (blockId: string) => { const [block, setBlock] = useState(); const requestReRender = useRequestReRender(); - const { editor } = useContext(RootContext); + const { editor } = useEditor(); useEffect(() => { if (!blockId) { return undefined; @@ -95,7 +95,7 @@ export const useOnSelect = ( blockId: string, cb: (isSelect: boolean) => void ) => { - const { editor } = useContext(RootContext); + const { editor } = useEditor(); useEffect(() => { editor.selectionManager.observe(blockId, SelectEventTypes.onSelect, cb); return () => { @@ -117,7 +117,7 @@ export const useOnSelectActive = ( blockId: string, cb: (position: Point | undefined) => void ) => { - const { editor } = useContext(RootContext); + const { editor } = useEditor(); useEffect(() => { editor.selectionManager.observe(blockId, SelectEventTypes.active, cb); return () => { @@ -139,7 +139,7 @@ export const useOnSelectSetSelection = ( blockId: string, cb: (args: SelectionSettingsMap[T]) => void ) => { - const { editor } = useContext(RootContext); + const { editor } = useEditor(); useEffect(() => { editor.selectionManager.observe( blockId, @@ -162,7 +162,7 @@ export const useOnSelectSetSelection = ( * @export */ export const useOnSelectChange = (cb: (info: SelectionInfo) => void) => { - const { editor } = useContext(RootContext); + const { editor } = useEditor(); useEffect(() => { editor.selectionManager.onSelectionChange(cb); return () => { @@ -177,7 +177,7 @@ export const useOnSelectChange = (cb: (info: SelectionInfo) => void) => { * @export */ export const useOnSelectEnd = (cb: (info: SelectionInfo) => void) => { - const { editor } = useContext(RootContext); + const { editor } = useEditor(); useEffect(() => { editor.selectionManager.onSelectEnd(cb); return () => { @@ -195,7 +195,7 @@ export const useOnSelectStartWith = ( blockId: string, cb: (args: MouseEvent) => void ) => { - const { editor } = useContext(RootContext); + const { editor } = useEditor(); useEffect(() => { editor.mouseManager.onSelectStartWith(blockId, cb); return () => { diff --git a/libs/components/editor-core/src/index.ts b/libs/components/editor-core/src/index.ts index 6cdd358058..bea2fed3a0 100644 --- a/libs/components/editor-core/src/index.ts +++ b/libs/components/editor-core/src/index.ts @@ -1,4 +1,3 @@ -export { ColumnsContext, RootContext } from './contexts'; export { RenderRoot, MIN_PAGE_WIDTH } from './RenderRoot'; export * from './render-block'; export * from './hooks'; @@ -16,3 +15,5 @@ export * from './kanban/types'; export * from './utils'; export * from './editor'; + +export { RefPageProvider, useRefPage } from './ref-page'; diff --git a/libs/components/editor-core/src/kanban/kanban.ts b/libs/components/editor-core/src/kanban/kanban.ts index ff33b17f1d..218476ac44 100644 --- a/libs/components/editor-core/src/kanban/kanban.ts +++ b/libs/components/editor-core/src/kanban/kanban.ts @@ -1,6 +1,6 @@ import { Protocol } from '@toeverything/datasource/db-service'; import { useCallback, useContext, useEffect, useState } from 'react'; -import { useEditor } from '../contexts'; +import { useEditor } from '../Contexts'; import { AsyncBlock } from '../editor'; import { useRecastView } from '../recast-block'; import { useRecastBlock } from '../recast-block/Context'; diff --git a/libs/components/editor-core/src/recast-block/Context.tsx b/libs/components/editor-core/src/recast-block/Context.tsx index 55d334039b..47ec6cfcdb 100644 --- a/libs/components/editor-core/src/recast-block/Context.tsx +++ b/libs/components/editor-core/src/recast-block/Context.tsx @@ -2,6 +2,7 @@ import { Protocol } from '@toeverything/datasource/db-service'; import { AsyncBlock } from '../editor'; import { ComponentType, createContext, ReactNode, useContext } from 'react'; import { RecastBlock } from './types'; +import { RefPageProvider } from '../ref-page'; /** * Determine whether the block supports RecastBlock @@ -47,7 +48,7 @@ export const RecastBlockProvider = ({ return ( - {children} + {children} ); }; @@ -60,7 +61,7 @@ export const useRecastBlock = () => { const recastBlock = useContext(RecastBlockContext); if (!recastBlock) { throw new Error( - 'Failed to find recastBlock! Please use the hook under `RecastTableProvider`.' + 'Failed to find recastBlock! Please use the hook under `RecastBlockProvider`.' ); } return recastBlock; diff --git a/libs/components/editor-core/src/recast-block/README.md b/libs/components/editor-core/src/recast-block/README.md index 59aed91be7..489412eab3 100644 --- a/libs/components/editor-core/src/recast-block/README.md +++ b/libs/components/editor-core/src/recast-block/README.md @@ -49,22 +49,3 @@ const SomeBlock = () => { return
...
; }; ``` - -## Scene - -**Notice: The scene API will refactor at next version.** - -```tsx -const SomeBlock = () => { - const { scene, setScene, setPage, setTable, setKanban } = - useRecastBlockScene(); - - return ( - <> -
Scene: {scene}
- - - - ); -}; -``` diff --git a/libs/components/editor-core/src/recast-block/group.ts b/libs/components/editor-core/src/recast-block/group.ts index ae846fc238..9879ba43df 100644 --- a/libs/components/editor-core/src/recast-block/group.ts +++ b/libs/components/editor-core/src/recast-block/group.ts @@ -32,7 +32,7 @@ export const mergeGroup = async (...groups: AsyncBlock[]) => { ); } - await mergeGroupProperties(...(groups as RecastBlock[])); + await mergeGroupProperties(...(groups as unknown as RecastBlock[])); const [headGroup, ...restGroups] = groups; // Add all children to the head group @@ -174,7 +174,7 @@ export const splitGroup = async ( } splitGroupProperties( - group as RecastBlock, + group as unknown as RecastBlock, newGroupBlock as unknown as RecastBlock ); await group.after(newGroupBlock); diff --git a/libs/components/editor-core/src/ref-page/ModalPage.tsx b/libs/components/editor-core/src/ref-page/ModalPage.tsx new file mode 100644 index 0000000000..52e1d6554f --- /dev/null +++ b/libs/components/editor-core/src/ref-page/ModalPage.tsx @@ -0,0 +1,87 @@ +import { MuiBackdrop, styled, useTheme } from '@toeverything/components/ui'; +import { createContext, ReactNode, useContext, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { RenderBlock } from '../render-block'; + +const Dialog = styled('div')({ + flex: 1, + width: '880px', + margin: '72px auto', + background: '#fff', + boxShadow: '0px 1px 10px rgba(152, 172, 189, 0.6)', + borderRadius: '10px', + padding: '72px 120px', + overflow: 'scroll', +}); + +const Modal = ({ open, children }: { open: boolean; children?: ReactNode }) => { + const theme = useTheme(); + const { closeSubPage } = useRefPage(); + + return createPortal( + + { + e.stopPropagation(); + }} + > + {children} + + , + + document.body + ); +}; + +const ModalPage = ({ blockId }: { blockId: string | null }) => { + return ( + + {blockId && } + + ); +}; + +const RefPageContext = createContext< + ReturnType> | undefined +>(undefined); + +export const RefPageProvider = ({ children }: { children: ReactNode }) => { + const state = useState(); + const [blockId, setBlockId] = state; + + return ( + + {children} + + + ); +}; + +export const useRefPage = () => { + const context = useContext(RefPageContext); + if (!context) { + throw new Error( + 'Wrap your app inside of a `SubPageProvider` to have access to the hook context!' + ); + } + const [blockId, setBlockId] = context; + const openSubPage = (blockId: string) => { + setBlockId(blockId); + }; + const closeSubPage = () => { + setBlockId(null); + }; + + return { blockId, open: !!blockId, openSubPage, closeSubPage }; +}; + +// export const openSubPage = () => {}; diff --git a/libs/components/editor-core/src/ref-page/index.ts b/libs/components/editor-core/src/ref-page/index.ts new file mode 100644 index 0000000000..4b06310a5c --- /dev/null +++ b/libs/components/editor-core/src/ref-page/index.ts @@ -0,0 +1 @@ +export { useRefPage, RefPageProvider } from './ModalPage'; diff --git a/libs/components/editor-core/src/render-block/RenderBlock.tsx b/libs/components/editor-core/src/render-block/RenderBlock.tsx index 46f068af2b..74f6c659cf 100644 --- a/libs/components/editor-core/src/render-block/RenderBlock.tsx +++ b/libs/components/editor-core/src/render-block/RenderBlock.tsx @@ -1,8 +1,8 @@ -import { styled, Theme } from '@toeverything/components/ui'; -import { FC, useContext, useLayoutEffect, useMemo, useRef } from 'react'; +import { styled } from '@toeverything/components/ui'; +import { FC, useLayoutEffect, useMemo, useRef } from 'react'; // import { RenderChildren } from './RenderChildren'; -import { RootContext } from '../contexts'; +import { useEditor } from '../Contexts'; import { useBlock } from '../hooks'; interface RenderBlockProps { @@ -14,7 +14,7 @@ export const RenderBlock: FC = ({ blockId, hasContainer = true, }) => { - const { editor, editorElement } = useContext(RootContext); + const { editor, editorElement } = useEditor(); const { block } = useBlock(blockId); const blockRef = useRef(null); diff --git a/libs/components/ui/src/mui.ts b/libs/components/ui/src/mui.ts index b0658ad526..47588ce5e6 100644 --- a/libs/components/ui/src/mui.ts +++ b/libs/components/ui/src/mui.ts @@ -13,6 +13,7 @@ import type { } from '@mui/material'; import { Avatar, + Backdrop, Box, Button, Checkbox, @@ -243,3 +244,7 @@ export const MuiFade = Fade; * @deprecated It is not recommended to use Mui directly, because the design will not refer to Mui's interaction logic. */ export const MuiRadio = Radio; +/** + * @deprecated It is not recommended to use Mui directly, because the design will not refer to Mui's interaction logic. + */ +export const MuiBackdrop = Backdrop; diff --git a/libs/datasource/feature-flags/README.md b/libs/datasource/feature-flags/README.md index f5c59f4e13..7a04209c5d 100644 --- a/libs/datasource/feature-flags/README.md +++ b/libs/datasource/feature-flags/README.md @@ -2,7 +2,15 @@ ## Usage -- set provider +- Set token at environment variable + - The key can be obtained from the [Feature Flag Portal](https://portal.featureflag.co/account-settings/projects) + +```shell +# .env.local +AFFINE_FEATURE_FLAG_TOKEN=XXXXXXX +``` + +- Set provider ```tsx import { FeatureFlagsProvider } from '@toeverything/datasource/feature-flags'; @@ -42,7 +50,8 @@ const App = () => { **When entering development mode feature flag will NOT be updated in real time** -- `activateFfcDevMode()` play with feature flags locally +- `activateFfcDevMode(PASSWORD)` play with feature flags locally + - The `devModePassword` can be obtained from `src/config.ts` - `quitFfcDevMode()` quit dev mode ## Running unit tests diff --git a/libs/datasource/feature-flags/src/config.ts b/libs/datasource/feature-flags/src/config.ts index 7f3360d6db..5295dae995 100644 --- a/libs/datasource/feature-flags/src/config.ts +++ b/libs/datasource/feature-flags/src/config.ts @@ -8,4 +8,18 @@ export const config: IOption = { // id: 'the user's unique identifier' // } devModePassword: '-', + enableDataSync: !!process.env['AFFINE_FEATURE_FLAG_TOKEN'], + // bootstrap: [ + // { + // // the feature flag key + // id: 'flag', + // // the feature flag value + // variation: false, + // // the variation data type, string is used if not provided + // variationType: VariationDataType.boolean, + // variationOptions: [], + // timestamp: 0, + // sendToExperiment: false, + // }, + // ], };