@streamplace/components 0.7.35 → 0.8.3

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 (45) hide show
  1. package/dist/components/content-metadata/content-metadata-form.js +467 -0
  2. package/dist/components/content-metadata/content-rights.js +78 -0
  3. package/dist/components/content-metadata/content-warnings.js +68 -0
  4. package/dist/components/content-metadata/index.js +11 -0
  5. package/dist/components/mobile-player/player.js +4 -0
  6. package/dist/components/mobile-player/ui/report-modal.js +3 -2
  7. package/dist/components/ui/checkbox.js +87 -0
  8. package/dist/components/ui/dialog.js +188 -83
  9. package/dist/components/ui/primitives/input.js +13 -1
  10. package/dist/components/ui/primitives/modal.js +2 -2
  11. package/dist/components/ui/select.js +89 -0
  12. package/dist/components/ui/textarea.js +23 -4
  13. package/dist/components/ui/toast.js +464 -114
  14. package/dist/components/ui/tooltip.js +103 -0
  15. package/dist/index.js +2 -0
  16. package/dist/lib/metadata-constants.js +157 -0
  17. package/dist/lib/theme/theme.js +5 -3
  18. package/dist/streamplace-provider/index.js +14 -4
  19. package/dist/streamplace-store/content-metadata-actions.js +124 -0
  20. package/dist/streamplace-store/streamplace-store.js +22 -5
  21. package/dist/streamplace-store/user.js +67 -7
  22. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  23. package/package.json +3 -3
  24. package/src/components/content-metadata/content-metadata-form.tsx +893 -0
  25. package/src/components/content-metadata/content-rights.tsx +104 -0
  26. package/src/components/content-metadata/content-warnings.tsx +100 -0
  27. package/src/components/content-metadata/index.tsx +10 -0
  28. package/src/components/mobile-player/player.tsx +5 -0
  29. package/src/components/mobile-player/ui/report-modal.tsx +13 -7
  30. package/src/components/ui/checkbox.tsx +147 -0
  31. package/src/components/ui/dialog.tsx +319 -99
  32. package/src/components/ui/primitives/input.tsx +19 -2
  33. package/src/components/ui/primitives/modal.tsx +4 -2
  34. package/src/components/ui/select.tsx +175 -0
  35. package/src/components/ui/textarea.tsx +47 -29
  36. package/src/components/ui/toast.tsx +785 -179
  37. package/src/components/ui/tooltip.tsx +131 -0
  38. package/src/index.tsx +3 -0
  39. package/src/lib/metadata-constants.ts +180 -0
  40. package/src/lib/theme/theme.tsx +10 -6
  41. package/src/streamplace-provider/index.tsx +20 -2
  42. package/src/streamplace-store/content-metadata-actions.tsx +145 -0
  43. package/src/streamplace-store/streamplace-store.tsx +41 -4
  44. package/src/streamplace-store/user.tsx +71 -7
  45. package/tsconfig.tsbuildinfo +1 -1
@@ -3,47 +3,63 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toast = void 0;
4
4
  exports.useToast = useToast;
5
5
  exports.ToastProvider = ToastProvider;
6
+ exports.AndMore = AndMore;
6
7
  exports.Toast = Toast;
8
+ const tslib_1 = require("tslib");
7
9
  const jsx_runtime_1 = require("react/jsx-runtime");
8
10
  const portal_1 = require("@rn-primitives/portal");
11
+ const lucide_react_native_1 = require("lucide-react-native");
9
12
  const react_1 = require("react");
10
13
  const react_native_1 = require("react-native");
14
+ const react_native_gesture_handler_1 = require("react-native-gesture-handler");
15
+ const react_native_reanimated_1 = tslib_1.__importStar(require("react-native-reanimated"));
11
16
  const react_native_safe_area_context_1 = require("react-native-safe-area-context");
17
+ const react_native_svg_1 = require("react-native-svg");
12
18
  const ui_1 = require("../../ui");
19
+ const button_1 = require("./button");
20
+ const text_1 = require("./text");
13
21
  class ToastManager {
14
22
  constructor() {
15
23
  this.listeners = new Set();
16
- this.currentToast = null;
17
- this.timeoutId = null;
24
+ this.toasts = [];
25
+ this.toastHeights = new Map();
26
+ this.hoverListeners = new Set();
27
+ this.isHovered = false;
18
28
  }
19
29
  show(config) {
20
- if (this.timeoutId) {
21
- clearTimeout(this.timeoutId);
22
- }
23
30
  const toast = {
24
- id: Math.random().toString(36).substr(2, 9),
31
+ id: Math.random().toString(36).slice(2, 12),
25
32
  open: true,
26
33
  title: config.title,
27
34
  description: config.description,
28
35
  duration: config.duration ?? 3,
29
36
  actionLabel: config.actionLabel,
30
37
  onAction: config.onAction,
38
+ onClose: config.onClose,
39
+ variant: config.variant ?? "default",
40
+ render: config.render,
41
+ position: config.position ?? "auto",
42
+ iconLeft: config.iconLeft,
43
+ iconRight: config.iconRight,
44
+ onToastPress: config.onToastPress,
31
45
  };
32
- this.currentToast = toast;
46
+ this.toasts = [...this.toasts, toast];
33
47
  this.notifyListeners();
34
- if (toast.duration > 0) {
35
- this.timeoutId = setTimeout(() => {
36
- this.hide();
37
- }, toast.duration * 1000);
38
- }
39
48
  }
40
- hide() {
41
- if (this.timeoutId) {
42
- clearTimeout(this.timeoutId);
43
- this.timeoutId = null;
49
+ getToasts() {
50
+ return this.toasts;
51
+ }
52
+ hide(id) {
53
+ const toast = this.toasts.find((t) => t.id === id);
54
+ if (toast?.onClose) {
55
+ toast.onClose();
44
56
  }
45
- this.currentToast = null;
57
+ this.toasts = this.toasts.map((toast) => toast.id === id ? { ...toast, open: false } : toast);
46
58
  this.notifyListeners();
59
+ setTimeout(() => {
60
+ this.toasts = this.toasts.filter((toast) => toast.id !== id);
61
+ this.notifyListeners();
62
+ }, 500);
47
63
  }
48
64
  subscribe(listener) {
49
65
  this.listeners.add(listener);
@@ -51,8 +67,27 @@ class ToastManager {
51
67
  this.listeners.delete(listener);
52
68
  };
53
69
  }
70
+ subscribeHover(listener) {
71
+ this.hoverListeners.add(listener);
72
+ return () => {
73
+ this.hoverListeners.delete(listener);
74
+ };
75
+ }
76
+ setHovered(hovered) {
77
+ this.isHovered = hovered;
78
+ this.notifyHoverListeners();
79
+ }
80
+ updateToastHeight(id, height) {
81
+ this.toastHeights.set(id, height);
82
+ }
83
+ getToastHeight(id) {
84
+ return this.toastHeights.get(id) || 100; // Default fallback height
85
+ }
54
86
  notifyListeners() {
55
- this.listeners.forEach((listener) => listener(this.currentToast));
87
+ this.listeners.forEach((listener) => listener(this.toasts));
88
+ }
89
+ notifyHoverListeners() {
90
+ this.hoverListeners.forEach((listener) => listener(this.isHovered));
56
91
  }
57
92
  }
58
93
  const toastManager = new ToastManager();
@@ -64,142 +99,457 @@ exports.toast = {
64
99
  ...options,
65
100
  });
66
101
  },
67
- hide: () => toastManager.hide(),
102
+ hide: (id) => {
103
+ if (id) {
104
+ toastManager.hide(id);
105
+ }
106
+ else {
107
+ const toasts = toastManager.getToasts();
108
+ if (toasts.length > 0) {
109
+ toastManager.hide(toasts[toasts.length - 1].id);
110
+ }
111
+ }
112
+ },
113
+ showManual: (render, options) => {
114
+ toastManager.show({
115
+ render,
116
+ ...options,
117
+ });
118
+ },
68
119
  };
69
120
  function useToast() {
70
- const [toastState, setToastState] = (0, react_1.useState)(null);
121
+ const [toasts, setToasts] = (0, react_1.useState)([]);
71
122
  (0, react_1.useEffect)(() => {
72
- return toastManager.subscribe(setToastState);
123
+ return toastManager.subscribe(setToasts);
73
124
  }, []);
74
125
  return {
75
- toast: toastState,
126
+ toasts,
76
127
  ...exports.toast,
77
128
  };
78
129
  }
79
130
  function ToastProvider() {
80
- const [toastState, setToastState] = (0, react_1.useState)(null);
131
+ const [toasts, setToasts] = (0, react_1.useState)([]);
81
132
  (0, react_1.useEffect)(() => {
82
- return toastManager.subscribe(setToastState);
133
+ return toastManager.subscribe(setToasts);
83
134
  }, []);
84
- if (!toastState?.open)
85
- return null;
86
- return ((0, jsx_runtime_1.jsx)(Toast, { open: toastState.open, onOpenChange: (open) => {
87
- if (!open)
88
- toastManager.hide();
89
- }, title: toastState.title, description: toastState.description, actionLabel: toastState.actionLabel, onAction: toastState.onAction, duration: toastState.duration }));
135
+ const toastsByPosition = toasts.reduce((acc, toast) => {
136
+ const { position } = toast;
137
+ if (!acc[position]) {
138
+ acc[position] = [];
139
+ }
140
+ acc[position].push(toast);
141
+ return acc;
142
+ }, {});
143
+ return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: Object.entries(toastsByPosition).map(([position, toasts]) => ((0, jsx_runtime_1.jsx)(ToastContainer, { position: position, toasts: toasts }, position))) }));
90
144
  }
91
- function Toast({ open, onOpenChange, title, description, actionLabel = "Action", onAction, duration = 3, }) {
92
- const [seconds, setSeconds] = (0, react_1.useState)(duration);
145
+ function ToastContainer({ position = "auto", toasts, }) {
93
146
  const insets = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
94
147
  const { theme } = (0, ui_1.useTheme)();
95
- const [fadeAnim] = (0, react_1.useState)(new react_native_1.Animated.Value(0));
96
- const { width } = (0, react_native_1.useWindowDimensions)();
97
148
  const isWeb = react_native_1.Platform.OS === "web";
149
+ const { width } = (0, react_native_1.useWindowDimensions)();
98
150
  const isDesktop = isWeb && width >= 768;
99
- const containerPosition = isDesktop
100
- ? {
101
- top: undefined,
102
- bottom: theme.spacing[4],
103
- right: theme.spacing[4], // <-- use spacing, not 1
104
- alignItems: "flex-end",
105
- minWidth: 400,
106
- width: 400,
107
- // Do NOT set left at all
108
- }
109
- : {
110
- bottom: insets.bottom + theme.spacing[1],
111
- left: 0,
112
- right: 0,
151
+ const isTop = position.includes("top");
152
+ const visibleToasts = toasts.slice(-4);
153
+ const prevToastIds = (0, react_1.useRef)(visibleToasts.map((t) => t.id));
154
+ const allKnownToastIds = (0, react_1.useRef)(new Set(toasts.map((t) => t.id)));
155
+ const [isHovered, setIsHovered] = (0, react_1.useState)(false);
156
+ (0, react_1.useEffect)(() => {
157
+ return toastManager.subscribeHover(setIsHovered);
158
+ }, []);
159
+ (0, react_1.useEffect)(() => {
160
+ const currentIds = visibleToasts.map((t) => t.id);
161
+ const hasNewToast = currentIds.some((id) => !allKnownToastIds.current.has(id));
162
+ if (hasNewToast && isHovered) {
163
+ // Brand new toast arrived while expanded - collapse momentarily
164
+ toastManager.setHovered(false);
165
+ setTimeout(() => {
166
+ toastManager.setHovered(true);
167
+ }, 700);
168
+ }
169
+ else if (isHovered) {
170
+ toastManager.setHovered(true);
171
+ setTimeout(() => {
172
+ toastManager.setHovered(true);
173
+ }, 700);
174
+ }
175
+ // Update known toast IDs
176
+ toasts.forEach((t) => allKnownToastIds.current.add(t.id));
177
+ prevToastIds.current = currentIds;
178
+ }, [visibleToasts, isHovered, toasts]);
179
+ const setHovered = (value) => toastManager.setHovered(value);
180
+ const pan = react_native_gesture_handler_1.Gesture.Pan()
181
+ .onUpdate((event) => {
182
+ const velocity = isTop ? -event.velocityY : event.velocityY;
183
+ if (velocity > 500) {
184
+ (0, react_native_reanimated_1.runOnJS)(setHovered)(true);
185
+ }
186
+ else if (velocity < -500) {
187
+ (0, react_native_reanimated_1.runOnJS)(setHovered)(false);
188
+ }
189
+ })
190
+ .onEnd((event) => {
191
+ const translationY = isTop ? -event.translationY : event.translationY;
192
+ if (translationY > 50) {
193
+ (0, react_native_reanimated_1.runOnJS)(setHovered)(true);
194
+ }
195
+ else if (translationY < -50) {
196
+ (0, react_native_reanimated_1.runOnJS)(setHovered)(false);
197
+ }
198
+ });
199
+ const gesture = react_native_1.Platform.OS === "web"
200
+ ? react_native_gesture_handler_1.Gesture.Hover()
201
+ .onStart(() => {
202
+ (0, react_native_reanimated_1.runOnJS)(setHovered)(true);
203
+ })
204
+ .onEnd(() => {
205
+ (0, react_native_reanimated_1.runOnJS)(setHovered)(false);
206
+ })
207
+ : pan;
208
+ const getPositionStyle = () => {
209
+ const resolvedPosition = position === "auto"
210
+ ? isDesktop
211
+ ? "bottom-right"
212
+ : "top-center"
213
+ : position;
214
+ const styles = {
215
+ position: "absolute",
216
+ zIndex: 1000,
217
+ paddingHorizontal: isDesktop ? 0 : theme.spacing[4],
218
+ };
219
+ // Set width/maxWidth
220
+ if (isDesktop) {
221
+ styles.width = 400;
222
+ }
223
+ else {
224
+ styles.width = "100%";
225
+ styles.maxWidth = 400;
226
+ }
227
+ if (resolvedPosition.includes("top")) {
228
+ styles.top = insets.top + theme.spacing[4];
229
+ }
230
+ if (resolvedPosition.includes("bottom")) {
231
+ styles.bottom = insets.bottom + theme.spacing[4];
232
+ }
233
+ if (resolvedPosition.includes("left")) {
234
+ styles.left = insets.left + theme.spacing[4];
235
+ styles.alignItems = "flex-start";
236
+ }
237
+ if (resolvedPosition.includes("right")) {
238
+ styles.right = insets.right + theme.spacing[4];
239
+ styles.alignItems = "flex-end";
240
+ }
241
+ if (resolvedPosition.includes("center")) {
242
+ styles.alignItems = "center";
243
+ // Center the container itself when it has maxWidth
244
+ if (!isDesktop) {
245
+ styles.left = "50%";
246
+ styles.transform = [{ translateX: "-50%" }];
247
+ }
248
+ else {
249
+ styles.left = 0;
250
+ styles.right = 0;
251
+ }
252
+ }
253
+ return styles;
254
+ };
255
+ return ((0, jsx_runtime_1.jsx)(portal_1.Portal, { name: "toasties", children: (0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.GestureDetector, { gesture: gesture, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: getPositionStyle(), children: visibleToasts.reverse().map((toastState, index) => ((0, jsx_runtime_1.jsx)(Toast, { ...toastState, onOpenChange: (open) => {
256
+ if (!open)
257
+ toastManager.hide(toastState.id);
258
+ }, index: index, isLatest: index === 0, position: position === "auto"
259
+ ? isDesktop
260
+ ? "bottom-right"
261
+ : "top-center"
262
+ : position }, toastState.id))) }) }) }));
263
+ }
264
+ function AndMore({ more }) {
265
+ const { theme } = (0, ui_1.useTheme)();
266
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: {
267
+ padding: theme.spacing[2],
268
+ paddingHorizontal: theme.spacing[4],
269
+ backgroundColor: theme.colors.muted,
270
+ borderRadius: theme.borderRadius.xl,
271
+ marginTop: theme.spacing[2],
272
+ alignSelf: "center",
273
+ }, children: (0, jsx_runtime_1.jsxs)(text_1.Text, { size: "sm", style: { color: theme.colors.mutedForeground }, children: ["and ", more, " more notification", more === 1 ? "" : "s"] }) }));
274
+ }
275
+ const AnimatedCircle = react_native_reanimated_1.default.createAnimatedComponent(react_native_svg_1.Circle);
276
+ function CloseButton({ onPress, isLatest, duration, animatedCircleProps, theme, RADIUS, CIRCUMFERENCE, }) {
277
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.Pressable, { onPress: onPress, style: {
113
278
  alignItems: "center",
114
- width: "100%",
115
- maxWidth: undefined,
279
+ justifyContent: "center",
280
+ }, children: [(0, jsx_runtime_1.jsxs)(react_native_svg_1.Svg, { width: RADIUS * 2, height: RADIUS * 2, viewBox: `0 0 ${RADIUS * 2 + 2} ${RADIUS * 2 + 2}`, children: [(0, jsx_runtime_1.jsx)(AnimatedCircle, { stroke: theme.colors.border, fill: "transparent", strokeWidth: "2", r: RADIUS, cx: RADIUS + 1, cy: RADIUS + 1 }), isLatest && duration > 0 && ((0, jsx_runtime_1.jsx)(AnimatedCircle, { animatedProps: animatedCircleProps, stroke: theme.colors.primary, fill: "transparent", strokeWidth: "2", strokeDasharray: CIRCUMFERENCE, r: RADIUS, cx: RADIUS + 1, cy: RADIUS + 1, rotation: "-90", originX: RADIUS + 1, originY: RADIUS + 1, strokeLinecap: "round" }))] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: { position: "absolute" }, children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.X, { color: theme.colors.foreground, size: 12 }) })] }));
281
+ }
282
+ function Toast({ open = false, onOpenChange = () => { }, title = "", description, actionLabel = "Action", onAction, duration = 60, index = 0, isLatest = true, variant = "default", render, position = "auto", id, iconLeft: IconLeft, iconRight: IconRight, onToastPress, }) {
283
+ const [isHovered, setIsHovered] = (0, react_1.useState)(false);
284
+ const progress = (0, react_native_reanimated_1.useSharedValue)(1);
285
+ const remainingTime = (0, react_native_reanimated_1.useSharedValue)(duration * 1000);
286
+ const wasOpen = (0, react_1.useRef)(open);
287
+ const [measuredHeight, setMeasuredHeight] = (0, react_1.useState)(100);
288
+ const { theme } = (0, ui_1.useTheme)();
289
+ const isWeb = react_native_1.Platform.OS === "web";
290
+ const { width } = (0, react_native_1.useWindowDimensions)();
291
+ const isDesktop = isWeb && width >= 768;
292
+ const { top, bottom } = (0, react_native_safe_area_context_1.useSafeAreaInsets)();
293
+ const isTop = position.includes("top");
294
+ const RADIUS = 12;
295
+ const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
296
+ const dismissTranslateY = (0, react_native_reanimated_1.useSharedValue)(0);
297
+ const dismissTranslateX = (0, react_native_reanimated_1.useSharedValue)(0);
298
+ // Auto-select iconLeft based on variant if not provided
299
+ const defaultIconLeft = !IconLeft && !onAction
300
+ ? variant === "success"
301
+ ? lucide_react_native_1.CheckCircle
302
+ : variant === "error"
303
+ ? lucide_react_native_1.XCircle
304
+ : variant === "info"
305
+ ? lucide_react_native_1.Info
306
+ : null
307
+ : null;
308
+ const FinalIconLeft = IconLeft || defaultIconLeft;
309
+ const FinalIconRight = IconRight;
310
+ const animatedCircleProps = (0, react_native_reanimated_1.useAnimatedProps)(() => {
311
+ return {
312
+ strokeDashoffset: CIRCUMFERENCE * (1 - progress.value),
116
313
  };
314
+ });
315
+ const opacity = (0, react_native_reanimated_1.useSharedValue)(0);
316
+ const translateY = (0, react_native_reanimated_1.useSharedValue)(isTop ? -100 : 100);
317
+ const scale = (0, react_native_reanimated_1.useSharedValue)(1 - index * 0.05);
318
+ (0, react_1.useEffect)(() => {
319
+ return toastManager.subscribeHover(setIsHovered);
320
+ }, []);
321
+ (0, react_1.useEffect)(() => {
322
+ if (open && !wasOpen.current) {
323
+ // Toast just opened
324
+ progress.value = 1;
325
+ remainingTime.value = duration * 1000;
326
+ }
327
+ wasOpen.current = open;
328
+ if (!open) {
329
+ // Close animation
330
+ opacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 250 });
331
+ translateY.value = (0, react_native_reanimated_1.withTiming)(isTop ? -100 : 100, { duration: 250 });
332
+ return;
333
+ }
334
+ if (isHovered) {
335
+ // Stack vertically with proper spacing when hovered
336
+ // Calculate cumulative height of all toasts before this one
337
+ let cumulativeHeight = 0;
338
+ const allToasts = toastManager.getToasts().slice(-4).reverse(); // Get visible toasts in same order as rendered
339
+ for (let i = 0; i < index; i++) {
340
+ if (allToasts[i]) {
341
+ cumulativeHeight += toastManager.getToastHeight(allToasts[i].id) + 8; // height + gap
342
+ }
343
+ }
344
+ translateY.value = (0, react_native_reanimated_1.withTiming)(isTop ? cumulativeHeight : -cumulativeHeight, {
345
+ duration: 750,
346
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
347
+ });
348
+ scale.value = (0, react_native_reanimated_1.withTiming)(1, {
349
+ duration: 750,
350
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
351
+ });
352
+ opacity.value = (0, react_native_reanimated_1.withTiming)(1, {
353
+ duration: 750,
354
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
355
+ });
356
+ }
357
+ else {
358
+ // Compact stacked view when not hovered
359
+ translateY.value = (0, react_native_reanimated_1.withTiming)((isTop ? index : -index) * 15, {
360
+ duration: 750,
361
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
362
+ });
363
+ scale.value = (0, react_native_reanimated_1.withTiming)(1 - index * 0.1, {
364
+ duration: 750,
365
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
366
+ });
367
+ opacity.value = (0, react_native_reanimated_1.withTiming)(isLatest ? 1 : 0.95, {
368
+ duration: 750,
369
+ easing: react_native_reanimated_1.Easing.out(react_native_reanimated_1.Easing.exp),
370
+ });
371
+ }
372
+ }, [open, isHovered, index, isLatest, measuredHeight, duration, id]);
117
373
  (0, react_1.useEffect)(() => {
118
- let interval = null;
119
- if (open) {
120
- setSeconds(duration);
121
- react_native_1.Animated.timing(fadeAnim, {
122
- toValue: 1,
123
- duration: 200,
124
- useNativeDriver: true,
125
- }).start();
126
- interval = setInterval(() => {
127
- setSeconds((prev) => {
128
- if (prev <= 1) {
129
- onOpenChange(false);
130
- if (interval)
131
- clearInterval(interval);
132
- return duration;
374
+ if (open && isLatest && duration > 0) {
375
+ if (isHovered) {
376
+ (0, react_native_reanimated_1.cancelAnimation)(progress);
377
+ remainingTime.value = progress.value * duration * 1000;
378
+ }
379
+ else {
380
+ progress.value = (0, react_native_reanimated_1.withTiming)(0, {
381
+ duration: remainingTime.value,
382
+ easing: react_native_reanimated_1.Easing.linear,
383
+ }, (finished) => {
384
+ if (finished) {
385
+ (0, react_native_reanimated_1.runOnJS)(onOpenChange)(false);
133
386
  }
134
- return prev - 1;
135
387
  });
136
- }, 1000);
388
+ }
137
389
  }
138
390
  else {
139
- if (interval)
140
- clearInterval(interval);
141
- react_native_1.Animated.timing(fadeAnim, {
142
- toValue: 0,
143
- duration: 150,
144
- useNativeDriver: true,
145
- }).start();
146
- setSeconds(duration);
391
+ (0, react_native_reanimated_1.cancelAnimation)(progress);
147
392
  }
148
393
  return () => {
149
- if (interval)
150
- clearInterval(interval);
394
+ (0, react_native_reanimated_1.cancelAnimation)(progress);
395
+ };
396
+ }, [open, isLatest, isHovered, duration]);
397
+ const dismissGestureVertical = react_native_gesture_handler_1.Gesture.Pan()
398
+ .enabled(isLatest && !isHovered)
399
+ .activeOffsetY(isTop ? [-10, 999999] : [-999999, 10])
400
+ .onUpdate((event) => {
401
+ const direction = isTop ? -1 : 1;
402
+ const movement = event.translationY * direction;
403
+ if (movement > 0) {
404
+ dismissTranslateY.value = event.translationY;
405
+ dismissTranslateX.value = 0;
406
+ }
407
+ })
408
+ .onEnd((event) => {
409
+ const direction = isTop ? -1 : 1;
410
+ const movement = event.translationY * direction;
411
+ const velocity = event.velocityY * direction;
412
+ if (movement > 50 || velocity > 500) {
413
+ // Dismiss - slide out in expansion direction
414
+ dismissTranslateY.value = (0, react_native_reanimated_1.withTiming)(isTop ? -200 : 200, { duration: 250 }, (finished) => {
415
+ if (finished) {
416
+ (0, react_native_reanimated_1.runOnJS)(onOpenChange)(false);
417
+ }
418
+ });
419
+ opacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 250 });
420
+ }
421
+ else {
422
+ // Spring back
423
+ dismissTranslateY.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 200 });
424
+ }
425
+ });
426
+ const dismissGestureHorizontal = react_native_gesture_handler_1.Gesture.Pan()
427
+ .enabled(isLatest || isHovered)
428
+ .activeOffsetX([-10, 10])
429
+ .onUpdate((event) => {
430
+ if (event.translationX < 0) {
431
+ dismissTranslateX.value = event.translationX;
432
+ dismissTranslateY.value = 0;
433
+ }
434
+ })
435
+ .onEnd((event) => {
436
+ // Check for horizontal swipe left
437
+ if (event.translationX < -100 || event.velocityX < -500) {
438
+ dismissTranslateX.value = (0, react_native_reanimated_1.withTiming)(-400, { duration: 250 }, (finished) => {
439
+ if (finished) {
440
+ (0, react_native_reanimated_1.runOnJS)(onOpenChange)(false);
441
+ }
442
+ });
443
+ opacity.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 250 });
444
+ }
445
+ else {
446
+ // Spring back
447
+ dismissTranslateX.value = (0, react_native_reanimated_1.withTiming)(0, { duration: 200 });
448
+ }
449
+ });
450
+ const dismissGesture = react_native_gesture_handler_1.Gesture.Race(dismissGestureVertical, dismissGestureHorizontal);
451
+ const animatedStyle = (0, react_native_reanimated_1.useAnimatedStyle)(() => {
452
+ return {
453
+ opacity: opacity.value,
454
+ transform: [
455
+ // +22 is to get it just below the header
456
+ {
457
+ translateY: translateY.value +
458
+ dismissTranslateY.value +
459
+ (isTop ? top / 2 : -bottom),
460
+ },
461
+ { translateX: dismissTranslateX.value },
462
+ { scale: scale.value },
463
+ ],
464
+ zIndex: 1000 - index,
151
465
  };
152
- // eslint-disable-next-line
153
- }, [open, duration]);
154
- if (!open)
155
- return null;
156
- return ((0, jsx_runtime_1.jsx)(portal_1.Portal, { name: "toast", children: (0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { style: [styles.container, containerPosition, { opacity: fadeAnim }], pointerEvents: "box-none", children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
466
+ });
467
+ const variantStyles = {
468
+ default: {
469
+ backgroundColor: theme.colors.secondary,
470
+ borderColor: theme.colors.border,
471
+ },
472
+ success: {
473
+ backgroundColor: theme.colors.success,
474
+ borderColor: theme.colors.success,
475
+ },
476
+ error: {
477
+ backgroundColor: theme.colors.destructive,
478
+ borderColor: theme.colors.destructive,
479
+ },
480
+ info: {
481
+ backgroundColor: theme.colors.info,
482
+ borderColor: theme.colors.info,
483
+ },
484
+ };
485
+ const buttonTypeMap = {
486
+ default: "primary",
487
+ success: "success",
488
+ error: "primary",
489
+ info: "secondary",
490
+ };
491
+ return ((0, jsx_runtime_1.jsx)(react_native_reanimated_1.default.View, { onLayout: (l) => {
492
+ const height = l.nativeEvent.layout.height;
493
+ setMeasuredHeight(height);
494
+ if (id) {
495
+ toastManager.updateToastHeight(id, height);
496
+ }
497
+ }, style: [
498
+ isTop ? styles.containerTop : styles.containerBottom,
499
+ animatedStyle,
500
+ ], children: (0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.GestureDetector, { gesture: dismissGesture, children: (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: onToastPress, disabled: !onToastPress, style: [
157
501
  styles.toast,
158
502
  {
159
- backgroundColor: theme.colors.secondary,
160
- borderColor: theme.colors.border,
161
503
  borderRadius: theme.borderRadius.xl,
162
504
  flexDirection: "column",
163
505
  justifyContent: "space-between",
164
506
  alignItems: "center",
165
507
  padding: theme.spacing[4],
166
- width: isDesktop ? "100%" : "95%",
508
+ width: "100%",
167
509
  },
168
- ], children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { gap: theme.spacing[1], width: "100%" }, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [
169
- {
170
- color: theme.colors.foreground,
171
- fontSize: 16,
172
- fontWeight: "500",
173
- },
174
- ], children: title }), description ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [{ color: theme.colors.foreground, fontSize: 14 }], children: description })) : null] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
175
- gap: theme.spacing[1],
176
- flexDirection: "row",
177
- justifyContent: "flex-end",
178
- width: "100%",
179
- }, children: [onAction && ((0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: [
180
- styles.button,
181
- {
182
- borderColor: theme.colors.primary,
183
- paddingHorizontal: theme.spacing[4],
184
- paddingVertical: theme.spacing[2],
185
- },
186
- ], onPress: onAction, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: { color: theme.colors.foreground }, children: actionLabel }) })), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: [
187
- styles.button,
188
- {
189
- borderColor: theme.colors.primary,
190
- paddingHorizontal: theme.spacing[4],
191
- paddingVertical: theme.spacing[2],
192
- },
193
- ], onPress: () => onOpenChange(false), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: { color: theme.colors.foreground }, children: "Close" }) })] })] }) }) }));
510
+ variantStyles[variant],
511
+ ], children: render ? (render({ close: () => onOpenChange(false), action: onAction })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
512
+ flexDirection: "row",
513
+ alignItems: "flex-start",
514
+ justifyContent: "space-between",
515
+ width: "100%",
516
+ gap: theme.spacing[4],
517
+ }, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
518
+ flexDirection: "row",
519
+ flex: 1,
520
+ gap: theme.spacing[3],
521
+ }, children: [FinalIconLeft && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: { paddingTop: 2 }, children: (0, jsx_runtime_1.jsx)(FinalIconLeft, { color: theme.colors.foreground }) })), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: { flex: 1 }, children: [(0, jsx_runtime_1.jsx)(text_1.Text, { size: "lg", children: title }), description ? (0, jsx_runtime_1.jsx)(text_1.Text, { children: description }) : null] })] }), FinalIconRight && !onAction ? ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
522
+ gap: theme.spacing[2],
523
+ height: "100%",
524
+ justifyContent: "space-between",
525
+ }, children: [(0, jsx_runtime_1.jsx)(react_native_1.Pressable, { onPress: onToastPress, children: (0, jsx_runtime_1.jsx)(FinalIconRight, { color: theme.colors.foreground }) }), (0, jsx_runtime_1.jsx)(CloseButton, { onPress: () => onOpenChange(false), isLatest: isLatest, duration: duration, animatedCircleProps: animatedCircleProps, theme: theme, RADIUS: RADIUS, CIRCUMFERENCE: CIRCUMFERENCE })] })) : !onAction ? ((0, jsx_runtime_1.jsx)(CloseButton, { onPress: () => onOpenChange(false), isLatest: isLatest, duration: duration, animatedCircleProps: animatedCircleProps, theme: theme, RADIUS: RADIUS, CIRCUMFERENCE: CIRCUMFERENCE })) : null] }), onAction && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: {
526
+ gap: theme.spacing[1],
527
+ flexDirection: "row",
528
+ justifyContent: "flex-end",
529
+ width: "100%",
530
+ }, children: [(0, jsx_runtime_1.jsx)(button_1.Button, { variant: buttonTypeMap[variant], onPress: onAction, children: (0, jsx_runtime_1.jsx)(text_1.Text, { style: { color: theme.colors.foreground }, children: actionLabel }) }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "secondary", onPress: () => onOpenChange(false), children: (0, jsx_runtime_1.jsx)(text_1.Text, { style: { color: theme.colors.foreground }, children: "Close" }) })] }))] })) }) }) }));
194
531
  }
195
532
  const styles = react_native_1.StyleSheet.create({
196
- container: {
533
+ providerContainer: {
197
534
  position: "absolute",
198
535
  zIndex: 1000,
199
- paddingHorizontal: 16,
536
+ },
537
+ containerBottom: {
538
+ position: "absolute",
539
+ bottom: 0,
540
+ left: 0,
541
+ right: 0,
542
+ alignItems: "center",
543
+ },
544
+ containerTop: {
545
+ position: "absolute",
546
+ top: 0,
547
+ left: 0,
548
+ right: 0,
549
+ alignItems: "center",
200
550
  },
201
551
  toast: {
202
- opacity: 0.95,
552
+ opacity: 0.99,
203
553
  borderWidth: 1,
204
554
  gap: 8,
205
555
  },