mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-09 02:49:18 +08:00
demo: add List drag sorting examples (#55077)
* docs: List drag sorting examples * refactor: standardize React imports and use consistent import patterns * fix: add null checks and remove optional chaining in drag sorting handlers * Update components/list/demo/grid-drag-sorting.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: FatahChan <ahmadfathallah89@gmail.com> * Update components/list/demo/grid-drag-sorting-handler.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: FatahChan <ahmadfathallah89@gmail.com> * chore: add React import to list demo components * fix: tests * refactor: improve drag sorting accessibility by wrapping list items in semantic containers * Update components/list/demo/drag-sorting-handler.tsx Co-authored-by: lijianan <574980606@qq.com> Signed-off-by: FatahChan <ahmadfathallah89@gmail.com> * Update components/list/demo/grid-drag-sorting.tsx Co-authored-by: lijianan <574980606@qq.com> Signed-off-by: FatahChan <ahmadfathallah89@gmail.com> * Update components/list/demo/drag-sorting.tsx Co-authored-by: lijianan <574980606@qq.com> Signed-off-by: FatahChan <ahmadfathallah89@gmail.com> * Update components/list/demo/grid-drag-sorting-handler.tsx Co-authored-by: lijianan <574980606@qq.com> Signed-off-by: FatahChan <ahmadfathallah89@gmail.com> * fix: remove itemkey prop from list items in drag-sorting demos * test: skip drag sorting demos in list component tests * fix: update drag-sorting aria-describedby IDs to be more semantic and consistent * test: update DndLiveRegion IDs in list component snapshots * test: skip drag sorting demos in list component tests dnd ids produce difference ids in react 17 vs react 19, and fails the demo-extend as the snap is different * refactor: update list drag sorting to use object items with unique keys * update code style * update code style --------- Signed-off-by: FatahChan <ahmadfathallah89@gmail.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: lijianan <574980606@qq.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,11 @@
|
||||
import { extendTest } from '../../../tests/shared/demoTest';
|
||||
|
||||
extendTest('list', { skip: ['virtual-list.tsx'] });
|
||||
extendTest('list', {
|
||||
skip: [
|
||||
'virtual-list.tsx',
|
||||
'grid-drag-sorting.tsx',
|
||||
'grid-drag-sorting-handler.tsx',
|
||||
'drag-sorting.tsx',
|
||||
'drag-sorting-handler.tsx',
|
||||
],
|
||||
});
|
||||
|
||||
7
components/list/demo/drag-sorting-handler.md
Normal file
7
components/list/demo/drag-sorting-handler.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
使用 [dnd-kit](https://github.com/clauderic/dnd-kit) 来实现一个拖拽操作列。
|
||||
|
||||
## en-US
|
||||
|
||||
Alternatively you can implement drag sorting with handler using [dnd-kit](https://github.com/clauderic/dnd-kit).
|
||||
113
components/list/demo/drag-sorting-handler.tsx
Normal file
113
components/list/demo/drag-sorting-handler.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||
import { HolderOutlined } from '@ant-design/icons';
|
||||
import type { DragEndEvent, DraggableAttributes } from '@dnd-kit/core';
|
||||
import { DndContext } from '@dnd-kit/core';
|
||||
import type { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities';
|
||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
useSortable,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { Button, GetProps, List } from 'antd';
|
||||
|
||||
interface SortableListItemContextProps {
|
||||
setActivatorNodeRef?: (element: HTMLElement | null) => void;
|
||||
listeners?: SyntheticListenerMap;
|
||||
attributes?: DraggableAttributes;
|
||||
}
|
||||
|
||||
const SortableListItemContext = createContext<SortableListItemContextProps>({});
|
||||
|
||||
const DragHandle: React.FC = () => {
|
||||
const { setActivatorNodeRef, listeners, attributes } = useContext(SortableListItemContext);
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<HolderOutlined />}
|
||||
style={{ cursor: 'move' }}
|
||||
ref={setActivatorNodeRef}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const SortableListItem: React.FC<GetProps<typeof List.Item> & { itemKey: number }> = (props) => {
|
||||
const { itemKey, style, ...rest } = props;
|
||||
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
setActivatorNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({ id: itemKey });
|
||||
|
||||
const listStyle: React.CSSProperties = {
|
||||
...style,
|
||||
transform: CSS.Translate.toString(transform),
|
||||
transition,
|
||||
...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
|
||||
};
|
||||
|
||||
const memoizedValue = useMemo<SortableListItemContextProps>(
|
||||
() => ({ setActivatorNodeRef, listeners, attributes }),
|
||||
[setActivatorNodeRef, listeners, attributes],
|
||||
);
|
||||
|
||||
return (
|
||||
<SortableListItemContext.Provider value={memoizedValue}>
|
||||
<List.Item {...rest} ref={setNodeRef} style={listStyle} />
|
||||
</SortableListItemContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [data, setData] = useState([
|
||||
{ key: 1, content: 'Racing car sprays burning fuel into crowd.' },
|
||||
{ key: 2, content: 'Japanese princess to wed commoner.' },
|
||||
{ key: 3, content: 'Australian walks 100km after outback crash.' },
|
||||
{ key: 4, content: 'Man charged over missing wedding girl.' },
|
||||
{ key: 5, content: 'Los Angeles battles huge wildfires.' },
|
||||
]);
|
||||
|
||||
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||||
if (!active || !over) {
|
||||
return;
|
||||
}
|
||||
if (active.id !== over.id) {
|
||||
setData((prevState) => {
|
||||
const activeIndex = prevState.findIndex((i) => i.key === active.id);
|
||||
const overIndex = prevState.findIndex((i) => i.key === over.id);
|
||||
return arrayMove(prevState, activeIndex, overIndex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
modifiers={[restrictToVerticalAxis]}
|
||||
onDragEnd={onDragEnd}
|
||||
id="list-drag-sorting-handler"
|
||||
>
|
||||
<SortableContext items={data.map((item) => item.key)} strategy={verticalListSortingStrategy}>
|
||||
<List
|
||||
dataSource={data}
|
||||
renderItem={(item) => (
|
||||
<SortableListItem key={item.key} itemKey={item.key}>
|
||||
<DragHandle /> {item.key} {item.content}
|
||||
</SortableListItem>
|
||||
)}
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
7
components/list/demo/drag-sorting.md
Normal file
7
components/list/demo/drag-sorting.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
使用自定义元素,我们可以集成 [dnd-kit](https://github.com/clauderic/dnd-kit) 来实现拖拽排序。
|
||||
|
||||
## en-US
|
||||
|
||||
By using `components`, we can integrate List with [dnd-kit](https://github.com/clauderic/dnd-kit) to implement drag sorting function.
|
||||
91
components/list/demo/drag-sorting.tsx
Normal file
91
components/list/demo/drag-sorting.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import React, { useState } from 'react';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import { DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
useSortable,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { List } from 'antd';
|
||||
import type { GetProps } from 'antd';
|
||||
|
||||
const SortableListItem: React.FC<GetProps<typeof List.Item> & { itemKey: number }> = (props) => {
|
||||
const { itemKey, style, children, ...rest } = props;
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: itemKey,
|
||||
});
|
||||
|
||||
const listStyle: React.CSSProperties = {
|
||||
...style,
|
||||
transform: CSS.Translate.toString(transform),
|
||||
transition,
|
||||
cursor: 'move',
|
||||
...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<List.Item {...rest} ref={setNodeRef} style={listStyle}>
|
||||
<div {...attributes} {...listeners}>
|
||||
{children}
|
||||
</div>
|
||||
</List.Item>
|
||||
);
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [data, setData] = useState([
|
||||
{ key: 1, content: 'Racing car sprays burning fuel into crowd.' },
|
||||
{ key: 2, content: 'Japanese princess to wed commoner.' },
|
||||
{ key: 3, content: 'Australian walks 100km after outback crash.' },
|
||||
{ key: 4, content: 'Man charged over missing wedding girl.' },
|
||||
{ key: 5, content: 'Los Angeles battles huge wildfires.' },
|
||||
]);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: {
|
||||
// https://docs.dndkit.com/api-documentation/sensors/pointer#activation-constraints
|
||||
distance: 1,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||||
if (!active || !over) {
|
||||
return;
|
||||
}
|
||||
if (active.id !== over.id) {
|
||||
setData((prev) => {
|
||||
const activeIndex = prev.findIndex((i) => i.key === active.id);
|
||||
const overIndex = prev.findIndex((i) => i.key === over.id);
|
||||
return arrayMove(prev, activeIndex, overIndex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
modifiers={[restrictToVerticalAxis]}
|
||||
onDragEnd={onDragEnd}
|
||||
id="list-drag-sorting"
|
||||
>
|
||||
<SortableContext items={data.map((item) => item.key)} strategy={verticalListSortingStrategy}>
|
||||
<List
|
||||
dataSource={data}
|
||||
renderItem={(item) => (
|
||||
<SortableListItem key={item.key} itemKey={item.key}>
|
||||
{item.key} {item.content}
|
||||
</SortableListItem>
|
||||
)}
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
7
components/list/demo/grid-drag-sorting-handler.md
Normal file
7
components/list/demo/grid-drag-sorting-handler.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
使用自定义元素和拖拽手柄,我们可以集成 [dnd-kit](https://github.com/clauderic/dnd-kit) 来实现网格布局的拖拽排序。
|
||||
|
||||
## en-US
|
||||
|
||||
By using custom components and drag handles, we can integrate List with [dnd-kit](https://github.com/clauderic/dnd-kit) to implement drag sorting function for grid layout.
|
||||
114
components/list/demo/grid-drag-sorting-handler.tsx
Normal file
114
components/list/demo/grid-drag-sorting-handler.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||
import { HolderOutlined } from '@ant-design/icons';
|
||||
import type { DragEndEvent, DraggableAttributes } from '@dnd-kit/core';
|
||||
import { DndContext } from '@dnd-kit/core';
|
||||
import type { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities';
|
||||
import { arrayMove, SortableContext, useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { Button, Card, GetProps, List } from 'antd';
|
||||
|
||||
interface SortableListItemContextProps {
|
||||
setActivatorNodeRef?: (element: HTMLElement | null) => void;
|
||||
listeners?: SyntheticListenerMap;
|
||||
attributes?: DraggableAttributes;
|
||||
}
|
||||
|
||||
const SortableListItemContext = createContext<SortableListItemContextProps>({});
|
||||
|
||||
const DragHandle: React.FC = () => {
|
||||
const { setActivatorNodeRef, listeners, attributes } = useContext(SortableListItemContext);
|
||||
return (
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={<HolderOutlined />}
|
||||
style={{ cursor: 'move' }}
|
||||
ref={setActivatorNodeRef}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const SortableListItem: React.FC<GetProps<typeof List.Item> & { itemKey: number }> = (props) => {
|
||||
const { itemKey, style, ...rest } = props;
|
||||
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
setActivatorNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({ id: itemKey });
|
||||
|
||||
const listStyle: React.CSSProperties = {
|
||||
...style,
|
||||
transform: CSS.Translate.toString(transform),
|
||||
transition,
|
||||
...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
|
||||
};
|
||||
|
||||
const memoizedValue = useMemo<SortableListItemContextProps>(
|
||||
() => ({ setActivatorNodeRef, listeners, attributes }),
|
||||
[setActivatorNodeRef, listeners, attributes],
|
||||
);
|
||||
|
||||
return (
|
||||
<SortableListItemContext.Provider value={memoizedValue}>
|
||||
<List.Item {...rest} ref={setNodeRef} style={listStyle} />
|
||||
</SortableListItemContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [data, setData] = useState([
|
||||
{ key: 1, title: 'Title 1' },
|
||||
{ key: 2, title: 'Title 2' },
|
||||
{ key: 3, title: 'Title 3' },
|
||||
{ key: 4, title: 'Title 4' },
|
||||
{ key: 5, title: 'Title 5' },
|
||||
{ key: 6, title: 'Title 6' },
|
||||
]);
|
||||
|
||||
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||||
if (!active || !over) {
|
||||
return;
|
||||
}
|
||||
if (active.id !== over.id) {
|
||||
setData((prevState) => {
|
||||
const activeIndex = prevState.findIndex((i) => i.key === active.id);
|
||||
const overIndex = prevState.findIndex((i) => i.key === over.id);
|
||||
return arrayMove(prevState, activeIndex, overIndex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext onDragEnd={onDragEnd} id="list-grid-drag-sorting-handler">
|
||||
<SortableContext items={data.map((i) => i.key)}>
|
||||
<List
|
||||
grid={{ gutter: 16, column: 4 }}
|
||||
dataSource={data}
|
||||
renderItem={(item) => (
|
||||
<SortableListItem key={item.key} itemKey={item.key}>
|
||||
<Card
|
||||
title={
|
||||
<>
|
||||
<DragHandle />
|
||||
{item.title}
|
||||
</>
|
||||
}
|
||||
>
|
||||
Card content
|
||||
</Card>
|
||||
</SortableListItem>
|
||||
)}
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
7
components/list/demo/grid-drag-sorting.md
Normal file
7
components/list/demo/grid-drag-sorting.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
使用自定义元素,我们可以集成 [dnd-kit](https://github.com/clauderic/dnd-kit) 来实现网格布局的拖拽排序。
|
||||
|
||||
## en-US
|
||||
|
||||
By using custom components, we can integrate List with [dnd-kit](https://github.com/clauderic/dnd-kit) to implement drag sorting function for grid layout.
|
||||
76
components/list/demo/grid-drag-sorting.tsx
Normal file
76
components/list/demo/grid-drag-sorting.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import React, { useState } from 'react';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import { DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
||||
import { arrayMove, SortableContext, useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { Card, List } from 'antd';
|
||||
import type { GetProps } from 'antd';
|
||||
|
||||
const SortableListItem: React.FC<GetProps<typeof List.Item> & { itemKey: number }> = (props) => {
|
||||
const { itemKey, style, ...rest } = props;
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: itemKey,
|
||||
});
|
||||
|
||||
const listStyle: React.CSSProperties = {
|
||||
...style,
|
||||
transform: CSS.Translate.toString(transform),
|
||||
transition,
|
||||
cursor: 'move',
|
||||
...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
|
||||
};
|
||||
|
||||
return <List.Item {...rest} ref={setNodeRef} style={listStyle} {...attributes} {...listeners} />;
|
||||
};
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [data, setData] = useState([
|
||||
{ key: 1, title: 'Title 1' },
|
||||
{ key: 2, title: 'Title 2' },
|
||||
{ key: 3, title: 'Title 3' },
|
||||
{ key: 4, title: 'Title 4' },
|
||||
{ key: 5, title: 'Title 5' },
|
||||
{ key: 6, title: 'Title 6' },
|
||||
]);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: {
|
||||
// https://docs.dndkit.com/api-documentation/sensors/pointer#activation-constraints
|
||||
distance: 1,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||||
if (!active || !over) {
|
||||
return;
|
||||
}
|
||||
if (active.id !== over.id) {
|
||||
setData((prev) => {
|
||||
const activeIndex = prev.findIndex((i) => i.key === active.id);
|
||||
const overIndex = prev.findIndex((i) => i.key === over.id);
|
||||
return arrayMove(prev, activeIndex, overIndex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext sensors={sensors} onDragEnd={onDragEnd} id="list-grid-drag-sorting">
|
||||
<SortableContext items={data.map((item) => item.key)}>
|
||||
<List
|
||||
grid={{ gutter: 16, column: 4 }}
|
||||
dataSource={data}
|
||||
renderItem={(item) => (
|
||||
<SortableListItem key={item.key} itemKey={item.key}>
|
||||
<Card title={item.title}>Card content</Card>
|
||||
</SortableListItem>
|
||||
)}
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -23,6 +23,10 @@ A list can be used to display content related to a single subject. The content c
|
||||
<code src="./demo/grid-test.tsx" debug>Test Grid</code>
|
||||
<code src="./demo/responsive.tsx">Responsive grid list</code>
|
||||
<code src="./demo/infinite-load.tsx">Scrolling loaded</code>
|
||||
<code src="./demo/drag-sorting.tsx">Drag sorting</code>
|
||||
<code src="./demo/drag-sorting-handler.tsx">Drag sorting with handler</code>
|
||||
<code src="./demo/grid-drag-sorting.tsx">Grid Drag sorting</code>
|
||||
<code src="./demo/grid-drag-sorting-handler.tsx">Grid Drag sorting with handler</code>
|
||||
<code src="./demo/virtual-list.tsx">virtual list</code>
|
||||
<code src="./demo/component-token.tsx" debug>custom component token</code>
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*tBzwQ7raKX8AAA
|
||||
<code src="./demo/grid-test.tsx" debug>测试栅格列表</code>
|
||||
<code src="./demo/responsive.tsx">响应式的栅格列表</code>
|
||||
<code src="./demo/infinite-load.tsx">滚动加载</code>
|
||||
<code src="./demo/drag-sorting.tsx">拖拽排序</code>
|
||||
<code src="./demo/drag-sorting-handler.tsx">拖拽排序(拖拽手柄)</code>
|
||||
<code src="./demo/grid-drag-sorting.tsx">栅格拖拽排序</code>
|
||||
<code src="./demo/grid-drag-sorting-handler.tsx">栅格拖拽排序(拖拽手柄)</code>
|
||||
<code src="./demo/virtual-list.tsx">滚动加载无限长列表</code>
|
||||
<code src="./demo/component-token.tsx" debug>自定义组件 token</code>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user