@godxjp/ui 20.2.1 → 22.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/app/app-provider.js +2 -1
- package/dist/components/data-display/badge.d.ts +28 -1
- package/dist/components/data-display/badge.js +2 -0
- package/dist/components/data-display/chat-bubble.d.ts +30 -0
- package/dist/components/data-display/chat-bubble.js +229 -0
- package/dist/components/data-display/data-table.d.ts +18 -2
- package/dist/components/data-display/data-table.js +4 -0
- package/dist/components/data-display/descriptions.js +4 -1
- package/dist/components/data-display/index.d.ts +5 -3
- package/dist/components/data-display/index.js +5 -2
- package/dist/components/data-display/popover.d.ts +19 -1
- package/dist/components/data-display/popover.js +12 -3
- package/dist/components/data-display/progress.d.ts +37 -0
- package/dist/components/data-display/progress.js +57 -5
- package/dist/components/data-display/scroll-area.js +26 -1
- package/dist/components/data-display/tree.d.ts +8 -0
- package/dist/components/data-display/tree.js +426 -0
- package/dist/components/data-entry/chat-composer.d.ts +50 -0
- package/dist/components/data-entry/chat-composer.js +163 -0
- package/dist/components/data-entry/chat-suggestion.d.ts +28 -0
- package/dist/components/data-entry/chat-suggestion.js +285 -0
- package/dist/components/data-entry/date-picker.d.ts +10 -3
- package/dist/components/data-entry/date-picker.js +503 -238
- package/dist/components/data-entry/form-field.d.ts +1 -1
- package/dist/components/data-entry/form-field.js +4 -1
- package/dist/components/data-entry/form.js +10 -1
- package/dist/components/data-entry/index.d.ts +4 -6
- package/dist/components/data-entry/index.js +4 -6
- package/dist/components/data-entry/label.d.ts +14 -1
- package/dist/components/data-entry/label.js +6 -1
- package/dist/components/data-entry/radio.d.ts +27 -6
- package/dist/components/data-entry/radio.js +88 -43
- package/dist/components/data-entry/search-select.js +8 -1
- package/dist/components/data-entry/select.js +2 -0
- package/dist/components/data-entry/switch.d.ts +6 -3
- package/dist/components/data-entry/switch.js +34 -11
- package/dist/components/data-entry/textarea.js +3 -1
- package/dist/components/data-entry/tree-utils.d.ts +10 -48
- package/dist/components/data-entry/tree-utils.js +1 -154
- package/dist/components/layout/mobile-shell.d.ts +1 -1
- package/dist/components/layout/mobile-shell.js +2 -0
- package/dist/components/navigation/menubar.d.ts +10 -3
- package/dist/components/navigation/steps.d.ts +1 -1
- package/dist/components/navigation/steps.js +3 -1
- package/dist/components/navigation/tabs-scroll.d.ts +27 -0
- package/dist/components/navigation/tabs-scroll.js +52 -1
- package/dist/components/navigation/tabs.d.ts +2 -2
- package/dist/components/navigation/tabs.js +88 -23
- package/dist/components/ui/index.d.ts +0 -1
- package/dist/components/ui/index.js +0 -1
- package/dist/components/ui/segmented.d.ts +15 -1
- package/dist/components/ui/segmented.js +13 -6
- package/dist/i18n/messages/en.json +48 -2
- package/dist/i18n/messages/ja.json +46 -2
- package/dist/i18n/messages/vi.json +47 -2
- package/dist/lib/datetime/picker-format.d.ts +18 -0
- package/dist/lib/datetime/picker-format.js +35 -1
- package/dist/lib/field-a11y.d.ts +12 -0
- package/dist/lib/field-a11y.js +14 -1
- package/dist/lib/tree.d.ts +53 -0
- package/dist/lib/tree.js +155 -0
- package/dist/props/components/data-display.prop.d.ts +194 -1
- package/dist/props/components/data-entry.prop.d.ts +307 -99
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +12 -1
- package/dist/props/components/navigation.prop.d.ts +76 -1
- package/dist/props/registry.d.ts +156 -19
- package/dist/props/registry.js +218 -18
- package/dist/props/vocabulary/data.prop.d.ts +12 -0
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/layout.prop.d.ts +11 -0
- package/dist/styles/badge-layout.css +4 -0
- package/dist/styles/card-layout.css +6 -6
- package/dist/styles/control.css +22 -6
- package/dist/styles/data-display-layout.css +385 -68
- package/dist/styles/data-entry-layout.css +64 -0
- package/dist/styles/focus-ring.css +4 -2
- package/dist/styles/navigation-layout.css +25 -0
- package/dist/styles/shell-layout.css +7 -1
- package/dist/styles/table-layout.css +34 -0
- package/dist/tokens/base.css +3 -0
- package/dist/tokens/components/chat-bubble.css +36 -0
- package/dist/tokens/components/chat-composer.css +19 -0
- package/dist/tokens/components/data-display.css +9 -6
- package/dist/tokens/components/descriptions.css +4 -0
- package/dist/tokens/components/navigation.css +6 -0
- package/dist/tokens/components/segmented.css +0 -2
- package/dist/tokens/components/shell.css +3 -0
- package/dist/tokens/components/table.css +3 -0
- package/dist/tokens/components/tree.css +27 -0
- package/dist/tokens/foundation.css +7 -0
- package/docs/COMPONENTS.md +14 -14
- package/docs/CONSUMER-RULES.md +24 -1
- package/docs/FRAME-COVERAGE-LEDGER.md +1 -1
- package/docs/FRAME-COVERAGE-REPORT.md +9 -7
- package/docs/TOKENS.md +65 -0
- package/docs/data-display/badge.tsx +37 -0
- package/docs/data-display/chat-bubble.tsx +397 -0
- package/docs/data-display/data-table/index.tsx +18 -0
- package/docs/data-display/popover.tsx +41 -1
- package/docs/data-display/progress.tsx +28 -0
- package/docs/data-display/timeline.tsx +46 -0
- package/docs/data-display/tree.tsx +394 -0
- package/docs/data-entry/calendar.tsx +2 -2
- package/docs/data-entry/chat-composer.tsx +464 -0
- package/docs/data-entry/chat-suggestion.tsx +301 -0
- package/docs/data-entry/date-picker.tsx +143 -3
- package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
- package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
- package/docs/data-entry/form.tsx +4 -4
- package/docs/data-entry/number-input.tsx +31 -0
- package/docs/data-entry/segmented.tsx +103 -1
- package/docs/layout/mobile-shell.tsx +5 -0
- package/docs/navigation/tabs.tsx +149 -0
- package/docs/roadmap/ai-chat-components.md +207 -0
- package/docs/roadmap/antd-parity.md +155 -0
- package/docs/roadmap/badge-tag-chip-count.md +172 -0
- package/docs/roadmap/list-masonry.md +159 -0
- package/docs/roadmap/parity-audit-data-display-feedback.md +567 -0
- package/docs/roadmap/parity-audit-data-entry.md +357 -0
- package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
- package/docs/roadmap/parity-backlog.md +78 -0
- package/docs/roadmap/tree-components.md +151 -0
- package/docs/showcase/acme-portal.tsx +7 -0
- package/docs/showcase/table-tree-rows.tsx +4 -4
- package/package.json +13 -23
- package/scripts/ui-audit.mjs +17 -4
- package/dist/components/data-display/tree-list.d.ts +0 -13
- package/dist/components/data-display/tree-list.js +0 -26
- package/dist/components/data-entry/date-range-picker.d.ts +0 -11
- package/dist/components/data-entry/date-range-picker.js +0 -349
- package/dist/components/data-entry/month-picker.d.ts +0 -10
- package/dist/components/data-entry/month-picker.js +0 -241
- package/dist/components/data-entry/month-range-picker.d.ts +0 -10
- package/dist/components/data-entry/month-range-picker.js +0 -301
- package/dist/components/ui/date-range-picker.d.ts +0 -1
- package/dist/components/ui/date-range-picker.js +0 -2
- package/docs/data-display/tree-list.tsx +0 -107
- package/docs/data-entry/date-range-picker.tsx +0 -175
- package/docs/data-entry/month-picker.tsx +0 -101
- package/docs/data-entry/month-range-picker.tsx +0 -115
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { FormFieldProp } from "../../props/components/data-entry.prop.js";
|
|
3
3
|
export type { FormFieldProp, FormFieldProp as FormFieldProps, } from "../../props/components/data-entry.prop.js";
|
|
4
|
-
export declare function FormField({ id, name, field, label, required, helper, error: errorProp, validateStatus, hasFeedback, feedback, labelAddon, layout: layoutProp, labelWidth: labelWidthProp, controlWidth: controlWidthProp, colSpan, className, children, staticText, }: FormFieldProp): React.JSX.Element;
|
|
4
|
+
export declare function FormField({ id, name, field, label, required, helper, helperPlacement, error: errorProp, validateStatus, hasFeedback, feedback, labelAddon, layout: layoutProp, labelWidth: labelWidthProp, controlWidth: controlWidthProp, colSpan, className, children, staticText, }: FormFieldProp): React.JSX.Element;
|
|
@@ -19,6 +19,7 @@ function FormField({
|
|
|
19
19
|
label,
|
|
20
20
|
required,
|
|
21
21
|
helper,
|
|
22
|
+
helperPlacement = "after",
|
|
22
23
|
error: errorProp,
|
|
23
24
|
validateStatus,
|
|
24
25
|
hasFeedback,
|
|
@@ -46,6 +47,7 @@ function FormField({
|
|
|
46
47
|
const resolvedId = id ?? autoId;
|
|
47
48
|
const labelId = `${resolvedId}-label`;
|
|
48
49
|
const helperId = helper ? `${resolvedId}-helper` : void 0;
|
|
50
|
+
const helperNode = helper ? /* @__PURE__ */ jsx("p", { id: helperId, className: "text-muted-foreground text-xs", children: helper }) : null;
|
|
49
51
|
const validationStatus = error ? "error" : validateStatus;
|
|
50
52
|
const feedbackId = hasFeedback && validationStatus ? `${resolvedId}-feedback` : void 0;
|
|
51
53
|
const FeedbackIcon = validationStatus === "validating" ? LoaderCircle : validationStatus === "error" ? CircleAlert : validationStatus === "warning" ? TriangleAlert : CheckCircle;
|
|
@@ -151,6 +153,7 @@ function FormField({
|
|
|
151
153
|
labelAddon
|
|
152
154
|
] }),
|
|
153
155
|
/* @__PURE__ */ jsxs("div", { "data-slot": "form-field-control", className: "ui-form-field-control", children: [
|
|
156
|
+
helperPlacement === "before" ? helperNode : null,
|
|
154
157
|
isStatic ? childWithA11y : /* @__PURE__ */ jsx(FieldNameContext.Provider, { value: fieldNameContext, children: /* @__PURE__ */ jsx(FieldIdentityContext.Provider, { value: fieldIdentityContext, children: /* @__PURE__ */ jsx(
|
|
155
158
|
"fieldset",
|
|
156
159
|
{
|
|
@@ -170,7 +173,7 @@ function FormField({
|
|
|
170
173
|
),
|
|
171
174
|
t(`dataEntry.form.${validationStatus}`)
|
|
172
175
|
] }) : null,
|
|
173
|
-
|
|
176
|
+
helperPlacement === "after" ? helperNode : null,
|
|
174
177
|
error ? /* @__PURE__ */ jsx("p", { id: errorId, role: "alert", className: "text-destructive text-xs", children: error }) : null
|
|
175
178
|
] })
|
|
176
179
|
]
|
|
@@ -9,6 +9,15 @@ const FormLayoutContext = React.createContext(null);
|
|
|
9
9
|
function useFormLayout() {
|
|
10
10
|
return React.useContext(FormLayoutContext);
|
|
11
11
|
}
|
|
12
|
+
function asChildContent(children, columns) {
|
|
13
|
+
if (columns == null) return children;
|
|
14
|
+
const child = React.Children.only(children);
|
|
15
|
+
return React.cloneElement(
|
|
16
|
+
child,
|
|
17
|
+
void 0,
|
|
18
|
+
/* @__PURE__ */ jsx(ResponsiveGrid, { columns, children: child.props.children })
|
|
19
|
+
);
|
|
20
|
+
}
|
|
12
21
|
const Form = React.forwardRef(function Form2({
|
|
13
22
|
layout = "vertical",
|
|
14
23
|
disabled,
|
|
@@ -41,7 +50,7 @@ const Form = React.forwardRef(function Form2({
|
|
|
41
50
|
"data-layout": layout,
|
|
42
51
|
className: cn("ui-form", density && `ui-density-${density}`, className),
|
|
43
52
|
...props,
|
|
44
|
-
children
|
|
53
|
+
children: asChildContent(children, columns)
|
|
45
54
|
}
|
|
46
55
|
)
|
|
47
56
|
) });
|
|
@@ -7,6 +7,10 @@ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScro
|
|
|
7
7
|
export { Checkbox } from "./checkbox.js";
|
|
8
8
|
export { CheckboxGroup } from "./checkbox-group.js";
|
|
9
9
|
export { Radio, RadioGroup, RadioItem, RadioGroupRoot } from "./radio.js";
|
|
10
|
+
export { ChatComposer } from "./chat-composer.js";
|
|
11
|
+
export type { ChatComposerProp, ChatComposerProps, ChatComposerSubmitTypeProp, } from "./chat-composer.js";
|
|
12
|
+
export { ChatSuggestion } from "./chat-suggestion.js";
|
|
13
|
+
export type { ChatSuggestionProp, ChatSuggestionProps, ChatSuggestionItemProp, ChatSuggestionRenderProp, } from "./chat-suggestion.js";
|
|
10
14
|
export { Textarea } from "./textarea.js";
|
|
11
15
|
export type { TextareaProps } from "./textarea.js";
|
|
12
16
|
export { Form, useFormLayout, type FormLayoutContextValue } from "./form.js";
|
|
@@ -28,14 +32,8 @@ export { Slider } from "./slider.js";
|
|
|
28
32
|
export type { SliderProps } from "./slider.js";
|
|
29
33
|
export { Calendar } from "./calendar.js";
|
|
30
34
|
export type { CalendarProps } from "./calendar.js";
|
|
31
|
-
export { MonthPicker } from "./month-picker.js";
|
|
32
|
-
export type { MonthPickerProps } from "./month-picker.js";
|
|
33
|
-
export { MonthRangePicker } from "./month-range-picker.js";
|
|
34
|
-
export type { MonthRangePickerProps } from "./month-range-picker.js";
|
|
35
35
|
export { DatePicker } from "./date-picker.js";
|
|
36
36
|
export type { DatePickerProps } from "./date-picker.js";
|
|
37
|
-
export { DateRangePicker } from "./date-range-picker.js";
|
|
38
|
-
export type { DateRangePickerProps } from "./date-range-picker.js";
|
|
39
37
|
export { TimePicker } from "./time-picker.js";
|
|
40
38
|
export type { TimePickerProps } from "./time-picker.js";
|
|
41
39
|
export { ColorPicker } from "./color-picker.js";
|
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
import { Checkbox } from "./checkbox.js";
|
|
17
17
|
import { CheckboxGroup } from "./checkbox-group.js";
|
|
18
18
|
import { Radio, RadioGroup, RadioItem, RadioGroupRoot } from "./radio.js";
|
|
19
|
+
import { ChatComposer } from "./chat-composer.js";
|
|
20
|
+
import { ChatSuggestion } from "./chat-suggestion.js";
|
|
19
21
|
import { Textarea } from "./textarea.js";
|
|
20
22
|
import { Form, useFormLayout } from "./form.js";
|
|
21
23
|
import { FormField } from "./form-field.js";
|
|
@@ -27,10 +29,7 @@ import { Toggle } from "./toggle.js";
|
|
|
27
29
|
import { ToggleGroup, ToggleGroupItem } from "./toggle-group.js";
|
|
28
30
|
import { Slider } from "./slider.js";
|
|
29
31
|
import { Calendar } from "./calendar.js";
|
|
30
|
-
import { MonthPicker } from "./month-picker.js";
|
|
31
|
-
import { MonthRangePicker } from "./month-range-picker.js";
|
|
32
32
|
import { DatePicker } from "./date-picker.js";
|
|
33
|
-
import { DateRangePicker } from "./date-range-picker.js";
|
|
34
33
|
import { TimePicker } from "./time-picker.js";
|
|
35
34
|
import { ColorPicker } from "./color-picker.js";
|
|
36
35
|
import {
|
|
@@ -64,6 +63,8 @@ export {
|
|
|
64
63
|
BranchScopePicker,
|
|
65
64
|
Calendar,
|
|
66
65
|
Cascader,
|
|
66
|
+
ChatComposer,
|
|
67
|
+
ChatSuggestion,
|
|
67
68
|
Checkbox,
|
|
68
69
|
CheckboxGroup,
|
|
69
70
|
ColorPicker,
|
|
@@ -75,7 +76,6 @@ export {
|
|
|
75
76
|
CommandList,
|
|
76
77
|
CommandPalette,
|
|
77
78
|
DatePicker,
|
|
78
|
-
DateRangePicker,
|
|
79
79
|
Field,
|
|
80
80
|
Form,
|
|
81
81
|
FormErrors,
|
|
@@ -87,8 +87,6 @@ export {
|
|
|
87
87
|
InputOTPSeparator,
|
|
88
88
|
InputOTPSlot,
|
|
89
89
|
Label,
|
|
90
|
-
MonthPicker,
|
|
91
|
-
MonthRangePicker,
|
|
92
90
|
NumberInput,
|
|
93
91
|
PasswordInput,
|
|
94
92
|
PasswordStrength,
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
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> & {
|
|
3
|
+
export declare const Label: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, "ref">, "slot"> & VariantProps<(props?: import("class-variance-authority/types").ClassProp | undefined) => string> & {
|
|
4
4
|
asChild?: boolean;
|
|
5
|
+
/**
|
|
6
|
+
* react-aria's slot channel, DEFAULTED OFF.
|
|
7
|
+
*
|
|
8
|
+
* A `Label` rendered inside a react-aria collection (a `RadioGroup`, say) is otherwise
|
|
9
|
+
* claimed by that collection as the GROUP's label: it comes out as a `<span>`, the `htmlFor`
|
|
10
|
+
* this library passed is dropped on the floor, and the control it was meant to name ends up
|
|
11
|
+
* with no accessible name at all — a silent failure, measured on `Radio.Group` the moment
|
|
12
|
+
* that group moved to react-aria. `<Label htmlFor="x">` means one thing in this library and
|
|
13
|
+
* a surrounding primitive does not get to redefine it, so the default is react-aria's own
|
|
14
|
+
* opt-out: "an explicit `null` value indicates that the local props completely override all
|
|
15
|
+
* props received from a parent". Pass a slot name to opt back in.
|
|
16
|
+
*/
|
|
17
|
+
slot?: string | null;
|
|
5
18
|
} & React.RefAttributes<HTMLLabelElement>>;
|
|
@@ -7,8 +7,13 @@ 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, onMouseDown, asChild = false, ...props }, ref) => {
|
|
10
|
+
const Label = React.forwardRef(({ className, onMouseDown, asChild = false, slot = null, ...props }, ref) => {
|
|
11
11
|
const shared = {
|
|
12
|
+
// `string | null`, cast because react-aria's own `LabelProps` extends `LabelHTMLAttributes`
|
|
13
|
+
// and so re-types `slot` as `string | undefined` — narrower than the `SlotProps` contract the
|
|
14
|
+
// rest of the library publishes, and narrower than `useSlottedContext`, which branches on
|
|
15
|
+
// `slot === null` explicitly. React drops a `null` attribute, so nothing reaches the DOM.
|
|
16
|
+
slot,
|
|
12
17
|
// `...props` đứng TRƯỚC, không phải sau: giữ đúng thứ tự thuộc tính mà
|
|
13
18
|
// @radix-ui/react-label phát ra (`for` → `data-slot` → `class`), nên phép so
|
|
14
19
|
// `outerHTML` trong __tests__/label-checkbox-rac.test.tsx khớp từng ký tự.
|
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
3
2
|
import type { RadioGroupProp } from "../../props/components/data-entry.prop.js";
|
|
4
3
|
export type { RadioGroupProp, RadioGroupProp as RadioGroupProps, } from "../../props/components/data-entry.prop.js";
|
|
5
|
-
declare const RadioGroupRoot: React.ForwardRefExoticComponent<Omit<
|
|
6
|
-
|
|
4
|
+
declare const RadioGroupRoot: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "defaultValue" | "dir" | "onChange"> & {
|
|
5
|
+
value?: string;
|
|
6
|
+
defaultValue?: string;
|
|
7
|
+
onValueChange?: (value: string) => void;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
required?: boolean;
|
|
10
|
+
name?: string;
|
|
11
|
+
orientation?: "horizontal" | "vertical";
|
|
12
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
13
|
+
declare const RadioItem: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value"> & {
|
|
14
|
+
value: string;
|
|
15
|
+
} & React.RefAttributes<HTMLLabelElement>>;
|
|
7
16
|
declare function RadioGroupOptions({ value, defaultValue, onValueChange, options, orientation, optionType, buttonStyle, disabled, name, id, className, children, ...ariaProps }: RadioGroupProp): React.JSX.Element;
|
|
8
17
|
/** Single radio — use inside `Radio.Group` / `RadioGroupRoot`, or via `options` API. */
|
|
9
|
-
export declare const Radio: React.ForwardRefExoticComponent<Omit<
|
|
10
|
-
|
|
18
|
+
export declare const Radio: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value"> & {
|
|
19
|
+
value: string;
|
|
20
|
+
} & React.RefAttributes<HTMLLabelElement>> & {
|
|
21
|
+
Root: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "defaultValue" | "dir" | "onChange"> & {
|
|
22
|
+
value?: string;
|
|
23
|
+
defaultValue?: string;
|
|
24
|
+
onValueChange?: (value: string) => void;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
required?: boolean;
|
|
27
|
+
name?: string;
|
|
28
|
+
orientation?: "horizontal" | "vertical";
|
|
29
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
11
30
|
Group: typeof RadioGroupOptions;
|
|
12
|
-
Item: React.ForwardRefExoticComponent<Omit<
|
|
31
|
+
Item: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value"> & {
|
|
32
|
+
value: string;
|
|
33
|
+
} & React.RefAttributes<HTMLLabelElement>>;
|
|
13
34
|
};
|
|
14
35
|
export { RadioGroupRoot, RadioItem, RadioGroupOptions as RadioGroup };
|
|
@@ -1,64 +1,108 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import
|
|
4
|
+
import { Radio as AriaRadio, RadioGroup as AriaRadioGroup } from "react-aria-components";
|
|
5
5
|
import { Circle } from "lucide-react";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
|
-
import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
|
|
7
|
+
import { pickFieldA11y, useFieldIdentity, useMirroredInputAttributes } from "../../lib/field-a11y.js";
|
|
8
8
|
import { Field } from "./field.js";
|
|
9
9
|
import { choiceGroupClassName } from "./choice-option.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
function checkedState(isSelected) {
|
|
11
|
+
return isSelected ? "checked" : "unchecked";
|
|
12
|
+
}
|
|
13
|
+
function useFieldKeyedInput(fieldKey) {
|
|
14
|
+
const inputRef = React.useRef(null);
|
|
15
|
+
useMirroredInputAttributes(inputRef, { "data-field": fieldKey });
|
|
16
|
+
return inputRef;
|
|
17
|
+
}
|
|
18
|
+
const RadioGroupRoot = React.forwardRef(
|
|
19
|
+
({ className, value, defaultValue, onValueChange, disabled, required, orientation, ...props }, ref) => {
|
|
20
|
+
const invalid = props["aria-invalid"];
|
|
21
|
+
const { "aria-label": ariaLabel, ...rest } = props;
|
|
22
|
+
return /* @__PURE__ */ jsx(
|
|
23
|
+
AriaRadioGroup,
|
|
24
|
+
{
|
|
25
|
+
ref,
|
|
26
|
+
"data-slot": "radio-group",
|
|
27
|
+
className: cn("ui-choice-group", className),
|
|
28
|
+
value,
|
|
29
|
+
defaultValue,
|
|
30
|
+
onChange: onValueChange,
|
|
31
|
+
isDisabled: disabled,
|
|
32
|
+
isRequired: required,
|
|
33
|
+
isInvalid: invalid !== void 0 && invalid !== false && invalid !== "false",
|
|
34
|
+
orientation,
|
|
35
|
+
"aria-label": props["aria-labelledby"] ? void 0 : ariaLabel,
|
|
36
|
+
...rest
|
|
37
|
+
}
|
|
38
|
+
);
|
|
17
39
|
}
|
|
18
|
-
)
|
|
19
|
-
RadioGroupRoot.displayName =
|
|
20
|
-
const RadioItem = React.forwardRef(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// `.ui-radio:disabled, .ui-radio[data-disabled]` in styles/control.css already declares both
|
|
27
|
-
// and reads --disabled-opacity. The utility was layered after components, so it silently
|
|
28
|
-
// outranked that token — a service theme's --disabled-opacity never reached a radio.
|
|
29
|
-
// Byte-identical: --disabled-opacity defaults to 0.5.
|
|
30
|
-
"ui-radio aria-invalid:border-destructive shrink-0 shadow-xs transition-shadow outline-none",
|
|
31
|
-
className
|
|
32
|
-
),
|
|
33
|
-
...props,
|
|
34
|
-
children: /* @__PURE__ */ jsx(
|
|
35
|
-
RadioGroupPrimitive.Indicator,
|
|
40
|
+
);
|
|
41
|
+
RadioGroupRoot.displayName = "RadioGroup";
|
|
42
|
+
const RadioItem = React.forwardRef(
|
|
43
|
+
({ className, disabled, value, ...props }, ref) => {
|
|
44
|
+
const { "data-field": fieldKey } = props;
|
|
45
|
+
const inputRef = useFieldKeyedInput(fieldKey);
|
|
46
|
+
return /* @__PURE__ */ jsx(
|
|
47
|
+
AriaRadio,
|
|
36
48
|
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
ref,
|
|
50
|
+
inputRef,
|
|
51
|
+
value,
|
|
52
|
+
isDisabled: disabled,
|
|
53
|
+
"data-slot": "radio-group-item",
|
|
54
|
+
className: cn(
|
|
55
|
+
// `.ui-radio:disabled, .ui-radio[data-disabled]` in styles/control.css already declares both
|
|
56
|
+
// and reads --disabled-opacity. The utility was layered after components, so it silently
|
|
57
|
+
// outranked that token — a service theme's --disabled-opacity never reached a radio.
|
|
58
|
+
// Byte-identical: --disabled-opacity defaults to 0.5.
|
|
59
|
+
//
|
|
60
|
+
// `inline-flex items-center justify-center` is load-bearing, for the reason Checkbox
|
|
61
|
+
// records: react-aria's root is a `<label>`, which is `display:inline`, so without it the
|
|
62
|
+
// 16px dot collapses to nothing. `data-[invalid]` replaces `aria-invalid:` because
|
|
63
|
+
// react-aria puts `aria-invalid` on the `<input>` and `data-invalid` on this box.
|
|
64
|
+
"ui-radio data-[invalid]:border-destructive inline-flex shrink-0 items-center justify-center shadow-xs transition-shadow outline-none",
|
|
65
|
+
className
|
|
66
|
+
),
|
|
67
|
+
...props,
|
|
68
|
+
render: (domProps, state) => /* @__PURE__ */ jsx("label", { ...domProps, "data-state": checkedState(state.isSelected) }),
|
|
69
|
+
children: /* @__PURE__ */ jsx("span", { "data-slot": "radio-group-indicator", className: "ui-choice-indicator", children: /* @__PURE__ */ jsx(Circle, { className: "ui-radio-icon", "aria-hidden": "true" }) })
|
|
40
70
|
}
|
|
41
|
-
)
|
|
71
|
+
);
|
|
42
72
|
}
|
|
43
|
-
)
|
|
44
|
-
RadioItem.displayName =
|
|
73
|
+
);
|
|
74
|
+
RadioItem.displayName = "Radio";
|
|
75
|
+
function RadioBarButton({
|
|
76
|
+
id,
|
|
77
|
+
option,
|
|
78
|
+
resolvedField
|
|
79
|
+
}) {
|
|
80
|
+
const inputRef = useFieldKeyedInput(resolvedField);
|
|
81
|
+
return /* @__PURE__ */ jsx(
|
|
82
|
+
AriaRadio,
|
|
83
|
+
{
|
|
84
|
+
id,
|
|
85
|
+
inputRef,
|
|
86
|
+
value: option.value,
|
|
87
|
+
isDisabled: option.disabled,
|
|
88
|
+
"data-slot": "radio-button",
|
|
89
|
+
className: "ui-radio-button ui-focus-ring",
|
|
90
|
+
render: (domProps, state) => /* @__PURE__ */ jsx("label", { ...domProps, "data-state": checkedState(state.isSelected) }),
|
|
91
|
+
children: /* @__PURE__ */ jsx("span", { className: "ui-radio-button-label", children: option.label })
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
45
95
|
function RadioButtonBar({
|
|
46
96
|
options,
|
|
47
|
-
value,
|
|
48
97
|
optionDomId,
|
|
49
98
|
resolvedField
|
|
50
99
|
}) {
|
|
51
100
|
return /* @__PURE__ */ jsx(Fragment, { children: options.map((opt, index) => /* @__PURE__ */ jsx(
|
|
52
|
-
|
|
101
|
+
RadioBarButton,
|
|
53
102
|
{
|
|
54
103
|
id: optionDomId(opt.value, index),
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"data-slot": "radio-button",
|
|
58
|
-
"data-field": resolvedField,
|
|
59
|
-
"data-state": value === opt.value ? "checked" : "unchecked",
|
|
60
|
-
className: "ui-radio-button ui-focus-ring",
|
|
61
|
-
children: /* @__PURE__ */ jsx("span", { className: "ui-radio-button-label", children: opt.label })
|
|
104
|
+
option: opt,
|
|
105
|
+
resolvedField
|
|
62
106
|
},
|
|
63
107
|
opt.value
|
|
64
108
|
)) });
|
|
@@ -94,6 +138,7 @@ function RadioGroupOptions({
|
|
|
94
138
|
disabled,
|
|
95
139
|
name: resolvedName,
|
|
96
140
|
id,
|
|
141
|
+
orientation,
|
|
97
142
|
...groupA11y,
|
|
98
143
|
"data-field": resolvedField,
|
|
99
144
|
"data-orientation": orientation,
|
|
@@ -108,7 +153,6 @@ function RadioGroupOptions({
|
|
|
108
153
|
RadioButtonBar,
|
|
109
154
|
{
|
|
110
155
|
options,
|
|
111
|
-
value,
|
|
112
156
|
optionDomId,
|
|
113
157
|
resolvedField
|
|
114
158
|
}
|
|
@@ -147,6 +191,7 @@ function RadioGroupOptions({
|
|
|
147
191
|
disabled,
|
|
148
192
|
name: resolvedName,
|
|
149
193
|
id,
|
|
194
|
+
orientation,
|
|
150
195
|
...groupA11y,
|
|
151
196
|
"data-field": resolvedField,
|
|
152
197
|
"data-orientation": orientation,
|
|
@@ -36,6 +36,7 @@ function SearchSelect(props) {
|
|
|
36
36
|
size,
|
|
37
37
|
status,
|
|
38
38
|
variant,
|
|
39
|
+
width = "full",
|
|
39
40
|
loading: loadingProp = false,
|
|
40
41
|
open: openProp,
|
|
41
42
|
defaultOpen = false,
|
|
@@ -334,9 +335,15 @@ function SearchSelect(props) {
|
|
|
334
335
|
"data-field": resolvedField,
|
|
335
336
|
"data-value": (multiple ? values.join(",") : value) || void 0,
|
|
336
337
|
"data-mode": multiple ? "multiple" : void 0,
|
|
338
|
+
"data-width": width,
|
|
337
339
|
className: cn(
|
|
338
340
|
controlSurfaceTriggerClass,
|
|
339
|
-
"
|
|
341
|
+
"justify-start",
|
|
342
|
+
// `bounded` deliberately emits NO width utility — its width is owned by the
|
|
343
|
+
// `[data-width="bounded"]` rule in control.css, and a utility here would win the
|
|
344
|
+
// layer order and make that token dead. Same reasoning as SelectTrigger.
|
|
345
|
+
width === "auto" && "w-auto",
|
|
346
|
+
width === "full" && "w-full",
|
|
340
347
|
// Reserve trailing room for the single clear-or-chevron overlay rendered below.
|
|
341
348
|
"ui-control-trigger-affixed"
|
|
342
349
|
),
|
|
@@ -298,6 +298,7 @@ function DataSelect(props) {
|
|
|
298
298
|
size,
|
|
299
299
|
status,
|
|
300
300
|
variant,
|
|
301
|
+
width,
|
|
301
302
|
loading,
|
|
302
303
|
open,
|
|
303
304
|
defaultOpen,
|
|
@@ -372,6 +373,7 @@ function DataSelect(props) {
|
|
|
372
373
|
size,
|
|
373
374
|
variant,
|
|
374
375
|
status,
|
|
376
|
+
width,
|
|
375
377
|
"aria-busy": loading || void 0,
|
|
376
378
|
className: cn(
|
|
377
379
|
(showClear || loading) && "ui-control-trigger-affixed",
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
3
2
|
export type { SwitchProp, SwitchProp as SwitchProps } from "../../props/components/data-entry.prop.js";
|
|
4
|
-
export declare const Switch: React.ForwardRefExoticComponent<Omit<
|
|
3
|
+
export declare const Switch: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "checked" | "defaultChecked" | "onChange" | "value"> & {
|
|
4
|
+
checked?: boolean;
|
|
5
|
+
defaultChecked?: boolean;
|
|
6
|
+
onCheckedChange?: (checked: boolean) => void;
|
|
7
|
+
required?: boolean;
|
|
5
8
|
size?: "sm" | "md";
|
|
6
9
|
loading?: boolean;
|
|
7
10
|
checkedChildren?: React.ReactNode;
|
|
8
11
|
unCheckedChildren?: React.ReactNode;
|
|
9
|
-
} & React.RefAttributes<
|
|
12
|
+
} & React.RefAttributes<HTMLLabelElement>>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import
|
|
4
|
+
import { Switch as AriaSwitch } from "react-aria-components";
|
|
5
5
|
import { Loader2 } from "lucide-react";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
|
-
import { useFieldIdentity } from "../../lib/field-a11y.js";
|
|
7
|
+
import { useFieldIdentity, useMirroredInputAttributes } from "../../lib/field-a11y.js";
|
|
8
8
|
const Switch = React.forwardRef(
|
|
9
9
|
({
|
|
10
10
|
className,
|
|
@@ -16,6 +16,8 @@ const Switch = React.forwardRef(
|
|
|
16
16
|
loading = false,
|
|
17
17
|
checkedChildren,
|
|
18
18
|
unCheckedChildren,
|
|
19
|
+
disabled,
|
|
20
|
+
required,
|
|
19
21
|
...props
|
|
20
22
|
}, ref) => {
|
|
21
23
|
const identity = useFieldIdentity({
|
|
@@ -34,19 +36,42 @@ const Switch = React.forwardRef(
|
|
|
34
36
|
}
|
|
35
37
|
onCheckedChange?.(next);
|
|
36
38
|
};
|
|
39
|
+
const ownField = props["data-field"];
|
|
40
|
+
const fieldKey = ownField ?? identity["data-field"];
|
|
41
|
+
const invalid = props["aria-invalid"];
|
|
42
|
+
const isInvalid = invalid !== void 0 && invalid !== false && invalid !== "false";
|
|
43
|
+
const inputRef = React.useRef(null);
|
|
44
|
+
useMirroredInputAttributes(inputRef, {
|
|
45
|
+
"aria-invalid": isInvalid ? "true" : void 0,
|
|
46
|
+
"aria-busy": loading ? "true" : void 0,
|
|
47
|
+
"aria-disabled": loading ? "true" : void 0,
|
|
48
|
+
// react-aria's `Switch` Omits `isRequired`, and a switch is never the target of native
|
|
49
|
+
// constraint validation in this library — the form layer owns that. The prop is accepted
|
|
50
|
+
// for shape and announced through `aria-required`.
|
|
51
|
+
"aria-required": required ? "true" : void 0,
|
|
52
|
+
"data-field": fieldKey
|
|
53
|
+
});
|
|
54
|
+
const state = isChecked ? "checked" : "unchecked";
|
|
37
55
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38
56
|
resolvedName ? /* @__PURE__ */ jsx("input", { type: "hidden", name: resolvedName, value: isChecked ? "1" : "0", readOnly: true }) : null,
|
|
39
57
|
/* @__PURE__ */ jsxs(
|
|
40
|
-
|
|
58
|
+
AriaSwitch,
|
|
41
59
|
{
|
|
60
|
+
...props,
|
|
42
61
|
ref,
|
|
62
|
+
inputRef,
|
|
43
63
|
"data-slot": "switch",
|
|
44
64
|
"data-size": size,
|
|
65
|
+
"data-state": state,
|
|
45
66
|
"data-loading": loading ? "true" : void 0,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
isSelected: isChecked,
|
|
68
|
+
isDisabled: disabled,
|
|
69
|
+
onChange: handleCheckedChange,
|
|
70
|
+
onKeyDown: (event) => {
|
|
71
|
+
if (event.key !== "Enter") return;
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
handleCheckedChange(!isChecked);
|
|
74
|
+
},
|
|
50
75
|
className: cn(
|
|
51
76
|
// `.ui-switch:disabled, .ui-switch[data-disabled]` in styles/control.css already
|
|
52
77
|
// declares both and reads --disabled-opacity. The utility was layered after
|
|
@@ -56,11 +81,9 @@ const Switch = React.forwardRef(
|
|
|
56
81
|
"peer ui-switch shadow-xs transition-all outline-none",
|
|
57
82
|
className
|
|
58
83
|
),
|
|
59
|
-
...props,
|
|
60
|
-
"data-field": identity["data-field"] ?? props["data-field"],
|
|
61
84
|
children: [
|
|
62
85
|
checkedChildren != null || unCheckedChildren != null ? /* @__PURE__ */ jsx("span", { "data-slot": "switch-content", className: "ui-switch-content", "aria-hidden": "true", children: isChecked ? checkedChildren : unCheckedChildren }) : null,
|
|
63
|
-
/* @__PURE__ */ jsx(
|
|
86
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "switch-thumb", "data-state": state, className: "ui-switch-thumb", children: loading ? /* @__PURE__ */ jsx(
|
|
64
87
|
Loader2,
|
|
65
88
|
{
|
|
66
89
|
"data-slot": "switch-spinner",
|
|
@@ -74,7 +97,7 @@ const Switch = React.forwardRef(
|
|
|
74
97
|
] });
|
|
75
98
|
}
|
|
76
99
|
);
|
|
77
|
-
Switch.displayName =
|
|
100
|
+
Switch.displayName = "Switch";
|
|
78
101
|
export {
|
|
79
102
|
Switch
|
|
80
103
|
};
|
|
@@ -158,11 +158,13 @@ const Textarea = React.forwardRef(
|
|
|
158
158
|
}
|
|
159
159
|
);
|
|
160
160
|
if (!needsWrapper) return field;
|
|
161
|
+
const placeholderText = typeof props.placeholder === "string" ? props.placeholder : void 0;
|
|
162
|
+
const mirrorText = mirror.length > 0 ? mirror : placeholderText ?? "";
|
|
161
163
|
return /* @__PURE__ */ jsxs(
|
|
162
164
|
"span",
|
|
163
165
|
{
|
|
164
166
|
"data-slot": "textarea-affix-wrapper",
|
|
165
|
-
"data-autogrow-value": growing ?
|
|
167
|
+
"data-autogrow-value": growing ? mirrorText : void 0,
|
|
166
168
|
style: { ...autoGrowVars, ...mirrorInset },
|
|
167
169
|
className: cn(
|
|
168
170
|
"relative w-full",
|
|
@@ -1,48 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
12
|
-
export type TreeFieldNames = {
|
|
13
|
-
label?: string;
|
|
14
|
-
value?: string;
|
|
15
|
-
children?: string;
|
|
16
|
-
};
|
|
17
|
-
export type NormalizedTreeOption = TreeOption & {
|
|
18
|
-
children?: NormalizedTreeOption[];
|
|
19
|
-
};
|
|
20
|
-
type RawTreeNode = Record<string, unknown>;
|
|
21
|
-
export declare function reactNodeText(value: React.ReactNode): string;
|
|
22
|
-
export declare function normalizeTreeOptions(nodes: RawTreeNode[] | undefined, fieldNames?: TreeFieldNames): NormalizedTreeOption[];
|
|
23
|
-
export declare function getNodeByPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
|
|
24
|
-
export declare function getOptionsAtPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
|
|
25
|
-
export declare function formatPathLabels(chain: NormalizedTreeOption[], separator?: string): string;
|
|
26
|
-
export type TreePath = {
|
|
27
|
-
path: string[];
|
|
28
|
-
labels: string[];
|
|
29
|
-
};
|
|
30
|
-
export declare function collectLeafPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
|
|
31
|
-
export declare function collectAllPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
|
|
32
|
-
export declare function pathKey(path: string[]): string;
|
|
33
|
-
export declare function pathsEqual(a: string[], b: string[]): boolean;
|
|
34
|
-
export declare function filterTreeOptions(options: NormalizedTreeOption[], query: string, filter?: (query: string, path: NormalizedTreeOption[]) => boolean): TreePath[];
|
|
35
|
-
export declare function getDescendantValues(node: NormalizedTreeOption): string[];
|
|
36
|
-
export declare function flattenVisibleTree(options: NormalizedTreeOption[], expandedKeys: Set<string>, depth?: number): {
|
|
37
|
-
node: NormalizedTreeOption;
|
|
38
|
-
depth: number;
|
|
39
|
-
hasChildren: boolean;
|
|
40
|
-
}[];
|
|
41
|
-
export declare function filterVisibleTree(options: NormalizedTreeOption[], query: string): {
|
|
42
|
-
node: NormalizedTreeOption;
|
|
43
|
-
depth: number;
|
|
44
|
-
hasChildren: boolean;
|
|
45
|
-
}[];
|
|
46
|
-
export declare function collectAllExpandableKeys(options: NormalizedTreeOption[]): string[];
|
|
47
|
-
export declare function findNodeByValue(options: NormalizedTreeOption[], value: string): NormalizedTreeOption | undefined;
|
|
48
|
-
export {};
|
|
1
|
+
/**
|
|
2
|
+
* RE-EXPORT SHIM — the tree model moved to `src/lib/tree.ts`.
|
|
3
|
+
*
|
|
4
|
+
* `Tree` (data-display) and `TreeSelect` (data-entry) are two SURFACES over ONE model. Forking the
|
|
5
|
+
* traversal would let a page tree and a dropdown tree disagree about what "expanded", "a leaf" or
|
|
6
|
+
* "every descendant" means — so the normalizer, the visible-row flattener and the descendant walk
|
|
7
|
+
* live in `src/lib/`, which both groups may import, and this path stays valid so nothing that
|
|
8
|
+
* already imports `./tree-utils` has to change.
|
|
9
|
+
*/
|
|
10
|
+
export * from "../../lib/tree.js";
|