@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
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@praxiis/ui",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"sideEffects": false,
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.mjs",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"react-native": "./src/index.tsx",
|
|
14
|
+
"import": {
|
|
15
|
+
"types": "./dist/index.d.mts",
|
|
16
|
+
"default": "./dist/index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"require": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"src",
|
|
28
|
+
"!src/**/__tests__",
|
|
29
|
+
"!src/**/*.test.*",
|
|
30
|
+
"!src/**/*.a11y.*",
|
|
31
|
+
"!src/**/*.md",
|
|
32
|
+
"!dist/*.map"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"dev": "tsup --watch",
|
|
37
|
+
"clean": "rm -rf dist",
|
|
38
|
+
"check-types": "tsc --noEmit",
|
|
39
|
+
"test": "jest",
|
|
40
|
+
"test:watch": "jest --watch",
|
|
41
|
+
"test:coverage": "jest --coverage"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@expo/vector-icons": "^15.1.1",
|
|
45
|
+
"@shopify/restyle": "^2.4.5",
|
|
46
|
+
"react": "19.2.0",
|
|
47
|
+
"react-native": "0.83.4",
|
|
48
|
+
"react-native-safe-area-context": "~5.6.2"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@babel/core": "^7.29.0",
|
|
52
|
+
"@babel/preset-env": "^7.29.2",
|
|
53
|
+
"@babel/preset-flow": "^7.27.1",
|
|
54
|
+
"@babel/preset-react": "^7.28.5",
|
|
55
|
+
"@babel/preset-typescript": "^7.28.5",
|
|
56
|
+
"@expo/vector-icons": "^15.1.1",
|
|
57
|
+
"@react-native/babel-preset": "0.83.2",
|
|
58
|
+
"@shopify/restyle": "^2.4.5",
|
|
59
|
+
"@testing-library/react-native": "^13.3.3",
|
|
60
|
+
"@types/jest": "^30.0.0",
|
|
61
|
+
"@types/node": "^25.4.0",
|
|
62
|
+
"@types/react": "~19.2.2",
|
|
63
|
+
"babel-jest": "^30.3.0",
|
|
64
|
+
"jest": "^30.3.0",
|
|
65
|
+
"react-native-safe-area-context": "~5.6.2",
|
|
66
|
+
"react-test-renderer": "19.2.0",
|
|
67
|
+
"tsup": "^8.4.0",
|
|
68
|
+
"typescript": "~5.9.2"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { DesignSystemProvider } from "../provider";
|
|
4
|
+
import { restyleLightTheme } from "../core/restyle/restyleTheme";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Standard test wrapper providing the DesignSystemProvider with default theme.
|
|
8
|
+
*/
|
|
9
|
+
export const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
10
|
+
<DesignSystemProvider>{children}</DesignSystemProvider>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extended theme with sample app-specific color tokens for testing AppThemeColors support.
|
|
15
|
+
*/
|
|
16
|
+
export const extendedTheme = {
|
|
17
|
+
...restyleLightTheme,
|
|
18
|
+
colors: {
|
|
19
|
+
...restyleLightTheme.colors,
|
|
20
|
+
pokemonFire: "#F08030",
|
|
21
|
+
pokemonWater: "#6890F0",
|
|
22
|
+
pokemonGrass: "#78C850",
|
|
23
|
+
pokemonElectric: "#F8D030",
|
|
24
|
+
},
|
|
25
|
+
} as typeof restyleLightTheme;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Test wrapper providing the DesignSystemProvider with extended theme colors.
|
|
29
|
+
* Used to verify components work with AppThemeColors augmentation.
|
|
30
|
+
*/
|
|
31
|
+
export const ExtendedThemeWrapper = ({
|
|
32
|
+
children,
|
|
33
|
+
}: {
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
}) => (
|
|
36
|
+
<DesignSystemProvider lightTheme={extendedTheme}>
|
|
37
|
+
{children}
|
|
38
|
+
</DesignSystemProvider>
|
|
39
|
+
);
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CalendarStrip Component Helpers
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for date arithmetic and size configuration.
|
|
5
|
+
* DayCard-specific helpers (key lookups) live in DayCard/DayCard.helpers.ts.
|
|
6
|
+
*
|
|
7
|
+
* ## Size Configurations
|
|
8
|
+
*
|
|
9
|
+
* | Size | Container Gap | Day Padding V | Day Number | Label |
|
|
10
|
+
* |------|---------------|---------------|---------------|-------------|
|
|
11
|
+
* | sm | xs | xs | labelSmall | captionSmall|
|
|
12
|
+
* | md | md | md | labelMedium | captionSmall|
|
|
13
|
+
* | lg | lg | lg | labelLarge | caption |
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
CalendarStripSize,
|
|
18
|
+
CalendarStripSizeConfig,
|
|
19
|
+
CalendarStripSizeConfigMap,
|
|
20
|
+
} from "./CalendarStrip.types";
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// DATE UTILITIES
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns true when two dates fall on the same calendar day.
|
|
28
|
+
*/
|
|
29
|
+
export function isSameDay(a: Date, b: Date): boolean {
|
|
30
|
+
return (
|
|
31
|
+
a.getDate() === b.getDate() &&
|
|
32
|
+
a.getMonth() === b.getMonth() &&
|
|
33
|
+
a.getFullYear() === b.getFullYear()
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns a new Date that is `days` days after `date`.
|
|
39
|
+
* Does not mutate the input.
|
|
40
|
+
*/
|
|
41
|
+
export function addDays(date: Date, days: number): Date {
|
|
42
|
+
const result = new Date(date);
|
|
43
|
+
result.setDate(result.getDate() + days);
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Generates an inclusive array of Date objects from `start` to `end`.
|
|
49
|
+
* Time components are stripped so comparisons are day-level.
|
|
50
|
+
*/
|
|
51
|
+
export function generateDateRange(start: Date, end: Date): Date[] {
|
|
52
|
+
const dates: Date[] = [];
|
|
53
|
+
const current = new Date(
|
|
54
|
+
start.getFullYear(),
|
|
55
|
+
start.getMonth(),
|
|
56
|
+
start.getDate(),
|
|
57
|
+
);
|
|
58
|
+
const last = new Date(
|
|
59
|
+
end.getFullYear(),
|
|
60
|
+
end.getMonth(),
|
|
61
|
+
end.getDate(),
|
|
62
|
+
).getTime();
|
|
63
|
+
while (current.getTime() <= last) {
|
|
64
|
+
dates.push(new Date(current));
|
|
65
|
+
current.setDate(current.getDate() + 1);
|
|
66
|
+
}
|
|
67
|
+
return dates;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// =============================================================================
|
|
71
|
+
// SIZE CONFIGURATION
|
|
72
|
+
// =============================================================================
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Size configuration map.
|
|
76
|
+
* Values mirror the calendarStripSizes entries in restyleTheme.ts.
|
|
77
|
+
*/
|
|
78
|
+
export const CALENDAR_STRIP_SIZE_CONFIG: CalendarStripSizeConfigMap = {
|
|
79
|
+
sm: {
|
|
80
|
+
containerGap: "xs",
|
|
81
|
+
dayPaddingVertical: "xs",
|
|
82
|
+
dayNumberVariant: "labelSmall",
|
|
83
|
+
labelVariant: "captionSmall",
|
|
84
|
+
},
|
|
85
|
+
md: {
|
|
86
|
+
containerGap: "md",
|
|
87
|
+
dayPaddingVertical: "md",
|
|
88
|
+
dayNumberVariant: "labelMedium",
|
|
89
|
+
labelVariant: "captionSmall",
|
|
90
|
+
},
|
|
91
|
+
lg: {
|
|
92
|
+
containerGap: "lg",
|
|
93
|
+
dayPaddingVertical: "lg",
|
|
94
|
+
dayNumberVariant: "labelLarge",
|
|
95
|
+
labelVariant: "caption",
|
|
96
|
+
},
|
|
97
|
+
} as const;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Returns the resolved styling configuration for a given size.
|
|
101
|
+
*/
|
|
102
|
+
export function getCalendarStripSizeConfig(
|
|
103
|
+
size: CalendarStripSize,
|
|
104
|
+
): CalendarStripSizeConfig {
|
|
105
|
+
return CALENDAR_STRIP_SIZE_CONFIG[size];
|
|
106
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { RestyleTheme } from "../../core/restyle";
|
|
2
|
+
import { Box } from "../../primitives";
|
|
3
|
+
import {
|
|
4
|
+
createRestyleComponent,
|
|
5
|
+
layout,
|
|
6
|
+
spacing,
|
|
7
|
+
spacingShorthand,
|
|
8
|
+
} from "@shopify/restyle";
|
|
9
|
+
import React, { memo, useCallback } from "react";
|
|
10
|
+
import { isSameDay } from "./CalendarStrip.helpers";
|
|
11
|
+
import {
|
|
12
|
+
BaseCalendarStripProps,
|
|
13
|
+
CalendarStripProps,
|
|
14
|
+
} from "./CalendarStrip.types";
|
|
15
|
+
import { DayCard } from "./DayCard";
|
|
16
|
+
import { useCalendarStripLogic } from "./useCalendarStripLogic";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Base CalendarStrip container with Restyle support.
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
const BaseCalendarStripContainer = createRestyleComponent<
|
|
23
|
+
BaseCalendarStripProps,
|
|
24
|
+
RestyleTheme
|
|
25
|
+
>([layout, spacing, spacingShorthand], Box);
|
|
26
|
+
|
|
27
|
+
function CalendarStripComponent({
|
|
28
|
+
selectedDate,
|
|
29
|
+
onDateSelected,
|
|
30
|
+
startDate,
|
|
31
|
+
endDate,
|
|
32
|
+
showDayName = true,
|
|
33
|
+
showMonthName = true,
|
|
34
|
+
size = "md",
|
|
35
|
+
testID,
|
|
36
|
+
...restyleProps
|
|
37
|
+
}: CalendarStripProps) {
|
|
38
|
+
const { dates, today, resolvedSize, containerGap, a11yProps } = useCalendarStripLogic({
|
|
39
|
+
size,
|
|
40
|
+
selectedDate,
|
|
41
|
+
startDate,
|
|
42
|
+
endDate,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const renderDay = useCallback(
|
|
46
|
+
(date: Date) => (
|
|
47
|
+
<Box key={date.toISOString()} flex={1}>
|
|
48
|
+
<DayCard
|
|
49
|
+
date={date}
|
|
50
|
+
isSelected={isSameDay(date, selectedDate)}
|
|
51
|
+
isToday={isSameDay(date, today)}
|
|
52
|
+
onPress={onDateSelected}
|
|
53
|
+
showDayName={showDayName}
|
|
54
|
+
showMonthName={showMonthName}
|
|
55
|
+
size={resolvedSize}
|
|
56
|
+
/>
|
|
57
|
+
</Box>
|
|
58
|
+
),
|
|
59
|
+
[
|
|
60
|
+
selectedDate,
|
|
61
|
+
today,
|
|
62
|
+
onDateSelected,
|
|
63
|
+
showDayName,
|
|
64
|
+
showMonthName,
|
|
65
|
+
resolvedSize,
|
|
66
|
+
],
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<BaseCalendarStripContainer
|
|
71
|
+
testID={testID}
|
|
72
|
+
flexDirection="row"
|
|
73
|
+
gap={containerGap}
|
|
74
|
+
{...a11yProps}
|
|
75
|
+
{...restyleProps}
|
|
76
|
+
>
|
|
77
|
+
{dates.map(renderDay)}
|
|
78
|
+
</BaseCalendarStripContainer>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const CalendarStrip = memo(CalendarStripComponent);
|
|
83
|
+
CalendarStrip.displayName = "CalendarStrip";
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CalendarStrip Component Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the CalendarStrip component and its logic hook.
|
|
5
|
+
* DayCard-specific types live in DayCard/DayCard.types.ts.
|
|
6
|
+
*
|
|
7
|
+
* @see CalendarStrip.tsx - Component implementation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BoxProps, ResponsiveValue } from "@shopify/restyle";
|
|
11
|
+
import { ViewProps } from "react-native";
|
|
12
|
+
import { RestyleTheme } from "../../core/restyle";
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// SIZE TYPES
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Size variants for the CalendarStrip component.
|
|
20
|
+
*
|
|
21
|
+
* - `sm`: Compact strip — tight spacing, small text; ideal for modals or sidebars
|
|
22
|
+
* - `md`: Standard strip — default spacing and text sizes
|
|
23
|
+
* - `lg`: Prominent strip — generous spacing, large text; ideal for full-page views
|
|
24
|
+
*/
|
|
25
|
+
export type CalendarStripSize = "sm" | "md" | "lg";
|
|
26
|
+
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// CONFIG TYPES
|
|
29
|
+
// =============================================================================
|
|
30
|
+
|
|
31
|
+
/** Resolved styling parameters for a given size. */
|
|
32
|
+
export interface CalendarStripSizeConfig {
|
|
33
|
+
/** Gap token between day cards in the container. */
|
|
34
|
+
containerGap: keyof RestyleTheme["spacing"];
|
|
35
|
+
/** Vertical padding token inside each day card. */
|
|
36
|
+
dayPaddingVertical: keyof RestyleTheme["spacing"];
|
|
37
|
+
/** Text variant for the day number (e.g. "19"). */
|
|
38
|
+
dayNumberVariant: "labelSmall" | "labelMedium" | "labelLarge";
|
|
39
|
+
/** Text variant for the day/month labels (e.g. "Mon", "Feb"). */
|
|
40
|
+
labelVariant: "captionSmall" | "caption";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Map of size configurations. */
|
|
44
|
+
export type CalendarStripSizeConfigMap = Record<
|
|
45
|
+
CalendarStripSize,
|
|
46
|
+
CalendarStripSizeConfig
|
|
47
|
+
>;
|
|
48
|
+
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// COMPONENT PROPS
|
|
51
|
+
// =============================================================================
|
|
52
|
+
|
|
53
|
+
/** Base props combining Restyle Box props with ViewProps. */
|
|
54
|
+
export type BaseCalendarStripProps = BoxProps<RestyleTheme> & ViewProps;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Props for the CalendarStrip component.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // Basic usage — 7-day strip starting today
|
|
61
|
+
* <CalendarStrip selectedDate={date} onDateSelected={setDate} />
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Custom range with compact size
|
|
65
|
+
* <CalendarStrip
|
|
66
|
+
* selectedDate={date}
|
|
67
|
+
* onDateSelected={setDate}
|
|
68
|
+
* startDate={rangeStart}
|
|
69
|
+
* endDate={rangeEnd}
|
|
70
|
+
* size="sm"
|
|
71
|
+
* />
|
|
72
|
+
*/
|
|
73
|
+
export interface CalendarStripProps
|
|
74
|
+
extends Omit<BaseCalendarStripProps, "style"> {
|
|
75
|
+
/** Currently selected date. */
|
|
76
|
+
selectedDate: Date;
|
|
77
|
+
|
|
78
|
+
/** Called when the user taps a day. */
|
|
79
|
+
onDateSelected: (date: Date) => void;
|
|
80
|
+
|
|
81
|
+
/** First visible date. Defaults to today. */
|
|
82
|
+
startDate?: Date;
|
|
83
|
+
|
|
84
|
+
/** Last visible date. Defaults to startDate + 6 days. */
|
|
85
|
+
endDate?: Date;
|
|
86
|
+
|
|
87
|
+
/** Whether to display the day name (e.g. "Mon"). Defaults to true. */
|
|
88
|
+
showDayName?: boolean;
|
|
89
|
+
|
|
90
|
+
/** Whether to display the month name (e.g. "Feb"). Defaults to true. */
|
|
91
|
+
showMonthName?: boolean;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Size variant controlling spacing and text scale.
|
|
95
|
+
* @default "md"
|
|
96
|
+
*/
|
|
97
|
+
size?: ResponsiveValue<CalendarStripSize, RestyleTheme["breakpoints"]>;
|
|
98
|
+
|
|
99
|
+
/** Test ID for testing frameworks. */
|
|
100
|
+
testID?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// HOOK TYPES
|
|
105
|
+
// =============================================================================
|
|
106
|
+
|
|
107
|
+
/** Parameters for the useCalendarStripLogic hook. */
|
|
108
|
+
export interface UseCalendarStripLogicParams {
|
|
109
|
+
size: ResponsiveValue<CalendarStripSize, RestyleTheme["breakpoints"]>;
|
|
110
|
+
selectedDate: Date;
|
|
111
|
+
startDate?: Date;
|
|
112
|
+
endDate?: Date;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Return value from the useCalendarStripLogic hook. */
|
|
116
|
+
export interface UseCalendarStripLogicReturn {
|
|
117
|
+
/** Ordered list of dates to display. */
|
|
118
|
+
dates: Date[];
|
|
119
|
+
/** Today's date (memoized). */
|
|
120
|
+
today: Date;
|
|
121
|
+
/** Resolved (non-responsive) size. */
|
|
122
|
+
resolvedSize: CalendarStripSize;
|
|
123
|
+
/** Gap token between day cards. */
|
|
124
|
+
containerGap: keyof RestyleTheme["spacing"];
|
|
125
|
+
/** Accessibility props for the container. */
|
|
126
|
+
a11yProps: {
|
|
127
|
+
accessible: boolean;
|
|
128
|
+
accessibilityRole: "list";
|
|
129
|
+
accessibilityLabel: string;
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DayCard Component Helpers
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for deriving i18n keys from a Date.
|
|
5
|
+
*
|
|
6
|
+
* @see DayCard.tsx - Component implementation
|
|
7
|
+
* @see CalendarStrip.helpers.ts - Date arithmetic and size configuration
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { DayKey, MonthKey } from "./DayCard.types";
|
|
11
|
+
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// KEY LOOKUP TABLES
|
|
14
|
+
// =============================================================================
|
|
15
|
+
|
|
16
|
+
const DAY_KEYS: DayKey[] = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
|
|
17
|
+
const MONTH_KEYS: MonthKey[] = [
|
|
18
|
+
"jan", "feb", "mar", "apr", "may", "jun",
|
|
19
|
+
"jul", "aug", "sep", "oct", "nov", "dec",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// KEY HELPERS
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns the i18n day key for a given date.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* getDayKey(new Date(2026, 1, 19)) // "thu"
|
|
31
|
+
*/
|
|
32
|
+
export function getDayKey(date: Date): DayKey {
|
|
33
|
+
return DAY_KEYS[date.getDay()];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Returns the i18n month key for a given date.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* getMonthKey(new Date(2026, 1, 19)) // "feb"
|
|
41
|
+
*/
|
|
42
|
+
export function getMonthKey(date: Date): MonthKey {
|
|
43
|
+
return MONTH_KEYS[date.getMonth()];
|
|
44
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { BoxRestyleProps } from "../../../primitives";
|
|
2
|
+
import { Box, Pressable, Text } from "../../../primitives";
|
|
3
|
+
import React, { memo } from "react";
|
|
4
|
+
import { DayCardProps } from "./DayCard.types";
|
|
5
|
+
import { useDayCardLogic } from "./useDayCardLogic";
|
|
6
|
+
|
|
7
|
+
export const DayCard = memo(function DayCard({
|
|
8
|
+
date,
|
|
9
|
+
isSelected,
|
|
10
|
+
isToday,
|
|
11
|
+
onPress,
|
|
12
|
+
showDayName = true,
|
|
13
|
+
showMonthName = true,
|
|
14
|
+
size = "md",
|
|
15
|
+
...restyleProps
|
|
16
|
+
}: DayCardProps) {
|
|
17
|
+
const {
|
|
18
|
+
dayLabel,
|
|
19
|
+
monthLabel,
|
|
20
|
+
dayNumberVariant,
|
|
21
|
+
labelVariant,
|
|
22
|
+
dayPaddingVertical,
|
|
23
|
+
handlePress,
|
|
24
|
+
a11yProps,
|
|
25
|
+
} = useDayCardLogic({ date, isSelected, isToday, onPress, size });
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Pressable onPress={handlePress} {...a11yProps}>
|
|
29
|
+
<Box
|
|
30
|
+
alignItems="center"
|
|
31
|
+
justifyContent="center"
|
|
32
|
+
paddingVertical={dayPaddingVertical}
|
|
33
|
+
borderRadius="lg"
|
|
34
|
+
backgroundColor={isSelected ? "accentPrimary" : "backgroundSecondary"}
|
|
35
|
+
{...(restyleProps as BoxRestyleProps)}
|
|
36
|
+
>
|
|
37
|
+
{showDayName && (
|
|
38
|
+
<Text
|
|
39
|
+
variant={labelVariant}
|
|
40
|
+
color={isSelected ? "textInverse" : "textTertiary"}
|
|
41
|
+
>
|
|
42
|
+
{dayLabel}
|
|
43
|
+
</Text>
|
|
44
|
+
)}
|
|
45
|
+
|
|
46
|
+
<Box
|
|
47
|
+
borderBottomWidth={2}
|
|
48
|
+
borderColor={isToday && !isSelected ? "accentPrimary" : "transparent"}
|
|
49
|
+
paddingHorizontal="2xs"
|
|
50
|
+
>
|
|
51
|
+
<Text
|
|
52
|
+
variant={dayNumberVariant}
|
|
53
|
+
color={isSelected ? "textInverse" : "textPrimary"}
|
|
54
|
+
>
|
|
55
|
+
{date.getDate()}
|
|
56
|
+
</Text>
|
|
57
|
+
</Box>
|
|
58
|
+
|
|
59
|
+
{showMonthName && (
|
|
60
|
+
<Text
|
|
61
|
+
variant={labelVariant}
|
|
62
|
+
color={isSelected ? "textInverse" : "textTertiary"}
|
|
63
|
+
>
|
|
64
|
+
{monthLabel}
|
|
65
|
+
</Text>
|
|
66
|
+
)}
|
|
67
|
+
</Box>
|
|
68
|
+
</Pressable>
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
DayCard.displayName = "DayCard";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DayCard Component Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the DayCard sub-component and its logic hook.
|
|
5
|
+
* All types are explicitly exported for external consumption.
|
|
6
|
+
*
|
|
7
|
+
* @see DayCard.tsx - Component implementation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { BoxProps } from "@shopify/restyle";
|
|
11
|
+
import { ViewProps } from "react-native";
|
|
12
|
+
import {
|
|
13
|
+
CalendarStripSize,
|
|
14
|
+
CalendarStripSizeConfig,
|
|
15
|
+
} from "../CalendarStrip.types";
|
|
16
|
+
import { RestyleTheme } from "../../../core/restyle";
|
|
17
|
+
|
|
18
|
+
// Re-export shared types used by consumers of DayCard
|
|
19
|
+
export type { CalendarStripSize, CalendarStripSizeConfig };
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// KEY TYPES
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
export type DayKey = "sun" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat";
|
|
26
|
+
export type MonthKey =
|
|
27
|
+
| "jan"
|
|
28
|
+
| "feb"
|
|
29
|
+
| "mar"
|
|
30
|
+
| "apr"
|
|
31
|
+
| "may"
|
|
32
|
+
| "jun"
|
|
33
|
+
| "jul"
|
|
34
|
+
| "aug"
|
|
35
|
+
| "sep"
|
|
36
|
+
| "oct"
|
|
37
|
+
| "nov"
|
|
38
|
+
| "dec";
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// COMPONENT PROPS
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
/** Base props combining Restyle Box props with ViewProps. */
|
|
45
|
+
export type BaseDayCardProps = BoxProps<RestyleTheme> & ViewProps;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Props for the DayCard sub-component.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* <DayCard
|
|
52
|
+
* date={new Date()}
|
|
53
|
+
* isSelected={true}
|
|
54
|
+
* isToday={true}
|
|
55
|
+
* onPress={handlePress}
|
|
56
|
+
* size="md"
|
|
57
|
+
* />
|
|
58
|
+
*/
|
|
59
|
+
export interface DayCardProps extends Omit<BaseDayCardProps, "style"> {
|
|
60
|
+
/** The date this card represents. */
|
|
61
|
+
date: Date;
|
|
62
|
+
|
|
63
|
+
/** Whether this day is currently selected. */
|
|
64
|
+
isSelected: boolean;
|
|
65
|
+
|
|
66
|
+
/** Whether this day is today. */
|
|
67
|
+
isToday: boolean;
|
|
68
|
+
|
|
69
|
+
/** Called when the card is pressed. */
|
|
70
|
+
onPress: (date: Date) => void;
|
|
71
|
+
|
|
72
|
+
/** Whether to display the day name (e.g. "Mon"). Defaults to true. */
|
|
73
|
+
showDayName?: boolean;
|
|
74
|
+
|
|
75
|
+
/** Whether to display the month name (e.g. "Feb"). Defaults to true. */
|
|
76
|
+
showMonthName?: boolean;
|
|
77
|
+
|
|
78
|
+
/** Size inherited from the parent CalendarStrip. Defaults to "md". */
|
|
79
|
+
size?: CalendarStripSize;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// =============================================================================
|
|
83
|
+
// HOOK TYPES
|
|
84
|
+
// =============================================================================
|
|
85
|
+
|
|
86
|
+
/** Parameters for the useDayCardLogic hook. */
|
|
87
|
+
export interface UseDayCardLogicParams {
|
|
88
|
+
date: Date;
|
|
89
|
+
isSelected: boolean;
|
|
90
|
+
isToday: boolean;
|
|
91
|
+
onPress: (date: Date) => void;
|
|
92
|
+
size: CalendarStripSize;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Return value from the useDayCardLogic hook. */
|
|
96
|
+
export interface UseDayCardLogicReturn {
|
|
97
|
+
/** Text variant for the day number. */
|
|
98
|
+
dayNumberVariant: "labelSmall" | "labelMedium" | "labelLarge";
|
|
99
|
+
/** Text variant for day/month labels. */
|
|
100
|
+
labelVariant: "captionSmall" | "caption";
|
|
101
|
+
/** Vertical padding token for the day card container. */
|
|
102
|
+
dayPaddingVertical: keyof RestyleTheme["spacing"];
|
|
103
|
+
/** Stable press handler. */
|
|
104
|
+
handlePress: () => void;
|
|
105
|
+
/** Translated day label (e.g. "Mon"). */
|
|
106
|
+
dayLabel: string;
|
|
107
|
+
/** Translated month label (e.g. "Feb"). */
|
|
108
|
+
monthLabel: string;
|
|
109
|
+
/** Accessibility props for the pressable. */
|
|
110
|
+
a11yProps: DayCardA11yProps;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// =============================================================================
|
|
114
|
+
// ACCESSIBILITY TYPES
|
|
115
|
+
// =============================================================================
|
|
116
|
+
|
|
117
|
+
/** Parameters for generating DayCard accessibility props. */
|
|
118
|
+
export interface DayCardA11yParams {
|
|
119
|
+
date: Date;
|
|
120
|
+
isSelected: boolean;
|
|
121
|
+
isToday: boolean;
|
|
122
|
+
dayLabel: string;
|
|
123
|
+
monthLabel: string;
|
|
124
|
+
todayLabel: string;
|
|
125
|
+
selectedLabel: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Accessibility props for the DayCard pressable. */
|
|
129
|
+
export interface DayCardA11yProps {
|
|
130
|
+
accessible: boolean;
|
|
131
|
+
accessibilityRole: "button";
|
|
132
|
+
accessibilityLabel: string;
|
|
133
|
+
accessibilityState: { selected: boolean };
|
|
134
|
+
}
|