@gouno/ui 0.3.2

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 (231) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/LICENSE +21 -0
  3. package/README.md +144 -0
  4. package/dist/base.css +239 -0
  5. package/dist/bootstrap.js +22 -0
  6. package/dist/components/primitives/alert-dialog.d.ts +18 -0
  7. package/dist/components/primitives/alert-dialog.js +43 -0
  8. package/dist/components/primitives/alert.d.ts +9 -0
  9. package/dist/components/primitives/alert.js +25 -0
  10. package/dist/components/primitives/avatar.d.ts +14 -0
  11. package/dist/components/primitives/avatar.js +42 -0
  12. package/dist/components/primitives/badge.d.ts +9 -0
  13. package/dist/components/primitives/badge.js +25 -0
  14. package/dist/components/primitives/button.d.ts +10 -0
  15. package/dist/components/primitives/button.js +36 -0
  16. package/dist/components/primitives/checkbox.d.ts +4 -0
  17. package/dist/components/primitives/checkbox.js +10 -0
  18. package/dist/components/primitives/dialog.d.ts +20 -0
  19. package/dist/components/primitives/dialog.js +37 -0
  20. package/dist/components/primitives/dropdown-menu.d.ts +25 -0
  21. package/dist/components/primitives/dropdown-menu.js +51 -0
  22. package/dist/components/primitives/empty.d.ts +11 -0
  23. package/dist/components/primitives/empty.js +33 -0
  24. package/dist/components/primitives/field.d.ts +24 -0
  25. package/dist/components/primitives/field.js +75 -0
  26. package/dist/components/primitives/input.d.ts +3 -0
  27. package/dist/components/primitives/input.js +7 -0
  28. package/dist/components/primitives/label.d.ts +4 -0
  29. package/dist/components/primitives/label.js +8 -0
  30. package/dist/components/primitives/popover.d.ts +16 -0
  31. package/dist/components/primitives/popover.js +31 -0
  32. package/dist/components/primitives/radio-group.d.ts +5 -0
  33. package/dist/components/primitives/radio-group.js +13 -0
  34. package/dist/components/primitives/select.d.ts +15 -0
  35. package/dist/components/primitives/select.js +38 -0
  36. package/dist/components/primitives/separator.d.ts +4 -0
  37. package/dist/components/primitives/separator.js +8 -0
  38. package/dist/components/primitives/sheet.d.ts +17 -0
  39. package/dist/components/primitives/sheet.js +41 -0
  40. package/dist/components/primitives/skeleton.d.ts +2 -0
  41. package/dist/components/primitives/skeleton.js +6 -0
  42. package/dist/components/primitives/switch.d.ts +6 -0
  43. package/dist/components/primitives/switch.js +8 -0
  44. package/dist/components/primitives/table.d.ts +28 -0
  45. package/dist/components/primitives/table.js +44 -0
  46. package/dist/components/primitives/tabs.d.ts +11 -0
  47. package/dist/components/primitives/tabs.js +30 -0
  48. package/dist/components/primitives/textarea.d.ts +3 -0
  49. package/dist/components/primitives/textarea.js +7 -0
  50. package/dist/components/primitives/tooltip.d.ts +13 -0
  51. package/dist/components/primitives/tooltip.js +24 -0
  52. package/dist/core/affix.d.ts +14 -0
  53. package/dist/core/affix.js +29 -0
  54. package/dist/core/alert.d.ts +54 -0
  55. package/dist/core/alert.js +56 -0
  56. package/dist/core/anchor.d.ts +16 -0
  57. package/dist/core/anchor.js +31 -0
  58. package/dist/core/app.d.ts +5 -0
  59. package/dist/core/app.js +5 -0
  60. package/dist/core/aspect-ratio.d.ts +6 -0
  61. package/dist/core/aspect-ratio.js +6 -0
  62. package/dist/core/autocomplete.d.ts +24 -0
  63. package/dist/core/autocomplete.js +139 -0
  64. package/dist/core/avatar.d.ts +26 -0
  65. package/dist/core/avatar.js +20 -0
  66. package/dist/core/badge.d.ts +16 -0
  67. package/dist/core/badge.js +22 -0
  68. package/dist/core/breadcrumb.d.ts +55 -0
  69. package/dist/core/breadcrumb.js +69 -0
  70. package/dist/core/button.d.ts +49 -0
  71. package/dist/core/button.js +66 -0
  72. package/dist/core/calendar.d.ts +48 -0
  73. package/dist/core/calendar.js +179 -0
  74. package/dist/core/card.d.ts +30 -0
  75. package/dist/core/card.js +12 -0
  76. package/dist/core/carousel.d.ts +49 -0
  77. package/dist/core/carousel.js +166 -0
  78. package/dist/core/cascader.d.ts +19 -0
  79. package/dist/core/cascader.js +45 -0
  80. package/dist/core/code-block.d.ts +16 -0
  81. package/dist/core/code-block.js +47 -0
  82. package/dist/core/collapse.d.ts +49 -0
  83. package/dist/core/collapse.js +96 -0
  84. package/dist/core/color-picker.d.ts +9 -0
  85. package/dist/core/color-picker.js +8 -0
  86. package/dist/core/config-provider.d.ts +10 -0
  87. package/dist/core/config-provider.js +14 -0
  88. package/dist/core/control-types.d.ts +2 -0
  89. package/dist/core/control-types.js +7 -0
  90. package/dist/core/date-picker.d.ts +13 -0
  91. package/dist/core/date-picker.js +14 -0
  92. package/dist/core/date-range-picker.d.ts +26 -0
  93. package/dist/core/date-range-picker.js +10 -0
  94. package/dist/core/descriptions.d.ts +41 -0
  95. package/dist/core/descriptions.js +133 -0
  96. package/dist/core/drawer.d.ts +9 -0
  97. package/dist/core/drawer.js +58 -0
  98. package/dist/core/empty.d.ts +8 -0
  99. package/dist/core/empty.js +5 -0
  100. package/dist/core/feedback.d.ts +5 -0
  101. package/dist/core/feedback.js +5 -0
  102. package/dist/core/field.d.ts +15 -0
  103. package/dist/core/field.js +19 -0
  104. package/dist/core/float-button.d.ts +13 -0
  105. package/dist/core/float-button.js +22 -0
  106. package/dist/core/form.d.ts +19 -0
  107. package/dist/core/form.js +56 -0
  108. package/dist/core/icon-button.d.ts +7 -0
  109. package/dist/core/icon-button.js +4 -0
  110. package/dist/core/icon.d.ts +17 -0
  111. package/dist/core/icon.js +35 -0
  112. package/dist/core/image.d.ts +76 -0
  113. package/dist/core/image.js +189 -0
  114. package/dist/core/index.d.ts +79 -0
  115. package/dist/core/index.js +77 -0
  116. package/dist/core/input-number.d.ts +24 -0
  117. package/dist/core/input-number.js +70 -0
  118. package/dist/core/input-otp.d.ts +14 -0
  119. package/dist/core/input-otp.js +76 -0
  120. package/dist/core/input.d.ts +14 -0
  121. package/dist/core/input.js +51 -0
  122. package/dist/core/kbd.d.ts +8 -0
  123. package/dist/core/kbd.js +11 -0
  124. package/dist/core/layout-primitives.d.ts +11 -0
  125. package/dist/core/layout-primitives.js +8 -0
  126. package/dist/core/layout.d.ts +71 -0
  127. package/dist/core/layout.js +119 -0
  128. package/dist/core/list.d.ts +37 -0
  129. package/dist/core/list.js +35 -0
  130. package/dist/core/locale.d.ts +48 -0
  131. package/dist/core/locale.js +32 -0
  132. package/dist/core/mentions.d.ts +10 -0
  133. package/dist/core/mentions.js +112 -0
  134. package/dist/core/menu.d.ts +79 -0
  135. package/dist/core/menu.js +265 -0
  136. package/dist/core/message.d.ts +16 -0
  137. package/dist/core/message.js +36 -0
  138. package/dist/core/modal.d.ts +40 -0
  139. package/dist/core/modal.js +67 -0
  140. package/dist/core/notification.d.ts +15 -0
  141. package/dist/core/notification.js +39 -0
  142. package/dist/core/page-layout.d.ts +6 -0
  143. package/dist/core/page-layout.js +7 -0
  144. package/dist/core/pagination.d.ts +28 -0
  145. package/dist/core/pagination.js +69 -0
  146. package/dist/core/popconfirm.d.ts +13 -0
  147. package/dist/core/popconfirm.js +42 -0
  148. package/dist/core/progress.d.ts +6 -0
  149. package/dist/core/progress.js +8 -0
  150. package/dist/core/public-props.d.ts +82 -0
  151. package/dist/core/public-props.js +1 -0
  152. package/dist/core/qrcode.d.ts +14 -0
  153. package/dist/core/qrcode.js +17 -0
  154. package/dist/core/rate.d.ts +11 -0
  155. package/dist/core/rate.js +48 -0
  156. package/dist/core/result.d.ts +12 -0
  157. package/dist/core/result.js +19 -0
  158. package/dist/core/segmented.d.ts +29 -0
  159. package/dist/core/segmented.js +38 -0
  160. package/dist/core/select.d.ts +27 -0
  161. package/dist/core/select.js +143 -0
  162. package/dist/core/selection-controls.d.ts +12 -0
  163. package/dist/core/selection-controls.js +23 -0
  164. package/dist/core/separator.d.ts +24 -0
  165. package/dist/core/separator.js +17 -0
  166. package/dist/core/slider.d.ts +2 -0
  167. package/dist/core/slider.js +6 -0
  168. package/dist/core/spin.d.ts +7 -0
  169. package/dist/core/spin.js +7 -0
  170. package/dist/core/spinner.d.ts +3 -0
  171. package/dist/core/spinner.js +6 -0
  172. package/dist/core/splitter.d.ts +44 -0
  173. package/dist/core/splitter.js +208 -0
  174. package/dist/core/statistic.d.ts +12 -0
  175. package/dist/core/statistic.js +6 -0
  176. package/dist/core/steps.d.ts +54 -0
  177. package/dist/core/steps.js +106 -0
  178. package/dist/core/tabs.d.ts +40 -0
  179. package/dist/core/tabs.js +80 -0
  180. package/dist/core/tag.d.ts +19 -0
  181. package/dist/core/tag.js +29 -0
  182. package/dist/core/textarea.d.ts +8 -0
  183. package/dist/core/textarea.js +14 -0
  184. package/dist/core/time-picker.d.ts +9 -0
  185. package/dist/core/time-picker.js +8 -0
  186. package/dist/core/timeline.d.ts +37 -0
  187. package/dist/core/timeline.js +48 -0
  188. package/dist/core/tour.d.ts +18 -0
  189. package/dist/core/tour.js +51 -0
  190. package/dist/core/transfer.d.ts +16 -0
  191. package/dist/core/transfer.js +46 -0
  192. package/dist/core/tree-select.d.ts +19 -0
  193. package/dist/core/tree-select.js +19 -0
  194. package/dist/core/tree.d.ts +89 -0
  195. package/dist/core/tree.js +386 -0
  196. package/dist/core/typography.d.ts +20 -0
  197. package/dist/core/typography.js +12 -0
  198. package/dist/core/upload.d.ts +20 -0
  199. package/dist/core/upload.js +72 -0
  200. package/dist/core/watermark.d.ts +10 -0
  201. package/dist/core/watermark.js +21 -0
  202. package/dist/fonts/inter-LICENSE +93 -0
  203. package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
  204. package/dist/fonts/jetbrains-mono-LICENSE +93 -0
  205. package/dist/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  206. package/dist/gouno/app-shell.d.ts +22 -0
  207. package/dist/gouno/app-shell.js +27 -0
  208. package/dist/gouno/index.d.ts +4 -0
  209. package/dist/gouno/index.js +4 -0
  210. package/dist/gouno/page-container.d.ts +4 -0
  211. package/dist/gouno/page-container.js +6 -0
  212. package/dist/gouno/page-header.d.ts +8 -0
  213. package/dist/gouno/page-header.js +6 -0
  214. package/dist/hooks/use-overlay-body.d.ts +6 -0
  215. package/dist/hooks/use-overlay-body.js +18 -0
  216. package/dist/index.d.ts +5 -0
  217. package/dist/index.js +5 -0
  218. package/dist/lib/utils.d.ts +2 -0
  219. package/dist/lib/utils.js +5 -0
  220. package/dist/patterns/bulk-action-bar.d.ts +8 -0
  221. package/dist/patterns/bulk-action-bar.js +7 -0
  222. package/dist/patterns/index.d.ts +5 -0
  223. package/dist/patterns/index.js +5 -0
  224. package/dist/theme/index.d.ts +2 -0
  225. package/dist/theme/index.js +2 -0
  226. package/dist/theme/provider.d.ts +22 -0
  227. package/dist/theme/provider.js +76 -0
  228. package/dist/theme/theme-toggle.d.ts +6 -0
  229. package/dist/theme/theme-toggle.js +10 -0
  230. package/dist/tokens.css +164 -0
  231. package/package.json +111 -0
@@ -0,0 +1,106 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import {} from "react";
3
+ import { Check, X } from "lucide-react";
4
+ import { cn } from "../lib/utils.js";
5
+ function clampPercent(value) {
6
+ if (value === undefined || !Number.isFinite(value))
7
+ return undefined;
8
+ return Math.min(100, Math.max(0, value));
9
+ }
10
+ function resolveStatus(item, index, current, currentStatus) {
11
+ if (item.status)
12
+ return item.status;
13
+ if (index < current)
14
+ return "finish";
15
+ if (index === current)
16
+ return currentStatus;
17
+ return "wait";
18
+ }
19
+ function visibleSteps(items, current, maxCount) {
20
+ const all = items.map((item, index) => ({ kind: "item", item, index }));
21
+ const limit = maxCount === undefined ? undefined : Math.floor(maxCount);
22
+ if (!limit || limit < 3 || items.length <= limit)
23
+ return all;
24
+ if (current <= Math.floor((limit - 1) / 2)) {
25
+ return [
26
+ ...all.slice(0, limit - 1),
27
+ { kind: "ellipsis", key: "steps-ellipsis-end" },
28
+ ];
29
+ }
30
+ if (current >= items.length - Math.ceil((limit - 1) / 2)) {
31
+ return [
32
+ { kind: "ellipsis", key: "steps-ellipsis-start" },
33
+ ...all.slice(items.length - (limit - 1)),
34
+ ];
35
+ }
36
+ const windowSize = limit - 2;
37
+ const start = Math.max(0, Math.min(current - Math.floor(windowSize / 2), items.length - windowSize));
38
+ return [
39
+ { kind: "ellipsis", key: "steps-ellipsis-start" },
40
+ ...all.slice(start, start + windowSize),
41
+ { kind: "ellipsis", key: "steps-ellipsis-end" },
42
+ ];
43
+ }
44
+ function defaultMarker(item, index, initial, status, type) {
45
+ if (item.icon)
46
+ return item.icon;
47
+ if (type === "dot")
48
+ return null;
49
+ if (status === "finish")
50
+ return _jsx(Check, { "aria-hidden": "true" });
51
+ if (status === "error")
52
+ return _jsx(X, { "aria-hidden": "true" });
53
+ return initial + index + 1;
54
+ }
55
+ export function Steps({ items, current = 0, initial = 0, orientation = "horizontal", titlePlacement = "horizontal", type = "default", variant = "filled", size = "middle", status = "process", percent, maxCount, responsive = true, iconRender, onChange, classNames, styles, className, style, ref, ...props }) {
56
+ const effectiveTitlePlacement = type === "dot" ? "vertical" : titlePlacement;
57
+ const semanticInfo = {
58
+ props: {
59
+ orientation,
60
+ titlePlacement: effectiveTitlePlacement,
61
+ type,
62
+ variant,
63
+ size,
64
+ },
65
+ };
66
+ const semanticClassNames = typeof classNames === "function" ? classNames(semanticInfo) : (classNames ?? {});
67
+ const semanticStyles = typeof styles === "function" ? styles(semanticInfo) : (styles ?? {});
68
+ const entries = visibleSteps(items, current, maxCount);
69
+ const normalizedPercent = clampPercent(percent);
70
+ return (_jsx("ol", { ...props, ref: ref, "data-slot": "steps", "data-orientation": orientation, "data-type": type, "data-variant": variant, className: cn("m-0 min-w-0 list-none p-0", orientation === "vertical"
71
+ ? "flex flex-col gap-1"
72
+ : "flex items-start gap-2 overflow-x-auto", responsive &&
73
+ orientation === "horizontal" &&
74
+ "max-[531px]:flex-col max-[531px]:overflow-visible", semanticClassNames.root, className), style: { ...semanticStyles.root, ...style }, children: entries.map((entry, visibleIndex) => {
75
+ if (entry.kind === "ellipsis") {
76
+ return (_jsx("li", { "aria-disabled": "true", "data-slot": "steps-item", "data-ellipsis": "true", className: cn("flex min-h-8 min-w-10 shrink-0 items-center justify-center text-sm text-muted-foreground", orientation === "horizontal" && "self-start", semanticClassNames.item), style: semanticStyles.item, children: _jsx("span", { children: "\u2026" }) }, entry.key));
77
+ }
78
+ const { item, index } = entry;
79
+ const itemStatus = resolveStatus(item, index, current, status);
80
+ const active = index === current;
81
+ const clickable = Boolean(onChange) && !item.disabled;
82
+ const originMarker = defaultMarker(item, index, initial, itemStatus, type);
83
+ const marker = iconRender?.(originMarker, {
84
+ index,
85
+ active,
86
+ status: itemStatus,
87
+ item,
88
+ }) ?? originMarker;
89
+ const showProgress = active &&
90
+ itemStatus === "process" &&
91
+ type === "default" &&
92
+ normalizedPercent !== undefined;
93
+ const hasConnector = visibleIndex < entries.length - 1;
94
+ const body = (_jsxs(_Fragment, { children: [_jsx("div", { "data-slot": "steps-marker", className: cn("relative z-[1] flex shrink-0 items-center justify-center rounded-full border text-xs font-medium [&_svg]:size-3.5", type === "dot"
95
+ ? "mt-1.5 size-2 border-current p-0"
96
+ : size === "small"
97
+ ? "size-5"
98
+ : "size-7", variant === "filled" && itemStatus === "process" && "border-primary bg-primary text-primary-foreground", variant === "filled" && itemStatus === "finish" && "border-primary/20 bg-primary/10 text-primary", variant === "filled" && itemStatus === "wait" && "border-muted bg-muted text-muted-foreground", variant === "filled" && itemStatus === "error" && "border-destructive/20 bg-destructive/10 text-destructive", variant === "outlined" && itemStatus === "process" && "border-primary bg-background text-primary", variant === "outlined" && itemStatus === "finish" && "border-primary/50 bg-background text-primary", variant === "outlined" && itemStatus === "wait" && "border-border bg-background text-muted-foreground", variant === "outlined" && itemStatus === "error" && "border-destructive bg-background text-destructive", type === "dot" && itemStatus === "process" && "bg-primary", type === "dot" && itemStatus === "finish" && "bg-primary/70", type === "dot" && itemStatus === "wait" && "bg-muted-foreground/30", type === "dot" && itemStatus === "error" && "bg-destructive", semanticClassNames.marker), style: semanticStyles.marker, children: marker }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 items-baseline gap-2", children: [_jsx("span", { "data-slot": "steps-title", className: cn("min-w-0 truncate font-medium", size === "small" ? "text-xs" : "text-sm", itemStatus === "wait" ? "text-muted-foreground" : "text-foreground", itemStatus === "error" && "text-destructive", semanticClassNames.title), style: semanticStyles.title, children: item.title }), item.subTitle !== undefined ? (_jsx("span", { "data-slot": "steps-subtitle", className: cn("shrink-0 text-xs text-muted-foreground", semanticClassNames.subTitle), style: semanticStyles.subTitle, children: item.subTitle })) : null] }), item.content !== undefined ? (_jsx("div", { "data-slot": "steps-content", className: cn("mt-1 text-xs text-muted-foreground", semanticClassNames.content), style: semanticStyles.content, children: item.content })) : null, showProgress ? (_jsx("div", { role: "progressbar", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": normalizedPercent, "data-slot": "steps-progress", className: cn("mt-2 h-1 w-full overflow-hidden rounded-full bg-muted", semanticClassNames.progress), style: semanticStyles.progress, children: _jsx("span", { className: "block h-full rounded-full bg-primary transition-[width]", style: { width: `${normalizedPercent}%` } }) })) : null] })] }));
99
+ return (_jsxs("li", { "data-slot": "steps-item", "data-status": itemStatus, "data-active": active ? "true" : undefined, className: cn("relative min-w-0", orientation === "horizontal" ? "min-w-36 flex-1" : "w-full pb-3", type === "panel" && "rounded-lg border border-border p-3", type === "navigation" && active && "border-b-2 border-primary", item.disabled && "opacity-60", semanticClassNames.item), style: semanticStyles.item, children: [hasConnector && type !== "panel" && type !== "navigation" && type !== "inline" ? (_jsx("span", { "aria-hidden": "true", "data-slot": "steps-connector", className: cn("absolute bg-border", orientation === "horizontal"
100
+ ? cn(size === "small" ? "left-5 top-2.5" : "left-7 top-3.5", "right-0 h-px")
101
+ : cn(size === "small" ? "left-2.5 top-5" : "left-3.5 top-7", "bottom-0 w-px"), semanticClassNames.connector), style: semanticStyles.connector })) : null, clickable ? (_jsx("button", { type: "button", disabled: item.disabled, "aria-current": active ? "step" : undefined, onClick: () => {
102
+ if (index !== current)
103
+ onChange?.(index);
104
+ }, className: cn("relative z-[2] flex w-full min-w-0 items-start gap-2 rounded-md text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", effectiveTitlePlacement === "vertical" && "flex-col items-start", type === "inline" && "items-center py-1"), children: body })) : (_jsx("div", { "aria-current": active ? "step" : undefined, "aria-disabled": item.disabled || undefined, className: cn("relative z-[2] flex min-w-0 items-start gap-2", effectiveTitlePlacement === "vertical" && "flex-col items-start", type === "inline" && "items-center py-1"), children: body }))] }, item.key));
105
+ }) }));
106
+ }
@@ -0,0 +1,40 @@
1
+ import type { ComponentProps, ReactNode } from "react";
2
+ import * as Primitive from "../components/primitives/tabs.js";
3
+ import type { ControlSize } from "./control-types.js";
4
+ export type TabsType = "line" | "card";
5
+ export type TabsPosition = "top" | "right" | "bottom" | "left";
6
+ export interface TabItem<T extends string = string> {
7
+ key: T;
8
+ label: ReactNode;
9
+ children?: ReactNode;
10
+ icon?: ReactNode;
11
+ disabled?: boolean;
12
+ }
13
+ type TabsRootProps = ComponentProps<typeof Primitive.Tabs>;
14
+ export interface TabsProps<T extends string = string> extends Omit<TabsRootProps, "defaultValue" | "onChange" | "onValueChange" | "orientation" | "value"> {
15
+ activeKey?: T;
16
+ defaultActiveKey?: T;
17
+ items?: readonly TabItem<T>[];
18
+ onChange?: (activeKey: T) => void;
19
+ type?: TabsType;
20
+ size?: ControlSize;
21
+ tabPosition?: TabsPosition;
22
+ centered?: boolean;
23
+ tabBarExtraContent?: ReactNode;
24
+ /** @deprecated Use the standard `aria-label` attribute. Retained until product artifact migration. */
25
+ ariaLabel?: string;
26
+ }
27
+ export declare function Tabs<T extends string = string>({ activeKey, defaultActiveKey, items, onChange, type, size, tabPosition, centered, tabBarExtraContent, className, children, style, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ariaLabel: legacyAriaLabel, ...props }: TabsProps<T>): import("react").JSX.Element;
28
+ export declare function TabList({ className, type, size, tabPosition, centered, extra, style, children, ...props }: Omit<ComponentProps<typeof Primitive.TabsList>, "variant"> & {
29
+ type?: TabsType;
30
+ size?: ControlSize;
31
+ tabPosition?: TabsPosition;
32
+ centered?: boolean;
33
+ extra?: ReactNode;
34
+ }): import("react").JSX.Element;
35
+ export declare function Tab({ className, size, tabPosition, ...props }: ComponentProps<typeof Primitive.TabsTrigger> & {
36
+ size?: ControlSize;
37
+ tabPosition?: TabsPosition;
38
+ }): import("react").JSX.Element;
39
+ export declare function TabPanel({ className, ...props }: ComponentProps<typeof Primitive.TabsContent>): import("react").JSX.Element;
40
+ export {};
@@ -0,0 +1,80 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import * as Primitive from "../components/primitives/tabs.js";
3
+ import { cn } from "../lib/utils.js";
4
+ const gapBySize = {
5
+ small: 16,
6
+ middle: 24,
7
+ large: 28,
8
+ };
9
+ const triggerSizeClass = {
10
+ small: "!h-8 px-1.5 !py-0 text-sm",
11
+ middle: "!h-10 px-2 !py-0 text-sm",
12
+ large: "!h-11 px-2.5 !py-0 text-base",
13
+ };
14
+ function orientationFor(position) {
15
+ return position === "left" || position === "right" ? "vertical" : "horizontal";
16
+ }
17
+ function flexDirectionFor(position) {
18
+ return {
19
+ top: "column",
20
+ bottom: "column-reverse",
21
+ left: "row",
22
+ right: "row-reverse",
23
+ }[position];
24
+ }
25
+ function listEdgeClass(position) {
26
+ switch (position) {
27
+ case "bottom":
28
+ return "w-full border-t border-border/70";
29
+ case "left":
30
+ return "h-fit w-48 shrink-0 border-r border-border/70";
31
+ case "right":
32
+ return "h-fit w-48 shrink-0 border-l border-border/70";
33
+ default:
34
+ return "w-full border-b border-border/70";
35
+ }
36
+ }
37
+ function lineIndicatorPositionClass(position) {
38
+ switch (position) {
39
+ case "bottom":
40
+ return "[&::after]:!top-0 [&::after]:!bottom-auto";
41
+ case "right":
42
+ return "[&::after]:!left-0 [&::after]:!right-auto";
43
+ default:
44
+ return "";
45
+ }
46
+ }
47
+ function cardTriggerPositionClass(position) {
48
+ switch (position) {
49
+ case "bottom":
50
+ return "group-data-[variant=default]/tabs-list:!rounded-t-none group-data-[variant=default]/tabs-list:!rounded-b-md group-data-[variant=default]/tabs-list:!border-t-0";
51
+ case "left":
52
+ return "group-data-[variant=default]/tabs-list:!rounded-r-none group-data-[variant=default]/tabs-list:!rounded-l-md group-data-[variant=default]/tabs-list:!border-r-0";
53
+ case "right":
54
+ return "group-data-[variant=default]/tabs-list:!rounded-l-none group-data-[variant=default]/tabs-list:!rounded-r-md group-data-[variant=default]/tabs-list:!border-l-0";
55
+ default:
56
+ return "group-data-[variant=default]/tabs-list:!rounded-b-none group-data-[variant=default]/tabs-list:!rounded-t-md group-data-[variant=default]/tabs-list:!border-b-0";
57
+ }
58
+ }
59
+ export function Tabs({ activeKey, defaultActiveKey, items, onChange, type = "line", size = "middle", tabPosition = "top", centered = false, tabBarExtraContent, className, children, style, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ariaLabel: legacyAriaLabel, ...props }) {
60
+ const firstEnabledKey = items?.find((item) => !item.disabled)?.key;
61
+ const orientation = orientationFor(tabPosition);
62
+ const resolvedDefaultKey = defaultActiveKey ?? (activeKey === undefined ? firstEnabledKey : undefined);
63
+ return (_jsxs(Primitive.Tabs, { ...props, value: activeKey, defaultValue: resolvedDefaultKey, onValueChange: (next) => onChange?.(next), orientation: orientation, "data-tab-position": tabPosition, "data-tab-type": type, "data-tab-size": size, className: cn("gap-5", className), style: { ...style, flexDirection: flexDirectionFor(tabPosition) }, children: [items ? (_jsx(TabList, { "aria-label": ariaLabel ?? legacyAriaLabel, "aria-labelledby": ariaLabelledBy, type: type, size: size, tabPosition: tabPosition, centered: centered, extra: tabBarExtraContent, children: items.map((item) => (_jsxs(Tab, { value: item.key, disabled: item.disabled, size: size, tabPosition: tabPosition, children: [item.icon, item.label] }, item.key))) })) : null, items?.map((item) => item.children === undefined ? null : (_jsx(TabPanel, { value: item.key, children: item.children }, item.key))), children] }));
64
+ }
65
+ export function TabList({ className, type = "line", size = "middle", tabPosition = "top", centered = false, extra, style, children, ...props }) {
66
+ const vertical = tabPosition === "left" || tabPosition === "right";
67
+ return (_jsxs(Primitive.TabsList, { ...props, variant: type === "line" ? "line" : "default", "data-tab-position": tabPosition, className: cn("max-w-full justify-start !rounded-none !bg-transparent !p-0", vertical
68
+ ? "max-h-full flex-col items-stretch overflow-x-hidden overflow-y-auto"
69
+ : "overflow-x-auto overflow-y-hidden", listEdgeClass(tabPosition), className), style: {
70
+ ...style,
71
+ gap: type === "card" ? 4 : gapBySize[size],
72
+ justifyContent: centered && !extra ? "center" : "flex-start",
73
+ }, children: [children, extra ? (_jsx("span", { className: cn("shrink-0", vertical ? "mt-2" : "ml-auto pl-4"), children: extra })) : null] }));
74
+ }
75
+ export function Tab({ className, size = "middle", tabPosition = "top", ...props }) {
76
+ return (_jsx(Primitive.TabsTrigger, { ...props, "data-tab-position": tabPosition, className: cn("!flex-none font-medium transition-colors", triggerSizeClass[size], "group-data-[variant=line]/tabs-list:!rounded-none group-data-[variant=line]/tabs-list:!border-0 group-data-[variant=line]/tabs-list:!bg-transparent group-data-[variant=line]/tabs-list:!px-0 group-data-[variant=line]/tabs-list:!shadow-none", "group-data-[variant=line]/tabs-list:text-muted-foreground group-data-[variant=line]/tabs-list:hover:text-foreground group-data-[variant=line]/tabs-list:data-[state=active]:!bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:text-primary", "group-data-[variant=line]/tabs-list:after:bg-primary group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100", "group-data-[variant=default]/tabs-list:border group-data-[variant=default]/tabs-list:border-border/70 group-data-[variant=default]/tabs-list:bg-muted/35 group-data-[variant=default]/tabs-list:data-[state=active]:bg-background group-data-[variant=default]/tabs-list:data-[state=active]:text-foreground", lineIndicatorPositionClass(tabPosition), cardTriggerPositionClass(tabPosition), className) }));
77
+ }
78
+ export function TabPanel({ className, ...props }) {
79
+ return _jsx(Primitive.TabsContent, { ...props, className: cn("min-w-0 flex-1", className) });
80
+ }
@@ -0,0 +1,19 @@
1
+ import { type HTMLAttributes, type MouseEvent, type ReactNode } from "react";
2
+ export type TagColor = "default" | "primary" | "success" | "warning" | "error" | "info";
3
+ export interface TagProps extends Omit<HTMLAttributes<HTMLSpanElement>, "color" | "onChange"> {
4
+ /** A semantic color, or a CSS color string for a custom background. */
5
+ color?: TagColor | string;
6
+ icon?: ReactNode;
7
+ bordered?: boolean;
8
+ closable?: boolean;
9
+ closeIcon?: ReactNode;
10
+ onClose?: (event: MouseEvent<HTMLButtonElement>) => void;
11
+ checkable?: boolean;
12
+ checked?: boolean;
13
+ defaultChecked?: boolean;
14
+ onChange?: (checked: boolean) => void;
15
+ disabled?: boolean;
16
+ }
17
+ export declare function Tag({ color, icon, bordered, closable, closeIcon, onClose, checkable, checked, defaultChecked, onChange, disabled, children, className, style, ...props }: TagProps): import("react").JSX.Element;
18
+ export type CheckableTagProps = Omit<TagProps, "checkable">;
19
+ export declare function CheckableTag(props: CheckableTagProps): import("react").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { X } from "lucide-react";
4
+ import { cn } from "../lib/utils.js";
5
+ const colorClass = {
6
+ success: "border-success/30 bg-success-subtle text-success",
7
+ warning: "border-warning/30 bg-warning-subtle text-warning",
8
+ error: "border-destructive/30 bg-danger-subtle text-destructive",
9
+ info: "border-info/30 bg-info-subtle text-info",
10
+ primary: "border-primary/30 bg-accent text-accent-foreground",
11
+ default: "border-border bg-muted text-muted-foreground",
12
+ };
13
+ function isSemanticColor(color) {
14
+ return typeof color === "string" && color in colorClass;
15
+ }
16
+ export function Tag({ color = "default", icon, bordered = true, closable, closeIcon, onClose, checkable, checked, defaultChecked = false, onChange, disabled, children, className, style, ...props }) {
17
+ const [internalChecked, setInternalChecked] = useState(defaultChecked);
18
+ const selected = checked ?? internalChecked;
19
+ const semanticColor = isSemanticColor(color);
20
+ const classes = cn("inline-flex min-h-6 items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium", bordered && "border", semanticColor ? colorClass[color] : "text-white", checkable && "cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", checkable && selected && "border-primary bg-primary text-primary-foreground", disabled && "pointer-events-none opacity-50", className);
21
+ const content = _jsxs(_Fragment, { children: [icon ? _jsx("span", { className: "inline-flex [&_svg]:size-3.5", "aria-hidden": "true", children: icon }) : null, _jsx("span", { children: children }), closable ? _jsx("button", { type: "button", "aria-label": `关闭 ${String(children ?? "标签")}`, disabled: disabled, className: "-mr-1 inline-flex rounded p-0.5 hover:bg-black/10", onClick: (event) => { event.stopPropagation(); onClose?.(event); }, children: closeIcon ?? _jsx(X, { className: "size-3" }) }) : null] });
22
+ if (checkable)
23
+ return _jsx("button", { ...props, type: "button", role: "checkbox", "aria-checked": selected, disabled: disabled, className: classes, style: { ...style, backgroundColor: selected || semanticColor ? undefined : color }, onClick: () => { const next = !selected; if (checked === undefined)
24
+ setInternalChecked(next); onChange?.(next); }, children: content });
25
+ return _jsx("span", { ...props, className: classes, style: { ...style, backgroundColor: semanticColor ? undefined : color }, children: content });
26
+ }
27
+ export function CheckableTag(props) {
28
+ return _jsx(Tag, { ...props, checkable: true });
29
+ }
@@ -0,0 +1,8 @@
1
+ import { type TextareaHTMLAttributes } from "react";
2
+ import type { ControlSize } from "./control-types.js";
3
+ export interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
4
+ size?: ControlSize;
5
+ status?: "error" | "warning";
6
+ showCount?: boolean;
7
+ }
8
+ export declare const Textarea: import("react").ForwardRefExoticComponent<TextareaProps & import("react").RefAttributes<HTMLTextAreaElement>>;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId, useState, } from "react";
3
+ import { Textarea as PrimitiveTextarea } from "../components/primitives/textarea.js";
4
+ import { cn } from "../lib/utils.js";
5
+ export const Textarea = forwardRef(function Textarea({ size = "middle", status, showCount, maxLength, value, defaultValue, onChange, className, "aria-describedby": describedBy, ...props }, ref) {
6
+ const [count, setCount] = useState(() => String(value ?? defaultValue ?? "").length);
7
+ const countId = useId();
8
+ return (_jsxs("span", { className: "block min-w-0", children: [_jsx(PrimitiveTextarea, { ...props, ref: ref, value: value, defaultValue: defaultValue, maxLength: maxLength, onChange: (event) => {
9
+ setCount(event.target.value.length);
10
+ onChange?.(event);
11
+ }, "aria-invalid": status === "error" || props["aria-invalid"] || undefined, "aria-describedby": [describedBy, showCount ? countId : undefined]
12
+ .filter(Boolean)
13
+ .join(" ") || undefined, "data-size": size, "data-status": status, className: cn(size === "small" && "min-h-16", size === "large" && "min-h-28 text-base", status === "warning" && "border-warning focus-visible:ring-warning", className) }), showCount ? (_jsxs("span", { id: countId, className: "mt-1 block text-right text-xs text-muted-foreground", "aria-live": "polite", children: [value !== undefined ? String(value).length : count, maxLength !== undefined ? ` / ${maxLength}` : ""] })) : null] }));
14
+ });
@@ -0,0 +1,9 @@
1
+ import { type InputHTMLAttributes } from "react";
2
+ import { type ControlSize } from "./control-types.js";
3
+ export interface TimePickerProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "size"> {
4
+ /** Gouno control size. Native input's numeric size attribute is intentionally not exposed. */
5
+ size?: ControlSize;
6
+ /** Validation state only; business copy remains caller-owned. */
7
+ status?: "error" | "warning";
8
+ }
9
+ export declare const TimePicker: import("react").ForwardRefExoticComponent<TimePickerProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import { cn } from "../lib/utils.js";
4
+ import { controlSizeClass } from "./control-types.js";
5
+ export const TimePicker = forwardRef(function TimePicker({ size = "middle", status, className, disabled, ...props }, ref) {
6
+ const ariaInvalid = status === "error" ? true : props["aria-invalid"];
7
+ return (_jsx("input", { ...props, ref: ref, type: "time", disabled: disabled, "aria-invalid": ariaInvalid, "data-slot": "time-picker", "data-status": status, className: cn("w-full min-w-0 rounded-md border border-border bg-input px-3 text-foreground outline-none transition-[border-color,box-shadow] focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", controlSizeClass(size), status === "error" && "border-destructive", status === "warning" && "border-warning", className) }));
8
+ });
@@ -0,0 +1,37 @@
1
+ import { type CSSProperties, type HTMLAttributes, type Key, type ReactNode, type Ref } from "react";
2
+ export type TimelineMode = "start" | "alternate" | "end";
3
+ export type TimelineOrientation = "vertical" | "horizontal";
4
+ export type TimelineVariant = "filled" | "outlined";
5
+ export type TimelinePlacement = "start" | "end";
6
+ export type TimelineSemantic = "root" | "item" | "wrapper" | "icon" | "section" | "header" | "title" | "content" | "rail";
7
+ export interface TimelineItem {
8
+ key: Key;
9
+ title?: ReactNode;
10
+ content?: ReactNode;
11
+ icon?: ReactNode;
12
+ color?: string;
13
+ loading?: boolean;
14
+ placement?: TimelinePlacement;
15
+ }
16
+ export interface TimelineSemanticInfo {
17
+ props: Readonly<{
18
+ mode: TimelineMode;
19
+ orientation: TimelineOrientation;
20
+ reverse: boolean;
21
+ variant: TimelineVariant;
22
+ }>;
23
+ }
24
+ export type TimelineClassNames = Partial<Record<TimelineSemantic, string>> | ((info: TimelineSemanticInfo) => Partial<Record<TimelineSemantic, string>>);
25
+ export type TimelineStyles = Partial<Record<TimelineSemantic, CSSProperties>> | ((info: TimelineSemanticInfo) => Partial<Record<TimelineSemantic, CSSProperties>>);
26
+ export interface TimelineProps extends Omit<HTMLAttributes<HTMLOListElement>, "children"> {
27
+ items: readonly TimelineItem[];
28
+ mode?: TimelineMode;
29
+ orientation?: TimelineOrientation;
30
+ reverse?: boolean;
31
+ titleSpan?: number | string;
32
+ variant?: TimelineVariant;
33
+ classNames?: TimelineClassNames;
34
+ styles?: TimelineStyles;
35
+ ref?: Ref<HTMLOListElement>;
36
+ }
37
+ export declare function Timeline({ items, mode, orientation, reverse, titleSpan, variant, classNames, styles, className, style, ref, ...props }: TimelineProps): import("react").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import {} from "react";
3
+ import { cn } from "../lib/utils.js";
4
+ import { Spinner } from "./spinner.js";
5
+ function resolveColorStyle(color) {
6
+ if (!color)
7
+ return undefined;
8
+ return { "--timeline-color": color };
9
+ }
10
+ export function Timeline({ items, mode = "start", orientation = "vertical", reverse = false, titleSpan = 12, variant = "outlined", classNames, styles, className, style, ref, ...props }) {
11
+ const orderedItems = reverse ? [...items].reverse() : items;
12
+ const semanticInfo = {
13
+ props: { mode, orientation, reverse, variant },
14
+ };
15
+ const semanticClassNames = typeof classNames === "function" ? classNames(semanticInfo) : (classNames ?? {});
16
+ const semanticStyles = typeof styles === "function" ? styles(semanticInfo) : (styles ?? {});
17
+ const titleSize = typeof titleSpan === "number" ? `${titleSpan}rem` : titleSpan;
18
+ return (_jsx("ol", { ...props, ref: ref, "data-slot": "timeline", "data-mode": mode, "data-orientation": orientation, "data-variant": variant, className: cn("m-0 list-none p-0 text-sm", orientation === "vertical"
19
+ ? "flex flex-col gap-0"
20
+ : "grid auto-cols-fr grid-flow-col overflow-x-auto pb-2", semanticClassNames.root, className), style: {
21
+ ...(orientation === "vertical"
22
+ ? { "--timeline-title-span": titleSize }
23
+ : undefined),
24
+ ...semanticStyles.root,
25
+ ...style,
26
+ }, children: orderedItems.map((item, index) => {
27
+ const automaticPlacement = mode === "end"
28
+ ? "end"
29
+ : mode === "alternate" && index % 2 === 1
30
+ ? "end"
31
+ : "start";
32
+ const placement = item.placement ?? automaticPlacement;
33
+ const last = index === orderedItems.length - 1;
34
+ return (_jsx("li", { "data-slot": "timeline-item", "data-placement": placement, "data-loading": item.loading || undefined, className: cn("relative min-w-0", orientation === "vertical"
35
+ ? "grid min-h-14 grid-cols-[minmax(0,var(--timeline-title-span))_1.5rem_minmax(0,1fr)] gap-x-3 pb-5 last:pb-0"
36
+ : "grid min-w-44 grid-rows-[auto_1.5rem_auto] gap-y-2 px-2 text-center first:pl-0 last:pr-0", semanticClassNames.item), style: { ...resolveColorStyle(item.color), ...semanticStyles.item }, children: _jsxs("div", { "data-slot": "timeline-wrapper", className: cn("contents", semanticClassNames.wrapper), style: semanticStyles.wrapper, children: [_jsx("div", { "data-slot": "timeline-header", className: cn("min-w-0 text-muted-foreground", orientation === "vertical"
37
+ ? cn("row-start-1 pt-0.5", placement === "start" ? "col-start-1 text-right" : "col-start-3 text-left")
38
+ : "col-start-1 row-start-1", semanticClassNames.header), style: semanticStyles.header, children: item.title !== undefined && item.title !== null ? (_jsx("div", { "data-slot": "timeline-title", className: cn("break-words", semanticClassNames.title), style: semanticStyles.title, children: item.title })) : null }), _jsx("div", { "data-slot": "timeline-icon", "aria-hidden": item.loading || item.icon ? undefined : true, className: cn("relative z-10 flex items-center justify-center", orientation === "vertical"
39
+ ? "col-start-2 row-start-1 size-6"
40
+ : "col-start-1 row-start-2 mx-auto size-6", semanticClassNames.icon), style: semanticStyles.icon, children: item.loading ? (_jsx(Spinner, { className: "size-4" })) : item.icon ? (_jsx("span", { className: "flex size-5 items-center justify-center rounded-full bg-background text-[var(--timeline-color,var(--primary))]", children: item.icon })) : (_jsx("span", { className: cn("block size-3 rounded-full border-2", variant === "filled"
41
+ ? "border-[var(--timeline-color,var(--primary))] bg-[var(--timeline-color,var(--primary))]"
42
+ : "border-[var(--timeline-color,var(--primary))] bg-background") })) }), !last ? (_jsx("span", { "data-slot": "timeline-rail", "aria-hidden": "true", className: cn("absolute bg-border", orientation === "vertical"
43
+ ? "bottom-0 left-[calc(var(--timeline-title-span)+1.5rem)] top-5 w-px -translate-x-1/2"
44
+ : "left-1/2 right-[-50%] top-[calc(100%-2.25rem)] h-px", semanticClassNames.rail), style: semanticStyles.rail })) : null, _jsx("div", { "data-slot": "timeline-section", className: cn("min-w-0", orientation === "vertical"
45
+ ? cn("row-start-1", placement === "start" ? "col-start-3" : "col-start-1 text-right")
46
+ : "col-start-1 row-start-3", semanticClassNames.section), style: semanticStyles.section, children: item.content !== undefined && item.content !== null ? (_jsx("div", { "data-slot": "timeline-content", className: cn("break-words text-foreground", semanticClassNames.content), style: semanticStyles.content, children: item.content })) : null })] }) }, item.key));
47
+ }) }));
48
+ }
@@ -0,0 +1,18 @@
1
+ import { type ReactNode, type Ref } from "react";
2
+ export interface TourStep {
3
+ title: ReactNode;
4
+ description?: ReactNode;
5
+ }
6
+ export interface TourProps {
7
+ open: boolean;
8
+ steps: readonly TourStep[];
9
+ current?: number;
10
+ onChange?: (current: number) => void;
11
+ onClose: () => void;
12
+ previousText: string;
13
+ nextText: string;
14
+ finishText: string;
15
+ className?: string;
16
+ ref?: Ref<HTMLDivElement>;
17
+ }
18
+ export declare function Tour({ open, steps, current, onChange, onClose, previousText, nextText, finishText, className, ref, }: TourProps): import("react").JSX.Element | null;
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState, } from "react";
3
+ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../components/primitives/dialog.js";
4
+ import { Button } from "./button.js";
5
+ import { cn } from "../lib/utils.js";
6
+ function normalizeIndex(index, length) {
7
+ if (length <= 0 || !Number.isFinite(index))
8
+ return 0;
9
+ return Math.min(Math.max(Math.trunc(index), 0), length - 1);
10
+ }
11
+ export function Tour({ open, steps, current, onChange, onClose, previousText, nextText, finishText, className, ref, }) {
12
+ const [innerCurrent, setInnerCurrent] = useState(0);
13
+ const restoreFocusRef = useRef(null);
14
+ const controlled = current !== undefined;
15
+ const index = normalizeIndex(controlled ? current : innerCurrent, steps.length);
16
+ const step = steps[index];
17
+ useEffect(() => {
18
+ if (!open && !controlled)
19
+ setInnerCurrent(0);
20
+ }, [controlled, open]);
21
+ const moveTo = (nextIndex) => {
22
+ const normalized = normalizeIndex(nextIndex, steps.length);
23
+ if (!controlled)
24
+ setInnerCurrent(normalized);
25
+ onChange?.(normalized);
26
+ };
27
+ const finish = () => {
28
+ if (!controlled)
29
+ setInnerCurrent(0);
30
+ onClose();
31
+ };
32
+ if (!open || !step)
33
+ return null;
34
+ return (_jsx(Dialog, { open: open, onOpenChange: (nextOpen) => {
35
+ if (!nextOpen)
36
+ onClose();
37
+ }, children: _jsxs(DialogContent, { ref: ref, showCloseButton: false, "data-slot": "tour-content", "data-step-index": index, onPointerDownOutside: (event) => event.preventDefault(), onOpenAutoFocus: () => {
38
+ const activeElement = document.activeElement;
39
+ restoreFocusRef.current =
40
+ activeElement instanceof HTMLElement && activeElement !== document.body
41
+ ? activeElement
42
+ : null;
43
+ }, onCloseAutoFocus: (event) => {
44
+ const restoreFocusTarget = restoreFocusRef.current;
45
+ restoreFocusRef.current = null;
46
+ if (!restoreFocusTarget?.isConnected)
47
+ return;
48
+ event.preventDefault();
49
+ restoreFocusTarget.focus();
50
+ }, className: cn("top-auto bottom-6 left-1/2 w-80 max-w-[calc(100vw-2rem)] -translate-x-1/2 translate-y-0 sm:max-w-[calc(100vw-2rem)]", className), children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: step.title }), step.description ? (_jsx(DialogDescription, { children: step.description })) : null] }), _jsxs("div", { className: "text-xs text-muted-foreground", "data-slot": "tour-progress", children: [index + 1, " / ", steps.length] }), _jsxs(DialogFooter, { children: [index > 0 ? (_jsx(Button, { size: "small", onClick: () => moveTo(index - 1), children: previousText })) : null, index < steps.length - 1 ? (_jsx(Button, { size: "small", variant: "solid", color: "primary", onClick: () => moveTo(index + 1), children: nextText })) : (_jsx(Button, { size: "small", variant: "solid", color: "primary", onClick: finish, children: finishText }))] })] }) }));
51
+ }
@@ -0,0 +1,16 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ export interface TransferItem {
3
+ key: string;
4
+ title: ReactNode;
5
+ disabled?: boolean;
6
+ }
7
+ export interface TransferProps extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "onChange"> {
8
+ dataSource: readonly TransferItem[];
9
+ targetKeys?: readonly string[];
10
+ defaultTargetKeys?: readonly string[];
11
+ onChange?: (keys: string[]) => void;
12
+ titles?: readonly [ReactNode, ReactNode];
13
+ operations?: readonly [string, string];
14
+ disabled?: boolean;
15
+ }
16
+ export declare const Transfer: import("react").ForwardRefExoticComponent<TransferProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useId, useMemo, useState, } from "react";
3
+ import { Button } from "./button.js";
4
+ import { cn } from "../lib/utils.js";
5
+ const compatibilityOperations = ["→", "←"];
6
+ export const Transfer = forwardRef(function Transfer({ dataSource, targetKeys, defaultTargetKeys = [], onChange, titles, operations = compatibilityOperations, disabled = false, className, ...props }, ref) {
7
+ const [inner, setInner] = useState(defaultTargetKeys);
8
+ const [selected, setSelected] = useState([]);
9
+ const sourceLabelId = useId();
10
+ const targetLabelId = useId();
11
+ const current = targetKeys ?? inner;
12
+ const targetKeySet = useMemo(() => new Set(current), [current]);
13
+ const itemByKey = useMemo(() => new Map(dataSource.map((item) => [item.key, item])), [dataSource]);
14
+ const sourceItems = dataSource.filter((item) => !targetKeySet.has(item.key));
15
+ const targetItems = dataSource.filter((item) => targetKeySet.has(item.key));
16
+ const movableSourceKeys = selected.filter((key) => {
17
+ const item = itemByKey.get(key);
18
+ return item && !item.disabled && !targetKeySet.has(key);
19
+ });
20
+ const movableTargetKeys = selected.filter((key) => {
21
+ const item = itemByKey.get(key);
22
+ return item && !item.disabled && targetKeySet.has(key);
23
+ });
24
+ const commit = (keys, movedKeys) => {
25
+ if (targetKeys === undefined)
26
+ setInner(keys);
27
+ onChange?.(keys);
28
+ const moved = new Set(movedKeys);
29
+ setSelected((previous) => previous.filter((key) => !moved.has(key)));
30
+ };
31
+ const toggle = (key, checked) => {
32
+ setSelected((previous) => checked
33
+ ? previous.includes(key)
34
+ ? previous
35
+ : [...previous, key]
36
+ : previous.filter((value) => value !== key));
37
+ };
38
+ const renderList = (items, side, labelId, title) => {
39
+ const labelled = title !== undefined && title !== null;
40
+ return (_jsxs("div", { role: labelled ? "group" : undefined, "aria-labelledby": labelled ? labelId : undefined, "data-slot": `transfer-${side}`, className: "min-w-0 rounded-md border bg-background", children: [labelled ? (_jsx("div", { id: labelId, "data-slot": "transfer-title", className: "border-b px-3 py-2 text-sm font-medium", children: title })) : null, _jsx("div", { "data-slot": "transfer-list", className: "max-h-48 overflow-auto p-2", children: items.map((item) => (_jsxs("label", { "data-slot": "transfer-item", className: cn("flex items-center gap-2 rounded p-1 text-sm", !disabled && !item.disabled && "hover:bg-accent", (disabled || item.disabled) && "opacity-50"), children: [_jsx("input", { type: "checkbox", disabled: disabled || item.disabled, checked: selected.includes(item.key), onChange: (event) => toggle(item.key, event.target.checked) }), _jsx("span", { children: item.title })] }, item.key))) })] }));
41
+ };
42
+ return (_jsxs("div", { ...props, ref: ref, "data-slot": "transfer", "data-disabled": disabled || undefined, "aria-disabled": disabled || undefined, className: cn("grid min-w-0 gap-3 sm:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] sm:items-center", className), children: [renderList(sourceItems, "source", sourceLabelId, titles?.[0]), _jsxs("div", { "data-slot": "transfer-operations", className: "flex justify-center gap-2 sm:flex-col", children: [_jsx(Button, { size: "small", disabled: disabled || movableSourceKeys.length === 0, onClick: () => commit([
43
+ ...current,
44
+ ...movableSourceKeys.filter((key) => !targetKeySet.has(key)),
45
+ ], movableSourceKeys), children: operations[0] }), _jsx(Button, { size: "small", disabled: disabled || movableTargetKeys.length === 0, onClick: () => commit(current.filter((key) => !movableTargetKeys.includes(key)), movableTargetKeys), children: operations[1] })] }), renderList(targetItems, "target", targetLabelId, titles?.[1])] }));
46
+ });
@@ -0,0 +1,19 @@
1
+ import { type SelectHTMLAttributes } from "react";
2
+ import { type ControlSize } from "./control-types.js";
3
+ export interface TreeSelectNode {
4
+ value: string;
5
+ title: string;
6
+ disabled?: boolean;
7
+ children?: readonly TreeSelectNode[];
8
+ }
9
+ export interface TreeSelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "size" | "value" | "defaultValue" | "multiple" | "onChange"> {
10
+ treeData: readonly TreeSelectNode[];
11
+ value?: string | readonly string[];
12
+ defaultValue?: string | readonly string[];
13
+ onChange?: (value: string | string[]) => void;
14
+ placeholder?: string;
15
+ multiple?: boolean;
16
+ size?: ControlSize;
17
+ status?: "error" | "warning";
18
+ }
19
+ export declare const TreeSelect: import("react").ForwardRefExoticComponent<TreeSelectProps & import("react").RefAttributes<HTMLSelectElement>>;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, } from "react";
3
+ import { cn } from "../lib/utils.js";
4
+ import { controlSizeClass } from "./control-types.js";
5
+ function flatten(nodes, depth = 0) {
6
+ return nodes.flatMap((node) => [
7
+ { ...node, depth },
8
+ ...flatten(node.children ?? [], depth + 1),
9
+ ]);
10
+ }
11
+ export const TreeSelect = forwardRef(function TreeSelect({ treeData, value, defaultValue, onChange, placeholder, disabled = false, multiple = false, size = "middle", status, className, ...props }, ref) {
12
+ const nodes = flatten(treeData);
13
+ const invalid = status === "error" ? true : props["aria-invalid"];
14
+ return (_jsxs("select", { ...props, ref: ref, multiple: multiple, disabled: disabled, value: value, defaultValue: defaultValue, "aria-disabled": disabled || props["aria-disabled"] || undefined, "aria-invalid": invalid, "data-slot": "tree-select", "data-status": status, onChange: (event) => onChange?.(multiple
15
+ ? Array.from(event.currentTarget.selectedOptions, (option) => option.value)
16
+ : event.currentTarget.value), className: cn("rounded-md border border-border bg-input px-3 outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", controlSizeClass(size), multiple && "h-auto min-h-24 py-2", status === "error" &&
17
+ "border-destructive focus-visible:ring-destructive", status === "warning" &&
18
+ "border-warning focus-visible:ring-warning", className), children: [!multiple && placeholder !== undefined ? (_jsx("option", { value: "", disabled: true, "data-slot": "tree-select-placeholder", children: placeholder })) : null, nodes.map((node) => (_jsx("option", { value: node.value, disabled: node.disabled, "data-slot": "tree-select-option", "data-depth": node.depth, children: `${"\u00A0\u00A0".repeat(node.depth)}${node.title}` }, node.value)))] }));
19
+ });