@panneau/lists 5.0.0-alpha.12 → 5.0.0-alpha.13

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.
Files changed (78) hide show
  1. package/README.md +13 -11
  2. package/dist/List.d.ts +1 -1
  3. package/dist/List.d.ts.map +1 -1
  4. package/dist/List.js +29 -50
  5. package/dist/List.js.map +1 -1
  6. package/dist/ListView.d.ts +34 -5
  7. package/dist/ListView.d.ts.map +1 -1
  8. package/dist/ListView.js +416 -359
  9. package/dist/ListView.js.map +1 -1
  10. package/dist/columns.d.ts +10 -0
  11. package/dist/columns.d.ts.map +1 -0
  12. package/dist/columns.js +25 -0
  13. package/dist/columns.js.map +1 -0
  14. package/dist/displays/Displays.css +12 -0
  15. package/dist/displays/Displays.module.js +6 -2
  16. package/dist/displays/Displays.module.js.map +1 -1
  17. package/dist/displays/displays.d.ts +11 -1
  18. package/dist/displays/displays.d.ts.map +1 -1
  19. package/dist/displays/displays.js +185 -22
  20. package/dist/displays/displays.js.map +1 -1
  21. package/dist/images.d.ts +6 -0
  22. package/dist/images.d.ts.map +1 -0
  23. package/dist/images.js +18 -0
  24. package/dist/images.js.map +1 -0
  25. package/dist/index.d.ts +5 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +5 -2
  28. package/dist/inline/Inline.css +58 -0
  29. package/dist/inline/Inline.module.js +20 -0
  30. package/dist/inline/Inline.module.js.map +1 -0
  31. package/dist/inline/InlineCell.d.ts +27 -0
  32. package/dist/inline/InlineCell.d.ts.map +1 -0
  33. package/dist/inline/InlineCell.js +471 -0
  34. package/dist/inline/InlineCell.js.map +1 -0
  35. package/dist/inline/editable.d.ts +12 -0
  36. package/dist/inline/editable.d.ts.map +1 -0
  37. package/dist/inline/editable.js +29 -0
  38. package/dist/inline/editable.js.map +1 -0
  39. package/dist/layouts/CalendarLayout.css +153 -0
  40. package/dist/layouts/CalendarLayout.d.ts +9 -0
  41. package/dist/layouts/CalendarLayout.d.ts.map +1 -0
  42. package/dist/layouts/CalendarLayout.js +298 -0
  43. package/dist/layouts/CalendarLayout.js.map +1 -0
  44. package/dist/layouts/CalendarLayout.module.js +28 -0
  45. package/dist/layouts/CalendarLayout.module.js.map +1 -0
  46. package/dist/layouts/CardsLayout.js +1 -1
  47. package/dist/layouts/GalleryLayout.css +80 -0
  48. package/dist/layouts/GalleryLayout.d.ts +8 -0
  49. package/dist/layouts/GalleryLayout.d.ts.map +1 -0
  50. package/dist/layouts/GalleryLayout.js +129 -0
  51. package/dist/layouts/GalleryLayout.js.map +1 -0
  52. package/dist/layouts/GalleryLayout.module.js +26 -0
  53. package/dist/layouts/GalleryLayout.module.js.map +1 -0
  54. package/dist/layouts/TableLayout.d.ts +1 -1
  55. package/dist/layouts/TableLayout.d.ts.map +1 -1
  56. package/dist/layouts/TableLayout.js +44 -39
  57. package/dist/layouts/TableLayout.js.map +1 -1
  58. package/dist/layouts/builtIn.d.ts +1 -1
  59. package/dist/layouts/builtIn.d.ts.map +1 -1
  60. package/dist/layouts/builtIn.js +6 -2
  61. package/dist/layouts/builtIn.js.map +1 -1
  62. package/dist/toolbar/ColumnMenu.d.ts +17 -0
  63. package/dist/toolbar/ColumnMenu.d.ts.map +1 -0
  64. package/dist/toolbar/ColumnMenu.js +193 -0
  65. package/dist/toolbar/ColumnMenu.js.map +1 -0
  66. package/dist/toolbar/LayoutSwitch.d.ts.map +1 -1
  67. package/dist/toolbar/LayoutSwitch.js +73 -55
  68. package/dist/toolbar/LayoutSwitch.js.map +1 -1
  69. package/dist/toolbar/Toolbar.css +36 -0
  70. package/dist/toolbar/Toolbar.module.js +12 -2
  71. package/dist/toolbar/Toolbar.module.js.map +1 -1
  72. package/dist/types.d.ts +42 -0
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/values.d.ts +1 -0
  75. package/dist/values.d.ts.map +1 -1
  76. package/dist/values.js +1 -1
  77. package/dist/values.js.map +1 -1
  78. package/package.json +7 -7
@@ -0,0 +1,471 @@
1
+ import { messages } from "../messages.js";
2
+ import Inline_module_default from "./Inline.module.js";
3
+ import { getProblem } from "@panneau/core";
4
+ import { c } from "react/compiler-runtime";
5
+ import { Button, IconButton, Spinner, usePanneauIntl } from "@panneau/ui";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { Pencil } from "lucide-react";
8
+ import { useEffect, useRef, useState } from "react";
9
+ import { DateFieldEntry } from "@panneau/fields/date";
10
+ import { checkValues } from "@panneau/protocol/runtime";
11
+ import { BooleanFieldEntry } from "@panneau/fields/boolean";
12
+ import { NumberFieldEntry } from "@panneau/fields/number";
13
+ import { SelectFieldEntry } from "@panneau/fields/select";
14
+ import { TextFieldEntry } from "@panneau/fields/text";
15
+ //#region src/inline/InlineCell.tsx
16
+ var ENTRIES = {
17
+ text: TextFieldEntry,
18
+ number: NumberFieldEntry,
19
+ select: SelectFieldEntry,
20
+ boolean: BooleanFieldEntry,
21
+ date: DateFieldEntry
22
+ };
23
+ /** Types that save as soon as a value is picked, rather than on Enter or when leaving the cell. */
24
+ var PICKED = /* @__PURE__ */ new Set(["select", "date"]);
25
+ function isEqualValue(a, b) {
26
+ return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
27
+ }
28
+ /**
29
+ * A cell that edits its field in place (decision 0043): a checkbox for a boolean, otherwise the
30
+ * value with a button that turns it into the field's control. Enter or leaving the cell saves,
31
+ * Escape cancels; a select and a date save when one is picked. The value is checked by the
32
+ * field's portable rules, then saved alone; errors and conflicts show in the cell.
33
+ */
34
+ function InlineCell(t0) {
35
+ const $ = c(76);
36
+ const { item, field, itemLabel, children, renderValue, onSave, onItem, formatError } = t0;
37
+ const intl = usePanneauIntl();
38
+ let t1;
39
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
40
+ t1 = { mode: "idle" };
41
+ $[0] = t1;
42
+ } else t1 = $[0];
43
+ const [state, setState] = useState(t1);
44
+ const root = useRef(null);
45
+ const focus = useRef(null);
46
+ const label = field.label ?? field.name;
47
+ const value = item[field.name];
48
+ const isBoolean = field.type === "boolean";
49
+ const Entry = ENTRIES[field.type];
50
+ let bare;
51
+ if ($[1] !== field) {
52
+ const { help: _help, ...t2 } = field;
53
+ bare = t2;
54
+ $[1] = field;
55
+ $[2] = bare;
56
+ } else bare = $[2];
57
+ let t2;
58
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
59
+ t2 = () => {
60
+ if (focus.current === "editor") root.current?.querySelector("input, [role=\"spinbutton\"], button")?.focus();
61
+ else if (focus.current === "button") root.current?.querySelector("button")?.focus();
62
+ focus.current = null;
63
+ };
64
+ $[3] = t2;
65
+ } else t2 = $[3];
66
+ useEffect(t2);
67
+ let save;
68
+ let t3;
69
+ let t4;
70
+ let t5;
71
+ let t6;
72
+ let t7;
73
+ let t8;
74
+ let t9;
75
+ if ($[4] !== Entry || $[5] !== bare || $[6] !== children || $[7] !== field || $[8] !== formatError || $[9] !== intl || $[10] !== isBoolean || $[11] !== item || $[12] !== itemLabel || $[13] !== label || $[14] !== onItem || $[15] !== onSave || $[16] !== renderValue || $[17] !== state.current || $[18] !== state.draft || $[19] !== state.errors || $[20] !== state.mode || $[21] !== value) {
76
+ t9 = Symbol.for("react.early_return_sentinel");
77
+ bb0: {
78
+ save = (base, draft) => {
79
+ setState({
80
+ mode: "saving",
81
+ draft
82
+ });
83
+ onSave(base, draft).then((saved) => {
84
+ onItem(saved);
85
+ focus.current = isBoolean ? null : "button";
86
+ setState({ mode: "idle" });
87
+ }, (error) => {
88
+ const problem = getProblem(error);
89
+ if (problem?.status === 412 && problem.current !== void 0) {
90
+ onItem(problem.current);
91
+ setState({
92
+ mode: "conflict",
93
+ draft,
94
+ current: problem.current
95
+ });
96
+ return;
97
+ }
98
+ const own = Object.entries(problem?.errors ?? {}).filter((t10) => {
99
+ const [path] = t10;
100
+ return path === field.name || path.startsWith(`${field.name}.`);
101
+ }).flatMap(_temp);
102
+ const message = problem?.detail ?? (problem !== null ? problem.title : null) ?? (error instanceof Error && error.message !== "" ? error.message : null) ?? intl.formatMessage(messages.saveValueFailed);
103
+ focus.current = "editor";
104
+ setState({
105
+ mode: "editing",
106
+ draft,
107
+ errors: own.length > 0 ? own : [message]
108
+ });
109
+ });
110
+ };
111
+ const commit = (draft_0) => {
112
+ if (isEqualValue(draft_0, value)) {
113
+ focus.current = "button";
114
+ setState({ mode: "idle" });
115
+ return;
116
+ }
117
+ const failures = checkValues([field], { [field.name]: draft_0 }, {
118
+ mode: "update",
119
+ partial: true,
120
+ item
121
+ });
122
+ const errors = Object.values(failures).flat().map((failure) => formatError(failure));
123
+ if (errors.length > 0) {
124
+ setState({
125
+ mode: "editing",
126
+ draft: draft_0,
127
+ errors
128
+ });
129
+ return;
130
+ }
131
+ save(item, draft_0);
132
+ };
133
+ const cancel = () => {
134
+ focus.current = "button";
135
+ setState({ mode: "idle" });
136
+ };
137
+ const onKeyDown = (event) => {
138
+ if (state.mode !== "editing") return;
139
+ if (event.key === "Escape") {
140
+ event.stopPropagation();
141
+ cancel();
142
+ } else if (event.key === "Enter" && !PICKED.has(field.type)) {
143
+ event.preventDefault();
144
+ commit(state.draft);
145
+ }
146
+ };
147
+ const onBlur = (event_0) => {
148
+ const next = event_0.relatedTarget;
149
+ if (state.mode !== "editing" || next instanceof Element && (event_0.currentTarget.contains(next) || next.closest(".panneau-popover") !== null)) return;
150
+ commit(state.draft);
151
+ };
152
+ const control = (draft_1, errors_0, disabled) => /* @__PURE__ */ jsx(Entry, {
153
+ field: bare,
154
+ path: field.name,
155
+ value: draft_1,
156
+ onChange: (next_0) => {
157
+ if (isBoolean || PICKED.has(field.type) && next_0 !== null) commit(next_0);
158
+ else setState({
159
+ mode: "editing",
160
+ draft: next_0,
161
+ errors: []
162
+ });
163
+ },
164
+ errors: errors_0,
165
+ disabled,
166
+ hideLabel: true
167
+ });
168
+ if (isBoolean) {
169
+ let t11;
170
+ if ($[30] !== state.errors || $[31] !== state.mode) {
171
+ t11 = state.mode === "editing" ? state.errors : [];
172
+ $[30] = state.errors;
173
+ $[31] = state.mode;
174
+ $[32] = t11;
175
+ } else t11 = $[32];
176
+ let t12;
177
+ if ($[33] !== intl || $[34] !== state.mode) {
178
+ t12 = state.mode === "saving" ? /* @__PURE__ */ jsx(Spinner, {
179
+ size: "sm",
180
+ label: intl.formatMessage(messages.savingValue)
181
+ }) : null;
182
+ $[33] = intl;
183
+ $[34] = state.mode;
184
+ $[35] = t12;
185
+ } else t12 = $[35];
186
+ const t13 = /* @__PURE__ */ jsxs("div", {
187
+ className: Inline_module_default.control,
188
+ children: [control(state.mode === "idle" ? value : state.draft, t11, state.mode === "saving"), t12]
189
+ });
190
+ const t14 = state.mode === "conflict" ? /* @__PURE__ */ jsx(Conflict, {
191
+ current: renderValue(state.current),
192
+ onKeep: () => setState({ mode: "idle" }),
193
+ onSave: () => save(state.current, state.draft)
194
+ }) : null;
195
+ let t15;
196
+ if ($[36] !== t13 || $[37] !== t14) {
197
+ t15 = /* @__PURE__ */ jsxs("div", {
198
+ className: Inline_module_default.cell,
199
+ children: [t13, t14]
200
+ });
201
+ $[36] = t13;
202
+ $[37] = t14;
203
+ $[38] = t15;
204
+ } else t15 = $[38];
205
+ t9 = t15;
206
+ break bb0;
207
+ }
208
+ if (state.mode === "idle") {
209
+ let t11;
210
+ if ($[39] !== children) {
211
+ t11 = /* @__PURE__ */ jsx("span", {
212
+ className: Inline_module_default.value,
213
+ children
214
+ });
215
+ $[39] = children;
216
+ $[40] = t11;
217
+ } else t11 = $[40];
218
+ let t12;
219
+ if ($[41] !== intl || $[42] !== itemLabel || $[43] !== label) {
220
+ t12 = intl.formatMessage(messages.editValue, {
221
+ field: label,
222
+ item: itemLabel
223
+ });
224
+ $[41] = intl;
225
+ $[42] = itemLabel;
226
+ $[43] = label;
227
+ $[44] = t12;
228
+ } else t12 = $[44];
229
+ let t13;
230
+ if ($[45] !== value) {
231
+ t13 = () => {
232
+ focus.current = "editor";
233
+ setState({
234
+ mode: "editing",
235
+ draft: value,
236
+ errors: []
237
+ });
238
+ };
239
+ $[45] = value;
240
+ $[46] = t13;
241
+ } else t13 = $[46];
242
+ let t14;
243
+ if ($[47] !== t12 || $[48] !== t13) {
244
+ t14 = /* @__PURE__ */ jsx(IconButton, {
245
+ icon: Pencil,
246
+ size: "sm",
247
+ label: t12,
248
+ showTooltip: false,
249
+ className: Inline_module_default.edit,
250
+ onPress: t13
251
+ });
252
+ $[47] = t12;
253
+ $[48] = t13;
254
+ $[49] = t14;
255
+ } else t14 = $[49];
256
+ let t15;
257
+ if ($[50] !== t11 || $[51] !== t14) {
258
+ t15 = /* @__PURE__ */ jsxs("div", {
259
+ ref: root,
260
+ className: Inline_module_default.cell,
261
+ children: [t11, t14]
262
+ });
263
+ $[50] = t11;
264
+ $[51] = t14;
265
+ $[52] = t15;
266
+ } else t15 = $[52];
267
+ t9 = t15;
268
+ break bb0;
269
+ }
270
+ t5 = root;
271
+ t6 = Inline_module_default.cell;
272
+ t7 = onKeyDown;
273
+ t8 = onBlur;
274
+ t3 = Inline_module_default.control;
275
+ let t11;
276
+ if ($[53] !== state.errors || $[54] !== state.mode) {
277
+ t11 = state.mode === "editing" ? state.errors : [];
278
+ $[53] = state.errors;
279
+ $[54] = state.mode;
280
+ $[55] = t11;
281
+ } else t11 = $[55];
282
+ t4 = control(state.draft, t11, state.mode === "saving");
283
+ }
284
+ $[4] = Entry;
285
+ $[5] = bare;
286
+ $[6] = children;
287
+ $[7] = field;
288
+ $[8] = formatError;
289
+ $[9] = intl;
290
+ $[10] = isBoolean;
291
+ $[11] = item;
292
+ $[12] = itemLabel;
293
+ $[13] = label;
294
+ $[14] = onItem;
295
+ $[15] = onSave;
296
+ $[16] = renderValue;
297
+ $[17] = state.current;
298
+ $[18] = state.draft;
299
+ $[19] = state.errors;
300
+ $[20] = state.mode;
301
+ $[21] = value;
302
+ $[22] = save;
303
+ $[23] = t3;
304
+ $[24] = t4;
305
+ $[25] = t5;
306
+ $[26] = t6;
307
+ $[27] = t7;
308
+ $[28] = t8;
309
+ $[29] = t9;
310
+ } else {
311
+ save = $[22];
312
+ t3 = $[23];
313
+ t4 = $[24];
314
+ t5 = $[25];
315
+ t6 = $[26];
316
+ t7 = $[27];
317
+ t8 = $[28];
318
+ t9 = $[29];
319
+ }
320
+ if (t9 !== Symbol.for("react.early_return_sentinel")) return t9;
321
+ let t10;
322
+ if ($[56] !== intl || $[57] !== state.mode) {
323
+ t10 = state.mode === "saving" ? /* @__PURE__ */ jsx(Spinner, {
324
+ size: "sm",
325
+ label: intl.formatMessage(messages.savingValue)
326
+ }) : null;
327
+ $[56] = intl;
328
+ $[57] = state.mode;
329
+ $[58] = t10;
330
+ } else t10 = $[58];
331
+ let t11;
332
+ if ($[59] !== t10 || $[60] !== t3 || $[61] !== t4) {
333
+ t11 = /* @__PURE__ */ jsxs("div", {
334
+ className: t3,
335
+ children: [t4, t10]
336
+ });
337
+ $[59] = t10;
338
+ $[60] = t3;
339
+ $[61] = t4;
340
+ $[62] = t11;
341
+ } else t11 = $[62];
342
+ let t12;
343
+ if ($[63] !== renderValue || $[64] !== save || $[65] !== state.current || $[66] !== state.draft || $[67] !== state.mode) {
344
+ t12 = state.mode === "conflict" ? /* @__PURE__ */ jsx(Conflict, {
345
+ current: renderValue(state.current),
346
+ onKeep: () => {
347
+ focus.current = "button";
348
+ setState({ mode: "idle" });
349
+ },
350
+ onSave: () => save(state.current, state.draft)
351
+ }) : null;
352
+ $[63] = renderValue;
353
+ $[64] = save;
354
+ $[65] = state.current;
355
+ $[66] = state.draft;
356
+ $[67] = state.mode;
357
+ $[68] = t12;
358
+ } else t12 = $[68];
359
+ let t13;
360
+ if ($[69] !== t11 || $[70] !== t12 || $[71] !== t5 || $[72] !== t6 || $[73] !== t7 || $[74] !== t8) {
361
+ t13 = /* @__PURE__ */ jsxs("div", {
362
+ ref: t5,
363
+ className: t6,
364
+ onKeyDown: t7,
365
+ onBlur: t8,
366
+ children: [t11, t12]
367
+ });
368
+ $[69] = t11;
369
+ $[70] = t12;
370
+ $[71] = t5;
371
+ $[72] = t6;
372
+ $[73] = t7;
373
+ $[74] = t8;
374
+ $[75] = t13;
375
+ } else t13 = $[75];
376
+ return t13;
377
+ }
378
+ /** What someone else saved, with the choice of keeping it or saving over it. */
379
+ function _temp(t0) {
380
+ const [, list] = t0;
381
+ return list;
382
+ }
383
+ function Conflict(t0) {
384
+ const $ = c(23);
385
+ const { current, onKeep, onSave } = t0;
386
+ const intl = usePanneauIntl();
387
+ let t1;
388
+ if ($[0] !== intl) {
389
+ t1 = intl.formatMessage(messages.valueConflict);
390
+ $[0] = intl;
391
+ $[1] = t1;
392
+ } else t1 = $[1];
393
+ let t2;
394
+ if ($[2] !== current) {
395
+ t2 = /* @__PURE__ */ jsx("strong", { children: current });
396
+ $[2] = current;
397
+ $[3] = t2;
398
+ } else t2 = $[3];
399
+ let t3;
400
+ if ($[4] !== t1 || $[5] !== t2) {
401
+ t3 = /* @__PURE__ */ jsxs("span", { children: [
402
+ t1,
403
+ " ",
404
+ t2
405
+ ] });
406
+ $[4] = t1;
407
+ $[5] = t2;
408
+ $[6] = t3;
409
+ } else t3 = $[6];
410
+ let t4;
411
+ if ($[7] !== intl) {
412
+ t4 = intl.formatMessage(messages.keepTheirs);
413
+ $[7] = intl;
414
+ $[8] = t4;
415
+ } else t4 = $[8];
416
+ let t5;
417
+ if ($[9] !== onKeep || $[10] !== t4) {
418
+ t5 = /* @__PURE__ */ jsx(Button, {
419
+ size: "sm",
420
+ appearance: "ghost",
421
+ onPress: onKeep,
422
+ children: t4
423
+ });
424
+ $[9] = onKeep;
425
+ $[10] = t4;
426
+ $[11] = t5;
427
+ } else t5 = $[11];
428
+ let t6;
429
+ if ($[12] !== intl) {
430
+ t6 = intl.formatMessage(messages.saveMine);
431
+ $[12] = intl;
432
+ $[13] = t6;
433
+ } else t6 = $[13];
434
+ let t7;
435
+ if ($[14] !== onSave || $[15] !== t6) {
436
+ t7 = /* @__PURE__ */ jsx(Button, {
437
+ size: "sm",
438
+ onPress: onSave,
439
+ children: t6
440
+ });
441
+ $[14] = onSave;
442
+ $[15] = t6;
443
+ $[16] = t7;
444
+ } else t7 = $[16];
445
+ let t8;
446
+ if ($[17] !== t5 || $[18] !== t7) {
447
+ t8 = /* @__PURE__ */ jsxs("span", {
448
+ className: Inline_module_default.conflictActions,
449
+ children: [t5, t7]
450
+ });
451
+ $[17] = t5;
452
+ $[18] = t7;
453
+ $[19] = t8;
454
+ } else t8 = $[19];
455
+ let t9;
456
+ if ($[20] !== t3 || $[21] !== t8) {
457
+ t9 = /* @__PURE__ */ jsxs("div", {
458
+ className: Inline_module_default.conflict,
459
+ role: "alert",
460
+ children: [t3, t8]
461
+ });
462
+ $[20] = t3;
463
+ $[21] = t8;
464
+ $[22] = t9;
465
+ } else t9 = $[22];
466
+ return t9;
467
+ }
468
+ //#endregion
469
+ export { InlineCell };
470
+
471
+ //# sourceMappingURL=InlineCell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InlineCell.js","names":["Pencil","FocusEvent","KeyboardEvent","ReactNode","useEffect","useRef","useState","getProblem","BooleanFieldEntry","DateFieldEntry","NumberFieldEntry","SelectFieldEntry","TextFieldEntry","Field","RuleFailure","checkValues","Button","IconButton","Spinner","usePanneauIntl","messages","ListItem","styles","ENTRIES","text","number","select","boolean","date","const","PICKED","Set","State","mode","draft","errors","current","InlineCellProps","item","field","itemLabel","children","renderValue","onSave","value","Promise","onItem","formatError","failure","isEqualValue","a","b","JSON","stringify","InlineCell","t0","$","_c","intl","t1","Symbol","for","state","setState","root","focus","label","name","isBoolean","type","Entry","bare","help","_help","t2","querySelector","save","t3","t4","t5","t6","t7","t8","t9","bb0","base","then","saved","error","problem","status","undefined","own","Object","entries","filter","t10","path","startsWith","flatMap","_temp","message","detail","title","Error","formatMessage","saveValueFailed","length","commit","draft_0","failures","partial","values","flat","map","cancel","onKeyDown","event","key","stopPropagation","has","preventDefault","onBlur","event_0","next","relatedTarget","Element","currentTarget","contains","closest","control","draft_1","errors_0","disabled","next_0","t11","t12","savingValue","t13","t14","t15","cell","editValue","edit","list","Conflict","onKeep","valueConflict","keepTheirs","saveMine","conflictActions","conflict","_c","styles"],"sources":["../../src/inline/InlineCell.tsx"],"sourcesContent":["import { Pencil } from 'lucide-react';\nimport {\n type FocusEvent,\n type KeyboardEvent,\n type ReactNode,\n useEffect,\n useRef,\n useState,\n} from 'react';\n\nimport { getProblem } from '@panneau/core';\nimport { BooleanFieldEntry } from '@panneau/fields/boolean';\nimport { DateFieldEntry } from '@panneau/fields/date';\nimport { NumberFieldEntry } from '@panneau/fields/number';\nimport { SelectFieldEntry } from '@panneau/fields/select';\nimport { TextFieldEntry } from '@panneau/fields/text';\nimport type { Field, RuleFailure } from '@panneau/protocol';\nimport { checkValues } from '@panneau/protocol/runtime';\nimport { Button, IconButton, Spinner, usePanneauIntl } from '@panneau/ui';\n\nimport { messages } from '../messages';\nimport type { ListItem } from '../types';\nimport styles from './Inline.module.css';\n\nconst ENTRIES = {\n text: TextFieldEntry,\n number: NumberFieldEntry,\n select: SelectFieldEntry,\n boolean: BooleanFieldEntry,\n date: DateFieldEntry,\n} as const;\n\n/** Types that save as soon as a value is picked, rather than on Enter or when leaving the cell. */\nconst PICKED = new Set(['select', 'date']);\n\ntype State =\n | { mode: 'idle' }\n | { mode: 'editing'; draft: unknown; errors: readonly string[] }\n | { mode: 'saving'; draft: unknown }\n | { mode: 'conflict'; draft: unknown; current: ListItem };\n\nexport interface InlineCellProps {\n item: ListItem;\n /** The field the cell edits, of a type of `INLINE_TYPES`. */\n field: Field;\n /** The label of the item, for the name of the edit button. */\n itemLabel: string;\n /** The content of the cell when it doesn't edit: the value, or the title's link. */\n children: ReactNode;\n /** Renders a value of the field, for the current value of a conflict. */\n renderValue: (item: ListItem) => ReactNode;\n /** Saves the value on an item (the current one after a conflict), with its version. */\n onSave: (item: ListItem, value: unknown) => Promise<ListItem>;\n /** Shows an item the backend gave back: saved, or changed by someone else. */\n onItem: (item: ListItem) => void;\n formatError: (failure: RuleFailure) => string;\n}\n\nfunction isEqualValue(a: unknown, b: unknown): boolean {\n return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);\n}\n\n/**\n * A cell that edits its field in place (decision 0043): a checkbox for a boolean, otherwise the\n * value with a button that turns it into the field's control. Enter or leaving the cell saves,\n * Escape cancels; a select and a date save when one is picked. The value is checked by the\n * field's portable rules, then saved alone; errors and conflicts show in the cell.\n */\nexport function InlineCell({\n item,\n field,\n itemLabel,\n children,\n renderValue,\n onSave,\n onItem,\n formatError,\n}: InlineCellProps) {\n const intl = usePanneauIntl();\n const [state, setState] = useState<State>({ mode: 'idle' });\n const root = useRef<HTMLDivElement>(null);\n // Where focus goes once the cell has drawn its new state.\n const focus = useRef<'editor' | 'button' | null>(null);\n const label = field.label ?? field.name;\n const value = item[field.name];\n const isBoolean = field.type === 'boolean';\n const Entry = ENTRIES[field.type as keyof typeof ENTRIES];\n // The help of the field is for its form: a cell has no room for it.\n const { help: _help, ...bare } = field;\n\n useEffect(() => {\n if (focus.current === 'editor') {\n root.current?.querySelector<HTMLElement>('input, [role=\"spinbutton\"], button')?.focus();\n } else if (focus.current === 'button') {\n root.current?.querySelector<HTMLElement>('button')?.focus();\n }\n focus.current = null;\n });\n\n const save = (base: ListItem, draft: unknown) => {\n setState({ mode: 'saving', draft });\n onSave(base, draft).then(\n (saved) => {\n onItem(saved);\n focus.current = isBoolean ? null : 'button';\n setState({ mode: 'idle' });\n },\n (error: unknown) => {\n const problem = getProblem(error);\n if (problem?.status === 412 && problem.current !== undefined) {\n onItem(problem.current);\n setState({ mode: 'conflict', draft, current: problem.current });\n return;\n }\n const own = Object.entries(problem?.errors ?? {})\n .filter(([path]) => path === field.name || path.startsWith(`${field.name}.`))\n .flatMap(([, list]) => list);\n const message =\n problem?.detail ??\n (problem !== null ? problem.title : null) ??\n (error instanceof Error && error.message !== '' ? error.message : null) ??\n intl.formatMessage(messages.saveValueFailed);\n focus.current = 'editor';\n setState({ mode: 'editing', draft, errors: own.length > 0 ? own : [message] });\n },\n );\n };\n\n const commit = (draft: unknown) => {\n if (isEqualValue(draft, value)) {\n focus.current = 'button';\n setState({ mode: 'idle' });\n return;\n }\n const failures = checkValues(\n [field],\n { [field.name]: draft },\n {\n mode: 'update',\n partial: true,\n item,\n },\n );\n const errors = Object.values(failures)\n .flat()\n .map((failure) => formatError(failure));\n if (errors.length > 0) {\n setState({ mode: 'editing', draft, errors });\n return;\n }\n save(item, draft);\n };\n\n const cancel = () => {\n focus.current = 'button';\n setState({ mode: 'idle' });\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (state.mode !== 'editing') {\n return;\n }\n if (event.key === 'Escape') {\n event.stopPropagation();\n cancel();\n } else if (event.key === 'Enter' && !PICKED.has(field.type)) {\n event.preventDefault();\n commit(state.draft);\n }\n };\n\n // Leaving the cell saves; the popover of a select or a date picker is part of it.\n const onBlur = (event: FocusEvent) => {\n const next = event.relatedTarget;\n if (\n state.mode !== 'editing' ||\n (next instanceof Element &&\n (event.currentTarget.contains(next) || next.closest('.panneau-popover') !== null))\n ) {\n return;\n }\n commit(state.draft);\n };\n\n const control = (draft: unknown, errors: readonly string[], disabled: boolean) => (\n <Entry\n field={bare}\n path={field.name}\n value={draft}\n onChange={(next) => {\n if (isBoolean || (PICKED.has(field.type) && next !== null)) {\n commit(next);\n } else {\n setState({ mode: 'editing', draft: next, errors: [] });\n }\n }}\n errors={errors}\n disabled={disabled}\n hideLabel\n />\n );\n\n if (isBoolean) {\n return (\n <div className={styles.cell}>\n <div className={styles.control}>\n {control(\n state.mode === 'idle' ? value : state.draft,\n state.mode === 'editing' ? state.errors : [],\n state.mode === 'saving',\n )}\n {state.mode === 'saving' ? (\n <Spinner size=\"sm\" label={intl.formatMessage(messages.savingValue)} />\n ) : null}\n </div>\n {state.mode === 'conflict' ? (\n <Conflict\n current={renderValue(state.current)}\n onKeep={() => setState({ mode: 'idle' })}\n onSave={() => save(state.current, state.draft)}\n />\n ) : null}\n </div>\n );\n }\n\n if (state.mode === 'idle') {\n return (\n <div ref={root} className={styles.cell}>\n <span className={styles.value}>{children}</span>\n <IconButton\n icon={Pencil}\n size=\"sm\"\n label={intl.formatMessage(messages.editValue, {\n field: label,\n item: itemLabel,\n })}\n showTooltip={false}\n className={styles.edit}\n onPress={() => {\n focus.current = 'editor';\n setState({ mode: 'editing', draft: value, errors: [] });\n }}\n />\n </div>\n );\n }\n\n return (\n <div ref={root} className={styles.cell} onKeyDown={onKeyDown} onBlur={onBlur}>\n <div className={styles.control}>\n {control(\n state.draft,\n state.mode === 'editing' ? state.errors : [],\n state.mode === 'saving',\n )}\n {state.mode === 'saving' ? (\n <Spinner size=\"sm\" label={intl.formatMessage(messages.savingValue)} />\n ) : null}\n </div>\n {state.mode === 'conflict' ? (\n <Conflict\n current={renderValue(state.current)}\n onKeep={() => {\n focus.current = 'button';\n setState({ mode: 'idle' });\n }}\n onSave={() => save(state.current, state.draft)}\n />\n ) : null}\n </div>\n );\n}\n\n/** What someone else saved, with the choice of keeping it or saving over it. */\nfunction Conflict({\n current,\n onKeep,\n onSave,\n}: {\n current: ReactNode;\n onKeep: () => void;\n onSave: () => void;\n}) {\n const intl = usePanneauIntl();\n return (\n <div className={styles.conflict} role=\"alert\">\n <span>\n {intl.formatMessage(messages.valueConflict)} <strong>{current}</strong>\n </span>\n <span className={styles.conflictActions}>\n <Button size=\"sm\" appearance=\"ghost\" onPress={onKeep}>\n {intl.formatMessage(messages.keepTheirs)}\n </Button>\n <Button size=\"sm\" onPress={onSave}>\n {intl.formatMessage(messages.saveMine)}\n </Button>\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Field } from '@panneau/protocol';
2
+ import type { ListColumn, ListItem } from '../types.js';
3
+ /** The types a column edits in place (decision 0043). */
4
+ export declare const INLINE_TYPES: ReadonlySet<string>;
5
+ /** Whether a field can be edited in place: a simple type, one value, not read-only. */
6
+ export declare function isInlineField(field: Field | null | undefined): field is Field;
7
+ /**
8
+ * The field a column edits in place on an item, or `null`: a field of `inlineEdit`, of a simple
9
+ * type, shown for the item (`visibleWhen`), on an item that can be updated.
10
+ */
11
+ export declare function getInlineField(column: ListColumn, item: ListItem, inlineEdit: readonly string[]): Field | null;
12
+ //# sourceMappingURL=editable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editable.d.ts","sourceRoot":"","sources":["../../src/inline/editable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAG/C,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAErD,yDAAyD;AACzD,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,CAM3C,CAAC;AAEH,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,KAAK,CAU7E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC1B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,SAAS,MAAM,EAAE,GAC9B,KAAK,GAAG,IAAI,CAYd"}
@@ -0,0 +1,29 @@
1
+ import { evaluateCondition } from "@panneau/protocol/runtime";
2
+ //#region src/inline/editable.ts
3
+ /** The types a column edits in place (decision 0043). */
4
+ var INLINE_TYPES = /* @__PURE__ */ new Set([
5
+ "text",
6
+ "number",
7
+ "select",
8
+ "boolean",
9
+ "date"
10
+ ]);
11
+ /** Whether a field can be edited in place: a simple type, one value, not read-only. */
12
+ function isInlineField(field) {
13
+ return field !== null && field !== void 0 && INLINE_TYPES.has(field.type) && field.localized !== true && field.multiple !== true && field.readOnly !== true && field.createOnly !== true;
14
+ }
15
+ /**
16
+ * The field a column edits in place on an item, or `null`: a field of `inlineEdit`, of a simple
17
+ * type, shown for the item (`visibleWhen`), on an item that can be updated.
18
+ */
19
+ function getInlineField(column, item, inlineEdit) {
20
+ const field = column.field;
21
+ if (!isInlineField(field) || !inlineEdit.includes(field.name)) return null;
22
+ if (item.abilities?.update !== true) return null;
23
+ if (field.visibleWhen !== void 0 && !evaluateCondition(field.visibleWhen, item)) return null;
24
+ return field;
25
+ }
26
+ //#endregion
27
+ export { INLINE_TYPES, getInlineField, isInlineField };
28
+
29
+ //# sourceMappingURL=editable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editable.js","names":["Field","evaluateCondition","ListColumn","ListItem","INLINE_TYPES","ReadonlySet","Set","isInlineField","field","undefined","has","type","localized","multiple","readOnly","createOnly","getInlineField","column","item","inlineEdit","includes","name","abilities","update","visibleWhen"],"sources":["../../src/inline/editable.ts"],"sourcesContent":["import type { Field } from '@panneau/protocol';\nimport { evaluateCondition } from '@panneau/protocol/runtime';\n\nimport type { ListColumn, ListItem } from '../types';\n\n/** The types a column edits in place (decision 0043). */\nexport const INLINE_TYPES: ReadonlySet<string> = new Set([\n 'text',\n 'number',\n 'select',\n 'boolean',\n 'date',\n]);\n\n/** Whether a field can be edited in place: a simple type, one value, not read-only. */\nexport function isInlineField(field: Field | null | undefined): field is Field {\n return (\n field !== null &&\n field !== undefined &&\n INLINE_TYPES.has(field.type) &&\n field.localized !== true &&\n field.multiple !== true &&\n field.readOnly !== true &&\n field.createOnly !== true\n );\n}\n\n/**\n * The field a column edits in place on an item, or `null`: a field of `inlineEdit`, of a simple\n * type, shown for the item (`visibleWhen`), on an item that can be updated.\n */\nexport function getInlineField(\n column: ListColumn,\n item: ListItem,\n inlineEdit: readonly string[],\n): Field | null {\n const field = column.field;\n if (!isInlineField(field) || !inlineEdit.includes(field.name)) {\n return null;\n }\n if (item.abilities?.update !== true) {\n return null;\n }\n if (field.visibleWhen !== undefined && !evaluateCondition(field.visibleWhen, item)) {\n return null;\n }\n return field;\n}\n"],"mappings":";;;AAMA,IAAaI,+BAAoC,IAAIE,IAAI;CACrD;CACA;CACA;CACA;CACA;AAAM,CACT;;AAGD,SAAgBC,cAAcC,OAAiD;CAC3E,OACIA,UAAU,QACVA,UAAUC,KAAAA,KACVL,aAAaM,IAAIF,MAAMG,IAAI,KAC3BH,MAAMI,cAAc,QACpBJ,MAAMK,aAAa,QACnBL,MAAMM,aAAa,QACnBN,MAAMO,eAAe;AAE7B;;;;;AAMA,SAAgBC,eACZC,QACAC,MACAC,YACY;CACZ,MAAMX,QAAQS,OAAOT;CACrB,IAAI,CAACD,cAAcC,KAAK,KAAK,CAACW,WAAWC,SAASZ,MAAMa,IAAI,GACxD,OAAO;CAEX,IAAIH,KAAKI,WAAWC,WAAW,MAC3B,OAAO;CAEX,IAAIf,MAAMgB,gBAAgBf,KAAAA,KAAa,CAACR,kBAAkBO,MAAMgB,aAAaN,IAAI,GAC7E,OAAO;CAEX,OAAOV;AACX"}