jfs-components 0.0.42 → 0.0.43

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.
@@ -0,0 +1,14 @@
1
+ import { type StyleProp, type ViewStyle } from 'react-native';
2
+ import { type ToastPlacement } from './useToast';
3
+ export interface ToastProps {
4
+ id: string;
5
+ title: string;
6
+ timeout?: number | undefined;
7
+ onClose?: (() => void) | undefined;
8
+ modes?: Record<string, any> | undefined;
9
+ placement?: ToastPlacement | undefined;
10
+ style?: StyleProp<ViewStyle> | undefined;
11
+ }
12
+ declare function Toast({ id, title, timeout, onClose, modes, placement, style, }: ToastProps): import("react/jsx-runtime").JSX.Element;
13
+ export default Toast;
14
+ //# sourceMappingURL=Toast.d.ts.map
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { type ToastPlacement } from './useToast';
3
+ export interface ToastProviderProps {
4
+ children: React.ReactNode;
5
+ maxVisibleToasts?: number;
6
+ placement?: ToastPlacement;
7
+ modes?: Record<string, any>;
8
+ }
9
+ declare function ToastProvider({ children, maxVisibleToasts, placement, modes, }: ToastProviderProps): import("react/jsx-runtime").JSX.Element;
10
+ export default ToastProvider;
11
+ //# sourceMappingURL=ToastProvider.d.ts.map
@@ -0,0 +1,24 @@
1
+ export type ToastPlacement = 'top' | 'bottom';
2
+ export interface ToastOptions {
3
+ title: string;
4
+ timeout?: number;
5
+ onClose?: () => void;
6
+ modes?: Record<string, any>;
7
+ }
8
+ export interface ToastEntry {
9
+ id: string;
10
+ title: string;
11
+ timeout: number;
12
+ onClose?: (() => void) | undefined;
13
+ modes?: Record<string, any> | undefined;
14
+ }
15
+ export declare function addToast(options: ToastOptions): string;
16
+ export declare function closeToast(id: string): void;
17
+ export declare function closeAll(): void;
18
+ export declare function useToast(): {
19
+ toasts: ToastEntry[];
20
+ addToast: typeof addToast;
21
+ closeToast: typeof closeToast;
22
+ closeAll: typeof closeAll;
23
+ };
24
+ //# sourceMappingURL=useToast.d.ts.map
@@ -50,4 +50,7 @@ export { default as InputSearch, type InputSearchProps } from './InputSearch/Inp
50
50
  export { default as SupportText, type SupportTextProps } from './SupportText/SupportText';
51
51
  export { default as SupportTextIcon, type SupportTextIconProps } from './SupportText/SupportTextIcon';
52
52
  export { default as RadioButton, type RadioButtonProps } from './RadioButton/RadioButton';
53
+ export { default as Toast, type ToastProps } from './Toast/Toast';
54
+ export { default as ToastProvider, type ToastProviderProps } from './Toast/ToastProvider';
55
+ export { useToast, addToast, closeToast, closeAll, type ToastOptions, type ToastEntry, type ToastPlacement } from './Toast/useToast';
53
56
  //# sourceMappingURL=index.d.ts.map
@@ -4,7 +4,7 @@
4
4
  * Auto-generated from SVG files in src/icons/
5
5
  * DO NOT EDIT MANUALLY - Run "npm run icons:generate" to regenerate
6
6
  *
7
- * Generated: 2026-02-18T13:27:13.799Z
7
+ * Generated: 2026-02-19T12:53:50.558Z
8
8
  */
9
9
  export declare const iconRegistry: Record<string, {
10
10
  path: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jfs-components",
3
- "version": "0.0.42",
3
+ "version": "0.0.43",
4
4
  "description": "React Native Jio Finance Components Library",
5
5
  "author": "sunshuaiqi@gmail.com",
6
6
  "license": "MIT",
@@ -0,0 +1,105 @@
1
+ import React, { useEffect, useRef } from 'react'
2
+ import { StyleSheet, Text, type StyleProp, type ViewStyle } from 'react-native'
3
+ import Animated, { FadeIn, FadeOut, SlideInDown, SlideInUp } from 'react-native-reanimated'
4
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
5
+ import { closeToast, type ToastPlacement } from './useToast'
6
+
7
+ export interface ToastProps {
8
+ id: string
9
+ title: string
10
+ timeout?: number | undefined
11
+ onClose?: (() => void) | undefined
12
+ modes?: Record<string, any> | undefined
13
+ placement?: ToastPlacement | undefined
14
+ style?: StyleProp<ViewStyle> | undefined
15
+ }
16
+
17
+ const ANIMATION_DURATION = 250
18
+
19
+ function Toast({
20
+ id,
21
+ title,
22
+ timeout = 4000,
23
+ onClose,
24
+ modes = {},
25
+ placement = 'bottom',
26
+ style,
27
+ }: ToastProps) {
28
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
29
+
30
+ useEffect(() => {
31
+ if (timeout <= 0) return
32
+ timerRef.current = setTimeout(() => closeToast(id), timeout)
33
+ return () => {
34
+ if (timerRef.current) clearTimeout(timerRef.current)
35
+ }
36
+ }, [id, timeout])
37
+
38
+ const backgroundColor = getVariableByName('toast/background', modes) || '#303338'
39
+ const foreground = getVariableByName('toast/foreground', modes) || '#ffffff'
40
+ const fontSize = getVariableByName('toast/fontSize', modes) || 14
41
+ const fontFamily = getVariableByName('toast/fontFamily', modes) || undefined
42
+ const fontWeight = getVariableByName('toast/fontWeight', modes) || '500'
43
+ const lineHeight = getVariableByName('toast/lineHeight', modes) || 18
44
+ const radius = getVariableByName('toast/radius', modes) || 14
45
+ const paddingHorizontal = getVariableByName('toast/padding/horizontal', modes) || 16
46
+ const paddingVertical = getVariableByName('toast/padding/vertical', modes) || 14
47
+ const gap = getVariableByName('toast/gap', modes) || 8
48
+ const borderWidth = getVariableByName('toast/border/size', modes) || 1
49
+ const borderColor = getVariableByName('toast/border/color', modes) || 'rgba(255,255,255,0.1)'
50
+ const shadowBlurPrimary = getVariableByName('toast/shadow/primary/blur', modes) || 48
51
+ const shadowOffsetYPrimary = getVariableByName('toast/shadow/primary/offsetY', modes) || 16
52
+ const shadowColorPrimary = getVariableByName('toast/shadow/primary/color', modes) || 'rgba(13,13,15,0.16)'
53
+
54
+ const enterAnimation = placement === 'top'
55
+ ? SlideInUp.duration(ANIMATION_DURATION)
56
+ : SlideInDown.duration(ANIMATION_DURATION)
57
+
58
+ const containerStyle: ViewStyle = {
59
+ backgroundColor,
60
+ borderRadius: radius,
61
+ paddingHorizontal,
62
+ paddingVertical,
63
+ gap,
64
+ borderWidth,
65
+ borderColor,
66
+ shadowColor: shadowColorPrimary,
67
+ shadowOffset: { width: 0, height: shadowOffsetYPrimary },
68
+ shadowOpacity: 1,
69
+ shadowRadius: shadowBlurPrimary / 2,
70
+ elevation: 8,
71
+ }
72
+
73
+ const textStyle = {
74
+ color: foreground,
75
+ fontSize,
76
+ fontFamily,
77
+ fontWeight: String(fontWeight) as any,
78
+ lineHeight,
79
+ }
80
+
81
+ return (
82
+ <Animated.View
83
+ entering={enterAnimation}
84
+ exiting={FadeOut.duration(ANIMATION_DURATION)}
85
+ style={[styles.toast, containerStyle, style]}
86
+ accessibilityRole="alert"
87
+ accessibilityLiveRegion="assertive"
88
+ >
89
+ <Text style={textStyle} numberOfLines={2}>
90
+ {title}
91
+ </Text>
92
+ </Animated.View>
93
+ )
94
+ }
95
+
96
+ const styles = StyleSheet.create({
97
+ toast: {
98
+ alignSelf: 'center',
99
+ maxWidth: '90%',
100
+ minWidth: 200,
101
+ overflow: 'hidden',
102
+ },
103
+ })
104
+
105
+ export default Toast
@@ -0,0 +1,75 @@
1
+ import React, { useMemo } from 'react'
2
+ import { StyleSheet, View } from 'react-native'
3
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
4
+ import { useToast, type ToastPlacement } from './useToast'
5
+ import Toast from './Toast'
6
+
7
+ export interface ToastProviderProps {
8
+ children: React.ReactNode
9
+ maxVisibleToasts?: number
10
+ placement?: ToastPlacement
11
+ modes?: Record<string, any>
12
+ }
13
+
14
+ function ToastProvider({
15
+ children,
16
+ maxVisibleToasts = 3,
17
+ placement = 'bottom',
18
+ modes,
19
+ }: ToastProviderProps) {
20
+ const { toasts } = useToast()
21
+ const insets = useSafeAreaInsets()
22
+
23
+ const visibleToasts = useMemo(
24
+ () => toasts.slice(-maxVisibleToasts),
25
+ [toasts, maxVisibleToasts],
26
+ )
27
+
28
+ const regionStyle = useMemo(
29
+ () => [
30
+ styles.region,
31
+ placement === 'top'
32
+ ? { top: insets.top + 8 }
33
+ : { bottom: insets.bottom + 8 },
34
+ ],
35
+ [placement, insets.top, insets.bottom],
36
+ )
37
+
38
+ return (
39
+ <View style={styles.container}>
40
+ {children}
41
+ {visibleToasts.length > 0 && (
42
+ <View style={regionStyle} pointerEvents="box-none">
43
+ {visibleToasts.map((entry) => (
44
+ <Toast
45
+ key={entry.id}
46
+ id={entry.id}
47
+ title={entry.title}
48
+ timeout={entry.timeout}
49
+ onClose={entry.onClose}
50
+ modes={entry.modes ?? modes}
51
+ placement={placement}
52
+ />
53
+ ))}
54
+ </View>
55
+ )}
56
+ </View>
57
+ )
58
+ }
59
+
60
+ const styles = StyleSheet.create({
61
+ container: {
62
+ flex: 1,
63
+ },
64
+ region: {
65
+ position: 'absolute',
66
+ left: 0,
67
+ right: 0,
68
+ alignItems: 'center',
69
+ gap: 8,
70
+ zIndex: 9999,
71
+ pointerEvents: 'box-none',
72
+ },
73
+ })
74
+
75
+ export default ToastProvider
@@ -0,0 +1,80 @@
1
+ import { useCallback, useSyncExternalStore } from 'react'
2
+
3
+ export type ToastPlacement = 'top' | 'bottom'
4
+
5
+ export interface ToastOptions {
6
+ title: string
7
+ timeout?: number
8
+ onClose?: () => void
9
+ modes?: Record<string, any>
10
+ }
11
+
12
+ export interface ToastEntry {
13
+ id: string
14
+ title: string
15
+ timeout: number
16
+ onClose?: (() => void) | undefined
17
+ modes?: Record<string, any> | undefined
18
+ }
19
+
20
+ type Listener = () => void
21
+
22
+ let idCounter = 0
23
+ let toasts: ToastEntry[] = []
24
+ const listeners = new Set<Listener>()
25
+
26
+ function emit() {
27
+ for (const listener of listeners) {
28
+ listener()
29
+ }
30
+ }
31
+
32
+ function getSnapshot(): ToastEntry[] {
33
+ return toasts
34
+ }
35
+
36
+ function subscribe(listener: Listener): () => void {
37
+ listeners.add(listener)
38
+ return () => {
39
+ listeners.delete(listener)
40
+ }
41
+ }
42
+
43
+ export function addToast(options: ToastOptions): string {
44
+ const id = `toast-${++idCounter}`
45
+ const entry: ToastEntry = {
46
+ id,
47
+ title: options.title,
48
+ timeout: options.timeout ?? 4000,
49
+ onClose: options.onClose,
50
+ modes: options.modes,
51
+ }
52
+ toasts = [...toasts, entry]
53
+ emit()
54
+ return id
55
+ }
56
+
57
+ export function closeToast(id: string): void {
58
+ const entry = toasts.find((t) => t.id === id)
59
+ toasts = toasts.filter((t) => t.id !== id)
60
+ emit()
61
+ entry?.onClose?.()
62
+ }
63
+
64
+ export function closeAll(): void {
65
+ const prev = toasts
66
+ toasts = []
67
+ emit()
68
+ prev.forEach((t) => t.onClose?.())
69
+ }
70
+
71
+ export function useToast() {
72
+ const queue = useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
73
+
74
+ return {
75
+ toasts: queue,
76
+ addToast: useCallback(addToast, []),
77
+ closeToast: useCallback(closeToast, []),
78
+ closeAll: useCallback(closeAll, []),
79
+ }
80
+ }
@@ -51,3 +51,6 @@ export { default as InputSearch, type InputSearchProps } from './InputSearch/Inp
51
51
  export { default as SupportText, type SupportTextProps } from './SupportText/SupportText';
52
52
  export { default as SupportTextIcon, type SupportTextIconProps } from './SupportText/SupportTextIcon';
53
53
  export { default as RadioButton, type RadioButtonProps } from './RadioButton/RadioButton';
54
+ export { default as Toast, type ToastProps } from './Toast/Toast';
55
+ export { default as ToastProvider, type ToastProviderProps } from './Toast/ToastProvider';
56
+ export { useToast, addToast, closeToast, closeAll, type ToastOptions, type ToastEntry, type ToastPlacement } from './Toast/useToast';