@karbonjs/ui-svelte 0.2.5 → 0.3.1
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/package.json +3 -2
- package/src/accordion/Accordion.svelte +198 -23
- package/src/alert/AlertMessage.svelte +114 -20
- package/src/avatar/Avatar.svelte +16 -2
- package/src/badge/Badge.svelte +99 -10
- package/src/breadcrumb/Breadcrumb.svelte +124 -13
- package/src/button/Button.svelte +106 -21
- package/src/button/ButtonBrand.svelte +229 -0
- package/src/carousel/Carousel.svelte +161 -28
- package/src/code/CodeBlock.svelte +323 -0
- package/src/data/DataTable.svelte +321 -8
- package/src/data/Pagination.svelte +168 -28
- package/src/divider/Divider.svelte +91 -10
- package/src/dropdown/Dropdown.svelte +171 -27
- package/src/editor/RichTextEditor.svelte +862 -108
- package/src/form/Checkbox.svelte +110 -18
- package/src/form/ColorPicker.svelte +28 -16
- package/src/form/DatePicker.svelte +20 -10
- package/src/form/{FormInput.svelte → Input.svelte} +41 -14
- package/src/form/Radio.svelte +86 -18
- package/src/form/Select.svelte +246 -33
- package/src/form/Slider.svelte +22 -7
- package/src/form/Textarea.svelte +53 -10
- package/src/form/Toggle.svelte +72 -18
- package/src/image/Image.svelte +6 -4
- package/src/image/ImageCompare.svelte +182 -0
- package/src/image/ImgZoom.svelte +131 -49
- package/src/index.ts +7 -1
- package/src/kbd/Kbd.svelte +4 -3
- package/src/layout/Card.svelte +12 -6
- package/src/layout/EmptyState.svelte +75 -8
- package/src/layout/PageHeader.svelte +111 -11
- package/src/overlay/Dialog.svelte +147 -67
- package/src/overlay/ImgBox.svelte +125 -21
- package/src/overlay/Modal.svelte +110 -28
- package/src/overlay/Toast.svelte +152 -55
- package/src/progress/Progress.svelte +137 -26
- package/src/skeleton/Skeleton.svelte +6 -4
- package/src/tabs/Tabs.svelte +133 -22
- package/src/tooltip/Tooltip.svelte +110 -20
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
backdrop?: OverlayBackdrop
|
|
9
9
|
captions?: string[]
|
|
10
10
|
class?: string
|
|
11
|
+
classes?: { root?: string, backdrop?: string, image?: string }
|
|
11
12
|
onclose: () => void
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
backdrop = 'blur',
|
|
19
20
|
captions = [],
|
|
20
21
|
class: className = '',
|
|
22
|
+
classes = {},
|
|
21
23
|
onclose
|
|
22
24
|
}: Props = $props()
|
|
23
25
|
|
|
@@ -27,6 +29,8 @@
|
|
|
27
29
|
let dragging = $state(false)
|
|
28
30
|
let visible = $state(false)
|
|
29
31
|
let portal: HTMLDivElement | null = $state(null)
|
|
32
|
+
let transitioning = $state(false)
|
|
33
|
+
let showThumbs = $state(false)
|
|
30
34
|
|
|
31
35
|
const backdropClasses: Record<string, string> = {
|
|
32
36
|
blur: 'bg-black/70 backdrop-blur-xl',
|
|
@@ -58,15 +62,32 @@
|
|
|
58
62
|
|
|
59
63
|
function close() {
|
|
60
64
|
visible = false
|
|
61
|
-
setTimeout(() => onclose(),
|
|
65
|
+
setTimeout(() => onclose(), 350)
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
function prev() {
|
|
65
|
-
if (hasPrev
|
|
69
|
+
if (hasPrev && !transitioning) {
|
|
70
|
+
transitioning = true
|
|
71
|
+
setTimeout(() => { index--; resetTransform(); transitioning = false }, 150)
|
|
72
|
+
}
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
function next() {
|
|
69
|
-
if (hasNext
|
|
76
|
+
if (hasNext && !transitioning) {
|
|
77
|
+
transitioning = true
|
|
78
|
+
setTimeout(() => { index++; resetTransform(); transitioning = false }, 150)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function goTo(i: number) {
|
|
83
|
+
if (i === index || transitioning) return
|
|
84
|
+
transitioning = true
|
|
85
|
+
setTimeout(() => { index = i; resetTransform(); transitioning = false }, 150)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleDblClick() {
|
|
89
|
+
if (scale > 1) resetTransform()
|
|
90
|
+
else scale = 2.5
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
function zoomIn() {
|
|
@@ -128,21 +149,22 @@
|
|
|
128
149
|
<div
|
|
129
150
|
use:teleport
|
|
130
151
|
data-imgbox-root
|
|
131
|
-
class="imgbox-root {className}"
|
|
152
|
+
class="imgbox-root {classes?.root ?? className}"
|
|
132
153
|
style="opacity: {visible ? 1 : 0};"
|
|
133
154
|
>
|
|
134
|
-
<!-- Backdrop
|
|
155
|
+
<!-- Backdrop -->
|
|
135
156
|
<div
|
|
136
|
-
class="imgbox-backdrop {backdropClasses[backdrop]}"
|
|
157
|
+
class="imgbox-backdrop {backdropClasses[backdrop]} {classes?.backdrop ?? ''}"
|
|
137
158
|
onclick={() => { if (scale <= 1) close() }}
|
|
138
159
|
role="presentation"
|
|
139
160
|
></div>
|
|
140
161
|
|
|
141
|
-
<!-- Close button
|
|
162
|
+
<!-- Close button -->
|
|
142
163
|
<button
|
|
143
164
|
onclick={close}
|
|
144
165
|
aria-label="Fermer"
|
|
145
166
|
class="imgbox-close"
|
|
167
|
+
style="opacity:{visible ? 1 : 0};transition:opacity 0.3s ease 0.15s,background 0.15s ease,color 0.15s ease;"
|
|
146
168
|
>
|
|
147
169
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
148
170
|
</button>
|
|
@@ -151,8 +173,9 @@
|
|
|
151
173
|
{#if hasPrev}
|
|
152
174
|
<button
|
|
153
175
|
onclick={prev}
|
|
154
|
-
aria-label="Image
|
|
176
|
+
aria-label="Image precedente"
|
|
155
177
|
class="imgbox-nav imgbox-nav-prev"
|
|
178
|
+
style="opacity:{visible ? 1 : 0};transition:opacity 0.3s ease 0.2s,background 0.15s ease,color 0.15s ease;"
|
|
156
179
|
>
|
|
157
180
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>
|
|
158
181
|
</button>
|
|
@@ -164,6 +187,7 @@
|
|
|
164
187
|
onclick={next}
|
|
165
188
|
aria-label="Image suivante"
|
|
166
189
|
class="imgbox-nav imgbox-nav-next"
|
|
190
|
+
style="opacity:{visible ? 1 : 0};transition:opacity 0.3s ease 0.2s,background 0.15s ease,color 0.15s ease;"
|
|
167
191
|
>
|
|
168
192
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
|
|
169
193
|
</button>
|
|
@@ -179,30 +203,69 @@
|
|
|
179
203
|
<img
|
|
180
204
|
src={images[index]}
|
|
181
205
|
alt={caption || `Image ${index + 1}`}
|
|
182
|
-
class="imgbox-image"
|
|
183
|
-
style="
|
|
206
|
+
class="imgbox-image {classes?.image ?? ''}"
|
|
207
|
+
style="
|
|
208
|
+
transform: scale({visible ? scale : 0.7}) translate({translateX / scale}px, {translateY / scale}px);
|
|
209
|
+
opacity: {visible && !transitioning ? 1 : 0};
|
|
210
|
+
filter: {visible ? 'blur(0)' : 'blur(8px)'};
|
|
211
|
+
transition: transform 0.35s cubic-bezier(0.16,1,0.3,1), opacity 0.3s ease, filter 0.3s ease;
|
|
212
|
+
"
|
|
184
213
|
draggable="false"
|
|
214
|
+
ondblclick={handleDblClick}
|
|
185
215
|
/>
|
|
186
216
|
</div>
|
|
187
217
|
|
|
188
|
-
<!--
|
|
218
|
+
<!-- Caption -->
|
|
219
|
+
{#if caption}
|
|
220
|
+
<div class="imgbox-caption">
|
|
221
|
+
<span style="background:rgba(0,0,0,0.5);color:rgba(255,255,255,0.85);backdrop-filter:blur(8px);padding:6px 14px;border-radius:8px;font-size:13px;">{caption}</span>
|
|
222
|
+
</div>
|
|
223
|
+
{/if}
|
|
224
|
+
|
|
225
|
+
<!-- Counter + thumbnails toggle -->
|
|
189
226
|
{#if images.length > 1}
|
|
190
227
|
<div class="imgbox-counter">
|
|
191
|
-
<
|
|
228
|
+
<div style="display:flex;align-items:center;gap:8px;background:rgba(0,0,0,0.4);border-radius:9999px;padding:4px 12px;backdrop-filter:blur(8px);">
|
|
229
|
+
<span style="color:rgba(255,255,255,0.5);font-size:12px;">{index + 1} / {images.length}</span>
|
|
230
|
+
<button
|
|
231
|
+
onclick={() => showThumbs = !showThumbs}
|
|
232
|
+
style="color:rgba(255,255,255,{showThumbs ? '0.9' : '0.4'});cursor:pointer;background:none;border:none;padding:2px;"
|
|
233
|
+
aria-label="Afficher les miniatures"
|
|
234
|
+
>
|
|
235
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/></svg>
|
|
236
|
+
</button>
|
|
237
|
+
</div>
|
|
192
238
|
</div>
|
|
193
239
|
{/if}
|
|
194
240
|
|
|
195
|
-
<!--
|
|
241
|
+
<!-- Thumbnails strip -->
|
|
242
|
+
{#if showThumbs && images.length > 1}
|
|
243
|
+
<div class="imgbox-thumbs">
|
|
244
|
+
<div style="display:flex;gap:6px;padding:8px 12px;background:rgba(0,0,0,0.5);border-radius:12px;backdrop-filter:blur(12px);">
|
|
245
|
+
{#each images as img, i}
|
|
246
|
+
<button
|
|
247
|
+
onclick={() => goTo(i)}
|
|
248
|
+
style="width:48px;height:36px;border-radius:6px;overflow:hidden;border:{i === index ? '2px solid white' : '2px solid transparent'};opacity:{i === index ? 1 : 0.5};cursor:pointer;padding:0;transition:all 0.15s ease;flex-shrink:0;"
|
|
249
|
+
aria-label="Image {i + 1}"
|
|
250
|
+
>
|
|
251
|
+
<img src={img} alt="" style="width:100%;height:100%;object-fit:cover;" draggable="false" />
|
|
252
|
+
</button>
|
|
253
|
+
{/each}
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
{/if}
|
|
257
|
+
|
|
258
|
+
<!-- Zoom controls -->
|
|
196
259
|
<div class="imgbox-controls" style="opacity: {visible ? 1 : 0};">
|
|
197
260
|
<div class="flex items-center gap-1 bg-black/40 rounded-full px-3 py-1.5">
|
|
198
|
-
<button onclick={zoomOut} aria-label="
|
|
261
|
+
<button onclick={zoomOut} aria-label="Dezoomer" class="text-white/60 hover:text-white transition-colors cursor-pointer p-1">
|
|
199
262
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="8" x2="14" y1="11" y2="11"/></svg>
|
|
200
263
|
</button>
|
|
201
264
|
<span class="text-white/80 text-xs font-medium min-w-[3rem] text-center">{Math.round(scale * 100)}%</span>
|
|
202
265
|
<button onclick={zoomIn} aria-label="Zoomer" class="text-white/60 hover:text-white transition-colors cursor-pointer p-1">
|
|
203
266
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" x2="16.65" y1="21" y2="16.65"/><line x1="11" x2="11" y1="8" y2="14"/><line x1="8" x2="14" y1="11" y2="11"/></svg>
|
|
204
267
|
</button>
|
|
205
|
-
<button onclick={resetTransform} aria-label="
|
|
268
|
+
<button onclick={resetTransform} aria-label="Reinitialiser" class="text-white/60 hover:text-white transition-colors cursor-pointer p-1 ml-1 border-l border-white/20 pl-2">
|
|
206
269
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
|
|
207
270
|
</button>
|
|
208
271
|
</div>
|
|
@@ -219,7 +282,7 @@
|
|
|
219
282
|
align-items: center;
|
|
220
283
|
justify-content: center;
|
|
221
284
|
pointer-events: auto;
|
|
222
|
-
transition: opacity 0.
|
|
285
|
+
transition: opacity 0.3s ease;
|
|
223
286
|
}
|
|
224
287
|
|
|
225
288
|
.imgbox-backdrop {
|
|
@@ -228,10 +291,16 @@
|
|
|
228
291
|
z-index: 0;
|
|
229
292
|
pointer-events: auto;
|
|
230
293
|
cursor: default;
|
|
294
|
+
transition: opacity 0.4s ease;
|
|
231
295
|
}
|
|
232
296
|
|
|
233
297
|
.imgbox-close {
|
|
234
|
-
|
|
298
|
+
border-radius: 9999px;
|
|
299
|
+
padding: 0.625rem;
|
|
300
|
+
color: rgba(255, 255, 255, 0.7);
|
|
301
|
+
background: rgba(0, 0, 0, 0.3);
|
|
302
|
+
transition: all 0.15s ease;
|
|
303
|
+
cursor: pointer;
|
|
235
304
|
position: absolute;
|
|
236
305
|
top: 16px;
|
|
237
306
|
right: 16px;
|
|
@@ -241,11 +310,17 @@
|
|
|
241
310
|
}
|
|
242
311
|
|
|
243
312
|
.imgbox-close:hover {
|
|
244
|
-
|
|
313
|
+
color: white;
|
|
314
|
+
background: rgba(0, 0, 0, 0.5);
|
|
245
315
|
}
|
|
246
316
|
|
|
247
317
|
.imgbox-nav {
|
|
248
|
-
|
|
318
|
+
border-radius: 9999px;
|
|
319
|
+
padding: 0.75rem;
|
|
320
|
+
color: rgba(255, 255, 255, 0.6);
|
|
321
|
+
background: rgba(0, 0, 0, 0.2);
|
|
322
|
+
transition: all 0.15s ease;
|
|
323
|
+
cursor: pointer;
|
|
249
324
|
position: absolute;
|
|
250
325
|
top: 50%;
|
|
251
326
|
transform: translateY(-50%);
|
|
@@ -255,7 +330,8 @@
|
|
|
255
330
|
}
|
|
256
331
|
|
|
257
332
|
.imgbox-nav:hover {
|
|
258
|
-
|
|
333
|
+
color: white;
|
|
334
|
+
background: rgba(0, 0, 0, 0.5);
|
|
259
335
|
}
|
|
260
336
|
|
|
261
337
|
.imgbox-nav-prev { left: 16px; }
|
|
@@ -282,7 +358,29 @@
|
|
|
282
358
|
pointer-events: none;
|
|
283
359
|
}
|
|
284
360
|
|
|
361
|
+
.imgbox-caption {
|
|
362
|
+
position: absolute;
|
|
363
|
+
top: 16px;
|
|
364
|
+
left: 0;
|
|
365
|
+
right: 0;
|
|
366
|
+
display: flex;
|
|
367
|
+
justify-content: center;
|
|
368
|
+
z-index: 10;
|
|
369
|
+
pointer-events: none;
|
|
370
|
+
}
|
|
371
|
+
|
|
285
372
|
.imgbox-counter {
|
|
373
|
+
position: absolute;
|
|
374
|
+
bottom: 80px;
|
|
375
|
+
left: 0;
|
|
376
|
+
right: 0;
|
|
377
|
+
display: flex;
|
|
378
|
+
justify-content: center;
|
|
379
|
+
z-index: 10;
|
|
380
|
+
pointer-events: auto;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.imgbox-thumbs {
|
|
286
384
|
position: absolute;
|
|
287
385
|
bottom: 56px;
|
|
288
386
|
left: 0;
|
|
@@ -290,7 +388,13 @@
|
|
|
290
388
|
display: flex;
|
|
291
389
|
justify-content: center;
|
|
292
390
|
z-index: 10;
|
|
293
|
-
pointer-events:
|
|
391
|
+
pointer-events: auto;
|
|
392
|
+
animation: karbon-imgbox-thumbs-in 0.2s ease;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
@keyframes karbon-imgbox-thumbs-in {
|
|
396
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
397
|
+
to { opacity: 1; transform: translateY(0); }
|
|
294
398
|
}
|
|
295
399
|
|
|
296
400
|
.imgbox-controls {
|
package/src/overlay/Modal.svelte
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte'
|
|
3
|
-
import type {
|
|
3
|
+
import type { ButtonColor } from '@karbonjs/ui-core'
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
open: boolean
|
|
7
7
|
title?: string
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
description?: string
|
|
9
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'full'
|
|
10
|
+
position?: 'center' | 'top' | 'right' | 'bottom'
|
|
11
|
+
backdrop?: 'blur' | 'dark' | 'light' | 'transparent'
|
|
10
12
|
closable?: boolean
|
|
11
13
|
closeOnOverlay?: boolean
|
|
14
|
+
color?: ButtonColor
|
|
12
15
|
class?: string
|
|
16
|
+
classes?: { overlay?: string, content?: string, header?: string, body?: string, footer?: string }
|
|
13
17
|
onclose: () => void
|
|
18
|
+
icon?: Snippet
|
|
14
19
|
children: Snippet
|
|
15
20
|
footer?: Snippet
|
|
16
21
|
}
|
|
@@ -18,78 +23,155 @@
|
|
|
18
23
|
let {
|
|
19
24
|
open = $bindable(false),
|
|
20
25
|
title = '',
|
|
26
|
+
description = '',
|
|
21
27
|
size = 'md',
|
|
22
|
-
|
|
28
|
+
position = 'center',
|
|
29
|
+
backdrop = 'blur',
|
|
23
30
|
closable = true,
|
|
24
31
|
closeOnOverlay = true,
|
|
32
|
+
color,
|
|
25
33
|
class: className = '',
|
|
34
|
+
classes = {},
|
|
26
35
|
onclose,
|
|
36
|
+
icon,
|
|
27
37
|
children,
|
|
28
38
|
footer
|
|
29
39
|
}: Props = $props()
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
let visible = $state(false)
|
|
42
|
+
let contentEl: HTMLDivElement | undefined = $state()
|
|
43
|
+
|
|
44
|
+
const accent = $derived(color ? `var(--karbon-${color}-500)` : '')
|
|
45
|
+
|
|
46
|
+
$effect(() => {
|
|
47
|
+
if (open) {
|
|
48
|
+
requestAnimationFrame(() => { visible = true })
|
|
49
|
+
document.body.style.overflow = 'hidden'
|
|
50
|
+
} else {
|
|
51
|
+
visible = false
|
|
52
|
+
document.body.style.overflow = ''
|
|
53
|
+
}
|
|
54
|
+
return () => { document.body.style.overflow = '' }
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const sizeMap: Record<string, string> = {
|
|
58
|
+
xs: 'max-w-xs',
|
|
32
59
|
sm: 'max-w-sm',
|
|
33
60
|
md: 'max-w-lg',
|
|
34
61
|
lg: 'max-w-2xl',
|
|
35
62
|
xl: 'max-w-4xl',
|
|
36
|
-
full: 'max-w-[calc(100vw-2rem)] max-h-[calc(100vh-2rem)]'
|
|
63
|
+
full: 'max-w-[calc(100vw-2rem)] max-h-[calc(100vh-2rem)]',
|
|
37
64
|
}
|
|
38
65
|
|
|
39
|
-
const
|
|
40
|
-
blur: '
|
|
41
|
-
dark: '
|
|
42
|
-
|
|
43
|
-
|
|
66
|
+
const backdropStyles: Record<string, string> = {
|
|
67
|
+
blur: 'background:rgba(0,0,0,0.5);backdrop-filter:blur(8px);',
|
|
68
|
+
dark: 'background:rgba(0,0,0,0.6);',
|
|
69
|
+
light: 'background:rgba(255,255,255,0.4);backdrop-filter:blur(4px);',
|
|
70
|
+
transparent: 'background:transparent;',
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const positionClasses: Record<string, string> = {
|
|
74
|
+
center: 'items-center justify-center',
|
|
75
|
+
top: 'items-start justify-center pt-16',
|
|
76
|
+
right: 'items-stretch justify-end',
|
|
77
|
+
bottom: 'items-end justify-center pb-4',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const contentPosition: Record<string, string> = {
|
|
81
|
+
center: 'rounded-xl',
|
|
82
|
+
top: 'rounded-xl',
|
|
83
|
+
right: 'rounded-l-xl rounded-r-none min-h-full',
|
|
84
|
+
bottom: 'rounded-t-xl rounded-b-none',
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function close() {
|
|
88
|
+
if (!closable) return
|
|
89
|
+
visible = false
|
|
90
|
+
setTimeout(() => onclose(), 150)
|
|
44
91
|
}
|
|
45
92
|
|
|
46
93
|
function handleOverlayClick() {
|
|
47
|
-
if (closeOnOverlay)
|
|
94
|
+
if (closeOnOverlay) close()
|
|
48
95
|
}
|
|
49
96
|
|
|
50
97
|
function handleKeydown(e: KeyboardEvent) {
|
|
51
|
-
if (e.key === 'Escape' && closable)
|
|
98
|
+
if (e.key === 'Escape' && closable && open) close()
|
|
52
99
|
}
|
|
53
100
|
</script>
|
|
54
101
|
|
|
55
102
|
<svelte:window onkeydown={handleKeydown} />
|
|
56
103
|
|
|
57
104
|
{#if open}
|
|
58
|
-
|
|
59
|
-
|
|
105
|
+
<div
|
|
106
|
+
class="fixed inset-0 z-50 flex p-4 {positionClasses[position]}"
|
|
107
|
+
style="opacity:{visible ? 1 : 0};transition:opacity 0.15s ease;"
|
|
108
|
+
>
|
|
109
|
+
<!-- Backdrop -->
|
|
60
110
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
111
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
112
|
+
<div
|
|
113
|
+
class="fixed inset-0 {classes?.overlay ?? ''}"
|
|
114
|
+
style="{backdropStyles[backdrop]}transition:opacity 0.15s ease;opacity:{visible ? 1 : 0};"
|
|
115
|
+
onclick={handleOverlayClick}
|
|
116
|
+
></div>
|
|
117
|
+
|
|
118
|
+
<!-- Content -->
|
|
64
119
|
<div
|
|
65
|
-
|
|
120
|
+
bind:this={contentEl}
|
|
121
|
+
class="relative z-10 w-full {sizeMap[size]} {contentPosition[position]} flex flex-col {classes?.content ?? className}"
|
|
122
|
+
style="background:var(--karbon-bg-card);box-shadow:0 25px 60px -12px rgba(0,0,0,0.4);border:1px solid var(--karbon-border);
|
|
123
|
+
transform:{visible ? 'scale(1) translateY(0)' : 'scale(0.95) translateY(8px)'};
|
|
124
|
+
transition:transform 0.2s cubic-bezier(0.16,1,0.3,1),opacity 0.15s ease;
|
|
125
|
+
opacity:{visible ? 1 : 0};"
|
|
66
126
|
role="dialog"
|
|
67
127
|
aria-modal="true"
|
|
68
128
|
aria-label={title || undefined}
|
|
69
129
|
>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
130
|
+
<!-- Header -->
|
|
131
|
+
{#if title || closable || icon || description}
|
|
132
|
+
<div class="flex gap-4 px-6 pt-6 pb-2 {classes?.header ?? ''}">
|
|
133
|
+
{#if icon}
|
|
134
|
+
<div class="shrink-0 mt-0.5">
|
|
135
|
+
{#if color}
|
|
136
|
+
<div class="w-10 h-10 rounded-xl flex items-center justify-center" style="background:color-mix(in srgb,{accent} 12%,transparent);color:{accent};">
|
|
137
|
+
{@render icon()}
|
|
138
|
+
</div>
|
|
139
|
+
{:else}
|
|
140
|
+
{@render icon()}
|
|
141
|
+
{/if}
|
|
142
|
+
</div>
|
|
74
143
|
{/if}
|
|
144
|
+
<div class="flex-1 min-w-0">
|
|
145
|
+
{#if title}
|
|
146
|
+
<h3 class="text-lg font-semibold" style="color:var(--karbon-text);">{title}</h3>
|
|
147
|
+
{/if}
|
|
148
|
+
{#if description}
|
|
149
|
+
<p class="text-sm mt-0.5" style="color:var(--karbon-text-3);">{description}</p>
|
|
150
|
+
{/if}
|
|
151
|
+
</div>
|
|
75
152
|
{#if closable}
|
|
76
153
|
<button
|
|
77
|
-
onclick={
|
|
154
|
+
onclick={close}
|
|
78
155
|
aria-label="Fermer"
|
|
79
|
-
class="rounded-lg p-1
|
|
156
|
+
class="shrink-0 rounded-lg p-1.5 transition-colors cursor-pointer"
|
|
157
|
+
style="color:var(--karbon-text-4);"
|
|
158
|
+
onmouseenter={(e) => { (e.currentTarget as HTMLElement).style.background = 'var(--karbon-nav-hover-bg)'; (e.currentTarget as HTMLElement).style.color = 'var(--karbon-text-2)' }}
|
|
159
|
+
onmouseleave={(e) => { (e.currentTarget as HTMLElement).style.background = 'transparent'; (e.currentTarget as HTMLElement).style.color = 'var(--karbon-text-4)' }}
|
|
80
160
|
>
|
|
81
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="
|
|
161
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
|
82
162
|
</button>
|
|
83
163
|
{/if}
|
|
84
164
|
</div>
|
|
85
165
|
{/if}
|
|
86
166
|
|
|
87
|
-
|
|
167
|
+
<!-- Body -->
|
|
168
|
+
<div class="px-6 py-4 flex-1 overflow-y-auto text-sm {classes?.body ?? ''}" style="color:var(--karbon-text-2);">
|
|
88
169
|
{@render children()}
|
|
89
170
|
</div>
|
|
90
171
|
|
|
172
|
+
<!-- Footer -->
|
|
91
173
|
{#if footer}
|
|
92
|
-
<div class="px-6
|
|
174
|
+
<div class="px-6 py-4 flex items-center justify-end gap-2 {classes?.footer ?? ''}" style="border-top:1px solid var(--karbon-border);">
|
|
93
175
|
{@render footer()}
|
|
94
176
|
</div>
|
|
95
177
|
{/if}
|