@crisp-ui-kit/crisp 0.52.0 → 0.52.1
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.
|
@@ -254,12 +254,61 @@ const CREATE_REFERENCE_VALUE = "__crisp_create_reference__";
|
|
|
254
254
|
function dataReferenceOptionLabel(o) {
|
|
255
255
|
return ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-datatable-refoption", children: [(0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "sm", className: "crisp-datatable-refoption-avatar", children: o.avatar ? ((0, jsx_runtime_1.jsx)("img", { src: o.avatar, alt: "" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.CircleUserRound, { size: 12, strokeWidth: 1.75, "aria-hidden": "true" })) }), o.label] }));
|
|
256
256
|
}
|
|
257
|
+
/** `Select` owns its own closed-by-default `open` state with no prop or
|
|
258
|
+
* imperative way to start it open (see select.tsx) -- the only thing that
|
|
259
|
+
* opens it is a real click on its trigger button. Attio's own picker opens
|
|
260
|
+
* the instant a `select`/`reference` cell enters edit mode (measured live
|
|
261
|
+
* against a GRC test company, 2026-09-24), not after a second click on an
|
|
262
|
+
* already-mounted, still-closed trigger, so drive that click ourselves.
|
|
263
|
+
* Guarded against firing twice (StrictMode double-invokes effects) --
|
|
264
|
+
* `Select`'s own trigger toggles, so a second click would close what the
|
|
265
|
+
* first one opened. */
|
|
266
|
+
function useAutoOpenSelect() {
|
|
267
|
+
const ref = React.useRef(null);
|
|
268
|
+
const opened = React.useRef(false);
|
|
269
|
+
React.useEffect(() => {
|
|
270
|
+
if (opened.current)
|
|
271
|
+
return;
|
|
272
|
+
opened.current = true;
|
|
273
|
+
ref.current
|
|
274
|
+
?.querySelector(".crisp-select-trigger")
|
|
275
|
+
?.click();
|
|
276
|
+
}, []);
|
|
277
|
+
return ref;
|
|
278
|
+
}
|
|
279
|
+
/** `select`/`reference` open crisp's own `Select`, which closes its own
|
|
280
|
+
* popover on Escape (see select.tsx's "document-level fallback for Escape")
|
|
281
|
+
* but has no callback telling the CELL to leave editing mode -- and, same
|
|
282
|
+
* problem that fallback exists for, DOM focus doesn't reliably end up
|
|
283
|
+
* inside the newly-opened popover, so a `TableCell`-level `onKeyDown` can't
|
|
284
|
+
* be trusted to see the key at all (#453). Mirror Select's own fix for
|
|
285
|
+
* that: a raw `document` listener, not a focus-dependent React one. */
|
|
286
|
+
function useEscapeExitsEditing(onCancel) {
|
|
287
|
+
React.useEffect(() => {
|
|
288
|
+
const onKey = (e) => {
|
|
289
|
+
if (e.key === "Escape")
|
|
290
|
+
onCancel();
|
|
291
|
+
};
|
|
292
|
+
document.addEventListener("keydown", onKey);
|
|
293
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
294
|
+
}, [onCancel]);
|
|
295
|
+
}
|
|
296
|
+
/** `editor: "select"`'s picker, its own component (not inline in the
|
|
297
|
+
* row-render loop, like `DataReferenceEditor` below) because it needs the
|
|
298
|
+
* open-on-mount and escape-exits-editing effects above (#453). */
|
|
299
|
+
function DataSelectEditor({ col, value, isPending, onCommit, onCancel, }) {
|
|
300
|
+
const ref = useAutoOpenSelect();
|
|
301
|
+
useEscapeExitsEditing(onCancel);
|
|
302
|
+
return ((0, jsx_runtime_1.jsx)("span", { ref: ref, style: { display: "contents" }, children: (0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", "aria-label": `Edit ${col.key}`, value: asText(value) || undefined, disabled: isPending, options: col.options ?? [], onValueChange: onCommit }) }));
|
|
303
|
+
}
|
|
257
304
|
/** `editor: "reference"`'s picker, its own component (not inline in the
|
|
258
305
|
* row-render loop) because it needs its own `useState` for the search query
|
|
259
306
|
* -- one instance per cell, same reasoning `select`/`switch` don't need a
|
|
260
307
|
* component of their own but this does. */
|
|
261
|
-
function DataReferenceEditor({ col, value, isPending, onCommit, }) {
|
|
308
|
+
function DataReferenceEditor({ col, value, isPending, onCommit, onCancel, }) {
|
|
262
309
|
const [query, setQuery] = React.useState("");
|
|
310
|
+
const ref = useAutoOpenSelect();
|
|
311
|
+
useEscapeExitsEditing(onCancel);
|
|
263
312
|
const options = (col.refOptions ?? []).map((o) => ({
|
|
264
313
|
value: o.value,
|
|
265
314
|
label: dataReferenceOptionLabel(o),
|
|
@@ -274,12 +323,12 @@ function DataReferenceEditor({ col, value, isPending, onCommit, }) {
|
|
|
274
323
|
keywords: query,
|
|
275
324
|
});
|
|
276
325
|
}
|
|
277
|
-
return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", searchable: true, searchPlaceholder: col.refSearchPlaceholder, onSearchChange: setQuery, "aria-label": `Edit ${col.key}`, value: asText(value) || undefined, disabled: isPending, options: options, onValueChange: (v) => {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
326
|
+
return ((0, jsx_runtime_1.jsx)("span", { ref: ref, style: { display: "contents" }, children: (0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", searchable: true, searchPlaceholder: col.refSearchPlaceholder, onSearchChange: setQuery, "aria-label": `Edit ${col.key}`, value: asText(value) || undefined, disabled: isPending, options: options, onValueChange: (v) => {
|
|
327
|
+
if (v === CREATE_REFERENCE_VALUE)
|
|
328
|
+
col.onCreateReference?.(query);
|
|
329
|
+
else
|
|
330
|
+
onCommit(v);
|
|
331
|
+
} }) }));
|
|
283
332
|
}
|
|
284
333
|
function DataTable({ columns: columnsProp, data, selectable = false, selectedIds, onSelectionChange, selectAllLabel = "Select all rows", selectRowLabel = (row) => `Select row ${row.id}`, selectionAnnouncement = (count) => count === 0 ? "Selection cleared" : `${count} selected`, columnMenu = false, onColumnsChange, groupBy, onRowsReorder, error, sort: sortProp, defaultSort, onSortChange, filterable = false, searchPlaceholder = "Search…", searchLabel = "Search table", onEdit, stickyFirst = true, stickyLast = false, locale: localeProp, avatarTone = "brand", surface, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", empty = "No results.", showCount = true, countLabel = (shown, total) => shown === total
|
|
285
334
|
? `${total} ${total === 1 ? "row" : "rows"}`
|
|
@@ -502,10 +551,11 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
502
551
|
* throughout, and the browser's own picker already
|
|
503
552
|
* carries both a calendar and a time control.
|
|
504
553
|
* text/entity a plain text field
|
|
505
|
-
* `
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
554
|
+
* `editor: "select"`/`"reference"` open their own picker instead of this
|
|
555
|
+
* native-input family, but through the SAME double-click gate (#453) --
|
|
556
|
+
* see the row-render loop's `isEditing` branch for those, this function
|
|
557
|
+
* only covers the native-input-backed types. `editor: "switch"` is the
|
|
558
|
+
* one editor with no double-click gate at all (always live).
|
|
509
559
|
*/
|
|
510
560
|
function editorFor(col) {
|
|
511
561
|
switch (col.type) {
|
|
@@ -559,11 +609,12 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
559
609
|
return;
|
|
560
610
|
}
|
|
561
611
|
else if (e.key === "Enter") {
|
|
562
|
-
//
|
|
563
|
-
//
|
|
564
|
-
//
|
|
565
|
-
//
|
|
566
|
-
|
|
612
|
+
// #453: `switch` has no separate editing mode to enter -- it's
|
|
613
|
+
// already its own live widget (#385); Enter there is a no-op, same
|
|
614
|
+
// as any other non-editable cell that isn't wired to onRowClick.
|
|
615
|
+
// `select`/`reference` DO have one now (like every other editor),
|
|
616
|
+
// so Enter opens them too.
|
|
617
|
+
if (columns[c]?.editable && columns[c]?.editor !== "switch") {
|
|
567
618
|
e.preventDefault();
|
|
568
619
|
openEditing(selCell.id, selCell.key);
|
|
569
620
|
}
|
|
@@ -794,14 +845,37 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
794
845
|
const errorHere = editError?.id === row.id && editError?.key === col.key
|
|
795
846
|
? editError.message
|
|
796
847
|
: null;
|
|
797
|
-
//
|
|
798
|
-
// live widget --
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
//
|
|
802
|
-
//
|
|
803
|
-
|
|
804
|
-
|
|
848
|
+
// #453: a `switch` editable column IS always its own
|
|
849
|
+
// live widget -- flipping the toggle IS the commit
|
|
850
|
+
// gesture, there's no meaningfully different "rest"
|
|
851
|
+
// display for a boolean. `select`/`reference` are
|
|
852
|
+
// NOT this: at rest they must show the column's own
|
|
853
|
+
// render/format like every other editable type
|
|
854
|
+
// (#412 empty handling included), and only open on
|
|
855
|
+
// double-click (#411) -- this branch previously
|
|
856
|
+
// rendered them unconditionally, replacing every
|
|
857
|
+
// row's own render with a picker trigger, which is
|
|
858
|
+
// the regression #453 reported (also #432/#449,
|
|
859
|
+
// which never had a "rest" state of their own to get
|
|
860
|
+
// right). See the `isEditing` block below for their
|
|
861
|
+
// actual editor.
|
|
862
|
+
if (col.editable && col.editor === "switch") {
|
|
863
|
+
return ((0, jsx_runtime_1.jsxs)(table_1.TableCell, { align: alignFor(col), ...sticky, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined, children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-datatable-edit-picker", children: [(0, jsx_runtime_1.jsx)(switch_1.Switch, { "aria-label": `Edit ${col.key}`, checked: value === true || value === "true", disabled: isPending, onCheckedChange: (checked) => commitEdit(row.id, col.key, String(checked)) }), isPending && ((0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
864
|
+
}
|
|
865
|
+
if (isEditing &&
|
|
866
|
+
(col.editor === "select" || col.editor === "reference")) {
|
|
867
|
+
return ((0, jsx_runtime_1.jsxs)(table_1.TableCell, { align: alignFor(col), ...sticky, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined,
|
|
868
|
+
// Same as the plain text editor's own cell:
|
|
869
|
+
// opening/using the picker must not also fire
|
|
870
|
+
// the row's onRowClick. Escape is handled by
|
|
871
|
+
// `DataSelectEditor`/`DataReferenceEditor`
|
|
872
|
+
// themselves (a document-level listener, not a
|
|
873
|
+
// `TableCell`-level `onKeyDown`) -- opening the
|
|
874
|
+
// picker moves DOM focus into a floating,
|
|
875
|
+
// portaled popover, so a handler here can't
|
|
876
|
+
// rely on the key event ever bubbling through
|
|
877
|
+
// this element (#453).
|
|
878
|
+
onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-datatable-edit-picker", children: [col.editor === "select" ? ((0, jsx_runtime_1.jsx)(DataSelectEditor, { col: col, value: value, isPending: isPending, onCommit: (v) => commitEdit(row.id, col.key, v), onCancel: () => setEditing(null) })) : ((0, jsx_runtime_1.jsx)(DataReferenceEditor, { col: col, value: value, isPending: isPending, onCommit: (v) => commitEdit(row.id, col.key, v), onCancel: () => setEditing(null) })), isPending && ((0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
805
879
|
}
|
|
806
880
|
if (isEditing) {
|
|
807
881
|
return ((0, jsx_runtime_1.jsxs)(table_1.TableCell, { align: alignFor(col), ...sticky, onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsx)("input", { className: "crisp-datatable-edit",
|
|
@@ -218,12 +218,61 @@ const CREATE_REFERENCE_VALUE = "__crisp_create_reference__";
|
|
|
218
218
|
function dataReferenceOptionLabel(o) {
|
|
219
219
|
return (_jsxs("span", { className: "crisp-datatable-refoption", children: [_jsx(Avatar, { size: "sm", className: "crisp-datatable-refoption-avatar", children: o.avatar ? (_jsx("img", { src: o.avatar, alt: "" })) : (_jsx(CircleUserRound, { size: 12, strokeWidth: 1.75, "aria-hidden": "true" })) }), o.label] }));
|
|
220
220
|
}
|
|
221
|
+
/** `Select` owns its own closed-by-default `open` state with no prop or
|
|
222
|
+
* imperative way to start it open (see select.tsx) -- the only thing that
|
|
223
|
+
* opens it is a real click on its trigger button. Attio's own picker opens
|
|
224
|
+
* the instant a `select`/`reference` cell enters edit mode (measured live
|
|
225
|
+
* against a GRC test company, 2026-09-24), not after a second click on an
|
|
226
|
+
* already-mounted, still-closed trigger, so drive that click ourselves.
|
|
227
|
+
* Guarded against firing twice (StrictMode double-invokes effects) --
|
|
228
|
+
* `Select`'s own trigger toggles, so a second click would close what the
|
|
229
|
+
* first one opened. */
|
|
230
|
+
function useAutoOpenSelect() {
|
|
231
|
+
const ref = React.useRef(null);
|
|
232
|
+
const opened = React.useRef(false);
|
|
233
|
+
React.useEffect(() => {
|
|
234
|
+
if (opened.current)
|
|
235
|
+
return;
|
|
236
|
+
opened.current = true;
|
|
237
|
+
ref.current
|
|
238
|
+
?.querySelector(".crisp-select-trigger")
|
|
239
|
+
?.click();
|
|
240
|
+
}, []);
|
|
241
|
+
return ref;
|
|
242
|
+
}
|
|
243
|
+
/** `select`/`reference` open crisp's own `Select`, which closes its own
|
|
244
|
+
* popover on Escape (see select.tsx's "document-level fallback for Escape")
|
|
245
|
+
* but has no callback telling the CELL to leave editing mode -- and, same
|
|
246
|
+
* problem that fallback exists for, DOM focus doesn't reliably end up
|
|
247
|
+
* inside the newly-opened popover, so a `TableCell`-level `onKeyDown` can't
|
|
248
|
+
* be trusted to see the key at all (#453). Mirror Select's own fix for
|
|
249
|
+
* that: a raw `document` listener, not a focus-dependent React one. */
|
|
250
|
+
function useEscapeExitsEditing(onCancel) {
|
|
251
|
+
React.useEffect(() => {
|
|
252
|
+
const onKey = (e) => {
|
|
253
|
+
if (e.key === "Escape")
|
|
254
|
+
onCancel();
|
|
255
|
+
};
|
|
256
|
+
document.addEventListener("keydown", onKey);
|
|
257
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
258
|
+
}, [onCancel]);
|
|
259
|
+
}
|
|
260
|
+
/** `editor: "select"`'s picker, its own component (not inline in the
|
|
261
|
+
* row-render loop, like `DataReferenceEditor` below) because it needs the
|
|
262
|
+
* open-on-mount and escape-exits-editing effects above (#453). */
|
|
263
|
+
function DataSelectEditor({ col, value, isPending, onCommit, onCancel, }) {
|
|
264
|
+
const ref = useAutoOpenSelect();
|
|
265
|
+
useEscapeExitsEditing(onCancel);
|
|
266
|
+
return (_jsx("span", { ref: ref, style: { display: "contents" }, children: _jsx(Select, { variant: "ghost", "aria-label": `Edit ${col.key}`, value: asText(value) || undefined, disabled: isPending, options: col.options ?? [], onValueChange: onCommit }) }));
|
|
267
|
+
}
|
|
221
268
|
/** `editor: "reference"`'s picker, its own component (not inline in the
|
|
222
269
|
* row-render loop) because it needs its own `useState` for the search query
|
|
223
270
|
* -- one instance per cell, same reasoning `select`/`switch` don't need a
|
|
224
271
|
* component of their own but this does. */
|
|
225
|
-
function DataReferenceEditor({ col, value, isPending, onCommit, }) {
|
|
272
|
+
function DataReferenceEditor({ col, value, isPending, onCommit, onCancel, }) {
|
|
226
273
|
const [query, setQuery] = React.useState("");
|
|
274
|
+
const ref = useAutoOpenSelect();
|
|
275
|
+
useEscapeExitsEditing(onCancel);
|
|
227
276
|
const options = (col.refOptions ?? []).map((o) => ({
|
|
228
277
|
value: o.value,
|
|
229
278
|
label: dataReferenceOptionLabel(o),
|
|
@@ -238,12 +287,12 @@ function DataReferenceEditor({ col, value, isPending, onCommit, }) {
|
|
|
238
287
|
keywords: query,
|
|
239
288
|
});
|
|
240
289
|
}
|
|
241
|
-
return (_jsx(Select, { variant: "ghost", searchable: true, searchPlaceholder: col.refSearchPlaceholder, onSearchChange: setQuery, "aria-label": `Edit ${col.key}`, value: asText(value) || undefined, disabled: isPending, options: options, onValueChange: (v) => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
290
|
+
return (_jsx("span", { ref: ref, style: { display: "contents" }, children: _jsx(Select, { variant: "ghost", searchable: true, searchPlaceholder: col.refSearchPlaceholder, onSearchChange: setQuery, "aria-label": `Edit ${col.key}`, value: asText(value) || undefined, disabled: isPending, options: options, onValueChange: (v) => {
|
|
291
|
+
if (v === CREATE_REFERENCE_VALUE)
|
|
292
|
+
col.onCreateReference?.(query);
|
|
293
|
+
else
|
|
294
|
+
onCommit(v);
|
|
295
|
+
} }) }));
|
|
247
296
|
}
|
|
248
297
|
export function DataTable({ columns: columnsProp, data, selectable = false, selectedIds, onSelectionChange, selectAllLabel = "Select all rows", selectRowLabel = (row) => `Select row ${row.id}`, selectionAnnouncement = (count) => count === 0 ? "Selection cleared" : `${count} selected`, columnMenu = false, onColumnsChange, groupBy, onRowsReorder, error, sort: sortProp, defaultSort, onSortChange, filterable = false, searchPlaceholder = "Search…", searchLabel = "Search table", onEdit, stickyFirst = true, stickyLast = false, locale: localeProp, avatarTone = "brand", surface, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", empty = "No results.", showCount = true, countLabel = (shown, total) => shown === total
|
|
249
298
|
? `${total} ${total === 1 ? "row" : "rows"}`
|
|
@@ -466,10 +515,11 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
466
515
|
* throughout, and the browser's own picker already
|
|
467
516
|
* carries both a calendar and a time control.
|
|
468
517
|
* text/entity a plain text field
|
|
469
|
-
* `
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
518
|
+
* `editor: "select"`/`"reference"` open their own picker instead of this
|
|
519
|
+
* native-input family, but through the SAME double-click gate (#453) --
|
|
520
|
+
* see the row-render loop's `isEditing` branch for those, this function
|
|
521
|
+
* only covers the native-input-backed types. `editor: "switch"` is the
|
|
522
|
+
* one editor with no double-click gate at all (always live).
|
|
473
523
|
*/
|
|
474
524
|
function editorFor(col) {
|
|
475
525
|
switch (col.type) {
|
|
@@ -523,11 +573,12 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
523
573
|
return;
|
|
524
574
|
}
|
|
525
575
|
else if (e.key === "Enter") {
|
|
526
|
-
//
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
//
|
|
530
|
-
|
|
576
|
+
// #453: `switch` has no separate editing mode to enter -- it's
|
|
577
|
+
// already its own live widget (#385); Enter there is a no-op, same
|
|
578
|
+
// as any other non-editable cell that isn't wired to onRowClick.
|
|
579
|
+
// `select`/`reference` DO have one now (like every other editor),
|
|
580
|
+
// so Enter opens them too.
|
|
581
|
+
if (columns[c]?.editable && columns[c]?.editor !== "switch") {
|
|
531
582
|
e.preventDefault();
|
|
532
583
|
openEditing(selCell.id, selCell.key);
|
|
533
584
|
}
|
|
@@ -758,14 +809,37 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
758
809
|
const errorHere = editError?.id === row.id && editError?.key === col.key
|
|
759
810
|
? editError.message
|
|
760
811
|
: null;
|
|
761
|
-
//
|
|
762
|
-
// live widget --
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
//
|
|
766
|
-
//
|
|
767
|
-
|
|
768
|
-
|
|
812
|
+
// #453: a `switch` editable column IS always its own
|
|
813
|
+
// live widget -- flipping the toggle IS the commit
|
|
814
|
+
// gesture, there's no meaningfully different "rest"
|
|
815
|
+
// display for a boolean. `select`/`reference` are
|
|
816
|
+
// NOT this: at rest they must show the column's own
|
|
817
|
+
// render/format like every other editable type
|
|
818
|
+
// (#412 empty handling included), and only open on
|
|
819
|
+
// double-click (#411) -- this branch previously
|
|
820
|
+
// rendered them unconditionally, replacing every
|
|
821
|
+
// row's own render with a picker trigger, which is
|
|
822
|
+
// the regression #453 reported (also #432/#449,
|
|
823
|
+
// which never had a "rest" state of their own to get
|
|
824
|
+
// right). See the `isEditing` block below for their
|
|
825
|
+
// actual editor.
|
|
826
|
+
if (col.editable && col.editor === "switch") {
|
|
827
|
+
return (_jsxs(TableCell, { align: alignFor(col), ...sticky, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined, children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [_jsx(Switch, { "aria-label": `Edit ${col.key}`, checked: value === true || value === "true", disabled: isPending, onCheckedChange: (checked) => commitEdit(row.id, col.key, String(checked)) }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
828
|
+
}
|
|
829
|
+
if (isEditing &&
|
|
830
|
+
(col.editor === "select" || col.editor === "reference")) {
|
|
831
|
+
return (_jsxs(TableCell, { align: alignFor(col), ...sticky, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined,
|
|
832
|
+
// Same as the plain text editor's own cell:
|
|
833
|
+
// opening/using the picker must not also fire
|
|
834
|
+
// the row's onRowClick. Escape is handled by
|
|
835
|
+
// `DataSelectEditor`/`DataReferenceEditor`
|
|
836
|
+
// themselves (a document-level listener, not a
|
|
837
|
+
// `TableCell`-level `onKeyDown`) -- opening the
|
|
838
|
+
// picker moves DOM focus into a floating,
|
|
839
|
+
// portaled popover, so a handler here can't
|
|
840
|
+
// rely on the key event ever bubbling through
|
|
841
|
+
// this element (#453).
|
|
842
|
+
onClick: (e) => e.stopPropagation(), children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [col.editor === "select" ? (_jsx(DataSelectEditor, { col: col, value: value, isPending: isPending, onCommit: (v) => commitEdit(row.id, col.key, v), onCancel: () => setEditing(null) })) : (_jsx(DataReferenceEditor, { col: col, value: value, isPending: isPending, onCommit: (v) => commitEdit(row.id, col.key, v), onCancel: () => setEditing(null) })), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
769
843
|
}
|
|
770
844
|
if (isEditing) {
|
|
771
845
|
return (_jsxs(TableCell, { align: alignFor(col), ...sticky, onClick: (e) => e.stopPropagation(), children: [_jsx("input", { className: "crisp-datatable-edit",
|
|
@@ -45,16 +45,17 @@ export type DataTableColumn<T> = {
|
|
|
45
45
|
* commits) — see `editor` for `select`/`switch`/`reference`. */
|
|
46
46
|
editable?: boolean;
|
|
47
47
|
/** The inline editor an `editable` column opens. Omitted: a plain field
|
|
48
|
-
* inferred from `type` (number/date/datetime/text)
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* the
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
48
|
+
* inferred from `type` (number/date/datetime/text). `"select"`/
|
|
49
|
+
* `"reference"` render crisp's own `Select` the same way — at rest the
|
|
50
|
+
* cell shows the column's `render`/formatted value like every other
|
|
51
|
+
* editable type (#453), and double-click (or Enter on the selected
|
|
52
|
+
* cell) opens the picker; choosing a value commits and closes it,
|
|
53
|
+
* Escape cancels. `select` reads `options`; `reference` reads
|
|
54
|
+
* `refOptions` and is search-enabled, the same searchable-`Select`
|
|
55
|
+
* shape as `RecordsTable`'s own `ReferenceCell`. `"switch"` is the one
|
|
56
|
+
* exception — ALWAYS its own live widget, no double-click gate, since
|
|
57
|
+
* flipping the toggle IS the commit gesture and there's no meaningfully
|
|
58
|
+
* different "rest" display for a boolean. */
|
|
58
59
|
editor?: "select" | "switch" | "reference";
|
|
59
60
|
/** Options for `editor: "select"` (value/label, an optional `color` a
|
|
60
61
|
* caller's own `render` can read) — same shape as RecordsTable's
|