@wordpress/dataviews 4.2.0 → 4.3.1-next.1f6eadc42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/README.md +47 -7
- package/build/components/dataviews/index.js +3 -5
- package/build/components/dataviews/index.js.map +1 -1
- package/build/components/dataviews-bulk-actions/index.js +145 -141
- package/build/components/dataviews-bulk-actions/index.js.map +1 -1
- package/build/components/dataviews-filters/add-filter.js +4 -6
- package/build/components/dataviews-filters/add-filter.js.map +1 -1
- package/build/components/dataviews-filters/search-widget.js +27 -25
- package/build/components/dataviews-filters/search-widget.js.map +1 -1
- package/build/components/dataviews-footer/index.js +45 -0
- package/build/components/dataviews-footer/index.js.map +1 -0
- package/build/components/dataviews-item-actions/index.js +5 -8
- package/build/components/dataviews-item-actions/index.js.map +1 -1
- package/build/components/dataviews-pagination/index.js +4 -4
- package/build/components/dataviews-pagination/index.js.map +1 -1
- package/build/components/dataviews-view-config/index.js +171 -32
- package/build/components/dataviews-view-config/index.js.map +1 -1
- package/build/dataforms-layouts/panel/index.js +4 -1
- package/build/dataforms-layouts/panel/index.js.map +1 -1
- package/build/dataviews-layouts/index.js +48 -2
- package/build/dataviews-layouts/index.js.map +1 -1
- package/build/dataviews-layouts/list/index.js +131 -91
- package/build/dataviews-layouts/list/index.js.map +1 -1
- package/build/dataviews-layouts/table/column-header-menu.js +52 -54
- package/build/dataviews-layouts/table/column-header-menu.js.map +1 -1
- package/build/dataviews-layouts/table/index.js +7 -35
- package/build/dataviews-layouts/table/index.js.map +1 -1
- package/build/normalize-fields.js +4 -2
- package/build/normalize-fields.js.map +1 -1
- package/build/types.js.map +1 -1
- package/build-module/components/dataviews/index.js +3 -5
- package/build-module/components/dataviews/index.js.map +1 -1
- package/build-module/components/dataviews-bulk-actions/index.js +145 -143
- package/build-module/components/dataviews-bulk-actions/index.js.map +1 -1
- package/build-module/components/dataviews-filters/add-filter.js +4 -6
- package/build-module/components/dataviews-filters/add-filter.js.map +1 -1
- package/build-module/components/dataviews-filters/search-widget.js +27 -25
- package/build-module/components/dataviews-filters/search-widget.js.map +1 -1
- package/build-module/components/dataviews-footer/index.js +38 -0
- package/build-module/components/dataviews-footer/index.js.map +1 -0
- package/build-module/components/dataviews-item-actions/index.js +5 -8
- package/build-module/components/dataviews-item-actions/index.js.map +1 -1
- package/build-module/components/dataviews-pagination/index.js +5 -5
- package/build-module/components/dataviews-pagination/index.js.map +1 -1
- package/build-module/components/dataviews-view-config/index.js +177 -38
- package/build-module/components/dataviews-view-config/index.js.map +1 -1
- package/build-module/dataforms-layouts/panel/index.js +4 -1
- package/build-module/dataforms-layouts/panel/index.js.map +1 -1
- package/build-module/dataviews-layouts/index.js +45 -1
- package/build-module/dataviews-layouts/index.js.map +1 -1
- package/build-module/dataviews-layouts/list/index.js +132 -90
- package/build-module/dataviews-layouts/list/index.js.map +1 -1
- package/build-module/dataviews-layouts/table/column-header-menu.js +52 -54
- package/build-module/dataviews-layouts/table/column-header-menu.js.map +1 -1
- package/build-module/dataviews-layouts/table/index.js +9 -37
- package/build-module/dataviews-layouts/table/index.js.map +1 -1
- package/build-module/normalize-fields.js +4 -2
- package/build-module/normalize-fields.js.map +1 -1
- package/build-module/types.js.map +1 -1
- package/build-style/style-rtl.css +79 -67
- package/build-style/style.css +79 -67
- package/build-types/components/dataviews/index.d.ts.map +1 -1
- package/build-types/components/dataviews/stories/fixtures.d.ts +27 -131
- package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
- package/build-types/components/dataviews/stories/index.story.d.ts +13 -53
- package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -1
- package/build-types/components/dataviews-bulk-actions/index.d.ts +11 -1
- package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -1
- package/build-types/components/dataviews-filters/add-filter.d.ts.map +1 -1
- package/build-types/components/dataviews-filters/search-widget.d.ts +3 -0
- package/build-types/components/dataviews-filters/search-widget.d.ts.map +1 -1
- package/build-types/components/dataviews-footer/index.d.ts +2 -0
- package/build-types/components/dataviews-footer/index.d.ts.map +1 -0
- package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
- package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
- package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
- package/build-types/dataviews-layouts/index.d.ts +4 -2
- package/build-types/dataviews-layouts/index.d.ts.map +1 -1
- package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
- package/build-types/dataviews-layouts/table/column-header-menu.d.ts.map +1 -1
- package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
- package/build-types/normalize-fields.d.ts.map +1 -1
- package/build-types/types.d.ts +2 -0
- package/build-types/types.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/components/dataviews/index.tsx +2 -6
- package/src/components/dataviews/stories/fixtures.tsx +698 -0
- package/src/components/dataviews/stories/index.story.tsx +186 -0
- package/src/components/dataviews/stories/style.css +4 -0
- package/src/components/dataviews/style.scss +2 -0
- package/src/components/dataviews-bulk-actions/index.tsx +264 -213
- package/src/components/dataviews-bulk-actions/style.scss +9 -4
- package/src/components/dataviews-filters/add-filter.tsx +7 -11
- package/src/components/dataviews-filters/search-widget.tsx +44 -29
- package/src/components/dataviews-filters/style.scss +12 -2
- package/src/components/dataviews-footer/index.tsx +50 -0
- package/src/components/dataviews-footer/style.scss +40 -0
- package/src/components/dataviews-item-actions/index.tsx +8 -14
- package/src/components/dataviews-pagination/index.tsx +5 -5
- package/src/components/dataviews-pagination/style.scss +0 -19
- package/src/components/dataviews-view-config/index.tsx +252 -53
- package/src/components/dataviews-view-config/style.scss +25 -0
- package/src/dataforms-layouts/panel/index.tsx +2 -0
- package/src/dataviews-layouts/grid/style.scss +1 -1
- package/src/dataviews-layouts/index.ts +63 -2
- package/src/dataviews-layouts/list/index.tsx +210 -139
- package/src/dataviews-layouts/list/style.scss +10 -4
- package/src/dataviews-layouts/table/column-header-menu.tsx +85 -87
- package/src/dataviews-layouts/table/index.tsx +8 -65
- package/src/dataviews-layouts/table/style.scss +0 -9
- package/src/normalize-fields.ts +2 -0
- package/src/style.scss +1 -1
- package/src/types.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/build/components/dataviews-bulk-actions-toolbar/index.js +0 -207
- package/build/components/dataviews-bulk-actions-toolbar/index.js.map +0 -1
- package/build-module/components/dataviews-bulk-actions-toolbar/index.js +0 -201
- package/build-module/components/dataviews-bulk-actions-toolbar/index.js.map +0 -1
- package/build-types/components/dataviews-bulk-actions-toolbar/index.d.ts +0 -2
- package/build-types/components/dataviews-bulk-actions-toolbar/index.d.ts.map +0 -1
- package/src/components/dataviews/stories/fixtures.js +0 -250
- package/src/components/dataviews/stories/index.story.js +0 -71
- package/src/components/dataviews-bulk-actions-toolbar/index.tsx +0 -288
- package/src/components/dataviews-bulk-actions-toolbar/style.scss +0 -45
|
@@ -2,17 +2,11 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import clsx from 'clsx';
|
|
5
|
-
// TODO: use the @wordpress/components one once public
|
|
6
|
-
// eslint-disable-next-line no-restricted-imports
|
|
7
|
-
import { useStoreState } from '@ariakit/react';
|
|
8
|
-
// Import CompositeStore type, which is not exported from @wordpress/components.
|
|
9
|
-
// eslint-disable-next-line no-restricted-imports
|
|
10
|
-
import type { CompositeStore } from '@ariakit/react';
|
|
11
5
|
|
|
12
6
|
/**
|
|
13
7
|
* WordPress dependencies
|
|
14
8
|
*/
|
|
15
|
-
import { useInstanceId } from '@wordpress/compose';
|
|
9
|
+
import { useInstanceId, usePrevious } from '@wordpress/compose';
|
|
16
10
|
import {
|
|
17
11
|
__experimentalHStack as HStack,
|
|
18
12
|
__experimentalVStack as VStack,
|
|
@@ -20,6 +14,7 @@ import {
|
|
|
20
14
|
privateApis as componentsPrivateApis,
|
|
21
15
|
Spinner,
|
|
22
16
|
VisuallyHidden,
|
|
17
|
+
Composite,
|
|
23
18
|
} from '@wordpress/components';
|
|
24
19
|
import {
|
|
25
20
|
useCallback,
|
|
@@ -44,39 +39,110 @@ import type { Action, NormalizedField, ViewListProps } from '../../types';
|
|
|
44
39
|
|
|
45
40
|
interface ListViewItemProps< Item > {
|
|
46
41
|
actions: Action< Item >[];
|
|
47
|
-
|
|
42
|
+
idPrefix: string;
|
|
48
43
|
isSelected: boolean;
|
|
49
44
|
item: Item;
|
|
50
45
|
mediaField?: NormalizedField< Item >;
|
|
51
46
|
onSelect: ( item: Item ) => void;
|
|
52
47
|
primaryField?: NormalizedField< Item >;
|
|
53
|
-
store: CompositeStore;
|
|
54
48
|
visibleFields: NormalizedField< Item >[];
|
|
49
|
+
onDropdownTriggerKeyDown: React.KeyboardEventHandler< HTMLButtonElement >;
|
|
55
50
|
}
|
|
56
51
|
|
|
57
|
-
const {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
const { DropdownMenuV2: DropdownMenu } = unlock( componentsPrivateApis );
|
|
53
|
+
|
|
54
|
+
function generateItemWrapperCompositeId( idPrefix: string ) {
|
|
55
|
+
return `${ idPrefix }-item-wrapper`;
|
|
56
|
+
}
|
|
57
|
+
function generatePrimaryActionCompositeId(
|
|
58
|
+
idPrefix: string,
|
|
59
|
+
primaryActionId: string
|
|
60
|
+
) {
|
|
61
|
+
return `${ idPrefix }-primary-action-${ primaryActionId }`;
|
|
62
|
+
}
|
|
63
|
+
function generateDropdownTriggerCompositeId( idPrefix: string ) {
|
|
64
|
+
return `${ idPrefix }-dropdown`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function PrimaryActionGridCell< Item >( {
|
|
68
|
+
idPrefix,
|
|
69
|
+
primaryAction,
|
|
70
|
+
item,
|
|
71
|
+
}: {
|
|
72
|
+
idPrefix: string;
|
|
73
|
+
primaryAction: Action< Item >;
|
|
74
|
+
item: Item;
|
|
75
|
+
} ) {
|
|
76
|
+
const registry = useRegistry();
|
|
77
|
+
const [ isModalOpen, setIsModalOpen ] = useState( false );
|
|
78
|
+
|
|
79
|
+
const compositeItemId = generatePrimaryActionCompositeId(
|
|
80
|
+
idPrefix,
|
|
81
|
+
primaryAction.id
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const label =
|
|
85
|
+
typeof primaryAction.label === 'string'
|
|
86
|
+
? primaryAction.label
|
|
87
|
+
: primaryAction.label( [ item ] );
|
|
88
|
+
|
|
89
|
+
return 'RenderModal' in primaryAction ? (
|
|
90
|
+
<div role="gridcell" key={ primaryAction.id }>
|
|
91
|
+
<Composite.Item
|
|
92
|
+
id={ compositeItemId }
|
|
93
|
+
render={
|
|
94
|
+
<Button
|
|
95
|
+
label={ label }
|
|
96
|
+
icon={ primaryAction.icon }
|
|
97
|
+
isDestructive={ primaryAction.isDestructive }
|
|
98
|
+
size="small"
|
|
99
|
+
onClick={ () => setIsModalOpen( true ) }
|
|
100
|
+
/>
|
|
101
|
+
}
|
|
102
|
+
>
|
|
103
|
+
{ isModalOpen && (
|
|
104
|
+
<ActionModal< Item >
|
|
105
|
+
action={ primaryAction }
|
|
106
|
+
items={ [ item ] }
|
|
107
|
+
closeModal={ () => setIsModalOpen( false ) }
|
|
108
|
+
/>
|
|
109
|
+
) }
|
|
110
|
+
</Composite.Item>
|
|
111
|
+
</div>
|
|
112
|
+
) : (
|
|
113
|
+
<div role="gridcell" key={ primaryAction.id }>
|
|
114
|
+
<Composite.Item
|
|
115
|
+
id={ compositeItemId }
|
|
116
|
+
render={
|
|
117
|
+
<Button
|
|
118
|
+
label={ label }
|
|
119
|
+
icon={ primaryAction.icon }
|
|
120
|
+
isDestructive={ primaryAction.isDestructive }
|
|
121
|
+
size="small"
|
|
122
|
+
onClick={ () => {
|
|
123
|
+
primaryAction.callback( [ item ], { registry } );
|
|
124
|
+
} }
|
|
125
|
+
/>
|
|
126
|
+
}
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
64
131
|
|
|
65
132
|
function ListItem< Item >( {
|
|
66
133
|
actions,
|
|
67
|
-
|
|
134
|
+
idPrefix,
|
|
68
135
|
isSelected,
|
|
69
136
|
item,
|
|
70
137
|
mediaField,
|
|
71
138
|
onSelect,
|
|
72
139
|
primaryField,
|
|
73
|
-
store,
|
|
74
140
|
visibleFields,
|
|
141
|
+
onDropdownTriggerKeyDown,
|
|
75
142
|
}: ListViewItemProps< Item > ) {
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const descriptionId = `${ id }-description`;
|
|
143
|
+
const itemRef = useRef< HTMLDivElement >( null );
|
|
144
|
+
const labelId = `${ idPrefix }-label`;
|
|
145
|
+
const descriptionId = `${ idPrefix }-description`;
|
|
80
146
|
|
|
81
147
|
const [ isHovered, setIsHovered ] = useState( false );
|
|
82
148
|
const handleMouseEnter = () => {
|
|
@@ -111,13 +177,6 @@ function ListItem< Item >( {
|
|
|
111
177
|
};
|
|
112
178
|
}, [ actions, item ] );
|
|
113
179
|
|
|
114
|
-
const [ isModalOpen, setIsModalOpen ] = useState( false );
|
|
115
|
-
const primaryActionLabel =
|
|
116
|
-
primaryAction &&
|
|
117
|
-
( typeof primaryAction.label === 'string'
|
|
118
|
-
? primaryAction.label
|
|
119
|
-
: primaryAction.label( [ item ] ) );
|
|
120
|
-
|
|
121
180
|
const renderedMediaField = mediaField?.render ? (
|
|
122
181
|
<mediaField.render item={ item } />
|
|
123
182
|
) : (
|
|
@@ -129,7 +188,7 @@ function ListItem< Item >( {
|
|
|
129
188
|
) : null;
|
|
130
189
|
|
|
131
190
|
return (
|
|
132
|
-
<
|
|
191
|
+
<Composite.Row
|
|
133
192
|
ref={ itemRef }
|
|
134
193
|
render={ <li /> }
|
|
135
194
|
role="row"
|
|
@@ -146,11 +205,10 @@ function ListItem< Item >( {
|
|
|
146
205
|
spacing={ 0 }
|
|
147
206
|
>
|
|
148
207
|
<div role="gridcell">
|
|
149
|
-
<
|
|
150
|
-
store={ store }
|
|
208
|
+
<Composite.Item
|
|
151
209
|
render={ <div /> }
|
|
152
210
|
role="button"
|
|
153
|
-
id={
|
|
211
|
+
id={ generateItemWrapperCompositeId( idPrefix ) }
|
|
154
212
|
aria-pressed={ isSelected }
|
|
155
213
|
aria-labelledby={ labelId }
|
|
156
214
|
aria-describedby={ descriptionId }
|
|
@@ -198,7 +256,7 @@ function ListItem< Item >( {
|
|
|
198
256
|
</div>
|
|
199
257
|
</VStack>
|
|
200
258
|
</HStack>
|
|
201
|
-
</
|
|
259
|
+
</Composite.Item>
|
|
202
260
|
</div>
|
|
203
261
|
{ eligibleActions?.length > 0 && (
|
|
204
262
|
<HStack
|
|
@@ -210,65 +268,20 @@ function ListItem< Item >( {
|
|
|
210
268
|
width: 'auto',
|
|
211
269
|
} }
|
|
212
270
|
>
|
|
213
|
-
{ primaryAction &&
|
|
214
|
-
<
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
label={ primaryActionLabel }
|
|
220
|
-
icon={ primaryAction.icon }
|
|
221
|
-
isDestructive={
|
|
222
|
-
primaryAction.isDestructive
|
|
223
|
-
}
|
|
224
|
-
size="small"
|
|
225
|
-
onClick={ () =>
|
|
226
|
-
setIsModalOpen( true )
|
|
227
|
-
}
|
|
228
|
-
/>
|
|
229
|
-
}
|
|
230
|
-
>
|
|
231
|
-
{ isModalOpen && (
|
|
232
|
-
<ActionModal< Item >
|
|
233
|
-
action={ primaryAction }
|
|
234
|
-
items={ [ item ] }
|
|
235
|
-
closeModal={ () =>
|
|
236
|
-
setIsModalOpen( false )
|
|
237
|
-
}
|
|
238
|
-
/>
|
|
239
|
-
) }
|
|
240
|
-
</CompositeItem>
|
|
241
|
-
</div>
|
|
271
|
+
{ primaryAction && (
|
|
272
|
+
<PrimaryActionGridCell
|
|
273
|
+
idPrefix={ idPrefix }
|
|
274
|
+
primaryAction={ primaryAction }
|
|
275
|
+
item={ item }
|
|
276
|
+
/>
|
|
242
277
|
) }
|
|
243
|
-
{ primaryAction &&
|
|
244
|
-
! ( 'RenderModal' in primaryAction ) && (
|
|
245
|
-
<div role="gridcell" key={ primaryAction.id }>
|
|
246
|
-
<CompositeItem
|
|
247
|
-
store={ store }
|
|
248
|
-
render={
|
|
249
|
-
<Button
|
|
250
|
-
label={ primaryActionLabel }
|
|
251
|
-
icon={ primaryAction.icon }
|
|
252
|
-
isDestructive={
|
|
253
|
-
primaryAction.isDestructive
|
|
254
|
-
}
|
|
255
|
-
size="small"
|
|
256
|
-
onClick={ () => {
|
|
257
|
-
primaryAction.callback(
|
|
258
|
-
[ item ],
|
|
259
|
-
{ registry }
|
|
260
|
-
);
|
|
261
|
-
} }
|
|
262
|
-
/>
|
|
263
|
-
}
|
|
264
|
-
/>
|
|
265
|
-
</div>
|
|
266
|
-
) }
|
|
267
278
|
<div role="gridcell">
|
|
268
279
|
<DropdownMenu
|
|
269
280
|
trigger={
|
|
270
|
-
<
|
|
271
|
-
|
|
281
|
+
<Composite.Item
|
|
282
|
+
id={ generateDropdownTriggerCompositeId(
|
|
283
|
+
idPrefix
|
|
284
|
+
) }
|
|
272
285
|
render={
|
|
273
286
|
<Button
|
|
274
287
|
size="small"
|
|
@@ -276,30 +289,9 @@ function ListItem< Item >( {
|
|
|
276
289
|
label={ __( 'Actions' ) }
|
|
277
290
|
accessibleWhenDisabled
|
|
278
291
|
disabled={ ! actions.length }
|
|
279
|
-
onKeyDown={
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
} ) => {
|
|
283
|
-
if (
|
|
284
|
-
event.key ===
|
|
285
|
-
'ArrowDown'
|
|
286
|
-
) {
|
|
287
|
-
// Prevent the default behaviour (open dropdown menu) and go down.
|
|
288
|
-
event.preventDefault();
|
|
289
|
-
store.move(
|
|
290
|
-
store.down()
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
if (
|
|
294
|
-
event.key === 'ArrowUp'
|
|
295
|
-
) {
|
|
296
|
-
// Prevent the default behavior (open dropdown menu) and go up.
|
|
297
|
-
event.preventDefault();
|
|
298
|
-
store.move(
|
|
299
|
-
store.up()
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
} }
|
|
292
|
+
onKeyDown={
|
|
293
|
+
onDropdownTriggerKeyDown
|
|
294
|
+
}
|
|
303
295
|
/>
|
|
304
296
|
}
|
|
305
297
|
/>
|
|
@@ -315,7 +307,7 @@ function ListItem< Item >( {
|
|
|
315
307
|
</HStack>
|
|
316
308
|
) }
|
|
317
309
|
</HStack>
|
|
318
|
-
</
|
|
310
|
+
</Composite.Row>
|
|
319
311
|
);
|
|
320
312
|
}
|
|
321
313
|
|
|
@@ -331,6 +323,7 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) {
|
|
|
331
323
|
view,
|
|
332
324
|
} = props;
|
|
333
325
|
const baseId = useInstanceId( ViewList, 'view-list' );
|
|
326
|
+
|
|
334
327
|
const selectedItem = data?.findLast( ( item ) =>
|
|
335
328
|
selection.includes( getItemId( item ) )
|
|
336
329
|
);
|
|
@@ -353,34 +346,111 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) {
|
|
|
353
346
|
const onSelect = ( item: Item ) =>
|
|
354
347
|
onChangeSelection( [ getItemId( item ) ] );
|
|
355
348
|
|
|
356
|
-
const
|
|
357
|
-
( item
|
|
358
|
-
item ? `${ baseId }-${ getItemId( item ) }` : undefined,
|
|
349
|
+
const generateCompositeItemIdPrefix = useCallback(
|
|
350
|
+
( item: Item ) => `${ baseId }-${ getItemId( item ) }`,
|
|
359
351
|
[ baseId, getItemId ]
|
|
360
352
|
);
|
|
361
353
|
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
state.items.some(
|
|
371
|
-
( item: { id: any } ) => item.id === state.activeId
|
|
372
|
-
)
|
|
354
|
+
const isActiveCompositeItem = useCallback(
|
|
355
|
+
( item: Item, idToCheck: string ) => {
|
|
356
|
+
// All composite items use the same prefix in their IDs.
|
|
357
|
+
return idToCheck.startsWith(
|
|
358
|
+
generateCompositeItemIdPrefix( item )
|
|
359
|
+
);
|
|
360
|
+
},
|
|
361
|
+
[ generateCompositeItemIdPrefix ]
|
|
373
362
|
);
|
|
363
|
+
|
|
364
|
+
// Controlled state for the active composite item.
|
|
365
|
+
const [ activeCompositeId, setActiveCompositeId ] = useState<
|
|
366
|
+
string | null | undefined
|
|
367
|
+
>( undefined );
|
|
368
|
+
|
|
369
|
+
// Update the active composite item when the selected item changes.
|
|
374
370
|
useEffect( () => {
|
|
375
|
-
if (
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
371
|
+
if ( selectedItem ) {
|
|
372
|
+
setActiveCompositeId(
|
|
373
|
+
generateItemWrapperCompositeId(
|
|
374
|
+
generateCompositeItemIdPrefix( selectedItem )
|
|
375
|
+
)
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}, [ selectedItem, generateCompositeItemIdPrefix ] );
|
|
379
|
+
|
|
380
|
+
const activeItemIndex = data.findIndex( ( item ) =>
|
|
381
|
+
isActiveCompositeItem( item, activeCompositeId ?? '' )
|
|
382
|
+
);
|
|
383
|
+
const previousActiveItemIndex = usePrevious( activeItemIndex );
|
|
384
|
+
const isActiveIdInList = activeItemIndex !== -1;
|
|
385
|
+
|
|
386
|
+
const selectCompositeItem = useCallback(
|
|
387
|
+
(
|
|
388
|
+
targetIndex: number,
|
|
389
|
+
// Allows invokers to specify a custom function to generate the
|
|
390
|
+
// target composite item ID
|
|
391
|
+
generateCompositeId: ( idPrefix: string ) => string
|
|
392
|
+
) => {
|
|
393
|
+
// Clamping between 0 and data.length - 1 to avoid out of bounds.
|
|
394
|
+
const clampedIndex = Math.min(
|
|
395
|
+
data.length - 1,
|
|
396
|
+
Math.max( 0, targetIndex )
|
|
397
|
+
);
|
|
398
|
+
if ( ! data[ clampedIndex ] ) {
|
|
399
|
+
return;
|
|
381
400
|
}
|
|
401
|
+
const itemIdPrefix = generateCompositeItemIdPrefix(
|
|
402
|
+
data[ clampedIndex ]
|
|
403
|
+
);
|
|
404
|
+
const targetCompositeItemId = generateCompositeId( itemIdPrefix );
|
|
405
|
+
|
|
406
|
+
setActiveCompositeId( targetCompositeItemId );
|
|
407
|
+
document.getElementById( targetCompositeItemId )?.focus();
|
|
408
|
+
},
|
|
409
|
+
[ data, generateCompositeItemIdPrefix ]
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
// Select a new active composite item when the current active item
|
|
413
|
+
// is removed from the list.
|
|
414
|
+
useEffect( () => {
|
|
415
|
+
const wasActiveIdInList =
|
|
416
|
+
previousActiveItemIndex !== undefined &&
|
|
417
|
+
previousActiveItemIndex !== -1;
|
|
418
|
+
if ( ! isActiveIdInList && wasActiveIdInList ) {
|
|
419
|
+
// By picking `previousActiveItemIndex` as the next item index, we are
|
|
420
|
+
// basically picking the item that would have been after the deleted one.
|
|
421
|
+
// If the previously active (and removed) item was the last of the list,
|
|
422
|
+
// we will select the item before it — which is the new last item.
|
|
423
|
+
selectCompositeItem(
|
|
424
|
+
previousActiveItemIndex,
|
|
425
|
+
generateItemWrapperCompositeId
|
|
426
|
+
);
|
|
382
427
|
}
|
|
383
|
-
}, [ isActiveIdInList ] );
|
|
428
|
+
}, [ isActiveIdInList, selectCompositeItem, previousActiveItemIndex ] );
|
|
429
|
+
|
|
430
|
+
// Prevent the default behavior (open dropdown menu) and instead select the
|
|
431
|
+
// dropdown menu trigger on the previous/next row.
|
|
432
|
+
// https://github.com/ariakit/ariakit/issues/3768
|
|
433
|
+
const onDropdownTriggerKeyDown = useCallback(
|
|
434
|
+
( event: React.KeyboardEvent< HTMLButtonElement > ) => {
|
|
435
|
+
if ( event.key === 'ArrowDown' ) {
|
|
436
|
+
// Select the dropdown menu trigger item in the next row.
|
|
437
|
+
event.preventDefault();
|
|
438
|
+
selectCompositeItem(
|
|
439
|
+
activeItemIndex + 1,
|
|
440
|
+
generateDropdownTriggerCompositeId
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
if ( event.key === 'ArrowUp' ) {
|
|
444
|
+
// Select the dropdown menu trigger item in the previous row.
|
|
445
|
+
event.preventDefault();
|
|
446
|
+
selectCompositeItem(
|
|
447
|
+
activeItemIndex - 1,
|
|
448
|
+
generateDropdownTriggerCompositeId
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
[ selectCompositeItem, activeItemIndex ]
|
|
453
|
+
);
|
|
384
454
|
|
|
385
455
|
const hasData = data?.length;
|
|
386
456
|
if ( ! hasData ) {
|
|
@@ -404,22 +474,23 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) {
|
|
|
404
474
|
render={ <ul /> }
|
|
405
475
|
className="dataviews-view-list"
|
|
406
476
|
role="grid"
|
|
407
|
-
|
|
477
|
+
activeId={ activeCompositeId }
|
|
478
|
+
setActiveId={ setActiveCompositeId }
|
|
408
479
|
>
|
|
409
480
|
{ data.map( ( item ) => {
|
|
410
|
-
const id =
|
|
481
|
+
const id = generateCompositeItemIdPrefix( item );
|
|
411
482
|
return (
|
|
412
483
|
<ListItem
|
|
413
484
|
key={ id }
|
|
414
|
-
|
|
485
|
+
idPrefix={ id }
|
|
415
486
|
actions={ actions }
|
|
416
487
|
item={ item }
|
|
417
488
|
isSelected={ item === selectedItem }
|
|
418
489
|
onSelect={ onSelect }
|
|
419
490
|
mediaField={ mediaField }
|
|
420
491
|
primaryField={ primaryField }
|
|
421
|
-
store={ store }
|
|
422
492
|
visibleFields={ visibleFields }
|
|
493
|
+
onDropdownTriggerKeyDown={ onDropdownTriggerKeyDown }
|
|
423
494
|
/>
|
|
424
495
|
);
|
|
425
496
|
} ) }
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
ul.dataviews-view-list {
|
|
2
|
+
list-style-type: none;
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
.dataviews-view-list {
|
|
2
6
|
margin: 0 0 auto;
|
|
3
7
|
|
|
@@ -73,6 +77,7 @@
|
|
|
73
77
|
color: $gray-900;
|
|
74
78
|
}
|
|
75
79
|
&:hover,
|
|
80
|
+
&.is-hovered,
|
|
76
81
|
&:focus-within {
|
|
77
82
|
color: var(--wp-admin-theme-color);
|
|
78
83
|
background-color: #f8f8f8;
|
|
@@ -100,6 +105,7 @@
|
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
.dataviews-view-list__item {
|
|
108
|
+
box-sizing: border-box;
|
|
103
109
|
padding: $grid-unit-20 $grid-unit-30;
|
|
104
110
|
width: 100%;
|
|
105
111
|
scroll-margin: $grid-unit-10 0;
|
|
@@ -108,12 +114,12 @@
|
|
|
108
114
|
&::before {
|
|
109
115
|
position: absolute;
|
|
110
116
|
content: "";
|
|
111
|
-
top:
|
|
117
|
+
top: var(--wp-admin-border-width-focus);
|
|
112
118
|
right: var(--wp-admin-border-width-focus);
|
|
113
119
|
bottom: var(--wp-admin-border-width-focus);
|
|
114
120
|
left: var(--wp-admin-border-width-focus);
|
|
115
121
|
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
|
|
116
|
-
border-radius: $radius-
|
|
122
|
+
border-radius: $radius-small;
|
|
117
123
|
}
|
|
118
124
|
}
|
|
119
125
|
.dataviews-view-list__primary-field {
|
|
@@ -151,8 +157,8 @@
|
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
.dataviews-view-list__media-placeholder {
|
|
154
|
-
|
|
155
|
-
height: $grid-unit-
|
|
160
|
+
width: $grid-unit-05 * 13;
|
|
161
|
+
height: $grid-unit-05 * 13;
|
|
156
162
|
background-color: $gray-200;
|
|
157
163
|
}
|
|
158
164
|
|