@godxjp/ui 18.15.4 → 19.1.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/app/app-provider.d.ts +1 -1
- package/dist/app/app-provider.js +3 -0
- package/dist/app/date-format-labels.d.ts +1 -1
- package/dist/app/time-format-labels.d.ts +1 -1
- package/dist/components/data-display/badge.d.ts +1 -1
- package/dist/components/data-display/card.d.ts +4 -13
- package/dist/components/data-display/card.js +2 -15
- package/dist/components/data-display/data-table.d.ts +3 -8
- package/dist/components/data-display/data-table.js +106 -68
- package/dist/components/data-display/descriptions.js +34 -5
- package/dist/components/data-display/empty-state.js +1 -1
- package/dist/components/data-display/permission-matrix.js +7 -7
- package/dist/components/data-display/popover.js +2 -9
- package/dist/components/data-display/qr-code.d.ts +1 -1
- package/dist/components/data-display/scroll-area.d.ts +3 -1
- package/dist/components/data-display/scroll-area.js +150 -16
- package/dist/components/data-display/table.js +12 -3
- package/dist/components/data-entry/branch-scope-picker.js +4 -4
- package/dist/components/data-entry/calendar.js +15 -24
- package/dist/components/data-entry/cascader.js +70 -72
- package/dist/components/data-entry/checkbox-group.js +12 -5
- package/dist/components/data-entry/checkbox.js +30 -13
- package/dist/components/data-entry/command.d.ts +6 -6
- package/dist/components/data-entry/date-picker.js +34 -17
- package/dist/components/data-entry/date-range-picker.js +19 -13
- package/dist/components/data-entry/form-field.d.ts +1 -1
- package/dist/components/data-entry/form-field.js +22 -5
- package/dist/components/data-entry/index.d.ts +2 -1
- package/dist/components/data-entry/input.js +18 -22
- package/dist/components/data-entry/label.js +4 -2
- package/dist/components/data-entry/month-picker.js +20 -17
- package/dist/components/data-entry/month-range-picker.js +26 -19
- package/dist/components/data-entry/radio.js +25 -6
- package/dist/components/data-entry/search-select.d.ts +1 -1
- package/dist/components/data-entry/search-select.js +37 -31
- package/dist/components/data-entry/select.d.ts +8 -0
- package/dist/components/data-entry/select.js +68 -26
- package/dist/components/data-entry/switch.js +16 -2
- package/dist/components/data-entry/textarea.d.ts +56 -0
- package/dist/components/data-entry/textarea.js +110 -40
- package/dist/components/data-entry/time-picker.js +52 -52
- package/dist/components/data-entry/toggle-group.d.ts +1 -0
- package/dist/components/data-entry/toggle.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +14 -15
- package/dist/components/data-entry/tree-select.js +19 -23
- package/dist/components/data-entry/upload-crop-dialog.js +4 -4
- package/dist/components/data-entry/upload.d.ts +1 -1
- package/dist/components/data-entry/upload.js +46 -46
- package/dist/components/feedback/alert.js +7 -14
- package/dist/components/feedback/dialog.d.ts +23 -32
- package/dist/components/feedback/dialog.js +16 -13
- package/dist/components/feedback/sheet.d.ts +1 -1
- package/dist/components/feedback/sheet.js +14 -14
- package/dist/components/feedback/skeleton.js +13 -10
- package/dist/components/feedback/sonner.d.ts +1 -1
- package/dist/components/feedback/sonner.js +17 -8
- package/dist/components/feedback/tooltip.js +1 -1
- package/dist/components/general/activity.d.ts +44 -0
- package/dist/components/general/activity.js +65 -0
- package/dist/components/general/button.d.ts +2 -2
- package/dist/components/general/button.js +11 -24
- package/dist/components/general/index.d.ts +2 -0
- package/dist/components/general/index.js +2 -0
- package/dist/components/layout/app-shell.js +5 -3
- package/dist/components/layout/auth-divider.d.ts +9 -2
- package/dist/components/layout/auth-divider.js +7 -11
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +32 -20
- package/dist/components/layout/resizable.d.ts +12 -12
- package/dist/components/layout/separator.d.ts +1 -0
- package/dist/components/layout/service-role-panel.js +47 -42
- package/dist/components/layout/sidebar.js +13 -1
- package/dist/components/layout/split-pane.d.ts +12 -1
- package/dist/components/layout/split-pane.js +13 -4
- package/dist/components/navigation/app-setting-picker.js +5 -14
- package/dist/components/navigation/dropdown-menu.js +10 -16
- package/dist/components/navigation/menubar.d.ts +3 -6
- package/dist/components/navigation/pagination-utils.d.ts +15 -2
- package/dist/components/navigation/pagination-utils.js +27 -21
- package/dist/components/navigation/pagination.d.ts +4 -4
- package/dist/components/navigation/pagination.js +2 -2
- package/dist/components/navigation/steps.js +31 -67
- package/dist/components/navigation/tabs.js +17 -5
- package/dist/components/query/infinite-query-state.js +2 -2
- package/dist/components/ui/accordion.d.ts +1 -1
- package/dist/components/ui/avatar.d.ts +1 -1
- package/dist/components/ui/avatar.js +29 -10
- package/dist/components/ui/input-otp.d.ts +8 -6
- package/dist/components/ui/rating.js +1 -1
- package/dist/components/ui/separator.d.ts +27 -2
- package/dist/components/ui/separator.js +42 -11
- package/dist/components/ui/toggle-group.d.ts +15 -5
- package/dist/components/ui/toggle-group.js +43 -18
- package/dist/components/ui/toggle.d.ts +66 -5
- package/dist/components/ui/toggle.js +64 -9
- package/dist/i18n/messages/en.json +8 -0
- package/dist/i18n/messages/ja.json +8 -0
- package/dist/i18n/messages/vi.json +8 -0
- package/dist/lib/control-styles.d.ts +14 -0
- package/dist/lib/control-styles.js +2 -0
- package/dist/lib/field-a11y.d.ts +52 -0
- package/dist/lib/field-a11y.js +17 -2
- package/dist/props/components/app.prop.d.ts +15 -0
- package/dist/props/components/data-display.prop.d.ts +85 -3
- package/dist/props/components/data-entry.prop.d.ts +35 -2
- package/dist/props/components/general.prop.d.ts +52 -1
- package/dist/props/components/layout.prop.d.ts +137 -2
- package/dist/props/registry.d.ts +116 -5
- package/dist/props/registry.js +165 -4
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/interaction.prop.d.ts +15 -0
- package/dist/props/vocabulary/layout.prop.d.ts +8 -0
- package/dist/styles/alert-layout.css +69 -3
- package/dist/styles/badge-layout.css +2 -2
- package/dist/styles/base.css +6 -0
- package/dist/styles/card-layout.css +25 -8
- package/dist/styles/control.css +1150 -29
- package/dist/styles/data-display-layout.css +185 -11
- package/dist/styles/data-entry-layout.css +352 -1
- package/dist/styles/density.css +2 -2
- package/dist/styles/dialog-layout.css +203 -4
- package/dist/styles/focus-ring.css +155 -0
- package/dist/styles/form-layout.css +50 -5
- package/dist/styles/index.css +1 -0
- package/dist/styles/layout.css +412 -23
- package/dist/styles/motion.css +178 -0
- package/dist/styles/navigation-layout.css +398 -22
- package/dist/styles/shell-layout.css +137 -39
- package/dist/styles/table-layout.css +153 -22
- package/dist/styles/toggle.css +59 -0
- package/dist/tokens/base.css +5 -0
- package/dist/tokens/components/activity.css +64 -0
- package/dist/tokens/components/badge.css +7 -0
- package/dist/tokens/components/card.css +14 -10
- package/dist/tokens/components/chart.css +1 -1
- package/dist/tokens/components/control.css +358 -8
- package/dist/tokens/components/data-display.css +75 -0
- package/dist/tokens/components/data-entry.css +25 -0
- package/dist/tokens/components/descriptions.css +22 -0
- package/dist/tokens/components/feedback.css +143 -4
- package/dist/tokens/components/form.css +16 -0
- package/dist/tokens/components/legal-document.css +5 -1
- package/dist/tokens/components/navigation.css +109 -0
- package/dist/tokens/components/scroll-area.css +21 -0
- package/dist/tokens/components/separator.css +52 -0
- package/dist/tokens/components/shell.css +132 -20
- package/dist/tokens/components/table.css +52 -3
- package/dist/tokens/components/toggle.css +82 -0
- package/dist/tokens/components/upload.css +107 -0
- package/dist/tokens/foundation.css +171 -10
- package/dist/tokens/semantic/layout.css +120 -1
- package/package.json +85 -68
- package/scripts/ui-audit.mjs +51 -2
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { CalendarIcon, ChevronLeft, ChevronRight, X } from "lucide-react";
|
|
5
5
|
import { usePickerLocales, useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { useControlledLatch } from "../../lib/hooks.js";
|
|
7
|
-
import { pickFieldA11y } from "../../lib/field-a11y.js";
|
|
7
|
+
import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
|
|
8
8
|
import { cn } from "../../lib/utils.js";
|
|
9
9
|
import { Button } from "../general/button.js";
|
|
10
10
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
|
|
@@ -38,6 +38,7 @@ function MonthPicker({
|
|
|
38
38
|
const inputId = id ?? autoId;
|
|
39
39
|
const dialogId = `${inputId}-dialog`;
|
|
40
40
|
const fieldA11y = pickFieldA11y(ariaProps);
|
|
41
|
+
const identity = useFieldIdentity({ id, name, "data-field": fieldA11y["data-field"] });
|
|
41
42
|
const isControlled = useControlledLatch(valueProp !== void 0);
|
|
42
43
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
43
44
|
const value = isControlled ? valueProp : internalValue;
|
|
@@ -61,11 +62,11 @@ function MonthPicker({
|
|
|
61
62
|
return /* @__PURE__ */ jsx(Popover, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
62
63
|
"div",
|
|
63
64
|
{
|
|
65
|
+
"data-disabled": disabled ? "" : void 0,
|
|
64
66
|
className: cn(
|
|
65
|
-
"ui-control
|
|
67
|
+
"ui-control ui-control-composite-field",
|
|
66
68
|
"focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
|
|
67
69
|
open && "border-ring ring-ring/50 ring-[3px]",
|
|
68
|
-
disabled && "pointer-events-none cursor-not-allowed opacity-50",
|
|
69
70
|
className
|
|
70
71
|
),
|
|
71
72
|
onClick: () => {
|
|
@@ -76,7 +77,8 @@ function MonthPicker({
|
|
|
76
77
|
"input",
|
|
77
78
|
{
|
|
78
79
|
id: inputId,
|
|
79
|
-
name,
|
|
80
|
+
name: name ?? identity.name,
|
|
81
|
+
"data-field": fieldA11y["data-field"] ?? identity["data-field"],
|
|
80
82
|
value: text,
|
|
81
83
|
disabled,
|
|
82
84
|
placeholder: placeholder ?? t("dataEntry.monthPicker.placeholder") ?? YM_HINT,
|
|
@@ -87,7 +89,7 @@ function MonthPicker({
|
|
|
87
89
|
"aria-haspopup": "dialog",
|
|
88
90
|
"aria-controls": open ? dialogId : void 0,
|
|
89
91
|
...fieldA11y,
|
|
90
|
-
className: "
|
|
92
|
+
className: "ui-month-picker-input",
|
|
91
93
|
onKeyDown: (event) => {
|
|
92
94
|
if (event.key === "ArrowDown") {
|
|
93
95
|
event.preventDefault();
|
|
@@ -117,9 +119,10 @@ function MonthPicker({
|
|
|
117
119
|
emit(void 0);
|
|
118
120
|
setText("");
|
|
119
121
|
},
|
|
120
|
-
children: /* @__PURE__ */ jsx(X, { className: "
|
|
122
|
+
children: /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
|
|
121
123
|
}
|
|
122
|
-
) :
|
|
124
|
+
) : null,
|
|
125
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
123
126
|
"button",
|
|
124
127
|
{
|
|
125
128
|
type: "button",
|
|
@@ -127,7 +130,7 @@ function MonthPicker({
|
|
|
127
130
|
tabIndex: -1,
|
|
128
131
|
"aria-label": t("dataEntry.monthPicker.openGrid") ?? "Open month grid",
|
|
129
132
|
className: "text-muted-foreground hover:text-foreground shrink-0",
|
|
130
|
-
children: /* @__PURE__ */ jsx(CalendarIcon, { className: "
|
|
133
|
+
children: /* @__PURE__ */ jsx(CalendarIcon, { className: "ui-month-picker-icon", "aria-hidden": "true" })
|
|
131
134
|
}
|
|
132
135
|
) }),
|
|
133
136
|
/* @__PURE__ */ jsxs(
|
|
@@ -136,12 +139,12 @@ function MonthPicker({
|
|
|
136
139
|
id: dialogId,
|
|
137
140
|
role: "dialog",
|
|
138
141
|
"aria-label": t("dataEntry.monthPicker.openGrid") ?? "Month grid",
|
|
139
|
-
className: "
|
|
142
|
+
className: "ui-month-picker-panel",
|
|
140
143
|
align: "start",
|
|
141
144
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
142
145
|
onClick: (event) => event.stopPropagation(),
|
|
143
146
|
children: [
|
|
144
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
147
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-month-picker-nav", children: [
|
|
145
148
|
/* @__PURE__ */ jsx(
|
|
146
149
|
Button,
|
|
147
150
|
{
|
|
@@ -150,12 +153,12 @@ function MonthPicker({
|
|
|
150
153
|
size: "icon-sm",
|
|
151
154
|
disabled: prevDisabled,
|
|
152
155
|
"aria-label": t("dataEntry.monthPicker.previousYear") ?? "Previous year",
|
|
153
|
-
className: "
|
|
156
|
+
className: "ui-month-picker-nav-button",
|
|
154
157
|
onClick: () => setViewYear((y) => y - 1),
|
|
155
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "
|
|
158
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "ui-month-picker-icon", "aria-hidden": "true" })
|
|
156
159
|
}
|
|
157
160
|
),
|
|
158
|
-
/* @__PURE__ */ jsx("span", { className: "
|
|
161
|
+
/* @__PURE__ */ jsx("span", { className: "ui-month-picker-nav-label", "aria-live": "polite", children: viewYear }),
|
|
159
162
|
/* @__PURE__ */ jsx(
|
|
160
163
|
Button,
|
|
161
164
|
{
|
|
@@ -164,13 +167,13 @@ function MonthPicker({
|
|
|
164
167
|
size: "icon-sm",
|
|
165
168
|
disabled: nextDisabled,
|
|
166
169
|
"aria-label": t("dataEntry.monthPicker.nextYear") ?? "Next year",
|
|
167
|
-
className: "
|
|
170
|
+
className: "ui-month-picker-nav-button",
|
|
168
171
|
onClick: () => setViewYear((y) => y + 1),
|
|
169
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "
|
|
172
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "ui-month-picker-icon", "aria-hidden": "true" })
|
|
170
173
|
}
|
|
171
174
|
)
|
|
172
175
|
] }),
|
|
173
|
-
/* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "
|
|
176
|
+
/* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "ui-month-picker-grid", children: monthLabels.map((label, i) => {
|
|
174
177
|
const selected = value?.getFullYear() === viewYear && value?.getMonth() === i;
|
|
175
178
|
return /* @__PURE__ */ jsx(
|
|
176
179
|
Button,
|
|
@@ -179,7 +182,7 @@ function MonthPicker({
|
|
|
179
182
|
variant: selected ? "default" : "ghost",
|
|
180
183
|
size: "sm",
|
|
181
184
|
"aria-pressed": selected,
|
|
182
|
-
className: "
|
|
185
|
+
className: "ui-month-picker-cell",
|
|
183
186
|
onClick: () => {
|
|
184
187
|
emit(new Date(viewYear, i, 1));
|
|
185
188
|
setOpen(false);
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { ArrowRight, CalendarIcon, ChevronLeft, ChevronRight, X } from "lucide-react";
|
|
5
5
|
import { usePickerLocales, useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { useControlledLatch } from "../../lib/hooks.js";
|
|
7
|
-
import { pickGroupFieldA11y } from "../../lib/field-a11y.js";
|
|
7
|
+
import { pickGroupFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
|
|
8
8
|
import { cn } from "../../lib/utils.js";
|
|
9
9
|
import { Button } from "../general/button.js";
|
|
10
10
|
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
|
|
@@ -40,6 +40,9 @@ function MonthRangePicker({
|
|
|
40
40
|
const groupId = id ?? autoId;
|
|
41
41
|
const fromId = `${groupId}-from`;
|
|
42
42
|
const toId = `${groupId}-to`;
|
|
43
|
+
const identity = useFieldIdentity({ id: groupId, name, "data-field": groupA11y["data-field"] });
|
|
44
|
+
const rangeField = groupA11y["data-field"] ?? identity["data-field"];
|
|
45
|
+
const rangeName = name ?? identity.name;
|
|
43
46
|
const isControlled = useControlledLatch(valueProp !== void 0);
|
|
44
47
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
45
48
|
const value = isControlled ? valueProp : internalValue;
|
|
@@ -99,7 +102,7 @@ function MonthRangePicker({
|
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
};
|
|
102
|
-
const innerInputClass = "
|
|
105
|
+
const innerInputClass = "ui-month-picker-input";
|
|
103
106
|
const resolvedPlaceholder = placeholder ?? t("dataEntry.monthPicker.placeholder") ?? YM_HINT;
|
|
104
107
|
return /* @__PURE__ */ jsx(Popover, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
105
108
|
"div",
|
|
@@ -107,12 +110,13 @@ function MonthRangePicker({
|
|
|
107
110
|
role: "group",
|
|
108
111
|
id: groupId,
|
|
109
112
|
...groupA11y,
|
|
113
|
+
"data-field": rangeField,
|
|
110
114
|
"aria-disabled": disabled ? true : void 0,
|
|
115
|
+
"data-disabled": disabled ? "" : void 0,
|
|
111
116
|
className: cn(
|
|
112
|
-
"ui-control
|
|
117
|
+
"ui-control ui-control-composite-field",
|
|
113
118
|
"focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
|
|
114
119
|
open && "border-ring ring-ring/50 ring-[3px]",
|
|
115
|
-
disabled && "pointer-events-none cursor-not-allowed opacity-50",
|
|
116
120
|
className
|
|
117
121
|
),
|
|
118
122
|
onClick: () => {
|
|
@@ -123,7 +127,8 @@ function MonthRangePicker({
|
|
|
123
127
|
"input",
|
|
124
128
|
{
|
|
125
129
|
id: fromId,
|
|
126
|
-
|
|
130
|
+
"data-field": rangeField ? `${rangeField}_from` : void 0,
|
|
131
|
+
name: rangeName ? `${rangeName}_from` : void 0,
|
|
127
132
|
value: fromText,
|
|
128
133
|
disabled,
|
|
129
134
|
placeholder: resolvedPlaceholder,
|
|
@@ -139,12 +144,13 @@ function MonthRangePicker({
|
|
|
139
144
|
onBlur: () => setFromText(toYmText(value?.from))
|
|
140
145
|
}
|
|
141
146
|
),
|
|
142
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "
|
|
147
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "ui-month-picker-separator-icon", "aria-hidden": "true" }),
|
|
143
148
|
/* @__PURE__ */ jsx(
|
|
144
149
|
"input",
|
|
145
150
|
{
|
|
146
151
|
id: toId,
|
|
147
|
-
|
|
152
|
+
"data-field": rangeField ? `${rangeField}_to` : void 0,
|
|
153
|
+
name: rangeName ? `${rangeName}_to` : void 0,
|
|
148
154
|
value: toText,
|
|
149
155
|
disabled,
|
|
150
156
|
placeholder: resolvedPlaceholder,
|
|
@@ -171,9 +177,10 @@ function MonthRangePicker({
|
|
|
171
177
|
event.stopPropagation();
|
|
172
178
|
clear();
|
|
173
179
|
},
|
|
174
|
-
children: /* @__PURE__ */ jsx(X, { className: "
|
|
180
|
+
children: /* @__PURE__ */ jsx(X, { className: "ui-month-picker-icon", "aria-hidden": "true" })
|
|
175
181
|
}
|
|
176
|
-
) :
|
|
182
|
+
) : null,
|
|
183
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
177
184
|
"button",
|
|
178
185
|
{
|
|
179
186
|
type: "button",
|
|
@@ -181,18 +188,18 @@ function MonthRangePicker({
|
|
|
181
188
|
tabIndex: -1,
|
|
182
189
|
"aria-label": t("dataEntry.monthPicker.openGrid") ?? "Open month grid",
|
|
183
190
|
className: "text-muted-foreground hover:text-foreground shrink-0",
|
|
184
|
-
children: /* @__PURE__ */ jsx(CalendarIcon, { className: "
|
|
191
|
+
children: /* @__PURE__ */ jsx(CalendarIcon, { className: "ui-month-picker-icon", "aria-hidden": "true" })
|
|
185
192
|
}
|
|
186
193
|
) }),
|
|
187
194
|
/* @__PURE__ */ jsxs(
|
|
188
195
|
PopoverContent,
|
|
189
196
|
{
|
|
190
|
-
className: "
|
|
197
|
+
className: "ui-month-picker-panel",
|
|
191
198
|
align: "start",
|
|
192
199
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
193
200
|
onClick: (event) => event.stopPropagation(),
|
|
194
201
|
children: [
|
|
195
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
202
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-month-picker-nav", children: [
|
|
196
203
|
/* @__PURE__ */ jsx(
|
|
197
204
|
Button,
|
|
198
205
|
{
|
|
@@ -201,12 +208,12 @@ function MonthRangePicker({
|
|
|
201
208
|
size: "icon-sm",
|
|
202
209
|
disabled: prevDisabled,
|
|
203
210
|
"aria-label": t("dataEntry.monthPicker.previousYear") ?? "Previous year",
|
|
204
|
-
className: "
|
|
211
|
+
className: "ui-month-picker-nav-button",
|
|
205
212
|
onClick: () => setViewYear((y) => y - 1),
|
|
206
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "
|
|
213
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "ui-month-picker-icon", "aria-hidden": "true" })
|
|
207
214
|
}
|
|
208
215
|
),
|
|
209
|
-
/* @__PURE__ */ jsx("span", { className: "
|
|
216
|
+
/* @__PURE__ */ jsx("span", { className: "ui-month-picker-nav-label", "aria-live": "polite", children: viewYear }),
|
|
210
217
|
/* @__PURE__ */ jsx(
|
|
211
218
|
Button,
|
|
212
219
|
{
|
|
@@ -215,13 +222,13 @@ function MonthRangePicker({
|
|
|
215
222
|
size: "icon-sm",
|
|
216
223
|
disabled: nextDisabled,
|
|
217
224
|
"aria-label": t("dataEntry.monthPicker.nextYear") ?? "Next year",
|
|
218
|
-
className: "
|
|
225
|
+
className: "ui-month-picker-nav-button",
|
|
219
226
|
onClick: () => setViewYear((y) => y + 1),
|
|
220
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "
|
|
227
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "ui-month-picker-icon", "aria-hidden": "true" })
|
|
221
228
|
}
|
|
222
229
|
)
|
|
223
230
|
] }),
|
|
224
|
-
/* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "
|
|
231
|
+
/* @__PURE__ */ jsx("div", { role: "grid", "aria-label": String(viewYear), className: "ui-month-picker-grid", children: monthLabels.map((label, i) => {
|
|
225
232
|
const cell = new Date(viewYear, i, 1);
|
|
226
233
|
const cellIdx = ymIndex(cell);
|
|
227
234
|
const fromIdx = value?.from ? ymIndex(value.from) : void 0;
|
|
@@ -235,7 +242,7 @@ function MonthRangePicker({
|
|
|
235
242
|
variant: isEdge ? "default" : inRange ? "secondary" : "ghost",
|
|
236
243
|
size: "sm",
|
|
237
244
|
"aria-pressed": isEdge,
|
|
238
|
-
className: "
|
|
245
|
+
className: "ui-month-picker-cell",
|
|
239
246
|
onClick: () => pickMonth(cell),
|
|
240
247
|
children: label
|
|
241
248
|
},
|
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
5
5
|
import { Circle } from "lucide-react";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
|
-
import { pickFieldA11y } from "../../lib/field-a11y.js";
|
|
7
|
+
import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
|
|
8
8
|
import { Field } from "./field.js";
|
|
9
9
|
import { choiceGroupClassName } from "./choice-option.js";
|
|
10
10
|
const RadioGroupRoot = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
@@ -23,7 +23,12 @@ const RadioItem = React.forwardRef(({ className, ...props }, ref) => /* @__PURE_
|
|
|
23
23
|
ref,
|
|
24
24
|
"data-slot": "radio-group-item",
|
|
25
25
|
className: cn(
|
|
26
|
-
|
|
26
|
+
// `disabled:cursor-not-allowed disabled:opacity-50` DELETED, not moved (#319):
|
|
27
|
+
// `.ui-radio:disabled, .ui-radio[data-disabled]` in styles/control.css already declares both
|
|
28
|
+
// and reads --disabled-opacity. The utility was layered after components, so it silently
|
|
29
|
+
// outranked that token — a service theme's --disabled-opacity never reached a radio.
|
|
30
|
+
// Byte-identical: --disabled-opacity defaults to 0.5.
|
|
31
|
+
"ui-radio aria-invalid:border-destructive aria-invalid:ring-destructive/20 shrink-0 shadow-xs transition-shadow outline-none",
|
|
27
32
|
className
|
|
28
33
|
),
|
|
29
34
|
...props,
|
|
@@ -53,6 +58,10 @@ function RadioGroupOptions({
|
|
|
53
58
|
}) {
|
|
54
59
|
const reactId = React.useId();
|
|
55
60
|
const groupA11y = pickFieldA11y(ariaProps);
|
|
61
|
+
const identity = useFieldIdentity({ id, name, "data-field": groupA11y["data-field"] });
|
|
62
|
+
const resolvedName = name ?? identity.name;
|
|
63
|
+
const resolvedField = groupA11y["data-field"] ?? identity["data-field"];
|
|
64
|
+
const optionDomId = (optionValue, index) => id ? `${id}-${optionValue}` : `${reactId}-${optionValue}-${index}`;
|
|
56
65
|
if (options && options.length > 0) {
|
|
57
66
|
return /* @__PURE__ */ jsx(
|
|
58
67
|
RadioGroupRoot,
|
|
@@ -61,14 +70,23 @@ function RadioGroupOptions({
|
|
|
61
70
|
defaultValue,
|
|
62
71
|
onValueChange,
|
|
63
72
|
disabled,
|
|
64
|
-
name,
|
|
73
|
+
name: resolvedName,
|
|
65
74
|
id,
|
|
66
75
|
...groupA11y,
|
|
76
|
+
"data-field": resolvedField,
|
|
67
77
|
"data-orientation": orientation,
|
|
68
78
|
className: choiceGroupClassName(orientation, className),
|
|
69
79
|
children: options.map((opt, index) => {
|
|
70
|
-
const
|
|
71
|
-
return /* @__PURE__ */ jsx(Field, { id:
|
|
80
|
+
const optionId = optionDomId(opt.value, index);
|
|
81
|
+
return /* @__PURE__ */ jsx(Field, { id: optionId, label: opt.label, description: opt.description, children: /* @__PURE__ */ jsx(
|
|
82
|
+
RadioItem,
|
|
83
|
+
{
|
|
84
|
+
id: optionId,
|
|
85
|
+
value: opt.value,
|
|
86
|
+
disabled: opt.disabled,
|
|
87
|
+
"data-field": resolvedField
|
|
88
|
+
}
|
|
89
|
+
) }, opt.value);
|
|
72
90
|
})
|
|
73
91
|
}
|
|
74
92
|
);
|
|
@@ -80,9 +98,10 @@ function RadioGroupOptions({
|
|
|
80
98
|
defaultValue,
|
|
81
99
|
onValueChange,
|
|
82
100
|
disabled,
|
|
83
|
-
name,
|
|
101
|
+
name: resolvedName,
|
|
84
102
|
id,
|
|
85
103
|
...groupA11y,
|
|
104
|
+
"data-field": resolvedField,
|
|
86
105
|
"data-orientation": orientation,
|
|
87
106
|
className: choiceGroupClassName(orientation, className),
|
|
88
107
|
children
|
|
@@ -9,4 +9,4 @@ export type { SearchSelectProp, SearchSelectProp as SearchSelectProps, SearchSel
|
|
|
9
9
|
* Custom per-option rendering via `renderOption` (Ant-Design style). Form-submittable via
|
|
10
10
|
* `name`; e2e-testable by the trigger's `data-testid` + each option's `${data-testid}-option-${value}`.
|
|
11
11
|
*/
|
|
12
|
-
export declare function SearchSelect({ value: valueProp, defaultValue, onValueChange, options: staticOptions, loadOptions, renderOption, labelRender, selectedLabel, selectedIcon, placeholder, searchPlaceholder, emptyMessage, loadingMessage, errorMessage, clearLabel, clearable, disabled, readOnly, size, open: openProp, onOpenChange, search: searchProp, onSearchChange, filterOption, renderError, renderLoadMore, name, id, className, "data-testid": dataTestId, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, "aria-errormessage": ariaErrorMessage, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, }: SearchSelectProp): React.JSX.Element;
|
|
12
|
+
export declare function SearchSelect({ value: valueProp, defaultValue, onValueChange, options: staticOptions, loadOptions, renderOption, labelRender, selectedLabel, selectedIcon, placeholder, searchPlaceholder, emptyMessage, loadingMessage, errorMessage, clearLabel, clearable, disabled, readOnly, size, open: openProp, onOpenChange, search: searchProp, onSearchChange, filterOption, renderError, renderLoadMore, name, id, className, "data-testid": dataTestId, "data-field": dataField, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, "aria-errormessage": ariaErrorMessage, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, }: SearchSelectProp): React.JSX.Element;
|
|
@@ -3,7 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { ChevronsUpDown, Loader2, X } from "lucide-react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
|
-
import { useFieldNameFallback } from "../../lib/field-a11y.js";
|
|
6
|
+
import { useFieldIdentity, useFieldNameFallback } from "../../lib/field-a11y.js";
|
|
7
7
|
import { cn } from "../../lib/utils.js";
|
|
8
8
|
import { controlOpenRingClass } from "../../lib/control-styles.js";
|
|
9
9
|
import { Button } from "../general/button.js";
|
|
@@ -42,6 +42,7 @@ function SearchSelect({
|
|
|
42
42
|
id,
|
|
43
43
|
className,
|
|
44
44
|
"data-testid": dataTestId,
|
|
45
|
+
"data-field": dataField,
|
|
45
46
|
"aria-label": ariaLabel,
|
|
46
47
|
"aria-labelledby": ariaLabelledby,
|
|
47
48
|
"aria-describedby": ariaDescribedby,
|
|
@@ -54,6 +55,9 @@ function SearchSelect({
|
|
|
54
55
|
"aria-label": ariaLabel,
|
|
55
56
|
"aria-labelledby": ariaLabelledby
|
|
56
57
|
});
|
|
58
|
+
const identity = useFieldIdentity({ id, name, "data-field": dataField });
|
|
59
|
+
const resolvedName = name ?? identity.name;
|
|
60
|
+
const resolvedField = dataField ?? identity["data-field"];
|
|
57
61
|
const triggerAriaLabel = ariaLabel ?? nameFallback["aria-label"];
|
|
58
62
|
const triggerAriaLabelledby = ariaLabelledby ?? nameFallback["aria-labelledby"];
|
|
59
63
|
const reactId = React.useId();
|
|
@@ -254,17 +258,19 @@ function SearchSelect({
|
|
|
254
258
|
"aria-readonly": readOnly || void 0,
|
|
255
259
|
disabled,
|
|
256
260
|
"data-testid": dataTestId,
|
|
261
|
+
"data-field": resolvedField,
|
|
262
|
+
"data-value": value || void 0,
|
|
257
263
|
className: cn(
|
|
258
264
|
"w-full justify-start font-normal",
|
|
259
265
|
controlOpenRingClass,
|
|
260
266
|
// Reserve trailing room for the single clear-or-chevron overlay rendered below.
|
|
261
|
-
"
|
|
267
|
+
"ui-control-trigger-affixed"
|
|
262
268
|
),
|
|
263
269
|
children: /* @__PURE__ */ jsx(
|
|
264
270
|
"span",
|
|
265
271
|
{
|
|
266
272
|
className: cn(
|
|
267
|
-
"
|
|
273
|
+
"ui-search-select-option-body text-start",
|
|
268
274
|
!value && "text-muted-foreground"
|
|
269
275
|
),
|
|
270
276
|
children: value && labelRender ? labelRender({ value, label: currentLabel, option: selectedOption ?? void 0 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -275,7 +281,7 @@ function SearchSelect({
|
|
|
275
281
|
)
|
|
276
282
|
}
|
|
277
283
|
) }),
|
|
278
|
-
|
|
284
|
+
resolvedName ? /* @__PURE__ */ jsx("input", { type: "hidden", name: resolvedName, value, readOnly: true }) : null,
|
|
279
285
|
/* @__PURE__ */ jsx(
|
|
280
286
|
PopoverContent,
|
|
281
287
|
{
|
|
@@ -284,9 +290,9 @@ function SearchSelect({
|
|
|
284
290
|
align: "start",
|
|
285
291
|
sideOffset: 4,
|
|
286
292
|
collisionPadding: 12,
|
|
287
|
-
className: "
|
|
288
|
-
children: /* @__PURE__ */ jsxs(Command, { value, shouldFilter: false, className: "
|
|
289
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
293
|
+
className: "ui-search-select-panel",
|
|
294
|
+
children: /* @__PURE__ */ jsxs(Command, { value, shouldFilter: false, className: "ui-search-select-command", children: [
|
|
295
|
+
/* @__PURE__ */ jsx("div", { className: "ui-search-select-search", children: /* @__PURE__ */ jsx(
|
|
290
296
|
Input,
|
|
291
297
|
{
|
|
292
298
|
autoFocus: true,
|
|
@@ -298,7 +304,7 @@ function SearchSelect({
|
|
|
298
304
|
onChange: (event) => setQuery(event.target.value),
|
|
299
305
|
onKeyDown,
|
|
300
306
|
placeholder: searchPlaceholder ?? t("dataEntry.searchSelect.search"),
|
|
301
|
-
className: "
|
|
307
|
+
className: "ui-search-select-search-input"
|
|
302
308
|
}
|
|
303
309
|
) }),
|
|
304
310
|
/* @__PURE__ */ jsxs(
|
|
@@ -307,7 +313,7 @@ function SearchSelect({
|
|
|
307
313
|
id: listId,
|
|
308
314
|
role: "listbox",
|
|
309
315
|
"aria-busy": loading,
|
|
310
|
-
className: "
|
|
316
|
+
className: "ui-search-select-list",
|
|
311
317
|
onScroll,
|
|
312
318
|
children: [
|
|
313
319
|
grouped.map((group) => {
|
|
@@ -331,11 +337,11 @@ function SearchSelect({
|
|
|
331
337
|
onMouseEnter: () => !option.disabled && setActiveIndex(index),
|
|
332
338
|
onMouseDown: (event) => event.preventDefault(),
|
|
333
339
|
onClick: () => select(option),
|
|
334
|
-
children: renderOption ? /* @__PURE__ */ jsx("div", { className: "
|
|
340
|
+
children: renderOption ? /* @__PURE__ */ jsx("div", { className: "ui-search-select-option-slot", children: renderOption(option) }) : /* @__PURE__ */ jsxs("div", { className: "ui-search-select-option-body", children: [
|
|
335
341
|
option.icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", "aria-hidden": "true", children: option.icon }) : null,
|
|
336
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
337
|
-
/* @__PURE__ */ jsx("span", { className: "
|
|
338
|
-
option.sublabel ? /* @__PURE__ */ jsx("span", { className: "
|
|
342
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-search-select-option-text", children: [
|
|
343
|
+
/* @__PURE__ */ jsx("span", { className: "ui-search-select-option-label", children: option.label }),
|
|
344
|
+
option.sublabel ? /* @__PURE__ */ jsx("span", { className: "ui-search-select-option-sublabel", children: option.sublabel }) : null
|
|
339
345
|
] })
|
|
340
346
|
] })
|
|
341
347
|
},
|
|
@@ -343,17 +349,10 @@ function SearchSelect({
|
|
|
343
349
|
));
|
|
344
350
|
return group.heading ? /* @__PURE__ */ jsx(CommandGroup, { heading: group.heading, children: rows }, group.heading) : /* @__PURE__ */ jsx(React.Fragment, { children: rows }, "__ungrouped");
|
|
345
351
|
}),
|
|
346
|
-
loading ? /* @__PURE__ */ jsxs(
|
|
347
|
-
"
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
className: "text-muted-foreground flex items-center gap-2 px-2 py-3 text-sm",
|
|
351
|
-
children: [
|
|
352
|
-
/* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": "true" }),
|
|
353
|
-
loadingMessage ?? t("dataEntry.searchSelect.loading")
|
|
354
|
-
]
|
|
355
|
-
}
|
|
356
|
-
) : error ? renderError ? renderError({
|
|
352
|
+
loading ? /* @__PURE__ */ jsxs("div", { role: "status", className: "ui-search-select-status", children: [
|
|
353
|
+
/* @__PURE__ */ jsx(Loader2, { className: "ui-search-select-spinner animate-spin", "aria-hidden": "true" }),
|
|
354
|
+
loadingMessage ?? t("dataEntry.searchSelect.loading")
|
|
355
|
+
] }) : error ? renderError ? renderError({
|
|
357
356
|
message: errorMessage ?? t("dataEntry.searchSelect.error"),
|
|
358
357
|
// Retry always reloads from the first page (a defined, predictable recovery —
|
|
359
358
|
// NOT a resume of a failed page-N append, which would need to re-derive state).
|
|
@@ -364,7 +363,8 @@ function SearchSelect({
|
|
|
364
363
|
role: "option",
|
|
365
364
|
"aria-disabled": "true",
|
|
366
365
|
"aria-selected": false,
|
|
367
|
-
className: "
|
|
366
|
+
className: "ui-search-select-placeholder",
|
|
367
|
+
"data-tone": "destructive",
|
|
368
368
|
children: errorMessage ?? t("dataEntry.searchSelect.error")
|
|
369
369
|
}
|
|
370
370
|
) : loaded.length === 0 ? /* @__PURE__ */ jsx(
|
|
@@ -373,7 +373,7 @@ function SearchSelect({
|
|
|
373
373
|
role: "option",
|
|
374
374
|
"aria-disabled": "true",
|
|
375
375
|
"aria-selected": false,
|
|
376
|
-
className: "
|
|
376
|
+
className: "ui-search-select-placeholder",
|
|
377
377
|
children: emptyMessage ?? t("dataEntry.searchSelect.empty")
|
|
378
378
|
}
|
|
379
379
|
) : null
|
|
@@ -383,7 +383,7 @@ function SearchSelect({
|
|
|
383
383
|
renderLoadMore && hasMore ? /* @__PURE__ */ jsx(
|
|
384
384
|
"div",
|
|
385
385
|
{
|
|
386
|
-
className: "
|
|
386
|
+
className: "ui-search-select-footer",
|
|
387
387
|
onKeyDown: (event) => event.stopPropagation(),
|
|
388
388
|
children: renderLoadMore({
|
|
389
389
|
hasMore,
|
|
@@ -400,17 +400,23 @@ function SearchSelect({
|
|
|
400
400
|
]
|
|
401
401
|
}
|
|
402
402
|
),
|
|
403
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
403
|
+
/* @__PURE__ */ jsx("div", { className: "ui-control-affix", children: showClear ? /* @__PURE__ */ jsx(
|
|
404
404
|
"button",
|
|
405
405
|
{
|
|
406
406
|
type: "button",
|
|
407
407
|
"aria-label": clearLabel ?? t("dataEntry.searchSelect.clear"),
|
|
408
408
|
"data-testid": optionTestId("clear"),
|
|
409
|
-
className: "
|
|
409
|
+
className: "ui-control-affix-action",
|
|
410
410
|
onClick: clear,
|
|
411
|
-
children: /* @__PURE__ */ jsx(X, { className: "
|
|
411
|
+
children: /* @__PURE__ */ jsx(X, { className: "ui-control-affix-icon", "aria-hidden": "true" })
|
|
412
|
+
}
|
|
413
|
+
) : /* @__PURE__ */ jsx(
|
|
414
|
+
ChevronsUpDown,
|
|
415
|
+
{
|
|
416
|
+
className: "ui-control-affix-icon ui-control-affix-indicator",
|
|
417
|
+
"aria-hidden": "true"
|
|
412
418
|
}
|
|
413
|
-
)
|
|
419
|
+
) })
|
|
414
420
|
] });
|
|
415
421
|
}
|
|
416
422
|
export {
|
|
@@ -27,6 +27,14 @@ export declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectP
|
|
|
27
27
|
* descendant CSS is needed to remove it.
|
|
28
28
|
*/
|
|
29
29
|
showIndicator?: boolean;
|
|
30
|
+
/** Stable machine key (gh#337) — normally inherited from `FormField` through the Select. */
|
|
31
|
+
"data-field"?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The selected CODE (gh#337). Normally supplied by `Select` itself — the trigger shows the
|
|
34
|
+
* option's LABEL, and this is the only place the underlying value is readable from the DOM
|
|
35
|
+
* without reaching into Radix's aria-hidden native `<select>`.
|
|
36
|
+
*/
|
|
37
|
+
"data-value"?: string;
|
|
30
38
|
} & React.RefAttributes<HTMLButtonElement>>;
|
|
31
39
|
export declare const SelectScrollUpButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollUpButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
32
40
|
export declare const SelectScrollDownButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollDownButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|