UNPKG

merchify-ui

Version:

React components for merchandise visualization and customization

1,498 lines (1,477 loc) 59.3 kB
import React, { ButtonHTMLAttributes, ReactNode } from 'react'; import * as react_jsx_runtime from 'react/jsx-runtime'; import { ProductPriceOptions, CatalogProduct, DesignElement, ImageAlignment, ProductContext as ProductContext$1, OptionSelection, WebSocketConfig } from 'merchify'; export { ArtworkData, ImageAlignment, ProductArtAlignmentContext, ProductArtAlignmentOptions, ProductData, ProductMockupData, ProductPlacement, ProductVariant, describeProductArtAlignment, getSnapPoints } from 'merchify'; /** * Button - A flexible button primitive with multiple variants * * Provides consistent button styles across the application with support for: * - Multiple visual variants (primary, outline, ghost, option-text, option-swatch) * - Disabled and loading states * - Theme-aware styling * - Full accessibility support * * @example * ```tsx * // Primary button * <Button variant="primary">Add to Cart</Button> * * // Option text button (for size selectors) * <Button * variant="option-text" * selected={isSelected} * onClick={() => setSize("8x10")} * > * 8x10 * </Button> * * // Option swatch button (for color/frame selectors) * <Button * variant="option-swatch" * selected={isSelected} * style={{ backgroundColor: '#8B4513' }} * /> * ``` */ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { /** * Visual variant of the button * - primary: Filled button with primary color * - outline: Button with border only * - ghost: Transparent button with hover effect * - secondary: Muted background button * - option-text: Rectangular option button for text choices (sizes, styles) * - option-swatch: Circular option button for visual choices (colors, frames) */ variant?: "primary" | "outline" | "ghost" | "secondary" | "option-text" | "option-swatch"; /** * Whether this option is currently selected (for option-text and option-swatch variants) */ selected?: boolean; /** * Whether the button is in a loading state */ loading?: boolean; /** * Full width button (spans container width) */ fullWidth?: boolean; } declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>; interface ColorSwatchChoice { value: string; label: string; hex?: string; imageUrl?: string; selected?: boolean; disabled?: boolean; } interface ColorSwatchProps { /** Array of color choices to display */ choices: ColorSwatchChoice[]; /** Callback when a color is selected */ onChange: (value: string) => void; /** Optional ARIA label for the color group */ ariaLabel?: string; /** Show tooltip on selection (default: true) */ showTooltip?: boolean; /** Custom size in pixels (default: 40) */ size?: number; /** Custom className for the container */ className?: string; } /** * ColorSwatch - Interactive color selector with visual feedback * * A primitive component for displaying color choices as clickable swatches with * support for hex colors, image thumbnails, selection states, and tooltips. * Designed for maximum accessibility and visual clarity. * * Features: * - Hex color or image thumbnail backgrounds * - Visual selection indicators (checkmark, rings) * - Disabled state with diagonal slash * - Hover scaling for interactivity * - Tooltip feedback on selection * - Screen reader announcements * - Accessible with ARIA roles and labels * - Dark mode support * - Customizable size * - Colorblind-friendly selection indicators * * **Visual States:** * - Default: Simple round swatch with subtle border * - Selected: Double ring + checkmark icon + pattern overlay * - Disabled: Diagonal slash overlay * - Hover: Slight scale animation * * **Accessibility:** * - Keyboard navigable * - Screen reader friendly with status announcements * - Visible selection indicators for colorblind users * - Proper ARIA roles and labels * * @example * ```tsx * // Basic color swatches * <ColorSwatch * choices={[ * { value: "red", label: "Red", hex: "#ff0000", selected: true }, * { value: "blue", label: "Blue", hex: "#0000ff" }, * { value: "green", label: "Green", hex: "#00ff00", disabled: true } * ]} * onChange={(value) => console.log('Selected:', value)} * ariaLabel="Choose product color" * /> * ``` * * @example * ```tsx * // With image thumbnails (patterns/textures) * <ColorSwatch * choices={[ * { value: "marble", label: "Marble", imageUrl: "/textures/marble.jpg" }, * { value: "wood", label: "Wood Grain", imageUrl: "/textures/wood.jpg" } * ]} * onChange={handleMaterialChange} * size={50} * /> * ``` * * @example * ```tsx * // Custom styling and no tooltips * <ColorSwatch * choices={colorOptions} * onChange={setColor} * showTooltip={false} * size={60} * className="gap-4 justify-center" * /> * ``` * * @param choices - Array of color/pattern choices to display * @param onChange - Callback when a swatch is clicked (receives choice value) * @param ariaLabel - Accessible label for the color group * @param showTooltip - Show selection tooltip (default: true) * @param size - Swatch diameter in pixels (default: 40) * @param className - Additional CSS classes for container */ declare function ColorSwatch({ choices, onChange, ariaLabel, showTooltip, size, className, }: ColorSwatchProps): react_jsx_runtime.JSX.Element; interface ProductPriceProps extends ProductPriceOptions { contextPrice?: number; showCents?: boolean; } /** * ProductPrice - Formatted price display with currency and locale support * * A primitive component for displaying product prices with automatic currency * formatting, smart cents display, and seamless Product context integration. * Uses semantic HTML for machine-readable price data. * * Features: * - Automatic currency formatting based on locale * - Smart cents display (hides .00, shows actual cents) * - Semantic HTML using `<data>` element with value attribute * - Accessible with ARIA labels for screen readers * - Works standalone or within Product context * - Automatically uses currentPrice from context (reflects variant selection) * - Supports international currencies and locales * - Machine-readable price in value attribute for SEO/scrapers * * **Price Format:** * - Prices are stored in cents to avoid floating point issues * - Example: 2999 cents = $29.99 * - Automatically converts to display format * * **Context Integration:** * - When used in `Product` context, automatically shows current price * - Updates when user selects different variants * - Falls back to prop value when used standalone * * @example * ```tsx * // Standalone usage with explicit price * <ProductPrice price={2999} showCents={true} /> * // Output: $29.99 * ``` * * @example * ```tsx * // Within Product context (automatically uses current price) * <Product productId="shirt-123"> * <ProductOptions /> * <ProductPrice showCents={false} /> * </Product> * // Output: $29 (or $35 if user selects a pricier variant) * ``` * * @example * ```tsx * // International currency and locale * <ProductPrice * price={2999} * currency="EUR" * locale="de-DE" * showCurrency={true} * /> * // Output: €29,99 * ``` * * @example * ```tsx * // Custom styling for sale prices * <div className="flex items-center gap-2"> * <ProductPrice * price={1999} * className="text-2xl font-bold text-green-600" * /> * <ProductPrice * price={2999} * showCents={false} * className="text-sm line-through text-gray-400" * /> * </div> * ``` * * @param price - Price in cents (e.g., 2999 = $29.99). Optional if used within Product context * @param currency - ISO 4217 currency code (default: "USD") * @param locale - BCP 47 locale string for formatting (default: "en-US") * @param showCurrency - Display full currency symbol (default: false, shows $ only) * @param showCents - Display cents portion (default: true, hides if .00) * @param className - Additional CSS classes for styling * @param contextPrice - Internal: Override context price (used by Product component) */ declare function ProductPrice({ price, currency, locale, showCurrency, className, contextPrice, showCents, }: ProductPriceProps): react_jsx_runtime.JSX.Element | null; interface FloatingActionItem { id: string; label: string; children?: React.ReactNode; onClick?: (e?: any) => void; selected?: boolean; disabled?: boolean; variant?: "circular" | "pill"; } interface FloatingActionGroupProps { items: FloatingActionItem[]; className?: string; scrollable?: boolean; leftAligned?: boolean; justify?: "start" | "end" | "center"; ariaLabel?: string; } /** * FloatingActionGroup - A primitive component for displaying a horizontal group of action buttons * * Creates a row of circular or pill-shaped buttons with selection states and disabled states. * Commonly used for filters, tabs, or quick actions. Supports horizontal scrolling for * overflow scenarios. * * Features: * - Two button variants: circular (icon-only) or pill (text-based) * - Visual selection states with background effects * - Optional horizontal scrolling with hidden scrollbars * - Smart alignment modes (left, center, right) * - Accessible with ARIA roles and labels * - Dark mode support * * @example * ```tsx * // Simple button group * <FloatingActionGroup * items={[ * { id: '1', label: 'All', selected: true, onClick: () => setFilter('all') }, * { id: '2', label: 'Active', onClick: () => setFilter('active') }, * { id: '3', label: 'Done', onClick: () => setFilter('done') } * ]} * ariaLabel="Filter tasks" * /> * ``` * * @example * ```tsx * // Scrollable with custom icons * <FloatingActionGroup * scrollable={true} * leftAligned={true} * items={[ * { id: 'edit', label: 'Edit', variant: 'circular', children: <PencilIcon /> }, * { id: 'delete', label: 'Delete', variant: 'circular', children: <TrashIcon />, disabled: true } * ]} * /> * ``` * * @param items - Array of action items to display * @param className - Additional CSS classes * @param scrollable - Enable horizontal scrolling for overflow (default: false) * @param leftAligned - Add left padding in scrollable mode (default: false) * @param justify - Horizontal alignment ("start", "end", or "center", default: "start") * @param ariaLabel - Accessible label for the button group */ declare function FloatingActionGroup({ items, className, scrollable, leftAligned, justify, ariaLabel, }: FloatingActionGroupProps): react_jsx_runtime.JSX.Element; interface ThemeToggleProps { isDark?: boolean; onToggle?: () => void; className?: string; position?: "bottom-right" | "bottom-left" | "top-right" | "top-left"; disabled?: boolean; } /** * ThemeToggle - A primitive floating button for toggling between light and dark themes * * A fixed-position button that displays a sun/moon icon based on the current theme. * Commonly used for implementing dark mode toggles in applications. * * Features: * - Fixed positioning with configurable corners * - Animated icon transitions * - Accessible with ARIA labels * - Responsive hover and active states * - Built-in disabled state * * @example * ```tsx * const [isDark, setIsDark] = useState(false); * * <ThemeToggle * isDark={isDark} * onToggle={() => setIsDark(!isDark)} * position="bottom-right" * /> * ``` * * @example * ```tsx * // With Next.js theme provider * import { useTheme } from 'next-themes'; * * function MyApp() { * const { theme, setTheme } = useTheme(); * return ( * <ThemeToggle * isDark={theme === 'dark'} * onToggle={() => setTheme(theme === 'dark' ? 'light' : 'dark')} * /> * ); * } * ``` * * @param isDark - Whether dark mode is currently active (default: false) * @param onToggle - Callback function when toggle is clicked * @param className - Additional CSS classes for customization * @param position - Corner position (default: "bottom-right") * @param disabled - Disable the toggle button (default: false) */ declare function ThemeToggle({ isDark, onToggle, className, position, disabled, }: ThemeToggleProps): react_jsx_runtime.JSX.Element; interface DragHintAnimationProps { /** The position to center the animation */ position: { x: number; y: number; }; /** Whether the drag direction is horizontal or vertical */ direction: "horizontal" | "vertical"; } /** * DragHintAnimation - Animated hand gesture hint for draggable elements * * Displays an animated hand icon that demonstrates drag interaction. * Automatically fades in and out with a smooth animation cycle. * * @example * ```tsx * <DragHintAnimation * position={{ x: 0, y: 10 }} * direction="horizontal" * /> * ``` */ declare function DragHintAnimation({ position, direction }: DragHintAnimationProps): react_jsx_runtime.JSX.Element; /** * ProductOptions - Product variant selector with theme-aware styling * * ⚠️ **IMPORTANT: Must be used within a `<Product>` context provider!** * * A composed component that renders interactive UI for selecting product variants * (size, color, material, etc.). Uses inline implementations with custom theme-aware * styling for maximum control and visual quality. * * **Context Requirements:** * - Reads product options, combinations, and current selection from Product context * - Automatically updates context when user selects options * - Returns `null` if product has no options * * Features: * - Theme-aware borders using CSS variables * - Custom color swatches with subtle borders (ring-border/30) * - Smooth hover transitions and opacity states * - Size buttons with proper spacing and rounded corners * - Theme-controlled internal spacing via CSS variables * - Inline implementations for full style control (no primitives) * * Spacing: * - Internal spacing controlled by CSS variables: * - `--spacing-option-groups` (default: 1rem) - gap between option groups * - `--spacing-option-items` (default: 0.5rem) - gap between label and choices * - External spacing should be controlled by parent container * * @example * ```tsx * // ✅ CORRECT - Basic usage within Product context * <Product productId="shirt-123"> * <ProductOptions /> * </Product> * ``` * * @example * ```tsx * // ✅ CORRECT - Recommended layout with proper spacing * <Product productId="shirt-123"> * <div className="flex flex-col gap-6"> * <ProductImage /> * <ProductOptions /> * <ProductPrice /> * <AddToCart /> * </div> * </Product> * ``` * * @example * ```tsx * // ❌ WRONG - Missing Product context (will throw error!) * <ProductOptions /> // ❌ useProduct() will throw! * ``` * * @example * ```css * // Customize internal spacing via CSS * :root { * --spacing-option-groups: 1.5rem; // Increase spacing between Size and Color * --spacing-option-items: 0.75rem; // Increase spacing between label and buttons * } * ``` */ declare function ProductOptions({ className }: { className?: string; }): react_jsx_runtime.JSX.Element | null; interface ProductCardProps { /** * Click handler for the card */ onClick?: () => void; /** * Additional CSS classes for the card container */ className?: string; /** * Card variant - determines the layout and styling */ variant?: "default" | "overlay" | "minimal"; /** * Whether to show the product price */ showPrice?: boolean; /** * Whether to show the product category/tag */ showCategory?: boolean; /** * Children to render inside the card (for custom content) */ children?: React.ReactNode; /** * Specific variant ID to use for the mockup */ variantId?: string; /** * Specific mockup ID to use for the mockup */ mockupId?: string; } /** * ProductCard - Display product with image, name, price, and category * * A composed component that renders a product card with multiple layout variants. * Integrates with Product context for automatic data binding and supports * clickable cards for navigation. * * Features: * - Three distinct variants (default, overlay, minimal) * - Optional artwork display via ProductImage * - Automatic price formatting * - Category/tag display * - Clickable card with accessible button semantics * - Gradient overlay for text visibility (overlay variant) * - Responsive aspect ratios * - Dark mode support * - CSS custom property theming * * **Variants:** * - `default` - Card with padding, rounded corners, and hover shadow * - `overlay` - Image with gradient overlay and text at bottom (search results style) * - `minimal` - Clean layout with image and text below (product grid style) * * **Context Integration:** * - Must be used within a `Product` context provider * - Automatically extracts product name, price, category from context * - ProductImage inside ProductCard reads artwork from Design context (set by ArtSelector) * - Works seamlessly with `ProductList` for catalog display * * @example * ```tsx * // In a product catalog with ProductList * <ProductList limit={6}> * <ProductCard * variant="overlay" * showCategory={true} * onClick={() => router.push('/product/${product.id}')} * /> * </ProductList> * ``` * * @example * ```tsx * // Standalone with explicit Product context and artwork via ArtSelector * <Product productId="shirt-123"> * <ArtSelector artworks={[...]} /> * <ProductCard * showPrice={true} * variant="minimal" * /> * </Product> * ``` * * @example * ```tsx * // With custom children for badges or actions * <ProductCard variant="default" showPrice> * <div className="mt-2"> * <span className="badge">New</span> * <button className="add-to-cart-btn">Quick Add</button> * </div> * </ProductCard> * ``` * * @example * ```tsx * // Grid layout example * <div className="grid grid-cols-3 gap-4"> * {products.map(product => ( * <Product key={product.id} productData={product}> * <ProductCard variant="minimal" showPrice /> * </Product> * ))} * </div> * ``` * * @param onClick - Click handler (makes card interactive/clickable) * @param className - Additional CSS classes for card container * @param variant - Layout variant ("default", "overlay", or "minimal") * @param showPrice - Display product price (default: false) * @param showCategory - Display product category/tag (default: true) * @param children - Custom content to render inside card (badges, buttons, etc.) */ declare function ProductCard({ onClick, className, variant, showPrice, showCategory, children, variantId, mockupId, }: ProductCardProps): react_jsx_runtime.JSX.Element | null; interface ProductListProps { endpoint?: string; limit?: number; children: React.ReactNode; } /** * ProductList - Fetch and display a grid of products * * A composed component that efficiently fetches multiple products in a single * API call and renders them in a responsive grid. Pre-fetches product data to * avoid duplicate API calls from child Product components. * * Features: * - Single API call for multiple products (efficient) * - Pre-fetched data passed to Product components * - Responsive grid layout (1/2/3 columns) * - Loading state with placeholder * - Integration with Shop context for configuration * - Automatic endpoint inheritance from Shop context * - Customizable limit * * **Performance:** * - Fetches all products once (not per-product) * - Passes productData to Product to skip individual fetches * - Reduces API calls from N to 1 * * **Context Integration:** * - Works with Shop context for endpoint configuration * - Falls back to props or environment variables * - Each child Product gets pre-fetched data * * @example * ```tsx * // Basic product grid * <ProductList limit={12}> * <ProductCard variant="minimal" showPrice /> * </ProductList> * ``` * * @example * ```tsx * // With Shop context * <Shop endpoint="http://localhost:3000"> * <ProductList limit={6}> * <ProductCard variant="overlay" showCategory /> * </ProductList> * </Shop> * ``` * * @example * ```tsx * // Custom children per product * <ProductList limit={8}> * <div className="space-y-2"> * <ProductImage /> * <ProductCard variant="minimal" /> * <AddToCart className="w-full" /> * </div> * </ProductList> * ``` * * @example * ```tsx * // Standalone with explicit configuration * <ProductList * endpoint="http://localhost:3000" * limit={20} * > * <ProductCard showPrice showCategory /> * </ProductList> * ``` * * @param endpoint - API endpoint URL (falls back to Shop context or env) * @param limit - Maximum number of products to fetch (default: 10) * @param children - Component(s) to render for each product (receives Product context) */ declare function ProductList({ endpoint, limit, children, }: ProductListProps): react_jsx_runtime.JSX.Element; interface UseProductGalleryOptions { endpoint?: string; mode?: "mock" | "live"; limit?: number; } interface UseProductGalleryReturn { products: CatalogProduct[]; loading: boolean; error: string | null; refetch: () => void; } /** * useProductGallery - Fetch and manage product collections * * A React hook for fetching either specific products by ID or listing all * available products. Handles loading states, errors, and provides refetch * capability. Perfect for product catalogs, related products, and galleries. * * Features: * - Fetch specific products by ID array * - List all products (empty array) * - Loading and error states * - Refetch capability * - Mock/live mode support * - Automatic deduplication via memoization * - Limit for product lists * * **Modes:** * - Specific products: Pass array of IDs `['id1', 'id2']` * - List all: Pass empty array `[]` or omit parameter * * **Use Cases:** * - Product catalog pages * - Related/recommended products * - Search results * - Recently viewed * - User favorites * * @example * ```tsx * // Fetch specific products * const { products, loading, error, refetch } = useProductGallery( * ['BEEB77', 'RQNU68'], * { endpoint: 'http://localhost:3000', mode: 'mock' } * ); * * if (loading) return <Spinner />; * if (error) return <Error message={error} onRetry={refetch} />; * * return ( * <div className="grid grid-cols-3 gap-4"> * {products.map(product => ( * <ProductCard key={product.id} product={product} /> * ))} * </div> * ); * ``` * * @example * ```tsx * // List all products with limit * const { products, loading } = useProductGallery( * [], // Empty array = list all * { mode: 'mock', limit: 12 } * ); * ``` * * @example * ```tsx * // Use in Remix loader * export async function loader() { * // Note: In loaders, use SDK functions directly * // This hook is for client components * const products = await listProducts({ ... }); * return json({ products }); * } * * // Then in component * function ProductGallery() { * const { products } = useLoaderData<typeof loader>(); * // Or use hook for client-side filtering * const { products: filtered } = useProductGallery( * products.map(p => p.id) * ); * } * ``` * * @example * ```tsx * // With error handling and retry * function ProductCatalog() { * const { products, loading, error, refetch } = useProductGallery([], { * mode: 'live', * limit: 20 * }); * * if (error) { * return ( * <div> * <p>Failed to load: {error}</p> * <button onClick={refetch}>Retry</button> * </div> * ); * } * * return <ProductGrid products={products} loading={loading} />; * } * ``` * * @param productIds - Array of product IDs to fetch (empty array = list all) * @param options - Configuration options * @param options.endpoint - API endpoint URL * @param options.mode - Data source ("mock" or "live", default: "mock") * @param options.limit - Max products to return when listing all (default: 10) * @returns Object with products array, loading state, error, and refetch function */ declare function useProductGallery(productIds?: string[], // Make optional - empty array means "list all products" options?: UseProductGalleryOptions): UseProductGalleryReturn; interface ProductGalleryProps extends UseProductGalleryOptions { productIds?: string[]; children: (products: CatalogProduct[], loading: boolean, error: string | null, refetch: () => void) => React.ReactNode; } /** * ProductGallery - Fetch products and provide them via render prop * * Two modes: * 1. Specific products: Pass productIds array * 2. List all products: Pass empty array or omit productIds * * @example Specific products * <ProductGallery productIds={["BEEB77", "RQNU68", "UJJ2QU"]}> * {(products, loading, error) => ( * <div className="grid grid-cols-3 gap-4"> * {products.map(product => ( * <ProductImage key={product.id} product={product} /> * ))} * </div> * )} * </ProductGallery> * * @example List all products * <ProductGallery limit={12}> * {(products, loading, error) => ( * <ProductCatalog products={products} /> * )} * </ProductGallery> */ declare function ProductGallery({ productIds, // Default to empty array for listing all products children, ...options }: ProductGalleryProps): react_jsx_runtime.JSX.Element; interface AddToCartProps { text?: string; loadingText?: string; successText?: string; disabled?: boolean; className?: string; quantity?: number; onClick?: (cartDetail: any) => void; onError?: (error: Error) => void; simulateDelay?: number; } /** * AddToCart - Add to cart button with validation and loading states * * A composed component that provides a complete add-to-cart experience with * automatic validation, loading states, success feedback, and cart detail creation. * Integrates with Product context for seamless variant and pricing handling. * * Features: * - Automatic selection validation before allowing add * - Loading state with customizable text * - Success state with visual feedback (green background) * - Automatic cart detail generation with variant info * - Simulated delay for cart operations * - Screen reader announcements for state changes * - Accessible with ARIA labels and busy states * - Disabled state when selection is invalid * - CSS custom property support for theming * * **Validation:** * - Ensures all required options are selected * - Checks variant availability * - Validates against product combinations * - Disables button when invalid * * **Context Integration:** * - Must be used within a `Product` context provider * - Automatically uses current selection from context * - Accesses pricing and variant data * * @example * ```tsx * // Basic usage within Product context * <Product productId="shirt-123"> * <ProductOptions /> * <AddToCart * onClick={(cartDetail) => { * console.log('Added to cart:', cartDetail); * // Add to your cart state/API * }} * /> * </Product> * ``` * * @example * ```tsx * // With custom text and quantity * <AddToCart * text="Buy Now" * loadingText="Processing..." * successText="Added to Cart!" * quantity={2} * onClick={(detail) => addToCart(detail)} * onError={(err) => showNotification(err.message)} * /> * ``` * * @example * ```tsx * // With custom styling * <AddToCart * className="w-full py-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700" * text="Add to Bag" * /> * ``` * * @example * ```tsx * // Faster simulation for testing * <AddToCart * simulateDelay={500} * onClick={(detail) => console.log(detail)} * /> * ``` * * @param text - Button text in default state (default: "Add to Bag") * @param loadingText - Button text while adding (default: "Adding...") * @param successText - Button text after successful add (default: "Added!") * @param disabled - Force button to disabled state * @param className - Additional CSS classes for button * @param quantity - Number of items to add (default: 1) * @param onClick - Callback with cart detail after successful add * @param onError - Callback when add fails with error * @param simulateDelay - Delay in ms to simulate cart operation (default: 1000) */ declare function AddToCart({ text, loadingText, successText, disabled, className, quantity, onClick, onError, simulateDelay, }: AddToCartProps): react_jsx_runtime.JSX.Element; /** * Regular artwork (photos, illustrations, logos) * Use with ArtAlignment for positioning */ type RegularArtwork$1 = { type: "regular"; src: string; }; /** * Seamless tiling pattern * Use with TileCount for density control * REQUIRES tileCount to be specified */ type SeamlessPattern$1 = { type: "pattern"; src: string; /** * Tile density (0.25 | 0.5 | 1 | 2 | 4) * REQUIRED for patterns - controls how many times the pattern repeats */ tileCount: 0.25 | 0.5 | 1 | 2 | 4; }; /** * Discriminated union of artwork types * TypeScript will enforce that SeamlessPattern MUST have tileCount */ type Artwork$1 = RegularArtwork$1 | SeamlessPattern$1; /** * Base props shared across all ProductImage configurations */ interface ProductImageBaseProps { productId?: string; mockupId?: string; variantId?: string; gvid?: string; images?: DesignElement[]; width?: number; effects?: { grain?: 1 | 2; }; className?: string; product?: { id: string; name?: string; placements?: any[]; }; } /** * ProductImage props - context-based artwork provision only * * **⚠️ CONTEXT-ONLY COMPONENT**: This component does NOT accept artwork as a prop! * * **Two ways to provide artwork:** * 1. **Context via ArtSelector** (RECOMMENDED - for interactive selection UI) * 2. **placements prop** (for multi-placement products with colors/images) * * ⚠️ If no artwork is provided through any method, component shows "Select artwork to preview" placeholder */ interface ProductImageProps extends ProductImageBaseProps { /** * Simplified placement map (label -> color or image URL) * For multi-placement products with colors * Example: `{ Front: '#FF0000', Back: '#0000FF' }` * * **Note:** For artwork images, use ArtSelector component for context-based management. * This placements prop is primarily for colors, though it can accept image URLs too. */ placements?: Record<string, string>; } declare function ProductImage({ productId, mockupId, variantId, gvid, images, width, effects, className, product, placements, }: ProductImageProps): react_jsx_runtime.JSX.Element; interface TileCountProps { value?: SeamlessPattern$1["tileCount"]; imageUrl?: string; className?: string; valueClassName?: string; valueStyle?: React.CSSProperties; thumbBorderColor?: string; placement?: string; artwork?: { src: string; tileCount?: SeamlessPattern$1["tileCount"]; }; } /** * TileCount - Interactive tile density control for seamless patterns * * A composed component that allows users to adjust the tile density (repetition frequency) * of seamless pattern artwork. Features a live tiled preview and a slider with 5 preset * density levels, making it easy to control how many times a pattern repeats. * * **🚨 DO NOT USE THIS COMPONENT DIRECTLY!** * **Use `<ArtworkCustomizer />` instead - it automatically handles both regular artwork AND patterns.** * * **Why use ArtworkCustomizer:** * - ✅ Single component for both regular artwork and patterns * - ✅ TypeScript enforces correct props based on artwork type * - ✅ Impossible to make mistakes (no manual conditionals needed) * - ✅ Cleaner, simpler code * * **Only use TileCount directly if:** * - You are 100% certain you only have seamless patterns (never regular artwork) * - You are building a custom component that wraps TileCount * - You have read the ArtworkCustomizer docs and understand why you need direct access * * **⚠️ CRITICAL: If you use TileCount directly:** * - ONLY for seamless pattern artwork - NEVER for regular photos/illustrations * - For regular artwork, use `ArtAlignment` component instead * - NEVER show both TileCount and ArtAlignment at the same time * - Check `artwork.type === 'seamless'` to decide which component to render * - Pattern: `{artwork.type === 'seamless' ? <TileCount /> : <ArtAlignment />}` * * **👉 See ArtworkCustomizer for the recommended approach!** * * **Features:** * - Live preview showing actual tile repetition at 64x64px * - 5 preset tile densities (Fewest → Most) * - Smooth slider with snap-to-preset behavior * - Descriptive labels for each density level * - Checkerboard background for transparent patterns * - Theme-aware styling with CSS variables * - Accessible slider with ARIA labels * - Customizable thumb border color * * **Tile Density Values:** * - `0.25` - "Fewest" - Pattern displays at 4x original size (120px tiles in preview) * - `0.5` - "Less" - Pattern displays at 2x original size (100px tiles in preview) * - `1` - "Normal" - Pattern displays at original size (80px tiles in preview) * - `2` - "More" - Pattern displays at 0.5x original size (60px tiles in preview) * - `4` - "Most" - Pattern displays at 0.25x original size (44px tiles in preview) * * **Visual Behavior:** * - Preview box: 64x64px with 2px border in border color * - Slider: 128px wide with 5 snap points (0, 25, 50, 75, 100) * - Thumb: 24x24px with customizable border, hover scale effect * - Label: "Tiles" with current density name in muted color * * **Context Independence:** * This is a controlled component that doesn't depend on any context. It simply * displays a preview and fires onChange events. Parent components handle state * and integration with design/artwork contexts. * * @example * ```tsx * // Basic usage with state * const [tileCount, setTileCount] = useState(1); * * <TileCount * value={tileCount} * onChange={setTileCount} * imageUrl="https://example.com/pattern.png" * /> * ``` * * @example * ```tsx * // Custom styling with theme integration * <TileCount * value={tileDensity} * onChange={handleTileChange} * imageUrl={patternUrl} * className="mb-6" * valueClassName="text-primary font-semibold" * thumbBorderColor="var(--color-primary)" * /> * ``` * * @example * ```tsx * // Without preview (slider only) * <TileCount * value={1} * onChange={setTileCount} * // No imageUrl = no preview shown * /> * ``` * * @param value - Current tile density (0.25, 0.5, 1, 2, or 4). Defaults to 1 (Normal). If not provided, reads from artwork or Design context * @param imageUrl - URL of the seamless pattern image to preview. If omitted, no preview is shown * @param className - Additional CSS classes for the container element * @param valueClassName - CSS classes for the density label text. Defaults to "text-sm font-normal text-muted-foreground" * @param valueStyle - Inline styles for the density label text * @param thumbBorderColor - Border color for the slider thumb. Defaults to black (#000) */ declare function TileCount({ value: propValue, imageUrl, className, valueClassName, valueStyle, thumbBorderColor, placement, artwork, }: TileCountProps): react_jsx_runtime.JSX.Element; interface CurrentSelectionDisplayProps { /** * Additional CSS classes to apply to the root element */ className?: string; /** * Whether to show the price in the selection display * @default true */ showPrice?: boolean; /** * Custom message to show when no options are selected * @default "No options selected yet. Try selecting some options above!" */ emptyMessage?: string; } /** * CurrentSelectionDisplay - Shows the current product option selections and price * * Automatically displays the selected options and calculated price from the Product context. * Useful for debugging, documentation, or showing a summary of selections to users. * * @example * ```tsx * import { Product, ProductOptions, CurrentSelectionDisplay } from '@yourorg/ui-react'; * * <Product productId="BEEB77"> * <ProductOptions /> * <CurrentSelectionDisplay /> * </Product> * ``` */ declare function CurrentSelectionDisplay({ className, showPrice, emptyMessage, }: CurrentSelectionDisplayProps): react_jsx_runtime.JSX.Element; interface PlacementDesign { imageUrl: string; alignment: ImageAlignment; tiles?: 0.25 | 0.5 | 1 | 2 | 4; } type RegularArtwork = { type: "regular"; src: string; }; type SeamlessPattern = { type: "pattern"; src: string; tileCount: 0.25 | 0.5 | 1 | 2 | 4; }; type Artwork = RegularArtwork | SeamlessPattern; interface ReactProductContext extends ProductContext$1 { updateSelection?: (selection: OptionSelection) => void; currentPrice?: number; placements: Record<string, PlacementDesign>; artworks: Artwork[]; selectedArtwork?: Artwork; setPlacementDesign: (placement: string, design: Partial<PlacementDesign>) => void; getPlacementDesign: (placement: string) => PlacementDesign | undefined; addArtwork: (artwork: Artwork) => void; setSelectedArtwork: (artwork: Artwork | undefined) => void; applyArtworkToPlacement: (placement: string, artwork?: Artwork) => void; } declare const ProductContext: React.Context<ReactProductContext | undefined>; interface ProductProviderProps { children: React.ReactNode; productId?: string; productData?: CatalogProduct; endpoint?: string; source?: string; fetcher?: typeof fetch; className?: string; initialSelection?: OptionSelection; initialPlacements?: Record<string, PlacementDesign>; renderLoading?: () => React.ReactNode; renderError?: (error: any) => React.ReactNode; } /** * Product - Context provider for product data, variant selection, and design management * * A composed pattern component that provides React context for product information, * variant selection, pricing, artwork management, and placement designs. Acts as the * central state container for product-related components. * * Features: * - Automatic product data fetching from API endpoint * - Variant selection and combination management * - Dynamic price calculation based on selected options * - Design/artwork management with placement-specific designs * - Integration with Shop context for shared artwork state * - Loading and error states with custom render props * - Universal provider sync for cross-framework compatibility * - SSR-friendly with optional data prop to skip fetching * * **Context Provided:** * - Product data and metadata * - Option attributes and combinations * - Current selection and price * - Artwork collection and selected artwork * - Placement designs (alignment, tiles, etc.) * - Methods to update selection, artwork, and placements * * **Integration with Shop:** * - Inherits endpoint from Shop context if available * - Shares artwork state across all Product instances in a Shop * - Falls back to local state if used standalone * * @example * ```tsx * // Basic usage - fetch product by ID * <Product productId="shirt-123"> * <ProductImage /> * <ProductOptions /> * <AddToCart /> * </Product> * ``` * * @example * ```tsx * // With initial selection and custom loading * <Product * productId="shirt-123" * initialSelection={{ Size: 'M', Color: 'Blue' }} * renderLoading={() => <Spinner />} * renderError={(err) => <ErrorMessage error={err} />} * > * <ProductCard /> * </Product> * ``` * * @example * ```tsx * // Standalone with explicit data (no fetching) * <Product * productData={myProductData} * initialPlacements={{ * Front: { imageUrl: 'logo.png', alignment: 'center' }, * Back: { imageUrl: 'text.png', alignment: 'top' } * }} * > * <ArtAlignment placement="Front" /> * <ProductImage /> * </Product> * ``` * * @example * ```tsx * // With Shop context * <Shop endpoint="http://localhost:3000"> * <Product productId="shirt-123"> * <ProductImage /> * </Product> * <Product productId="mug-456"> * <ProductImage /> * </Product> * </Shop> * // Both products share artwork state from Shop * ``` * * @param children - Child components that consume product context * @param productId - Product identifier to fetch * @param productData - Explicit product data (skips fetching if provided) * @param endpoint - API endpoint URL (overrides Shop context) * @param source - Source identifier for data fetching (default: "auto") * @param fetcher - Custom fetch function (e.g., for auth headers) * @param className - Additional CSS classes for wrapper div * @param initialSelection - Pre-select variant options (e.g., { Size: 'M', Color: 'Blue' }) * @param initialPlacements - Pre-configure placement designs * @param renderLoading - Custom loading UI component * @param renderError - Custom error UI component */ declare function Product({ children, productId, productData, endpoint, source, fetcher, className, initialSelection, initialPlacements, renderLoading, renderError, }: ProductProviderProps): react_jsx_runtime.JSX.Element; declare function useProduct(): ReactProductContext; declare function useProductOptional(): ReactProductContext | undefined; declare function useDesign(): ReactProductContext; declare function useDesignOptional(): ReactProductContext | undefined; interface ArtSelectorProps { className?: string; artworks: string[] | Artwork[]; } /** * ArtSelector - Interactive artwork selection gallery * * ⚠️ **IMPORTANT:** This component renders visible UI (a scrollable gallery of thumbnails). * For custom artwork selection UI, use the `useShop()` hook instead. * * @see https://merchify-site-staging.driuqzy.workers.dev/docs/react/components/select-artwork * * A composed component that displays a scrollable gallery of artwork thumbnails * with automatic dimension loading and selection state management. Works with * both Shop and Product contexts. * * **🎯 Primary Purpose:** * This component sets the `selectedArtwork` in Design context, which is then * automatically read by components like `<ProductImage>` and `<ArtworkCustomizer>`. * * **Features:** * - Renders a horizontal scrollable gallery of artwork thumbnails * - Automatic image dimension detection on mount * - Aspect ratio preservation in thumbnails (60px square) * - Selection state with visual feedback (brightness filter) * - Keyboard navigation support (Enter/Space to select) * - Theme-aware borders using CSS variables * - Checkerboard background pattern for transparent images * - Focus visible ring on keyboard navigation * * **Context Integration:** * - Works with `Shop` context for global artwork management * - Also works with `Product`/`Design` context for product-specific artwork * - **Automatically updates `selectedArtwork` in context on click** * - Loads artwork dimensions asynchronously on mount * - Auto-selects first artwork if none selected * - Other components like `ProductImage` and `ArtworkCustomizer` read from this context * * **Visual Behavior:** * - Selected artwork: Full brightness with accent border * - Unselected artwork: 50% brightness, 80% on hover * - Loading state: Shows "Loading artwork..." text * * @example * ```tsx * // Basic usage with Shop context * <Shop> * <ArtSelector artworks={[ * 'https://example.com/art1.jpg', * 'https://example.com/art2.jpg', * 'https://example.com/art3.jpg' * ]} /> * <Product productId="shirt-123"> * <ProductImage /> * </Product> * </Shop> * ``` * * @example * ```tsx * // Within Product context with ArtworkCustomizer * <Product productId="shirt-123"> * <ArtSelector artworks={[ * { type: 'regular', src: 'https://example.com/photo.jpg' }, * { type: 'pattern', src: 'https://example.com/pattern.jpg', tileCount: 1 } * ]} /> * <ProductImage /> * <ArtworkCustomizer /> * </Product> * ``` * * @example * ```tsx * // Custom artwork selector using useShop() hook (recommended for custom UI) * function MyCustomSelector() { * const { setSelectedArtwork } = useShop(); * * const selectArtwork = async (url: string) => { * const img = new Image(); * img.onload = () => { * setSelectedArtwork({ * type: 'regular', * src: url, * width: img.naturalWidth, * height: img.naturalHeight, * aspectRatio: img.naturalWidth / img.naturalHeight, * }); * }; * img.src = url; * }; * * return <button onClick={() => selectArtwork(url)}>Select</button>; * } * ``` * * @param artworks - Array of artwork URLs (strings) or Artwork objects ({ type: 'regular' | 'pattern', src: string, tileCount?: number }). Note: use "artworks" (plural), not "artwork" (singular). * @param className - Additional CSS classes for the container element */ declare function ArtSelector({ className, artworks: customArtworkUrls, }: ArtSelectorProps): react_jsx_runtime.JSX.Element; interface RealtimeMockupProps { config: WebSocketConfig; onMockupRendered?: (mockupUrl: string) => void; className?: string; showStatus?: boolean; showLogs?: boolean; autoConnect?: boolean; } declare const RealtimeMockup: React.FC<RealtimeMockupProps>; interface LightboxProps { imageUrl: string; alt?: string; onClose: () => void; className?: string; } declare function Lightbox({ imageUrl, alt, onClose, className, }: LightboxProps): react_jsx_runtime.JSX.Element; /** * usePlacementsProcessor - Process placement prop to separate colors from images * * Takes a placements object where values can be either color codes or image URLs, * and separates them into distinct arrays/objects for easier processing. * * **Color Detection:** * - Hex colors: `#fff`, `#FF00FF` * - RGB/RGBA: `rgb(255, 0, 0)`, `rgba(255, 0, 0, 0.5)` * - HSL/HSLA: `hsl(120, 100%, 50%)`, `hsla(120, 100%, 50%, 0.3)` * * @example * ```tsx * const { processedImages, colorPlacements } = usePlacementsProcessor({ * Front: 'https://example.com/art.jpg', * Back: '#FF0000', * Sleeve: 'rgb(0, 255, 0)' * }, []); * * // Result: * // processedImages = [{ placement: 'Front', imageUrl: 'https://example.com/art.jpg' }] * // colorPlacements = { Back: '#FF0000', Sleeve: 'rgb(0, 255, 0)' } * ``` * * @param placements - Object mapping placement labels to color codes or image URLs * @param fallbackImages - Images array to return if no placements provided * @returns Object with processedImages array and colorPlacements object */ declare function usePlacementsProcessor(placements: Record<string, string> | undefined, fallbackImages: DesignElement[]): { processedImages: DesignElement[]; colorPlacements: Record<string, string> | null; }; interface ImageUpdateRequest { id: string; execute: () => void; priority?: number; url?: string; } interface ShopContextValue { endpoint?: string; artworks: Artwork[]; selectedArtwork?: Artwork; addArtwork: (artwork: Artwork) => void; setSelectedArtwork: (artwork: Artwork | undefined) => void; queueImageUpdate: (request: ImageUpdateRequest) => void; cancelImageUpdate: (id: string) => void; getProduct: (productId: string) => CatalogProduct | undefined; addProduct: (product: CatalogProduct) => void; addProducts: (products: CatalogProduct[]) => void; clearProductCache: () => void; } declare const ShopContext: React.Context<ShopContextValue | undefined>; interface ShopProps { children: ReactNode; endpoint?: string; mockupUrl?: string; accountId?: string; } /** * Shop - Global shop context provider with artwork and image throttling * * A pattern component that provides centralized state management for shop-wide * concerns like artwork selection, product caching, and coordinated image generation * throttling. Wrap your entire app (or shop section) in this provider. * * Features: * - Centralized artwork state shared across all Product instances * - Image update queueing with intelligent throttling (5 images/sec) * - Product data caching for performance * - LRU URL cache to bypass throttling for repeated requests * - Per-component throttling (500ms) to prevent rapid updates * - Priority-based queue processing * - Automatic SDK configuration * - Mode/endpoint configuration cascade to child components * * **Image Throttling:** * - Limits mockup generation to 5 images per second (non-cached) * - Cached URLs bypass throttling entirely * - Per-component throttle prevents same component from spamming * - Priority queue ensures important images load first * - LRU cache (max 100 URLs) remembers recent requests * * **Artwork Management:** * - Centralized artwork collection * - Shared selected artwork across all products * - Automatic sync between Product instances * * **Product Caching:** * - In-memory product cache to reduce API calls * - Add/get/clear cache methods * - Useful for product lists and galleries * * @example * ```tsx * // Basic shop setup * <Shop endpoint="http://localhost:3000"> * <ProductList limit={12}> * <ProductCard variant="overlay" /> * </ProductList> * </Shop> * ``` * * @example * ```tsx * // Multiple products sharing artwork state * <Shop mockupUrl="https://api.example.com"> * <ArtSelector /> * * <div class