@godxjp/ui 19.5.0 → 20.0.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/card.d.ts +2 -2
- package/dist/components/data-display/collapsible.d.ts +23 -4
- package/dist/components/data-display/collapsible.js +119 -4
- package/dist/components/data-display/index.d.ts +3 -1
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/legend.d.ts +15 -0
- package/dist/components/data-display/legend.js +11 -0
- package/dist/components/data-display/list-row.js +1 -1
- package/dist/components/data-display/popover.d.ts +58 -5
- package/dist/components/data-display/popover.js +206 -27
- package/dist/components/data-display/progress.d.ts +50 -2
- package/dist/components/data-display/progress.js +57 -9
- package/dist/components/data-entry/calendar.d.ts +1 -1
- package/dist/components/data-entry/calendar.js +67 -32
- package/dist/components/data-entry/checkbox.d.ts +19 -6
- package/dist/components/data-entry/checkbox.js +55 -16
- package/dist/components/data-entry/choice-option.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/field.js +0 -1
- package/dist/components/data-entry/form.js +1 -1
- package/dist/components/data-entry/label.d.ts +3 -2
- package/dist/components/data-entry/label.js +23 -10
- package/dist/components/data-entry/month-picker.d.ts +1 -1
- package/dist/components/data-entry/month-range-picker.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +16 -6
- package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
- package/dist/components/data-entry/tree-select.js +29 -12
- package/dist/components/data-entry/tree-utils.d.ts +1 -1
- package/dist/components/data-entry/upload.js +7 -2
- package/dist/components/feedback/dialog.d.ts +105 -38
- package/dist/components/feedback/dialog.js +272 -194
- package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
- package/dist/components/feedback/overlay-close-focus.js +31 -0
- package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
- package/dist/components/feedback/sheet.d.ts +53 -11
- package/dist/components/feedback/sheet.js +150 -81
- package/dist/components/feedback/tooltip.d.ts +51 -7
- package/dist/components/feedback/tooltip.js +107 -25
- package/dist/components/general/button.js +2 -2
- package/dist/components/general/logo.d.ts +17 -0
- package/dist/components/general/logo.js +22 -16
- package/dist/components/general/typography.d.ts +7 -1
- package/dist/components/general/typography.js +20 -5
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +52 -8
- package/dist/components/layout/aspect-ratio.js +0 -1
- package/dist/components/layout/auth-divider.js +0 -1
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +19 -2
- package/dist/components/layout/nav-surface.d.ts +26 -0
- package/dist/components/layout/nav-surface.js +17 -0
- package/dist/components/layout/org-switcher.d.ts +5 -1
- package/dist/components/layout/org-switcher.js +5 -3
- package/dist/components/layout/separator.js +0 -1
- package/dist/components/layout/sidebar.js +4 -1
- package/dist/components/layout/split-pane.d.ts +14 -1
- package/dist/components/layout/split-pane.js +26 -13
- package/dist/components/layout/topbar-item.js +1 -2
- package/dist/components/navigation/app-setting-picker.js +21 -2
- package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
- package/dist/components/navigation/app-setting-toggle.js +96 -0
- package/dist/components/navigation/dropdown-menu.d.ts +199 -18
- package/dist/components/navigation/dropdown-menu.js +344 -117
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/navigation/pagination-utils.d.ts +2 -1
- package/dist/components/navigation/tabs.d.ts +47 -6
- package/dist/components/navigation/tabs.js +152 -90
- package/dist/components/ui/accordion.d.ts +50 -5
- package/dist/components/ui/accordion.js +239 -33
- package/dist/components/ui/aspect-ratio.d.ts +23 -2
- package/dist/components/ui/aspect-ratio.js +15 -13
- package/dist/components/ui/avatar.d.ts +29 -4
- package/dist/components/ui/avatar.js +111 -25
- package/dist/components/ui/hover-card.d.ts +42 -4
- package/dist/components/ui/hover-card.js +183 -27
- package/dist/components/ui/label.js +0 -1
- package/dist/components/ui/segmented.d.ts +3 -3
- package/dist/components/ui/separator.d.ts +8 -2
- package/dist/components/ui/separator.js +17 -9
- package/dist/components/ui/toggle-group.d.ts +50 -5
- package/dist/components/ui/toggle-group.js +79 -20
- package/dist/components/ui/toggle.d.ts +31 -5
- package/dist/components/ui/toggle.js +42 -3
- 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 +1 -1
- package/dist/lib/control-styles.js +1 -1
- package/dist/lib/slot.d.ts +32 -0
- package/dist/lib/slot.js +22 -0
- package/dist/lib/variants.d.ts +22 -3
- package/dist/lib/variants.js +56 -1
- package/dist/props/components/app.prop.d.ts +25 -1
- package/dist/props/components/data-display.prop.d.ts +17 -0
- package/dist/props/components/data-entry.prop.d.ts +32 -4
- package/dist/props/components/general.prop.d.ts +31 -1
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +118 -5
- package/dist/props/components/navigation.prop.d.ts +1 -1
- package/dist/props/registry.d.ts +22 -2
- package/dist/props/registry.js +33 -2
- package/dist/props/vocabulary/content.prop.d.ts +1 -1
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/interaction.prop.d.ts +17 -1
- package/dist/props/vocabulary/layout.prop.d.ts +57 -1
- package/dist/styles/control.css +142 -1
- package/dist/styles/core.css +5 -2
- package/dist/styles/data-display-layout.css +72 -0
- package/dist/styles/focus-ring.css +13 -1
- package/dist/styles/index.css +5 -2
- package/dist/styles/layout.css +106 -0
- package/dist/styles/shell-layout.css +140 -8
- package/dist/styles/text-layout.css +16 -4
- package/dist/tokens/base.css +2 -1
- package/dist/tokens/components/data-display.css +11 -0
- package/dist/tokens/components/shell.css +3 -1
- package/dist/tokens/components/text.css +7 -0
- package/dist/tokens/foundation.css +2 -0
- package/docs/COMPONENTS.md +9 -3
- package/docs/DESIGN-AUTHORITY.md +96 -71
- package/docs/FRAME-COVERAGE-REPORT.md +5 -3
- package/docs/README.md +14 -14
- package/docs/WHAT-BELONGS-HERE.md +179 -0
- package/docs/data-display/legend.tsx +145 -0
- package/docs/data-display/progress.tsx +32 -0
- package/docs/data-entry/segmented.tsx +1 -1
- package/docs/data-entry/select.tsx +2 -2
- package/docs/feedback/sheet.tsx +1 -1
- package/docs/general/typography.tsx +84 -1
- package/docs/layout/app-shell-arrangements.tsx +225 -0
- package/docs/layout/aspect-ratio.tsx +1 -1
- package/docs/navigation/app-setting-picker.tsx +26 -1
- package/docs/navigation/app-setting-toggle.tsx +111 -0
- package/package.json +11 -8
- package/scripts/_agent-setup.mjs +165 -3
- package/scripts/consumer-rule.md +98 -0
- package/scripts/guinea-pig-skill.md +322 -0
- package/scripts/init-guinea-pig.mjs +62 -0
- package/scripts/postinstall.mjs +13 -2
- package/scripts/ui-audit.mjs +115 -14
- /package/dist/tokens/{antd.generated.css → derived.css} +0 -0
|
@@ -1,27 +1,75 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
4
|
+
import { useTranslation } from "../../i18n/use-translation.js";
|
|
5
|
+
import { cn } from "../../lib/utils.js";
|
|
6
|
+
function Progress(props) {
|
|
7
|
+
const { t } = useTranslation();
|
|
8
8
|
const labelId = React.useId();
|
|
9
|
+
const {
|
|
10
|
+
label,
|
|
11
|
+
className,
|
|
12
|
+
segments,
|
|
13
|
+
value,
|
|
14
|
+
tone,
|
|
15
|
+
over = false,
|
|
16
|
+
"aria-label": ariaLabel,
|
|
17
|
+
"aria-labelledby": ariaLabelledBy,
|
|
18
|
+
...domProps
|
|
19
|
+
} = props;
|
|
20
|
+
const named = ariaLabel !== void 0 || ariaLabelledBy !== void 0;
|
|
21
|
+
const labelledBy = ariaLabelledBy ?? (label !== void 0 ? labelId : void 0);
|
|
22
|
+
const caption = label !== void 0 ? /* @__PURE__ */ jsx("div", { className: "ui-progress-label", id: labelId, children: label }) : null;
|
|
23
|
+
if (segments !== void 0) {
|
|
24
|
+
const amounts = segments.map((segment) => Math.max(0, segment.value));
|
|
25
|
+
const total = amounts.reduce((sum, amount) => sum + amount, 0);
|
|
26
|
+
const separator = t("dataDisplay.progress.breakdownSeparator");
|
|
27
|
+
const spoken = segments.map((segment, index) => `${segment.label} ${amounts[index]}`).join(separator);
|
|
28
|
+
return /* @__PURE__ */ jsxs(
|
|
29
|
+
"div",
|
|
30
|
+
{
|
|
31
|
+
className: cn("ui-progress", className),
|
|
32
|
+
"data-breakdown": "",
|
|
33
|
+
role: "img",
|
|
34
|
+
"aria-labelledby": ariaLabelledBy,
|
|
35
|
+
"aria-label": ariaLabelledBy !== void 0 ? void 0 : ariaLabel !== void 0 ? `${ariaLabel}${separator}${spoken}` : label !== void 0 ? `${label}${separator}${spoken}` : spoken,
|
|
36
|
+
...domProps,
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsx("div", { className: "ui-progress-track", children: segments.map((segment, index) => /* @__PURE__ */ jsx(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
className: "ui-progress-segment",
|
|
42
|
+
"data-tone": segment.tone,
|
|
43
|
+
style: { inlineSize: total === 0 ? "0%" : `${amounts[index] / total * 100}%` }
|
|
44
|
+
},
|
|
45
|
+
`${segment.label}-${index}`
|
|
46
|
+
)) }),
|
|
47
|
+
caption
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const meterValue = value ?? 0;
|
|
53
|
+
const isOver = over && meterValue > 100;
|
|
54
|
+
const boundedValue = Math.max(0, Math.min(100, meterValue));
|
|
55
|
+
const effectiveTone = tone ?? (isOver ? "destructive" : "success");
|
|
9
56
|
return /* @__PURE__ */ jsxs(
|
|
10
57
|
"div",
|
|
11
58
|
{
|
|
12
|
-
className: "ui-progress",
|
|
59
|
+
className: cn("ui-progress", className),
|
|
13
60
|
"data-tone": effectiveTone,
|
|
14
61
|
"data-over": isOver ? "" : void 0,
|
|
15
62
|
role: "progressbar",
|
|
16
63
|
"aria-valuenow": boundedValue,
|
|
17
64
|
"aria-valuemin": 0,
|
|
18
65
|
"aria-valuemax": 100,
|
|
19
|
-
"aria-valuetext": `${isOver ? Math.round(
|
|
20
|
-
"aria-labelledby":
|
|
21
|
-
"aria-label":
|
|
66
|
+
"aria-valuetext": `${isOver ? Math.round(meterValue) : boundedValue}%`,
|
|
67
|
+
"aria-labelledby": labelledBy,
|
|
68
|
+
"aria-label": labelledBy !== void 0 ? void 0 : named ? ariaLabel : t("dataDisplay.progress.ariaLabel"),
|
|
69
|
+
...domProps,
|
|
22
70
|
children: [
|
|
23
71
|
/* @__PURE__ */ jsx("div", { className: "ui-progress-track", children: /* @__PURE__ */ jsx("div", { className: "ui-progress-bar", style: { width: `${boundedValue}%` } }) }),
|
|
24
|
-
|
|
72
|
+
caption
|
|
25
73
|
]
|
|
26
74
|
}
|
|
27
75
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { CalendarProp } from "../../props/components/data-entry.prop.js";
|
|
3
3
|
export type { CalendarProp, CalendarProp as CalendarProps, CalendarFooterProp, } from "../../props/components/data-entry.prop.js";
|
|
4
|
-
export declare function Calendar({ className, classNames, showOutsideDays, "aria-label": ariaLabel, labels, showToday, showClose, onClose, footer, month: monthProp, onMonthChange, ...props }: CalendarProp): React.JSX.Element;
|
|
4
|
+
export declare function Calendar({ className, classNames, showOutsideDays, "aria-label": ariaLabel, labels, showToday, showClose, onClose, footer, width, bordered, month: monthProp, onMonthChange, ...props }: CalendarProp): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
4
|
+
import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from "lucide-react";
|
|
5
5
|
import { DayPicker, dateMatchModifiers } from "react-day-picker";
|
|
6
6
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
7
7
|
import { cn } from "../../lib/utils.js";
|
|
@@ -21,6 +21,8 @@ function Calendar({
|
|
|
21
21
|
showClose = false,
|
|
22
22
|
onClose,
|
|
23
23
|
footer,
|
|
24
|
+
width = "auto",
|
|
25
|
+
bordered = false,
|
|
24
26
|
month: monthProp,
|
|
25
27
|
onMonthChange,
|
|
26
28
|
...props
|
|
@@ -78,6 +80,8 @@ function Calendar({
|
|
|
78
80
|
labelNav: labels?.labelNav ?? (() => `${ariaLabel ?? "Calendar"} navigation`)
|
|
79
81
|
},
|
|
80
82
|
className: cn("ui-calendar", className),
|
|
83
|
+
"data-width": width === "full" ? "full" : void 0,
|
|
84
|
+
"data-bordered": bordered ? "true" : void 0,
|
|
81
85
|
classNames: {
|
|
82
86
|
months: cn("ui-calendar-months", classNames?.months),
|
|
83
87
|
month: cn("ui-calendar-month", classNames?.month),
|
|
@@ -100,51 +104,82 @@ function Calendar({
|
|
|
100
104
|
classNames?.button_next
|
|
101
105
|
),
|
|
102
106
|
month_grid: cn("ui-calendar-grid", classNames?.month_grid),
|
|
103
|
-
|
|
107
|
+
/*
|
|
108
|
+
* The weekday row needs a DS class of its own. It only ever carried `flex`, so any rule
|
|
109
|
+
* written for it — the ruled grid's end edge, the header tint — matched nothing and failed
|
|
110
|
+
* silently while looking correct in the stylesheet.
|
|
111
|
+
*/
|
|
112
|
+
weekdays: cn("ui-calendar-weekdays flex", classNames?.weekdays),
|
|
113
|
+
/*
|
|
114
|
+
* Named for the same reason the weekday row is: a slot with no DS class cannot be styled
|
|
115
|
+
* from this package, and a rule written for one fails silently. These two only render with
|
|
116
|
+
* `showWeekNumber`, which is exactly why the gap went unnoticed.
|
|
117
|
+
*/
|
|
118
|
+
week_number: cn("ui-calendar-week-number", classNames?.week_number),
|
|
119
|
+
week_number_header: cn("ui-calendar-week-number-header", classNames?.week_number_header),
|
|
104
120
|
weekday: cn("ui-calendar-weekday", classNames?.weekday),
|
|
105
121
|
week: cn("ui-calendar-week", classNames?.week),
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
122
|
+
/*
|
|
123
|
+
* ONE ELEMENT OWNS THE DAY SURFACE, and it is the `<button>`.
|
|
124
|
+
*
|
|
125
|
+
* It used to be split: hover painted the button, selection painted the `<td>`. Measured on
|
|
126
|
+
* a real page — the selected day came out a SHARP blue square (td, border-radius 0) while
|
|
127
|
+
* hover was a rounded grey (button, 3.71px). Same for `outside`: `text-muted-foreground`
|
|
128
|
+
* sat on the td while the ghost button set its own colour underneath, so days from the
|
|
129
|
+
* neighbouring month were not greyed at all. Two symptoms, one cause.
|
|
130
|
+
*
|
|
131
|
+
* So the `td` keeps ONLY grid duties — column width, the row it sits in — and every state
|
|
132
|
+
* (hover, selected, today, outside, disabled, range) is expressed on the button, where the
|
|
133
|
+
* radius and the padding already live. Adding a state later means adding it in one place;
|
|
134
|
+
* that was the part that kept going wrong.
|
|
135
|
+
*/
|
|
136
|
+
day: cn("ui-calendar-day", classNames?.day),
|
|
112
137
|
day_button: cn(
|
|
113
138
|
buttonVariants({ variant: "ghost" }),
|
|
114
139
|
"ui-calendar-day-button",
|
|
115
140
|
classNames?.day_button
|
|
116
141
|
),
|
|
117
|
-
range_start: cn("day-range-start
|
|
118
|
-
range_end: cn("day-range-end
|
|
119
|
-
range_middle: cn(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
),
|
|
123
|
-
selected: cn(
|
|
124
|
-
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
|
|
125
|
-
// The day is a ghost <button> that sets its OWN (dark --foreground) text colour, which
|
|
126
|
-
// overrides the cell's text-primary-foreground and fails contrast on the blue fill
|
|
127
|
-
// (axe color-contrast). Force the button label to primary-foreground through hover/focus.
|
|
128
|
-
"[&>button]:text-primary-foreground [&>button:hover]:bg-primary [&>button:hover]:text-primary-foreground [&>button:focus]:text-primary-foreground",
|
|
129
|
-
classNames?.selected
|
|
130
|
-
),
|
|
131
|
-
today: cn("bg-accent text-accent-foreground", classNames?.today),
|
|
132
|
-
outside: cn(
|
|
133
|
-
"day-outside text-muted-foreground aria-selected:text-muted-foreground",
|
|
134
|
-
classNames?.outside
|
|
135
|
-
),
|
|
142
|
+
range_start: cn("day-range-start", classNames?.range_start),
|
|
143
|
+
range_end: cn("day-range-end", classNames?.range_end),
|
|
144
|
+
range_middle: cn("day-range-middle", classNames?.range_middle),
|
|
145
|
+
selected: cn("day-selected", classNames?.selected),
|
|
146
|
+
today: cn("day-today", classNames?.today),
|
|
147
|
+
outside: cn("day-outside", classNames?.outside),
|
|
136
148
|
disabled: cn("ui-calendar-day-disabled", classNames?.disabled),
|
|
137
149
|
hidden: cn("invisible", classNames?.hidden),
|
|
138
|
-
footer: cn("ui-calendar-footer", classNames?.footer)
|
|
139
|
-
|
|
150
|
+
footer: cn("ui-calendar-footer", classNames?.footer)
|
|
151
|
+
/*
|
|
152
|
+
* NO `...classNames` here. Spreading the consumer's object last REPLACED each slot that it
|
|
153
|
+
* names, so `classNames={{ day: "my-class" }}` dropped `.ui-calendar-day` — and with it
|
|
154
|
+
* every state selector, because they all key on that class. The slots above already merge
|
|
155
|
+
* the consumer's value through `cn()`; this line only ever un-merged them.
|
|
156
|
+
*
|
|
157
|
+
* A slot this component does not name still passes straight through: react-day-picker
|
|
158
|
+
* reads its own defaults for anything absent from this object.
|
|
159
|
+
*/
|
|
140
160
|
},
|
|
161
|
+
...props,
|
|
141
162
|
components: {
|
|
163
|
+
/*
|
|
164
|
+
* FOUR orientations, not two. `Nav` asks for left/right, but `Dropdown` asks for "down" —
|
|
165
|
+
* and mapping everything that is not "left" to ChevronRight pointed the month and year
|
|
166
|
+
* selects sideways. react-day-picker's own Chevron handles all four; ours has to as well.
|
|
167
|
+
*/
|
|
142
168
|
Chevron: ({ orientation, className: chevronClassName }) => {
|
|
143
|
-
const Icon = orientation === "left" ? ChevronLeft : ChevronRight;
|
|
169
|
+
const Icon = orientation === "left" ? ChevronLeft : orientation === "up" ? ChevronUp : orientation === "down" ? ChevronDown : ChevronRight;
|
|
144
170
|
return /* @__PURE__ */ jsx(Icon, { className: cn("ui-calendar-chevron", chevronClassName), "aria-hidden": "true" });
|
|
145
|
-
}
|
|
171
|
+
},
|
|
172
|
+
/*
|
|
173
|
+
* A consumer's `components` MERGE with ours; they must not replace the object.
|
|
174
|
+
*
|
|
175
|
+
* `{...props}` used to sit after this block, so `components={{ DayButton: Custom }}` — the
|
|
176
|
+
* documented way to put a marker on a day — silently deleted our `Chevron` too. Measured
|
|
177
|
+
* on a live page: the nav arrows came back as react-day-picker's own polygon chevron,
|
|
178
|
+
* which needs a `fill` and therefore picked up `--rdp-accent-color: blue`. The consumer
|
|
179
|
+
* replaced one component and lost an unrelated one, with no error.
|
|
180
|
+
*/
|
|
181
|
+
...props.components
|
|
146
182
|
},
|
|
147
|
-
...props,
|
|
148
183
|
...rangeDefaults
|
|
149
184
|
}
|
|
150
185
|
);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
3
2
|
import { CheckboxGroup } from "./checkbox-group.js";
|
|
4
3
|
/**
|
|
5
4
|
* Decorative checkbox glyph — a non-interactive `<span>`, NOT the real {@link Checkbox} (which is a
|
|
6
|
-
* `<
|
|
7
|
-
* be invalid HTML (Cascader's option rows). Shares
|
|
8
|
-
* drift apart from the real control's rendering again.
|
|
5
|
+
* real `<input>` wrapped by react-aria-components). Used where the row itself is the interactive
|
|
6
|
+
* element and a nested control would be invalid HTML (Cascader's option rows). Shares
|
|
7
|
+
* {@link CheckboxGlyph} so "partial" can never drift apart from the real control's rendering again.
|
|
9
8
|
*/
|
|
10
9
|
export declare function CheckboxVisual({ checked, indeterminate, disabled, className, }: {
|
|
11
10
|
checked: boolean;
|
|
@@ -13,7 +12,21 @@ export declare function CheckboxVisual({ checked, indeterminate, disabled, class
|
|
|
13
12
|
disabled?: boolean;
|
|
14
13
|
className?: string;
|
|
15
14
|
}): React.JSX.Element;
|
|
16
|
-
/**
|
|
17
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The PUBLIC prop shape, unchanged from the @radix-ui/react-checkbox era: `checked` /
|
|
17
|
+
* `defaultChecked` accept the tri-state `"indeterminate"`, `onCheckedChange` reports it back, and
|
|
18
|
+
* `disabled` / `required` keep their HTML spelling. react-aria-components spells the same four
|
|
19
|
+
* `isSelected` + `isIndeterminate` / `defaultSelected` / `onChange` / `isDisabled` / `isRequired`;
|
|
20
|
+
* that translation happens INSIDE this component and none of those names reach a consumer.
|
|
21
|
+
*/
|
|
22
|
+
interface CheckboxRootProps extends Omit<React.ComponentPropsWithoutRef<"button">, "checked" | "defaultChecked" | "onChange"> {
|
|
23
|
+
checked?: boolean | "indeterminate";
|
|
24
|
+
defaultChecked?: boolean | "indeterminate";
|
|
25
|
+
onCheckedChange?: (checked: boolean | "indeterminate") => void;
|
|
26
|
+
required?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Checkbox — dùng standalone hoặc `Checkbox.Group` với `options` (theo quy ước phổ biến). */
|
|
29
|
+
export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxRootProps & React.RefAttributes<HTMLLabelElement>> & {
|
|
18
30
|
Group: typeof CheckboxGroup;
|
|
19
31
|
};
|
|
32
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import
|
|
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
7
|
import { useFieldIdentity } from "../../lib/field-a11y.js";
|
|
@@ -28,44 +28,83 @@ function CheckboxVisual({
|
|
|
28
28
|
}
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
|
-
const CheckboxRoot = React.forwardRef((
|
|
31
|
+
const CheckboxRoot = React.forwardRef((props, ref) => {
|
|
32
|
+
const {
|
|
33
|
+
className,
|
|
34
|
+
checked,
|
|
35
|
+
defaultChecked,
|
|
36
|
+
onCheckedChange,
|
|
37
|
+
disabled,
|
|
38
|
+
required,
|
|
39
|
+
value,
|
|
40
|
+
tabIndex,
|
|
41
|
+
"data-field": ownField,
|
|
42
|
+
...rest
|
|
43
|
+
} = props;
|
|
32
44
|
const identity = useFieldIdentity({
|
|
33
45
|
id: props.id,
|
|
34
46
|
name: props.name,
|
|
35
|
-
"data-field":
|
|
47
|
+
"data-field": ownField
|
|
36
48
|
});
|
|
49
|
+
const fieldKey = ownField ?? identity["data-field"];
|
|
50
|
+
const inputRef = React.useRef(null);
|
|
51
|
+
React.useLayoutEffect(() => {
|
|
52
|
+
const input = inputRef.current;
|
|
53
|
+
if (!input) return;
|
|
54
|
+
if (fieldKey === void 0) {
|
|
55
|
+
input.removeAttribute("data-field");
|
|
56
|
+
} else {
|
|
57
|
+
input.setAttribute("data-field", fieldKey);
|
|
58
|
+
}
|
|
59
|
+
}, [fieldKey]);
|
|
37
60
|
const [uncontrolled, setUncontrolled] = React.useState(
|
|
38
|
-
|
|
61
|
+
defaultChecked ?? false
|
|
39
62
|
);
|
|
40
|
-
const state =
|
|
63
|
+
const state = checked ?? uncontrolled;
|
|
64
|
+
const invalid = props["aria-invalid"];
|
|
41
65
|
return /* @__PURE__ */ jsx(
|
|
42
|
-
|
|
66
|
+
AriaCheckbox,
|
|
43
67
|
{
|
|
68
|
+
...rest,
|
|
44
69
|
ref,
|
|
70
|
+
inputRef,
|
|
45
71
|
"data-slot": "checkbox",
|
|
72
|
+
"data-state": state === "indeterminate" ? "indeterminate" : state ? "checked" : "unchecked",
|
|
73
|
+
name: identity.name ?? props.name,
|
|
74
|
+
value,
|
|
75
|
+
isSelected: state === true,
|
|
76
|
+
isIndeterminate: state === "indeterminate",
|
|
77
|
+
isDisabled: disabled,
|
|
78
|
+
isRequired: required,
|
|
79
|
+
isInvalid: invalid !== void 0 && invalid !== false && invalid !== "false",
|
|
80
|
+
excludeFromTabOrder: tabIndex !== void 0 && tabIndex < 0,
|
|
46
81
|
className: cn(
|
|
47
82
|
// `disabled:cursor-not-allowed disabled:opacity-50` and the checked fill are DELETED, not
|
|
48
83
|
// moved: `.ui-checkbox:disabled, .ui-checkbox[data-disabled]` and
|
|
49
84
|
// `.ui-checkbox[data-state="checked"]` in styles/control.css already declare both, reading
|
|
50
85
|
// --disabled-opacity and --checkbox-checked-background. Utilities are layered AFTER
|
|
51
86
|
// components in Tailwind v4, so these literals were silently OUTRANKING those knobs — a
|
|
52
|
-
// service overriding --checkbox-checked-background got no fill change at all.
|
|
53
|
-
// `data-
|
|
54
|
-
// both knobs default to exactly the values these utilities hard-coded.
|
|
55
|
-
|
|
87
|
+
// service overriding --checkbox-checked-background got no fill change at all. `data-state`
|
|
88
|
+
// above and react-aria's own `data-disabled` keep those CSS rules matching. Rendering is
|
|
89
|
+
// unchanged: both knobs default to exactly the values these utilities hard-coded.
|
|
90
|
+
//
|
|
91
|
+
// `inline-flex items-center justify-center` is NEW and load-bearing: the root used to be a
|
|
92
|
+
// `<button>` (inline-block, so `.ui-checkbox`'s width/height applied and the glyph centred
|
|
93
|
+
// itself). react-aria's root is a `<label>`, which is `display:inline` — without this the
|
|
94
|
+
// 16px box collapses to nothing. CheckboxVisual already carries the same three for the
|
|
95
|
+
// same reason.
|
|
96
|
+
"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 outline-none",
|
|
56
97
|
className
|
|
57
98
|
),
|
|
58
|
-
|
|
59
|
-
...identity,
|
|
60
|
-
onCheckedChange: (next) => {
|
|
99
|
+
onChange: (next) => {
|
|
61
100
|
setUncontrolled(next);
|
|
62
|
-
|
|
101
|
+
onCheckedChange?.(next);
|
|
63
102
|
},
|
|
64
|
-
children: /* @__PURE__ */ jsx(
|
|
103
|
+
children: /* @__PURE__ */ jsx("span", { "data-slot": "checkbox-indicator", className: "ui-choice-indicator", children: state ? /* @__PURE__ */ jsx(CheckboxGlyph, { state }) : null })
|
|
65
104
|
}
|
|
66
105
|
);
|
|
67
106
|
});
|
|
68
|
-
CheckboxRoot.displayName =
|
|
107
|
+
CheckboxRoot.displayName = "Checkbox";
|
|
69
108
|
const Checkbox = Object.assign(CheckboxRoot, {
|
|
70
109
|
Group: CheckboxGroup
|
|
71
110
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as React from "react";
|
|
2
|
-
/** Shared option shape —
|
|
2
|
+
/** Shared option shape — the conventional `CheckboxOptionType` / `Radio` options entry. */
|
|
3
3
|
export type ChoiceOption = {
|
|
4
4
|
label: React.ReactNode;
|
|
5
5
|
value: string;
|
|
@@ -3,7 +3,7 @@ import type { DateRangePickerProp } from "../../props/components/data-entry.prop
|
|
|
3
3
|
export type { DateRangePickerProp, DateRangePickerProp as DateRangePickerProps, } from "../../props/components/data-entry.prop.js";
|
|
4
4
|
/**
|
|
5
5
|
* DateRangePicker — WAI-ARIA date-range combobox rendered as ONE input-styled control
|
|
6
|
-
* (
|
|
6
|
+
* (the established RangePicker convention): `[ from → to ✕ 📅 ]`. The two inner fields stay
|
|
7
7
|
* real, typeable ISO `yyyy-MM-dd` inputs (form-submittable via `${name}_from` /
|
|
8
8
|
* `${name}_to`, screen-reader friendly, e2e-testable by filling either input); the
|
|
9
9
|
* range calendar is the visual affordance.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Slot } from "
|
|
3
|
+
import { Slot } from "../../lib/slot.js";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
6
|
import { FormErrorsProvider } from "./form-errors.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
3
2
|
import { type VariantProps } from "class-variance-authority";
|
|
4
|
-
export declare const Label: React.ForwardRefExoticComponent<Omit<
|
|
3
|
+
export declare const Label: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, "ref"> & VariantProps<(props?: import("class-variance-authority/types").ClassProp | undefined) => string> & {
|
|
4
|
+
asChild?: boolean;
|
|
5
|
+
} & React.RefAttributes<HTMLLabelElement>>;
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
"use client";
|
|
2
1
|
import { jsx } from "react/jsx-runtime";
|
|
3
2
|
import * as React from "react";
|
|
4
|
-
import
|
|
3
|
+
import { Label as AriaLabel } from "react-aria-components";
|
|
5
4
|
import { cva } from "class-variance-authority";
|
|
6
5
|
import { cn } from "../../lib/utils.js";
|
|
6
|
+
import { Slot } from "../../lib/slot.js";
|
|
7
7
|
const labelVariants = cva(
|
|
8
8
|
"font-medium leading-[var(--control-label-line-height)] peer-disabled:cursor-not-allowed peer-disabled:opacity-[var(--control-label-disabled-alpha)]"
|
|
9
9
|
);
|
|
10
|
-
const Label = React.forwardRef(({ className, ...props }, ref) =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const Label = React.forwardRef(({ className, onMouseDown, asChild = false, ...props }, ref) => {
|
|
11
|
+
const shared = {
|
|
12
|
+
// `...props` đứng TRƯỚC, không phải sau: giữ đúng thứ tự thuộc tính mà
|
|
13
|
+
// @radix-ui/react-label phát ra (`for` → `data-slot` → `class`), nên phép so
|
|
14
|
+
// `outerHTML` trong __tests__/label-checkbox-rac.test.tsx khớp từng ký tự.
|
|
15
|
+
// `className` / `onMouseDown` / `asChild` đã được tách ra nên không bị ghi đè.
|
|
16
|
+
...props,
|
|
14
17
|
"data-slot": "label",
|
|
15
18
|
className: cn(
|
|
16
19
|
// A label inside a disabled group reads at the SYSTEM disabled alpha (--disabled-opacity),
|
|
@@ -19,10 +22,20 @@ const Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
19
22
|
labelVariants(),
|
|
20
23
|
className
|
|
21
24
|
),
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
// Carried over BY HAND from @radix-ui/react-label, which react-aria-components' Label does not
|
|
26
|
+
// do: without it a double click on the label text selects the surrounding paragraph instead of
|
|
27
|
+
// just toggling the control twice. A press that started ON the control is left alone — cancelling
|
|
28
|
+
// that one would swallow the control's own activation.
|
|
29
|
+
onMouseDown: (event) => {
|
|
30
|
+
const target = event.target;
|
|
31
|
+
if (target.closest("button, input, select")) return;
|
|
32
|
+
onMouseDown?.(event);
|
|
33
|
+
if (!event.defaultPrevented && event.detail > 1) event.preventDefault();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return asChild ? /* @__PURE__ */ jsx(Slot, { ref, ...shared }) : /* @__PURE__ */ jsx(AriaLabel, { ref, ...shared });
|
|
37
|
+
});
|
|
38
|
+
Label.displayName = "Label";
|
|
26
39
|
export {
|
|
27
40
|
Label
|
|
28
41
|
};
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import type { MonthPickerProp } from "../../props/components/data-entry.prop.js";
|
|
3
3
|
export type { MonthPickerProp, MonthPickerProp as MonthPickerProps, } from "../../props/components/data-entry.prop.js";
|
|
4
4
|
/**
|
|
5
|
-
* MonthPicker — year/month (`yyyy/MM`) input with
|
|
5
|
+
* MonthPicker — year/month (`yyyy/MM`) input with a 12-cell month grid
|
|
6
6
|
* popover: a year header with chevrons over a 3×4 grid of the twelve months.
|
|
7
7
|
* The field stays a real, typeable input (form-submittable via `name` as
|
|
8
8
|
* `yyyy-MM`); the grid is the visual affordance.
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import type { MonthRangePickerProp } from "../../props/components/data-entry.prop.js";
|
|
3
3
|
export type { MonthRangePickerProp, MonthRangePickerProp as MonthRangePickerProps, } from "../../props/components/data-entry.prop.js";
|
|
4
4
|
/**
|
|
5
|
-
* MonthRangePicker — year/month (`yyyy/MM`) RANGE rendered as ONE input-styled control (
|
|
5
|
+
* MonthRangePicker — year/month (`yyyy/MM`) RANGE rendered as ONE input-styled control (the
|
|
6
6
|
* RangePicker convention): `[ from → to ✕ 📅 ]`. The two inner fields stay real, typeable
|
|
7
7
|
* `yyyy/MM` inputs (form-submittable via `${name}_from` / `${name}_to`); the Ant-style month grid
|
|
8
8
|
* is the visual affordance.
|
|
@@ -42,7 +42,7 @@ function TransferPanel({
|
|
|
42
42
|
};
|
|
43
43
|
return /* @__PURE__ */ jsxs("div", { className: "ui-transfer-pane", children: [
|
|
44
44
|
/* @__PURE__ */ jsxs("div", { className: "ui-transfer-pane-header", children: [
|
|
45
|
-
/* @__PURE__ */ jsxs("
|
|
45
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-transfer-pane-check", children: [
|
|
46
46
|
/* @__PURE__ */ jsx(
|
|
47
47
|
Checkbox,
|
|
48
48
|
{
|
|
@@ -70,7 +70,7 @@ function TransferPanel({
|
|
|
70
70
|
}
|
|
71
71
|
) }),
|
|
72
72
|
/* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("ul", { className: "ui-transfer-list", "aria-labelledby": titleId, children: filtered.length === 0 ? /* @__PURE__ */ jsx("li", { className: "ui-transfer-empty", children: emptyText }) : filtered.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
73
|
-
"
|
|
73
|
+
"div",
|
|
74
74
|
{
|
|
75
75
|
className: cn(
|
|
76
76
|
"ui-transfer-row",
|
|
@@ -81,16 +81,26 @@ function TransferPanel({
|
|
|
81
81
|
/* @__PURE__ */ jsx(
|
|
82
82
|
Checkbox,
|
|
83
83
|
{
|
|
84
|
+
id: `${titleId}-${item.key}-box`,
|
|
84
85
|
checked: selectedKeys.includes(item.key),
|
|
85
86
|
disabled: Boolean(disabled) || Boolean(item.disabled),
|
|
86
87
|
onCheckedChange: (v) => toggleKey(item.key, v === true),
|
|
88
|
+
"aria-labelledby": `${titleId}-${item.key}`,
|
|
87
89
|
className: "ui-transfer-row-check"
|
|
88
90
|
}
|
|
89
91
|
),
|
|
90
|
-
/* @__PURE__ */ jsxs(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
/* @__PURE__ */ jsxs(
|
|
93
|
+
"label",
|
|
94
|
+
{
|
|
95
|
+
className: "ui-transfer-row-body",
|
|
96
|
+
id: `${titleId}-${item.key}`,
|
|
97
|
+
htmlFor: `${titleId}-${item.key}-box`,
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate font-medium", children: item.title }),
|
|
100
|
+
item.description && /* @__PURE__ */ jsx("span", { className: "ui-transfer-row-description text-muted-foreground block truncate", children: item.description })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
)
|
|
94
104
|
]
|
|
95
105
|
}
|
|
96
106
|
) }, item.key)) }) })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** `showCheckedStrategy` equivalents for TreeSelect — the conventional enterprise names. */
|
|
2
2
|
export declare const SHOW_CHILD: "SHOW_CHILD";
|
|
3
3
|
export declare const SHOW_PARENT: "SHOW_PARENT";
|
|
4
4
|
export declare const SHOW_ALL: "SHOW_ALL";
|
|
@@ -259,18 +259,35 @@ function TreeSelectRoot({
|
|
|
259
259
|
children: expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "ui-tree-select-toggle-icon", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "ui-tree-select-toggle-icon", "aria-hidden": "true" })
|
|
260
260
|
}
|
|
261
261
|
),
|
|
262
|
-
checkable ?
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
262
|
+
checkable ? (
|
|
263
|
+
// The row must NOT WRAP the Checkbox in a `<label>`: the Checkbox is itself a
|
|
264
|
+
// `<label>` around a real `<input>`, and a label nested in a label is
|
|
265
|
+
// invalid HTML — the browser resolves neither, so the box loses its
|
|
266
|
+
// accessible name. The node text sits BESIDE the box as a `for=`-associated
|
|
267
|
+
// label instead, which both names it and keeps the text clickable.
|
|
268
|
+
/* @__PURE__ */ jsxs("div", { className: "ui-tree-select-label", children: [
|
|
269
|
+
/* @__PURE__ */ jsx(
|
|
270
|
+
Checkbox,
|
|
271
|
+
{
|
|
272
|
+
id: `${treeId}-${node.value}-box`,
|
|
273
|
+
checked: isSelected,
|
|
274
|
+
tabIndex: -1,
|
|
275
|
+
disabled: Boolean(node.disabled) || Boolean(node.disableCheckbox),
|
|
276
|
+
onCheckedChange: () => toggleSelect(node),
|
|
277
|
+
"aria-labelledby": `${treeId}-${node.value}-label`
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
/* @__PURE__ */ jsx(
|
|
281
|
+
"label",
|
|
282
|
+
{
|
|
283
|
+
className: "truncate",
|
|
284
|
+
id: `${treeId}-${node.value}-label`,
|
|
285
|
+
htmlFor: `${treeId}-${node.value}-box`,
|
|
286
|
+
children: node.label
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
] })
|
|
290
|
+
) : /* @__PURE__ */ jsx(
|
|
274
291
|
"button",
|
|
275
292
|
{
|
|
276
293
|
type: "button",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as React from "react";
|
|
2
|
-
/** Normalized tree node —
|
|
2
|
+
/** Normalized tree node — the conventional `treeData` / Cascader `options` shape. */
|
|
3
3
|
export type TreeOption = {
|
|
4
4
|
value: string;
|
|
5
5
|
label: React.ReactNode;
|