@anubis609/astroanimate-core 0.1.2

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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +212 -0
  3. package/dist/components/AnimatedBorderButton/AnimatedBorderButton.astro +129 -0
  4. package/dist/components/AnimatedBorderButton/index.js +3 -0
  5. package/dist/components/AnimatedBorderButton/index.js.map +1 -0
  6. package/dist/components/AnimatedButton/AnimatedButton.astro +299 -0
  7. package/dist/components/AnimatedButton/index.js +3 -0
  8. package/dist/components/AnimatedButton/index.js.map +1 -0
  9. package/dist/components/AnimatedCard/AnimatedCard.astro +832 -0
  10. package/dist/components/AnimatedCard/index.js +3 -0
  11. package/dist/components/AnimatedCard/index.js.map +1 -0
  12. package/dist/components/AnimatedTabs/AnimatedTabs.astro +348 -0
  13. package/dist/components/AnimatedTabs/index.js +3 -0
  14. package/dist/components/AnimatedTabs/index.js.map +1 -0
  15. package/dist/components/ArrowCTAButton/ArrowCTAButton.astro +159 -0
  16. package/dist/components/ArticleCard/ArticleCard.astro +208 -0
  17. package/dist/components/CardStack/CardStack.astro +444 -0
  18. package/dist/components/CardStack/index.js +3 -0
  19. package/dist/components/CardStack/index.js.map +1 -0
  20. package/dist/components/CountUp/CountUp.astro +89 -0
  21. package/dist/components/CountUp/index.js +3 -0
  22. package/dist/components/CountUp/index.js.map +1 -0
  23. package/dist/components/Dock/Dock.astro +567 -0
  24. package/dist/components/Dock/DockItem.astro +135 -0
  25. package/dist/components/Dropdown/Dropdown.astro +264 -0
  26. package/dist/components/ExpandableCard/ExpandableCard.astro +402 -0
  27. package/dist/components/ExpandableCard/index.js +3 -0
  28. package/dist/components/ExpandableCard/index.js.map +1 -0
  29. package/dist/components/FadeInText/FadeInText.astro +314 -0
  30. package/dist/components/FadeInText/index.js +3 -0
  31. package/dist/components/FadeInText/index.js.map +1 -0
  32. package/dist/components/FillHoverButton/FillHoverButton.astro +125 -0
  33. package/dist/components/GitHubShineButton/GitHubShineButton.astro +208 -0
  34. package/dist/components/GlassCard/GlassCard.astro +245 -0
  35. package/dist/components/GlassCard/index.js +3 -0
  36. package/dist/components/GlassCard/index.js.map +1 -0
  37. package/dist/components/GridDotsBackground/GridDotsBackground.astro +144 -0
  38. package/dist/components/HighlightText/HighlightText.astro +106 -0
  39. package/dist/components/InfiniteMarquee/InfiniteMarquee.astro +339 -0
  40. package/dist/components/JobCard/JobCard.astro +230 -0
  41. package/dist/components/LiquidGlassCard/LiquidGlassCard.astro +569 -0
  42. package/dist/components/Loader/Loader.astro +156 -0
  43. package/dist/components/Loader/index.js +3 -0
  44. package/dist/components/Loader/index.js.map +1 -0
  45. package/dist/components/NewsletterPopupCard/NewsletterPopupCard.astro +331 -0
  46. package/dist/components/ProductReviewCard/ProductReviewCard.astro +188 -0
  47. package/dist/components/ProgressBar/ProgressBar.astro +137 -0
  48. package/dist/components/ProgressBar/index.js +3 -0
  49. package/dist/components/ProgressBar/index.js.map +1 -0
  50. package/dist/components/RevealImage/RevealImage.astro +160 -0
  51. package/dist/components/RevealImage/index.js +3 -0
  52. package/dist/components/RevealImage/index.js.map +1 -0
  53. package/dist/components/ScaleIn/ScaleIn.astro +231 -0
  54. package/dist/components/ScaleIn/index.js +3 -0
  55. package/dist/components/ScaleIn/index.js.map +1 -0
  56. package/dist/components/SlidingOverlayButton/SlidingOverlayButton.astro +126 -0
  57. package/dist/components/StaggerTextButton/StaggerTextButton.astro +132 -0
  58. package/dist/components/Tooltip/Tooltip.astro +255 -0
  59. package/dist/components/Tooltip/index.js +3 -0
  60. package/dist/components/Tooltip/index.js.map +1 -0
  61. package/dist/components/TypewriterText/TypewriterText.astro +380 -0
  62. package/dist/components/TypewriterText/index.js +3 -0
  63. package/dist/components/TypewriterText/index.js.map +1 -0
  64. package/dist/components/index.js +33 -0
  65. package/dist/components/index.js.map +1 -0
  66. package/dist/index.js +31 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/internal/countup.js +90 -0
  69. package/dist/internal/countup.js.map +1 -0
  70. package/dist/internal/dropdown.js +166 -0
  71. package/dist/internal/dropdown.js.map +1 -0
  72. package/dist/internal/fadein.js +116 -0
  73. package/dist/internal/fadein.js.map +1 -0
  74. package/dist/internal/guards.js +12 -0
  75. package/dist/internal/guards.js.map +1 -0
  76. package/dist/internal/tabs.js +140 -0
  77. package/dist/internal/tabs.js.map +1 -0
  78. package/package.json +229 -0
@@ -0,0 +1,569 @@
1
+ ---
2
+ /**
3
+ * LiquidGlassCard.astro
4
+ */
5
+
6
+ interface Props {
7
+ class?: string;
8
+ contentClass?: string;
9
+ draggable?: boolean;
10
+ expandable?: boolean;
11
+ width?: string;
12
+ height?: string;
13
+ expandedWidth?: string;
14
+ expandedHeight?: string;
15
+ blurIntensity?: "sm" | "md" | "lg" | "xl";
16
+ borderRadius?: string;
17
+ glowIntensity?: "none" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
18
+ shadowIntensity?: "none" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
19
+ paddingTop?: string;
20
+ paddingBottom?: string;
21
+ paddingLeft?: string;
22
+ paddingRight?: string;
23
+ [key: string]: unknown;
24
+ }
25
+
26
+ const {
27
+ class: className = "",
28
+ contentClass = "",
29
+ draggable = false,
30
+ expandable = false,
31
+ width = "360px",
32
+ height = "160px",
33
+ expandedWidth = "520px",
34
+ expandedHeight = "320px",
35
+ blurIntensity = "sm",
36
+ borderRadius = "24px",
37
+ glowIntensity = "sm",
38
+ shadowIntensity = "md",
39
+ paddingTop = "24px",
40
+ paddingBottom = "24px",
41
+ paddingLeft = "24px",
42
+ paddingRight = "24px",
43
+ ...rest
44
+ } = Astro.props;
45
+
46
+ const blurMap = { sm: "8px", md: "14px", lg: "20px", xl: "28px" };
47
+ const blurPx = blurMap[blurIntensity] ?? "8px";
48
+
49
+ const glowMap = {
50
+ none: "none",
51
+ xs: "0 0 8px rgba(255,255,255,.08)",
52
+ sm: "0 0 16px rgba(255,255,255,.12)",
53
+ md: "0 0 28px rgba(255,255,255,.18)",
54
+ lg: "0 0 40px rgba(255,255,255,.22)",
55
+ xl: "0 0 56px rgba(255,255,255,.28)",
56
+ "2xl": "0 0 72px rgba(255,255,255,.35)",
57
+ };
58
+ const glowShadow = glowMap[glowIntensity] ?? glowMap.sm;
59
+
60
+ const shadowMap = {
61
+ none: "none",
62
+ xs: "inset 0 0 6px rgba(0,0,0,.06)",
63
+ sm: "inset 0 2px 8px rgba(0,0,0,.10)",
64
+ md: "inset 0 4px 18px rgba(0,0,0,.18)",
65
+ lg: "inset 0 6px 28px rgba(0,0,0,.24)",
66
+ xl: "inset 0 8px 40px rgba(0,0,0,.30)",
67
+ "2xl": "inset 0 12px 56px rgba(0,0,0,.36)",
68
+ };
69
+ const innerShadow = shadowMap[shadowIntensity] ?? shadowMap.md;
70
+
71
+ const boxShadow = [
72
+ "0 4px 32px rgba(0,0,0,.18)",
73
+ glowShadow !== "none" ? glowShadow : null,
74
+ innerShadow !== "none" ? innerShadow : null,
75
+ "inset 0 1px 0 rgba(255,255,255,.35)",
76
+ ]
77
+ .filter(Boolean)
78
+ .join(", ");
79
+
80
+ const instanceId = `lg-${Math.random().toString(36).slice(2, 10)}`;
81
+
82
+ const inlineStyle = [
83
+ `--lg-width:${width};`,
84
+ `--lg-height:${height};`,
85
+ `--lg-expanded-width:${expandedWidth};`,
86
+ `--lg-expanded-height:${expandedHeight};`,
87
+ `--lg-blur:${blurPx};`,
88
+ `--lg-radius:${borderRadius};`,
89
+ `--lg-shadow:${boxShadow};`,
90
+ `--lg-padding-top:${paddingTop};`,
91
+ `--lg-padding-bottom:${paddingBottom};`,
92
+ `--lg-padding-left:${paddingLeft};`,
93
+ `--lg-padding-right:${paddingRight};`,
94
+ ].join("");
95
+ ---
96
+
97
+ <div
98
+ data-lg-instance={instanceId}
99
+ class:list={["lg-card", className]}
100
+ data-lg-expanded="false"
101
+ data-lg-draggable={draggable ? "true" : undefined}
102
+ data-lg-expandable={expandable ? "true" : undefined}
103
+ data-lg-width={width}
104
+ data-lg-height={height}
105
+ data-lg-expanded-width={expandedWidth}
106
+ data-lg-expanded-height={expandedHeight}
107
+ style={inlineStyle}
108
+ {...rest}
109
+ >
110
+ <div class:list={["lg-content", contentClass]}>
111
+ <slot>
112
+ <div class="lg-default-content">
113
+ <span class="lg-kicker">Liquid Glass</span>
114
+ <strong>Glass Card</strong>
115
+ <p>Draggable, expandable, and beautifully blurred.</p>
116
+ </div>
117
+ </slot>
118
+ </div>
119
+ </div>
120
+
121
+ <style>
122
+ .lg-card {
123
+ position: relative;
124
+ isolation: isolate;
125
+ overflow: hidden;
126
+ flex: none;
127
+ display: block;
128
+ box-sizing: border-box;
129
+ width: var(--lg-width);
130
+ height: var(--lg-height);
131
+ --lg-tx: 0px;
132
+ --lg-ty: 0px;
133
+ transform: translate(var(--lg-tx), var(--lg-ty));
134
+ background: rgba(255, 255, 255, 0.08);
135
+ backdrop-filter: blur(var(--lg-blur, 8px));
136
+ -webkit-backdrop-filter: blur(var(--lg-blur, 8px));
137
+ border-radius: var(--lg-radius, 32px);
138
+ border: 1px solid rgba(255, 255, 255, 0.22);
139
+ box-shadow: var(--lg-shadow);
140
+ transition:
141
+ box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1),
142
+ border-color 300ms cubic-bezier(0.4, 0, 0.2, 1),
143
+ width 350ms cubic-bezier(0.4, 0, 0.2, 1),
144
+ height 350ms cubic-bezier(0.4, 0, 0.2, 1),
145
+ transform 500ms cubic-bezier(0.34, 1.56, 0.64, 1);
146
+ cursor: default;
147
+ user-select: none;
148
+ color: white;
149
+ font-family:
150
+ Inter,
151
+ ui-sans-serif,
152
+ system-ui,
153
+ -apple-system,
154
+ BlinkMacSystemFont,
155
+ "Segoe UI",
156
+ sans-serif;
157
+ }
158
+
159
+ .lg-card[data-lg-expanded="true"] {
160
+ width: var(--lg-expanded-width);
161
+ height: var(--lg-expanded-height);
162
+ }
163
+
164
+ .lg-card[data-lg-draggable="true"] {
165
+ cursor: grab;
166
+ position: relative;
167
+ }
168
+
169
+ .lg-card[data-lg-draggable="true"][data-lg-dragging="true"] {
170
+ cursor: grabbing;
171
+ z-index: 100;
172
+ transition:
173
+ box-shadow 100ms ease,
174
+ border-color 100ms ease;
175
+ border-color: rgba(255, 255, 255, 0.45);
176
+ }
177
+
178
+ .lg-card[data-lg-snap="true"] {
179
+ transition: transform 500ms cubic-bezier(0.34, 1.56, 0.64, 1) !important;
180
+ }
181
+
182
+ .lg-card[data-lg-expandable="true"] {
183
+ cursor: pointer;
184
+ }
185
+
186
+ .lg-card::before {
187
+ content: "";
188
+ position: absolute;
189
+ inset: 0;
190
+ z-index: 1;
191
+ border-radius: inherit;
192
+ pointer-events: none;
193
+ background: linear-gradient(
194
+ 135deg,
195
+ rgba(255, 255, 255, 0.22) 0%,
196
+ rgba(255, 255, 255, 0.04) 40%,
197
+ transparent 70%
198
+ );
199
+ transition: background 300ms ease;
200
+ }
201
+
202
+ .lg-card::after {
203
+ content: "";
204
+ position: absolute;
205
+ inset: 0;
206
+ z-index: 2;
207
+ border-radius: inherit;
208
+ pointer-events: none;
209
+ box-shadow:
210
+ inset 0 0 20px -6px rgba(255, 255, 255, 0.18),
211
+ inset 0 0 2px 0 rgba(255, 255, 255, 0.3);
212
+ transition: box-shadow 300ms ease;
213
+ }
214
+
215
+ .lg-card:hover {
216
+ border-color: rgba(255, 255, 255, 0.38);
217
+ }
218
+
219
+ .lg-card:hover::before {
220
+ background: linear-gradient(
221
+ 135deg,
222
+ rgba(255, 255, 255, 0.3) 0%,
223
+ rgba(255, 255, 255, 0.06) 40%,
224
+ transparent 65%
225
+ );
226
+ }
227
+
228
+ .lg-card:hover::after {
229
+ box-shadow:
230
+ inset 0 0 28px -6px rgba(255, 255, 255, 0.26),
231
+ inset 0 0 3px 0 rgba(255, 255, 255, 0.42);
232
+ }
233
+
234
+ .lg-content {
235
+ position: relative;
236
+ z-index: 3;
237
+ width: 100%;
238
+ height: 100%;
239
+ padding-top: var(--lg-padding-top, 24px);
240
+ padding-bottom: var(--lg-padding-bottom, 24px);
241
+ padding-left: var(--lg-padding-left, 24px);
242
+ padding-right: var(--lg-padding-right, 24px);
243
+ box-sizing: border-box;
244
+ text-shadow: 0 1px 18px rgba(0, 0, 0, 0.22);
245
+ }
246
+
247
+ .lg-content :global(*) {
248
+ box-sizing: border-box;
249
+ }
250
+
251
+ .lg-content :global(p),
252
+ .lg-content :global(h1),
253
+ .lg-content :global(h2),
254
+ .lg-content :global(h3),
255
+ .lg-content :global(h4) {
256
+ margin: 0;
257
+ }
258
+
259
+ .lg-content :global(.lg-hourly) {
260
+ width: 100%;
261
+ height: 100%;
262
+ display: flex;
263
+ justify-content: center;
264
+ align-items: center;
265
+ gap: 0.75rem;
266
+ font-size: 0.875rem;
267
+ font-weight: 500;
268
+ }
269
+
270
+ .lg-content :global(.lg-hour-item) {
271
+ display: flex;
272
+ min-width: 3.5rem;
273
+ flex-direction: column;
274
+ align-items: center;
275
+ gap: 0.5rem;
276
+ }
277
+
278
+ .lg-content :global(.lg-panel) {
279
+ width: 100%;
280
+ height: 100%;
281
+ display: flex;
282
+ flex-direction: column;
283
+ align-items: center;
284
+ justify-content: center;
285
+ text-align: center;
286
+ }
287
+
288
+ .lg-content :global(.lg-title) {
289
+ font-size: 3.75rem;
290
+ line-height: 1;
291
+ font-weight: 600;
292
+ letter-spacing: 0;
293
+ }
294
+
295
+ .lg-content :global(.lg-subtitle) {
296
+ margin-top: 0.35rem;
297
+ font-size: 1.125rem;
298
+ line-height: 1.4;
299
+ }
300
+
301
+ .lg-content :global(.lg-pill) {
302
+ margin-top: 1rem;
303
+ display: inline-flex;
304
+ align-items: center;
305
+ border: 0;
306
+ border-radius: 999px;
307
+ padding: 0.35rem 0.7rem;
308
+ color: inherit;
309
+ font: inherit;
310
+ font-size: 0.875rem;
311
+ font-weight: 500;
312
+ background: rgba(0, 0, 0, 0.12);
313
+ backdrop-filter: blur(18px);
314
+ -webkit-backdrop-filter: blur(18px);
315
+ }
316
+
317
+ .lg-content :global(.lg-list) {
318
+ width: 100%;
319
+ height: 100%;
320
+ display: flex;
321
+ flex-direction: column;
322
+ align-items: center;
323
+ justify-content: center;
324
+ gap: 1rem;
325
+ font-size: 1rem;
326
+ }
327
+
328
+ .lg-content :global(.lg-list-row) {
329
+ display: flex;
330
+ align-items: center;
331
+ justify-content: space-between;
332
+ gap: 1rem;
333
+ }
334
+
335
+ .lg-default-content {
336
+ display: grid;
337
+ gap: 0.5rem;
338
+ justify-items: center;
339
+ }
340
+
341
+ .lg-kicker {
342
+ font-size: 0.75rem;
343
+ font-weight: 700;
344
+ letter-spacing: 0.14em;
345
+ text-transform: uppercase;
346
+ opacity: 0.72;
347
+ }
348
+
349
+ .lg-default-content strong {
350
+ font-size: 2rem;
351
+ line-height: 1;
352
+ }
353
+
354
+ .lg-default-content p {
355
+ max-width: 17rem;
356
+ font-size: 0.95rem;
357
+ line-height: 1.45;
358
+ opacity: 0.82;
359
+ }
360
+
361
+ @media (prefers-reduced-motion: reduce) {
362
+ .lg-card,
363
+ .lg-card[data-lg-snap="true"],
364
+ .lg-card[data-lg-expanded="true"],
365
+ .lg-card::before,
366
+ .lg-card::after {
367
+ transition: none !important;
368
+ animation: none !important;
369
+ }
370
+
371
+ .lg-card {
372
+ transform: translate(0, 0) !important;
373
+ --lg-tx: 0px !important;
374
+ --lg-ty: 0px !important;
375
+ }
376
+
377
+ .lg-card:hover,
378
+ .lg-card:hover::before,
379
+ .lg-card:hover::after {
380
+ transition: none !important;
381
+ }
382
+ }
383
+ </style>
384
+
385
+ <script is:inline define:vars={{ draggable, expandable, blurPx, instanceId }}>
386
+ (function () {
387
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
388
+
389
+ const root = document.querySelector(`[data-lg-instance="${instanceId}"]`);
390
+ if (!root) return;
391
+
392
+ if (root._lgCleanup) return;
393
+ root._lgCleanup = [];
394
+
395
+ const filterId = `lgf-${instanceId}`;
396
+ const NS = "http://www.w3.org/2000/svg";
397
+
398
+ let svg = document.querySelector(`svg[data-lg-instance="${instanceId}"]`);
399
+ if (!svg) {
400
+ svg = document.createElementNS(NS, "svg");
401
+ svg.setAttribute("data-lg-instance", instanceId);
402
+ svg.setAttribute("width", "0");
403
+ svg.setAttribute("height", "0");
404
+ svg.setAttribute("aria-hidden", "true");
405
+ svg.style.cssText =
406
+ "position:absolute;overflow:hidden;pointer-events:none;";
407
+
408
+ const defs = document.createElementNS(NS, "defs");
409
+ const filt = document.createElementNS(NS, "filter");
410
+ filt.id = filterId;
411
+ filt.setAttribute("x", "-10%");
412
+ filt.setAttribute("y", "-10%");
413
+ filt.setAttribute("width", "120%");
414
+ filt.setAttribute("height", "120%");
415
+ filt.setAttribute("color-interpolation-filters", "sRGB");
416
+
417
+ const turb = document.createElementNS(NS, "feTurbulence");
418
+ Object.entries({
419
+ type: "fractalNoise",
420
+ baseFrequency: "0.012 0.018",
421
+ numOctaves: "3",
422
+ seed: "42",
423
+ result: "noise",
424
+ }).forEach(([k, v]) => turb.setAttribute(k, v));
425
+
426
+ const gblur = document.createElementNS(NS, "feGaussianBlur");
427
+ Object.entries({
428
+ in: "noise",
429
+ stdDeviation: "2.5",
430
+ result: "blurred",
431
+ }).forEach(([k, v]) => gblur.setAttribute(k, v));
432
+
433
+ const disp = document.createElementNS(NS, "feDisplacementMap");
434
+ Object.entries({
435
+ in: "SourceGraphic",
436
+ in2: "blurred",
437
+ scale: "18",
438
+ xChannelSelector: "R",
439
+ yChannelSelector: "G",
440
+ }).forEach(([k, v]) => disp.setAttribute(k, v));
441
+
442
+ filt.append(turb, gblur, disp);
443
+ defs.append(filt);
444
+ svg.append(defs);
445
+ document.body.appendChild(svg);
446
+ root._lgCleanup.push(() => svg.remove());
447
+ }
448
+
449
+ let styleEl = document.querySelector(
450
+ `style[data-lg-instance="${instanceId}"]`,
451
+ );
452
+ if (!styleEl) {
453
+ styleEl = document.createElement("style");
454
+ styleEl.setAttribute("data-lg-instance", instanceId);
455
+ styleEl.textContent = `[data-lg-instance="${instanceId}"]{backdrop-filter:url(#${filterId}) blur(${blurPx});-webkit-backdrop-filter:blur(${blurPx});}`;
456
+ document.head.appendChild(styleEl);
457
+ root._lgCleanup.push(() => styleEl.remove());
458
+ }
459
+
460
+ if (expandable) {
461
+ const expandHandler = () => {
462
+ const expanded = root.dataset.lgExpanded === "true";
463
+ root.dataset.lgExpanded = String(!expanded);
464
+ };
465
+ root.addEventListener("click", expandHandler, { passive: true });
466
+ root._lgCleanup.push(() =>
467
+ root.removeEventListener("click", expandHandler),
468
+ );
469
+ }
470
+
471
+ if (draggable) {
472
+ let sx = 0;
473
+ let sy = 0;
474
+ let ox = 0;
475
+ let oy = 0;
476
+ let cx = 0;
477
+ let cy = 0;
478
+ let active = false;
479
+ let raf = null;
480
+ let snapTimer = null;
481
+
482
+ const move = (e) => {
483
+ if (!active) return;
484
+ e.preventDefault();
485
+ const px = e.touches?.[0].clientX ?? e.clientX;
486
+ const py = e.touches?.[0].clientY ?? e.clientY;
487
+ ox = px - sx;
488
+ oy = py - sy;
489
+ if (raf) cancelAnimationFrame(raf);
490
+ raf = requestAnimationFrame(() => {
491
+ root.style.setProperty("--lg-tx", `${ox}px`);
492
+ root.style.setProperty("--lg-ty", `${oy}px`);
493
+ cx = ox;
494
+ cy = oy;
495
+ });
496
+ };
497
+
498
+ const up = () => {
499
+ if (!active) return;
500
+ active = false;
501
+ root.dataset.lgDragging = "false";
502
+
503
+ window.removeEventListener("mousemove", move);
504
+ window.removeEventListener("touchmove", move);
505
+ window.removeEventListener("mouseup", up);
506
+ window.removeEventListener("touchend", up);
507
+
508
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
509
+ root.style.setProperty("--lg-tx", "0px");
510
+ root.style.setProperty("--lg-ty", "0px");
511
+ cx = 0;
512
+ cy = 0;
513
+ root.dataset.lgSnap = "false";
514
+ } else {
515
+ root.dataset.lgSnap = "true";
516
+ root.style.setProperty("--lg-tx", "0px");
517
+ root.style.setProperty("--lg-ty", "0px");
518
+ cx = 0;
519
+ cy = 0;
520
+
521
+ clearTimeout(snapTimer);
522
+ snapTimer = setTimeout(() => {
523
+ root.dataset.lgSnap = "false";
524
+ }, 510);
525
+ }
526
+ };
527
+
528
+ const down = (e) => {
529
+ active = true;
530
+ const px = e.touches?.[0].clientX ?? e.clientX;
531
+ const py = e.touches?.[0].clientY ?? e.clientY;
532
+ sx = px - cx;
533
+ sy = py - cy;
534
+ root.dataset.lgDragging = "true";
535
+
536
+ window.addEventListener("mousemove", move, { passive: false });
537
+ window.addEventListener("touchmove", move, { passive: false });
538
+ window.addEventListener("mouseup", up, { passive: true });
539
+ window.addEventListener("touchend", up, { passive: true });
540
+ };
541
+
542
+ root.addEventListener("mousedown", down, { passive: true });
543
+ root.addEventListener("touchstart", down, { passive: true });
544
+
545
+ root._lgCleanup.push(() => {
546
+ root.removeEventListener("mousedown", down);
547
+ root.removeEventListener("touchstart", down);
548
+ window.removeEventListener("mousemove", move);
549
+ window.removeEventListener("touchmove", move);
550
+ window.removeEventListener("mouseup", up);
551
+ window.removeEventListener("touchend", up);
552
+ if (raf) cancelAnimationFrame(raf);
553
+ clearTimeout(snapTimer);
554
+ });
555
+ }
556
+
557
+ const observer = new MutationObserver(() => {
558
+ if (!document.contains(root)) {
559
+ if (root._lgCleanup) {
560
+ root._lgCleanup.forEach((fn) => fn());
561
+ root._lgCleanup = null;
562
+ }
563
+ observer.disconnect();
564
+ }
565
+ });
566
+ observer.observe(document.body, { childList: true, subtree: true });
567
+ root._lgCleanup.push(() => observer.disconnect());
568
+ })();
569
+ </script>
@@ -0,0 +1,156 @@
1
+ ---
2
+ interface Props {
3
+ /** Size in pixels (default: 40) */
4
+ size?: number;
5
+ /** Primary color (default: 'currentColor' or white) */
6
+ color?: string;
7
+ /** Animation speed in seconds (default: 1s) */
8
+ speed?: number;
9
+ /** Loader type variant */
10
+ type?: "spinner" | "dots" | "pulse";
11
+ mainClassName?: string;
12
+ }
13
+
14
+ const {
15
+ size = 40,
16
+ color = "#3b82f6",
17
+ speed = 1,
18
+ type = "spinner",
19
+ mainClassName = "",
20
+ } = Astro.props;
21
+ ---
22
+
23
+ <div
24
+ class:list={["loader-wrapper", mainClassName]}
25
+ style={`--loader-size: ${size}px; --loader-color: ${color}; --loader-speed: ${speed}s;`}
26
+ aria-label="Loading"
27
+ role="status"
28
+ >
29
+ {
30
+ type === "spinner" && (
31
+ <svg class="loader-spinner" viewBox="0 0 50 50">
32
+ <circle
33
+ class="path"
34
+ cx="25"
35
+ cy="25"
36
+ r="20"
37
+ fill="none"
38
+ stroke-width="4"
39
+ />
40
+ </svg>
41
+ )
42
+ }
43
+
44
+ {
45
+ type === "dots" && (
46
+ <div class="loader-dots">
47
+ <div class="dot" />
48
+ <div class="dot" />
49
+ <div class="dot" />
50
+ </div>
51
+ )
52
+ }
53
+
54
+ {type === "pulse" && <div class="loader-pulse" />}
55
+ </div>
56
+
57
+ <style>
58
+ .loader-wrapper {
59
+ display: inline-flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ color: var(--loader-color);
63
+ }
64
+
65
+ /* SPINNER */
66
+ .loader-spinner {
67
+ width: var(--loader-size);
68
+ height: var(--loader-size);
69
+ animation: rotate var(--loader-speed) linear infinite;
70
+ }
71
+ .loader-spinner .path {
72
+ stroke: currentColor;
73
+ stroke-linecap: round;
74
+ animation: dash 1.5s ease-in-out infinite;
75
+ }
76
+
77
+ @keyframes rotate {
78
+ 100% {
79
+ transform: rotate(360deg);
80
+ }
81
+ }
82
+ @keyframes dash {
83
+ 0% {
84
+ stroke-dasharray: 1, 150;
85
+ stroke-dashoffset: 0;
86
+ }
87
+ 50% {
88
+ stroke-dasharray: 90, 150;
89
+ stroke-dashoffset: -35;
90
+ }
91
+ 100% {
92
+ stroke-dasharray: 90, 150;
93
+ stroke-dashoffset: -124;
94
+ }
95
+ }
96
+
97
+ /* DOTS */
98
+ .loader-dots {
99
+ display: flex;
100
+ gap: calc(var(--loader-size) * 0.2);
101
+ }
102
+ .dot {
103
+ width: calc(var(--loader-size) * 0.25);
104
+ height: calc(var(--loader-size) * 0.25);
105
+ background-color: currentColor;
106
+ border-radius: 50%;
107
+ animation: bounce 1.4s infinite ease-in-out both;
108
+ }
109
+ .dot:nth-child(1) {
110
+ animation-delay: -0.32s;
111
+ }
112
+ .dot:nth-child(2) {
113
+ animation-delay: -0.16s;
114
+ }
115
+
116
+ @keyframes bounce {
117
+ 0%,
118
+ 80%,
119
+ 100% {
120
+ transform: scale(0);
121
+ }
122
+ 40% {
123
+ transform: scale(1);
124
+ }
125
+ }
126
+
127
+ /* PULSE */
128
+ .loader-pulse {
129
+ width: var(--loader-size);
130
+ height: var(--loader-size);
131
+ background-color: currentColor;
132
+ border-radius: 50%;
133
+ opacity: 0.6;
134
+ animation: pulse 2s infinite ease-in-out;
135
+ }
136
+
137
+ @keyframes pulse {
138
+ 0% {
139
+ transform: scale(0);
140
+ opacity: 0.8;
141
+ }
142
+ 100% {
143
+ transform: scale(1);
144
+ opacity: 0;
145
+ }
146
+ }
147
+
148
+ @media (prefers-reduced-motion: reduce) {
149
+ .loader-spinner,
150
+ .loader-spinner .path,
151
+ .dot,
152
+ .loader-pulse {
153
+ animation: none !important;
154
+ }
155
+ }
156
+ </style>