twico-ui 1.10.0 → 1.11.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 +114 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.mjs +114 -8
- package/dist/index.mjs.map +1 -1
- package/llms.txt +2 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1783,8 +1783,13 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
|
|
|
1783
1783
|
serverMode?: boolean;
|
|
1784
1784
|
/** Total row count on the server (required for pagination in server mode). */
|
|
1785
1785
|
rowCount?: number;
|
|
1786
|
-
/** Server-mode callback fired (debounced) whenever the query changes.
|
|
1786
|
+
/** Server-mode callback fired (debounced) whenever the query changes. The query now
|
|
1787
|
+
* includes `visibleColumns`/`hiddenColumns` so you can project only the shown columns. */
|
|
1787
1788
|
onServerChange?: (state: DatatableQuery) => void;
|
|
1789
|
+
/** Fired when the built-in Columns menu shows/hides a column, with the currently visible
|
|
1790
|
+
* column `field`s (in column order). Lets a server-mode grid drive column projection from
|
|
1791
|
+
* the built-in menu instead of a duplicate control. Not fired on initial mount. */
|
|
1792
|
+
onColumnVisibilityChange?: (visible: string[]) => void;
|
|
1788
1793
|
/** Show the Export toolbar button (split button: click = CSV, chevron = format menu). @default false */
|
|
1789
1794
|
showExport?: boolean;
|
|
1790
1795
|
/** Show the row-density toolbar button (cycles compact / standard / comfortable). The `density` prop
|
|
@@ -1903,6 +1908,14 @@ interface DatatableQuery {
|
|
|
1903
1908
|
filters: DatatableFilter[];
|
|
1904
1909
|
/** Quick-search text. */
|
|
1905
1910
|
quickFilter: string;
|
|
1911
|
+
/** `field`s of the columns currently shown (in column order) — the built-in Columns menu
|
|
1912
|
+
* is the source of truth. Use it to fetch/project only the visible columns server-side.
|
|
1913
|
+
* Always populated by the grid in `onServerChange`; optional so a hand-built query passed
|
|
1914
|
+
* to `runDatatableQuery` (which ignores it) need not supply it. */
|
|
1915
|
+
visibleColumns?: string[];
|
|
1916
|
+
/** `field`s of the columns currently hidden via the Columns menu. Always populated in
|
|
1917
|
+
* `onServerChange`; optional for the same reason as `visibleColumns`. */
|
|
1918
|
+
hiddenColumns?: string[];
|
|
1906
1919
|
}
|
|
1907
1920
|
|
|
1908
1921
|
interface DatatableColumn<T = any> {
|
package/dist/index.d.ts
CHANGED
|
@@ -1783,8 +1783,13 @@ interface DatatableProps<T = any> extends Omit<React.HTMLAttributes<HTMLDivEleme
|
|
|
1783
1783
|
serverMode?: boolean;
|
|
1784
1784
|
/** Total row count on the server (required for pagination in server mode). */
|
|
1785
1785
|
rowCount?: number;
|
|
1786
|
-
/** Server-mode callback fired (debounced) whenever the query changes.
|
|
1786
|
+
/** Server-mode callback fired (debounced) whenever the query changes. The query now
|
|
1787
|
+
* includes `visibleColumns`/`hiddenColumns` so you can project only the shown columns. */
|
|
1787
1788
|
onServerChange?: (state: DatatableQuery) => void;
|
|
1789
|
+
/** Fired when the built-in Columns menu shows/hides a column, with the currently visible
|
|
1790
|
+
* column `field`s (in column order). Lets a server-mode grid drive column projection from
|
|
1791
|
+
* the built-in menu instead of a duplicate control. Not fired on initial mount. */
|
|
1792
|
+
onColumnVisibilityChange?: (visible: string[]) => void;
|
|
1788
1793
|
/** Show the Export toolbar button (split button: click = CSV, chevron = format menu). @default false */
|
|
1789
1794
|
showExport?: boolean;
|
|
1790
1795
|
/** Show the row-density toolbar button (cycles compact / standard / comfortable). The `density` prop
|
|
@@ -1903,6 +1908,14 @@ interface DatatableQuery {
|
|
|
1903
1908
|
filters: DatatableFilter[];
|
|
1904
1909
|
/** Quick-search text. */
|
|
1905
1910
|
quickFilter: string;
|
|
1911
|
+
/** `field`s of the columns currently shown (in column order) — the built-in Columns menu
|
|
1912
|
+
* is the source of truth. Use it to fetch/project only the visible columns server-side.
|
|
1913
|
+
* Always populated by the grid in `onServerChange`; optional so a hand-built query passed
|
|
1914
|
+
* to `runDatatableQuery` (which ignores it) need not supply it. */
|
|
1915
|
+
visibleColumns?: string[];
|
|
1916
|
+
/** `field`s of the columns currently hidden via the Columns menu. Always populated in
|
|
1917
|
+
* `onServerChange`; optional for the same reason as `visibleColumns`. */
|
|
1918
|
+
hiddenColumns?: string[];
|
|
1906
1919
|
}
|
|
1907
1920
|
|
|
1908
1921
|
interface DatatableColumn<T = any> {
|
package/dist/index.mjs
CHANGED
|
@@ -25,7 +25,7 @@ function useScopedStyles(id, css) {
|
|
|
25
25
|
}
|
|
26
26
|
if (el.textContent !== css) el.textContent = css;
|
|
27
27
|
}, [id, css, nonce]);
|
|
28
|
-
return SUPPORTS_STYLE_HOIST && css ? React51.createElement("style", { href: id, precedence: "twc-ui"
|
|
28
|
+
return SUPPORTS_STYLE_HOIST && css ? React51.createElement("style", { href: id, precedence: "twc-ui" }, css) : null;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// components/TwicoProvider.jsx
|
|
@@ -1287,6 +1287,62 @@ function compileSx(obj, selector) {
|
|
|
1287
1287
|
out += rules;
|
|
1288
1288
|
return out;
|
|
1289
1289
|
}
|
|
1290
|
+
var BOX_EDGE4 = {
|
|
1291
|
+
padding: ["paddingTop", "paddingRight", "paddingBottom", "paddingLeft"],
|
|
1292
|
+
margin: ["marginTop", "marginRight", "marginBottom", "marginLeft"],
|
|
1293
|
+
inset: ["top", "right", "bottom", "left"]
|
|
1294
|
+
};
|
|
1295
|
+
var BOX_EDGE2 = {
|
|
1296
|
+
paddingBlock: ["paddingBlockStart", "paddingBlockEnd"],
|
|
1297
|
+
paddingInline: ["paddingInlineStart", "paddingInlineEnd"],
|
|
1298
|
+
marginBlock: ["marginBlockStart", "marginBlockEnd"],
|
|
1299
|
+
marginInline: ["marginInlineStart", "marginInlineEnd"]
|
|
1300
|
+
};
|
|
1301
|
+
function splitShorthand(value) {
|
|
1302
|
+
const s = String(value).trim();
|
|
1303
|
+
if (!s) return [];
|
|
1304
|
+
const out = [];
|
|
1305
|
+
let depth = 0, cur = "";
|
|
1306
|
+
for (let i = 0; i < s.length; i++) {
|
|
1307
|
+
const c = s[i];
|
|
1308
|
+
if (c === "(") {
|
|
1309
|
+
depth++;
|
|
1310
|
+
cur += c;
|
|
1311
|
+
} else if (c === ")") {
|
|
1312
|
+
depth = Math.max(0, depth - 1);
|
|
1313
|
+
cur += c;
|
|
1314
|
+
} else if (depth === 0 && (c === " " || c === " " || c === "\n" || c === "\r")) {
|
|
1315
|
+
if (cur) {
|
|
1316
|
+
out.push(cur);
|
|
1317
|
+
cur = "";
|
|
1318
|
+
}
|
|
1319
|
+
} else cur += c;
|
|
1320
|
+
}
|
|
1321
|
+
if (cur) out.push(cur);
|
|
1322
|
+
return out;
|
|
1323
|
+
}
|
|
1324
|
+
function expandBoxShorthand(key, value) {
|
|
1325
|
+
const four = BOX_EDGE4[key];
|
|
1326
|
+
const two = BOX_EDGE2[key];
|
|
1327
|
+
if (!four && !two || value == null) return null;
|
|
1328
|
+
if (typeof value === "number") return (four || two).map((edge) => [edge, value]);
|
|
1329
|
+
const p = splitShorthand(value);
|
|
1330
|
+
if (p.length === 0) return null;
|
|
1331
|
+
if (two) return [[two[0], p[0]], [two[1], p.length > 1 ? p[1] : p[0]]];
|
|
1332
|
+
let top, right, bottom, left;
|
|
1333
|
+
if (p.length === 1) top = right = bottom = left = p[0];
|
|
1334
|
+
else if (p.length === 2) {
|
|
1335
|
+
top = bottom = p[0];
|
|
1336
|
+
right = left = p[1];
|
|
1337
|
+
} else if (p.length === 3) {
|
|
1338
|
+
top = p[0];
|
|
1339
|
+
right = left = p[1];
|
|
1340
|
+
bottom = p[2];
|
|
1341
|
+
} else {
|
|
1342
|
+
[top, right, bottom, left] = p;
|
|
1343
|
+
}
|
|
1344
|
+
return [[four[0], top], [four[1], right], [four[2], bottom], [four[3], left]];
|
|
1345
|
+
}
|
|
1290
1346
|
function buildSx(sx, uid) {
|
|
1291
1347
|
if (!isObject(sx)) return { flatStyle: void 0, css: "" };
|
|
1292
1348
|
const scope = `[data-twc-sx="${uid}"]`;
|
|
@@ -1304,7 +1360,13 @@ function buildSx(sx, uid) {
|
|
|
1304
1360
|
css += compileSx(value, resolveSelector(key, scope));
|
|
1305
1361
|
}
|
|
1306
1362
|
} else {
|
|
1307
|
-
|
|
1363
|
+
const expanded = expandBoxShorthand(key, value);
|
|
1364
|
+
if (expanded) {
|
|
1365
|
+
flatStyle || (flatStyle = {});
|
|
1366
|
+
for (const [k, v] of expanded) flatStyle[k] = v;
|
|
1367
|
+
} else {
|
|
1368
|
+
(flatStyle || (flatStyle = {}))[key] = value;
|
|
1369
|
+
}
|
|
1308
1370
|
}
|
|
1309
1371
|
}
|
|
1310
1372
|
return { flatStyle, css };
|
|
@@ -8678,6 +8740,7 @@ function Datatable({
|
|
|
8678
8740
|
serverMode = false,
|
|
8679
8741
|
rowCount,
|
|
8680
8742
|
onServerChange,
|
|
8743
|
+
onColumnVisibilityChange,
|
|
8681
8744
|
batchActions = [],
|
|
8682
8745
|
showExport = false,
|
|
8683
8746
|
showDensity = false,
|
|
@@ -8984,6 +9047,19 @@ function Datatable({
|
|
|
8984
9047
|
}, [exportOpen]);
|
|
8985
9048
|
const colByField = React51__default.useMemo(() => Object.fromEntries(cols.map((c) => [c.field, c])), [cols]);
|
|
8986
9049
|
const visibleCols = cols.filter((c) => !hidden.has(c.field));
|
|
9050
|
+
const colFieldsKey = JSON.stringify(
|
|
9051
|
+
cols.filter((c) => typeof c.field === "string" && c.type !== "actions").map((c) => c.field)
|
|
9052
|
+
);
|
|
9053
|
+
const colFields = React51__default.useMemo(() => JSON.parse(colFieldsKey), [colFieldsKey]);
|
|
9054
|
+
const hiddenKey = JSON.stringify([...hidden].filter((f) => typeof f === "string").sort());
|
|
9055
|
+
const visibleColumns = React51__default.useMemo(() => {
|
|
9056
|
+
const h = new Set(JSON.parse(hiddenKey));
|
|
9057
|
+
return colFields.filter((f) => !h.has(f));
|
|
9058
|
+
}, [colFields, hiddenKey]);
|
|
9059
|
+
const hiddenColumns = React51__default.useMemo(() => {
|
|
9060
|
+
const h = new Set(JSON.parse(hiddenKey));
|
|
9061
|
+
return colFields.filter((f) => h.has(f));
|
|
9062
|
+
}, [colFields, hiddenKey]);
|
|
8987
9063
|
const ordered = React51__default.useMemo(() => {
|
|
8988
9064
|
const orderIdx = (f) => {
|
|
8989
9065
|
const i = order.indexOf(f);
|
|
@@ -9326,11 +9402,27 @@ function Datatable({
|
|
|
9326
9402
|
pageSize: sizeVal,
|
|
9327
9403
|
sort,
|
|
9328
9404
|
filters: filters.map(({ id, ...f }) => f),
|
|
9329
|
-
quickFilter: quick.trim()
|
|
9405
|
+
quickFilter: quick.trim(),
|
|
9406
|
+
visibleColumns,
|
|
9407
|
+
hiddenColumns
|
|
9408
|
+
// #191: server can project only the visible columns
|
|
9330
9409
|
});
|
|
9331
9410
|
}, 250);
|
|
9332
9411
|
return () => clearTimeout(t);
|
|
9333
|
-
}, [serverMode, pageVal, sizeVal, sort, filters, quick]);
|
|
9412
|
+
}, [serverMode, pageVal, sizeVal, sort, filters, quick, visibleColumns, hiddenColumns]);
|
|
9413
|
+
const onColVisRef = React51__default.useRef(onColumnVisibilityChange);
|
|
9414
|
+
onColVisRef.current = onColumnVisibilityChange;
|
|
9415
|
+
const lastVisibleKey = React51__default.useRef(null);
|
|
9416
|
+
React51__default.useEffect(() => {
|
|
9417
|
+
const key = JSON.stringify(visibleColumns);
|
|
9418
|
+
if (lastVisibleKey.current === null) {
|
|
9419
|
+
lastVisibleKey.current = key;
|
|
9420
|
+
return;
|
|
9421
|
+
}
|
|
9422
|
+
if (lastVisibleKey.current === key) return;
|
|
9423
|
+
lastVisibleKey.current = key;
|
|
9424
|
+
onColVisRef.current?.(visibleColumns);
|
|
9425
|
+
}, [visibleColumns]);
|
|
9334
9426
|
function cycleSort(field) {
|
|
9335
9427
|
setSort((s) => !s || s.field !== field ? { field, dir: "asc" } : s.dir === "asc" ? { field, dir: "desc" } : null);
|
|
9336
9428
|
}
|
|
@@ -15353,6 +15445,12 @@ var SHELL_CSS = `
|
|
|
15353
15445
|
min-height: 60px; padding-inline: var(--space-5); border-bottom: var(--border-thin) solid var(--color-border); background: var(--color-surface); }
|
|
15354
15446
|
.twc-shell__content { flex: 1 1 auto; min-height: 0; overflow: auto; }
|
|
15355
15447
|
.twc-shell__content[data-padded="true"] { padding: var(--space-6) var(--space-5); }
|
|
15448
|
+
/* #190: <main> is the skip-link target (tabindex="-1") \u2014 a programmatic focus target,
|
|
15449
|
+
not tab-reachable and not an interactive control. Clicking content mouse-focuses it,
|
|
15450
|
+
then the first keypress flips to keyboard modality and :focus-visible would draw a UA
|
|
15451
|
+
outline around the ENTIRE content area. Suppress it \u2014 the skip LINK keeps its own
|
|
15452
|
+
visible ring (.twc-shell__skip:focus-visible) and SR focus announcement is unaffected. */
|
|
15453
|
+
.twc-shell__content:focus, .twc-shell__content:focus-visible { outline: none; }
|
|
15356
15454
|
/* Mobile: stack the shell so the sidebar (rendered as an off-canvas overlay) no
|
|
15357
15455
|
longer reserves a column \u2014 the main content spans the full width. */
|
|
15358
15456
|
@media (max-width: 720px) {
|
|
@@ -15487,8 +15585,10 @@ var Container = React51__default.forwardRef(function Container2({
|
|
|
15487
15585
|
style: {
|
|
15488
15586
|
width: "100%",
|
|
15489
15587
|
maxWidth: typeof max === "number" ? `${max}px` : max,
|
|
15490
|
-
|
|
15491
|
-
|
|
15588
|
+
marginInlineStart: "auto",
|
|
15589
|
+
marginInlineEnd: "auto",
|
|
15590
|
+
paddingInlineStart: padded ? "var(--space-5)" : void 0,
|
|
15591
|
+
paddingInlineEnd: padded ? "var(--space-5)" : void 0,
|
|
15492
15592
|
...style,
|
|
15493
15593
|
...flatStyle
|
|
15494
15594
|
},
|
|
@@ -15738,7 +15838,10 @@ var Heading = React51__default.forwardRef(function Heading2({
|
|
|
15738
15838
|
className: `twc-heading ${className}`.trim(),
|
|
15739
15839
|
"data-twc-sx": sxAttr,
|
|
15740
15840
|
style: {
|
|
15741
|
-
|
|
15841
|
+
marginTop: 0,
|
|
15842
|
+
marginRight: 0,
|
|
15843
|
+
marginBottom: 0,
|
|
15844
|
+
marginLeft: 0,
|
|
15742
15845
|
fontFamily: "var(--font-sans)",
|
|
15743
15846
|
fontWeight: "var(--font-bold)",
|
|
15744
15847
|
color: "var(--color-text)",
|
|
@@ -15803,7 +15906,10 @@ var Text = React51__default.forwardRef(function Text2({
|
|
|
15803
15906
|
className: `twc-text ${className}`.trim(),
|
|
15804
15907
|
"data-twc-sx": sxAttr,
|
|
15805
15908
|
style: {
|
|
15806
|
-
|
|
15909
|
+
marginTop: 0,
|
|
15910
|
+
marginRight: 0,
|
|
15911
|
+
marginBottom: 0,
|
|
15912
|
+
marginLeft: 0,
|
|
15807
15913
|
fontFamily: "var(--font-sans)",
|
|
15808
15914
|
lineHeight: 1.6,
|
|
15809
15915
|
fontSize: `var(--text-${size})`,
|