zabi-components 5.0.14 → 5.0.17

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.
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { onMount } from "svelte";
3
+
2
4
  interface Props {
3
5
  content?: string;
4
6
  placement?: "top" | "bottom" | "left" | "right";
@@ -14,22 +16,82 @@
14
16
  children,
15
17
  ...restProps
16
18
  }: Props & { children?: any } = $props();
19
+
20
+ let triggerId = `tooltip-trigger-${Math.random().toString(36).substr(2, 9)}`;
21
+ let tooltipId = `tooltip-${Math.random().toString(36).substr(2, 9)}`;
22
+ let isVisible = $state(false);
23
+ let triggerElement: HTMLElement | null = $state(null);
24
+
25
+ // Handle keyboard events
26
+ function handleKeydown(event: KeyboardEvent) {
27
+ if (event.key === "Escape" && isVisible) {
28
+ isVisible = false;
29
+ triggerElement?.focus();
30
+ }
31
+ }
32
+
33
+ // Handle focus events for accessibility
34
+ function handleFocus() {
35
+ if (!disabled && content) {
36
+ isVisible = true;
37
+ }
38
+ }
39
+
40
+ function handleBlur() {
41
+ // Delay hiding to allow clicking on tooltip content if needed
42
+ setTimeout(() => {
43
+ isVisible = false;
44
+ }, 100);
45
+ }
46
+
47
+ function handleMouseEnter() {
48
+ if (!disabled && content) {
49
+ if (delay > 0) {
50
+ setTimeout(() => {
51
+ isVisible = true;
52
+ }, delay);
53
+ } else {
54
+ isVisible = true;
55
+ }
56
+ }
57
+ }
58
+
59
+ function handleMouseLeave() {
60
+ isVisible = false;
61
+ }
62
+
63
+ onMount(() => {
64
+ if (typeof window !== "undefined") {
65
+ window.addEventListener("keydown", handleKeydown);
66
+ return () => {
67
+ window.removeEventListener("keydown", handleKeydown);
68
+ };
69
+ }
70
+ });
17
71
  </script>
18
72
 
19
73
  <div
20
- class="tooltip-container group relative inline-block"
74
+ class="tooltip-container relative inline-block"
21
75
  data-placement={placement}
22
- data-delay={delay}
23
76
  data-disabled={disabled}
77
+ onmouseenter={handleMouseEnter}
78
+ onmouseleave={handleMouseLeave}
79
+ onfocusin={handleFocus}
80
+ onfocusout={handleBlur}
24
81
  {...restProps}
25
82
  >
26
- {@render children?.()}
83
+ <div bind:this={triggerElement} id={triggerId} aria-describedby={isVisible ? tooltipId : undefined}>
84
+ {@render children?.()}
85
+ </div>
27
86
 
28
87
  {#if content && !disabled}
29
88
  <div
30
- class="tooltip group-hover:opacity-100 group-hover:visible group-focus-within:opacity-100 group-focus-within:visible"
89
+ id={tooltipId}
90
+ class="tooltip"
31
91
  role="tooltip"
32
- aria-hidden="true"
92
+ aria-hidden={!isVisible}
93
+ data-visible={isVisible}
94
+ data-placement={placement}
33
95
  >
34
96
  {content}
35
97
  </div>
@@ -48,7 +110,7 @@
48
110
  --tooltip-gap: 0.5rem;
49
111
  --tooltip-arrow-size: 4px;
50
112
  --tooltip-transition: opacity 0.2s ease-in-out,
51
- visibility 0.2s ease-in-out;
113
+ visibility 0.2s ease-in-out, transform 0.2s ease-in-out;
52
114
  }
53
115
 
54
116
  .tooltip-container {
@@ -58,7 +120,7 @@
58
120
 
59
121
  .tooltip {
60
122
  position: absolute;
61
- z-index: 50;
123
+ z-index: var(--z-tooltip, 1070);
62
124
  padding: var(--tooltip-padding);
63
125
  background-color: var(--tooltip-bg);
64
126
  color: var(--tooltip-color);
@@ -72,41 +134,61 @@
72
134
  visibility: hidden;
73
135
  transition: var(--tooltip-transition);
74
136
  pointer-events: none;
75
- /* Use logical properties for better internationalization */
76
- inset-inline-start: 50%;
77
- inset-block-start: 50%;
78
- transform: translate(-50%, -50%);
79
137
  }
80
138
 
81
139
  /* Simplified positioning using CSS custom properties and logical properties */
82
140
  .tooltip-container[data-placement="top"] .tooltip {
83
141
  inset-block-end: 100%;
84
142
  inset-inline-start: 50%;
85
- transform: translateX(-50%);
143
+ transform: translateX(-50%) translateY(4px) scale(0.95);
86
144
  margin-block-end: var(--tooltip-gap);
87
145
  }
88
146
 
147
+ .tooltip-container[data-placement="top"] .tooltip[data-visible="true"] {
148
+ opacity: 1;
149
+ visibility: visible;
150
+ transform: translateX(-50%) translateY(0) scale(1);
151
+ }
152
+
89
153
  .tooltip-container[data-placement="bottom"] .tooltip {
90
154
  inset-block-start: 100%;
91
155
  inset-inline-start: 50%;
92
- transform: translateX(-50%);
156
+ transform: translateX(-50%) translateY(-4px) scale(0.95);
93
157
  margin-block-start: var(--tooltip-gap);
94
158
  }
95
159
 
160
+ .tooltip-container[data-placement="bottom"] .tooltip[data-visible="true"] {
161
+ opacity: 1;
162
+ visibility: visible;
163
+ transform: translateX(-50%) translateY(0) scale(1);
164
+ }
165
+
96
166
  .tooltip-container[data-placement="left"] .tooltip {
97
167
  inset-inline-end: 100%;
98
168
  inset-block-start: 50%;
99
- transform: translateY(-50%);
169
+ transform: translateY(-50%) translateX(4px) scale(0.95);
100
170
  margin-inline-end: var(--tooltip-gap);
101
171
  }
102
172
 
173
+ .tooltip-container[data-placement="left"] .tooltip[data-visible="true"] {
174
+ opacity: 1;
175
+ visibility: visible;
176
+ transform: translateY(-50%) translateX(0) scale(1);
177
+ }
178
+
103
179
  .tooltip-container[data-placement="right"] .tooltip {
104
180
  inset-inline-start: 100%;
105
181
  inset-block-start: 50%;
106
- transform: translateY(-50%);
182
+ transform: translateY(-50%) translateX(-4px) scale(0.95);
107
183
  margin-inline-start: var(--tooltip-gap);
108
184
  }
109
185
 
186
+ .tooltip-container[data-placement="right"] .tooltip[data-visible="true"] {
187
+ opacity: 1;
188
+ visibility: visible;
189
+ transform: translateY(-50%) translateX(0) scale(1);
190
+ }
191
+
110
192
  /* Modern arrow implementation using CSS clip-path for better performance */
111
193
  .tooltip::before {
112
194
  content: "";
@@ -134,14 +216,14 @@
134
216
  inset-inline-start: 100%;
135
217
  inset-block-start: 50%;
136
218
  transform: translateY(-50%);
137
- clip-path: polygon(0 50%, 100% 0, 100% 100%);
219
+ clip-path: polygon(0 0, 100% 50%, 0 100%);
138
220
  }
139
221
 
140
222
  .tooltip-container[data-placement="right"] .tooltip::before {
141
223
  inset-inline-end: 100%;
142
224
  inset-block-start: 50%;
143
225
  transform: translateY(-50%);
144
- clip-path: polygon(0 0, 100% 50%, 0 100%);
226
+ clip-path: polygon(0 50%, 100% 0, 100% 100%);
145
227
  }
146
228
 
147
229
  /* Delay support using CSS custom properties */
@@ -155,10 +237,14 @@
155
237
  --tooltip-max-width: calc(100vw - 2rem);
156
238
  inset-inline-start: 50% !important;
157
239
  inset-inline-end: auto !important;
158
- transform: translateX(-50%) !important;
240
+ transform: translateX(-50%) scale(0.95) !important;
159
241
  margin: var(--tooltip-gap) 0 !important;
160
242
  }
161
243
 
244
+ .tooltip[data-visible="true"] {
245
+ transform: translateX(-50%) scale(1) !important;
246
+ }
247
+
162
248
  .tooltip::before {
163
249
  display: none;
164
250
  }
@@ -171,18 +257,41 @@
171
257
  }
172
258
  }
173
259
 
260
+ /* Use CSS color-scheme for better dark mode support */
261
+ .dark :root {
262
+ --tooltip-bg: #374151;
263
+ }
264
+
174
265
  /* High contrast mode support */
175
266
  @media (prefers-contrast: high) {
176
267
  :root {
177
268
  --tooltip-bg: #000000;
178
269
  --tooltip-color: #ffffff;
270
+ --tooltip-padding: 0.75rem 1rem;
179
271
  }
180
272
  }
181
273
 
182
274
  /* Reduced motion support */
183
275
  @media (prefers-reduced-motion: reduce) {
184
276
  .tooltip {
185
- transition: none;
277
+ transition: opacity 0.1s ease-in-out, visibility 0.1s ease-in-out;
278
+ }
279
+
280
+ .tooltip-container[data-placement="top"] .tooltip[data-visible="true"],
281
+ .tooltip-container[data-placement="bottom"] .tooltip[data-visible="true"] {
282
+ transform: translateX(-50%) !important;
283
+ }
284
+
285
+ .tooltip-container[data-placement="left"] .tooltip[data-visible="true"],
286
+ .tooltip-container[data-placement="right"] .tooltip[data-visible="true"] {
287
+ transform: translateY(-50%) !important;
186
288
  }
187
289
  }
290
+
291
+ /* Focus-visible for better keyboard navigation */
292
+ .tooltip-container:focus-visible {
293
+ outline: 2px solid var(--color-focus, #8fa8ff);
294
+ outline-offset: 2px;
295
+ border-radius: var(--tooltip-radius);
296
+ }
188
297
  </style>
@@ -1,6 +1,12 @@
1
1
  <script lang="ts">
2
2
  interface Props {
3
- variant?: "info" | "success" | "warning" | "error" | "neutral" | "energetic";
3
+ variant?:
4
+ | "info"
5
+ | "success"
6
+ | "warning"
7
+ | "error"
8
+ | "neutral"
9
+ | "energetic";
4
10
  title?: string;
5
11
  message?: string;
6
12
  closable?: boolean;
@@ -27,12 +33,12 @@
27
33
  }
28
34
 
29
35
  let alertClasses = $derived({
30
- info: "bg-surface-level-1 text-info border border-info",
31
- success: "bg-surface-level-1 text-success border border-success",
32
- warning: "bg-surface-level-1 text-warning border border-warning",
33
- error: "bg-surface-level-1 text-error border border-error",
34
- neutral: "bg-surface-level-1 text-neutral border border-neutral",
35
- energetic: "bg-surface-level-1 text-energetic border border-energetic",
36
+ info: "text-info border border-info",
37
+ success: "text-success border border-success",
38
+ warning: "text-warning border border-warning",
39
+ error: "text-error border border-error",
40
+ neutral: "text-neutral border border-neutral",
41
+ energetic: "text-energetic border border-energetic",
36
42
  });
37
43
 
38
44
  let alertRole = $derived(
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import CodeBlock from "../atoms/CodeBlock.svelte";
3
+ import Card from "../atoms/Card.svelte";
3
4
 
4
5
  interface Props {
5
6
  title: string;
@@ -22,75 +23,67 @@
22
23
  let showCode = $state(false);
23
24
  </script>
24
25
 
25
- <div
26
- class="component-demo bg-surface-level-0 border border-border rounded-lg overflow-hidden {className}"
27
- {...restProps}
28
- >
29
- <!-- Header -->
30
- <div class="px-6 py-4 border-b border-border bg-surface-level-1">
31
- <div class="flex items-center justify-between">
32
- <div>
33
- <h3 class="text-lg font-semibold text-text">{title}</h3>
34
- {#if description}
35
- <p class="text-sm text-text-secondary mt-1">
36
- {description}
37
- </p>
38
- {/if}
39
- </div>
40
- <button
41
- onclick={() => (showCode = !showCode)}
42
- class="flex items-center gap-2 px-3 py-1.5 text-sm text-text-secondary hover:text-text hover:bg-surface-level-0 rounded-md transition-colors duration-200"
43
- aria-label={showCode ? "Show preview" : "Show code"}
44
- >
45
- {#if showCode}
46
- <svg
47
- class="w-4 h-4"
48
- fill="none"
49
- stroke="currentColor"
50
- viewBox="0 0 24 24"
51
- >
52
- <path
53
- stroke-linecap="round"
54
- stroke-linejoin="round"
55
- stroke-width="2"
56
- d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
57
- />
58
- <path
59
- stroke-linecap="round"
60
- stroke-linejoin="round"
61
- stroke-width="2"
62
- d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
63
- />
64
- </svg>
65
- Preview
66
- {:else}
67
- <svg
68
- class="w-4 h-4"
69
- fill="none"
70
- stroke="currentColor"
71
- viewBox="0 0 24 24"
72
- >
73
- <path
74
- stroke-linecap="round"
75
- stroke-linejoin="round"
76
- stroke-width="2"
77
- d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
78
- />
79
- </svg>
80
- Code
81
- {/if}
82
- </button>
83
- </div>
84
- </div>
26
+ <div class="relative">
27
+ <Card
28
+ size="lg"
29
+ fullWidth={true}
30
+ {title}
31
+ {description}
32
+ className="overflow-hidden {className}"
33
+ {...restProps}
34
+ >
35
+ <!-- Toggle Button positioned absolutely -->
36
+ <button
37
+ onclick={() => (showCode = !showCode)}
38
+ class="absolute top-8 right-8 flex items-center gap-2 px-3 py-1.5 text-sm text-description hover:text-body hover:bg-base-50 rounded-md transition-colors duration-200 z-10"
39
+ aria-label={showCode ? "Show preview" : "Show code"}
40
+ >
41
+ {#if showCode}
42
+ <svg
43
+ class="w-4 h-4"
44
+ fill="none"
45
+ stroke="currentColor"
46
+ viewBox="0 0 24 24"
47
+ >
48
+ <path
49
+ stroke-linecap="round"
50
+ stroke-linejoin="round"
51
+ stroke-width="2"
52
+ d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
53
+ />
54
+ <path
55
+ stroke-linecap="round"
56
+ stroke-linejoin="round"
57
+ stroke-width="2"
58
+ d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
59
+ />
60
+ </svg>
61
+ Preview
62
+ {:else}
63
+ <svg
64
+ class="w-4 h-4"
65
+ fill="none"
66
+ stroke="currentColor"
67
+ viewBox="0 0 24 24"
68
+ >
69
+ <path
70
+ stroke-linecap="round"
71
+ stroke-linejoin="round"
72
+ stroke-width="2"
73
+ d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
74
+ />
75
+ </svg>
76
+ Code
77
+ {/if}
78
+ </button>
85
79
 
86
- <!-- Content -->
87
- <div class="p-6">
80
+ <!-- Content -->
88
81
  {#if showCode}
89
82
  <CodeBlock {code} {language} />
90
83
  {:else}
91
- <div class="min-h-[100px] flex items-center justify-center">
84
+ <div class="min-h-[100px] flex items-center justify-center w-full">
92
85
  {@render children?.()}
93
86
  </div>
94
87
  {/if}
95
- </div>
88
+ </Card>
96
89
  </div>
@@ -42,7 +42,7 @@
42
42
  const dropdownContentClasses = $derived(() => {
43
43
  return `
44
44
  ${placementClasses()}
45
- bg-brand-100
45
+ bg-input
46
46
  rounded-lg
47
47
  shadow-lg
48
48
  border-0
@@ -51,7 +51,9 @@
51
51
  duration-200
52
52
  ease-in-out
53
53
  ${transformClasses()}
54
- `.trim().replace(/\s+/g, " ");
54
+ `
55
+ .trim()
56
+ .replace(/\s+/g, " ");
55
57
  });
56
58
  </script>
57
59
 
@@ -87,7 +87,7 @@
87
87
  {:else}
88
88
  <!-- Empty State -->
89
89
  <div
90
- class="border-2 border-dashed border-stone-200 rounded-lg p-6 text-center hover:border-brand-500 transition-colors {disabled
90
+ class="border-2 border-dashed border-base-200 rounded-lg p-6 text-center hover:border-brand-500 transition-colors {disabled
91
91
  ? 'cursor-not-allowed opacity-50'
92
92
  : 'cursor-pointer'}"
93
93
  onclick={triggerFileSelect}
@@ -99,7 +99,7 @@
99
99
  >
100
100
  <div class="space-y-3">
101
101
  <div
102
- class="w-12 h-12 mx-auto bg-surface-level-1 rounded-lg flex items-center justify-center"
102
+ class="w-12 h-12 mx-auto bg-base-100 rounded-lg flex items-center justify-center"
103
103
  >
104
104
  <Image size={24} class="text-description" />
105
105
  </div>
@@ -63,7 +63,7 @@
63
63
  tabindex="-1"
64
64
  >
65
65
  <div
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
+ class="bg-base-50 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"
67
67
  >
68
68
  <!-- Header -->
69
69
  {#if title}
@@ -77,7 +77,7 @@
77
77
  <button
78
78
  type="button"
79
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"
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-base-100"
81
81
  aria-label="Close"
82
82
  >
83
83
  ×
@@ -48,7 +48,7 @@
48
48
  tabindex="-1"
49
49
  >
50
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"
51
+ class="fixed bottom-0 left-0 right-0 bg-base-50 rounded-t-3xl shadow-xl z-modal max-h-[90vh] overflow-y-auto animate-[slideUp_0.3s_ease-out] flex flex-col"
52
52
  >
53
53
  <!-- Header -->
54
54
  {#if title}
@@ -62,7 +62,7 @@
62
62
  <button
63
63
  type="button"
64
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"
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-base-100"
66
66
  aria-label="Close"
67
67
  >
68
68
  ×
@@ -37,7 +37,7 @@
37
37
  <div class="tabs-container">
38
38
  <!-- Tab List -->
39
39
  <div
40
- class="flex border-b border-gray-200"
40
+ class="flex border-b border-base-200"
41
41
  role="tablist"
42
42
  tabindex="0"
43
43
  onkeydown={handleKeydown}
@@ -51,7 +51,7 @@
51
51
  ? variant === 'pills'
52
52
  ? 'bg-brand-100 text-brand-700 border-brand-500'
53
53
  : 'border-brand-500 text-body'
54
- : 'border-transparent text-description hover:text-body hover:border-gray-300'}"
54
+ : 'border-transparent text-description hover:text-body hover:border-base-300'}"
55
55
  onclick={() => selectTab(tab.id)}
56
56
  disabled={tab.disabled}
57
57
  aria-selected={activeTab === tab.id}
@@ -31,7 +31,7 @@
31
31
  </script>
32
32
 
33
33
  <nav
34
- class="bg-surface-elevated border-b border-border sticky top-0 z-50 {className}"
34
+ class="bg-base-50 border-b border-border sticky top-0 z-50 {className}"
35
35
  {...restProps}
36
36
  >
37
37
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@@ -9,7 +9,7 @@ export type Variant = 'default' | 'success' | 'warning' | 'error' | 'info';
9
9
  */
10
10
  export function getInputVariantClasses(variant: Variant): string {
11
11
  const variantMap: Record<Variant, string> = {
12
- default: 'border-gray-300 focus:border-brand-500 focus:ring-brand-500',
12
+ default: 'border-base-300 focus:border-brand-500 focus:ring-brand-500',
13
13
  success: 'border-green-300 focus:border-green-500 focus:ring-green-500',
14
14
  warning: 'border-yellow-300 focus:border-yellow-500 focus:ring-yellow-500',
15
15
  error: 'border-red-300 focus:border-red-500 focus:ring-red-500',
@@ -24,7 +24,7 @@ export function getInputVariantClasses(variant: Variant): string {
24
24
  */
25
25
  export function getCardVariantClasses(variant: Variant): string {
26
26
  const variantMap: Record<Variant, string> = {
27
- default: 'border-gray-200 bg-white',
27
+ default: 'border-base-200 bg-white',
28
28
  success: 'border-green-200 bg-green-50',
29
29
  warning: 'border-yellow-200 bg-yellow-50',
30
30
  error: 'border-red-200 bg-red-50',
@@ -40,8 +40,8 @@ export function getCardVariantClasses(variant: Variant): string {
40
40
  export function getVariantClasses(variant: Variant, type: 'border' | 'text' | 'bg'): string {
41
41
  const variantMap: Record<Variant, Record<'border' | 'text' | 'bg', string>> = {
42
42
  default: {
43
- border: 'border-gray-300',
44
- text: 'text-gray-900',
43
+ border: 'border-base-300',
44
+ text: 'text-base-900',
45
45
  bg: 'bg-white'
46
46
  },
47
47
  success: {