grav-svelte 0.1.225 → 0.1.232

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,581 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import { expoOut, cubicOut } from "svelte/easing";
4
+
5
+ export let title: string = "";
6
+ export let message: string = "";
7
+ export let icon: "success" | "error" | "warning" | "info" | "none" = "none";
8
+ export let confirmText: string = "OK";
9
+ export let cancelText: string | null = null;
10
+ export let destructive: boolean = false;
11
+ export let onConfirm: () => void = () => {};
12
+ export let onCancel: () => void = () => {};
13
+ export let onDestroy: () => void = () => {};
14
+
15
+ let visible = false;
16
+
17
+ function close(callback: () => void) {
18
+ visible = false;
19
+ setTimeout(() => {
20
+ callback();
21
+ onDestroy();
22
+ }, 240);
23
+ }
24
+
25
+ function handleConfirm() {
26
+ close(onConfirm);
27
+ }
28
+
29
+ function handleCancel() {
30
+ close(onCancel);
31
+ }
32
+
33
+ function handleBackdropClick() {
34
+ if (cancelText !== null) handleCancel();
35
+ }
36
+
37
+ function handleKeydown(e: KeyboardEvent) {
38
+ if (e.key === "Enter") {
39
+ e.preventDefault();
40
+ handleConfirm();
41
+ } else if (e.key === "Escape") {
42
+ e.preventDefault();
43
+ if (cancelText !== null) handleCancel();
44
+ else handleConfirm();
45
+ }
46
+ }
47
+
48
+ onMount(() => {
49
+ // Disparar el {#if} en el siguiente frame garantiza que la transición de entrada se ejecute
50
+ requestAnimationFrame(() => {
51
+ visible = true;
52
+ });
53
+ window.addEventListener("keydown", handleKeydown);
54
+ return () => window.removeEventListener("keydown", handleKeydown);
55
+ });
56
+
57
+ // Spring con overshoot visible — easing custom (más fuerte que backOut)
58
+ // Se aplica al SCALE/TRANSLATE para que el sobrepaso se note
59
+ function strongSpring(t: number): number {
60
+ const c = 2.4;
61
+ return 1 + c * Math.pow(t - 1, 3) + (c - 1) * Math.pow(t - 1, 2);
62
+ }
63
+
64
+ function dialogIn(_node: Element, { duration = 550 } = {}) {
65
+ return {
66
+ duration,
67
+ css: (t: number) => {
68
+ const sp = strongSpring(t);
69
+ const scale = 0.7 + 0.3 * sp;
70
+ const translate = 30 * (1 - sp);
71
+ const opacity = Math.min(1, t * 2.5);
72
+ return `
73
+ opacity: ${opacity};
74
+ transform: scale(${scale}) translateY(${translate}px);
75
+ transform-origin: center center;
76
+ `;
77
+ },
78
+ };
79
+ }
80
+
81
+ function dialogOut(_node: Element, { duration = 220 } = {}) {
82
+ return {
83
+ duration,
84
+ easing: cubicOut,
85
+ css: (t: number) => `
86
+ opacity: ${t};
87
+ transform: scale(${0.94 + 0.06 * t}) translateY(${10 * (1 - t)}px);
88
+ transform-origin: center center;
89
+ `,
90
+ };
91
+ }
92
+
93
+ // Backdrop: fade + blur progresivo (pequeña animación de entrada)
94
+ function backdropIn(_node: Element, { duration = 400 } = {}) {
95
+ return {
96
+ duration,
97
+ easing: expoOut,
98
+ css: (t: number) => `
99
+ opacity: ${t};
100
+ backdrop-filter: blur(${8 * t}px) saturate(${100 + 40 * t}%);
101
+ -webkit-backdrop-filter: blur(${8 * t}px) saturate(${100 + 40 * t}%);
102
+ `,
103
+ };
104
+ }
105
+
106
+ function backdropOut(_node: Element, { duration = 220 } = {}) {
107
+ return {
108
+ duration,
109
+ easing: cubicOut,
110
+ css: (t: number) => `
111
+ opacity: ${t};
112
+ backdrop-filter: blur(${8 * t}px) saturate(${100 + 40 * t}%);
113
+ -webkit-backdrop-filter: blur(${8 * t}px) saturate(${100 + 40 * t}%);
114
+ `,
115
+ };
116
+ }
117
+ </script>
118
+
119
+ {#if visible}
120
+ <div class="modern-alert-root" role="alertdialog" aria-modal="true">
121
+ <!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
122
+ <div
123
+ class="modern-alert-backdrop"
124
+ in:backdropIn={{ duration: 400 }}
125
+ out:backdropOut={{ duration: 220 }}
126
+ on:click={handleBackdropClick}
127
+ ></div>
128
+
129
+ <div
130
+ class="modern-alert-dialog"
131
+ in:dialogIn={{ duration: 550 }}
132
+ out:dialogOut={{ duration: 220 }}
133
+ >
134
+ {#if icon !== "none"}
135
+ <div class="modern-alert-icon-wrap modern-alert-icon-{icon}">
136
+ <div class="modern-alert-icon-ring"></div>
137
+ <div class="modern-alert-icon-circle">
138
+ {#if icon === "success"}
139
+ <svg viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg">
140
+ <path
141
+ class="check-path"
142
+ d="M14 27 l8 8 l16 -16"
143
+ fill="none"
144
+ stroke="currentColor"
145
+ stroke-width="3.5"
146
+ stroke-linecap="round"
147
+ stroke-linejoin="round"
148
+ />
149
+ </svg>
150
+ {:else if icon === "error"}
151
+ <svg viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg">
152
+ <path
153
+ class="cross-path-1"
154
+ d="M17 17 L35 35"
155
+ fill="none"
156
+ stroke="currentColor"
157
+ stroke-width="3.5"
158
+ stroke-linecap="round"
159
+ />
160
+ <path
161
+ class="cross-path-2"
162
+ d="M35 17 L17 35"
163
+ fill="none"
164
+ stroke="currentColor"
165
+ stroke-width="3.5"
166
+ stroke-linecap="round"
167
+ />
168
+ </svg>
169
+ {:else if icon === "warning"}
170
+ <svg viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg">
171
+ <path
172
+ class="warn-stem"
173
+ d="M26 14 L26 30"
174
+ fill="none"
175
+ stroke="currentColor"
176
+ stroke-width="3.5"
177
+ stroke-linecap="round"
178
+ />
179
+ <circle
180
+ class="warn-dot"
181
+ cx="26"
182
+ cy="37"
183
+ r="2"
184
+ fill="currentColor"
185
+ />
186
+ </svg>
187
+ {:else if icon === "info"}
188
+ <svg viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg">
189
+ <path
190
+ class="info-stem"
191
+ d="M26 22 L26 38"
192
+ fill="none"
193
+ stroke="currentColor"
194
+ stroke-width="3.5"
195
+ stroke-linecap="round"
196
+ />
197
+ <circle
198
+ class="info-dot"
199
+ cx="26"
200
+ cy="15"
201
+ r="2"
202
+ fill="currentColor"
203
+ />
204
+ </svg>
205
+ {/if}
206
+ </div>
207
+ </div>
208
+ {/if}
209
+
210
+ <div class="modern-alert-content">
211
+ {#if title}
212
+ <h3 class="modern-alert-title">{title}</h3>
213
+ {/if}
214
+ {#if message}
215
+ <p class="modern-alert-message">{message}</p>
216
+ {/if}
217
+ </div>
218
+
219
+ <div class="modern-alert-actions" class:single={cancelText === null}>
220
+ {#if cancelText !== null}
221
+ <button
222
+ type="button"
223
+ class="modern-alert-btn modern-alert-btn-cancel"
224
+ on:click={handleCancel}>{cancelText}</button
225
+ >
226
+ {/if}
227
+ <button
228
+ type="button"
229
+ class="modern-alert-btn modern-alert-btn-confirm"
230
+ class:destructive={destructive || icon === "error"}
231
+ on:click={handleConfirm}>{confirmText}</button
232
+ >
233
+ </div>
234
+ </div>
235
+ </div>
236
+ {/if}
237
+
238
+ <style>
239
+ .modern-alert-root {
240
+ position: fixed;
241
+ inset: 0;
242
+ z-index: 9999;
243
+ display: flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ padding: 1rem;
247
+ font-family:
248
+ "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
249
+ "Helvetica Neue", sans-serif;
250
+ }
251
+
252
+ .modern-alert-backdrop {
253
+ position: absolute;
254
+ inset: 0;
255
+ background: rgba(15, 23, 42, 0.3);
256
+ backdrop-filter: blur(8px) saturate(140%);
257
+ -webkit-backdrop-filter: blur(8px) saturate(140%);
258
+ cursor: pointer;
259
+ }
260
+
261
+ .modern-alert-dialog {
262
+ position: relative;
263
+ width: 100%;
264
+ max-width: 420px;
265
+ background: #ffffff;
266
+ border-radius: 20px;
267
+ box-shadow:
268
+ 0 30px 60px -15px rgba(0, 0, 0, 0.3),
269
+ 0 12px 24px -10px rgba(0, 0, 0, 0.12),
270
+ 0 0 0 1px rgba(0, 0, 0, 0.04);
271
+ padding: 2rem 1.75rem 1.5rem;
272
+ display: flex;
273
+ flex-direction: column;
274
+ align-items: stretch;
275
+ gap: 1.25rem;
276
+ transform-origin: center center;
277
+ will-change: transform, opacity;
278
+ text-align: center;
279
+ }
280
+
281
+ /* Icono grande centrado en la parte superior */
282
+ .modern-alert-icon-wrap {
283
+ position: relative;
284
+ width: 80px;
285
+ height: 80px;
286
+ margin: 0 auto;
287
+ display: flex;
288
+ align-items: center;
289
+ justify-content: center;
290
+ }
291
+
292
+ .modern-alert-icon-ring {
293
+ position: absolute;
294
+ inset: 0;
295
+ border-radius: 50%;
296
+ opacity: 0.18;
297
+ animation: ring-pulse 1.6s ease-out infinite;
298
+ }
299
+
300
+ .modern-alert-icon-circle {
301
+ position: relative;
302
+ width: 80px;
303
+ height: 80px;
304
+ border-radius: 50%;
305
+ display: flex;
306
+ align-items: center;
307
+ justify-content: center;
308
+ }
309
+
310
+ .modern-alert-icon-circle svg {
311
+ width: 56px;
312
+ height: 56px;
313
+ }
314
+
315
+ /* Color schemes */
316
+ .modern-alert-icon-success .modern-alert-icon-ring {
317
+ background: radial-gradient(
318
+ circle,
319
+ rgba(34, 197, 94, 0.45) 0%,
320
+ rgba(34, 197, 94, 0) 70%
321
+ );
322
+ }
323
+ .modern-alert-icon-success .modern-alert-icon-circle {
324
+ background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%);
325
+ color: #15803d;
326
+ box-shadow: 0 8px 24px -6px rgba(34, 197, 94, 0.35);
327
+ }
328
+
329
+ .modern-alert-icon-error .modern-alert-icon-ring {
330
+ background: radial-gradient(
331
+ circle,
332
+ rgba(239, 68, 68, 0.45) 0%,
333
+ rgba(239, 68, 68, 0) 70%
334
+ );
335
+ }
336
+ .modern-alert-icon-error .modern-alert-icon-circle {
337
+ background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%);
338
+ color: #b91c1c;
339
+ box-shadow: 0 8px 24px -6px rgba(239, 68, 68, 0.35);
340
+ }
341
+
342
+ .modern-alert-icon-warning .modern-alert-icon-ring {
343
+ background: radial-gradient(
344
+ circle,
345
+ rgba(245, 158, 11, 0.45) 0%,
346
+ rgba(245, 158, 11, 0) 70%
347
+ );
348
+ }
349
+ .modern-alert-icon-warning .modern-alert-icon-circle {
350
+ background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
351
+ color: #b45309;
352
+ box-shadow: 0 8px 24px -6px rgba(245, 158, 11, 0.35);
353
+ }
354
+
355
+ .modern-alert-icon-info .modern-alert-icon-ring {
356
+ background: radial-gradient(
357
+ circle,
358
+ rgba(59, 130, 246, 0.45) 0%,
359
+ rgba(59, 130, 246, 0) 70%
360
+ );
361
+ }
362
+ .modern-alert-icon-info .modern-alert-icon-circle {
363
+ background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
364
+ color: #1d4ed8;
365
+ box-shadow: 0 8px 24px -6px rgba(59, 130, 246, 0.35);
366
+ }
367
+
368
+ /* Animación de dibujo de los iconos SVG */
369
+ .check-path {
370
+ stroke-dasharray: 36;
371
+ stroke-dashoffset: 36;
372
+ animation: draw-stroke 0.5s 0.15s cubic-bezier(0.65, 0, 0.45, 1) forwards;
373
+ }
374
+ .cross-path-1,
375
+ .cross-path-2 {
376
+ stroke-dasharray: 26;
377
+ stroke-dashoffset: 26;
378
+ animation: draw-stroke 0.32s cubic-bezier(0.65, 0, 0.45, 1) forwards;
379
+ }
380
+ .cross-path-1 {
381
+ animation-delay: 0.15s;
382
+ }
383
+ .cross-path-2 {
384
+ animation-delay: 0.3s;
385
+ }
386
+ .warn-stem,
387
+ .info-stem {
388
+ stroke-dasharray: 16;
389
+ stroke-dashoffset: 16;
390
+ animation: draw-stroke 0.32s 0.15s cubic-bezier(0.65, 0, 0.45, 1) forwards;
391
+ }
392
+ .warn-dot,
393
+ .info-dot {
394
+ transform: scale(0);
395
+ transform-origin: center;
396
+ transform-box: fill-box;
397
+ animation: pop-dot 0.25s 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
398
+ }
399
+
400
+ @keyframes draw-stroke {
401
+ to {
402
+ stroke-dashoffset: 0;
403
+ }
404
+ }
405
+ @keyframes pop-dot {
406
+ to {
407
+ transform: scale(1);
408
+ }
409
+ }
410
+ @keyframes ring-pulse {
411
+ 0% {
412
+ transform: scale(0.85);
413
+ opacity: 0.25;
414
+ }
415
+ 50% {
416
+ transform: scale(1.15);
417
+ opacity: 0.05;
418
+ }
419
+ 100% {
420
+ transform: scale(0.85);
421
+ opacity: 0.25;
422
+ }
423
+ }
424
+
425
+ /* Contenido */
426
+ .modern-alert-content {
427
+ display: flex;
428
+ flex-direction: column;
429
+ gap: 0.5rem;
430
+ padding: 0 0.5rem;
431
+ }
432
+
433
+ .modern-alert-title {
434
+ margin: 0;
435
+ padding: 0;
436
+ font-size: 1.375rem;
437
+ font-weight: 700;
438
+ line-height: 1.3;
439
+ color: #0f172a;
440
+ letter-spacing: -0.02em;
441
+ }
442
+
443
+ .modern-alert-message {
444
+ margin: 0;
445
+ padding: 0;
446
+ font-size: 0.9375rem;
447
+ line-height: 1.55;
448
+ color: #64748b;
449
+ }
450
+
451
+ /* Acciones — botones centrados, mismo ancho */
452
+ .modern-alert-actions {
453
+ display: flex;
454
+ justify-content: center;
455
+ gap: 0.625rem;
456
+ margin-top: 0.5rem;
457
+ }
458
+
459
+ .modern-alert-actions.single {
460
+ justify-content: stretch;
461
+ }
462
+
463
+ .modern-alert-actions.single .modern-alert-btn {
464
+ flex: 1;
465
+ }
466
+
467
+ .modern-alert-actions:not(.single) .modern-alert-btn {
468
+ flex: 1;
469
+ }
470
+
471
+ .modern-alert-btn {
472
+ height: 44px;
473
+ padding: 0 1.25rem;
474
+ border-radius: 10px;
475
+ font-size: 0.9375rem;
476
+ font-weight: 600;
477
+ cursor: pointer;
478
+ transition:
479
+ background-color 0.15s ease,
480
+ color 0.15s ease,
481
+ transform 0.08s ease,
482
+ box-shadow 0.15s ease;
483
+ border: 1px solid transparent;
484
+ font-family: inherit;
485
+ letter-spacing: -0.005em;
486
+ }
487
+
488
+ .modern-alert-btn:active {
489
+ transform: scale(0.97);
490
+ }
491
+
492
+ .modern-alert-btn-cancel {
493
+ background: #f1f5f9;
494
+ color: #334155;
495
+ }
496
+ .modern-alert-btn-cancel:hover {
497
+ background: #e2e8f0;
498
+ }
499
+
500
+ .modern-alert-btn-confirm {
501
+ background: #0f172a;
502
+ color: #ffffff;
503
+ box-shadow: 0 4px 12px -2px rgba(15, 23, 42, 0.3);
504
+ }
505
+ .modern-alert-btn-confirm:hover {
506
+ background: #1e293b;
507
+ box-shadow: 0 6px 16px -2px rgba(15, 23, 42, 0.4);
508
+ }
509
+
510
+ .modern-alert-btn-confirm.destructive {
511
+ background: linear-gradient(180deg, #ef4444 0%, #dc2626 100%);
512
+ box-shadow: 0 4px 12px -2px rgba(220, 38, 38, 0.4);
513
+ }
514
+ .modern-alert-btn-confirm.destructive:hover {
515
+ background: linear-gradient(180deg, #dc2626 0%, #b91c1c 100%);
516
+ box-shadow: 0 6px 16px -2px rgba(220, 38, 38, 0.5);
517
+ }
518
+
519
+ @media (prefers-color-scheme: dark) {
520
+ .modern-alert-dialog {
521
+ background: #18181b;
522
+ box-shadow:
523
+ 0 30px 60px -15px rgba(0, 0, 0, 0.7),
524
+ 0 12px 24px -10px rgba(0, 0, 0, 0.5),
525
+ 0 0 0 1px rgba(255, 255, 255, 0.06);
526
+ }
527
+ .modern-alert-title {
528
+ color: #fafafa;
529
+ }
530
+ .modern-alert-message {
531
+ color: #a1a1aa;
532
+ }
533
+ .modern-alert-icon-success .modern-alert-icon-circle {
534
+ background: linear-gradient(
535
+ 135deg,
536
+ rgba(34, 197, 94, 0.2) 0%,
537
+ rgba(34, 197, 94, 0.1) 100%
538
+ );
539
+ color: #4ade80;
540
+ }
541
+ .modern-alert-icon-error .modern-alert-icon-circle {
542
+ background: linear-gradient(
543
+ 135deg,
544
+ rgba(239, 68, 68, 0.2) 0%,
545
+ rgba(239, 68, 68, 0.1) 100%
546
+ );
547
+ color: #f87171;
548
+ }
549
+ .modern-alert-icon-warning .modern-alert-icon-circle {
550
+ background: linear-gradient(
551
+ 135deg,
552
+ rgba(245, 158, 11, 0.2) 0%,
553
+ rgba(245, 158, 11, 0.1) 100%
554
+ );
555
+ color: #fbbf24;
556
+ }
557
+ .modern-alert-icon-info .modern-alert-icon-circle {
558
+ background: linear-gradient(
559
+ 135deg,
560
+ rgba(59, 130, 246, 0.2) 0%,
561
+ rgba(59, 130, 246, 0.1) 100%
562
+ );
563
+ color: #60a5fa;
564
+ }
565
+ .modern-alert-btn-cancel {
566
+ background: #27272a;
567
+ color: #e4e4e7;
568
+ }
569
+ .modern-alert-btn-cancel:hover {
570
+ background: #3f3f46;
571
+ }
572
+ .modern-alert-btn-confirm {
573
+ background: #fafafa;
574
+ color: #0f172a;
575
+ box-shadow: 0 4px 12px -2px rgba(255, 255, 255, 0.15);
576
+ }
577
+ .modern-alert-btn-confirm:hover {
578
+ background: #e4e4e7;
579
+ }
580
+ }
581
+ </style>
@@ -0,0 +1,24 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ title?: string;
5
+ message?: string;
6
+ icon?: "success" | "error" | "warning" | "info" | "none";
7
+ confirmText?: string;
8
+ cancelText?: string | null;
9
+ destructive?: boolean;
10
+ onConfirm?: () => void;
11
+ onCancel?: () => void;
12
+ onDestroy?: () => void;
13
+ };
14
+ events: {
15
+ [evt: string]: CustomEvent<any>;
16
+ };
17
+ slots: {};
18
+ };
19
+ export type AppleAlertProps = typeof __propDef.props;
20
+ export type AppleAlertEvents = typeof __propDef.events;
21
+ export type AppleAlertSlots = typeof __propDef.slots;
22
+ export default class AppleAlert extends SvelteComponentTyped<AppleAlertProps, AppleAlertEvents, AppleAlertSlots> {
23
+ }
24
+ export {};
@@ -0,0 +1,283 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import { fly, fade } from "svelte/transition";
4
+ import { expoOut, cubicOut } from "svelte/easing";
5
+
6
+ export let title: string = "";
7
+ export let message: string = "";
8
+ export let icon: "success" | "error" | "info" | "warning" = "success";
9
+ export let duration: number = 3500;
10
+ export let onDestroy: () => void = () => {};
11
+
12
+ let visible = false;
13
+ let hovered = false;
14
+ let timer: ReturnType<typeof setTimeout> | null = null;
15
+
16
+ function close() {
17
+ visible = false;
18
+ setTimeout(() => onDestroy(), 250);
19
+ }
20
+
21
+ function startTimer() {
22
+ if (timer) clearTimeout(timer);
23
+ timer = setTimeout(() => {
24
+ if (!hovered) close();
25
+ }, duration);
26
+ }
27
+
28
+ onMount(() => {
29
+ requestAnimationFrame(() => {
30
+ visible = true;
31
+ });
32
+ startTimer();
33
+ return () => {
34
+ if (timer) clearTimeout(timer);
35
+ };
36
+ });
37
+
38
+ function handleMouseEnter() {
39
+ hovered = true;
40
+ if (timer) clearTimeout(timer);
41
+ }
42
+
43
+ function handleMouseLeave() {
44
+ hovered = false;
45
+ startTimer();
46
+ }
47
+ </script>
48
+
49
+ {#if visible}
50
+ <div class="modern-toast-root">
51
+ <div
52
+ class="modern-toast-card"
53
+ role="status"
54
+ aria-live="polite"
55
+ in:fly={{ y: 20, x: 0, duration: 350, easing: expoOut }}
56
+ out:fade={{ duration: 200, easing: cubicOut }}
57
+ on:mouseenter={handleMouseEnter}
58
+ on:mouseleave={handleMouseLeave}
59
+ >
60
+ <div class="modern-toast-icon modern-toast-icon-{icon}">
61
+ {#if icon === "success"}
62
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
63
+ <path
64
+ d="M5 12.5l5 5 9-11"
65
+ stroke-width="2.5"
66
+ stroke-linecap="round"
67
+ stroke-linejoin="round"
68
+ />
69
+ </svg>
70
+ {:else if icon === "error"}
71
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
72
+ <path
73
+ d="M6 6l12 12M18 6L6 18"
74
+ stroke-width="2.5"
75
+ stroke-linecap="round"
76
+ />
77
+ </svg>
78
+ {:else if icon === "warning"}
79
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
80
+ <path
81
+ d="M12 8v5M12 16.5v.5"
82
+ stroke-width="2.5"
83
+ stroke-linecap="round"
84
+ />
85
+ <path
86
+ d="M10.3 3.86l-8.07 14a2 2 0 001.73 3h16.14a2 2 0 001.73-3l-8.07-14a2 2 0 00-3.46 0z"
87
+ stroke-width="2"
88
+ stroke-linejoin="round"
89
+ />
90
+ </svg>
91
+ {:else}
92
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
93
+ <circle cx="12" cy="12" r="9" stroke-width="2" />
94
+ <path
95
+ d="M12 11v5M12 7.5v.5"
96
+ stroke-width="2.5"
97
+ stroke-linecap="round"
98
+ />
99
+ </svg>
100
+ {/if}
101
+ </div>
102
+
103
+ <div class="modern-toast-text">
104
+ <div class="modern-toast-title">{title || message}</div>
105
+ {#if title && message}
106
+ <div class="modern-toast-description">{message}</div>
107
+ {/if}
108
+ </div>
109
+
110
+ <button
111
+ type="button"
112
+ class="modern-toast-close"
113
+ aria-label="Cerrar"
114
+ on:click={close}
115
+ >
116
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
117
+ <path
118
+ d="M6 6l12 12M18 6L6 18"
119
+ stroke-width="2"
120
+ stroke-linecap="round"
121
+ />
122
+ </svg>
123
+ </button>
124
+ </div>
125
+ </div>
126
+ {/if}
127
+
128
+ <style>
129
+ .modern-toast-root {
130
+ position: fixed;
131
+ bottom: 1.25rem;
132
+ right: 1.25rem;
133
+ z-index: 10000;
134
+ display: flex;
135
+ justify-content: flex-end;
136
+ pointer-events: none;
137
+ max-width: calc(100vw - 2.5rem);
138
+ font-family:
139
+ "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
140
+ "Helvetica Neue", sans-serif;
141
+ }
142
+
143
+ .modern-toast-card {
144
+ pointer-events: auto;
145
+ display: flex;
146
+ align-items: flex-start;
147
+ gap: 0.75rem;
148
+ padding: 0.875rem 0.875rem 0.875rem 1rem;
149
+ background: #ffffff;
150
+ border-radius: 12px;
151
+ box-shadow:
152
+ 0 10px 25px -5px rgba(0, 0, 0, 0.12),
153
+ 0 4px 10px -3px rgba(0, 0, 0, 0.06),
154
+ 0 0 0 1px rgba(0, 0, 0, 0.05);
155
+ min-width: 320px;
156
+ max-width: 400px;
157
+ will-change: transform, opacity;
158
+ }
159
+
160
+ .modern-toast-icon {
161
+ width: 22px;
162
+ height: 22px;
163
+ flex-shrink: 0;
164
+ margin-top: 1px;
165
+ }
166
+
167
+ .modern-toast-icon svg {
168
+ width: 100%;
169
+ height: 100%;
170
+ }
171
+
172
+ .modern-toast-icon-success {
173
+ color: #16a34a;
174
+ }
175
+ .modern-toast-icon-error {
176
+ color: #dc2626;
177
+ }
178
+ .modern-toast-icon-warning {
179
+ color: #d97706;
180
+ }
181
+ .modern-toast-icon-info {
182
+ color: #2563eb;
183
+ }
184
+
185
+ .modern-toast-text {
186
+ flex: 1;
187
+ min-width: 0;
188
+ display: flex;
189
+ flex-direction: column;
190
+ gap: 2px;
191
+ }
192
+
193
+ .modern-toast-title {
194
+ font-size: 0.875rem;
195
+ font-weight: 600;
196
+ color: #0f172a;
197
+ line-height: 1.4;
198
+ letter-spacing: -0.005em;
199
+ }
200
+
201
+ .modern-toast-description {
202
+ font-size: 0.8125rem;
203
+ color: #64748b;
204
+ line-height: 1.45;
205
+ }
206
+
207
+ .modern-toast-close {
208
+ background: transparent;
209
+ border: none;
210
+ cursor: pointer;
211
+ padding: 2px;
212
+ color: #94a3b8;
213
+ border-radius: 6px;
214
+ transition: all 0.15s ease;
215
+ flex-shrink: 0;
216
+ margin-top: -2px;
217
+ margin-right: -4px;
218
+ width: 22px;
219
+ height: 22px;
220
+ display: flex;
221
+ align-items: center;
222
+ justify-content: center;
223
+ }
224
+
225
+ .modern-toast-close svg {
226
+ width: 14px;
227
+ height: 14px;
228
+ }
229
+
230
+ .modern-toast-close:hover {
231
+ color: #475569;
232
+ background: #f1f5f9;
233
+ }
234
+
235
+ @media (max-width: 640px) {
236
+ .modern-toast-root {
237
+ left: 1rem;
238
+ right: 1rem;
239
+ bottom: 1rem;
240
+ justify-content: stretch;
241
+ }
242
+ .modern-toast-card {
243
+ min-width: 0;
244
+ max-width: 100%;
245
+ width: 100%;
246
+ }
247
+ }
248
+
249
+ @media (prefers-color-scheme: dark) {
250
+ .modern-toast-card {
251
+ background: #18181b;
252
+ box-shadow:
253
+ 0 10px 25px -5px rgba(0, 0, 0, 0.5),
254
+ 0 4px 10px -3px rgba(0, 0, 0, 0.3),
255
+ 0 0 0 1px rgba(255, 255, 255, 0.06);
256
+ }
257
+ .modern-toast-title {
258
+ color: #fafafa;
259
+ }
260
+ .modern-toast-description {
261
+ color: #a1a1aa;
262
+ }
263
+ .modern-toast-close {
264
+ color: #71717a;
265
+ }
266
+ .modern-toast-close:hover {
267
+ color: #d4d4d8;
268
+ background: #27272a;
269
+ }
270
+ .modern-toast-icon-success {
271
+ color: #4ade80;
272
+ }
273
+ .modern-toast-icon-error {
274
+ color: #f87171;
275
+ }
276
+ .modern-toast-icon-warning {
277
+ color: #fbbf24;
278
+ }
279
+ .modern-toast-icon-info {
280
+ color: #60a5fa;
281
+ }
282
+ }
283
+ </style>
@@ -0,0 +1,20 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ title?: string;
5
+ message?: string;
6
+ icon?: "success" | "error" | "info" | "warning";
7
+ duration?: number;
8
+ onDestroy?: () => void;
9
+ };
10
+ events: {
11
+ [evt: string]: CustomEvent<any>;
12
+ };
13
+ slots: {};
14
+ };
15
+ export type AppleToastProps = typeof __propDef.props;
16
+ export type AppleToastEvents = typeof __propDef.events;
17
+ export type AppleToastSlots = typeof __propDef.slots;
18
+ export default class AppleToast extends SvelteComponentTyped<AppleToastProps, AppleToastEvents, AppleToastSlots> {
19
+ }
20
+ export {};
@@ -1,4 +1,3 @@
1
- import './alertStyles.css';
2
- export declare function Exito_Alert(titulo?: string): void;
1
+ export declare function Exito_Alert(titulo?: string, mensaje?: string): void;
3
2
  export declare function Error_Alert(titulo?: string): void;
4
- export declare function Confirmacion_Alert(titulo: string | undefined, texto: string | undefined, callback: () => void): Promise<void>;
3
+ export declare function Confirmacion_Alert(titulo: string | undefined, texto: string | undefined, callback: () => void): void;
@@ -1,50 +1,65 @@
1
- import Swal from 'sweetalert2';
2
- import './alertStyles.css';
3
- export function Exito_Alert(titulo = 'Se guardo correctamente') {
4
- Swal.fire({
5
- icon: 'success',
1
+ import * as svelte from 'svelte';
2
+ import AppleAlert from './AppleAlert.svelte';
3
+ import AppleToast from './AppleToast.svelte';
4
+ const svelteAny = svelte;
5
+ const isSvelte5 = typeof svelteAny.mount === 'function';
6
+ function mountComponent(Component, props) {
7
+ if (typeof document === 'undefined')
8
+ return;
9
+ const target = document.createElement('div');
10
+ document.body.appendChild(target);
11
+ let instance;
12
+ let destroyed = false;
13
+ const cleanup = () => {
14
+ if (destroyed)
15
+ return;
16
+ destroyed = true;
17
+ try {
18
+ if (isSvelte5 && svelteAny.unmount) {
19
+ svelteAny.unmount(instance);
20
+ }
21
+ else if (instance && typeof instance.$destroy === 'function') {
22
+ instance.$destroy();
23
+ }
24
+ }
25
+ catch (err) {
26
+ console.error('Error destroying alert component:', err);
27
+ }
28
+ if (target.parentNode)
29
+ target.parentNode.removeChild(target);
30
+ };
31
+ const finalProps = { ...props, onDestroy: cleanup };
32
+ if (isSvelte5 && svelteAny.mount) {
33
+ instance = svelteAny.mount(Component, { target, props: finalProps, intro: true });
34
+ }
35
+ else {
36
+ const Ctor = Component;
37
+ instance = new Ctor({ target, props: finalProps, intro: true });
38
+ }
39
+ }
40
+ export function Exito_Alert(titulo = 'Se guardó correctamente', mensaje = '') {
41
+ mountComponent(AppleToast, {
6
42
  title: titulo,
7
- showConfirmButton: false,
8
- timer: 1500
43
+ message: mensaje,
44
+ icon: 'success',
45
+ duration: 3500,
9
46
  });
10
47
  }
11
48
  export function Error_Alert(titulo = 'Algo salió mal') {
12
- Swal.fire({
13
- icon: 'error',
49
+ mountComponent(AppleAlert, {
14
50
  title: titulo,
15
- confirmButtonColor: '#10b981',
16
- cancelButtonColor: '#EF4444',
17
- confirmButtonText: 'OK',
18
- focusConfirm: false,
19
- focusCancel: false,
20
- buttonsStyling: false,
21
- customClass: {
22
- denyButton: 'hidden',
23
- confirmButton: 'alert-error-button'
24
- }
51
+ icon: 'error',
52
+ confirmText: 'OK',
53
+ cancelText: null,
25
54
  });
26
55
  }
27
- export function Confirmacion_Alert(titulo = 'Confirmación', texto = 'Desea guardar los cambios?', callback) {
28
- return Swal.fire({
56
+ export function Confirmacion_Alert(titulo = 'Confirmación', texto = '¿Desea guardar los cambios?', callback) {
57
+ mountComponent(AppleAlert, {
29
58
  title: titulo,
30
- text: texto,
59
+ message: texto,
31
60
  icon: 'warning',
32
- showCancelButton: true,
33
- confirmButtonColor: '#10b981',
34
- cancelButtonColor: '#EF4444',
35
- confirmButtonText: 'Sí',
36
- cancelButtonText: 'No',
37
- focusConfirm: false,
38
- focusCancel: false,
39
- buttonsStyling: false,
40
- customClass: {
41
- denyButton: 'hidden',
42
- confirmButton: 'alert-confirm-button',
43
- cancelButton: 'alert-cancel-button'
44
- }
45
- }).then((result) => {
46
- if (result.isConfirmed) {
47
- callback();
48
- }
61
+ confirmText: 'Sí',
62
+ cancelText: 'No',
63
+ onConfirm: callback,
49
64
  });
50
65
  }
@@ -202,11 +202,18 @@
202
202
  }
203
203
 
204
204
  .modal-backdrop {
205
- opacity: 0.25;
206
205
  position: fixed;
207
206
  inset: 0;
208
207
  z-index: 40;
209
- background-color: black;
208
+ background-color: rgba(0, 0, 0, 0.25);
209
+ backdrop-filter: blur(12px) saturate(180%);
210
+ -webkit-backdrop-filter: blur(12px) saturate(180%);
211
+ }
212
+
213
+ .modal-content {
214
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.35),
215
+ 0 0 0 0.5px rgba(255, 255, 255, 0.1);
216
+ will-change: transform, opacity;
210
217
  }
211
218
 
212
219
  /* Modal-specific CSS variables for inputs */
@@ -1,5 +1,27 @@
1
1
  <script lang="ts">
2
2
  import "./Grav_Modal.css";
3
+ import { fade } from "svelte/transition";
4
+ import { expoOut, cubicOut } from "svelte/easing";
5
+
6
+ // Apple-like modal entrance: scale + slight translateY + opacity
7
+ function appleModal(
8
+ _node: Element,
9
+ { duration = 400, reverse = false } = {},
10
+ ) {
11
+ return {
12
+ duration,
13
+ easing: reverse ? cubicOut : expoOut,
14
+ css: (t: number) => {
15
+ const scale = 0.92 + 0.08 * t;
16
+ const translate = 16 * (1 - t);
17
+ return `
18
+ opacity: ${t};
19
+ transform: scale(${scale}) translateY(${translate}px);
20
+ transform-origin: center center;
21
+ `;
22
+ },
23
+ };
24
+ }
3
25
 
4
26
  // Define size type
5
27
  type ModalSize = "lg" | "md" | "sm" | "xs";
@@ -80,7 +102,11 @@
80
102
  tabindex="0"
81
103
  >
82
104
  <!--content-->
83
- <div class="modal-content {size}">
105
+ <div
106
+ class="modal-content {size}"
107
+ in:appleModal={{ duration: 400 }}
108
+ out:appleModal={{ duration: 250, reverse: true }}
109
+ >
84
110
  <!-- Encabezado Modal -->
85
111
  <div class="modal-header">
86
112
  <h3 class="modal-title">
@@ -163,4 +189,8 @@
163
189
  {/if}
164
190
  </div>
165
191
  </div>
166
- <div class="modal-backdrop" />
192
+ <div
193
+ class="modal-backdrop"
194
+ in:fade={{ duration: 350, easing: expoOut }}
195
+ out:fade={{ duration: 200, easing: cubicOut }}
196
+ />
@@ -4,23 +4,26 @@
4
4
 
5
5
  <div>
6
6
  {#each $modals as modal, index (modal.id + "-" + index)}
7
- <div class="modal-overlay">
7
+ <div class="modal-container-overlay">
8
8
  <svelte:component this={modal.component} {...modal.props} />
9
9
  </div>
10
10
  {/each}
11
11
  </div>
12
12
 
13
13
  <style>
14
- .modal-overlay {
14
+ .modal-container-overlay {
15
15
  position: fixed;
16
16
  top: 0;
17
17
  left: 0;
18
18
  right: 0;
19
19
  bottom: 0;
20
- background: rgba(0, 0, 0, 0.5);
21
20
  display: flex;
22
21
  justify-content: center;
23
22
  align-items: center;
24
23
  z-index: 500;
24
+ pointer-events: none;
25
+ }
26
+ .modal-container-overlay > :global(*) {
27
+ pointer-events: auto;
25
28
  }
26
29
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grav-svelte",
3
- "version": "0.1.225",
3
+ "version": "0.1.232",
4
4
  "description": "A collection of Svelte components",
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -52,7 +52,6 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "grav-svelte": "^0.1.211",
55
- "sweetalert2": "^11.22.0",
56
55
  "xlsx": "^0.18.5"
57
56
  },
58
57
  "peerDependencies": {
@@ -1,60 +0,0 @@
1
- .alert-confirm-button {
2
- display: inline-flex;
3
- align-items: center;
4
- justify-content: center;
5
- background-color: #10b981;
6
- color: white;
7
- font-weight: bold;
8
- text-transform: uppercase;
9
- font-size: 0.875rem;
10
- padding: 0.75rem 1.5rem;
11
- border: none;
12
- border-radius: 0.25rem;
13
- cursor: pointer;
14
- transition: all 150ms ease-in-out;
15
- }
16
-
17
- .alert-confirm-button:hover {
18
- }
19
-
20
- .alert-confirm-button:focus {
21
- outline: none;
22
- }
23
-
24
- .alert-cancel-button {
25
- display: inline-flex;
26
- align-items: center;
27
- justify-content: center;
28
- color: #EF4444;
29
- background: transparent;
30
- font-weight: bold;
31
- text-transform: uppercase;
32
- padding: 0.5rem 1.5rem;
33
- font-size: 0.875rem;
34
- border: none;
35
- cursor: pointer;
36
- transition: all 150ms ease-in-out;
37
- }
38
-
39
- .alert-cancel-button:focus {
40
- outline: none;
41
- }
42
-
43
- .alert-error-button {
44
- display: inline-flex;
45
- align-items: center;
46
- justify-content: center;
47
- color: #EF4444;
48
- background: transparent;
49
- font-weight: bold;
50
- text-transform: uppercase;
51
- cursor: pointer;
52
- padding: 0.5rem 1.5rem;
53
- font-size: 1rem;
54
- border: none;
55
- transition: all 150ms ease-in-out;
56
- }
57
-
58
- .alert-error-button:focus {
59
- outline: none;
60
- }