@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,45 @@
|
|
|
1
|
+
import { useDesignSystem } from "../../../provider";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { getCalendarStripSizeConfig } from "../CalendarStrip.helpers";
|
|
4
|
+
import { getDayCardA11y } from "./DayCard.a11y";
|
|
5
|
+
import { getDayKey, getMonthKey } from "./DayCard.helpers";
|
|
6
|
+
import {
|
|
7
|
+
UseDayCardLogicParams,
|
|
8
|
+
UseDayCardLogicReturn,
|
|
9
|
+
} from "./DayCard.types";
|
|
10
|
+
|
|
11
|
+
export function useDayCardLogic({
|
|
12
|
+
date,
|
|
13
|
+
onPress,
|
|
14
|
+
size,
|
|
15
|
+
isSelected,
|
|
16
|
+
isToday,
|
|
17
|
+
}: UseDayCardLogicParams): UseDayCardLogicReturn {
|
|
18
|
+
const { labels: t } = useDesignSystem();
|
|
19
|
+
const config = getCalendarStripSizeConfig(size);
|
|
20
|
+
|
|
21
|
+
const dayLabel = t.calendar.days[getDayKey(date)];
|
|
22
|
+
const monthLabel = t.calendar.months[getMonthKey(date)];
|
|
23
|
+
|
|
24
|
+
const handlePress = useCallback(() => onPress(date), [date, onPress]);
|
|
25
|
+
|
|
26
|
+
const a11yProps = getDayCardA11y({
|
|
27
|
+
date,
|
|
28
|
+
isSelected,
|
|
29
|
+
isToday,
|
|
30
|
+
dayLabel,
|
|
31
|
+
monthLabel,
|
|
32
|
+
todayLabel: t.calendar.datePicker.today,
|
|
33
|
+
selectedLabel: t.calendar.datePicker.selected,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
dayNumberVariant: config.dayNumberVariant,
|
|
38
|
+
labelVariant: config.labelVariant,
|
|
39
|
+
dayPaddingVertical: config.dayPaddingVertical,
|
|
40
|
+
handlePress,
|
|
41
|
+
dayLabel,
|
|
42
|
+
monthLabel,
|
|
43
|
+
a11yProps,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { CalendarStrip } from "./CalendarStrip";
|
|
2
|
+
export { getCalendarStripA11y } from "./CalendarStrip.a11y";
|
|
3
|
+
export type {
|
|
4
|
+
BaseCalendarStripProps,
|
|
5
|
+
CalendarStripProps,
|
|
6
|
+
CalendarStripSize,
|
|
7
|
+
} from "./CalendarStrip.types";
|
|
8
|
+
export { DayCard } from "./DayCard";
|
|
9
|
+
export type { DayCardProps } from "./DayCard";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useDesignSystem } from "../../provider";
|
|
2
|
+
import { useResponsiveProp } from "@shopify/restyle";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { getCalendarStripA11y } from "./CalendarStrip.a11y";
|
|
5
|
+
import {
|
|
6
|
+
addDays,
|
|
7
|
+
generateDateRange,
|
|
8
|
+
getCalendarStripSizeConfig,
|
|
9
|
+
} from "./CalendarStrip.helpers";
|
|
10
|
+
import {
|
|
11
|
+
CalendarStripSize,
|
|
12
|
+
UseCalendarStripLogicParams,
|
|
13
|
+
UseCalendarStripLogicReturn,
|
|
14
|
+
} from "./CalendarStrip.types";
|
|
15
|
+
|
|
16
|
+
export function useCalendarStripLogic({
|
|
17
|
+
size,
|
|
18
|
+
selectedDate,
|
|
19
|
+
startDate,
|
|
20
|
+
endDate,
|
|
21
|
+
}: UseCalendarStripLogicParams): UseCalendarStripLogicReturn {
|
|
22
|
+
const { labels: t } = useDesignSystem();
|
|
23
|
+
const resolvedSize = (useResponsiveProp(size) ?? "md") as CalendarStripSize;
|
|
24
|
+
|
|
25
|
+
const config = getCalendarStripSizeConfig(resolvedSize);
|
|
26
|
+
|
|
27
|
+
const today = new Date();
|
|
28
|
+
|
|
29
|
+
const from = startDate ?? today;
|
|
30
|
+
const to = endDate ?? addDays(from, 6);
|
|
31
|
+
|
|
32
|
+
const dates = generateDateRange(from, to);
|
|
33
|
+
|
|
34
|
+
const a11yProps = useMemo(
|
|
35
|
+
() =>
|
|
36
|
+
getCalendarStripA11y({
|
|
37
|
+
label: t.calendar.datePicker.label,
|
|
38
|
+
selectedLabel: t.calendar.datePicker.selected,
|
|
39
|
+
daysShownLabel: t.calendar.datePicker.daysShown,
|
|
40
|
+
selectedDate,
|
|
41
|
+
dateCount: dates.length,
|
|
42
|
+
}),
|
|
43
|
+
[t, selectedDate, dates.length],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
dates,
|
|
48
|
+
today,
|
|
49
|
+
resolvedSize,
|
|
50
|
+
containerGap: config.containerGap,
|
|
51
|
+
a11yProps,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmptyState Component Helpers
|
|
3
|
+
*
|
|
4
|
+
* Pure functions for computing styles and configurations.
|
|
5
|
+
* All values are derived from design tokens for consistency.
|
|
6
|
+
*
|
|
7
|
+
* ## Variant Configurations
|
|
8
|
+
*
|
|
9
|
+
* | Variant | Icon Size | Title Variant | Description Variant | Padding | Gap |
|
|
10
|
+
* |---------|-----------|---------------|---------------------|---------|-----|
|
|
11
|
+
* | default | 2xl (40px)| headingMedium | bodyMedium | xl | lg |
|
|
12
|
+
* | compact | xl (32px) | headingSmall | bodySmall | lg | md |
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
EmptyStateVariant,
|
|
17
|
+
EmptyStateVariantConfig,
|
|
18
|
+
EmptyStateVariantConfigMap,
|
|
19
|
+
} from "./EmptyState.types";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Variant configuration map.
|
|
23
|
+
* Defines styling parameters for each variant.
|
|
24
|
+
*/
|
|
25
|
+
export const EMPTY_STATE_VARIANT_CONFIG: EmptyStateVariantConfigMap = {
|
|
26
|
+
default: {
|
|
27
|
+
iconSize: "2xl",
|
|
28
|
+
titleVariant: "headingMedium",
|
|
29
|
+
descriptionVariant: "bodyMedium",
|
|
30
|
+
containerPadding: "xl",
|
|
31
|
+
contentGap: "lg",
|
|
32
|
+
},
|
|
33
|
+
compact: {
|
|
34
|
+
iconSize: "xl",
|
|
35
|
+
titleVariant: "headingSmall",
|
|
36
|
+
descriptionVariant: "bodySmall",
|
|
37
|
+
containerPadding: "lg",
|
|
38
|
+
contentGap: "md",
|
|
39
|
+
},
|
|
40
|
+
} as const;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get configuration for a specific variant.
|
|
44
|
+
*
|
|
45
|
+
* @param variant - The empty state variant
|
|
46
|
+
* @returns Configuration object with styling parameters
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* getEmptyStateConfig("default")
|
|
50
|
+
* // { iconSize: "2xl", titleVariant: "headingMedium", ... }
|
|
51
|
+
*/
|
|
52
|
+
export function getEmptyStateConfig(variant: EmptyStateVariant): EmptyStateVariantConfig {
|
|
53
|
+
return EMPTY_STATE_VARIANT_CONFIG[variant];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get icon size token based on variant.
|
|
58
|
+
*
|
|
59
|
+
* @param variant - The empty state variant
|
|
60
|
+
* @returns Icon size token
|
|
61
|
+
*/
|
|
62
|
+
export function getIconSize(variant: EmptyStateVariant): "2xl" | "xl" {
|
|
63
|
+
return EMPTY_STATE_VARIANT_CONFIG[variant].iconSize;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get title text variant based on empty state variant.
|
|
68
|
+
*
|
|
69
|
+
* @param variant - The empty state variant
|
|
70
|
+
* @returns Text variant for the title
|
|
71
|
+
*/
|
|
72
|
+
export function getTitleVariant(variant: EmptyStateVariant): "headingSmall" | "headingMedium" {
|
|
73
|
+
return EMPTY_STATE_VARIANT_CONFIG[variant].titleVariant;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get description text variant based on empty state variant.
|
|
78
|
+
*
|
|
79
|
+
* @param variant - The empty state variant
|
|
80
|
+
* @returns Text variant for the description
|
|
81
|
+
*/
|
|
82
|
+
export function getDescriptionVariant(variant: EmptyStateVariant): "bodySmall" | "bodyMedium" {
|
|
83
|
+
return EMPTY_STATE_VARIANT_CONFIG[variant].descriptionVariant;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get container padding token based on variant.
|
|
88
|
+
*
|
|
89
|
+
* @param variant - The empty state variant
|
|
90
|
+
* @returns Spacing token for container padding
|
|
91
|
+
*/
|
|
92
|
+
export function getContainerPadding(variant: EmptyStateVariant): string {
|
|
93
|
+
return EMPTY_STATE_VARIANT_CONFIG[variant].containerPadding;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get content gap token based on variant.
|
|
98
|
+
*
|
|
99
|
+
* @param variant - The empty state variant
|
|
100
|
+
* @returns Spacing token for gap between elements
|
|
101
|
+
*/
|
|
102
|
+
export function getContentGap(variant: EmptyStateVariant): string {
|
|
103
|
+
return EMPTY_STATE_VARIANT_CONFIG[variant].contentGap;
|
|
104
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmptyState Component
|
|
3
|
+
*
|
|
4
|
+
* @description Displays a centered message when content is unavailable - Atom
|
|
5
|
+
*
|
|
6
|
+
* EmptyState provides visual feedback when a list, search, or view has no
|
|
7
|
+
* content to display. It guides users with optional actions to resolve
|
|
8
|
+
* the empty state.
|
|
9
|
+
*
|
|
10
|
+
* ## Variants
|
|
11
|
+
* | Variant | Use Case |
|
|
12
|
+
* |---------|----------|
|
|
13
|
+
* | default | Full-page or section empty states |
|
|
14
|
+
* | compact | Card or constrained space empty states |
|
|
15
|
+
*
|
|
16
|
+
* ## Features
|
|
17
|
+
* - Optional centered icon
|
|
18
|
+
* - Required title text
|
|
19
|
+
* - Optional description text
|
|
20
|
+
* - Optional primary action button
|
|
21
|
+
* - Optional secondary action (text link)
|
|
22
|
+
* - Vertical and horizontal centering
|
|
23
|
+
* - Responsive variant support
|
|
24
|
+
* - Full accessibility support
|
|
25
|
+
*
|
|
26
|
+
* @performance
|
|
27
|
+
* - Wrapped with React.memo() to prevent unnecessary re-renders
|
|
28
|
+
* - Uses useMemo() for configuration lookups and color resolution
|
|
29
|
+
* - Icon and text variant calculated once per variant change
|
|
30
|
+
* - Element visibility flags cached to avoid render-time checks
|
|
31
|
+
*
|
|
32
|
+
* ## Best Practices
|
|
33
|
+
* - Use clear, actionable titles
|
|
34
|
+
* - Provide guidance in the description
|
|
35
|
+
* - Include an action when users can resolve the state
|
|
36
|
+
* - Keep text concise and helpful
|
|
37
|
+
*
|
|
38
|
+
* @see EmptyState.types.ts - Type definitions
|
|
39
|
+
* @see EmptyState.helpers.ts - Variant configurations
|
|
40
|
+
* @see EmptyState.a11y.ts - Accessibility support
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* // Basic usage
|
|
44
|
+
* <EmptyState title="No items found" />
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // With icon and description
|
|
48
|
+
* <EmptyState
|
|
49
|
+
* iconName="inbox"
|
|
50
|
+
* title="Your inbox is empty"
|
|
51
|
+
* description="Messages you receive will appear here"
|
|
52
|
+
* />
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // With actions
|
|
56
|
+
* <EmptyState
|
|
57
|
+
* iconName="search"
|
|
58
|
+
* title="No results"
|
|
59
|
+
* description="Try adjusting your search terms"
|
|
60
|
+
* actionLabel="Clear filters"
|
|
61
|
+
* onAction={handleClearFilters}
|
|
62
|
+
* secondaryActionLabel="Browse all"
|
|
63
|
+
* onSecondaryAction={handleBrowseAll}
|
|
64
|
+
* />
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* // Compact variant
|
|
68
|
+
* <EmptyState
|
|
69
|
+
* variant="compact"
|
|
70
|
+
* title="No notifications"
|
|
71
|
+
* description="You're all caught up!"
|
|
72
|
+
* />
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
import {
|
|
76
|
+
createRestyleComponent,
|
|
77
|
+
layout,
|
|
78
|
+
spacing,
|
|
79
|
+
spacingShorthand,
|
|
80
|
+
} from "@shopify/restyle";
|
|
81
|
+
import React, { memo } from "react";
|
|
82
|
+
import { RestyleTheme } from "../../core/restyle";
|
|
83
|
+
import { Button } from "../../primitives/actions/Button";
|
|
84
|
+
import { Icon } from "../../primitives/content/Icon";
|
|
85
|
+
import { Box } from "../../primitives/layout";
|
|
86
|
+
import { Text } from "../../primitives/typography";
|
|
87
|
+
import { BaseEmptyStateProps, EmptyStateProps } from "./EmptyState.types";
|
|
88
|
+
import { useEmptyStateLogic } from "./useEmptyStateLogic";
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Base EmptyState container with Restyle support.
|
|
92
|
+
* @internal
|
|
93
|
+
*/
|
|
94
|
+
const BaseEmptyStateContainer = createRestyleComponent<
|
|
95
|
+
BaseEmptyStateProps,
|
|
96
|
+
RestyleTheme
|
|
97
|
+
>([layout, spacing, spacingShorthand], Box);
|
|
98
|
+
|
|
99
|
+
function EmptyStateComponent({
|
|
100
|
+
type = "empty",
|
|
101
|
+
iconName,
|
|
102
|
+
title,
|
|
103
|
+
description,
|
|
104
|
+
actionLabel,
|
|
105
|
+
onAction,
|
|
106
|
+
secondaryActionLabel,
|
|
107
|
+
onSecondaryAction,
|
|
108
|
+
variant = "default",
|
|
109
|
+
testID,
|
|
110
|
+
accessibilityLabel,
|
|
111
|
+
...rest
|
|
112
|
+
}: EmptyStateProps) {
|
|
113
|
+
const {
|
|
114
|
+
iconSize,
|
|
115
|
+
iconColor,
|
|
116
|
+
titleColor,
|
|
117
|
+
titleVariant,
|
|
118
|
+
descriptionVariant,
|
|
119
|
+
containerPadding,
|
|
120
|
+
contentGap,
|
|
121
|
+
a11yProps,
|
|
122
|
+
showIcon,
|
|
123
|
+
showAction,
|
|
124
|
+
showSecondaryAction,
|
|
125
|
+
resolvedIconName,
|
|
126
|
+
} = useEmptyStateLogic({
|
|
127
|
+
type,
|
|
128
|
+
variant,
|
|
129
|
+
iconName,
|
|
130
|
+
title,
|
|
131
|
+
description,
|
|
132
|
+
actionLabel,
|
|
133
|
+
secondaryActionLabel,
|
|
134
|
+
accessibilityLabel,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<BaseEmptyStateContainer
|
|
139
|
+
testID={testID}
|
|
140
|
+
flex={1}
|
|
141
|
+
alignItems="center"
|
|
142
|
+
justifyContent="center"
|
|
143
|
+
padding={containerPadding}
|
|
144
|
+
{...a11yProps}
|
|
145
|
+
{...rest}
|
|
146
|
+
>
|
|
147
|
+
<Box alignItems="center" gap={contentGap}>
|
|
148
|
+
{showIcon && (
|
|
149
|
+
<Icon
|
|
150
|
+
name={resolvedIconName}
|
|
151
|
+
size={iconSize}
|
|
152
|
+
color={iconColor}
|
|
153
|
+
accessibilityLabel={resolvedIconName}
|
|
154
|
+
/>
|
|
155
|
+
)}
|
|
156
|
+
|
|
157
|
+
<Box alignItems="center" gap="sm">
|
|
158
|
+
<Text variant={titleVariant} color={titleColor} textAlign="center">
|
|
159
|
+
{title}
|
|
160
|
+
</Text>
|
|
161
|
+
|
|
162
|
+
{description && (
|
|
163
|
+
<Text
|
|
164
|
+
variant={descriptionVariant}
|
|
165
|
+
color="textSecondary"
|
|
166
|
+
textAlign="center"
|
|
167
|
+
>
|
|
168
|
+
{description}
|
|
169
|
+
</Text>
|
|
170
|
+
)}
|
|
171
|
+
</Box>
|
|
172
|
+
|
|
173
|
+
{(showAction || showSecondaryAction) && (
|
|
174
|
+
<Box alignItems="center" gap="md" marginTop="md">
|
|
175
|
+
{showAction && actionLabel && onAction && (
|
|
176
|
+
<Button
|
|
177
|
+
title={actionLabel}
|
|
178
|
+
onPress={onAction}
|
|
179
|
+
variant="solid"
|
|
180
|
+
size="md"
|
|
181
|
+
accessibilityLabel={actionLabel}
|
|
182
|
+
/>
|
|
183
|
+
)}
|
|
184
|
+
|
|
185
|
+
{showSecondaryAction &&
|
|
186
|
+
secondaryActionLabel &&
|
|
187
|
+
onSecondaryAction && (
|
|
188
|
+
<Button
|
|
189
|
+
title={secondaryActionLabel}
|
|
190
|
+
onPress={onSecondaryAction}
|
|
191
|
+
variant="ghost"
|
|
192
|
+
size="md"
|
|
193
|
+
color="accentPrimary"
|
|
194
|
+
accessibilityLabel={secondaryActionLabel}
|
|
195
|
+
/>
|
|
196
|
+
)}
|
|
197
|
+
</Box>
|
|
198
|
+
)}
|
|
199
|
+
</Box>
|
|
200
|
+
</BaseEmptyStateContainer>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const EmptyState = memo(EmptyStateComponent);
|
|
205
|
+
EmptyState.displayName = "EmptyState";
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmptyState Component Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the EmptyState component and its related hooks/helpers.
|
|
5
|
+
* All types are explicitly exported for external consumption.
|
|
6
|
+
*
|
|
7
|
+
* @see EmptyState.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
|
+
import { IconName } from "../../primitives/content/Icon";
|
|
14
|
+
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// VARIANT TYPES
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Visual variant for the EmptyState component.
|
|
21
|
+
* Derived from the restyle theme's `emptyStateVariants` keys.
|
|
22
|
+
*
|
|
23
|
+
* - `default`: Full-size empty state with generous spacing
|
|
24
|
+
* - `compact`: Reduced padding for constrained spaces
|
|
25
|
+
*/
|
|
26
|
+
export type EmptyStateVariant = Exclude<keyof RestyleTheme["emptyStateVariants"], "defaults">;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* State type for the EmptyState component.
|
|
30
|
+
*
|
|
31
|
+
* - `empty`: No content available (neutral)
|
|
32
|
+
* - `error`: Something went wrong (shows error styling)
|
|
33
|
+
*/
|
|
34
|
+
export type EmptyStateType = "empty" | "error";
|
|
35
|
+
|
|
36
|
+
// =============================================================================
|
|
37
|
+
// COMPONENT PROPS
|
|
38
|
+
// =============================================================================
|
|
39
|
+
|
|
40
|
+
/** Base props combining Restyle Box props with ViewProps */
|
|
41
|
+
export type BaseEmptyStateProps = BoxProps<RestyleTheme> & ViewProps;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Props for the EmptyState component.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Basic usage
|
|
48
|
+
* <EmptyState title="No items found" />
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // Full featured
|
|
52
|
+
* <EmptyState
|
|
53
|
+
* iconName="inbox"
|
|
54
|
+
* title="Your inbox is empty"
|
|
55
|
+
* description="Messages you receive will appear here"
|
|
56
|
+
* actionLabel="Compose message"
|
|
57
|
+
* onAction={handleCompose}
|
|
58
|
+
* secondaryActionLabel="Learn more"
|
|
59
|
+
* onSecondaryAction={handleLearnMore}
|
|
60
|
+
* />
|
|
61
|
+
*/
|
|
62
|
+
export interface EmptyStateProps extends Omit<BaseEmptyStateProps, "style"> {
|
|
63
|
+
/**
|
|
64
|
+
* Type of empty state to display.
|
|
65
|
+
* @default "empty"
|
|
66
|
+
*/
|
|
67
|
+
type?: EmptyStateType;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Icon name to display above the title.
|
|
71
|
+
* Uses design system Icon component.
|
|
72
|
+
* If not provided, defaults based on type (inbox for empty, error for error).
|
|
73
|
+
*/
|
|
74
|
+
iconName?: IconName;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Primary title text (required).
|
|
78
|
+
* Should be concise and descriptive.
|
|
79
|
+
*/
|
|
80
|
+
title: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Optional description text below the title.
|
|
84
|
+
* Provides additional context or guidance.
|
|
85
|
+
*/
|
|
86
|
+
description?: string;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Label for the primary action button.
|
|
90
|
+
* When provided with onAction, renders a Button.
|
|
91
|
+
*/
|
|
92
|
+
actionLabel?: string;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Handler for the primary action button.
|
|
96
|
+
* Required if actionLabel is provided.
|
|
97
|
+
*/
|
|
98
|
+
onAction?: () => void;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Label for the secondary action (text link style).
|
|
102
|
+
* When provided with onSecondaryAction, renders a text link.
|
|
103
|
+
*/
|
|
104
|
+
secondaryActionLabel?: string;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Handler for the secondary action.
|
|
108
|
+
* Required if secondaryActionLabel is provided.
|
|
109
|
+
*/
|
|
110
|
+
onSecondaryAction?: () => void;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Visual variant controlling spacing and sizing.
|
|
114
|
+
* @default "default"
|
|
115
|
+
*/
|
|
116
|
+
variant?: ResponsiveValue<EmptyStateVariant, RestyleTheme["breakpoints"]>;
|
|
117
|
+
|
|
118
|
+
/** Test ID for testing frameworks */
|
|
119
|
+
testID?: string;
|
|
120
|
+
|
|
121
|
+
/** Accessibility label for the entire empty state container */
|
|
122
|
+
accessibilityLabel?: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// HOOK TYPES
|
|
127
|
+
// =============================================================================
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Parameters for the useEmptyStateLogic hook.
|
|
131
|
+
*/
|
|
132
|
+
export interface UseEmptyStateLogicParams {
|
|
133
|
+
type: EmptyStateType;
|
|
134
|
+
variant: ResponsiveValue<EmptyStateVariant, RestyleTheme["breakpoints"]>;
|
|
135
|
+
iconName?: IconName;
|
|
136
|
+
title: string;
|
|
137
|
+
description?: string;
|
|
138
|
+
actionLabel?: string;
|
|
139
|
+
secondaryActionLabel?: string;
|
|
140
|
+
accessibilityLabel?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Return value from the useEmptyStateLogic hook.
|
|
145
|
+
*/
|
|
146
|
+
export interface UseEmptyStateLogicReturn {
|
|
147
|
+
/** Resolved icon size token */
|
|
148
|
+
iconSize: "2xl" | "xl";
|
|
149
|
+
/** Icon color token */
|
|
150
|
+
iconColor: keyof RestyleTheme["colors"];
|
|
151
|
+
/** Title color token */
|
|
152
|
+
titleColor: keyof RestyleTheme["colors"];
|
|
153
|
+
/** Title text variant */
|
|
154
|
+
titleVariant: "headingSmall" | "headingMedium";
|
|
155
|
+
/** Description text variant */
|
|
156
|
+
descriptionVariant: "bodySmall" | "bodyMedium";
|
|
157
|
+
/** Container padding token */
|
|
158
|
+
containerPadding: keyof RestyleTheme["spacing"];
|
|
159
|
+
/** Gap between elements */
|
|
160
|
+
contentGap: keyof RestyleTheme["spacing"];
|
|
161
|
+
/** Accessibility props */
|
|
162
|
+
a11yProps: EmptyStateA11yProps;
|
|
163
|
+
/** Whether to show the icon */
|
|
164
|
+
showIcon: boolean;
|
|
165
|
+
/** Whether to show the primary action button */
|
|
166
|
+
showAction: boolean;
|
|
167
|
+
/** Whether to show the secondary action link */
|
|
168
|
+
showSecondaryAction: boolean;
|
|
169
|
+
/** Resolved icon name (with default fallback) */
|
|
170
|
+
resolvedIconName: IconName;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// =============================================================================
|
|
174
|
+
// HELPER TYPES
|
|
175
|
+
// =============================================================================
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Configuration for variant-specific styling.
|
|
179
|
+
*/
|
|
180
|
+
export interface EmptyStateVariantConfig {
|
|
181
|
+
iconSize: "2xl" | "xl";
|
|
182
|
+
titleVariant: "headingSmall" | "headingMedium";
|
|
183
|
+
descriptionVariant: "bodySmall" | "bodyMedium";
|
|
184
|
+
containerPadding: keyof RestyleTheme["spacing"];
|
|
185
|
+
contentGap: keyof RestyleTheme["spacing"];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Map of variant configurations.
|
|
190
|
+
*/
|
|
191
|
+
export type EmptyStateVariantConfigMap = Record<EmptyStateVariant, EmptyStateVariantConfig>;
|
|
192
|
+
|
|
193
|
+
// =============================================================================
|
|
194
|
+
// ACCESSIBILITY TYPES
|
|
195
|
+
// =============================================================================
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Parameters for generating empty state accessibility props.
|
|
199
|
+
*/
|
|
200
|
+
export interface EmptyStateA11yParams {
|
|
201
|
+
title: string;
|
|
202
|
+
description?: string;
|
|
203
|
+
accessibilityLabel?: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Accessibility props returned by getEmptyStateA11y.
|
|
208
|
+
*/
|
|
209
|
+
export interface EmptyStateA11yProps {
|
|
210
|
+
accessible: boolean;
|
|
211
|
+
accessibilityRole: "text";
|
|
212
|
+
accessibilityLabel: string;
|
|
213
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmptyState Component
|
|
3
|
+
*
|
|
4
|
+
* @description Displays a centered message when content is unavailable
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { EmptyState } from "@/design-system";
|
|
8
|
+
*
|
|
9
|
+
* // Basic usage
|
|
10
|
+
* <EmptyState title="No items found" />
|
|
11
|
+
*
|
|
12
|
+
* // With icon and actions
|
|
13
|
+
* <EmptyState
|
|
14
|
+
* iconName="inbox"
|
|
15
|
+
* title="Your inbox is empty"
|
|
16
|
+
* description="Messages will appear here"
|
|
17
|
+
* actionLabel="Compose"
|
|
18
|
+
* onAction={handleCompose}
|
|
19
|
+
* />
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export { EmptyState } from "./EmptyState";
|
|
23
|
+
export type {
|
|
24
|
+
BaseEmptyStateProps,
|
|
25
|
+
EmptyStateA11yParams,
|
|
26
|
+
EmptyStateA11yProps,
|
|
27
|
+
EmptyStateProps,
|
|
28
|
+
EmptyStateVariant,
|
|
29
|
+
EmptyStateVariantConfig,
|
|
30
|
+
EmptyStateVariantConfigMap,
|
|
31
|
+
UseEmptyStateLogicParams,
|
|
32
|
+
UseEmptyStateLogicReturn,
|
|
33
|
+
} from "./EmptyState.types";
|
|
34
|
+
export {
|
|
35
|
+
EMPTY_STATE_VARIANT_CONFIG,
|
|
36
|
+
getContainerPadding,
|
|
37
|
+
getContentGap,
|
|
38
|
+
getDescriptionVariant,
|
|
39
|
+
getEmptyStateConfig,
|
|
40
|
+
getIconSize,
|
|
41
|
+
getTitleVariant,
|
|
42
|
+
} from "./EmptyState.helpers";
|
|
43
|
+
export { getEmptyStateA11y } from "./EmptyState.a11y";
|
|
44
|
+
export { useEmptyStateLogic } from "./useEmptyStateLogic";
|