@godxjp/ui 27.1.0 → 27.3.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/dist/components/data-display/badge.d.ts +10 -3
- package/dist/components/data-display/badge.js +14 -3
- package/dist/components/data-display/data-table.d.ts +20 -12
- package/dist/components/data-display/data-table.js +53 -5
- package/dist/components/data-display/range-timeline.d.ts +16 -0
- package/dist/components/data-display/range-timeline.js +115 -80
- package/dist/components/data-display/table.d.ts +16 -0
- package/dist/components/data-display/table.js +2 -0
- package/dist/components/data-entry/checkbox.js +23 -5
- package/dist/components/data-entry/command.d.ts +3 -1
- package/dist/components/data-entry/command.js +10 -2
- package/dist/components/data-entry/form-field.js +9 -8
- package/dist/components/data-entry/index.d.ts +1 -0
- package/dist/components/data-entry/search-select.js +145 -136
- package/dist/components/data-entry/select.js +38 -3
- package/dist/components/layout/org-switcher.js +1 -1
- package/dist/components/layout/responsive-grid.d.ts +8 -2
- package/dist/components/layout/responsive-grid.js +2 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/form/form-field-control.d.ts +1 -1
- package/dist/form/form-field-control.js +32 -23
- package/dist/i18n/messages/en.json +2 -1
- package/dist/i18n/messages/ja.json +2 -1
- package/dist/i18n/messages/vi.json +2 -1
- package/dist/props/components/data-display.prop.d.ts +12 -0
- package/dist/props/components/data-entry.prop.d.ts +10 -0
- package/dist/props/components/form.prop.d.ts +34 -2
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +7 -0
- package/dist/props/registry.d.ts +26 -3
- package/dist/props/registry.js +25 -2
- package/dist/props/vocabulary/data.prop.d.ts +27 -1
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/styles/control.css +44 -1
- package/dist/styles/data-display-layout.css +58 -1
- package/dist/styles/form-layout.css +21 -0
- package/dist/styles/layout.css +7 -0
- package/dist/styles/navigation-layout.css +5 -1
- package/dist/styles/shell-layout.css +1 -14
- package/dist/styles/table-layout.css +80 -8
- package/dist/tokens/components/control.css +5 -0
- package/dist/tokens/components/data-display.css +6 -0
- package/dist/tokens/components/navigation.css +1 -1
- package/dist/tokens/components/table.css +2 -0
- package/docs/DESIGN-AUTHORITY.md +32 -1
- package/docs/data-display/data-table/examples/server-paged.tsx +156 -0
- package/docs/data-display/data-table/examples/striped-list.tsx +173 -0
- package/docs/data-display/popover.tsx +43 -1
- package/docs/data-display/timeline.tsx +73 -9
- package/docs/data-entry/command.tsx +80 -1
- package/docs/data-entry/form-root.tsx +12 -2
- package/docs/layout/responsive-grid.tsx +100 -0
- package/package.json +3 -2
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Checkbox as AriaCheckbox } from "react-aria-components";
|
|
5
5
|
import { Check, Minus } from "lucide-react";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
|
-
import { useFieldIdentity } from "../../lib/field-a11y.js";
|
|
7
|
+
import { mergeAriaIds, useFieldIdentity } from "../../lib/field-a11y.js";
|
|
8
|
+
import { Label } from "./label.js";
|
|
8
9
|
import { CheckboxGroup } from "./checkbox-group.js";
|
|
9
10
|
import { withOwnHitTarget } from "./choice-hit-target.js";
|
|
10
11
|
function CheckboxGlyph({ state }) {
|
|
@@ -39,9 +40,14 @@ const CheckboxRoot = React.forwardRef((props, ref) => {
|
|
|
39
40
|
required,
|
|
40
41
|
value,
|
|
41
42
|
tabIndex,
|
|
43
|
+
children,
|
|
42
44
|
"data-field": ownField,
|
|
43
45
|
...rest
|
|
44
46
|
} = props;
|
|
47
|
+
const autoId = React.useId();
|
|
48
|
+
const hasInlineLabel = !(children === void 0 || children === null || children === false || children === "");
|
|
49
|
+
const inputId = props.id ?? (hasInlineLabel ? autoId : void 0);
|
|
50
|
+
const inlineLabelId = `${inputId}-inline-label`;
|
|
45
51
|
const identity = useFieldIdentity({
|
|
46
52
|
id: props.id,
|
|
47
53
|
name: props.name,
|
|
@@ -63,7 +69,10 @@ const CheckboxRoot = React.forwardRef((props, ref) => {
|
|
|
63
69
|
);
|
|
64
70
|
const state = checked ?? uncontrolled;
|
|
65
71
|
const invalid = props["aria-invalid"];
|
|
66
|
-
|
|
72
|
+
const isInvalid = invalid !== void 0 && invalid !== false && invalid !== "false";
|
|
73
|
+
const describedBy = isInvalid ? mergeAriaIds(props["aria-describedby"], props["aria-errormessage"]) : props["aria-describedby"];
|
|
74
|
+
const labelledBy = hasInlineLabel && props["aria-labelledby"] !== void 0 ? mergeAriaIds(props["aria-labelledby"], inlineLabelId) : props["aria-labelledby"];
|
|
75
|
+
const box = /* @__PURE__ */ jsx(
|
|
67
76
|
AriaCheckbox,
|
|
68
77
|
{
|
|
69
78
|
...rest,
|
|
@@ -77,7 +86,10 @@ const CheckboxRoot = React.forwardRef((props, ref) => {
|
|
|
77
86
|
isIndeterminate: state === "indeterminate",
|
|
78
87
|
isDisabled: disabled,
|
|
79
88
|
isRequired: required,
|
|
80
|
-
isInvalid
|
|
89
|
+
isInvalid,
|
|
90
|
+
id: inputId,
|
|
91
|
+
"aria-describedby": describedBy,
|
|
92
|
+
"aria-labelledby": labelledBy,
|
|
81
93
|
excludeFromTabOrder: tabIndex !== void 0 && tabIndex < 0,
|
|
82
94
|
className: cn(
|
|
83
95
|
// `disabled:cursor-not-allowed disabled:opacity-50` and the checked fill are DELETED, not
|
|
@@ -95,7 +107,8 @@ const CheckboxRoot = React.forwardRef((props, ref) => {
|
|
|
95
107
|
// 16px box collapses to nothing. CheckboxVisual already carries the same three for the
|
|
96
108
|
// same reason.
|
|
97
109
|
"peer ui-checkbox data-[invalid]:border-destructive data-[state=checked]:border-primary data-[state=checked]:text-primary-foreground inline-flex shrink-0 items-center justify-center shadow-xs transition-shadow",
|
|
98
|
-
|
|
110
|
+
// With an inline label the row is what a caller styles; the box keeps its own classes.
|
|
111
|
+
hasInlineLabel ? void 0 : className
|
|
99
112
|
),
|
|
100
113
|
render: (domProps) => /* @__PURE__ */ jsx("label", { ...domProps, children: withOwnHitTarget(domProps.children) }),
|
|
101
114
|
onChange: (next) => {
|
|
@@ -105,6 +118,11 @@ const CheckboxRoot = React.forwardRef((props, ref) => {
|
|
|
105
118
|
children: /* @__PURE__ */ jsx("span", { "data-slot": "checkbox-indicator", className: "ui-choice-indicator", children: state ? /* @__PURE__ */ jsx(CheckboxGlyph, { state }) : null })
|
|
106
119
|
}
|
|
107
120
|
);
|
|
121
|
+
if (!hasInlineLabel) return box;
|
|
122
|
+
return /* @__PURE__ */ jsxs("div", { "data-slot": "checkbox-field", className: cn("ui-choice-field", className), children: [
|
|
123
|
+
/* @__PURE__ */ jsx("div", { className: "ui-choice-control", children: box }),
|
|
124
|
+
/* @__PURE__ */ jsx("div", { className: "ui-choice-content", children: /* @__PURE__ */ jsx(Label, { id: inlineLabelId, htmlFor: inputId, className: "ui-choice-label", children }) })
|
|
125
|
+
] });
|
|
108
126
|
});
|
|
109
127
|
CheckboxRoot.displayName = "Checkbox";
|
|
110
128
|
const Checkbox = Object.assign(CheckboxRoot, {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import type { CommandProp } from "../../props/components/data-entry.prop.js";
|
|
3
|
+
export type { CommandProp, CommandProp as CommandProps };
|
|
2
4
|
export declare const Command: React.ForwardRefExoticComponent<Omit<{
|
|
3
5
|
children?: React.ReactNode;
|
|
4
6
|
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
|
|
@@ -15,7 +17,7 @@ export declare const Command: React.ForwardRefExoticComponent<Omit<{
|
|
|
15
17
|
loop?: boolean;
|
|
16
18
|
disablePointerSelection?: boolean;
|
|
17
19
|
vimBindings?: boolean;
|
|
18
|
-
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
20
|
+
} & React.RefAttributes<HTMLDivElement>, "ref"> & CommandProp & React.RefAttributes<HTMLDivElement>>;
|
|
19
21
|
export declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "key" | keyof React.InputHTMLAttributes<HTMLInputElement>> & {
|
|
20
22
|
ref?: React.Ref<HTMLInputElement>;
|
|
21
23
|
} & {
|
|
@@ -5,7 +5,7 @@ import { Command as CommandPrimitive } from "cmdk";
|
|
|
5
5
|
import { Search } from "lucide-react";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
7
|
import { controlIconLeadingClass } from "../../lib/control-styles.js";
|
|
8
|
-
const Command = React.forwardRef(({ className, ...props }, ref) => {
|
|
8
|
+
const Command = React.forwardRef(({ className, split = false, ...props }, ref) => {
|
|
9
9
|
const rootRef = React.useRef(null);
|
|
10
10
|
const setRefs = (node) => {
|
|
11
11
|
rootRef.current = node;
|
|
@@ -22,7 +22,15 @@ const Command = React.forwardRef(({ className, ...props }, ref) => {
|
|
|
22
22
|
if (input && expectedId && !input.id) input.id = expectedId;
|
|
23
23
|
else if (!input || input.id !== expectedId) label.removeAttribute("for");
|
|
24
24
|
});
|
|
25
|
-
return /* @__PURE__ */ jsx(
|
|
25
|
+
return /* @__PURE__ */ jsx(
|
|
26
|
+
CommandPrimitive,
|
|
27
|
+
{
|
|
28
|
+
ref: setRefs,
|
|
29
|
+
className: cn("ui-command", className),
|
|
30
|
+
"data-split": split ? "" : void 0,
|
|
31
|
+
...props
|
|
32
|
+
}
|
|
33
|
+
);
|
|
26
34
|
});
|
|
27
35
|
Command.displayName = CommandPrimitive.displayName;
|
|
28
36
|
const CommandInput = React.forwardRef(({ className, ...props }, ref) => (
|
|
@@ -47,6 +47,7 @@ function FormField({
|
|
|
47
47
|
const autoId = React.useId();
|
|
48
48
|
const resolvedId = id ?? autoId;
|
|
49
49
|
const labelId = `${resolvedId}-label`;
|
|
50
|
+
const hasLabel = !(label === void 0 || label === null || label === false || label === "");
|
|
50
51
|
const helperId = helper ? `${resolvedId}-helper` : void 0;
|
|
51
52
|
const helperNode = helper ? /* @__PURE__ */ jsx("p", { id: helperId, className: "ui-form-field-note text-muted-foreground text-xs", children: helper }) : null;
|
|
52
53
|
const validationStatus = error ? "error" : validateStatus;
|
|
@@ -62,8 +63,8 @@ function FormField({
|
|
|
62
63
|
);
|
|
63
64
|
}
|
|
64
65
|
const fieldNameContext = React.useMemo(
|
|
65
|
-
() =>
|
|
66
|
-
[labelId, label]
|
|
66
|
+
() => hasLabel ? { labelId, label: typeof label === "string" ? label : void 0 } : null,
|
|
67
|
+
[hasLabel, labelId, label]
|
|
67
68
|
);
|
|
68
69
|
const fieldIdentityContext = React.useMemo(
|
|
69
70
|
() => fieldKey === void 0 ? null : { emitName: emitFieldNames },
|
|
@@ -88,12 +89,12 @@ function FormField({
|
|
|
88
89
|
// `"data-field": fieldKey` would erase a value the control set for itself.
|
|
89
90
|
"data-field": childProps?.["data-field"] ?? fieldKey,
|
|
90
91
|
...emitFieldNames ? { name: childProps?.name ?? fieldKey } : void 0,
|
|
91
|
-
"aria-labelledby": childProps?.["aria-labelledby"] ?? labelId,
|
|
92
|
+
"aria-labelledby": childProps?.["aria-labelledby"] ?? (hasLabel ? labelId : void 0),
|
|
92
93
|
// Redundant `aria-label` fallback (belt-and-suspenders): the accessible name is the
|
|
93
94
|
// SAME string as the visible label, just reachable even if an aria-labelledby lookup
|
|
94
95
|
// ever comes back empty (id-ref timing, AT quirks). Only when `label` is plain text and
|
|
95
96
|
// the child hasn't already set its own aria-label.
|
|
96
|
-
"aria-label": childProps?.["aria-label"] ?? (typeof label === "string" ? label : void 0),
|
|
97
|
+
"aria-label": childProps?.["aria-label"] ?? (hasLabel && typeof label === "string" ? label : void 0),
|
|
97
98
|
// Helper and error can coexist: helper stays on aria-describedby, the error on
|
|
98
99
|
// aria-errormessage (surfaced when aria-invalid is true).
|
|
99
100
|
"aria-describedby": mergeIds(
|
|
@@ -124,8 +125,8 @@ function FormField({
|
|
|
124
125
|
style: Object.keys(style).length ? style : void 0,
|
|
125
126
|
className: cn("ui-form-field", className),
|
|
126
127
|
children: [
|
|
127
|
-
/* @__PURE__ */ jsxs("div", { "data-slot": "form-field-label", className: "ui-form-field-label", children: [
|
|
128
|
-
/* @__PURE__ */ jsx(
|
|
128
|
+
hasLabel || labelAddon != null ? /* @__PURE__ */ jsxs("div", { "data-slot": "form-field-label", className: "ui-form-field-label", children: [
|
|
129
|
+
hasLabel ? /* @__PURE__ */ jsx(
|
|
129
130
|
Label,
|
|
130
131
|
{
|
|
131
132
|
asChild: true,
|
|
@@ -151,9 +152,9 @@ function FormField({
|
|
|
151
152
|
}
|
|
152
153
|
)
|
|
153
154
|
}
|
|
154
|
-
),
|
|
155
|
+
) : null,
|
|
155
156
|
labelAddon
|
|
156
|
-
] }),
|
|
157
|
+
] }) : null,
|
|
157
158
|
/* @__PURE__ */ jsxs("div", { "data-slot": "form-field-control", className: "ui-form-field-control", children: [
|
|
158
159
|
helperPlacement === "before" ? helperNode : null,
|
|
159
160
|
isStatic ? childWithA11y : /* @__PURE__ */ jsx(FieldNameContext.Provider, { value: fieldNameContext, children: /* @__PURE__ */ jsx(FieldIdentityContext.Provider, { value: fieldIdentityContext, children: /* @__PURE__ */ jsx(
|
|
@@ -49,6 +49,7 @@ export type { TreeSelectProps } from "./tree-select.js";
|
|
|
49
49
|
export { Transfer } from "./transfer.js";
|
|
50
50
|
export type { TransferProps, TransferItemProp } from "./transfer.js";
|
|
51
51
|
export { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "./command.js";
|
|
52
|
+
export type { CommandProps } from "./command.js";
|
|
52
53
|
export { CommandPalette } from "./command-palette.js";
|
|
53
54
|
export type { CommandPaletteGroup, CommandPaletteItem, CommandPaletteLabels, CommandPaletteProps, } from "./command-palette.js";
|
|
54
55
|
export { PasswordInput } from "./password-input.js";
|
|
@@ -511,144 +511,153 @@ function SearchSelect(props) {
|
|
|
511
511
|
style: typeof popupMatchSelectWidth === "number" ? {
|
|
512
512
|
"--search-select-panel-inline-size": `${popupMatchSelectWidth}px`
|
|
513
513
|
} : void 0,
|
|
514
|
-
children: /* @__PURE__ */ jsx(PopupRender, { render: popupRender, children: /* @__PURE__ */ jsxs(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
"
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
514
|
+
children: /* @__PURE__ */ jsx(PopupRender, { render: popupRender, children: /* @__PURE__ */ jsxs(
|
|
515
|
+
Command,
|
|
516
|
+
{
|
|
517
|
+
value,
|
|
518
|
+
shouldFilter: false,
|
|
519
|
+
className: "ui-search-select-command",
|
|
520
|
+
split: multiple,
|
|
521
|
+
children: [
|
|
522
|
+
/* @__PURE__ */ jsx("div", { className: "ui-search-select-search", children: /* @__PURE__ */ jsx(
|
|
523
|
+
Input,
|
|
524
|
+
{
|
|
525
|
+
autoFocus: true,
|
|
526
|
+
"aria-controls": listId,
|
|
527
|
+
"aria-autocomplete": "list",
|
|
528
|
+
"aria-activedescendant": activeOptionId,
|
|
529
|
+
"aria-label": searchPlaceholder ?? t("dataEntry.searchSelect.search"),
|
|
530
|
+
value: query,
|
|
531
|
+
onChange: (event) => {
|
|
532
|
+
const next = event.target.value;
|
|
533
|
+
if (multiple && tokenSeparators?.length && tokenSeparators.some((separator) => next.includes(separator))) {
|
|
534
|
+
selectTokens(next);
|
|
535
|
+
setQuery("");
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
setQuery(next);
|
|
539
|
+
},
|
|
540
|
+
onPaste: (event) => {
|
|
541
|
+
if (!multiple || !tokenSeparators?.length) return;
|
|
542
|
+
const text = event.clipboardData.getData("text");
|
|
543
|
+
if (!tokenSeparators.some((separator) => text.includes(separator))) return;
|
|
544
|
+
event.preventDefault();
|
|
545
|
+
selectTokens(text);
|
|
546
|
+
setQuery("");
|
|
547
|
+
},
|
|
548
|
+
onKeyDown,
|
|
549
|
+
placeholder: searchPlaceholder ?? t("dataEntry.searchSelect.search"),
|
|
550
|
+
className: "ui-search-select-search-input"
|
|
530
551
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
"data-tone": "destructive",
|
|
622
|
-
children: errorMessage ?? t("dataEntry.searchSelect.error")
|
|
623
|
-
}
|
|
624
|
-
) : displayOptions.length === 0 ? /* @__PURE__ */ jsx(
|
|
625
|
-
"div",
|
|
626
|
-
{
|
|
627
|
-
role: "option",
|
|
628
|
-
"aria-disabled": "true",
|
|
629
|
-
"aria-selected": false,
|
|
630
|
-
className: "ui-search-select-placeholder",
|
|
631
|
-
children: notFoundContent ?? emptyMessage ?? t("dataEntry.searchSelect.empty")
|
|
632
|
-
}
|
|
633
|
-
) : null
|
|
634
|
-
]
|
|
635
|
-
}
|
|
636
|
-
),
|
|
637
|
-
renderLoadMore && hasMore ? /* @__PURE__ */ jsx(
|
|
638
|
-
"div",
|
|
639
|
-
{
|
|
640
|
-
className: "ui-search-select-footer",
|
|
641
|
-
onKeyDown: (event) => event.stopPropagation(),
|
|
642
|
-
children: renderLoadMore({
|
|
643
|
-
hasMore,
|
|
644
|
-
loading,
|
|
645
|
-
loadMore: () => {
|
|
646
|
-
if (!loading) void fetchPage(page + 1, debouncedQuery, true);
|
|
552
|
+
) }),
|
|
553
|
+
/* @__PURE__ */ jsxs(
|
|
554
|
+
"div",
|
|
555
|
+
{
|
|
556
|
+
id: listId,
|
|
557
|
+
role: "listbox",
|
|
558
|
+
"aria-multiselectable": multiple || void 0,
|
|
559
|
+
"aria-busy": loading,
|
|
560
|
+
className: "ui-search-select-list",
|
|
561
|
+
style: listHeight === void 0 ? void 0 : {
|
|
562
|
+
"--search-select-list-max-height": `${listHeight}px`
|
|
563
|
+
},
|
|
564
|
+
onScroll: (event) => {
|
|
565
|
+
onPopupScroll?.(event);
|
|
566
|
+
onScroll(event);
|
|
567
|
+
},
|
|
568
|
+
children: [
|
|
569
|
+
grouped.map((group) => {
|
|
570
|
+
const rows = group.items.map(({ option, index }) => /* @__PURE__ */ jsxs(
|
|
571
|
+
"div",
|
|
572
|
+
{
|
|
573
|
+
id: optionDomId(option.value),
|
|
574
|
+
role: "option",
|
|
575
|
+
"data-testid": optionTestId(option.value),
|
|
576
|
+
"aria-selected": isSelected(option.value),
|
|
577
|
+
"aria-disabled": option.disabled || atMaxCount && !isSelected(option.value) || void 0,
|
|
578
|
+
"data-disabled": option.disabled || atMaxCount && !isSelected(option.value) || void 0,
|
|
579
|
+
className: cn(
|
|
580
|
+
"ui-command-item",
|
|
581
|
+
// Selected = persistent bg-accent + medium weight (NO check icon — saves width),
|
|
582
|
+
// matching the plain SelectItem's `data-[state=checked]` convention; active =
|
|
583
|
+
// hover/keyboard accent. Same bg so selection stays coherent across both Selects.
|
|
584
|
+
isSelected(option.value) && "bg-accent text-foreground font-medium",
|
|
585
|
+
activeIndex === index && "bg-accent text-accent-foreground"
|
|
586
|
+
),
|
|
587
|
+
onMouseEnter: () => !option.disabled && setActiveIndex(index),
|
|
588
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
589
|
+
onClick: () => select(option),
|
|
590
|
+
children: [
|
|
591
|
+
optionRender ? /* @__PURE__ */ jsx("div", { className: "ui-search-select-option-slot", children: optionRender(option, { index }) }) : renderOption ? /* @__PURE__ */ jsx("div", { className: "ui-search-select-option-slot", children: renderOption(option) }) : /* @__PURE__ */ jsxs("div", { className: "ui-search-select-option-body", children: [
|
|
592
|
+
option.icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", "aria-hidden": "true", children: option.icon }) : null,
|
|
593
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-search-select-option-text", children: [
|
|
594
|
+
/* @__PURE__ */ jsx("span", { className: "ui-search-select-option-label", children: option.label }),
|
|
595
|
+
option.sublabel ? /* @__PURE__ */ jsx("span", { className: "ui-search-select-option-sublabel", children: option.sublabel }) : null
|
|
596
|
+
] })
|
|
597
|
+
] }),
|
|
598
|
+
menuItemSelectedIcon && isSelected(option.value) ? /* @__PURE__ */ jsx(
|
|
599
|
+
"span",
|
|
600
|
+
{
|
|
601
|
+
"data-slot": "search-select-selected-icon",
|
|
602
|
+
className: "ui-search-select-selected-icon",
|
|
603
|
+
"aria-hidden": "true",
|
|
604
|
+
children: menuItemSelectedIcon
|
|
605
|
+
}
|
|
606
|
+
) : null
|
|
607
|
+
]
|
|
608
|
+
},
|
|
609
|
+
option.value
|
|
610
|
+
));
|
|
611
|
+
return group.heading ? /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: rows }, group.heading) : /* @__PURE__ */ jsx(React.Fragment, { children: rows }, "__ungrouped");
|
|
612
|
+
}),
|
|
613
|
+
loading ? /* @__PURE__ */ jsxs("div", { role: "status", className: "ui-search-select-status", children: [
|
|
614
|
+
/* @__PURE__ */ jsx(Loader2, { className: "ui-search-select-spinner animate-spin", "aria-hidden": "true" }),
|
|
615
|
+
loadingMessage ?? t("dataEntry.searchSelect.loading")
|
|
616
|
+
] }) : error ? renderError ? renderError({
|
|
617
|
+
message: errorMessage ?? t("dataEntry.searchSelect.error"),
|
|
618
|
+
// Retry always reloads from the first page (a defined, predictable recovery —
|
|
619
|
+
// NOT a resume of a failed page-N append, which would need to re-derive state).
|
|
620
|
+
retry: () => void fetchPage(1, debouncedQuery, false)
|
|
621
|
+
}) : /* @__PURE__ */ jsx(
|
|
622
|
+
"div",
|
|
623
|
+
{
|
|
624
|
+
role: "option",
|
|
625
|
+
"aria-disabled": "true",
|
|
626
|
+
"aria-selected": false,
|
|
627
|
+
className: "ui-search-select-placeholder",
|
|
628
|
+
"data-tone": "destructive",
|
|
629
|
+
children: errorMessage ?? t("dataEntry.searchSelect.error")
|
|
630
|
+
}
|
|
631
|
+
) : displayOptions.length === 0 ? /* @__PURE__ */ jsx(
|
|
632
|
+
"div",
|
|
633
|
+
{
|
|
634
|
+
role: "option",
|
|
635
|
+
"aria-disabled": "true",
|
|
636
|
+
"aria-selected": false,
|
|
637
|
+
className: "ui-search-select-placeholder",
|
|
638
|
+
children: notFoundContent ?? emptyMessage ?? t("dataEntry.searchSelect.empty")
|
|
639
|
+
}
|
|
640
|
+
) : null
|
|
641
|
+
]
|
|
647
642
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
643
|
+
),
|
|
644
|
+
renderLoadMore && hasMore ? /* @__PURE__ */ jsx(
|
|
645
|
+
"div",
|
|
646
|
+
{
|
|
647
|
+
className: "ui-search-select-footer",
|
|
648
|
+
onKeyDown: (event) => event.stopPropagation(),
|
|
649
|
+
children: renderLoadMore({
|
|
650
|
+
hasMore,
|
|
651
|
+
loading,
|
|
652
|
+
loadMore: () => {
|
|
653
|
+
if (!loading) void fetchPage(page + 1, debouncedQuery, true);
|
|
654
|
+
}
|
|
655
|
+
})
|
|
656
|
+
}
|
|
657
|
+
) : null
|
|
658
|
+
]
|
|
659
|
+
}
|
|
660
|
+
) })
|
|
652
661
|
}
|
|
653
662
|
)
|
|
654
663
|
]
|
|
@@ -55,6 +55,35 @@ function itemTextValue(node) {
|
|
|
55
55
|
}
|
|
56
56
|
return "";
|
|
57
57
|
}
|
|
58
|
+
function useSettledDefaultOpen(rootRef, defaultOpen, open) {
|
|
59
|
+
const [deferred] = React.useState(() => defaultOpen === true && open === void 0);
|
|
60
|
+
const [settledOpen, setSettledOpen] = React.useState(false);
|
|
61
|
+
React.useLayoutEffect(() => {
|
|
62
|
+
if (!deferred) return;
|
|
63
|
+
let cancelled = false;
|
|
64
|
+
const moving = [];
|
|
65
|
+
for (let node = rootRef.current?.parentElement; node; node = node.parentElement) {
|
|
66
|
+
if (typeof node.getAnimations !== "function") break;
|
|
67
|
+
for (const animation of node.getAnimations()) {
|
|
68
|
+
const end = animation.effect?.getComputedTiming().endTime;
|
|
69
|
+
if (animation.playState === "running" && Number.isFinite(Number(end))) {
|
|
70
|
+
moving.push(animation.finished);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (moving.length === 0) {
|
|
75
|
+
setSettledOpen(true);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
void Promise.allSettled(moving).then(() => {
|
|
79
|
+
if (!cancelled) setSettledOpen(true);
|
|
80
|
+
});
|
|
81
|
+
return () => {
|
|
82
|
+
cancelled = true;
|
|
83
|
+
};
|
|
84
|
+
}, [deferred, rootRef]);
|
|
85
|
+
return deferred ? { open: settledOpen, setOpen: setSettledOpen } : null;
|
|
86
|
+
}
|
|
58
87
|
function SelectRoot({
|
|
59
88
|
id,
|
|
60
89
|
value,
|
|
@@ -76,9 +105,12 @@ function SelectRoot({
|
|
|
76
105
|
children
|
|
77
106
|
}) {
|
|
78
107
|
void dir;
|
|
108
|
+
const rootRef = React.useRef(null);
|
|
109
|
+
const settled = useSettledDefaultOpen(rootRef, defaultOpen, open);
|
|
79
110
|
return /* @__PURE__ */ jsx(
|
|
80
111
|
AriaSelect,
|
|
81
112
|
{
|
|
113
|
+
ref: rootRef,
|
|
82
114
|
"data-slot": "select",
|
|
83
115
|
className: "ui-select-root",
|
|
84
116
|
id,
|
|
@@ -87,9 +119,12 @@ function SelectRoot({
|
|
|
87
119
|
onChange: (key) => {
|
|
88
120
|
if (key != null) onValueChange?.(String(key));
|
|
89
121
|
},
|
|
90
|
-
isOpen: open,
|
|
91
|
-
defaultOpen,
|
|
92
|
-
onOpenChange
|
|
122
|
+
isOpen: open ?? settled?.open,
|
|
123
|
+
defaultOpen: settled ? void 0 : defaultOpen,
|
|
124
|
+
onOpenChange: (next) => {
|
|
125
|
+
settled?.setOpen(next);
|
|
126
|
+
onOpenChange?.(next);
|
|
127
|
+
},
|
|
93
128
|
isDisabled: disabled || isDisabled,
|
|
94
129
|
isRequired: required,
|
|
95
130
|
name,
|
|
@@ -105,7 +105,7 @@ function OrgSwitcherPanel({
|
|
|
105
105
|
] }) : null
|
|
106
106
|
] });
|
|
107
107
|
}
|
|
108
|
-
return /* @__PURE__ */ jsxs(Command, { label: labels.title, className: "ui-org-switcher-command", children: [
|
|
108
|
+
return /* @__PURE__ */ jsxs(Command, { label: labels.title, className: "ui-org-switcher-command", split: true, children: [
|
|
109
109
|
/* @__PURE__ */ jsx(CommandInput, { placeholder: labels.search, "aria-label": labels.search }),
|
|
110
110
|
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
111
111
|
/* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
-
import type { ResponsiveGridColumnsProp, ResponsiveGridFlowProp, ResponsiveGridPresetProp } from "../../props/components/layout.prop.js";
|
|
2
|
+
import type { ResponsiveGridAlignProp, ResponsiveGridColumnsProp, ResponsiveGridFlowProp, ResponsiveGridPresetProp } from "../../props/components/layout.prop.js";
|
|
3
3
|
import type { PadProp, PadRawProp } from "../../props/vocabulary/index.js";
|
|
4
4
|
import type { GapProp } from "../../props/vocabulary/layout.prop.js";
|
|
5
5
|
export type ResponsiveGridProps = {
|
|
@@ -12,6 +12,12 @@ export type ResponsiveGridProps = {
|
|
|
12
12
|
columns?: ResponsiveGridColumnsProp;
|
|
13
13
|
/** Keep collections in a horizontal sequence; pair with ScrollArea orientation="horizontal". */
|
|
14
14
|
flow?: ResponsiveGridFlowProp;
|
|
15
|
+
/**
|
|
16
|
+
* Block-axis alignment of the cells (antd `Row align`). `stretch` makes every cell as tall as
|
|
17
|
+
* the tallest — Kanban lanes, where an empty lane must stay a full-height drop zone. Omitted,
|
|
18
|
+
* `flow="columns"` aligns to `start` and `flow="rows"` stretches, as before.
|
|
19
|
+
*/
|
|
20
|
+
align?: ResponsiveGridAlignProp;
|
|
15
21
|
/**
|
|
16
22
|
* Named column geometry for a recognised collection shape — see `ResponsiveGridPresetProp`.
|
|
17
23
|
* Wins over `columns` when both are set, so a caller migrating to a preset does not also need
|
|
@@ -20,7 +26,7 @@ export type ResponsiveGridProps = {
|
|
|
20
26
|
preset?: ResponsiveGridPresetProp;
|
|
21
27
|
children: ReactNode;
|
|
22
28
|
};
|
|
23
|
-
export declare function ResponsiveGrid({ columns, flow, gap, pad, padRaw, preset, className, children, }: ResponsiveGridProps): import("react").JSX.Element;
|
|
29
|
+
export declare function ResponsiveGrid({ columns, flow, align, gap, pad, padRaw, preset, className, children, }: ResponsiveGridProps): import("react").JSX.Element;
|
|
24
30
|
export declare namespace ResponsiveGrid {
|
|
25
31
|
export { ResponsiveGridItem as Item };
|
|
26
32
|
}
|
|
@@ -31,6 +31,7 @@ function toStyle(resolved) {
|
|
|
31
31
|
function ResponsiveGrid({
|
|
32
32
|
columns = 4,
|
|
33
33
|
flow = "rows",
|
|
34
|
+
align,
|
|
34
35
|
gap,
|
|
35
36
|
pad,
|
|
36
37
|
padRaw,
|
|
@@ -45,6 +46,7 @@ function ResponsiveGrid({
|
|
|
45
46
|
className: cn("ui-responsive-grid", className),
|
|
46
47
|
"data-gap": gap,
|
|
47
48
|
"data-flow": flow === "columns" ? "columns" : void 0,
|
|
49
|
+
"data-align": align,
|
|
48
50
|
style: { ...toStyle(resolved), ...padStyle(pad, padRaw) },
|
|
49
51
|
"data-pad-raw": padRaw === void 0 ? void 0 : "",
|
|
50
52
|
children
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
|
|
3
|
-
"version": "27.1
|
|
3
|
+
"version": "27.3.1",
|
|
4
4
|
"targetSize": {
|
|
5
5
|
"standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
|
|
6
6
|
"min": 24,
|
|
@@ -2,4 +2,4 @@ import * as React from "react";
|
|
|
2
2
|
import { type FieldValues } from "react-hook-form";
|
|
3
3
|
import type { FormFieldControlProp } from "../props/components/form.prop.js";
|
|
4
4
|
export type { FormFieldControlProp } from "../props/components/form.prop.js";
|
|
5
|
-
export declare function FormFieldControl<TFieldValues extends FieldValues>({ name, id, field: fieldKey, labelAddon, layout, labelWidth, controlWidth, colSpan, label, required, helper, disabled, dependencies, getValueFromEvent, normalize, help, validateStatus, hasFeedback, feedback, preserve, className, children, }: FormFieldControlProp<TFieldValues>): React.JSX.Element;
|
|
5
|
+
export declare function FormFieldControl<TFieldValues extends FieldValues>({ name, id, field: fieldKey, labelAddon, layout, labelWidth, controlWidth, colSpan, label, required, helper, disabled, dependencies, getValueFromEvent, normalize, help, validateStatus, hasFeedback, feedback, preserve, className, valuePropName, children, }: FormFieldControlProp<TFieldValues>): React.JSX.Element;
|