diff --git a/.changeset/dnd-ui.md b/.changeset/dnd-ui.md new file mode 100644 index 0000000000..30e540db39 --- /dev/null +++ b/.changeset/dnd-ui.md @@ -0,0 +1,29 @@ +--- +'@udecode/plate-ui-dnd': major +--- + +Unstyled logic has been moved to `@udecode/plate-dnd` +```ts +// before +import { createDndPlugin } from '@udecode/plate-ui-dnd'; + +// after +import { createDndPlugin } from '@udecode/plate-dnd'; +``` +Renamed: +- `withDraggables` -> `withPlateDraggables`. In the second parameter, draggable props options have been moved under `draggableProps`: +```tsx +// before +{ + onRenderDragHandle: () => {} + styles, +} + +// after +{ + draggableProps: { + onRenderDragHandle: () => {} + styles, + }, +} +``` \ No newline at end of file diff --git a/.changeset/dnd.md b/.changeset/dnd.md new file mode 100644 index 0000000000..1fab34e291 --- /dev/null +++ b/.changeset/dnd.md @@ -0,0 +1,5 @@ +--- +'@udecode/plate-dnd': minor +--- + +New package extracting unstyled logic from `@udecode/plate-ui-dnd` diff --git a/.changeset/two-glasses-doubt.md b/.changeset/two-glasses-doubt.md new file mode 100644 index 0000000000..244b50e0dd --- /dev/null +++ b/.changeset/two-glasses-doubt.md @@ -0,0 +1,6 @@ +--- +"@udecode/plate-core": patch + +--- + +`createHOC`: deep merge props diff --git a/config/aliases-plate.js b/config/aliases-plate.js index cf4c34c099..f9a6497fcd 100644 --- a/config/aliases-plate.js +++ b/config/aliases-plate.js @@ -11,6 +11,7 @@ module.exports = { '@udecode/plate-combobox': 'editor/combobox', '@udecode/plate-comments': 'comments', '@udecode/plate-core': 'core', + '@udecode/plate-dnd': 'dnd', '@udecode/plate-emoji': 'nodes/emoji', '@udecode/plate-serializer-csv': 'serializers/csv', '@udecode/plate-serializer-docx': 'serializers/docx', diff --git a/docs/docs/components/dnd.mdx b/docs/docs/components/dnd.mdx index 350241ed2b..ee8076626a 100644 --- a/docs/docs/components/dnd.mdx +++ b/docs/docs/components/dnd.mdx @@ -8,7 +8,10 @@ Includes drag & drop feature using a drag handle on the left side of each block. ### Installation ```bash npm2yarn -npm install @udecode/plate-ui-dnd react-dnd react-dnd-html5-backend +npm install @udecode/plate-dnd react-dnd react-dnd-html5-backend + +# with Plate UI +npm install @udecode/plate-ui-dnd ``` ### Usage @@ -17,6 +20,7 @@ npm install @udecode/plate-ui-dnd react-dnd react-dnd-html5-backend - Override your components with `components = withStyledDraggables(components)`, these will wrap your block components with a draggable handle. - Use `createNodeIdPlugin()` plugin as it requires `id` property on your blocks. -import { DndSandpack } from './DndSandpack' + +import { DndSandpack } from "./DndSandpack"; diff --git a/docs/docs/sandpack/code-deps.ts b/docs/docs/sandpack/code-deps.ts index 5a7a792e56..aac5a91062 100644 --- a/docs/docs/sandpack/code-deps.ts +++ b/docs/docs/sandpack/code-deps.ts @@ -1,6 +1,7 @@ import { cloudUiVersion, cloudVersion, + dndUiVersion, dndVersion, excalidrawVersion, juiceVersion, @@ -31,7 +32,8 @@ export const cloudDeps = { }; export const dndDeps = { - '@udecode/plate-ui-dnd': dndVersion, + '@udecode/plate-dnd': dndVersion, + '@udecode/plate-ui-dnd': dndUiVersion, 'react-dnd': '15.1.2', 'react-dnd-html5-backend': '15.1.3', }; diff --git a/docs/docs/sandpack/plate-versions.ts b/docs/docs/sandpack/plate-versions.ts index d5f567a396..8a8daca542 100644 --- a/docs/docs/sandpack/plate-versions.ts +++ b/docs/docs/sandpack/plate-versions.ts @@ -6,3 +6,4 @@ export const selectionVersion = '19.7.0'; export const cloudVersion = '19.7.0'; export const cloudUiVersion = '19.7.0'; export const dndVersion = '19.7.0'; +export const dndUiVersion = '19.7.0'; diff --git a/examples/src/DndApp.tsx b/examples/src/DndApp.tsx index df1f80c4a7..cf2aed3a60 100644 --- a/examples/src/DndApp.tsx +++ b/examples/src/DndApp.tsx @@ -6,7 +6,7 @@ import { createNodeIdPlugin, Plate, } from '@udecode/plate'; -import { createDndPlugin } from '@udecode/plate-ui-dnd'; +import { createDndPlugin } from '@udecode/plate-dnd'; import { basicElementsValue } from './basic-elements/basicElementsValue'; import { editableProps } from './common/editableProps'; import { plateUI } from './common/plateUI'; diff --git a/examples/src/PlaygroundApp.tsx b/examples/src/PlaygroundApp.tsx index 9702ffb03d..fe25d6109d 100644 --- a/examples/src/PlaygroundApp.tsx +++ b/examples/src/PlaygroundApp.tsx @@ -51,9 +51,9 @@ import { PlateFloatingComments, PlateProvider, } from '@udecode/plate'; +import { createDndPlugin } from '@udecode/plate-dnd'; import { createJuicePlugin } from '@udecode/plate-juice'; import { createBlockSelectionPlugin } from '@udecode/plate-selection'; -import { createDndPlugin } from '@udecode/plate-ui-dnd'; import { createExcalidrawPlugin, ELEMENT_EXCALIDRAW, diff --git a/examples/src/dnd/withStyledDraggables.tsx b/examples/src/dnd/withStyledDraggables.tsx index b4f6d4aa4b..8ce2a4d996 100644 --- a/examples/src/dnd/withStyledDraggables.tsx +++ b/examples/src/dnd/withStyledDraggables.tsx @@ -18,7 +18,7 @@ import { ELEMENT_TODO_LI, ELEMENT_UL, } from '@udecode/plate'; -import { withDraggables } from '@udecode/plate-ui-dnd'; +import { withPlateDraggables } from '@udecode/plate-ui-dnd'; const styles = { grabber: { fontSize: 12 }, @@ -50,7 +50,7 @@ export const grabberTooltipProps: TippyProps = { }; export const withStyledDraggables = (components: any) => { - return withDraggables(components, [ + return withPlateDraggables(components, [ { keys: [ELEMENT_PARAGRAPH, ELEMENT_UL, ELEMENT_OL], level: 0, @@ -73,85 +73,101 @@ export const withStyledDraggables = (components: any) => { ELEMENT_MEDIA_EMBED, ELEMENT_CODE_BLOCK, ], - onRenderDragHandle: () => { - return ( - - - - ); + draggableProps: { + onRenderDragHandle: () => { + return ( + + + + ); + }, }, }, { key: ELEMENT_H1, - styles: { - gutterLeft: { - padding: '2em 0 4px', - fontSize: '1.875em', - }, - blockToolbarWrapper: { - height: '1.3em', + draggableProps: { + styles: { + gutterLeft: { + padding: '2em 0 4px', + fontSize: '1.875em', + }, + blockToolbarWrapper: { + height: '1.3em', + }, }, }, }, { key: ELEMENT_H2, - styles: { - gutterLeft: { - padding: '1.4em 0 1px', - fontSize: '1.5em', - }, - blockToolbarWrapper: { - height: '1.3em', + draggableProps: { + styles: { + gutterLeft: { + padding: '1.4em 0 1px', + fontSize: '1.5em', + }, + blockToolbarWrapper: { + height: '1.3em', + }, }, }, }, { key: ELEMENT_H3, - styles: { - gutterLeft: { - padding: '1em 0 1px', - fontSize: '1.25em', - }, - blockToolbarWrapper: { - height: '1.3em', + draggableProps: { + styles: { + gutterLeft: { + padding: '1em 0 1px', + fontSize: '1.25em', + }, + blockToolbarWrapper: { + height: '1.3em', + }, }, }, }, { keys: [ELEMENT_H4, ELEMENT_H5, ELEMENT_H6], - styles: { - gutterLeft: { - padding: '0.75em 0 0', - fontSize: '1.1em', - }, - blockToolbarWrapper: { - height: '1.3em', + draggableProps: { + styles: { + gutterLeft: { + padding: '0.75em 0 0', + fontSize: '1.1em', + }, + blockToolbarWrapper: { + height: '1.3em', + }, }, }, }, { keys: [ELEMENT_PARAGRAPH, ELEMENT_UL, ELEMENT_OL], - styles: { - gutterLeft: { - padding: '4px 0 0', + draggableProps: { + styles: { + gutterLeft: { + padding: '4px 0 0', + }, }, }, }, { key: ELEMENT_BLOCKQUOTE, - styles: { - gutterLeft: { - padding: '18px 0 0', + draggableProps: { + styles: { + gutterLeft: { + padding: '18px 0 0', + }, }, }, }, { key: ELEMENT_CODE_BLOCK, - styles: { - gutterLeft: { - padding: '12px 0 0', + draggableProps: { + styles: { + gutterLeft: { + padding: '12px 0 0', + }, }, }, }, diff --git a/packages/core/src/utils/react/createNodesHOC.tsx b/packages/core/src/utils/react/createNodesHOC.tsx index 77f77a01ba..e7b5b64711 100644 --- a/packages/core/src/utils/react/createNodesHOC.tsx +++ b/packages/core/src/utils/react/createNodesHOC.tsx @@ -1,5 +1,6 @@ import { FunctionComponent } from 'react'; import { castArray } from 'lodash'; +import merge from 'lodash/merge'; import { AnyObject } from '../../types/misc/AnyObject'; import { createNodeHOC } from './createNodeHOC'; @@ -30,7 +31,7 @@ const createHOC = (withHOC: any) => { const _keys: string[] = key ? [key] : keys ?? Object.keys(_components); _keys.forEach((_key) => { - optionsByKey[_key] = { ...optionsByKey[_key], ...opt }; + optionsByKey[_key] = merge(optionsByKey[_key], opt); }); }); diff --git a/packages/dnd/.npmignore b/packages/dnd/.npmignore new file mode 100644 index 0000000000..7d3b305b17 --- /dev/null +++ b/packages/dnd/.npmignore @@ -0,0 +1,3 @@ +__tests__ +__test-utils__ +__mocks__ diff --git a/packages/dnd/README.md b/packages/dnd/README.md new file mode 100644 index 0000000000..39b7db2e6c --- /dev/null +++ b/packages/dnd/README.md @@ -0,0 +1,17 @@ +# Plate drag & drop feature + +This package implements the drag & drop feature. It allows you to drag & +drop blocks using a grabber. + +## Documentation + +Check out +[Drag & Drop](https://plate.udecode.io/docs/components/dnd). + +## API + +See the [API documentation](https://plate-api.udecode.io/globals.html). + +## License + +[MIT](../../LICENSE) diff --git a/packages/dnd/package.json b/packages/dnd/package.json new file mode 100644 index 0000000000..7642331795 --- /dev/null +++ b/packages/dnd/package.json @@ -0,0 +1,42 @@ +{ + "name": "@udecode/plate-dnd", + "version": "19.7.0", + "description": "Drag & drop feature for Plate", + "license": "MIT", + "homepage": "https://plate.udecode.io", + "repository": { + "type": "git", + "url": "https://github.com/udecode/plate.git", + "directory": "packages/dnd" + }, + "bugs": { + "url": "https://github.com/udecode/plate/issues" + }, + "main": "dist/index.js", + "module": "dist/index.es.js", + "files": [ + "dist" + ], + "types": "dist/index.d.ts", + "dependencies": { + "@udecode/plate-core": "19.7.0", + "raf": "^3.4.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dnd": ">=14.0.0", + "react-dnd-html5-backend": ">=14.0.0", + "react-dom": ">=16.8.0", + "slate": ">=0.87.0", + "slate-history": ">=0.86.0", + "slate-react": ">=0.88.0" + }, + "keywords": [ + "plate", + "plugin", + "slate" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/dnd/src/components/DragHandle.ts b/packages/dnd/src/components/DragHandle.ts new file mode 100644 index 0000000000..73189d54cc --- /dev/null +++ b/packages/dnd/src/components/DragHandle.ts @@ -0,0 +1,25 @@ +import { + createComponentAs, + createElementAs, + HTMLPropsAs, + TElement, +} from '@udecode/plate-core'; + +export type PlateDragHandleProps = { + element: TElement; +} & HTMLPropsAs<'button'>; + +export const usePlateDragHandleProps = ( + props: PlateDragHandleProps +): HTMLPropsAs<'button'> => { + return { + ...props, + onMouseDown: (e: any) => e.stopPropagation(), + }; +}; + +export const DragHandle = createComponentAs((props) => { + const htmlProps = usePlateDragHandleProps(props); + + return createElementAs('button', htmlProps); +}); diff --git a/packages/dnd/src/components/Draggable.ts b/packages/dnd/src/components/Draggable.ts new file mode 100644 index 0000000000..e406d4fccd --- /dev/null +++ b/packages/dnd/src/components/Draggable.ts @@ -0,0 +1,17 @@ +import { DraggableBlock } from './DraggableBlock'; +import { DraggableBlockToolbar } from './DraggableBlockToolbar'; +import { DraggableBlockToolbarWrapper } from './DraggableBlockToolbarWrapper'; +import { DraggableDropline } from './DraggableDropline'; +import { DraggableGutterLeft } from './DraggableGutterLeftProps'; +import { DraggableRoot } from './DraggableRoot'; +import { DragHandle } from './DragHandle'; + +export const Draggable = { + Root: DraggableRoot, + Block: DraggableBlock, + BlockToolbar: DraggableBlockToolbar, + Wrapper: DraggableBlockToolbarWrapper, + GutenLeft: DraggableGutterLeft, + Handle: DragHandle, + Dropline: DraggableDropline, +}; diff --git a/packages/dnd/src/components/DraggableBlock.ts b/packages/dnd/src/components/DraggableBlock.ts new file mode 100644 index 0000000000..f2e3ae242e --- /dev/null +++ b/packages/dnd/src/components/DraggableBlock.ts @@ -0,0 +1,28 @@ +import { useRef } from 'react'; +import { + createComponentAs, + createElementAs, + HTMLPropsAs, + useComposedRef, +} from '@udecode/plate-core'; + +export type DraggableBlockProps = {} & HTMLPropsAs<'div'>; + +export const useDraggableBlockProps = ( + props: DraggableBlockProps +): HTMLPropsAs<'div'> => { + const blockRef = useRef(null); + + return { + ...props, + ref: useComposedRef(props.ref, blockRef), + }; +}; + +export const DraggableBlock = createComponentAs( + (props) => { + const htmlProps = useDraggableBlockProps(props); + + return createElementAs('div', htmlProps); + } +); diff --git a/packages/dnd/src/components/DraggableBlockToolbar.ts b/packages/dnd/src/components/DraggableBlockToolbar.ts new file mode 100644 index 0000000000..fe4073bbf7 --- /dev/null +++ b/packages/dnd/src/components/DraggableBlockToolbar.ts @@ -0,0 +1,37 @@ +import { useRef } from 'react'; +import { ConnectDragSource } from 'react-dnd'; +import { + createComponentAs, + createElementAs, + HTMLPropsAs, + TElement, + useComposedRef, +} from '@udecode/plate-core'; + +export type DraggableBlockToolbarProps = { + element: TElement; + dragRef: ConnectDragSource; +} & HTMLPropsAs<'div'>; + +export const useDraggableBlockToolbarProps = ({ + element, + dragRef, + ...props +}: DraggableBlockToolbarProps): HTMLPropsAs<'div'> => { + const dragWrapperRef = useRef(null); + + const multiDragRef = useComposedRef(dragRef, dragWrapperRef); + + return { + ...props, + ref: useComposedRef(props.ref, multiDragRef), + }; +}; + +export const DraggableBlockToolbar = createComponentAs( + (props) => { + const htmlProps = useDraggableBlockToolbarProps(props); + + return createElementAs('div', htmlProps); + } +); diff --git a/packages/dnd/src/components/DraggableBlockToolbarWrapper.ts b/packages/dnd/src/components/DraggableBlockToolbarWrapper.ts new file mode 100644 index 0000000000..80bea261df --- /dev/null +++ b/packages/dnd/src/components/DraggableBlockToolbarWrapper.ts @@ -0,0 +1,9 @@ +import { + AsProps, + createComponentAs, + createElementAs, +} from '@udecode/plate-core'; + +export const DraggableBlockToolbarWrapper = createComponentAs>( + (props) => createElementAs('div', props) +); diff --git a/packages/dnd/src/components/DraggableDropline.ts b/packages/dnd/src/components/DraggableDropline.ts new file mode 100644 index 0000000000..64722c44f5 --- /dev/null +++ b/packages/dnd/src/components/DraggableDropline.ts @@ -0,0 +1,24 @@ +import { + createComponentAs, + createElementAs, + HTMLPropsAs, +} from '@udecode/plate-core'; + +export type DraggableDroplineProps = {} & HTMLPropsAs<'div'>; + +export const useDraggableDroplineProps = ( + props: DraggableDroplineProps +): HTMLPropsAs<'div'> => { + return { + contentEditable: false, + ...props, + }; +}; + +export const DraggableDropline = createComponentAs( + (props) => { + const htmlProps = useDraggableDroplineProps(props); + + return createElementAs('div', htmlProps); + } +); diff --git a/packages/dnd/src/components/DraggableGutterLeftProps.ts b/packages/dnd/src/components/DraggableGutterLeftProps.ts new file mode 100644 index 0000000000..5afd081bc8 --- /dev/null +++ b/packages/dnd/src/components/DraggableGutterLeftProps.ts @@ -0,0 +1,25 @@ +import { + AsProps, + createComponentAs, + createElementAs, + HTMLPropsAs, +} from '@udecode/plate-core'; + +export type DraggableGutterLeftProps = {} & HTMLPropsAs<'div'>; + +export const useDraggableGutterLeft = ( + props: DraggableGutterLeftProps +): HTMLPropsAs<'div'> => { + return { + contentEditable: false, + ...props, + }; +}; + +export const DraggableGutterLeft = createComponentAs>( + (props) => { + const htmlProps = useDraggableGutterLeft(props); + + return createElementAs('div', htmlProps); + } +); diff --git a/packages/dnd/src/components/DraggableRoot.ts b/packages/dnd/src/components/DraggableRoot.ts new file mode 100644 index 0000000000..4344eb8e57 --- /dev/null +++ b/packages/dnd/src/components/DraggableRoot.ts @@ -0,0 +1,27 @@ +import React from 'react'; +import { + createComponentAs, + createElementAs, + HTMLPropsAs, + useComposedRef, +} from '@udecode/plate-core'; +import { DraggableState } from './useDraggableState'; + +export type DraggableRootProps = {} & HTMLPropsAs<'div'> & + Pick; + +export const useDraggableRootProps = ({ + rootRef, + ...props +}: DraggableRootProps): HTMLPropsAs<'div'> => { + return { + ...props, + ref: useComposedRef(props.ref, rootRef), + }; +}; + +export const DraggableRoot = createComponentAs((props) => { + const htmlProps = useDraggableRootProps(props); + + return createElementAs('div', htmlProps); +}); diff --git a/packages/ui/dnd/src/components/Scroller/DndScroller.tsx b/packages/dnd/src/components/Scroller/DndScroller.tsx similarity index 100% rename from packages/ui/dnd/src/components/Scroller/DndScroller.tsx rename to packages/dnd/src/components/Scroller/DndScroller.tsx diff --git a/packages/ui/dnd/src/components/Scroller/ScrollArea.tsx b/packages/dnd/src/components/Scroller/ScrollArea.tsx similarity index 100% rename from packages/ui/dnd/src/components/Scroller/ScrollArea.tsx rename to packages/dnd/src/components/Scroller/ScrollArea.tsx diff --git a/packages/ui/dnd/src/components/Scroller/Scroller.tsx b/packages/dnd/src/components/Scroller/Scroller.tsx similarity index 100% rename from packages/ui/dnd/src/components/Scroller/Scroller.tsx rename to packages/dnd/src/components/Scroller/Scroller.tsx diff --git a/packages/ui/dnd/src/components/Scroller/index.ts b/packages/dnd/src/components/Scroller/index.ts similarity index 100% rename from packages/ui/dnd/src/components/Scroller/index.ts rename to packages/dnd/src/components/Scroller/index.ts diff --git a/packages/dnd/src/components/index.ts b/packages/dnd/src/components/index.ts new file mode 100644 index 0000000000..3f24f5e3c1 --- /dev/null +++ b/packages/dnd/src/components/index.ts @@ -0,0 +1,16 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './DragHandle'; +export * from './Draggable'; +export * from './DraggableBlock'; +export * from './DraggableBlockToolbar'; +export * from './DraggableBlockToolbarWrapper'; +export * from './DraggableDropline'; +export * from './DraggableGutterLeftProps'; +export * from './DraggableRoot'; +export * from './useDraggableState'; +export * from './useWithDraggableState'; +export * from './withDraggable'; +export * from './Scroller/index'; diff --git a/packages/dnd/src/components/useDraggableState.ts b/packages/dnd/src/components/useDraggableState.ts new file mode 100644 index 0000000000..45927599c3 --- /dev/null +++ b/packages/dnd/src/components/useDraggableState.ts @@ -0,0 +1,25 @@ +import React, { useRef } from 'react'; +import { ConnectDragSource } from 'react-dnd'; +import { TElement } from '@udecode/plate-core'; +import { useDndBlock } from '../hooks'; +import { DropLineDirection } from '../types'; + +export type DraggableState = { + dropLine: DropLineDirection; + isDragging: boolean; + rootRef: React.RefObject; + dragRef: ConnectDragSource; +}; + +export const useDraggableState = (props: { + element: TElement; +}): DraggableState => { + const { element } = props; + const rootRef = useRef(null); + const { dropLine, isDragging, dragRef } = useDndBlock({ + id: element.id as string, + nodeRef: rootRef, + }); + + return { dropLine, isDragging, rootRef, dragRef }; +}; diff --git a/packages/dnd/src/components/useWithDraggableState.ts b/packages/dnd/src/components/useWithDraggableState.ts new file mode 100644 index 0000000000..cbf295ae79 --- /dev/null +++ b/packages/dnd/src/components/useWithDraggableState.ts @@ -0,0 +1,57 @@ +import { useMemo } from 'react'; +import { + findNodePath, + PlateRenderElementProps, + TEditor, +} from '@udecode/plate-core'; +import { Path } from 'slate'; +import { useReadOnly } from 'slate-react'; + +export interface WithDraggableOptions { + /** + * Document level where dnd is enabled. 0 = root blocks, 1 = first level of children, etc. + * Set to null to allow all levels. + * @default 0 + */ + level?: number | null; + + /** + * Filter out elements that can't be dragged. + */ + filter?: (editor: TEditor, path: Path) => boolean; + + /** + * Enables dnd in read-only. + */ + allowReadOnly?: boolean; + draggableProps?: T; +} + +export const useWithDraggableState = ({ + editor, + level = 0, + filter, + element, + allowReadOnly = false, + draggableProps, +}: WithDraggableOptions & PlateRenderElementProps) => { + const readOnly = useReadOnly(); + const path = useMemo(() => findNodePath(editor, element), [editor, element]); + + const filteredOut = useMemo( + () => + path && + ((Number.isInteger(level) && level !== path.length - 1) || + (filter && filter(editor, path))), + [path, level, filter, editor] + ); + + return { + disabled: filteredOut || (!allowReadOnly && readOnly), + draggableProps: { + editor, + element, + ...draggableProps, + }, + }; +}; diff --git a/packages/dnd/src/components/withDraggable.tsx b/packages/dnd/src/components/withDraggable.tsx new file mode 100644 index 0000000000..ec5b0b85de --- /dev/null +++ b/packages/dnd/src/components/withDraggable.tsx @@ -0,0 +1,32 @@ +import React, { forwardRef } from 'react'; +import { + AnyObject, + PlateRenderElementProps, + RenderFunction, +} from '@udecode/plate-core'; +import { + useWithDraggableState, + WithDraggableOptions, +} from './useWithDraggableState'; + +export const withDraggable = ( + Draggable: RenderFunction, + Component: RenderFunction, + options?: WithDraggableOptions +) => + forwardRef((props, ref) => { + const { disabled, draggableProps } = useWithDraggableState({ + ...options, + ...props, + }); + + if (disabled) { + return ; + } + + return ( + + + + ); + }); diff --git a/packages/ui/dnd/src/createDndPlugin.tsx b/packages/dnd/src/createDndPlugin.tsx similarity index 91% rename from packages/ui/dnd/src/createDndPlugin.tsx rename to packages/dnd/src/createDndPlugin.tsx index e4f6f4541a..6b2dff4253 100644 --- a/packages/ui/dnd/src/createDndPlugin.tsx +++ b/packages/dnd/src/createDndPlugin.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { createPluginFactory } from '@udecode/plate-core'; -import { DndScroller, ScrollerProps } from './components/index'; +import { DndScroller, ScrollerProps } from './components/Scroller'; import { dndStore } from './dndStore'; export interface DndPlugin { diff --git a/packages/ui/dnd/src/dndStore.ts b/packages/dnd/src/dndStore.ts similarity index 100% rename from packages/ui/dnd/src/dndStore.ts rename to packages/dnd/src/dndStore.ts diff --git a/packages/ui/dnd/src/hooks/index.ts b/packages/dnd/src/hooks/index.ts similarity index 100% rename from packages/ui/dnd/src/hooks/index.ts rename to packages/dnd/src/hooks/index.ts diff --git a/packages/ui/dnd/src/hooks/useDndBlock.ts b/packages/dnd/src/hooks/useDndBlock.ts similarity index 100% rename from packages/ui/dnd/src/hooks/useDndBlock.ts rename to packages/dnd/src/hooks/useDndBlock.ts diff --git a/packages/ui/dnd/src/hooks/useDndNode.ts b/packages/dnd/src/hooks/useDndNode.ts similarity index 100% rename from packages/ui/dnd/src/hooks/useDndNode.ts rename to packages/dnd/src/hooks/useDndNode.ts diff --git a/packages/ui/dnd/src/hooks/useDragBlock.ts b/packages/dnd/src/hooks/useDragBlock.ts similarity index 100% rename from packages/ui/dnd/src/hooks/useDragBlock.ts rename to packages/dnd/src/hooks/useDragBlock.ts diff --git a/packages/ui/dnd/src/hooks/useDragNode.ts b/packages/dnd/src/hooks/useDragNode.ts similarity index 100% rename from packages/ui/dnd/src/hooks/useDragNode.ts rename to packages/dnd/src/hooks/useDragNode.ts diff --git a/packages/ui/dnd/src/hooks/useDropBlock.ts b/packages/dnd/src/hooks/useDropBlock.ts similarity index 100% rename from packages/ui/dnd/src/hooks/useDropBlock.ts rename to packages/dnd/src/hooks/useDropBlock.ts diff --git a/packages/ui/dnd/src/hooks/useDropNode.ts b/packages/dnd/src/hooks/useDropNode.ts similarity index 100% rename from packages/ui/dnd/src/hooks/useDropNode.ts rename to packages/dnd/src/hooks/useDropNode.ts diff --git a/packages/dnd/src/index.ts b/packages/dnd/src/index.ts new file mode 100644 index 0000000000..21faf2c126 --- /dev/null +++ b/packages/dnd/src/index.ts @@ -0,0 +1,12 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './createDndPlugin'; +export * from './dndStore'; +export * from './types'; +export * from './components/index'; +export * from './hooks/index'; +export * from './queries/index'; +export * from './transforms/index'; +export * from './utils/index'; diff --git a/packages/ui/dnd/src/queries/getBlocksWithId.ts b/packages/dnd/src/queries/getBlocksWithId.ts similarity index 100% rename from packages/ui/dnd/src/queries/getBlocksWithId.ts rename to packages/dnd/src/queries/getBlocksWithId.ts diff --git a/packages/ui/dnd/src/queries/getNodesRange.ts b/packages/dnd/src/queries/getNodesRange.ts similarity index 100% rename from packages/ui/dnd/src/queries/getNodesRange.ts rename to packages/dnd/src/queries/getNodesRange.ts diff --git a/packages/ui/dnd/src/queries/index.ts b/packages/dnd/src/queries/index.ts similarity index 100% rename from packages/ui/dnd/src/queries/index.ts rename to packages/dnd/src/queries/index.ts diff --git a/packages/ui/dnd/src/transforms/focusBlockStartById.ts b/packages/dnd/src/transforms/focusBlockStartById.ts similarity index 100% rename from packages/ui/dnd/src/transforms/focusBlockStartById.ts rename to packages/dnd/src/transforms/focusBlockStartById.ts diff --git a/packages/ui/dnd/src/transforms/index.ts b/packages/dnd/src/transforms/index.ts similarity index 100% rename from packages/ui/dnd/src/transforms/index.ts rename to packages/dnd/src/transforms/index.ts diff --git a/packages/ui/dnd/src/transforms/onDropNode.ts b/packages/dnd/src/transforms/onDropNode.ts similarity index 93% rename from packages/ui/dnd/src/transforms/onDropNode.ts rename to packages/dnd/src/transforms/onDropNode.ts index ce2f402df2..7132ddf1ff 100644 --- a/packages/ui/dnd/src/transforms/onDropNode.ts +++ b/packages/dnd/src/transforms/onDropNode.ts @@ -7,9 +7,9 @@ import { Value, } from '@udecode/plate-core'; import { Path } from 'slate'; -import { UseDropNodeOptions } from '../hooks/index'; +import { UseDropNodeOptions } from '../hooks'; import { DragItemNode } from '../types'; -import { getHoverDirection } from '../utils/index'; +import { getHoverDirection } from '../utils'; /** * Callback called on drag an drop a node with id. diff --git a/packages/ui/dnd/src/transforms/onHoverNode.ts b/packages/dnd/src/transforms/onHoverNode.ts similarity index 93% rename from packages/ui/dnd/src/transforms/onHoverNode.ts rename to packages/dnd/src/transforms/onHoverNode.ts index d061902aee..f18e6f0be0 100644 --- a/packages/ui/dnd/src/transforms/onHoverNode.ts +++ b/packages/dnd/src/transforms/onHoverNode.ts @@ -8,7 +8,7 @@ import { } from '@udecode/plate-core'; import { UseDropNodeOptions } from '../hooks/useDropNode'; import { DragItemNode } from '../types'; -import { getHoverDirection, getNewDirection } from '../utils/index'; +import { getHoverDirection, getNewDirection } from '../utils'; /** * Callback called when dragging a node and hovering nodes. diff --git a/packages/ui/dnd/src/transforms/removeBlocksAndFocus.ts b/packages/dnd/src/transforms/removeBlocksAndFocus.ts similarity index 100% rename from packages/ui/dnd/src/transforms/removeBlocksAndFocus.ts rename to packages/dnd/src/transforms/removeBlocksAndFocus.ts diff --git a/packages/ui/dnd/src/transforms/selectBlockById.ts b/packages/dnd/src/transforms/selectBlockById.ts similarity index 100% rename from packages/ui/dnd/src/transforms/selectBlockById.ts rename to packages/dnd/src/transforms/selectBlockById.ts diff --git a/packages/ui/dnd/src/transforms/selectBlocksBySelectionOrId.ts b/packages/dnd/src/transforms/selectBlocksBySelectionOrId.ts similarity index 100% rename from packages/ui/dnd/src/transforms/selectBlocksBySelectionOrId.ts rename to packages/dnd/src/transforms/selectBlocksBySelectionOrId.ts diff --git a/packages/ui/dnd/src/types.ts b/packages/dnd/src/types.ts similarity index 100% rename from packages/ui/dnd/src/types.ts rename to packages/dnd/src/types.ts diff --git a/packages/ui/dnd/src/utils/getHoverDirection.ts b/packages/dnd/src/utils/getHoverDirection.ts similarity index 100% rename from packages/ui/dnd/src/utils/getHoverDirection.ts rename to packages/dnd/src/utils/getHoverDirection.ts diff --git a/packages/ui/dnd/src/utils/getNewDirection.ts b/packages/dnd/src/utils/getNewDirection.ts similarity index 100% rename from packages/ui/dnd/src/utils/getNewDirection.ts rename to packages/dnd/src/utils/getNewDirection.ts diff --git a/packages/ui/dnd/src/utils/index.ts b/packages/dnd/src/utils/index.ts similarity index 100% rename from packages/ui/dnd/src/utils/index.ts rename to packages/dnd/src/utils/index.ts diff --git a/packages/dnd/tsconfig.json b/packages/dnd/tsconfig.json new file mode 100644 index 0000000000..9f68b4628c --- /dev/null +++ b/packages/dnd/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../config/tsconfig.build.json", + "compilerOptions": { + "declarationDir": "./dist" + }, + "include": ["src"] +} diff --git a/packages/nodes/table/src/index.ts b/packages/nodes/table/src/index.ts index eccb991726..033f521a8b 100644 --- a/packages/nodes/table/src/index.ts +++ b/packages/nodes/table/src/index.ts @@ -12,6 +12,7 @@ export * from './withInsertFragmentTable'; export * from './withInsertTextTable'; export * from './withNormalizeTable'; export * from './withSelectionTable'; +export * from './withSetFragmentDataTable'; export * from './withTable'; export * from './queries/index'; export * from './transforms/index'; diff --git a/packages/ui/dnd/README.md b/packages/ui/dnd/README.md index 71c466de08..005f7afcf3 100644 --- a/packages/ui/dnd/README.md +++ b/packages/ui/dnd/README.md @@ -1,7 +1,6 @@ -# Plate drag and drop feature +# Plate drag & drop UI -This package implements the drag & drop feature. It allows you to drag & -drop blocks using a grabber. +This package implements the drag & drop UI. ## Documentation diff --git a/packages/ui/dnd/package.json b/packages/ui/dnd/package.json index ca07b1ddd8..ed7d3953a3 100644 --- a/packages/ui/dnd/package.json +++ b/packages/ui/dnd/package.json @@ -19,11 +19,10 @@ ], "types": "dist/index.d.ts", "dependencies": { - "@react-hook/merged-ref": "^1.3.2", "@tippyjs/react": "^4.2.6", "@udecode/plate-core": "19.7.0", - "@udecode/plate-styled-components": "19.7.0", - "raf": "^3.4.1" + "@udecode/plate-dnd": "19.7.0", + "@udecode/plate-styled-components": "19.7.0" }, "peerDependencies": { "react": ">=16.8.0", diff --git a/packages/ui/dnd/src/components/Draggable.styles.ts b/packages/ui/dnd/src/PlateDraggable.styles.ts similarity index 78% rename from packages/ui/dnd/src/components/Draggable.styles.ts rename to packages/ui/dnd/src/PlateDraggable.styles.ts index 416cb60b45..93a217cb79 100644 --- a/packages/ui/dnd/src/components/Draggable.styles.ts +++ b/packages/ui/dnd/src/PlateDraggable.styles.ts @@ -1,12 +1,17 @@ -import { Value } from '@udecode/plate-core'; +import { DropLineDirection } from '@udecode/plate-dnd'; import { createStyles } from '@udecode/plate-styled-components'; import { css } from 'styled-components'; import tw from 'twin.macro'; -import { DraggableStyleProps } from './Draggable.types'; +import { PlateDraggableProps } from './PlateDraggable'; -export const getDraggableStyles = ( - props: DraggableStyleProps -) => +export interface DraggableStyleProps extends PlateDraggableProps { + direction: DropLineDirection; + isDragging: boolean; + + selected?: boolean; +} + +export const getDraggableStyles = (props: DraggableStyleProps) => createStyles( { prefixClassNames: 'Draggable', ...props }, { diff --git a/packages/ui/dnd/src/PlateDraggable.tsx b/packages/ui/dnd/src/PlateDraggable.tsx new file mode 100644 index 0000000000..c635494c8f --- /dev/null +++ b/packages/ui/dnd/src/PlateDraggable.tsx @@ -0,0 +1,90 @@ +import React, { forwardRef } from 'react'; +import { EElement, Value } from '@udecode/plate-core'; +import { + DraggableBlock, + DraggableBlockToolbar, + DraggableBlockToolbarWrapper, + DraggableDropline, + DraggableGutterLeft, + DraggableRoot, + DragHandle as DefaultDragHandle, + useDraggableState, +} from '@udecode/plate-dnd'; +import { StyledElementProps } from '@udecode/plate-styled-components'; +import { getDraggableStyles } from './PlateDraggable.styles'; +import { DraggableStyles, DragHandleProps } from './PlateDraggable.types'; + +export interface PlateDraggableProps + extends StyledElementProps, DraggableStyles> { + /** + * An override to render the drag handle. + */ + onRenderDragHandle?: (props: DragHandleProps) => JSX.Element; +} + +export const PlateDraggable = forwardRef( + (props, ref) => { + const { children, element, onRenderDragHandle } = props; + + const DragHandle = onRenderDragHandle ?? DefaultDragHandle; + + const { dropLine, isDragging, rootRef, dragRef } = useDraggableState(props); + + const styles = getDraggableStyles({ + ...props, + direction: dropLine, + isDragging, + }); + + return ( + + + + + + + + + + + {children} + + {!!dropLine && ( + + )} + + + ); + } +); diff --git a/packages/ui/dnd/src/PlateDraggable.types.ts b/packages/ui/dnd/src/PlateDraggable.types.ts new file mode 100644 index 0000000000..d6b7be9b45 --- /dev/null +++ b/packages/ui/dnd/src/PlateDraggable.types.ts @@ -0,0 +1,56 @@ +import React from 'react'; +import { TElement } from '@udecode/plate-core'; +import { CSSProp } from 'styled-components'; + +export interface DraggableStyles { + /** + * Block and gutter. + */ + blockAndGutter: CSSProp; + + /** + * Block. + */ + block: CSSProp; + + /** + * Gutter at the left side of the editor. + * It has the height of the block + */ + gutterLeft: CSSProp; + + /** + * Block toolbar wrapper in the gutter left. + * It has the height of a line of the block. + */ + blockToolbarWrapper: CSSProp; + + /** + * Block toolbar in the gutter. + */ + blockToolbar: CSSProp; + + /** + * Button to dnd the block, in the block toolbar. + */ + dragHandle: CSSProp; + + /** + * Icon of the drag button, in the drag icon. + */ + dragIcon: CSSProp; + + /** + * Show a dropline above or below the block when dragging a block. + */ + dropLine: CSSProp; +} + +export interface DragHandleProps + extends React.DetailedHTMLProps< + React.ButtonHTMLAttributes, + HTMLButtonElement + > { + element: TElement; + styles?: CSSProp; +} diff --git a/packages/ui/dnd/src/components/Draggable.tsx b/packages/ui/dnd/src/components/Draggable.tsx deleted file mode 100644 index f77123db3a..0000000000 --- a/packages/ui/dnd/src/components/Draggable.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useRef } from 'react'; -import useMergedRef from '@react-hook/merged-ref'; -import { Value } from '@udecode/plate-core'; -import { useDndBlock } from '../hooks/useDndBlock'; -import { getDraggableStyles } from './Draggable.styles'; -import { DraggableProps, DragHandleProps } from './Draggable.types'; - -const DefaultDragHandle = ({ styles, ...props }: DragHandleProps) => ( -