@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.
Files changed (187) hide show
  1. package/dist/index.d.mts +52556 -0
  2. package/dist/index.d.ts +52556 -0
  3. package/dist/index.js +8753 -0
  4. package/dist/index.mjs +8777 -0
  5. package/package.json +70 -0
  6. package/src/__test-utils__/index.tsx +39 -0
  7. package/src/components/CalendarStrip/CalendarStrip.helpers.ts +106 -0
  8. package/src/components/CalendarStrip/CalendarStrip.tsx +83 -0
  9. package/src/components/CalendarStrip/CalendarStrip.types.ts +133 -0
  10. package/src/components/CalendarStrip/DayCard/DayCard.helpers.ts +44 -0
  11. package/src/components/CalendarStrip/DayCard/DayCard.tsx +71 -0
  12. package/src/components/CalendarStrip/DayCard/DayCard.types.ts +134 -0
  13. package/src/components/CalendarStrip/DayCard/index.ts +2 -0
  14. package/src/components/CalendarStrip/DayCard/useDayCardLogic.ts +45 -0
  15. package/src/components/CalendarStrip/index.ts +9 -0
  16. package/src/components/CalendarStrip/useCalendarStripLogic.ts +53 -0
  17. package/src/components/EmptyState/EmptyState.helpers.ts +104 -0
  18. package/src/components/EmptyState/EmptyState.tsx +205 -0
  19. package/src/components/EmptyState/EmptyState.types.ts +213 -0
  20. package/src/components/EmptyState/index.ts +44 -0
  21. package/src/components/EmptyState/useEmptyStateLogic.ts +131 -0
  22. package/src/components/Header/Header.helpers.ts +93 -0
  23. package/src/components/Header/Header.tsx +185 -0
  24. package/src/components/Header/Header.types.ts +153 -0
  25. package/src/components/Header/index.ts +44 -0
  26. package/src/components/Header/useHeaderLogic.ts +146 -0
  27. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.helpers.ts +50 -0
  28. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.tsx +78 -0
  29. package/src/components/ScheduleItem/ScheduleItem/ScheduleItem.types.ts +99 -0
  30. package/src/components/ScheduleItem/ScheduleItem/index.ts +16 -0
  31. package/src/components/ScheduleItem/ScheduleItem/useScheduleItemLogic.ts +31 -0
  32. package/src/components/ScheduleItem/index.ts +15 -0
  33. package/src/components/index.ts +40 -0
  34. package/src/core/index.ts +34 -0
  35. package/src/core/restyle/RestyleThemeProviderWrapper.tsx +31 -0
  36. package/src/core/restyle/index.ts +38 -0
  37. package/src/core/restyle/restylePresetRegistry.ts +195 -0
  38. package/src/core/restyle/restyleTheme.ts +1352 -0
  39. package/src/core/restyle/restyleTypes.ts +8 -0
  40. package/src/core/restyle/useRestyleTheme.ts +10 -0
  41. package/src/hooks/animations/index.ts +3 -0
  42. package/src/hooks/animations/useAnimatedValue.ts +10 -0
  43. package/src/hooks/animations/useEntranceAnimation.ts +106 -0
  44. package/src/hooks/animations/usePulseAnimation.ts +63 -0
  45. package/src/hooks/index.ts +30 -0
  46. package/src/hooks/useReducedMotion.ts +60 -0
  47. package/src/i18n/index.ts +2 -0
  48. package/src/i18n/labels/en.ts +120 -0
  49. package/src/i18n/labels/es.ts +120 -0
  50. package/src/i18n/labels/index.ts +6 -0
  51. package/src/i18n/labels/types.ts +165 -0
  52. package/src/index.tsx +215 -0
  53. package/src/primitives/actions/Button/Button.helpers.ts +243 -0
  54. package/src/primitives/actions/Button/Button.tsx +198 -0
  55. package/src/primitives/actions/Button/Button.types.ts +207 -0
  56. package/src/primitives/actions/Button/index.ts +41 -0
  57. package/src/primitives/actions/Button/useButtonLogic.ts +160 -0
  58. package/src/primitives/actions/IconButton/IconButton.helpers.ts +235 -0
  59. package/src/primitives/actions/IconButton/IconButton.tsx +177 -0
  60. package/src/primitives/actions/IconButton/IconButton.types.ts +273 -0
  61. package/src/primitives/actions/IconButton/index.ts +30 -0
  62. package/src/primitives/actions/IconButton/useIconButtonLogic.ts +172 -0
  63. package/src/primitives/actions/index.ts +20 -0
  64. package/src/primitives/content/Avatar/Avatar.helpers.ts +177 -0
  65. package/src/primitives/content/Avatar/Avatar.tsx +199 -0
  66. package/src/primitives/content/Avatar/Avatar.types.ts +222 -0
  67. package/src/primitives/content/Avatar/index.ts +46 -0
  68. package/src/primitives/content/Avatar/useAvatarLogic.ts +149 -0
  69. package/src/primitives/content/Badge/Badge.helpers.ts +175 -0
  70. package/src/primitives/content/Badge/Badge.tsx +174 -0
  71. package/src/primitives/content/Badge/Badge.types.ts +223 -0
  72. package/src/primitives/content/Badge/index.ts +40 -0
  73. package/src/primitives/content/Badge/useBadgeLogic.ts +128 -0
  74. package/src/primitives/content/Card/Card.helpers.ts +27 -0
  75. package/src/primitives/content/Card/Card.tsx +123 -0
  76. package/src/primitives/content/Card/Card.types.ts +95 -0
  77. package/src/primitives/content/Card/index.ts +20 -0
  78. package/src/primitives/content/Card/useCardLogic.ts +48 -0
  79. package/src/primitives/content/Chip/Chip.helpers.ts +304 -0
  80. package/src/primitives/content/Chip/Chip.tsx +205 -0
  81. package/src/primitives/content/Chip/Chip.types.ts +234 -0
  82. package/src/primitives/content/Chip/index.ts +47 -0
  83. package/src/primitives/content/Chip/useChipLogic.ts +167 -0
  84. package/src/primitives/content/Icon/Icon.helpers.ts +54 -0
  85. package/src/primitives/content/Icon/Icon.tsx +110 -0
  86. package/src/primitives/content/Icon/Icon.types.ts +95 -0
  87. package/src/primitives/content/Icon/index.ts +20 -0
  88. package/src/primitives/content/Icon/useIconLogic.ts +73 -0
  89. package/src/primitives/content/index.ts +45 -0
  90. package/src/primitives/feedback/ProgressBar/ProgressBar.helpers.ts +122 -0
  91. package/src/primitives/feedback/ProgressBar/ProgressBar.tsx +154 -0
  92. package/src/primitives/feedback/ProgressBar/ProgressBar.types.ts +178 -0
  93. package/src/primitives/feedback/ProgressBar/index.ts +17 -0
  94. package/src/primitives/feedback/ProgressBar/useProgressBarLogic.ts +120 -0
  95. package/src/primitives/feedback/Skeleton/Skeleton.helpers.ts +145 -0
  96. package/src/primitives/feedback/Skeleton/Skeleton.tsx +155 -0
  97. package/src/primitives/feedback/Skeleton/Skeleton.types.ts +223 -0
  98. package/src/primitives/feedback/Skeleton/index.ts +44 -0
  99. package/src/primitives/feedback/Skeleton/useSkeletonLogic.ts +125 -0
  100. package/src/primitives/feedback/Spinner/Spinner.helpers.ts +40 -0
  101. package/src/primitives/feedback/Spinner/Spinner.tsx +105 -0
  102. package/src/primitives/feedback/Spinner/Spinner.types.ts +114 -0
  103. package/src/primitives/feedback/Spinner/index.ts +18 -0
  104. package/src/primitives/feedback/Spinner/useSpinnerLogic.ts +84 -0
  105. package/src/primitives/feedback/Toast/Toast.helpers.ts +163 -0
  106. package/src/primitives/feedback/Toast/Toast.tsx +190 -0
  107. package/src/primitives/feedback/Toast/Toast.types.ts +270 -0
  108. package/src/primitives/feedback/Toast/ToastContext.tsx +96 -0
  109. package/src/primitives/feedback/Toast/ToastProvider.tsx +241 -0
  110. package/src/primitives/feedback/Toast/index.ts +59 -0
  111. package/src/primitives/feedback/Toast/useToastLogic.ts +112 -0
  112. package/src/primitives/feedback/index.ts +45 -0
  113. package/src/primitives/index.ts +158 -0
  114. package/src/primitives/inputs/Checkbox/Checkbox.helpers.ts +132 -0
  115. package/src/primitives/inputs/Checkbox/Checkbox.tsx +150 -0
  116. package/src/primitives/inputs/Checkbox/Checkbox.types.ts +106 -0
  117. package/src/primitives/inputs/Checkbox/index.ts +30 -0
  118. package/src/primitives/inputs/Checkbox/useCheckboxLogic.ts +121 -0
  119. package/src/primitives/inputs/RadioButton/RadioButton.helpers.ts +123 -0
  120. package/src/primitives/inputs/RadioButton/RadioButton.tsx +159 -0
  121. package/src/primitives/inputs/RadioButton/RadioButton.types.ts +106 -0
  122. package/src/primitives/inputs/RadioButton/index.ts +25 -0
  123. package/src/primitives/inputs/RadioButton/useRadioButtonLogic.ts +117 -0
  124. package/src/primitives/inputs/SegmentedControl/SegmentedControl.helpers.ts +174 -0
  125. package/src/primitives/inputs/SegmentedControl/SegmentedControl.tsx +224 -0
  126. package/src/primitives/inputs/SegmentedControl/SegmentedControl.types.ts +187 -0
  127. package/src/primitives/inputs/SegmentedControl/index.ts +39 -0
  128. package/src/primitives/inputs/SegmentedControl/useSegmentedControlLogic.ts +151 -0
  129. package/src/primitives/inputs/SelectSheet/SelectSheet.helpers.ts +147 -0
  130. package/src/primitives/inputs/SelectSheet/SelectSheet.tsx +247 -0
  131. package/src/primitives/inputs/SelectSheet/SelectSheet.types.ts +196 -0
  132. package/src/primitives/inputs/SelectSheet/SelectSheetOption.tsx +177 -0
  133. package/src/primitives/inputs/SelectSheet/index.ts +48 -0
  134. package/src/primitives/inputs/SelectSheet/useSelectSheetLogic.ts +309 -0
  135. package/src/primitives/inputs/Switch/Switch.helpers.ts +109 -0
  136. package/src/primitives/inputs/Switch/Switch.tsx +191 -0
  137. package/src/primitives/inputs/Switch/Switch.types.ts +154 -0
  138. package/src/primitives/inputs/Switch/index.ts +40 -0
  139. package/src/primitives/inputs/Switch/useSwitchLogic.ts +192 -0
  140. package/src/primitives/inputs/TextInput/TextInput.helpers.ts +206 -0
  141. package/src/primitives/inputs/TextInput/TextInput.tsx +392 -0
  142. package/src/primitives/inputs/TextInput/TextInput.types.ts +216 -0
  143. package/src/primitives/inputs/TextInput/index.ts +37 -0
  144. package/src/primitives/inputs/TextInput/useTextInputLogic.ts +195 -0
  145. package/src/primitives/inputs/index.ts +52 -0
  146. package/src/primitives/layout/AnimatedBox.tsx +44 -0
  147. package/src/primitives/layout/Box.tsx +71 -0
  148. package/src/primitives/layout/Divider/Divider.helpers.ts +115 -0
  149. package/src/primitives/layout/Divider/Divider.tsx +139 -0
  150. package/src/primitives/layout/Divider/Divider.types.ts +178 -0
  151. package/src/primitives/layout/Divider/index.ts +24 -0
  152. package/src/primitives/layout/Divider/useDividerLogic.ts +109 -0
  153. package/src/primitives/layout/FlatList.tsx +66 -0
  154. package/src/primitives/layout/Pressable.tsx +74 -0
  155. package/src/primitives/layout/ScrollView.tsx +63 -0
  156. package/src/primitives/layout/Stack.tsx +69 -0
  157. package/src/primitives/layout/index.ts +40 -0
  158. package/src/primitives/navigation/index.ts +6 -0
  159. package/src/primitives/overlays/Modal/Modal.helpers.ts +31 -0
  160. package/src/primitives/overlays/Modal/Modal.tsx +264 -0
  161. package/src/primitives/overlays/Modal/Modal.types.ts +193 -0
  162. package/src/primitives/overlays/Modal/index.ts +43 -0
  163. package/src/primitives/overlays/Modal/useModalLogic.ts +103 -0
  164. package/src/primitives/overlays/index.ts +12 -0
  165. package/src/primitives/typography/Text.tsx +51 -0
  166. package/src/primitives/typography/index.ts +1 -0
  167. package/src/provider/DesignSystemContext.ts +22 -0
  168. package/src/provider/DesignSystemProvider.tsx +121 -0
  169. package/src/provider/index.ts +7 -0
  170. package/src/providers/ThemeProvider/createTheme.ts +304 -0
  171. package/src/providers/ThemeProvider/defaultTheme.ts +70 -0
  172. package/src/providers/ThemeProvider/index.ts +34 -0
  173. package/src/providers/ThemeProvider/types.ts +249 -0
  174. package/src/providers/index.ts +29 -0
  175. package/src/tokens/colors.ts +371 -0
  176. package/src/tokens/index.ts +145 -0
  177. package/src/tokens/motion.ts +176 -0
  178. package/src/tokens/radii.ts +82 -0
  179. package/src/tokens/scales.ts +588 -0
  180. package/src/tokens/shadows.ts +190 -0
  181. package/src/tokens/spacing.ts +140 -0
  182. package/src/tokens/tokens.json +207 -0
  183. package/src/tokens/typography.ts +251 -0
  184. package/src/types.ts +50 -0
  185. package/src/utils/accessibility.ts +169 -0
  186. package/src/utils/index.ts +25 -0
  187. 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";