@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,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProgressBar Logic Hook
|
|
3
|
+
*
|
|
4
|
+
* Extracts all computational logic from the ProgressBar component.
|
|
5
|
+
* Returns memoized values for optimal render performance.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Clamp and validate progress value
|
|
9
|
+
* - Calculate width percentage
|
|
10
|
+
* - Pass through the `color` prop as `indicatorColorToken`
|
|
11
|
+
* - Always use `"borderSubtle"` as the track color
|
|
12
|
+
* - Resolve size dimensions
|
|
13
|
+
* - Generate accessibility props
|
|
14
|
+
*
|
|
15
|
+
* @see ProgressBar.tsx - Component consuming this hook
|
|
16
|
+
* @see ProgressBar.helpers.ts - Pure calculation functions
|
|
17
|
+
* @see ProgressBar.a11y.ts - Accessibility prop generation
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { useResponsiveProp } from "@shopify/restyle";
|
|
21
|
+
import { useMemo } from "react";
|
|
22
|
+
import { useDesignSystem } from "../../../provider";
|
|
23
|
+
import { getProgressBarA11y } from "./ProgressBar.a11y";
|
|
24
|
+
import {
|
|
25
|
+
clampProgress,
|
|
26
|
+
getProgressBarBorderRadius,
|
|
27
|
+
getWidthPercent,
|
|
28
|
+
progressToPercent,
|
|
29
|
+
} from "./ProgressBar.helpers";
|
|
30
|
+
import {
|
|
31
|
+
ProgressBarSize,
|
|
32
|
+
UseProgressBarLogicParams,
|
|
33
|
+
UseProgressBarLogicReturn,
|
|
34
|
+
} from "./ProgressBar.types";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Orchestrates ProgressBar rendering logic and state management.
|
|
38
|
+
*
|
|
39
|
+
* Handles:
|
|
40
|
+
* - Responsive size resolution
|
|
41
|
+
* - Progress value clamping (0.0 to 1.0 range)
|
|
42
|
+
* - Width percentage calculation for indicator
|
|
43
|
+
* - Color token pass-through for indicator; track is always "borderSubtle"
|
|
44
|
+
* - Border radius calculation based on size
|
|
45
|
+
* - Accessibility props with progress value
|
|
46
|
+
*
|
|
47
|
+
* @param params - Configuration object for progress bar behavior
|
|
48
|
+
* @param params.progress - Progress value (0.0 to 1.0, values outside clamped)
|
|
49
|
+
* @param params.color - Color token for the indicator (RestyleColor)
|
|
50
|
+
* @param params.size - Size variant ('sm' | 'md' | 'lg')
|
|
51
|
+
* @param params.accessibilityLabel - Custom a11y label
|
|
52
|
+
*
|
|
53
|
+
* @returns Computed values for rendering ProgressBar
|
|
54
|
+
*
|
|
55
|
+
* @performance This hook uses useMemo() for expensive calculations
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const {
|
|
59
|
+
* clampedProgress,
|
|
60
|
+
* widthPercent,
|
|
61
|
+
* indicatorColorToken,
|
|
62
|
+
* trackColorToken,
|
|
63
|
+
* borderRadius,
|
|
64
|
+
* a11yProps,
|
|
65
|
+
* } = useProgressBarLogic({
|
|
66
|
+
* progress: 0.75,
|
|
67
|
+
* color: "feedbackSuccess",
|
|
68
|
+
* size: "md",
|
|
69
|
+
* });
|
|
70
|
+
*/
|
|
71
|
+
export function useProgressBarLogic({
|
|
72
|
+
progress,
|
|
73
|
+
color,
|
|
74
|
+
size,
|
|
75
|
+
accessibilityLabel,
|
|
76
|
+
}: UseProgressBarLogicParams): UseProgressBarLogicReturn {
|
|
77
|
+
const { labels: t } = useDesignSystem();
|
|
78
|
+
// Resolve responsive size
|
|
79
|
+
const resolvedSize = (useResponsiveProp(size) ?? "md") as ProgressBarSize;
|
|
80
|
+
|
|
81
|
+
// Memoized progress calculations
|
|
82
|
+
const progressValues = useMemo(() => {
|
|
83
|
+
const clamped = clampProgress(progress);
|
|
84
|
+
return {
|
|
85
|
+
clampedProgress: clamped,
|
|
86
|
+
progressPercent: progressToPercent(clamped),
|
|
87
|
+
widthPercent: getWidthPercent(clamped),
|
|
88
|
+
};
|
|
89
|
+
}, [progress]);
|
|
90
|
+
|
|
91
|
+
// Color tokens: indicator uses the color prop directly, track is always borderSubtle
|
|
92
|
+
const colorTokens = useMemo(
|
|
93
|
+
() => ({
|
|
94
|
+
indicatorColorToken: color,
|
|
95
|
+
trackColorToken: "borderSubtle" as const,
|
|
96
|
+
}),
|
|
97
|
+
[color]
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Border radius based on size
|
|
101
|
+
const borderRadius = getProgressBarBorderRadius(resolvedSize);
|
|
102
|
+
|
|
103
|
+
// Accessibility props
|
|
104
|
+
const a11yProps = useMemo(
|
|
105
|
+
() =>
|
|
106
|
+
getProgressBarA11y({
|
|
107
|
+
progress,
|
|
108
|
+
accessibilityLabel,
|
|
109
|
+
fallbackLabel: t.designSystem.progressBar.fallbackLabel,
|
|
110
|
+
}),
|
|
111
|
+
[progress, accessibilityLabel, t.designSystem.progressBar.fallbackLabel]
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
...progressValues,
|
|
116
|
+
...colorTokens,
|
|
117
|
+
borderRadius,
|
|
118
|
+
a11yProps,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skeleton Component Helpers
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for calculating skeleton dimensions and styles.
|
|
5
|
+
* All values are derived from design tokens for consistency.
|
|
6
|
+
*
|
|
7
|
+
* ## Size Scale by Variant
|
|
8
|
+
*
|
|
9
|
+
* ### Text Variant (rounded rectangle)
|
|
10
|
+
* | Size | Width | Height | Use Case |
|
|
11
|
+
* |------|--------|--------|----------|
|
|
12
|
+
* | sm | 100% | 16px | Caption, labels |
|
|
13
|
+
* | md | 100% | 20px | Body text (default) |
|
|
14
|
+
* | lg | 100% | 28px | Headings |
|
|
15
|
+
*
|
|
16
|
+
* ### Circular Variant (avatar/icon)
|
|
17
|
+
* | Size | Width | Height | Use Case |
|
|
18
|
+
* |------|--------|--------|----------|
|
|
19
|
+
* | sm | 32px | 32px | Small avatar |
|
|
20
|
+
* | md | 40px | 40px | Default avatar |
|
|
21
|
+
* | lg | 56px | 56px | Large avatar |
|
|
22
|
+
*
|
|
23
|
+
* ### Rectangular Variant (image/card)
|
|
24
|
+
* | Size | Width | Height | Use Case |
|
|
25
|
+
* |------|--------|--------|----------|
|
|
26
|
+
* | sm | 100% | 80px | Thumbnail |
|
|
27
|
+
* | md | 100% | 120px | Card image |
|
|
28
|
+
* | lg | 100% | 200px | Hero image |
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { RestyleTheme } from "../../../core/restyle";
|
|
32
|
+
import {
|
|
33
|
+
ResolveSkeletonDimensionsParams,
|
|
34
|
+
SkeletonDimensionsResult,
|
|
35
|
+
SkeletonSize,
|
|
36
|
+
SkeletonSizeConfig,
|
|
37
|
+
SkeletonVariant,
|
|
38
|
+
} from "./Skeleton.types";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Predefined size configurations for each variant.
|
|
42
|
+
* Following 8px grid system for consistency.
|
|
43
|
+
*/
|
|
44
|
+
export const SKELETON_SIZE_CONFIG: SkeletonSizeConfig = {
|
|
45
|
+
text: {
|
|
46
|
+
sm: { width: "100%", height: 16 },
|
|
47
|
+
md: { width: "100%", height: 20 },
|
|
48
|
+
lg: { width: "100%", height: 28 },
|
|
49
|
+
},
|
|
50
|
+
circular: {
|
|
51
|
+
sm: { width: 32, height: 32 },
|
|
52
|
+
md: { width: 40, height: 40 },
|
|
53
|
+
lg: { width: 56, height: 56 },
|
|
54
|
+
},
|
|
55
|
+
rectangular: {
|
|
56
|
+
sm: { width: "100%", height: 80 },
|
|
57
|
+
md: { width: "100%", height: 120 },
|
|
58
|
+
lg: { width: "100%", height: 200 },
|
|
59
|
+
},
|
|
60
|
+
} as const;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Border radius mapping by variant.
|
|
64
|
+
* - text: small radius for text-like appearance
|
|
65
|
+
* - circular: full radius for perfect circle
|
|
66
|
+
* - rectangular: medium radius for card-like appearance
|
|
67
|
+
*/
|
|
68
|
+
export const SKELETON_BORDER_RADIUS: Record<SkeletonVariant, keyof RestyleTheme["borderRadii"]> = {
|
|
69
|
+
text: "xs",
|
|
70
|
+
circular: "full",
|
|
71
|
+
rectangular: "md",
|
|
72
|
+
} as const;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Animation timing constants.
|
|
76
|
+
*/
|
|
77
|
+
export const SKELETON_ANIMATION = {
|
|
78
|
+
/** Default pulse animation duration in ms */
|
|
79
|
+
DEFAULT_DURATION: 1500,
|
|
80
|
+
/** Minimum opacity during pulse */
|
|
81
|
+
MIN_OPACITY: 0.4,
|
|
82
|
+
/** Maximum opacity during pulse */
|
|
83
|
+
MAX_OPACITY: 1,
|
|
84
|
+
} as const;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get dimensions for a skeleton based on variant and size.
|
|
88
|
+
*
|
|
89
|
+
* @param params - Variant, size, and optional custom dimensions
|
|
90
|
+
* @returns Resolved width and height values
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* getSkeletonDimensions({ variant: "text", size: "md" })
|
|
94
|
+
* // { width: "100%", height: 20 }
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* getSkeletonDimensions({ variant: "circular", size: "lg", customWidth: 64 })
|
|
98
|
+
* // { width: 64, height: 64 } // circular maintains aspect ratio
|
|
99
|
+
*/
|
|
100
|
+
export function getSkeletonDimensions(
|
|
101
|
+
params: ResolveSkeletonDimensionsParams
|
|
102
|
+
): SkeletonDimensionsResult {
|
|
103
|
+
const { variant, size, customWidth, customHeight } = params;
|
|
104
|
+
const config = SKELETON_SIZE_CONFIG[variant][size];
|
|
105
|
+
|
|
106
|
+
let width = customWidth ?? config.width;
|
|
107
|
+
let height = customHeight ?? config.height;
|
|
108
|
+
|
|
109
|
+
// For circular variant, maintain 1:1 aspect ratio
|
|
110
|
+
if (variant === "circular") {
|
|
111
|
+
if (customWidth !== undefined && customHeight === undefined) {
|
|
112
|
+
height = typeof customWidth === "number" ? customWidth : config.height;
|
|
113
|
+
} else if (customHeight !== undefined && customWidth === undefined) {
|
|
114
|
+
width = customHeight;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { width, height };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get border radius token for variant.
|
|
123
|
+
*
|
|
124
|
+
* @param variant - Skeleton shape variant
|
|
125
|
+
* @returns Border radius token key
|
|
126
|
+
*/
|
|
127
|
+
export function getSkeletonBorderRadius(
|
|
128
|
+
variant: SkeletonVariant
|
|
129
|
+
): keyof RestyleTheme["borderRadii"] {
|
|
130
|
+
return SKELETON_BORDER_RADIUS[variant];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get default dimensions for a size preset.
|
|
135
|
+
*
|
|
136
|
+
* @param variant - Shape variant
|
|
137
|
+
* @param size - Size preset
|
|
138
|
+
* @returns Default dimensions
|
|
139
|
+
*/
|
|
140
|
+
export function getSkeletonSizePreset(
|
|
141
|
+
variant: SkeletonVariant,
|
|
142
|
+
size: SkeletonSize
|
|
143
|
+
): SkeletonDimensionsResult {
|
|
144
|
+
return SKELETON_SIZE_CONFIG[variant][size];
|
|
145
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skeleton Component
|
|
3
|
+
*
|
|
4
|
+
* @description Animated placeholder for loading states - Atom
|
|
5
|
+
*
|
|
6
|
+
* Skeleton provides visual feedback during content loading, reducing
|
|
7
|
+
* perceived wait time and preventing layout shift. Uses performant
|
|
8
|
+
* native driver animations for smooth 60fps rendering.
|
|
9
|
+
*
|
|
10
|
+
* ## Variants
|
|
11
|
+
* | Variant | Shape | Use Case |
|
|
12
|
+
* |-------------|--------------------| ---------|
|
|
13
|
+
* | text | Rounded rectangle | Text placeholders |
|
|
14
|
+
* | circular | Perfect circle | Avatars, icons |
|
|
15
|
+
* | rectangular | Sharp corners | Images, cards |
|
|
16
|
+
*
|
|
17
|
+
* ## Size Presets
|
|
18
|
+
* | Size | Text Height | Circular | Rectangular |
|
|
19
|
+
* |------|-------------|----------|-------------|
|
|
20
|
+
* | sm | 16px | 32×32 | 80px height |
|
|
21
|
+
* | md | 20px | 40×40 | 120px height |
|
|
22
|
+
* | lg | 28px | 56×56 | 200px height |
|
|
23
|
+
*
|
|
24
|
+
* ## Animation Types
|
|
25
|
+
* - `pulse`: Smooth opacity fade (default, best performance)
|
|
26
|
+
* - `wave`: Shimmer effect (premium feel)
|
|
27
|
+
* - `none`: Static placeholder
|
|
28
|
+
*
|
|
29
|
+
* ## Features
|
|
30
|
+
* - Three shape variants (text, circular, rectangular)
|
|
31
|
+
* - Three size presets with custom override support
|
|
32
|
+
* - Smooth pulse animation using native driver
|
|
33
|
+
* - Theme-aware skeleton color
|
|
34
|
+
* - Hidden from screen readers (decorative)
|
|
35
|
+
* - Responsive size support
|
|
36
|
+
*
|
|
37
|
+
* @performance
|
|
38
|
+
* - Wrapped with React.memo() to prevent unnecessary re-renders
|
|
39
|
+
* - Uses native driver for smooth 60fps pulse animations
|
|
40
|
+
* - Animated loop runs independently per skeleton instance
|
|
41
|
+
* - Respects reduced motion preferences for accessibility
|
|
42
|
+
* - Dimension calculations cached with useMemo()
|
|
43
|
+
*
|
|
44
|
+
* ## Best Practices
|
|
45
|
+
* - Match skeleton dimensions to actual content
|
|
46
|
+
* - Use consistent animation across loading states
|
|
47
|
+
* - Provide actual loading state via aria-live regions
|
|
48
|
+
* - Avoid excessive skeleton count (cognitive load)
|
|
49
|
+
*
|
|
50
|
+
* @see Skeleton.types.ts - Type definitions
|
|
51
|
+
* @see Skeleton.helpers.ts - Dimension calculations
|
|
52
|
+
* @see Skeleton.a11y.ts - Accessibility (hidden from AT)
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Basic text skeleton
|
|
56
|
+
* <Skeleton />
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Avatar skeleton
|
|
60
|
+
* <Skeleton variant="circular" size="lg" />
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* // Custom dimensions
|
|
64
|
+
* <Skeleton variant="rectangular" width={300} height={200} />
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* // Responsive size
|
|
68
|
+
* <Skeleton size={{ phone: "sm", tablet: "lg" }} />
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // Text block placeholder
|
|
72
|
+
* <Box gap="xs">
|
|
73
|
+
* <Skeleton width="90%" />
|
|
74
|
+
* <Skeleton width="75%" />
|
|
75
|
+
* <Skeleton width="85%" />
|
|
76
|
+
* </Box>
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Card skeleton
|
|
80
|
+
* <Card>
|
|
81
|
+
* <Skeleton variant="rectangular" height={150} />
|
|
82
|
+
* <Box padding="md" gap="sm">
|
|
83
|
+
* <Skeleton variant="text" size="lg" width="60%" />
|
|
84
|
+
* <Skeleton variant="text" size="sm" />
|
|
85
|
+
* <Skeleton variant="text" size="sm" width="80%" />
|
|
86
|
+
* </Box>
|
|
87
|
+
* </Card>
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
import {
|
|
91
|
+
createRestyleComponent,
|
|
92
|
+
layout,
|
|
93
|
+
spacing,
|
|
94
|
+
spacingShorthand,
|
|
95
|
+
} from "@shopify/restyle";
|
|
96
|
+
import React, { memo } from "react";
|
|
97
|
+
import { RestyleTheme } from "../../../core/restyle";
|
|
98
|
+
import { BaseThemeColor } from "../../../types";
|
|
99
|
+
import { AnimatedBox } from "../../layout";
|
|
100
|
+
import { BaseSkeletonContainerProps, SkeletonProps } from "./Skeleton.types";
|
|
101
|
+
import { useSkeletonLogic } from "./useSkeletonLogic";
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Base Skeleton container with Restyle support.
|
|
105
|
+
* @internal
|
|
106
|
+
*/
|
|
107
|
+
const BaseSkeletonContainer = createRestyleComponent<
|
|
108
|
+
BaseSkeletonContainerProps,
|
|
109
|
+
RestyleTheme
|
|
110
|
+
>([layout, spacing, spacingShorthand], AnimatedBox);
|
|
111
|
+
|
|
112
|
+
function SkeletonComponent({
|
|
113
|
+
variant = "text",
|
|
114
|
+
size = "md",
|
|
115
|
+
width,
|
|
116
|
+
height,
|
|
117
|
+
animation = "pulse",
|
|
118
|
+
color = "specialSkeleton",
|
|
119
|
+
duration = 1500,
|
|
120
|
+
testID,
|
|
121
|
+
...rest
|
|
122
|
+
}: SkeletonProps) {
|
|
123
|
+
const {
|
|
124
|
+
resolvedWidth,
|
|
125
|
+
resolvedHeight,
|
|
126
|
+
borderRadius,
|
|
127
|
+
animatedOpacity,
|
|
128
|
+
backgroundColor,
|
|
129
|
+
a11yProps,
|
|
130
|
+
} = useSkeletonLogic({
|
|
131
|
+
variant,
|
|
132
|
+
size,
|
|
133
|
+
width,
|
|
134
|
+
height,
|
|
135
|
+
animation,
|
|
136
|
+
color,
|
|
137
|
+
duration,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<BaseSkeletonContainer
|
|
142
|
+
testID={testID}
|
|
143
|
+
backgroundColor={backgroundColor as BaseThemeColor}
|
|
144
|
+
borderRadius={borderRadius}
|
|
145
|
+
width={resolvedWidth as any}
|
|
146
|
+
height={resolvedHeight}
|
|
147
|
+
style={{ opacity: animatedOpacity }}
|
|
148
|
+
{...a11yProps}
|
|
149
|
+
{...rest}
|
|
150
|
+
/>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const Skeleton = memo(SkeletonComponent);
|
|
155
|
+
Skeleton.displayName = "Skeleton";
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skeleton Component Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the Skeleton component and its related hooks/helpers.
|
|
5
|
+
* All types are explicitly exported for external consumption.
|
|
6
|
+
*
|
|
7
|
+
* @see Skeleton.tsx - Component implementation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BoxProps, ResponsiveValue } from "@shopify/restyle";
|
|
11
|
+
import { Animated, ViewProps } from "react-native";
|
|
12
|
+
import { RestyleTheme } from "../../../core/restyle";
|
|
13
|
+
import { RestyleColor } from "../../../types";
|
|
14
|
+
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// VARIANT TYPES
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Shape variant for the Skeleton component.
|
|
21
|
+
* Derived from the restyle theme's `skeletonVariants` keys.
|
|
22
|
+
*
|
|
23
|
+
* - `text`: Rounded rectangle for text placeholders (default)
|
|
24
|
+
* - `circular`: Perfect circle for avatars/icons
|
|
25
|
+
* - `rectangular`: Sharp corners for images/cards
|
|
26
|
+
*/
|
|
27
|
+
export type SkeletonVariant = Exclude<keyof RestyleTheme["skeletonVariants"], "defaults">;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Size preset for the Skeleton component.
|
|
31
|
+
* Derived from the restyle theme's `skeletonSizes` keys.
|
|
32
|
+
* Maps to predefined dimensions for common use cases.
|
|
33
|
+
*
|
|
34
|
+
* - `sm`: Small - compact UI elements (16px height for text)
|
|
35
|
+
* - `md`: Medium - standard content (20px height for text)
|
|
36
|
+
* - `lg`: Large - headings/prominent elements (28px height for text)
|
|
37
|
+
*/
|
|
38
|
+
export type SkeletonSize = Exclude<keyof RestyleTheme["skeletonSizes"], "defaults">;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Animation style for the Skeleton component.
|
|
42
|
+
*
|
|
43
|
+
* - `pulse`: Opacity fades in/out (default, performant)
|
|
44
|
+
* - `wave`: Shimmer effect moving left to right (premium feel)
|
|
45
|
+
* - `none`: No animation (static placeholder)
|
|
46
|
+
*/
|
|
47
|
+
export type SkeletonAnimation = "pulse" | "wave" | "none";
|
|
48
|
+
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// COMPONENT PROPS
|
|
51
|
+
// =============================================================================
|
|
52
|
+
|
|
53
|
+
/** Base props combining Restyle Box props with ViewProps */
|
|
54
|
+
export type BaseSkeletonProps = BoxProps<RestyleTheme> & ViewProps;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Base props for the Skeleton container.
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export type BaseSkeletonContainerProps = BoxProps<RestyleTheme> & ViewProps;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Props for the Skeleton component.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* // Text skeleton (default)
|
|
67
|
+
* <Skeleton width={200} />
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* // Avatar skeleton
|
|
71
|
+
* <Skeleton variant="circular" size="lg" />
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* // Image placeholder
|
|
75
|
+
* <Skeleton variant="rectangular" width={300} height={200} />
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Multiple text lines
|
|
79
|
+
* <Skeleton.Group>
|
|
80
|
+
* <Skeleton width="80%" />
|
|
81
|
+
* <Skeleton width="60%" />
|
|
82
|
+
* <Skeleton width="90%" />
|
|
83
|
+
* </Skeleton.Group>
|
|
84
|
+
*/
|
|
85
|
+
export interface SkeletonProps extends Omit<BaseSkeletonProps, "width" | "height"> {
|
|
86
|
+
/**
|
|
87
|
+
* Shape variant of the skeleton (supports responsive values).
|
|
88
|
+
* @default "text"
|
|
89
|
+
*/
|
|
90
|
+
variant?: ResponsiveValue<SkeletonVariant, RestyleTheme["breakpoints"]>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Size preset (supports responsive values).
|
|
94
|
+
* Provides predefined dimensions based on variant.
|
|
95
|
+
* @default "md"
|
|
96
|
+
*/
|
|
97
|
+
size?: ResponsiveValue<SkeletonSize, RestyleTheme["breakpoints"]>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Custom width. Overrides size preset width.
|
|
101
|
+
* Accepts number (pixels), string (percentage), or responsive value.
|
|
102
|
+
*/
|
|
103
|
+
width?: ResponsiveValue<number | string, RestyleTheme["breakpoints"]>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Custom height. Overrides size preset height.
|
|
107
|
+
* Accepts number (pixels) or responsive value.
|
|
108
|
+
*/
|
|
109
|
+
height?: ResponsiveValue<number, RestyleTheme["breakpoints"]>;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Animation style.
|
|
113
|
+
* @default "pulse"
|
|
114
|
+
*/
|
|
115
|
+
animation?: SkeletonAnimation;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Background color token from theme.
|
|
119
|
+
* @default "specialSkeleton"
|
|
120
|
+
*/
|
|
121
|
+
color?: RestyleColor;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Animation duration in milliseconds.
|
|
125
|
+
* @default 1500
|
|
126
|
+
*/
|
|
127
|
+
duration?: number;
|
|
128
|
+
|
|
129
|
+
/** Test ID for testing frameworks */
|
|
130
|
+
testID?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// =============================================================================
|
|
134
|
+
// HOOK TYPES
|
|
135
|
+
// =============================================================================
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Parameters for the useSkeletonLogic hook.
|
|
139
|
+
*/
|
|
140
|
+
export interface UseSkeletonLogicParams {
|
|
141
|
+
variant: ResponsiveValue<SkeletonVariant, RestyleTheme["breakpoints"]>;
|
|
142
|
+
size: ResponsiveValue<SkeletonSize, RestyleTheme["breakpoints"]>;
|
|
143
|
+
width?: ResponsiveValue<number | string, RestyleTheme["breakpoints"]>;
|
|
144
|
+
height?: ResponsiveValue<number, RestyleTheme["breakpoints"]>;
|
|
145
|
+
animation: SkeletonAnimation;
|
|
146
|
+
color: RestyleColor;
|
|
147
|
+
duration: number;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Return value from the useSkeletonLogic hook.
|
|
152
|
+
*/
|
|
153
|
+
export interface UseSkeletonLogicReturn {
|
|
154
|
+
/** Resolved width value */
|
|
155
|
+
resolvedWidth: number | string;
|
|
156
|
+
/** Resolved height value */
|
|
157
|
+
resolvedHeight: number;
|
|
158
|
+
/** Border radius based on variant */
|
|
159
|
+
borderRadius: keyof RestyleTheme["borderRadii"];
|
|
160
|
+
/** Animated opacity value for pulse animation */
|
|
161
|
+
animatedOpacity: Animated.Value;
|
|
162
|
+
/** Background color token */
|
|
163
|
+
backgroundColor: RestyleColor;
|
|
164
|
+
/** Accessibility props */
|
|
165
|
+
a11yProps: SkeletonA11yProps;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// =============================================================================
|
|
169
|
+
// HELPER TYPES
|
|
170
|
+
// =============================================================================
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Dimensions configuration for skeleton sizes.
|
|
174
|
+
*/
|
|
175
|
+
export interface SkeletonDimensions {
|
|
176
|
+
width: number | string;
|
|
177
|
+
height: number;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Size configuration map by variant.
|
|
182
|
+
*/
|
|
183
|
+
export type SkeletonSizeConfig = Record<SkeletonVariant, Record<SkeletonSize, SkeletonDimensions>>;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Parameters for resolving skeleton dimensions.
|
|
187
|
+
*/
|
|
188
|
+
export interface ResolveSkeletonDimensionsParams {
|
|
189
|
+
variant: SkeletonVariant;
|
|
190
|
+
size: SkeletonSize;
|
|
191
|
+
customWidth?: number | string;
|
|
192
|
+
customHeight?: number;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Result from resolving skeleton dimensions.
|
|
197
|
+
*/
|
|
198
|
+
export interface SkeletonDimensionsResult {
|
|
199
|
+
width: number | string;
|
|
200
|
+
height: number;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// =============================================================================
|
|
204
|
+
// ACCESSIBILITY TYPES
|
|
205
|
+
// =============================================================================
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Parameters for generating skeleton accessibility props.
|
|
209
|
+
*/
|
|
210
|
+
export interface SkeletonA11yParams {
|
|
211
|
+
/** Optional label for screen readers */
|
|
212
|
+
accessibilityLabel?: string;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Accessibility props returned by getSkeletonA11y.
|
|
217
|
+
*/
|
|
218
|
+
export interface SkeletonA11yProps {
|
|
219
|
+
accessible: boolean;
|
|
220
|
+
accessibilityRole: "none";
|
|
221
|
+
accessibilityElementsHidden: boolean;
|
|
222
|
+
importantForAccessibility: "no-hide-descendants";
|
|
223
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skeleton Component
|
|
3
|
+
*
|
|
4
|
+
* @description Animated placeholder for loading states
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { Skeleton } from "@/design-system";
|
|
8
|
+
*
|
|
9
|
+
* // Text skeleton
|
|
10
|
+
* <Skeleton />
|
|
11
|
+
*
|
|
12
|
+
* // Avatar skeleton
|
|
13
|
+
* <Skeleton variant="circular" size="lg" />
|
|
14
|
+
*
|
|
15
|
+
* // Image placeholder
|
|
16
|
+
* <Skeleton variant="rectangular" width={300} height={200} />
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export { Skeleton } from "./Skeleton";
|
|
20
|
+
export type {
|
|
21
|
+
BaseSkeletonProps,
|
|
22
|
+
ResolveSkeletonDimensionsParams,
|
|
23
|
+
SkeletonA11yParams,
|
|
24
|
+
SkeletonA11yProps,
|
|
25
|
+
SkeletonAnimation,
|
|
26
|
+
SkeletonDimensions,
|
|
27
|
+
SkeletonDimensionsResult,
|
|
28
|
+
SkeletonProps,
|
|
29
|
+
SkeletonSize,
|
|
30
|
+
SkeletonSizeConfig,
|
|
31
|
+
SkeletonVariant,
|
|
32
|
+
UseSkeletonLogicParams,
|
|
33
|
+
UseSkeletonLogicReturn,
|
|
34
|
+
} from "./Skeleton.types";
|
|
35
|
+
export {
|
|
36
|
+
getSkeletonBorderRadius,
|
|
37
|
+
getSkeletonDimensions,
|
|
38
|
+
getSkeletonSizePreset,
|
|
39
|
+
SKELETON_ANIMATION,
|
|
40
|
+
SKELETON_BORDER_RADIUS,
|
|
41
|
+
SKELETON_SIZE_CONFIG,
|
|
42
|
+
} from "./Skeleton.helpers";
|
|
43
|
+
export { getSkeletonA11y } from "./Skeleton.a11y";
|
|
44
|
+
export { useSkeletonLogic } from "./useSkeletonLogic";
|