twico-ui 1.13.0 → 1.14.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.
- package/CHANGELOG.md +14 -0
- package/dist/index.cjs +79 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.mjs +79 -14
- package/dist/index.mjs.map +1 -1
- package/llms.txt +3 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1762,6 +1762,14 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
|
|
|
1762
1762
|
/** Restrict the toolbar quick-search to these column `field`s (client mode); defaults to every
|
|
1763
1763
|
* visible column. Mirrors `runDatatableQuery`'s `options.searchFields`. */
|
|
1764
1764
|
searchFields?: string[];
|
|
1765
|
+
/** Render the built-in toolbar quick-search box. Set `false` to externalize search — supply your
|
|
1766
|
+
* own input wired to `quickFilter`/`onQuickFilterChange` (mirrors CardGrid). @default true */
|
|
1767
|
+
searchable?: boolean;
|
|
1768
|
+
/** Controlled quick-search string; pair with `onQuickFilterChange` to drive search from outside
|
|
1769
|
+
* (the value still flows into client filtering and the `onServerChange` query). */
|
|
1770
|
+
quickFilter?: string;
|
|
1771
|
+
/** Fires with the new quick-search string as the user types (and when a host sets it). */
|
|
1772
|
+
onQuickFilterChange?: (value: string) => void;
|
|
1765
1773
|
/**
|
|
1766
1774
|
* Actions shown in the toolbar when one or more rows are selected (requires
|
|
1767
1775
|
* `checkboxSelection`). Each handler receives the selected keys, the resolved
|
|
@@ -1982,6 +1990,13 @@ interface DatatableColumn<T = any> {
|
|
|
1982
1990
|
editable?: boolean;
|
|
1983
1991
|
/** Editor type. "select" (or any column with `valueOptions`) renders a dropdown; else a text/number input by column type. */
|
|
1984
1992
|
editType?: "text" | "number" | "select";
|
|
1993
|
+
/** Full escape hatch for the inline cell editor — render your own control (a searchable / creatable /
|
|
1994
|
+
* async `Combobox`, a `MasterCombobox`, …) for a value backed by a large, extensible vocabulary.
|
|
1995
|
+
* Providing it makes the column editable (unless `editable: false`) and takes precedence over the
|
|
1996
|
+
* built-in select/text editor. Call `commit(nextValue)` to save (fires `onRowUpdate`/`onRowsChange`)
|
|
1997
|
+
* or `cancel()` to discard. Twico overlay dropdowns (portaled as `.twc-pop`) are exempt from the
|
|
1998
|
+
* cell's outside-click auto-cancel, so a Combobox popover works inside the cell. */
|
|
1999
|
+
renderEditCell?: (args: { value: any; row: T; field: string; commit: (nextValue: any) => void; cancel: () => void }) => React.ReactNode;
|
|
1985
2000
|
/** Hide the column header ⋮ menu. @default false */
|
|
1986
2001
|
disableColumnMenu?: boolean;
|
|
1987
2002
|
/** Start with this column's cell text wrapped onto multiple lines (the row grows down) instead of
|
|
@@ -1989,6 +2004,11 @@ interface DatatableColumn<T = any> {
|
|
|
1989
2004
|
wrapText?: boolean;
|
|
1990
2005
|
/** Distinct values for the "is any of" multi-value filter (recommended in server mode). */
|
|
1991
2006
|
valueOptions?: Array<string | { value: string; label: string }>;
|
|
2007
|
+
/** Async loader for the "is any of" filter's options — searchable/lazy server-backed values (for a
|
|
2008
|
+
* large reference/master list). Called with the type-ahead query (debounced; `""` primes the list)
|
|
2009
|
+
* and takes precedence over `valueOptions` for the filter picker, which then loads server-side
|
|
2010
|
+
* (no local filtering) with a loading state. */
|
|
2011
|
+
loadValueOptions?: (query: string) => Promise<Array<string | { value: string; label: string }>>;
|
|
1992
2012
|
/** Format the raw value to a string/number for display. */
|
|
1993
2013
|
valueFormatter?: (value: any, row: T) => React.ReactNode;
|
|
1994
2014
|
/** Fully custom cell renderer (badges, avatars, etc.). */
|
package/dist/index.d.ts
CHANGED
|
@@ -1762,6 +1762,14 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
|
|
|
1762
1762
|
/** Restrict the toolbar quick-search to these column `field`s (client mode); defaults to every
|
|
1763
1763
|
* visible column. Mirrors `runDatatableQuery`'s `options.searchFields`. */
|
|
1764
1764
|
searchFields?: string[];
|
|
1765
|
+
/** Render the built-in toolbar quick-search box. Set `false` to externalize search — supply your
|
|
1766
|
+
* own input wired to `quickFilter`/`onQuickFilterChange` (mirrors CardGrid). @default true */
|
|
1767
|
+
searchable?: boolean;
|
|
1768
|
+
/** Controlled quick-search string; pair with `onQuickFilterChange` to drive search from outside
|
|
1769
|
+
* (the value still flows into client filtering and the `onServerChange` query). */
|
|
1770
|
+
quickFilter?: string;
|
|
1771
|
+
/** Fires with the new quick-search string as the user types (and when a host sets it). */
|
|
1772
|
+
onQuickFilterChange?: (value: string) => void;
|
|
1765
1773
|
/**
|
|
1766
1774
|
* Actions shown in the toolbar when one or more rows are selected (requires
|
|
1767
1775
|
* `checkboxSelection`). Each handler receives the selected keys, the resolved
|
|
@@ -1982,6 +1990,13 @@ interface DatatableColumn<T = any> {
|
|
|
1982
1990
|
editable?: boolean;
|
|
1983
1991
|
/** Editor type. "select" (or any column with `valueOptions`) renders a dropdown; else a text/number input by column type. */
|
|
1984
1992
|
editType?: "text" | "number" | "select";
|
|
1993
|
+
/** Full escape hatch for the inline cell editor — render your own control (a searchable / creatable /
|
|
1994
|
+
* async `Combobox`, a `MasterCombobox`, …) for a value backed by a large, extensible vocabulary.
|
|
1995
|
+
* Providing it makes the column editable (unless `editable: false`) and takes precedence over the
|
|
1996
|
+
* built-in select/text editor. Call `commit(nextValue)` to save (fires `onRowUpdate`/`onRowsChange`)
|
|
1997
|
+
* or `cancel()` to discard. Twico overlay dropdowns (portaled as `.twc-pop`) are exempt from the
|
|
1998
|
+
* cell's outside-click auto-cancel, so a Combobox popover works inside the cell. */
|
|
1999
|
+
renderEditCell?: (args: { value: any; row: T; field: string; commit: (nextValue: any) => void; cancel: () => void }) => React.ReactNode;
|
|
1985
2000
|
/** Hide the column header ⋮ menu. @default false */
|
|
1986
2001
|
disableColumnMenu?: boolean;
|
|
1987
2002
|
/** Start with this column's cell text wrapped onto multiple lines (the row grows down) instead of
|
|
@@ -1989,6 +2004,11 @@ interface DatatableColumn<T = any> {
|
|
|
1989
2004
|
wrapText?: boolean;
|
|
1990
2005
|
/** Distinct values for the "is any of" multi-value filter (recommended in server mode). */
|
|
1991
2006
|
valueOptions?: Array<string | { value: string; label: string }>;
|
|
2007
|
+
/** Async loader for the "is any of" filter's options — searchable/lazy server-backed values (for a
|
|
2008
|
+
* large reference/master list). Called with the type-ahead query (debounced; `""` primes the list)
|
|
2009
|
+
* and takes precedence over `valueOptions` for the filter picker, which then loads server-side
|
|
2010
|
+
* (no local filtering) with a loading state. */
|
|
2011
|
+
loadValueOptions?: (query: string) => Promise<Array<string | { value: string; label: string }>>;
|
|
1992
2012
|
/** Format the raw value to a string/number for display. */
|
|
1993
2013
|
valueFormatter?: (value: any, row: T) => React.ReactNode;
|
|
1994
2014
|
/** Fully custom cell renderer (badges, avatars, etc.). */
|
package/dist/index.mjs
CHANGED
|
@@ -7070,7 +7070,8 @@ var FIELD_CSS2 = `
|
|
|
7070
7070
|
.twc-field__req { color: var(--color-danger); }
|
|
7071
7071
|
.twc-field__hint { font-size: var(--text-xs); color: var(--color-text-muted); }
|
|
7072
7072
|
.twc-field__error { font-size: var(--text-xs); color: var(--color-danger-subtle-fg); font-weight: var(--font-medium); }
|
|
7073
|
-
|
|
7073
|
+
`;
|
|
7074
|
+
var INPUT_EXTRA_CSS = `
|
|
7074
7075
|
.twc-input {
|
|
7075
7076
|
--_h: var(--control-h-md);
|
|
7076
7077
|
display: flex; align-items: center; gap: var(--space-2);
|
|
@@ -7079,6 +7080,7 @@ var FIELD_CSS2 = `
|
|
|
7079
7080
|
background: var(--color-surface);
|
|
7080
7081
|
border: var(--border-thin) solid var(--color-border);
|
|
7081
7082
|
border-radius: var(--radius-md);
|
|
7083
|
+
overflow: hidden;
|
|
7082
7084
|
transition: border-color var(--duration-fast) var(--ease-standard),
|
|
7083
7085
|
box-shadow var(--duration-fast) var(--ease-standard),
|
|
7084
7086
|
background-color var(--duration-fast) var(--ease-standard);
|
|
@@ -7112,9 +7114,6 @@ var FIELD_CSS2 = `
|
|
|
7112
7114
|
.twc-input__reveal:hover { background: var(--color-surface-sunken); color: var(--color-text); }
|
|
7113
7115
|
.twc-input__reveal:focus-visible { outline: none; box-shadow: var(--ring); }
|
|
7114
7116
|
.twc-input__reveal svg { width: 17px; height: 17px; }
|
|
7115
|
-
`;
|
|
7116
|
-
var INPUT_EXTRA_CSS = `
|
|
7117
|
-
.twc-input { overflow: hidden; }
|
|
7118
7117
|
.twc-input__addon { align-self: stretch; display: inline-flex; align-items: center; padding-inline: var(--space-3);
|
|
7119
7118
|
background: var(--color-surface-sunken); color: var(--color-text-muted); font-size: var(--text-sm);
|
|
7120
7119
|
font-weight: var(--font-semibold); white-space: nowrap; flex: none; }
|
|
@@ -8625,6 +8624,53 @@ function Caret({ pos }) {
|
|
|
8625
8624
|
const left = Math.max(12, Math.min(pos.anchorX - pos.left, pos.width - 20));
|
|
8626
8625
|
return /* @__PURE__ */ jsx("span", { className: "twc-dt__caret", style: { left } });
|
|
8627
8626
|
}
|
|
8627
|
+
function AsyncFilterValue({ col, value, onChange }) {
|
|
8628
|
+
const [opts, setOpts] = React51__default.useState([]);
|
|
8629
|
+
const [loading, setLoading] = React51__default.useState(false);
|
|
8630
|
+
const reqRef = React51__default.useRef(0);
|
|
8631
|
+
const debRef = React51__default.useRef();
|
|
8632
|
+
const load = React51__default.useCallback((query) => {
|
|
8633
|
+
const id = ++reqRef.current;
|
|
8634
|
+
setLoading(true);
|
|
8635
|
+
Promise.resolve(col.loadValueOptions(query)).then(
|
|
8636
|
+
(res) => {
|
|
8637
|
+
if (reqRef.current === id) {
|
|
8638
|
+
setOpts((Array.isArray(res) ? res : []).map((o) => typeof o === "string" ? { value: o, label: o } : o));
|
|
8639
|
+
setLoading(false);
|
|
8640
|
+
}
|
|
8641
|
+
},
|
|
8642
|
+
() => {
|
|
8643
|
+
if (reqRef.current === id) setLoading(false);
|
|
8644
|
+
}
|
|
8645
|
+
);
|
|
8646
|
+
}, [col]);
|
|
8647
|
+
React51__default.useEffect(() => {
|
|
8648
|
+
load("");
|
|
8649
|
+
return () => clearTimeout(debRef.current);
|
|
8650
|
+
}, [load]);
|
|
8651
|
+
const onInput = React51__default.useCallback((q) => {
|
|
8652
|
+
clearTimeout(debRef.current);
|
|
8653
|
+
debRef.current = setTimeout(() => load(q), 250);
|
|
8654
|
+
}, [load]);
|
|
8655
|
+
const selected = Array.isArray(value) ? value : [];
|
|
8656
|
+
const merged = React51__default.useMemo(() => {
|
|
8657
|
+
const seen2 = new Set(opts.map((o) => o.value));
|
|
8658
|
+
return [...selected.filter((v) => !seen2.has(v)).map((v) => ({ value: v, label: String(v) })), ...opts];
|
|
8659
|
+
}, [opts, selected]);
|
|
8660
|
+
return /* @__PURE__ */ jsx(
|
|
8661
|
+
MultiSelect,
|
|
8662
|
+
{
|
|
8663
|
+
portal: true,
|
|
8664
|
+
placeholder: "Any of\u2026",
|
|
8665
|
+
value: selected,
|
|
8666
|
+
options: merged,
|
|
8667
|
+
filter: false,
|
|
8668
|
+
loading,
|
|
8669
|
+
onInputChange: onInput,
|
|
8670
|
+
onChange
|
|
8671
|
+
}
|
|
8672
|
+
);
|
|
8673
|
+
}
|
|
8628
8674
|
function EditCell({ col, value, options, onChange, onCommit, onCommitValue, onCancel, onKeyDown }) {
|
|
8629
8675
|
const ref = React51__default.useRef(null);
|
|
8630
8676
|
const isSelect = col.editType === "select" || options && col.editType !== "text";
|
|
@@ -8825,6 +8871,9 @@ function Datatable({
|
|
|
8825
8871
|
rowGrouping = [],
|
|
8826
8872
|
rowNumbers = false,
|
|
8827
8873
|
searchFields = null,
|
|
8874
|
+
searchable = true,
|
|
8875
|
+
quickFilter,
|
|
8876
|
+
onQuickFilterChange,
|
|
8828
8877
|
rowPinning = false,
|
|
8829
8878
|
rowReorder = false,
|
|
8830
8879
|
rowResize = false,
|
|
@@ -8907,7 +8956,13 @@ function Datatable({
|
|
|
8907
8956
|
return { left, right };
|
|
8908
8957
|
});
|
|
8909
8958
|
const [density, setDensity] = React51__default.useState(densityProp);
|
|
8910
|
-
const [
|
|
8959
|
+
const [internalQuick, setInternalQuick] = React51__default.useState("");
|
|
8960
|
+
const quick = quickFilter !== void 0 ? quickFilter : internalQuick;
|
|
8961
|
+
const commitQuick = (v) => {
|
|
8962
|
+
onQuickFilterChange?.(v);
|
|
8963
|
+
if (quickFilter === void 0) setInternalQuick(v);
|
|
8964
|
+
commitPage(0);
|
|
8965
|
+
};
|
|
8911
8966
|
const [selected, setSelected] = React51__default.useState(() => /* @__PURE__ */ new Set());
|
|
8912
8967
|
const [activeRow, setActiveRow] = React51__default.useState(null);
|
|
8913
8968
|
const [activeCell, setActiveCell] = React51__default.useState(null);
|
|
@@ -9787,7 +9842,7 @@ function Datatable({
|
|
|
9787
9842
|
setSelected(/* @__PURE__ */ new Set());
|
|
9788
9843
|
}
|
|
9789
9844
|
const [editing, setEditing] = React51__default.useState(null);
|
|
9790
|
-
const isColEditable = (c) => c.type !== "actions" &&
|
|
9845
|
+
const isColEditable = (c) => c.type !== "actions" && c.editable !== false && (c.editable === true || c.renderEditCell != null || editMode);
|
|
9791
9846
|
function beginEdit(rowK, col, row) {
|
|
9792
9847
|
if (!isColEditable(col)) return;
|
|
9793
9848
|
setEditing({ key: rowK, field: col.field, value: row[col.field] ?? "" });
|
|
@@ -10118,7 +10173,13 @@ function Datatable({
|
|
|
10118
10173
|
!checkboxSelection && reorderable && ci === 0 && !isEditing ? /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 6, maxWidth: "100%" }, children: [
|
|
10119
10174
|
rowHandle(k, midIdx, row),
|
|
10120
10175
|
/* @__PURE__ */ jsx("span", { style: { overflow: "hidden", textOverflow: "ellipsis" }, children: isActions ? renderActions(c, row) : c.renderCell ? c.renderCell(val, row) : c.valueFormatter ? c.valueFormatter(val, row) : val })
|
|
10121
|
-
] }) : isEditing ?
|
|
10176
|
+
] }) : isEditing ? c.renderEditCell ? (
|
|
10177
|
+
// #236: full escape hatch — the host drops in its own editor (searchable/creatable/
|
|
10178
|
+
// async Combobox, …) and drives the lifecycle via commit/cancel. Wrapped in
|
|
10179
|
+
// .twc-dt__editor-wrap so inline clicks (and any .twc-pop portal dropdown) don't
|
|
10180
|
+
// trip the outside-click auto-cancel.
|
|
10181
|
+
/* @__PURE__ */ jsx("div", { className: "twc-dt__editor-wrap", children: c.renderEditCell({ value: editing.value, row, field: c.field, commit: (v) => commitEdit(v), cancel: cancelEdit }) })
|
|
10182
|
+
) : /* @__PURE__ */ jsx(
|
|
10122
10183
|
EditCell,
|
|
10123
10184
|
{
|
|
10124
10185
|
col: c,
|
|
@@ -10408,13 +10469,10 @@ function Datatable({
|
|
|
10408
10469
|
}
|
|
10409
10470
|
)
|
|
10410
10471
|
] }) : null,
|
|
10411
|
-
/* @__PURE__ */ jsxs("div", { className: "twc-dt__search", children: [
|
|
10472
|
+
searchable ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__search", children: [
|
|
10412
10473
|
/* @__PURE__ */ jsx(Svg, { d: I.search }),
|
|
10413
|
-
/* @__PURE__ */ jsx("input", { placeholder: "Search\u2026", "aria-label": "Search rows", value: quick, onChange: (e) =>
|
|
10414
|
-
|
|
10415
|
-
commitPage(0);
|
|
10416
|
-
} })
|
|
10417
|
-
] })
|
|
10474
|
+
/* @__PURE__ */ jsx("input", { placeholder: "Search\u2026", "aria-label": "Search rows", value: quick, onChange: (e) => commitQuick(e.target.value) })
|
|
10475
|
+
] }) : null
|
|
10418
10476
|
] }),
|
|
10419
10477
|
!pivotActive && activeGroupBy.length ? /* @__PURE__ */ jsxs("div", { className: "twc-dt__groupbar", children: [
|
|
10420
10478
|
/* @__PURE__ */ jsx(Svg, { d: I.group }),
|
|
@@ -11273,7 +11331,14 @@ function Datatable({
|
|
|
11273
11331
|
}))
|
|
11274
11332
|
}
|
|
11275
11333
|
) }),
|
|
11276
|
-
/* @__PURE__ */ jsx("div", { className: "twc-dt__f-val", children: op.noInput ? null : op.multi ? /* @__PURE__ */ jsx(
|
|
11334
|
+
/* @__PURE__ */ jsx("div", { className: "twc-dt__f-val", children: op.noInput ? null : op.multi ? col.loadValueOptions ? /* @__PURE__ */ jsx(
|
|
11335
|
+
AsyncFilterValue,
|
|
11336
|
+
{
|
|
11337
|
+
col,
|
|
11338
|
+
value: f.value,
|
|
11339
|
+
onChange: (vals) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: vals } : x))
|
|
11340
|
+
}
|
|
11341
|
+
) : /* @__PURE__ */ jsx(
|
|
11277
11342
|
MultiSelect,
|
|
11278
11343
|
{
|
|
11279
11344
|
portal: true,
|