@dreamtree-org/twreact-ui 1.3.11 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/README.md +40 -6
  2. package/ai-skills/dreamtree-ui.md +47 -9
  3. package/codemods/v2.cjs +198 -0
  4. package/dist/ai-schema.json +609 -33
  5. package/dist/components/blocks/AppShell.d.ts.map +1 -1
  6. package/dist/components/blocks/BottomActionBar.d.ts +6 -0
  7. package/dist/components/blocks/BottomActionBar.d.ts.map +1 -0
  8. package/dist/components/blocks/ConfirmDialog.d.ts.map +1 -1
  9. package/dist/components/blocks/PageHeader.d.ts.map +1 -1
  10. package/dist/components/core/Checkbox.d.ts.map +1 -1
  11. package/dist/components/core/DatePicker.d.ts.map +1 -1
  12. package/dist/components/core/DateRangePicker.d.ts.map +1 -1
  13. package/dist/components/core/Form.d.ts.map +1 -1
  14. package/dist/components/core/Input.d.ts.map +1 -1
  15. package/dist/components/core/Popover.d.ts.map +1 -1
  16. package/dist/components/core/ProgressBar.d.ts.map +1 -1
  17. package/dist/components/core/Rate.d.ts.map +1 -1
  18. package/dist/components/core/SearchField.d.ts +9 -0
  19. package/dist/components/core/SearchField.d.ts.map +1 -0
  20. package/dist/components/core/SegmentedControl.d.ts +8 -0
  21. package/dist/components/core/SegmentedControl.d.ts.map +1 -0
  22. package/dist/components/core/Select.d.ts.map +1 -1
  23. package/dist/components/core/SwipeableListItem.d.ts +4 -0
  24. package/dist/components/core/SwipeableListItem.d.ts.map +1 -0
  25. package/dist/components/core/SwipeableListItemGesture.d.ts +58 -0
  26. package/dist/components/core/SwipeableListItemGesture.d.ts.map +1 -0
  27. package/dist/components/core/Switch.d.ts.map +1 -1
  28. package/dist/components/core/Table/ActionMenu.d.ts +10 -3
  29. package/dist/components/core/Table/ActionMenu.d.ts.map +1 -1
  30. package/dist/components/core/Table/MobileCard.d.ts +10 -1
  31. package/dist/components/core/Table/MobileCard.d.ts.map +1 -1
  32. package/dist/components/core/Table/MobileFilters.d.ts +8 -2
  33. package/dist/components/core/Table/MobileFilters.d.ts.map +1 -1
  34. package/dist/components/core/Table/Table.d.ts.map +1 -1
  35. package/dist/components/core/Table/TableRow.d.ts +2 -1
  36. package/dist/components/core/Table/TableRow.d.ts.map +1 -1
  37. package/dist/components/core/Tabs.d.ts +3 -3
  38. package/dist/components/core/Tabs.d.ts.map +1 -1
  39. package/dist/components/core/ThreeDotPopover.d.ts +10 -22
  40. package/dist/components/core/ThreeDotPopover.d.ts.map +1 -1
  41. package/dist/components/core/Tooltip.d.ts +2 -1
  42. package/dist/components/core/Tooltip.d.ts.map +1 -1
  43. package/dist/components/core/internal/Calendar.d.ts +77 -0
  44. package/dist/components/core/internal/Calendar.d.ts.map +1 -0
  45. package/dist/components/core/menu/ActionMenuSurface.d.ts +42 -0
  46. package/dist/components/core/menu/ActionMenuSurface.d.ts.map +1 -0
  47. package/dist/components/core/menu/ActionSheetMenu.d.ts +17 -0
  48. package/dist/components/core/menu/ActionSheetMenu.d.ts.map +1 -0
  49. package/dist/components/core/menu/MenuPopover.d.ts +21 -0
  50. package/dist/components/core/menu/MenuPopover.d.ts.map +1 -0
  51. package/dist/components/core/menu/menuItems.d.ts +21 -0
  52. package/dist/components/core/menu/menuItems.d.ts.map +1 -0
  53. package/dist/components/core/menu/menuKeyboard.d.ts +16 -0
  54. package/dist/components/core/menu/menuKeyboard.d.ts.map +1 -0
  55. package/dist/components/feedback/ActionSheet.d.ts +4 -0
  56. package/dist/components/feedback/ActionSheet.d.ts.map +1 -0
  57. package/dist/components/feedback/Alert.d.ts.map +1 -1
  58. package/dist/components/feedback/Dialog.d.ts.map +1 -1
  59. package/dist/components/feedback/Drawer.d.ts.map +1 -1
  60. package/dist/components/feedback/Sheet.d.ts +4 -0
  61. package/dist/components/feedback/Sheet.d.ts.map +1 -0
  62. package/dist/components/feedback/Toast.d.ts +4 -3
  63. package/dist/components/feedback/Toast.d.ts.map +1 -1
  64. package/dist/components/feedback/ToastItem.d.ts +47 -0
  65. package/dist/components/feedback/ToastItem.d.ts.map +1 -0
  66. package/dist/components/feedback/ToastStack.d.ts +20 -0
  67. package/dist/components/feedback/ToastStack.d.ts.map +1 -0
  68. package/dist/components/feedback/ToastSwipe.d.ts +22 -0
  69. package/dist/components/feedback/ToastSwipe.d.ts.map +1 -0
  70. package/dist/components/feedback/Tour.d.ts.map +1 -1
  71. package/dist/components/feedback/sheetCore.d.ts +30 -0
  72. package/dist/components/feedback/sheetCore.d.ts.map +1 -0
  73. package/dist/components/navigation/Navbar.d.ts.map +1 -1
  74. package/dist/components/navigation/Sidebar.d.ts +6 -0
  75. package/dist/components/navigation/Sidebar.d.ts.map +1 -1
  76. package/dist/components/rich-text/RichTextEditor.d.ts.map +1 -1
  77. package/dist/components/rich-text/RichTextToolbar.d.ts +6 -2
  78. package/dist/components/rich-text/RichTextToolbar.d.ts.map +1 -1
  79. package/dist/components/utility/PullToRefresh.d.ts +5 -0
  80. package/dist/components/utility/PullToRefresh.d.ts.map +1 -0
  81. package/dist/esm/components/blocks/AppShell.js +97 -90
  82. package/dist/esm/components/blocks/BottomActionBar.js +132 -0
  83. package/dist/esm/components/blocks/ConfirmDialog.js +40 -4
  84. package/dist/esm/components/blocks/PageHeader.js +16 -3
  85. package/dist/esm/components/core/Checkbox.js +13 -24
  86. package/dist/esm/components/core/DatePicker.js +117 -304
  87. package/dist/esm/components/core/DateRangePicker.js +260 -308
  88. package/dist/esm/components/core/Form.js +11 -2
  89. package/dist/esm/components/core/Input.js +13 -18
  90. package/dist/esm/components/core/Popover.js +59 -48
  91. package/dist/esm/components/core/ProgressBar.js +36 -64
  92. package/dist/esm/components/core/Rate.js +5 -10
  93. package/dist/esm/components/core/SearchField.js +311 -0
  94. package/dist/esm/components/core/SegmentedControl.js +189 -0
  95. package/dist/esm/components/core/Select.js +185 -190
  96. package/dist/esm/components/core/SwipeableListItem.js +286 -0
  97. package/dist/esm/components/core/SwipeableListItemGesture.js +219 -0
  98. package/dist/esm/components/core/Switch.js +11 -13
  99. package/dist/esm/components/core/Table/ActionMenu.js +45 -231
  100. package/dist/esm/components/core/Table/ColumnMenu.js +1 -1
  101. package/dist/esm/components/core/Table/MobileCard.js +66 -16
  102. package/dist/esm/components/core/Table/MobileFilters.js +181 -10
  103. package/dist/esm/components/core/Table/Table.js +149 -102
  104. package/dist/esm/components/core/Table/TableRow.js +10 -2
  105. package/dist/esm/components/core/Tabs.js +7 -7
  106. package/dist/esm/components/core/ThreeDotPopover.js +93 -99
  107. package/dist/esm/components/core/Tooltip.js +138 -145
  108. package/dist/esm/components/core/internal/Calendar.js +381 -0
  109. package/dist/esm/components/core/menu/ActionMenuSurface.js +110 -0
  110. package/dist/esm/components/core/menu/ActionSheetMenu.js +140 -0
  111. package/dist/esm/components/core/menu/MenuPopover.js +177 -0
  112. package/dist/esm/components/core/menu/menuItems.js +61 -0
  113. package/dist/esm/components/core/menu/menuKeyboard.js +54 -0
  114. package/dist/esm/components/feedback/ActionSheet.js +55 -0
  115. package/dist/esm/components/feedback/Alert.js +63 -53
  116. package/dist/esm/components/feedback/Dialog.js +141 -53
  117. package/dist/esm/components/feedback/Drawer.js +130 -132
  118. package/dist/esm/components/feedback/Sheet.js +125 -0
  119. package/dist/esm/components/feedback/Toast.js +21 -291
  120. package/dist/esm/components/feedback/ToastItem.js +348 -0
  121. package/dist/esm/components/feedback/ToastStack.js +130 -0
  122. package/dist/esm/components/feedback/ToastSwipe.js +52 -0
  123. package/dist/esm/components/feedback/Tour.js +107 -30
  124. package/dist/esm/components/feedback/sheetCore.js +494 -0
  125. package/dist/esm/components/navigation/Dropdown.js +1 -1
  126. package/dist/esm/components/navigation/Navbar.js +172 -37
  127. package/dist/esm/components/navigation/Sidebar.js +263 -111
  128. package/dist/esm/components/rich-text/RichTextEditor.js +9 -3
  129. package/dist/esm/components/rich-text/RichTextToolbar.js +29 -9
  130. package/dist/esm/components/utility/Portal.js +31 -0
  131. package/dist/esm/components/utility/PullToRefresh.js +229 -0
  132. package/dist/esm/hooks/useFloatingPosition.js +82 -0
  133. package/dist/esm/hooks/useHaptics.js +65 -0
  134. package/dist/esm/hooks/usePresence.js +58 -0
  135. package/dist/esm/hooks/usePresentation.js +21 -0
  136. package/dist/esm/hooks/useSheetDrag.js +220 -0
  137. package/dist/esm/index.js +13 -2
  138. package/dist/esm/utils/position.js +10 -63
  139. package/dist/hooks/useFloatingPosition.d.ts +8 -4
  140. package/dist/hooks/useFloatingPosition.d.ts.map +1 -1
  141. package/dist/hooks/useHaptics.d.ts +19 -0
  142. package/dist/hooks/useHaptics.d.ts.map +1 -0
  143. package/dist/hooks/useSheetDrag.d.ts +31 -0
  144. package/dist/hooks/useSheetDrag.d.ts.map +1 -0
  145. package/dist/index.css +1 -1
  146. package/dist/index.d.ts +9 -1
  147. package/dist/index.esm.css +1 -1
  148. package/dist/index.esm.js +8851 -3198
  149. package/dist/index.js +8856 -3195
  150. package/dist/rich-text/index.js +41 -11
  151. package/dist/types/blocks.d.ts +32 -2
  152. package/dist/types/blocks.d.ts.map +1 -1
  153. package/dist/types/index.d.ts +353 -19
  154. package/dist/types/index.d.ts.map +1 -1
  155. package/dist/types/rich-text.d.ts +5 -0
  156. package/dist/types/rich-text.d.ts.map +1 -1
  157. package/dist/utils/position.d.ts +0 -15
  158. package/dist/utils/position.d.ts.map +1 -1
  159. package/doc/README.md +15 -0
  160. package/mcp/catalog.snapshot.json +1398 -464
  161. package/package.json +7 -2
  162. package/dist/esm/hooks/useSwipeToClose.js +0 -66
  163. package/dist/hooks/useSwipeToClose.d.ts +0 -24
  164. package/dist/hooks/useSwipeToClose.d.ts.map +0 -1
@@ -0,0 +1,177 @@
1
+ 'use client';
2
+ import { slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2 } from '../../../_virtual/_rollupPluginBabelHelpers.js';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { useRef, useState, useLayoutEffect, useCallback, useMemo, useEffect } from 'react';
5
+ import { cn } from '../../../utils/cn.js';
6
+ import { setRefs } from '../../../utils/position.js';
7
+ import Portal from '../../utility/Portal.js';
8
+ import { useFloatingPosition } from '../../../hooks/useFloatingPosition.js';
9
+ import { useFocusTrap } from '../../../hooks/useFocusTrap.js';
10
+ import { useClickOutside } from '../../../hooks/useClickOutside.js';
11
+ import { useLatestRef } from '../../../hooks/useLatestRef.js';
12
+ import { getMenuItems, handleMenuKeyDown } from './menuKeyboard.js';
13
+ import { isDestructive } from './menuItems.js';
14
+
15
+ // Internal (not exported). Desktop presentation of the action menu: a
16
+ // portaled, floating `role="menu"` anchored to its trigger (flip + shift via
17
+ // useFloatingPosition), with roving focus and Escape/outside-press dismissal.
18
+ var MENU_CLASSES = "z-popover min-w-[10rem] max-h-80 overflow-y-auto rounded-control border border-line bg-surface p-1 text-foreground shadow-overlay focus:outline-none transition-opacity duration-fast motion-reduce:transition-none";
19
+ var ITEM_CLASSES = "flex w-full items-center gap-3 rounded-md px-3 py-2 text-left text-sm outline-none transition-colors focus:bg-line/50 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary-500";
20
+ function itemToneClass(item) {
21
+ if (item.disabled) return "cursor-not-allowed opacity-50 text-muted";
22
+ if (isDestructive(item)) return "text-error-600 dark:text-error-400 hover:bg-error-50 dark:hover:bg-error-900/30";
23
+ return "text-foreground hover:bg-line/50";
24
+ }
25
+ /**
26
+ * @param {object} props
27
+ * @param {HTMLElement|null} props.anchorEl - the trigger the menu floats next to.
28
+ * @param {Array} props.items - normalized items (see menuItems.js).
29
+ * @param {(item: object) => void} props.onSelectItem
30
+ * @param {(reason: string) => void} props.onRequestClose - 'escape' | 'outside' | 'tab' | 'scroll'.
31
+ * @param {string} [props.placement='bottom-start']
32
+ * @param {'first'|'last'} [props.initialFocus='first']
33
+ * @param {boolean} [props.closeOnScroll=false] - close when a scroll happens outside the menu.
34
+ */
35
+ function MenuPopover(_ref) {
36
+ var anchorEl = _ref.anchorEl,
37
+ items = _ref.items,
38
+ onSelectItem = _ref.onSelectItem,
39
+ onRequestClose = _ref.onRequestClose,
40
+ _ref$placement = _ref.placement,
41
+ placement = _ref$placement === void 0 ? "bottom-start" : _ref$placement,
42
+ _ref$initialFocus = _ref.initialFocus,
43
+ initialFocus = _ref$initialFocus === void 0 ? "first" : _ref$initialFocus,
44
+ _ref$closeOnScroll = _ref.closeOnScroll,
45
+ closeOnScroll = _ref$closeOnScroll === void 0 ? false : _ref$closeOnScroll,
46
+ menuRef = _ref.menuRef,
47
+ id = _ref.id,
48
+ label = _ref.label,
49
+ className = _ref.className,
50
+ itemClassName = _ref.itemClassName,
51
+ _ref$emptyLabel = _ref.emptyLabel,
52
+ emptyLabel = _ref$emptyLabel === void 0 ? "No actions" : _ref$emptyLabel;
53
+ var _useFloatingPosition = useFloatingPosition({
54
+ placement: placement,
55
+ open: true
56
+ }),
57
+ refs = _useFloatingPosition.refs,
58
+ floatingStyles = _useFloatingPosition.floatingStyles,
59
+ isPositioned = _useFloatingPosition.isPositioned;
60
+ var elRef = useRef(null);
61
+ // The menu node once the Portal has rendered it; focus management waits.
62
+ var _useState = useState(null),
63
+ _useState2 = _slicedToArray(_useState, 2),
64
+ menuEl = _useState2[0],
65
+ setMenuEl = _useState2[1];
66
+ var closeRef = useLatestRef(onRequestClose);
67
+ useLayoutEffect(function () {
68
+ refs.setReference(anchorEl !== null && anchorEl !== void 0 ? anchorEl : null);
69
+ }, [refs, anchorEl]);
70
+ var setMenuNode = useCallback(function (node) {
71
+ return setRefs(node, elRef, refs.setFloating, menuRef, setMenuEl);
72
+ }, [refs, menuRef]);
73
+ var focusTarget = useMemo(function () {
74
+ return {
75
+ get current() {
76
+ var _ref2;
77
+ var list = getMenuItems(menuEl);
78
+ return (_ref2 = initialFocus === "last" ? list[list.length - 1] : list[0]) !== null && _ref2 !== void 0 ? _ref2 : menuEl;
79
+ }
80
+ };
81
+ }, [menuEl, initialFocus]);
82
+ useFocusTrap(elRef, {
83
+ active: menuEl != null,
84
+ trap: false,
85
+ initialFocusRef: focusTarget,
86
+ onEscape: function onEscape(e) {
87
+ var _e$preventDefault, _closeRef$current;
88
+ e === null || e === void 0 || (_e$preventDefault = e.preventDefault) === null || _e$preventDefault === void 0 || _e$preventDefault.call(e);
89
+ (_closeRef$current = closeRef.current) === null || _closeRef$current === void 0 || _closeRef$current.call(closeRef, "escape");
90
+ }
91
+ });
92
+ var anchorRef = useMemo(function () {
93
+ return {
94
+ current: anchorEl
95
+ };
96
+ }, [anchorEl]);
97
+ useClickOutside([elRef, anchorRef], function () {
98
+ var _closeRef$current2;
99
+ return (_closeRef$current2 = closeRef.current) === null || _closeRef$current2 === void 0 ? void 0 : _closeRef$current2.call(closeRef, "outside");
100
+ });
101
+ useEffect(function () {
102
+ if (!closeOnScroll) return undefined;
103
+ var onScroll = function onScroll(e) {
104
+ var _closeRef$current3;
105
+ var node = elRef.current;
106
+ if (node && e.target instanceof Node && node.contains(e.target)) return;
107
+ (_closeRef$current3 = closeRef.current) === null || _closeRef$current3 === void 0 || _closeRef$current3.call(closeRef, "scroll");
108
+ };
109
+ window.addEventListener("scroll", onScroll, true);
110
+ return function () {
111
+ return window.removeEventListener("scroll", onScroll, true);
112
+ };
113
+ }, [closeOnScroll, closeRef]);
114
+ var hasItems = items.length > 0;
115
+ return jsx(Portal, {
116
+ children: jsx("div", {
117
+ ref: setMenuNode,
118
+ id: id,
119
+ role: "menu",
120
+ "aria-orientation": "vertical",
121
+ "aria-label": label,
122
+ tabIndex: -1,
123
+ "data-presentation": "popover",
124
+ className: cn(MENU_CLASSES, className),
125
+ style: _objectSpread2(_objectSpread2({}, floatingStyles), {}, {
126
+ opacity: isPositioned ? 1 : 0
127
+ }),
128
+ onKeyDown: function onKeyDown(e) {
129
+ return handleMenuKeyDown(e, elRef.current, {
130
+ onTab: function onTab() {
131
+ var _closeRef$current4;
132
+ return (_closeRef$current4 = closeRef.current) === null || _closeRef$current4 === void 0 ? void 0 : _closeRef$current4.call(closeRef, "tab");
133
+ }
134
+ });
135
+ },
136
+ onClick: function onClick(e) {
137
+ return e.stopPropagation();
138
+ },
139
+ children: !hasItems ? jsx("div", {
140
+ className: "px-3 py-2 text-sm italic text-muted",
141
+ children: emptyLabel
142
+ }) : items.map(function (item, idx) {
143
+ var _item$content, _item$key;
144
+ return jsx("button", {
145
+ type: "button",
146
+ role: "menuitem",
147
+ tabIndex: -1,
148
+ disabled: item.disabled || undefined,
149
+ "aria-disabled": item.disabled || undefined,
150
+ "data-tone": isDestructive(item) ? "destructive" : undefined,
151
+ className: cn(ITEM_CLASSES, itemToneClass(item), itemClassName, !item.disabled && item.className),
152
+ onMouseEnter: function onMouseEnter(e) {
153
+ if (!item.disabled) e.currentTarget.focus({
154
+ preventScroll: true
155
+ });
156
+ },
157
+ onClick: function onClick(e) {
158
+ e.stopPropagation();
159
+ if (!item.disabled) onSelectItem(item);
160
+ },
161
+ children: (_item$content = item.content) !== null && _item$content !== void 0 ? _item$content : jsxs(Fragment, {
162
+ children: [item.icon != null && jsx("span", {
163
+ className: "inline-flex shrink-0",
164
+ "aria-hidden": "true",
165
+ children: item.icon
166
+ }), jsx("span", {
167
+ className: "min-w-0 flex-1 truncate",
168
+ children: item.label
169
+ })]
170
+ })
171
+ }, (_item$key = item.key) !== null && _item$key !== void 0 ? _item$key : "item-".concat(idx));
172
+ })
173
+ })
174
+ });
175
+ }
176
+
177
+ export { MenuPopover as default, itemToneClass };
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+ import { typeof as _typeof } from '../../../_virtual/_rollupPluginBabelHelpers.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import React from 'react';
5
+
6
+ // Internal (not exported). Shared helpers for the action-menu primitive used
7
+ // by ThreeDotPopover and Table's row ActionMenu (DTUI-36).
8
+ //
9
+ // A normalized menu item looks like:
10
+ // {
11
+ // key: string | number, // React key
12
+ // label: ReactNode, // visible text (also used for typeahead)
13
+ // icon?: ReactNode, // rendered before the label
14
+ // content?: ReactNode, // replaces icon + label entirely
15
+ // disabled?: boolean,
16
+ // tone?: 'default' | 'destructive',
17
+ // destructive?: boolean, // legacy alias for tone: 'destructive'
18
+ // className?: string, // popover-mode-only extra item classes
19
+ // onSelect?: () => void,
20
+ // }
21
+ /** True when an item should render in the error (destructive) tone. */
22
+ var isDestructive = function isDestructive(item) {
23
+ return Boolean(item) && (item.tone === "destructive" || item.destructive === true);
24
+ };
25
+ /**
26
+ * Renders an icon given either a component type (`Edit2`) or a ready node
27
+ * (`<Edit2 />`). Returns null for anything else.
28
+ */
29
+ function renderIcon(icon, className) {
30
+ if (icon == null || icon === false) return null;
31
+ if (/*#__PURE__*/React.isValidElement(icon)) return icon;
32
+ var isComponent = typeof icon === "function" || _typeof(icon) === "object" && icon.$$typeof != null;
33
+ if (isComponent) {
34
+ var Icon = icon;
35
+ return jsx(Icon, {
36
+ className: className,
37
+ "aria-hidden": "true"
38
+ });
39
+ }
40
+ return icon;
41
+ }
42
+ /**
43
+ * Normalizes a public ActionSheet action
44
+ * ({ key?, label, icon?, tone?, onSelect?, disabled? }) into a menu item.
45
+ * `icon` may be a node or a component type.
46
+ */
47
+ function normalizeAction(action, idx) {
48
+ var _a$key;
49
+ var a = action || {};
50
+ return {
51
+ key: (_a$key = a.key) !== null && _a$key !== void 0 ? _a$key : "action-".concat(idx),
52
+ label: a.label,
53
+ icon: renderIcon(a.icon, "h-5 w-5"),
54
+ disabled: Boolean(a.disabled),
55
+ tone: a.tone,
56
+ destructive: a.destructive,
57
+ onSelect: a.onSelect
58
+ };
59
+ }
60
+
61
+ export { isDestructive, normalizeAction, renderIcon };
@@ -0,0 +1,54 @@
1
+ // Internal (not exported). Roving focus for a `role="menu"` element: the
2
+ // menu's items all carry tabIndex=-1 and focus moves between them with the
3
+ // keyboard (WAI-ARIA menu pattern).
4
+ var ITEM_SELECTOR = '[role="menuitem"]:not([disabled]):not([aria-disabled="true"])';
5
+ /** The enabled menu items inside `menuEl`, in DOM order. */
6
+ var getMenuItems = function getMenuItems(menuEl) {
7
+ return menuEl ? Array.from(menuEl.querySelectorAll(ITEM_SELECTOR)) : [];
8
+ };
9
+ var focusItem = function focusItem(el) {
10
+ var _el$focus;
11
+ return el === null || el === void 0 || (_el$focus = el.focus) === null || _el$focus === void 0 ? void 0 : _el$focus.call(el, {
12
+ preventScroll: true
13
+ });
14
+ };
15
+ var isTypeaheadKey = function isTypeaheadKey(e) {
16
+ return e.key.length === 1 && /\S/.test(e.key) && !e.ctrlKey && !e.metaKey && !e.altKey;
17
+ };
18
+ /**
19
+ * Keydown handler for the menu element. ArrowDown/ArrowUp move (and wrap),
20
+ * Home/End jump, a printable character jumps to the next item starting with
21
+ * it, Tab calls `onTab`. Escape is handled by the caller's focus layer.
22
+ *
23
+ * @param {KeyboardEvent} e
24
+ * @param {HTMLElement} menuEl
25
+ * @param {{ onTab?: () => void }} [options]
26
+ */
27
+ function handleMenuKeyDown(e, menuEl) {
28
+ var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
29
+ onTab = _ref.onTab;
30
+ if (e.key === "Tab") {
31
+ onTab === null || onTab === void 0 || onTab();
32
+ return;
33
+ }
34
+ var items = getMenuItems(menuEl);
35
+ if (items.length === 0) return;
36
+ var current = items.indexOf(document.activeElement);
37
+ var last = items.length - 1;
38
+ var next;
39
+ if (e.key === "ArrowDown") next = current < 0 || current === last ? 0 : current + 1;else if (e.key === "ArrowUp") next = current <= 0 ? last : current - 1;else if (e.key === "Home") next = 0;else if (e.key === "End") next = last;else if (isTypeaheadKey(e)) {
40
+ var key = e.key.toLowerCase();
41
+ for (var step = 1; step <= items.length; step += 1) {
42
+ var i = (current + step + items.length) % items.length;
43
+ if ((items[i].textContent || "").trim().toLowerCase().startsWith(key)) {
44
+ next = i;
45
+ break;
46
+ }
47
+ }
48
+ }
49
+ if (next === undefined) return;
50
+ e.preventDefault();
51
+ focusItem(items[next]);
52
+ }
53
+
54
+ export { getMenuItems, handleMenuKeyDown };
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+ import { objectWithoutProperties as _objectWithoutProperties, objectSpread2 as _objectSpread2 } from '../../_virtual/_rollupPluginBabelHelpers.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+ import React from 'react';
5
+ import { cn } from '../../utils/cn.js';
6
+ import ActionSheetMenu from '../core/menu/ActionSheetMenu.js';
7
+ import { normalizeAction } from '../core/menu/menuItems.js';
8
+
9
+ var _excluded = ["open", "isOpen", "onClose", "title", "message", "actions", "cancelLabel", "className", "aria-label"];
10
+ // iOS UIAlertController-style action sheet on the bottom Sheet: an optional
11
+ // title + message, a card of actions (destructive ones in the error tone) and
12
+ // a separate Cancel. The same implementation (ActionSheetMenu) renders the
13
+ // phone presentation of ThreeDotPopover and Table's row menu.
14
+ //
15
+ // Selecting an action calls its `onSelect`, then `onClose('select')`. Focus
16
+ // returns to the element that was focused before the sheet opened (the
17
+ // Sheet's focus trap restores it).
18
+ var ActionSheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
19
+ var open = _ref.open,
20
+ isOpen = _ref.isOpen,
21
+ _onClose = _ref.onClose,
22
+ title = _ref.title,
23
+ message = _ref.message,
24
+ _ref$actions = _ref.actions,
25
+ actions = _ref$actions === void 0 ? [] : _ref$actions,
26
+ _ref$cancelLabel = _ref.cancelLabel,
27
+ cancelLabel = _ref$cancelLabel === void 0 ? "Cancel" : _ref$cancelLabel,
28
+ className = _ref.className,
29
+ ariaLabel = _ref["aria-label"],
30
+ rest = _objectWithoutProperties(_ref, _excluded);
31
+ var items = (Array.isArray(actions) ? actions : []).filter(Boolean).map(normalizeAction);
32
+ var selectItem = function selectItem(item) {
33
+ var _item$onSelect;
34
+ if (item.disabled) return;
35
+ (_item$onSelect = item.onSelect) === null || _item$onSelect === void 0 || _item$onSelect.call(item);
36
+ _onClose === null || _onClose === void 0 || _onClose("select");
37
+ };
38
+ return jsx(ActionSheetMenu, _objectSpread2({
39
+ ref: ref,
40
+ open: Boolean(open !== null && open !== void 0 ? open : isOpen),
41
+ onClose: function onClose(reason) {
42
+ return _onClose === null || _onClose === void 0 ? void 0 : _onClose(reason);
43
+ },
44
+ groups: [items],
45
+ onSelectItem: selectItem,
46
+ title: title,
47
+ message: message,
48
+ label: ariaLabel,
49
+ cancelLabel: cancelLabel,
50
+ className: cn(className)
51
+ }, rest));
52
+ });
53
+ ActionSheet.displayName = "ActionSheet";
54
+
55
+ export { ActionSheet as default };
@@ -6,92 +6,102 @@ import { cn } from '../../utils/cn.js';
6
6
  import { Info, AlertTriangle, AlertCircle, CheckCircle, X } from 'lucide-react';
7
7
  import { TOUCH_TARGET } from '../../utils/touchTarget.js';
8
8
 
9
- var _excluded = ["variant", "title", "children", "dismissible", "onDismiss", "className"];
9
+ var _excluded = ["variant", "title", "children", "actions", "dismissible", "onDismiss", "dismissLabel", "className"];
10
10
  // error / warning need attention now (role="alert", assertive); info / success
11
- // are neutral and must not interrupt (role="status", polite).
11
+ // / neutral must not interrupt (role="status", polite).
12
12
  var URGENT_VARIANTS = new Set(['error', 'warning']);
13
+ var VARIANTS = {
14
+ success: {
15
+ container: 'bg-success-50 border-success-200 dark:bg-success-950/40 dark:border-success-800',
16
+ Icon: CheckCircle,
17
+ icon: 'text-success-500 dark:text-success-400',
18
+ title: 'text-success-800 dark:text-success-200',
19
+ content: 'text-success-700 dark:text-success-300',
20
+ dismiss: 'text-success-500 hover:bg-success-100 focus-visible:ring-success-600 dark:text-success-400 dark:hover:bg-success-900/50'
21
+ },
22
+ error: {
23
+ container: 'bg-error-50 border-error-200 dark:bg-error-950/40 dark:border-error-800',
24
+ Icon: AlertCircle,
25
+ icon: 'text-error-500 dark:text-error-400',
26
+ title: 'text-error-800 dark:text-error-200',
27
+ content: 'text-error-700 dark:text-error-300',
28
+ dismiss: 'text-error-500 hover:bg-error-100 focus-visible:ring-error-600 dark:text-error-400 dark:hover:bg-error-900/50'
29
+ },
30
+ warning: {
31
+ container: 'bg-warning-50 border-warning-200 dark:bg-warning-950/40 dark:border-warning-800',
32
+ Icon: AlertTriangle,
33
+ icon: 'text-warning-500 dark:text-warning-400',
34
+ title: 'text-warning-800 dark:text-warning-200',
35
+ content: 'text-warning-700 dark:text-warning-300',
36
+ dismiss: 'text-warning-500 hover:bg-warning-100 focus-visible:ring-warning-600 dark:text-warning-400 dark:hover:bg-warning-900/50'
37
+ },
38
+ info: {
39
+ container: 'bg-primary-50 border-primary-200 dark:bg-primary-950/40 dark:border-primary-800',
40
+ Icon: Info,
41
+ icon: 'text-primary-500 dark:text-primary-400',
42
+ title: 'text-primary-800 dark:text-primary-200',
43
+ content: 'text-primary-700 dark:text-primary-300',
44
+ dismiss: 'text-primary-500 hover:bg-primary-100 focus-visible:ring-primary-600 dark:text-primary-400 dark:hover:bg-primary-900/50'
45
+ },
46
+ neutral: {
47
+ container: 'bg-surface border-line dark:bg-surface dark:border-line',
48
+ Icon: Info,
49
+ icon: 'text-muted dark:text-muted',
50
+ title: 'text-foreground dark:text-foreground',
51
+ content: 'text-muted dark:text-muted',
52
+ dismiss: 'text-muted hover:bg-line/50 focus-visible:ring-primary-500 dark:text-muted dark:hover:bg-line/50'
53
+ }
54
+ };
13
55
  var Alert = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
14
56
  var _ref$variant = _ref.variant,
15
57
  variant = _ref$variant === void 0 ? 'info' : _ref$variant,
16
58
  title = _ref.title,
17
59
  children = _ref.children,
60
+ actions = _ref.actions,
18
61
  _ref$dismissible = _ref.dismissible,
19
62
  dismissible = _ref$dismissible === void 0 ? false : _ref$dismissible,
20
63
  onDismiss = _ref.onDismiss,
64
+ _ref$dismissLabel = _ref.dismissLabel,
65
+ dismissLabel = _ref$dismissLabel === void 0 ? 'Dismiss' : _ref$dismissLabel,
21
66
  className = _ref.className,
22
67
  props = _objectWithoutProperties(_ref, _excluded);
23
- var variants = {
24
- success: {
25
- container: 'bg-success-50 border-success-200',
26
- icon: jsx(CheckCircle, {
27
- className: "h-5 w-5 text-success-500",
28
- "aria-hidden": "true"
29
- }),
30
- title: 'text-success-800',
31
- content: 'text-success-700'
32
- },
33
- error: {
34
- container: 'bg-error-50 border-error-200',
35
- icon: jsx(AlertCircle, {
36
- className: "h-5 w-5 text-error-500",
37
- "aria-hidden": "true"
38
- }),
39
- title: 'text-error-800',
40
- content: 'text-error-700'
41
- },
42
- warning: {
43
- container: 'bg-warning-50 border-warning-200',
44
- icon: jsx(AlertTriangle, {
45
- className: "h-5 w-5 text-warning-500",
46
- "aria-hidden": "true"
47
- }),
48
- title: 'text-warning-800',
49
- content: 'text-warning-700'
50
- },
51
- info: {
52
- container: 'bg-primary-50 border-primary-200',
53
- icon: jsx(Info, {
54
- className: "h-5 w-5 text-primary-500",
55
- "aria-hidden": "true"
56
- }),
57
- title: 'text-primary-800',
58
- content: 'text-primary-700'
59
- }
60
- };
61
- var variantStyles = variants[variant];
68
+ var styles = VARIANTS[variant] || VARIANTS.info;
69
+ var Icon = styles.Icon;
62
70
  return jsx("div", _objectSpread2(_objectSpread2({
63
71
  ref: ref,
64
- className: cn('rounded-md border p-4', variantStyles.container, className),
72
+ className: cn('rounded-md border p-4 animate-fade-in motion-reduce:animate-none', styles.container, className),
65
73
  role: URGENT_VARIANTS.has(variant) ? 'alert' : 'status'
66
74
  }, props), {}, {
67
75
  children: jsxs("div", {
68
76
  className: "flex",
69
77
  children: [jsx("div", {
70
78
  className: "flex-shrink-0",
71
- children: variantStyles.icon
79
+ children: jsx(Icon, {
80
+ className: cn('h-5 w-5', styles.icon),
81
+ "aria-hidden": "true"
82
+ })
72
83
  }), jsxs("div", {
73
- className: "ml-3 flex-1",
84
+ className: "ml-3 flex-1 min-w-0",
74
85
  children: [title && jsx("h3", {
75
- className: cn('text-sm font-medium', variantStyles.title),
86
+ className: cn('text-sm font-medium', styles.title),
76
87
  children: title
77
88
  }), jsx("div", {
78
- className: cn('text-sm', variantStyles.content, {
89
+ className: cn('text-sm', styles.content, {
79
90
  'mt-1': title
80
91
  }),
81
92
  children: children
93
+ }), actions != null && actions !== false && jsx("div", {
94
+ "data-slot": "actions",
95
+ className: "mt-3 flex flex-wrap items-center gap-2",
96
+ children: actions
82
97
  })]
83
98
  }), dismissible && jsx("div", {
84
99
  className: "ml-auto pl-3",
85
100
  children: jsx("button", {
86
101
  type: "button",
87
- className: cn('inline-flex items-center justify-center rounded-md p-1.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2', TOUCH_TARGET, {
88
- 'text-success-500 hover:bg-success-100 focus-visible:ring-success-600': variant === 'success',
89
- 'text-error-500 hover:bg-error-100 focus-visible:ring-error-600': variant === 'error',
90
- 'text-warning-500 hover:bg-warning-100 focus-visible:ring-warning-600': variant === 'warning',
91
- 'text-primary-500 hover:bg-primary-100 focus-visible:ring-primary-600': variant === 'info'
92
- }),
102
+ className: cn('inline-flex items-center justify-center rounded-md p-1.5 transition-colors motion-reduce:transition-none focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-background', TOUCH_TARGET, styles.dismiss),
93
103
  onClick: onDismiss,
94
- "aria-label": "Dismiss",
104
+ "aria-label": dismissLabel,
95
105
  children: jsx(X, {
96
106
  className: "h-4 w-4",
97
107
  "aria-hidden": "true"