panelui-native 0.7.0 → 0.12.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 (161) hide show
  1. package/README.md +5 -3
  2. package/lib/module/components/attachment/index.js +219 -0
  3. package/lib/module/components/attachment/index.js.map +1 -0
  4. package/lib/module/components/bottom-sheet/index.js +53 -3
  5. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  6. package/lib/module/components/button/index.js +19 -1
  7. package/lib/module/components/button/index.js.map +1 -1
  8. package/lib/module/components/dialog/index.js +6 -2
  9. package/lib/module/components/dialog/index.js.map +1 -1
  10. package/lib/module/components/empty-state/index.js +94 -17
  11. package/lib/module/components/empty-state/index.js.map +1 -1
  12. package/lib/module/components/frame/index.js +128 -15
  13. package/lib/module/components/frame/index.js.map +1 -1
  14. package/lib/module/components/input/index.js +107 -15
  15. package/lib/module/components/input/index.js.map +1 -1
  16. package/lib/module/components/line-chart/index.js +1024 -0
  17. package/lib/module/components/line-chart/index.js.map +1 -0
  18. package/lib/module/components/marker/index.js +190 -0
  19. package/lib/module/components/marker/index.js.map +1 -0
  20. package/lib/module/components/message/index.js +24 -8
  21. package/lib/module/components/message/index.js.map +1 -1
  22. package/lib/module/components/message-scroller/index.js +451 -0
  23. package/lib/module/components/message-scroller/index.js.map +1 -0
  24. package/lib/module/components/popover/index.js +511 -0
  25. package/lib/module/components/popover/index.js.map +1 -0
  26. package/lib/module/components/progress/index.js +52 -2
  27. package/lib/module/components/progress/index.js.map +1 -1
  28. package/lib/module/components/radio-group/index.js +132 -17
  29. package/lib/module/components/radio-group/index.js.map +1 -1
  30. package/lib/module/components/section-rail/index.js +355 -0
  31. package/lib/module/components/section-rail/index.js.map +1 -0
  32. package/lib/module/components/select/index.js +245 -56
  33. package/lib/module/components/select/index.js.map +1 -1
  34. package/lib/module/components/separator/index.js +91 -0
  35. package/lib/module/components/separator/index.js.map +1 -0
  36. package/lib/module/components/slider/index.js +355 -0
  37. package/lib/module/components/slider/index.js.map +1 -0
  38. package/lib/module/components/surface/index.js +21 -2
  39. package/lib/module/components/surface/index.js.map +1 -1
  40. package/lib/module/components/switch/index.js +10 -1
  41. package/lib/module/components/switch/index.js.map +1 -1
  42. package/lib/module/components/tabs/index.js +186 -16
  43. package/lib/module/components/tabs/index.js.map +1 -1
  44. package/lib/module/components/toggle-button/index.js +297 -0
  45. package/lib/module/components/toggle-button/index.js.map +1 -0
  46. package/lib/module/hooks/index.js +1 -0
  47. package/lib/module/hooks/index.js.map +1 -1
  48. package/lib/module/hooks/use-keyboard-avoidance.js +57 -8
  49. package/lib/module/hooks/use-keyboard-avoidance.js.map +1 -1
  50. package/lib/module/hooks/use-scroll-sections.js +89 -0
  51. package/lib/module/hooks/use-scroll-sections.js.map +1 -0
  52. package/lib/module/icons/index.js +69 -0
  53. package/lib/module/icons/index.js.map +1 -1
  54. package/lib/module/index.js +11 -2
  55. package/lib/module/index.js.map +1 -1
  56. package/lib/module/native/index.js.map +1 -1
  57. package/lib/module/primitives/keyboard-avoider.js +2 -0
  58. package/lib/module/primitives/keyboard-avoider.js.map +1 -1
  59. package/lib/module/primitives/scrim.js +72 -0
  60. package/lib/module/primitives/scrim.js.map +1 -0
  61. package/lib/module/utils/haptics.js +57 -0
  62. package/lib/module/utils/haptics.js.map +1 -0
  63. package/lib/typescript/src/components/attachment/index.d.ts +93 -0
  64. package/lib/typescript/src/components/attachment/index.d.ts.map +1 -0
  65. package/lib/typescript/src/components/bottom-sheet/index.d.ts +6 -1
  66. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  67. package/lib/typescript/src/components/button/index.d.ts +9 -4
  68. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  69. package/lib/typescript/src/components/dialog/index.d.ts +7 -1
  70. package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
  71. package/lib/typescript/src/components/empty-state/index.d.ts +152 -2
  72. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
  73. package/lib/typescript/src/components/frame/index.d.ts +47 -7
  74. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  75. package/lib/typescript/src/components/input/index.d.ts +113 -2
  76. package/lib/typescript/src/components/input/index.d.ts.map +1 -1
  77. package/lib/typescript/src/components/line-chart/index.d.ts +247 -0
  78. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -0
  79. package/lib/typescript/src/components/marker/index.d.ts +137 -0
  80. package/lib/typescript/src/components/marker/index.d.ts.map +1 -0
  81. package/lib/typescript/src/components/message/index.d.ts +8 -1
  82. package/lib/typescript/src/components/message/index.d.ts.map +1 -1
  83. package/lib/typescript/src/components/message-scroller/index.d.ts +150 -0
  84. package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -0
  85. package/lib/typescript/src/components/popover/index.d.ts +136 -0
  86. package/lib/typescript/src/components/popover/index.d.ts.map +1 -0
  87. package/lib/typescript/src/components/progress/index.d.ts +33 -0
  88. package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
  89. package/lib/typescript/src/components/radio-group/index.d.ts +39 -0
  90. package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
  91. package/lib/typescript/src/components/section-rail/index.d.ts +115 -0
  92. package/lib/typescript/src/components/section-rail/index.d.ts.map +1 -0
  93. package/lib/typescript/src/components/select/index.d.ts +46 -7
  94. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  95. package/lib/typescript/src/components/separator/index.d.ts +47 -0
  96. package/lib/typescript/src/components/separator/index.d.ts.map +1 -0
  97. package/lib/typescript/src/components/slider/index.d.ts +203 -0
  98. package/lib/typescript/src/components/slider/index.d.ts.map +1 -0
  99. package/lib/typescript/src/components/surface/index.d.ts +51 -2
  100. package/lib/typescript/src/components/surface/index.d.ts.map +1 -1
  101. package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
  102. package/lib/typescript/src/components/tabs/index.d.ts +46 -4
  103. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  104. package/lib/typescript/src/components/toggle-button/index.d.ts +244 -0
  105. package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -0
  106. package/lib/typescript/src/hooks/index.d.ts +1 -0
  107. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  108. package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts +10 -1
  109. package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts.map +1 -1
  110. package/lib/typescript/src/hooks/use-scroll-sections.d.ts +31 -0
  111. package/lib/typescript/src/hooks/use-scroll-sections.d.ts.map +1 -0
  112. package/lib/typescript/src/icons/index.d.ts +6 -0
  113. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  114. package/lib/typescript/src/index.d.ts +13 -4
  115. package/lib/typescript/src/index.d.ts.map +1 -1
  116. package/lib/typescript/src/native/index.d.ts +16 -1
  117. package/lib/typescript/src/native/index.d.ts.map +1 -1
  118. package/lib/typescript/src/primitives/keyboard-avoider.d.ts +8 -1
  119. package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
  120. package/lib/typescript/src/primitives/scrim.d.ts +21 -0
  121. package/lib/typescript/src/primitives/scrim.d.ts.map +1 -0
  122. package/lib/typescript/src/utils/haptics.d.ts +12 -0
  123. package/lib/typescript/src/utils/haptics.d.ts.map +1 -0
  124. package/package.json +9 -1
  125. package/src/components/attachment/index.tsx +279 -0
  126. package/src/components/bottom-sheet/index.tsx +58 -2
  127. package/src/components/button/index.tsx +26 -5
  128. package/src/components/dialog/index.tsx +10 -1
  129. package/src/components/empty-state/index.tsx +60 -14
  130. package/src/components/frame/index.tsx +175 -29
  131. package/src/components/input/index.tsx +122 -16
  132. package/src/components/line-chart/index.tsx +1224 -0
  133. package/src/components/marker/index.tsx +194 -0
  134. package/src/components/message/index.tsx +29 -5
  135. package/src/components/message-scroller/index.tsx +574 -0
  136. package/src/components/popover/index.tsx +624 -0
  137. package/src/components/progress/index.tsx +81 -1
  138. package/src/components/radio-group/index.tsx +156 -18
  139. package/src/components/section-rail/index.tsx +459 -0
  140. package/src/components/select/index.tsx +265 -52
  141. package/src/components/separator/index.tsx +97 -0
  142. package/src/components/slider/index.tsx +371 -0
  143. package/src/components/surface/index.tsx +29 -3
  144. package/src/components/switch/index.tsx +8 -1
  145. package/src/components/tabs/index.tsx +208 -28
  146. package/src/components/toggle-button/index.tsx +374 -0
  147. package/src/hooks/index.ts +5 -0
  148. package/src/hooks/use-keyboard-avoidance.ts +73 -7
  149. package/src/hooks/use-scroll-sections.ts +127 -0
  150. package/src/icons/index.tsx +48 -0
  151. package/src/index.ts +97 -6
  152. package/src/native/index.ts +13 -1
  153. package/src/primitives/keyboard-avoider.tsx +9 -1
  154. package/src/primitives/scrim.tsx +95 -0
  155. package/src/utils/haptics.ts +63 -0
  156. package/theme.css +64 -2
  157. package/lib/module/components/inline-select/index.js +0 -284
  158. package/lib/module/components/inline-select/index.js.map +0 -1
  159. package/lib/typescript/src/components/inline-select/index.d.ts +0 -48
  160. package/lib/typescript/src/components/inline-select/index.d.ts.map +0 -1
  161. package/src/components/inline-select/index.tsx +0 -332
@@ -1,284 +0,0 @@
1
- "use strict";
2
-
3
- /**
4
- * InlineSelect — a select that reveals its options next to the trigger instead
5
- * of taking over the screen with a sheet.
6
- *
7
- * Two presentations, because the right one depends on what surrounds it:
8
- *
9
- * - Inline (default) expands the list in normal layout flow. Everything below
10
- * moves down. Fine inside a settings list where that reads as the row
11
- * growing, wrong anywhere the shift is jarring.
12
- * - `overlay` floats the list above the page through a portal, anchored to the
13
- * trigger and flipped above it when there is no room below. Nothing else on
14
- * the screen moves.
15
- */
16
- import { Children, createContext, isValidElement, useCallback, useContext, useMemo, useRef, useState } from 'react';
17
- import { Pressable, ScrollView, useWindowDimensions, View } from 'react-native';
18
- import Animated, { FadeIn, FadeOut, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
19
- import { tv } from 'tailwind-variants';
20
- import { useCSSVariable } from 'uniwind';
21
- import { CheckIcon, ChevronDownIcon } from "../../icons/index.js";
22
- import { Portal } from "../../primitives/portal.js";
23
- import { Text } from "../../primitives/text.js";
24
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
25
- const inlineSelectVariants = tv({
26
- slots: {
27
- trigger: 'w-full flex-row items-center justify-between gap-3 rounded-2xl border border-input bg-background px-4 py-3.5',
28
- triggerLabel: 'flex-1 text-base font-medium text-foreground',
29
- placeholder: 'flex-1 text-base text-muted-foreground',
30
- list: 'overflow-hidden rounded-2xl border border-border bg-popover p-2 shadow-sm',
31
- item: 'flex-row items-center gap-2 rounded-xl px-3 py-3',
32
- itemLabel: 'flex-1 text-base font-medium text-foreground',
33
- itemIndicator: 'h-5 w-5 items-center justify-center'
34
- },
35
- variants: {
36
- selected: {
37
- true: {
38
- item: 'bg-accent'
39
- }
40
- },
41
- disabled: {
42
- true: {
43
- trigger: 'opacity-[0.64]'
44
- }
45
- },
46
- presentation: {
47
- inline: {
48
- list: 'mt-2'
49
- },
50
- overlay: {
51
- list: 'shadow-lg'
52
- }
53
- }
54
- },
55
- defaultVariants: {
56
- presentation: 'inline'
57
- }
58
- });
59
- const InlineSelectContext = /*#__PURE__*/createContext(null);
60
- /** Option row. */
61
- function InlineSelectItem({
62
- value,
63
- label
64
- }) {
65
- const context = useContext(InlineSelectContext);
66
- if (!context) {
67
- throw new Error('InlineSelect.Item must be used within an <InlineSelect>');
68
- }
69
- const selected = context.value === value;
70
- const {
71
- item,
72
- itemLabel,
73
- itemIndicator
74
- } = inlineSelectVariants({
75
- selected
76
- });
77
- const checkColor = useCSSVariable('--color-muted-foreground');
78
- return /*#__PURE__*/_jsxs(Pressable, {
79
- accessibilityRole: "menuitem",
80
- accessibilityState: {
81
- selected
82
- },
83
- onPress: () => context.onSelect(value),
84
- className: item(),
85
- children: [/*#__PURE__*/_jsx(Text, {
86
- className: itemLabel(),
87
- children: label
88
- }), /*#__PURE__*/_jsx(View, {
89
- className: itemIndicator(),
90
- children: selected ? /*#__PURE__*/_jsx(CheckIcon, {
91
- size: 16,
92
- color: typeof checkColor === 'string' ? checkColor : '#737373'
93
- }) : null
94
- })]
95
- });
96
- }
97
-
98
- /** Trigger frame in window coordinates, measured when the list opens. */
99
-
100
- function InlineSelectRoot({
101
- className,
102
- value,
103
- onValueChange,
104
- placeholder = 'Select an option',
105
- disabled,
106
- overlay = false,
107
- overlayWidth = 'trigger',
108
- overlayOffset = 8,
109
- onOpenChange,
110
- children
111
- }) {
112
- const [open, setOpen] = useState(false);
113
- const [anchor, setAnchor] = useState(null);
114
- const [listHeight, setListHeight] = useState(0);
115
- const triggerRef = useRef(null);
116
- const chevron = useSharedValue(0);
117
- const {
118
- height: screenHeight
119
- } = useWindowDimensions();
120
- const selectedLabel = useMemo(() => {
121
- let label;
122
- Children.forEach(children, child => {
123
- if (/*#__PURE__*/isValidElement(child) && child.props.value === value) {
124
- label = child.props.label;
125
- }
126
- });
127
- return label;
128
- }, [children, value]);
129
- const close = useCallback(() => {
130
- chevron.value = withTiming(0, {
131
- duration: 160
132
- });
133
- setOpen(false);
134
- onOpenChange?.(false);
135
- }, [chevron, onOpenChange]);
136
- const toggle = useCallback(() => {
137
- if (open) {
138
- close();
139
- return;
140
- }
141
- const show = () => {
142
- chevron.value = withTiming(1, {
143
- duration: 160
144
- });
145
- setOpen(true);
146
- onOpenChange?.(true);
147
- };
148
- if (!overlay) {
149
- show();
150
- return;
151
- }
152
-
153
- // The list is positioned in window coordinates, so it has to know where
154
- // the trigger actually landed — not where layout said it would.
155
- triggerRef.current?.measureInWindow((x, y, width, height) => {
156
- setAnchor({
157
- x,
158
- y,
159
- width,
160
- height
161
- });
162
- show();
163
- });
164
- }, [chevron, close, onOpenChange, open, overlay]);
165
- const context = useMemo(() => ({
166
- value,
167
- onSelect: next => {
168
- onValueChange(next);
169
- close();
170
- }
171
- }), [value, onValueChange, close]);
172
- const slots = inlineSelectVariants({
173
- disabled: !!disabled,
174
- presentation: overlay ? 'overlay' : 'inline'
175
- });
176
- const chevronColor = useCSSVariable('--color-muted-foreground');
177
- const chevronStyle = useAnimatedStyle(() => ({
178
- transform: [{
179
- rotate: `${chevron.value * 180}deg`
180
- }]
181
- }));
182
- const trigger = /*#__PURE__*/_jsxs(Pressable, {
183
- ref: triggerRef,
184
- accessibilityRole: "button",
185
- accessibilityState: {
186
- disabled: !!disabled,
187
- expanded: open
188
- },
189
- disabled: disabled,
190
- onPress: toggle,
191
- className: slots.trigger(),
192
- children: [selectedLabel ? /*#__PURE__*/_jsx(Text, {
193
- className: slots.triggerLabel(),
194
- children: selectedLabel
195
- }) : /*#__PURE__*/_jsx(Text, {
196
- className: slots.placeholder(),
197
- children: placeholder
198
- }), /*#__PURE__*/_jsx(Animated.View, {
199
- style: chevronStyle,
200
- children: /*#__PURE__*/_jsx(ChevronDownIcon, {
201
- color: typeof chevronColor === 'string' ? chevronColor : '#737373'
202
- })
203
- })]
204
- });
205
- if (!overlay) {
206
- return /*#__PURE__*/_jsx(InlineSelectContext.Provider, {
207
- value: context,
208
- children: /*#__PURE__*/_jsxs(View, {
209
- className: className,
210
- children: [trigger, open ? /*#__PURE__*/_jsx(Animated.View, {
211
- entering: FadeIn.duration(140),
212
- exiting: FadeOut.duration(120),
213
- className: slots.list(),
214
- children: children
215
- }) : null]
216
- })
217
- });
218
- }
219
-
220
- // Flip above the trigger when the list would run off the bottom. listHeight
221
- // is 0 on the first frame, so the list opens downwards and corrects itself
222
- // once measured — which is invisible inside the 140ms fade.
223
- const spaceBelow = anchor ? screenHeight - (anchor.y + anchor.height) - overlayOffset : 0;
224
- const flip = !!anchor && listHeight > 0 && listHeight > spaceBelow;
225
- const overlayPosition = anchor ? {
226
- position: 'absolute',
227
- left: anchor.x,
228
- ...(flip ? {
229
- bottom: screenHeight - anchor.y + overlayOffset
230
- } : {
231
- top: anchor.y + anchor.height + overlayOffset
232
- }),
233
- ...(overlayWidth === 'trigger' ? {
234
- width: anchor.width
235
- } : typeof overlayWidth === 'number' ? {
236
- width: overlayWidth
237
- } : {
238
- minWidth: anchor.width
239
- }),
240
- // Never collapse to nothing in a cramped viewport — the list scrolls.
241
- maxHeight: Math.max((flip ? anchor.y : spaceBelow) - overlayOffset, 160)
242
- } : null;
243
- const onListLayout = event => {
244
- setListHeight(event.nativeEvent.layout.height);
245
- };
246
- return /*#__PURE__*/_jsxs(InlineSelectContext.Provider, {
247
- value: context,
248
- children: [/*#__PURE__*/_jsx(View, {
249
- className: className,
250
- children: trigger
251
- }), open && overlayPosition ? /*#__PURE__*/_jsxs(Portal, {
252
- children: [/*#__PURE__*/_jsx(Pressable, {
253
- accessibilityRole: "button",
254
- accessibilityLabel: "Close",
255
- onPress: close,
256
- style: {
257
- position: 'absolute',
258
- top: 0,
259
- left: 0,
260
- right: 0,
261
- bottom: 0
262
- }
263
- }), /*#__PURE__*/_jsx(InlineSelectContext.Provider, {
264
- value: context,
265
- children: /*#__PURE__*/_jsx(Animated.View, {
266
- entering: FadeIn.duration(140),
267
- exiting: FadeOut.duration(120),
268
- onLayout: onListLayout,
269
- style: overlayPosition,
270
- className: slots.list(),
271
- children: /*#__PURE__*/_jsx(ScrollView, {
272
- bounces: false,
273
- showsVerticalScrollIndicator: false,
274
- children: children
275
- })
276
- })
277
- })]
278
- }) : null]
279
- });
280
- }
281
- export const InlineSelect = Object.assign(InlineSelectRoot, {
282
- Item: InlineSelectItem
283
- });
284
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["Children","createContext","isValidElement","useCallback","useContext","useMemo","useRef","useState","Pressable","ScrollView","useWindowDimensions","View","Animated","FadeIn","FadeOut","useAnimatedStyle","useSharedValue","withTiming","tv","useCSSVariable","CheckIcon","ChevronDownIcon","Portal","Text","jsx","_jsx","jsxs","_jsxs","inlineSelectVariants","slots","trigger","triggerLabel","placeholder","list","item","itemLabel","itemIndicator","variants","selected","true","disabled","presentation","inline","overlay","defaultVariants","InlineSelectContext","InlineSelectItem","value","label","context","Error","checkColor","accessibilityRole","accessibilityState","onPress","onSelect","className","children","size","color","InlineSelectRoot","onValueChange","overlayWidth","overlayOffset","onOpenChange","open","setOpen","anchor","setAnchor","listHeight","setListHeight","triggerRef","chevron","height","screenHeight","selectedLabel","forEach","child","props","close","duration","toggle","show","current","measureInWindow","x","y","width","next","chevronColor","chevronStyle","transform","rotate","ref","expanded","style","Provider","entering","exiting","spaceBelow","flip","overlayPosition","position","left","bottom","top","minWidth","maxHeight","Math","max","onListLayout","event","nativeEvent","layout","accessibilityLabel","right","onLayout","bounces","showsVerticalScrollIndicator","InlineSelect","Object","assign","Item"],"sourceRoot":"../../../../src","sources":["components/inline-select/index.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,48 +0,0 @@
1
- /**
2
- * InlineSelect — a select that reveals its options next to the trigger instead
3
- * of taking over the screen with a sheet.
4
- *
5
- * Two presentations, because the right one depends on what surrounds it:
6
- *
7
- * - Inline (default) expands the list in normal layout flow. Everything below
8
- * moves down. Fine inside a settings list where that reads as the row
9
- * growing, wrong anywhere the shift is jarring.
10
- * - `overlay` floats the list above the page through a portal, anchored to the
11
- * trigger and flipped above it when there is no room below. Nothing else on
12
- * the screen moves.
13
- */
14
- import { type ReactNode } from 'react';
15
- export interface InlineSelectItemProps {
16
- value: string;
17
- label: string;
18
- }
19
- /** Option row. */
20
- declare function InlineSelectItem({ value, label }: InlineSelectItemProps): import("react").JSX.Element;
21
- export interface InlineSelectProps {
22
- className?: string;
23
- value?: string;
24
- onValueChange: (value: string) => void;
25
- placeholder?: string;
26
- disabled?: boolean;
27
- /**
28
- * Float the list above the page instead of expanding it in layout flow, so
29
- * opening the select does not push the content below it down.
30
- */
31
- overlay?: boolean;
32
- /**
33
- * Width of the floating list. `trigger` matches the trigger, `content` sizes
34
- * to the longest option, or pass a pixel value. Ignored without `overlay`.
35
- */
36
- overlayWidth?: 'trigger' | 'content' | number;
37
- /** Gap between the trigger and the floating list. Ignored without `overlay`. */
38
- overlayOffset?: number;
39
- /** Called when the list opens or closes. */
40
- onOpenChange?: (open: boolean) => void;
41
- children: ReactNode;
42
- }
43
- declare function InlineSelectRoot({ className, value, onValueChange, placeholder, disabled, overlay, overlayWidth, overlayOffset, onOpenChange, children, }: InlineSelectProps): import("react").JSX.Element;
44
- export declare const InlineSelect: typeof InlineSelectRoot & {
45
- Item: typeof InlineSelectItem;
46
- };
47
- export {};
48
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/inline-select/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAwDf,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,kBAAkB;AAClB,iBAAS,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,qBAAqB,+BA4BhE;AAUD,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC9C,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,KAAK,EACL,aAAa,EACb,WAAgC,EAChC,QAAQ,EACR,OAAe,EACf,YAAwB,EACxB,aAAiB,EACjB,YAAY,EACZ,QAAQ,GACT,EAAE,iBAAiB,+BA0KnB;AAED,eAAO,MAAM,YAAY;;CAEvB,CAAC"}
@@ -1,332 +0,0 @@
1
- /**
2
- * InlineSelect — a select that reveals its options next to the trigger instead
3
- * of taking over the screen with a sheet.
4
- *
5
- * Two presentations, because the right one depends on what surrounds it:
6
- *
7
- * - Inline (default) expands the list in normal layout flow. Everything below
8
- * moves down. Fine inside a settings list where that reads as the row
9
- * growing, wrong anywhere the shift is jarring.
10
- * - `overlay` floats the list above the page through a portal, anchored to the
11
- * trigger and flipped above it when there is no room below. Nothing else on
12
- * the screen moves.
13
- */
14
- import {
15
- Children,
16
- createContext,
17
- isValidElement,
18
- useCallback,
19
- useContext,
20
- useMemo,
21
- useRef,
22
- useState,
23
- type ReactNode,
24
- } from 'react';
25
- import {
26
- Pressable,
27
- ScrollView,
28
- useWindowDimensions,
29
- View,
30
- type LayoutChangeEvent,
31
- } from 'react-native';
32
- import Animated, {
33
- FadeIn,
34
- FadeOut,
35
- useAnimatedStyle,
36
- useSharedValue,
37
- withTiming,
38
- } from 'react-native-reanimated';
39
- import { tv } from 'tailwind-variants';
40
- import { useCSSVariable } from 'uniwind';
41
- import { CheckIcon, ChevronDownIcon } from '../../icons';
42
- import { Portal } from '../../primitives/portal';
43
- import { Text } from '../../primitives/text';
44
-
45
- const inlineSelectVariants = tv({
46
- slots: {
47
- trigger:
48
- 'w-full flex-row items-center justify-between gap-3 rounded-2xl border border-input bg-background px-4 py-3.5',
49
- triggerLabel: 'flex-1 text-base font-medium text-foreground',
50
- placeholder: 'flex-1 text-base text-muted-foreground',
51
- list: 'overflow-hidden rounded-2xl border border-border bg-popover p-2 shadow-sm',
52
- item: 'flex-row items-center gap-2 rounded-xl px-3 py-3',
53
- itemLabel: 'flex-1 text-base font-medium text-foreground',
54
- itemIndicator: 'h-5 w-5 items-center justify-center',
55
- },
56
- variants: {
57
- selected: {
58
- true: { item: 'bg-accent' },
59
- },
60
- disabled: {
61
- true: { trigger: 'opacity-[0.64]' },
62
- },
63
- presentation: {
64
- inline: { list: 'mt-2' },
65
- overlay: { list: 'shadow-lg' },
66
- },
67
- },
68
- defaultVariants: {
69
- presentation: 'inline',
70
- },
71
- });
72
-
73
- interface InlineSelectContextValue {
74
- value: string | undefined;
75
- onSelect: (value: string) => void;
76
- }
77
-
78
- const InlineSelectContext = createContext<InlineSelectContextValue | null>(null);
79
-
80
- export interface InlineSelectItemProps {
81
- value: string;
82
- label: string;
83
- }
84
-
85
- /** Option row. */
86
- function InlineSelectItem({ value, label }: InlineSelectItemProps) {
87
- const context = useContext(InlineSelectContext);
88
- if (!context) {
89
- throw new Error('InlineSelect.Item must be used within an <InlineSelect>');
90
- }
91
-
92
- const selected = context.value === value;
93
- const { item, itemLabel, itemIndicator } = inlineSelectVariants({ selected });
94
- const checkColor = useCSSVariable('--color-muted-foreground');
95
-
96
- return (
97
- <Pressable
98
- accessibilityRole="menuitem"
99
- accessibilityState={{ selected }}
100
- onPress={() => context.onSelect(value)}
101
- className={item()}
102
- >
103
- <Text className={itemLabel()}>{label}</Text>
104
- <View className={itemIndicator()}>
105
- {selected ? (
106
- <CheckIcon
107
- size={16}
108
- color={typeof checkColor === 'string' ? checkColor : '#737373'}
109
- />
110
- ) : null}
111
- </View>
112
- </Pressable>
113
- );
114
- }
115
-
116
- /** Trigger frame in window coordinates, measured when the list opens. */
117
- interface Anchor {
118
- x: number;
119
- y: number;
120
- width: number;
121
- height: number;
122
- }
123
-
124
- export interface InlineSelectProps {
125
- className?: string;
126
- value?: string;
127
- onValueChange: (value: string) => void;
128
- placeholder?: string;
129
- disabled?: boolean;
130
- /**
131
- * Float the list above the page instead of expanding it in layout flow, so
132
- * opening the select does not push the content below it down.
133
- */
134
- overlay?: boolean;
135
- /**
136
- * Width of the floating list. `trigger` matches the trigger, `content` sizes
137
- * to the longest option, or pass a pixel value. Ignored without `overlay`.
138
- */
139
- overlayWidth?: 'trigger' | 'content' | number;
140
- /** Gap between the trigger and the floating list. Ignored without `overlay`. */
141
- overlayOffset?: number;
142
- /** Called when the list opens or closes. */
143
- onOpenChange?: (open: boolean) => void;
144
- children: ReactNode;
145
- }
146
-
147
- function InlineSelectRoot({
148
- className,
149
- value,
150
- onValueChange,
151
- placeholder = 'Select an option',
152
- disabled,
153
- overlay = false,
154
- overlayWidth = 'trigger',
155
- overlayOffset = 8,
156
- onOpenChange,
157
- children,
158
- }: InlineSelectProps) {
159
- const [open, setOpen] = useState(false);
160
- const [anchor, setAnchor] = useState<Anchor | null>(null);
161
- const [listHeight, setListHeight] = useState(0);
162
- const triggerRef = useRef<View>(null);
163
- const chevron = useSharedValue(0);
164
- const { height: screenHeight } = useWindowDimensions();
165
-
166
- const selectedLabel = useMemo(() => {
167
- let label: string | undefined;
168
- Children.forEach(children, (child) => {
169
- if (isValidElement<InlineSelectItemProps>(child) && child.props.value === value) {
170
- label = child.props.label;
171
- }
172
- });
173
- return label;
174
- }, [children, value]);
175
-
176
- const close = useCallback(() => {
177
- chevron.value = withTiming(0, { duration: 160 });
178
- setOpen(false);
179
- onOpenChange?.(false);
180
- }, [chevron, onOpenChange]);
181
-
182
- const toggle = useCallback(() => {
183
- if (open) {
184
- close();
185
- return;
186
- }
187
-
188
- const show = () => {
189
- chevron.value = withTiming(1, { duration: 160 });
190
- setOpen(true);
191
- onOpenChange?.(true);
192
- };
193
-
194
- if (!overlay) {
195
- show();
196
- return;
197
- }
198
-
199
- // The list is positioned in window coordinates, so it has to know where
200
- // the trigger actually landed — not where layout said it would.
201
- triggerRef.current?.measureInWindow((x, y, width, height) => {
202
- setAnchor({ x, y, width, height });
203
- show();
204
- });
205
- }, [chevron, close, onOpenChange, open, overlay]);
206
-
207
- const context = useMemo<InlineSelectContextValue>(
208
- () => ({
209
- value,
210
- onSelect: (next) => {
211
- onValueChange(next);
212
- close();
213
- },
214
- }),
215
- [value, onValueChange, close]
216
- );
217
-
218
- const slots = inlineSelectVariants({
219
- disabled: !!disabled,
220
- presentation: overlay ? 'overlay' : 'inline',
221
- });
222
- const chevronColor = useCSSVariable('--color-muted-foreground');
223
-
224
- const chevronStyle = useAnimatedStyle(() => ({
225
- transform: [{ rotate: `${chevron.value * 180}deg` }],
226
- }));
227
-
228
- const trigger = (
229
- <Pressable
230
- ref={triggerRef}
231
- accessibilityRole="button"
232
- accessibilityState={{ disabled: !!disabled, expanded: open }}
233
- disabled={disabled}
234
- onPress={toggle}
235
- className={slots.trigger()}
236
- >
237
- {selectedLabel ? (
238
- <Text className={slots.triggerLabel()}>{selectedLabel}</Text>
239
- ) : (
240
- <Text className={slots.placeholder()}>{placeholder}</Text>
241
- )}
242
- <Animated.View style={chevronStyle}>
243
- <ChevronDownIcon
244
- color={typeof chevronColor === 'string' ? chevronColor : '#737373'}
245
- />
246
- </Animated.View>
247
- </Pressable>
248
- );
249
-
250
- if (!overlay) {
251
- return (
252
- <InlineSelectContext.Provider value={context}>
253
- <View className={className}>
254
- {trigger}
255
- {open ? (
256
- <Animated.View
257
- entering={FadeIn.duration(140)}
258
- exiting={FadeOut.duration(120)}
259
- className={slots.list()}
260
- >
261
- {children}
262
- </Animated.View>
263
- ) : null}
264
- </View>
265
- </InlineSelectContext.Provider>
266
- );
267
- }
268
-
269
- // Flip above the trigger when the list would run off the bottom. listHeight
270
- // is 0 on the first frame, so the list opens downwards and corrects itself
271
- // once measured — which is invisible inside the 140ms fade.
272
- const spaceBelow = anchor
273
- ? screenHeight - (anchor.y + anchor.height) - overlayOffset
274
- : 0;
275
- const flip = !!anchor && listHeight > 0 && listHeight > spaceBelow;
276
-
277
- const overlayPosition = anchor
278
- ? {
279
- position: 'absolute' as const,
280
- left: anchor.x,
281
- ...(flip
282
- ? { bottom: screenHeight - anchor.y + overlayOffset }
283
- : { top: anchor.y + anchor.height + overlayOffset }),
284
- ...(overlayWidth === 'trigger'
285
- ? { width: anchor.width }
286
- : typeof overlayWidth === 'number'
287
- ? { width: overlayWidth }
288
- : { minWidth: anchor.width }),
289
- // Never collapse to nothing in a cramped viewport — the list scrolls.
290
- maxHeight: Math.max((flip ? anchor.y : spaceBelow) - overlayOffset, 160),
291
- }
292
- : null;
293
-
294
- const onListLayout = (event: LayoutChangeEvent) => {
295
- setListHeight(event.nativeEvent.layout.height);
296
- };
297
-
298
- return (
299
- <InlineSelectContext.Provider value={context}>
300
- <View className={className}>{trigger}</View>
301
-
302
- {open && overlayPosition ? (
303
- <Portal>
304
- {/* Full-screen catcher so a press anywhere else dismisses the list. */}
305
- <Pressable
306
- accessibilityRole="button"
307
- accessibilityLabel="Close"
308
- onPress={close}
309
- style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}
310
- />
311
- <InlineSelectContext.Provider value={context}>
312
- <Animated.View
313
- entering={FadeIn.duration(140)}
314
- exiting={FadeOut.duration(120)}
315
- onLayout={onListLayout}
316
- style={overlayPosition}
317
- className={slots.list()}
318
- >
319
- <ScrollView bounces={false} showsVerticalScrollIndicator={false}>
320
- {children}
321
- </ScrollView>
322
- </Animated.View>
323
- </InlineSelectContext.Provider>
324
- </Portal>
325
- ) : null}
326
- </InlineSelectContext.Provider>
327
- );
328
- }
329
-
330
- export const InlineSelect = Object.assign(InlineSelectRoot, {
331
- Item: InlineSelectItem,
332
- });