@praxiis/ui 0.0.1
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.
- package/dist/index.d.mts +52556 -0
- package/dist/index.d.ts +52556 -0
- package/dist/index.js +8753 -0
- package/dist/index.mjs +8777 -0
- package/package.json +70 -0
- package/src/__test-utils__/index.tsx +39 -0
- package/src/components/CalendarStrip/CalendarStrip.helpers.ts +106 -0
- package/src/components/CalendarStrip/CalendarStrip.tsx +83 -0
- package/src/components/CalendarStrip/CalendarStrip.types.ts +133 -0
- package/src/components/CalendarStrip/DayCard/DayCard.helpers.ts +44 -0
- package/src/components/CalendarStrip/DayCard/DayCard.tsx +71 -0
- package/src/components/CalendarStrip/DayCard/DayCard.types.ts +134 -0
- package/src/components/CalendarStrip/DayCard/index.ts +2 -0
- package/src/components/CalendarStrip/DayCard/useDayCardLogic.ts +45 -0
- package/src/components/CalendarStrip/index.ts +9 -0
- package/src/components/CalendarStrip/useCalendarStripLogic.ts +53 -0
- package/src/components/EmptyState/EmptyState.helpers.ts +104 -0
- package/src/components/EmptyState/EmptyState.tsx +205 -0
- package/src/components/EmptyState/EmptyState.types.ts +213 -0
- package/src/components/EmptyState/index.ts +44 -0
- package/src/components/EmptyState/useEmptyStateLogic.ts +131 -0
- package/src/components/Header/Header.helpers.ts +93 -0
- package/src/components/Header/Header.tsx +185 -0
- package/src/components/Header/Header.types.ts +153 -0
- package/src/components/Header/index.ts +44 -0
- package/src/components/Header/useHeaderLogic.ts +146 -0
- package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.helpers.ts +50 -0
- package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.tsx +78 -0
- package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.types.ts +99 -0
- package/src/components/ScheduleItem/ScheduleItem/index.ts +16 -0
- package/src/components/ScheduleItem/ScheduleItem/useScheduleItemLogic.ts +31 -0
- package/src/components/ScheduleItem/index.ts +15 -0
- package/src/components/index.ts +40 -0
- package/src/core/index.ts +34 -0
- package/src/core/restyle/RestyleThemeProviderWrapper.tsx +31 -0
- package/src/core/restyle/index.ts +38 -0
- package/src/core/restyle/restylePresetRegistry.ts +195 -0
- package/src/core/restyle/restyleTheme.ts +1352 -0
- package/src/core/restyle/restyleTypes.ts +8 -0
- package/src/core/restyle/useRestyleTheme.ts +10 -0
- package/src/hooks/animations/index.ts +3 -0
- package/src/hooks/animations/useAnimatedValue.ts +10 -0
- package/src/hooks/animations/useEntranceAnimation.ts +106 -0
- package/src/hooks/animations/usePulseAnimation.ts +63 -0
- package/src/hooks/index.ts +30 -0
- package/src/hooks/useReducedMotion.ts +60 -0
- package/src/i18n/index.ts +2 -0
- package/src/i18n/labels/en.ts +120 -0
- package/src/i18n/labels/es.ts +120 -0
- package/src/i18n/labels/index.ts +6 -0
- package/src/i18n/labels/types.ts +165 -0
- package/src/index.tsx +215 -0
- package/src/primitives/actions/Button/Button.helpers.ts +243 -0
- package/src/primitives/actions/Button/Button.tsx +198 -0
- package/src/primitives/actions/Button/Button.types.ts +207 -0
- package/src/primitives/actions/Button/index.ts +41 -0
- package/src/primitives/actions/Button/useButtonLogic.ts +160 -0
- package/src/primitives/actions/IconButton/IconButton.helpers.ts +235 -0
- package/src/primitives/actions/IconButton/IconButton.tsx +177 -0
- package/src/primitives/actions/IconButton/IconButton.types.ts +273 -0
- package/src/primitives/actions/IconButton/index.ts +30 -0
- package/src/primitives/actions/IconButton/useIconButtonLogic.ts +172 -0
- package/src/primitives/actions/index.ts +20 -0
- package/src/primitives/content/Avatar/Avatar.helpers.ts +177 -0
- package/src/primitives/content/Avatar/Avatar.tsx +199 -0
- package/src/primitives/content/Avatar/Avatar.types.ts +222 -0
- package/src/primitives/content/Avatar/index.ts +46 -0
- package/src/primitives/content/Avatar/useAvatarLogic.ts +149 -0
- package/src/primitives/content/Badge/Badge.helpers.ts +175 -0
- package/src/primitives/content/Badge/Badge.tsx +174 -0
- package/src/primitives/content/Badge/Badge.types.ts +223 -0
- package/src/primitives/content/Badge/index.ts +40 -0
- package/src/primitives/content/Badge/useBadgeLogic.ts +128 -0
- package/src/primitives/content/Card/Card.helpers.ts +27 -0
- package/src/primitives/content/Card/Card.tsx +123 -0
- package/src/primitives/content/Card/Card.types.ts +95 -0
- package/src/primitives/content/Card/index.ts +20 -0
- package/src/primitives/content/Card/useCardLogic.ts +48 -0
- package/src/primitives/content/Chip/Chip.helpers.ts +304 -0
- package/src/primitives/content/Chip/Chip.tsx +205 -0
- package/src/primitives/content/Chip/Chip.types.ts +234 -0
- package/src/primitives/content/Chip/index.ts +47 -0
- package/src/primitives/content/Chip/useChipLogic.ts +167 -0
- package/src/primitives/content/Icon/Icon.helpers.ts +54 -0
- package/src/primitives/content/Icon/Icon.tsx +110 -0
- package/src/primitives/content/Icon/Icon.types.ts +95 -0
- package/src/primitives/content/Icon/index.ts +20 -0
- package/src/primitives/content/Icon/useIconLogic.ts +73 -0
- package/src/primitives/content/index.ts +45 -0
- package/src/primitives/feedback/ProgressBar/ProgressBar.helpers.ts +122 -0
- package/src/primitives/feedback/ProgressBar/ProgressBar.tsx +154 -0
- package/src/primitives/feedback/ProgressBar/ProgressBar.types.ts +178 -0
- package/src/primitives/feedback/ProgressBar/index.ts +17 -0
- package/src/primitives/feedback/ProgressBar/useProgressBarLogic.ts +120 -0
- package/src/primitives/feedback/Skeleton/Skeleton.helpers.ts +145 -0
- package/src/primitives/feedback/Skeleton/Skeleton.tsx +155 -0
- package/src/primitives/feedback/Skeleton/Skeleton.types.ts +223 -0
- package/src/primitives/feedback/Skeleton/index.ts +44 -0
- package/src/primitives/feedback/Skeleton/useSkeletonLogic.ts +125 -0
- package/src/primitives/feedback/Spinner/Spinner.helpers.ts +40 -0
- package/src/primitives/feedback/Spinner/Spinner.tsx +105 -0
- package/src/primitives/feedback/Spinner/Spinner.types.ts +114 -0
- package/src/primitives/feedback/Spinner/index.ts +18 -0
- package/src/primitives/feedback/Spinner/useSpinnerLogic.ts +84 -0
- package/src/primitives/feedback/Toast/Toast.helpers.ts +163 -0
- package/src/primitives/feedback/Toast/Toast.tsx +190 -0
- package/src/primitives/feedback/Toast/Toast.types.ts +270 -0
- package/src/primitives/feedback/Toast/ToastContext.tsx +96 -0
- package/src/primitives/feedback/Toast/ToastProvider.tsx +241 -0
- package/src/primitives/feedback/Toast/index.ts +59 -0
- package/src/primitives/feedback/Toast/useToastLogic.ts +112 -0
- package/src/primitives/feedback/index.ts +45 -0
- package/src/primitives/index.ts +158 -0
- package/src/primitives/inputs/Checkbox/Checkbox.helpers.ts +132 -0
- package/src/primitives/inputs/Checkbox/Checkbox.tsx +150 -0
- package/src/primitives/inputs/Checkbox/Checkbox.types.ts +106 -0
- package/src/primitives/inputs/Checkbox/index.ts +30 -0
- package/src/primitives/inputs/Checkbox/useCheckboxLogic.ts +121 -0
- package/src/primitives/inputs/RadioButton/RadioButton.helpers.ts +123 -0
- package/src/primitives/inputs/RadioButton/RadioButton.tsx +159 -0
- package/src/primitives/inputs/RadioButton/RadioButton.types.ts +106 -0
- package/src/primitives/inputs/RadioButton/index.ts +25 -0
- package/src/primitives/inputs/RadioButton/useRadioButtonLogic.ts +117 -0
- package/src/primitives/inputs/SegmentedControl/SegmentedControl.helpers.ts +174 -0
- package/src/primitives/inputs/SegmentedControl/SegmentedControl.tsx +224 -0
- package/src/primitives/inputs/SegmentedControl/SegmentedControl.types.ts +187 -0
- package/src/primitives/inputs/SegmentedControl/index.ts +39 -0
- package/src/primitives/inputs/SegmentedControl/useSegmentedControlLogic.ts +151 -0
- package/src/primitives/inputs/SelectSheet/SelectSheet.helpers.ts +147 -0
- package/src/primitives/inputs/SelectSheet/SelectSheet.tsx +247 -0
- package/src/primitives/inputs/SelectSheet/SelectSheet.types.ts +196 -0
- package/src/primitives/inputs/SelectSheet/SelectSheetOption.tsx +177 -0
- package/src/primitives/inputs/SelectSheet/index.ts +48 -0
- package/src/primitives/inputs/SelectSheet/useSelectSheetLogic.ts +309 -0
- package/src/primitives/inputs/Switch/Switch.helpers.ts +109 -0
- package/src/primitives/inputs/Switch/Switch.tsx +191 -0
- package/src/primitives/inputs/Switch/Switch.types.ts +154 -0
- package/src/primitives/inputs/Switch/index.ts +40 -0
- package/src/primitives/inputs/Switch/useSwitchLogic.ts +192 -0
- package/src/primitives/inputs/TextInput/TextInput.helpers.ts +206 -0
- package/src/primitives/inputs/TextInput/TextInput.tsx +392 -0
- package/src/primitives/inputs/TextInput/TextInput.types.ts +216 -0
- package/src/primitives/inputs/TextInput/index.ts +37 -0
- package/src/primitives/inputs/TextInput/useTextInputLogic.ts +195 -0
- package/src/primitives/inputs/index.ts +52 -0
- package/src/primitives/layout/AnimatedBox.tsx +44 -0
- package/src/primitives/layout/Box.tsx +71 -0
- package/src/primitives/layout/Divider/Divider.helpers.ts +115 -0
- package/src/primitives/layout/Divider/Divider.tsx +139 -0
- package/src/primitives/layout/Divider/Divider.types.ts +178 -0
- package/src/primitives/layout/Divider/index.ts +24 -0
- package/src/primitives/layout/Divider/useDividerLogic.ts +109 -0
- package/src/primitives/layout/FlatList.tsx +66 -0
- package/src/primitives/layout/Pressable.tsx +74 -0
- package/src/primitives/layout/ScrollView.tsx +63 -0
- package/src/primitives/layout/Stack.tsx +69 -0
- package/src/primitives/layout/index.ts +40 -0
- package/src/primitives/navigation/index.ts +6 -0
- package/src/primitives/overlays/Modal/Modal.helpers.ts +31 -0
- package/src/primitives/overlays/Modal/Modal.tsx +264 -0
- package/src/primitives/overlays/Modal/Modal.types.ts +193 -0
- package/src/primitives/overlays/Modal/index.ts +43 -0
- package/src/primitives/overlays/Modal/useModalLogic.ts +103 -0
- package/src/primitives/overlays/index.ts +12 -0
- package/src/primitives/typography/Text.tsx +51 -0
- package/src/primitives/typography/index.ts +1 -0
- package/src/provider/DesignSystemContext.ts +22 -0
- package/src/provider/DesignSystemProvider.tsx +121 -0
- package/src/provider/index.ts +7 -0
- package/src/providers/ThemeProvider/createTheme.ts +304 -0
- package/src/providers/ThemeProvider/defaultTheme.ts +70 -0
- package/src/providers/ThemeProvider/index.ts +34 -0
- package/src/providers/ThemeProvider/types.ts +249 -0
- package/src/providers/index.ts +29 -0
- package/src/tokens/colors.ts +371 -0
- package/src/tokens/index.ts +145 -0
- package/src/tokens/motion.ts +176 -0
- package/src/tokens/radii.ts +82 -0
- package/src/tokens/scales.ts +588 -0
- package/src/tokens/shadows.ts +190 -0
- package/src/tokens/spacing.ts +140 -0
- package/src/tokens/tokens.json +207 -0
- package/src/tokens/typography.ts +251 -0
- package/src/types.ts +50 -0
- package/src/utils/accessibility.ts +169 -0
- package/src/utils/index.ts +25 -0
- package/src/utils/platform.ts +72 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skeleton Logic Hook
|
|
3
|
+
*
|
|
4
|
+
* Extracts all computational logic from the Skeleton component.
|
|
5
|
+
* Returns memoized values for optimal render performance.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Resolve responsive size prop
|
|
9
|
+
* - Calculate dimensions based on variant and size
|
|
10
|
+
* - Manage pulse animation
|
|
11
|
+
* - Provide accessibility props
|
|
12
|
+
*
|
|
13
|
+
* @see Skeleton.tsx - Component consuming this hook
|
|
14
|
+
* @see Skeleton.helpers.ts - Pure calculation functions
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useResponsiveProp } from "@shopify/restyle";
|
|
18
|
+
import { useMemo } from "react";
|
|
19
|
+
import { usePulseAnimation } from "../../../hooks/animations";
|
|
20
|
+
import { getSkeletonA11y } from "./Skeleton.a11y";
|
|
21
|
+
import {
|
|
22
|
+
getSkeletonBorderRadius,
|
|
23
|
+
getSkeletonDimensions,
|
|
24
|
+
SKELETON_ANIMATION,
|
|
25
|
+
} from "./Skeleton.helpers";
|
|
26
|
+
import { SkeletonSize, SkeletonVariant, UseSkeletonLogicParams, UseSkeletonLogicReturn } from "./Skeleton.types";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Orchestrates Skeleton rendering logic and state management.
|
|
30
|
+
*
|
|
31
|
+
* Handles:
|
|
32
|
+
* - Responsive size and variant resolution
|
|
33
|
+
* - Dimension calculation (width, height) with custom overrides
|
|
34
|
+
* - Border radius determination per variant
|
|
35
|
+
* - Pulse animation management (looping opacity)
|
|
36
|
+
* - Reduced motion support (disables animation)
|
|
37
|
+
* - Accessibility props generation
|
|
38
|
+
*
|
|
39
|
+
* @param params - Configuration object for skeleton behavior
|
|
40
|
+
* @param params.variant - Shape variant ('text' | 'circular' | 'rectangular')
|
|
41
|
+
* @param params.size - Size variant ('sm' | 'md' | 'lg')
|
|
42
|
+
* @param params.width - Custom width override (responsive)
|
|
43
|
+
* @param params.height - Custom height override (responsive)
|
|
44
|
+
* @param params.animation - Animation type ('pulse' | 'wave' | 'none')
|
|
45
|
+
* @param params.color - Theme color token for skeleton
|
|
46
|
+
* @param params.duration - Animation duration in ms (default: 1500)
|
|
47
|
+
*
|
|
48
|
+
* @returns Computed values for rendering Skeleton
|
|
49
|
+
* @returns {number} resolvedWidth - Computed width in pixels
|
|
50
|
+
* @returns {number} resolvedHeight - Computed height in pixels
|
|
51
|
+
* @returns {number} borderRadius - Border radius in pixels
|
|
52
|
+
* @returns {Animated.Value} animatedOpacity - Animated opacity (0.3-1.0)
|
|
53
|
+
* @returns {string} backgroundColor - Background color token
|
|
54
|
+
* @returns {object} a11yProps - Accessibility props
|
|
55
|
+
*
|
|
56
|
+
* @performance
|
|
57
|
+
* - Uses native driver for smooth 60fps animations
|
|
58
|
+
* - Respects reduced motion preferences for accessibility
|
|
59
|
+
* - Animation loop runs independently per component
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* const {
|
|
63
|
+
* resolvedWidth,
|
|
64
|
+
* resolvedHeight,
|
|
65
|
+
* borderRadius,
|
|
66
|
+
* animatedOpacity,
|
|
67
|
+
* backgroundColor,
|
|
68
|
+
* } = useSkeletonLogic({
|
|
69
|
+
* variant: "text",
|
|
70
|
+
* size: "md",
|
|
71
|
+
* animation: "pulse",
|
|
72
|
+
* color: "specialSkeleton",
|
|
73
|
+
* duration: 1500,
|
|
74
|
+
* });
|
|
75
|
+
*/
|
|
76
|
+
export function useSkeletonLogic({
|
|
77
|
+
variant,
|
|
78
|
+
size,
|
|
79
|
+
width: customWidth,
|
|
80
|
+
height: customHeight,
|
|
81
|
+
animation,
|
|
82
|
+
color,
|
|
83
|
+
duration,
|
|
84
|
+
}: UseSkeletonLogicParams): UseSkeletonLogicReturn {
|
|
85
|
+
// Resolve responsive size
|
|
86
|
+
const resolvedSize = (useResponsiveProp(size) ?? "md") as SkeletonSize;
|
|
87
|
+
const resolvedVariant = (useResponsiveProp(variant) ?? "text") as SkeletonVariant;
|
|
88
|
+
const resolvedCustomWidth = useResponsiveProp(customWidth);
|
|
89
|
+
const resolvedCustomHeight = useResponsiveProp(customHeight);
|
|
90
|
+
|
|
91
|
+
// Calculate dimensions
|
|
92
|
+
const { width: resolvedWidth, height: resolvedHeight } = useMemo(
|
|
93
|
+
() =>
|
|
94
|
+
getSkeletonDimensions({
|
|
95
|
+
variant: resolvedVariant,
|
|
96
|
+
size: resolvedSize,
|
|
97
|
+
customWidth: resolvedCustomWidth,
|
|
98
|
+
customHeight: resolvedCustomHeight,
|
|
99
|
+
}),
|
|
100
|
+
[resolvedVariant, resolvedSize, resolvedCustomWidth, resolvedCustomHeight]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Get border radius for variant
|
|
104
|
+
const borderRadius = getSkeletonBorderRadius(resolvedVariant);
|
|
105
|
+
|
|
106
|
+
// Pulse animation (respects reduced motion via usePulseAnimation)
|
|
107
|
+
const animatedOpacity = usePulseAnimation({
|
|
108
|
+
duration,
|
|
109
|
+
minOpacity: SKELETON_ANIMATION.MIN_OPACITY,
|
|
110
|
+
maxOpacity: SKELETON_ANIMATION.MAX_OPACITY,
|
|
111
|
+
enabled: animation === "pulse",
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Accessibility props
|
|
115
|
+
const a11yProps = getSkeletonA11y({});
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
resolvedWidth,
|
|
119
|
+
resolvedHeight,
|
|
120
|
+
borderRadius,
|
|
121
|
+
animatedOpacity,
|
|
122
|
+
backgroundColor: color,
|
|
123
|
+
a11yProps,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Component Helpers
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for mapping Spinner props to ActivityIndicator values.
|
|
5
|
+
*
|
|
6
|
+
* ## Size Mapping
|
|
7
|
+
* | Spinner Size | ActivityIndicator | Visual Size |
|
|
8
|
+
* |--------------|-------------------|-------------|
|
|
9
|
+
* | sm | "small" | ~20px |
|
|
10
|
+
* | lg | "large" | ~36px |
|
|
11
|
+
*
|
|
12
|
+
* Note: Exact pixel sizes vary by platform (iOS vs Android).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { SpinnerSize } from "./Spinner.types";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Maps design system size tokens to ActivityIndicator size prop.
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
const SPINNER_ACTIVITY_INDICATOR_SIZES: Record<SpinnerSize, "small" | "large"> = {
|
|
22
|
+
sm: "small",
|
|
23
|
+
lg: "large",
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get ActivityIndicator size from Spinner size token.
|
|
28
|
+
*
|
|
29
|
+
* @param size - The spinner size variant (sm | lg)
|
|
30
|
+
* @returns The corresponding ActivityIndicator size
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* getSpinnerActivityIndicatorSize("sm") // "small"
|
|
34
|
+
* getSpinnerActivityIndicatorSize("lg") // "large"
|
|
35
|
+
*/
|
|
36
|
+
export function getSpinnerActivityIndicatorSize(
|
|
37
|
+
size: SpinnerSize
|
|
38
|
+
): "small" | "large" {
|
|
39
|
+
return SPINNER_ACTIVITY_INDICATOR_SIZES[size] || SPINNER_ACTIVITY_INDICATOR_SIZES.sm;
|
|
40
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Component
|
|
3
|
+
*
|
|
4
|
+
* @description Loading indicator with theme integration - Atom
|
|
5
|
+
*
|
|
6
|
+
* Spinners provide visual feedback during asynchronous operations.
|
|
7
|
+
* Uses React Native's ActivityIndicator with theme-aware colors.
|
|
8
|
+
*
|
|
9
|
+
* ## Sizes
|
|
10
|
+
* | Size | Platform Mapping | Use Case |
|
|
11
|
+
* |------|------------------|----------|
|
|
12
|
+
* | sm | "small" | Inline loading, buttons |
|
|
13
|
+
* | lg | "large" | Full-screen loading, cards |
|
|
14
|
+
*
|
|
15
|
+
* ## Features
|
|
16
|
+
* - Two sizes: sm (small) and lg (large)
|
|
17
|
+
* - Responsive size support (phone/tablet breakpoints)
|
|
18
|
+
* - Theme-aware color tokens
|
|
19
|
+
* - Full accessibility support (progressbar role)
|
|
20
|
+
*
|
|
21
|
+
* @performance
|
|
22
|
+
* - Wrapped with React.memo() to prevent unnecessary re-renders
|
|
23
|
+
* - Uses useMemo() for color resolution from theme tokens
|
|
24
|
+
* - ActivityIndicator size mapped via constant-time lookup table
|
|
25
|
+
* - Native ActivityIndicator optimized by React Native for smooth animation
|
|
26
|
+
*
|
|
27
|
+
* @see Spinner.types.ts - Type definitions
|
|
28
|
+
* @see Spinner.helpers.ts - Size mapping functions
|
|
29
|
+
* @see Spinner.a11y.ts - Accessibility prop generation
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Basic usage
|
|
33
|
+
* <Spinner />
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Large with custom color
|
|
37
|
+
* <Spinner size="lg" color="feedbackSuccess" />
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Responsive size
|
|
41
|
+
* <Spinner size={{ phone: "sm", tablet: "lg" }} />
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // With accessibility
|
|
45
|
+
* <Spinner accessibilityLabel="Loading your data" />
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
import {
|
|
49
|
+
createRestyleComponent,
|
|
50
|
+
createVariant,
|
|
51
|
+
layout,
|
|
52
|
+
spacing,
|
|
53
|
+
spacingShorthand,
|
|
54
|
+
} from "@shopify/restyle";
|
|
55
|
+
import React, { memo } from "react";
|
|
56
|
+
import { ActivityIndicator } from "react-native";
|
|
57
|
+
import { RestyleTheme } from "../../../core/restyle";
|
|
58
|
+
import { Box } from "../../layout";
|
|
59
|
+
import { BaseSpinnerProps, SpinnerProps } from "./Spinner.types";
|
|
60
|
+
import { useSpinnerLogic } from "./useSpinnerLogic";
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Base Spinner container with Restyle variant support.
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
const BaseSpinner = createRestyleComponent<BaseSpinnerProps, RestyleTheme>(
|
|
67
|
+
[
|
|
68
|
+
createVariant({ themeKey: "spinnerSizes", property: "size" }),
|
|
69
|
+
layout,
|
|
70
|
+
spacing,
|
|
71
|
+
spacingShorthand,
|
|
72
|
+
],
|
|
73
|
+
Box,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
function SpinnerComponent({
|
|
77
|
+
size = "sm",
|
|
78
|
+
color = "accentPrimary",
|
|
79
|
+
accessibilityLabel,
|
|
80
|
+
testID,
|
|
81
|
+
...rest
|
|
82
|
+
}: SpinnerProps) {
|
|
83
|
+
const { resolvedSize, activityIndicatorSize, spinnerColor, a11yProps } =
|
|
84
|
+
useSpinnerLogic({
|
|
85
|
+
size,
|
|
86
|
+
color,
|
|
87
|
+
accessibilityLabel,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<BaseSpinner
|
|
92
|
+
size={resolvedSize}
|
|
93
|
+
alignItems="center"
|
|
94
|
+
justifyContent="center"
|
|
95
|
+
testID={testID}
|
|
96
|
+
{...a11yProps}
|
|
97
|
+
{...rest}
|
|
98
|
+
>
|
|
99
|
+
<ActivityIndicator size={activityIndicatorSize} color={spinnerColor} />
|
|
100
|
+
</BaseSpinner>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const Spinner = memo(SpinnerComponent);
|
|
105
|
+
Spinner.displayName = "Spinner";
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Component Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the Spinner component and its related hooks/helpers.
|
|
5
|
+
*
|
|
6
|
+
* @see Spinner.tsx - Component implementation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { BoxProps, ResponsiveValue, VariantProps } from "@shopify/restyle";
|
|
10
|
+
import { ViewProps } from "react-native";
|
|
11
|
+
import { RestyleTheme } from "../../../core/restyle";
|
|
12
|
+
import { RestyleColor } from "../../../types";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Size variants for the Spinner.
|
|
16
|
+
* Derived from theme spinnerSizes.
|
|
17
|
+
* Maps to React Native ActivityIndicator sizes.
|
|
18
|
+
*
|
|
19
|
+
* - `sm`: "small" ActivityIndicator - for inline/button loading
|
|
20
|
+
* - `lg`: "large" ActivityIndicator - for full-screen/card loading
|
|
21
|
+
*/
|
|
22
|
+
export type SpinnerSize = Exclude<
|
|
23
|
+
keyof RestyleTheme["spinnerSizes"],
|
|
24
|
+
"defaults"
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
/** Restyle props for Spinner size variant */
|
|
28
|
+
type SpinnerSizeProps = VariantProps<RestyleTheme, "spinnerSizes", "size">;
|
|
29
|
+
|
|
30
|
+
/** Combined restyle props for base spinner */
|
|
31
|
+
export type BaseSpinnerProps = SpinnerSizeProps &
|
|
32
|
+
BoxProps<RestyleTheme> &
|
|
33
|
+
ViewProps;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Props for the Spinner component.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* <Spinner size="lg" color="accentPrimary" accessibilityLabel="Loading" />
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // Responsive size
|
|
43
|
+
* <Spinner size={{ phone: "sm", tablet: "lg" }} />
|
|
44
|
+
*/
|
|
45
|
+
export interface SpinnerProps extends Omit<BaseSpinnerProps, "size"> {
|
|
46
|
+
/**
|
|
47
|
+
* Size of the spinner (supports responsive values).
|
|
48
|
+
* @default "sm"
|
|
49
|
+
*/
|
|
50
|
+
size?: ResponsiveValue<SpinnerSize, RestyleTheme["breakpoints"]>;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Spinner color from theme palette.
|
|
54
|
+
* @default "accentPrimary"
|
|
55
|
+
*/
|
|
56
|
+
color?: RestyleColor;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Accessibility label for screen readers.
|
|
60
|
+
* @default "Loading"
|
|
61
|
+
*/
|
|
62
|
+
accessibilityLabel?: string;
|
|
63
|
+
|
|
64
|
+
/** Test ID for testing frameworks */
|
|
65
|
+
testID?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parameters for the useSpinnerLogic hook.
|
|
70
|
+
*/
|
|
71
|
+
export interface UseSpinnerLogicParams {
|
|
72
|
+
size: ResponsiveValue<SpinnerSize, RestyleTheme["breakpoints"]>;
|
|
73
|
+
color: RestyleColor;
|
|
74
|
+
accessibilityLabel?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Return value from the useSpinnerLogic hook.
|
|
79
|
+
*/
|
|
80
|
+
export interface UseSpinnerLogicReturn {
|
|
81
|
+
/** Resolved size for restyle variant */
|
|
82
|
+
resolvedSize: SpinnerSize;
|
|
83
|
+
/** Mapped ActivityIndicator size ("small" | "large") */
|
|
84
|
+
activityIndicatorSize: "small" | "large";
|
|
85
|
+
/** Resolved hex color string */
|
|
86
|
+
spinnerColor: string;
|
|
87
|
+
/** Accessibility props */
|
|
88
|
+
a11yProps: SpinnerA11yProps;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Parameters for generating spinner accessibility props.
|
|
93
|
+
*/
|
|
94
|
+
export interface SpinnerA11yParams {
|
|
95
|
+
/**
|
|
96
|
+
* Text description of what is loading.
|
|
97
|
+
*/
|
|
98
|
+
accessibilityLabel?: string;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Pre-translated fallback label from i18n.
|
|
102
|
+
* Used when accessibilityLabel is not provided.
|
|
103
|
+
*/
|
|
104
|
+
fallbackLabel: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Accessibility props returned by getSpinnerA11y.
|
|
109
|
+
*/
|
|
110
|
+
export interface SpinnerA11yProps {
|
|
111
|
+
accessibilityLabel: string;
|
|
112
|
+
accessibilityRole: "progressbar";
|
|
113
|
+
accessibilityState: { busy: boolean };
|
|
114
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Module
|
|
3
|
+
*
|
|
4
|
+
* Atom component for displaying loading indicators with theme integration.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { Spinner } from "@/design-system/primitives/feedback/Spinner";
|
|
8
|
+
*
|
|
9
|
+
* <Spinner size="lg" color="accentPrimary" accessibilityLabel="Loading" />
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { Spinner } from "./Spinner";
|
|
13
|
+
export type {
|
|
14
|
+
SpinnerProps,
|
|
15
|
+
SpinnerSize,
|
|
16
|
+
BaseSpinnerProps,
|
|
17
|
+
} from "./Spinner.types";
|
|
18
|
+
export * from "./Spinner.a11y";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Logic Hook
|
|
3
|
+
*
|
|
4
|
+
* Extracts all computational logic from the Spinner component.
|
|
5
|
+
* Returns memoized values for optimal render performance.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Resolve responsive size prop
|
|
9
|
+
* - Map size token to ActivityIndicator size
|
|
10
|
+
* - Resolve color token to hex value
|
|
11
|
+
*
|
|
12
|
+
* @see Spinner.tsx - Component consuming this hook
|
|
13
|
+
* @see Spinner.helpers.ts - Pure calculation functions
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { useResponsiveProp } from "@shopify/restyle";
|
|
17
|
+
import { useMemo } from "react";
|
|
18
|
+
import { useDesignSystem } from "../../../provider";
|
|
19
|
+
import { useRestyleTheme } from "../../../core/restyle";
|
|
20
|
+
import { BaseThemeColor } from "../../../types";
|
|
21
|
+
import { getSpinnerA11y } from "./Spinner.a11y";
|
|
22
|
+
import { getSpinnerActivityIndicatorSize } from "./Spinner.helpers";
|
|
23
|
+
import { SpinnerSize, UseSpinnerLogicParams, UseSpinnerLogicReturn } from "./Spinner.types";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Orchestrates Spinner rendering logic and state management.
|
|
27
|
+
*
|
|
28
|
+
* Handles:
|
|
29
|
+
* - Responsive size resolution
|
|
30
|
+
* - Size mapping to ActivityIndicator size ('small' | 'large')
|
|
31
|
+
* - Color token resolution to hex value
|
|
32
|
+
* - Accessibility props generation
|
|
33
|
+
*
|
|
34
|
+
* @param params - Configuration object for spinner behavior
|
|
35
|
+
* @param params.size - Size variant ('sm' | 'md' | 'lg')
|
|
36
|
+
* @param params.color - Theme color token for spinner
|
|
37
|
+
* @param params.accessibilityLabel - Custom a11y label
|
|
38
|
+
*
|
|
39
|
+
* @returns Computed values for rendering Spinner
|
|
40
|
+
* @returns {string} resolvedSize - Resolved size variant
|
|
41
|
+
* @returns {string} activityIndicatorSize - RN ActivityIndicator size
|
|
42
|
+
* @returns {string} spinnerColor - Resolved hex color
|
|
43
|
+
* @returns {object} a11yProps - Accessibility props
|
|
44
|
+
*
|
|
45
|
+
* @performance This hook uses useMemo() for color resolution
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* const { resolvedSize, activityIndicatorSize, spinnerColor } = useSpinnerLogic({
|
|
49
|
+
* size: "lg",
|
|
50
|
+
* color: "accentPrimary",
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // With responsive size
|
|
55
|
+
* const { activityIndicatorSize, spinnerColor } = useSpinnerLogic({
|
|
56
|
+
* size: { phone: "sm", tablet: "lg" },
|
|
57
|
+
* color: "accentPrimary",
|
|
58
|
+
* });
|
|
59
|
+
*/
|
|
60
|
+
export function useSpinnerLogic({
|
|
61
|
+
size,
|
|
62
|
+
color,
|
|
63
|
+
accessibilityLabel,
|
|
64
|
+
}: UseSpinnerLogicParams): UseSpinnerLogicReturn {
|
|
65
|
+
const { labels: t } = useDesignSystem();
|
|
66
|
+
const theme = useRestyleTheme();
|
|
67
|
+
const resolvedSize = (useResponsiveProp(size) ?? "sm") as SpinnerSize;
|
|
68
|
+
|
|
69
|
+
const activityIndicatorSize = getSpinnerActivityIndicatorSize(resolvedSize);
|
|
70
|
+
|
|
71
|
+
const spinnerColor = theme.colors[color as BaseThemeColor];
|
|
72
|
+
|
|
73
|
+
const a11yProps = useMemo(
|
|
74
|
+
() => getSpinnerA11y({ accessibilityLabel, fallbackLabel: t.designSystem.spinner.fallbackLabel }),
|
|
75
|
+
[accessibilityLabel, t.designSystem.spinner.fallbackLabel],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
resolvedSize,
|
|
80
|
+
activityIndicatorSize,
|
|
81
|
+
spinnerColor,
|
|
82
|
+
a11yProps,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toast Component Helpers
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for mapping Toast props to style values.
|
|
5
|
+
*
|
|
6
|
+
* ## Variant Color Mapping
|
|
7
|
+
* | Variant | Background Token | Border Token | Icon Token |
|
|
8
|
+
* |---------|----------------------|----------------------|---------------------|
|
|
9
|
+
* | success | feedbackSuccessLight | feedbackSuccess | feedbackSuccessDark |
|
|
10
|
+
* | error | feedbackErrorLight | feedbackError | feedbackErrorDark |
|
|
11
|
+
* | warning | feedbackWarningLight | feedbackWarning | feedbackWarningDark |
|
|
12
|
+
* | info | feedbackInfoLight | feedbackInfo | feedbackInfoDark |
|
|
13
|
+
*
|
|
14
|
+
* @see Toast.types.ts - Type definitions
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { MaterialIconName, RestyleColor } from "../../../types";
|
|
18
|
+
import { ToastVariant } from "./Toast.types";
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// VARIANT COLOR MAPPINGS
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Background color tokens for each variant.
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
const TOAST_BACKGROUND_COLORS: Record<ToastVariant, RestyleColor> = {
|
|
29
|
+
success: "feedbackSuccessLight",
|
|
30
|
+
error: "feedbackErrorLight",
|
|
31
|
+
warning: "feedbackWarningLight",
|
|
32
|
+
info: "feedbackInfoLight",
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Border color tokens for each variant.
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
const TOAST_BORDER_COLORS: Record<ToastVariant, RestyleColor> = {
|
|
40
|
+
success: "feedbackSuccess",
|
|
41
|
+
error: "feedbackError",
|
|
42
|
+
warning: "feedbackWarning",
|
|
43
|
+
info: "feedbackInfo",
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Icon color tokens for each variant.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
const TOAST_ICON_COLORS: Record<ToastVariant, RestyleColor> = {
|
|
51
|
+
success: "feedbackSuccessDark",
|
|
52
|
+
error: "feedbackErrorDark",
|
|
53
|
+
warning: "feedbackWarningDark",
|
|
54
|
+
info: "feedbackInfoDark",
|
|
55
|
+
} as const;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Text color tokens for each variant.
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
const TOAST_TEXT_COLORS: Record<ToastVariant, RestyleColor> = {
|
|
62
|
+
success: "feedbackSuccessDark",
|
|
63
|
+
error: "feedbackErrorDark",
|
|
64
|
+
warning: "feedbackWarningDark",
|
|
65
|
+
info: "feedbackInfoDark",
|
|
66
|
+
} as const;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Default icon for each variant.
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
const TOAST_DEFAULT_ICONS: Record<ToastVariant, MaterialIconName> = {
|
|
73
|
+
success: "check-circle",
|
|
74
|
+
error: "error",
|
|
75
|
+
warning: "warning",
|
|
76
|
+
info: "info",
|
|
77
|
+
} as const;
|
|
78
|
+
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// PUBLIC HELPERS
|
|
81
|
+
// =============================================================================
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get background color token for toast variant.
|
|
85
|
+
*
|
|
86
|
+
* @param variant - Toast variant
|
|
87
|
+
* @returns Background color token
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* getToastBackgroundColor("success") // "feedbackSuccessLight"
|
|
91
|
+
*/
|
|
92
|
+
export function getToastBackgroundColor(variant: ToastVariant): RestyleColor {
|
|
93
|
+
return TOAST_BACKGROUND_COLORS[variant] ?? TOAST_BACKGROUND_COLORS.info;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get border color token for toast variant.
|
|
98
|
+
*
|
|
99
|
+
* @param variant - Toast variant
|
|
100
|
+
* @returns Border color token
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* getToastBorderColor("error") // "feedbackError"
|
|
104
|
+
*/
|
|
105
|
+
export function getToastBorderColor(variant: ToastVariant): RestyleColor {
|
|
106
|
+
return TOAST_BORDER_COLORS[variant] ?? TOAST_BORDER_COLORS.info;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get icon color token for toast variant.
|
|
111
|
+
*
|
|
112
|
+
* @param variant - Toast variant
|
|
113
|
+
* @returns Icon color token
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* getToastIconColor("warning") // "feedbackWarningDark"
|
|
117
|
+
*/
|
|
118
|
+
export function getToastIconColor(variant: ToastVariant): RestyleColor {
|
|
119
|
+
return TOAST_ICON_COLORS[variant] ?? TOAST_ICON_COLORS.info;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get text color token for toast variant.
|
|
124
|
+
*
|
|
125
|
+
* @param variant - Toast variant
|
|
126
|
+
* @returns Text color token
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* getToastTextColor("info") // "feedbackInfoDark"
|
|
130
|
+
*/
|
|
131
|
+
export function getToastTextColor(variant: ToastVariant): RestyleColor {
|
|
132
|
+
return TOAST_TEXT_COLORS[variant] ?? TOAST_TEXT_COLORS.info;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get default icon for toast variant, or use custom icon if provided.
|
|
137
|
+
*
|
|
138
|
+
* @param variant - Toast variant
|
|
139
|
+
* @param customIcon - Optional custom icon override
|
|
140
|
+
* @returns Icon name to display
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* getToastIcon("success") // "check-circle"
|
|
144
|
+
* getToastIcon("success", "thumb-up") // "thumb-up"
|
|
145
|
+
*/
|
|
146
|
+
export function getToastIcon(
|
|
147
|
+
variant: ToastVariant,
|
|
148
|
+
customIcon?: MaterialIconName
|
|
149
|
+
): MaterialIconName {
|
|
150
|
+
return customIcon ?? TOAST_DEFAULT_ICONS[variant] ?? TOAST_DEFAULT_ICONS.info;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Generate a unique ID for a toast.
|
|
155
|
+
*
|
|
156
|
+
* @returns Unique toast ID
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* generateToastId() // "toast-1234567890-abc"
|
|
160
|
+
*/
|
|
161
|
+
export function generateToastId(): string {
|
|
162
|
+
return `toast-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
163
|
+
}
|