panelui-native 0.25.0 → 0.30.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 (242) hide show
  1. package/README.md +18 -1
  2. package/lib/module/components/accordion/index.js +8 -8
  3. package/lib/module/components/accordion/index.js.map +1 -1
  4. package/lib/module/components/alert/index.js +2 -2
  5. package/lib/module/components/alert/index.js.map +1 -1
  6. package/lib/module/components/area-chart/index.js +182 -50
  7. package/lib/module/components/area-chart/index.js.map +1 -1
  8. package/lib/module/components/attachment/index.js +2 -1
  9. package/lib/module/components/attachment/index.js.map +1 -1
  10. package/lib/module/components/avatar/index.js +3 -3
  11. package/lib/module/components/avatar/index.js.map +1 -1
  12. package/lib/module/components/badge/index.js +4 -4
  13. package/lib/module/components/badge/index.js.map +1 -1
  14. package/lib/module/components/bar-chart/index.js +197 -56
  15. package/lib/module/components/bar-chart/index.js.map +1 -1
  16. package/lib/module/components/bottom-sheet/index.js +15 -10
  17. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  18. package/lib/module/components/breadcrumb/index.js +6 -6
  19. package/lib/module/components/breadcrumb/index.js.map +1 -1
  20. package/lib/module/components/button/index.js +11 -6
  21. package/lib/module/components/button/index.js.map +1 -1
  22. package/lib/module/components/calendar/index.js +792 -0
  23. package/lib/module/components/calendar/index.js.map +1 -0
  24. package/lib/module/components/carousel/index.js +680 -0
  25. package/lib/module/components/carousel/index.js.map +1 -0
  26. package/lib/module/components/chip/index.js +4 -4
  27. package/lib/module/components/chip/index.js.map +1 -1
  28. package/lib/module/components/code-block/highlight.js +363 -0
  29. package/lib/module/components/code-block/highlight.js.map +1 -0
  30. package/lib/module/components/code-block/index.js +313 -0
  31. package/lib/module/components/code-block/index.js.map +1 -0
  32. package/lib/module/components/date-picker/index.js +165 -0
  33. package/lib/module/components/date-picker/index.js.map +1 -0
  34. package/lib/module/components/dialog/index.js +7 -2
  35. package/lib/module/components/dialog/index.js.map +1 -1
  36. package/lib/module/components/direction/index.js +2 -1
  37. package/lib/module/components/direction/index.js.map +1 -1
  38. package/lib/module/components/empty-state/index.js +2 -2
  39. package/lib/module/components/empty-state/index.js.map +1 -1
  40. package/lib/module/components/field/index.js +5 -5
  41. package/lib/module/components/field/index.js.map +1 -1
  42. package/lib/module/components/flow/index.js +3 -3
  43. package/lib/module/components/flow/index.js.map +1 -1
  44. package/lib/module/components/frame/index.js +5 -5
  45. package/lib/module/components/frame/index.js.map +1 -1
  46. package/lib/module/components/input-group/index.js +3 -2
  47. package/lib/module/components/input-group/index.js.map +1 -1
  48. package/lib/module/components/item/index.js +9 -9
  49. package/lib/module/components/item/index.js.map +1 -1
  50. package/lib/module/components/label/index.js +4 -4
  51. package/lib/module/components/label/index.js.map +1 -1
  52. package/lib/module/components/line-chart/index.js +167 -44
  53. package/lib/module/components/line-chart/index.js.map +1 -1
  54. package/lib/module/components/map/index.js +7 -7
  55. package/lib/module/components/map/index.js.map +1 -1
  56. package/lib/module/components/marker/index.js +3 -3
  57. package/lib/module/components/marker/index.js.map +1 -1
  58. package/lib/module/components/menu/index.js +714 -0
  59. package/lib/module/components/menu/index.js.map +1 -0
  60. package/lib/module/components/message/index.js +7 -7
  61. package/lib/module/components/message/index.js.map +1 -1
  62. package/lib/module/components/message-scroller/index.js +5 -4
  63. package/lib/module/components/message-scroller/index.js.map +1 -1
  64. package/lib/module/components/plan/index.js +296 -0
  65. package/lib/module/components/plan/index.js.map +1 -0
  66. package/lib/module/components/popover/index.js +73 -10
  67. package/lib/module/components/popover/index.js.map +1 -1
  68. package/lib/module/components/radio-group/index.js +15 -10
  69. package/lib/module/components/radio-group/index.js.map +1 -1
  70. package/lib/module/components/reasoning/index.js +244 -0
  71. package/lib/module/components/reasoning/index.js.map +1 -0
  72. package/lib/module/components/section-rail/index.js +4 -4
  73. package/lib/module/components/section-rail/index.js.map +1 -1
  74. package/lib/module/components/select/index.js +10 -4
  75. package/lib/module/components/select/index.js.map +1 -1
  76. package/lib/module/components/sources/index.js +221 -0
  77. package/lib/module/components/sources/index.js.map +1 -0
  78. package/lib/module/components/steps/index.js +4 -4
  79. package/lib/module/components/steps/index.js.map +1 -1
  80. package/lib/module/components/tabs/index.js +7 -7
  81. package/lib/module/components/tabs/index.js.map +1 -1
  82. package/lib/module/components/task/index.js +300 -0
  83. package/lib/module/components/task/index.js.map +1 -0
  84. package/lib/module/components/textarea/index.js +295 -0
  85. package/lib/module/components/textarea/index.js.map +1 -0
  86. package/lib/module/components/timeline/index.js +4 -4
  87. package/lib/module/components/timeline/index.js.map +1 -1
  88. package/lib/module/components/toast/index.js +2 -2
  89. package/lib/module/components/toast/index.js.map +1 -1
  90. package/lib/module/components/toggle-button/index.js +5 -5
  91. package/lib/module/components/toggle-button/index.js.map +1 -1
  92. package/lib/module/components/tooltip/index.js +648 -0
  93. package/lib/module/components/tooltip/index.js.map +1 -0
  94. package/lib/module/components/typography/index.js +2 -2
  95. package/lib/module/components/typography/index.js.map +1 -1
  96. package/lib/module/hooks/index.js +1 -0
  97. package/lib/module/hooks/index.js.map +1 -1
  98. package/lib/module/hooks/use-back-handler.js +37 -0
  99. package/lib/module/hooks/use-back-handler.js.map +1 -0
  100. package/lib/module/icons/index.js +202 -0
  101. package/lib/module/icons/index.js.map +1 -1
  102. package/lib/module/index.js +13 -1
  103. package/lib/module/index.js.map +1 -1
  104. package/lib/module/primitives/collapse.js +112 -0
  105. package/lib/module/primitives/collapse.js.map +1 -0
  106. package/lib/module/primitives/keyboard-avoider.js +2 -1
  107. package/lib/module/primitives/keyboard-avoider.js.map +1 -1
  108. package/lib/module/primitives/text.js +25 -1
  109. package/lib/module/primitives/text.js.map +1 -1
  110. package/lib/module/utils/date.js +386 -0
  111. package/lib/module/utils/date.js.map +1 -0
  112. package/lib/typescript/src/components/accordion/index.d.ts.map +1 -1
  113. package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
  114. package/lib/typescript/src/components/area-chart/index.d.ts +41 -1
  115. package/lib/typescript/src/components/area-chart/index.d.ts.map +1 -1
  116. package/lib/typescript/src/components/attachment/index.d.ts.map +1 -1
  117. package/lib/typescript/src/components/bar-chart/index.d.ts +44 -2
  118. package/lib/typescript/src/components/bar-chart/index.d.ts.map +1 -1
  119. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  120. package/lib/typescript/src/components/breadcrumb/index.d.ts.map +1 -1
  121. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  122. package/lib/typescript/src/components/calendar/index.d.ts +173 -0
  123. package/lib/typescript/src/components/calendar/index.d.ts.map +1 -0
  124. package/lib/typescript/src/components/carousel/index.d.ts +179 -0
  125. package/lib/typescript/src/components/carousel/index.d.ts.map +1 -0
  126. package/lib/typescript/src/components/chip/index.d.ts.map +1 -1
  127. package/lib/typescript/src/components/code-block/highlight.d.ts +39 -0
  128. package/lib/typescript/src/components/code-block/highlight.d.ts.map +1 -0
  129. package/lib/typescript/src/components/code-block/index.d.ts +113 -0
  130. package/lib/typescript/src/components/code-block/index.d.ts.map +1 -0
  131. package/lib/typescript/src/components/date-picker/index.d.ts +97 -0
  132. package/lib/typescript/src/components/date-picker/index.d.ts.map +1 -0
  133. package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
  134. package/lib/typescript/src/components/direction/index.d.ts.map +1 -1
  135. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
  136. package/lib/typescript/src/components/field/index.d.ts.map +1 -1
  137. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  138. package/lib/typescript/src/components/input-group/index.d.ts.map +1 -1
  139. package/lib/typescript/src/components/item/index.d.ts.map +1 -1
  140. package/lib/typescript/src/components/label/index.d.ts.map +1 -1
  141. package/lib/typescript/src/components/line-chart/index.d.ts +41 -2
  142. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -1
  143. package/lib/typescript/src/components/map/index.d.ts.map +1 -1
  144. package/lib/typescript/src/components/marker/index.d.ts.map +1 -1
  145. package/lib/typescript/src/components/menu/index.d.ts +261 -0
  146. package/lib/typescript/src/components/menu/index.d.ts.map +1 -0
  147. package/lib/typescript/src/components/message/index.d.ts.map +1 -1
  148. package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -1
  149. package/lib/typescript/src/components/plan/index.d.ts +127 -0
  150. package/lib/typescript/src/components/plan/index.d.ts.map +1 -0
  151. package/lib/typescript/src/components/popover/index.d.ts +35 -1
  152. package/lib/typescript/src/components/popover/index.d.ts.map +1 -1
  153. package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
  154. package/lib/typescript/src/components/reasoning/index.d.ts +97 -0
  155. package/lib/typescript/src/components/reasoning/index.d.ts.map +1 -0
  156. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  157. package/lib/typescript/src/components/sources/index.d.ts +85 -0
  158. package/lib/typescript/src/components/sources/index.d.ts.map +1 -0
  159. package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
  160. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  161. package/lib/typescript/src/components/task/index.d.ts +103 -0
  162. package/lib/typescript/src/components/task/index.d.ts.map +1 -0
  163. package/lib/typescript/src/components/textarea/index.d.ts +175 -0
  164. package/lib/typescript/src/components/textarea/index.d.ts.map +1 -0
  165. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
  166. package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
  167. package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -1
  168. package/lib/typescript/src/components/tooltip/index.d.ts +180 -0
  169. package/lib/typescript/src/components/tooltip/index.d.ts.map +1 -0
  170. package/lib/typescript/src/components/typography/index.d.ts.map +1 -1
  171. package/lib/typescript/src/hooks/index.d.ts +1 -0
  172. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  173. package/lib/typescript/src/hooks/use-back-handler.d.ts +19 -0
  174. package/lib/typescript/src/hooks/use-back-handler.d.ts.map +1 -0
  175. package/lib/typescript/src/icons/index.d.ts +17 -0
  176. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  177. package/lib/typescript/src/index.d.ts +16 -4
  178. package/lib/typescript/src/index.d.ts.map +1 -1
  179. package/lib/typescript/src/primitives/collapse.d.ts +42 -0
  180. package/lib/typescript/src/primitives/collapse.d.ts.map +1 -0
  181. package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
  182. package/lib/typescript/src/primitives/text.d.ts +20 -0
  183. package/lib/typescript/src/primitives/text.d.ts.map +1 -1
  184. package/lib/typescript/src/utils/date.d.ts +116 -0
  185. package/lib/typescript/src/utils/date.d.ts.map +1 -0
  186. package/package.json +1 -1
  187. package/src/components/accordion/index.tsx +8 -12
  188. package/src/components/alert/index.tsx +2 -2
  189. package/src/components/area-chart/index.tsx +208 -55
  190. package/src/components/attachment/index.tsx +2 -2
  191. package/src/components/avatar/index.tsx +3 -3
  192. package/src/components/badge/index.tsx +6 -6
  193. package/src/components/bar-chart/index.tsx +214 -54
  194. package/src/components/bottom-sheet/index.tsx +17 -16
  195. package/src/components/breadcrumb/index.tsx +6 -8
  196. package/src/components/button/index.tsx +12 -8
  197. package/src/components/calendar/index.tsx +970 -0
  198. package/src/components/carousel/index.tsx +788 -0
  199. package/src/components/chip/index.tsx +4 -6
  200. package/src/components/code-block/highlight.ts +278 -0
  201. package/src/components/code-block/index.tsx +341 -0
  202. package/src/components/date-picker/index.tsx +255 -0
  203. package/src/components/dialog/index.tsx +7 -2
  204. package/src/components/direction/index.tsx +2 -1
  205. package/src/components/empty-state/index.tsx +2 -2
  206. package/src/components/field/index.tsx +3 -5
  207. package/src/components/flow/index.tsx +3 -3
  208. package/src/components/frame/index.tsx +5 -7
  209. package/src/components/input-group/index.tsx +3 -2
  210. package/src/components/item/index.tsx +9 -9
  211. package/src/components/label/index.tsx +2 -2
  212. package/src/components/line-chart/index.tsx +189 -50
  213. package/src/components/map/index.tsx +7 -9
  214. package/src/components/marker/index.tsx +3 -3
  215. package/src/components/menu/index.tsx +812 -0
  216. package/src/components/message/index.tsx +7 -7
  217. package/src/components/message-scroller/index.tsx +5 -4
  218. package/src/components/plan/index.tsx +325 -0
  219. package/src/components/popover/index.tsx +111 -8
  220. package/src/components/radio-group/index.tsx +13 -4
  221. package/src/components/reasoning/index.tsx +297 -0
  222. package/src/components/section-rail/index.tsx +4 -4
  223. package/src/components/select/index.tsx +10 -4
  224. package/src/components/sources/index.tsx +278 -0
  225. package/src/components/steps/index.tsx +4 -4
  226. package/src/components/tabs/index.tsx +7 -9
  227. package/src/components/task/index.tsx +329 -0
  228. package/src/components/textarea/index.tsx +381 -0
  229. package/src/components/timeline/index.tsx +4 -4
  230. package/src/components/toast/index.tsx +2 -2
  231. package/src/components/toggle-button/index.tsx +5 -7
  232. package/src/components/tooltip/index.tsx +793 -0
  233. package/src/components/typography/index.tsx +2 -2
  234. package/src/hooks/index.ts +1 -0
  235. package/src/hooks/use-back-handler.ts +37 -0
  236. package/src/icons/index.tsx +147 -0
  237. package/src/index.ts +116 -0
  238. package/src/primitives/collapse.tsx +128 -0
  239. package/src/primitives/keyboard-avoider.tsx +2 -1
  240. package/src/primitives/text.tsx +28 -1
  241. package/src/utils/date.ts +446 -0
  242. package/theme.css +112 -0
@@ -0,0 +1,714 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Menu — the list of things you can do to something.
5
+ *
6
+ * It is not a Select, and the difference is what the rows *are*. A select's
7
+ * rows are values: picking one answers a question the form asked, and the
8
+ * trigger then shows the answer. A menu's rows are verbs — rename, duplicate,
9
+ * delete — and the trigger goes on saying the same thing afterwards, because
10
+ * nothing about it was chosen. Anything built out of a popover and a column of
11
+ * pressables ends up re-deriving that distinction by hand, and re-deriving the
12
+ * roles, the dismiss-on-select rule and the destructive colour with it.
13
+ *
14
+ * ```tsx
15
+ * <Menu>
16
+ * <Menu.Trigger>
17
+ * <Button variant="outline">Options</Button>
18
+ * </Menu.Trigger>
19
+ * <Menu.Content align="start">
20
+ * <Menu.Item icon={<PencilIcon size={16} />}>Rename</Menu.Item>
21
+ * <Menu.Separator />
22
+ * <Menu.Item variant="destructive" icon={<TrashIcon size={16} />}>
23
+ * Delete
24
+ * </Menu.Item>
25
+ * </Menu.Content>
26
+ * </Menu>
27
+ * ```
28
+ *
29
+ * The panel is a `Popover` underneath, so the menu inherits its measuring,
30
+ * flipping and edge-clamping rather than owning a second copy of them — a menu
31
+ * near the bottom of the screen opens upwards for the same reason a popover
32
+ * does, and `presentation="bottom-sheet"` moves the same rows into a sheet.
33
+ *
34
+ * Submenus expand in place rather than flying out sideways. A flyout needs a
35
+ * pointer to travel from the parent row to the child panel without crossing
36
+ * anything that would close it; a finger has no such path, and a second panel
37
+ * hanging off the first is usually the thing that pushes a menu off the edge
38
+ * of a phone. Opening downwards into the panel keeps every row under the
39
+ * thumb that opened it.
40
+ */
41
+ import { Children, createContext, isValidElement, useCallback, useContext, useEffect, useMemo, useState } from 'react';
42
+ import { Pressable, View } from 'react-native';
43
+ import Animated, { useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from 'react-native-reanimated';
44
+ import { tv } from 'tailwind-variants';
45
+ import { useCSSVariable } from 'uniwind';
46
+ import { CheckIcon, ChevronRightIcon } from "../../icons/index.js";
47
+ import { Text, textChildren } from "../../primitives/text.js";
48
+ import { useDirectionSign } from "../../hooks/use-direction.js";
49
+ import { selectionTick } from "../../utils/haptics.js";
50
+ import { cn } from "../../utils/cn.js";
51
+ import { Popover } from "../popover/index.js";
52
+
53
+ /** Side of the column every indicator is drawn in. */
54
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
55
+ const INDICATOR_SIZE = 18;
56
+
57
+ /**
58
+ * A theme token as a colour an icon will accept.
59
+ *
60
+ * `useCSSVariable` answers with whatever the token holds, which for a length
61
+ * or a number is not a colour at all — so anything that is not a string is
62
+ * dropped and the icon falls back to what it inherits.
63
+ */
64
+ function useTint(variable) {
65
+ const raw = useCSSVariable(variable);
66
+ return typeof raw === 'string' ? raw : undefined;
67
+ }
68
+
69
+ /** How long a row takes to light up under a finger, and to let go again. */
70
+ const PRESS_IN_DURATION = 90;
71
+ const PRESS_OUT_DURATION = 160;
72
+
73
+ /** How far a pressed row shrinks. Enough to feel, not enough to see move. */
74
+ const PRESS_SCALE = 0.98;
75
+ const menuVariants = tv({
76
+ slots: {
77
+ /*
78
+ * No background here: the panel's surface is a layer of its own, drawn by
79
+ * `Menu.Background` behind the rows, so that a caller can replace it with
80
+ * a gradient or a blur without also having to redraw the rows.
81
+ */
82
+ content: 'gap-0.5 rounded-3xl p-1.5',
83
+ background: 'absolute inset-0 rounded-3xl bg-overlay',
84
+ label: 'px-3 pb-1 pt-2',
85
+ item: 'w-full flex-row items-center gap-2.5 rounded-2xl px-2.5 py-2.5',
86
+ itemLabel: 'text-base font-medium text-overlay-foreground',
87
+ itemDescription: 'text-sm',
88
+ shortcut: 'text-xs tracking-widest',
89
+ separator: 'my-1 h-px bg-border',
90
+ indicator: 'items-center justify-center'
91
+ },
92
+ variants: {
93
+ variant: {
94
+ // The pressed fill is animated rather than switched, so it is not a
95
+ // class here — only what the animation cannot carry.
96
+ default: {},
97
+ /*
98
+ * The row is tinted rather than only recoloured. A red word on an
99
+ * otherwise ordinary row is easy to read past at a glance, and this is
100
+ * the one row in the menu where reading past it is expensive.
101
+ */
102
+ destructive: {
103
+ itemLabel: 'text-destructive'
104
+ }
105
+ },
106
+ disabled: {
107
+ true: {
108
+ item: 'opacity-[0.45]'
109
+ }
110
+ },
111
+ /** Reserves the indicator column on a row that has no indicator of its own. */
112
+ inset: {
113
+ true: {
114
+ item: 'ps-[38px]',
115
+ label: 'ps-[38px]'
116
+ }
117
+ }
118
+ },
119
+ defaultVariants: {
120
+ variant: 'default',
121
+ disabled: false,
122
+ inset: false
123
+ }
124
+ });
125
+ const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
126
+
127
+ /**
128
+ * The pressed state of a row, animated rather than switched.
129
+ *
130
+ * `active:` swaps a class wholesale, which lands the fill in one frame and
131
+ * takes it away in one frame — on a row the size of a menu item that reads as
132
+ * a flash rather than as a press. Interpolating a shared value fades it in and
133
+ * back out on the UI thread, and carries a shrink along with it that a class
134
+ * cannot express at all.
135
+ */
136
+ function useMenuPress(variant) {
137
+ const reduced = useReducedMotion();
138
+ const pressed = useSharedValue(0);
139
+ const accent = useTint('--color-accent');
140
+ const destructive = useTint('--color-destructive-subtle');
141
+ const fill = variant === 'destructive' ? destructive : accent;
142
+
143
+ /*
144
+ * The fill is a layer with an animated opacity rather than an animated
145
+ * `backgroundColor`, because crossing *from* transparent needs a colour to
146
+ * cross from and the row has none of its own — what is behind it is whatever
147
+ * the panel's background layer happens to be. Fading the row itself would
148
+ * take the label with it, so only the fill fades.
149
+ */
150
+ const fillStyle = useAnimatedStyle(() => ({
151
+ opacity: pressed.value
152
+ }));
153
+ const rowStyle = useAnimatedStyle(() => ({
154
+ transform: [{
155
+ scale: 1 - (1 - PRESS_SCALE) * pressed.value
156
+ }]
157
+ }));
158
+ return {
159
+ rowStyle,
160
+ fillStyle,
161
+ fill,
162
+ onPressIn: () => {
163
+ pressed.value = reduced ? 1 : withTiming(1, {
164
+ duration: PRESS_IN_DURATION
165
+ });
166
+ },
167
+ onPressOut: () => {
168
+ pressed.value = reduced ? 0 : withTiming(0, {
169
+ duration: PRESS_OUT_DURATION
170
+ });
171
+ }
172
+ };
173
+ }
174
+ const MenuContext = /*#__PURE__*/createContext(null);
175
+ function useMenu(component) {
176
+ const context = useContext(MenuContext);
177
+ if (!context) {
178
+ throw new Error(`${component} must be used within a <Menu>`);
179
+ }
180
+ return context;
181
+ }
182
+ function MenuRoot({
183
+ children,
184
+ open,
185
+ onOpenChange,
186
+ defaultOpen,
187
+ haptics = false,
188
+ ...props
189
+ }) {
190
+ const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);
191
+ const isControlled = open !== undefined;
192
+
193
+ /*
194
+ * The open state is mirrored here as well as inside the popover, because a
195
+ * row has to be able to dismiss the panel it is sitting in and the popover
196
+ * only publishes that through `Popover.Close` — which wraps a child and so
197
+ * cannot be handed to a row that already owns its own press handler.
198
+ */
199
+ const setOpen = useCallback(next => {
200
+ if (!isControlled) setInternalOpen(next);
201
+ onOpenChange?.(next);
202
+ }, [isControlled, onOpenChange]);
203
+ const context = useMemo(() => ({
204
+ close: () => setOpen(false),
205
+ haptics
206
+ }), [setOpen, haptics]);
207
+ return /*#__PURE__*/_jsx(MenuContext.Provider, {
208
+ value: context,
209
+ children: /*#__PURE__*/_jsx(Popover, {
210
+ open: isControlled ? open : internalOpen,
211
+ onOpenChange: setOpen,
212
+ ...props,
213
+ children: children
214
+ })
215
+ });
216
+ }
217
+ /** Wraps a single child and opens the menu on press. Also what gets measured. */
218
+ function MenuTrigger({
219
+ children
220
+ }) {
221
+ return /*#__PURE__*/_jsx(Popover.Trigger, {
222
+ children: children
223
+ });
224
+ }
225
+ /**
226
+ * The panel, and the thing screen readers announce as a menu.
227
+ *
228
+ * Its padding is the row gutter rather than the popover's content padding —
229
+ * rows run to the panel's inner edge so that a pressed row's highlight reads
230
+ * as part of the panel instead of a floating chip inside it.
231
+ *
232
+ * The surface is a layer rather than a background on the panel itself, so that
233
+ * a caller can put something *behind* the rows. Pass a `Menu.Background` of
234
+ * your own with a gradient, an image or a blur inside it and it replaces the
235
+ * default one; pass nothing and the default is drawn for you.
236
+ */
237
+ function MenuContent({
238
+ className,
239
+ children,
240
+ scrollable = true,
241
+ ...props
242
+ }) {
243
+ const {
244
+ content
245
+ } = menuVariants();
246
+ const context = useMenu('Menu.Content');
247
+
248
+ /*
249
+ * A caller's own background replaces the default rather than stacking on top
250
+ * of it, and it is lifted out of the children so that it lands outside the
251
+ * scroller — a surface inside one scrolls away with the rows sitting on it.
252
+ */
253
+ const items = [];
254
+ let background = null;
255
+ for (const child of Children.toArray(children)) {
256
+ if (/*#__PURE__*/isValidElement(child) && child.type === MenuBackground) {
257
+ background = child;
258
+ } else {
259
+ items.push(child);
260
+ }
261
+ }
262
+ return /*#__PURE__*/_jsx(Popover.Content, {
263
+ accessibilityRole: "menu",
264
+ scrollable: scrollable,
265
+ unstyled: true,
266
+ background: background ?? /*#__PURE__*/_jsx(MenuBackground, {}),
267
+ className: cn(content(), 'shadow-lg', className),
268
+ ...props,
269
+ children: /*#__PURE__*/_jsx(MenuContext.Provider, {
270
+ value: context,
271
+ children: textChildren(items)
272
+ })
273
+ });
274
+ }
275
+ /**
276
+ * The panel's surface, drawn behind every row.
277
+ *
278
+ * It exists as a part rather than as a background on the panel because a
279
+ * background cannot be got behind. A menu that wants to be frosted, tinted or
280
+ * gradient-filled needs something under the rows and over nothing, and
281
+ * `overflow-hidden` on the panel is what keeps whatever that is inside the
282
+ * corner radius.
283
+ */
284
+ function MenuBackground({
285
+ className,
286
+ children,
287
+ ...props
288
+ }) {
289
+ const {
290
+ background
291
+ } = menuVariants();
292
+ return /*#__PURE__*/_jsx(View, {
293
+ accessibilityElementsHidden: true,
294
+ importantForAccessibility: "no-hide-descendants",
295
+ pointerEvents: "none",
296
+ className: cn(background(), className),
297
+ ...props,
298
+ children: children
299
+ });
300
+ }
301
+ /** Non-interactive heading over a run of rows. */
302
+ function MenuLabel({
303
+ className,
304
+ inset,
305
+ children,
306
+ ...props
307
+ }) {
308
+ const {
309
+ label
310
+ } = menuVariants({
311
+ inset
312
+ });
313
+ return /*#__PURE__*/_jsx(View, {
314
+ accessibilityRole: "header",
315
+ className: cn(label(), className),
316
+ ...props,
317
+ children: textChildren(children, text => /*#__PURE__*/_jsx(Text, {
318
+ size: "xs",
319
+ weight: "medium",
320
+ muted: true,
321
+ className: "uppercase tracking-wide",
322
+ children: text
323
+ }))
324
+ });
325
+ }
326
+ /** Hairline between two runs of rows. */
327
+ function MenuSeparator({
328
+ className,
329
+ ...props
330
+ }) {
331
+ const {
332
+ separator
333
+ } = menuVariants();
334
+ return /*#__PURE__*/_jsx(View, {
335
+ accessibilityElementsHidden: true,
336
+ importantForAccessibility: "no-hide-descendants",
337
+ className: cn(separator(), className),
338
+ ...props
339
+ });
340
+ }
341
+ /**
342
+ * One row of the menu.
343
+ *
344
+ * `onSelect` rather than `onPress` is the handler that closes the panel:
345
+ * `onPress` is still forwarded and still fires, so a row can keep whatever
346
+ * press behaviour it had, but the dismissal is tied to the semantic event so
347
+ * that a row which is disabled or which opts out via `closeOnSelect` behaves
348
+ * the same either way.
349
+ */
350
+ function MenuItem({
351
+ className,
352
+ children,
353
+ icon,
354
+ description,
355
+ shortcut,
356
+ trailing,
357
+ variant = 'default',
358
+ inset,
359
+ disabled = false,
360
+ closeOnSelect = true,
361
+ onSelect,
362
+ onPress,
363
+ ...props
364
+ }) {
365
+ const {
366
+ close,
367
+ haptics
368
+ } = useMenu('Menu.Item');
369
+ const slots = menuVariants({
370
+ variant,
371
+ disabled,
372
+ inset: inset && !icon
373
+ });
374
+ const press = useMenuPress(variant);
375
+ const handlePress = (...args) => {
376
+ if (disabled) return;
377
+ if (haptics) selectionTick();
378
+ onPress?.(...args);
379
+ onSelect?.();
380
+ if (closeOnSelect) close();
381
+ };
382
+ return /*#__PURE__*/_jsxs(AnimatedPressable, {
383
+ accessibilityRole: "menuitem",
384
+ accessibilityState: {
385
+ disabled
386
+ },
387
+ disabled: disabled,
388
+ onPress: handlePress,
389
+ onPressIn: press.onPressIn,
390
+ onPressOut: press.onPressOut,
391
+ style: press.rowStyle,
392
+ className: cn(slots.item(), 'overflow-hidden', className),
393
+ ...props,
394
+ children: [/*#__PURE__*/_jsx(Animated.View, {
395
+ pointerEvents: "none",
396
+ className: "absolute inset-0",
397
+ style: [press.fillStyle, {
398
+ backgroundColor: press.fill
399
+ }]
400
+ }), icon ? /*#__PURE__*/_jsx(MenuIndicatorSlot, {
401
+ children: icon
402
+ }) : null, /*#__PURE__*/_jsxs(View, {
403
+ className: "flex-1",
404
+ children: [textChildren(children, text => /*#__PURE__*/_jsx(Text, {
405
+ className: slots.itemLabel(),
406
+ children: text
407
+ })), description ? /*#__PURE__*/_jsx(Text, {
408
+ size: "xs",
409
+ muted: true,
410
+ className: slots.itemDescription(),
411
+ children: description
412
+ }) : null]
413
+ }), shortcut ? /*#__PURE__*/_jsx(Text, {
414
+ size: "xs",
415
+ muted: true,
416
+ className: slots.shortcut(),
417
+ children: shortcut
418
+ }) : null, trailing]
419
+ });
420
+ }
421
+
422
+ /**
423
+ * Fixed-width column the leading glyph sits in.
424
+ *
425
+ * Fixed rather than sized to its contents so that a checkmark appearing and
426
+ * disappearing does not shift the label beside it, and so that rows with and
427
+ * without a glyph line their labels up in the same panel.
428
+ */
429
+ function MenuIndicatorSlot({
430
+ children
431
+ }) {
432
+ const {
433
+ indicator
434
+ } = menuVariants();
435
+ return /*#__PURE__*/_jsx(View, {
436
+ className: indicator(),
437
+ style: {
438
+ width: INDICATOR_SIZE,
439
+ height: INDICATOR_SIZE
440
+ },
441
+ children: children
442
+ });
443
+ }
444
+
445
+ /**
446
+ * Placeholder that keeps the indicator column open on an unmarked row.
447
+ *
448
+ * `Menu.Item` draws no column at all when it has no icon, which is right for a
449
+ * row that will never have one — but wrong for a row whose mark appears and
450
+ * disappears as it is toggled.
451
+ */
452
+ function MenuIndicatorSpacer() {
453
+ return /*#__PURE__*/_jsx(View, {});
454
+ }
455
+ /**
456
+ * A row that carries a state instead of running an action.
457
+ *
458
+ * It keeps the menu open by default, which is the opposite of `Menu.Item` and
459
+ * deliberately so: a set of toggles is nearly always set more than one at a
460
+ * time, and closing after each one turns three taps into six.
461
+ */
462
+ function MenuCheckboxItem({
463
+ checked = false,
464
+ onCheckedChange,
465
+ onSelect,
466
+ closeOnSelect = false,
467
+ ...props
468
+ }) {
469
+ const tint = useTint('--color-popover-foreground');
470
+ return /*#__PURE__*/_jsx(MenuItem, {
471
+ accessibilityRole: "menuitem",
472
+ accessibilityState: {
473
+ checked,
474
+ disabled: props.disabled ?? false
475
+ },
476
+ closeOnSelect: closeOnSelect
477
+ // An empty column rather than no column: the mark comes and goes as the
478
+ // row is toggled, and a row whose label steps sideways each time reads
479
+ // as a different row rather than the same one in a new state.
480
+ ,
481
+ icon: checked ? /*#__PURE__*/_jsx(CheckIcon, {
482
+ size: 16,
483
+ color: tint
484
+ }) : /*#__PURE__*/_jsx(MenuIndicatorSpacer, {}),
485
+ onSelect: () => {
486
+ onCheckedChange?.(!checked);
487
+ onSelect?.();
488
+ },
489
+ ...props
490
+ });
491
+ }
492
+ const MenuRadioContext = /*#__PURE__*/createContext(null);
493
+ /** A run of rows of which exactly one is chosen. */
494
+ function MenuRadioGroup({
495
+ value,
496
+ onValueChange,
497
+ className,
498
+ children,
499
+ ...props
500
+ }) {
501
+ const context = useMemo(() => ({
502
+ value,
503
+ select: next => onValueChange?.(next)
504
+ }), [value, onValueChange]);
505
+ return /*#__PURE__*/_jsx(MenuRadioContext.Provider, {
506
+ value: context,
507
+ children: /*#__PURE__*/_jsx(View, {
508
+ accessibilityRole: "radiogroup",
509
+ className: className,
510
+ ...props,
511
+ children: children
512
+ })
513
+ });
514
+ }
515
+ /** One option inside a `Menu.RadioGroup`. */
516
+ function MenuRadioItem({
517
+ value,
518
+ indicator = 'check',
519
+ onSelect,
520
+ closeOnSelect = true,
521
+ ...props
522
+ }) {
523
+ const group = useContext(MenuRadioContext);
524
+ if (!group) {
525
+ throw new Error('Menu.RadioItem must be used within a <Menu.RadioGroup>');
526
+ }
527
+ const tint = useTint('--color-popover-foreground');
528
+ const selected = group.value === value;
529
+ return /*#__PURE__*/_jsx(MenuItem, {
530
+ accessibilityRole: "menuitem",
531
+ accessibilityState: {
532
+ selected,
533
+ disabled: props.disabled ?? false
534
+ },
535
+ closeOnSelect: closeOnSelect,
536
+ icon: selected ? indicator === 'dot' ? /*#__PURE__*/_jsx(View, {
537
+ className: "h-2 w-2 rounded-full bg-popover-foreground"
538
+ }) : /*#__PURE__*/_jsx(CheckIcon, {
539
+ size: 16,
540
+ color: tint
541
+ }) :
542
+ /*#__PURE__*/
543
+ // Held open, so the options stay in one column as the choice moves
544
+ // between them.
545
+ _jsx(MenuIndicatorSpacer, {}),
546
+ onSelect: () => {
547
+ group.select(value);
548
+ onSelect?.();
549
+ },
550
+ ...props
551
+ });
552
+ }
553
+ const MenuSubContext = /*#__PURE__*/createContext(null);
554
+ function useMenuSub(component) {
555
+ const context = useContext(MenuSubContext);
556
+ if (!context) {
557
+ throw new Error(`${component} must be used within a <Menu.Sub>`);
558
+ }
559
+ return context;
560
+ }
561
+ /** Groups a `Menu.SubTrigger` with the rows it reveals. */
562
+ function MenuSub({
563
+ children,
564
+ defaultOpen = false,
565
+ open,
566
+ onOpenChange
567
+ }) {
568
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
569
+ const isControlled = open !== undefined;
570
+ const resolved = isControlled ? open : internalOpen;
571
+ const context = useMemo(() => ({
572
+ open: resolved,
573
+ toggle: () => {
574
+ if (!isControlled) setInternalOpen(!resolved);
575
+ onOpenChange?.(!resolved);
576
+ }
577
+ }), [resolved, isControlled, onOpenChange]);
578
+ return /*#__PURE__*/_jsx(MenuSubContext.Provider, {
579
+ value: context,
580
+ children: children
581
+ });
582
+ }
583
+ /**
584
+ * The row that opens a submenu.
585
+ *
586
+ * Its chevron points along the reading direction while closed and turns to
587
+ * point down once open, so the row states which way its rows will appear
588
+ * rather than only that it has some.
589
+ */
590
+ function MenuSubTrigger({
591
+ className,
592
+ children,
593
+ icon,
594
+ onSelect,
595
+ ...props
596
+ }) {
597
+ const {
598
+ open,
599
+ toggle
600
+ } = useMenuSub('Menu.SubTrigger');
601
+ const sign = useDirectionSign();
602
+ const reducedMotion = useReducedMotion();
603
+ const chevronTint = useTint('--color-muted-foreground');
604
+ const progress = useSharedValue(open ? 1 : 0);
605
+ useEffect(() => {
606
+ progress.value = reducedMotion ? open ? 1 : 0 : withTiming(open ? 1 : 0, {
607
+ duration: 180
608
+ });
609
+ }, [open, reducedMotion, progress]);
610
+
611
+ // 90° takes a chevron from pointing along the line of text to pointing down
612
+ // into the rows it is about to reveal. Mirrored under RTL, where the closed
613
+ // state points the other way to begin with.
614
+ const chevronStyle = useAnimatedStyle(() => ({
615
+ transform: [{
616
+ rotate: `${progress.value * 90 * sign}deg`
617
+ }]
618
+ }));
619
+ return /*#__PURE__*/_jsx(MenuItem, {
620
+ accessibilityState: {
621
+ expanded: open,
622
+ disabled: props.disabled ?? false
623
+ },
624
+ closeOnSelect: false,
625
+ icon: icon,
626
+ className: className,
627
+ trailing: /*#__PURE__*/_jsx(Animated.View, {
628
+ style: chevronStyle,
629
+ children: /*#__PURE__*/_jsx(ChevronRightIcon, {
630
+ size: 16,
631
+ color: chevronTint
632
+ })
633
+ }),
634
+ onSelect: () => {
635
+ toggle();
636
+ onSelect?.();
637
+ },
638
+ ...props,
639
+ children: children
640
+ });
641
+ }
642
+ /**
643
+ * The rows a submenu reveals, opening in place.
644
+ *
645
+ * The height is animated from a measurement rather than left to a layout
646
+ * animation, and the measured copy is absolutely positioned so it always lays
647
+ * out at its natural size — a child of a view whose height is mid-animation
648
+ * would otherwise report the animated height back, and the panel would settle
649
+ * at whatever it happened to measure on the first frame.
650
+ */
651
+ function MenuSubContent({
652
+ className,
653
+ children,
654
+ ...props
655
+ }) {
656
+ const {
657
+ open
658
+ } = useMenuSub('Menu.SubContent');
659
+ const reducedMotion = useReducedMotion();
660
+ const [height, setHeight] = useState(0);
661
+ const progress = useSharedValue(open ? 1 : 0);
662
+ useEffect(() => {
663
+ progress.value = reducedMotion ? open ? 1 : 0 : withTiming(open ? 1 : 0, {
664
+ duration: 200
665
+ });
666
+ }, [open, reducedMotion, progress]);
667
+ const style = useAnimatedStyle(() => ({
668
+ height: progress.value * height,
669
+ opacity: progress.value
670
+ }));
671
+ return /*#__PURE__*/_jsx(Animated.View, {
672
+ style: style,
673
+ className: "overflow-hidden",
674
+ children: /*#__PURE__*/_jsx(View, {
675
+ onLayout: event => setHeight(event.nativeEvent.layout.height),
676
+ style: {
677
+ position: 'absolute',
678
+ left: 0,
679
+ right: 0,
680
+ top: 0
681
+ },
682
+ className: cn('ms-4 gap-0.5 border-s border-border ps-1', className),
683
+ ...props,
684
+ children: children
685
+ })
686
+ });
687
+ }
688
+ MenuTrigger.displayName = 'Menu.Trigger';
689
+ MenuContent.displayName = 'Menu.Content';
690
+ MenuBackground.displayName = 'Menu.Background';
691
+ MenuLabel.displayName = 'Menu.Label';
692
+ MenuItem.displayName = 'Menu.Item';
693
+ MenuCheckboxItem.displayName = 'Menu.CheckboxItem';
694
+ MenuRadioGroup.displayName = 'Menu.RadioGroup';
695
+ MenuRadioItem.displayName = 'Menu.RadioItem';
696
+ MenuSeparator.displayName = 'Menu.Separator';
697
+ MenuSub.displayName = 'Menu.Sub';
698
+ MenuSubTrigger.displayName = 'Menu.SubTrigger';
699
+ MenuSubContent.displayName = 'Menu.SubContent';
700
+ export const Menu = Object.assign(MenuRoot, {
701
+ Trigger: MenuTrigger,
702
+ Content: MenuContent,
703
+ Background: MenuBackground,
704
+ Label: MenuLabel,
705
+ Item: MenuItem,
706
+ CheckboxItem: MenuCheckboxItem,
707
+ RadioGroup: MenuRadioGroup,
708
+ RadioItem: MenuRadioItem,
709
+ Separator: MenuSeparator,
710
+ Sub: MenuSub,
711
+ SubTrigger: MenuSubTrigger,
712
+ SubContent: MenuSubContent
713
+ });
714
+ //# sourceMappingURL=index.js.map