@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,89 @@
1
+ import { type ChangeEvent, type CSSProperties, type HTMLAttributes, type Key, type KeyboardEvent, type MouseEvent, type ReactNode, type Ref } from "react";
2
+ export interface TreeNode {
3
+ key: Key;
4
+ title: ReactNode;
5
+ children?: readonly TreeNode[];
6
+ disabled?: boolean;
7
+ selectable?: boolean;
8
+ checkable?: boolean;
9
+ disableCheckbox?: boolean;
10
+ icon?: ReactNode | ((info: TreeNodeRenderInfo) => ReactNode);
11
+ isLeaf?: boolean;
12
+ }
13
+ export interface TreeNodeRenderInfo {
14
+ node: TreeNode;
15
+ expanded: boolean;
16
+ selected: boolean;
17
+ checked: boolean;
18
+ halfChecked: boolean;
19
+ loading: boolean;
20
+ disabled: boolean;
21
+ }
22
+ export interface TreeExpandInfo {
23
+ expanded: boolean;
24
+ node: TreeNode;
25
+ }
26
+ export interface TreeSelectInfo {
27
+ selected: boolean;
28
+ selectedNodes: TreeNode[];
29
+ node: TreeNode;
30
+ nativeEvent: MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>;
31
+ }
32
+ export interface TreeCheckInfo {
33
+ checked: boolean;
34
+ checkedNodes: TreeNode[];
35
+ halfCheckedKeys: Key[];
36
+ node: TreeNode;
37
+ nativeEvent: ChangeEvent<HTMLInputElement> | KeyboardEvent<HTMLDivElement>;
38
+ }
39
+ export type TreeCheckedKeys = readonly Key[] | {
40
+ checked: readonly Key[];
41
+ halfChecked: readonly Key[];
42
+ };
43
+ export type TreeSemantic = "root" | "item" | "switcher" | "checkbox" | "icon" | "title" | "group";
44
+ export interface TreeSemanticInfo {
45
+ props: Readonly<{
46
+ checkable: boolean;
47
+ multiple: boolean;
48
+ disabled: boolean;
49
+ showLine: boolean;
50
+ blockNode: boolean;
51
+ }>;
52
+ }
53
+ export type TreeClassNames = Partial<Record<TreeSemantic, string>> | ((info: TreeSemanticInfo) => Partial<Record<TreeSemantic, string>>);
54
+ export type TreeStyles = Partial<Record<TreeSemantic, CSSProperties>> | ((info: TreeSemanticInfo) => Partial<Record<TreeSemantic, CSSProperties>>);
55
+ export interface TreeProps extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "onLoad" | "onSelect"> {
56
+ treeData: readonly TreeNode[];
57
+ expandedKeys?: readonly Key[];
58
+ defaultExpandedKeys?: readonly Key[];
59
+ defaultExpandAll?: boolean;
60
+ selectedKeys?: readonly Key[];
61
+ defaultSelectedKeys?: readonly Key[];
62
+ checkedKeys?: TreeCheckedKeys;
63
+ defaultCheckedKeys?: readonly Key[];
64
+ loadedKeys?: readonly Key[];
65
+ checkable?: boolean;
66
+ checkStrictly?: boolean;
67
+ multiple?: boolean;
68
+ selectable?: boolean;
69
+ disabled?: boolean;
70
+ blockNode?: boolean;
71
+ showIcon?: boolean;
72
+ showLine?: boolean;
73
+ icon?: ReactNode | ((info: TreeNodeRenderInfo) => ReactNode);
74
+ switcherIcon?: ReactNode | ((info: TreeNodeRenderInfo) => ReactNode);
75
+ switcherLoadingIcon?: ReactNode;
76
+ titleRender?: (node: TreeNode) => ReactNode;
77
+ filterTreeNode?: (node: TreeNode) => boolean;
78
+ loadData?: (node: TreeNode) => Promise<void>;
79
+ onExpand?: (expandedKeys: Key[], info: TreeExpandInfo) => void;
80
+ onSelect?: (selectedKeys: Key[], info: TreeSelectInfo) => void;
81
+ onCheck?: (checkedKeys: TreeCheckedKeys, info: TreeCheckInfo) => void;
82
+ onLoad?: (loadedKeys: Key[], info: {
83
+ node: TreeNode;
84
+ }) => void;
85
+ classNames?: TreeClassNames;
86
+ styles?: TreeStyles;
87
+ ref?: Ref<HTMLDivElement>;
88
+ }
89
+ export declare function Tree({ treeData, expandedKeys, defaultExpandedKeys, defaultExpandAll, selectedKeys, defaultSelectedKeys, checkedKeys, defaultCheckedKeys, loadedKeys, checkable, checkStrictly, multiple, selectable, disabled, blockNode, showIcon, showLine, icon, switcherIcon, switcherLoadingIcon, titleRender, filterTreeNode, loadData, onExpand, onSelect, onCheck, onLoad, classNames, styles, className, style, ref, ...props }: TreeProps): import("react").JSX.Element;
@@ -0,0 +1,386 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useRef, useState, } from "react";
3
+ import { ChevronDown, ChevronRight } from "lucide-react";
4
+ import { cn } from "../lib/utils.js";
5
+ import { Button } from "./button.js";
6
+ import { Spinner } from "./spinner.js";
7
+ function keyToken(key) {
8
+ return `${typeof key === "number" ? "n" : "s"}:${String(key)}`;
9
+ }
10
+ function keyIncludes(keys, key) {
11
+ return keys.some((candidate) => candidate === key);
12
+ }
13
+ function flattenAll(nodes, parentKey, level = 1) {
14
+ return nodes.flatMap((node, index) => [
15
+ { node, parentKey, level, index },
16
+ ...flattenAll(node.children ?? [], node.key, level + 1),
17
+ ]);
18
+ }
19
+ function flattenVisible(nodes, expanded, parentKey, level = 1) {
20
+ return nodes.flatMap((node, index) => {
21
+ const entry = { node, parentKey, level, index };
22
+ return [
23
+ entry,
24
+ ...(keyIncludes(expanded, node.key)
25
+ ? flattenVisible(node.children ?? [], expanded, node.key, level + 1)
26
+ : []),
27
+ ];
28
+ });
29
+ }
30
+ function branchKeys(nodes) {
31
+ return flattenAll(nodes)
32
+ .filter(({ node }) => Boolean(node.children?.length) || node.isLeaf === false)
33
+ .map(({ node }) => node.key);
34
+ }
35
+ function uniqueKeys(keys) {
36
+ return Array.from(new Set(keys));
37
+ }
38
+ function isStrictCheckedKeys(value) {
39
+ return !Array.isArray(value);
40
+ }
41
+ function rawCheckedKeys(value) {
42
+ if (!value)
43
+ return [];
44
+ return isStrictCheckedKeys(value) ? value.checked : value;
45
+ }
46
+ function checkedNodesFor(keys, entries) {
47
+ const set = new Set(keys);
48
+ return entries.filter(({ node }) => set.has(node.key)).map(({ node }) => node);
49
+ }
50
+ function resolveNodeIcon(source, info) {
51
+ return typeof source === "function" ? source(info) : source;
52
+ }
53
+ function resolveSwitcherIcon(source, info) {
54
+ if (source)
55
+ return typeof source === "function" ? source(info) : source;
56
+ return info.expanded ? (_jsx(ChevronDown, { "aria-hidden": "true" })) : (_jsx(ChevronRight, { "aria-hidden": "true" }));
57
+ }
58
+ function titleId(key, level, index) {
59
+ const safeKey = keyToken(key).replace(/[^a-zA-Z0-9_-]/g, "-");
60
+ return `gouno-tree-title-${level}-${index}-${safeKey}`;
61
+ }
62
+ export function Tree({ treeData, expandedKeys, defaultExpandedKeys = [], defaultExpandAll = false, selectedKeys, defaultSelectedKeys = [], checkedKeys, defaultCheckedKeys = [], loadedKeys, checkable = false, checkStrictly = false, multiple = false, selectable = true, disabled = false, blockNode = false, showIcon = false, showLine = false, icon, switcherIcon, switcherLoadingIcon, titleRender, filterTreeNode, loadData, onExpand, onSelect, onCheck, onLoad, classNames, styles, className, style, ref, ...props }) {
63
+ const rootRef = useRef(null);
64
+ const allEntries = useMemo(() => flattenAll(treeData), [treeData]);
65
+ const entryByKey = useMemo(() => new Map(allEntries.map((entry) => [entry.node.key, entry])), [allEntries]);
66
+ const orderByKey = useMemo(() => new Map(allEntries.map((entry, index) => [entry.node.key, index])), [allEntries]);
67
+ const [internalExpandedKeys, setInternalExpandedKeys] = useState(() => uniqueKeys(defaultExpandAll
68
+ ? [...branchKeys(treeData), ...defaultExpandedKeys]
69
+ : defaultExpandedKeys));
70
+ const activeExpandedKeys = expandedKeys ?? internalExpandedKeys;
71
+ const [internalSelectedKeys, setInternalSelectedKeys] = useState(() => uniqueKeys(defaultSelectedKeys));
72
+ const activeSelectedKeys = selectedKeys ?? internalSelectedKeys;
73
+ const [internalCheckedKeys, setInternalCheckedKeys] = useState(() => uniqueKeys(defaultCheckedKeys));
74
+ const incomingCheckedKeys = checkedKeys
75
+ ? rawCheckedKeys(checkedKeys)
76
+ : internalCheckedKeys;
77
+ const [internalLoadedKeys, setInternalLoadedKeys] = useState([]);
78
+ const activeLoadedKeys = loadedKeys ?? internalLoadedKeys;
79
+ const [loadingKeys, setLoadingKeys] = useState([]);
80
+ const semanticInfo = {
81
+ props: { checkable, multiple, disabled, showLine, blockNode },
82
+ };
83
+ const semanticClassNames = typeof classNames === "function" ? classNames(semanticInfo) : (classNames ?? {});
84
+ const semanticStyles = typeof styles === "function" ? styles(semanticInfo) : (styles ?? {});
85
+ const conductedCheckedKeys = useMemo(() => {
86
+ if (checkStrictly)
87
+ return uniqueKeys(incomingCheckedKeys);
88
+ const checked = new Set();
89
+ const addDescendants = (node) => {
90
+ if (!node.disabled && !node.disableCheckbox && node.checkable !== false) {
91
+ checked.add(node.key);
92
+ }
93
+ for (const child of node.children ?? [])
94
+ addDescendants(child);
95
+ };
96
+ for (const key of incomingCheckedKeys) {
97
+ const entry = entryByKey.get(key);
98
+ if (entry)
99
+ addDescendants(entry.node);
100
+ }
101
+ for (const { node } of [...allEntries].reverse()) {
102
+ if (node.disabled || node.disableCheckbox || node.checkable === false)
103
+ continue;
104
+ const eligibleChildren = (node.children ?? []).filter((child) => !child.disabled && !child.disableCheckbox && child.checkable !== false);
105
+ if (eligibleChildren.length &&
106
+ eligibleChildren.every((child) => checked.has(child.key))) {
107
+ checked.add(node.key);
108
+ }
109
+ }
110
+ return allEntries
111
+ .filter(({ node }) => checked.has(node.key))
112
+ .map(({ node }) => node.key);
113
+ }, [allEntries, checkStrictly, entryByKey, incomingCheckedKeys]);
114
+ const halfCheckedKeys = useMemo(() => {
115
+ if (checkStrictly) {
116
+ return checkedKeys && isStrictCheckedKeys(checkedKeys)
117
+ ? [...checkedKeys.halfChecked]
118
+ : [];
119
+ }
120
+ const checked = new Set(conductedCheckedKeys);
121
+ const half = new Set();
122
+ for (const { node } of [...allEntries].reverse()) {
123
+ if (checked.has(node.key))
124
+ continue;
125
+ const children = (node.children ?? []).filter((child) => !child.disabled && !child.disableCheckbox && child.checkable !== false);
126
+ if (children.some((child) => checked.has(child.key) || half.has(child.key))) {
127
+ half.add(node.key);
128
+ }
129
+ }
130
+ return allEntries
131
+ .filter(({ node }) => half.has(node.key))
132
+ .map(({ node }) => node.key);
133
+ }, [allEntries, checkStrictly, checkedKeys, conductedCheckedKeys]);
134
+ const visibleEntries = useMemo(() => flattenVisible(treeData, activeExpandedKeys), [activeExpandedKeys, treeData]);
135
+ const initialFocusKey = activeSelectedKeys.find((key) => visibleEntries.some(({ node }) => node.key === key)) ?? visibleEntries[0]?.node.key;
136
+ const [focusKey, setFocusKey] = useState(initialFocusKey);
137
+ const setRootRef = (node) => {
138
+ rootRef.current = node;
139
+ if (typeof ref === "function")
140
+ ref(node);
141
+ else if (ref)
142
+ ref.current = node;
143
+ };
144
+ const focusNode = (key) => {
145
+ if (key === undefined)
146
+ return;
147
+ setFocusKey(key);
148
+ queueMicrotask(() => {
149
+ const token = keyToken(key);
150
+ const candidates = rootRef.current?.querySelectorAll("[data-tree-key]") ?? [];
151
+ Array.from(candidates)
152
+ .find((element) => element.dataset.treeKey === token)
153
+ ?.focus();
154
+ });
155
+ };
156
+ const sortKeys = (keys) => Array.from(new Set(keys)).sort((a, b) => (orderByKey.get(a) ?? Number.MAX_SAFE_INTEGER) -
157
+ (orderByKey.get(b) ?? Number.MAX_SAFE_INTEGER));
158
+ const requestLoad = async (node) => {
159
+ if (!loadData ||
160
+ node.isLeaf === true ||
161
+ keyIncludes(activeLoadedKeys, node.key) ||
162
+ keyIncludes(loadingKeys, node.key)) {
163
+ return;
164
+ }
165
+ setLoadingKeys((keys) => uniqueKeys([...keys, node.key]));
166
+ try {
167
+ await loadData(node);
168
+ const nextLoadedKeys = sortKeys([...activeLoadedKeys, node.key]);
169
+ if (loadedKeys === undefined)
170
+ setInternalLoadedKeys(nextLoadedKeys);
171
+ onLoad?.(nextLoadedKeys, { node });
172
+ }
173
+ finally {
174
+ setLoadingKeys((keys) => keys.filter((key) => key !== node.key));
175
+ }
176
+ };
177
+ const setExpanded = (node, expanded) => {
178
+ const nextKeys = expanded
179
+ ? sortKeys([...activeExpandedKeys, node.key])
180
+ : activeExpandedKeys.filter((key) => key !== node.key);
181
+ if (expandedKeys === undefined)
182
+ setInternalExpandedKeys(nextKeys);
183
+ onExpand?.(nextKeys, { expanded, node });
184
+ if (expanded)
185
+ void requestLoad(node);
186
+ };
187
+ const selectNode = (node, event) => {
188
+ const nodeDisabled = disabled || node.disabled;
189
+ if (nodeDisabled || !selectable || node.selectable === false)
190
+ return;
191
+ const currentlySelected = keyIncludes(activeSelectedKeys, node.key);
192
+ const additive = multiple && (event.metaKey || event.ctrlKey);
193
+ const nextKeys = !multiple || !additive
194
+ ? currentlySelected && multiple
195
+ ? []
196
+ : [node.key]
197
+ : currentlySelected
198
+ ? activeSelectedKeys.filter((key) => key !== node.key)
199
+ : sortKeys([...activeSelectedKeys, node.key]);
200
+ if (selectedKeys === undefined)
201
+ setInternalSelectedKeys(nextKeys);
202
+ onSelect?.(nextKeys, {
203
+ selected: keyIncludes(nextKeys, node.key),
204
+ selectedNodes: checkedNodesFor(nextKeys, allEntries),
205
+ node,
206
+ nativeEvent: event,
207
+ });
208
+ };
209
+ const setChecked = (node, nextChecked, event) => {
210
+ const nodeDisabled = disabled || node.disabled || node.disableCheckbox || node.checkable === false;
211
+ if (nodeDisabled || !checkable)
212
+ return;
213
+ const current = new Set(conductedCheckedKeys);
214
+ if (checkStrictly) {
215
+ if (nextChecked)
216
+ current.add(node.key);
217
+ else
218
+ current.delete(node.key);
219
+ }
220
+ else {
221
+ const descendants = flattenAll([node]).map(({ node: child }) => child);
222
+ for (const child of descendants) {
223
+ if (child.disabled || child.disableCheckbox || child.checkable === false) {
224
+ continue;
225
+ }
226
+ if (nextChecked)
227
+ current.add(child.key);
228
+ else
229
+ current.delete(child.key);
230
+ }
231
+ let parentKey = entryByKey.get(node.key)?.parentKey;
232
+ while (parentKey !== undefined) {
233
+ current.delete(parentKey);
234
+ parentKey = entryByKey.get(parentKey)?.parentKey;
235
+ }
236
+ for (const { node: candidate } of [...allEntries].reverse()) {
237
+ if (candidate.disabled ||
238
+ candidate.disableCheckbox ||
239
+ candidate.checkable === false) {
240
+ continue;
241
+ }
242
+ const children = (candidate.children ?? []).filter((child) => !child.disabled && !child.disableCheckbox && child.checkable !== false);
243
+ if (children.length && children.every((child) => current.has(child.key))) {
244
+ current.add(candidate.key);
245
+ }
246
+ }
247
+ }
248
+ const nextKeys = sortKeys(current);
249
+ if (checkedKeys === undefined)
250
+ setInternalCheckedKeys(nextKeys);
251
+ const nextHalfKeys = checkStrictly
252
+ ? checkedKeys && isStrictCheckedKeys(checkedKeys)
253
+ ? [...checkedKeys.halfChecked]
254
+ : []
255
+ : (() => {
256
+ const checked = new Set(nextKeys);
257
+ const half = new Set();
258
+ for (const { node: candidate } of [...allEntries].reverse()) {
259
+ if (checked.has(candidate.key))
260
+ continue;
261
+ const children = (candidate.children ?? []).filter((child) => !child.disabled &&
262
+ !child.disableCheckbox &&
263
+ child.checkable !== false);
264
+ if (children.some((child) => checked.has(child.key) || half.has(child.key))) {
265
+ half.add(candidate.key);
266
+ }
267
+ }
268
+ return sortKeys(half);
269
+ })();
270
+ const output = checkStrictly
271
+ ? { checked: nextKeys, halfChecked: nextHalfKeys }
272
+ : nextKeys;
273
+ onCheck?.(output, {
274
+ checked: nextChecked,
275
+ checkedNodes: checkedNodesFor(nextKeys, allEntries),
276
+ halfCheckedKeys: nextHalfKeys,
277
+ node,
278
+ nativeEvent: event,
279
+ });
280
+ };
281
+ const handleKeyDown = (event, entry) => {
282
+ const { node, parentKey } = entry;
283
+ const visibleIndex = visibleEntries.findIndex(({ node: visibleNode }) => visibleNode.key === node.key);
284
+ const expanded = keyIncludes(activeExpandedKeys, node.key);
285
+ const hasChildren = Boolean(node.children?.length) || (Boolean(loadData) && node.isLeaf !== true);
286
+ if (event.key === "ArrowDown") {
287
+ event.preventDefault();
288
+ focusNode(visibleEntries[Math.min(visibleEntries.length - 1, visibleIndex + 1)]
289
+ ?.node.key);
290
+ return;
291
+ }
292
+ if (event.key === "ArrowUp") {
293
+ event.preventDefault();
294
+ focusNode(visibleEntries[Math.max(0, visibleIndex - 1)]?.node.key);
295
+ return;
296
+ }
297
+ if (event.key === "Home") {
298
+ event.preventDefault();
299
+ focusNode(visibleEntries[0]?.node.key);
300
+ return;
301
+ }
302
+ if (event.key === "End") {
303
+ event.preventDefault();
304
+ focusNode(visibleEntries.at(-1)?.node.key);
305
+ return;
306
+ }
307
+ if (event.key === "ArrowRight" && hasChildren) {
308
+ event.preventDefault();
309
+ if (!expanded)
310
+ setExpanded(node, true);
311
+ else
312
+ focusNode(node.children?.[0]?.key);
313
+ return;
314
+ }
315
+ if (event.key === "ArrowLeft") {
316
+ if (expanded && hasChildren) {
317
+ event.preventDefault();
318
+ setExpanded(node, false);
319
+ }
320
+ else if (parentKey !== undefined) {
321
+ event.preventDefault();
322
+ focusNode(parentKey);
323
+ }
324
+ return;
325
+ }
326
+ if (event.key === " " &&
327
+ checkable &&
328
+ node.checkable !== false &&
329
+ !node.disableCheckbox) {
330
+ event.preventDefault();
331
+ setChecked(node, !keyIncludes(conductedCheckedKeys, node.key), event);
332
+ return;
333
+ }
334
+ if (event.key === "Enter" || event.key === " ") {
335
+ event.preventDefault();
336
+ selectNode(node, event);
337
+ }
338
+ };
339
+ const renderNodes = (nodes, parentKey, level = 1) => (_jsx("ul", { role: level === 1 ? "none" : "group", "data-slot": level === 1 ? "tree-list" : "tree-group", className: cn("m-0 flex list-none flex-col gap-0 p-0", level > 1 && (showLine ? "ml-3 border-l pl-3" : "pl-6"), level > 1 && semanticClassNames.group), style: level > 1 ? semanticStyles.group : undefined, children: nodes.map((node, index) => {
340
+ const entry = { node, parentKey, level, index };
341
+ const nodeDisabled = disabled || Boolean(node.disabled);
342
+ const expanded = keyIncludes(activeExpandedKeys, node.key);
343
+ const selected = keyIncludes(activeSelectedKeys, node.key);
344
+ const checked = keyIncludes(conductedCheckedKeys, node.key);
345
+ const halfChecked = keyIncludes(halfCheckedKeys, node.key);
346
+ const loading = keyIncludes(loadingKeys, node.key);
347
+ const hasChildren = Boolean(node.children?.length) ||
348
+ (Boolean(loadData) && node.isLeaf !== true);
349
+ const nodeInfo = {
350
+ node,
351
+ expanded,
352
+ selected,
353
+ checked,
354
+ halfChecked,
355
+ loading,
356
+ disabled: nodeDisabled,
357
+ };
358
+ const resolvedIcon = showIcon
359
+ ? resolveNodeIcon(node.icon ?? icon, nodeInfo)
360
+ : null;
361
+ const title = titleRender ? titleRender(node) : node.title;
362
+ const matched = Boolean(filterTreeNode?.(node));
363
+ const tabbable = focusKey === undefined ? node.key === initialFocusKey : node.key === focusKey;
364
+ const nodeTitleId = titleId(node.key, level, index);
365
+ return (_jsxs("li", { role: "none", className: "min-w-0", children: [_jsxs("div", { role: "treeitem", tabIndex: tabbable ? 0 : -1, "data-tree-key": keyToken(node.key), "data-slot": "tree-item", "data-selected": selected || undefined, "data-filter-match": matched || undefined, "aria-expanded": hasChildren ? expanded : undefined, "aria-selected": selectable && node.selectable !== false ? selected : undefined, "aria-checked": checkable && node.checkable !== false
366
+ ? halfChecked
367
+ ? "mixed"
368
+ : checked
369
+ : undefined, "aria-disabled": nodeDisabled || undefined, "aria-busy": loading || undefined, "aria-level": level, "aria-posinset": index + 1, "aria-setsize": nodes.length, onFocus: () => setFocusKey(node.key), onClick: (event) => selectNode(node, event), onKeyDown: (event) => handleKeyDown(event, entry), className: cn("flex min-w-0 items-center gap-1 rounded-md px-1 py-0.5 text-sm outline-none transition-colors", blockNode ? "w-full" : "w-fit max-w-full", !nodeDisabled &&
370
+ "hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring", selected && "bg-accent text-accent-foreground", matched && "font-medium", nodeDisabled && "cursor-not-allowed opacity-50", semanticClassNames.item), style: semanticStyles.item, children: [hasChildren ? (_jsx(Button, { variant: "text", shape: "circle", size: "small", tabIndex: -1, "aria-label": expanded ? "Collapse" : "Expand", "aria-expanded": expanded, disabled: nodeDisabled, onClick: (event) => {
371
+ event.stopPropagation();
372
+ setExpanded(node, !expanded);
373
+ }, className: cn("size-6 min-h-0 shrink-0 p-0", semanticClassNames.switcher), style: semanticStyles.switcher, children: loading
374
+ ? switcherLoadingIcon ?? _jsx(Spinner, {})
375
+ : resolveSwitcherIcon(switcherIcon, nodeInfo) })) : (_jsx("span", { "aria-hidden": "true", "data-slot": "tree-switcher-placeholder", className: cn("size-6 shrink-0", semanticClassNames.switcher), style: semanticStyles.switcher })), checkable && node.checkable !== false ? (_jsx("input", { ref: (input) => {
376
+ if (input)
377
+ input.indeterminate = halfChecked;
378
+ }, type: "checkbox", tabIndex: -1, checked: checked, disabled: nodeDisabled || node.disableCheckbox, "aria-labelledby": nodeTitleId, "aria-checked": halfChecked ? "mixed" : checked, "data-indeterminate": halfChecked || undefined, onClick: (event) => event.stopPropagation(), onChange: (event) => {
379
+ event.stopPropagation();
380
+ setChecked(node, event.currentTarget.checked, event);
381
+ }, className: cn("size-4 shrink-0 accent-primary", semanticClassNames.checkbox), style: semanticStyles.checkbox })) : null, resolvedIcon ? (_jsx("span", { "data-slot": "tree-icon", "aria-hidden": "true", className: cn("flex size-5 shrink-0 items-center justify-center", semanticClassNames.icon), style: semanticStyles.icon, children: resolvedIcon })) : null, _jsx("span", { id: nodeTitleId, "data-slot": "tree-title", className: cn("min-w-0 break-words", semanticClassNames.title), style: semanticStyles.title, children: title })] }), expanded && node.children?.length
382
+ ? renderNodes(node.children, node.key, level + 1)
383
+ : null] }, node.key));
384
+ }) }));
385
+ return (_jsx("div", { ...props, ref: setRootRef, role: "tree", "aria-multiselectable": multiple || undefined, "aria-disabled": disabled || undefined, "data-slot": "tree", "data-checkable": checkable || undefined, "data-show-line": showLine || undefined, className: cn("min-w-0", semanticClassNames.root, className), style: { ...semanticStyles.root, ...style }, children: renderNodes(treeData) }));
386
+ }
@@ -0,0 +1,20 @@
1
+ import type { ElementType, HTMLAttributes, ReactNode } from "react";
2
+ export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
3
+ export type TextSize = "xs" | "sm" | "md" | "lg";
4
+ export type TextTone = "default" | "muted" | "danger" | "success";
5
+ export interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
6
+ level?: HeadingLevel;
7
+ children?: ReactNode;
8
+ }
9
+ export interface TextProps extends HTMLAttributes<HTMLElement> {
10
+ as?: ElementType;
11
+ size?: TextSize;
12
+ tone?: TextTone;
13
+ children?: ReactNode;
14
+ }
15
+ export interface TypographyProps extends HTMLAttributes<HTMLElement> {
16
+ as?: ElementType;
17
+ }
18
+ export declare function Heading({ level, children, className, ...props }: HeadingProps): import("react").JSX.Element;
19
+ export declare function Text({ as: Component, size, tone, children, className, ...props }: TextProps): import("react").JSX.Element;
20
+ export declare function Typography({ as: Component, className, ...props }: TypographyProps): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ export function Heading({ level = 2, children, className, ...props }) {
4
+ const Tag = `h${level}`;
5
+ return _jsx(Tag, { ...props, "data-slot": "heading", className: cn(level === 1 ? "text-3xl font-semibold tracking-tight" : level === 2 ? "text-2xl font-semibold tracking-tight" : level === 3 ? "text-xl font-semibold" : "text-lg font-semibold", className), children: children });
6
+ }
7
+ export function Text({ as: Component = "p", size = "md", tone = "default", children, className, ...props }) {
8
+ return _jsx(Component, { ...props, "data-slot": "text", className: cn(size === "xs" ? "text-xs" : size === "sm" ? "text-sm" : size === "lg" ? "text-lg" : "text-base", tone === "muted" && "text-muted-foreground", tone === "danger" && "text-destructive", tone === "success" && "text-success", className), children: children });
9
+ }
10
+ export function Typography({ as: Component = "p", className, ...props }) {
11
+ return _jsx(Component, { ...props, "data-slot": "typography", className: cn("text-sm text-foreground", className) });
12
+ }
@@ -0,0 +1,20 @@
1
+ import type { UploadLocale } from "./locale.js";
2
+ import { type InputHTMLAttributes, type ReactNode } from "react";
3
+ export interface UploadProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "onChange" | "multiple" | "accept" | "value" | "defaultValue"> {
4
+ locale?: Partial<UploadLocale>;
5
+ children?: ReactNode;
6
+ files?: File[];
7
+ defaultFiles?: File[];
8
+ onFiles?: (files: File[]) => void;
9
+ onRemove?: (file: File) => void;
10
+ multiple?: boolean;
11
+ accept?: string;
12
+ maxCount?: number;
13
+ maxSize?: number;
14
+ error?: ReactNode;
15
+ showFileList?: boolean;
16
+ drag?: boolean;
17
+ beforeSelect?: (file: File, files: File[]) => boolean;
18
+ onReject?: (file: File, reason: "type" | "size" | "beforeSelect" | "maxCount") => void;
19
+ }
20
+ export declare function Upload({ locale, children, files, defaultFiles, onFiles, onRemove, multiple, accept, maxCount, maxSize, disabled, readOnly, error, showFileList, drag, beforeSelect, onReject, id, className, ...props }: UploadProps): import("react").JSX.Element;
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useComponentLocale } from "./config-provider.js";
3
+ import { useId, useState, } from "react";
4
+ import { UploadCloud, X } from "lucide-react";
5
+ import { cn } from "../lib/utils.js";
6
+ export function Upload({ locale, children, files, defaultFiles = [], onFiles, onRemove, multiple = false, accept, maxCount, maxSize, disabled, readOnly, error, showFileList = true, drag = false, beforeSelect, onReject, id, className, ...props }) {
7
+ const text = useComponentLocale("upload", locale);
8
+ const generatedId = useId();
9
+ const inputId = id ?? generatedId;
10
+ const [internalFiles, setInternalFiles] = useState(defaultFiles);
11
+ const currentFiles = files ?? internalFiles;
12
+ const update = (next) => {
13
+ if (files === undefined)
14
+ setInternalFiles(next);
15
+ onFiles?.(next);
16
+ };
17
+ const acceptTokens = accept
18
+ ?.split(",")
19
+ .map((token) => token.trim())
20
+ .filter(Boolean) ?? [];
21
+ const accepts = (file) => acceptTokens.length === 0 ||
22
+ acceptTokens.some((token) => token.startsWith(".")
23
+ ? file.name.toLowerCase().endsWith(token.toLowerCase())
24
+ : token.endsWith("/*")
25
+ ? file.type.startsWith(token.slice(0, -1))
26
+ : file.type === token);
27
+ const select = (incoming) => {
28
+ if (disabled || readOnly)
29
+ return;
30
+ const selected = incoming.filter((file) => {
31
+ if (!accepts(file)) {
32
+ onReject?.(file, "type");
33
+ return false;
34
+ }
35
+ if (maxSize !== undefined && file.size > maxSize) {
36
+ onReject?.(file, "size");
37
+ return false;
38
+ }
39
+ if (beforeSelect && !beforeSelect(file, incoming)) {
40
+ onReject?.(file, "beforeSelect");
41
+ return false;
42
+ }
43
+ return true;
44
+ });
45
+ const combined = multiple
46
+ ? [...currentFiles, ...selected]
47
+ : selected.slice(0, 1);
48
+ if (maxCount !== undefined && combined.length > maxCount)
49
+ combined.slice(maxCount).forEach((file) => onReject?.(file, "maxCount"));
50
+ update(maxCount === undefined ? combined : combined.slice(0, maxCount));
51
+ };
52
+ const change = (event) => {
53
+ select(Array.from(event.target.files ?? []));
54
+ event.target.value = "";
55
+ };
56
+ const remove = (file) => {
57
+ if (disabled || readOnly)
58
+ return;
59
+ update(currentFiles.filter((candidate) => candidate !== file));
60
+ onRemove?.(file);
61
+ };
62
+ const descriptionId = error ? `${inputId}-error` : undefined;
63
+ return (_jsxs("div", { className: cn("space-y-3", className), "data-disabled": disabled || undefined, children: [_jsx("input", { ...props, id: inputId, type: "file", className: "sr-only", multiple: multiple, accept: accept, disabled: disabled || readOnly, "aria-invalid": Boolean(error) || undefined, "aria-describedby": descriptionId, onChange: change }), _jsxs("label", { htmlFor: inputId, onDragOver: drag ? (event) => event.preventDefault() : undefined, onDrop: drag
64
+ ? (event) => {
65
+ event.preventDefault();
66
+ if (!disabled && !readOnly)
67
+ select(Array.from(event.dataTransfer.files));
68
+ }
69
+ : undefined, className: cn("inline-flex min-h-9 cursor-pointer items-center rounded-md border bg-background px-3 py-2 text-sm font-medium transition-colors hover:bg-muted focus-within:ring-2 focus-within:ring-ring", drag &&
70
+ "flex min-h-36 w-full flex-col justify-center gap-2 border-dashed text-center", (disabled || readOnly) &&
71
+ "pointer-events-none cursor-not-allowed opacity-50"), children: [drag ? (_jsx(UploadCloud, { "aria-hidden": "true", className: "size-7 text-muted-foreground" })) : null, children ?? (drag ? "点击或拖放文件到这里" : "Choose file")] }), showFileList && currentFiles.length > 0 ? (_jsx("ul", { className: "space-y-1", "aria-label": text.fileListLabel, children: currentFiles.map((file, index) => (_jsxs("li", { className: "flex items-center justify-between gap-3 rounded-md border px-3 py-2 text-sm", children: [_jsx("span", { className: "min-w-0 truncate", children: file.name }), _jsx("button", { type: "button", className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground", "aria-label": text.removeLabel(file.name), disabled: disabled || readOnly, onClick: () => remove(file), children: _jsx(X, { className: "size-4" }) })] }, `${file.name}-${file.size}-${index}`))) })) : null, error ? (_jsx("div", { id: descriptionId, role: "alert", className: "text-sm text-destructive", children: error })) : null] }));
72
+ }
@@ -0,0 +1,10 @@
1
+ import type { HTMLAttributes, ReactNode, Ref } from "react";
2
+ export interface WatermarkProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
3
+ content: string;
4
+ children: ReactNode;
5
+ rotate?: number;
6
+ gap?: number;
7
+ opacity?: number;
8
+ ref?: Ref<HTMLDivElement>;
9
+ }
10
+ export declare function Watermark({ content, children, rotate, gap, opacity, ref, className, style, ...props }: WatermarkProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ function finiteOr(value, fallback) {
4
+ return Number.isFinite(value) ? value : fallback;
5
+ }
6
+ function escapeXmlText(value) {
7
+ return value
8
+ .replaceAll("&", "&amp;")
9
+ .replaceAll("<", "&lt;")
10
+ .replaceAll(">", "&gt;");
11
+ }
12
+ export function Watermark({ content, children, rotate = -22, gap = 120, opacity = 0.12, ref, className, style, ...props }) {
13
+ const tileSize = Math.max(32, Math.round(finiteOr(gap, 120)));
14
+ const center = tileSize / 2;
15
+ const safeRotate = finiteOr(rotate, -22);
16
+ const safeOpacity = Math.min(1, Math.max(0, finiteOr(opacity, 0.12)));
17
+ const escapedContent = escapeXmlText(content);
18
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${tileSize}" height="${tileSize}" viewBox="0 0 ${tileSize} ${tileSize}"><text x="${center}" y="${center}" text-anchor="middle" dominant-baseline="middle" transform="rotate(${safeRotate} ${center} ${center})" fill="rgba(0,0,0,${safeOpacity})" font-size="16">${escapedContent}</text></svg>`;
19
+ const backgroundImage = `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
20
+ return (_jsx("div", { ...props, ref: ref, "data-slot": "watermark", className: cn("relative", className), style: { backgroundImage, ...style }, children: children }));
21
+ }