twico-ui 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/index.cjs +78 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.mjs +78 -8
- package/dist/index.mjs.map +1 -1
- package/llms.txt +1 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1946,8 +1946,12 @@ interface DatatableColumn<T = any> {
|
|
|
1946
1946
|
headerName?: string;
|
|
1947
1947
|
/** Data type. "actions" renders per-row action buttons via getActions. @default "string" */
|
|
1948
1948
|
type?: "string" | "number" | "actions";
|
|
1949
|
-
/** Column width
|
|
1950
|
-
width?: number;
|
|
1949
|
+
/** Column width: a px number, or `"auto"` to size to the header's intrinsic width (chrome + label) so a short column fits without truncating or wasting the 160px default. @default 160 (120 for actions) */
|
|
1950
|
+
width?: number | "auto";
|
|
1951
|
+
/** Lower bound (px) for the resolved width, incl. `"auto"` and user resizes. */
|
|
1952
|
+
minWidth?: number;
|
|
1953
|
+
/** Upper bound (px) for the resolved width; `minWidth` wins if they conflict. */
|
|
1954
|
+
maxWidth?: number;
|
|
1951
1955
|
/** Cell alignment; currently affects the actions column's button justification. @default "right" for number/actions columns, else "left" */
|
|
1952
1956
|
align?: "left" | "right";
|
|
1953
1957
|
/** Allow sorting this column. @default true */
|
|
@@ -1966,6 +1970,8 @@ interface DatatableColumn<T = any> {
|
|
|
1966
1970
|
resizable?: boolean;
|
|
1967
1971
|
/** Allow drag / menu / keyboard reorder of this column (independent of the table-wide `disableColumnReorder`). @default true */
|
|
1968
1972
|
reorderable?: boolean;
|
|
1973
|
+
/** Offer the "Wrap text" / "Unwrap text" item in the ⋮ menu. Set `false` for a fixed single-token column (e.g. an ordinal) whose menu should show only Sort + Hide. @default true */
|
|
1974
|
+
wrappable?: boolean;
|
|
1969
1975
|
/** Summary-footer aggregation: a preset, or a function over the column's values. */
|
|
1970
1976
|
aggregation?: "sum" | "avg" | "min" | "max" | "count" | ((values: any[], rows: T[]) => React.ReactNode);
|
|
1971
1977
|
/** Format an aggregation result for the footer (falls back to valueFormatter). */
|
|
@@ -1983,6 +1989,11 @@ interface DatatableColumn<T = any> {
|
|
|
1983
1989
|
wrapText?: boolean;
|
|
1984
1990
|
/** Distinct values for the "is any of" multi-value filter (recommended in server mode). */
|
|
1985
1991
|
valueOptions?: Array<string | { value: string; label: string }>;
|
|
1992
|
+
/** Async loader for the "is any of" filter's options — searchable/lazy server-backed values (for a
|
|
1993
|
+
* large reference/master list). Called with the type-ahead query (debounced; `""` primes the list)
|
|
1994
|
+
* and takes precedence over `valueOptions` for the filter picker, which then loads server-side
|
|
1995
|
+
* (no local filtering) with a loading state. */
|
|
1996
|
+
loadValueOptions?: (query: string) => Promise<Array<string | { value: string; label: string }>>;
|
|
1986
1997
|
/** Format the raw value to a string/number for display. */
|
|
1987
1998
|
valueFormatter?: (value: any, row: T) => React.ReactNode;
|
|
1988
1999
|
/** Fully custom cell renderer (badges, avatars, etc.). */
|
|
@@ -2130,6 +2141,8 @@ interface CardGridProps<Row = any> extends Omit<React.HTMLAttributes<HTMLDivElem
|
|
|
2130
2141
|
onSortChange?: (sort: CardGridSort | null) => void;
|
|
2131
2142
|
/** Fields offered by the built-in sort control (renders it when provided). */
|
|
2132
2143
|
sortOptions?: CardGridSortOption[];
|
|
2144
|
+
/** Min width (px or CSS length) for the built-in sort control's trigger, so long option labels aren't clipped. Defaults to an estimate from the longest label. */
|
|
2145
|
+
sortMinWidth?: string | number;
|
|
2133
2146
|
/** Extra node rendered in the toolbar row (before the sort control). */
|
|
2134
2147
|
toolbar?: React.ReactNode;
|
|
2135
2148
|
/** Shown when there are no rows and not loading. @default "No results." */
|
package/dist/index.d.ts
CHANGED
|
@@ -1946,8 +1946,12 @@ interface DatatableColumn<T = any> {
|
|
|
1946
1946
|
headerName?: string;
|
|
1947
1947
|
/** Data type. "actions" renders per-row action buttons via getActions. @default "string" */
|
|
1948
1948
|
type?: "string" | "number" | "actions";
|
|
1949
|
-
/** Column width
|
|
1950
|
-
width?: number;
|
|
1949
|
+
/** Column width: a px number, or `"auto"` to size to the header's intrinsic width (chrome + label) so a short column fits without truncating or wasting the 160px default. @default 160 (120 for actions) */
|
|
1950
|
+
width?: number | "auto";
|
|
1951
|
+
/** Lower bound (px) for the resolved width, incl. `"auto"` and user resizes. */
|
|
1952
|
+
minWidth?: number;
|
|
1953
|
+
/** Upper bound (px) for the resolved width; `minWidth` wins if they conflict. */
|
|
1954
|
+
maxWidth?: number;
|
|
1951
1955
|
/** Cell alignment; currently affects the actions column's button justification. @default "right" for number/actions columns, else "left" */
|
|
1952
1956
|
align?: "left" | "right";
|
|
1953
1957
|
/** Allow sorting this column. @default true */
|
|
@@ -1966,6 +1970,8 @@ interface DatatableColumn<T = any> {
|
|
|
1966
1970
|
resizable?: boolean;
|
|
1967
1971
|
/** Allow drag / menu / keyboard reorder of this column (independent of the table-wide `disableColumnReorder`). @default true */
|
|
1968
1972
|
reorderable?: boolean;
|
|
1973
|
+
/** Offer the "Wrap text" / "Unwrap text" item in the ⋮ menu. Set `false` for a fixed single-token column (e.g. an ordinal) whose menu should show only Sort + Hide. @default true */
|
|
1974
|
+
wrappable?: boolean;
|
|
1969
1975
|
/** Summary-footer aggregation: a preset, or a function over the column's values. */
|
|
1970
1976
|
aggregation?: "sum" | "avg" | "min" | "max" | "count" | ((values: any[], rows: T[]) => React.ReactNode);
|
|
1971
1977
|
/** Format an aggregation result for the footer (falls back to valueFormatter). */
|
|
@@ -1983,6 +1989,11 @@ interface DatatableColumn<T = any> {
|
|
|
1983
1989
|
wrapText?: boolean;
|
|
1984
1990
|
/** Distinct values for the "is any of" multi-value filter (recommended in server mode). */
|
|
1985
1991
|
valueOptions?: Array<string | { value: string; label: string }>;
|
|
1992
|
+
/** Async loader for the "is any of" filter's options — searchable/lazy server-backed values (for a
|
|
1993
|
+
* large reference/master list). Called with the type-ahead query (debounced; `""` primes the list)
|
|
1994
|
+
* and takes precedence over `valueOptions` for the filter picker, which then loads server-side
|
|
1995
|
+
* (no local filtering) with a loading state. */
|
|
1996
|
+
loadValueOptions?: (query: string) => Promise<Array<string | { value: string; label: string }>>;
|
|
1986
1997
|
/** Format the raw value to a string/number for display. */
|
|
1987
1998
|
valueFormatter?: (value: any, row: T) => React.ReactNode;
|
|
1988
1999
|
/** Fully custom cell renderer (badges, avatars, etc.). */
|
|
@@ -2130,6 +2141,8 @@ interface CardGridProps<Row = any> extends Omit<React.HTMLAttributes<HTMLDivElem
|
|
|
2130
2141
|
onSortChange?: (sort: CardGridSort | null) => void;
|
|
2131
2142
|
/** Fields offered by the built-in sort control (renders it when provided). */
|
|
2132
2143
|
sortOptions?: CardGridSortOption[];
|
|
2144
|
+
/** Min width (px or CSS length) for the built-in sort control's trigger, so long option labels aren't clipped. Defaults to an estimate from the longest label. */
|
|
2145
|
+
sortMinWidth?: string | number;
|
|
2133
2146
|
/** Extra node rendered in the toolbar row (before the sort control). */
|
|
2134
2147
|
toolbar?: React.ReactNode;
|
|
2135
2148
|
/** Shown when there are no rows and not loading. @default "No results." */
|
package/dist/index.mjs
CHANGED
|
@@ -8625,6 +8625,53 @@ function Caret({ pos }) {
|
|
|
8625
8625
|
const left = Math.max(12, Math.min(pos.anchorX - pos.left, pos.width - 20));
|
|
8626
8626
|
return /* @__PURE__ */ jsx("span", { className: "twc-dt__caret", style: { left } });
|
|
8627
8627
|
}
|
|
8628
|
+
function AsyncFilterValue({ col, value, onChange }) {
|
|
8629
|
+
const [opts, setOpts] = React51__default.useState([]);
|
|
8630
|
+
const [loading, setLoading] = React51__default.useState(false);
|
|
8631
|
+
const reqRef = React51__default.useRef(0);
|
|
8632
|
+
const debRef = React51__default.useRef();
|
|
8633
|
+
const load = React51__default.useCallback((query) => {
|
|
8634
|
+
const id = ++reqRef.current;
|
|
8635
|
+
setLoading(true);
|
|
8636
|
+
Promise.resolve(col.loadValueOptions(query)).then(
|
|
8637
|
+
(res) => {
|
|
8638
|
+
if (reqRef.current === id) {
|
|
8639
|
+
setOpts((Array.isArray(res) ? res : []).map((o) => typeof o === "string" ? { value: o, label: o } : o));
|
|
8640
|
+
setLoading(false);
|
|
8641
|
+
}
|
|
8642
|
+
},
|
|
8643
|
+
() => {
|
|
8644
|
+
if (reqRef.current === id) setLoading(false);
|
|
8645
|
+
}
|
|
8646
|
+
);
|
|
8647
|
+
}, [col]);
|
|
8648
|
+
React51__default.useEffect(() => {
|
|
8649
|
+
load("");
|
|
8650
|
+
return () => clearTimeout(debRef.current);
|
|
8651
|
+
}, [load]);
|
|
8652
|
+
const onInput = React51__default.useCallback((q) => {
|
|
8653
|
+
clearTimeout(debRef.current);
|
|
8654
|
+
debRef.current = setTimeout(() => load(q), 250);
|
|
8655
|
+
}, [load]);
|
|
8656
|
+
const selected = Array.isArray(value) ? value : [];
|
|
8657
|
+
const merged = React51__default.useMemo(() => {
|
|
8658
|
+
const seen2 = new Set(opts.map((o) => o.value));
|
|
8659
|
+
return [...selected.filter((v) => !seen2.has(v)).map((v) => ({ value: v, label: String(v) })), ...opts];
|
|
8660
|
+
}, [opts, selected]);
|
|
8661
|
+
return /* @__PURE__ */ jsx(
|
|
8662
|
+
MultiSelect,
|
|
8663
|
+
{
|
|
8664
|
+
portal: true,
|
|
8665
|
+
placeholder: "Any of\u2026",
|
|
8666
|
+
value: selected,
|
|
8667
|
+
options: merged,
|
|
8668
|
+
filter: false,
|
|
8669
|
+
loading,
|
|
8670
|
+
onInputChange: onInput,
|
|
8671
|
+
onChange
|
|
8672
|
+
}
|
|
8673
|
+
);
|
|
8674
|
+
}
|
|
8628
8675
|
function EditCell({ col, value, options, onChange, onCommit, onCommitValue, onCancel, onKeyDown }) {
|
|
8629
8676
|
const ref = React51__default.useRef(null);
|
|
8630
8677
|
const isSelect = col.editType === "select" || options && col.editType !== "text";
|
|
@@ -9003,7 +9050,19 @@ function Datatable({
|
|
|
9003
9050
|
rowGroupingKeyRef.current = rowGroupingKey;
|
|
9004
9051
|
setGroupBy(rowGrouping || []);
|
|
9005
9052
|
}, [rowGroupingKey]);
|
|
9006
|
-
const
|
|
9053
|
+
const AUTO_CHROME = 74;
|
|
9054
|
+
const autoWidth = (c) => {
|
|
9055
|
+
const label = typeof c.headerName === "string" ? c.headerName : String(c.field ?? "");
|
|
9056
|
+
return AUTO_CHROME + Math.ceil(label.length * 8);
|
|
9057
|
+
};
|
|
9058
|
+
const clampWidth = (w, c) => {
|
|
9059
|
+
let x = w;
|
|
9060
|
+
if (typeof c.maxWidth === "number") x = Math.min(x, c.maxWidth);
|
|
9061
|
+
if (typeof c.minWidth === "number") x = Math.max(x, c.minWidth);
|
|
9062
|
+
return Math.max(40, x);
|
|
9063
|
+
};
|
|
9064
|
+
const intrinsicWidth = (c) => c.width === "auto" ? autoWidth(c) : typeof c.width === "number" ? c.width : 160;
|
|
9065
|
+
const widthOf = (c) => clampWidth(widths[c.field] ?? intrinsicWidth(c), c);
|
|
9007
9066
|
const [colMenu, setColMenu] = React51__default.useState(null);
|
|
9008
9067
|
const [menuPos, openMenu, closeMenu] = useFloating();
|
|
9009
9068
|
const [panel, setPanel] = React51__default.useState(null);
|
|
@@ -10591,7 +10650,7 @@ function Datatable({
|
|
|
10591
10650
|
e.preventDefault();
|
|
10592
10651
|
e.stopPropagation();
|
|
10593
10652
|
const delta = e.key === "ArrowRight" ? 10 : -10;
|
|
10594
|
-
setWidths((m) => ({ ...m, [c.field]: Math.max(72, (m[c.field] ?? c
|
|
10653
|
+
setWidths((m) => ({ ...m, [c.field]: Math.max(72, (m[c.field] ?? intrinsicWidth(c)) + delta) }));
|
|
10595
10654
|
},
|
|
10596
10655
|
title: "Drag to resize \xB7 double-click to fit content"
|
|
10597
10656
|
}
|
|
@@ -10834,10 +10893,10 @@ function Datatable({
|
|
|
10834
10893
|
" ",
|
|
10835
10894
|
groupBy.includes(c.field) ? "Stop grouping" : "Group by this column"
|
|
10836
10895
|
] }) : null,
|
|
10837
|
-
!disableColumnReorder && c.type !== "actions" && !pins.left.includes(c.field) && !pins.right.includes(c.field) ? (() => {
|
|
10896
|
+
!disableColumnReorder && c.reorderable !== false && c.type !== "actions" && !pins.left.includes(c.field) && !pins.right.includes(c.field) && movableMidFields.length > 1 ? (() => {
|
|
10838
10897
|
const midIdx = movableMidFields.indexOf(c.field);
|
|
10839
|
-
const atFirst = midIdx
|
|
10840
|
-
const atLast = midIdx ===
|
|
10898
|
+
const atFirst = midIdx === 0;
|
|
10899
|
+
const atLast = midIdx === movableMidFields.length - 1;
|
|
10841
10900
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10842
10901
|
/* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "twc-dt__mi", disabled: atFirst, "aria-disabled": atFirst || void 0, onClick: () => {
|
|
10843
10902
|
moveCol(c.field, -1);
|
|
@@ -10873,7 +10932,7 @@ function Datatable({
|
|
|
10873
10932
|
pins.right.includes(c.field) ? "Unpin" : "Pin to right"
|
|
10874
10933
|
] })
|
|
10875
10934
|
] }) : null,
|
|
10876
|
-
c.type !== "actions" ? /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "twc-dt__mi", "data-active": wrapped.has(c.field) || void 0, onClick: () => {
|
|
10935
|
+
c.type !== "actions" && c.wrappable !== false ? /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "twc-dt__mi", "data-active": wrapped.has(c.field) || void 0, onClick: () => {
|
|
10877
10936
|
toggleWrap(c.field);
|
|
10878
10937
|
close();
|
|
10879
10938
|
}, children: [
|
|
@@ -11261,7 +11320,14 @@ function Datatable({
|
|
|
11261
11320
|
}))
|
|
11262
11321
|
}
|
|
11263
11322
|
) }),
|
|
11264
|
-
/* @__PURE__ */ jsx("div", { className: "twc-dt__f-val", children: op.noInput ? null : op.multi ? /* @__PURE__ */ jsx(
|
|
11323
|
+
/* @__PURE__ */ jsx("div", { className: "twc-dt__f-val", children: op.noInput ? null : op.multi ? col.loadValueOptions ? /* @__PURE__ */ jsx(
|
|
11324
|
+
AsyncFilterValue,
|
|
11325
|
+
{
|
|
11326
|
+
col,
|
|
11327
|
+
value: f.value,
|
|
11328
|
+
onChange: (vals) => setFilters((arr) => arr.map((x) => x.id === f.id ? { ...x, value: vals } : x))
|
|
11329
|
+
}
|
|
11330
|
+
) : /* @__PURE__ */ jsx(
|
|
11265
11331
|
MultiSelect,
|
|
11266
11332
|
{
|
|
11267
11333
|
portal: true,
|
|
@@ -11377,7 +11443,7 @@ var CARDGRID_CSS = `
|
|
|
11377
11443
|
.twc-cardgrid__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
|
|
11378
11444
|
.twc-cardgrid__search { flex: 1 1 200px; min-width: 160px; }
|
|
11379
11445
|
.twc-cardgrid__spacer { flex: 1 1 auto; }
|
|
11380
|
-
.twc-cardgrid__sort { display: inline-flex; align-items: center; gap: var(--space-1); }
|
|
11446
|
+
.twc-cardgrid__sort { display: inline-flex; align-items: center; gap: var(--space-1); flex: none; }
|
|
11381
11447
|
.twc-cardgrid__dir { display: inline-grid; place-items: center; width: 32px; height: 32px; border: var(--border-thin) solid var(--color-border); background: var(--color-surface); color: var(--color-text-muted); cursor: pointer; border-radius: var(--radius-md); transition: background-color var(--duration-fast), color var(--duration-fast); }
|
|
11382
11448
|
.twc-cardgrid__dir:hover { background: var(--color-surface-sunken); color: var(--color-text); }
|
|
11383
11449
|
.twc-cardgrid__dir svg { width: 16px; height: 16px; transition: transform var(--duration-fast) var(--ease-standard); }
|
|
@@ -11425,6 +11491,7 @@ function CardGrid({
|
|
|
11425
11491
|
defaultSort = null,
|
|
11426
11492
|
onSortChange,
|
|
11427
11493
|
sortOptions,
|
|
11494
|
+
sortMinWidth,
|
|
11428
11495
|
// slots / states
|
|
11429
11496
|
toolbar,
|
|
11430
11497
|
emptyState = "No results.",
|
|
@@ -11496,6 +11563,8 @@ function CardGrid({
|
|
|
11496
11563
|
const to = paginated ? Math.min((pageVal + 1) * sizeVal, total) : total;
|
|
11497
11564
|
const rppOptions = Array.from(new Set([...pageSizeOptions || [], pageSize].filter((n) => n > 0))).sort((a, b) => a - b).map((n) => ({ value: String(n), label: String(n) }));
|
|
11498
11565
|
const sortSelectOptions = sortOptions ? [{ value: "", label: "Sort by\u2026" }, ...sortOptions.map((o) => ({ value: o.field, label: o.label || o.field }))] : null;
|
|
11566
|
+
const sortAutoWidth = sortSelectOptions ? Math.min(300, Math.max(120, Math.ceil(Math.max(...sortSelectOptions.map((o) => String(o.label).length)) * 7.2) + 46)) : void 0;
|
|
11567
|
+
const sortWidth = sortMinWidth != null ? sortMinWidth : sortAutoWidth;
|
|
11499
11568
|
const hasToolbar = searchable || sortSelectOptions || toolbar;
|
|
11500
11569
|
return /* @__PURE__ */ jsxs("div", { className: `twc-cardgrid ${className}`, id: fieldId, ...rest, children: [
|
|
11501
11570
|
styles,
|
|
@@ -11523,6 +11592,7 @@ function CardGrid({
|
|
|
11523
11592
|
value: sortVal?.field || "",
|
|
11524
11593
|
options: sortSelectOptions,
|
|
11525
11594
|
placeholder: "Sort by\u2026",
|
|
11595
|
+
style: { minWidth: sortWidth },
|
|
11526
11596
|
"aria-label": "Sort by",
|
|
11527
11597
|
onChange: (field) => commitSort(field ? { field, dir: sortVal?.dir || "asc" } : null)
|
|
11528
11598
|
}
|