@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.
Files changed (142) hide show
  1. package/dist/components/data-display/card.d.ts +2 -2
  2. package/dist/components/data-display/collapsible.d.ts +23 -4
  3. package/dist/components/data-display/collapsible.js +119 -4
  4. package/dist/components/data-display/index.d.ts +3 -1
  5. package/dist/components/data-display/index.js +2 -0
  6. package/dist/components/data-display/legend.d.ts +15 -0
  7. package/dist/components/data-display/legend.js +11 -0
  8. package/dist/components/data-display/list-row.js +1 -1
  9. package/dist/components/data-display/popover.d.ts +58 -5
  10. package/dist/components/data-display/popover.js +206 -27
  11. package/dist/components/data-display/progress.d.ts +50 -2
  12. package/dist/components/data-display/progress.js +57 -9
  13. package/dist/components/data-entry/calendar.d.ts +1 -1
  14. package/dist/components/data-entry/calendar.js +67 -32
  15. package/dist/components/data-entry/checkbox.d.ts +19 -6
  16. package/dist/components/data-entry/checkbox.js +55 -16
  17. package/dist/components/data-entry/choice-option.d.ts +1 -1
  18. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  19. package/dist/components/data-entry/field.js +0 -1
  20. package/dist/components/data-entry/form.js +1 -1
  21. package/dist/components/data-entry/label.d.ts +3 -2
  22. package/dist/components/data-entry/label.js +23 -10
  23. package/dist/components/data-entry/month-picker.d.ts +1 -1
  24. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  25. package/dist/components/data-entry/transfer.js +16 -6
  26. package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
  27. package/dist/components/data-entry/tree-select.js +29 -12
  28. package/dist/components/data-entry/tree-utils.d.ts +1 -1
  29. package/dist/components/data-entry/upload.js +7 -2
  30. package/dist/components/feedback/dialog.d.ts +105 -38
  31. package/dist/components/feedback/dialog.js +272 -194
  32. package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
  33. package/dist/components/feedback/overlay-close-focus.js +31 -0
  34. package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
  35. package/dist/components/feedback/sheet.d.ts +53 -11
  36. package/dist/components/feedback/sheet.js +150 -81
  37. package/dist/components/feedback/tooltip.d.ts +51 -7
  38. package/dist/components/feedback/tooltip.js +107 -25
  39. package/dist/components/general/button.js +2 -2
  40. package/dist/components/general/logo.d.ts +17 -0
  41. package/dist/components/general/logo.js +22 -16
  42. package/dist/components/general/typography.d.ts +7 -1
  43. package/dist/components/general/typography.js +20 -5
  44. package/dist/components/layout/app-shell.d.ts +1 -1
  45. package/dist/components/layout/app-shell.js +52 -8
  46. package/dist/components/layout/aspect-ratio.js +0 -1
  47. package/dist/components/layout/auth-divider.js +0 -1
  48. package/dist/components/layout/flex.d.ts +1 -1
  49. package/dist/components/layout/flex.js +19 -2
  50. package/dist/components/layout/nav-surface.d.ts +26 -0
  51. package/dist/components/layout/nav-surface.js +17 -0
  52. package/dist/components/layout/org-switcher.d.ts +5 -1
  53. package/dist/components/layout/org-switcher.js +5 -3
  54. package/dist/components/layout/separator.js +0 -1
  55. package/dist/components/layout/sidebar.js +4 -1
  56. package/dist/components/layout/split-pane.d.ts +14 -1
  57. package/dist/components/layout/split-pane.js +26 -13
  58. package/dist/components/layout/topbar-item.js +1 -2
  59. package/dist/components/navigation/app-setting-picker.js +21 -2
  60. package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
  61. package/dist/components/navigation/app-setting-toggle.js +96 -0
  62. package/dist/components/navigation/dropdown-menu.d.ts +199 -18
  63. package/dist/components/navigation/dropdown-menu.js +344 -117
  64. package/dist/components/navigation/index.d.ts +2 -0
  65. package/dist/components/navigation/index.js +2 -0
  66. package/dist/components/navigation/pagination-utils.d.ts +2 -1
  67. package/dist/components/navigation/tabs.d.ts +47 -6
  68. package/dist/components/navigation/tabs.js +152 -90
  69. package/dist/components/ui/accordion.d.ts +50 -5
  70. package/dist/components/ui/accordion.js +239 -33
  71. package/dist/components/ui/aspect-ratio.d.ts +23 -2
  72. package/dist/components/ui/aspect-ratio.js +15 -13
  73. package/dist/components/ui/avatar.d.ts +29 -4
  74. package/dist/components/ui/avatar.js +111 -25
  75. package/dist/components/ui/hover-card.d.ts +42 -4
  76. package/dist/components/ui/hover-card.js +183 -27
  77. package/dist/components/ui/label.js +0 -1
  78. package/dist/components/ui/segmented.d.ts +3 -3
  79. package/dist/components/ui/separator.d.ts +8 -2
  80. package/dist/components/ui/separator.js +17 -9
  81. package/dist/components/ui/toggle-group.d.ts +50 -5
  82. package/dist/components/ui/toggle-group.js +79 -20
  83. package/dist/components/ui/toggle.d.ts +31 -5
  84. package/dist/components/ui/toggle.js +42 -3
  85. package/dist/i18n/messages/en.json +8 -0
  86. package/dist/i18n/messages/ja.json +8 -0
  87. package/dist/i18n/messages/vi.json +8 -0
  88. package/dist/lib/control-styles.d.ts +1 -1
  89. package/dist/lib/control-styles.js +1 -1
  90. package/dist/lib/slot.d.ts +32 -0
  91. package/dist/lib/slot.js +22 -0
  92. package/dist/lib/variants.d.ts +22 -3
  93. package/dist/lib/variants.js +56 -1
  94. package/dist/props/components/app.prop.d.ts +25 -1
  95. package/dist/props/components/data-display.prop.d.ts +17 -0
  96. package/dist/props/components/data-entry.prop.d.ts +32 -4
  97. package/dist/props/components/general.prop.d.ts +31 -1
  98. package/dist/props/components/index.d.ts +1 -1
  99. package/dist/props/components/layout.prop.d.ts +118 -5
  100. package/dist/props/components/navigation.prop.d.ts +1 -1
  101. package/dist/props/registry.d.ts +22 -2
  102. package/dist/props/registry.js +33 -2
  103. package/dist/props/vocabulary/content.prop.d.ts +1 -1
  104. package/dist/props/vocabulary/index.d.ts +2 -2
  105. package/dist/props/vocabulary/interaction.prop.d.ts +17 -1
  106. package/dist/props/vocabulary/layout.prop.d.ts +57 -1
  107. package/dist/styles/control.css +142 -1
  108. package/dist/styles/core.css +5 -2
  109. package/dist/styles/data-display-layout.css +72 -0
  110. package/dist/styles/focus-ring.css +13 -1
  111. package/dist/styles/index.css +5 -2
  112. package/dist/styles/layout.css +106 -0
  113. package/dist/styles/shell-layout.css +140 -8
  114. package/dist/styles/text-layout.css +16 -4
  115. package/dist/tokens/base.css +2 -1
  116. package/dist/tokens/components/data-display.css +11 -0
  117. package/dist/tokens/components/shell.css +3 -1
  118. package/dist/tokens/components/text.css +7 -0
  119. package/dist/tokens/foundation.css +2 -0
  120. package/docs/COMPONENTS.md +9 -3
  121. package/docs/DESIGN-AUTHORITY.md +96 -71
  122. package/docs/FRAME-COVERAGE-REPORT.md +5 -3
  123. package/docs/README.md +14 -14
  124. package/docs/WHAT-BELONGS-HERE.md +179 -0
  125. package/docs/data-display/legend.tsx +145 -0
  126. package/docs/data-display/progress.tsx +32 -0
  127. package/docs/data-entry/segmented.tsx +1 -1
  128. package/docs/data-entry/select.tsx +2 -2
  129. package/docs/feedback/sheet.tsx +1 -1
  130. package/docs/general/typography.tsx +84 -1
  131. package/docs/layout/app-shell-arrangements.tsx +225 -0
  132. package/docs/layout/aspect-ratio.tsx +1 -1
  133. package/docs/navigation/app-setting-picker.tsx +26 -1
  134. package/docs/navigation/app-setting-toggle.tsx +111 -0
  135. package/package.json +11 -8
  136. package/scripts/_agent-setup.mjs +165 -3
  137. package/scripts/consumer-rule.md +98 -0
  138. package/scripts/guinea-pig-skill.md +322 -0
  139. package/scripts/init-guinea-pig.mjs +62 -0
  140. package/scripts/postinstall.mjs +13 -2
  141. package/scripts/ui-audit.mjs +115 -14
  142. /package/dist/tokens/{antd.generated.css → derived.css} +0 -0
@@ -1,19 +1,64 @@
1
1
  import * as React from "react";
2
- import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
3
2
  import { type ToggleCountFields, type ToggleProp } from "./toggle.js";
4
3
  type ToggleGroupVariant = ToggleProp["variant"];
5
4
  type ToggleGroupSize = ToggleProp["size"];
6
- export declare const ToggleGroup: React.ForwardRefExoticComponent<((Omit<ToggleGroupPrimitive.ToggleGroupMultipleProps & React.RefAttributes<HTMLDivElement>, "ref"> | Omit<ToggleGroupPrimitive.ToggleGroupSingleProps & React.RefAttributes<HTMLDivElement>, "ref">) & {
5
+ type ToggleGroupBaseProp = Omit<React.ComponentPropsWithoutRef<"div">, "defaultValue" | "dir"> & {
7
6
  variant?: ToggleGroupVariant;
8
7
  size?: ToggleGroupSize;
9
- }) & React.RefAttributes<HTMLDivElement>>;
8
+ /** Disable every item at once — RAC `isDisabled`. */
9
+ disabled?: boolean;
10
+ /** Which way the arrow keys walk the group. */
11
+ orientation?: "horizontal" | "vertical";
12
+ /**
13
+ * Writing direction. It still lands on the DOM node, but React Aria reads the direction it
14
+ * navigates by from the ambient locale (`I18nProvider`), NOT from this attribute — where Radix
15
+ * read this prop directly. An RTL app must set the locale, not just `dir`.
16
+ */
17
+ dir?: "ltr" | "rtl";
18
+ /**
19
+ * Accepted for source compatibility with the Radix era, where it wrapped arrow-key focus around
20
+ * the ends. React Aria's toolbar navigation stops at the ends and has no equivalent switch, so
21
+ * this prop no longer changes anything.
22
+ */
23
+ loop?: boolean;
24
+ };
25
+ /**
26
+ * `type` is the discriminant, exactly as it was under Radix: `"single"` reports the one selected
27
+ * value as a string (`""` once it is deselected), `"multiple"` reports an array. React Aria models
28
+ * both as `selectionMode` + a `Set` of keys — that Set is built and unpacked here so the public
29
+ * shape stays a string / an array of strings.
30
+ */
31
+ type ToggleGroupSingleProp = ToggleGroupBaseProp & {
32
+ type: "single";
33
+ value?: string;
34
+ defaultValue?: string;
35
+ onValueChange?: (value: string) => void;
36
+ };
37
+ type ToggleGroupMultipleProp = ToggleGroupBaseProp & {
38
+ type: "multiple";
39
+ value?: string[];
40
+ defaultValue?: string[];
41
+ onValueChange?: (value: string[]) => void;
42
+ };
43
+ type ToggleGroupProp = ToggleGroupSingleProp | ToggleGroupMultipleProp;
44
+ export declare const ToggleGroup: React.ForwardRefExoticComponent<ToggleGroupProp & React.RefAttributes<HTMLDivElement>>;
10
45
  /** Same vocabulary as `Toggle` and `Button` — one counter pill for the whole library. */
11
- export type ToggleGroupItemProp = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> & ToggleCountFields & {
46
+ export type ToggleGroupItemProp = Omit<React.ComponentPropsWithoutRef<"button">, "value"> & ToggleCountFields & {
47
+ /**
48
+ * This item's key in the group's value. React Aria calls it `id` (the key it stores in
49
+ * `selectedKeys`); the public spelling stays `value`, as it was under Radix.
50
+ */
51
+ value: string;
12
52
  variant?: ToggleGroupVariant;
13
53
  size?: ToggleGroupSize;
14
54
  };
15
55
  export type ToggleGroupItemProps = ToggleGroupItemProp;
16
- export declare const ToggleGroupItem: React.ForwardRefExoticComponent<Omit<ToggleGroupPrimitive.ToggleGroupItemProps & React.RefAttributes<HTMLButtonElement>, "ref"> & ToggleCountFields & {
56
+ export declare const ToggleGroupItem: React.ForwardRefExoticComponent<Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "value"> & ToggleCountFields & {
57
+ /**
58
+ * This item's key in the group's value. React Aria calls it `id` (the key it stores in
59
+ * `selectedKeys`); the public spelling stays `value`, as it was under Radix.
60
+ */
61
+ value: string;
17
62
  variant?: ToggleGroupVariant;
18
63
  size?: ToggleGroupSize;
19
64
  } & React.RefAttributes<HTMLButtonElement>>;
@@ -1,26 +1,77 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
4
+ import {
5
+ ToggleButton,
6
+ ToggleButtonGroup,
7
+ ToggleGroupStateContext
8
+ } from "react-aria-components";
5
9
  import { cn } from "../../lib/utils.js";
6
- import { toggleVariants, useCounterPill } from "./toggle.js";
10
+ import {
11
+ restoreDomProps,
12
+ toggleVariants,
13
+ useCounterPill
14
+ } from "./toggle.js";
7
15
  const ToggleGroupContext = React.createContext({});
8
- const ToggleGroup = React.forwardRef(({ className, variant, size, children, ...props }, ref) => {
9
- const context = React.useMemo(() => ({ variant, size }), [variant, size]);
10
- return /* @__PURE__ */ jsx(
11
- ToggleGroupPrimitive.Root,
12
- {
13
- ref,
14
- "data-slot": "toggle-group",
15
- "data-variant": variant,
16
- "data-size": size,
17
- className: cn("ui-toggle-group", className),
18
- ...props,
19
- children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: context, children })
20
- }
21
- );
22
- });
23
- ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
16
+ function toSelectedKeys(value) {
17
+ if (value == null) {
18
+ return void 0;
19
+ }
20
+ if (Array.isArray(value)) {
21
+ return new Set(value);
22
+ }
23
+ return new Set(value === "" ? [] : [value]);
24
+ }
25
+ const ToggleGroup = React.forwardRef(
26
+ // No destructuring defaults: an unset prop must stay `undefined` so the emitted attribute is
27
+ // either absent or a declared union member.
28
+ // is NOT in the `sm | md | lg` size union — the group advertised an invalid value for its own
29
+ // type.) The real default lives in `toggleVariants`, applied per item.
30
+ ({
31
+ className,
32
+ variant,
33
+ size,
34
+ children,
35
+ type,
36
+ value,
37
+ defaultValue,
38
+ onValueChange,
39
+ disabled,
40
+ loop: _loop,
41
+ ...props
42
+ }, ref) => {
43
+ const context = React.useMemo(() => ({ variant, size }), [variant, size]);
44
+ const selectedKeys = React.useMemo(() => toSelectedKeys(value), [value]);
45
+ const defaultSelectedKeys = React.useMemo(() => toSelectedKeys(defaultValue), [defaultValue]);
46
+ const handleSelectionChange = (keys) => {
47
+ const next = [...keys].map(String);
48
+ if (type === "single") {
49
+ onValueChange?.(next[0] ?? "");
50
+ } else {
51
+ onValueChange?.(next);
52
+ }
53
+ };
54
+ return /* @__PURE__ */ jsx(
55
+ ToggleButtonGroup,
56
+ {
57
+ ref,
58
+ "data-slot": "toggle-group",
59
+ "data-variant": variant,
60
+ "data-size": size,
61
+ className: cn("ui-toggle-group", className),
62
+ ...props,
63
+ selectionMode: type,
64
+ selectedKeys,
65
+ defaultSelectedKeys,
66
+ onSelectionChange: handleSelectionChange,
67
+ isDisabled: disabled,
68
+ render: (domProps) => /* @__PURE__ */ jsx("div", { ...props, ...domProps, tabIndex: props.tabIndex ?? domProps.tabIndex }),
69
+ children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: context, children })
70
+ }
71
+ );
72
+ }
73
+ );
74
+ ToggleGroup.displayName = "ToggleGroup";
24
75
  const ToggleGroupItem = React.forwardRef(
25
76
  ({
26
77
  className,
@@ -32,6 +83,8 @@ const ToggleGroupItem = React.forwardRef(
32
83
  countLabel,
33
84
  children,
34
85
  "aria-label": ariaLabel,
86
+ value,
87
+ disabled,
35
88
  ...props
36
89
  }, ref) => {
37
90
  const context = React.useContext(ToggleGroupContext);
@@ -44,16 +97,22 @@ const ToggleGroupItem = React.forwardRef(
44
97
  countLabel,
45
98
  ariaLabel
46
99
  });
100
+ const groupState = React.useContext(ToggleGroupStateContext);
101
+ const isPressed = groupState?.selectedKeys.has(value) ?? false;
47
102
  return /* @__PURE__ */ jsxs(
48
- ToggleGroupPrimitive.Item,
103
+ ToggleButton,
49
104
  {
50
105
  ref,
51
106
  "data-slot": "toggle-group-item",
107
+ "data-state": isPressed ? "on" : "off",
52
108
  "data-variant": resolvedVariant,
53
109
  "data-size": resolvedSize,
54
110
  "aria-label": resolvedAriaLabel,
55
111
  className: cn(toggleVariants({ variant: resolvedVariant, size: resolvedSize }), className),
56
112
  ...props,
113
+ id: value,
114
+ isDisabled: disabled,
115
+ render: (domProps) => restoreDomProps(props, domProps),
57
116
  children: [
58
117
  children,
59
118
  pill
@@ -62,7 +121,7 @@ const ToggleGroupItem = React.forwardRef(
62
121
  );
63
122
  }
64
123
  );
65
- ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
124
+ ToggleGroupItem.displayName = "ToggleGroupItem";
66
125
  export {
67
126
  ToggleGroup,
68
127
  ToggleGroupItem
@@ -1,5 +1,4 @@
1
1
  import * as React from "react";
2
- import * as TogglePrimitive from "@radix-ui/react-toggle";
3
2
  import { type VariantProps } from "class-variance-authority";
4
3
  declare const toggleVariants: (props?: ({
5
4
  variant?: "default" | "outline" | null | undefined;
@@ -29,6 +28,19 @@ type ToggleCountFields = {
29
28
  */
30
29
  countLabel?: string;
31
30
  };
31
+ /**
32
+ * The repo's two-state vocabulary, kept VERBATIM from the Radix era. React Aria spells the same
33
+ * four things `isSelected` / `defaultSelected` / `onChange` / `isDisabled`; the translation lives
34
+ * inside this file so no consumer ever has to learn RAC's names.
35
+ */
36
+ type TogglePressedFields = {
37
+ /** Controlled pressed state — RAC `isSelected`. */
38
+ pressed?: boolean;
39
+ /** Uncontrolled initial pressed state — RAC `defaultSelected`. */
40
+ defaultPressed?: boolean;
41
+ /** Fired with the NEW pressed state — RAC `onChange`. */
42
+ onPressedChange?: (pressed: boolean) => void;
43
+ };
32
44
  /** Counter pill shared by `Toggle` and `ToggleGroupItem`. ACCESSIBLE NAME. */
33
45
  declare function useCounterPill({ count, overflowCount, showZero, countLabel, ariaLabel, }: ToggleCountFields & {
34
46
  ariaLabel?: string;
@@ -39,11 +51,25 @@ declare function useCounterPill({ count, overflowCount, showZero, countLabel, ar
39
51
  pill: React.JSX.Element;
40
52
  resolvedAriaLabel: string | undefined;
41
53
  };
42
- export type ToggleProp = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants> & ToggleCountFields;
54
+ /**
55
+ * RAC's `filterDOMProps` forwards only `id`, `data-*`, the four labelling `aria-*` and the global
56
+ * mouse/pointer events; `title`, `tabIndex` and the rarer DOM props are dropped silently, and
57
+ * Radix forwarded ALL of them. `render` is RAC's own escape hatch: the raw props go on first, RAC's
58
+ * merged ones second so its `type`/`disabled`/`aria-pressed`/handlers always win.
59
+ *
60
+ * Two exceptions, both of which Radix owned: `tabIndex` stays the caller's, and so does `id` —
61
+ * inside a ToggleButtonGroup RAC spends `id` as the selection KEY and blanks the DOM attribute,
62
+ * which would otherwise silently drop the id off every grouped item.
63
+ */
64
+ declare function restoreDomProps<P extends {
65
+ id?: string;
66
+ tabIndex?: number;
67
+ }>(raw: P, domProps: React.JSX.IntrinsicElements["button"]): React.JSX.Element;
68
+ export type ToggleProp = React.ComponentPropsWithoutRef<"button"> & TogglePressedFields & VariantProps<typeof toggleVariants> & ToggleCountFields;
43
69
  export type ToggleProps = ToggleProp;
44
- export declare const Toggle: React.ForwardRefExoticComponent<Omit<TogglePrimitive.ToggleProps & React.RefAttributes<HTMLButtonElement>, "ref"> & VariantProps<(props?: ({
70
+ export declare const Toggle: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & TogglePressedFields & VariantProps<(props?: ({
45
71
  variant?: "default" | "outline" | null | undefined;
46
72
  size?: "lg" | "md" | "sm" | null | undefined;
47
73
  } & import("class-variance-authority/types").ClassProp) | undefined) => string> & ToggleCountFields & React.RefAttributes<HTMLButtonElement>>;
48
- export { toggleVariants, useCounterPill };
49
- export type { ToggleCountFields };
74
+ export { toggleVariants, useCounterPill, restoreDomProps };
75
+ export type { ToggleCountFields, TogglePressedFields };
@@ -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 * as TogglePrimitive from "@radix-ui/react-toggle";
4
+ import { ToggleButton } from "react-aria-components";
5
5
  import { cva } from "class-variance-authority";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  import { useTranslation } from "../../i18n/use-translation.js";
@@ -48,6 +48,30 @@ function useCounterPill({
48
48
  resolvedAriaLabel: ariaLabel == null ? ariaLabel : `${ariaLabel}, ${spoken}`
49
49
  };
50
50
  }
51
+ function usePressedState({ pressed, defaultPressed, onPressedChange }) {
52
+ const [uncontrolled, setUncontrolled] = React.useState(defaultPressed ?? false);
53
+ const isPressed = pressed ?? uncontrolled;
54
+ return {
55
+ isPressed,
56
+ onSelectionChange: (next) => {
57
+ if (pressed == null) {
58
+ setUncontrolled(next);
59
+ }
60
+ onPressedChange?.(next);
61
+ }
62
+ };
63
+ }
64
+ function restoreDomProps(raw, domProps) {
65
+ return /* @__PURE__ */ jsx(
66
+ "button",
67
+ {
68
+ ...raw,
69
+ ...domProps,
70
+ id: raw.id ?? domProps.id,
71
+ tabIndex: raw.tabIndex ?? domProps.tabIndex
72
+ }
73
+ );
74
+ }
51
75
  const Toggle = React.forwardRef(
52
76
  ({
53
77
  className,
@@ -59,6 +83,10 @@ const Toggle = React.forwardRef(
59
83
  countLabel,
60
84
  children,
61
85
  "aria-label": ariaLabel,
86
+ pressed,
87
+ defaultPressed,
88
+ onPressedChange,
89
+ disabled,
62
90
  ...props
63
91
  }, ref) => {
64
92
  const { pill, resolvedAriaLabel } = useCounterPill({
@@ -68,14 +96,24 @@ const Toggle = React.forwardRef(
68
96
  countLabel,
69
97
  ariaLabel
70
98
  });
99
+ const { isPressed, onSelectionChange } = usePressedState({
100
+ pressed,
101
+ defaultPressed,
102
+ onPressedChange
103
+ });
71
104
  return /* @__PURE__ */ jsxs(
72
- TogglePrimitive.Root,
105
+ ToggleButton,
73
106
  {
74
107
  ref,
75
108
  "data-slot": "toggle",
109
+ "data-state": isPressed ? "on" : "off",
76
110
  "aria-label": resolvedAriaLabel,
77
111
  className: cn(toggleVariants({ variant, size }), className),
78
112
  ...props,
113
+ isSelected: isPressed,
114
+ onChange: onSelectionChange,
115
+ isDisabled: disabled,
116
+ render: (domProps) => restoreDomProps(props, domProps),
79
117
  children: [
80
118
  children,
81
119
  pill
@@ -84,9 +122,10 @@ const Toggle = React.forwardRef(
84
122
  );
85
123
  }
86
124
  );
87
- Toggle.displayName = TogglePrimitive.Root.displayName;
125
+ Toggle.displayName = "Toggle";
88
126
  export {
89
127
  Toggle,
128
+ restoreDomProps,
90
129
  toggleVariants,
91
130
  useCounterPill
92
131
  };
@@ -157,6 +157,7 @@
157
157
  },
158
158
  "appShell": {
159
159
  "sidebarLabel": "Sidebar",
160
+ "navRailLabel": "Workspaces",
160
161
  "headerLabel": "Header",
161
162
  "mainLabel": "Main content",
162
163
  "footerLabel": "Footer",
@@ -252,6 +253,10 @@
252
253
  "denied": "You do not have permission to view this matrix",
253
254
  "deniedDescription": "Ask an administrator to grant you access.",
254
255
  "loading": "Loading permissions…"
256
+ },
257
+ "progress": {
258
+ "ariaLabel": "Progress",
259
+ "breakdownSeparator": ", "
255
260
  }
256
261
  },
257
262
  "ui": {
@@ -359,6 +364,9 @@
359
364
  "logistics": "Logistics Teal",
360
365
  "partner": "Partner Orange",
361
366
  "slate": "HQ Slate"
367
+ },
368
+ "settingToggle": {
369
+ "label": "{setting}: {value}"
362
370
  }
363
371
  },
364
372
  "locale": {
@@ -154,6 +154,7 @@
154
154
  },
155
155
  "appShell": {
156
156
  "sidebarLabel": "サイドバー",
157
+ "navRailLabel": "ワークスペース",
157
158
  "headerLabel": "ヘッダー",
158
159
  "mainLabel": "メインコンテンツ",
159
160
  "footerLabel": "フッター",
@@ -246,6 +247,10 @@
246
247
  "denied": "このマトリクスを表示する権限がありません",
247
248
  "deniedDescription": "閲覧するには管理者に権限の付与を依頼してください。",
248
249
  "loading": "権限を読み込み中…"
250
+ },
251
+ "progress": {
252
+ "ariaLabel": "進捗",
253
+ "breakdownSeparator": "、"
249
254
  }
250
255
  },
251
256
  "ui": {
@@ -347,6 +352,9 @@
347
352
  "logistics": "Logistics Teal",
348
353
  "partner": "Partner Orange",
349
354
  "slate": "HQ Slate"
355
+ },
356
+ "settingToggle": {
357
+ "label": "{setting}:{value}"
350
358
  }
351
359
  },
352
360
  "locale": {
@@ -154,6 +154,7 @@
154
154
  },
155
155
  "appShell": {
156
156
  "sidebarLabel": "Thanh bên",
157
+ "navRailLabel": "Không gian làm việc",
157
158
  "headerLabel": "Đầu trang",
158
159
  "mainLabel": "Nội dung chính",
159
160
  "footerLabel": "Chân trang",
@@ -246,6 +247,10 @@
246
247
  "denied": "Bạn không có quyền xem ma trận này",
247
248
  "deniedDescription": "Hãy đề nghị quản trị viên cấp quyền truy cập.",
248
249
  "loading": "Đang tải quyền…"
250
+ },
251
+ "progress": {
252
+ "ariaLabel": "Tiến độ",
253
+ "breakdownSeparator": ", "
249
254
  }
250
255
  },
251
256
  "ui": {
@@ -347,6 +352,9 @@
347
352
  "logistics": "Logistics Teal",
348
353
  "partner": "Partner Orange",
349
354
  "slate": "HQ Slate"
355
+ },
356
+ "settingToggle": {
357
+ "label": "{setting}: {value}"
350
358
  }
351
359
  },
352
360
  "locale": {
@@ -12,7 +12,7 @@ export declare const controlMultilineGhostClass = "ui-control-multiline w-full m
12
12
  * an open popover shows only a border change (no ring), inconsistent with a focused Input.
13
13
  */
14
14
  export declare const controlOpenRingClass = "ui-control-trigger";
15
- export declare const controlTriggerClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] border-input bg-background transition-[color,box-shadow] focus-visible:outline-none [&>span]:line-clamp-1";
15
+ export declare const controlTriggerClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] border-input bg-background transition-[color,box-shadow] focus-visible:outline-none [&>[data-slot=select-value]]:line-clamp-1";
16
16
  export declare const controlIconClass = "size-[length:var(--control-height)] shrink-0";
17
17
  export declare const controlIconSmClass = "size-[calc(var(--control-height)-0.5rem)] shrink-0";
18
18
  /** Leading/affix icon inside an input row (search, command) — sized to `--control-icon-size`. */
@@ -1,7 +1,7 @@
1
1
  const controlMultilineClass = "ui-control-multiline aria-invalid:border-destructive w-full rounded-[var(--control-radius)] border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none";
2
2
  const controlMultilineGhostClass = "ui-control-multiline w-full min-h-0 border-0 bg-transparent shadow-none placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0";
3
3
  const controlOpenRingClass = "ui-control-trigger";
4
- const controlTriggerClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] border-input bg-background transition-[color,box-shadow] focus-visible:outline-none [&>span]:line-clamp-1";
4
+ const controlTriggerClass = "ui-control ui-control-trigger flex items-center justify-between gap-2 whitespace-nowrap rounded-[var(--control-radius)] border-input bg-background transition-[color,box-shadow] focus-visible:outline-none [&>[data-slot=select-value]]:line-clamp-1";
5
5
  const controlIconClass = "size-[length:var(--control-height)] shrink-0";
6
6
  const controlIconSmClass = "size-[calc(var(--control-height)-0.5rem)] shrink-0";
7
7
  const controlIconLeadingClass = "size-[length:var(--control-icon-size)] shrink-0";
@@ -0,0 +1,32 @@
1
+ import * as React from "react";
2
+ /**
3
+ * `Slot` — nền của `asChild`, và từ v20 là của CHÍNH kho này.
4
+ *
5
+ * ## Vì sao tự dựng
6
+ *
7
+ * `asChild` là API công khai của @godxjp/ui: 171 chỗ trong thư viện và mọi
8
+ * consumer đều dùng. React Aria KHÔNG có mô hình đó — nó dùng prop `render`.
9
+ * Nếu để nền quyết định API thì đợt đổi nền sẽ phá mọi consumer; nếu tự giữ
10
+ * `Slot` thì đổi nền là chuyện nội bộ. Ba mươi dòng ở đây mua lấy điều đó.
11
+ *
12
+ * ## Ngữ nghĩa, và một chỗ KHÁC `mergeProps`
13
+ *
14
+ * Gộp prop đi qua `mergeProps` của React Aria — cùng luật mà chính các
15
+ * component React Aria dùng bên trong, nên `Slot` cư xử nhất quán với phần còn
16
+ * lại của hệ. Nó cho: `className` nối chuỗi, handler CHAIN (cha chạy rồi tới
17
+ * con, không ghi đè), prop thường thì CON thắng.
18
+ *
19
+ * Nhưng `mergeProps` để `style` của con GHI ĐÈ HẲN style của cha — đo được:
20
+ * cha `{color:'red'}` + con `{margin:1}` ra `{margin:1}`, mất màu. Slot của
21
+ * Radix thì gộp nông hai object, và 171 call site đang dựa vào hành vi ấy. Nên
22
+ * `style` được gộp riêng ở đây, sau `mergeProps`.
23
+ *
24
+ * ## Đúng MỘT con
25
+ *
26
+ * `asChild` nghĩa là "đừng dựng thẻ của mình, mượn thẻ của con". Hai con thì
27
+ * không có thẻ nào để mượn, nên `Children.only` ném lỗi — sớm và rõ, thay vì
28
+ * lặng lẽ dựng ra một cây DOM không ai định.
29
+ */
30
+ export declare function Slot({ children, ...slotProps }: React.HTMLAttributes<HTMLElement> & React.RefAttributes<HTMLElement> & {
31
+ children?: React.ReactNode;
32
+ }): React.ReactElement<React.HTMLAttributes<HTMLElement>, string | React.JSXElementConstructor<any>>;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { mergeProps } from "@react-aria/utils";
3
+ import * as React from "react";
4
+ function Slot({
5
+ children,
6
+ ...slotProps
7
+ }) {
8
+ const child = React.Children.only(children);
9
+ const merged = mergeProps(
10
+ slotProps,
11
+ child.props
12
+ );
13
+ const slotStyle = slotProps.style;
14
+ const childStyle = child.props.style;
15
+ return React.cloneElement(child, {
16
+ ...merged,
17
+ style: slotStyle === void 0 && childStyle === void 0 ? void 0 : { ...slotStyle, ...childStyle }
18
+ });
19
+ }
20
+ export {
21
+ Slot
22
+ };
@@ -1,12 +1,31 @@
1
1
  /** Prop → internal class maps. Apps use component props, never these classes directly. */
2
- import type { GapProp, PageContainerVariantProp, PageDensityProp } from "../props/vocabulary/index.js";
2
+ import type * as React from "react";
3
+ import type { GapNameProp, GapProp, PadProp, PadRawProp, PageContainerVariantProp, PageDensityProp } from "../props/vocabulary/index.js";
3
4
  export type Density = PageDensityProp;
4
5
  export type StackGap = GapProp;
5
6
  export type FlexGap = GapProp;
6
- export type InlineGap = Exclude<GapProp, "xl" | "none">;
7
+ export type InlineGap = Exclude<GapNameProp, "xl" | "none">;
7
8
  export type PageContainerVariant = PageContainerVariantProp;
8
9
  export declare const densityClass: Record<PageDensityProp, string>;
9
10
  export declare const pageContainerVariantClass: Record<PageContainerVariantProp, string | undefined>;
10
- export declare const stackGapClass: Record<Exclude<GapProp, "none">, string>;
11
+ export declare const stackGapClass: Record<Exclude<GapNameProp, "none">, string>;
11
12
  export declare const inlineGapClass: Record<InlineGap, string>;
12
13
  export declare const flexGapClass: Record<GapProp, string>;
14
+ /**
15
+ * Bậc thang → giá trị CSS cho đệm.
16
+ *
17
+ * Bậc TÊN ánh xạ vào thang dọc (`--space-stack-*`) chứ không theo trục như
18
+ * `gap`: đệm bao quanh một khối, nó không có "trục viết" để chặt hơn. Bậc SỐ
19
+ * đi thẳng vào `--space-{n}` của thang gốc, giống hệt `gap={n}`.
20
+ */
21
+ export declare function padStepToken(step: GapProp): string;
22
+ /**
23
+ * `pad` / `padRaw` → style đệm theo tên cạnh LOGIC.
24
+ *
25
+ * Trả về `undefined` khi không có gì để đặt, để `Flex` không phát một thuộc
26
+ * tính `style` rỗng (và phép so với bản Radix không lệch vì một chuỗi thừa).
27
+ *
28
+ * `padRaw` thắng `pad` ở TỪNG CẠNH, không phải cả cụm: một thiết kế có thể cần
29
+ * `blockStart` đúng 10px trong khi ba cạnh còn lại vẫn theo thang.
30
+ */
31
+ export declare function padStyle(pad: PadProp | undefined, padRaw: PadRawProp | undefined): React.CSSProperties | undefined;
@@ -28,12 +28,67 @@ const flexGapClass = {
28
28
  sm: "ui-flex-gap-sm",
29
29
  md: "ui-flex-gap-md",
30
30
  lg: "ui-flex-gap-lg",
31
- xl: "ui-flex-gap-xl"
31
+ xl: "ui-flex-gap-xl",
32
+ // Bậc SỐ: thang gốc `--space-{n}`, không theo trục. Xem GapStepProp.
33
+ 0: "ui-flex-gap-none",
34
+ 1: "ui-flex-gap-1",
35
+ 2: "ui-flex-gap-2",
36
+ 3: "ui-flex-gap-3",
37
+ 4: "ui-flex-gap-4",
38
+ 5: "ui-flex-gap-5",
39
+ 6: "ui-flex-gap-6",
40
+ 8: "ui-flex-gap-8",
41
+ 10: "ui-flex-gap-10",
42
+ 12: "ui-flex-gap-12"
32
43
  };
44
+ function padStepToken(step) {
45
+ if (step === "none" || step === 0) {
46
+ return "0";
47
+ }
48
+ return typeof step === "number" ? `var(--space-${step})` : `var(--space-stack-${step})`;
49
+ }
50
+ function padStyle(pad, padRaw) {
51
+ const scalar = (v) => v !== void 0 && (typeof v !== "object" || v === null) ? v : void 0;
52
+ const sides = (v) => v !== void 0 && typeof v === "object" && v !== null ? v : {};
53
+ const allStep = scalar(pad);
54
+ const allRaw = scalar(padRaw);
55
+ const stepSides = sides(pad);
56
+ const rawSides = sides(padRaw);
57
+ const AXES = {
58
+ inline: ["paddingInlineStart", "paddingInlineEnd"],
59
+ block: ["paddingBlockStart", "paddingBlockEnd"],
60
+ inlineStart: ["paddingInlineStart"],
61
+ inlineEnd: ["paddingInlineEnd"],
62
+ blockStart: ["paddingBlockStart"],
63
+ blockEnd: ["paddingBlockEnd"]
64
+ };
65
+ const out = {};
66
+ if (allStep !== void 0) {
67
+ out.padding = padStepToken(allStep);
68
+ }
69
+ if (allRaw !== void 0) {
70
+ out.padding = `${allRaw}px`;
71
+ }
72
+ for (const [key, props] of Object.entries(AXES)) {
73
+ const step = stepSides[key];
74
+ if (step !== void 0) {
75
+ for (const prop of props) out[prop] = padStepToken(step);
76
+ }
77
+ }
78
+ for (const [key, props] of Object.entries(AXES)) {
79
+ const raw = rawSides[key];
80
+ if (raw !== void 0) {
81
+ for (const prop of props) out[prop] = `${raw}px`;
82
+ }
83
+ }
84
+ return Object.keys(out).length > 0 ? out : void 0;
85
+ }
33
86
  export {
34
87
  densityClass,
35
88
  flexGapClass,
36
89
  inlineGapClass,
90
+ padStepToken,
91
+ padStyle,
37
92
  pageContainerVariantClass,
38
93
  stackGapClass
39
94
  };
@@ -3,7 +3,7 @@ import type { Locale } from "date-fns";
3
3
  import type { DayPickerProps } from "react-day-picker";
4
4
  import type { AppLocale, AppRequestHeaders, AppTimeFormat, AppTimezone, AppTimezoneDefault, AppDateFormat } from "../../app/types.js";
5
5
  import type { AppBrand, AppDensity, AppFontSize, AppTheme } from "../../app/theme-axes.js";
6
- import type { AppSettingPickerAppearanceProp, ChildrenProp, ClassNameProp, DisabledProp, IdProp, NameProp, OnValueChangeProp, ValueProp } from "../vocabulary/index.js";
6
+ import type { AppSettingPickerAppearanceProp, AppSettingToggleAppearanceProp, ChildrenProp, ClassNameProp, DisabledProp, IdProp, NameProp, OnValueChangeProp, ValueProp } from "../vocabulary/index.js";
7
7
  /** @see AppProvider */
8
8
  export type AppProviderProp = {
9
9
  children: ChildrenProp;
@@ -93,6 +93,30 @@ export type AppSettingPickerProp = {
93
93
  value?: ValueProp<string>;
94
94
  onValueChange?: OnValueChangeProp<string>;
95
95
  };
96
+ /**
97
+ * The {@link AppSettingToggle} subset of {@link AppSettingKind} — the settings whose value set is
98
+ * CLOSED and short enough to cycle by tapping. `locale`, `timezone`, `dateFormat` and `brand` are
99
+ * deliberately absent: a 400-entry IANA list (or a palette a service extends) is a menu, not a
100
+ * cycle, and tapping through it is not a control anyone can use.
101
+ */
102
+ export type AppSettingToggleKind = Extract<AppSettingKind, "theme" | "density" | "fontSize" | "timeFormat">;
103
+ /**
104
+ * @see AppSettingToggle — one BUTTON that steps a single AppProvider setting to its next value and
105
+ * shows that value as its glyph. The no-menu counterpart to {@link AppSettingPickerProp}: same
106
+ * binding contract (context-bound by default, controlled via value + onValueChange), same option
107
+ * order, one tap instead of open-then-choose.
108
+ */
109
+ export type AppSettingToggleProp = {
110
+ kind: AppSettingToggleKind;
111
+ /** Box the button takes. Default: `"bar"` — a toggle exists for a top bar. */
112
+ appearance?: AppSettingToggleAppearanceProp;
113
+ className?: ClassNameProp;
114
+ disabled?: DisabledProp;
115
+ id?: IdProp;
116
+ /** Controlled value; default reads/writes the matching AppProvider context. */
117
+ value?: ValueProp<string>;
118
+ onValueChange?: OnValueChangeProp<string>;
119
+ };
96
120
  /** Value exposed by `useAppContext`. */
97
121
  export type AppContextValue = {
98
122
  locale: AppLocale;