@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,725 @@
1
+ // src/LikeButton/DefaultHeartIcon.tsx
2
+ import { jsx } from "react/jsx-runtime";
3
+ function DefaultHeartIcon({ size, className }) {
4
+ return /* @__PURE__ */ jsx(
5
+ "svg",
6
+ {
7
+ className,
8
+ style: {
9
+ width: size,
10
+ height: size,
11
+ stroke: "#111827",
12
+ strokeWidth: 2,
13
+ fill: "transparent"
14
+ },
15
+ viewBox: "0 0 24 24",
16
+ "aria-hidden": "true",
17
+ children: /* @__PURE__ */ jsx(
18
+ "path",
19
+ {
20
+ strokeLinecap: "round",
21
+ strokeLinejoin: "round",
22
+ 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"
23
+ }
24
+ )
25
+ }
26
+ );
27
+ }
28
+
29
+ // src/Particle/presets.ts
30
+ var DEFAULT_PARTICLE_CONFIG = {
31
+ shape: "heart",
32
+ speed: 500,
33
+ distance: { min: 60, max: 100 },
34
+ spread: 360,
35
+ spreadOffset: 0,
36
+ size: { min: 1, max: 1.5 },
37
+ colors: ["#EF4444", "#B9FF14", "#3B82F6"],
38
+ count: 8,
39
+ easing: "cubic-bezier(0.22, 1, 0.36, 1)",
40
+ fadeOut: true
41
+ };
42
+ var PARTICLE_PRESETS = {
43
+ /**
44
+ * Burst - Fast, enthusiastic explosion effect
45
+ * - Wide 360° spread
46
+ * - Fast animation (400ms)
47
+ * - Multiple colors
48
+ * - 12 particles
49
+ * - Perfect for: Likes, favorites, celebrations
50
+ */
51
+ burst: {
52
+ shape: "heart",
53
+ speed: 400,
54
+ distance: { min: 80, max: 120 },
55
+ spread: 360,
56
+ spreadOffset: 0,
57
+ size: { min: 1, max: 1.5 },
58
+ colors: ["#EF4444", "#F59E0B", "#3B82F6"],
59
+ count: 12,
60
+ easing: "cubic-bezier(0.22, 1, 0.36, 1)",
61
+ fadeOut: true
62
+ },
63
+ /**
64
+ * Fountain - Upward spray effect
65
+ * - 120° upward spread
66
+ * - Medium animation (600ms)
67
+ * - Cool colors (blue, purple, pink)
68
+ * - 10 particles
69
+ * - Perfect for: Achievements, upgrades, success
70
+ */
71
+ fountain: {
72
+ shape: "circle",
73
+ speed: 600,
74
+ distance: { min: 60, max: 100 },
75
+ spread: 120,
76
+ spreadOffset: -90,
77
+ // Upward
78
+ size: { min: 0.8, max: 1.2 },
79
+ colors: ["#3B82F6", "#8B5CF6", "#EC4899"],
80
+ count: 10,
81
+ easing: "cubic-bezier(0.22, 1, 0.36, 1)",
82
+ fadeOut: true
83
+ },
84
+ /**
85
+ * Confetti - Colorful celebration effect
86
+ * - Full 360° spread
87
+ * - Slow animation (800ms)
88
+ * - Rainbow colors
89
+ * - 15 particles
90
+ * - Perfect for: Milestones, victories, special events
91
+ */
92
+ confetti: {
93
+ shape: "square",
94
+ speed: 800,
95
+ distance: { min: 70, max: 110 },
96
+ spread: 360,
97
+ spreadOffset: 0,
98
+ size: { min: 0.6, max: 1.4 },
99
+ colors: ["#EF4444", "#F59E0B", "#10B981", "#3B82F6", "#8B5CF6", "#EC4899"],
100
+ count: 15,
101
+ easing: "ease-out",
102
+ fadeOut: true
103
+ },
104
+ /**
105
+ * Gentle - Subtle, calm effect
106
+ * - 180° upward spread
107
+ * - Slow animation (700ms)
108
+ * - Soft red tones
109
+ * - 6 particles
110
+ * - Perfect for: Subtle interactions, quiet appreciation
111
+ */
112
+ gentle: {
113
+ shape: "heart",
114
+ speed: 700,
115
+ distance: { min: 40, max: 60 },
116
+ spread: 180,
117
+ spreadOffset: -90,
118
+ size: { min: 0.8, max: 1 },
119
+ colors: ["#EF4444", "#F87171"],
120
+ count: 6,
121
+ easing: "ease-in-out",
122
+ fadeOut: true
123
+ },
124
+ /**
125
+ * Fireworks - Explosive sparkle effect
126
+ * - Full 360° spread
127
+ * - Medium-fast animation (500ms)
128
+ * - Warm sparkle colors
129
+ * - 16 particles
130
+ * - Large size range
131
+ * - Perfect for: Big celebrations, major achievements
132
+ */
133
+ fireworks: {
134
+ shape: "sparkle",
135
+ speed: 500,
136
+ distance: { min: 100, max: 150 },
137
+ spread: 360,
138
+ spreadOffset: 0,
139
+ size: { min: 1.2, max: 2 },
140
+ colors: ["#FBBF24", "#F59E0B", "#EF4444", "#EC4899"],
141
+ count: 16,
142
+ easing: "cubic-bezier(0.22, 1, 0.36, 1)",
143
+ fadeOut: true
144
+ }
145
+ };
146
+ function getParticlePreset(preset) {
147
+ return PARTICLE_PRESETS[preset];
148
+ }
149
+
150
+ // src/LikeButton/useLikeButton.ts
151
+ import { useCallback, useEffect, useRef, useState } from "react";
152
+
153
+ // src/Particle/utils.ts
154
+ function normalizeRange(value) {
155
+ return typeof value === "number" ? { min: value, max: value } : value;
156
+ }
157
+ function randomInRange(range) {
158
+ return range.min + Math.random() * (range.max - range.min);
159
+ }
160
+ function normalizeAngle(angle) {
161
+ const normalized = angle % 360;
162
+ const result = normalized < 0 ? normalized + 360 : normalized;
163
+ return result === 0 ? 0 : result;
164
+ }
165
+ function randomAngle(spread, offset) {
166
+ const angle = offset + Math.random() * spread;
167
+ return normalizeAngle(angle);
168
+ }
169
+ function mergeParticleConfig(base, override) {
170
+ if (!override) {
171
+ return base;
172
+ }
173
+ return {
174
+ ...base,
175
+ ...override
176
+ };
177
+ }
178
+ function resolveParticleConfig(preset, config) {
179
+ let resolved = { ...DEFAULT_PARTICLE_CONFIG };
180
+ if (preset) {
181
+ resolved = mergeParticleConfig(resolved, getParticlePreset(preset));
182
+ }
183
+ if (config) {
184
+ resolved = mergeParticleConfig(resolved, config);
185
+ }
186
+ return resolved;
187
+ }
188
+
189
+ // src/LikeButton/utils.ts
190
+ var MAX_FILL_HEIGHT = 85;
191
+ var ICON_SIZE_RATIO = 0.5;
192
+ var MIN_HOVER_OFFSET = 2;
193
+ var PARTICLE_CLEANUP_BUFFER_MS = 100;
194
+ var WAVE_BACK_DURATION_S = 3;
195
+ var WAVE_FRONT_DURATION_S = 1.5;
196
+ var DEFAULT_STYLES = {
197
+ borderWidth: 4,
198
+ borderColor: "#111827",
199
+ shadowOffset: 8,
200
+ shadowColor: "#111827",
201
+ backgroundColor: "white"
202
+ };
203
+ function getShapeStyles(shape = "circle") {
204
+ if (typeof shape === "string") {
205
+ switch (shape) {
206
+ case "circle":
207
+ return { borderRadius: "9999px" };
208
+ case "rounded":
209
+ return { borderRadius: "1rem" };
210
+ case "square":
211
+ return { borderRadius: "0" };
212
+ default:
213
+ return { borderRadius: "9999px" };
214
+ }
215
+ }
216
+ const styles = {};
217
+ if (shape.clipPath) {
218
+ styles.clipPath = shape.clipPath;
219
+ styles.borderRadius = shape.borderRadius ?? "0";
220
+ } else if (shape.borderRadius) {
221
+ styles.borderRadius = shape.borderRadius;
222
+ }
223
+ return styles;
224
+ }
225
+ function computeHoverOffset(shadowOffset) {
226
+ return Math.max(shadowOffset / 2, MIN_HOVER_OFFSET);
227
+ }
228
+ function computeButtonStyles(size, mergedStyles, shapeStyles) {
229
+ return {
230
+ width: size,
231
+ height: size,
232
+ borderWidth: mergedStyles.borderWidth,
233
+ borderColor: mergedStyles.borderColor,
234
+ backgroundColor: mergedStyles.backgroundColor,
235
+ boxShadow: `${mergedStyles.shadowOffset}px ${mergedStyles.shadowOffset}px 0px ${mergedStyles.shadowColor}`,
236
+ ...shapeStyles
237
+ };
238
+ }
239
+ function computeHoverActiveVars(hoverShadowOffset, mergedStyles) {
240
+ const translateOnHover = mergedStyles.shadowOffset - hoverShadowOffset;
241
+ return {
242
+ "--shadow-offset": `${mergedStyles.shadowOffset}px`,
243
+ "--shadow-color": mergedStyles.shadowColor,
244
+ "--hover-shadow-offset": `${hoverShadowOffset}px`,
245
+ "--translate-hover": `${translateOnHover}px`
246
+ };
247
+ }
248
+ var CURSOR_SVGS = {
249
+ 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>`,
250
+ 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>`,
251
+ "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>`
252
+ };
253
+ var DEFAULT_HOTSPOT = 16;
254
+ function getCursorStyle(cursor = "heart") {
255
+ if (typeof cursor === "string") {
256
+ switch (cursor) {
257
+ case "pointer":
258
+ return "pointer";
259
+ case "none":
260
+ return "none";
261
+ case "heart":
262
+ case "star":
263
+ case "thumbs-up":
264
+ return `url("${CURSOR_SVGS[cursor]}") ${DEFAULT_HOTSPOT} ${DEFAULT_HOTSPOT}, pointer`;
265
+ default:
266
+ return "pointer";
267
+ }
268
+ }
269
+ const {
270
+ url,
271
+ hotspotX = DEFAULT_HOTSPOT,
272
+ hotspotY = DEFAULT_HOTSPOT,
273
+ fallback = "pointer"
274
+ } = cursor;
275
+ return `url("${url}") ${hotspotX} ${hotspotY}, ${fallback}`;
276
+ }
277
+
278
+ // src/LikeButton/useLikeButton.ts
279
+ var LIKE_BUTTON_DEFAULTS = {
280
+ maxClicks: 1,
281
+ size: 96,
282
+ fillColor: "#EF4444",
283
+ waveColor: "#B91C1C"
284
+ };
285
+ function useLikeButton(options = {}) {
286
+ const {
287
+ clicks: externalClicks,
288
+ defaultClicks = 0,
289
+ maxClicks = LIKE_BUTTON_DEFAULTS.maxClicks,
290
+ onClick,
291
+ onChange,
292
+ onRightClick,
293
+ disabled: externalDisabled,
294
+ showParticles = true,
295
+ particlePreset,
296
+ particleConfig,
297
+ ariaLabel: customAriaLabel
298
+ } = options;
299
+ if (externalClicks !== void 0 && options.defaultClicks !== void 0) {
300
+ console.warn(
301
+ "LikeButton: `defaultClicks` is ignored when `clicks` is provided (controlled mode)."
302
+ );
303
+ }
304
+ const [internalClicks, setInternalClicks] = useState(defaultClicks);
305
+ const [particles, setParticles] = useState([]);
306
+ const timeoutRefs = useRef(/* @__PURE__ */ new Set());
307
+ useEffect(() => {
308
+ return () => {
309
+ for (const timeoutId of timeoutRefs.current) {
310
+ clearTimeout(timeoutId);
311
+ }
312
+ };
313
+ }, []);
314
+ const clicks = externalClicks ?? internalClicks;
315
+ const isMaxed = clicks >= maxClicks;
316
+ const disabled = externalDisabled ?? isMaxed;
317
+ const spawnParticles = useCallback(() => {
318
+ if (!showParticles) return;
319
+ const config = resolveParticleConfig(particlePreset, particleConfig);
320
+ const distanceRange = normalizeRange(config.distance);
321
+ const sizeRange = normalizeRange(config.size);
322
+ const id = Date.now();
323
+ const newParticles = Array.from({ length: config.count }).map((_, i) => ({
324
+ id: `${id}-${i}`,
325
+ angle: randomAngle(config.spread, config.spreadOffset),
326
+ distance: randomInRange(distanceRange),
327
+ scale: randomInRange(sizeRange),
328
+ color: config.colors[Math.floor(Math.random() * config.colors.length)],
329
+ shape: config.shape,
330
+ speed: config.speed,
331
+ easing: config.easing,
332
+ fadeOut: config.fadeOut
333
+ }));
334
+ setParticles((prev) => [...prev, ...newParticles]);
335
+ const cleanupDelay = config.speed + PARTICLE_CLEANUP_BUFFER_MS;
336
+ const idsToRemove = new Set(newParticles.map((p) => p.id));
337
+ const timeoutId = setTimeout(() => {
338
+ setParticles((prev) => prev.filter((p) => !idsToRemove.has(p.id)));
339
+ timeoutRefs.current.delete(timeoutId);
340
+ }, cleanupDelay);
341
+ timeoutRefs.current.add(timeoutId);
342
+ }, [showParticles, particlePreset, particleConfig]);
343
+ const handleClick = useCallback(
344
+ (e) => {
345
+ if (disabled) return;
346
+ const newClicks = clicks + 1;
347
+ if (externalClicks === void 0) {
348
+ setInternalClicks(newClicks);
349
+ }
350
+ spawnParticles();
351
+ onChange?.(newClicks);
352
+ onClick?.(newClicks, e);
353
+ },
354
+ [disabled, clicks, externalClicks, spawnParticles, onChange, onClick]
355
+ );
356
+ const handleRightClick = useCallback(
357
+ (e) => {
358
+ e.preventDefault();
359
+ if (disabled) return;
360
+ onRightClick?.(clicks, e);
361
+ },
362
+ [disabled, clicks, onRightClick]
363
+ );
364
+ const handleKeyDown = useCallback(
365
+ (e) => {
366
+ if (e.shiftKey && e.key === "Enter") {
367
+ e.preventDefault();
368
+ if (disabled) return;
369
+ onRightClick?.(clicks, e);
370
+ }
371
+ },
372
+ [disabled, clicks, onRightClick]
373
+ );
374
+ const fillPercentage = clicks / maxClicks * 100;
375
+ const defaultAriaLabel = isMaxed ? "Thank you for your likes!" : `Like this content. ${maxClicks - clicks} clicks remaining`;
376
+ const ariaLabelState = {
377
+ isMaxed,
378
+ remaining: maxClicks - clicks,
379
+ clicks,
380
+ maxClicks
381
+ };
382
+ const computedAriaLabel = customAriaLabel === void 0 ? defaultAriaLabel : typeof customAriaLabel === "function" ? customAriaLabel(ariaLabelState) : customAriaLabel;
383
+ return {
384
+ clicks,
385
+ isMaxed,
386
+ disabled,
387
+ fillPercentage,
388
+ particles,
389
+ handleClick,
390
+ handleRightClick,
391
+ handleKeyDown,
392
+ ariaLabel: computedAriaLabel,
393
+ isPressed: clicks > 0,
394
+ hasRightClickAction: onRightClick !== void 0
395
+ };
396
+ }
397
+
398
+ // src/LikeButton/LikeButton.vanilla.tsx
399
+ import { forwardRef, useId, useMemo } from "react";
400
+
401
+ // src/Particle/shapes/CircleShape.tsx
402
+ import { jsx as jsx2 } from "react/jsx-runtime";
403
+ function CircleShape({ size, color, className = "" }) {
404
+ return /* @__PURE__ */ jsx2(
405
+ "svg",
406
+ {
407
+ width: size,
408
+ height: size,
409
+ viewBox: "0 0 24 24",
410
+ className: `fill-current ${className}`,
411
+ style: { color },
412
+ "aria-hidden": "true",
413
+ children: /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" })
414
+ }
415
+ );
416
+ }
417
+
418
+ // src/Particle/shapes/HeartShape.tsx
419
+ import { jsx as jsx3 } from "react/jsx-runtime";
420
+ function HeartShape({ size, color, className = "" }) {
421
+ return /* @__PURE__ */ jsx3(
422
+ "svg",
423
+ {
424
+ width: size,
425
+ height: size,
426
+ viewBox: "0 0 24 24",
427
+ className: `fill-current ${className}`,
428
+ style: { color },
429
+ "aria-hidden": "true",
430
+ children: /* @__PURE__ */ jsx3("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" })
431
+ }
432
+ );
433
+ }
434
+
435
+ // src/Particle/shapes/SparkleShape.tsx
436
+ import { jsx as jsx4 } from "react/jsx-runtime";
437
+ function SparkleShape({ size, color, className = "" }) {
438
+ return /* @__PURE__ */ jsx4(
439
+ "svg",
440
+ {
441
+ width: size,
442
+ height: size,
443
+ viewBox: "0 0 24 24",
444
+ className: `fill-current ${className}`,
445
+ style: { color },
446
+ "aria-hidden": "true",
447
+ children: /* @__PURE__ */ jsx4("path", { d: "M12 2l2.5 7.5L22 12l-7.5 2.5L12 22l-2.5-7.5L2 12l7.5-2.5L12 2z" })
448
+ }
449
+ );
450
+ }
451
+
452
+ // src/Particle/shapes/SquareShape.tsx
453
+ import { jsx as jsx5 } from "react/jsx-runtime";
454
+ function SquareShape({ size, color, className = "" }) {
455
+ return /* @__PURE__ */ jsx5(
456
+ "svg",
457
+ {
458
+ width: size,
459
+ height: size,
460
+ viewBox: "0 0 24 24",
461
+ className: `fill-current ${className}`,
462
+ style: { color },
463
+ "aria-hidden": "true",
464
+ children: /* @__PURE__ */ jsx5("rect", { x: "3", y: "3", width: "18", height: "18", rx: "3" })
465
+ }
466
+ );
467
+ }
468
+
469
+ // src/Particle/shapes/StarShape.tsx
470
+ import { jsx as jsx6 } from "react/jsx-runtime";
471
+ function StarShape({ size, color, className = "" }) {
472
+ return /* @__PURE__ */ jsx6(
473
+ "svg",
474
+ {
475
+ width: size,
476
+ height: size,
477
+ viewBox: "0 0 24 24",
478
+ className: `fill-current ${className}`,
479
+ style: { color },
480
+ "aria-hidden": "true",
481
+ children: /* @__PURE__ */ jsx6("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" })
482
+ }
483
+ );
484
+ }
485
+
486
+ // src/Particle/shapes/utils.ts
487
+ function getParticleShape(shape) {
488
+ if (typeof shape === "object" && "render" in shape) {
489
+ return ({ size, color, className }) => shape.render({ size, color, className });
490
+ }
491
+ switch (shape) {
492
+ case "heart":
493
+ return HeartShape;
494
+ case "star":
495
+ return StarShape;
496
+ case "circle":
497
+ return CircleShape;
498
+ case "square":
499
+ return SquareShape;
500
+ case "sparkle":
501
+ return SparkleShape;
502
+ default:
503
+ return HeartShape;
504
+ }
505
+ }
506
+
507
+ // src/Particle/useParticle.ts
508
+ import { useEffect as useEffect2, useState as useState2 } from "react";
509
+ function useParticle({
510
+ angle,
511
+ distance,
512
+ scale,
513
+ speed,
514
+ easing,
515
+ fadeOut
516
+ }) {
517
+ const [isAnimating, setIsAnimating] = useState2(false);
518
+ useEffect2(() => {
519
+ let cancelled = false;
520
+ let raf2;
521
+ const raf1 = requestAnimationFrame(() => {
522
+ raf2 = requestAnimationFrame(() => {
523
+ if (!cancelled) {
524
+ setIsAnimating(true);
525
+ }
526
+ });
527
+ });
528
+ return () => {
529
+ cancelled = true;
530
+ cancelAnimationFrame(raf1);
531
+ if (raf2 !== void 0) cancelAnimationFrame(raf2);
532
+ };
533
+ }, []);
534
+ const x = Math.cos(angle * Math.PI / 180) * distance;
535
+ const y = Math.sin(angle * Math.PI / 180) * distance;
536
+ return {
537
+ isAnimating,
538
+ x,
539
+ y,
540
+ transform: isAnimating ? `translate(${x}px, ${y}px) scale(${scale})` : "translate(0, 0) scale(1)",
541
+ opacity: isAnimating ? 0 : 1,
542
+ speed,
543
+ easing,
544
+ fadeOut
545
+ };
546
+ }
547
+
548
+ // src/Particle/Particle.vanilla.tsx
549
+ import { jsx as jsx7 } from "react/jsx-runtime";
550
+ function ParticleVanilla({
551
+ angle,
552
+ distance,
553
+ scale,
554
+ color,
555
+ shape,
556
+ speed,
557
+ easing,
558
+ fadeOut
559
+ }) {
560
+ const { transform, opacity } = useParticle({
561
+ angle,
562
+ distance,
563
+ scale,
564
+ speed,
565
+ easing,
566
+ fadeOut
567
+ });
568
+ const ShapeComponent = getParticleShape(shape);
569
+ return /* @__PURE__ */ jsx7(
570
+ "div",
571
+ {
572
+ className: "particle",
573
+ style: {
574
+ color,
575
+ transform,
576
+ opacity: fadeOut ? opacity : 1,
577
+ transitionDuration: `${speed}ms`,
578
+ transitionTimingFunction: easing
579
+ },
580
+ children: /* @__PURE__ */ jsx7(ShapeComponent, { size: 40, color, className: "particle__icon" })
581
+ }
582
+ );
583
+ }
584
+
585
+ // src/LikeButton/LikeButton.vanilla.tsx
586
+ import { Fragment, jsx as jsx8, jsxs } from "react/jsx-runtime";
587
+ var LikeButtonVanilla = forwardRef(
588
+ function LikeButtonVanilla2({
589
+ size = LIKE_BUTTON_DEFAULTS.size,
590
+ fillColor = LIKE_BUTTON_DEFAULTS.fillColor,
591
+ waveColor = LIKE_BUTTON_DEFAULTS.waveColor,
592
+ className = "",
593
+ showParticles = true,
594
+ showWaves = true,
595
+ renderIcon,
596
+ shape = "circle",
597
+ styles = {},
598
+ cursor = "heart",
599
+ minFillPercent = 0,
600
+ ...hookOptions
601
+ }, ref) {
602
+ const clampedMinFill = Math.max(0, Math.min(MAX_FILL_HEIGHT, minFillPercent));
603
+ const reactId = useId();
604
+ const buttonId = `like-button${reactId.replace(/:/g, "-")}`;
605
+ const {
606
+ handleClick,
607
+ handleRightClick,
608
+ handleKeyDown,
609
+ disabled,
610
+ ariaLabel,
611
+ isPressed,
612
+ isMaxed,
613
+ fillPercentage,
614
+ particles,
615
+ hasRightClickAction
616
+ } = useLikeButton({ showParticles, ...hookOptions });
617
+ const mergedStyles = useMemo(() => ({ ...DEFAULT_STYLES, ...styles }), [styles]);
618
+ const shapeStyles = useMemo(() => getShapeStyles(shape), [shape]);
619
+ const buttonStyle = useMemo(
620
+ () => computeButtonStyles(size, mergedStyles, shapeStyles),
621
+ [size, mergedStyles, shapeStyles]
622
+ );
623
+ const hoverShadowOffset = useMemo(
624
+ () => computeHoverOffset(mergedStyles.shadowOffset),
625
+ [mergedStyles.shadowOffset]
626
+ );
627
+ const hoverActiveVars = useMemo(
628
+ () => computeHoverActiveVars(hoverShadowOffset, mergedStyles),
629
+ [hoverShadowOffset, mergedStyles]
630
+ );
631
+ const cursorStyle = useMemo(
632
+ () => disabled ? "not-allowed" : getCursorStyle(cursor),
633
+ [cursor, disabled]
634
+ );
635
+ const iconSize = size * ICON_SIZE_RATIO;
636
+ const iconRenderProps = {
637
+ size: iconSize,
638
+ className: "like-button__icon",
639
+ isMaxed,
640
+ fillPercentage
641
+ };
642
+ const renderedIcon = renderIcon === null ? null : renderIcon === void 0 ? /* @__PURE__ */ jsx8(DefaultHeartIcon, { ...iconRenderProps }) : renderIcon(iconRenderProps);
643
+ return /* @__PURE__ */ jsxs("div", { className: "like-button-container", children: [
644
+ /* @__PURE__ */ jsxs(
645
+ "button",
646
+ {
647
+ ref,
648
+ id: buttonId,
649
+ type: "button",
650
+ onClick: handleClick,
651
+ onContextMenu: handleRightClick,
652
+ onKeyDown: handleKeyDown,
653
+ disabled,
654
+ "aria-label": ariaLabel,
655
+ "aria-pressed": isPressed,
656
+ "aria-disabled": disabled,
657
+ "aria-keyshortcuts": hasRightClickAction ? "Shift+Enter" : void 0,
658
+ style: { ...buttonStyle, ...hoverActiveVars, cursor: cursorStyle },
659
+ className: `like-button ${className}`.trim(),
660
+ children: [
661
+ /* @__PURE__ */ jsx8(
662
+ "div",
663
+ {
664
+ className: "like-button__fill",
665
+ style: {
666
+ backgroundColor: fillColor,
667
+ height: isMaxed ? "100%" : `${clampedMinFill + fillPercentage / 100 * (MAX_FILL_HEIGHT - clampedMinFill)}%`
668
+ },
669
+ children: showWaves && /* @__PURE__ */ jsxs(Fragment, { children: [
670
+ /* @__PURE__ */ jsx8("div", { className: "like-button__wave like-button__wave--back", children: [0, 1].map((i) => /* @__PURE__ */ jsx8(
671
+ "svg",
672
+ {
673
+ className: "like-button__wave-svg",
674
+ style: { color: waveColor },
675
+ viewBox: "0 0 100 20",
676
+ preserveAspectRatio: "none",
677
+ "aria-hidden": "true",
678
+ children: /* @__PURE__ */ jsx8("path", { d: "M0,10 Q25,0 50,10 T100,10 V20 H0 Z" })
679
+ },
680
+ i
681
+ )) }),
682
+ /* @__PURE__ */ jsx8("div", { className: "like-button__wave like-button__wave--front", children: [0, 1].map((i) => /* @__PURE__ */ jsx8(
683
+ "svg",
684
+ {
685
+ className: "like-button__wave-svg",
686
+ style: { color: fillColor },
687
+ viewBox: "0 0 100 20",
688
+ preserveAspectRatio: "none",
689
+ "aria-hidden": "true",
690
+ children: /* @__PURE__ */ jsx8("path", { d: "M0,10 Q25,5 50,10 T100,10 V20 H0 Z" })
691
+ },
692
+ i
693
+ )) })
694
+ ] })
695
+ }
696
+ ),
697
+ renderedIcon
698
+ ]
699
+ }
700
+ ),
701
+ showParticles && /* @__PURE__ */ jsx8("div", { className: "like-button__particles", "aria-hidden": "true", children: particles.map((p) => /* @__PURE__ */ jsx8(ParticleVanilla, { ...p }, p.id)) })
702
+ ] });
703
+ }
704
+ );
705
+
706
+ export {
707
+ DefaultHeartIcon,
708
+ getParticleShape,
709
+ useParticle,
710
+ PARTICLE_PRESETS,
711
+ MAX_FILL_HEIGHT,
712
+ ICON_SIZE_RATIO,
713
+ WAVE_BACK_DURATION_S,
714
+ WAVE_FRONT_DURATION_S,
715
+ DEFAULT_STYLES,
716
+ getShapeStyles,
717
+ computeHoverOffset,
718
+ computeButtonStyles,
719
+ computeHoverActiveVars,
720
+ getCursorStyle,
721
+ LIKE_BUTTON_DEFAULTS,
722
+ useLikeButton,
723
+ LikeButtonVanilla
724
+ };
725
+ //# sourceMappingURL=chunk-VLFZGMEX.js.map