zabi-components 5.0.11 → 5.0.13

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.
@@ -54,41 +54,45 @@
54
54
 
55
55
  {#if isOpen}
56
56
  <div
57
- class="fixed inset-0 bg-background/50 dark:bg-background/80 flex items-end md:items-center justify-center p-0 md:p-4 z-50"
57
+ class="fixed inset-0 bg-black/50 dark:bg-black/70 flex items-end md:items-center justify-center p-0 md:p-4 z-modal"
58
58
  onclick={handleBackdropClick}
59
59
  onkeydown={handleKeydown}
60
60
  role="dialog"
61
61
  aria-modal="true"
62
+ aria-labelledby={title ? "modal-title" : undefined}
62
63
  tabindex="-1"
63
64
  >
64
65
  <div
65
- class="bg-surface-elevated rounded-t-2xl md:rounded-lg shadow-xl min-w-[320px] {sizeClasses} max-h-[90vh] overflow-y-auto animate-[slideUp_0.3s_ease-out] md:animate-none"
66
+ class="bg-surface-elevated rounded-t-3xl md:rounded-3xl shadow-xl min-w-[320px] {sizeClasses} max-h-[90vh] overflow-y-auto animate-[slideUp_0.3s_ease-out] md:animate-none flex flex-col"
66
67
  >
67
68
  <!-- Header -->
68
- <div
69
- class="flex items-center justify-between p-6 border-b border-(--color-border)"
70
- >
71
- <h2 class="text-xl font-semibold text-headline">{title}</h2>
72
- <button
73
- type="button"
74
- onclick={closeModal}
75
- class="text-description hover:text-headline text-2xl cursor-pointer transition-colors"
76
- aria-label="Close"
77
- >
78
- ×
79
- </button>
80
- </div>
69
+ {#if title}
70
+ <div class="flex items-center justify-between px-6 pt-6 pb-4">
71
+ <h2
72
+ id="modal-title"
73
+ class="text-2xl font-normal leading-8 text-headline tracking-normal"
74
+ >
75
+ {title}
76
+ </h2>
77
+ <button
78
+ type="button"
79
+ onclick={closeModal}
80
+ class="text-description hover:text-headline text-2xl cursor-pointer transition-colors w-8 h-8 flex items-center justify-center rounded-full hover:bg-surface-hover"
81
+ aria-label="Close"
82
+ >
83
+ ×
84
+ </button>
85
+ </div>
86
+ {/if}
81
87
 
82
88
  <!-- Content -->
83
- <div class="p-6">
89
+ <div class="px-6 pb-6 flex-1">
84
90
  {@render children?.()}
85
91
  </div>
86
92
 
87
93
  <!-- Footer -->
88
94
  {#if footer}
89
- <div
90
- class="flex justify-end gap-3 p-6 border-t border-(--color-border)"
91
- >
95
+ <div class="flex justify-end gap-3 px-6 pb-6 pt-4">
92
96
  {@render footer?.()}
93
97
  </div>
94
98
  {/if}
@@ -13,61 +13,67 @@
13
13
  ...restProps
14
14
  } = $props<Props & { children?: any }>();
15
15
 
16
- function closeSlideUp() {
16
+ function closeSlideUp(event?: Event) {
17
17
  isOpen = false;
18
+ if (onclick && event) {
19
+ (onclick as (event: Event) => void)(event);
20
+ }
18
21
  }
19
22
 
20
23
  function handleBackdropClick(event: Event) {
21
24
  if (event.target === event.currentTarget) {
22
- closeSlideUp();
25
+ closeSlideUp(event);
23
26
  }
24
27
  }
25
28
 
26
29
  function handleKeydown(event: Event) {
27
30
  const keyboardEvent = event as KeyboardEvent;
28
31
  if (keyboardEvent.key === "Escape") {
29
- closeSlideUp();
32
+ closeSlideUp(event);
33
+ }
34
+ if (onkeydown) {
35
+ (onkeydown as (event: Event) => void)(event);
30
36
  }
31
37
  }
32
38
  </script>
33
39
 
34
40
  {#if isOpen}
35
- <!-- Backdrop -->
36
41
  <div
37
- class="fixed inset-0 bg-black/50 z-50"
42
+ class="fixed inset-0 bg-black/50 dark:bg-black/70 z-modal"
38
43
  onclick={handleBackdropClick}
39
44
  onkeydown={handleKeydown}
40
45
  role="dialog"
41
46
  aria-modal="true"
47
+ aria-labelledby={title ? "slideup-title" : undefined}
42
48
  tabindex="-1"
43
- ></div>
44
-
45
- <!-- SlideUp Content -->
46
- <div
47
- class="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 rounded-t-xl shadow-xl z-50 max-h-[80vh] overflow-y-auto"
48
- role="dialog"
49
- aria-modal="true"
50
49
  >
51
- <!-- Header -->
52
- {#if title}
53
- <div
54
- class="flex items-center justify-between p-4 border-b border-gray-200"
55
- >
56
- <h3 class="text-lg font-semibold text-gray-900">{title}</h3>
57
- <button
58
- type="button"
59
- class="text-gray-400 hover:text-gray-600 text-2xl"
60
- onclick={closeSlideUp}
61
- aria-label="Close"
62
- >
63
- ×
64
- </button>
65
- </div>
66
- {/if}
50
+ <div
51
+ class="fixed bottom-0 left-0 right-0 bg-surface-elevated rounded-t-3xl shadow-xl z-modal max-h-[90vh] overflow-y-auto animate-[slideUp_0.3s_ease-out] flex flex-col"
52
+ >
53
+ <!-- Header -->
54
+ {#if title}
55
+ <div class="flex items-center justify-between px-6 pt-6 pb-4">
56
+ <h2
57
+ id="slideup-title"
58
+ class="text-2xl font-normal leading-8 text-headline tracking-normal"
59
+ >
60
+ {title}
61
+ </h2>
62
+ <button
63
+ type="button"
64
+ onclick={closeSlideUp}
65
+ class="text-description hover:text-headline text-2xl cursor-pointer transition-colors w-8 h-8 flex items-center justify-center rounded-full hover:bg-surface-hover"
66
+ aria-label="Close"
67
+ >
68
+ ×
69
+ </button>
70
+ </div>
71
+ {/if}
67
72
 
68
- <!-- Content -->
69
- <div class="p-4">
70
- {@render children?.()}
73
+ <!-- Content -->
74
+ <div class="px-6 pb-6 flex-1">
75
+ {@render children?.()}
76
+ </div>
71
77
  </div>
72
78
  </div>
73
79
  {/if}
@@ -1,10 +1,16 @@
1
1
  <script lang="ts">
2
+ import type { Component } from "svelte";
3
+
4
+ interface NavigationItem {
5
+ label: string;
6
+ href: string;
7
+ icon?: Component<any>; // Outline icon component
8
+ iconFilled?: Component<any>; // Filled icon component (for active state)
9
+ }
10
+
2
11
  interface Props {
3
12
  variant?: "header" | "sidebar";
4
- items?: Array<{
5
- label: string;
6
- href: string;
7
- }>;
13
+ items?: NavigationItem[];
8
14
  currentPath?: string;
9
15
  onclick?: (event: Event) => void;
10
16
  className?: string;
@@ -19,41 +25,97 @@
19
25
  ...restProps
20
26
  }: Props & { children?: any } = $props();
21
27
 
22
- function handleClick(item: any, event: MouseEvent) {
28
+ function handleClick(item: NavigationItem, event: MouseEvent) {
23
29
  event.preventDefault();
24
- // Navigation is now handled by the parent component
25
- // through event forwarding
30
+ if (onclick) {
31
+ onclick(event as any);
32
+ }
26
33
  }
27
34
 
28
35
  // Derived class for ul container based on variant
29
36
  const ulClasses = $derived(() => {
30
37
  return variant === "sidebar"
31
- ? "flex flex-col space-y-2"
32
- : "flex space-x-6";
38
+ ? "flex flex-col gap-1"
39
+ : "flex items-center justify-between gap-1";
33
40
  });
34
41
 
35
- // Function to get link classes based on active state
36
- function getLinkClasses(itemHref: string): string {
42
+ // Function to get nav item classes based on active state
43
+ function getNavItemClasses(itemHref: string): string {
44
+ const baseClasses = "flex flex-col gap-1 grow h-full items-center justify-center min-h-0 min-w-0 relative shrink-0 cursor-pointer";
45
+
46
+ return baseClasses;
47
+ }
48
+
49
+ // Function to get icon container classes based on active state
50
+ function getIconContainerClasses(itemHref: string): string {
51
+ const isActive = currentPath === itemHref;
52
+ const baseClasses = "flex flex-col items-center justify-center overflow-clip relative rounded-[20px] shrink-0 transition-colors duration-200";
53
+
54
+ if (isActive) {
55
+ return `${baseClasses} bg-[var(--color-action-primary-subtle-hover)]`;
56
+ }
57
+ return `${baseClasses}`;
58
+ }
59
+
60
+ // Function to get state layer classes
61
+ function getStateLayerClasses(): string {
62
+ return "box-border flex gap-1 h-10 items-center px-4 py-2 relative shrink-0";
63
+ }
64
+
65
+ // Function to get label classes based on active state
66
+ function getLabelClasses(itemHref: string): string {
67
+ const isActive = currentPath === itemHref;
68
+ const baseClasses = "font-medium leading-4 relative shrink-0 text-center text-nowrap tracking-[0.5px] whitespace-pre text-xs";
69
+
70
+ if (isActive) {
71
+ return `${baseClasses} text-link`;
72
+ }
73
+ return `${baseClasses} text-description`;
74
+ }
75
+
76
+ // Function to get icon classes based on active state (matches text color)
77
+ function getIconClasses(itemHref: string): string {
37
78
  const isActive = currentPath === itemHref;
38
- const baseClasses = "px-3 py-2 text-sm font-medium rounded-md";
39
79
 
40
80
  if (isActive) {
41
- return `${baseClasses} text-body font-semibold`;
81
+ return "text-link";
42
82
  }
43
- return `${baseClasses} text-description hover:text-body`;
83
+ return "text-description";
44
84
  }
45
85
  </script>
46
86
 
47
87
  <ul class="{ulClasses()} {className}" {...restProps}>
48
88
  {#each items as item}
49
- {@const linkClasses = getLinkClasses(item.href)}
50
- <li>
89
+ {@const isActive = currentPath === item.href}
90
+ {@const navItemClasses = getNavItemClasses(item.href)}
91
+ {@const iconContainerClasses = getIconContainerClasses(item.href)}
92
+ {@const labelClasses = getLabelClasses(item.href)}
93
+ <li class={navItemClasses}>
51
94
  <a
52
95
  href={item.href}
53
- class={linkClasses}
96
+ class={iconContainerClasses}
54
97
  onclick={(e) => handleClick(item, e)}
98
+ role="button"
99
+ aria-current={isActive ? "page" : undefined}
55
100
  >
56
- {item.label}
101
+ <div class={getStateLayerClasses()}>
102
+ {#if item.iconFilled && isActive}
103
+ {@const iconClasses = getIconClasses(item.href)}
104
+ {@const Icon = item.iconFilled}
105
+ <div class="overflow-clip relative shrink-0 size-4 {iconClasses}">
106
+ <Icon size={16} class="w-4 h-4" />
107
+ </div>
108
+ {:else if item.icon}
109
+ {@const iconClasses = getIconClasses(item.href)}
110
+ {@const Icon = item.icon}
111
+ <div class="overflow-clip relative shrink-0 size-4 {iconClasses}">
112
+ <Icon size={16} class="w-4 h-4" />
113
+ </div>
114
+ {/if}
115
+ <p class={labelClasses}>
116
+ {item.label}
117
+ </p>
118
+ </div>
57
119
  </a>
58
120
  </li>
59
121
  {/each}
@@ -1,9 +1,13 @@
1
+ import type { Component } from "svelte";
2
+ interface NavigationItem {
3
+ label: string;
4
+ href: string;
5
+ icon?: Component<any>;
6
+ iconFilled?: Component<any>;
7
+ }
1
8
  interface Props {
2
9
  variant?: "header" | "sidebar";
3
- items?: Array<{
4
- label: string;
5
- href: string;
6
- }>;
10
+ items?: NavigationItem[];
7
11
  currentPath?: string;
8
12
  onclick?: (event: Event) => void;
9
13
  className?: string;
@@ -11,6 +15,6 @@ interface Props {
11
15
  type $$ComponentProps = Props & {
12
16
  children?: any;
13
17
  };
14
- declare const Navigation: import("svelte").Component<$$ComponentProps, {}, "">;
18
+ declare const Navigation: Component<$$ComponentProps, {}, "">;
15
19
  type Navigation = ReturnType<typeof Navigation>;
16
20
  export default Navigation;
@@ -74,6 +74,7 @@
74
74
  --color-surface-active: theme(colors.stone.200);
75
75
  --color-surface-inverse: theme(colors.stone.900);
76
76
  --color-surface-disabled: theme(colors.stone.200);
77
+ --color-surface-container-high: #ece6f0;
77
78
 
78
79
  /* Background Colors */
79
80
  --color-background: theme(colors.white);
@@ -251,6 +252,7 @@ body {
251
252
  --color-surface-active: theme(colors.stone.600);
252
253
  --color-surface-inverse: theme(colors.stone.100);
253
254
  --color-surface-disabled: theme(colors.stone.700);
255
+ --color-surface-container-high: theme(colors.stone.700);
254
256
 
255
257
  /* Background Colors */
256
258
  --color-background: theme(colors.stone.900);
@@ -412,10 +414,141 @@ body {
412
414
  background-color: var(--color-surface-active);
413
415
  }
414
416
 
417
+ .bg-surface-container-high {
418
+ background-color: var(--color-surface-container-high);
419
+ }
420
+
415
421
  .bg-page {
416
422
  background-color: var(--color-page);
417
423
  }
418
424
 
425
+ /* Brand color utilities */
426
+ .bg-brand-50 {
427
+ background-color: var(--color-brand-50);
428
+ }
429
+
430
+ .bg-brand-100 {
431
+ background-color: var(--color-brand-100);
432
+ }
433
+
434
+ .bg-brand-200 {
435
+ background-color: var(--color-brand-200);
436
+ }
437
+
438
+ .bg-brand-300 {
439
+ background-color: var(--color-brand-300);
440
+ }
441
+
442
+ .bg-brand-400 {
443
+ background-color: var(--color-brand-400);
444
+ }
445
+
446
+ .bg-brand-500 {
447
+ background-color: var(--color-brand-500);
448
+ }
449
+
450
+ .bg-brand-600 {
451
+ background-color: var(--color-brand-600);
452
+ }
453
+
454
+ .bg-brand-700 {
455
+ background-color: var(--color-brand-700);
456
+ }
457
+
458
+ .bg-brand-800 {
459
+ background-color: var(--color-brand-800);
460
+ }
461
+
462
+ .bg-brand-900 {
463
+ background-color: var(--color-brand-900);
464
+ }
465
+
466
+ .bg-brand-950 {
467
+ background-color: var(--color-brand-950);
468
+ }
469
+
470
+ .hover\:bg-brand-50:hover {
471
+ background-color: var(--color-brand-50);
472
+ }
473
+
474
+ .hover\:bg-brand-100:hover {
475
+ background-color: var(--color-brand-100);
476
+ }
477
+
478
+ .hover\:bg-brand-200:hover {
479
+ background-color: var(--color-brand-200);
480
+ }
481
+
482
+ .text-brand-50 {
483
+ color: var(--color-brand-50);
484
+ }
485
+
486
+ .text-brand-100 {
487
+ color: var(--color-brand-100);
488
+ }
489
+
490
+ .text-brand-200 {
491
+ color: var(--color-brand-200);
492
+ }
493
+
494
+ .text-brand-300 {
495
+ color: var(--color-brand-300);
496
+ }
497
+
498
+ .text-brand-400 {
499
+ color: var(--color-brand-400);
500
+ }
501
+
502
+ .text-brand-500 {
503
+ color: var(--color-brand-500);
504
+ }
505
+
506
+ .text-brand-600 {
507
+ color: var(--color-brand-600);
508
+ }
509
+
510
+ .text-brand-700 {
511
+ color: var(--color-brand-700);
512
+ }
513
+
514
+ .text-brand-800 {
515
+ color: var(--color-brand-800);
516
+ }
517
+
518
+ .text-brand-900 {
519
+ color: var(--color-brand-900);
520
+ }
521
+
522
+ .text-brand-950 {
523
+ color: var(--color-brand-950);
524
+ }
525
+
526
+ .border-brand-50 {
527
+ border-color: var(--color-brand-50);
528
+ }
529
+
530
+ .border-brand-100 {
531
+ border-color: var(--color-brand-100);
532
+ }
533
+
534
+ .border-brand-500 {
535
+ border-color: var(--color-brand-500);
536
+ }
537
+
538
+ .border-brand-600 {
539
+ border-color: var(--color-brand-600);
540
+ }
541
+
542
+ .focus\:ring-brand-500:focus {
543
+ --tw-ring-color: var(--color-brand-500);
544
+ box-shadow: 0 0 0 2px var(--tw-ring-color);
545
+ }
546
+
547
+ .focus\:ring-brand-600:focus {
548
+ --tw-ring-color: var(--color-brand-600);
549
+ box-shadow: 0 0 0 2px var(--tw-ring-color);
550
+ }
551
+
419
552
  /* Text utilities */
420
553
  .text-headline {
421
554
  color: var(--color-headline);
@@ -484,6 +617,14 @@ body {
484
617
  }
485
618
 
486
619
  /* Border utilities */
620
+ .border-border {
621
+ border-color: var(--color-border);
622
+ }
623
+
624
+ .border-border-medium {
625
+ border-color: var(--color-border-medium);
626
+ }
627
+
487
628
  .border-primary {
488
629
  border-color: var(--color-border);
489
630
  }
@@ -615,6 +756,31 @@ body {
615
756
  background-color: var(--color-action-danger-subtle-hover);
616
757
  }
617
758
 
759
+ /* Z-index utilities */
760
+ .z-dropdown {
761
+ z-index: var(--z-dropdown);
762
+ }
763
+
764
+ .z-modal {
765
+ z-index: var(--z-modal);
766
+ }
767
+
768
+ .z-modal-backdrop {
769
+ z-index: var(--z-modal-backdrop);
770
+ }
771
+
772
+ .z-popover {
773
+ z-index: var(--z-popover);
774
+ }
775
+
776
+ .z-tooltip {
777
+ z-index: var(--z-tooltip);
778
+ }
779
+
780
+ .z-toast {
781
+ z-index: var(--z-toast);
782
+ }
783
+
618
784
  /* Modal Animation */
619
785
  @keyframes slideUp {
620
786
  from {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zabi-components",
3
- "version": "5.0.11",
3
+ "version": "5.0.13",
4
4
  "description": "A modern Svelte 5 component library with TypeScript, Tailwind CSS v4, and 100% SSR-safe components",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",