Skip to content

Commit e5d776c

Browse files
committed
feat: add orderlist and picklist headless docs
1 parent 4f5ae13 commit e5d776c

File tree

6 files changed

+451
-1
lines changed

6 files changed

+451
-1
lines changed

apps/showcase/assets/menu/submenu/menu-headless.data.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,18 @@ export const headlessMenu = [
9696
name: 'useDataView',
9797
href: '/docs/headless/dataview'
9898
},
99+
{
100+
name: 'useOrderList',
101+
href: '/docs/headless/orderlist'
102+
},
99103
{
100104
name: 'usePaginator',
101105
href: '/docs/headless/paginator'
102106
},
107+
{
108+
name: 'usePickList',
109+
href: '/docs/headless/picklist'
110+
},
103111
{
104112
name: 'useTimeline',
105113
href: '/docs/headless/timeline'
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use client';
2+
import { useOrderList } from '@primereact/headless/orderlist';
3+
import type { useOrderListReorderEvent } from '@primereact/types/shared/orderlist';
4+
import { useState } from 'react';
5+
6+
const cities = [
7+
{ name: 'New York', code: 'NY' },
8+
{ name: 'Rome', code: 'RM' },
9+
{ name: 'London', code: 'LDN' },
10+
{ name: 'Istanbul', code: 'IST' },
11+
{ name: 'Paris', code: 'PRS' }
12+
];
13+
14+
type City = (typeof cities)[0];
15+
16+
export default function BasicDemo() {
17+
const [items, setItems] = useState(cities);
18+
const [selection, setSelection] = useState<City[]>([]);
19+
20+
const orderList = useOrderList({
21+
value: items,
22+
selection,
23+
onReorder: (e: useOrderListReorderEvent) => setItems(e.value as typeof cities)
24+
});
25+
26+
const toggleSelection = (item: City) => {
27+
setSelection((prev) => (prev.includes(item) ? prev.filter((s) => s !== item) : [...prev, item]));
28+
};
29+
30+
const btnClassName = 'px-2 py-1 rounded bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700 text-sm disabled:opacity-50';
31+
const hasSelection = selection.length > 0;
32+
33+
return (
34+
<div className="flex justify-center">
35+
<div className="flex gap-2 w-full max-w-xs">
36+
<div {...orderList.controlsProps} className="flex flex-col gap-1">
37+
<button onClick={orderList.firstProps.onClick as React.MouseEventHandler} disabled={!hasSelection || undefined} className={btnClassName}>
38+
39+
</button>
40+
<button onClick={orderList.prevProps.onClick as React.MouseEventHandler} disabled={!hasSelection || undefined} className={btnClassName}>
41+
42+
</button>
43+
<button onClick={orderList.nextProps.onClick as React.MouseEventHandler} disabled={!hasSelection || undefined} className={btnClassName}>
44+
45+
</button>
46+
<button onClick={orderList.lastProps.onClick as React.MouseEventHandler} disabled={!hasSelection || undefined} className={btnClassName}>
47+
48+
</button>
49+
</div>
50+
<div {...orderList.listProps} className="flex-1 border border-surface-200 dark:border-surface-700 rounded-lg overflow-hidden">
51+
{(orderList.state.value as typeof cities).map((item, i) => (
52+
<div
53+
key={item.code}
54+
{...orderList.getOptionProps(item, i)}
55+
onClick={() => toggleSelection(item)}
56+
className={`px-3 py-2 cursor-pointer select-none text-sm border-b border-surface-100 dark:border-surface-800 last:border-b-0 ${selection.includes(item) ? 'bg-primary/10 text-primary' : 'hover:bg-surface-50 dark:hover:bg-surface-900'}`}
57+
>
58+
{item.name}
59+
</div>
60+
))}
61+
</div>
62+
</div>
63+
</div>
64+
);
65+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
'use client';
2+
import { usePickList } from '@primereact/headless/picklist';
3+
import type { usePickListReorderEvent, usePickListSelectionChangeEvent } from '@primereact/types/shared/picklist';
4+
import { useState } from 'react';
5+
6+
const cities = [
7+
{ name: 'New York', code: 'NY' },
8+
{ name: 'Rome', code: 'RM' },
9+
{ name: 'London', code: 'LDN' },
10+
{ name: 'Istanbul', code: 'IST' },
11+
{ name: 'Paris', code: 'PRS' }
12+
];
13+
14+
type City = (typeof cities)[0];
15+
16+
export default function BasicDemo() {
17+
const [source, setSource] = useState(cities);
18+
const [target, setTarget] = useState<City[]>([]);
19+
const [selection, setSelection] = useState<City[]>([]);
20+
21+
const pickList = usePickList({
22+
source,
23+
target,
24+
selection,
25+
onChange: (e: usePickListReorderEvent) => {
26+
setSource(e.source as typeof cities);
27+
setTarget(e.target as typeof cities);
28+
},
29+
onSelectionChange: (e: usePickListSelectionChangeEvent) => setSelection(e.value as City[])
30+
});
31+
32+
const toggleSelection = (item: City) => {
33+
setSelection((prev) => (prev.includes(item) ? prev.filter((s) => s !== item) : [...prev, item]));
34+
};
35+
36+
const listClassName = 'flex-1 border border-surface-200 dark:border-surface-700 rounded-lg overflow-hidden min-h-48';
37+
const itemClassName = (item: City) =>
38+
`px-3 py-2 cursor-pointer select-none text-sm border-b border-surface-100 dark:border-surface-800 last:border-b-0 ${selection.includes(item) ? 'bg-primary/10 text-primary' : 'hover:bg-surface-50 dark:hover:bg-surface-900'}`;
39+
const btnClassName = 'px-2 py-1 rounded bg-surface-100 dark:bg-surface-800 hover:bg-surface-200 dark:hover:bg-surface-700 text-sm disabled:opacity-50';
40+
41+
const hasSourceSelection = selection.some((s) => source.includes(s));
42+
const hasTargetSelection = selection.some((s) => target.includes(s));
43+
44+
return (
45+
<div className="flex justify-center">
46+
<div {...pickList.rootProps} className="flex gap-3 items-center w-full max-w-lg">
47+
<div {...pickList.sourceListProps} className={listClassName}>
48+
{(pickList.state.source as typeof cities).map((item, i) => (
49+
<div key={item.code} {...pickList.getOptionProps(item, i, 'source')} onClick={() => toggleSelection(item)} className={itemClassName(item)}>
50+
{item.name}
51+
</div>
52+
))}
53+
</div>
54+
<div className="flex flex-col gap-1">
55+
<button onClick={pickList.moveToTargetProps.onClick as React.MouseEventHandler} disabled={!hasSourceSelection || undefined} className={btnClassName}>
56+
57+
</button>
58+
<button onClick={pickList.moveAllToTargetProps.onClick as React.MouseEventHandler} disabled={source.length === 0 || undefined} className={btnClassName}>
59+
60+
</button>
61+
<button onClick={pickList.moveToSourceProps.onClick as React.MouseEventHandler} disabled={!hasTargetSelection || undefined} className={btnClassName}>
62+
63+
</button>
64+
<button onClick={pickList.moveAllToSourceProps.onClick as React.MouseEventHandler} disabled={target.length === 0 || undefined} className={btnClassName}>
65+
66+
</button>
67+
</div>
68+
<div {...pickList.targetListProps} className={listClassName}>
69+
{(pickList.state.target as typeof cities).map((item, i) => (
70+
<div key={item.code} {...pickList.getOptionProps(item, i, 'target')} onClick={() => toggleSelection(item)} className={itemClassName(item)}>
71+
{item.name}
72+
</div>
73+
))}
74+
</div>
75+
</div>
76+
</div>
77+
);
78+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
---
2+
title: useOrderList
3+
description: A headless hook that manages list ordering with selection-based controls and optional drag-and-drop support.
4+
component: orderlist
5+
---
6+
7+
<DocDemoViewer name="orderlist:basic-demo" mode="collapsible"/>
8+
9+
## Usage
10+
11+
```tsx showLineNumbers {1,3-8,11-26}
12+
import { useOrderList } from '@primereact/headless/orderlist';
13+
14+
const orderList = useOrderList({
15+
value: items,
16+
selection,
17+
draggable: true,
18+
onReorder: (e) => setItems(e.value)
19+
});
20+
21+
return (
22+
<div {...orderList.rootProps}>
23+
<div {...orderList.controlsProps}>
24+
<button {...orderList.firstProps}>Top</button>
25+
<button {...orderList.prevProps}>Up</button>
26+
<button {...orderList.nextProps}>Down</button>
27+
<button {...orderList.lastProps}>Bottom</button>
28+
</div>
29+
<div {...orderList.listProps}>
30+
{orderList.state.value.map((item, i) => (
31+
<div key={i} {...orderList.getOptionProps(item, i)}>
32+
{item.name}
33+
</div>
34+
))}
35+
</div>
36+
</div>
37+
);
38+
```
39+
40+
`useOrderList` manages item ordering with move operations and drag-and-drop. Pair it with **Listbox** for built-in selection and keyboard navigation.
41+
42+
## Features
43+
44+
- Returns spread-ready prop objects (`rootProps`, `listProps`, `controlsProps`, `prevProps`, `nextProps`, `firstProps`, `lastProps`)
45+
- Selection-based move operations: `moveUp`, `moveDown`, `moveTop`, `moveBottom`
46+
- Built-in drag-and-drop with `draggable` prop
47+
- Clone or empty placeholder modes during drag
48+
- Per-option props via `getOptionProps`
49+
50+
## Behavior
51+
52+
### Basic Ordering
53+
54+
Pass `value` and `onReorder` to manage the list. Use `moveUp`, `moveDown`, `moveTop`, `moveBottom` with selection to reorder items.
55+
56+
```tsx
57+
const [items, setItems] = React.useState(['A', 'B', 'C']);
58+
const [selection, setSelection] = React.useState([]);
59+
60+
const orderList = useOrderList({
61+
value: items,
62+
selection,
63+
onReorder: (e) => setItems(e.value)
64+
});
65+
```
66+
67+
### Drag and Drop
68+
69+
Set `draggable` to enable drag-and-drop reordering.
70+
71+
```tsx
72+
const orderList = useOrderList({
73+
value: items,
74+
draggable: true,
75+
onReorder: (e) => setItems(e.value)
76+
});
77+
```
78+
79+
### Placeholder
80+
81+
Set `placeholder` to `"clone"` to show a visual copy of the dragged item in its original position. Default is `"empty"` which preserves height only.
82+
83+
```tsx
84+
const orderList = useOrderList({
85+
value: items,
86+
draggable: true,
87+
placeholder: 'clone',
88+
onReorder: (e) => setItems(e.value)
89+
});
90+
```
91+
92+
Style the placeholder with `[data-sortable-placeholder]` attribute.
93+
94+
### Disabled
95+
96+
Set `disabled` to prevent all interactions.
97+
98+
```tsx
99+
const orderList = useOrderList({ value: items, disabled: true });
100+
```
101+
102+
### Custom Styling with Data Attributes
103+
104+
Every prop object includes `data-scope` and `data-part` attributes for CSS targeting.
105+
106+
```css
107+
[data-scope='orderlist'][data-part='root'] {
108+
display: flex;
109+
gap: 1rem;
110+
}
111+
112+
[data-scope='orderlist'][data-part='list'] {
113+
min-height: 200px;
114+
}
115+
116+
[data-sortable-placeholder] {
117+
opacity: 0.4;
118+
}
119+
```
120+
121+
#### Option Attributes
122+
123+
| Attribute | Value |
124+
| -------------------------- | -------------------------------------- |
125+
| `data-selected` | Present when the item is selected |
126+
| `data-sortable-item` | Present on every sortable item |
127+
| `data-sortable-container` | Container id for the sortable group |
128+
| `data-dragging` | Present on the item being dragged |
129+
| `data-dropping` | Present briefly during the drop animation |
130+
| `data-sortable-placeholder`| Present on the placeholder clone left behind during drag |
131+
132+
## API
133+
134+
### useOrderList
135+
136+
<DocApiTable name="useOrderList" category="api" />

0 commit comments

Comments
 (0)