@wzrhine/react 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/LICENSE +21 -0
  3. package/README.md +37 -3
  4. package/dist/advanced.css +398 -0
  5. package/dist/archive.css +704 -0
  6. package/dist/command.css +671 -0
  7. package/dist/components/Accordion.d.ts +82 -0
  8. package/dist/components/Accordion.d.ts.map +1 -0
  9. package/dist/components/Accordion.js +19 -0
  10. package/dist/components/Accordion.js.map +1 -0
  11. package/dist/components/Alert.d.ts +63 -0
  12. package/dist/components/Alert.d.ts.map +1 -0
  13. package/dist/components/Alert.js +29 -0
  14. package/dist/components/Alert.js.map +1 -0
  15. package/dist/components/AlertDialog.d.ts +49 -0
  16. package/dist/components/AlertDialog.d.ts.map +1 -0
  17. package/dist/components/AlertDialog.js +122 -0
  18. package/dist/components/AlertDialog.js.map +1 -0
  19. package/dist/components/ArchiveCard.d.ts +58 -0
  20. package/dist/components/ArchiveCard.d.ts.map +1 -0
  21. package/dist/components/ArchiveCard.js +40 -0
  22. package/dist/components/ArchiveCard.js.map +1 -0
  23. package/dist/components/Badge.d.ts +17 -0
  24. package/dist/components/Badge.d.ts.map +1 -0
  25. package/dist/components/Badge.js +16 -0
  26. package/dist/components/Badge.js.map +1 -0
  27. package/dist/components/Breadcrumb.d.ts +48 -0
  28. package/dist/components/Breadcrumb.d.ts.map +1 -0
  29. package/dist/components/Breadcrumb.js +55 -0
  30. package/dist/components/Breadcrumb.js.map +1 -0
  31. package/dist/components/Button.d.ts +33 -0
  32. package/dist/components/Button.d.ts.map +1 -0
  33. package/dist/components/Button.js +24 -0
  34. package/dist/components/Button.js.map +1 -0
  35. package/dist/components/Checkbox.d.ts +81 -0
  36. package/dist/components/Checkbox.d.ts.map +1 -0
  37. package/dist/components/Checkbox.js +34 -0
  38. package/dist/components/Checkbox.js.map +1 -0
  39. package/dist/components/Collapsible.d.ts +47 -0
  40. package/dist/components/Collapsible.d.ts.map +1 -0
  41. package/dist/components/Collapsible.js +12 -0
  42. package/dist/components/Collapsible.js.map +1 -0
  43. package/dist/components/Combobox.d.ts +74 -0
  44. package/dist/components/Combobox.d.ts.map +1 -0
  45. package/dist/components/Combobox.js +61 -0
  46. package/dist/components/Combobox.js.map +1 -0
  47. package/dist/components/CommandPalette.d.ts +77 -0
  48. package/dist/components/CommandPalette.d.ts.map +1 -0
  49. package/dist/components/CommandPalette.js +150 -0
  50. package/dist/components/CommandPalette.js.map +1 -0
  51. package/dist/components/ContextMenu.d.ts +31 -0
  52. package/dist/components/ContextMenu.d.ts.map +1 -0
  53. package/dist/components/ContextMenu.js +25 -0
  54. package/dist/components/ContextMenu.js.map +1 -0
  55. package/dist/components/DataTable.d.ts +83 -0
  56. package/dist/components/DataTable.d.ts.map +1 -0
  57. package/dist/components/DataTable.js +97 -0
  58. package/dist/components/DataTable.js.map +1 -0
  59. package/dist/components/Dialog.d.ts +49 -0
  60. package/dist/components/Dialog.d.ts.map +1 -0
  61. package/dist/components/Dialog.js +48 -0
  62. package/dist/components/Dialog.js.map +1 -0
  63. package/dist/components/Drawer.d.ts +56 -0
  64. package/dist/components/Drawer.d.ts.map +1 -0
  65. package/dist/components/Drawer.js +92 -0
  66. package/dist/components/Drawer.js.map +1 -0
  67. package/dist/components/EmptyState.d.ts +39 -0
  68. package/dist/components/EmptyState.d.ts.map +1 -0
  69. package/dist/components/EmptyState.js +19 -0
  70. package/dist/components/EmptyState.js.map +1 -0
  71. package/dist/components/Field.d.ts +37 -0
  72. package/dist/components/Field.d.ts.map +1 -0
  73. package/dist/components/Field.js +12 -0
  74. package/dist/components/Field.js.map +1 -0
  75. package/dist/components/Fieldset.d.ts +25 -0
  76. package/dist/components/Fieldset.d.ts.map +1 -0
  77. package/dist/components/Fieldset.js +17 -0
  78. package/dist/components/Fieldset.js.map +1 -0
  79. package/dist/components/FileDropZone.d.ts +60 -0
  80. package/dist/components/FileDropZone.d.ts.map +1 -0
  81. package/dist/components/FileDropZone.js +121 -0
  82. package/dist/components/FileDropZone.js.map +1 -0
  83. package/dist/components/Form.d.ts +22 -0
  84. package/dist/components/Form.d.ts.map +1 -0
  85. package/dist/components/Form.js +20 -0
  86. package/dist/components/Form.js.map +1 -0
  87. package/dist/components/Icon.d.ts +25 -0
  88. package/dist/components/Icon.d.ts.map +1 -0
  89. package/dist/components/Icon.js +38 -0
  90. package/dist/components/Icon.js.map +1 -0
  91. package/dist/components/IconButton.d.ts +25 -0
  92. package/dist/components/IconButton.d.ts.map +1 -0
  93. package/dist/components/IconButton.js +14 -0
  94. package/dist/components/IconButton.js.map +1 -0
  95. package/dist/components/Input.d.ts +24 -0
  96. package/dist/components/Input.d.ts.map +1 -0
  97. package/dist/components/Input.js +15 -0
  98. package/dist/components/Input.js.map +1 -0
  99. package/dist/components/Menu.d.ts +66 -0
  100. package/dist/components/Menu.d.ts.map +1 -0
  101. package/dist/components/Menu.js +34 -0
  102. package/dist/components/Menu.js.map +1 -0
  103. package/dist/components/MetadataList.d.ts +51 -0
  104. package/dist/components/MetadataList.d.ts.map +1 -0
  105. package/dist/components/MetadataList.js +25 -0
  106. package/dist/components/MetadataList.js.map +1 -0
  107. package/dist/components/NumberField.d.ts +54 -0
  108. package/dist/components/NumberField.d.ts.map +1 -0
  109. package/dist/components/NumberField.js +27 -0
  110. package/dist/components/NumberField.js.map +1 -0
  111. package/dist/components/Pagination.d.ts +40 -0
  112. package/dist/components/Pagination.d.ts.map +1 -0
  113. package/dist/components/Pagination.js +64 -0
  114. package/dist/components/Pagination.js.map +1 -0
  115. package/dist/components/Popover.d.ts +49 -0
  116. package/dist/components/Popover.d.ts.map +1 -0
  117. package/dist/components/Popover.js +14 -0
  118. package/dist/components/Popover.js.map +1 -0
  119. package/dist/components/Progress.d.ts +57 -0
  120. package/dist/components/Progress.d.ts.map +1 -0
  121. package/dist/components/Progress.js +31 -0
  122. package/dist/components/Progress.js.map +1 -0
  123. package/dist/components/Radio.d.ts +68 -0
  124. package/dist/components/Radio.d.ts.map +1 -0
  125. package/dist/components/Radio.js +18 -0
  126. package/dist/components/Radio.js.map +1 -0
  127. package/dist/components/RecordHeader.d.ts +53 -0
  128. package/dist/components/RecordHeader.d.ts.map +1 -0
  129. package/dist/components/RecordHeader.js +19 -0
  130. package/dist/components/RecordHeader.js.map +1 -0
  131. package/dist/components/ResizeHandle.d.ts +32 -0
  132. package/dist/components/ResizeHandle.d.ts.map +1 -0
  133. package/dist/components/ResizeHandle.js +33 -0
  134. package/dist/components/ResizeHandle.js.map +1 -0
  135. package/dist/components/ScrollArea.d.ts +60 -0
  136. package/dist/components/ScrollArea.d.ts.map +1 -0
  137. package/dist/components/ScrollArea.js +63 -0
  138. package/dist/components/ScrollArea.js.map +1 -0
  139. package/dist/components/Select.d.ts +39 -0
  140. package/dist/components/Select.d.ts.map +1 -0
  141. package/dist/components/Select.js +24 -0
  142. package/dist/components/Select.js.map +1 -0
  143. package/dist/components/Separator.d.ts +26 -0
  144. package/dist/components/Separator.d.ts.map +1 -0
  145. package/dist/components/Separator.js +22 -0
  146. package/dist/components/Separator.js.map +1 -0
  147. package/dist/components/Skeleton.d.ts +31 -0
  148. package/dist/components/Skeleton.d.ts.map +1 -0
  149. package/dist/components/Skeleton.js +17 -0
  150. package/dist/components/Skeleton.js.map +1 -0
  151. package/dist/components/Slider.d.ts +65 -0
  152. package/dist/components/Slider.d.ts.map +1 -0
  153. package/dist/components/Slider.js +30 -0
  154. package/dist/components/Slider.js.map +1 -0
  155. package/dist/components/Spinner.d.ts +23 -0
  156. package/dist/components/Spinner.d.ts.map +1 -0
  157. package/dist/components/Spinner.js +13 -0
  158. package/dist/components/Spinner.js.map +1 -0
  159. package/dist/components/SplitButton.d.ts +59 -0
  160. package/dist/components/SplitButton.d.ts.map +1 -0
  161. package/dist/components/SplitButton.js +30 -0
  162. package/dist/components/SplitButton.js.map +1 -0
  163. package/dist/components/Stat.d.ts +53 -0
  164. package/dist/components/Stat.d.ts.map +1 -0
  165. package/dist/components/Stat.js +23 -0
  166. package/dist/components/Stat.js.map +1 -0
  167. package/dist/components/StatusBadge.d.ts +19 -0
  168. package/dist/components/StatusBadge.d.ts.map +1 -0
  169. package/dist/components/StatusBadge.js +8 -0
  170. package/dist/components/StatusBadge.js.map +1 -0
  171. package/dist/components/StatusIndicator.d.ts +29 -0
  172. package/dist/components/StatusIndicator.d.ts.map +1 -0
  173. package/dist/components/StatusIndicator.js +8 -0
  174. package/dist/components/StatusIndicator.js.map +1 -0
  175. package/dist/components/Surface.d.ts +29 -0
  176. package/dist/components/Surface.d.ts.map +1 -0
  177. package/dist/components/Surface.js +8 -0
  178. package/dist/components/Surface.js.map +1 -0
  179. package/dist/components/Switch.d.ts +22 -0
  180. package/dist/components/Switch.d.ts.map +1 -0
  181. package/dist/components/Switch.js +11 -0
  182. package/dist/components/Switch.js.map +1 -0
  183. package/dist/components/Tabs.d.ts +45 -0
  184. package/dist/components/Tabs.d.ts.map +1 -0
  185. package/dist/components/Tabs.js +49 -0
  186. package/dist/components/Tabs.js.map +1 -0
  187. package/dist/components/Text.d.ts +33 -0
  188. package/dist/components/Text.d.ts.map +1 -0
  189. package/dist/components/Text.js +21 -0
  190. package/dist/components/Text.js.map +1 -0
  191. package/dist/components/Textarea.d.ts +54 -0
  192. package/dist/components/Textarea.d.ts.map +1 -0
  193. package/dist/components/Textarea.js +31 -0
  194. package/dist/components/Textarea.js.map +1 -0
  195. package/dist/components/Timeline.d.ts +35 -0
  196. package/dist/components/Timeline.d.ts.map +1 -0
  197. package/dist/components/Timeline.js +13 -0
  198. package/dist/components/Timeline.js.map +1 -0
  199. package/dist/components/Toast.d.ts +67 -0
  200. package/dist/components/Toast.d.ts.map +1 -0
  201. package/dist/components/Toast.js +94 -0
  202. package/dist/components/Toast.js.map +1 -0
  203. package/dist/components/Tooltip.d.ts +26 -0
  204. package/dist/components/Tooltip.d.ts.map +1 -0
  205. package/dist/components/Tooltip.js +15 -0
  206. package/dist/components/Tooltip.js.map +1 -0
  207. package/dist/components/Tree.d.ts +28 -0
  208. package/dist/components/Tree.d.ts.map +1 -0
  209. package/dist/components/Tree.js +129 -0
  210. package/dist/components/Tree.js.map +1 -0
  211. package/dist/components/VisuallyHidden.d.ts +17 -0
  212. package/dist/components/VisuallyHidden.d.ts.map +1 -0
  213. package/dist/components/VisuallyHidden.js +12 -0
  214. package/dist/components/VisuallyHidden.js.map +1 -0
  215. package/dist/components/Workbench.d.ts +31 -0
  216. package/dist/components/Workbench.d.ts.map +1 -0
  217. package/dist/components/Workbench.js +71 -0
  218. package/dist/components/Workbench.js.map +1 -0
  219. package/dist/components/fieldParts.d.ts +17 -0
  220. package/dist/components/fieldParts.d.ts.map +1 -0
  221. package/dist/components/fieldParts.js +22 -0
  222. package/dist/components/fieldParts.js.map +1 -0
  223. package/dist/components/meter-utils.d.ts +15 -0
  224. package/dist/components/meter-utils.d.ts.map +1 -0
  225. package/dist/components/meter-utils.js +15 -0
  226. package/dist/components/meter-utils.js.map +1 -0
  227. package/dist/components/navigation.d.ts +38 -0
  228. package/dist/components/navigation.d.ts.map +1 -0
  229. package/dist/components/navigation.js +96 -0
  230. package/dist/components/navigation.js.map +1 -0
  231. package/dist/components/pagination-utils.d.ts +10 -0
  232. package/dist/components/pagination-utils.d.ts.map +1 -0
  233. package/dist/components/pagination-utils.js +36 -0
  234. package/dist/components/pagination-utils.js.map +1 -0
  235. package/dist/components.css +678 -0
  236. package/dist/data.css +600 -0
  237. package/dist/disclosure.css +93 -0
  238. package/dist/drawer.css +226 -0
  239. package/dist/feedback.css +454 -0
  240. package/dist/forms.css +533 -0
  241. package/dist/index.d.ts +129 -0
  242. package/dist/index.d.ts.map +1 -0
  243. package/dist/index.js +67 -0
  244. package/dist/index.js.map +1 -0
  245. package/dist/motion/lifecycle.d.ts +44 -0
  246. package/dist/motion/lifecycle.d.ts.map +1 -0
  247. package/dist/motion/lifecycle.js +73 -0
  248. package/dist/motion/lifecycle.js.map +1 -0
  249. package/dist/motion/presence.d.ts +69 -0
  250. package/dist/motion/presence.d.ts.map +1 -0
  251. package/dist/motion/presence.js +103 -0
  252. package/dist/motion/presence.js.map +1 -0
  253. package/dist/motion/presets.d.ts +63 -0
  254. package/dist/motion/presets.d.ts.map +1 -0
  255. package/dist/motion/presets.js +30 -0
  256. package/dist/motion/presets.js.map +1 -0
  257. package/dist/motion/resolve.d.ts +31 -0
  258. package/dist/motion/resolve.d.ts.map +1 -0
  259. package/dist/motion/resolve.js +40 -0
  260. package/dist/motion/resolve.js.map +1 -0
  261. package/dist/motion/useRhineMotion.d.ts +19 -0
  262. package/dist/motion/useRhineMotion.d.ts.map +1 -0
  263. package/dist/motion/useRhineMotion.js +19 -0
  264. package/dist/motion/useRhineMotion.js.map +1 -0
  265. package/dist/motion/visibility.d.ts +6 -0
  266. package/dist/motion/visibility.d.ts.map +1 -0
  267. package/dist/motion/visibility.js +31 -0
  268. package/dist/motion/visibility.js.map +1 -0
  269. package/dist/overlays.css +465 -0
  270. package/dist/provider/RhineProvider.d.ts +65 -0
  271. package/dist/provider/RhineProvider.d.ts.map +1 -0
  272. package/dist/provider/RhineProvider.js +26 -0
  273. package/dist/provider/RhineProvider.js.map +1 -0
  274. package/dist/provider/messages.d.ts +209 -0
  275. package/dist/provider/messages.d.ts.map +1 -0
  276. package/dist/provider/messages.js +168 -0
  277. package/dist/provider/messages.js.map +1 -0
  278. package/dist/provider/portal.d.ts +17 -0
  279. package/dist/provider/portal.d.ts.map +1 -0
  280. package/dist/provider/portal.js +20 -0
  281. package/dist/provider/portal.js.map +1 -0
  282. package/dist/reset.css +45 -0
  283. package/dist/split.css +159 -0
  284. package/dist/status.css +353 -0
  285. package/dist/utils/cx.d.ts +2 -0
  286. package/dist/utils/cx.d.ts.map +1 -0
  287. package/dist/utils/cx.js +4 -0
  288. package/dist/utils/cx.js.map +1 -0
  289. package/package.json +67 -3
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Dialog as BaseDialog } from '@base-ui/react/dialog';
5
+ import { AnimatePresence, motion } from 'motion/react';
6
+ import { usePortalContainer } from '../provider/portal.js';
7
+ import { useRhineMotion } from '../motion/useRhineMotion.js';
8
+ import { cx } from '../utils/cx.js';
9
+ import { IconButton } from './IconButton.js';
10
+ import { Icon } from './Icon.js';
11
+ const closeReasons = {
12
+ 'escape-key': 'escape',
13
+ 'outside-press': 'outside',
14
+ 'close-press': 'close-button',
15
+ 'trigger-press': 'trigger',
16
+ };
17
+ const offset = 8;
18
+ export function Dialog({ trigger, title, description, eyebrow, actions, children, open: openProp, defaultOpen = false, onOpenChange, onClosed, size = 'md', dismissOnOutsideClick = true, initialFocus, closeButton = true, finalFocus, onCloseRequest, className, }) {
19
+ const [uncontrolled, setUncontrolled] = React.useState(defaultOpen);
20
+ const controlled = openProp !== undefined;
21
+ const open = controlled ? openProp : uncontrolled;
22
+ const container = usePortalContainer();
23
+ const motionPrefs = useRhineMotion();
24
+ const rise = motionPrefs.reduced ? 0 : offset;
25
+ const setOpen = (next) => {
26
+ if (!controlled)
27
+ setUncontrolled(next);
28
+ onOpenChange?.(next);
29
+ };
30
+ return (_jsxs(BaseDialog.Root, { open: open, onOpenChange: (next, eventDetails) => {
31
+ if (!next && onCloseRequest) {
32
+ const reason = closeReasons[eventDetails.reason];
33
+ if (reason && onCloseRequest(reason) === false) {
34
+ eventDetails.cancel();
35
+ return;
36
+ }
37
+ }
38
+ setOpen(next);
39
+ }, onOpenChangeComplete: (isOpen) => {
40
+ if (!isOpen)
41
+ onClosed?.();
42
+ }, disablePointerDismissal: !dismissOnOutsideClick, children: [trigger && _jsx(BaseDialog.Trigger, { render: trigger }), _jsx(AnimatePresence, { children: open && (_jsxs(BaseDialog.Portal, { keepMounted: true, container: container, children: [_jsx(BaseDialog.Backdrop, { className: "rl-dialog__backdrop", render: _jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: motionPrefs.get('fast') }) }), _jsxs(BaseDialog.Popup, { className: cx('rl-dialog', className), "data-slot": "dialog", "data-size": size, initialFocus: initialFocus, finalFocus: finalFocus === null ? false : finalFocus, render: _jsx(motion.div, { initial: { opacity: 0, y: rise }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: rise }, transition: motionPrefs.get('panel') }), children: [_jsxs("header", { className: "rl-dialog__header", children: [eyebrow != null && _jsx("p", { className: "rl-dialog__eyebrow", children: eyebrow }), _jsx(BaseDialog.Title, { className: "rl-dialog__title", children: title }), description != null && (_jsx(BaseDialog.Description, { className: "rl-dialog__description", children: description })), closeButton && _jsx(BaseDialog.Close, { render: _jsx(IconButton, { className: "rl-dialog__close", variant: "ghost", size: "sm", icon: _jsx(Icon, { name: "X" }), label: "Close" }) })] }), children != null && _jsx("div", { className: "rl-dialog__body", children: children }), actions != null && _jsx("footer", { className: "rl-dialog__actions", children: actions })] })] })) })] }));
43
+ }
44
+ /** Wraps a button so activating it closes the nearest Dialog. */
45
+ export function DialogClose({ children }) {
46
+ return _jsx(BaseDialog.Close, { render: children });
47
+ }
48
+ //# sourceMappingURL=Dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dialog.js","sourceRoot":"","sources":["../../src/components/Dialog.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Edge panel on Base UI Drawer (ADR-0006 §4): focus enters, Tab loops, Escape and backdrop clicks
4
+ * close, page scroll is locked, the background is inert and focus returns to the trigger — all
5
+ * from Base UI. Below 40rem the panel becomes a bottom sheet (swipe down to dismiss); wider it
6
+ * slides from the inline `side` (swipe toward that edge). CSS owns `transform` (Base UI writes
7
+ * the swipe offset into the same property), so enter / exit are CSS transitions on
8
+ * data-starting-style / data-ending-style: interruptible, and Base UI unmounts after they end.
9
+ */
10
+ export type DrawerSide = 'start' | 'end';
11
+ export type DrawerSize = 'sm' | 'md' | 'lg';
12
+ /**
13
+ * Why the user is trying to close the drawer: the `DialogCloseReason` values (ADR-0006 §3)
14
+ * plus `swipe` for the dismiss gesture.
15
+ */
16
+ export type DrawerCloseReason = 'escape' | 'outside' | 'close-button' | 'trigger' | 'swipe';
17
+ export type DrawerProps = {
18
+ /** Element that opens the drawer (usually a Button). Omit when opening programmatically. */
19
+ trigger?: React.ReactElement<Record<string, unknown>>;
20
+ title: React.ReactNode;
21
+ description?: React.ReactNode;
22
+ /** Small archive-style label above the title, e.g. "ARCHIVE / DETAIL". */
23
+ eyebrow?: React.ReactNode;
24
+ children?: React.ReactNode;
25
+ /** Footer action bar. Use `<DrawerClose>` for buttons that only dismiss. */
26
+ actions?: React.ReactNode;
27
+ open?: boolean;
28
+ defaultOpen?: boolean;
29
+ onOpenChange?: (open: boolean) => void;
30
+ /** Fires after the exit transition, when focus has returned. */
31
+ onClosed?: () => void;
32
+ /** Logical edge the panel attaches to on wide viewports; follows the writing direction. */
33
+ side?: DrawerSide;
34
+ /** Panel width on wide viewports (the bottom sheet is always full width). */
35
+ size?: DrawerSize;
36
+ /** Accessible name of the header close button. */
37
+ closeLabel?: string;
38
+ /**
39
+ * Called before a user-initiated close. Return `false` to keep the drawer open (focus stays put),
40
+ * e.g. while a save is in flight. Programmatic `open={false}` skips it.
41
+ */
42
+ onCloseRequest?: (reason: DrawerCloseReason) => boolean | void;
43
+ /** Element to focus on open; defaults to the panel itself so its title is announced first. */
44
+ initialFocus?: React.RefObject<HTMLElement | null>;
45
+ /** Element to focus after closing; defaults to the trigger or the previously focused element. */
46
+ finalFocus?: React.RefObject<HTMLElement | null>;
47
+ className?: string;
48
+ };
49
+ export declare function Drawer({ trigger, title, description, eyebrow, children, actions, open: openProp, defaultOpen, onOpenChange, onClosed, side, size, closeLabel: closeLabelProp, onCloseRequest, initialFocus, finalFocus, className, }: DrawerProps): React.JSX.Element;
50
+ export type DrawerCloseProps = {
51
+ /** The element that closes the drawer, typically `<Button variant="ghost">取消</Button>`. */
52
+ children: React.ReactElement<Record<string, unknown>>;
53
+ };
54
+ /** Wraps a button so activating it closes the nearest Drawer (reason `close-button`). */
55
+ export declare function DrawerClose({ children }: DrawerCloseProps): React.JSX.Element;
56
+ //# sourceMappingURL=Drawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Drawer.d.ts","sourceRoot":"","sources":["../../src/components/Drawer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B;;;;;;;GAOG;AAEH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,KAAK,CAAC;AACzC,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5F,MAAM,MAAM,WAAW,GAAG;IACxB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,4EAA4E;IAC5E,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,2FAA2F;IAC3F,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,KAAK,OAAO,GAAG,IAAI,CAAC;IAC/D,8FAA8F;IAC9F,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACnD,iGAAiG;IACjG,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwCF,wBAAgB,MAAM,CAAC,EACrB,OAAO,EACP,KAAK,EACL,WAAW,EACX,OAAO,EACP,QAAQ,EACR,OAAO,EACP,IAAI,EAAE,QAAQ,EACd,WAAmB,EACnB,YAAY,EACZ,QAAQ,EACR,IAAY,EACZ,IAAW,EACX,UAAU,EAAE,cAAc,EAC1B,cAAc,EACd,YAAY,EACZ,UAAU,EACV,SAAS,GACV,EAAE,WAAW,qBAmGb;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2FAA2F;IAC3F,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD,CAAC;AAEF,yFAAyF;AACzF,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE,gBAAgB,qBAEzD"}
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Drawer as BaseDrawer } from '@base-ui/react/drawer';
5
+ import { useRhineMessages } from '../provider/messages.js';
6
+ import { usePortalContainer } from '../provider/portal.js';
7
+ import { cx } from '../utils/cx.js';
8
+ import { IconButton } from './IconButton.js';
9
+ const closeReasons = {
10
+ 'escape-key': 'escape',
11
+ // Android back gesture (CloseWatcher) is the platform's Escape.
12
+ 'close-watcher': 'escape',
13
+ 'outside-press': 'outside',
14
+ 'close-press': 'close-button',
15
+ 'trigger-press': 'trigger',
16
+ swipe: 'swipe',
17
+ };
18
+ /** Below this width the drawer is a bottom sheet (ADR-0006 §4: 40rem). */
19
+ const sheetQuery = '(max-width: 39.99rem)';
20
+ const hasMatchMedia = () => typeof window !== 'undefined' && typeof window.matchMedia === 'function';
21
+ function subscribeSheet(onChange) {
22
+ if (!hasMatchMedia())
23
+ return () => { };
24
+ const list = window.matchMedia(sheetQuery);
25
+ list.addEventListener('change', onChange);
26
+ return () => list.removeEventListener('change', onChange);
27
+ }
28
+ const getSheet = () => hasMatchMedia() && window.matchMedia(sheetQuery).matches;
29
+ // Server and hydration render the wide layout; the panel only exists once open.
30
+ const getServerSheet = () => false;
31
+ const useIsoLayoutEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;
32
+ function CloseIcon() {
33
+ return (_jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", focusable: "false", children: _jsx("path", { d: "M6 6l12 12M18 6L6 18" }) }));
34
+ }
35
+ export function Drawer({ trigger, title, description, eyebrow, children, actions, open: openProp, defaultOpen = false, onOpenChange, onClosed, side = 'end', size = 'md', closeLabel: closeLabelProp, onCloseRequest, initialFocus, finalFocus, className, }) {
36
+ const messages = useRhineMessages();
37
+ const closeLabel = closeLabelProp ?? messages.drawer.close;
38
+ const [uncontrolled, setUncontrolled] = React.useState(defaultOpen);
39
+ const controlled = openProp !== undefined;
40
+ const open = controlled ? openProp : uncontrolled;
41
+ const container = usePortalContainer();
42
+ const sheet = React.useSyncExternalStore(subscribeSheet, getSheet, getServerSheet);
43
+ const titleId = React.useId();
44
+ // `side` is logical: read the writing direction where the panel renders, once per opening.
45
+ const [rtl, setRtl] = React.useState(false);
46
+ useIsoLayoutEffect(() => {
47
+ if (!open)
48
+ return;
49
+ const host = container ?? document.body;
50
+ setRtl(getComputedStyle(host).direction === 'rtl' || host.closest('[dir]')?.getAttribute('dir') === 'rtl');
51
+ }, [open, container]);
52
+ const placement = sheet ? 'bottom' : (side === 'end') !== rtl ? 'right' : 'left';
53
+ // A body that overflows becomes a focusable, named region so keyboard users can scroll it.
54
+ const [body, setBody] = React.useState(null);
55
+ const [scrollable, setScrollable] = React.useState(false);
56
+ useIsoLayoutEffect(() => {
57
+ if (!body)
58
+ return undefined;
59
+ const update = () => setScrollable(body.scrollHeight > body.clientHeight + 1);
60
+ update();
61
+ if (typeof ResizeObserver === 'undefined')
62
+ return undefined;
63
+ const observer = new ResizeObserver(update);
64
+ observer.observe(body);
65
+ for (const child of Array.from(body.children))
66
+ observer.observe(child);
67
+ return () => observer.disconnect();
68
+ }, [body, children]);
69
+ const setOpen = (next) => {
70
+ if (!controlled)
71
+ setUncontrolled(next);
72
+ onOpenChange?.(next);
73
+ };
74
+ return (_jsxs(BaseDrawer.Root, { open: open, swipeDirection: placement === 'bottom' ? 'down' : placement, onOpenChange: (next, eventDetails) => {
75
+ if (!next && onCloseRequest) {
76
+ const reason = closeReasons[eventDetails.reason];
77
+ if (reason && onCloseRequest(reason) === false) {
78
+ eventDetails.cancel();
79
+ return;
80
+ }
81
+ }
82
+ setOpen(next);
83
+ }, onOpenChangeComplete: (isOpen) => {
84
+ if (!isOpen)
85
+ onClosed?.();
86
+ }, children: [trigger && _jsx(BaseDrawer.Trigger, { render: trigger }), _jsxs(BaseDrawer.Portal, { container: container, children: [_jsx(BaseDrawer.Backdrop, { className: "rl-drawer__backdrop" }), _jsx(BaseDrawer.Viewport, { className: "rl-drawer__viewport", "data-placement": placement, children: _jsxs(BaseDrawer.Popup, { className: cx('rl-drawer', className), "data-slot": "drawer", "data-placement": placement, "data-size": size, initialFocus: initialFocus, finalFocus: finalFocus, children: [_jsx("span", { className: "rl-drawer__handle", "aria-hidden": "true" }), _jsxs(BaseDrawer.Content, { className: "rl-drawer__content", children: [_jsxs("header", { className: "rl-drawer__header", children: [_jsxs("div", { className: "rl-drawer__heading", children: [eyebrow != null && _jsx("p", { className: "rl-dialog__eyebrow", children: eyebrow }), _jsx(BaseDrawer.Title, { id: titleId, className: "rl-dialog__title rl-drawer__title", children: title }), description != null && (_jsx(BaseDrawer.Description, { className: "rl-dialog__description", children: description }))] }), _jsx(BaseDrawer.Close, { render: _jsx(IconButton, { className: "rl-drawer__close", icon: _jsx(CloseIcon, {}), label: closeLabel }) })] }), _jsx("div", { ref: setBody, className: "rl-drawer__body", role: scrollable ? 'region' : undefined, "aria-labelledby": scrollable ? titleId : undefined, tabIndex: scrollable ? 0 : undefined, children: children }), actions != null && _jsx("footer", { className: "rl-drawer__actions", children: actions })] })] }) })] })] }));
87
+ }
88
+ /** Wraps a button so activating it closes the nearest Drawer (reason `close-button`). */
89
+ export function DrawerClose({ children }) {
90
+ return _jsx(BaseDrawer.Close, { render: children });
91
+ }
92
+ //# sourceMappingURL=Drawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Drawer.js","sourceRoot":"","sources":["../../src/components/Drawer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Placeholder for a region with nothing to show (ADR-0006 §6). `kind` only picks the default
4
+ * decorative icon. The title is a heading when `headingLevel` is given, otherwise a paragraph
5
+ * (e.g. inside a table cell). No live role: the filter result count announces empty results.
6
+ *
7
+ * <EmptyState kind="no-results" title="没有符合条件的记录" headingLevel={3}
8
+ * description="调整筛选条件后重试。" action={<Button size="sm" variant="secondary">清除筛选</Button>} />
9
+ */
10
+ export type EmptyStateKind = 'empty' | 'no-results' | 'no-permission' | 'first-use';
11
+ export type EmptyStateSize = 'sm' | 'md';
12
+ export type EmptyStateHeadingLevel = 2 | 3 | 4 | 5 | 6;
13
+ export type EmptyStateProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> & {
14
+ title: React.ReactNode;
15
+ kind?: EmptyStateKind;
16
+ description?: React.ReactNode;
17
+ /** Next step, e.g. 「清除筛选」 or 「新建记录」. */
18
+ action?: React.ReactNode;
19
+ /** Replace the kind's icon; `null` removes it. Always decorative. */
20
+ icon?: React.ReactNode;
21
+ /** Render the title as `<h2>`–`<h6>`; omitted = `<p>`. */
22
+ headingLevel?: EmptyStateHeadingLevel;
23
+ /** `sm` inside tables and cards; `md` for a whole region. */
24
+ size?: EmptyStateSize;
25
+ };
26
+ export declare const EmptyState: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLDivElement>, "title"> & {
27
+ title: React.ReactNode;
28
+ kind?: EmptyStateKind;
29
+ description?: React.ReactNode;
30
+ /** Next step, e.g. 「清除筛选」 or 「新建记录」. */
31
+ action?: React.ReactNode;
32
+ /** Replace the kind's icon; `null` removes it. Always decorative. */
33
+ icon?: React.ReactNode;
34
+ /** Render the title as `<h2>`–`<h6>`; omitted = `<p>`. */
35
+ headingLevel?: EmptyStateHeadingLevel;
36
+ /** `sm` inside tables and cards; `md` for a whole region. */
37
+ size?: EmptyStateSize;
38
+ } & React.RefAttributes<HTMLDivElement>>;
39
+ //# sourceMappingURL=EmptyState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmptyState.d.ts","sourceRoot":"","sources":["../../src/components/EmptyState.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B;;;;;;;GAOG;AAEH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,YAAY,GAAG,eAAe,GAAG,WAAW,CAAC;AACpF,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,MAAM,MAAM,sBAAsB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEvD,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,GAAG;IAClF,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,wCAAwC;IACxC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,qEAAqE;IACrE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,sBAAsB,CAAC;IACtC,6DAA6D;IAC7D,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAaF,eAAO,MAAM,UAAU;WAxBd,KAAK,CAAC,SAAS;WACf,cAAc;kBACP,KAAK,CAAC,SAAS;IAC7B,wCAAwC;aAC/B,KAAK,CAAC,SAAS;IACxB,qEAAqE;WAC9D,KAAK,CAAC,SAAS;IACtB,0DAA0D;mBAC3C,sBAAsB;IACrC,6DAA6D;WACtD,cAAc;wCAmCrB,CAAC"}
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { cx } from '../utils/cx.js';
5
+ const glyphs = {
6
+ // Open tray.
7
+ empty: _jsx("path", { d: "M2.5 9.5 4.5 3.5h7l2 6M2.5 9.5v3h11v-3M2.5 9.5h3.25l.75 1.5h3l.75-1.5h3.25" }),
8
+ // Magnifier with a dash: nothing matched.
9
+ 'no-results': _jsx("path", { d: "M7 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10ZM10.75 10.75 14 14M5 7h4" }),
10
+ // Padlock.
11
+ 'no-permission': _jsx("path", { d: "M4 7.5h8v6H4zM5.5 7.5V5.5a2.5 2.5 0 0 1 5 0v2M8 10v1.5" }),
12
+ // Sheet with a plus: create the first item.
13
+ 'first-use': _jsx("path", { d: "M4 2.5h5.5l2.5 2.5v8.5H4zM9.5 2.5V5H12M8 7.5v4M6 9.5h4" }),
14
+ };
15
+ export const EmptyState = React.forwardRef(function EmptyState({ title, kind = 'empty', description, action, icon, headingLevel, size = 'md', className, ...rest }, ref) {
16
+ const Title = (headingLevel ? `h${headingLevel}` : 'p');
17
+ return (_jsxs("div", { ...rest, ref: ref, className: cx('rl-empty', className), "data-slot": "empty-state", "data-kind": kind, "data-size": size, children: [icon === undefined ? (_jsx("svg", { className: "rl-empty__icon rl-empty__glyph", viewBox: "0 0 16 16", "aria-hidden": "true", focusable: "false", children: glyphs[kind] })) : icon === null ? null : (_jsx("span", { className: "rl-empty__icon", "aria-hidden": "true", children: icon })), _jsx(Title, { className: "rl-empty__title", children: title }), description != null && description !== false && _jsx("p", { className: "rl-empty__description", children: description }), action != null && action !== false && _jsx("div", { className: "rl-empty__action", children: action })] }));
18
+ });
19
+ //# sourceMappingURL=EmptyState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmptyState.js","sourceRoot":"","sources":["../../src/components/EmptyState.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AA6BpC,MAAM,MAAM,GAA4C;IACtD,aAAa;IACb,KAAK,EAAE,eAAM,CAAC,EAAC,4EAA4E,GAAG;IAC9F,0CAA0C;IAC1C,YAAY,EAAE,eAAM,CAAC,EAAC,8DAA8D,GAAG;IACvF,WAAW;IACX,eAAe,EAAE,eAAM,CAAC,EAAC,wDAAwD,GAAG;IACpF,4CAA4C;IAC5C,WAAW,EAAE,eAAM,CAAC,EAAC,wDAAwD,GAAG;CACjF,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAkC,SAAS,UAAU,CAC7F,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,EACnG,GAAG;IAEH,MAAM,KAAK,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC,CAAC,GAAG,CAAQ,CAAC;IAC/D,OAAO,CACL,kBAAS,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,eAAY,aAAa,eAAY,IAAI,eAAa,IAAI,aACpH,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CACpB,cAAK,SAAS,EAAC,gCAAgC,EAAC,OAAO,EAAC,WAAW,iBAAa,MAAM,EAAC,SAAS,EAAC,OAAO,YACrG,MAAM,CAAC,IAAI,CAAC,GACT,CACP,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACzB,eAAM,SAAS,EAAC,gBAAgB,iBAAa,MAAM,YAChD,IAAI,GACA,CACR,EACD,KAAC,KAAK,IAAC,SAAS,EAAC,iBAAiB,YAAE,KAAK,GAAS,EACjD,WAAW,IAAI,IAAI,IAAI,WAAW,KAAK,KAAK,IAAI,YAAG,SAAS,EAAC,uBAAuB,YAAE,WAAW,GAAK,EACtG,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,KAAK,IAAI,cAAK,SAAS,EAAC,kBAAkB,YAAE,MAAM,GAAO,IACnF,CACP,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+ import { Field as BaseField } from '@base-ui/react/field';
3
+ /**
4
+ * Form field built on Base UI Field (ADR-0001): label, description and error are
5
+ * wired to the control via aria-labelledby / aria-describedby / aria-invalid automatically.
6
+ *
7
+ * Simple form:
8
+ * <Field label="记录编号" description="由归档系统分配" error="请填写记录编号" required>
9
+ * <Input name="id" />
10
+ * </Field>
11
+ */
12
+ type BaseRootProps = React.ComponentProps<typeof BaseField.Root>;
13
+ export type FieldValidationMode = 'onSubmit' | 'onBlur' | 'onChange';
14
+ export type FieldProps = Omit<BaseRootProps, 'className' | 'style' | 'render' | 'children'> & {
15
+ label: React.ReactNode;
16
+ /** Persistent hint, read after the label. Keep it about the expected format, not marketing copy. */
17
+ description?: React.ReactNode;
18
+ /**
19
+ * Error text. A string shows on any native validity failure; pass `invalid` together
20
+ * with `error` to drive it from an external validator.
21
+ */
22
+ error?: React.ReactNode;
23
+ /** Restrict when `error` is shown to one ValidityState key (e.g. `valueMissing`). */
24
+ errorMatch?: React.ComponentProps<typeof BaseField.Error>['match'];
25
+ /** Visual + semantic required marker. Also set `required` on the control for native validation. */
26
+ required?: boolean;
27
+ /** Visually de-emphasised "(可选)" suffix for optional fields in mostly-required forms. */
28
+ optional?: boolean;
29
+ /** Stack (default) or place the label in a fixed-width column on wide containers. */
30
+ layout?: 'stacked' | 'inline';
31
+ className?: string;
32
+ style?: React.CSSProperties;
33
+ children: React.ReactNode;
34
+ };
35
+ export declare const Field: React.ForwardRefExoticComponent<Omit<FieldProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
36
+ export {};
37
+ //# sourceMappingURL=Field.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../src/components/Field.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAI1D;;;;;;;;GAQG;AAEH,KAAK,aAAa,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;AAEjE,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAErE,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC,GAAG;IAC5F,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,oGAAoG;IACpG,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,qFAAqF;IACrF,UAAU,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;IACnE,mGAAmG;IACnG,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qFAAqF;IACrF,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,KAAK,gGAwChB,CAAC"}
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Field as BaseField } from '@base-ui/react/field';
5
+ import { cx } from '../utils/cx.js';
6
+ import { FieldErrorMessage, LabelText } from './fieldParts.js';
7
+ export const Field = React.forwardRef(function Field({ label, description, error, errorMatch, required = false, optional = false, invalid, layout = 'stacked', className, style, children, ...rest }, ref) {
8
+ // An externally driven error (`invalid` + `error`) must always render; otherwise follow native validity.
9
+ const match = errorMatch ?? (invalid ? true : undefined);
10
+ return (_jsxs(BaseField.Root, { ...rest, ref: ref, invalid: invalid, className: cx('rl-field', className), style: style, "data-slot": "field", "data-layout": layout, "data-required": required || undefined, children: [_jsx(BaseField.Label, { className: "rl-field__label", children: _jsx(LabelText, { label: label, required: required, optional: optional }) }), _jsxs("div", { className: "rl-field__body", children: [children, description != null && _jsx(BaseField.Description, { className: "rl-field__description", children: description }), (error != null || rest.name != null) && _jsx(FieldErrorMessage, { error: error, match: match, name: rest.name })] })] }));
11
+ });
12
+ //# sourceMappingURL=Field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Field.js","sourceRoot":"","sources":["../../src/components/Field.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAsC/D,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAA6B,SAAS,KAAK,CAC9E,EACE,KAAK,EACL,WAAW,EACX,KAAK,EACL,UAAU,EACV,QAAQ,GAAG,KAAK,EAChB,QAAQ,GAAG,KAAK,EAChB,OAAO,EACP,MAAM,GAAG,SAAS,EAClB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,GAAG,IAAI,EACR,EACD,GAAG;IAEH,yGAAyG;IACzG,MAAM,KAAK,GAAG,UAAU,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACzD,OAAO,CACL,MAAC,SAAS,CAAC,IAAI,OACT,IAAI,EACR,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,EACpC,KAAK,EAAE,KAAK,eACF,OAAO,iBACJ,MAAM,mBACJ,QAAQ,IAAI,SAAS,aAEpC,KAAC,SAAS,CAAC,KAAK,IAAC,SAAS,EAAC,iBAAiB,YAC1C,KAAC,SAAS,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,GAAI,GACnD,EAClB,eAAK,SAAS,EAAC,gBAAgB,aAC5B,QAAQ,EACR,WAAW,IAAI,IAAI,IAAI,KAAC,SAAS,CAAC,WAAW,IAAC,SAAS,EAAC,uBAAuB,YAAE,WAAW,GAAyB,EACrH,CAAC,KAAK,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAC,iBAAiB,IAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,GAAI,IACvG,IACS,CAClB,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import * as React from 'react';
2
+ import { Fieldset as BaseFieldset } from '@base-ui/react/fieldset';
3
+ type BaseRootProps = React.ComponentProps<typeof BaseFieldset.Root>;
4
+ export type FieldsetProps = Omit<BaseRootProps, 'className' | 'style' | 'render' | 'children'> & {
5
+ /** Section title; names the group for assistive tech. */
6
+ legend: React.ReactNode;
7
+ /** Read after the legend as the group's description. */
8
+ description?: React.ReactNode;
9
+ /** `disabled` disables every Field and control inside, including Base UI ones. */
10
+ disabled?: boolean;
11
+ legendOptional?: boolean;
12
+ className?: string;
13
+ style?: React.CSSProperties;
14
+ children?: React.ReactNode;
15
+ };
16
+ /**
17
+ * Groups related fields under a visible legend (a native `<fieldset>`):
18
+ *
19
+ * <Fieldset legend="联系方式" description="用于复核提醒。">
20
+ * <Field label="邮箱"><Input type="email" /></Field>
21
+ * </Fieldset>
22
+ */
23
+ export declare const Fieldset: React.ForwardRefExoticComponent<Omit<FieldsetProps, "ref"> & React.RefAttributes<HTMLElement>>;
24
+ export {};
25
+ //# sourceMappingURL=Fieldset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Fieldset.d.ts","sourceRoot":"","sources":["../../src/components/Fieldset.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAGnE,KAAK,aAAa,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAEpE,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,UAAU,CAAC,GAAG;IAC/F,yDAAyD;IACzD,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,wDAAwD;IACxD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,gGAuBnB,CAAC"}
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { Fieldset as BaseFieldset } from '@base-ui/react/fieldset';
5
+ import { cx } from '../utils/cx.js';
6
+ /**
7
+ * Groups related fields under a visible legend (a native `<fieldset>`):
8
+ *
9
+ * <Fieldset legend="联系方式" description="用于复核提醒。">
10
+ * <Field label="邮箱"><Input type="email" /></Field>
11
+ * </Fieldset>
12
+ */
13
+ export const Fieldset = React.forwardRef(function Fieldset({ legend, description, legendOptional = false, className, style, children, ...rest }, ref) {
14
+ const descriptionId = React.useId();
15
+ return (_jsxs(BaseFieldset.Root, { ...rest, ref: ref, className: cx('rl-fieldset', className), style: style, "data-slot": "fieldset", "aria-describedby": description != null ? descriptionId : undefined, children: [_jsxs(BaseFieldset.Legend, { className: "rl-fieldset__legend", children: [legend, _jsx("span", { className: legendOptional ? undefined : 'rl-visually-hidden', children: legendOptional ? '(optional)' : '' })] }), description != null && (_jsx("p", { id: descriptionId, className: "rl-fieldset__description", children: description })), _jsx("div", { className: "rl-fieldset__body", children: children })] }));
16
+ });
17
+ //# sourceMappingURL=Fieldset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Fieldset.js","sourceRoot":"","sources":["../../src/components/Fieldset.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAiBpC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAA6B,SAAS,QAAQ,CACpF,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EACpF,GAAG;IAEH,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IACpC,OAAO,CACL,MAAC,YAAY,CAAC,IAAI,OACZ,IAAI,EACR,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC,EACvC,KAAK,EAAE,KAAK,eACF,UAAU,sBACF,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,aAEjE,MAAC,YAAY,CAAC,MAAM,IAAC,SAAS,EAAC,qBAAqB,aAAE,MAAM,EAAC,eAAM,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,oBAAoB,YAAG,cAAc,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,GAAQ,IAAsB,EACjM,WAAW,IAAI,IAAI,IAAI,CACtB,YAAG,EAAE,EAAE,aAAa,EAAE,SAAS,EAAC,0BAA0B,YACvD,WAAW,GACV,CACL,EACD,cAAK,SAAS,EAAC,mBAAmB,YAAE,QAAQ,GAAO,IACjC,CACrB,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * File picker and drop target (ADR-0010 §2). The control is a real `<input type="file">`: it is
4
+ * visually hidden but stays focusable, and its visible `<label>` looks like a button, so Tab,
5
+ * Enter / Space and screen readers use the native picker. Dropping files on the area runs the
6
+ * same validation (`accept` like the native attribute, `maxSize`, `maxFiles`). Rejections are
7
+ * announced in a polite live region. No upload logic: `onFiles` receives the accepted files.
8
+ *
9
+ * <FileDropZone label="样本图像" accept="image/*" maxSize={1024 * 1024} onFiles={add} />
10
+ */
11
+ export type FileRejectionReason = 'type' | 'size' | 'count';
12
+ export interface FileRejection {
13
+ file: File;
14
+ reason: FileRejectionReason;
15
+ }
16
+ export type FileDropZoneProps = {
17
+ /** Visible label above the drop area. */
18
+ label: React.ReactNode;
19
+ description?: React.ReactNode;
20
+ /** Same syntax as the native attribute: `.png,.jpg`, `image/*`, `application/pdf`. */
21
+ accept?: string;
22
+ multiple?: boolean;
23
+ /** Maximum size of one file, in bytes. */
24
+ maxSize?: number;
25
+ /** Maximum number of files accepted from one pick or drop (default: 1 unless `multiple`). */
26
+ maxFiles?: number;
27
+ disabled?: boolean;
28
+ /** Form field name of the native input. The input is cleared after each pick, so submit files yourself. */
29
+ name?: string;
30
+ /** Accepted files of one pick or drop (not called when every file was rejected). */
31
+ onFiles?: (files: File[]) => void;
32
+ /** Rejected files of one pick or drop, with the first reason found (type, then size, then count). */
33
+ onReject?: (rejections: FileRejection[]) => void;
34
+ /** Custom prompt inside the drop area (replaces the default text, not the button). */
35
+ children?: React.ReactNode;
36
+ /** Text of the browse button (default from messages). */
37
+ browseLabel?: React.ReactNode;
38
+ /** Formats `maxSize` for rejection messages (default: B / KB / MB in the provider locale). */
39
+ formatSize?: (bytes: number) => string;
40
+ /** Visual and semantic invalid state, e.g. a required attachment is missing. */
41
+ invalid?: boolean;
42
+ /** Error text shown under the area while `invalid`. */
43
+ error?: React.ReactNode;
44
+ id?: string;
45
+ className?: string;
46
+ style?: React.CSSProperties;
47
+ };
48
+ /** Whether `file` matches a native-style `accept` list (extensions, exact MIME types, `type/*`). */
49
+ export declare function matchesAccept(file: Pick<File, 'name' | 'type'>, accept: string | undefined): boolean;
50
+ /** Splits files into accepted and rejected ones; first failing rule wins (type → size → count). */
51
+ export declare function validateFiles(files: readonly File[], { accept, maxSize, maxFiles }: {
52
+ accept?: string;
53
+ maxSize?: number;
54
+ maxFiles?: number;
55
+ }): {
56
+ accepted: File[];
57
+ rejected: FileRejection[];
58
+ };
59
+ export declare const FileDropZone: React.ForwardRefExoticComponent<FileDropZoneProps & React.RefAttributes<HTMLInputElement>>;
60
+ //# sourceMappingURL=FileDropZone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileDropZone.d.ts","sourceRoot":"","sources":["../../src/components/FileDropZone.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B;;;;;;;;GAQG;AAEH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,mBAAmB,CAAC;CAC7B;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,yCAAyC;IACzC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2GAA2G;IAC3G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAClC,qGAAqG;IACrG,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IACjD,sFAAsF;IACtF,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,yDAAyD;IACzD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,8FAA8F;IAC9F,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,gFAAgF;IAChF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B,CAAC;AAEF,oGAAoG;AACpG,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAapG;AAED,mGAAmG;AACnG,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACtF;IAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,CAUjD;AAmBD,eAAO,MAAM,YAAY,4FA+IvB,CAAC"}
@@ -0,0 +1,121 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ import { useRhineLocale } from '../provider/messages.js';
5
+ import { cx } from '../utils/cx.js';
6
+ /** Whether `file` matches a native-style `accept` list (extensions, exact MIME types, `type/*`). */
7
+ export function matchesAccept(file, accept) {
8
+ if (!accept || accept.trim() === '')
9
+ return true;
10
+ const name = file.name.toLowerCase();
11
+ const type = (file.type || '').toLowerCase();
12
+ return accept
13
+ .split(',')
14
+ .map((token) => token.trim().toLowerCase())
15
+ .filter(Boolean)
16
+ .some((token) => {
17
+ if (token.startsWith('.'))
18
+ return name.endsWith(token);
19
+ if (token.endsWith('/*'))
20
+ return type.startsWith(token.slice(0, -1));
21
+ return type === token;
22
+ });
23
+ }
24
+ /** Splits files into accepted and rejected ones; first failing rule wins (type → size → count). */
25
+ export function validateFiles(files, { accept, maxSize, maxFiles }) {
26
+ const accepted = [];
27
+ const rejected = [];
28
+ for (const file of files) {
29
+ if (!matchesAccept(file, accept))
30
+ rejected.push({ file, reason: 'type' });
31
+ else if (maxSize !== undefined && file.size > maxSize)
32
+ rejected.push({ file, reason: 'size' });
33
+ else if (maxFiles !== undefined && accepted.length >= maxFiles)
34
+ rejected.push({ file, reason: 'count' });
35
+ else
36
+ accepted.push(file);
37
+ }
38
+ return { accepted, rejected };
39
+ }
40
+ function defaultFormatSize(bytes, format) {
41
+ if (bytes >= 1024 * 1024)
42
+ return `${format(Math.round((bytes / (1024 * 1024)) * 10) / 10)} MB`;
43
+ if (bytes >= 1024)
44
+ return `${format(Math.round((bytes / 1024) * 10) / 10)} KB`;
45
+ return `${format(bytes)} B`;
46
+ }
47
+ function UploadIcon() {
48
+ return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", focusable: "false", children: [_jsx("path", { d: "M12 15V4m0 0-4 4m4-4 4 4", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M4 14v4.5A1.5 1.5 0 0 0 5.5 20h13a1.5 1.5 0 0 0 1.5-1.5V14", strokeLinecap: "round" })] }));
49
+ }
50
+ const hasFiles = (event) => Array.from(event.dataTransfer?.types ?? []).includes('Files');
51
+ export const FileDropZone = React.forwardRef(function FileDropZone({ label, description, accept, multiple = false, maxSize, maxFiles, disabled = false, name, onFiles, onReject, children, browseLabel, formatSize, invalid = false, error, id, className, style, }, ref) {
52
+ const { messages, formatNumber } = useRhineLocale();
53
+ const copy = messages.fileDropZone;
54
+ const autoId = React.useId();
55
+ const inputId = id ?? `${autoId}-input`;
56
+ const labelId = `${autoId}-label`;
57
+ const descriptionId = `${autoId}-description`;
58
+ const errorId = `${autoId}-error`;
59
+ const [dragOver, setDragOver] = React.useState(false);
60
+ const [announcement, setAnnouncement] = React.useState('');
61
+ // Nested children fire dragenter / dragleave; count them so the highlight doesn't flicker.
62
+ const depth = React.useRef(0);
63
+ const limit = maxFiles ?? (multiple ? undefined : 1);
64
+ const describeRejection = (rejection) => {
65
+ const fileName = rejection.file.name;
66
+ if (rejection.reason === 'type')
67
+ return copy.rejectedType(fileName);
68
+ if (rejection.reason === 'size') {
69
+ const size = maxSize ?? 0;
70
+ return copy.rejectedSize(fileName, formatSize ? formatSize(size) : defaultFormatSize(size, formatNumber));
71
+ }
72
+ return copy.rejectedCount(fileName, limit ?? 0, formatNumber);
73
+ };
74
+ const handleFiles = (list) => {
75
+ const files = list ? Array.from(list) : [];
76
+ if (files.length === 0)
77
+ return;
78
+ const { accepted, rejected } = validateFiles(files, { accept, maxSize, maxFiles: limit });
79
+ const lines = [...(accepted.length > 0 ? [copy.accepted(accepted.length, formatNumber)] : []), ...rejected.map(describeRejection)];
80
+ setAnnouncement(lines.join('\n'));
81
+ if (accepted.length > 0)
82
+ onFiles?.(accepted);
83
+ if (rejected.length > 0)
84
+ onReject?.(rejected);
85
+ };
86
+ const onDragEnter = (event) => {
87
+ if (disabled || !hasFiles(event))
88
+ return;
89
+ event.preventDefault();
90
+ depth.current += 1;
91
+ setDragOver(true);
92
+ };
93
+ const onDragOver = (event) => {
94
+ if (disabled || !hasFiles(event))
95
+ return;
96
+ event.preventDefault();
97
+ event.dataTransfer.dropEffect = 'copy';
98
+ };
99
+ const onDragLeave = () => {
100
+ if (disabled)
101
+ return;
102
+ depth.current = Math.max(0, depth.current - 1);
103
+ if (depth.current === 0)
104
+ setDragOver(false);
105
+ };
106
+ const onDrop = (event) => {
107
+ depth.current = 0;
108
+ setDragOver(false);
109
+ if (disabled)
110
+ return;
111
+ event.preventDefault();
112
+ handleFiles(event.dataTransfer?.files);
113
+ };
114
+ const describedBy = [description != null ? descriptionId : null, invalid && error != null ? errorId : null].filter(Boolean).join(' ') || undefined;
115
+ return (_jsxs("div", { className: cx('rl-file-drop', className), style: style, "data-slot": "file-drop-zone", "data-disabled": disabled || undefined, "data-invalid": invalid || undefined, children: [_jsx("span", { id: labelId, className: "rl-file-drop__label", children: label }), description != null && (_jsx("p", { id: descriptionId, className: "rl-file-drop__description", children: description })), _jsxs("div", { className: "rl-file-drop__area", "data-drag-over": dragOver || undefined, onDragEnter: onDragEnter, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, children: [_jsx("span", { className: "rl-file-drop__icon", "aria-hidden": "true", children: _jsx(UploadIcon, {}) }), _jsx("span", { className: "rl-file-drop__prompt", children: children ?? copy.prompt }), _jsx("input", { ref: ref, id: inputId, type: "file", className: "rl-file-drop__input", name: name, accept: accept, multiple: multiple, disabled: disabled, "aria-labelledby": `${labelId} ${inputId}-browse`, "aria-describedby": describedBy, "aria-invalid": invalid || undefined, onChange: (event) => {
116
+ handleFiles(event.currentTarget.files);
117
+ // Allow picking the same file again.
118
+ event.currentTarget.value = '';
119
+ } }), _jsx("label", { id: `${inputId}-browse`, htmlFor: inputId, className: "rl-file-drop__browse", children: browseLabel ?? copy.browse })] }), invalid && error != null && (_jsx("p", { id: errorId, className: "rl-file-drop__error", children: error })), _jsx("p", { className: "rl-file-drop__status", role: "status", "aria-live": "polite", children: announcement })] }));
120
+ });
121
+ //# sourceMappingURL=FileDropZone.js.map