@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,472 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Icon } from '@iconify/react';
|
|
4
|
+
import { Product } from '../../patterns/Product';
|
|
5
|
+
import { ProductOptions } from '../../composed/ProductOptions';
|
|
6
|
+
import { AddToCart } from '../../composed/AddToCart';
|
|
7
|
+
import {
|
|
8
|
+
Accordion,
|
|
9
|
+
AccordionItem,
|
|
10
|
+
AccordionTrigger,
|
|
11
|
+
AccordionContent,
|
|
12
|
+
} from '../../primitives/accordion';
|
|
13
|
+
import type { CatalogProduct } from '@snowcone-app/sdk';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* PDPInfoPanel - Product Detail Page Info Panel
|
|
17
|
+
*
|
|
18
|
+
* The right-side panel on a Product Detail Page (PDP) that displays:
|
|
19
|
+
* - Product name and price
|
|
20
|
+
* - Rating and favorite button
|
|
21
|
+
* - Artwork customizer (optional)
|
|
22
|
+
* - Product options (size, color, etc.) - uses real ProductOptions component
|
|
23
|
+
* - Add to Cart button - uses real AddToCart component
|
|
24
|
+
* - Product details accordion (description, materials, care, shipping)
|
|
25
|
+
*
|
|
26
|
+
* This story uses REAL components with Product context.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// Helper to generate all combinations for sizes and colors
|
|
30
|
+
const sizes = ['XS', 'S', 'M', 'L', 'XL', '2XL'];
|
|
31
|
+
const colors = ['Black', 'White', 'Navy', 'Heather Gray'];
|
|
32
|
+
|
|
33
|
+
const generateCombinations = (sizeList: string[], colorList: string[], price: number) => {
|
|
34
|
+
const combinations: { price: number; variantId: string; Size: string; Color: string }[] = [];
|
|
35
|
+
for (const size of sizeList) {
|
|
36
|
+
for (const color of colorList) {
|
|
37
|
+
combinations.push({
|
|
38
|
+
price,
|
|
39
|
+
variantId: `${size.toLowerCase()}-${color.toLowerCase().replace(' ', '-')}`,
|
|
40
|
+
Size: size,
|
|
41
|
+
Color: color,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return combinations;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Mock product data that matches CatalogProduct interface
|
|
49
|
+
const mockProduct: CatalogProduct = {
|
|
50
|
+
id: 'classic-cotton-tshirt',
|
|
51
|
+
name: 'Classic Cotton T-Shirt',
|
|
52
|
+
slug: 'classic-cotton-tshirt',
|
|
53
|
+
price: 2999, // $29.99 in cents
|
|
54
|
+
options: {
|
|
55
|
+
attributes: {
|
|
56
|
+
Size: {
|
|
57
|
+
type: 'select',
|
|
58
|
+
affectsCombinations: true,
|
|
59
|
+
choices: sizes.map(label => ({ label })),
|
|
60
|
+
},
|
|
61
|
+
Color: {
|
|
62
|
+
type: 'swatch',
|
|
63
|
+
affectsCombinations: true,
|
|
64
|
+
choices: [
|
|
65
|
+
{ label: 'Black', hex: '#1a1a1a' },
|
|
66
|
+
{ label: 'White', hex: '#ffffff' },
|
|
67
|
+
{ label: 'Navy', hex: '#1e3a5f' },
|
|
68
|
+
{ label: 'Heather Gray', hex: '#9ca3af' },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
combinations: generateCombinations(sizes, colors, 2999),
|
|
73
|
+
attributesList: ['Size', 'Color'],
|
|
74
|
+
},
|
|
75
|
+
description: ['Premium quality with exceptional comfort and style.'],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Product with minimal options
|
|
79
|
+
const minimalProduct: CatalogProduct = {
|
|
80
|
+
id: 'simple-product',
|
|
81
|
+
name: 'Simple Product',
|
|
82
|
+
slug: 'simple-product',
|
|
83
|
+
price: 1999,
|
|
84
|
+
options: {
|
|
85
|
+
attributes: {
|
|
86
|
+
Size: {
|
|
87
|
+
type: 'select',
|
|
88
|
+
affectsCombinations: true,
|
|
89
|
+
choices: [{ label: 'One Size' }],
|
|
90
|
+
},
|
|
91
|
+
Color: {
|
|
92
|
+
type: 'swatch',
|
|
93
|
+
affectsCombinations: true,
|
|
94
|
+
choices: [{ label: 'Natural', hex: '#f5f5dc' }],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
combinations: [{ price: 1999, variantId: 'one-size-natural' }],
|
|
98
|
+
attributesList: ['Size', 'Color'],
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Product with many colors
|
|
103
|
+
const manyColorsSizes = ['S', 'M', 'L'];
|
|
104
|
+
const manyColorsColors = ['Black', 'White', 'Navy', 'Red', 'Green', 'Purple', 'Orange', 'Pink'];
|
|
105
|
+
const manyColorsProduct: CatalogProduct = {
|
|
106
|
+
id: 'colorful-product',
|
|
107
|
+
name: 'Colorful T-Shirt',
|
|
108
|
+
slug: 'colorful-product',
|
|
109
|
+
price: 2999,
|
|
110
|
+
options: {
|
|
111
|
+
attributes: {
|
|
112
|
+
Size: {
|
|
113
|
+
type: 'select',
|
|
114
|
+
affectsCombinations: true,
|
|
115
|
+
choices: manyColorsSizes.map(label => ({ label })),
|
|
116
|
+
},
|
|
117
|
+
Color: {
|
|
118
|
+
type: 'swatch',
|
|
119
|
+
affectsCombinations: true,
|
|
120
|
+
choices: [
|
|
121
|
+
{ label: 'Black', hex: '#1a1a1a' },
|
|
122
|
+
{ label: 'White', hex: '#ffffff' },
|
|
123
|
+
{ label: 'Navy', hex: '#1e3a5f' },
|
|
124
|
+
{ label: 'Red', hex: '#dc2626' },
|
|
125
|
+
{ label: 'Green', hex: '#16a34a' },
|
|
126
|
+
{ label: 'Purple', hex: '#9333ea' },
|
|
127
|
+
{ label: 'Orange', hex: '#ea580c' },
|
|
128
|
+
{ label: 'Pink', hex: '#ec4899' },
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
combinations: generateCombinations(manyColorsSizes, manyColorsColors, 2999),
|
|
133
|
+
attributesList: ['Size', 'Color'],
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
interface PDPInfoPanelProps {
|
|
138
|
+
product: CatalogProduct;
|
|
139
|
+
tag?: string;
|
|
140
|
+
rating?: number;
|
|
141
|
+
reviewCount?: number;
|
|
142
|
+
originalPrice?: string;
|
|
143
|
+
showArtworkCustomizer?: boolean;
|
|
144
|
+
onAddToCart?: (cartDetail: any) => void;
|
|
145
|
+
className?: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const PDPInfoPanel = ({
|
|
149
|
+
product,
|
|
150
|
+
tag,
|
|
151
|
+
rating = 4.2,
|
|
152
|
+
reviewCount = 143,
|
|
153
|
+
originalPrice,
|
|
154
|
+
showArtworkCustomizer = false,
|
|
155
|
+
onAddToCart,
|
|
156
|
+
className,
|
|
157
|
+
}: PDPInfoPanelProps) => {
|
|
158
|
+
const [isFavorite, setIsFavorite] = React.useState(false);
|
|
159
|
+
|
|
160
|
+
// Format price from cents
|
|
161
|
+
const formatPrice = (cents: number) => `$${(cents / 100).toFixed(2)}`;
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<Product productData={product} initialSelection={{ Size: 'M', Color: product.options?.attributes?.Color?.choices?.[0]?.label || 'Black' }}>
|
|
165
|
+
<div
|
|
166
|
+
className={`bg-background/85 backdrop-blur-sm rounded-3xl p-6 shadow-lg max-h-[calc(100vh-11rem)] overflow-y-auto ${className || ''}`}
|
|
167
|
+
>
|
|
168
|
+
<div className="flex flex-col gap-6">
|
|
169
|
+
{/* Tag, Product Name and Price */}
|
|
170
|
+
<div className="flex flex-col gap-3">
|
|
171
|
+
{/* Tag */}
|
|
172
|
+
{tag && (
|
|
173
|
+
<span className="text-sm font-medium text-primary">{tag}</span>
|
|
174
|
+
)}
|
|
175
|
+
|
|
176
|
+
{/* Name and Price */}
|
|
177
|
+
<div className="flex items-end justify-between gap-4">
|
|
178
|
+
<h1 className="text-2xl font-bold text-foreground leading-tight">{product.name}</h1>
|
|
179
|
+
<div className="flex items-baseline gap-2 shrink-0">
|
|
180
|
+
<span className="text-lg font-semibold text-foreground">
|
|
181
|
+
{formatPrice(product.price)}
|
|
182
|
+
</span>
|
|
183
|
+
{originalPrice && (
|
|
184
|
+
<span className="text-sm text-muted-foreground line-through">
|
|
185
|
+
{originalPrice}
|
|
186
|
+
</span>
|
|
187
|
+
)}
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
{/* Rating and Favorite */}
|
|
192
|
+
<div className="flex items-center justify-start gap-2">
|
|
193
|
+
<button
|
|
194
|
+
onClick={() => setIsFavorite(!isFavorite)}
|
|
195
|
+
className="w-10 h-10 bg-foreground/5 rounded-full flex items-center justify-center hover:bg-foreground/10 transition-colors"
|
|
196
|
+
>
|
|
197
|
+
<Icon
|
|
198
|
+
icon={isFavorite ? 'gravity-ui:heart-fill' : 'gravity-ui:heart'}
|
|
199
|
+
className={`w-5 h-5 ${isFavorite ? 'text-red-500' : 'text-muted-foreground/60'}`}
|
|
200
|
+
/>
|
|
201
|
+
</button>
|
|
202
|
+
<div className="h-10 bg-foreground/5 rounded-full px-3 flex items-center gap-1 text-sm font-caption text-muted-foreground">
|
|
203
|
+
<Icon
|
|
204
|
+
icon="gravity-ui:star"
|
|
205
|
+
className="w-4 h-4 text-muted-foreground"
|
|
206
|
+
/>
|
|
207
|
+
<span className="text-sm font-medium">{rating}</span>
|
|
208
|
+
<span className="text-sm">({reviewCount})</span>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
{/* Artwork Customizer Placeholder */}
|
|
214
|
+
{showArtworkCustomizer && (
|
|
215
|
+
<div className="flex flex-col gap-4">
|
|
216
|
+
<div className="aspect-square bg-muted rounded-2xl flex items-center justify-center border-2 border-dashed border-border">
|
|
217
|
+
<div className="text-center text-muted-foreground">
|
|
218
|
+
<Icon icon="gravity-ui:picture" className="w-12 h-12 mx-auto mb-2 opacity-50" />
|
|
219
|
+
<p className="text-sm">Artwork Customizer</p>
|
|
220
|
+
<p className="text-xs opacity-70">Drag to position artwork</p>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
)}
|
|
225
|
+
|
|
226
|
+
{/* Product Options - REAL COMPONENT */}
|
|
227
|
+
<ProductOptions />
|
|
228
|
+
|
|
229
|
+
{/* Add to Cart - REAL COMPONENT */}
|
|
230
|
+
<AddToCart onClick={onAddToCart} />
|
|
231
|
+
|
|
232
|
+
{/* Product Details Accordion */}
|
|
233
|
+
<Accordion type="single" collapsible className="w-full [&>*]:border-0">
|
|
234
|
+
<AccordionItem value="description">
|
|
235
|
+
<AccordionTrigger className="text-left text-lg font-bold text-foreground hover:no-underline">
|
|
236
|
+
Description
|
|
237
|
+
</AccordionTrigger>
|
|
238
|
+
<AccordionContent>
|
|
239
|
+
<p className="text-base font-caption text-muted-foreground leading-relaxed">
|
|
240
|
+
{product.description?.[0] || 'Premium quality with exceptional comfort and style.'}
|
|
241
|
+
</p>
|
|
242
|
+
</AccordionContent>
|
|
243
|
+
</AccordionItem>
|
|
244
|
+
|
|
245
|
+
<AccordionItem value="materials">
|
|
246
|
+
<AccordionTrigger className="text-left text-lg font-bold text-foreground hover:no-underline">
|
|
247
|
+
Materials
|
|
248
|
+
</AccordionTrigger>
|
|
249
|
+
<AccordionContent>
|
|
250
|
+
<p className="text-base font-caption text-muted-foreground leading-relaxed">
|
|
251
|
+
100% organic cotton with sustainable manufacturing process.
|
|
252
|
+
</p>
|
|
253
|
+
</AccordionContent>
|
|
254
|
+
</AccordionItem>
|
|
255
|
+
|
|
256
|
+
<AccordionItem value="care">
|
|
257
|
+
<AccordionTrigger className="text-left text-lg font-bold text-foreground hover:no-underline">
|
|
258
|
+
Care Instructions
|
|
259
|
+
</AccordionTrigger>
|
|
260
|
+
<AccordionContent>
|
|
261
|
+
<p className="text-base font-caption text-muted-foreground leading-relaxed">
|
|
262
|
+
Machine wash cold, tumble dry low. Do not bleach.
|
|
263
|
+
</p>
|
|
264
|
+
</AccordionContent>
|
|
265
|
+
</AccordionItem>
|
|
266
|
+
|
|
267
|
+
<AccordionItem value="shipping">
|
|
268
|
+
<AccordionTrigger className="text-left text-lg font-bold text-foreground hover:no-underline">
|
|
269
|
+
Shipping & Returns
|
|
270
|
+
</AccordionTrigger>
|
|
271
|
+
<AccordionContent>
|
|
272
|
+
<p className="text-base font-caption text-muted-foreground leading-relaxed">
|
|
273
|
+
Free shipping on orders over $50. 30-day return policy.
|
|
274
|
+
</p>
|
|
275
|
+
</AccordionContent>
|
|
276
|
+
</AccordionItem>
|
|
277
|
+
</Accordion>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
</Product>
|
|
281
|
+
);
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const meta: Meta<typeof PDPInfoPanel> = {
|
|
285
|
+
title: 'Layouts/PDP Info Panel',
|
|
286
|
+
component: PDPInfoPanel,
|
|
287
|
+
parameters: {
|
|
288
|
+
layout: 'centered',
|
|
289
|
+
docs: {
|
|
290
|
+
description: {
|
|
291
|
+
component: `
|
|
292
|
+
The PDP Info Panel uses **real ProductOptions and AddToCart components** with Product context.
|
|
293
|
+
|
|
294
|
+
## Structure
|
|
295
|
+
|
|
296
|
+
1. **Header Section**: Product name, price, and discount
|
|
297
|
+
2. **Social Actions**: Favorite button and rating display
|
|
298
|
+
3. **Artwork Customizer** (optional): For print-on-demand products
|
|
299
|
+
4. **Product Options**: Real \`<ProductOptions />\` component with context
|
|
300
|
+
5. **Add to Cart**: Real \`<AddToCart />\` component with validation
|
|
301
|
+
6. **Details Accordion**: Description, materials, care, shipping
|
|
302
|
+
|
|
303
|
+
## Usage
|
|
304
|
+
|
|
305
|
+
\`\`\`tsx
|
|
306
|
+
<Product productData={productData}>
|
|
307
|
+
<div className="bg-background/85 backdrop-blur-sm rounded-3xl p-6">
|
|
308
|
+
{/* Product Name + Price */}
|
|
309
|
+
{/* Rating + Favorite */}
|
|
310
|
+
<ProductOptions />
|
|
311
|
+
<AddToCart onClick={(detail) => console.log(detail)} />
|
|
312
|
+
{/* Accordion */}
|
|
313
|
+
</div>
|
|
314
|
+
</Product>
|
|
315
|
+
\`\`\`
|
|
316
|
+
`,
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
tags: ['autodocs'],
|
|
321
|
+
argTypes: {
|
|
322
|
+
tag: {
|
|
323
|
+
control: 'text',
|
|
324
|
+
description: 'Product tag/badge (e.g., "Upgraded", "New", "Sale")',
|
|
325
|
+
},
|
|
326
|
+
showArtworkCustomizer: {
|
|
327
|
+
control: 'boolean',
|
|
328
|
+
description: 'Show artwork customizer placeholder',
|
|
329
|
+
},
|
|
330
|
+
rating: {
|
|
331
|
+
control: { type: 'number', min: 0, max: 5, step: 0.1 },
|
|
332
|
+
description: 'Product rating (0-5)',
|
|
333
|
+
},
|
|
334
|
+
reviewCount: {
|
|
335
|
+
control: 'number',
|
|
336
|
+
description: 'Number of reviews',
|
|
337
|
+
},
|
|
338
|
+
originalPrice: {
|
|
339
|
+
control: 'text',
|
|
340
|
+
description: 'Original price (for showing discount)',
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
export default meta;
|
|
346
|
+
type Story = StoryObj<typeof meta>;
|
|
347
|
+
|
|
348
|
+
export const Default: Story = {
|
|
349
|
+
args: {
|
|
350
|
+
product: mockProduct,
|
|
351
|
+
tag: 'Upgraded',
|
|
352
|
+
},
|
|
353
|
+
render: (args) => (
|
|
354
|
+
<div className="w-[380px]">
|
|
355
|
+
<PDPInfoPanel {...args} />
|
|
356
|
+
</div>
|
|
357
|
+
),
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export const WithArtworkCustomizer: Story = {
|
|
361
|
+
args: {
|
|
362
|
+
product: mockProduct,
|
|
363
|
+
showArtworkCustomizer: true,
|
|
364
|
+
},
|
|
365
|
+
render: (args) => (
|
|
366
|
+
<div className="w-[380px]">
|
|
367
|
+
<PDPInfoPanel {...args} />
|
|
368
|
+
</div>
|
|
369
|
+
),
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
export const OnSale: Story = {
|
|
373
|
+
args: {
|
|
374
|
+
product: mockProduct,
|
|
375
|
+
originalPrice: '$39.99',
|
|
376
|
+
},
|
|
377
|
+
render: (args) => (
|
|
378
|
+
<div className="w-[380px]">
|
|
379
|
+
<PDPInfoPanel {...args} />
|
|
380
|
+
</div>
|
|
381
|
+
),
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
export const HighlyRated: Story = {
|
|
385
|
+
args: {
|
|
386
|
+
product: mockProduct,
|
|
387
|
+
rating: 4.9,
|
|
388
|
+
reviewCount: 2847,
|
|
389
|
+
},
|
|
390
|
+
render: (args) => (
|
|
391
|
+
<div className="w-[380px]">
|
|
392
|
+
<PDPInfoPanel {...args} />
|
|
393
|
+
</div>
|
|
394
|
+
),
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
export const MinimalOptions: Story = {
|
|
398
|
+
args: {
|
|
399
|
+
product: minimalProduct,
|
|
400
|
+
},
|
|
401
|
+
render: (args) => (
|
|
402
|
+
<div className="w-[380px]">
|
|
403
|
+
<PDPInfoPanel {...args} />
|
|
404
|
+
</div>
|
|
405
|
+
),
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
export const ManyColors: Story = {
|
|
409
|
+
args: {
|
|
410
|
+
product: manyColorsProduct,
|
|
411
|
+
},
|
|
412
|
+
render: (args) => (
|
|
413
|
+
<div className="w-[380px]">
|
|
414
|
+
<PDPInfoPanel {...args} />
|
|
415
|
+
</div>
|
|
416
|
+
),
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// Full panel in context (simulating desktop layout with overlapping panel)
|
|
420
|
+
const demoMockups = [
|
|
421
|
+
{ id: 'front', label: 'Front' },
|
|
422
|
+
{ id: 'back', label: 'Back' },
|
|
423
|
+
{ id: 'detail', label: 'Detail' },
|
|
424
|
+
];
|
|
425
|
+
|
|
426
|
+
export const InContext: Story = {
|
|
427
|
+
args: {
|
|
428
|
+
product: mockProduct,
|
|
429
|
+
tag: 'Upgraded',
|
|
430
|
+
showArtworkCustomizer: true,
|
|
431
|
+
},
|
|
432
|
+
parameters: {
|
|
433
|
+
layout: 'fullscreen',
|
|
434
|
+
},
|
|
435
|
+
render: (args) => (
|
|
436
|
+
<div className="min-h-screen bg-background relative">
|
|
437
|
+
{/* Full-width Hero Images - content centered in visible area (left of panel) */}
|
|
438
|
+
<div className="w-full">
|
|
439
|
+
{demoMockups.map((mockup) => (
|
|
440
|
+
<div
|
|
441
|
+
key={mockup.id}
|
|
442
|
+
className="relative w-full"
|
|
443
|
+
style={{ height: '70vh' }}
|
|
444
|
+
>
|
|
445
|
+
<div className="w-full h-full bg-gradient-to-br from-muted to-muted/50 flex items-center justify-center">
|
|
446
|
+
{/* Content offset to left to center in visible area */}
|
|
447
|
+
<div
|
|
448
|
+
className="text-center text-muted-foreground"
|
|
449
|
+
style={{ marginRight: '30%' }}
|
|
450
|
+
>
|
|
451
|
+
<Icon
|
|
452
|
+
icon="gravity-ui:t-shirt"
|
|
453
|
+
className="w-32 h-32 mx-auto mb-4 opacity-20"
|
|
454
|
+
/>
|
|
455
|
+
<p className="text-xl font-medium opacity-60">{mockup.label} View</p>
|
|
456
|
+
<p className="text-sm opacity-40">Product mockup</p>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
))}
|
|
461
|
+
</div>
|
|
462
|
+
|
|
463
|
+
{/* Overlapping Info Panel - fixed on right side */}
|
|
464
|
+
<div
|
|
465
|
+
className="hidden md:block fixed right-8 top-8 w-[30%] xl:w-[35%] max-w-96 z-10"
|
|
466
|
+
style={{ maxHeight: 'calc(100vh - 4rem)' }}
|
|
467
|
+
>
|
|
468
|
+
<PDPInfoPanel {...args} />
|
|
469
|
+
</div>
|
|
470
|
+
</div>
|
|
471
|
+
),
|
|
472
|
+
};
|