@snowcone-app/ui 0.1.43 → 0.2.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/CHANGELOG.md +32 -0
- package/README.md +18 -4
- package/dist/index.cjs +5 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/package.json +9 -5
- package/src/components/CanvasIsolationBoundary.tsx +202 -0
- package/src/components/LoadingOverlayPrism.tsx +251 -0
- package/src/composed/AddToCart.tsx +229 -0
- package/src/composed/ArtAlignment.tsx +703 -0
- package/src/composed/ArtSelector.tsx +290 -0
- package/src/composed/ArtworkCustomizer.tsx +212 -0
- package/src/composed/CanvasEditor.tsx +79 -0
- package/src/composed/ColorPicker.tsx +111 -0
- package/src/composed/CurrentSelectionDisplay.tsx +86 -0
- package/src/composed/HeroProductImage.tsx +1079 -0
- package/src/composed/Lightbox.index.ts +2 -0
- package/src/composed/Lightbox.tsx +230 -0
- package/src/composed/PlacementClipShapeSelector.tsx +88 -0
- package/src/composed/PlacementTabs.tsx +179 -0
- package/src/composed/ProductCard.tsx +298 -0
- package/src/composed/ProductGallery.tsx +54 -0
- package/src/composed/ProductImage.tsx +129 -0
- package/src/composed/ProductList.tsx +147 -0
- package/src/composed/ProductOptions.tsx +305 -0
- package/src/composed/RealtimeMockup.tsx +121 -0
- package/src/composed/TileCount.tsx +348 -0
- package/src/composed/carousels/HeroCarousel.tsx +240 -0
- package/src/composed/carousels/MobileProductCarousel.tsx +1002 -0
- package/src/composed/carousels/index.ts +11 -0
- package/src/composed/carousels/types.ts +58 -0
- package/src/composed/grids/MasonryGrid.tsx +238 -0
- package/src/composed/grids/index.ts +9 -0
- package/src/composed/search/CurrentRefinements.tsx +80 -0
- package/src/composed/search/Filters.tsx +49 -0
- package/src/composed/search/FiltersButton.tsx +57 -0
- package/src/composed/search/FiltersDrawer.tsx +375 -0
- package/src/composed/search/ProductGrid.tsx +118 -0
- package/src/composed/search/ProductHit.tsx +56 -0
- package/src/composed/search/SearchBox.tsx +109 -0
- package/src/composed/search/SearchProvider.tsx +136 -0
- package/src/composed/search/facetConfig.ts +16 -0
- package/src/composed/search/index.ts +22 -0
- package/src/composed/search/meilisearchAdapter.ts +20 -0
- package/src/composed/search/types.ts +22 -0
- package/src/composed/zoom/EnhancedImageViewer.tsx +505 -0
- package/src/composed/zoom/ResponsiveZoom.tsx +134 -0
- package/src/composed/zoom/ZoomOverlay.tsx +194 -0
- package/src/composed/zoom/index.ts +12 -0
- package/src/composed/zoom/types.ts +12 -0
- package/src/design-system/ColorPalette.tsx +126 -0
- package/src/design-system/ColorSwatch.tsx +49 -0
- package/src/design-system/DesignSystemPage.tsx +130 -0
- package/src/design-system/ThemeSwitcher.tsx +181 -0
- package/src/design-system/TypographyScale.tsx +106 -0
- package/src/design-system/index.ts +5 -0
- package/src/ecommerce/stories/HeroProductImage.stories.tsx +66 -0
- package/src/ecommerce/stories/PDPHeroGallery.stories.tsx +105 -0
- package/src/ecommerce/stories/PDPInfoPanel.stories.tsx +472 -0
- package/src/ecommerce/stories/PDPLayout.stories.tsx +365 -0
- package/src/hooks/useBrand.ts +41 -0
- package/src/hooks/useCanvasContext.ts +127 -0
- package/src/hooks/useDeviceDetection.ts +64 -0
- package/src/hooks/useFocusTrap.ts +70 -0
- package/src/hooks/useImagePreloader.ts +268 -0
- package/src/hooks/useImageTransition.ts +608 -0
- package/src/hooks/usePlacementsProcessor.ts +74 -0
- package/src/hooks/useProductGallery.ts +193 -0
- package/src/hooks/useProductPage.ts +467 -0
- package/src/hooks/useRenderGuard.ts +96 -0
- package/src/hooks/useScrollDirection.ts +196 -0
- package/src/hooks/viewport/index.ts +25 -0
- package/src/hooks/viewport/useContainerWidth.ts +59 -0
- package/src/hooks/viewport/useMediaQuery.ts +52 -0
- package/src/hooks/viewport/useResponsiveImageCap.ts +149 -0
- package/src/hooks/viewport/useViewportDimensions.ts +135 -0
- package/src/hooks/viewport/useWideMonitorMode.ts +150 -0
- package/src/hooks/visibility/index.ts +15 -0
- package/src/hooks/visibility/observerPool.ts +150 -0
- package/src/index.ts +240 -0
- package/src/layouts/hero-zoom/HeroShrinkLayout.tsx +209 -0
- package/src/layouts/hero-zoom/HeroZoomLayout.tsx +351 -0
- package/src/layouts/hero-zoom/index.ts +30 -0
- package/src/layouts/hero-zoom/stories/HeroZoomLayout.stories.tsx +350 -0
- package/src/layouts/hero-zoom/types.ts +113 -0
- package/src/layouts/hero-zoom/useHeroZoomScales.ts +156 -0
- package/src/layouts/index.ts +9 -0
- package/src/layouts/pdp/EdgeBlurBox.tsx +210 -0
- package/src/layouts/pdp/ImageBlurExtension.tsx +215 -0
- package/src/layouts/pdp/ImageEdgeBlur.tsx +215 -0
- package/src/layouts/pdp/PDPLayout.tsx +246 -0
- package/src/layouts/pdp/SimpleImageBlur.tsx +140 -0
- package/src/layouts/pdp/index.ts +40 -0
- package/src/lib/env.ts +15 -0
- package/src/lib/locale.ts +167 -0
- package/src/lib/router.tsx +46 -0
- package/src/lib/utils.ts +6 -0
- package/src/lightbox/README.md +77 -0
- package/src/next/index.tsx +26 -0
- package/src/patterns/MockupPriorityProvider.tsx +1014 -0
- package/src/patterns/Product.tsx +850 -0
- package/src/patterns/ProductPageProvider.tsx +224 -0
- package/src/patterns/RealtimeProvider.tsx +1162 -0
- package/src/patterns/ShopProvider.tsx +603 -0
- package/src/personalization/PersonalizationBridge.tsx +235 -0
- package/src/personalization/PersonalizationContext.ts +29 -0
- package/src/personalization/PersonalizationInputs.tsx +110 -0
- package/src/personalization/PersonalizationProvider.tsx +407 -0
- package/src/personalization/canvas-stub.d.ts +22 -0
- package/src/personalization/index.ts +43 -0
- package/src/personalization/types.ts +48 -0
- package/src/personalization/usePersonalization.ts +32 -0
- package/src/personalization/usePersonalizationShimmer.ts +159 -0
- package/src/personalization/utils.ts +59 -0
- package/src/primitives/BrandLogo.tsx +65 -0
- package/src/primitives/BrandName.tsx +51 -0
- package/src/primitives/Button.tsx +123 -0
- package/src/primitives/ColorSwatch.tsx +221 -0
- package/src/primitives/DragHintAnimation.tsx +190 -0
- package/src/primitives/EdgeSwipeGuards.tsx +60 -0
- package/src/primitives/FloatingActionGroup.tsx +176 -0
- package/src/primitives/ProductPrice.tsx +171 -0
- package/src/primitives/ProgressiveBlur.tsx +295 -0
- package/src/primitives/ThemeToggle.tsx +125 -0
- package/src/primitives/__tests__/story-coverage.test.ts +98 -0
- package/src/primitives/accordion.tsx +280 -0
- package/src/primitives/badge.tsx +137 -0
- package/src/primitives/card.tsx +61 -0
- package/src/primitives/checkbox.tsx +56 -0
- package/src/primitives/collapsible.tsx +51 -0
- package/src/primitives/drawer.tsx +828 -0
- package/src/primitives/dropdown-menu.tsx +197 -0
- package/src/primitives/fieldset.tsx +73 -0
- package/src/primitives/index.ts +138 -0
- package/src/primitives/input.tsx +91 -0
- package/src/primitives/kbd.tsx +130 -0
- package/src/primitives/label.tsx +20 -0
- package/src/primitives/link.tsx +182 -0
- package/src/primitives/popover.tsx +80 -0
- package/src/primitives/radio-group.tsx +79 -0
- package/src/primitives/scroll-fade.tsx +159 -0
- package/src/primitives/select.tsx +170 -0
- package/src/primitives/separator.tsx +25 -0
- package/src/primitives/slider.tsx +221 -0
- package/src/primitives/spinner.tsx +72 -0
- package/src/primitives/stories/Accordion.stories.tsx +121 -0
- package/src/primitives/stories/Badge.stories.tsx +221 -0
- package/src/primitives/stories/Button.stories.tsx +185 -0
- package/src/primitives/stories/Card.stories.tsx +171 -0
- package/src/primitives/stories/Checkbox.stories.tsx +214 -0
- package/src/primitives/stories/Collapsible.stories.tsx +230 -0
- package/src/primitives/stories/Drawer.stories.tsx +378 -0
- package/src/primitives/stories/DropdownMenu.stories.tsx +182 -0
- package/src/primitives/stories/Fieldset.stories.tsx +212 -0
- package/src/primitives/stories/Input.stories.tsx +172 -0
- package/src/primitives/stories/Kbd.stories.tsx +183 -0
- package/src/primitives/stories/Label.stories.tsx +98 -0
- package/src/primitives/stories/Link.stories.tsx +260 -0
- package/src/primitives/stories/Popover.stories.tsx +178 -0
- package/src/primitives/stories/RadioGroup.stories.tsx +205 -0
- package/src/primitives/stories/Select.stories.tsx +222 -0
- package/src/primitives/stories/Separator.stories.tsx +134 -0
- package/src/primitives/stories/Slider.stories.tsx +203 -0
- package/src/primitives/stories/Spinner.stories.tsx +142 -0
- package/src/primitives/stories/Surface.stories.tsx +257 -0
- package/src/primitives/stories/Switch.stories.tsx +131 -0
- package/src/primitives/stories/Tabs.stories.tsx +275 -0
- package/src/primitives/stories/TextField.stories.tsx +139 -0
- package/src/primitives/stories/Textarea.stories.tsx +148 -0
- package/src/primitives/stories/Tooltip.stories.tsx +119 -0
- package/src/primitives/surface.tsx +86 -0
- package/src/primitives/switch.tsx +35 -0
- package/src/primitives/tabs.tsx +206 -0
- package/src/primitives/text-field.tsx +84 -0
- package/src/primitives/textarea.tsx +50 -0
- package/src/primitives/tooltip.tsx +58 -0
- package/src/services/CanvasExportService.ts +518 -0
- package/src/styles/base.css +380 -0
- package/src/styles/defaults.css +280 -0
- package/src/styles/globals.css +1242 -0
- package/src/styles/index.css +17 -0
- package/src/styles/ne-themes.css +4740 -0
- package/src/styles/tailwind.css +11 -0
- package/src/styles/tokens.css +117 -0
- package/src/styles/utilities.css +188 -0
- package/src/themes/apply-theme.ts +449 -0
- package/src/themes/getThemeStyles.ts +454 -0
- package/src/themes/index.ts +48 -0
- package/src/themes/oklch-theme.ts +283 -0
- package/src/themes/presets.ts +989 -0
- package/src/themes/types.ts +386 -0
- package/src/themes/useTheme.tsx +450 -0
- package/src/utils/dev-warnings.ts +161 -0
- package/src/utils/devWarnings.ts +153 -0
- 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
|
+
}
|