@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,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal Component
|
|
3
|
+
*
|
|
4
|
+
* Barrel export for the Modal dialog overlay component and related types.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { Modal, type ModalProps, type ModalSize } from '@/design-system/primitives/overlays/Modal';
|
|
9
|
+
*
|
|
10
|
+
* <Modal
|
|
11
|
+
* visible={isVisible}
|
|
12
|
+
* onClose={handleClose}
|
|
13
|
+
* title="Confirm Action"
|
|
14
|
+
* position="center"
|
|
15
|
+
* size="md"
|
|
16
|
+
* primaryAction={{ label: "Confirm", onPress: handleConfirm }}
|
|
17
|
+
* >
|
|
18
|
+
* <Text>Are you sure?</Text>
|
|
19
|
+
* </Modal>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
export { Modal } from "./Modal";
|
|
24
|
+
export { useModalLogic } from "./useModalLogic";
|
|
25
|
+
|
|
26
|
+
export type {
|
|
27
|
+
ModalProps,
|
|
28
|
+
ModalPosition,
|
|
29
|
+
ModalAction,
|
|
30
|
+
ModalA11yParams,
|
|
31
|
+
ModalA11yProps,
|
|
32
|
+
UseModalLogicParams,
|
|
33
|
+
UseModalLogicReturn,
|
|
34
|
+
} from "./Modal.types";
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
getAnimationType,
|
|
38
|
+
} from "./Modal.helpers";
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
getModalA11y,
|
|
42
|
+
getModalCloseButtonA11yProps,
|
|
43
|
+
} from "./Modal.a11y";
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal Logic Hook
|
|
3
|
+
*
|
|
4
|
+
* Extracts all computational logic from the Modal component.
|
|
5
|
+
* Returns memoized values for optimal render performance.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Determine animation type
|
|
9
|
+
* - Handle backdrop press behavior
|
|
10
|
+
* - Generate accessibility props
|
|
11
|
+
*
|
|
12
|
+
* The component (Modal.tsx) is a "dumb view" that only renders
|
|
13
|
+
* the pre-computed values from this hook.
|
|
14
|
+
*
|
|
15
|
+
* @see Modal.tsx - Component consuming this hook
|
|
16
|
+
* @see Modal.helpers.ts - Pure calculation functions
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { useCallback, useMemo } from "react";
|
|
20
|
+
|
|
21
|
+
import { useDesignSystem } from "../../../provider";
|
|
22
|
+
import { getModalA11y, getModalCloseButtonA11yProps } from "./Modal.a11y";
|
|
23
|
+
import { getAnimationType } from "./Modal.helpers";
|
|
24
|
+
import type { UseModalLogicParams, UseModalLogicReturn } from "./Modal.types";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Orchestrates Modal rendering logic and state management.
|
|
28
|
+
*
|
|
29
|
+
* Handles:
|
|
30
|
+
* - Animation type determination based on position
|
|
31
|
+
* - Backdrop press handling with configurable behavior
|
|
32
|
+
* - Accessibility props generation with modal role
|
|
33
|
+
*
|
|
34
|
+
* @param params - Configuration object for modal behavior
|
|
35
|
+
* @param params.onClose - Callback when modal should close
|
|
36
|
+
* @param params.title - Modal title text
|
|
37
|
+
* @param params.position - Modal position ('center' | 'bottom')
|
|
38
|
+
* @param params.closeOnBackdropPress - Whether to close on backdrop press
|
|
39
|
+
* @param params.accessibilityLabel - Custom a11y label
|
|
40
|
+
*
|
|
41
|
+
* @returns Computed values for rendering Modal
|
|
42
|
+
* @returns {function} handleBackdropPress - Backdrop press handler
|
|
43
|
+
* @returns {string} animationType - RN Modal animation type
|
|
44
|
+
* @returns {object} a11yProps - Accessibility props with modal role
|
|
45
|
+
*
|
|
46
|
+
* @performance This hook uses useMemo() and useCallback() for optimization
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const { handleBackdropPress, animationType, a11yProps } = useModalLogic({
|
|
50
|
+
* onClose: () => setVisible(false),
|
|
51
|
+
* title: "Confirm Action",
|
|
52
|
+
* position: "center",
|
|
53
|
+
* closeOnBackdropPress: true,
|
|
54
|
+
* });
|
|
55
|
+
*/
|
|
56
|
+
export function useModalLogic({
|
|
57
|
+
onClose,
|
|
58
|
+
title,
|
|
59
|
+
position,
|
|
60
|
+
closeOnBackdropPress,
|
|
61
|
+
accessibilityLabel,
|
|
62
|
+
}: UseModalLogicParams): UseModalLogicReturn {
|
|
63
|
+
const { labels: t } = useDesignSystem();
|
|
64
|
+
const animationType = getAnimationType(position);
|
|
65
|
+
|
|
66
|
+
const handleBackdropPress = useCallback(() => {
|
|
67
|
+
if (closeOnBackdropPress) {
|
|
68
|
+
onClose();
|
|
69
|
+
}
|
|
70
|
+
}, [closeOnBackdropPress, onClose]);
|
|
71
|
+
|
|
72
|
+
const positionLabels = useMemo(() => ({
|
|
73
|
+
bottom: t.designSystem.modal.positionBottom,
|
|
74
|
+
top: t.designSystem.modal.positionTop,
|
|
75
|
+
center: t.designSystem.modal.positionCenter,
|
|
76
|
+
}), [t]);
|
|
77
|
+
|
|
78
|
+
const a11yProps = useMemo(
|
|
79
|
+
() => getModalA11y({
|
|
80
|
+
title,
|
|
81
|
+
position,
|
|
82
|
+
accessibilityLabel,
|
|
83
|
+
fallbackTitle: t.designSystem.modal.fallbackTitle,
|
|
84
|
+
positionLabels,
|
|
85
|
+
}),
|
|
86
|
+
[title, position, accessibilityLabel, t, positionLabels]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const closeButtonA11yProps = useMemo(
|
|
90
|
+
() => getModalCloseButtonA11yProps({
|
|
91
|
+
closeLabel: t.designSystem.modal.closeLabel,
|
|
92
|
+
closeHint: t.designSystem.modal.closeHint,
|
|
93
|
+
}),
|
|
94
|
+
[t]
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
handleBackdropPress,
|
|
99
|
+
animationType,
|
|
100
|
+
a11yProps,
|
|
101
|
+
closeButtonA11yProps,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text Component
|
|
3
|
+
*
|
|
4
|
+
* @description Text primitive with Restyle typography integration - Atom
|
|
5
|
+
*
|
|
6
|
+
* Text is the base text component with access to all theme
|
|
7
|
+
* typography variants and color tokens.
|
|
8
|
+
*
|
|
9
|
+
* ## Typography Variants
|
|
10
|
+
* | Variant | Size | Weight | Use Case |
|
|
11
|
+
* |---------------|------|----------|----------|
|
|
12
|
+
* | displayLarge | 48px | Bold | Hero sections |
|
|
13
|
+
* | displayMedium | 36px | Bold | Page titles |
|
|
14
|
+
* | displaySmall | 30px | Bold | Section headers |
|
|
15
|
+
* | headingLarge | 24px | Semibold | Card titles |
|
|
16
|
+
* | headingMedium | 20px | Semibold | Subsections |
|
|
17
|
+
* | headingSmall | 17px | Semibold | List headers |
|
|
18
|
+
* | bodyLarge | 17px | Regular | Primary content |
|
|
19
|
+
* | bodyMedium | 15px | Regular | Default body |
|
|
20
|
+
* | bodySmall | 13px | Regular | Secondary content |
|
|
21
|
+
* | labelLarge | 17px | Medium | Large buttons |
|
|
22
|
+
* | labelMedium | 15px | Medium | Buttons, inputs |
|
|
23
|
+
* | labelSmall | 13px | Medium | Small buttons |
|
|
24
|
+
* | caption | 13px | Regular | Help text |
|
|
25
|
+
* | captionSmall | 11px | Regular | Timestamps |
|
|
26
|
+
* | overline | 11px | Semibold | Section labels |
|
|
27
|
+
*
|
|
28
|
+
* @see tokens/typography.ts - Typography token definitions
|
|
29
|
+
* @see core/restyle/restyleTheme.ts - Text variant configurations
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Basic usage
|
|
33
|
+
* <Text variant="bodyMedium">Hello World</Text>
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // With color
|
|
37
|
+
* <Text variant="headingLarge" color="accentPrimary">
|
|
38
|
+
* Welcome
|
|
39
|
+
* </Text>
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // Responsive variant
|
|
43
|
+
* <Text variant={{ phone: "bodyMedium", tablet: "bodyLarge" }}>
|
|
44
|
+
* Responsive text
|
|
45
|
+
* </Text>
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
import { createText } from "@shopify/restyle";
|
|
49
|
+
import { RestyleTheme } from "../../core/restyle";
|
|
50
|
+
|
|
51
|
+
export const Text = createText<RestyleTheme>();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Text";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import type { DesignSystemLabels } from "../i18n";
|
|
3
|
+
import { en } from "../i18n";
|
|
4
|
+
|
|
5
|
+
export interface DesignSystemContextValue {
|
|
6
|
+
labels: DesignSystemLabels;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const DesignSystemContext = createContext<DesignSystemContextValue>({
|
|
10
|
+
labels: en,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Access the design system context.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const { labels } = useDesignSystem();
|
|
18
|
+
* const loadingText = labels.designSystem.button.loadingLabel;
|
|
19
|
+
*/
|
|
20
|
+
export function useDesignSystem(): DesignSystemContextValue {
|
|
21
|
+
return useContext(DesignSystemContext);
|
|
22
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DesignSystemProvider
|
|
3
|
+
*
|
|
4
|
+
* Single provider for the design system. Combines:
|
|
5
|
+
* - Restyle theme (light/dark based on system color scheme)
|
|
6
|
+
* - i18n labels with built-in defaults and optional overrides
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // Basic — English defaults, auto light/dark
|
|
10
|
+
* <DesignSystemProvider>
|
|
11
|
+
* <App />
|
|
12
|
+
* </DesignSystemProvider>
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Reactive to app language
|
|
16
|
+
* const { language } = useAppI18n();
|
|
17
|
+
* <DesignSystemProvider language={language}>
|
|
18
|
+
* <App />
|
|
19
|
+
* </DesignSystemProvider>
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // With partial overrides on top of the active language
|
|
23
|
+
* <DesignSystemProvider language="es" overrides={{ designSystem: { button: { loadingLabel: "Guardando..." } } }}>
|
|
24
|
+
* <App />
|
|
25
|
+
* </DesignSystemProvider>
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // With custom themes from outside the package
|
|
29
|
+
* <DesignSystemProvider lightTheme={myBrandLightTheme} darkTheme={myBrandDarkTheme}>
|
|
30
|
+
* <App />
|
|
31
|
+
* </DesignSystemProvider>
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { ThemeProvider } from "@shopify/restyle";
|
|
35
|
+
import React, { ReactNode, useMemo } from "react";
|
|
36
|
+
import { useColorScheme } from "react-native";
|
|
37
|
+
import type { RestyleTheme } from "../core/restyle";
|
|
38
|
+
import { restyleDarkTheme, restyleLightTheme } from "../core/restyle/restyleTheme";
|
|
39
|
+
import { en, es } from "../i18n";
|
|
40
|
+
import type { DesignSystemLabels, DesignSystemLabelsOverride } from "../i18n";
|
|
41
|
+
import { DesignSystemContext } from "./DesignSystemContext";
|
|
42
|
+
|
|
43
|
+
/** Languages with built-in label sets. */
|
|
44
|
+
export type DesignSystemLanguage = "en" | "es";
|
|
45
|
+
|
|
46
|
+
const builtInLabels: Record<DesignSystemLanguage, DesignSystemLabels> = {
|
|
47
|
+
en,
|
|
48
|
+
es,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export interface DesignSystemProviderProps {
|
|
52
|
+
children: ReactNode;
|
|
53
|
+
/** Active language — switches the built-in label set. Defaults to "en". */
|
|
54
|
+
language?: DesignSystemLanguage;
|
|
55
|
+
/** Partial overrides applied on top of the active language's labels. */
|
|
56
|
+
overrides?: DesignSystemLabelsOverride;
|
|
57
|
+
/**
|
|
58
|
+
* Custom light theme. When provided, replaces the built-in light theme.
|
|
59
|
+
* Must be a valid Restyle theme created with `createTheme`.
|
|
60
|
+
*/
|
|
61
|
+
lightTheme?: RestyleTheme;
|
|
62
|
+
/**
|
|
63
|
+
* Custom dark theme. When provided, replaces the built-in dark theme.
|
|
64
|
+
* Must be a valid Restyle theme created with `createTheme`.
|
|
65
|
+
*/
|
|
66
|
+
darkTheme?: RestyleTheme;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function deepMerge<T extends object>(
|
|
70
|
+
base: T,
|
|
71
|
+
overrides: Record<string, unknown> | undefined,
|
|
72
|
+
): T {
|
|
73
|
+
if (!overrides) return base;
|
|
74
|
+
const result = { ...base };
|
|
75
|
+
const baseRecord = base as Record<string, unknown>;
|
|
76
|
+
for (const key of Object.keys(overrides)) {
|
|
77
|
+
const overrideVal = overrides[key];
|
|
78
|
+
if (
|
|
79
|
+
overrideVal &&
|
|
80
|
+
typeof overrideVal === "object" &&
|
|
81
|
+
!Array.isArray(overrideVal)
|
|
82
|
+
) {
|
|
83
|
+
result[key as keyof T] = deepMerge(
|
|
84
|
+
(baseRecord[key] ?? {}) as Record<string, unknown>,
|
|
85
|
+
overrideVal as Record<string, unknown>,
|
|
86
|
+
) as T[keyof T];
|
|
87
|
+
} else if (overrideVal !== undefined) {
|
|
88
|
+
result[key as keyof T] = overrideVal as T[keyof T];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function DesignSystemProvider({
|
|
95
|
+
children,
|
|
96
|
+
language = "en",
|
|
97
|
+
overrides,
|
|
98
|
+
lightTheme,
|
|
99
|
+
darkTheme,
|
|
100
|
+
}: DesignSystemProviderProps) {
|
|
101
|
+
const isDark = useColorScheme() === "dark";
|
|
102
|
+
|
|
103
|
+
const activeTheme = isDark
|
|
104
|
+
? (darkTheme ?? restyleDarkTheme)
|
|
105
|
+
: (lightTheme ?? restyleLightTheme);
|
|
106
|
+
|
|
107
|
+
const labels = useMemo<DesignSystemLabels>(() => {
|
|
108
|
+
const base = builtInLabels[language] ?? en;
|
|
109
|
+
return overrides ? deepMerge(base, overrides) : base;
|
|
110
|
+
}, [language, overrides]);
|
|
111
|
+
|
|
112
|
+
const contextValue = useMemo(() => ({ labels }), [labels]);
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<DesignSystemContext.Provider value={contextValue}>
|
|
116
|
+
<ThemeProvider theme={activeTheme}>
|
|
117
|
+
{children}
|
|
118
|
+
</ThemeProvider>
|
|
119
|
+
</DesignSystemContext.Provider>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { DesignSystemProvider } from "./DesignSystemProvider";
|
|
2
|
+
export type {
|
|
3
|
+
DesignSystemLanguage,
|
|
4
|
+
DesignSystemProviderProps,
|
|
5
|
+
} from "./DesignSystemProvider";
|
|
6
|
+
export { useDesignSystem } from "./DesignSystemContext";
|
|
7
|
+
export type { DesignSystemContextValue } from "./DesignSystemContext";
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El Sendero Design System - Theme Factory
|
|
3
|
+
*
|
|
4
|
+
* Create custom themes by extending the base theme with overrides.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const myTheme = createTheme(defaultTheme, {
|
|
8
|
+
* colors: {
|
|
9
|
+
* accent: {
|
|
10
|
+
* primary: '#8B5CF6', // Purple accent
|
|
11
|
+
* }
|
|
12
|
+
* }
|
|
13
|
+
* });
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Theme, ThemeOverrides, DeepPartial } from './types';
|
|
17
|
+
import { lightTheme, darkTheme } from './defaultTheme';
|
|
18
|
+
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// DEEP MERGE UTILITY
|
|
21
|
+
// =============================================================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Deep merge two objects, with source values overriding target
|
|
25
|
+
*/
|
|
26
|
+
function deepMerge<T extends object>(
|
|
27
|
+
target: T,
|
|
28
|
+
source: DeepPartial<T>
|
|
29
|
+
): T {
|
|
30
|
+
const result = { ...target };
|
|
31
|
+
|
|
32
|
+
for (const key in source) {
|
|
33
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
34
|
+
const sourceValue = source[key as keyof typeof source];
|
|
35
|
+
const targetValue = (target as Record<string, unknown>)[key];
|
|
36
|
+
|
|
37
|
+
if (
|
|
38
|
+
sourceValue !== null &&
|
|
39
|
+
typeof sourceValue === 'object' &&
|
|
40
|
+
!Array.isArray(sourceValue) &&
|
|
41
|
+
targetValue !== null &&
|
|
42
|
+
typeof targetValue === 'object' &&
|
|
43
|
+
!Array.isArray(targetValue)
|
|
44
|
+
) {
|
|
45
|
+
result[key as keyof T] = deepMerge(
|
|
46
|
+
targetValue as Record<string, unknown>,
|
|
47
|
+
sourceValue as DeepPartial<Record<string, unknown>>
|
|
48
|
+
) as T[keyof T];
|
|
49
|
+
} else if (sourceValue !== undefined) {
|
|
50
|
+
result[key as keyof T] = sourceValue as T[keyof T];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// =============================================================================
|
|
59
|
+
// CREATE THEME
|
|
60
|
+
// =============================================================================
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create a custom theme by extending a base theme with overrides.
|
|
64
|
+
*
|
|
65
|
+
* @param baseTheme - The theme to extend (usually defaultTheme)
|
|
66
|
+
* @param overrides - Partial theme object with your customizations
|
|
67
|
+
* @returns A complete Theme object with your overrides applied
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* // Create a sage-accented theme
|
|
71
|
+
* const sageTheme = createTheme(defaultTheme, {
|
|
72
|
+
* name: 'Sage',
|
|
73
|
+
* colors: {
|
|
74
|
+
* accent: {
|
|
75
|
+
* primary: '#6B8F6B',
|
|
76
|
+
* primaryHover: '#557255',
|
|
77
|
+
* primaryPressed: '#445944',
|
|
78
|
+
* }
|
|
79
|
+
* }
|
|
80
|
+
* });
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* // Create a more rounded theme
|
|
84
|
+
* const softTheme = createTheme(defaultTheme, {
|
|
85
|
+
* radii: {
|
|
86
|
+
* md: 16,
|
|
87
|
+
* lg: 24,
|
|
88
|
+
* xl: 32,
|
|
89
|
+
* }
|
|
90
|
+
* });
|
|
91
|
+
*/
|
|
92
|
+
export function createTheme(
|
|
93
|
+
baseTheme: Theme,
|
|
94
|
+
overrides: ThemeOverrides
|
|
95
|
+
): Theme {
|
|
96
|
+
return deepMerge(baseTheme, overrides);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// =============================================================================
|
|
100
|
+
// CREATE THEME PAIR
|
|
101
|
+
// =============================================================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create both light and dark versions of a custom theme.
|
|
105
|
+
*
|
|
106
|
+
* @param overrides - Overrides to apply to both light and dark themes
|
|
107
|
+
* @param lightOverrides - Additional overrides for light theme only
|
|
108
|
+
* @param darkOverrides - Additional overrides for dark theme only
|
|
109
|
+
* @returns Object with light and dark theme variants
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* const { light, dark } = createThemePair(
|
|
113
|
+
* { name: 'MyBrand' },
|
|
114
|
+
* { colors: { accent: { primary: '#A68B6A' } } }, // Light-specific
|
|
115
|
+
* { colors: { accent: { primary: '#C4A285' } } } // Dark-specific
|
|
116
|
+
* );
|
|
117
|
+
*/
|
|
118
|
+
export function createThemePair(
|
|
119
|
+
overrides: ThemeOverrides = {},
|
|
120
|
+
lightOverrides: ThemeOverrides = {},
|
|
121
|
+
darkOverrides: ThemeOverrides = {}
|
|
122
|
+
): { light: Theme; dark: Theme } {
|
|
123
|
+
return {
|
|
124
|
+
light: createTheme(
|
|
125
|
+
createTheme(lightTheme, overrides),
|
|
126
|
+
lightOverrides
|
|
127
|
+
),
|
|
128
|
+
dark: createTheme(
|
|
129
|
+
createTheme(darkTheme, overrides),
|
|
130
|
+
darkOverrides
|
|
131
|
+
),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// =============================================================================
|
|
136
|
+
// PRESET THEMES
|
|
137
|
+
// =============================================================================
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Horizon Default - Blue with a calming feel
|
|
141
|
+
*/
|
|
142
|
+
export const horizonTheme = createThemePair({
|
|
143
|
+
name: 'Horizon',
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Sage Theme - Green-focused for hope and growth
|
|
148
|
+
*/
|
|
149
|
+
export const sageTheme = createThemePair(
|
|
150
|
+
{ name: 'Sage' },
|
|
151
|
+
{
|
|
152
|
+
colors: {
|
|
153
|
+
accent: {
|
|
154
|
+
primary: '#6B8F6B',
|
|
155
|
+
primaryHover: '#557255',
|
|
156
|
+
primaryPressed: '#445944',
|
|
157
|
+
},
|
|
158
|
+
text: {
|
|
159
|
+
link: '#6B8F6B',
|
|
160
|
+
},
|
|
161
|
+
border: {
|
|
162
|
+
focus: '#6B8F6B',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
colors: {
|
|
168
|
+
accent: {
|
|
169
|
+
primary: '#8FAF8F',
|
|
170
|
+
primaryHover: '#B3C7B3',
|
|
171
|
+
primaryPressed: '#6B8F6B',
|
|
172
|
+
},
|
|
173
|
+
text: {
|
|
174
|
+
link: '#8FAF8F',
|
|
175
|
+
},
|
|
176
|
+
border: {
|
|
177
|
+
focus: '#8FAF8F',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Sunset Theme - Warm coral/peach for energy and comfort
|
|
185
|
+
*/
|
|
186
|
+
export const sunsetTheme = createThemePair(
|
|
187
|
+
{ name: 'Sunset' },
|
|
188
|
+
{
|
|
189
|
+
colors: {
|
|
190
|
+
accent: {
|
|
191
|
+
primary: '#E8836B',
|
|
192
|
+
primaryHover: '#CC6B55',
|
|
193
|
+
primaryPressed: '#A85545',
|
|
194
|
+
},
|
|
195
|
+
text: {
|
|
196
|
+
link: '#E8836B',
|
|
197
|
+
},
|
|
198
|
+
border: {
|
|
199
|
+
focus: '#E8836B',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
colors: {
|
|
205
|
+
accent: {
|
|
206
|
+
primary: '#FF9B85',
|
|
207
|
+
primaryHover: '#FFBAA8',
|
|
208
|
+
primaryPressed: '#E8836B',
|
|
209
|
+
},
|
|
210
|
+
text: {
|
|
211
|
+
link: '#FF9B85',
|
|
212
|
+
},
|
|
213
|
+
border: {
|
|
214
|
+
focus: '#FF9B85',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Ocean Theme - Teal/cyan accents, calming water vibes
|
|
222
|
+
*/
|
|
223
|
+
export const oceanTheme = createThemePair(
|
|
224
|
+
{
|
|
225
|
+
colors: {
|
|
226
|
+
accent: {
|
|
227
|
+
primary: '#0E9AA5',
|
|
228
|
+
primaryHover: '#0B7E87',
|
|
229
|
+
primaryPressed: '#086269',
|
|
230
|
+
},
|
|
231
|
+
text: { link: '#0E9AA5' },
|
|
232
|
+
border: { focus: '#0E9AA5' },
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
colors: {
|
|
237
|
+
accent: {
|
|
238
|
+
primary: '#2DD4BF',
|
|
239
|
+
primaryHover: '#5EEAD4',
|
|
240
|
+
primaryPressed: '#0E9AA5',
|
|
241
|
+
},
|
|
242
|
+
text: { link: '#2DD4BF' },
|
|
243
|
+
border: { focus: '#2DD4BF' },
|
|
244
|
+
},
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Lavender Theme - Soft purple accents, relaxing and spiritual
|
|
250
|
+
*/
|
|
251
|
+
export const lavenderTheme = createThemePair(
|
|
252
|
+
{ name: 'Lavender' },
|
|
253
|
+
{
|
|
254
|
+
colors: {
|
|
255
|
+
accent: {
|
|
256
|
+
primary: '#8B5CF6',
|
|
257
|
+
primaryHover: '#7C3AED',
|
|
258
|
+
primaryPressed: '#6D28D9',
|
|
259
|
+
},
|
|
260
|
+
text: { link: '#8B5CF6' },
|
|
261
|
+
border: { focus: '#8B5CF6' },
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
colors: {
|
|
266
|
+
accent: {
|
|
267
|
+
primary: '#A78BFA',
|
|
268
|
+
primaryHover: '#C4B5FD',
|
|
269
|
+
primaryPressed: '#8B5CF6',
|
|
270
|
+
},
|
|
271
|
+
text: { link: '#A78BFA' },
|
|
272
|
+
border: { focus: '#A78BFA' },
|
|
273
|
+
},
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Rose Theme - Warm pink/rose accents, gentle and nurturing
|
|
279
|
+
*/
|
|
280
|
+
export const roseTheme = createThemePair(
|
|
281
|
+
{ name: 'Rose' },
|
|
282
|
+
{
|
|
283
|
+
colors: {
|
|
284
|
+
accent: {
|
|
285
|
+
primary: '#E11D6C',
|
|
286
|
+
primaryHover: '#BE185D',
|
|
287
|
+
primaryPressed: '#9D174D',
|
|
288
|
+
},
|
|
289
|
+
text: { link: '#E11D6C' },
|
|
290
|
+
border: { focus: '#E11D6C' },
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
colors: {
|
|
295
|
+
accent: {
|
|
296
|
+
primary: '#F472B6',
|
|
297
|
+
primaryHover: '#F9A8D4',
|
|
298
|
+
primaryPressed: '#E11D6C',
|
|
299
|
+
},
|
|
300
|
+
text: { link: '#F472B6' },
|
|
301
|
+
border: { focus: '#F472B6' },
|
|
302
|
+
},
|
|
303
|
+
}
|
|
304
|
+
);
|