@fmarlats/react-like-button 1.1.4

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/LikeButton/DefaultHeartIcon.tsx","../src/Particle/presets.ts","../src/LikeButton/useLikeButton.ts","../src/Particle/utils.ts","../src/LikeButton/utils.ts","../src/LikeButton/LikeButton.vanilla.tsx","../src/Particle/shapes/CircleShape.tsx","../src/Particle/shapes/HeartShape.tsx","../src/Particle/shapes/SparkleShape.tsx","../src/Particle/shapes/SquareShape.tsx","../src/Particle/shapes/StarShape.tsx","../src/Particle/shapes/utils.ts","../src/Particle/useParticle.ts","../src/Particle/Particle.vanilla.tsx"],"sourcesContent":["import type { IconRenderProps } from \"./types\"\n\n/**\n * Default heart icon for LikeButton.\n * Can be used as reference for creating custom icons.\n */\nexport function DefaultHeartIcon({ size, className }: IconRenderProps) {\n return (\n <svg\n className={className}\n style={{\n width: size,\n height: size,\n stroke: \"#111827\",\n strokeWidth: 2,\n fill: \"transparent\",\n }}\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z\"\n />\n </svg>\n )\n}\n\nexport default DefaultHeartIcon\n","import type { ParticleConfig, ParticlePreset, ParticlePresetConfig } from \"./types\"\n\n/**\n * Default particle configuration.\n * Used as the base for all particle effects when no preset or custom config is provided.\n */\nexport const DEFAULT_PARTICLE_CONFIG: Required<ParticleConfig> = {\n shape: \"heart\",\n speed: 500,\n distance: { min: 60, max: 100 },\n spread: 360,\n spreadOffset: 0,\n size: { min: 1.0, max: 1.5 },\n colors: [\"#EF4444\", \"#B9FF14\", \"#3B82F6\"],\n count: 8,\n easing: \"cubic-bezier(0.22, 1, 0.36, 1)\",\n fadeOut: true,\n}\n\n/**\n * Built-in particle effect presets.\n * Each preset provides a distinct visual effect optimized for different use cases.\n *\n * @example\n * ```tsx\n * import { PARTICLE_PRESETS } from '@fmarlats/react-like-button';\n *\n * // Use a preset directly\n * const burstConfig = PARTICLE_PRESETS.burst;\n * ```\n */\nexport const PARTICLE_PRESETS: Record<ParticlePreset, ParticlePresetConfig> = {\n /**\n * Burst - Fast, enthusiastic explosion effect\n * - Wide 360° spread\n * - Fast animation (400ms)\n * - Multiple colors\n * - 12 particles\n * - Perfect for: Likes, favorites, celebrations\n */\n burst: {\n shape: \"heart\",\n speed: 400,\n distance: { min: 80, max: 120 },\n spread: 360,\n spreadOffset: 0,\n size: { min: 1.0, max: 1.5 },\n colors: [\"#EF4444\", \"#F59E0B\", \"#3B82F6\"],\n count: 12,\n easing: \"cubic-bezier(0.22, 1, 0.36, 1)\",\n fadeOut: true,\n },\n\n /**\n * Fountain - Upward spray effect\n * - 120° upward spread\n * - Medium animation (600ms)\n * - Cool colors (blue, purple, pink)\n * - 10 particles\n * - Perfect for: Achievements, upgrades, success\n */\n fountain: {\n shape: \"circle\",\n speed: 600,\n distance: { min: 60, max: 100 },\n spread: 120,\n spreadOffset: -90, // Upward\n size: { min: 0.8, max: 1.2 },\n colors: [\"#3B82F6\", \"#8B5CF6\", \"#EC4899\"],\n count: 10,\n easing: \"cubic-bezier(0.22, 1, 0.36, 1)\",\n fadeOut: true,\n },\n\n /**\n * Confetti - Colorful celebration effect\n * - Full 360° spread\n * - Slow animation (800ms)\n * - Rainbow colors\n * - 15 particles\n * - Perfect for: Milestones, victories, special events\n */\n confetti: {\n shape: \"square\",\n speed: 800,\n distance: { min: 70, max: 110 },\n spread: 360,\n spreadOffset: 0,\n size: { min: 0.6, max: 1.4 },\n colors: [\"#EF4444\", \"#F59E0B\", \"#10B981\", \"#3B82F6\", \"#8B5CF6\", \"#EC4899\"],\n count: 15,\n easing: \"ease-out\",\n fadeOut: true,\n },\n\n /**\n * Gentle - Subtle, calm effect\n * - 180° upward spread\n * - Slow animation (700ms)\n * - Soft red tones\n * - 6 particles\n * - Perfect for: Subtle interactions, quiet appreciation\n */\n gentle: {\n shape: \"heart\",\n speed: 700,\n distance: { min: 40, max: 60 },\n spread: 180,\n spreadOffset: -90,\n size: { min: 0.8, max: 1.0 },\n colors: [\"#EF4444\", \"#F87171\"],\n count: 6,\n easing: \"ease-in-out\",\n fadeOut: true,\n },\n\n /**\n * Fireworks - Explosive sparkle effect\n * - Full 360° spread\n * - Medium-fast animation (500ms)\n * - Warm sparkle colors\n * - 16 particles\n * - Large size range\n * - Perfect for: Big celebrations, major achievements\n */\n fireworks: {\n shape: \"sparkle\",\n speed: 500,\n distance: { min: 100, max: 150 },\n spread: 360,\n spreadOffset: 0,\n size: { min: 1.2, max: 2.0 },\n colors: [\"#FBBF24\", \"#F59E0B\", \"#EF4444\", \"#EC4899\"],\n count: 16,\n easing: \"cubic-bezier(0.22, 1, 0.36, 1)\",\n fadeOut: true,\n },\n}\n\n/**\n * Get a particle preset configuration by name.\n *\n * @param preset - The preset name\n * @returns The complete particle configuration for the preset\n *\n * @example\n * ```tsx\n * import { getParticlePreset } from '@fmarlats/react-like-button';\n *\n * const burstConfig = getParticlePreset('burst');\n * const fountainConfig = getParticlePreset('fountain');\n * ```\n */\nexport function getParticlePreset(preset: ParticlePreset): ParticlePresetConfig {\n return PARTICLE_PRESETS[preset]\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport type { ParticleConfig, ParticlePreset, ParticleShape } from \"../Particle/types\"\nimport {\n normalizeRange,\n randomAngle,\n randomInRange,\n resolveParticleConfig,\n} from \"../Particle/utils\"\nimport { PARTICLE_CLEANUP_BUFFER_MS } from \"./utils\"\n\n/** Data structure for a single particle effect */\nexport interface ParticleData {\n id: string\n angle: number\n distance: number\n scale: number\n color: string\n shape: ParticleShape\n speed: number\n easing: string\n fadeOut: boolean\n}\n\n/**\n * State object passed to dynamic aria-label functions.\n * Enables internationalization (i18n) support for accessibility labels.\n */\nexport interface AriaLabelState {\n /** Whether the maximum clicks have been reached */\n isMaxed: boolean\n /** Number of clicks remaining before max */\n remaining: number\n /** Current number of clicks by the user */\n clicks: number\n /** Maximum number of clicks allowed */\n maxClicks: number\n}\n\n/**\n * Aria label can be either:\n * - A static string for simple use cases\n * - A function that receives state for dynamic/i18n labels\n *\n * @example\n * ```tsx\n * // Static string\n * ariaLabel=\"Like this post\"\n *\n * // Dynamic function for i18n\n * ariaLabel={({ isMaxed, remaining }) =>\n * isMaxed ? t('likes.maxed') : t('likes.remaining', { count: remaining })\n * }\n * ```\n */\nexport type AriaLabelProp = string | ((state: AriaLabelState) => string)\n\n/** Options for the useLikeButton hook */\nexport interface UseLikeButtonOptions {\n /** Current click count (controlled mode). If not provided, internal state is used. */\n clicks?: number\n /**\n * Initial click count for uncontrolled mode.\n * Only used on first render; ignored if `clicks` prop is provided.\n * @default 0\n */\n defaultClicks?: number\n /** Maximum number of clicks allowed per user */\n maxClicks?: number\n /**\n * Callback when button is clicked.\n * @param clicks - The current click count, after increment\n * @param event - The mouse event that triggered the click\n */\n onClick?: (clicks: number, event: React.MouseEvent<HTMLButtonElement>) => void\n /**\n * Callback when click count changes. Simpler alternative to onClick for controlled mode.\n * Called with just the new count, ideal for state setters like `onChange={setClicks}`.\n * @param clicks - The new click count\n */\n onChange?: (clicks: number) => void\n /**\n * Callback when button is right-clicked. Also triggered by Shift+Enter for keyboard accessibility.\n * @param clicks - The current click count, not incremented as right-click does not increment\n * @param event - The mouse or keyboard event that triggered the action\n */\n onRightClick?: (\n clicks: number,\n event: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLButtonElement>,\n ) => void\n /** Whether the button is disabled */\n disabled?: boolean\n /**\n * Show particle effects on click.\n * When false, no particles are spawned or rendered.\n * @default true\n */\n showParticles?: boolean\n /**\n * Custom aria-label override. Accepts either a static string or a function\n * that receives the current state for dynamic/i18n labels.\n *\n * @example\n * ```tsx\n * // Static string\n * ariaLabel=\"Like this post\"\n *\n * // Dynamic function for i18n support\n * ariaLabel={({ isMaxed, remaining }) =>\n * isMaxed ? t('likes.maxed') : t('likes.remaining', { count: remaining })\n * }\n * ```\n */\n ariaLabel?: AriaLabelProp\n\n // ========== PARTICLE CONFIGURATION ==========\n /**\n * Particle effect preset (burst, fountain, confetti, gentle, fireworks)\n * @example\n * ```tsx\n * useLikeButton({ particlePreset: 'burst' })\n * ```\n */\n particlePreset?: ParticlePreset\n\n /**\n * Custom particle configuration (overrides preset)\n * @example\n * ```tsx\n * useLikeButton({\n * particleConfig: {\n * shape: 'star',\n * colors: ['#FFD700', '#FFA500'],\n * count: 12,\n * }\n * })\n * ```\n */\n particleConfig?: Partial<ParticleConfig>\n}\n\n/** Default values */\nexport const LIKE_BUTTON_DEFAULTS = {\n maxClicks: 1,\n size: 96,\n fillColor: \"#EF4444\",\n waveColor: \"#B91C1C\",\n} as const\n\n/** Return type for the useLikeButton hook */\nexport interface UseLikeButtonReturn {\n /** Current click count */\n clicks: number\n /** Whether max clicks reached */\n isMaxed: boolean\n /** Whether button is disabled */\n disabled: boolean\n /** Fill percentage (0-100) */\n fillPercentage: number\n /** Active particles to render */\n particles: ParticleData[]\n /** Click handler for the button */\n handleClick: (e: React.MouseEvent<HTMLButtonElement>) => void\n /** Right-click handler for the button */\n handleRightClick: (e: React.MouseEvent<HTMLButtonElement>) => void\n /**\n * Keyboard handler for accessibility.\n * Triggers onRightClick when Shift+Enter is pressed.\n */\n handleKeyDown: (e: React.KeyboardEvent<HTMLButtonElement>) => void\n /** Aria label for accessibility */\n ariaLabel: string\n /** Whether button has been pressed */\n isPressed: boolean\n /** Whether onRightClick is provided (for aria-keyshortcuts) */\n hasRightClickAction: boolean\n}\n\n/**\n * Headless hook for LikeButton logic.\n * Handles state management, particle spawning, and accessibility.\n *\n * @example\n * ```tsx\n * const { handleClick, clicks, particles, ariaLabel } = useLikeButton({\n * maxClicks: 10,\n * onClick: (count, event) => {\n * console.log('Clicked!', count)\n * // Access event if needed\n * event.stopPropagation()\n * },\n * });\n * ```\n */\nexport function useLikeButton(options: UseLikeButtonOptions = {}): UseLikeButtonReturn {\n const {\n clicks: externalClicks,\n defaultClicks = 0,\n maxClicks = LIKE_BUTTON_DEFAULTS.maxClicks,\n onClick,\n onChange,\n onRightClick,\n disabled: externalDisabled,\n showParticles = true,\n particlePreset,\n particleConfig,\n ariaLabel: customAriaLabel,\n } = options\n\n // Warn if both controlled and default values are provided\n if (externalClicks !== undefined && options.defaultClicks !== undefined) {\n console.warn(\n \"LikeButton: `defaultClicks` is ignored when `clicks` is provided (controlled mode).\",\n )\n }\n\n // Internal state for uncontrolled mode (initialized with defaultClicks)\n const [internalClicks, setInternalClicks] = useState(defaultClicks)\n const [particles, setParticles] = useState<ParticleData[]>([])\n\n // Track active timeout IDs for cleanup on unmount\n const timeoutRefs = useRef<Set<ReturnType<typeof setTimeout>>>(new Set())\n\n // Cleanup all pending timeouts on unmount to prevent memory leaks\n // and \"setState on unmounted component\" warnings\n useEffect(() => {\n return () => {\n for (const timeoutId of timeoutRefs.current) {\n clearTimeout(timeoutId)\n }\n }\n }, [])\n\n // Use external state if provided, otherwise use internal\n const clicks = externalClicks ?? internalClicks\n const isMaxed = clicks >= maxClicks\n const disabled = externalDisabled ?? isMaxed\n\n const spawnParticles = useCallback(() => {\n if (!showParticles) return\n\n // Resolve final particle configuration from preset and custom config\n const config = resolveParticleConfig(particlePreset, particleConfig)\n\n // Normalize ranges for random value generation\n const distanceRange = normalizeRange(config.distance)\n const sizeRange = normalizeRange(config.size)\n\n const id = Date.now()\n\n const newParticles = Array.from({ length: config.count }).map((_, i) => ({\n id: `${id}-${i}`,\n angle: randomAngle(config.spread, config.spreadOffset),\n distance: randomInRange(distanceRange),\n scale: randomInRange(sizeRange),\n color: config.colors[Math.floor(Math.random() * config.colors.length)],\n shape: config.shape,\n speed: config.speed,\n easing: config.easing,\n fadeOut: config.fadeOut,\n }))\n\n setParticles((prev) => [...prev, ...newParticles])\n\n // Cleanup particles after animation completes\n // Add buffer time to ensure animation finishes\n const cleanupDelay = config.speed + PARTICLE_CLEANUP_BUFFER_MS\n // Use Set for O(1) lookup instead of O(n) find() - avoids O(n²) filter\n const idsToRemove = new Set(newParticles.map((p) => p.id))\n const timeoutId = setTimeout(() => {\n setParticles((prev) => prev.filter((p) => !idsToRemove.has(p.id)))\n // Remove from tracking set after completion\n timeoutRefs.current.delete(timeoutId)\n }, cleanupDelay)\n // Track timeout for cleanup on unmount\n timeoutRefs.current.add(timeoutId)\n }, [showParticles, particlePreset, particleConfig])\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n if (disabled) return\n\n const newClicks = clicks + 1\n\n // Update internal state if in uncontrolled mode\n if (externalClicks === undefined) {\n setInternalClicks(newClicks)\n }\n\n spawnParticles()\n onChange?.(newClicks)\n onClick?.(newClicks, e)\n },\n [disabled, clicks, externalClicks, spawnParticles, onChange, onClick],\n )\n\n const handleRightClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n e.preventDefault() // Prevent context menu\n if (disabled) return\n\n // Right-click does not increment clicks or spawn particles\n // It only calls the callback with the current click count\n onRightClick?.(clicks, e)\n },\n [disabled, clicks, onRightClick],\n )\n\n /**\n * Keyboard handler for accessibility.\n * Shift+Enter triggers the right-click action as a keyboard alternative.\n */\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLButtonElement>) => {\n if (e.shiftKey && e.key === \"Enter\") {\n e.preventDefault()\n if (disabled) return\n onRightClick?.(clicks, e)\n }\n },\n [disabled, clicks, onRightClick],\n )\n\n const fillPercentage = (clicks / maxClicks) * 100\n\n const defaultAriaLabel = isMaxed\n ? \"Thank you for your likes!\"\n : `Like this content. ${maxClicks - clicks} clicks remaining`\n\n // Compute aria label - support both static string and dynamic function\n const ariaLabelState: AriaLabelState = {\n isMaxed,\n remaining: maxClicks - clicks,\n clicks,\n maxClicks,\n }\n\n const computedAriaLabel =\n customAriaLabel === undefined\n ? defaultAriaLabel\n : typeof customAriaLabel === \"function\"\n ? customAriaLabel(ariaLabelState)\n : customAriaLabel\n\n return {\n clicks,\n isMaxed,\n disabled,\n fillPercentage,\n particles,\n handleClick,\n handleRightClick,\n handleKeyDown,\n ariaLabel: computedAriaLabel,\n isPressed: clicks > 0,\n hasRightClickAction: onRightClick !== undefined,\n }\n}\n","import { DEFAULT_PARTICLE_CONFIG, getParticlePreset } from \"./presets\"\nimport type { ParticleConfig, ParticlePreset, Range } from \"./types\"\n\n/**\n * Normalize a number or range configuration to a Range object.\n *\n * @param value - Either a single number or a range object\n * @returns Range object with min and max values\n *\n * @example\n * ```ts\n * normalizeRange(100) // { min: 100, max: 100 }\n * normalizeRange({ min: 50, max: 150 }) // { min: 50, max: 150 }\n * ```\n */\nexport function normalizeRange(value: number | Range): Range {\n return typeof value === \"number\" ? { min: value, max: value } : value\n}\n\n/**\n * Generate a random value within a range.\n *\n * @param range - Range object with min and max values\n * @returns Random number between min and max (inclusive)\n *\n * @example\n * ```ts\n * randomInRange({ min: 50, max: 100 }) // e.g., 73.42\n * randomInRange({ min: 1, max: 1 }) // 1\n * ```\n */\nexport function randomInRange(range: Range): number {\n return range.min + Math.random() * (range.max - range.min)\n}\n\n/**\n * Normalize an angle to the 0-360 degree range.\n *\n * @param angle - Angle in degrees (can be negative or > 360)\n * @returns Normalized angle between 0 and 360\n *\n * @example\n * ```ts\n * normalizeAngle(450) // 90\n * normalizeAngle(-90) // 270\n * normalizeAngle(180) // 180\n * ```\n */\nexport function normalizeAngle(angle: number): number {\n const normalized = angle % 360\n const result = normalized < 0 ? normalized + 360 : normalized\n // Handle -0 edge case\n return result === 0 ? 0 : result\n}\n\n/**\n * Generate a random angle within a spread range.\n *\n * @param spread - Spread angle in degrees (0-360)\n * @param offset - Starting angle offset in degrees\n * @returns Random angle within the spread range\n *\n * @example\n * ```ts\n * // Full circle\n * randomAngle(360, 0) // e.g., 234.5\n *\n * // Upward semicircle\n * randomAngle(180, -90) // e.g., -45 (normalized to 315)\n *\n * // Narrow 45° cone pointing right\n * randomAngle(45, -22.5) // e.g., 10\n * ```\n */\nexport function randomAngle(spread: number, offset: number): number {\n const angle = offset + Math.random() * spread\n return normalizeAngle(angle)\n}\n\n/**\n * Merge two particle configurations, with override taking precedence.\n * Performs a shallow merge - override values replace base values.\n *\n * @param base - Base configuration\n * @param override - Override configuration (takes precedence)\n * @returns Merged configuration\n *\n * @example\n * ```ts\n * const base = { count: 8, colors: ['#FF0000'], speed: 500 };\n * const override = { count: 12, speed: 600 };\n * const merged = mergeParticleConfig(base, override);\n * // Result: { count: 12, colors: ['#FF0000'], speed: 600 }\n * ```\n */\nexport function mergeParticleConfig(\n base: Partial<ParticleConfig>,\n override?: Partial<ParticleConfig>,\n): Partial<ParticleConfig> {\n if (!override) {\n return base\n }\n\n return {\n ...base,\n ...override,\n }\n}\n\n/**\n * Resolve final particle configuration from preset and custom config.\n * Merge priority: defaults < preset < custom config\n *\n * @param preset - Optional preset name\n * @param config - Optional custom configuration\n * @returns Complete particle configuration with all required fields\n *\n * @example\n * ```ts\n * // Use defaults only\n * const config1 = resolveParticleConfig(undefined, undefined);\n *\n * // Use preset\n * const config2 = resolveParticleConfig('burst', undefined);\n *\n * // Use preset with overrides\n * const config3 = resolveParticleConfig('burst', { count: 20 });\n *\n * // Use custom config only\n * const config4 = resolveParticleConfig(undefined, { count: 15, colors: ['#000'] });\n * ```\n */\nexport function resolveParticleConfig(\n preset: ParticlePreset | undefined,\n config: Partial<ParticleConfig> | undefined,\n): Required<ParticleConfig> {\n // Start with defaults\n let resolved: Partial<ParticleConfig> = { ...DEFAULT_PARTICLE_CONFIG }\n\n // Apply preset if provided\n if (preset) {\n resolved = mergeParticleConfig(resolved, getParticlePreset(preset))\n }\n\n // Apply custom config (highest priority)\n if (config) {\n resolved = mergeParticleConfig(resolved, config)\n }\n\n return resolved as Required<ParticleConfig>\n}\n","import type { Cursor, CursorPreset, Shape, StyleOverrides } from \"./types\"\n\n// ============================================\n// Layout & Animation Constants\n// ============================================\n\n/**\n * Maximum fill height percentage.\n * Leaves room for wave animation at top (15% reserved for wave visual).\n */\nexport const MAX_FILL_HEIGHT = 85\n\n/**\n * Icon size as a ratio of the button size.\n * Icon will be 50% of the button dimension.\n */\nexport const ICON_SIZE_RATIO = 0.5\n\n/**\n * Minimum hover offset in pixels.\n * Used to ensure the button always has some visible hover movement.\n */\nexport const MIN_HOVER_OFFSET = 2\n\n/**\n * Buffer time (ms) added to particle animation duration before cleanup.\n * Ensures animation completes before particle is removed from DOM.\n */\nexport const PARTICLE_CLEANUP_BUFFER_MS = 100\n\n// ============================================\n// Wave Animation Durations (seconds)\n// These are used in inline styles and CSS animations\n// ============================================\n\n/** Duration for the back wave animation (slower, creates depth) */\nexport const WAVE_BACK_DURATION_S = 3\n\n/** Duration for the front wave animation (faster, creates motion) */\nexport const WAVE_FRONT_DURATION_S = 1.5\n\n// ============================================\n// Default Styles\n// ============================================\n\n/** Default style values for brutalist design */\nexport const DEFAULT_STYLES: Required<StyleOverrides> = {\n borderWidth: 4,\n borderColor: \"#111827\",\n shadowOffset: 8,\n shadowColor: \"#111827\",\n backgroundColor: \"white\",\n}\n\n/**\n * Compute CSS properties from a shape prop value.\n * Handles both preset strings and custom shape objects.\n */\nexport function getShapeStyles(shape: Shape = \"circle\"): React.CSSProperties {\n if (typeof shape === \"string\") {\n switch (shape) {\n case \"circle\":\n return { borderRadius: \"9999px\" }\n case \"rounded\":\n return { borderRadius: \"1rem\" }\n case \"square\":\n return { borderRadius: \"0\" }\n default:\n return { borderRadius: \"9999px\" }\n }\n }\n\n // Custom shape object\n const styles: React.CSSProperties = {}\n if (shape.clipPath) {\n styles.clipPath = shape.clipPath\n // When using clipPath, default to no border-radius unless specified\n styles.borderRadius = shape.borderRadius ?? \"0\"\n } else if (shape.borderRadius) {\n styles.borderRadius = shape.borderRadius\n }\n return styles\n}\n\n/**\n * Compute the shadow offset for hover state.\n * Returns half the original offset, with a minimum defined by MIN_HOVER_OFFSET.\n */\nexport function computeHoverOffset(shadowOffset: number): number {\n return Math.max(shadowOffset / 2, MIN_HOVER_OFFSET)\n}\n\n/**\n * Compute button inline styles from merged style overrides and shape styles.\n */\nexport function computeButtonStyles(\n size: number,\n mergedStyles: Required<StyleOverrides>,\n shapeStyles: React.CSSProperties,\n): React.CSSProperties {\n return {\n width: size,\n height: size,\n borderWidth: mergedStyles.borderWidth,\n borderColor: mergedStyles.borderColor,\n backgroundColor: mergedStyles.backgroundColor,\n boxShadow: `${mergedStyles.shadowOffset}px ${mergedStyles.shadowOffset}px 0px ${mergedStyles.shadowColor}`,\n ...shapeStyles,\n }\n}\n\n/**\n * Generate CSS custom properties for hover/active states.\n * These are set as inline styles on the button element.\n *\n * The static CSS rules in LikeButton.vanilla.css (or Tailwind's hover: classes)\n * reference these custom properties for the actual hover/active styling.\n *\n * This approach:\n * - Eliminates inline <style> tag injection (better for CSP)\n * - Reduces DOM pollution (no duplicate keyframe definitions)\n * - Works with both Tailwind and vanilla CSS versions\n */\nexport function computeHoverActiveVars(\n hoverShadowOffset: number,\n mergedStyles: Required<StyleOverrides>,\n): React.CSSProperties {\n const translateOnHover = mergedStyles.shadowOffset - hoverShadowOffset\n\n return {\n \"--shadow-offset\": `${mergedStyles.shadowOffset}px`,\n \"--shadow-color\": mergedStyles.shadowColor,\n \"--hover-shadow-offset\": `${hoverShadowOffset}px`,\n \"--translate-hover\": `${translateOnHover}px`,\n } as React.CSSProperties\n}\n\n// ============================================\n// Cursor Utilities\n// ============================================\n\n/** SVG cursor data URLs for preset cursors (32x32, hotspot at center) */\nconst CURSOR_SVGS: Record<Exclude<CursorPreset, \"pointer\" | \"none\">, string> = {\n heart: `data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24' fill='%23EF4444' stroke='%23111827' stroke-width='1.5'><path d='M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z'/></svg>`,\n star: `data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24' fill='%23FBBF24' stroke='%23111827' stroke-width='1.5'><path d='M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z'/></svg>`,\n \"thumbs-up\": `data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24' fill='%233B82F6' stroke='%23111827' stroke-width='1.5'><path d='M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3'/></svg>`,\n}\n\n/** Default hotspot position (center of 32x32 cursor) */\nconst DEFAULT_HOTSPOT = 16\n\n/**\n * Generate CSS cursor value from cursor prop.\n * Returns a complete CSS cursor property value.\n */\nexport function getCursorStyle(cursor: Cursor = \"heart\"): string {\n // Handle preset strings\n if (typeof cursor === \"string\") {\n switch (cursor) {\n case \"pointer\":\n return \"pointer\"\n case \"none\":\n return \"none\"\n case \"heart\":\n case \"star\":\n case \"thumbs-up\":\n return `url(\"${CURSOR_SVGS[cursor]}\") ${DEFAULT_HOTSPOT} ${DEFAULT_HOTSPOT}, pointer`\n default:\n // Fallback for unknown presets\n return \"pointer\"\n }\n }\n\n // Handle custom cursor object\n const {\n url,\n hotspotX = DEFAULT_HOTSPOT,\n hotspotY = DEFAULT_HOTSPOT,\n fallback = \"pointer\",\n } = cursor\n return `url(\"${url}\") ${hotspotX} ${hotspotY}, ${fallback}`\n}\n","import { forwardRef, useId, useMemo } from \"react\"\nimport { ParticleVanilla } from \"../Particle/Particle.vanilla\"\nimport { DefaultHeartIcon } from \"./DefaultHeartIcon\"\nimport type { IconRenderProps, LikeButtonVanillaProps } from \"./types\"\nimport { LIKE_BUTTON_DEFAULTS, useLikeButton } from \"./useLikeButton\"\nimport {\n computeButtonStyles,\n computeHoverActiveVars,\n computeHoverOffset,\n DEFAULT_STYLES,\n getCursorStyle,\n getShapeStyles,\n ICON_SIZE_RATIO,\n MAX_FILL_HEIGHT,\n} from \"./utils\"\n\nexport type { LikeButtonVanillaProps }\n\n/**\n * LikeButton - Animated like button with liquid fill and particle effects.\n * This version uses vanilla CSS (no Tailwind dependency).\n *\n * @example\n * ```tsx\n * import { LikeButtonVanilla } from '@fmarlats/react-like-button';\n * import '@fmarlats/react-like-button/styles.css';\n *\n * // Default usage\n * <LikeButtonVanilla onClick={(clicks, event) => console.log('Clicks:', clicks)} />\n *\n * // Custom icon\n * <LikeButtonVanilla renderIcon={({ size }) => <CustomIcon size={size} />} />\n *\n * // Custom shape\n * <LikeButtonVanilla shape=\"rounded\" />\n * <LikeButtonVanilla shape={{ clipPath: \"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)\" }} />\n *\n * // Custom styles\n * <LikeButtonVanilla styles={{ shadowOffset: 4, borderWidth: 2 }} />\n *\n * // Custom cursor\n * <LikeButtonVanilla cursor=\"star\" />\n * <LikeButtonVanilla cursor={{ url: \"data:image/svg+xml;...\", hotspotX: 16, hotspotY: 16 }} />\n *\n * // Minimum fill for custom shapes\n * <LikeButtonVanilla minFillPercent={15} shape={{ clipPath: \"polygon(...)\" }} />\n *\n * // Particle presets (same API as Tailwind version)\n * <LikeButtonVanilla particlePreset=\"burst\" />\n * <LikeButtonVanilla particlePreset=\"confetti\" />\n * <LikeButtonVanilla particlePreset=\"fireworks\" />\n *\n * // Custom particle configuration\n * <LikeButtonVanilla particleConfig={{\n * shape: 'star',\n * colors: ['#FFD700', '#FFA500'],\n * count: 15,\n * speed: 800\n * }} />\n *\n * // Combine preset with custom config\n * <LikeButtonVanilla\n * particlePreset=\"burst\"\n * particleConfig={{ count: 20 }}\n * />\n * ```\n */\nexport const LikeButtonVanilla = forwardRef<HTMLButtonElement, LikeButtonVanillaProps>(\n function LikeButtonVanilla(\n {\n size = LIKE_BUTTON_DEFAULTS.size,\n fillColor = LIKE_BUTTON_DEFAULTS.fillColor,\n waveColor = LIKE_BUTTON_DEFAULTS.waveColor,\n className = \"\",\n showParticles = true,\n showWaves = true,\n renderIcon,\n shape = \"circle\",\n styles = {},\n cursor = \"heart\",\n minFillPercent = 0,\n ...hookOptions\n },\n ref,\n ) {\n // Clamp minFillPercent to valid range (0-85)\n const clampedMinFill = Math.max(0, Math.min(MAX_FILL_HEIGHT, minFillPercent))\n // Hooks must be called first and in consistent order\n const reactId = useId()\n const buttonId = `like-button${reactId.replace(/:/g, \"-\")}`\n\n const {\n handleClick,\n handleRightClick,\n handleKeyDown,\n disabled,\n ariaLabel,\n isPressed,\n isMaxed,\n fillPercentage,\n particles,\n hasRightClickAction,\n } = useLikeButton({ showParticles, ...hookOptions })\n\n // Memoize style computations\n const mergedStyles = useMemo(() => ({ ...DEFAULT_STYLES, ...styles }), [styles])\n\n const shapeStyles = useMemo(() => getShapeStyles(shape), [shape])\n\n const buttonStyle = useMemo(\n () => computeButtonStyles(size, mergedStyles, shapeStyles),\n [size, mergedStyles, shapeStyles],\n )\n\n const hoverShadowOffset = useMemo(\n () => computeHoverOffset(mergedStyles.shadowOffset),\n [mergedStyles.shadowOffset],\n )\n\n // CSS custom properties for hover/active states (no <style> tag needed)\n const hoverActiveVars = useMemo(\n () => computeHoverActiveVars(hoverShadowOffset, mergedStyles),\n [hoverShadowOffset, mergedStyles],\n )\n\n // Cursor style (not-allowed when disabled)\n const cursorStyle = useMemo(\n () => (disabled ? \"not-allowed\" : getCursorStyle(cursor)),\n [cursor, disabled],\n )\n\n // Icon configuration\n const iconSize = size * ICON_SIZE_RATIO\n const iconRenderProps: IconRenderProps = {\n size: iconSize,\n className: \"like-button__icon\",\n isMaxed,\n fillPercentage,\n }\n\n const renderedIcon =\n renderIcon === null ? null : renderIcon === undefined ? (\n <DefaultHeartIcon {...iconRenderProps} />\n ) : (\n renderIcon(iconRenderProps)\n )\n\n return (\n <div className=\"like-button-container\">\n <button\n ref={ref}\n id={buttonId}\n type=\"button\"\n onClick={handleClick}\n onContextMenu={handleRightClick}\n onKeyDown={handleKeyDown}\n disabled={disabled}\n aria-label={ariaLabel}\n aria-pressed={isPressed}\n aria-disabled={disabled}\n aria-keyshortcuts={hasRightClickAction ? \"Shift+Enter\" : undefined}\n style={{ ...buttonStyle, ...hoverActiveVars, cursor: cursorStyle }}\n className={`like-button ${className}`.trim()}\n >\n {/* Liquid Fill Container */}\n <div\n className=\"like-button__fill\"\n style={{\n backgroundColor: fillColor,\n height: isMaxed\n ? \"100%\"\n : `${clampedMinFill + (fillPercentage / 100) * (MAX_FILL_HEIGHT - clampedMinFill)}%`,\n }}\n >\n {showWaves && (\n <>\n {/* Wave 1 (Back Layer) */}\n <div className=\"like-button__wave like-button__wave--back\">\n {[0, 1].map((i) => (\n <svg\n key={i}\n className=\"like-button__wave-svg\"\n style={{ color: waveColor }}\n viewBox=\"0 0 100 20\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n <path d=\"M0,10 Q25,0 50,10 T100,10 V20 H0 Z\" />\n </svg>\n ))}\n </div>\n\n {/* Wave 2 (Front Layer) */}\n <div className=\"like-button__wave like-button__wave--front\">\n {[0, 1].map((i) => (\n <svg\n key={i}\n className=\"like-button__wave-svg\"\n style={{ color: fillColor }}\n viewBox=\"0 0 100 20\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n <path d=\"M0,10 Q25,5 50,10 T100,10 V20 H0 Z\" />\n </svg>\n ))}\n </div>\n </>\n )}\n </div>\n\n {/* Icon (customizable via renderIcon prop) */}\n {renderedIcon}\n </button>\n\n {/* Particles */}\n {showParticles && (\n <div className=\"like-button__particles\" aria-hidden=\"true\">\n {particles.map((p) => (\n <ParticleVanilla key={p.id} {...p} />\n ))}\n </div>\n )}\n </div>\n )\n },\n)\n\nexport default LikeButtonVanilla\n","import type { ParticleShapeProps } from \"../types\"\n\n/**\n * Circle-shaped particle component.\n * Simple filled circle for clean, minimal effects.\n */\nexport function CircleShape({ size, color, className = \"\" }: ParticleShapeProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n className={`fill-current ${className}`}\n style={{ color }}\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n </svg>\n )\n}\n","import type { ParticleShapeProps } from \"../types\"\n\n/**\n * Heart-shaped particle component.\n * Classic heart shape for like/love interactions.\n */\nexport function HeartShape({ size, color, className = \"\" }: ParticleShapeProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n className={`fill-current ${className}`}\n style={{ color }}\n aria-hidden=\"true\"\n >\n <path d=\"M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z\" />\n </svg>\n )\n}\n","import type { ParticleShapeProps } from \"../types\"\n\n/**\n * Sparkle-shaped particle component.\n * 4-pointed sparkle/diamond for magical, glittery effects.\n */\nexport function SparkleShape({ size, color, className = \"\" }: ParticleShapeProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n className={`fill-current ${className}`}\n style={{ color }}\n aria-hidden=\"true\"\n >\n <path d=\"M12 2l2.5 7.5L22 12l-7.5 2.5L12 22l-2.5-7.5L2 12l7.5-2.5L12 2z\" />\n </svg>\n )\n}\n","import type { ParticleShapeProps } from \"../types\"\n\n/**\n * Square-shaped particle component.\n * Rounded square for geometric, modern effects.\n */\nexport function SquareShape({ size, color, className = \"\" }: ParticleShapeProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n className={`fill-current ${className}`}\n style={{ color }}\n aria-hidden=\"true\"\n >\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" />\n </svg>\n )\n}\n","import type { ParticleShapeProps } from \"../types\"\n\n/**\n * Star-shaped particle component.\n * Classic 5-pointed star for celebration effects.\n */\nexport function StarShape({ size, color, className = \"\" }: ParticleShapeProps) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n className={`fill-current ${className}`}\n style={{ color }}\n aria-hidden=\"true\"\n >\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n )\n}\n","import type { ParticleShape, ParticleShapeProps } from \"../types\"\nimport { CircleShape } from \"./CircleShape\"\nimport { HeartShape } from \"./HeartShape\"\nimport { SparkleShape } from \"./SparkleShape\"\nimport { SquareShape } from \"./SquareShape\"\nimport { StarShape } from \"./StarShape\"\n\n/**\n * Get the appropriate shape component based on shape configuration.\n *\n * @param shape - Either a preset shape name or custom shape config\n * @returns React component that renders the shape\n *\n * @example\n * ```tsx\n * const ShapeComponent = getParticleShape('star');\n * return <ShapeComponent size={40} color=\"#FFD700\" />;\n * ```\n *\n * @example\n * ```tsx\n * const ShapeComponent = getParticleShape({\n * render: ({ size, color }) => <div style={{ width: size, height: size, background: color }} />\n * });\n * return <ShapeComponent size={40} color=\"#FFD700\" />;\n * ```\n */\nexport function getParticleShape(shape: ParticleShape): React.ComponentType<ParticleShapeProps> {\n // Handle custom shape\n if (typeof shape === \"object\" && \"render\" in shape) {\n // Return a wrapper component that calls the custom render function\n return ({ size, color, className }: ParticleShapeProps) =>\n shape.render({ size, color, className }) as React.ReactElement\n }\n\n // Handle preset shapes\n switch (shape) {\n case \"heart\":\n return HeartShape\n case \"star\":\n return StarShape\n case \"circle\":\n return CircleShape\n case \"square\":\n return SquareShape\n case \"sparkle\":\n return SparkleShape\n default:\n // TypeScript should prevent this, but fallback to heart for safety\n return HeartShape\n }\n}\n","import { useEffect, useState } from \"react\"\nimport type { ParticleShape } from \"./types\"\n\n/** Props for particle components */\nexport interface ParticleProps {\n /** Angle in degrees (0-360) for particle direction */\n angle: number\n /** Distance in pixels the particle travels */\n distance: number\n /** Scale multiplier for particle size */\n scale: number\n /** Color of the particle (hex or CSS color) */\n color: string\n /** Particle shape (preset or custom) */\n shape: ParticleShape\n /** Animation duration in milliseconds */\n speed: number\n /** CSS easing function for animation */\n easing: string\n /** Whether particle should fade out */\n fadeOut: boolean\n}\n\n/** Return type for the useParticle hook */\nexport interface UseParticleReturn {\n /** Whether the animation has started */\n isAnimating: boolean\n /** X position offset in pixels */\n x: number\n /** Y position offset in pixels */\n y: number\n /** Computed transform string */\n transform: string\n /** Computed opacity value */\n opacity: number\n /** Animation duration in milliseconds */\n speed: number\n /** CSS easing function */\n easing: string\n /** Whether to fade out */\n fadeOut: boolean\n}\n\n/**\n * Headless hook for particle animation logic.\n * Handles animation state and position calculations.\n */\nexport function useParticle({\n angle,\n distance,\n scale,\n speed,\n easing,\n fadeOut,\n}: Pick<\n ParticleProps,\n \"angle\" | \"distance\" | \"scale\" | \"speed\" | \"easing\" | \"fadeOut\"\n>): UseParticleReturn {\n const [isAnimating, setIsAnimating] = useState(false)\n\n // Trigger animation after browser has painted the initial state\n // Using double RAF ensures the initial state is committed before transitioning\n useEffect(() => {\n let cancelled = false\n let raf2: number | undefined\n\n // First RAF: scheduled during current frame\n const raf1 = requestAnimationFrame(() => {\n // Second RAF: runs after browser has painted\n raf2 = requestAnimationFrame(() => {\n if (!cancelled) {\n setIsAnimating(true)\n }\n })\n })\n\n return () => {\n cancelled = true\n cancelAnimationFrame(raf1)\n if (raf2 !== undefined) cancelAnimationFrame(raf2)\n }\n }, [])\n\n // Calculate final position\n const x = Math.cos((angle * Math.PI) / 180) * distance\n const y = Math.sin((angle * Math.PI) / 180) * distance\n\n return {\n isAnimating,\n x,\n y,\n transform: isAnimating\n ? `translate(${x}px, ${y}px) scale(${scale})`\n : \"translate(0, 0) scale(1)\",\n opacity: isAnimating ? 0 : 1,\n speed,\n easing,\n fadeOut,\n }\n}\n","import { getParticleShape } from \"./shapes\"\nimport type { ParticleProps } from \"./useParticle\"\nimport { useParticle } from \"./useParticle\"\n\n/**\n * Particle - Animated particle for burst effects.\n * This version uses vanilla CSS (no Tailwind dependency).\n *\n * Supports multiple shapes (heart, star, circle, square, sparkle) and custom shapes.\n * Animation is fully configurable via speed, easing, and fadeOut props.\n */\nexport function ParticleVanilla({\n angle,\n distance,\n scale,\n color,\n shape,\n speed,\n easing,\n fadeOut,\n}: ParticleProps) {\n const { transform, opacity } = useParticle({\n angle,\n distance,\n scale,\n speed,\n easing,\n fadeOut,\n })\n\n // Get the shape component (preset or custom)\n const ShapeComponent = getParticleShape(shape)\n\n return (\n <div\n className=\"particle\"\n style={{\n color,\n transform,\n opacity: fadeOut ? opacity : 1,\n transitionDuration: `${speed}ms`,\n transitionTimingFunction: easing,\n }}\n >\n <ShapeComponent size={40} color={color} className=\"particle__icon\" />\n </div>\n )\n}\n\nexport default ParticleVanilla\n"],"mappings":"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","names":["jsx","jsx","jsx","jsx","jsx","useEffect","useState","jsx","jsx","LikeButtonVanilla"]}