@godxjp/ui 18.8.0 → 18.9.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/dist/components/data-display/data-table.js +2 -3
- package/dist/components/data-entry/command-palette.d.ts +42 -1
- package/dist/components/data-entry/command-palette.js +39 -3
- package/dist/props/registry.d.ts +1 -1
- package/dist/props/registry.js +1 -1
- package/dist/styles/base.css +28 -0
- package/dist/styles/table-layout.css +27 -0
- package/dist/tokens/components/table.css +21 -0
- package/package.json +2 -2
|
@@ -636,9 +636,8 @@ DataTable.Content = function DataTableContent() {
|
|
|
636
636
|
),
|
|
637
637
|
children: col.render ? col.render(original) : (() => {
|
|
638
638
|
const v = original[col.key];
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
return "\u2014";
|
|
639
|
+
const text = v == null || !(typeof v === "string" || typeof v === "number") ? "\u2014" : String(v);
|
|
640
|
+
return /* @__PURE__ */ jsx("span", { "data-slot": "table-cell-text", children: text });
|
|
642
641
|
})()
|
|
643
642
|
},
|
|
644
643
|
col.key
|
|
@@ -29,9 +29,50 @@ export type CommandPaletteProps = {
|
|
|
29
29
|
open?: boolean;
|
|
30
30
|
defaultOpen?: boolean;
|
|
31
31
|
onOpenChange?: (open: boolean) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Controlled search-box query. Pairs with `onSearchChange`; leave it out for the uncontrolled
|
|
34
|
+
* palette (seeded by `defaultSearch`). Same idiom as `SearchSelect`'s `search`.
|
|
35
|
+
*/
|
|
36
|
+
search?: string;
|
|
37
|
+
/** Initial uncontrolled query, and the value the palette resets to when it closes. Default `""`. */
|
|
38
|
+
defaultSearch?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Fires on every keystroke with the current query — the seam a server-backed group needs to run
|
|
41
|
+
* search-as-you-type. Also fires with `defaultSearch` when the palette closes.
|
|
42
|
+
*/
|
|
43
|
+
onSearchChange?: (query: string) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the palette filters `groups` itself (cmdk's client-side fuzzy match). Set `false` when
|
|
46
|
+
* the QUERY is answered by a server and `groups` already holds the matches — otherwise every row
|
|
47
|
+
* is scored a second time against the same string and late-arriving matches are dropped.
|
|
48
|
+
*
|
|
49
|
+
* It also decides who owns the empty node; see the component doc block. Default `true`.
|
|
50
|
+
*/
|
|
51
|
+
shouldFilter?: boolean;
|
|
32
52
|
loading?: boolean;
|
|
33
53
|
error?: React.ReactNode;
|
|
34
54
|
trigger?: React.ReactNode;
|
|
35
55
|
shortcut?: boolean;
|
|
36
56
|
};
|
|
37
|
-
|
|
57
|
+
/**
|
|
58
|
+
* CommandPalette — the ⌘K search dialog.
|
|
59
|
+
*
|
|
60
|
+
* ## The empty-state contract (gh#412)
|
|
61
|
+
*
|
|
62
|
+
* `labels.empty` renders under exactly one rule, and which mechanism decides follows `shouldFilter`:
|
|
63
|
+
*
|
|
64
|
+
* - **`shouldFilter` (default, client-side)** — cmdk owns it: the palette holds items, the query
|
|
65
|
+
* matches none of them. Unchanged.
|
|
66
|
+
* - **`shouldFilter={false}` (server-side)** — the PALETTE owns it, derived synchronously from
|
|
67
|
+
* props: the empty node renders when `groups` carries no items, and never while `loading` or
|
|
68
|
+
* `error` is set. cmdk's own empty node cannot be trusted here, because it is driven by a
|
|
69
|
+
* scheduled count of the items that have REGISTERED in the DOM, not by what the consumer knows —
|
|
70
|
+
* so an async group that populates a frame late flips it on and off and any assertion over it
|
|
71
|
+
* races. Reading `groups` instead makes "did we render the empty state?" a pure function of the
|
|
72
|
+
* props at that render, which is what a contract test can assert.
|
|
73
|
+
*
|
|
74
|
+
* The practical consequence for search-as-you-type: hold `loading` true for the whole in-flight
|
|
75
|
+
* window. While it is true the palette shows `labels.loading` and never claims "no results" —
|
|
76
|
+
* a request that has not answered yet is not an empty result.
|
|
77
|
+
*/
|
|
78
|
+
export declare function CommandPalette({ groups, labels, onSelect, open: controlledOpen, defaultOpen, onOpenChange, search: controlledSearch, defaultSearch, onSearchChange, shouldFilter, loading, error, trigger, shortcut, }: CommandPaletteProps): React.JSX.Element;
|
|
@@ -19,14 +19,20 @@ function CommandPalette({
|
|
|
19
19
|
open: controlledOpen,
|
|
20
20
|
defaultOpen = false,
|
|
21
21
|
onOpenChange,
|
|
22
|
+
search: controlledSearch,
|
|
23
|
+
defaultSearch = "",
|
|
24
|
+
onSearchChange,
|
|
25
|
+
shouldFilter = true,
|
|
22
26
|
loading = false,
|
|
23
27
|
error,
|
|
24
28
|
trigger,
|
|
25
29
|
shortcut = true
|
|
26
30
|
}) {
|
|
27
31
|
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
|
|
32
|
+
const [uncontrolledSearch, setUncontrolledSearch] = React.useState(defaultSearch);
|
|
28
33
|
const shortcutRestoreFocusRef = React.useRef(null);
|
|
29
34
|
const open = controlledOpen ?? uncontrolledOpen;
|
|
35
|
+
const search = controlledSearch ?? uncontrolledSearch;
|
|
30
36
|
const setOpen = React.useCallback(
|
|
31
37
|
(next) => {
|
|
32
38
|
if (controlledOpen === void 0) {
|
|
@@ -36,6 +42,22 @@ function CommandPalette({
|
|
|
36
42
|
},
|
|
37
43
|
[controlledOpen, onOpenChange]
|
|
38
44
|
);
|
|
45
|
+
const setSearch = React.useCallback(
|
|
46
|
+
(next) => {
|
|
47
|
+
if (controlledSearch === void 0) {
|
|
48
|
+
setUncontrolledSearch(next);
|
|
49
|
+
}
|
|
50
|
+
onSearchChange?.(next);
|
|
51
|
+
},
|
|
52
|
+
[controlledSearch, onSearchChange]
|
|
53
|
+
);
|
|
54
|
+
const previousOpenRef = React.useRef(open);
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
const wasOpen = previousOpenRef.current;
|
|
57
|
+
previousOpenRef.current = open;
|
|
58
|
+
if (!wasOpen || open || search === defaultSearch) return;
|
|
59
|
+
setSearch(defaultSearch);
|
|
60
|
+
}, [open, search, defaultSearch, setSearch]);
|
|
39
61
|
React.useEffect(() => {
|
|
40
62
|
if (!shortcut) return;
|
|
41
63
|
const handleKeyDown = (event) => {
|
|
@@ -50,6 +72,7 @@ function CommandPalette({
|
|
|
50
72
|
document.addEventListener("keydown", handleKeyDown);
|
|
51
73
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
52
74
|
}, [open, setOpen, shortcut]);
|
|
75
|
+
const hasItems = React.useMemo(() => groups.some((group) => group.items.length > 0), [groups]);
|
|
53
76
|
const triggerNode = trigger ?? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "ui-command-palette-trigger", children: [
|
|
54
77
|
/* @__PURE__ */ jsx(Search, { "aria-hidden": "true" }),
|
|
55
78
|
/* @__PURE__ */ jsx("span", { children: labels.open }),
|
|
@@ -79,10 +102,23 @@ function CommandPalette({
|
|
|
79
102
|
children: [
|
|
80
103
|
/* @__PURE__ */ jsx(Dialog.Title, { className: "sr-only", children: labels.title }),
|
|
81
104
|
/* @__PURE__ */ jsx(Dialog.Description, { id: "ui-command-palette-description", className: "sr-only", children: labels.description }),
|
|
82
|
-
/* @__PURE__ */ jsxs(Command, { label: labels.title, children: [
|
|
83
|
-
/* @__PURE__ */ jsx(
|
|
105
|
+
/* @__PURE__ */ jsxs(Command, { label: labels.title, shouldFilter, children: [
|
|
106
|
+
/* @__PURE__ */ jsx(
|
|
107
|
+
CommandInput,
|
|
108
|
+
{
|
|
109
|
+
autoFocus: true,
|
|
110
|
+
value: search,
|
|
111
|
+
onValueChange: setSearch,
|
|
112
|
+
placeholder: labels.placeholder,
|
|
113
|
+
"aria-label": labels.placeholder
|
|
114
|
+
}
|
|
115
|
+
),
|
|
84
116
|
/* @__PURE__ */ jsx(CommandList, { "aria-busy": loading, children: loading ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "alert", children: error }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
85
|
-
/* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty })
|
|
117
|
+
shouldFilter ? /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }) : hasItems ? null : (
|
|
118
|
+
// Same node cmdk would have rendered (class + role), decided from `groups`
|
|
119
|
+
// instead of from its scheduled DOM-registration count — see the doc block.
|
|
120
|
+
/* @__PURE__ */ jsx("div", { className: "ui-command-empty", role: "presentation", children: labels.empty })
|
|
121
|
+
),
|
|
86
122
|
groups.map((group) => /* @__PURE__ */ jsx(CommandGroup, { heading: group.label, children: group.items.map((item) => /* @__PURE__ */ jsxs(
|
|
87
123
|
CommandItem,
|
|
88
124
|
{
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -1547,7 +1547,7 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
1547
1547
|
readonly CommandPaletteProp: {
|
|
1548
1548
|
readonly group: "data-entry";
|
|
1549
1549
|
readonly file: "components/data-entry/command-palette.tsx";
|
|
1550
|
-
readonly vocabulary: readonly ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp"];
|
|
1550
|
+
readonly vocabulary: readonly ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp", "OnSearchChangeProp"];
|
|
1551
1551
|
};
|
|
1552
1552
|
readonly TwoFactorSetupProp: {
|
|
1553
1553
|
readonly group: "feedback";
|
package/dist/props/registry.js
CHANGED
|
@@ -1697,7 +1697,7 @@ const COMPONENT_PROP_REGISTRY = {
|
|
|
1697
1697
|
CommandPaletteProp: {
|
|
1698
1698
|
group: "data-entry",
|
|
1699
1699
|
file: "components/data-entry/command-palette.tsx",
|
|
1700
|
-
vocabulary: ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp"]
|
|
1700
|
+
vocabulary: ["OpenProp", "DefaultOpenProp", "OnOpenChangeProp", "OnSearchChangeProp"]
|
|
1701
1701
|
},
|
|
1702
1702
|
TwoFactorSetupProp: {
|
|
1703
1703
|
group: "feedback",
|
package/dist/styles/base.css
CHANGED
|
@@ -28,6 +28,34 @@
|
|
|
28
28
|
* ───────────────────────────────────────────────────────────────────────── */
|
|
29
29
|
@import "tailwindcss";
|
|
30
30
|
|
|
31
|
+
/* ── THE LAYER CONTRACT (gh#412) ───────────────────────────────────────────
|
|
32
|
+
* `@import "tailwindcss"` establishes `theme, base, components, utilities`. Everything this
|
|
33
|
+
* package styles lives in `@layer components`, which is EARLIER than `utilities` — so a Tailwind
|
|
34
|
+
* utility a component itself emits silently outranks the package rule that is supposed to own the
|
|
35
|
+
* same property, no matter how specific that rule is. That is not a specificity bug you can fix
|
|
36
|
+
* with another selector; layer order beats specificity outright.
|
|
37
|
+
*
|
|
38
|
+
* It shipped as a real defect: `<table class="… text-sm">` (utilities) vs the collection preset's
|
|
39
|
+
* `font-size: var(--table-action-collection-font-size-compact)` (components) — the compact type
|
|
40
|
+
* tier never applied at ANY width, so the documented token was dead and a 5–6 character Japanese
|
|
41
|
+
* label could not fit the narrow-frame column measure (WCAG 2.2 SC 1.4.10, and only ever visible
|
|
42
|
+
* in Japanese). Two independent consumers reported it.
|
|
43
|
+
*
|
|
44
|
+
* `godxjp-ui-responsive` is declared HERE, after Tailwind, so it is the LAST layer and outranks
|
|
45
|
+
* `utilities`. It is reserved for one thing: RESPONSIVE RE-POINTS — the container/media blocks
|
|
46
|
+
* that must win over a component's own static utility for the layout contract to hold. Nothing
|
|
47
|
+
* static belongs in it; a rule that does not sit inside a `@container`/`@media` query goes in
|
|
48
|
+
* `@layer components` like everything else.
|
|
49
|
+
*
|
|
50
|
+
* What this layer canNOT do — and the rule consumers must follow: an UNLAYERED consumer rule
|
|
51
|
+
* outranks EVERY layer, including this one. So app CSS must theme this package through TOKENS on
|
|
52
|
+
* a wrapper, never by writing its own selectors against `[data-slot]` / `[data-priority]`
|
|
53
|
+
* elements. A consumer that does write such a rule must put it in a layer
|
|
54
|
+
* (`@layer components { … }`), or it will silently kill the responsive re-points the package
|
|
55
|
+
* makes — a column measured to 0px and wrapped one character per line is what that looks like.
|
|
56
|
+
* See docs/TOKENS.md · "The layer contract". */
|
|
57
|
+
@layer godxjp-ui-responsive;
|
|
58
|
+
|
|
31
59
|
/* Per-language font wiring — token-only, NO hardcoded faces. Each locale reads
|
|
32
60
|
* an OPTIONAL per-lang slot token (empty by default) and falls back to the
|
|
33
61
|
* base sans. Consumers switch a locale's face by setting its slot, e.g.
|
|
@@ -307,7 +307,18 @@
|
|
|
307
307
|
--table-action-collection-column-width: var(--table-action-collection-actions-width);
|
|
308
308
|
text-align: end;
|
|
309
309
|
}
|
|
310
|
+
}
|
|
310
311
|
|
|
312
|
+
/* The compact tier lives in `godxjp-ui-responsive` — the LAST layer, declared after Tailwind in
|
|
313
|
+
* styles/base.css — not in `@layer components`. `<table>` carries `text-sm`, a Tailwind utility,
|
|
314
|
+
* and `utilities` outranks `components` by LAYER ORDER, so a `font-size:` re-point written here as
|
|
315
|
+
* a component rule can never apply however specific it is: that is exactly why
|
|
316
|
+
* `--table-action-collection-font-size-compact` was dead at every width (gh#412), leaving the
|
|
317
|
+
* narrow frame at 14px where a 5–6 character Japanese label overflows its column measure. The
|
|
318
|
+
* column measures move with it so the whole compact tier is decided in ONE place, and so a
|
|
319
|
+
* consumer utility on the table can't half-collapse it either. See styles/base.css · THE LAYER
|
|
320
|
+
* CONTRACT. */
|
|
321
|
+
@layer godxjp-ui-responsive {
|
|
311
322
|
/* Below the step the SOURCE measures are re-pointed to their compact tier (and the type / row
|
|
312
323
|
* density with them); nothing else moves. One block per canonical step — a media/container
|
|
313
324
|
* query cannot read a `var()`, so the tokenized scale is written out (sm 40rem · md 48rem ·
|
|
@@ -324,6 +335,10 @@
|
|
|
324
335
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
325
336
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
326
337
|
font-size: var(--table-action-collection-font-size-compact);
|
|
338
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
339
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
340
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
341
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
327
342
|
}
|
|
328
343
|
|
|
329
344
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -345,6 +360,10 @@
|
|
|
345
360
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
346
361
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
347
362
|
font-size: var(--table-action-collection-font-size-compact);
|
|
363
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
364
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
365
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
366
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
348
367
|
}
|
|
349
368
|
|
|
350
369
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -366,6 +385,10 @@
|
|
|
366
385
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
367
386
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
368
387
|
font-size: var(--table-action-collection-font-size-compact);
|
|
388
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
389
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
390
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
391
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
369
392
|
}
|
|
370
393
|
|
|
371
394
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -387,6 +410,10 @@
|
|
|
387
410
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
388
411
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
389
412
|
font-size: var(--table-action-collection-font-size-compact);
|
|
413
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
414
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
415
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
416
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
390
417
|
}
|
|
391
418
|
|
|
392
419
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -68,4 +68,25 @@
|
|
|
68
68
|
--table-action-collection-cell-space-x-compact: var(--space-2);
|
|
69
69
|
--table-action-collection-cell-padding-y-compact: var(--space-2);
|
|
70
70
|
--table-action-collection-row-height-compact: var(--table-row-height-compact);
|
|
71
|
+
/* Compact-tier LEGIBILITY FLOOR — the measure below which the preset stops fitting the table
|
|
72
|
+
* to its container and lets the scroll region it already owns take over.
|
|
73
|
+
*
|
|
74
|
+
* The percentage budget above is sized for ONE column per priority tier plus one free-text
|
|
75
|
+
* column. A queue that repeats a tier — two `secondary` columns, three `meta` columns, or an
|
|
76
|
+
* unmarked column beside them — asks for more than 100%, and under `table-layout: fixed` the
|
|
77
|
+
* surplus is taken out of the columns rather than out of the table: measured on a seven-column
|
|
78
|
+
* Japanese admin queue at 390, every column landed at 44–59px and CJK labels wrapped at ONE to
|
|
79
|
+
* TWO characters per line (dxs-platform/platform#680). That is a WCAG 2.2 SC 1.4.10 Reflow (AA)
|
|
80
|
+
* failure, and it is the failure mode this floor removes.
|
|
81
|
+
*
|
|
82
|
+
* Scrolling is the correct escape, not a concession: SC 1.4.10 exempts "parts of the content
|
|
83
|
+
* which require two-dimensional layout for usage or meaning", and its own note names data
|
|
84
|
+
* tables as the example. A table that scrolls horizontally inside its card conforms; a table
|
|
85
|
+
* whose cells are one character wide does not.
|
|
86
|
+
*
|
|
87
|
+
* Default `0` so nothing changes for any existing consumer: a queue that fits its priority
|
|
88
|
+
* budget keeps fitting. A consumer whose queue carries more columns than the budget sets this
|
|
89
|
+
* to the measure at which its narrowest column is still readable (roughly 5rem per column for
|
|
90
|
+
* Japanese at the compact type tier) and the table scrolls instead of crushing. */
|
|
91
|
+
--table-action-collection-min-inline-size-compact: 0;
|
|
71
92
|
}
|