@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
@@ -87,13 +87,13 @@ export declare const CardContent: React.ForwardRefExoticComponent<React.HTMLAttr
87
87
  export type CardFooterProps = React.HTMLAttributes<HTMLDivElement> & {
88
88
  /** Top border + symmetric action band — form Save/Cancel, table summary. */
89
89
  separated?: boolean;
90
- /** Full-bleed footer (Ant Design `actions` bar). */
90
+ /** Full-bleed footer (the conventional `actions` bar). */
91
91
  flush?: boolean;
92
92
  };
93
93
  export declare const CardFooter: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
94
94
  /** Top border + symmetric action band — form Save/Cancel, table summary. */
95
95
  separated?: boolean;
96
- /** Full-bleed footer (Ant Design `actions` bar). */
96
+ /** Full-bleed footer (the conventional `actions` bar). */
97
97
  flush?: boolean;
98
98
  } & React.RefAttributes<HTMLDivElement>>;
99
99
  export type CardBarProps = React.HTMLAttributes<HTMLDivElement> & {
@@ -1,4 +1,23 @@
1
- import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
2
- export declare const Collapsible: import("react").ForwardRefExoticComponent<CollapsiblePrimitive.CollapsibleProps & import("react").RefAttributes<HTMLDivElement>>;
3
- export declare const CollapsibleTrigger: import("react").ForwardRefExoticComponent<CollapsiblePrimitive.CollapsibleTriggerProps & import("react").RefAttributes<HTMLButtonElement>>;
4
- export declare const CollapsibleContent: import("react").ForwardRefExoticComponent<CollapsiblePrimitive.CollapsibleContentProps & import("react").RefAttributes<HTMLDivElement>>;
1
+ import * as React from "react";
2
+ interface CollapsibleProps extends React.ComponentPropsWithRef<"div"> {
3
+ /** Trạng thái mở, kiểm soát. */
4
+ open?: boolean;
5
+ /** Trạng thái mở ban đầu khi không kiểm soát. */
6
+ defaultOpen?: boolean;
7
+ /** Gọi khi trạng thái mở/đóng đổi. */
8
+ onOpenChange?: (open: boolean) => void;
9
+ /** Khoá trigger; `data-disabled` được phát ra để CSS bám vào. */
10
+ disabled?: boolean;
11
+ }
12
+ export declare function Collapsible({ open, defaultOpen, onOpenChange, disabled, children, ref, ...props }: CollapsibleProps): React.JSX.Element;
13
+ interface CollapsibleTriggerProps extends React.ComponentPropsWithRef<"button"> {
14
+ /** Mượn thẻ của con thay vì dựng `<button>` riêng. */
15
+ asChild?: boolean;
16
+ }
17
+ export declare function CollapsibleTrigger({ asChild, onClick, disabled, ...props }: CollapsibleTriggerProps): React.JSX.Element;
18
+ interface CollapsibleContentProps extends React.ComponentPropsWithRef<"div"> {
19
+ /** Giữ nội dung trong DOM cả khi đóng. */
20
+ forceMount?: true;
21
+ }
22
+ export declare function CollapsibleContent({ forceMount, children, ref, ...props }: CollapsibleContentProps): React.JSX.Element;
23
+ export {};
@@ -1,8 +1,123 @@
1
1
  "use client";
2
- import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
3
- const Collapsible = CollapsiblePrimitive.Root;
4
- const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
5
- const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { chain, mergeRefs } from "@react-aria/utils";
5
+ import {
6
+ ButtonContext,
7
+ Disclosure,
8
+ DisclosurePanel,
9
+ DisclosureStateContext,
10
+ useSlottedContext
11
+ } from "react-aria-components";
12
+ import { Slot } from "../../lib/slot.js";
13
+ function Collapsible({
14
+ open,
15
+ defaultOpen,
16
+ onOpenChange,
17
+ disabled,
18
+ children,
19
+ ref,
20
+ ...props
21
+ }) {
22
+ return /* @__PURE__ */ jsx(
23
+ Disclosure,
24
+ {
25
+ isExpanded: open,
26
+ defaultExpanded: defaultOpen,
27
+ onExpandedChange: onOpenChange,
28
+ isDisabled: disabled,
29
+ render: (racProps, { isExpanded }) => {
30
+ const {
31
+ className: _racClass,
32
+ "data-rac": _rac,
33
+ ref: racRef,
34
+ ...rest
35
+ } = racProps;
36
+ return /* @__PURE__ */ jsx(
37
+ "div",
38
+ {
39
+ ...props,
40
+ ...rest,
41
+ ref: mergeRefs(ref, racRef),
42
+ "data-state": isExpanded ? "open" : "closed",
43
+ "data-disabled": disabled ? "" : void 0,
44
+ children
45
+ }
46
+ );
47
+ }
48
+ }
49
+ );
50
+ }
51
+ function CollapsibleTrigger({
52
+ asChild,
53
+ onClick,
54
+ disabled,
55
+ ...props
56
+ }) {
57
+ const trigger = useSlottedContext(ButtonContext, "trigger");
58
+ const state = React.useContext(DisclosureStateContext);
59
+ if (!trigger || !state) {
60
+ throw new Error("`CollapsibleTrigger` ph\u1EA3i n\u1EB1m trong `Collapsible`.");
61
+ }
62
+ const isDisabled = disabled ?? trigger.isDisabled ?? false;
63
+ const Comp = asChild ? Slot : "button";
64
+ return /* @__PURE__ */ jsx(
65
+ Comp,
66
+ {
67
+ type: "button",
68
+ id: trigger.id,
69
+ "aria-controls": state.isExpanded ? trigger["aria-controls"] : void 0,
70
+ "aria-expanded": state.isExpanded,
71
+ "data-state": state.isExpanded ? "open" : "closed",
72
+ "data-disabled": isDisabled ? "" : void 0,
73
+ disabled: isDisabled,
74
+ ...props,
75
+ onClick: chain(onClick, () => {
76
+ if (!isDisabled) {
77
+ state.toggle();
78
+ }
79
+ })
80
+ }
81
+ );
82
+ }
83
+ function CollapsibleContent({
84
+ forceMount,
85
+ children,
86
+ ref,
87
+ ...props
88
+ }) {
89
+ const trigger = useSlottedContext(ButtonContext, "trigger");
90
+ const state = React.useContext(DisclosureStateContext);
91
+ if (!state) {
92
+ throw new Error("`CollapsibleContent` ph\u1EA3i n\u1EB1m trong `Collapsible`.");
93
+ }
94
+ return /* @__PURE__ */ jsx(
95
+ DisclosurePanel,
96
+ {
97
+ render: (racProps) => {
98
+ const {
99
+ className: _racClass,
100
+ "data-rac": _rac,
101
+ children: _racChildren,
102
+ ref: racRef,
103
+ ...rest
104
+ } = racProps;
105
+ return /* @__PURE__ */ jsx(
106
+ "div",
107
+ {
108
+ ...props,
109
+ ...rest,
110
+ ref: mergeRefs(ref, racRef),
111
+ "data-state": state.isExpanded ? "open" : "closed",
112
+ "data-disabled": trigger?.isDisabled ? "" : void 0,
113
+ children: state.isExpanded || forceMount ? children : null
114
+ }
115
+ );
116
+ },
117
+ children: null
118
+ }
119
+ );
120
+ }
6
121
  export {
7
122
  Collapsible,
8
123
  CollapsibleContent,
@@ -1,5 +1,7 @@
1
1
  export { Badge, StatusBadge } from "./badge.js";
2
2
  export type { BadgeProps } from "./badge.js";
3
+ export { Legend } from "./legend.js";
4
+ export type { LegendItemProp, LegendProp, LegendProps } from "./legend.js";
3
5
  export { ListRow } from "./list-row.js";
4
6
  export type { ListRowProps, ListRowDensity } from "./list-row.js";
5
7
  export { CredentialReveal } from "./credential-reveal.js";
@@ -23,7 +25,7 @@ export { CodeBlock } from "./code-block.js";
23
25
  export type { CodeBlockProp, CodeBlockProps } from "./code-block.js";
24
26
  export { Prose } from "./prose.js";
25
27
  export type { ProseProp, ProseProps } from "./prose.js";
26
- export type { ProgressProps, ProgressTone } from "./progress.js";
28
+ export type { ProgressProps, ProgressSegment, ProgressTone } from "./progress.js";
27
29
  export { TreeList } from "./tree-list.js";
28
30
  export type { TreeListItem, TreeListProps } from "./tree-list.js";
29
31
  export { Timeline } from "./timeline.js";
@@ -1,4 +1,5 @@
1
1
  import { Badge, StatusBadge } from "./badge.js";
2
+ import { Legend } from "./legend.js";
2
3
  import { ListRow } from "./list-row.js";
3
4
  import { CredentialReveal } from "./credential-reveal.js";
4
5
  import { QrCode } from "./qr-code.js";
@@ -88,6 +89,7 @@ export {
88
89
  HoverCard,
89
90
  HoverCardContent,
90
91
  HoverCardTrigger,
92
+ Legend,
91
93
  ListRow,
92
94
  PermissionMatrix,
93
95
  Popover,
@@ -0,0 +1,15 @@
1
+ import type { LegendProp } from "../../props/components/data-display.prop.js";
2
+ export type { LegendItemProp, LegendProp, LegendProp as LegendProps } from "../../props/components/data-display.prop.js";
3
+ /**
4
+ * Legend — the KEY for a set of tones: which colour means what.
5
+ *
6
+ * Pair it with anything that encodes meaning as colour and cannot repeat the words on every mark:
7
+ * a `Progress` breakdown, a chart, a status column. It is the reason a colour-coded surface can
8
+ * satisfy WCAG 1.4.1 — the tone is never the only carrier of the meaning, because the key spells
9
+ * it out once in words.
10
+ *
11
+ * The swatch is `aria-hidden`: it is the same information as the label beside it, and a screen
12
+ * reader announcing "square, overdue" twice per key is noise. What reaches assistive tech is a
13
+ * plain list of the labels, which is exactly what the key says.
14
+ */
15
+ export declare function Legend({ items, className, ...props }: LegendProp): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/utils.js";
3
+ function Legend({ items, className, ...props }) {
4
+ return /* @__PURE__ */ jsx("ul", { className: cn("ui-legend", className), ...props, children: items.map((item, index) => /* @__PURE__ */ jsxs("li", { className: "ui-legend-item", children: [
5
+ /* @__PURE__ */ jsx("span", { className: "ui-legend-swatch", "data-tone": item.tone, "aria-hidden": "true" }),
6
+ item.label
7
+ ] }, index)) });
8
+ }
9
+ export {
10
+ Legend
11
+ };
@@ -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 { Slot } from "@radix-ui/react-slot";
4
+ import { Slot } from "../../lib/slot.js";
5
5
  import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  import { Text } from "../general/typography.js";
@@ -1,9 +1,28 @@
1
1
  import * as React from "react";
2
- import * as PopoverPrimitive from "@radix-ui/react-popover";
3
2
  import type { FlushProp } from "../../props/vocabulary/index.js";
4
- export declare function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>): React.JSX.Element;
5
- export declare function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>): React.JSX.Element;
6
- export declare function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>): React.JSX.Element;
3
+ type Side = "top" | "right" | "bottom" | "left";
4
+ type Align = "start" | "center" | "end";
5
+ interface PopoverProps {
6
+ /** Trạng thái mở, có kiểm soát. */
7
+ open?: boolean;
8
+ /** Trạng thái mở ban đầu khi không kiểm soát. */
9
+ defaultOpen?: boolean;
10
+ /** Gọi khi trạng thái mở/đóng đổi. */
11
+ onOpenChange?: (open: boolean) => void;
12
+ /** Khoá tương tác ngoài panel và giam tiêu điểm, đúng như `modal` của Radix. */
13
+ modal?: boolean;
14
+ }
15
+ export declare function Popover({ open, defaultOpen, onOpenChange, modal, children, }: React.PropsWithChildren<PopoverProps>): React.JSX.Element;
16
+ interface PopoverTriggerProps extends React.ComponentPropsWithRef<"button"> {
17
+ /** Mượn thẻ của con thay vì dựng `<button>` riêng. */
18
+ asChild?: boolean;
19
+ }
20
+ export declare function PopoverTrigger({ asChild, onClick, ref, ...props }: PopoverTriggerProps): React.JSX.Element;
21
+ interface PopoverAnchorProps extends React.ComponentPropsWithRef<"span"> {
22
+ /** Mượn thẻ của con thay vì dựng `<span>` riêng. */
23
+ asChild?: boolean;
24
+ }
25
+ export declare function PopoverAnchor({ asChild, ref, ...props }: PopoverAnchorProps): React.JSX.Element;
7
26
  /**
8
27
  * The panel's CONTENT owns its inset — a Command list, a menu or a table that must run edge to
9
28
  * edge and draw its own separators across the full width. The popover drops its own padding by
@@ -14,7 +33,41 @@ export declare function PopoverAnchor(props: React.ComponentProps<typeof Popover
14
33
  type PopoverContentFlush = {
15
34
  flush?: FlushProp;
16
35
  };
17
- export declare const PopoverContent: React.ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & PopoverContentFlush & React.RefAttributes<HTMLDivElement>>;
36
+ interface PopoverContentProps extends React.ComponentPropsWithRef<"div">, PopoverContentFlush {
37
+ /** Cạnh của trigger mà panel bám vào. */
38
+ side?: Side;
39
+ /** Canh panel theo cạnh của trigger. */
40
+ align?: Align;
41
+ /** Khoảng cách theo trục chính, tính bằng px. */
42
+ sideOffset?: number;
43
+ /** Khoảng cách theo trục phụ, tính bằng px. */
44
+ alignOffset?: number;
45
+ /** Lật panel sang cạnh đối diện khi hết chỗ. */
46
+ avoidCollisions?: boolean;
47
+ /** Khoảng chừa với mép khung nhìn. RAC chỉ nhận một số; object lấy cạnh lớn nhất. */
48
+ collisionPadding?: number | Partial<Record<Side, number>>;
49
+ /** Không còn tác dụng trên nền RAC — giữ trong kiểu vì là API công khai. */
50
+ sticky?: "partial" | "always";
51
+ /** Không còn tác dụng trên nền RAC — giữ trong kiểu vì là API công khai. */
52
+ hideWhenDetached?: boolean;
53
+ /** Không còn tác dụng trên nền RAC — giữ trong kiểu vì là API công khai. */
54
+ forceMount?: true;
55
+ /**
56
+ * Chặn việc panel tự lấy tiêu điểm khi mở, bằng `event.preventDefault()`.
57
+ *
58
+ * Năm picker trong kho dựa vào đúng điều này để giữ tiêu điểm ở ô nhập, nên hành vi được chép
59
+ * tay từ Radix chứ không bỏ.
60
+ */
61
+ onOpenAutoFocus?: (event: Event) => void;
62
+ /**
63
+ * Chặn việc trả tiêu điểm về nơi nó đứng trước khi panel mở, bằng `event.preventDefault()`.
64
+ *
65
+ * Phát ở MỌI đường đóng (chọn một mục, Escape, bấm ra ngoài, Tab ra ngoài), nên consumer đưa
66
+ * được tiêu điểm tới đúng chỗ người dùng cần gõ tiếp thay vì để nó quay về trigger.
67
+ */
68
+ onCloseAutoFocus?: (event: Event) => void;
69
+ }
70
+ export declare function PopoverContent({ className, style, children, ref, flush, side, align, sideOffset, alignOffset, avoidCollisions, collisionPadding, sticky: _sticky, hideWhenDetached: _hideWhenDetached, forceMount: _forceMount, onOpenAutoFocus, onCloseAutoFocus, ...props }: PopoverContentProps): React.JSX.Element;
18
71
  export declare const PopoverHeader: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
19
72
  export declare const PopoverTitle: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
20
73
  export declare const PopoverDescription: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => React.JSX.Element;
@@ -1,38 +1,217 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import * as PopoverPrimitive from "@radix-ui/react-popover";
4
+ import { chain, mergeRefs } from "@react-aria/utils";
5
+ import { Popover as AriaPopover } from "react-aria-components";
5
6
  import { cn } from "../../lib/utils.js";
6
- function Popover(props) {
7
- return /* @__PURE__ */ jsx(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
7
+ import { Slot } from "../../lib/slot.js";
8
+ import { useOverlayCloseFocus } from "../feedback/overlay-close-focus.js";
9
+ function toPlacement(side, align) {
10
+ if (align === "center") return side;
11
+ if (side === "top" || side === "bottom") return `${side} ${align}`;
12
+ return `${side} ${align === "start" ? "top" : "bottom"}`;
8
13
  }
9
- function PopoverTrigger(props) {
10
- return /* @__PURE__ */ jsx(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
14
+ function toContainerPadding(padding) {
15
+ if (padding == null) return void 0;
16
+ if (typeof padding === "number") return padding;
17
+ const sides = Object.values(padding).filter(
18
+ (value) => typeof value === "number"
19
+ );
20
+ return sides.length ? Math.max(...sides) : void 0;
11
21
  }
12
- function PopoverAnchor(props) {
13
- return /* @__PURE__ */ jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
22
+ function firstTabbable(container) {
23
+ for (const node of container.querySelectorAll("*")) {
24
+ if (node.tagName === "A") continue;
25
+ if (node.hidden || node.disabled) continue;
26
+ if (node.tagName === "INPUT" && node.type === "hidden") continue;
27
+ if (node.tabIndex >= 0) return node;
28
+ }
29
+ return null;
14
30
  }
15
- const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, flush, style, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
16
- PopoverPrimitive.Content,
17
- {
18
- ref,
19
- "data-slot": "popover-content",
20
- "data-flush": flush ? "" : void 0,
21
- align,
22
- sideOffset,
23
- style: flush ? { ...style, "--popover-space-inset": "0" } : style,
24
- className: cn(
25
- "ui-popover-content origin-[var(--radix-popover-content-transform-origin)]",
26
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
27
- "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
28
- "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
29
- "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
30
- className
31
- ),
32
- ...props
31
+ const PopoverRootContext = React.createContext(null);
32
+ function usePopoverRoot(component) {
33
+ const context = React.useContext(PopoverRootContext);
34
+ if (!context) {
35
+ throw new Error(`\`${component}\` ph\u1EA3i n\u1EB1m trong \`Popover\`.`);
33
36
  }
34
- ) }));
35
- PopoverContent.displayName = PopoverPrimitive.Content.displayName;
37
+ return context;
38
+ }
39
+ function Popover({
40
+ open,
41
+ defaultOpen,
42
+ onOpenChange,
43
+ modal = false,
44
+ children
45
+ }) {
46
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false);
47
+ const [anchored, setAnchored] = React.useState(false);
48
+ const triggerRef = React.useRef(null);
49
+ const anchorRef = React.useRef(null);
50
+ const contentId = React.useId();
51
+ const isOpen = open ?? uncontrolledOpen;
52
+ const setOpen = React.useCallback(
53
+ (next) => {
54
+ if (open === void 0) {
55
+ setUncontrolledOpen(next);
56
+ }
57
+ onOpenChange?.(next);
58
+ },
59
+ [open, onOpenChange]
60
+ );
61
+ const value = React.useMemo(
62
+ () => ({
63
+ open: isOpen,
64
+ setOpen,
65
+ contentId,
66
+ isNonModal: !modal,
67
+ triggerRef,
68
+ anchorRef,
69
+ anchored,
70
+ setAnchored
71
+ }),
72
+ [isOpen, setOpen, contentId, modal, anchored]
73
+ );
74
+ return /* @__PURE__ */ jsx(PopoverRootContext.Provider, { value, children });
75
+ }
76
+ function PopoverTrigger({ asChild, onClick, ref, ...props }) {
77
+ const root = usePopoverRoot("PopoverTrigger");
78
+ const Comp = asChild ? Slot : "button";
79
+ return /* @__PURE__ */ jsx(
80
+ Comp,
81
+ {
82
+ type: "button",
83
+ "data-slot": "popover-trigger",
84
+ "aria-haspopup": "dialog",
85
+ "aria-expanded": root.open,
86
+ "aria-controls": root.open ? root.contentId : void 0,
87
+ "data-state": root.open ? "open" : "closed",
88
+ ...props,
89
+ ref: mergeRefs(ref, root.triggerRef),
90
+ onClick: chain(onClick, () => root.setOpen(!root.open))
91
+ }
92
+ );
93
+ }
94
+ function PopoverAnchor({ asChild, ref, ...props }) {
95
+ const root = usePopoverRoot("PopoverAnchor");
96
+ const Comp = asChild ? Slot : "span";
97
+ const { setAnchored } = root;
98
+ React.useEffect(() => {
99
+ setAnchored(true);
100
+ return () => setAnchored(false);
101
+ }, [setAnchored]);
102
+ return /* @__PURE__ */ jsx(Comp, { "data-slot": "popover-anchor", ...props, ref: mergeRefs(ref, root.anchorRef) });
103
+ }
104
+ function PopoverContent({
105
+ className,
106
+ style,
107
+ children,
108
+ ref,
109
+ flush,
110
+ side = "bottom",
111
+ align = "center",
112
+ sideOffset = 4,
113
+ alignOffset,
114
+ avoidCollisions = true,
115
+ collisionPadding,
116
+ sticky: _sticky,
117
+ hideWhenDetached: _hideWhenDetached,
118
+ forceMount: _forceMount,
119
+ onOpenAutoFocus,
120
+ onCloseAutoFocus,
121
+ ...props
122
+ }) {
123
+ const root = usePopoverRoot("PopoverContent");
124
+ const contentRef = React.useRef(null);
125
+ const openAutoFocusRef = React.useRef(onOpenAutoFocus);
126
+ openAutoFocusRef.current = onOpenAutoFocus;
127
+ useOverlayCloseFocus(root.open, onCloseAutoFocus, contentRef);
128
+ React.useEffect(() => {
129
+ if (!root.open) return;
130
+ const node = contentRef.current;
131
+ if (!node) return;
132
+ const event = new Event("popover.openAutoFocus", { bubbles: false, cancelable: true });
133
+ openAutoFocusRef.current?.(event);
134
+ if (!event.defaultPrevented) {
135
+ const target = firstTabbable(node);
136
+ target?.focus({ preventScroll: true });
137
+ if (!node.contains(document.activeElement)) {
138
+ node.focus({ preventScroll: true });
139
+ }
140
+ }
141
+ }, [root.open]);
142
+ const { open, setOpen, triggerRef } = root;
143
+ React.useEffect(() => {
144
+ if (!open) return;
145
+ const onPointerDown = (event) => {
146
+ const target = event.target;
147
+ if (!target) return;
148
+ if (contentRef.current?.contains(target)) return;
149
+ if (triggerRef.current?.contains(target)) return;
150
+ setOpen(false);
151
+ };
152
+ document.addEventListener("pointerdown", onPointerDown);
153
+ return () => document.removeEventListener("pointerdown", onPointerDown);
154
+ }, [open, setOpen, triggerRef]);
155
+ return /* @__PURE__ */ jsx(
156
+ AriaPopover,
157
+ {
158
+ isOpen: root.open,
159
+ onOpenChange: root.setOpen,
160
+ isNonModal: root.isNonModal,
161
+ triggerRef: root.anchored ? root.anchorRef : root.triggerRef,
162
+ placement: toPlacement(side, align),
163
+ offset: sideOffset,
164
+ crossOffset: alignOffset,
165
+ shouldFlip: avoidCollisions,
166
+ containerPadding: toContainerPadding(collisionPadding),
167
+ render: (racProps, { isExiting }) => {
168
+ const {
169
+ className: _racClassName,
170
+ "data-rac": _rac,
171
+ ref: racRef,
172
+ style: racStyle,
173
+ ...rest
174
+ } = racProps;
175
+ const availableHeight = racStyle?.maxHeight;
176
+ return /* @__PURE__ */ jsx(
177
+ "div",
178
+ {
179
+ ...rest,
180
+ id: root.contentId,
181
+ role: "dialog",
182
+ tabIndex: -1,
183
+ "data-slot": "popover-content",
184
+ "data-side": side,
185
+ "data-align": align,
186
+ "data-state": isExiting ? "closed" : "open",
187
+ "data-flush": flush ? "" : void 0,
188
+ ...props,
189
+ ref: mergeRefs(ref, racRef, contentRef),
190
+ className: cn(
191
+ "ui-popover-content origin-[var(--radix-popover-content-transform-origin)]",
192
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
193
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
194
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
195
+ "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
196
+ className
197
+ ),
198
+ style: {
199
+ ...racStyle,
200
+ "--radix-popover-trigger-width": "var(--trigger-width)",
201
+ "--radix-popover-content-transform-origin": "var(--trigger-anchor-point)",
202
+ ...availableHeight == null ? null : {
203
+ "--radix-popover-content-available-height": typeof availableHeight === "number" ? `${availableHeight}px` : availableHeight
204
+ },
205
+ ...style,
206
+ ...flush ? { "--popover-space-inset": "0" } : null
207
+ },
208
+ children
209
+ }
210
+ );
211
+ }
212
+ }
213
+ );
214
+ }
36
215
  const PopoverHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { "data-slot": "popover-header", className: cn("ui-popover-header", className), ...props });
37
216
  const PopoverTitle = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { "data-slot": "popover-title", className: cn("font-medium", className), ...props });
38
217
  const PopoverDescription = ({
@@ -1,8 +1,29 @@
1
1
  import * as React from "react";
2
2
  export type ProgressTone = "success" | "warning" | "destructive";
3
- export type ProgressProps = {
3
+ /**
4
+ * One slice of a BREAKDOWN bar — how much of the total this state holds, in the SAME unit as every
5
+ * other slice (counts, bytes, yen). Never a percentage: the bar owns the ratio arithmetic, so the
6
+ * caller never has to round three numbers that must still add up to 100.
7
+ *
8
+ * `label` is not decoration. Colour alone cannot carry the meaning of a slice (WCAG 1.4.1), and it
9
+ * is the only thing a screen reader has to say about it, so it is required.
10
+ */
11
+ export type ProgressSegment = {
4
12
  value: number;
13
+ tone: ProgressTone;
14
+ label: string;
15
+ };
16
+ type ProgressBase = Omit<React.HTMLAttributes<HTMLDivElement>, "role" | "children" | "aria-valuenow" | "aria-valuemin" | "aria-valuemax" | "aria-valuetext"> & {
17
+ /**
18
+ * Visible caption under the bar; it also becomes the bar's accessible name. Omit it and the bar
19
+ * falls back to the catalogue name — or to whatever `aria-label`/`aria-labelledby` the caller
20
+ * passes, which is how a bar gets its name from a heading that is already on screen.
21
+ */
5
22
  label?: string;
23
+ };
24
+ /** A METER: one ratio of one whole. */
25
+ type ProgressMeterProps = ProgressBase & {
26
+ value: number;
6
27
  tone?: ProgressTone;
7
28
  /**
8
29
  * Allow `value` to exceed 100 and render an OVER-CAPACITY fill: the bar caps at 100% width but
@@ -11,5 +32,32 @@ export type ProgressProps = {
11
32
  * real ratio.
12
33
  */
13
34
  over?: boolean;
35
+ segments?: never;
36
+ };
37
+ /**
38
+ * A BREAKDOWN: one whole split into its states.
39
+ *
40
+ * ## Why this is not a meter with three calls
41
+ *
42
+ * A meter answers "how far along?" and has ONE value, so `role="progressbar"` fits it exactly.
43
+ * A breakdown answers "what is this total made of?" — 2 overdue · 3 due soon · 12 done. Stacking
44
+ * three progressbars says the wrong thing three times over (three separate 0–100 measurements),
45
+ * and no ARIA value pattern models a partition. It is a picture of data, so it names itself once
46
+ * as `role="img"` and reads out every slice with its own label and amount.
47
+ *
48
+ * ## Why it is a taller bar than the meter
49
+ *
50
+ * A 0.5rem pill is legible when it carries one fill against one track. Three abutting fills at
51
+ * that height read as a coloured hairline — the ratios, which are the entire point, stop being
52
+ * comparable. So the breakdown takes its own block size and corner, both retunable
53
+ * (`--progress-breakdown-*`).
54
+ */
55
+ type ProgressBreakdownProps = ProgressBase & {
56
+ segments: ProgressSegment[];
57
+ value?: never;
58
+ tone?: never;
59
+ over?: never;
14
60
  };
15
- export declare function Progress({ value, label, tone, over }: ProgressProps): React.JSX.Element;
61
+ export type ProgressProps = ProgressMeterProps | ProgressBreakdownProps;
62
+ export declare function Progress(props: ProgressProps): React.JSX.Element;
63
+ export {};