From a5a6203a955680f32310b03c7ac427b37bc91fc4 Mon Sep 17 00:00:00 2001 From: Qi <474021214@qq.com> Date: Fri, 21 Apr 2023 12:27:32 +0800 Subject: [PATCH] feat: replace react-dnd to dnd-kit (#2028) Co-authored-by: Himself65 --- .../components/__tests__/PinBoard.spec.tsx | 2 - .../src/components/affine/pinboard/styles.ts | 11 +- .../workspace-slider-bar/shared-styles.ts | 10 +- packages/component/package.json | 2 - .../component/src/ui/tree-view/TreeNode.tsx | 168 ++++-------------- .../src/ui/tree-view/TreeNodeInner.tsx | 92 ++++++++++ .../component/src/ui/tree-view/TreeView.tsx | 125 ++++++------- .../src/ui/tree-view/hooks/useCollapsed.ts | 32 ++++ .../tree-view/hooks/useSelectWithKeyboard.ts | 63 +++++++ packages/component/src/ui/tree-view/styles.ts | 38 ++-- packages/component/src/ui/tree-view/types.ts | 10 +- packages/component/src/ui/tree-view/utils.ts | 20 ++- yarn.lock | 13 +- 13 files changed, 334 insertions(+), 252 deletions(-) create mode 100644 packages/component/src/ui/tree-view/TreeNodeInner.tsx create mode 100644 packages/component/src/ui/tree-view/hooks/useCollapsed.ts create mode 100644 packages/component/src/ui/tree-view/hooks/useSelectWithKeyboard.ts diff --git a/apps/web/src/components/__tests__/PinBoard.spec.tsx b/apps/web/src/components/__tests__/PinBoard.spec.tsx index 9fec085e9c..0021ef1625 100644 --- a/apps/web/src/components/__tests__/PinBoard.spec.tsx +++ b/apps/web/src/components/__tests__/PinBoard.spec.tsx @@ -4,7 +4,6 @@ import 'fake-indexeddb/auto'; import { rootCurrentWorkspaceIdAtom } from '@affine/workspace/atom'; -import type { PageMeta } from '@blocksuite/store'; import matchers from '@testing-library/jest-dom/matchers'; import type { RenderResult } from '@testing-library/react'; import { render, renderHook } from '@testing-library/react'; @@ -95,7 +94,6 @@ const initPinBoard = async () => { const app = render( {}} /> ); diff --git a/apps/web/src/components/affine/pinboard/styles.ts b/apps/web/src/components/affine/pinboard/styles.ts index 376b74661c..9448c60360 100644 --- a/apps/web/src/components/affine/pinboard/styles.ts +++ b/apps/web/src/components/affine/pinboard/styles.ts @@ -8,7 +8,7 @@ import { export const StyledCollapsedButton = styled('button')<{ collapse: boolean; show?: boolean; -}>(({ collapse, show = true, theme }) => { +}>(({ collapse, show = true }) => { return { width: '16px', height: '100%', @@ -43,7 +43,6 @@ export const StyledPinboard = styled('div')<{ disableCollapse, disable = false, active = false, - theme, isOver, textWrap = false, }) => { @@ -66,7 +65,7 @@ export const StyledPinboard = styled('div')<{ userSelect: 'none', ...(textWrap ? { - wordBreak: 'break-all', + wordBreak: 'break-word', whiteSpace: 'pre-wrap', } : {}), @@ -106,7 +105,7 @@ export const StyledOperationButton = styled(IconButton, { }; }); -export const StyledSearchContainer = styled('div')(({ theme }) => { +export const StyledSearchContainer = styled('div')(() => { return { width: 'calc(100% - 24px)', margin: '0 auto', @@ -125,7 +124,7 @@ export const StyledMenuContent = styled('div')(() => { overflow: 'auto', }; }); -export const StyledMenuSubTitle = styled('div')(({ theme }) => { +export const StyledMenuSubTitle = styled('div')(() => { return { color: 'var(--affine-text-secondary-color)', lineHeight: '36px', @@ -133,7 +132,7 @@ export const StyledMenuSubTitle = styled('div')(({ theme }) => { }; }); -export const StyledMenuFooter = styled('div')(({ theme }) => { +export const StyledMenuFooter = styled('div')(() => { return { width: 'calc(100% - 24px)', margin: '0 auto', diff --git a/apps/web/src/components/pure/workspace-slider-bar/shared-styles.ts b/apps/web/src/components/pure/workspace-slider-bar/shared-styles.ts index 10cda75037..afaa6695e8 100644 --- a/apps/web/src/components/pure/workspace-slider-bar/shared-styles.ts +++ b/apps/web/src/components/pure/workspace-slider-bar/shared-styles.ts @@ -9,7 +9,7 @@ import { export const StyledListItem = styled('div')<{ active?: boolean; disabled?: boolean; -}>(({ theme, active, disabled }) => { +}>(({ active, disabled }) => { return { height: '32px', color: active @@ -46,7 +46,7 @@ export const StyledListItem = styled('div')<{ export const StyledCollapseButton = styled('button')<{ collapse: boolean; show?: boolean; -}>(({ collapse, show = true, theme }) => { +}>(({ collapse, show = true }) => { return { width: '16px', height: '100%', @@ -75,7 +75,7 @@ export const StyledCollapseItem = styled('div')<{ active?: boolean; isOver?: boolean; textWrap?: boolean; -}>(({ disable = false, active = false, theme, isOver, textWrap = false }) => { +}>(({ disable = false, active = false, isOver, textWrap = false }) => { return { width: '100%', lineHeight: '1.5', @@ -94,7 +94,7 @@ export const StyledCollapseItem = styled('div')<{ userSelect: 'none', ...(textWrap ? { - wordBreak: 'break-all', + wordBreak: 'break-word', whiteSpace: 'pre-wrap', } : {}), @@ -174,7 +174,7 @@ const slideOut2 = keyframes({ export const StyledChangeLog = styled('div')<{ isClose?: boolean; -}>(({ theme, isClose }) => { +}>(({ isClose }) => { return { width: '110%', height: '32px', diff --git a/packages/component/package.json b/packages/component/package.json index de6471fcf8..1a4b9b9e59 100644 --- a/packages/component/package.json +++ b/packages/component/package.json @@ -41,8 +41,6 @@ "lit": "^2.7.2", "lottie-web": "^5.11.0", "react": "^18.2.0", - "react-dnd": "^16.0.1", - "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.3", "react-is": "^18.2.0" diff --git a/packages/component/src/ui/tree-view/TreeNode.tsx b/packages/component/src/ui/tree-view/TreeNode.tsx index f68be7bee7..26db3a2def 100644 --- a/packages/component/src/ui/tree-view/TreeNode.tsx +++ b/packages/component/src/ui/tree-view/TreeNode.tsx @@ -1,147 +1,43 @@ -import { useEffect } from 'react'; -import { useDrag, useDrop } from 'react-dnd'; +import { useDraggable } from '@dnd-kit/core'; +import { useMemo } from 'react'; import { StyledCollapse, - StyledNodeLine, StyledTreeNodeContainer, StyledTreeNodeWrapper, } from './styles'; -import type { - Node, - NodeLIneProps, - TreeNodeItemProps, - TreeNodeProps, -} from './types'; - -const NodeLine = ({ - node, - onDrop, - allowDrop = true, - isTop = false, -}: NodeLIneProps) => { - const [{ isOver }, drop] = useDrop( - () => ({ - accept: 'node', - drop: (item: Node, monitor) => { - const didDrop = monitor.didDrop(); - if (didDrop) { - return; - } - onDrop?.(item.id, node.id, { - internal: false, - topLine: isTop, - bottomLine: !isTop, - }); - }, - collect: monitor => ({ - isOver: monitor.isOver(), - canDrop: monitor.canDrop(), - }), - }), - [onDrop] - ); - - return ; -}; -const TreeNodeItemWithDnd = ({ - node, - allowDrop, - setCollapsed, - ...otherProps -}: TreeNodeItemProps) => { - const { onAdd, onDelete, onDrop } = otherProps; - - const [{ canDrop, isOver }, drop] = useDrop( - () => ({ - accept: 'node', - drop: (item: Node, monitor) => { - const didDrop = monitor.didDrop(); - if (didDrop || item.id === node.id || !allowDrop) { - return; - } - onDrop?.(item.id, node.id, { - internal: true, - topLine: false, - bottomLine: false, - }); - }, - collect: monitor => ({ - isOver: monitor.isOver(), - canDrop: monitor.canDrop() && allowDrop, - }), - }), - [onDrop, allowDrop] - ); - - useEffect(() => { - if (isOver && canDrop) { - setCollapsed(node.id, false); - } - }, [isOver, canDrop, setCollapsed, node.id]); - - return ( - - ); -}; - -const TreeNodeItem = ({ - node, - collapsed, - setCollapsed, - selectedId, - isOver = false, - canDrop = false, - onAdd, - onDelete, - dropRef, - disableCollapse, -}: TreeNodeItemProps) => { - return ( -
- {node.render?.(node, { - isOver: isOver && canDrop, - onAdd: () => onAdd?.(node.id), - onDelete: () => onDelete?.(node.id), - collapsed, - setCollapsed, - isSelected: selectedId === node.id, - disableCollapse, - })} -
- ); -}; - +import { NodeLine, TreeNodeItem, TreeNodeItemWithDnd } from './TreeNodeInner'; +import type { TreeNodeProps } from './types'; export const TreeNodeWithDnd = ( props: TreeNodeProps ) => { - const [{ isDragging }, drag] = useDrag(() => ({ - type: 'node', - item: props.node, - collect: monitor => ({ - isDragging: monitor.isDragging(), - }), - })); - - return ; + const { draggingId, node, allowDrop } = props; + const { attributes, listeners, setNodeRef } = useDraggable({ + id: props.node.id, + }); + const isDragging = useMemo( + () => draggingId === node.id, + [draggingId, node.id] + ); + return ( + + + + ); }; export const TreeNode = ({ node, index, - isDragging = false, allowDrop = true, - dragRef, ...otherProps }: TreeNodeProps) => { const { indent, enableDnd, collapsedIds } = otherProps; @@ -149,13 +45,13 @@ export const TreeNode = ({ const { renderTopLine = true, renderBottomLine = true } = node; return ( - + <> {enableDnd && renderTopLine && index === 0 && ( )} @@ -180,11 +76,7 @@ export const TreeNode = ({ {enableDnd && renderBottomLine && (!node.children?.length || collapsed) && ( - + )} @@ -195,8 +87,8 @@ export const TreeNode = ({ key={childNode.id} node={childNode} index={index} - allowDrop={isDragging ? false : allowDrop} {...otherProps} + allowDrop={allowDrop} /> ) : ( ({ ) )} - + ); }; diff --git a/packages/component/src/ui/tree-view/TreeNodeInner.tsx b/packages/component/src/ui/tree-view/TreeNodeInner.tsx new file mode 100644 index 0000000000..e3797956ec --- /dev/null +++ b/packages/component/src/ui/tree-view/TreeNodeInner.tsx @@ -0,0 +1,92 @@ +import { useDroppable } from '@dnd-kit/core'; + +import { StyledNodeLine } from './styles'; +import type { NodeLIneProps, TreeNodeItemProps } from './types'; + +export const NodeLine = ({ + node, + allowDrop = true, + isTop = false, +}: NodeLIneProps) => { + const { isOver, setNodeRef } = useDroppable({ + id: `${node.id}-${isTop ? 'top' : 'bottom'}-line`, + disabled: !allowDrop, + data: { + node, + position: { + topLine: isTop, + bottomLine: !isTop, + internal: false, + }, + }, + }); + + return ( + + ); +}; +export const TreeNodeItemWithDnd = ({ + node, + allowDrop, + setCollapsed, + ...otherProps +}: TreeNodeItemProps) => { + const { onAdd, onDelete } = otherProps; + + const { isOver, setNodeRef } = useDroppable({ + id: node.id, + disabled: !allowDrop, + data: { + node, + position: { + topLine: false, + bottomLine: false, + internal: true, + }, + }, + }); + + return ( +
+ +
+ ); +}; + +export const TreeNodeItem = ({ + node, + collapsed, + setCollapsed, + selectedId, + isOver = false, + onAdd, + onDelete, + disableCollapse, + allowDrop = true, +}: TreeNodeItemProps) => { + return ( + <> + {node.render?.(node, { + isOver: isOver && allowDrop, + onAdd: () => onAdd?.(node.id), + onDelete: () => onDelete?.(node.id), + collapsed, + setCollapsed, + isSelected: selectedId === node.id, + disableCollapse, + })} + + ); +}; diff --git a/packages/component/src/ui/tree-view/TreeView.tsx b/packages/component/src/ui/tree-view/TreeView.tsx index 4ca9ab1ea1..0a256a023d 100644 --- a/packages/component/src/ui/tree-view/TreeView.tsx +++ b/packages/component/src/ui/tree-view/TreeView.tsx @@ -1,11 +1,20 @@ -import { useEffect, useState } from 'react'; -import { DndProvider } from 'react-dnd'; -import { HTML5Backend } from 'react-dnd-html5-backend'; +import type { + DragEndEvent} from '@dnd-kit/core'; +import { + closestCenter, + DndContext, + DragOverlay, + PointerSensor, + useSensor, + useSensors +} from '@dnd-kit/core'; +import { useCallback, useState } from 'react'; +import useCollapsed from './hooks/useCollapsed'; +import useSelectWithKeyboard from './hooks/useSelectWithKeyboard'; import { TreeNode, TreeNodeWithDnd } from './TreeNode'; -import type { TreeNodeProps, TreeViewProps } from './types'; -import { flattenIds } from './utils'; - +import type { TreeViewProps } from './types'; +import { findNode } from './utils'; export const TreeView = ({ data, enableKeyboardSelection, @@ -13,69 +22,51 @@ export const TreeView = ({ enableDnd = true, initialCollapsedIds = [], disableCollapse, + onDrop, ...otherProps }: TreeViewProps) => { - const [selectedId, setSelectedId] = useState(); - // TODO: should record collapsedIds in localStorage - const [collapsedIds, setCollapsedIds] = - useState(initialCollapsedIds); + const sensors = useSensors( + useSensor(PointerSensor, { + activationConstraint: { + distance: 8, + }, + }) + ); + const { selectedId } = useSelectWithKeyboard({ + data, + onSelect, + enableKeyboardSelection, + }); - useEffect(() => { - if (!enableKeyboardSelection) { - return; - } + const { collapsedIds, setCollapsed } = useCollapsed({ disableCollapse }); - const flattenedIds = flattenIds(data); + const [draggingId, setDraggingId] = useState(); - const handleDirectionKeyDown = (e: KeyboardEvent) => { - if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') { + const onDragEnd = useCallback( + (e: DragEndEvent) => { + const { active, over } = e; + const position = over?.data.current?.position; + const dropId = over?.data.current?.node.id; + + if (!over || !active || !position) { return; } - if (selectedId === undefined) { - setSelectedId(flattenedIds[0]); - return; - } - let selectedIndex = flattenedIds.indexOf(selectedId); - if (e.key === 'ArrowDown') { - selectedIndex < flattenedIds.length - 1 && selectedIndex++; - } - if (e.key === 'ArrowUp') { - selectedIndex > 0 && selectedIndex--; - } - - setSelectedId(flattenedIds[selectedIndex]); - }; - - const handleEnterKeyDown = (e: KeyboardEvent) => { - if (e.key !== 'Enter') { - return; - } - selectedId && onSelect?.(selectedId); - }; - - document.addEventListener('keydown', handleDirectionKeyDown); - document.addEventListener('keydown', handleEnterKeyDown); - - return () => { - document.removeEventListener('keydown', handleDirectionKeyDown); - document.removeEventListener('keydown', handleEnterKeyDown); - }; - }, [data, enableKeyboardSelection, onSelect, selectedId]); - - const setCollapsed: TreeNodeProps['setCollapsed'] = (id, collapsed) => { - if (disableCollapse) { - return; - } - if (collapsed) { - setCollapsedIds(ids => [...ids, id]); - } else { - setCollapsedIds(ids => ids.filter(i => i !== id)); - } - }; + onDrop?.(active.id as string, dropId, position); + }, + [onDrop] + ); + const onDragMove = useCallback((e: DragEndEvent) => { + setDraggingId(e.active.id as string); + }, []); if (enableDnd) { return ( - + {data.map((node, index) => ( ({ selectedId={selectedId} enableDnd={enableDnd} disableCollapse={disableCollapse} + draggingId={draggingId} {...otherProps} /> ))} - + + + {draggingId && ( + {}} + {...otherProps} + /> + )} + + ); } diff --git a/packages/component/src/ui/tree-view/hooks/useCollapsed.ts b/packages/component/src/ui/tree-view/hooks/useCollapsed.ts new file mode 100644 index 0000000000..c61900686a --- /dev/null +++ b/packages/component/src/ui/tree-view/hooks/useCollapsed.ts @@ -0,0 +1,32 @@ +import { useState } from 'react'; + +import type { TreeNodeProps } from '../types'; +export const useCollapsed = ({ + initialCollapsedIds = [], + disableCollapse = false, +}: { + disableCollapse?: boolean; + initialCollapsedIds?: string[]; +}) => { + // TODO: should record collapsedIds in localStorage + const [collapsedIds, setCollapsedIds] = + useState(initialCollapsedIds); + + const setCollapsed: TreeNodeProps['setCollapsed'] = (id, collapsed) => { + if (disableCollapse) { + return; + } + if (collapsed) { + setCollapsedIds(ids => [...ids, id]); + } else { + setCollapsedIds(ids => ids.filter(i => i !== id)); + } + }; + + return { + collapsedIds, + setCollapsed, + }; +}; + +export default useCollapsed; diff --git a/packages/component/src/ui/tree-view/hooks/useSelectWithKeyboard.ts b/packages/component/src/ui/tree-view/hooks/useSelectWithKeyboard.ts new file mode 100644 index 0000000000..79eb78260a --- /dev/null +++ b/packages/component/src/ui/tree-view/hooks/useSelectWithKeyboard.ts @@ -0,0 +1,63 @@ +import { useEffect, useState } from 'react'; + +import type { TreeViewProps } from '../types'; +import { flattenIds } from '../utils'; +export const useSelectWithKeyboard = ({ + data, + enableKeyboardSelection, + onSelect, +}: Pick< + TreeViewProps, + 'data' | 'enableKeyboardSelection' | 'onSelect' +>) => { + const [selectedId, setSelectedId] = useState(); + // TODO: should record collapsedIds in localStorage + + useEffect(() => { + if (!enableKeyboardSelection) { + return; + } + + const flattenedIds = flattenIds(data); + + const handleDirectionKeyDown = (e: KeyboardEvent) => { + if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') { + return; + } + if (selectedId === undefined) { + setSelectedId(flattenedIds[0]); + return; + } + let selectedIndex = flattenedIds.indexOf(selectedId); + if (e.key === 'ArrowDown') { + selectedIndex < flattenedIds.length - 1 && selectedIndex++; + } + if (e.key === 'ArrowUp') { + selectedIndex > 0 && selectedIndex--; + } + + setSelectedId(flattenedIds[selectedIndex]); + }; + + const handleEnterKeyDown = (e: KeyboardEvent) => { + if (e.key !== 'Enter') { + return; + } + selectedId && onSelect?.(selectedId); + }; + + document.addEventListener('keydown', handleDirectionKeyDown); + document.addEventListener('keydown', handleEnterKeyDown); + + return () => { + document.removeEventListener('keydown', handleDirectionKeyDown); + document.removeEventListener('keydown', handleEnterKeyDown); + }; + }, [data, enableKeyboardSelection, onSelect, selectedId]); + + return { + selectedId, + }; +}; + +export default useSelectWithKeyboard; diff --git a/packages/component/src/ui/tree-view/styles.ts b/packages/component/src/ui/tree-view/styles.ts index 6bc821b90e..2bc08c3117 100644 --- a/packages/component/src/ui/tree-view/styles.ts +++ b/packages/component/src/ui/tree-view/styles.ts @@ -16,28 +16,28 @@ export const StyledTreeNodeWrapper = styled('div')(() => { }; }); export const StyledTreeNodeContainer = styled('div')<{ isDragging?: boolean }>( - ({ isDragging = false, theme }) => { + ({ isDragging = false }) => { return { background: isDragging ? 'var(--affine-hover-color)' : '', - // opacity: isDragging ? 0.4 : 1, }; } ); -export const StyledNodeLine = styled('div')<{ show: boolean; isTop?: boolean }>( - ({ show, isTop = false, theme }) => { - return { - position: 'absolute', - left: '0', - ...(isTop ? { top: '-1px' } : { bottom: '-1px' }), - width: '100%', - paddingTop: '2x', - borderTop: '2px solid', - borderColor: show ? 'var(--affine-primary-color)' : 'transparent', - boxShadow: show - ? `0px 0px 8px ${alpha(lightTheme.primaryColor, 0.35)}` - : 'none', - zIndex: 1, - }; - } -); +export const StyledNodeLine = styled('div')<{ + isOver: boolean; + isTop?: boolean; +}>(({ isOver, isTop = false }) => { + return { + position: 'absolute', + left: '0', + ...(isTop ? { top: '-1px' } : { bottom: '-1px' }), + width: '100%', + paddingTop: '2x', + borderTop: '2px solid', + borderColor: isOver ? 'var(--affine-primary-color)' : 'transparent', + boxShadow: isOver + ? `0px 0px 8px ${alpha(lightTheme.primaryColor, 0.35)}` + : 'none', + zIndex: 1, + }; +}); diff --git a/packages/component/src/ui/tree-view/types.ts b/packages/component/src/ui/tree-view/types.ts index 4801ecb39c..ebaf60722c 100644 --- a/packages/component/src/ui/tree-view/types.ts +++ b/packages/component/src/ui/tree-view/types.ts @@ -1,4 +1,4 @@ -import type { CSSProperties, ReactNode, Ref } from 'react'; +import type { CSSProperties, ReactNode } from 'react'; export type DropPosition = { topLine: boolean; @@ -50,8 +50,7 @@ export type TreeNodeProps = { setCollapsed: (id: string, collapsed: boolean) => void; allowDrop?: boolean; selectedId?: string; - isDragging?: boolean; - dragRef?: Ref; + draggingId?: string; } & CommonProps; export type TreeNodeItemProps = { @@ -59,9 +58,6 @@ export type TreeNodeItemProps = { setCollapsed: (id: string, collapsed: boolean) => void; isOver?: boolean; - canDrop?: boolean; - - dropRef?: Ref; } & TreeNodeProps; export type TreeViewProps = { @@ -73,4 +69,4 @@ export type TreeViewProps = { export type NodeLIneProps = { allowDrop: boolean; isTop?: boolean; -} & Pick, 'node' | 'onDrop'>; +} & Pick, 'node'>; diff --git a/packages/component/src/ui/tree-view/utils.ts b/packages/component/src/ui/tree-view/utils.ts index 869ad1aea3..a71a7773a7 100644 --- a/packages/component/src/ui/tree-view/utils.ts +++ b/packages/component/src/ui/tree-view/utils.ts @@ -1,4 +1,4 @@ -import type { Node } from '@affine/component'; +import type { Node } from './types'; export function flattenIds(arr: Node[]): string[] { const result: string[] = []; @@ -16,3 +16,21 @@ export function flattenIds(arr: Node[]): string[] { flatten(arr); return result; } + +export function findNode( + id: string, + nodes: Node[] +): Node | undefined { + for (let i = 0, len = nodes.length; i < len; i++) { + const node = nodes[i]; + if (node.id === id) { + return node; + } + if (node.children) { + const result = findNode(id, node.children); + if (result) { + return result; + } + } + } +} diff --git a/yarn.lock b/yarn.lock index 6653bfc2f3..46e0878300 100644 --- a/yarn.lock +++ b/yarn.lock @@ -80,8 +80,6 @@ __metadata: lit: ^2.7.2 lottie-web: ^5.11.0 react: ^18.2.0 - react-dnd: ^16.0.1 - react-dnd-html5-backend: ^16.0.1 react-dom: ^18.2.0 react-error-boundary: ^4.0.3 react-is: ^18.2.0 @@ -19643,16 +19641,7 @@ __metadata: languageName: node linkType: hard -"react-dnd-html5-backend@npm:^16.0.1": - version: 16.0.1 - resolution: "react-dnd-html5-backend@npm:16.0.1" - dependencies: - dnd-core: ^16.0.1 - checksum: e2368bf85d5632a5cd867b743feb54c9052d909ea5331608860fa455edf3c633ac791f5b338e3db29b19ea8670c0ba5fb43c9c1c2510760bea030811d726cdfa - languageName: node - linkType: hard - -"react-dnd@npm:*, react-dnd@npm:^16.0.1": +"react-dnd@npm:*": version: 16.0.1 resolution: "react-dnd@npm:16.0.1" dependencies: