@snowcone-app/ui 0.1.42 → 0.2.0

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 (192) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +18 -4
  3. package/package.json +9 -5
  4. package/src/components/CanvasIsolationBoundary.tsx +202 -0
  5. package/src/components/LoadingOverlayPrism.tsx +251 -0
  6. package/src/composed/AddToCart.tsx +229 -0
  7. package/src/composed/ArtAlignment.tsx +703 -0
  8. package/src/composed/ArtSelector.tsx +290 -0
  9. package/src/composed/ArtworkCustomizer.tsx +212 -0
  10. package/src/composed/CanvasEditor.tsx +79 -0
  11. package/src/composed/ColorPicker.tsx +111 -0
  12. package/src/composed/CurrentSelectionDisplay.tsx +86 -0
  13. package/src/composed/HeroProductImage.tsx +1071 -0
  14. package/src/composed/Lightbox.index.ts +2 -0
  15. package/src/composed/Lightbox.tsx +230 -0
  16. package/src/composed/PlacementClipShapeSelector.tsx +88 -0
  17. package/src/composed/PlacementTabs.tsx +179 -0
  18. package/src/composed/ProductCard.tsx +298 -0
  19. package/src/composed/ProductGallery.tsx +54 -0
  20. package/src/composed/ProductImage.tsx +129 -0
  21. package/src/composed/ProductList.tsx +147 -0
  22. package/src/composed/ProductOptions.tsx +305 -0
  23. package/src/composed/RealtimeMockup.tsx +121 -0
  24. package/src/composed/TileCount.tsx +348 -0
  25. package/src/composed/carousels/HeroCarousel.tsx +240 -0
  26. package/src/composed/carousels/MobileProductCarousel.tsx +1002 -0
  27. package/src/composed/carousels/index.ts +11 -0
  28. package/src/composed/carousels/types.ts +58 -0
  29. package/src/composed/grids/MasonryGrid.tsx +238 -0
  30. package/src/composed/grids/index.ts +9 -0
  31. package/src/composed/search/CurrentRefinements.tsx +80 -0
  32. package/src/composed/search/Filters.tsx +49 -0
  33. package/src/composed/search/FiltersButton.tsx +57 -0
  34. package/src/composed/search/FiltersDrawer.tsx +375 -0
  35. package/src/composed/search/ProductGrid.tsx +118 -0
  36. package/src/composed/search/ProductHit.tsx +56 -0
  37. package/src/composed/search/SearchBox.tsx +109 -0
  38. package/src/composed/search/SearchProvider.tsx +136 -0
  39. package/src/composed/search/facetConfig.ts +16 -0
  40. package/src/composed/search/index.ts +22 -0
  41. package/src/composed/search/meilisearchAdapter.ts +20 -0
  42. package/src/composed/search/types.ts +22 -0
  43. package/src/composed/zoom/EnhancedImageViewer.tsx +505 -0
  44. package/src/composed/zoom/ResponsiveZoom.tsx +134 -0
  45. package/src/composed/zoom/ZoomOverlay.tsx +194 -0
  46. package/src/composed/zoom/index.ts +12 -0
  47. package/src/composed/zoom/types.ts +12 -0
  48. package/src/design-system/ColorPalette.tsx +126 -0
  49. package/src/design-system/ColorSwatch.tsx +49 -0
  50. package/src/design-system/DesignSystemPage.tsx +130 -0
  51. package/src/design-system/ThemeSwitcher.tsx +181 -0
  52. package/src/design-system/TypographyScale.tsx +106 -0
  53. package/src/design-system/index.ts +5 -0
  54. package/src/ecommerce/stories/HeroProductImage.stories.tsx +66 -0
  55. package/src/ecommerce/stories/PDPHeroGallery.stories.tsx +105 -0
  56. package/src/ecommerce/stories/PDPInfoPanel.stories.tsx +472 -0
  57. package/src/ecommerce/stories/PDPLayout.stories.tsx +365 -0
  58. package/src/hooks/useBrand.ts +41 -0
  59. package/src/hooks/useCanvasContext.ts +127 -0
  60. package/src/hooks/useDeviceDetection.ts +64 -0
  61. package/src/hooks/useFocusTrap.ts +70 -0
  62. package/src/hooks/useImagePreloader.ts +268 -0
  63. package/src/hooks/useImageTransition.ts +608 -0
  64. package/src/hooks/usePlacementsProcessor.ts +74 -0
  65. package/src/hooks/useProductGallery.ts +193 -0
  66. package/src/hooks/useProductPage.ts +467 -0
  67. package/src/hooks/useRenderGuard.ts +96 -0
  68. package/src/hooks/useScrollDirection.ts +196 -0
  69. package/src/hooks/viewport/index.ts +25 -0
  70. package/src/hooks/viewport/useContainerWidth.ts +59 -0
  71. package/src/hooks/viewport/useMediaQuery.ts +52 -0
  72. package/src/hooks/viewport/useResponsiveImageCap.ts +149 -0
  73. package/src/hooks/viewport/useViewportDimensions.ts +135 -0
  74. package/src/hooks/viewport/useWideMonitorMode.ts +150 -0
  75. package/src/hooks/visibility/index.ts +15 -0
  76. package/src/hooks/visibility/observerPool.ts +150 -0
  77. package/src/index.ts +240 -0
  78. package/src/layouts/hero-zoom/HeroShrinkLayout.tsx +209 -0
  79. package/src/layouts/hero-zoom/HeroZoomLayout.tsx +351 -0
  80. package/src/layouts/hero-zoom/index.ts +30 -0
  81. package/src/layouts/hero-zoom/stories/HeroZoomLayout.stories.tsx +350 -0
  82. package/src/layouts/hero-zoom/types.ts +113 -0
  83. package/src/layouts/hero-zoom/useHeroZoomScales.ts +156 -0
  84. package/src/layouts/index.ts +9 -0
  85. package/src/layouts/pdp/EdgeBlurBox.tsx +210 -0
  86. package/src/layouts/pdp/ImageBlurExtension.tsx +215 -0
  87. package/src/layouts/pdp/ImageEdgeBlur.tsx +215 -0
  88. package/src/layouts/pdp/PDPLayout.tsx +246 -0
  89. package/src/layouts/pdp/SimpleImageBlur.tsx +140 -0
  90. package/src/layouts/pdp/index.ts +40 -0
  91. package/src/lib/env.ts +15 -0
  92. package/src/lib/locale.ts +167 -0
  93. package/src/lib/router.tsx +46 -0
  94. package/src/lib/utils.ts +6 -0
  95. package/src/lightbox/README.md +77 -0
  96. package/src/next/index.tsx +26 -0
  97. package/src/patterns/MockupPriorityProvider.tsx +1014 -0
  98. package/src/patterns/Product.tsx +850 -0
  99. package/src/patterns/ProductPageProvider.tsx +224 -0
  100. package/src/patterns/RealtimeProvider.tsx +1162 -0
  101. package/src/patterns/ShopProvider.tsx +603 -0
  102. package/src/personalization/PersonalizationBridge.tsx +235 -0
  103. package/src/personalization/PersonalizationContext.ts +29 -0
  104. package/src/personalization/PersonalizationInputs.tsx +110 -0
  105. package/src/personalization/PersonalizationProvider.tsx +407 -0
  106. package/src/personalization/canvas-stub.d.ts +22 -0
  107. package/src/personalization/index.ts +43 -0
  108. package/src/personalization/types.ts +48 -0
  109. package/src/personalization/usePersonalization.ts +32 -0
  110. package/src/personalization/usePersonalizationShimmer.ts +159 -0
  111. package/src/personalization/utils.ts +59 -0
  112. package/src/primitives/BrandLogo.tsx +65 -0
  113. package/src/primitives/BrandName.tsx +51 -0
  114. package/src/primitives/Button.tsx +123 -0
  115. package/src/primitives/ColorSwatch.tsx +221 -0
  116. package/src/primitives/DragHintAnimation.tsx +190 -0
  117. package/src/primitives/EdgeSwipeGuards.tsx +60 -0
  118. package/src/primitives/FloatingActionGroup.tsx +176 -0
  119. package/src/primitives/ProductPrice.tsx +171 -0
  120. package/src/primitives/ProgressiveBlur.tsx +295 -0
  121. package/src/primitives/ThemeToggle.tsx +125 -0
  122. package/src/primitives/__tests__/story-coverage.test.ts +98 -0
  123. package/src/primitives/accordion.tsx +280 -0
  124. package/src/primitives/badge.tsx +137 -0
  125. package/src/primitives/card.tsx +61 -0
  126. package/src/primitives/checkbox.tsx +56 -0
  127. package/src/primitives/collapsible.tsx +51 -0
  128. package/src/primitives/drawer.tsx +828 -0
  129. package/src/primitives/dropdown-menu.tsx +197 -0
  130. package/src/primitives/fieldset.tsx +73 -0
  131. package/src/primitives/index.ts +138 -0
  132. package/src/primitives/input.tsx +91 -0
  133. package/src/primitives/kbd.tsx +130 -0
  134. package/src/primitives/label.tsx +20 -0
  135. package/src/primitives/link.tsx +182 -0
  136. package/src/primitives/popover.tsx +80 -0
  137. package/src/primitives/radio-group.tsx +79 -0
  138. package/src/primitives/scroll-fade.tsx +159 -0
  139. package/src/primitives/select.tsx +170 -0
  140. package/src/primitives/separator.tsx +25 -0
  141. package/src/primitives/slider.tsx +221 -0
  142. package/src/primitives/spinner.tsx +72 -0
  143. package/src/primitives/stories/Accordion.stories.tsx +121 -0
  144. package/src/primitives/stories/Badge.stories.tsx +221 -0
  145. package/src/primitives/stories/Button.stories.tsx +185 -0
  146. package/src/primitives/stories/Card.stories.tsx +171 -0
  147. package/src/primitives/stories/Checkbox.stories.tsx +214 -0
  148. package/src/primitives/stories/Collapsible.stories.tsx +230 -0
  149. package/src/primitives/stories/Drawer.stories.tsx +378 -0
  150. package/src/primitives/stories/DropdownMenu.stories.tsx +182 -0
  151. package/src/primitives/stories/Fieldset.stories.tsx +212 -0
  152. package/src/primitives/stories/Input.stories.tsx +172 -0
  153. package/src/primitives/stories/Kbd.stories.tsx +183 -0
  154. package/src/primitives/stories/Label.stories.tsx +98 -0
  155. package/src/primitives/stories/Link.stories.tsx +260 -0
  156. package/src/primitives/stories/Popover.stories.tsx +178 -0
  157. package/src/primitives/stories/RadioGroup.stories.tsx +205 -0
  158. package/src/primitives/stories/Select.stories.tsx +222 -0
  159. package/src/primitives/stories/Separator.stories.tsx +134 -0
  160. package/src/primitives/stories/Slider.stories.tsx +203 -0
  161. package/src/primitives/stories/Spinner.stories.tsx +142 -0
  162. package/src/primitives/stories/Surface.stories.tsx +257 -0
  163. package/src/primitives/stories/Switch.stories.tsx +131 -0
  164. package/src/primitives/stories/Tabs.stories.tsx +275 -0
  165. package/src/primitives/stories/TextField.stories.tsx +139 -0
  166. package/src/primitives/stories/Textarea.stories.tsx +148 -0
  167. package/src/primitives/stories/Tooltip.stories.tsx +119 -0
  168. package/src/primitives/surface.tsx +86 -0
  169. package/src/primitives/switch.tsx +35 -0
  170. package/src/primitives/tabs.tsx +206 -0
  171. package/src/primitives/text-field.tsx +84 -0
  172. package/src/primitives/textarea.tsx +50 -0
  173. package/src/primitives/tooltip.tsx +58 -0
  174. package/src/services/CanvasExportService.ts +518 -0
  175. package/src/styles/base.css +380 -0
  176. package/src/styles/defaults.css +280 -0
  177. package/src/styles/globals.css +1242 -0
  178. package/src/styles/index.css +17 -0
  179. package/src/styles/ne-themes.css +4740 -0
  180. package/src/styles/tailwind.css +11 -0
  181. package/src/styles/tokens.css +117 -0
  182. package/src/styles/utilities.css +188 -0
  183. package/src/themes/apply-theme.ts +449 -0
  184. package/src/themes/getThemeStyles.ts +454 -0
  185. package/src/themes/index.ts +48 -0
  186. package/src/themes/oklch-theme.ts +283 -0
  187. package/src/themes/presets.ts +989 -0
  188. package/src/themes/types.ts +386 -0
  189. package/src/themes/useTheme.tsx +450 -0
  190. package/src/utils/dev-warnings.ts +161 -0
  191. package/src/utils/devWarnings.ts +153 -0
  192. package/dist/styles.css +0 -1
@@ -0,0 +1,229 @@
1
+ "use client";
2
+
3
+ import React, { useState, useEffect } from "react";
4
+ import { useProduct, useProductOptional } from "../patterns/Product";
5
+ import { useShopOptional } from "../patterns/ShopProvider";
6
+ import {
7
+ createCartDetail,
8
+ simulateCartOperation,
9
+ validateProductSelection,
10
+ } from "@snowcone-app/sdk";
11
+ import { checkProviders } from "../utils/devWarnings";
12
+
13
+ export interface AddToCartProps {
14
+ text?: string;
15
+ loadingText?: string;
16
+ successText?: string;
17
+ disabled?: boolean;
18
+ className?: string;
19
+ quantity?: number;
20
+ onClick?: (cartDetail: any) => void;
21
+ onError?: (error: Error) => void;
22
+ simulateDelay?: number;
23
+ }
24
+
25
+ /**
26
+ * AddToCart - Add to cart button with validation and loading states
27
+ *
28
+ * A composed component that provides a complete add-to-cart experience with
29
+ * automatic validation, loading states, success feedback, and cart detail creation.
30
+ * Integrates with Product context for seamless variant and pricing handling.
31
+ *
32
+ * Features:
33
+ * - Automatic selection validation before allowing add
34
+ * - Loading state with customizable text
35
+ * - Success state with visual feedback (green background)
36
+ * - Automatic cart detail generation with variant info
37
+ * - Simulated delay for cart operations
38
+ * - Screen reader announcements for state changes
39
+ * - Accessible with ARIA labels and busy states
40
+ * - Disabled state when selection is invalid
41
+ * - CSS custom property support for theming
42
+ *
43
+ * **Validation:**
44
+ * - Ensures all required options are selected
45
+ * - Checks variant availability
46
+ * - Validates against product combinations
47
+ * - Disables button when invalid
48
+ *
49
+ * **Context Integration:**
50
+ * - Must be used within a `Product` context provider
51
+ * - Automatically uses current selection from context
52
+ * - Accesses pricing and variant data
53
+ *
54
+ * @example
55
+ * ```tsx
56
+ * // Basic usage within Product context
57
+ * <Product productId="shirt-123">
58
+ * <ProductOptions />
59
+ * <AddToCart
60
+ * onClick={(cartDetail) => {
61
+ * console.log('Added to cart:', cartDetail);
62
+ * // Add to your cart state/API
63
+ * }}
64
+ * />
65
+ * </Product>
66
+ * ```
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * // With custom text and quantity
71
+ * <AddToCart
72
+ * text="Buy Now"
73
+ * loadingText="Processing..."
74
+ * successText="Added to Cart!"
75
+ * quantity={2}
76
+ * onClick={(detail) => addToCart(detail)}
77
+ * onError={(err) => showNotification(err.message)}
78
+ * />
79
+ * ```
80
+ *
81
+ * @example
82
+ * ```tsx
83
+ * // With custom styling
84
+ * <AddToCart
85
+ * className="w-full py-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
86
+ * text="Add to Bag"
87
+ * />
88
+ * ```
89
+ *
90
+ * @example
91
+ * ```tsx
92
+ * // Faster simulation for testing
93
+ * <AddToCart
94
+ * simulateDelay={500}
95
+ * onClick={(detail) => console.log(detail)}
96
+ * />
97
+ * ```
98
+ *
99
+ * @param text - Button text in default state (default: "Add to Bag")
100
+ * @param loadingText - Button text while adding (default: "Adding...")
101
+ * @param successText - Button text after successful add (default: "Added!")
102
+ * @param disabled - Force button to disabled state
103
+ * @param className - Additional CSS classes for button
104
+ * @param quantity - Number of items to add (default: 1)
105
+ * @param onClick - Callback with cart detail after successful add
106
+ * @param onError - Callback when add fails with error
107
+ * @param simulateDelay - Delay in ms to simulate cart operation (default: 1000)
108
+ */
109
+ export function AddToCart({
110
+ text = "Add to Bag",
111
+ loadingText = "Adding...",
112
+ successText = "Added!",
113
+ disabled = false,
114
+ className = "",
115
+ quantity = 1,
116
+ onClick,
117
+ onError,
118
+ simulateDelay = 1000,
119
+ }: AddToCartProps) {
120
+ const context = useProduct();
121
+ const shopContext = useShopOptional();
122
+ const [loading, setLoading] = useState(false);
123
+ const [success, setSuccess] = useState(false);
124
+
125
+ // Dev-mode warning for missing providers
126
+ useEffect(() => {
127
+ checkProviders(
128
+ "AddToCart",
129
+ {
130
+ product: context,
131
+ shop: shopContext,
132
+ },
133
+ "/components/add-to-cart"
134
+ );
135
+ }, []);
136
+
137
+ const { product, selection, combinations, optionAttributes } = context;
138
+
139
+ // Check if selection is valid using the shared validation from ui-core
140
+ const isValidSelection = () => {
141
+ return validateProductSelection(
142
+ optionAttributes || {},
143
+ selection || {},
144
+ combinations || []
145
+ );
146
+ };
147
+
148
+ const isDisabled = disabled || loading || !isValidSelection();
149
+
150
+ const handleClick = async () => {
151
+ if (isDisabled || !product) return;
152
+
153
+ try {
154
+ setLoading(true);
155
+
156
+ // Create cart detail using ui-core
157
+ const cartDetail = createCartDetail({
158
+ productId: product.id,
159
+ quantity,
160
+ selection: selection || {},
161
+ combinations: combinations || [],
162
+ attributes: optionAttributes || {},
163
+ });
164
+
165
+ if (!cartDetail) {
166
+ throw new Error("Invalid selection - unable to create cart detail");
167
+ }
168
+
169
+ // Simulate cart operation delay
170
+ await simulateCartOperation(simulateDelay);
171
+
172
+ // Call onClick handler with cart detail
173
+ onClick?.(cartDetail);
174
+
175
+ // Show success state
176
+ setSuccess(true);
177
+
178
+ // Reset success after 2 seconds
179
+ setTimeout(() => {
180
+ setSuccess(false);
181
+ }, 2000);
182
+ } catch (error) {
183
+ onError?.(error as Error);
184
+ console.error("Add to cart failed:", error);
185
+ } finally {
186
+ setLoading(false);
187
+ }
188
+ };
189
+
190
+ const buttonText = loading ? loadingText : success ? successText : text;
191
+
192
+ // Default styles that work out of the box - LLM-friendly
193
+ // These are always applied, then className can override/extend them
194
+ const defaultStyles = "w-full bg-primary text-primary-foreground hover:bg-primary/90 px-6 py-3 rounded-button font-medium transition-colors";
195
+
196
+ const buttonClasses = `
197
+ ${defaultStyles}
198
+ ${className || ""}
199
+ ${loading ? "cursor-wait" : ""}
200
+ ${isDisabled && !loading ? "opacity-50 cursor-not-allowed" : ""}
201
+ `.trim();
202
+
203
+ // Generate descriptive aria-label
204
+ const getAriaLabel = () => {
205
+ if (loading) return loadingText;
206
+ if (success) return successText;
207
+ if (!product) return text;
208
+ return `${text} - ${product.name || 'product'}`;
209
+ };
210
+
211
+ return (
212
+ <>
213
+ {/* Screen reader announcements for state changes */}
214
+ <div className="sr-only" role="status" aria-live="polite" aria-atomic="true">
215
+ {loading ? loadingText : success ? successText : ''}
216
+ </div>
217
+
218
+ <button
219
+ className={buttonClasses}
220
+ disabled={isDisabled}
221
+ onClick={handleClick}
222
+ aria-label={getAriaLabel()}
223
+ aria-busy={loading}
224
+ >
225
+ {buttonText}
226
+ </button>
227
+ </>
228
+ );
229
+ }