@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,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmptyState Logic Hook
|
|
3
|
+
*
|
|
4
|
+
* Extracts all computational logic from the EmptyState component.
|
|
5
|
+
* Returns memoized values for optimal render performance.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities:
|
|
8
|
+
* - Resolve responsive variant prop
|
|
9
|
+
* - Calculate styling configuration based on variant
|
|
10
|
+
* - Determine visibility of optional elements
|
|
11
|
+
* - Provide accessibility props
|
|
12
|
+
*
|
|
13
|
+
* @see EmptyState.tsx - Component consuming this hook
|
|
14
|
+
* @see EmptyState.helpers.ts - Pure calculation functions
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useResponsiveProp } from "@shopify/restyle";
|
|
18
|
+
import { useMemo } from "react";
|
|
19
|
+
import { getEmptyStateA11y } from "./EmptyState.a11y";
|
|
20
|
+
import { getEmptyStateConfig } from "./EmptyState.helpers";
|
|
21
|
+
import { EmptyStateVariant, UseEmptyStateLogicParams, UseEmptyStateLogicReturn } from "./EmptyState.types";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Orchestrates EmptyState rendering logic and state management.
|
|
25
|
+
*
|
|
26
|
+
* Handles:
|
|
27
|
+
* - Responsive variant resolution
|
|
28
|
+
* - Configuration lookup (icon size, text variants, spacing)
|
|
29
|
+
* - Icon resolution (custom or type-based defaults)
|
|
30
|
+
* - Color determination based on type (error vs default)
|
|
31
|
+
* - Element visibility flags (icon, actions)
|
|
32
|
+
* - Accessibility props generation
|
|
33
|
+
*
|
|
34
|
+
* @param params - Configuration object for empty state behavior
|
|
35
|
+
* @param params.type - Semantic type ('default' | 'error')
|
|
36
|
+
* @param params.variant - Size variant ('default' | 'compact')
|
|
37
|
+
* @param params.iconName - Optional custom icon
|
|
38
|
+
* @param params.title - Title text
|
|
39
|
+
* @param params.description - Description text
|
|
40
|
+
* @param params.actionLabel - Primary action button label
|
|
41
|
+
* @param params.secondaryActionLabel - Secondary action button label
|
|
42
|
+
* @param params.accessibilityLabel - Custom a11y label
|
|
43
|
+
*
|
|
44
|
+
* @returns Computed values for rendering EmptyState
|
|
45
|
+
* @returns {number} iconSize - Icon size in pixels
|
|
46
|
+
* @returns {string} iconColor - Icon color token
|
|
47
|
+
* @returns {string} titleColor - Title color token
|
|
48
|
+
* @returns {string} titleVariant - Title text variant
|
|
49
|
+
* @returns {string} descriptionVariant - Description text variant
|
|
50
|
+
* @returns {number} containerPadding - Container padding in pixels
|
|
51
|
+
* @returns {number} contentGap - Gap between elements in pixels
|
|
52
|
+
* @returns {object} a11yProps - Accessibility props
|
|
53
|
+
* @returns {boolean} showIcon - Whether to show icon
|
|
54
|
+
* @returns {boolean} showAction - Whether to show primary action
|
|
55
|
+
* @returns {boolean} showSecondaryAction - Whether to show secondary action
|
|
56
|
+
* @returns {string} resolvedIconName - Resolved icon name
|
|
57
|
+
*
|
|
58
|
+
* @performance This hook uses useMemo() for configuration lookups
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const {
|
|
62
|
+
* iconSize,
|
|
63
|
+
* iconColor,
|
|
64
|
+
* titleVariant,
|
|
65
|
+
* showIcon,
|
|
66
|
+
* showAction,
|
|
67
|
+
* resolvedIconName,
|
|
68
|
+
* } = useEmptyStateLogic({
|
|
69
|
+
* variant: "default",
|
|
70
|
+
* iconName: "inbox",
|
|
71
|
+
* title: "No messages",
|
|
72
|
+
* description: "Your inbox is empty",
|
|
73
|
+
* actionLabel: "Compose",
|
|
74
|
+
* secondaryActionLabel: "Learn more",
|
|
75
|
+
* });
|
|
76
|
+
*/
|
|
77
|
+
export function useEmptyStateLogic({
|
|
78
|
+
type,
|
|
79
|
+
variant,
|
|
80
|
+
iconName,
|
|
81
|
+
title,
|
|
82
|
+
description,
|
|
83
|
+
actionLabel,
|
|
84
|
+
secondaryActionLabel,
|
|
85
|
+
accessibilityLabel,
|
|
86
|
+
}: UseEmptyStateLogicParams): UseEmptyStateLogicReturn {
|
|
87
|
+
// Resolve responsive variant
|
|
88
|
+
const resolvedVariant = (useResponsiveProp(variant) ?? "default") as EmptyStateVariant;
|
|
89
|
+
|
|
90
|
+
// Get variant configuration
|
|
91
|
+
const config = getEmptyStateConfig(resolvedVariant);
|
|
92
|
+
|
|
93
|
+
// Determine icon based on type if not provided
|
|
94
|
+
const resolvedIconName = iconName || (type === "error" ? "error" : "inbox");
|
|
95
|
+
|
|
96
|
+
// Determine colors based on type
|
|
97
|
+
const iconColor = type === "error" ? "feedbackError" : "textSecondary";
|
|
98
|
+
|
|
99
|
+
const titleColor = type === "error" ? "feedbackError" : "textPrimary";
|
|
100
|
+
|
|
101
|
+
// Determine element visibility
|
|
102
|
+
const showIcon = resolvedIconName !== undefined;
|
|
103
|
+
const showAction = actionLabel !== undefined;
|
|
104
|
+
const showSecondaryAction = secondaryActionLabel !== undefined;
|
|
105
|
+
|
|
106
|
+
// Accessibility props
|
|
107
|
+
const a11yProps = useMemo(
|
|
108
|
+
() =>
|
|
109
|
+
getEmptyStateA11y({
|
|
110
|
+
title,
|
|
111
|
+
description,
|
|
112
|
+
accessibilityLabel,
|
|
113
|
+
}),
|
|
114
|
+
[title, description, accessibilityLabel]
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
iconSize: config.iconSize,
|
|
119
|
+
iconColor,
|
|
120
|
+
titleColor,
|
|
121
|
+
titleVariant: config.titleVariant,
|
|
122
|
+
descriptionVariant: config.descriptionVariant,
|
|
123
|
+
containerPadding: config.containerPadding,
|
|
124
|
+
contentGap: config.contentGap,
|
|
125
|
+
a11yProps,
|
|
126
|
+
showIcon,
|
|
127
|
+
showAction,
|
|
128
|
+
showSecondaryAction,
|
|
129
|
+
resolvedIconName,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header Component Helpers
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for computing Header dimensions, sizes, and mappings.
|
|
5
|
+
*
|
|
6
|
+
* ## Size Configuration
|
|
7
|
+
* | Header Size | Height | Text Variant | IconButton |
|
|
8
|
+
* |-------------|--------|---------------|------------|
|
|
9
|
+
* | sm | 44px | headingSmall | sm (36px) |
|
|
10
|
+
* | md | 56px | headingMedium | md (44px) |
|
|
11
|
+
* | lg | 64px | headingLarge | lg (56px) |
|
|
12
|
+
*
|
|
13
|
+
* ## Safe Area Calculation
|
|
14
|
+
* Total height = base height + safe area top inset (when enabled)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { ResponsiveValue } from "@shopify/restyle";
|
|
18
|
+
import { ViewStyle } from "react-native";
|
|
19
|
+
import { RestyleTheme } from "../../core/restyle";
|
|
20
|
+
import { IconButtonSize } from "../../primitives/actions/IconButton";
|
|
21
|
+
import {
|
|
22
|
+
HeaderDimensionsResult,
|
|
23
|
+
HeaderSize,
|
|
24
|
+
HeaderTextVariant,
|
|
25
|
+
ResolveHeaderDimensionsParams,
|
|
26
|
+
} from "./Header.types";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Maps header size to text variant.
|
|
30
|
+
*/
|
|
31
|
+
export const SIZE_TO_TEXT_VARIANT: Record<HeaderSize, HeaderTextVariant> = {
|
|
32
|
+
sm: "headingSmall",
|
|
33
|
+
md: "headingMedium",
|
|
34
|
+
lg: "headingLarge",
|
|
35
|
+
} as const;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Maps Header size to IconButton size for proper visual alignment.
|
|
39
|
+
*
|
|
40
|
+
* | Header | IconButton | Ratio |
|
|
41
|
+
* |--------|------------|-------|
|
|
42
|
+
* | sm 44px| sm 36px | 82% |
|
|
43
|
+
* | md 56px| md 44px | 79% |
|
|
44
|
+
* | lg 64px| lg 56px | 87% |
|
|
45
|
+
*
|
|
46
|
+
* @param headerSize - Header size (supports responsive values)
|
|
47
|
+
* @returns Corresponding IconButton size
|
|
48
|
+
*/
|
|
49
|
+
export function getHeaderIconButtonSize(
|
|
50
|
+
headerSize: ResponsiveValue<HeaderSize, RestyleTheme["breakpoints"]>,
|
|
51
|
+
): ResponsiveValue<IconButtonSize, RestyleTheme["breakpoints"]> {
|
|
52
|
+
// If responsive, map each breakpoint
|
|
53
|
+
if (typeof headerSize === "object") {
|
|
54
|
+
return {
|
|
55
|
+
phone:
|
|
56
|
+
headerSize.phone === "sm"
|
|
57
|
+
? "sm"
|
|
58
|
+
: headerSize.phone === "lg"
|
|
59
|
+
? "lg"
|
|
60
|
+
: "md",
|
|
61
|
+
tablet:
|
|
62
|
+
headerSize.tablet === "sm"
|
|
63
|
+
? "sm"
|
|
64
|
+
: headerSize.tablet === "lg"
|
|
65
|
+
? "lg"
|
|
66
|
+
: "md",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// If single value, map directly
|
|
70
|
+
return headerSize === "sm" ? "sm" : headerSize === "lg" ? "lg" : "md";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const BACK_BUTTON_HIT_SLOP = {
|
|
74
|
+
top: 8,
|
|
75
|
+
bottom: 8,
|
|
76
|
+
left: 8,
|
|
77
|
+
right: 8,
|
|
78
|
+
} as const;
|
|
79
|
+
|
|
80
|
+
export function resolveHeaderDimensions(
|
|
81
|
+
params: ResolveHeaderDimensionsParams,
|
|
82
|
+
): HeaderDimensionsResult {
|
|
83
|
+
const { resolvedSize, safeAreaTop, topInset, theme } = params;
|
|
84
|
+
const { height } = theme.headerSizes[resolvedSize];
|
|
85
|
+
const topPadding = safeAreaTop ? topInset : 0;
|
|
86
|
+
const totalHeight = height + topPadding;
|
|
87
|
+
|
|
88
|
+
return { topPadding, totalHeight };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function resolveContainerStyle(topPadding: number): ViewStyle {
|
|
92
|
+
return { paddingTop: topPadding };
|
|
93
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header Component
|
|
3
|
+
*
|
|
4
|
+
* @description Navigation header with title and actions - Organism
|
|
5
|
+
*
|
|
6
|
+
* Header is a complex component combining multiple atoms and molecules
|
|
7
|
+
* into a complete navigation bar with back button, title, and actions.
|
|
8
|
+
*
|
|
9
|
+
* ## Size Scale
|
|
10
|
+
* | Size | Height | Text Variant | IconButton |
|
|
11
|
+
* |------|--------|--------------|------------|
|
|
12
|
+
* | sm | 44px | headingSmall | sm (36px) |
|
|
13
|
+
* | md | 56px | headingMedium| md (44px) |
|
|
14
|
+
* | lg | 64px | headingLarge | lg (56px) |
|
|
15
|
+
*
|
|
16
|
+
* ## Safe Area Handling
|
|
17
|
+
* When `safeAreaTop={true}` (default), the header adds top padding
|
|
18
|
+
* for the device safe area (notch/status bar). Total height = size + insets.top.
|
|
19
|
+
*
|
|
20
|
+
* ## Back Button Behavior
|
|
21
|
+
* - Shows when `showBackButton={true}` AND `onBackPress` is provided
|
|
22
|
+
* - Calls `onBackPress` when pressed
|
|
23
|
+
* - Accessibility labels from i18n translations
|
|
24
|
+
*
|
|
25
|
+
* ## Features
|
|
26
|
+
* - Responsive size prop (phone/tablet breakpoints)
|
|
27
|
+
* - Optional back button with smart visibility
|
|
28
|
+
* - Custom left/right items for actions
|
|
29
|
+
* - Safe area aware (status bar + notch)
|
|
30
|
+
* - Full accessibility support (header role)
|
|
31
|
+
*
|
|
32
|
+
* @performance
|
|
33
|
+
* - Wrapped with React.memo() to prevent unnecessary re-renders
|
|
34
|
+
* - Uses useMemo() for dimension calculations with safe area insets
|
|
35
|
+
* - Back button visibility computed once per canGoBack() change
|
|
36
|
+
* - Container styles cached to avoid style object recreation
|
|
37
|
+
* - Integrates with expo-router for efficient navigation state
|
|
38
|
+
*
|
|
39
|
+
* @see Header.types.ts - Type definitions
|
|
40
|
+
* @see Header.helpers.ts - Size and dimension functions
|
|
41
|
+
* @see Header.a11y.ts - Accessibility prop generation
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // Basic usage
|
|
45
|
+
* <Header title="Settings" />
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* // With actions
|
|
49
|
+
* <Header
|
|
50
|
+
* title="Profile"
|
|
51
|
+
* size={{ phone: "md", tablet: "lg" }}
|
|
52
|
+
* rightItems={
|
|
53
|
+
* <HeaderRightItems>
|
|
54
|
+
* <IconButton iconName="edit" onPress={handleEdit} accessibilityLabel="Edit" />
|
|
55
|
+
* </HeaderRightItems>
|
|
56
|
+
* }
|
|
57
|
+
* />
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // Custom back handler
|
|
61
|
+
* <Header
|
|
62
|
+
* title="Checkout"
|
|
63
|
+
* onBackPress={handleConfirmCancel}
|
|
64
|
+
* accessibilityLabel="Checkout header"
|
|
65
|
+
* />
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
import { RestyleTheme } from "../../core/restyle";
|
|
69
|
+
import { Box, IconButton, Text } from "../../primitives";
|
|
70
|
+
import {
|
|
71
|
+
backgroundColor,
|
|
72
|
+
border,
|
|
73
|
+
createRestyleComponent,
|
|
74
|
+
createVariant,
|
|
75
|
+
layout,
|
|
76
|
+
opacity,
|
|
77
|
+
spacing,
|
|
78
|
+
spacingShorthand,
|
|
79
|
+
} from "@shopify/restyle";
|
|
80
|
+
import React, { memo } from "react";
|
|
81
|
+
import { getHeaderIconButtonSize } from "./Header.helpers";
|
|
82
|
+
import {
|
|
83
|
+
BaseHeaderContainerProps,
|
|
84
|
+
HeaderProps,
|
|
85
|
+
HeaderRightItemsProps,
|
|
86
|
+
} from "./Header.types";
|
|
87
|
+
import { useHeaderLogic } from "./useHeaderLogic";
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Base container with Restyle variant support.
|
|
91
|
+
* @internal
|
|
92
|
+
*/
|
|
93
|
+
const BaseHeaderContainer = createRestyleComponent<
|
|
94
|
+
BaseHeaderContainerProps,
|
|
95
|
+
RestyleTheme
|
|
96
|
+
>(
|
|
97
|
+
[
|
|
98
|
+
createVariant({ themeKey: "headerVariants" }),
|
|
99
|
+
createVariant({ themeKey: "headerSizes", property: "size" }),
|
|
100
|
+
backgroundColor,
|
|
101
|
+
border,
|
|
102
|
+
layout,
|
|
103
|
+
opacity,
|
|
104
|
+
spacing,
|
|
105
|
+
spacingShorthand,
|
|
106
|
+
],
|
|
107
|
+
Box,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
function HeaderComponent({
|
|
111
|
+
title,
|
|
112
|
+
accessibilityLabel,
|
|
113
|
+
accessibilityHint,
|
|
114
|
+
size = "md",
|
|
115
|
+
variant,
|
|
116
|
+
showBackButton = true,
|
|
117
|
+
rightItems,
|
|
118
|
+
leftItems,
|
|
119
|
+
safeAreaTop = true,
|
|
120
|
+
onBackPress,
|
|
121
|
+
testID,
|
|
122
|
+
...rest
|
|
123
|
+
}: HeaderProps) {
|
|
124
|
+
const {
|
|
125
|
+
showBack,
|
|
126
|
+
textVariant,
|
|
127
|
+
dimensions,
|
|
128
|
+
containerStyle,
|
|
129
|
+
backButtonA11yProps,
|
|
130
|
+
a11yContainerProps,
|
|
131
|
+
handleBack,
|
|
132
|
+
} = useHeaderLogic({
|
|
133
|
+
accessibilityLabel,
|
|
134
|
+
accessibilityHint,
|
|
135
|
+
size,
|
|
136
|
+
showBackButton,
|
|
137
|
+
safeAreaTop,
|
|
138
|
+
onBackPress,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<BaseHeaderContainer
|
|
143
|
+
height={dimensions.totalHeight}
|
|
144
|
+
variant={variant}
|
|
145
|
+
style={containerStyle}
|
|
146
|
+
testID={testID}
|
|
147
|
+
{...a11yContainerProps}
|
|
148
|
+
{...rest}
|
|
149
|
+
>
|
|
150
|
+
{showBack && (
|
|
151
|
+
<IconButton
|
|
152
|
+
iconName="arrow-back"
|
|
153
|
+
onPress={handleBack}
|
|
154
|
+
variant="ghost"
|
|
155
|
+
size={getHeaderIconButtonSize(size)}
|
|
156
|
+
accessibilityLabel={
|
|
157
|
+
backButtonA11yProps.accessibilityLabel ?? "Go back"
|
|
158
|
+
}
|
|
159
|
+
accessibilityHint={backButtonA11yProps.accessibilityHint}
|
|
160
|
+
/>
|
|
161
|
+
)}
|
|
162
|
+
{leftItems}
|
|
163
|
+
{title && (
|
|
164
|
+
<Text flex={1} numberOfLines={1} variant={textVariant}>
|
|
165
|
+
{title}
|
|
166
|
+
</Text>
|
|
167
|
+
)}
|
|
168
|
+
{rightItems}
|
|
169
|
+
</BaseHeaderContainer>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function HeaderRightItemsComponent({ children }: HeaderRightItemsProps) {
|
|
174
|
+
return (
|
|
175
|
+
<Box flexDirection="row" alignItems="center" gap="xs">
|
|
176
|
+
{children}
|
|
177
|
+
</Box>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const Header = memo(HeaderComponent);
|
|
182
|
+
Header.displayName = "Header";
|
|
183
|
+
|
|
184
|
+
export const HeaderRightItems = memo(HeaderRightItemsComponent);
|
|
185
|
+
HeaderRightItems.displayName = "HeaderRightItems";
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header Component Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the Header component and its related hooks/helpers.
|
|
5
|
+
*
|
|
6
|
+
* @see Header.tsx - Component implementation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { BoxProps, ResponsiveValue, VariantProps } from "@shopify/restyle";
|
|
10
|
+
import { ReactNode } from "react";
|
|
11
|
+
import { AccessibilityRole, ViewProps, ViewStyle } from "react-native";
|
|
12
|
+
import { RestyleTheme } from "../../core/restyle";
|
|
13
|
+
|
|
14
|
+
type HeaderVariantProps = VariantProps<
|
|
15
|
+
RestyleTheme,
|
|
16
|
+
"headerVariants",
|
|
17
|
+
"variant"
|
|
18
|
+
>;
|
|
19
|
+
type HeaderSizeProps = VariantProps<RestyleTheme, "headerSizes", "size">;
|
|
20
|
+
type HeaderRestyleProps = HeaderVariantProps &
|
|
21
|
+
HeaderSizeProps &
|
|
22
|
+
BoxProps<RestyleTheme>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Size variants for the Header.
|
|
26
|
+
*
|
|
27
|
+
* | Size | Height | Text Variant | Use Case |
|
|
28
|
+
* |------|--------|---------------|----------|
|
|
29
|
+
* | sm | 44px | headingSmall | Compact screens |
|
|
30
|
+
* | md | 56px | headingMedium | Default mobile |
|
|
31
|
+
* | lg | 64px | headingLarge | Tablets, emphasis |
|
|
32
|
+
*/
|
|
33
|
+
export type HeaderSize = Exclude<
|
|
34
|
+
keyof RestyleTheme["headerSizes"],
|
|
35
|
+
"defaults"
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
/** Visual variants derived from theme headerVariants */
|
|
39
|
+
export type HeaderVariant = Exclude<
|
|
40
|
+
keyof RestyleTheme["headerVariants"],
|
|
41
|
+
"defaults"
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
/** @internal Base container props for Restyle integration */
|
|
45
|
+
export type BaseHeaderContainerProps = HeaderRestyleProps & ViewProps;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Props for the Header component.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* <Header
|
|
52
|
+
* title="Settings"
|
|
53
|
+
* size="md"
|
|
54
|
+
* showBackButton
|
|
55
|
+
* rightItems={<IconButton iconName="more-vert" onPress={handleMore} accessibilityLabel="More" />}
|
|
56
|
+
* />
|
|
57
|
+
*/
|
|
58
|
+
export interface HeaderProps extends Omit<HeaderRestyleProps, "size"> {
|
|
59
|
+
/** Title text displayed in header */
|
|
60
|
+
title?: string;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Size of the header (supports responsive values).
|
|
64
|
+
* @default "md"
|
|
65
|
+
*/
|
|
66
|
+
size?: ResponsiveValue<HeaderSize, RestyleTheme["breakpoints"]>;
|
|
67
|
+
|
|
68
|
+
/** Accessibility label for the header container */
|
|
69
|
+
accessibilityLabel?: string;
|
|
70
|
+
|
|
71
|
+
/** Accessibility hint for additional context */
|
|
72
|
+
accessibilityHint?: string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Whether to show back button (only shows when `onBackPress` is also provided).
|
|
76
|
+
* @default true
|
|
77
|
+
*/
|
|
78
|
+
showBackButton?: boolean;
|
|
79
|
+
|
|
80
|
+
/** Content rendered on the right side of the header */
|
|
81
|
+
rightItems?: ReactNode;
|
|
82
|
+
|
|
83
|
+
/** Content rendered on the left side (after back button) */
|
|
84
|
+
leftItems?: ReactNode;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Whether to add safe area top padding.
|
|
88
|
+
* @default true
|
|
89
|
+
*/
|
|
90
|
+
safeAreaTop?: boolean;
|
|
91
|
+
|
|
92
|
+
/** Back button press handler — back button only renders when this is provided */
|
|
93
|
+
onBackPress?: () => void;
|
|
94
|
+
|
|
95
|
+
/** Test ID for testing frameworks */
|
|
96
|
+
testID?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface HeaderRightItemsProps {
|
|
100
|
+
children: ReactNode;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type HeaderTextVariant =
|
|
104
|
+
| "headingSmall"
|
|
105
|
+
| "headingMedium"
|
|
106
|
+
| "headingLarge";
|
|
107
|
+
|
|
108
|
+
export interface UseHeaderLogicParams {
|
|
109
|
+
accessibilityLabel: string | undefined;
|
|
110
|
+
accessibilityHint: string | undefined;
|
|
111
|
+
size: ResponsiveValue<HeaderSize, RestyleTheme["breakpoints"]>;
|
|
112
|
+
showBackButton: boolean;
|
|
113
|
+
safeAreaTop: boolean;
|
|
114
|
+
onBackPress: (() => void) | undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface UseHeaderLogicReturn {
|
|
118
|
+
handleBack: () => void;
|
|
119
|
+
showBack: boolean;
|
|
120
|
+
dimensions: HeaderDimensionsResult;
|
|
121
|
+
containerStyle: ViewStyle;
|
|
122
|
+
a11yContainerProps: HeaderA11yProps;
|
|
123
|
+
textVariant: HeaderTextVariant;
|
|
124
|
+
backButtonA11yProps: HeaderA11yProps;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface ResolveHeaderDimensionsParams {
|
|
128
|
+
resolvedSize: HeaderSize;
|
|
129
|
+
safeAreaTop: boolean;
|
|
130
|
+
topInset: number;
|
|
131
|
+
theme: RestyleTheme;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface HeaderDimensionsResult {
|
|
135
|
+
topPadding: number;
|
|
136
|
+
totalHeight: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface HeaderA11yParams {
|
|
140
|
+
accessibilityLabel?: string;
|
|
141
|
+
accessibilityHint?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface HeaderBackButtonA11yParams {
|
|
145
|
+
accessibilityLabel?: string;
|
|
146
|
+
accessibilityHint?: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface HeaderA11yProps {
|
|
150
|
+
accessibilityRole: AccessibilityRole;
|
|
151
|
+
accessibilityLabel?: string;
|
|
152
|
+
accessibilityHint?: string;
|
|
153
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header Module
|
|
3
|
+
*
|
|
4
|
+
* Organism component for navigation headers with title and actions.
|
|
5
|
+
*
|
|
6
|
+
* Combines IconButton, Text, and Box atoms into a complete
|
|
7
|
+
* navigation bar with safe area awareness.
|
|
8
|
+
*
|
|
9
|
+
* ## Components
|
|
10
|
+
* - **Header**: Main navigation header
|
|
11
|
+
* - **HeaderRightItems**: Container for right-side action buttons
|
|
12
|
+
*
|
|
13
|
+
* ## Size Scale
|
|
14
|
+
* | Size | Height | Use Case |
|
|
15
|
+
* |------|--------|----------|
|
|
16
|
+
* | sm | 44px | Compact screens |
|
|
17
|
+
* | md | 56px | Default mobile |
|
|
18
|
+
* | lg | 64px | Tablets |
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* import { Header, HeaderRightItems } from "@/design-system/primitives/navigation/Header";
|
|
22
|
+
*
|
|
23
|
+
* <Header
|
|
24
|
+
* title="Profile"
|
|
25
|
+
* rightItems={
|
|
26
|
+
* <HeaderRightItems>
|
|
27
|
+
* <IconButton iconName="edit" onPress={handleEdit} accessibilityLabel="Edit" />
|
|
28
|
+
* </HeaderRightItems>
|
|
29
|
+
* }
|
|
30
|
+
* />
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export { Header, HeaderRightItems } from "./Header";
|
|
34
|
+
|
|
35
|
+
export type {
|
|
36
|
+
HeaderProps,
|
|
37
|
+
HeaderSize,
|
|
38
|
+
HeaderVariant,
|
|
39
|
+
HeaderRightItemsProps,
|
|
40
|
+
HeaderA11yParams,
|
|
41
|
+
HeaderA11yProps,
|
|
42
|
+
} from "./Header.types";
|
|
43
|
+
|
|
44
|
+
export { getHeaderA11y, getHeaderBackButtonA11y } from "./Header.a11y";
|