@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.
- package/LICENSE +22 -0
- package/README.md +482 -0
- package/dist/chunk-VLFZGMEX.js +725 -0
- package/dist/chunk-VLFZGMEX.js.map +1 -0
- package/dist/index.cjs +926 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +68 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +209 -0
- package/dist/index.js.map +1 -0
- package/dist/like-button.css +68 -0
- package/dist/styles.css +181 -0
- package/dist/vanilla.cjs +742 -0
- package/dist/vanilla.cjs.map +1 -0
- package/dist/vanilla.d.cts +562 -0
- package/dist/vanilla.d.ts +562 -0
- package/dist/vanilla.js +16 -0
- package/dist/vanilla.js.map +1 -0
- package/package.json +109 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,926 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
DefaultHeartIcon: () => DefaultHeartIcon,
|
|
24
|
+
LIKE_BUTTON_DEFAULTS: () => LIKE_BUTTON_DEFAULTS,
|
|
25
|
+
LikeButton: () => LikeButton,
|
|
26
|
+
LikeButtonVanilla: () => LikeButtonVanilla,
|
|
27
|
+
PARTICLE_PRESETS: () => PARTICLE_PRESETS,
|
|
28
|
+
default: () => LikeButton,
|
|
29
|
+
useLikeButton: () => useLikeButton
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(src_exports);
|
|
32
|
+
|
|
33
|
+
// src/LikeButton/DefaultHeartIcon.tsx
|
|
34
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
35
|
+
function DefaultHeartIcon({ size, className }) {
|
|
36
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
37
|
+
"svg",
|
|
38
|
+
{
|
|
39
|
+
className,
|
|
40
|
+
style: {
|
|
41
|
+
width: size,
|
|
42
|
+
height: size,
|
|
43
|
+
stroke: "#111827",
|
|
44
|
+
strokeWidth: 2,
|
|
45
|
+
fill: "transparent"
|
|
46
|
+
},
|
|
47
|
+
viewBox: "0 0 24 24",
|
|
48
|
+
"aria-hidden": "true",
|
|
49
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
50
|
+
"path",
|
|
51
|
+
{
|
|
52
|
+
strokeLinecap: "round",
|
|
53
|
+
strokeLinejoin: "round",
|
|
54
|
+
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"
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/LikeButton/LikeButton.tsx
|
|
62
|
+
var import_react3 = require("react");
|
|
63
|
+
|
|
64
|
+
// src/Particle/shapes/CircleShape.tsx
|
|
65
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
66
|
+
function CircleShape({ size, color, className = "" }) {
|
|
67
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
68
|
+
"svg",
|
|
69
|
+
{
|
|
70
|
+
width: size,
|
|
71
|
+
height: size,
|
|
72
|
+
viewBox: "0 0 24 24",
|
|
73
|
+
className: `fill-current ${className}`,
|
|
74
|
+
style: { color },
|
|
75
|
+
"aria-hidden": "true",
|
|
76
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10" })
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/Particle/shapes/HeartShape.tsx
|
|
82
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
83
|
+
function HeartShape({ size, color, className = "" }) {
|
|
84
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
85
|
+
"svg",
|
|
86
|
+
{
|
|
87
|
+
width: size,
|
|
88
|
+
height: size,
|
|
89
|
+
viewBox: "0 0 24 24",
|
|
90
|
+
className: `fill-current ${className}`,
|
|
91
|
+
style: { color },
|
|
92
|
+
"aria-hidden": "true",
|
|
93
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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" })
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/Particle/shapes/SparkleShape.tsx
|
|
99
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
100
|
+
function SparkleShape({ size, color, className = "" }) {
|
|
101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
102
|
+
"svg",
|
|
103
|
+
{
|
|
104
|
+
width: size,
|
|
105
|
+
height: size,
|
|
106
|
+
viewBox: "0 0 24 24",
|
|
107
|
+
className: `fill-current ${className}`,
|
|
108
|
+
style: { color },
|
|
109
|
+
"aria-hidden": "true",
|
|
110
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 2l2.5 7.5L22 12l-7.5 2.5L12 22l-2.5-7.5L2 12l7.5-2.5L12 2z" })
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/Particle/shapes/SquareShape.tsx
|
|
116
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
117
|
+
function SquareShape({ size, color, className = "" }) {
|
|
118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
119
|
+
"svg",
|
|
120
|
+
{
|
|
121
|
+
width: size,
|
|
122
|
+
height: size,
|
|
123
|
+
viewBox: "0 0 24 24",
|
|
124
|
+
className: `fill-current ${className}`,
|
|
125
|
+
style: { color },
|
|
126
|
+
"aria-hidden": "true",
|
|
127
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "3" })
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/Particle/shapes/StarShape.tsx
|
|
133
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
134
|
+
function StarShape({ size, color, className = "" }) {
|
|
135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
136
|
+
"svg",
|
|
137
|
+
{
|
|
138
|
+
width: size,
|
|
139
|
+
height: size,
|
|
140
|
+
viewBox: "0 0 24 24",
|
|
141
|
+
className: `fill-current ${className}`,
|
|
142
|
+
style: { color },
|
|
143
|
+
"aria-hidden": "true",
|
|
144
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" })
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/Particle/shapes/utils.ts
|
|
150
|
+
function getParticleShape(shape) {
|
|
151
|
+
if (typeof shape === "object" && "render" in shape) {
|
|
152
|
+
return ({ size, color, className }) => shape.render({ size, color, className });
|
|
153
|
+
}
|
|
154
|
+
switch (shape) {
|
|
155
|
+
case "heart":
|
|
156
|
+
return HeartShape;
|
|
157
|
+
case "star":
|
|
158
|
+
return StarShape;
|
|
159
|
+
case "circle":
|
|
160
|
+
return CircleShape;
|
|
161
|
+
case "square":
|
|
162
|
+
return SquareShape;
|
|
163
|
+
case "sparkle":
|
|
164
|
+
return SparkleShape;
|
|
165
|
+
default:
|
|
166
|
+
return HeartShape;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/Particle/useParticle.ts
|
|
171
|
+
var import_react = require("react");
|
|
172
|
+
function useParticle({
|
|
173
|
+
angle,
|
|
174
|
+
distance,
|
|
175
|
+
scale,
|
|
176
|
+
speed,
|
|
177
|
+
easing,
|
|
178
|
+
fadeOut
|
|
179
|
+
}) {
|
|
180
|
+
const [isAnimating, setIsAnimating] = (0, import_react.useState)(false);
|
|
181
|
+
(0, import_react.useEffect)(() => {
|
|
182
|
+
let cancelled = false;
|
|
183
|
+
let raf2;
|
|
184
|
+
const raf1 = requestAnimationFrame(() => {
|
|
185
|
+
raf2 = requestAnimationFrame(() => {
|
|
186
|
+
if (!cancelled) {
|
|
187
|
+
setIsAnimating(true);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
return () => {
|
|
192
|
+
cancelled = true;
|
|
193
|
+
cancelAnimationFrame(raf1);
|
|
194
|
+
if (raf2 !== void 0) cancelAnimationFrame(raf2);
|
|
195
|
+
};
|
|
196
|
+
}, []);
|
|
197
|
+
const x = Math.cos(angle * Math.PI / 180) * distance;
|
|
198
|
+
const y = Math.sin(angle * Math.PI / 180) * distance;
|
|
199
|
+
return {
|
|
200
|
+
isAnimating,
|
|
201
|
+
x,
|
|
202
|
+
y,
|
|
203
|
+
transform: isAnimating ? `translate(${x}px, ${y}px) scale(${scale})` : "translate(0, 0) scale(1)",
|
|
204
|
+
opacity: isAnimating ? 0 : 1,
|
|
205
|
+
speed,
|
|
206
|
+
easing,
|
|
207
|
+
fadeOut
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/Particle/Particle.tsx
|
|
212
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
213
|
+
function Particle({
|
|
214
|
+
angle,
|
|
215
|
+
distance,
|
|
216
|
+
scale,
|
|
217
|
+
color,
|
|
218
|
+
shape,
|
|
219
|
+
speed,
|
|
220
|
+
easing,
|
|
221
|
+
fadeOut
|
|
222
|
+
}) {
|
|
223
|
+
const { transform, opacity } = useParticle({
|
|
224
|
+
angle,
|
|
225
|
+
distance,
|
|
226
|
+
scale,
|
|
227
|
+
speed,
|
|
228
|
+
easing,
|
|
229
|
+
fadeOut
|
|
230
|
+
});
|
|
231
|
+
const ShapeComponent = getParticleShape(shape);
|
|
232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
233
|
+
"div",
|
|
234
|
+
{
|
|
235
|
+
className: "absolute w-10 h-10 transition-all",
|
|
236
|
+
style: {
|
|
237
|
+
color,
|
|
238
|
+
transform,
|
|
239
|
+
opacity: fadeOut ? opacity : 1,
|
|
240
|
+
transitionDuration: `${speed}ms`,
|
|
241
|
+
transitionTimingFunction: easing
|
|
242
|
+
},
|
|
243
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ShapeComponent, { size: 40, color, className: "w-full h-full" })
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/LikeButton/useLikeButton.ts
|
|
249
|
+
var import_react2 = require("react");
|
|
250
|
+
|
|
251
|
+
// src/Particle/presets.ts
|
|
252
|
+
var DEFAULT_PARTICLE_CONFIG = {
|
|
253
|
+
shape: "heart",
|
|
254
|
+
speed: 500,
|
|
255
|
+
distance: { min: 60, max: 100 },
|
|
256
|
+
spread: 360,
|
|
257
|
+
spreadOffset: 0,
|
|
258
|
+
size: { min: 1, max: 1.5 },
|
|
259
|
+
colors: ["#EF4444", "#B9FF14", "#3B82F6"],
|
|
260
|
+
count: 8,
|
|
261
|
+
easing: "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
262
|
+
fadeOut: true
|
|
263
|
+
};
|
|
264
|
+
var PARTICLE_PRESETS = {
|
|
265
|
+
/**
|
|
266
|
+
* Burst - Fast, enthusiastic explosion effect
|
|
267
|
+
* - Wide 360° spread
|
|
268
|
+
* - Fast animation (400ms)
|
|
269
|
+
* - Multiple colors
|
|
270
|
+
* - 12 particles
|
|
271
|
+
* - Perfect for: Likes, favorites, celebrations
|
|
272
|
+
*/
|
|
273
|
+
burst: {
|
|
274
|
+
shape: "heart",
|
|
275
|
+
speed: 400,
|
|
276
|
+
distance: { min: 80, max: 120 },
|
|
277
|
+
spread: 360,
|
|
278
|
+
spreadOffset: 0,
|
|
279
|
+
size: { min: 1, max: 1.5 },
|
|
280
|
+
colors: ["#EF4444", "#F59E0B", "#3B82F6"],
|
|
281
|
+
count: 12,
|
|
282
|
+
easing: "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
283
|
+
fadeOut: true
|
|
284
|
+
},
|
|
285
|
+
/**
|
|
286
|
+
* Fountain - Upward spray effect
|
|
287
|
+
* - 120° upward spread
|
|
288
|
+
* - Medium animation (600ms)
|
|
289
|
+
* - Cool colors (blue, purple, pink)
|
|
290
|
+
* - 10 particles
|
|
291
|
+
* - Perfect for: Achievements, upgrades, success
|
|
292
|
+
*/
|
|
293
|
+
fountain: {
|
|
294
|
+
shape: "circle",
|
|
295
|
+
speed: 600,
|
|
296
|
+
distance: { min: 60, max: 100 },
|
|
297
|
+
spread: 120,
|
|
298
|
+
spreadOffset: -90,
|
|
299
|
+
// Upward
|
|
300
|
+
size: { min: 0.8, max: 1.2 },
|
|
301
|
+
colors: ["#3B82F6", "#8B5CF6", "#EC4899"],
|
|
302
|
+
count: 10,
|
|
303
|
+
easing: "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
304
|
+
fadeOut: true
|
|
305
|
+
},
|
|
306
|
+
/**
|
|
307
|
+
* Confetti - Colorful celebration effect
|
|
308
|
+
* - Full 360° spread
|
|
309
|
+
* - Slow animation (800ms)
|
|
310
|
+
* - Rainbow colors
|
|
311
|
+
* - 15 particles
|
|
312
|
+
* - Perfect for: Milestones, victories, special events
|
|
313
|
+
*/
|
|
314
|
+
confetti: {
|
|
315
|
+
shape: "square",
|
|
316
|
+
speed: 800,
|
|
317
|
+
distance: { min: 70, max: 110 },
|
|
318
|
+
spread: 360,
|
|
319
|
+
spreadOffset: 0,
|
|
320
|
+
size: { min: 0.6, max: 1.4 },
|
|
321
|
+
colors: ["#EF4444", "#F59E0B", "#10B981", "#3B82F6", "#8B5CF6", "#EC4899"],
|
|
322
|
+
count: 15,
|
|
323
|
+
easing: "ease-out",
|
|
324
|
+
fadeOut: true
|
|
325
|
+
},
|
|
326
|
+
/**
|
|
327
|
+
* Gentle - Subtle, calm effect
|
|
328
|
+
* - 180° upward spread
|
|
329
|
+
* - Slow animation (700ms)
|
|
330
|
+
* - Soft red tones
|
|
331
|
+
* - 6 particles
|
|
332
|
+
* - Perfect for: Subtle interactions, quiet appreciation
|
|
333
|
+
*/
|
|
334
|
+
gentle: {
|
|
335
|
+
shape: "heart",
|
|
336
|
+
speed: 700,
|
|
337
|
+
distance: { min: 40, max: 60 },
|
|
338
|
+
spread: 180,
|
|
339
|
+
spreadOffset: -90,
|
|
340
|
+
size: { min: 0.8, max: 1 },
|
|
341
|
+
colors: ["#EF4444", "#F87171"],
|
|
342
|
+
count: 6,
|
|
343
|
+
easing: "ease-in-out",
|
|
344
|
+
fadeOut: true
|
|
345
|
+
},
|
|
346
|
+
/**
|
|
347
|
+
* Fireworks - Explosive sparkle effect
|
|
348
|
+
* - Full 360° spread
|
|
349
|
+
* - Medium-fast animation (500ms)
|
|
350
|
+
* - Warm sparkle colors
|
|
351
|
+
* - 16 particles
|
|
352
|
+
* - Large size range
|
|
353
|
+
* - Perfect for: Big celebrations, major achievements
|
|
354
|
+
*/
|
|
355
|
+
fireworks: {
|
|
356
|
+
shape: "sparkle",
|
|
357
|
+
speed: 500,
|
|
358
|
+
distance: { min: 100, max: 150 },
|
|
359
|
+
spread: 360,
|
|
360
|
+
spreadOffset: 0,
|
|
361
|
+
size: { min: 1.2, max: 2 },
|
|
362
|
+
colors: ["#FBBF24", "#F59E0B", "#EF4444", "#EC4899"],
|
|
363
|
+
count: 16,
|
|
364
|
+
easing: "cubic-bezier(0.22, 1, 0.36, 1)",
|
|
365
|
+
fadeOut: true
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
function getParticlePreset(preset) {
|
|
369
|
+
return PARTICLE_PRESETS[preset];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/Particle/utils.ts
|
|
373
|
+
function normalizeRange(value) {
|
|
374
|
+
return typeof value === "number" ? { min: value, max: value } : value;
|
|
375
|
+
}
|
|
376
|
+
function randomInRange(range) {
|
|
377
|
+
return range.min + Math.random() * (range.max - range.min);
|
|
378
|
+
}
|
|
379
|
+
function normalizeAngle(angle) {
|
|
380
|
+
const normalized = angle % 360;
|
|
381
|
+
const result = normalized < 0 ? normalized + 360 : normalized;
|
|
382
|
+
return result === 0 ? 0 : result;
|
|
383
|
+
}
|
|
384
|
+
function randomAngle(spread, offset) {
|
|
385
|
+
const angle = offset + Math.random() * spread;
|
|
386
|
+
return normalizeAngle(angle);
|
|
387
|
+
}
|
|
388
|
+
function mergeParticleConfig(base, override) {
|
|
389
|
+
if (!override) {
|
|
390
|
+
return base;
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
...base,
|
|
394
|
+
...override
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function resolveParticleConfig(preset, config) {
|
|
398
|
+
let resolved = { ...DEFAULT_PARTICLE_CONFIG };
|
|
399
|
+
if (preset) {
|
|
400
|
+
resolved = mergeParticleConfig(resolved, getParticlePreset(preset));
|
|
401
|
+
}
|
|
402
|
+
if (config) {
|
|
403
|
+
resolved = mergeParticleConfig(resolved, config);
|
|
404
|
+
}
|
|
405
|
+
return resolved;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/LikeButton/utils.ts
|
|
409
|
+
var MAX_FILL_HEIGHT = 85;
|
|
410
|
+
var ICON_SIZE_RATIO = 0.5;
|
|
411
|
+
var MIN_HOVER_OFFSET = 2;
|
|
412
|
+
var PARTICLE_CLEANUP_BUFFER_MS = 100;
|
|
413
|
+
var WAVE_BACK_DURATION_S = 3;
|
|
414
|
+
var WAVE_FRONT_DURATION_S = 1.5;
|
|
415
|
+
var DEFAULT_STYLES = {
|
|
416
|
+
borderWidth: 4,
|
|
417
|
+
borderColor: "#111827",
|
|
418
|
+
shadowOffset: 8,
|
|
419
|
+
shadowColor: "#111827",
|
|
420
|
+
backgroundColor: "white"
|
|
421
|
+
};
|
|
422
|
+
function getShapeStyles(shape = "circle") {
|
|
423
|
+
if (typeof shape === "string") {
|
|
424
|
+
switch (shape) {
|
|
425
|
+
case "circle":
|
|
426
|
+
return { borderRadius: "9999px" };
|
|
427
|
+
case "rounded":
|
|
428
|
+
return { borderRadius: "1rem" };
|
|
429
|
+
case "square":
|
|
430
|
+
return { borderRadius: "0" };
|
|
431
|
+
default:
|
|
432
|
+
return { borderRadius: "9999px" };
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const styles = {};
|
|
436
|
+
if (shape.clipPath) {
|
|
437
|
+
styles.clipPath = shape.clipPath;
|
|
438
|
+
styles.borderRadius = shape.borderRadius ?? "0";
|
|
439
|
+
} else if (shape.borderRadius) {
|
|
440
|
+
styles.borderRadius = shape.borderRadius;
|
|
441
|
+
}
|
|
442
|
+
return styles;
|
|
443
|
+
}
|
|
444
|
+
function computeHoverOffset(shadowOffset) {
|
|
445
|
+
return Math.max(shadowOffset / 2, MIN_HOVER_OFFSET);
|
|
446
|
+
}
|
|
447
|
+
function computeButtonStyles(size, mergedStyles, shapeStyles) {
|
|
448
|
+
return {
|
|
449
|
+
width: size,
|
|
450
|
+
height: size,
|
|
451
|
+
borderWidth: mergedStyles.borderWidth,
|
|
452
|
+
borderColor: mergedStyles.borderColor,
|
|
453
|
+
backgroundColor: mergedStyles.backgroundColor,
|
|
454
|
+
boxShadow: `${mergedStyles.shadowOffset}px ${mergedStyles.shadowOffset}px 0px ${mergedStyles.shadowColor}`,
|
|
455
|
+
...shapeStyles
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function computeHoverActiveVars(hoverShadowOffset, mergedStyles) {
|
|
459
|
+
const translateOnHover = mergedStyles.shadowOffset - hoverShadowOffset;
|
|
460
|
+
return {
|
|
461
|
+
"--shadow-offset": `${mergedStyles.shadowOffset}px`,
|
|
462
|
+
"--shadow-color": mergedStyles.shadowColor,
|
|
463
|
+
"--hover-shadow-offset": `${hoverShadowOffset}px`,
|
|
464
|
+
"--translate-hover": `${translateOnHover}px`
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
var CURSOR_SVGS = {
|
|
468
|
+
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>`,
|
|
469
|
+
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>`,
|
|
470
|
+
"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>`
|
|
471
|
+
};
|
|
472
|
+
var DEFAULT_HOTSPOT = 16;
|
|
473
|
+
function getCursorStyle(cursor = "heart") {
|
|
474
|
+
if (typeof cursor === "string") {
|
|
475
|
+
switch (cursor) {
|
|
476
|
+
case "pointer":
|
|
477
|
+
return "pointer";
|
|
478
|
+
case "none":
|
|
479
|
+
return "none";
|
|
480
|
+
case "heart":
|
|
481
|
+
case "star":
|
|
482
|
+
case "thumbs-up":
|
|
483
|
+
return `url("${CURSOR_SVGS[cursor]}") ${DEFAULT_HOTSPOT} ${DEFAULT_HOTSPOT}, pointer`;
|
|
484
|
+
default:
|
|
485
|
+
return "pointer";
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
const {
|
|
489
|
+
url,
|
|
490
|
+
hotspotX = DEFAULT_HOTSPOT,
|
|
491
|
+
hotspotY = DEFAULT_HOTSPOT,
|
|
492
|
+
fallback = "pointer"
|
|
493
|
+
} = cursor;
|
|
494
|
+
return `url("${url}") ${hotspotX} ${hotspotY}, ${fallback}`;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/LikeButton/useLikeButton.ts
|
|
498
|
+
var LIKE_BUTTON_DEFAULTS = {
|
|
499
|
+
maxClicks: 1,
|
|
500
|
+
size: 96,
|
|
501
|
+
fillColor: "#EF4444",
|
|
502
|
+
waveColor: "#B91C1C"
|
|
503
|
+
};
|
|
504
|
+
function useLikeButton(options = {}) {
|
|
505
|
+
const {
|
|
506
|
+
clicks: externalClicks,
|
|
507
|
+
defaultClicks = 0,
|
|
508
|
+
maxClicks = LIKE_BUTTON_DEFAULTS.maxClicks,
|
|
509
|
+
onClick,
|
|
510
|
+
onChange,
|
|
511
|
+
onRightClick,
|
|
512
|
+
disabled: externalDisabled,
|
|
513
|
+
showParticles = true,
|
|
514
|
+
particlePreset,
|
|
515
|
+
particleConfig,
|
|
516
|
+
ariaLabel: customAriaLabel
|
|
517
|
+
} = options;
|
|
518
|
+
if (externalClicks !== void 0 && options.defaultClicks !== void 0) {
|
|
519
|
+
console.warn(
|
|
520
|
+
"LikeButton: `defaultClicks` is ignored when `clicks` is provided (controlled mode)."
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
const [internalClicks, setInternalClicks] = (0, import_react2.useState)(defaultClicks);
|
|
524
|
+
const [particles, setParticles] = (0, import_react2.useState)([]);
|
|
525
|
+
const timeoutRefs = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
526
|
+
(0, import_react2.useEffect)(() => {
|
|
527
|
+
return () => {
|
|
528
|
+
for (const timeoutId of timeoutRefs.current) {
|
|
529
|
+
clearTimeout(timeoutId);
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
}, []);
|
|
533
|
+
const clicks = externalClicks ?? internalClicks;
|
|
534
|
+
const isMaxed = clicks >= maxClicks;
|
|
535
|
+
const disabled = externalDisabled ?? isMaxed;
|
|
536
|
+
const spawnParticles = (0, import_react2.useCallback)(() => {
|
|
537
|
+
if (!showParticles) return;
|
|
538
|
+
const config = resolveParticleConfig(particlePreset, particleConfig);
|
|
539
|
+
const distanceRange = normalizeRange(config.distance);
|
|
540
|
+
const sizeRange = normalizeRange(config.size);
|
|
541
|
+
const id = Date.now();
|
|
542
|
+
const newParticles = Array.from({ length: config.count }).map((_, i) => ({
|
|
543
|
+
id: `${id}-${i}`,
|
|
544
|
+
angle: randomAngle(config.spread, config.spreadOffset),
|
|
545
|
+
distance: randomInRange(distanceRange),
|
|
546
|
+
scale: randomInRange(sizeRange),
|
|
547
|
+
color: config.colors[Math.floor(Math.random() * config.colors.length)],
|
|
548
|
+
shape: config.shape,
|
|
549
|
+
speed: config.speed,
|
|
550
|
+
easing: config.easing,
|
|
551
|
+
fadeOut: config.fadeOut
|
|
552
|
+
}));
|
|
553
|
+
setParticles((prev) => [...prev, ...newParticles]);
|
|
554
|
+
const cleanupDelay = config.speed + PARTICLE_CLEANUP_BUFFER_MS;
|
|
555
|
+
const idsToRemove = new Set(newParticles.map((p) => p.id));
|
|
556
|
+
const timeoutId = setTimeout(() => {
|
|
557
|
+
setParticles((prev) => prev.filter((p) => !idsToRemove.has(p.id)));
|
|
558
|
+
timeoutRefs.current.delete(timeoutId);
|
|
559
|
+
}, cleanupDelay);
|
|
560
|
+
timeoutRefs.current.add(timeoutId);
|
|
561
|
+
}, [showParticles, particlePreset, particleConfig]);
|
|
562
|
+
const handleClick = (0, import_react2.useCallback)(
|
|
563
|
+
(e) => {
|
|
564
|
+
if (disabled) return;
|
|
565
|
+
const newClicks = clicks + 1;
|
|
566
|
+
if (externalClicks === void 0) {
|
|
567
|
+
setInternalClicks(newClicks);
|
|
568
|
+
}
|
|
569
|
+
spawnParticles();
|
|
570
|
+
onChange?.(newClicks);
|
|
571
|
+
onClick?.(newClicks, e);
|
|
572
|
+
},
|
|
573
|
+
[disabled, clicks, externalClicks, spawnParticles, onChange, onClick]
|
|
574
|
+
);
|
|
575
|
+
const handleRightClick = (0, import_react2.useCallback)(
|
|
576
|
+
(e) => {
|
|
577
|
+
e.preventDefault();
|
|
578
|
+
if (disabled) return;
|
|
579
|
+
onRightClick?.(clicks, e);
|
|
580
|
+
},
|
|
581
|
+
[disabled, clicks, onRightClick]
|
|
582
|
+
);
|
|
583
|
+
const handleKeyDown = (0, import_react2.useCallback)(
|
|
584
|
+
(e) => {
|
|
585
|
+
if (e.shiftKey && e.key === "Enter") {
|
|
586
|
+
e.preventDefault();
|
|
587
|
+
if (disabled) return;
|
|
588
|
+
onRightClick?.(clicks, e);
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
[disabled, clicks, onRightClick]
|
|
592
|
+
);
|
|
593
|
+
const fillPercentage = clicks / maxClicks * 100;
|
|
594
|
+
const defaultAriaLabel = isMaxed ? "Thank you for your likes!" : `Like this content. ${maxClicks - clicks} clicks remaining`;
|
|
595
|
+
const ariaLabelState = {
|
|
596
|
+
isMaxed,
|
|
597
|
+
remaining: maxClicks - clicks,
|
|
598
|
+
clicks,
|
|
599
|
+
maxClicks
|
|
600
|
+
};
|
|
601
|
+
const computedAriaLabel = customAriaLabel === void 0 ? defaultAriaLabel : typeof customAriaLabel === "function" ? customAriaLabel(ariaLabelState) : customAriaLabel;
|
|
602
|
+
return {
|
|
603
|
+
clicks,
|
|
604
|
+
isMaxed,
|
|
605
|
+
disabled,
|
|
606
|
+
fillPercentage,
|
|
607
|
+
particles,
|
|
608
|
+
handleClick,
|
|
609
|
+
handleRightClick,
|
|
610
|
+
handleKeyDown,
|
|
611
|
+
ariaLabel: computedAriaLabel,
|
|
612
|
+
isPressed: clicks > 0,
|
|
613
|
+
hasRightClickAction: onRightClick !== void 0
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// src/LikeButton/LikeButton.tsx
|
|
618
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
619
|
+
var BUTTON_BASE_CLASSES = "relative overflow-hidden z-10 border-solid flex items-center justify-center group transition-all focus:outline-none focus-visible:ring-4 focus-visible:ring-primary-dark focus-visible:ring-offset-2";
|
|
620
|
+
var WRAPPER_CLASSES = "relative inline-block";
|
|
621
|
+
var FILL_CONTAINER_CLASSES = "absolute bottom-0 left-0 right-0 z-0 transition-[height] duration-500 ease-out";
|
|
622
|
+
var WAVE_CONTAINER_CLASSES = "absolute bottom-full left-0 w-[200%] h-4 flex";
|
|
623
|
+
var WAVE_SVG_CLASSES = "w-1/2 h-full fill-current";
|
|
624
|
+
var PARTICLE_CONTAINER_CLASSES = "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 pointer-events-none";
|
|
625
|
+
var LikeButton = (0, import_react3.forwardRef)(function LikeButton2({
|
|
626
|
+
size = LIKE_BUTTON_DEFAULTS.size,
|
|
627
|
+
fillColor = LIKE_BUTTON_DEFAULTS.fillColor,
|
|
628
|
+
waveColor = LIKE_BUTTON_DEFAULTS.waveColor,
|
|
629
|
+
className = "",
|
|
630
|
+
showParticles = true,
|
|
631
|
+
showWaves = true,
|
|
632
|
+
renderIcon,
|
|
633
|
+
shape = "circle",
|
|
634
|
+
styles = {},
|
|
635
|
+
cursor = "heart",
|
|
636
|
+
minFillPercent = 0,
|
|
637
|
+
...hookOptions
|
|
638
|
+
}, ref) {
|
|
639
|
+
const clampedMinFill = Math.max(0, Math.min(MAX_FILL_HEIGHT, minFillPercent));
|
|
640
|
+
const reactId = (0, import_react3.useId)();
|
|
641
|
+
const buttonId = `like-button${reactId.replace(/:/g, "-")}`;
|
|
642
|
+
const {
|
|
643
|
+
handleClick,
|
|
644
|
+
handleRightClick,
|
|
645
|
+
handleKeyDown,
|
|
646
|
+
disabled,
|
|
647
|
+
ariaLabel,
|
|
648
|
+
isPressed,
|
|
649
|
+
isMaxed,
|
|
650
|
+
fillPercentage,
|
|
651
|
+
particles,
|
|
652
|
+
hasRightClickAction
|
|
653
|
+
} = useLikeButton({ showParticles, ...hookOptions });
|
|
654
|
+
const mergedStyles = (0, import_react3.useMemo)(() => ({ ...DEFAULT_STYLES, ...styles }), [styles]);
|
|
655
|
+
const shapeStyles = (0, import_react3.useMemo)(() => getShapeStyles(shape), [shape]);
|
|
656
|
+
const buttonStyle = (0, import_react3.useMemo)(
|
|
657
|
+
() => computeButtonStyles(size, mergedStyles, shapeStyles),
|
|
658
|
+
[size, mergedStyles, shapeStyles]
|
|
659
|
+
);
|
|
660
|
+
const hoverShadowOffset = (0, import_react3.useMemo)(
|
|
661
|
+
() => computeHoverOffset(mergedStyles.shadowOffset),
|
|
662
|
+
[mergedStyles.shadowOffset]
|
|
663
|
+
);
|
|
664
|
+
const hoverActiveVars = (0, import_react3.useMemo)(
|
|
665
|
+
() => computeHoverActiveVars(hoverShadowOffset, mergedStyles),
|
|
666
|
+
[hoverShadowOffset, mergedStyles]
|
|
667
|
+
);
|
|
668
|
+
const cursorStyle = (0, import_react3.useMemo)(
|
|
669
|
+
() => disabled ? "not-allowed" : getCursorStyle(cursor),
|
|
670
|
+
[cursor, disabled]
|
|
671
|
+
);
|
|
672
|
+
const iconSize = size * ICON_SIZE_RATIO;
|
|
673
|
+
const iconRenderProps = {
|
|
674
|
+
size: iconSize,
|
|
675
|
+
className: "relative z-20 transition-colors duration-300 pointer-events-none",
|
|
676
|
+
isMaxed,
|
|
677
|
+
fillPercentage
|
|
678
|
+
};
|
|
679
|
+
const renderedIcon = renderIcon === null ? null : renderIcon === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DefaultHeartIcon, { ...iconRenderProps }) : renderIcon(iconRenderProps);
|
|
680
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: WRAPPER_CLASSES, children: [
|
|
681
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
682
|
+
"button",
|
|
683
|
+
{
|
|
684
|
+
ref,
|
|
685
|
+
id: buttonId,
|
|
686
|
+
"data-like-button": true,
|
|
687
|
+
type: "button",
|
|
688
|
+
onClick: handleClick,
|
|
689
|
+
onContextMenu: handleRightClick,
|
|
690
|
+
onKeyDown: handleKeyDown,
|
|
691
|
+
disabled,
|
|
692
|
+
"aria-label": ariaLabel,
|
|
693
|
+
"aria-pressed": isPressed,
|
|
694
|
+
"aria-disabled": disabled,
|
|
695
|
+
"aria-keyshortcuts": hasRightClickAction ? "Shift+Enter" : void 0,
|
|
696
|
+
style: { ...buttonStyle, ...hoverActiveVars, cursor: cursorStyle },
|
|
697
|
+
className: `${BUTTON_BASE_CLASSES} ${className}`,
|
|
698
|
+
children: [
|
|
699
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
700
|
+
"div",
|
|
701
|
+
{
|
|
702
|
+
className: FILL_CONTAINER_CLASSES,
|
|
703
|
+
style: {
|
|
704
|
+
backgroundColor: fillColor,
|
|
705
|
+
height: isMaxed ? "100%" : `${clampedMinFill + fillPercentage / 100 * (MAX_FILL_HEIGHT - clampedMinFill)}%`
|
|
706
|
+
},
|
|
707
|
+
children: showWaves && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
708
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
709
|
+
"div",
|
|
710
|
+
{
|
|
711
|
+
className: WAVE_CONTAINER_CLASSES,
|
|
712
|
+
style: { animation: `wave-scroll-left ${WAVE_BACK_DURATION_S}s linear infinite` },
|
|
713
|
+
children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
714
|
+
"svg",
|
|
715
|
+
{
|
|
716
|
+
className: WAVE_SVG_CLASSES,
|
|
717
|
+
style: { color: waveColor },
|
|
718
|
+
viewBox: "0 0 100 20",
|
|
719
|
+
preserveAspectRatio: "none",
|
|
720
|
+
"aria-hidden": "true",
|
|
721
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M0,10 Q25,0 50,10 T100,10 V20 H0 Z" })
|
|
722
|
+
},
|
|
723
|
+
i
|
|
724
|
+
))
|
|
725
|
+
}
|
|
726
|
+
),
|
|
727
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
728
|
+
"div",
|
|
729
|
+
{
|
|
730
|
+
className: WAVE_CONTAINER_CLASSES,
|
|
731
|
+
style: { animation: `wave-scroll-right ${WAVE_FRONT_DURATION_S}s linear infinite` },
|
|
732
|
+
children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
733
|
+
"svg",
|
|
734
|
+
{
|
|
735
|
+
className: WAVE_SVG_CLASSES,
|
|
736
|
+
style: { color: fillColor },
|
|
737
|
+
viewBox: "0 0 100 20",
|
|
738
|
+
preserveAspectRatio: "none",
|
|
739
|
+
"aria-hidden": "true",
|
|
740
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M0,10 Q25,5 50,10 T100,10 V20 H0 Z" })
|
|
741
|
+
},
|
|
742
|
+
i
|
|
743
|
+
))
|
|
744
|
+
}
|
|
745
|
+
)
|
|
746
|
+
] })
|
|
747
|
+
}
|
|
748
|
+
),
|
|
749
|
+
renderedIcon
|
|
750
|
+
]
|
|
751
|
+
}
|
|
752
|
+
),
|
|
753
|
+
showParticles && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: PARTICLE_CONTAINER_CLASSES, "aria-hidden": "true", children: particles.map((p) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Particle, { ...p }, p.id)) })
|
|
754
|
+
] });
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
// src/LikeButton/LikeButton.vanilla.tsx
|
|
758
|
+
var import_react4 = require("react");
|
|
759
|
+
|
|
760
|
+
// src/Particle/Particle.vanilla.tsx
|
|
761
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
762
|
+
function ParticleVanilla({
|
|
763
|
+
angle,
|
|
764
|
+
distance,
|
|
765
|
+
scale,
|
|
766
|
+
color,
|
|
767
|
+
shape,
|
|
768
|
+
speed,
|
|
769
|
+
easing,
|
|
770
|
+
fadeOut
|
|
771
|
+
}) {
|
|
772
|
+
const { transform, opacity } = useParticle({
|
|
773
|
+
angle,
|
|
774
|
+
distance,
|
|
775
|
+
scale,
|
|
776
|
+
speed,
|
|
777
|
+
easing,
|
|
778
|
+
fadeOut
|
|
779
|
+
});
|
|
780
|
+
const ShapeComponent = getParticleShape(shape);
|
|
781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
782
|
+
"div",
|
|
783
|
+
{
|
|
784
|
+
className: "particle",
|
|
785
|
+
style: {
|
|
786
|
+
color,
|
|
787
|
+
transform,
|
|
788
|
+
opacity: fadeOut ? opacity : 1,
|
|
789
|
+
transitionDuration: `${speed}ms`,
|
|
790
|
+
transitionTimingFunction: easing
|
|
791
|
+
},
|
|
792
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ShapeComponent, { size: 40, color, className: "particle__icon" })
|
|
793
|
+
}
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// src/LikeButton/LikeButton.vanilla.tsx
|
|
798
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
799
|
+
var LikeButtonVanilla = (0, import_react4.forwardRef)(
|
|
800
|
+
function LikeButtonVanilla2({
|
|
801
|
+
size = LIKE_BUTTON_DEFAULTS.size,
|
|
802
|
+
fillColor = LIKE_BUTTON_DEFAULTS.fillColor,
|
|
803
|
+
waveColor = LIKE_BUTTON_DEFAULTS.waveColor,
|
|
804
|
+
className = "",
|
|
805
|
+
showParticles = true,
|
|
806
|
+
showWaves = true,
|
|
807
|
+
renderIcon,
|
|
808
|
+
shape = "circle",
|
|
809
|
+
styles = {},
|
|
810
|
+
cursor = "heart",
|
|
811
|
+
minFillPercent = 0,
|
|
812
|
+
...hookOptions
|
|
813
|
+
}, ref) {
|
|
814
|
+
const clampedMinFill = Math.max(0, Math.min(MAX_FILL_HEIGHT, minFillPercent));
|
|
815
|
+
const reactId = (0, import_react4.useId)();
|
|
816
|
+
const buttonId = `like-button${reactId.replace(/:/g, "-")}`;
|
|
817
|
+
const {
|
|
818
|
+
handleClick,
|
|
819
|
+
handleRightClick,
|
|
820
|
+
handleKeyDown,
|
|
821
|
+
disabled,
|
|
822
|
+
ariaLabel,
|
|
823
|
+
isPressed,
|
|
824
|
+
isMaxed,
|
|
825
|
+
fillPercentage,
|
|
826
|
+
particles,
|
|
827
|
+
hasRightClickAction
|
|
828
|
+
} = useLikeButton({ showParticles, ...hookOptions });
|
|
829
|
+
const mergedStyles = (0, import_react4.useMemo)(() => ({ ...DEFAULT_STYLES, ...styles }), [styles]);
|
|
830
|
+
const shapeStyles = (0, import_react4.useMemo)(() => getShapeStyles(shape), [shape]);
|
|
831
|
+
const buttonStyle = (0, import_react4.useMemo)(
|
|
832
|
+
() => computeButtonStyles(size, mergedStyles, shapeStyles),
|
|
833
|
+
[size, mergedStyles, shapeStyles]
|
|
834
|
+
);
|
|
835
|
+
const hoverShadowOffset = (0, import_react4.useMemo)(
|
|
836
|
+
() => computeHoverOffset(mergedStyles.shadowOffset),
|
|
837
|
+
[mergedStyles.shadowOffset]
|
|
838
|
+
);
|
|
839
|
+
const hoverActiveVars = (0, import_react4.useMemo)(
|
|
840
|
+
() => computeHoverActiveVars(hoverShadowOffset, mergedStyles),
|
|
841
|
+
[hoverShadowOffset, mergedStyles]
|
|
842
|
+
);
|
|
843
|
+
const cursorStyle = (0, import_react4.useMemo)(
|
|
844
|
+
() => disabled ? "not-allowed" : getCursorStyle(cursor),
|
|
845
|
+
[cursor, disabled]
|
|
846
|
+
);
|
|
847
|
+
const iconSize = size * ICON_SIZE_RATIO;
|
|
848
|
+
const iconRenderProps = {
|
|
849
|
+
size: iconSize,
|
|
850
|
+
className: "like-button__icon",
|
|
851
|
+
isMaxed,
|
|
852
|
+
fillPercentage
|
|
853
|
+
};
|
|
854
|
+
const renderedIcon = renderIcon === null ? null : renderIcon === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(DefaultHeartIcon, { ...iconRenderProps }) : renderIcon(iconRenderProps);
|
|
855
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "like-button-container", children: [
|
|
856
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
857
|
+
"button",
|
|
858
|
+
{
|
|
859
|
+
ref,
|
|
860
|
+
id: buttonId,
|
|
861
|
+
type: "button",
|
|
862
|
+
onClick: handleClick,
|
|
863
|
+
onContextMenu: handleRightClick,
|
|
864
|
+
onKeyDown: handleKeyDown,
|
|
865
|
+
disabled,
|
|
866
|
+
"aria-label": ariaLabel,
|
|
867
|
+
"aria-pressed": isPressed,
|
|
868
|
+
"aria-disabled": disabled,
|
|
869
|
+
"aria-keyshortcuts": hasRightClickAction ? "Shift+Enter" : void 0,
|
|
870
|
+
style: { ...buttonStyle, ...hoverActiveVars, cursor: cursorStyle },
|
|
871
|
+
className: `like-button ${className}`.trim(),
|
|
872
|
+
children: [
|
|
873
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
874
|
+
"div",
|
|
875
|
+
{
|
|
876
|
+
className: "like-button__fill",
|
|
877
|
+
style: {
|
|
878
|
+
backgroundColor: fillColor,
|
|
879
|
+
height: isMaxed ? "100%" : `${clampedMinFill + fillPercentage / 100 * (MAX_FILL_HEIGHT - clampedMinFill)}%`
|
|
880
|
+
},
|
|
881
|
+
children: showWaves && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
882
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "like-button__wave like-button__wave--back", children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
883
|
+
"svg",
|
|
884
|
+
{
|
|
885
|
+
className: "like-button__wave-svg",
|
|
886
|
+
style: { color: waveColor },
|
|
887
|
+
viewBox: "0 0 100 20",
|
|
888
|
+
preserveAspectRatio: "none",
|
|
889
|
+
"aria-hidden": "true",
|
|
890
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M0,10 Q25,0 50,10 T100,10 V20 H0 Z" })
|
|
891
|
+
},
|
|
892
|
+
i
|
|
893
|
+
)) }),
|
|
894
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "like-button__wave like-button__wave--front", children: [0, 1].map((i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
895
|
+
"svg",
|
|
896
|
+
{
|
|
897
|
+
className: "like-button__wave-svg",
|
|
898
|
+
style: { color: fillColor },
|
|
899
|
+
viewBox: "0 0 100 20",
|
|
900
|
+
preserveAspectRatio: "none",
|
|
901
|
+
"aria-hidden": "true",
|
|
902
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M0,10 Q25,5 50,10 T100,10 V20 H0 Z" })
|
|
903
|
+
},
|
|
904
|
+
i
|
|
905
|
+
)) })
|
|
906
|
+
] })
|
|
907
|
+
}
|
|
908
|
+
),
|
|
909
|
+
renderedIcon
|
|
910
|
+
]
|
|
911
|
+
}
|
|
912
|
+
),
|
|
913
|
+
showParticles && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "like-button__particles", "aria-hidden": "true", children: particles.map((p) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ParticleVanilla, { ...p }, p.id)) })
|
|
914
|
+
] });
|
|
915
|
+
}
|
|
916
|
+
);
|
|
917
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
918
|
+
0 && (module.exports = {
|
|
919
|
+
DefaultHeartIcon,
|
|
920
|
+
LIKE_BUTTON_DEFAULTS,
|
|
921
|
+
LikeButton,
|
|
922
|
+
LikeButtonVanilla,
|
|
923
|
+
PARTICLE_PRESETS,
|
|
924
|
+
useLikeButton
|
|
925
|
+
});
|
|
926
|
+
//# sourceMappingURL=index.cjs.map
|