@panneau/lists 5.0.0-alpha.2 → 5.0.0-alpha.3
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/README.md +2 -2
- package/dist/ListView.d.ts +8 -2
- package/dist/ListView.d.ts.map +1 -1
- package/dist/ListView.js +55 -27
- package/dist/ListView.js.map +1 -1
- package/dist/actions/ConfirmDialog.d.ts +15 -3
- package/dist/actions/ConfirmDialog.d.ts.map +1 -1
- package/dist/actions/ConfirmDialog.js +39 -122
- package/dist/actions/ConfirmDialog.js.map +1 -1
- package/dist/actions/RowActions.js +1 -1
- package/dist/displays/displays.js +1 -1
- package/dist/filters/Filters.js +1 -1
- package/dist/filters/controls.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/layouts/CardsLayout.js +1 -1
- package/dist/layouts/ItemTitle.js +1 -1
- package/dist/layouts/TableLayout.js +1 -1
- package/dist/toolbar/LayoutSwitch.js +1 -1
- package/dist/toolbar/SearchField.js +1 -1
- package/dist/toolbar/SortSelect.js +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -89,9 +89,9 @@ const { data, isFetching, error } = useResourceList('pages', query); // @panneau
|
|
|
89
89
|
| Actions | `row` (buttons), `menu` (the row's menu), `toolbar` (selection and resource actions), with `confirm`, hidden by abilities ([0011](../../docs/decisions/0011-permissions.md)) |
|
|
90
90
|
| States | A spinner, an error with a retry, an empty message, and "no results" with a button that clears the filters |
|
|
91
91
|
|
|
92
|
-
An action shows the
|
|
92
|
+
An action shows the icon it names (`"icon": "send"`, through `getNamedIcon()` of `@panneau/ui`, which needs an `iconLoader` on `PanneauRoot`, such as `lucideIconLoader` of `@panneau/ui/named-icons`), or the icon of a built-in action (`edit`, `delete`…). Actions written in code can get icon components by id instead, which win over names: `actionIcons={{ archive: Archive }}`.
|
|
93
93
|
|
|
94
|
-
Lists confirm an action when it has `confirm` (its title and message are ICU messages with `{count}`), or when some selected items don't allow it,
|
|
94
|
+
Lists confirm an action when it has `confirm` (its title and message are ICU messages with `{count}`), or when some selected items don't allow it. For every item matching a query, they only know the current page: with `previewAction` (`useActionPreview()` of `@panneau/actions`), an action that declares `dryRun` asks the backend first, and the confirmation gives its count of skipped items. Then they call `onAction` with the action, its scope, the item or the `Selection` of the protocol, the count and the skipped items. A promise shows the action as pending; when it rejects, a toast says that it failed. After a selection action, the selection is cleared.
|
|
95
95
|
|
|
96
96
|
A `relation` filter keeps ids in the query (`filter[author]=7`). Its chip and picker show the labels of the items it picked, and those of ids from the URL once the relation source's `resolve()` gives them.
|
|
97
97
|
|
package/dist/ListView.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { List } from '@panneau/core';
|
|
|
3
3
|
import type { Field, Filter } from '@panneau/protocol';
|
|
4
4
|
import { type ListActions } from './actions/actions';
|
|
5
5
|
import type { ActionIcons } from './actions/icons';
|
|
6
|
-
import type { DisplayEntry, FilterEntry, ListActionHandler, ListColumn, ListItem, ListLayoutEntry } from './types';
|
|
6
|
+
import type { DisplayEntry, FilterEntry, ListActionHandler, ListActionPreviewer, ListColumn, ListItem, ListLayoutEntry } from './types';
|
|
7
7
|
export interface ListViewProps {
|
|
8
8
|
/** The list state of `@panneau/core` (`useList()`): query and selection. */
|
|
9
9
|
list: List;
|
|
@@ -42,6 +42,12 @@ export interface ListViewProps {
|
|
|
42
42
|
actions?: ListActions;
|
|
43
43
|
/** Runs an action, after its confirmation. */
|
|
44
44
|
onAction?: ListActionHandler;
|
|
45
|
+
/**
|
|
46
|
+
* Asks the backend how many items of a query selection ("Select all N items") an action will
|
|
47
|
+
* skip, before the confirmation: the list only knows the abilities of the current page. Only
|
|
48
|
+
* for actions with `dryRun`. `useActionPreview()` of `@panneau/actions` is one.
|
|
49
|
+
*/
|
|
50
|
+
previewAction?: ListActionPreviewer;
|
|
45
51
|
/**
|
|
46
52
|
* Icons of actions by action id (`{ archive: Archive }`), for actions written in code. They
|
|
47
53
|
* win over the Lucide icon an action names (`"icon": "archive"`).
|
|
@@ -72,5 +78,5 @@ export interface ListViewProps {
|
|
|
72
78
|
* cards), with sorting, search, filters and their chips, pagination, selection and actions. It
|
|
73
79
|
* fetches nothing: the caller gives the items of the list's query and their total.
|
|
74
80
|
*/
|
|
75
|
-
export declare function ListView({ list, items, columns, total, isLoading, error, onRetry, filters, fields, search, layouts: layoutsProp, layout: controlledLayout, onLayoutChange, perPageOptions, selectable: selectableProp, actions: actionsProp, onAction, actionIcons, itemTitle, getItemLabel: getItemLabelProp, getItemHref, displays, layoutComponents, filterControls, toolbar, emptyMessage, 'aria-label': ariaLabel, className, }: ListViewProps): import("react").JSX.Element;
|
|
81
|
+
export declare function ListView({ list, items, columns, total, isLoading, error, onRetry, filters, fields, search, layouts: layoutsProp, layout: controlledLayout, onLayoutChange, perPageOptions, selectable: selectableProp, actions: actionsProp, onAction, previewAction, actionIcons, itemTitle, getItemLabel: getItemLabelProp, getItemHref, displays, layoutComponents, filterControls, toolbar, emptyMessage, 'aria-label': ariaLabel, className, }: ListViewProps): import("react").JSX.Element;
|
|
76
82
|
//# sourceMappingURL=ListView.d.ts.map
|
package/dist/ListView.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListView.d.ts","sourceRoot":"","sources":["../src/ListView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"ListView.d.ts","sourceRoot":"","sources":["../src/ListView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAEzD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAU,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAO/D,OAAO,EACH,KAAK,WAAW,EAKnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAWnD,OAAO,KAAK,EACR,YAAY,EACZ,WAAW,EAEX,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,EACV,QAAQ,EACR,eAAe,EAClB,MAAM,SAAS,CAAC;AAIjB,MAAM,WAAW,aAAa;IAC1B,4EAA4E;IAC5E,IAAI,EAAE,IAAI,CAAC;IACX,qFAAqF;IACrF,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,CAAC;IACvC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IAC/B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wEAAwE;IACxE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,uFAAuF;IACvF,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;IAC1B,kCAAkC;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wFAAwF;IACxF,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,iCAAiC;IACjC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,MAAM,CAAC;IAC1C,uCAAuC;IACvC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,MAAM,GAAG,IAAI,CAAC;IAChD,yEAAyE;IACzE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IAClD,sEAAsE;IACtE,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;IAC7D,8DAA8D;IAC9D,cAAc,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IACvD,6EAA6E;IAC7E,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,mEAAmE;IACnE,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EACrB,IAAI,EACJ,KAAK,EACL,OAAO,EACP,KAAK,EACL,SAAiB,EACjB,KAAY,EACZ,OAAO,EACP,OAAY,EACZ,MAAW,EACX,MAAc,EACd,OAAO,EAAE,WAAuB,EAChC,MAAM,EAAE,gBAAgB,EACxB,cAAc,EACd,cAAc,EACd,UAAU,EAAE,cAAc,EAC1B,OAAO,EAAE,WAAW,EACpB,QAAQ,EACR,aAAa,EACb,WAAW,EACX,SAAS,EACT,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EACX,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,YAAY,EACZ,YAAY,EAAE,SAAS,EACvB,SAAS,GACZ,EAAE,aAAa,+BAsbf"}
|
package/dist/ListView.js
CHANGED
|
@@ -18,7 +18,7 @@ import { useListState } from "./useListState.js";
|
|
|
18
18
|
import { usePlugins } from "@panneau/core";
|
|
19
19
|
import { Alert, Button, Checkbox, Spinner, usePanneauIntl, useToast } from "@panneau/ui";
|
|
20
20
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
-
import { useState } from "react";
|
|
21
|
+
import { useRef, useState } from "react";
|
|
22
22
|
//#region src/ListView.tsx
|
|
23
23
|
function keyOf(action, item) {
|
|
24
24
|
return item !== void 0 ? `${action.id}\u0000${item.id}` : action.id;
|
|
@@ -28,7 +28,7 @@ function keyOf(action, item) {
|
|
|
28
28
|
* cards), with sorting, search, filters and their chips, pagination, selection and actions. It
|
|
29
29
|
* fetches nothing: the caller gives the items of the list's query and their total.
|
|
30
30
|
*/
|
|
31
|
-
function ListView({ list, items, columns, total, isLoading = false, error = null, onRetry, filters = [], fields = [], search = false, layouts: layoutsProp = ["table"], layout: controlledLayout, onLayoutChange, perPageOptions, selectable: selectableProp, actions: actionsProp, onAction, actionIcons, itemTitle, getItemLabel: getItemLabelProp, getItemHref, displays, layoutComponents, filterControls, toolbar, emptyMessage, "aria-label": ariaLabel, className }) {
|
|
31
|
+
function ListView({ list, items, columns, total, isLoading = false, error = null, onRetry, filters = [], fields = [], search = false, layouts: layoutsProp = ["table"], layout: controlledLayout, onLayoutChange, perPageOptions, selectable: selectableProp, actions: actionsProp, onAction, previewAction, actionIcons, itemTitle, getItemLabel: getItemLabelProp, getItemHref, displays, layoutComponents, filterControls, toolbar, emptyMessage, "aria-label": ariaLabel, className }) {
|
|
32
32
|
const intl = usePanneauIntl();
|
|
33
33
|
const toasts = useToast();
|
|
34
34
|
const plugins = usePlugins();
|
|
@@ -36,6 +36,8 @@ function ListView({ list, items, columns, total, isLoading = false, error = null
|
|
|
36
36
|
const filterLabels = useFilterLabels();
|
|
37
37
|
const [uncontrolledLayout, setLayout] = useState(null);
|
|
38
38
|
const [confirming, setConfirming] = useState(null);
|
|
39
|
+
const [preview, setPreview] = useState(null);
|
|
40
|
+
const confirmation = useRef(0);
|
|
39
41
|
const [pending, setPending] = useState(null);
|
|
40
42
|
const label = ariaLabel ?? intl.formatMessage(messages.list);
|
|
41
43
|
const pageItems = items ?? [];
|
|
@@ -84,8 +86,13 @@ function ListView({ list, items, columns, total, isLoading = false, error = null
|
|
|
84
86
|
field: column_0.field ?? null
|
|
85
87
|
});
|
|
86
88
|
};
|
|
87
|
-
const
|
|
89
|
+
const closeConfirmation = () => {
|
|
90
|
+
confirmation.current += 1;
|
|
88
91
|
setConfirming(null);
|
|
92
|
+
setPreview(null);
|
|
93
|
+
};
|
|
94
|
+
const execute = async (event) => {
|
|
95
|
+
closeConfirmation();
|
|
89
96
|
setPending(keyOf(event.action, event.item));
|
|
90
97
|
try {
|
|
91
98
|
const outcome = await onAction?.(event);
|
|
@@ -101,36 +108,56 @@ function ListView({ list, items, columns, total, isLoading = false, error = null
|
|
|
101
108
|
}
|
|
102
109
|
};
|
|
103
110
|
const run = (event_0) => {
|
|
111
|
+
const { action: action_4, selection: selection_0 } = event_0;
|
|
112
|
+
if (previewAction !== void 0 && action_4.dryRun === true && selection_0 !== void 0 && "query" in selection_0) {
|
|
113
|
+
const current = ++confirmation.current;
|
|
114
|
+
const asks = action_4.confirm !== void 0 && action_4.confirm !== false;
|
|
115
|
+
setConfirming(event_0);
|
|
116
|
+
setPreview({ status: "loading" });
|
|
117
|
+
previewAction(event_0).then((result) => {
|
|
118
|
+
if (confirmation.current !== current) return;
|
|
119
|
+
if ((result?.skipped ?? event_0.skipped.length) === 0 && !asks) execute(event_0);
|
|
120
|
+
else setPreview(result !== null ? {
|
|
121
|
+
status: "done",
|
|
122
|
+
...result
|
|
123
|
+
} : null);
|
|
124
|
+
}, () => {
|
|
125
|
+
if (confirmation.current !== current) return;
|
|
126
|
+
if (needsConfirmation(event_0)) setPreview(null);
|
|
127
|
+
else execute(event_0);
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
104
131
|
if (needsConfirmation(event_0)) setConfirming(event_0);
|
|
105
132
|
else execute(event_0);
|
|
106
133
|
};
|
|
107
|
-
const runOnItem = (
|
|
108
|
-
action:
|
|
134
|
+
const runOnItem = (action_5, item_2) => run({
|
|
135
|
+
action: action_5,
|
|
109
136
|
scope: "item",
|
|
110
137
|
item: item_2,
|
|
111
138
|
count: 1,
|
|
112
139
|
skipped: []
|
|
113
140
|
});
|
|
114
|
-
const runOnSelection = (
|
|
141
|
+
const runOnSelection = (action_6) => {
|
|
115
142
|
const selected = list.getSelection();
|
|
116
143
|
if (selected === null) return;
|
|
117
144
|
run({
|
|
118
|
-
action:
|
|
145
|
+
action: action_6,
|
|
119
146
|
scope: "selection",
|
|
120
147
|
selection: selected,
|
|
121
148
|
count: selectedCount,
|
|
122
|
-
skipped: selectedItems.filter((item_3) => !isAllowedOn(
|
|
149
|
+
skipped: selectedItems.filter((item_3) => !isAllowedOn(action_6, item_3))
|
|
123
150
|
});
|
|
124
151
|
};
|
|
125
|
-
const runOnResource = (
|
|
126
|
-
action:
|
|
152
|
+
const runOnResource = (action_7) => run({
|
|
153
|
+
action: action_7,
|
|
127
154
|
scope: "resource",
|
|
128
155
|
count,
|
|
129
156
|
skipped: []
|
|
130
157
|
});
|
|
131
158
|
const renderActions = (item_4) => {
|
|
132
|
-
const allowed = rowActions.filter((
|
|
133
|
-
const menu = rowMenu.filter((
|
|
159
|
+
const allowed = rowActions.filter((action_8) => isAllowedOn(action_8, item_4));
|
|
160
|
+
const menu = rowMenu.filter((action_9) => isAllowedOn(action_9, item_4));
|
|
134
161
|
if (allowed.length === 0 && menu.length === 0) return null;
|
|
135
162
|
const running = pending?.endsWith(`\u0000${item_4.id}`) === true ? pending.split("\0")[0] : null;
|
|
136
163
|
return /* @__PURE__ */ jsx(RowActions, {
|
|
@@ -139,7 +166,7 @@ function ListView({ list, items, columns, total, isLoading = false, error = null
|
|
|
139
166
|
actions: allowed,
|
|
140
167
|
menu,
|
|
141
168
|
pending: running ?? null,
|
|
142
|
-
onRun: (
|
|
169
|
+
onRun: (action_10) => runOnItem(action_10, item_4),
|
|
143
170
|
icons: actionIcons
|
|
144
171
|
});
|
|
145
172
|
};
|
|
@@ -238,14 +265,14 @@ function ListView({ list, items, columns, total, isLoading = false, error = null
|
|
|
238
265
|
onLayoutChange?.(next_0);
|
|
239
266
|
}
|
|
240
267
|
}) : null,
|
|
241
|
-
resourceActions.filter((
|
|
242
|
-
action:
|
|
268
|
+
resourceActions.filter((action_11) => selectedCount === 0 || !hasScope(action_11, "selection")).map((action_12) => /* @__PURE__ */ jsx(ActionButton, {
|
|
269
|
+
action: action_12,
|
|
243
270
|
icons: actionIcons,
|
|
244
271
|
size: "md",
|
|
245
|
-
href: getActionHref(
|
|
246
|
-
isPending: pending === keyOf(
|
|
247
|
-
onPress: () => runOnResource(
|
|
248
|
-
},
|
|
272
|
+
href: getActionHref(action_12),
|
|
273
|
+
isPending: pending === keyOf(action_12),
|
|
274
|
+
onPress: () => runOnResource(action_12)
|
|
275
|
+
}, action_12.id)),
|
|
249
276
|
toolbar
|
|
250
277
|
]
|
|
251
278
|
})]
|
|
@@ -283,17 +310,17 @@ function ListView({ list, items, columns, total, isLoading = false, error = null
|
|
|
283
310
|
}),
|
|
284
311
|
selectionActions.length > 0 ? /* @__PURE__ */ jsx("div", {
|
|
285
312
|
className: ListView_module_default.selectionActions,
|
|
286
|
-
children: selectionActions.map((
|
|
287
|
-
const
|
|
313
|
+
children: selectionActions.map((action_13) => {
|
|
314
|
+
const skipped_0 = selectedItems.filter((item_8) => !isAllowedOn(action_13, item_8)).length;
|
|
288
315
|
return /* @__PURE__ */ jsx(ActionButton, {
|
|
289
|
-
action:
|
|
316
|
+
action: action_13,
|
|
290
317
|
icons: actionIcons,
|
|
291
318
|
size: "md",
|
|
292
319
|
href: null,
|
|
293
|
-
isPending: pending === keyOf(
|
|
294
|
-
isDisabled: pending !== null && pending !== keyOf(
|
|
295
|
-
onPress: () => runOnSelection(
|
|
296
|
-
},
|
|
320
|
+
isPending: pending === keyOf(action_13),
|
|
321
|
+
isDisabled: pending !== null && pending !== keyOf(action_13) || skipped_0 >= selectedCount,
|
|
322
|
+
onPress: () => runOnSelection(action_13)
|
|
323
|
+
}, action_13.id);
|
|
297
324
|
})
|
|
298
325
|
}) : null
|
|
299
326
|
]
|
|
@@ -313,8 +340,9 @@ function ListView({ list, items, columns, total, isLoading = false, error = null
|
|
|
313
340
|
}) : null,
|
|
314
341
|
/* @__PURE__ */ jsx(ConfirmDialog, {
|
|
315
342
|
event: confirming,
|
|
343
|
+
preview,
|
|
316
344
|
onConfirm: (event_1) => void execute(event_1),
|
|
317
|
-
onCancel:
|
|
345
|
+
onCancel: closeConfirmation
|
|
318
346
|
})
|
|
319
347
|
]
|
|
320
348
|
});
|
package/dist/ListView.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListView.js","names":["action","item","column","event","id","next","styles"],"sources":["../src/ListView.tsx"],"sourcesContent":["import { type ReactNode, useState } from 'react';\n\nimport type { List } from '@panneau/core';\nimport { usePlugins } from '@panneau/core';\nimport type { Action, Field, Filter } from '@panneau/protocol';\nimport { Alert, Button, Checkbox, Spinner, usePanneauIntl, useToast } from '@panneau/ui';\n\nimport styles from './ListView.module.css';\nimport { ActionButton } from './actions/ActionButton';\nimport { ConfirmDialog, needsConfirmation } from './actions/ConfirmDialog';\nimport { RowActions } from './actions/RowActions';\nimport {\n type ListActions,\n getActionHref,\n groupListActions,\n hasScope,\n isAllowedOn,\n} from './actions/actions';\nimport type { ActionIcons } from './actions/icons';\nimport { cx } from './cx';\nimport { builtInDisplays } from './displays/displays';\nimport { FilterChips, FilterControls, useFilterLabels } from './filters/Filters';\nimport { builtInLayouts } from './layouts/builtIn';\nimport { messages } from './messages';\nimport { countSelected, getPageSelection, isItemSelected } from './selection';\nimport { LayoutSwitch } from './toolbar/LayoutSwitch';\nimport { Pagination } from './toolbar/Pagination';\nimport { SearchField } from './toolbar/SearchField';\nimport { SortSelect } from './toolbar/SortSelect';\nimport type {\n DisplayEntry,\n FilterEntry,\n ListActionEvent,\n ListActionHandler,\n ListColumn,\n ListItem,\n ListLayoutEntry,\n} from './types';\nimport { useListState } from './useListState';\nimport { getColumnValue, toText } from './values';\n\nexport interface ListViewProps {\n /** The list state of `@panneau/core` (`useList()`): query and selection. */\n list: List;\n /** The items of the current page. `undefined` while they load for the first time. */\n items: readonly ListItem[] | undefined;\n columns: readonly ListColumn[];\n /**\n * How many items match the query, on every page (`meta.total`). With it, the list shows\n * pagination and offers to select every matching item. Without it, `items` is the whole list.\n */\n total?: number | undefined;\n /** Shows that items are loading. Items already shown stay, dimmed. */\n isLoading?: boolean;\n /** An error while loading: an alert with a retry replaces the items. */\n error?: Error | null;\n onRetry?: () => void;\n /** Filter controls, with chips for the active filters. */\n filters?: readonly Filter[];\n /** Fields of the items, for the labels and options of filters that are not columns. */\n fields?: readonly Field[];\n /** Shows a search field (`q`). */\n search?: boolean;\n /** Layouts to choose from, the first one being the default. Defaults to `['table']`. */\n layouts?: readonly string[];\n /** The layout, controlled. */\n layout?: string;\n onLayoutChange?: (layout: string) => void;\n /** Page sizes to choose from. */\n perPageOptions?: readonly number[];\n /** Shows checkboxes. Defaults to whether there are selection actions. */\n selectable?: boolean;\n /**\n * Actions: a list (placed by their `placement`, or by scope), or grouped by placement as\n * `resolveList()` does. Those the user can't run (`abilities.run`) are hidden.\n */\n actions?: ListActions;\n /** Runs an action, after its confirmation. */\n onAction?: ListActionHandler;\n /**\n * Icons of actions by action id (`{ archive: Archive }`), for actions written in code. They\n * win over the Lucide icon an action names (`\"icon\": \"archive\"`).\n */\n actionIcons?: ActionIcons;\n /** The field that titles items, for their label (`itemTitle` of the resource). */\n itemTitle?: string;\n /** The label of an item, for checkboxes and actions. Defaults to its title, then its id. */\n getItemLabel?: (item: ListItem) => string;\n /** A link to an item, on its title. */\n getItemHref?: (item: ListItem) => string | null;\n /** Displays by name, over the built-in ones and those of the plugins. */\n displays?: Readonly<Record<string, DisplayEntry>>;\n /** Layouts by id, over the built-in ones and those of the plugins. */\n layoutComponents?: Readonly<Record<string, ListLayoutEntry>>;\n /** Filter controls by filter type, over the built-in ones. */\n filterControls?: Readonly<Record<string, FilterEntry>>;\n /** Content at the end of the toolbar, such as a button to create an item. */\n toolbar?: ReactNode;\n /** Shown when the list has no items, without filters or search. */\n emptyMessage?: ReactNode;\n /** Accessible name of the list. Defaults to \"Items\". */\n 'aria-label'?: string;\n className?: string;\n}\n\nfunction keyOf(action: Action, item?: ListItem): string {\n return item !== undefined ? `${action.id}\\u0000${item.id}` : action.id;\n}\n\n/**\n * Renders the list state of `@panneau/core`: the items of the current page in a layout (table or\n * cards), with sorting, search, filters and their chips, pagination, selection and actions. It\n * fetches nothing: the caller gives the items of the list's query and their total.\n */\nexport function ListView({\n list,\n items,\n columns,\n total,\n isLoading = false,\n error = null,\n onRetry,\n filters = [],\n fields = [],\n search = false,\n layouts: layoutsProp = ['table'],\n layout: controlledLayout,\n onLayoutChange,\n perPageOptions,\n selectable: selectableProp,\n actions: actionsProp,\n onAction,\n actionIcons,\n itemTitle,\n getItemLabel: getItemLabelProp,\n getItemHref,\n displays,\n layoutComponents,\n filterControls,\n toolbar,\n emptyMessage,\n 'aria-label': ariaLabel,\n className,\n}: ListViewProps) {\n const intl = usePanneauIntl();\n const toasts = useToast();\n const plugins = usePlugins();\n const { query, selection } = useListState(list);\n const filterLabels = useFilterLabels();\n const [uncontrolledLayout, setLayout] = useState<string | null>(null);\n const [confirming, setConfirming] = useState<ListActionEvent | null>(null);\n const [pending, setPending] = useState<string | null>(null);\n\n const label = ariaLabel ?? intl.formatMessage(messages.list);\n const pageItems = items ?? [];\n const count = total ?? pageItems.length;\n const sort = query.sort ?? [];\n const filterValues = query.filter ?? {};\n const hasQuery = Object.keys(filterValues).length > 0 || (query.q ?? '') !== '';\n\n const allDisplays: Record<string, DisplayEntry> = {\n ...builtInDisplays,\n ...(Object.fromEntries(plugins.displays) as Record<string, DisplayEntry>),\n ...displays,\n };\n const allLayouts: Record<string, ListLayoutEntry> = {\n ...builtInLayouts,\n ...(Object.fromEntries(plugins.listLayouts) as Record<string, ListLayoutEntry>),\n ...layoutComponents,\n };\n // Layouts without a component (a plugin's that isn't installed) are left out.\n const available = layoutsProp.filter((id) => allLayouts[id] !== undefined);\n const layouts = available.length > 0 ? available : ['table'];\n const layout = controlledLayout ?? uncontrolledLayout ?? layouts[0] ?? 'table';\n const Layout = allLayouts[layout] ?? builtInLayouts.table;\n\n const grouped = groupListActions(actionsProp);\n const rowActions = grouped.row.filter((action) => hasScope(action, 'item'));\n const rowMenu = grouped.menu.filter((action) => hasScope(action, 'item'));\n // Actions placed in the menu also apply to selections, from the toolbar (once each).\n const toolbarActions = [...grouped.toolbar, ...grouped.menu].filter(\n (action, index, all) =>\n (hasScope(action, 'selection') || hasScope(action, 'resource')) &&\n all.findIndex((candidate) => candidate.id === action.id) === index,\n );\n const selectionActions = toolbarActions.filter((action) => hasScope(action, 'selection'));\n const resourceActions = toolbarActions.filter((action) => hasScope(action, 'resource'));\n const selectable = selectableProp ?? selectionActions.length > 0;\n\n const selectedCount = selectable ? countSelected(selection, count) : 0;\n const selectedItems = pageItems.filter((item) => isItemSelected(selection, item.id));\n const pageSelection = getPageSelection(selection, pageItems);\n\n const getItemLabel = (item: ListItem): string => {\n if (getItemLabelProp !== undefined) {\n return getItemLabelProp(item);\n }\n const titleColumn =\n columns.find((column) => column.id === itemTitle) ??\n (itemTitle === undefined ? columns[0] : undefined);\n const title =\n titleColumn !== undefined\n ? toText(getColumnValue(item, titleColumn, intl.locale), intl.locale)\n : itemTitle !== undefined\n ? toText(item[itemTitle], intl.locale)\n : '';\n return title !== '' ? title : item.id;\n };\n\n const renderValue = (item: ListItem, column: ListColumn): ReactNode => {\n if (column.render !== undefined) {\n return column.render(item);\n }\n const Display =\n (column.display !== undefined && column.display !== null\n ? allDisplays[column.display]\n : undefined) ?? builtInDisplays.text;\n if (Display === undefined) {\n return null;\n }\n return (\n <Display\n value={getColumnValue(item, column, intl.locale)}\n item={item}\n column={column}\n field={column.field ?? null}\n />\n );\n };\n\n const execute = async (event: ListActionEvent) => {\n setConfirming(null);\n setPending(keyOf(event.action, event.item));\n try {\n const outcome = await onAction?.(event);\n // `false`: the action didn't run (its parameters were cancelled).\n if (event.scope === 'selection' && outcome !== false) {\n list.clearSelection();\n }\n } catch (caught) {\n toasts.show({\n title: intl.formatMessage(messages.actionFailed, { action: event.action.label }),\n ...(caught instanceof Error && caught.message !== ''\n ? { description: caught.message }\n : {}),\n tone: 'danger',\n });\n } finally {\n setPending(null);\n }\n };\n\n const run = (event: ListActionEvent) => {\n if (needsConfirmation(event)) {\n setConfirming(event);\n } else {\n void execute(event);\n }\n };\n\n const runOnItem = (action: Action, item: ListItem) =>\n run({ action, scope: 'item', item, count: 1, skipped: [] });\n\n const runOnSelection = (action: Action) => {\n const selected = list.getSelection();\n if (selected === null) {\n return;\n }\n run({\n action,\n scope: 'selection',\n selection: selected,\n count: selectedCount,\n skipped: selectedItems.filter((item) => !isAllowedOn(action, item)),\n });\n };\n\n const runOnResource = (action: Action) =>\n run({ action, scope: 'resource', count: count, skipped: [] });\n\n const renderActions = (item: ListItem): ReactNode => {\n const allowed = rowActions.filter((action) => isAllowedOn(action, item));\n const menu = rowMenu.filter((action) => isAllowedOn(action, item));\n if (allowed.length === 0 && menu.length === 0) {\n return null;\n }\n const running =\n pending?.endsWith(`\\u0000${item.id}`) === true ? pending.split('\\u0000')[0] : null;\n return (\n <RowActions\n item={item}\n itemLabel={getItemLabel(item)}\n actions={allowed}\n menu={menu}\n pending={running ?? null}\n onRun={(action) => runOnItem(action, item)}\n icons={actionIcons}\n />\n );\n };\n\n const pageCheckbox = (\n <Checkbox\n aria-label={intl.formatMessage(messages.selectPage)}\n isSelected={pageSelection === 'all'}\n isIndeterminate={pageSelection === 'some'}\n isDisabled={pageItems.length === 0}\n onChange={() => {\n const ids = pageItems.map((item) => item.id);\n if (pageSelection === 'all') {\n list.deselect(ids);\n } else {\n list.select(ids);\n }\n }}\n />\n );\n\n const sortable = columns.some((column) => column.sortable === true);\n const showToolbar =\n search ||\n filters.length > 0 ||\n sortable ||\n layouts.length > 1 ||\n resourceActions.length > 0 ||\n toolbar !== undefined;\n\n const body = (() => {\n if (error !== null) {\n return (\n <Alert\n tone=\"danger\"\n title={intl.formatMessage(messages.loadFailed)}\n actions={\n onRetry !== undefined ? (\n <Button size=\"sm\" onPress={onRetry}>\n {intl.formatMessage(messages.retry)}\n </Button>\n ) : undefined\n }\n >\n {error.message}\n </Alert>\n );\n }\n if (items === undefined) {\n return (\n <div className={styles.state}>\n <Spinner showLabel />\n </div>\n );\n }\n if (pageItems.length === 0) {\n return (\n <div className={styles.state}>\n <p>\n {hasQuery\n ? intl.formatMessage(messages.noResults)\n : (emptyMessage ?? intl.formatMessage(messages.empty))}\n </p>\n {hasQuery ? (\n <Button size=\"sm\" onPress={() => clearFilters()}>\n {intl.formatMessage(messages.clearFilters)}\n </Button>\n ) : null}\n </div>\n );\n }\n return (\n <Layout\n items={pageItems}\n columns={columns}\n renderValue={renderValue}\n getItemLabel={getItemLabel}\n getItemHref={getItemHref}\n selectable={selectable}\n isSelected={(item) => isItemSelected(selection, item.id)}\n onToggle={(item) => list.toggle(item.id)}\n renderActions={renderActions}\n sort={sort}\n onSort={(column) => list.toggleSort(column.id)}\n label={label}\n pageSelection={pageCheckbox}\n />\n );\n })();\n\n function clearFilters() {\n Object.keys(filterValues).forEach((id) => list.setFilter(id, undefined));\n if ((query.q ?? '') !== '') {\n list.setSearch('');\n }\n }\n\n return (\n <div className={cx(styles.list, className)}>\n {showToolbar ? (\n <div className={styles.toolbar}>\n <div className={styles.toolbarStart}>\n {search ? (\n <SearchField\n value={query.q ?? ''}\n onChange={(q) => list.setSearch(q)}\n />\n ) : null}\n {filters.length > 0 ? (\n <FilterControls\n filters={filters}\n values={filterValues}\n onChange={(id, value) => list.setFilter(id, value)}\n onLabel={filterLabels.remember}\n controls={filterControls}\n columns={columns}\n fields={fields}\n labels={filterLabels.labels}\n />\n ) : null}\n </div>\n <div className={styles.toolbarEnd}>\n {sortable ? (\n <div className={layout === 'table' ? styles.tableSort : undefined}>\n <SortSelect\n columns={columns}\n sort={sort}\n onChange={(next) => list.setSort(next)}\n />\n </div>\n ) : null}\n {layouts.length > 1 ? (\n <LayoutSwitch\n layouts={layouts}\n value={layout}\n onChange={(next) => {\n setLayout(next);\n onLayoutChange?.(next);\n }}\n />\n ) : null}\n {resourceActions\n .filter(\n (action) => selectedCount === 0 || !hasScope(action, 'selection'),\n )\n .map((action) => (\n <ActionButton\n key={action.id}\n action={action}\n icons={actionIcons}\n size=\"md\"\n href={getActionHref(action)}\n isPending={pending === keyOf(action)}\n onPress={() => runOnResource(action)}\n />\n ))}\n {toolbar}\n </div>\n </div>\n ) : null}\n <FilterChips\n filters={filters}\n values={filterValues}\n q={query.q}\n onRemove={(id) => list.setFilter(id, undefined)}\n onClearSearch={() => list.setSearch('')}\n onClear={clearFilters}\n columns={columns}\n fields={fields}\n labels={filterLabels.labels}\n />\n {selectedCount > 0 ? (\n <div className={styles.selection}>\n <span className={styles.selectionCount} role=\"status\">\n {selection.mode === 'all'\n ? intl.formatMessage(messages.allMatchingSelected, {\n total: selectedCount,\n })\n : intl.formatMessage(messages.selectedCount, { count: selectedCount })}\n </span>\n {selection.mode === 'ids' &&\n pageSelection === 'all' &&\n count > pageItems.length ? (\n <Button size=\"sm\" variant=\"ghost\" onPress={() => list.selectAllMatching()}>\n {intl.formatMessage(messages.selectAllMatching, { total: count })}\n </Button>\n ) : null}\n <Button size=\"sm\" variant=\"ghost\" onPress={() => list.clearSelection()}>\n {intl.formatMessage(messages.clearSelection)}\n </Button>\n {selectionActions.length > 0 ? (\n <div className={styles.selectionActions}>\n {selectionActions.map((action) => {\n const skipped = selectedItems.filter(\n (item) => !isAllowedOn(action, item),\n ).length;\n return (\n <ActionButton\n key={action.id}\n action={action}\n icons={actionIcons}\n size=\"md\"\n href={null}\n isPending={pending === keyOf(action)}\n isDisabled={\n (pending !== null && pending !== keyOf(action)) ||\n skipped >= selectedCount\n }\n onPress={() => runOnSelection(action)}\n />\n );\n })}\n </div>\n ) : null}\n </div>\n ) : null}\n <div className={styles.body} aria-busy={isLoading && items !== undefined}>\n {body}\n </div>\n {total !== undefined && error === null && total > 0 ? (\n <Pagination\n page={query.page ?? 1}\n perPage={query.perPage ?? pageItems.length}\n total={total}\n onPageChange={(page) => list.setPage(page)}\n perPageOptions={perPageOptions}\n onPerPageChange={(perPage) => list.setPerPage(perPage)}\n />\n ) : null}\n <ConfirmDialog\n event={confirming}\n onConfirm={(event) => void execute(event)}\n onCancel={() => setConfirming(null)}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ListView.js","names":["action","item","column","event","selection","id","next","skipped","styles"],"sources":["../src/ListView.tsx"],"sourcesContent":["import { type ReactNode, useRef, useState } from 'react';\n\nimport type { List } from '@panneau/core';\nimport { usePlugins } from '@panneau/core';\nimport type { Action, Field, Filter } from '@panneau/protocol';\nimport { Alert, Button, Checkbox, Spinner, usePanneauIntl, useToast } from '@panneau/ui';\n\nimport styles from './ListView.module.css';\nimport { ActionButton } from './actions/ActionButton';\nimport { ConfirmDialog, type ConfirmPreview, needsConfirmation } from './actions/ConfirmDialog';\nimport { RowActions } from './actions/RowActions';\nimport {\n type ListActions,\n getActionHref,\n groupListActions,\n hasScope,\n isAllowedOn,\n} from './actions/actions';\nimport type { ActionIcons } from './actions/icons';\nimport { cx } from './cx';\nimport { builtInDisplays } from './displays/displays';\nimport { FilterChips, FilterControls, useFilterLabels } from './filters/Filters';\nimport { builtInLayouts } from './layouts/builtIn';\nimport { messages } from './messages';\nimport { countSelected, getPageSelection, isItemSelected } from './selection';\nimport { LayoutSwitch } from './toolbar/LayoutSwitch';\nimport { Pagination } from './toolbar/Pagination';\nimport { SearchField } from './toolbar/SearchField';\nimport { SortSelect } from './toolbar/SortSelect';\nimport type {\n DisplayEntry,\n FilterEntry,\n ListActionEvent,\n ListActionHandler,\n ListActionPreviewer,\n ListColumn,\n ListItem,\n ListLayoutEntry,\n} from './types';\nimport { useListState } from './useListState';\nimport { getColumnValue, toText } from './values';\n\nexport interface ListViewProps {\n /** The list state of `@panneau/core` (`useList()`): query and selection. */\n list: List;\n /** The items of the current page. `undefined` while they load for the first time. */\n items: readonly ListItem[] | undefined;\n columns: readonly ListColumn[];\n /**\n * How many items match the query, on every page (`meta.total`). With it, the list shows\n * pagination and offers to select every matching item. Without it, `items` is the whole list.\n */\n total?: number | undefined;\n /** Shows that items are loading. Items already shown stay, dimmed. */\n isLoading?: boolean;\n /** An error while loading: an alert with a retry replaces the items. */\n error?: Error | null;\n onRetry?: () => void;\n /** Filter controls, with chips for the active filters. */\n filters?: readonly Filter[];\n /** Fields of the items, for the labels and options of filters that are not columns. */\n fields?: readonly Field[];\n /** Shows a search field (`q`). */\n search?: boolean;\n /** Layouts to choose from, the first one being the default. Defaults to `['table']`. */\n layouts?: readonly string[];\n /** The layout, controlled. */\n layout?: string;\n onLayoutChange?: (layout: string) => void;\n /** Page sizes to choose from. */\n perPageOptions?: readonly number[];\n /** Shows checkboxes. Defaults to whether there are selection actions. */\n selectable?: boolean;\n /**\n * Actions: a list (placed by their `placement`, or by scope), or grouped by placement as\n * `resolveList()` does. Those the user can't run (`abilities.run`) are hidden.\n */\n actions?: ListActions;\n /** Runs an action, after its confirmation. */\n onAction?: ListActionHandler;\n /**\n * Asks the backend how many items of a query selection (\"Select all N items\") an action will\n * skip, before the confirmation: the list only knows the abilities of the current page. Only\n * for actions with `dryRun`. `useActionPreview()` of `@panneau/actions` is one.\n */\n previewAction?: ListActionPreviewer;\n /**\n * Icons of actions by action id (`{ archive: Archive }`), for actions written in code. They\n * win over the Lucide icon an action names (`\"icon\": \"archive\"`).\n */\n actionIcons?: ActionIcons;\n /** The field that titles items, for their label (`itemTitle` of the resource). */\n itemTitle?: string;\n /** The label of an item, for checkboxes and actions. Defaults to its title, then its id. */\n getItemLabel?: (item: ListItem) => string;\n /** A link to an item, on its title. */\n getItemHref?: (item: ListItem) => string | null;\n /** Displays by name, over the built-in ones and those of the plugins. */\n displays?: Readonly<Record<string, DisplayEntry>>;\n /** Layouts by id, over the built-in ones and those of the plugins. */\n layoutComponents?: Readonly<Record<string, ListLayoutEntry>>;\n /** Filter controls by filter type, over the built-in ones. */\n filterControls?: Readonly<Record<string, FilterEntry>>;\n /** Content at the end of the toolbar, such as a button to create an item. */\n toolbar?: ReactNode;\n /** Shown when the list has no items, without filters or search. */\n emptyMessage?: ReactNode;\n /** Accessible name of the list. Defaults to \"Items\". */\n 'aria-label'?: string;\n className?: string;\n}\n\nfunction keyOf(action: Action, item?: ListItem): string {\n return item !== undefined ? `${action.id}\\u0000${item.id}` : action.id;\n}\n\n/**\n * Renders the list state of `@panneau/core`: the items of the current page in a layout (table or\n * cards), with sorting, search, filters and their chips, pagination, selection and actions. It\n * fetches nothing: the caller gives the items of the list's query and their total.\n */\nexport function ListView({\n list,\n items,\n columns,\n total,\n isLoading = false,\n error = null,\n onRetry,\n filters = [],\n fields = [],\n search = false,\n layouts: layoutsProp = ['table'],\n layout: controlledLayout,\n onLayoutChange,\n perPageOptions,\n selectable: selectableProp,\n actions: actionsProp,\n onAction,\n previewAction,\n actionIcons,\n itemTitle,\n getItemLabel: getItemLabelProp,\n getItemHref,\n displays,\n layoutComponents,\n filterControls,\n toolbar,\n emptyMessage,\n 'aria-label': ariaLabel,\n className,\n}: ListViewProps) {\n const intl = usePanneauIntl();\n const toasts = useToast();\n const plugins = usePlugins();\n const { query, selection } = useListState(list);\n const filterLabels = useFilterLabels();\n const [uncontrolledLayout, setLayout] = useState<string | null>(null);\n const [confirming, setConfirming] = useState<ListActionEvent | null>(null);\n const [preview, setPreview] = useState<ConfirmPreview | null>(null);\n // The confirmation a dry run belongs to: a later one, or a cancel, discards its answer.\n const confirmation = useRef(0);\n const [pending, setPending] = useState<string | null>(null);\n\n const label = ariaLabel ?? intl.formatMessage(messages.list);\n const pageItems = items ?? [];\n const count = total ?? pageItems.length;\n const sort = query.sort ?? [];\n const filterValues = query.filter ?? {};\n const hasQuery = Object.keys(filterValues).length > 0 || (query.q ?? '') !== '';\n\n const allDisplays: Record<string, DisplayEntry> = {\n ...builtInDisplays,\n ...(Object.fromEntries(plugins.displays) as Record<string, DisplayEntry>),\n ...displays,\n };\n const allLayouts: Record<string, ListLayoutEntry> = {\n ...builtInLayouts,\n ...(Object.fromEntries(plugins.listLayouts) as Record<string, ListLayoutEntry>),\n ...layoutComponents,\n };\n // Layouts without a component (a plugin's that isn't installed) are left out.\n const available = layoutsProp.filter((id) => allLayouts[id] !== undefined);\n const layouts = available.length > 0 ? available : ['table'];\n const layout = controlledLayout ?? uncontrolledLayout ?? layouts[0] ?? 'table';\n const Layout = allLayouts[layout] ?? builtInLayouts.table;\n\n const grouped = groupListActions(actionsProp);\n const rowActions = grouped.row.filter((action) => hasScope(action, 'item'));\n const rowMenu = grouped.menu.filter((action) => hasScope(action, 'item'));\n // Actions placed in the menu also apply to selections, from the toolbar (once each).\n const toolbarActions = [...grouped.toolbar, ...grouped.menu].filter(\n (action, index, all) =>\n (hasScope(action, 'selection') || hasScope(action, 'resource')) &&\n all.findIndex((candidate) => candidate.id === action.id) === index,\n );\n const selectionActions = toolbarActions.filter((action) => hasScope(action, 'selection'));\n const resourceActions = toolbarActions.filter((action) => hasScope(action, 'resource'));\n const selectable = selectableProp ?? selectionActions.length > 0;\n\n const selectedCount = selectable ? countSelected(selection, count) : 0;\n const selectedItems = pageItems.filter((item) => isItemSelected(selection, item.id));\n const pageSelection = getPageSelection(selection, pageItems);\n\n const getItemLabel = (item: ListItem): string => {\n if (getItemLabelProp !== undefined) {\n return getItemLabelProp(item);\n }\n const titleColumn =\n columns.find((column) => column.id === itemTitle) ??\n (itemTitle === undefined ? columns[0] : undefined);\n const title =\n titleColumn !== undefined\n ? toText(getColumnValue(item, titleColumn, intl.locale), intl.locale)\n : itemTitle !== undefined\n ? toText(item[itemTitle], intl.locale)\n : '';\n return title !== '' ? title : item.id;\n };\n\n const renderValue = (item: ListItem, column: ListColumn): ReactNode => {\n if (column.render !== undefined) {\n return column.render(item);\n }\n const Display =\n (column.display !== undefined && column.display !== null\n ? allDisplays[column.display]\n : undefined) ?? builtInDisplays.text;\n if (Display === undefined) {\n return null;\n }\n return (\n <Display\n value={getColumnValue(item, column, intl.locale)}\n item={item}\n column={column}\n field={column.field ?? null}\n />\n );\n };\n\n const closeConfirmation = () => {\n confirmation.current += 1;\n setConfirming(null);\n setPreview(null);\n };\n\n const execute = async (event: ListActionEvent) => {\n closeConfirmation();\n setPending(keyOf(event.action, event.item));\n try {\n const outcome = await onAction?.(event);\n // `false`: the action didn't run (its parameters were cancelled).\n if (event.scope === 'selection' && outcome !== false) {\n list.clearSelection();\n }\n } catch (caught) {\n toasts.show({\n title: intl.formatMessage(messages.actionFailed, { action: event.action.label }),\n ...(caught instanceof Error && caught.message !== ''\n ? { description: caught.message }\n : {}),\n tone: 'danger',\n });\n } finally {\n setPending(null);\n }\n };\n\n const run = (event: ListActionEvent) => {\n const { action, selection } = event;\n if (\n previewAction !== undefined &&\n action.dryRun === true &&\n selection !== undefined &&\n 'query' in selection\n ) {\n // Every item matching a query: only the backend knows how many it would skip.\n const current = ++confirmation.current;\n const asks = action.confirm !== undefined && action.confirm !== false;\n setConfirming(event);\n setPreview({ status: 'loading' });\n previewAction(event).then(\n (result) => {\n if (confirmation.current !== current) {\n return;\n }\n const skipped = result?.skipped ?? event.skipped.length;\n if (skipped === 0 && !asks) {\n void execute(event);\n } else {\n setPreview(result !== null ? { status: 'done', ...result } : null);\n }\n },\n () => {\n // Without an answer, the list confirms with what it knows.\n if (confirmation.current !== current) {\n return;\n }\n if (needsConfirmation(event)) {\n setPreview(null);\n } else {\n void execute(event);\n }\n },\n );\n return;\n }\n if (needsConfirmation(event)) {\n setConfirming(event);\n } else {\n void execute(event);\n }\n };\n\n const runOnItem = (action: Action, item: ListItem) =>\n run({ action, scope: 'item', item, count: 1, skipped: [] });\n\n const runOnSelection = (action: Action) => {\n const selected = list.getSelection();\n if (selected === null) {\n return;\n }\n run({\n action,\n scope: 'selection',\n selection: selected,\n count: selectedCount,\n skipped: selectedItems.filter((item) => !isAllowedOn(action, item)),\n });\n };\n\n const runOnResource = (action: Action) =>\n run({ action, scope: 'resource', count: count, skipped: [] });\n\n const renderActions = (item: ListItem): ReactNode => {\n const allowed = rowActions.filter((action) => isAllowedOn(action, item));\n const menu = rowMenu.filter((action) => isAllowedOn(action, item));\n if (allowed.length === 0 && menu.length === 0) {\n return null;\n }\n const running =\n pending?.endsWith(`\\u0000${item.id}`) === true ? pending.split('\\u0000')[0] : null;\n return (\n <RowActions\n item={item}\n itemLabel={getItemLabel(item)}\n actions={allowed}\n menu={menu}\n pending={running ?? null}\n onRun={(action) => runOnItem(action, item)}\n icons={actionIcons}\n />\n );\n };\n\n const pageCheckbox = (\n <Checkbox\n aria-label={intl.formatMessage(messages.selectPage)}\n isSelected={pageSelection === 'all'}\n isIndeterminate={pageSelection === 'some'}\n isDisabled={pageItems.length === 0}\n onChange={() => {\n const ids = pageItems.map((item) => item.id);\n if (pageSelection === 'all') {\n list.deselect(ids);\n } else {\n list.select(ids);\n }\n }}\n />\n );\n\n const sortable = columns.some((column) => column.sortable === true);\n const showToolbar =\n search ||\n filters.length > 0 ||\n sortable ||\n layouts.length > 1 ||\n resourceActions.length > 0 ||\n toolbar !== undefined;\n\n const body = (() => {\n if (error !== null) {\n return (\n <Alert\n tone=\"danger\"\n title={intl.formatMessage(messages.loadFailed)}\n actions={\n onRetry !== undefined ? (\n <Button size=\"sm\" onPress={onRetry}>\n {intl.formatMessage(messages.retry)}\n </Button>\n ) : undefined\n }\n >\n {error.message}\n </Alert>\n );\n }\n if (items === undefined) {\n return (\n <div className={styles.state}>\n <Spinner showLabel />\n </div>\n );\n }\n if (pageItems.length === 0) {\n return (\n <div className={styles.state}>\n <p>\n {hasQuery\n ? intl.formatMessage(messages.noResults)\n : (emptyMessage ?? intl.formatMessage(messages.empty))}\n </p>\n {hasQuery ? (\n <Button size=\"sm\" onPress={() => clearFilters()}>\n {intl.formatMessage(messages.clearFilters)}\n </Button>\n ) : null}\n </div>\n );\n }\n return (\n <Layout\n items={pageItems}\n columns={columns}\n renderValue={renderValue}\n getItemLabel={getItemLabel}\n getItemHref={getItemHref}\n selectable={selectable}\n isSelected={(item) => isItemSelected(selection, item.id)}\n onToggle={(item) => list.toggle(item.id)}\n renderActions={renderActions}\n sort={sort}\n onSort={(column) => list.toggleSort(column.id)}\n label={label}\n pageSelection={pageCheckbox}\n />\n );\n })();\n\n function clearFilters() {\n Object.keys(filterValues).forEach((id) => list.setFilter(id, undefined));\n if ((query.q ?? '') !== '') {\n list.setSearch('');\n }\n }\n\n return (\n <div className={cx(styles.list, className)}>\n {showToolbar ? (\n <div className={styles.toolbar}>\n <div className={styles.toolbarStart}>\n {search ? (\n <SearchField\n value={query.q ?? ''}\n onChange={(q) => list.setSearch(q)}\n />\n ) : null}\n {filters.length > 0 ? (\n <FilterControls\n filters={filters}\n values={filterValues}\n onChange={(id, value) => list.setFilter(id, value)}\n onLabel={filterLabels.remember}\n controls={filterControls}\n columns={columns}\n fields={fields}\n labels={filterLabels.labels}\n />\n ) : null}\n </div>\n <div className={styles.toolbarEnd}>\n {sortable ? (\n <div className={layout === 'table' ? styles.tableSort : undefined}>\n <SortSelect\n columns={columns}\n sort={sort}\n onChange={(next) => list.setSort(next)}\n />\n </div>\n ) : null}\n {layouts.length > 1 ? (\n <LayoutSwitch\n layouts={layouts}\n value={layout}\n onChange={(next) => {\n setLayout(next);\n onLayoutChange?.(next);\n }}\n />\n ) : null}\n {resourceActions\n .filter(\n (action) => selectedCount === 0 || !hasScope(action, 'selection'),\n )\n .map((action) => (\n <ActionButton\n key={action.id}\n action={action}\n icons={actionIcons}\n size=\"md\"\n href={getActionHref(action)}\n isPending={pending === keyOf(action)}\n onPress={() => runOnResource(action)}\n />\n ))}\n {toolbar}\n </div>\n </div>\n ) : null}\n <FilterChips\n filters={filters}\n values={filterValues}\n q={query.q}\n onRemove={(id) => list.setFilter(id, undefined)}\n onClearSearch={() => list.setSearch('')}\n onClear={clearFilters}\n columns={columns}\n fields={fields}\n labels={filterLabels.labels}\n />\n {selectedCount > 0 ? (\n <div className={styles.selection}>\n <span className={styles.selectionCount} role=\"status\">\n {selection.mode === 'all'\n ? intl.formatMessage(messages.allMatchingSelected, {\n total: selectedCount,\n })\n : intl.formatMessage(messages.selectedCount, { count: selectedCount })}\n </span>\n {selection.mode === 'ids' &&\n pageSelection === 'all' &&\n count > pageItems.length ? (\n <Button size=\"sm\" variant=\"ghost\" onPress={() => list.selectAllMatching()}>\n {intl.formatMessage(messages.selectAllMatching, { total: count })}\n </Button>\n ) : null}\n <Button size=\"sm\" variant=\"ghost\" onPress={() => list.clearSelection()}>\n {intl.formatMessage(messages.clearSelection)}\n </Button>\n {selectionActions.length > 0 ? (\n <div className={styles.selectionActions}>\n {selectionActions.map((action) => {\n const skipped = selectedItems.filter(\n (item) => !isAllowedOn(action, item),\n ).length;\n return (\n <ActionButton\n key={action.id}\n action={action}\n icons={actionIcons}\n size=\"md\"\n href={null}\n isPending={pending === keyOf(action)}\n isDisabled={\n (pending !== null && pending !== keyOf(action)) ||\n skipped >= selectedCount\n }\n onPress={() => runOnSelection(action)}\n />\n );\n })}\n </div>\n ) : null}\n </div>\n ) : null}\n <div className={styles.body} aria-busy={isLoading && items !== undefined}>\n {body}\n </div>\n {total !== undefined && error === null && total > 0 ? (\n <Pagination\n page={query.page ?? 1}\n perPage={query.perPage ?? pageItems.length}\n total={total}\n onPageChange={(page) => list.setPage(page)}\n perPageOptions={perPageOptions}\n onPerPageChange={(perPage) => list.setPerPage(perPage)}\n />\n ) : null}\n <ConfirmDialog\n event={confirming}\n preview={preview}\n onConfirm={(event) => void execute(event)}\n onCancel={closeConfirmation}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import type { ListActionEvent } from '../types';
|
|
1
|
+
import type { ListActionEvent, ListActionPreview } from '../types';
|
|
2
2
|
/** Whether an action needs a confirmation: it asks for one, or some selected items are skipped. */
|
|
3
3
|
export declare function needsConfirmation(event: ListActionEvent): boolean;
|
|
4
|
+
/** The dry run of a query selection: in progress, or done with the backend's count. */
|
|
5
|
+
export type ConfirmPreview = {
|
|
6
|
+
status: 'loading';
|
|
7
|
+
} | ({
|
|
8
|
+
status: 'done';
|
|
9
|
+
} & ListActionPreview);
|
|
4
10
|
export interface ConfirmDialogProps {
|
|
5
11
|
/** The action to confirm, or `null` when the dialog is closed. */
|
|
6
12
|
event: ListActionEvent | null;
|
|
13
|
+
/**
|
|
14
|
+
* What the backend told about a query selection. Its count of skipped items replaces the
|
|
15
|
+
* count of the current page; while it loads, the dialog says so and can't be confirmed.
|
|
16
|
+
*/
|
|
17
|
+
preview?: ConfirmPreview | null;
|
|
7
18
|
onConfirm: (event: ListActionEvent) => void;
|
|
8
19
|
onCancel: () => void;
|
|
9
20
|
}
|
|
10
21
|
/**
|
|
11
22
|
* Asks before running an action (decision 0017): the action's `confirm` title and message (an ICU
|
|
12
|
-
* message with `{count}`), or default ones, and how many selected items will be skipped
|
|
23
|
+
* message with `{count}`), or default ones, and how many selected items will be skipped: those of
|
|
24
|
+
* the current page that don't allow it, or the backend's count for every item matching a query.
|
|
13
25
|
*/
|
|
14
|
-
export declare function ConfirmDialog({ event, onConfirm, onCancel }: ConfirmDialogProps): import("react").JSX.Element | null;
|
|
26
|
+
export declare function ConfirmDialog({ event, preview, onConfirm, onCancel }: ConfirmDialogProps): import("react").JSX.Element | null;
|
|
15
27
|
//# sourceMappingURL=ConfirmDialog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmDialog.d.ts","sourceRoot":"","sources":["../../src/actions/ConfirmDialog.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfirmDialog.d.ts","sourceRoot":"","sources":["../../src/actions/ConfirmDialog.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAoBnE,mGAAmG;AACnG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAGjE;AAED,uFAAuF;AACvF,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,iBAAiB,CAAC,CAAC;AAE9F,MAAM,WAAW,kBAAkB;IAC/B,kEAAkE;IAClE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,OAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,kBAAkB,sCA8D/F"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { messages } from "../messages.js";
|
|
2
2
|
import Actions_module_default from "./Actions.module.js";
|
|
3
|
-
import {
|
|
4
|
-
import { Button, Dialog, usePanneauIntl } from "@panneau/ui";
|
|
3
|
+
import { Button, Dialog, Spinner, usePanneauIntl } from "@panneau/ui";
|
|
5
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
5
|
//#region src/actions/ConfirmDialog.tsx
|
|
7
6
|
/** Formats an ICU message of the definition (`{count, plural, …}`) in the locale. */
|
|
@@ -19,137 +18,55 @@ function needsConfirmation(event) {
|
|
|
19
18
|
}
|
|
20
19
|
/**
|
|
21
20
|
* Asks before running an action (decision 0017): the action's `confirm` title and message (an ICU
|
|
22
|
-
* message with `{count}`), or default ones, and how many selected items will be skipped
|
|
21
|
+
* message with `{count}`), or default ones, and how many selected items will be skipped: those of
|
|
22
|
+
* the current page that don't allow it, or the backend's count for every item matching a query.
|
|
23
23
|
*/
|
|
24
|
-
function ConfirmDialog(
|
|
25
|
-
const $ = c(40);
|
|
26
|
-
const { event, onConfirm, onCancel } = t0;
|
|
24
|
+
function ConfirmDialog({ event, preview = null, onConfirm, onCancel }) {
|
|
27
25
|
const intl = usePanneauIntl();
|
|
28
26
|
if (event === null) return null;
|
|
29
|
-
const { action, count
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
t3 = true;
|
|
48
|
-
if ($[18] !== onCancel) {
|
|
49
|
-
t4 = (open) => {
|
|
50
|
-
if (!open) onCancel();
|
|
51
|
-
};
|
|
52
|
-
$[18] = onCancel;
|
|
53
|
-
$[19] = t4;
|
|
54
|
-
} else t4 = $[19];
|
|
55
|
-
t5 = "alertdialog";
|
|
56
|
-
t6 = false;
|
|
57
|
-
t7 = "sm";
|
|
58
|
-
t8 = confirm.title !== void 0 ? formatDefinitionMessage(intl, confirm.title, values) : intl.formatMessage(messages.confirmTitle, { action: action.label });
|
|
59
|
-
let t10;
|
|
60
|
-
if ($[20] !== event || $[21] !== onConfirm) {
|
|
61
|
-
t10 = () => onConfirm(event);
|
|
62
|
-
$[20] = event;
|
|
63
|
-
$[21] = onConfirm;
|
|
64
|
-
$[22] = t10;
|
|
65
|
-
} else t10 = $[22];
|
|
66
|
-
t9 = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
27
|
+
const { action, count } = event;
|
|
28
|
+
const loading = preview?.status === "loading";
|
|
29
|
+
const skipped = preview?.status === "done" ? preview.skipped : event.skipped.length;
|
|
30
|
+
const confirm = typeof action.confirm === "object" ? action.confirm : {};
|
|
31
|
+
const values = {
|
|
32
|
+
count,
|
|
33
|
+
action: action.label
|
|
34
|
+
};
|
|
35
|
+
return /* @__PURE__ */ jsxs(Dialog, {
|
|
36
|
+
isOpen: true,
|
|
37
|
+
onOpenChange: (open) => {
|
|
38
|
+
if (!open) onCancel();
|
|
39
|
+
},
|
|
40
|
+
role: "alertdialog",
|
|
41
|
+
isDismissable: false,
|
|
42
|
+
size: "sm",
|
|
43
|
+
title: confirm.title !== void 0 ? formatDefinitionMessage(intl, confirm.title, values) : intl.formatMessage(messages.confirmTitle, { action: action.label }),
|
|
44
|
+
footer: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
67
45
|
variant: "ghost",
|
|
68
46
|
onPress: onCancel,
|
|
69
47
|
children: confirm.cancelLabel ?? intl.formatMessage(messages.cancel)
|
|
70
48
|
}), /* @__PURE__ */ jsx(Button, {
|
|
71
49
|
variant: action.destructive === true ? "danger" : "primary",
|
|
72
50
|
autoFocus: true,
|
|
73
|
-
|
|
51
|
+
isDisabled: loading,
|
|
52
|
+
onPress: () => onConfirm(event),
|
|
74
53
|
children: confirm.confirmLabel ?? action.label
|
|
75
|
-
})] })
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
$[9] = t1;
|
|
88
|
-
$[10] = t2;
|
|
89
|
-
$[11] = t3;
|
|
90
|
-
$[12] = t4;
|
|
91
|
-
$[13] = t5;
|
|
92
|
-
$[14] = t6;
|
|
93
|
-
$[15] = t7;
|
|
94
|
-
$[16] = t8;
|
|
95
|
-
$[17] = t9;
|
|
96
|
-
} else {
|
|
97
|
-
T0 = $[8];
|
|
98
|
-
t1 = $[9];
|
|
99
|
-
t2 = $[10];
|
|
100
|
-
t3 = $[11];
|
|
101
|
-
t4 = $[12];
|
|
102
|
-
t5 = $[13];
|
|
103
|
-
t6 = $[14];
|
|
104
|
-
t7 = $[15];
|
|
105
|
-
t8 = $[16];
|
|
106
|
-
t9 = $[17];
|
|
107
|
-
}
|
|
108
|
-
let t10;
|
|
109
|
-
if ($[23] !== t1 || $[24] !== t2) {
|
|
110
|
-
t10 = /* @__PURE__ */ jsx("p", {
|
|
111
|
-
className: t1,
|
|
112
|
-
children: t2
|
|
113
|
-
});
|
|
114
|
-
$[23] = t1;
|
|
115
|
-
$[24] = t2;
|
|
116
|
-
$[25] = t10;
|
|
117
|
-
} else t10 = $[25];
|
|
118
|
-
let t11;
|
|
119
|
-
if ($[26] !== intl || $[27] !== skipped.length) {
|
|
120
|
-
t11 = skipped.length > 0 ? /* @__PURE__ */ jsx("p", {
|
|
54
|
+
})] }),
|
|
55
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
56
|
+
className: Actions_module_default.message,
|
|
57
|
+
children: confirm.message !== void 0 ? formatDefinitionMessage(intl, confirm.message, values) : intl.formatMessage(messages.confirmMessage, values)
|
|
58
|
+
}), loading ? /* @__PURE__ */ jsx("div", {
|
|
59
|
+
className: Actions_module_default.skipped,
|
|
60
|
+
children: /* @__PURE__ */ jsx(Spinner, {
|
|
61
|
+
size: "sm",
|
|
62
|
+
label: intl.formatMessage(messages.confirmChecking),
|
|
63
|
+
showLabel: true
|
|
64
|
+
})
|
|
65
|
+
}) : skipped > 0 ? /* @__PURE__ */ jsx("p", {
|
|
121
66
|
className: Actions_module_default.skipped,
|
|
122
|
-
children: intl.formatMessage(messages.confirmSkipped, { count: skipped
|
|
123
|
-
}) : null
|
|
124
|
-
|
|
125
|
-
$[27] = skipped.length;
|
|
126
|
-
$[28] = t11;
|
|
127
|
-
} else t11 = $[28];
|
|
128
|
-
let t12;
|
|
129
|
-
if ($[29] !== T0 || $[30] !== t10 || $[31] !== t11 || $[32] !== t3 || $[33] !== t4 || $[34] !== t5 || $[35] !== t6 || $[36] !== t7 || $[37] !== t8 || $[38] !== t9) {
|
|
130
|
-
t12 = /* @__PURE__ */ jsxs(T0, {
|
|
131
|
-
isOpen: t3,
|
|
132
|
-
onOpenChange: t4,
|
|
133
|
-
role: t5,
|
|
134
|
-
isDismissable: t6,
|
|
135
|
-
size: t7,
|
|
136
|
-
title: t8,
|
|
137
|
-
footer: t9,
|
|
138
|
-
children: [t10, t11]
|
|
139
|
-
});
|
|
140
|
-
$[29] = T0;
|
|
141
|
-
$[30] = t10;
|
|
142
|
-
$[31] = t11;
|
|
143
|
-
$[32] = t3;
|
|
144
|
-
$[33] = t4;
|
|
145
|
-
$[34] = t5;
|
|
146
|
-
$[35] = t6;
|
|
147
|
-
$[36] = t7;
|
|
148
|
-
$[37] = t8;
|
|
149
|
-
$[38] = t9;
|
|
150
|
-
$[39] = t12;
|
|
151
|
-
} else t12 = $[39];
|
|
152
|
-
return t12;
|
|
67
|
+
children: intl.formatMessage(messages.confirmSkipped, { count: skipped })
|
|
68
|
+
}) : null]
|
|
69
|
+
});
|
|
153
70
|
}
|
|
154
71
|
//#endregion
|
|
155
72
|
export { ConfirmDialog, needsConfirmation };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmDialog.js","names":["
|
|
1
|
+
{"version":3,"file":"ConfirmDialog.js","names":["_Fragment","styles"],"sources":["../../src/actions/ConfirmDialog.tsx"],"sourcesContent":["import type { IntlShape } from 'react-intl';\n\nimport { Button, Dialog, Spinner, usePanneauIntl } from '@panneau/ui';\n\nimport { messages } from '../messages';\nimport type { ListActionEvent, ListActionPreview } from '../types';\nimport styles from './Actions.module.css';\n\n/** Formats an ICU message of the definition (`{count, plural, …}`) in the locale. */\nfunction formatDefinitionMessage(\n intl: IntlShape,\n message: string,\n values: Record<string, unknown>,\n) {\n try {\n return String(\n intl.formatters\n .getMessageFormat(message, intl.locale)\n .format(values as Record<string, string | number>),\n );\n } catch {\n return message;\n }\n}\n\n/** Whether an action needs a confirmation: it asks for one, or some selected items are skipped. */\nexport function needsConfirmation(event: ListActionEvent): boolean {\n const { confirm } = event.action;\n return (confirm !== undefined && confirm !== false) || event.skipped.length > 0;\n}\n\n/** The dry run of a query selection: in progress, or done with the backend's count. */\nexport type ConfirmPreview = { status: 'loading' } | ({ status: 'done' } & ListActionPreview);\n\nexport interface ConfirmDialogProps {\n /** The action to confirm, or `null` when the dialog is closed. */\n event: ListActionEvent | null;\n /**\n * What the backend told about a query selection. Its count of skipped items replaces the\n * count of the current page; while it loads, the dialog says so and can't be confirmed.\n */\n preview?: ConfirmPreview | null;\n onConfirm: (event: ListActionEvent) => void;\n onCancel: () => void;\n}\n\n/**\n * Asks before running an action (decision 0017): the action's `confirm` title and message (an ICU\n * message with `{count}`), or default ones, and how many selected items will be skipped: those of\n * the current page that don't allow it, or the backend's count for every item matching a query.\n */\nexport function ConfirmDialog({ event, preview = null, onConfirm, onCancel }: ConfirmDialogProps) {\n const intl = usePanneauIntl();\n if (event === null) {\n return null;\n }\n const { action, count } = event;\n const loading = preview?.status === 'loading';\n const skipped = preview?.status === 'done' ? preview.skipped : event.skipped.length;\n const confirm = typeof action.confirm === 'object' ? action.confirm : {};\n const values = { count, action: action.label };\n return (\n <Dialog\n isOpen\n onOpenChange={(open) => {\n if (!open) {\n onCancel();\n }\n }}\n role=\"alertdialog\"\n isDismissable={false}\n size=\"sm\"\n title={\n confirm.title !== undefined\n ? formatDefinitionMessage(intl, confirm.title, values)\n : intl.formatMessage(messages.confirmTitle, { action: action.label })\n }\n footer={\n <>\n <Button variant=\"ghost\" onPress={onCancel}>\n {confirm.cancelLabel ?? intl.formatMessage(messages.cancel)}\n </Button>\n <Button\n variant={action.destructive === true ? 'danger' : 'primary'}\n autoFocus\n isDisabled={loading}\n onPress={() => onConfirm(event)}\n >\n {confirm.confirmLabel ?? action.label}\n </Button>\n </>\n }\n >\n <p className={styles.message}>\n {confirm.message !== undefined\n ? formatDefinitionMessage(intl, confirm.message, values)\n : intl.formatMessage(messages.confirmMessage, values)}\n </p>\n {loading ? (\n <div className={styles.skipped}>\n <Spinner\n size=\"sm\"\n label={intl.formatMessage(messages.confirmChecking)}\n showLabel\n />\n </div>\n ) : skipped > 0 ? (\n <p className={styles.skipped}>\n {intl.formatMessage(messages.confirmSkipped, { count: skipped })}\n </p>\n ) : null}\n </Dialog>\n );\n}\n"],"mappings":";;;;;;AASA,SAAS,wBACL,MACA,SACA,QACF;CACE,IAAI;EACA,OAAO,OACH,KAAK,WACA,iBAAiB,SAAS,KAAK,MAAM,CAAC,CACtC,OAAO,MAAyC,CACzD;CACJ,QAAQ;EACJ,OAAO;CACX;AACJ;;AAGA,SAAgB,kBAAkB,OAAiC;CAC/D,MAAM,EAAE,YAAY,MAAM;CAC1B,OAAQ,YAAY,KAAA,KAAa,YAAY,SAAU,MAAM,QAAQ,SAAS;AAClF;;;;;;AAsBA,SAAgB,cAAc,EAAE,OAAO,UAAU,MAAM,WAAW,YAAgC;CAC9F,MAAM,OAAO,eAAe;CAC5B,IAAI,UAAU,MACV,OAAO;CAEX,MAAM,EAAE,QAAQ,UAAU;CAC1B,MAAM,UAAU,SAAS,WAAW;CACpC,MAAM,UAAU,SAAS,WAAW,SAAS,QAAQ,UAAU,MAAM,QAAQ;CAC7E,MAAM,UAAU,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU,CAAC;CACvE,MAAM,SAAS;EAAE;EAAO,QAAQ,OAAO;CAAM;CAC7C,OACI,qBAAC,QAAD;EACI,QAAA;EACA,eAAe,SAAS;GACpB,IAAI,CAAC,MACD,SAAS;EAEjB;EACA,MAAK;EACL,eAAe;EACf,MAAK;EACL,OACI,QAAQ,UAAU,KAAA,IACZ,wBAAwB,MAAM,QAAQ,OAAO,MAAM,IACnD,KAAK,cAAc,SAAS,cAAc,EAAE,QAAQ,OAAO,MAAM,CAAC;EAE5E,QACI,qBAAAA,UAAA,EAAA,UAAA,CACI,oBAAC,QAAD;GAAQ,SAAQ;GAAQ,SAAS;GAC5B,UAAA,QAAQ,eAAe,KAAK,cAAc,SAAS,MAAM;EACtD,CAAA,GACR,oBAAC,QAAD;GACI,SAAS,OAAO,gBAAgB,OAAO,WAAW;GAClD,WAAA;GACA,YAAY;GACZ,eAAe,UAAU,KAAK;GAE7B,UAAA,QAAQ,gBAAgB,OAAO;EAC5B,CAAA,CACZ,EAAA,CAAA;EA5BR,UAAA,CA+BI,oBAAC,KAAD;GAAG,WAAWC,uBAAO;GAChB,UAAA,QAAQ,YAAY,KAAA,IACf,wBAAwB,MAAM,QAAQ,SAAS,MAAM,IACrD,KAAK,cAAc,SAAS,gBAAgB,MAAM;EACzD,CAAA,GACF,UACG,oBAAC,OAAD;GAAK,WAAWA,uBAAO;GACnB,UAAA,oBAAC,SAAD;IACI,MAAK;IACL,OAAO,KAAK,cAAc,SAAS,eAAe;IAClD,WAAA;GAAS,CAAA;EAEZ,CAAA,IACL,UAAU,IACV,oBAAC,KAAD;GAAG,WAAWA,uBAAO;GAChB,UAAA,KAAK,cAAc,SAAS,gBAAgB,EAAE,OAAO,QAAQ,CAAC;EAChE,CAAA,IACH,IACA;;AAEhB"}
|
|
@@ -3,10 +3,10 @@ import { messages } from "../messages.js";
|
|
|
3
3
|
import Actions_module_default from "./Actions.module.js";
|
|
4
4
|
import { getActionIcon } from "./icons.js";
|
|
5
5
|
import { ActionButton } from "./ActionButton.js";
|
|
6
|
-
import { c } from "react/compiler-runtime";
|
|
7
6
|
import { IconButton, Menu, MenuItem, MenuTrigger, usePanneauIntl } from "@panneau/ui";
|
|
8
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
8
|
import { Ellipsis } from "lucide-react";
|
|
9
|
+
import { c } from "react/compiler-runtime";
|
|
10
10
|
//#region src/actions/RowActions.tsx
|
|
11
11
|
/** The actions of a row: buttons, then an overflow menu. */
|
|
12
12
|
function RowActions(t0) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { messages } from "../messages.js";
|
|
2
2
|
import { getFieldOptions, getOptionLabel, toText } from "../values.js";
|
|
3
3
|
import Displays_module_default from "./Displays.module.js";
|
|
4
|
-
import { c } from "react/compiler-runtime";
|
|
5
4
|
import { Badge, Icon, usePanneauIntl } from "@panneau/ui";
|
|
6
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
6
|
import { Check, Minus } from "lucide-react";
|
|
7
|
+
import { c } from "react/compiler-runtime";
|
|
8
8
|
import { toReferences } from "@panneau/fields";
|
|
9
9
|
//#region src/displays/displays.tsx
|
|
10
10
|
/** What a cell shows without a value. Hidden from assistive technologies, which skip the cell. */
|
package/dist/filters/Filters.js
CHANGED
|
@@ -2,10 +2,10 @@ import { messages } from "../messages.js";
|
|
|
2
2
|
import Filters_module_default from "./Filters.module.js";
|
|
3
3
|
import { builtInFilters } from "./controls.js";
|
|
4
4
|
import { formatFilterValue, getFilterLabel, getFilterOptions } from "./format.js";
|
|
5
|
-
import { c } from "react/compiler-runtime";
|
|
6
5
|
import { Button, IconButton, usePanneauIntl } from "@panneau/ui";
|
|
7
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
import { X } from "lucide-react";
|
|
8
|
+
import { c } from "react/compiler-runtime";
|
|
9
9
|
import { useState } from "react";
|
|
10
10
|
//#region src/filters/Filters.tsx
|
|
11
11
|
/** Remembers the labels of filter values, for the chips of active filters. */
|
package/dist/filters/controls.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { messages } from "../messages.js";
|
|
2
2
|
import { useDraft } from "../useDraft.js";
|
|
3
3
|
import Filters_module_default from "./Filters.module.js";
|
|
4
|
-
import { c } from "react/compiler-runtime";
|
|
5
4
|
import { Fieldset, Select, Switch, TextInput, usePanneauIntl } from "@panneau/ui";
|
|
6
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { c } from "react/compiler-runtime";
|
|
7
7
|
import { useFieldsContext } from "@panneau/fields";
|
|
8
8
|
import { useEffect } from "react";
|
|
9
9
|
import { DateField } from "@panneau/fields/date";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { type ListActions, getActionHref, groupListActions, isAllowedOn } from './actions/actions';
|
|
2
|
-
export { ConfirmDialog, type ConfirmDialogProps } from './actions/ConfirmDialog';
|
|
2
|
+
export { ConfirmDialog, type ConfirmDialogProps, type ConfirmPreview, } from './actions/ConfirmDialog';
|
|
3
3
|
export { type ActionIcons, getActionIcon } from './actions/icons';
|
|
4
4
|
export { BooleanDisplay, ColorDisplay, DateDisplay, DateTimeDisplay, EmailDisplay, EmptyValue, MediaDisplay, NumberDisplay, RelationDisplay, RichTextDisplay, SelectDisplay, TextDisplay, TimeDisplay, UrlDisplay, builtInDisplays, } from './displays/displays';
|
|
5
5
|
export { DateFilter, DateRangeFilter, RelationFilter, SelectFilter, TextFilter, ToggleFilter, builtInFilters, } from './filters/controls';
|
|
@@ -13,7 +13,7 @@ export { type LocalQueryOptions, type LocalQueryResult, applyListQuery } from '.
|
|
|
13
13
|
export { countSelected, getPageSelection, isItemSelected } from './selection';
|
|
14
14
|
export { Pagination, type PaginationProps } from './toolbar/Pagination';
|
|
15
15
|
export { SearchField, type SearchFieldProps } from './toolbar/SearchField';
|
|
16
|
-
export type { DisplayEntry, DisplayProps, FilterControlProps, FilterEntry, ListActionEvent, ListActionHandler, ListColumn, ListItem, ListLayoutEntry, ListLayoutProps, } from './types';
|
|
16
|
+
export type { DisplayEntry, DisplayProps, FilterControlProps, FilterEntry, ListActionEvent, ListActionHandler, ListActionPreview, ListActionPreviewer, ListColumn, ListItem, ListLayoutEntry, ListLayoutProps, } from './types';
|
|
17
17
|
export { useListState } from './useListState';
|
|
18
18
|
export { getLocalizedValue, toText } from './values';
|
|
19
19
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACnG,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACnG,OAAO,EACH,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,KAAK,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EACH,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,aAAa,EACb,eAAe,EACf,eAAe,EACf,aAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,eAAe,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACH,UAAU,EACV,eAAe,EACf,cAAc,EACd,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,cAAc,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,YAAY,EACR,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,EACV,QAAQ,EACR,eAAe,EACf,eAAe,GAClB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { messages } from "../messages.js";
|
|
2
2
|
import CardsLayout_module_default from "./CardsLayout.module.js";
|
|
3
3
|
import { ItemTitle } from "./ItemTitle.js";
|
|
4
|
-
import { c } from "react/compiler-runtime";
|
|
5
4
|
import { Checkbox, usePanneauIntl } from "@panneau/ui";
|
|
6
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { c } from "react/compiler-runtime";
|
|
7
7
|
//#region src/layouts/CardsLayout.tsx
|
|
8
8
|
/** Items as cards: the first column is the title, the others are listed under it. */
|
|
9
9
|
function CardsLayout(t0) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import link_module_default from "./link.module.js";
|
|
2
|
-
import { c } from "react/compiler-runtime";
|
|
3
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { c } from "react/compiler-runtime";
|
|
4
4
|
import { Link } from "react-aria-components";
|
|
5
5
|
//#region src/layouts/ItemTitle.tsx
|
|
6
6
|
/**
|
|
@@ -2,10 +2,10 @@ import { messages } from "../messages.js";
|
|
|
2
2
|
import { ItemTitle } from "./ItemTitle.js";
|
|
3
3
|
import { cx } from "../cx.js";
|
|
4
4
|
import TableLayout_module_default from "./TableLayout.module.js";
|
|
5
|
-
import { c } from "react/compiler-runtime";
|
|
6
5
|
import { Checkbox, Icon, usePanneauIntl } from "@panneau/ui";
|
|
7
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
import { ArrowDown, ArrowUp, ArrowUpDown } from "lucide-react";
|
|
8
|
+
import { c } from "react/compiler-runtime";
|
|
9
9
|
//#region src/layouts/TableLayout.tsx
|
|
10
10
|
/** The sort direction of a column, from the first `sort` value. */
|
|
11
11
|
function getSortDirection(sort, column) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { messages } from "../messages.js";
|
|
2
2
|
import Toolbar_module_default from "./Toolbar.module.js";
|
|
3
|
-
import { c } from "react/compiler-runtime";
|
|
4
3
|
import { Icon, Tooltip, usePanneauIntl } from "@panneau/ui";
|
|
5
4
|
import { jsx } from "react/jsx-runtime";
|
|
6
5
|
import { LayoutGrid, Rows3, Square } from "lucide-react";
|
|
6
|
+
import { c } from "react/compiler-runtime";
|
|
7
7
|
import { ToggleButton, ToggleButtonGroup } from "react-aria-components";
|
|
8
8
|
//#region src/toolbar/LayoutSwitch.tsx
|
|
9
9
|
var ICONS = {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { messages } from "../messages.js";
|
|
2
2
|
import { useDraft } from "../useDraft.js";
|
|
3
3
|
import Toolbar_module_default from "./Toolbar.module.js";
|
|
4
|
-
import { c } from "react/compiler-runtime";
|
|
5
4
|
import { Icon, TextInput, usePanneauIntl } from "@panneau/ui";
|
|
6
5
|
import { jsx } from "react/jsx-runtime";
|
|
7
6
|
import { Search } from "lucide-react";
|
|
7
|
+
import { c } from "react/compiler-runtime";
|
|
8
8
|
//#region src/toolbar/SearchField.tsx
|
|
9
9
|
/** The search of a list (`q`). */
|
|
10
10
|
function SearchField(t0) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { messages } from "../messages.js";
|
|
2
2
|
import Toolbar_module_default from "./Toolbar.module.js";
|
|
3
|
-
import { c } from "react/compiler-runtime";
|
|
4
3
|
import { Select, usePanneauIntl } from "@panneau/ui";
|
|
5
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { c } from "react/compiler-runtime";
|
|
6
6
|
//#region src/toolbar/SortSelect.tsx
|
|
7
7
|
var DEFAULT = "";
|
|
8
8
|
/** The sort of a list without column headers (cards), by sortable column and direction. */
|
package/dist/types.d.ts
CHANGED
|
@@ -52,6 +52,16 @@ export interface ListActionEvent {
|
|
|
52
52
|
/** Selected items of the current page that don't allow the action: the backend skips them. */
|
|
53
53
|
skipped: readonly ListItem[];
|
|
54
54
|
}
|
|
55
|
+
/** What the backend told about an action on a query selection: how many items it would skip. */
|
|
56
|
+
export interface ListActionPreview {
|
|
57
|
+
skipped: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Asks the backend what an action would do on a query selection, before the list confirms it (a
|
|
61
|
+
* dry run, for actions with `dryRun`), or resolves with `null` when it can't tell.
|
|
62
|
+
* `useActionPreview()` of `@panneau/actions` is one.
|
|
63
|
+
*/
|
|
64
|
+
export type ListActionPreviewer = (event: ListActionEvent) => Promise<ListActionPreview | null>;
|
|
55
65
|
/**
|
|
56
66
|
* Runs an action. Lists ask for confirmation first when the action has `confirm`. A returned
|
|
57
67
|
* promise shows the action as pending until it settles; after a selection action succeeds, the
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panneau/lists",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.3",
|
|
4
4
|
"description": "Lists of Panneau: columns with displays, table and cards layouts, sorting, filters, search, pagination, selection and actions, driven by the list state of @panneau/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"react-intl": "^12.0.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@panneau/core": "5.0.0-alpha.
|
|
41
|
-
"@panneau/fields": "5.0.0-alpha.
|
|
42
|
-
"@panneau/intl": "5.0.0-alpha.
|
|
43
|
-
"@panneau/protocol": "5.0.0-alpha.
|
|
44
|
-
"@panneau/ui": "5.0.0-alpha.
|
|
40
|
+
"@panneau/core": "5.0.0-alpha.3",
|
|
41
|
+
"@panneau/fields": "5.0.0-alpha.3",
|
|
42
|
+
"@panneau/intl": "5.0.0-alpha.3",
|
|
43
|
+
"@panneau/protocol": "5.0.0-alpha.3",
|
|
44
|
+
"@panneau/ui": "5.0.0-alpha.3",
|
|
45
45
|
"lucide-react": "^1.57.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@panneau/data": "5.0.0-alpha.
|
|
48
|
+
"@panneau/data": "5.0.0-alpha.3"
|
|
49
49
|
}
|
|
50
50
|
}
|