diff --git a/packages/antd/package.json b/packages/antd/package.json index 9350845ce68..5c6c5d4b71c 100644 --- a/packages/antd/package.json +++ b/packages/antd/package.json @@ -68,7 +68,8 @@ "@formily/reactive-react": "2.2.26", "@formily/shared": "2.2.26", "classnames": "^2.2.6", - "react-sortable-hoc": "^1.11.0", + "@dnd-kit/core": "^6.0.0", + "@dnd-kit/sortable": "^7.0.0", "react-sticky-box": "^0.9.3" }, "publishConfig": { diff --git a/packages/antd/src/__builtins__/index.ts b/packages/antd/src/__builtins__/index.ts index bf25acb1147..8b367228ec4 100644 --- a/packages/antd/src/__builtins__/index.ts +++ b/packages/antd/src/__builtins__/index.ts @@ -3,3 +3,4 @@ export * from './hooks' export * from './portal' export * from './loading' export * from './pickDataProps' +export * from './sort' diff --git a/packages/antd/src/__builtins__/sort.tsx b/packages/antd/src/__builtins__/sort.tsx new file mode 100644 index 00000000000..a3dd1f21a1f --- /dev/null +++ b/packages/antd/src/__builtins__/sort.tsx @@ -0,0 +1,143 @@ +import { DndContext, DragEndEvent, DragStartEvent } from '@dnd-kit/core' +import { + SortableContext, + useSortable, + verticalListSortingStrategy, +} from '@dnd-kit/sortable' +import { ReactFC } from '@formily/reactive-react' +import React, { createContext, useContext, useMemo } from 'react' + +export interface ISortableContainerProps { + list: any[] + start?: number + accessibility?: { + container?: Element + } + onSortStart?: (event: DragStartEvent) => void + onSortEnd?: (event: { oldIndex: number; newIndex: number }) => void +} + +export function SortableContainer>( + Component: ReactFC +): ReactFC { + return ({ + list, + start = 0, + accessibility, + onSortStart, + onSortEnd, + ...props + }) => { + const _onSortEnd = (event: DragEndEvent) => { + const { active, over } = event + const oldIndex = (active.id as number) - 1 + const newIndex = (over?.id as number) - 1 + onSortEnd?.({ + oldIndex, + newIndex, + }) + } + + return ( + + index + start + 1)} + strategy={verticalListSortingStrategy} + > + {props.children} + + + ) + } +} + +export const useSortableItem = () => { + return useContext(SortableItemContext) +} + +export const SortableItemContext = createContext< + Partial> +>({}) + +export interface ISortableElementProps { + index?: number + lockAxis?: 'x' | 'y' +} + +export function SortableElement>( + Component: ReactFC +): ReactFC { + return ({ index = 0, lockAxis, ...props }) => { + const sortable = useSortable({ + id: index + 1, + }) + const { setNodeRef, transform, transition, isDragging } = sortable + if (transform) { + switch (lockAxis) { + case 'x': + transform.y = 0 + break + case 'y': + transform.x = 0 + break + default: + break + } + } + + const style = useMemo(() => { + const itemStyle: React.CSSProperties = { + position: 'relative', + touchAction: 'none', + zIndex: 1, + transform: `translate3d(${transform?.x || 0}px, ${ + transform?.y || 0 + }px, 0)`, + transition: `${transform ? 'all 200ms ease' : ''}`, + } + const dragStyle = { + transition, + opacity: '0.8', + transform: `translate3d(${transform?.x || 0}px, ${ + transform?.y || 0 + }px, 0)`, + } + + const computedStyle = isDragging + ? { + ...itemStyle, + ...dragStyle, + ...props.style, + } + : { + ...itemStyle, + ...props.style, + } + + return computedStyle + }, [isDragging, transform, transition, props.style]) + + return ( + + {Component({ + ...props, + style, + ref: setNodeRef, + } as unknown as T)} + + ) + } +} + +export function SortableHandle>( + Component: ReactFC +): ReactFC { + return (props: T) => { + const { attributes, listeners } = useSortableItem() + return + } +} diff --git a/packages/antd/src/array-base/index.tsx b/packages/antd/src/array-base/index.tsx index e12a189c844..55b872a24c3 100644 --- a/packages/antd/src/array-base/index.tsx +++ b/packages/antd/src/array-base/index.tsx @@ -12,8 +12,7 @@ import { ButtonProps } from 'antd/lib/button' import { ArrayField } from '@formily/core' import { useField, useFieldSchema, Schema, JSXComponent } from '@formily/react' import { isValid, clone, isUndef } from '@formily/shared' -import { SortableHandle } from 'react-sortable-hoc' -import { usePrefixCls } from '../__builtins__' +import { usePrefixCls, SortableHandle } from '../__builtins__' import cls from 'classnames' export interface IArrayBaseAdditionProps extends ButtonProps { diff --git a/packages/antd/src/array-items/index.tsx b/packages/antd/src/array-items/index.tsx index d501cd669e2..4c8444fb506 100644 --- a/packages/antd/src/array-items/index.tsx +++ b/packages/antd/src/array-items/index.tsx @@ -5,17 +5,14 @@ import { observer, useFieldSchema, RecursionField, - ReactFC, } from '@formily/react' import cls from 'classnames' +import { ISchema } from '@formily/json-schema' import { + usePrefixCls, SortableContainer, SortableElement, - SortableContainerProps, - SortableElementProps, -} from 'react-sortable-hoc' -import { ISchema } from '@formily/json-schema' -import { usePrefixCls } from '../__builtins__' +} from '../__builtins__' import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from '../array-base' type ComposedArrayItems = React.FC< @@ -31,9 +28,7 @@ type ComposedArrayItems = React.FC< > } -const SortableItem: ReactFC< - React.HTMLAttributes & SortableElementProps -> = SortableElement( +const SortableItem = SortableElement( (props: React.PropsWithChildren>) => { const prefixCls = usePrefixCls('formily-array-items') return ( @@ -42,11 +37,9 @@ const SortableItem: ReactFC< ) } -) as any +) -const SortableList: ReactFC< - React.HTMLAttributes & SortableContainerProps -> = SortableContainer( +const SortableList = SortableContainer( (props: React.PropsWithChildren>) => { const prefixCls = usePrefixCls('formily-array-items') return ( @@ -55,7 +48,7 @@ const SortableList: ReactFC< ) } -) as any +) const isAdditionComponent = (schema: ISchema) => { return schema['x-component']?.indexOf('Addition') > -1 @@ -95,12 +88,8 @@ export const ArrayItems: ComposedArrayItems = observer((props) => { className={cls(prefixCls, props.className)} > - ref.current?.querySelector(`.${prefixCls}-list`) - } + list={dataSource.slice()} + className={`${prefixCls}-sort-helper`} onSortEnd={({ oldIndex, newIndex }) => { field.move(oldIndex, newIndex) }} @@ -115,7 +104,7 @@ export const ArrayItems: ComposedArrayItems = observer((props) => { index={index} record={() => field.value?.[index]} > - +
diff --git a/packages/antd/src/array-table/index.tsx b/packages/antd/src/array-table/index.tsx index f464d2ab3cc..b4e66b0e754 100644 --- a/packages/antd/src/array-table/index.tsx +++ b/packages/antd/src/array-table/index.tsx @@ -12,7 +12,6 @@ import { PaginationProps } from 'antd/lib/pagination' import { TableProps, ColumnProps } from 'antd/lib/table' import { SelectProps } from 'antd/lib/select' import cls from 'classnames' -import { SortableContainer, SortableElement } from 'react-sortable-hoc' import { GeneralField, FieldDisplayTypes, ArrayField } from '@formily/core' import { useField, @@ -23,7 +22,11 @@ import { } from '@formily/react' import { isArr, isBool, isFn } from '@formily/shared' import { Schema } from '@formily/json-schema' -import { usePrefixCls } from '../__builtins__' +import { + usePrefixCls, + SortableContainer, + SortableElement, +} from '../__builtins__' import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from '../array-base' interface ObservableColumnSource { @@ -37,7 +40,10 @@ interface IArrayTablePaginationProps extends PaginationProps { dataSource?: any[] children?: ( dataSource: any[], - pagination: React.ReactNode + pagination: React.ReactNode, + options: { + startIndex: number + } ) => React.ReactElement } @@ -280,15 +286,25 @@ const ArrayTablePagination: ReactFC = (props) => { > {props.children?.( dataSource?.slice(startIndex, endIndex + 1), - renderPagination() + renderPagination(), + { startIndex } )} ) } -const RowComp = (props: any) => { - return +const RowComp: ReactFC> = (props) => { + const prefixCls = usePrefixCls('formily-array-table') + const index = props['data-row-key'] || 0 + return ( + + ) } export const ArrayTable: ComposedArrayTable = observer((props) => { @@ -304,42 +320,44 @@ export const ArrayTable: ComposedArrayTable = observer((props) => { const defaultRowKey = (record: any) => { return dataSource.indexOf(record) } - const addTdStyles = (node: HTMLElement) => { + const addTdStyles = (id: number) => { + const node = ref.current?.querySelector(`.${prefixCls}-row-${id}`) const helper = document.body.querySelector(`.${prefixCls}-sort-helper`) - if (helper) { - const tds = node.querySelectorAll('td') - requestAnimationFrame(() => { - helper.querySelectorAll('td').forEach((td, index) => { - if (tds[index]) { - td.style.width = getComputedStyle(tds[index]).width - } - }) + if (!helper) return + const tds = node?.querySelectorAll('td') + if (!tds) return + requestAnimationFrame(() => { + helper.querySelectorAll('td').forEach((td, index) => { + if (tds[index]) { + td.style.width = getComputedStyle(tds[index]).width + } }) - } + }) } - const WrapperComp = useCallback( - (props: any) => ( - { - return ref.current?.querySelector('tbody') - }} - onSortStart={({ node }) => { - addTdStyles(node as HTMLElement) - }} - onSortEnd={({ oldIndex, newIndex }) => { - field.move(oldIndex, newIndex) - }} - {...props} - /> - ), + const getWrapperComp = useCallback( + (dataSource: any[], start: number) => (props: any) => + ( + { + addTdStyles(event.active.id as number) + }} + onSortEnd={({ oldIndex, newIndex }) => { + field.move(oldIndex, newIndex) + }} + className={cls(`${prefixCls}-sort-helper`, props.className)} + /> + ), [field] ) return ( - {(dataSource, pager) => ( + {(dataSource, pager, { startIndex }) => (
{ dataSource={dataSource} components={{ body: { - wrapper: WrapperComp, + wrapper: getWrapperComp(dataSource, startIndex), row: RowComp, }, }} diff --git a/yarn.lock b/yarn.lock index d5df85c2b41..5544a1bc6a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1361,6 +1361,37 @@ resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz#75b4c27948c81e88ccd3a8902047bcd797f38d32" integrity sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw== +"@dnd-kit/accessibility@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz#3ccbefdfca595b0a23a5dc57d3de96bc6935641c" + integrity sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg== + dependencies: + tslib "^2.0.0" + +"@dnd-kit/core@^6.0.0": + version "6.0.8" + resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-6.0.8.tgz#040ae13fea9787ee078e5f0361f3b49b07f3f005" + integrity sha512-lYaoP8yHTQSLlZe6Rr9qogouGUz9oRUj4AHhDQGQzq/hqaJRpFo65X+JKsdHf8oUFBzx5A+SJPUvxAwTF2OabA== + dependencies: + "@dnd-kit/accessibility" "^3.0.0" + "@dnd-kit/utilities" "^3.2.1" + tslib "^2.0.0" + +"@dnd-kit/sortable@^7.0.0": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-7.0.2.tgz#791d550872457f3f3c843e00d159b640f982011c" + integrity sha512-wDkBHHf9iCi1veM834Gbk1429bd4lHX4RpAwT0y2cHLf246GAvU2sVw/oxWNpPKQNQRQaeGXhAVgrOl1IT+iyA== + dependencies: + "@dnd-kit/utilities" "^3.2.0" + tslib "^2.0.0" + +"@dnd-kit/utilities@^3.2.0", "@dnd-kit/utilities@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-3.2.1.tgz#53f9e2016fd2506ec49e404c289392cfff30332a" + integrity sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA== + dependencies: + tslib "^2.0.0" + "@emotion/cache@^10.0.27": version "10.0.29" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" @@ -19575,6 +19606,11 @@ tslib@^2, tslib@^2.0.3, tslib@^2.3.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@^2.0.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"