@marianmeres/stuic 2.66.0 → 3.0.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/README.md +292 -4
- package/dist/README.md +41 -18
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/popover/README.md +19 -0
- package/dist/actions/popover/index.css +6 -9
- package/dist/actions/popover/popover.svelte.js +2 -2
- package/dist/actions/tooltip/README.md +18 -0
- package/dist/actions/tooltip/index.css +5 -8
- package/dist/actions/tooltip/tooltip.svelte.js +1 -1
- package/dist/actions/typeahead.svelte.d.ts +53 -0
- package/dist/actions/typeahead.svelte.js +328 -0
- package/dist/base.css +17 -0
- package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte +10 -10
- package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte.d.ts +4 -3
- package/dist/components/AlertConfirmPrompt/Current.svelte +15 -18
- package/dist/components/AlertConfirmPrompt/Current.svelte.d.ts +4 -3
- package/dist/components/AlertConfirmPrompt/acp-icons.js +5 -4
- package/dist/components/AlertConfirmPrompt/index.css +66 -0
- package/dist/components/AssetsPreview/AssetsPreview.svelte +91 -73
- package/dist/components/AssetsPreview/index.css +61 -0
- package/dist/components/Avatar/Avatar.svelte +31 -18
- package/dist/components/Avatar/README.md +166 -0
- package/dist/components/Avatar/index.css +130 -0
- package/dist/components/Backdrop/Backdrop.svelte +7 -2
- package/dist/components/Backdrop/README.md +71 -6
- package/dist/components/Backdrop/index.css +31 -0
- package/dist/components/Button/Button.svelte +116 -124
- package/dist/components/Button/Button.svelte.d.ts +35 -24
- package/dist/components/Button/README.md +87 -21
- package/dist/components/Button/index.css +475 -9
- package/dist/components/Button/index.d.ts +1 -1
- package/dist/components/Button/index.js +1 -1
- package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +7 -39
- package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte.d.ts +0 -1
- package/dist/components/ButtonGroupRadio/README.md +82 -4
- package/dist/components/ButtonGroupRadio/index.css +158 -14
- package/dist/components/Collapsible/Collapsible.svelte +7 -7
- package/dist/components/Collapsible/Collapsible.svelte.d.ts +2 -2
- package/dist/components/Collapsible/README.md +34 -2
- package/dist/components/Collapsible/index.css +40 -0
- package/dist/components/CommandMenu/CommandMenu.svelte +18 -26
- package/dist/components/CommandMenu/CommandMenu.svelte.d.ts +0 -1
- package/dist/components/CommandMenu/README.md +39 -0
- package/dist/components/CommandMenu/index.css +47 -2
- package/dist/components/DismissibleMessage/DismissibleMessage.svelte +53 -51
- package/dist/components/DismissibleMessage/DismissibleMessage.svelte.d.ts +6 -6
- package/dist/components/DismissibleMessage/README.md +93 -11
- package/dist/components/DismissibleMessage/index.css +128 -8
- package/dist/components/DismissibleMessage/index.d.ts +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.svelte +14 -51
- package/dist/components/DropdownMenu/DropdownMenu.svelte.d.ts +6 -7
- package/dist/components/DropdownMenu/README.md +132 -0
- package/dist/components/DropdownMenu/index.css +258 -52
- package/dist/components/Input/FieldAssets.svelte +8 -5
- package/dist/components/Input/FieldCheckbox.svelte +7 -44
- package/dist/components/Input/FieldFile.svelte +1 -6
- package/dist/components/Input/FieldInput.svelte +9 -1
- package/dist/components/Input/FieldInput.svelte.d.ts +2 -0
- package/dist/components/Input/FieldOptions.svelte +42 -39
- package/dist/components/Input/FieldRadios.svelte +7 -16
- package/dist/components/Input/FieldSelect.svelte +1 -1
- package/dist/components/Input/FieldSwitch.svelte +1 -5
- package/dist/components/Input/FieldTextarea.svelte +1 -1
- package/dist/components/Input/README.md +194 -0
- package/dist/components/Input/_internal/FieldRadioInternal.svelte +2 -40
- package/dist/components/Input/_internal/InputWrap.svelte +8 -48
- package/dist/components/Input/index.css +524 -116
- package/dist/components/KbdShortcut/KbdShortcut.svelte +4 -12
- package/dist/components/KbdShortcut/README.md +34 -0
- package/dist/components/KbdShortcut/index.css +55 -0
- package/dist/components/ListItemButton/ListItemButton.svelte +37 -74
- package/dist/components/ListItemButton/ListItemButton.svelte.d.ts +1 -10
- package/dist/components/ListItemButton/README.md +100 -45
- package/dist/components/ListItemButton/index.css +173 -52
- package/dist/components/ListItemButton/index.d.ts +1 -1
- package/dist/components/ListItemButton/index.js +1 -1
- package/dist/components/Modal/Modal.svelte +1 -8
- package/dist/components/Modal/README.md +29 -0
- package/dist/components/Modal/index.css +38 -0
- package/dist/components/ModalDialog/ModalDialog.svelte +2 -21
- package/dist/components/ModalDialog/README.md +35 -0
- package/dist/components/ModalDialog/index.css +59 -0
- package/dist/components/Nav/Nav.svelte +732 -0
- package/dist/components/Nav/Nav.svelte.d.ts +110 -0
- package/dist/components/Nav/README.md +334 -0
- package/dist/components/Nav/index.css +318 -0
- package/dist/components/Nav/index.d.ts +1 -0
- package/dist/components/Nav/index.js +1 -0
- package/dist/components/Notifications/Notifications.svelte +44 -129
- package/dist/components/Notifications/Notifications.svelte.d.ts +9 -18
- package/dist/components/Notifications/README.md +186 -70
- package/dist/components/Notifications/index.css +212 -15
- package/dist/components/Notifications/notifications-stack.svelte.d.ts +4 -0
- package/dist/components/Notifications/notifications-stack.svelte.js +8 -0
- package/dist/components/Progress/Progress.svelte +4 -2
- package/dist/components/Progress/Progress.svelte.d.ts +1 -0
- package/dist/components/Progress/README.md +97 -11
- package/dist/components/Progress/_internal/Bar.svelte +4 -15
- package/dist/components/Progress/_internal/Bar.svelte.d.ts +1 -1
- package/dist/components/Progress/_internal/Circle.svelte +30 -2
- package/dist/components/Progress/_internal/Circle.svelte.d.ts +1 -0
- package/dist/components/Progress/index.css +50 -4
- package/dist/components/Skeleton/README.md +152 -0
- package/dist/components/Skeleton/Skeleton.svelte +9 -9
- package/dist/components/Skeleton/Skeleton.svelte.d.ts +0 -1
- package/dist/components/Skeleton/index.css +72 -45
- package/dist/components/Spinner/README.md +149 -37
- package/dist/components/Spinner/Spinner.svelte +14 -38
- package/dist/components/Spinner/Spinner.svelte.d.ts +2 -1
- package/dist/components/Spinner/SpinnerCircle.svelte +6 -34
- package/dist/components/Spinner/SpinnerCircle.svelte.d.ts +1 -0
- package/dist/components/Spinner/SpinnerCircleOscillate.svelte +10 -5
- package/dist/components/Spinner/SpinnerUnicode.svelte +3 -1
- package/dist/components/Spinner/SpinnerUnicode.svelte.d.ts +1 -0
- package/dist/components/Spinner/index.css +104 -0
- package/dist/components/Switch/README.md +45 -14
- package/dist/components/Switch/Switch.svelte +23 -48
- package/dist/components/Switch/Switch.svelte.d.ts +4 -2
- package/dist/components/Switch/index.css +121 -4
- package/dist/components/Switch/index.d.ts +1 -2
- package/dist/components/Switch/index.js +1 -2
- package/dist/components/TabbedMenu/README.md +37 -21
- package/dist/components/TabbedMenu/TabbedMenu.svelte +5 -46
- package/dist/components/TabbedMenu/TabbedMenu.svelte.d.ts +0 -1
- package/dist/components/TabbedMenu/index.css +84 -17
- package/dist/components/ThemePreview/README.md +289 -0
- package/dist/components/ThemePreview/ThemePreview.svelte +394 -0
- package/dist/components/ThemePreview/ThemePreview.svelte.d.ts +35 -0
- package/dist/components/ThemePreview/index.css +509 -0
- package/dist/components/ThemePreview/index.d.ts +1 -0
- package/dist/components/ThemePreview/index.js +1 -0
- package/dist/components/TwCheck/README.md +32 -13
- package/dist/components/TwCheck/TwCheck.svelte +11 -9
- package/dist/components/TwCheck/TwCheck.svelte.d.ts +0 -1
- package/dist/components/TwCheck/index.css +17 -2
- package/dist/components/TypeaheadInput/TypeaheadInput.svelte +20 -188
- package/dist/components/TypeaheadInput/TypeaheadInput.svelte.d.ts +4 -2
- package/dist/components/X/X.svelte +12 -5
- package/dist/components/X/X.svelte.d.ts +1 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.css +46 -26
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/themes/blue-orange.css +217 -0
- package/dist/themes/blue-orange.d.ts +6 -0
- package/dist/themes/blue-orange.js +175 -0
- package/dist/themes/cyan-red.css +217 -0
- package/dist/themes/cyan-red.d.ts +6 -0
- package/dist/themes/cyan-red.js +175 -0
- package/dist/themes/cyan-slate.css +217 -0
- package/dist/themes/cyan-slate.d.ts +6 -0
- package/dist/themes/cyan-slate.js +175 -0
- package/dist/themes/emerald-pink.css +217 -0
- package/dist/themes/emerald-pink.d.ts +6 -0
- package/dist/themes/emerald-pink.js +175 -0
- package/dist/themes/fuchsia-emerald.css +217 -0
- package/dist/themes/fuchsia-emerald.d.ts +6 -0
- package/dist/themes/fuchsia-emerald.js +175 -0
- package/dist/themes/gray.css +217 -0
- package/dist/themes/gray.d.ts +6 -0
- package/dist/themes/gray.js +175 -0
- package/dist/themes/indigo-amber.css +217 -0
- package/dist/themes/indigo-amber.d.ts +6 -0
- package/dist/themes/indigo-amber.js +175 -0
- package/dist/themes/neutral.css +217 -0
- package/dist/themes/neutral.d.ts +6 -0
- package/dist/themes/neutral.js +175 -0
- package/dist/themes/pink-emerald.css +217 -0
- package/dist/themes/pink-emerald.d.ts +6 -0
- package/dist/themes/pink-emerald.js +175 -0
- package/dist/themes/purple-yellow.css +217 -0
- package/dist/themes/purple-yellow.d.ts +6 -0
- package/dist/themes/purple-yellow.js +175 -0
- package/dist/themes/rainbow.css +217 -0
- package/dist/themes/rainbow.d.ts +6 -0
- package/dist/themes/rainbow.js +180 -0
- package/dist/themes/red-blue.css +217 -0
- package/dist/themes/red-blue.d.ts +6 -0
- package/dist/themes/red-blue.js +175 -0
- package/dist/themes/red-cyan.css +217 -0
- package/dist/themes/red-cyan.d.ts +6 -0
- package/dist/themes/red-cyan.js +175 -0
- package/dist/themes/rose-teal.css +217 -0
- package/dist/themes/rose-teal.d.ts +6 -0
- package/dist/themes/rose-teal.js +175 -0
- package/dist/themes/sky-amber.css +217 -0
- package/dist/themes/sky-amber.d.ts +6 -0
- package/dist/themes/sky-amber.js +175 -0
- package/dist/themes/slate-cyan.css +217 -0
- package/dist/themes/slate-cyan.d.ts +6 -0
- package/dist/themes/slate-cyan.js +175 -0
- package/dist/themes/tailwind-color-pairs.md +31 -0
- package/dist/themes/teal-rose.css +217 -0
- package/dist/themes/teal-rose.d.ts +6 -0
- package/dist/themes/teal-rose.js +175 -0
- package/dist/themes/violet-lime.css +217 -0
- package/dist/themes/violet-lime.d.ts +6 -0
- package/dist/themes/violet-lime.js +175 -0
- package/dist/utils/design-tokens.d.ts +43 -0
- package/dist/utils/design-tokens.js +127 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/storage-abstraction.js +1 -1
- package/package.json +14 -11
- package/dist/components/Switch/SwitchButton.svelte +0 -135
- package/dist/components/Switch/SwitchButton.svelte.d.ts +0 -21
|
@@ -171,6 +171,7 @@
|
|
|
171
171
|
</script>
|
|
172
172
|
|
|
173
173
|
<script lang="ts">
|
|
174
|
+
import Button from "../Button/Button.svelte";
|
|
174
175
|
const clog = createClog("AssetsPreview", { color: "auto" });
|
|
175
176
|
|
|
176
177
|
let {
|
|
@@ -219,7 +220,13 @@
|
|
|
219
220
|
let imgEl: HTMLImageElement | null = null;
|
|
220
221
|
let containerEl: HTMLDivElement | null = $state(null);
|
|
221
222
|
|
|
222
|
-
const
|
|
223
|
+
const BUTTON_CLS = "stuic-assets-preview-control pointer-events-auto p-0!";
|
|
224
|
+
|
|
225
|
+
const BUTTON_PROPS = {
|
|
226
|
+
aspect1: true,
|
|
227
|
+
variant: "soft",
|
|
228
|
+
roundedFull: true,
|
|
229
|
+
};
|
|
223
230
|
|
|
224
231
|
$effect(() => {
|
|
225
232
|
const visible = modal?.visibility().visible;
|
|
@@ -457,6 +464,7 @@
|
|
|
457
464
|
previewIdx = idx % assets.length;
|
|
458
465
|
}
|
|
459
466
|
|
|
467
|
+
const ICON_SIZE = 24;
|
|
460
468
|
// $inspect(assets).with(clog);
|
|
461
469
|
</script>
|
|
462
470
|
|
|
@@ -513,7 +521,9 @@
|
|
|
513
521
|
class: "mx-auto",
|
|
514
522
|
})}
|
|
515
523
|
</div>
|
|
516
|
-
<div class="
|
|
524
|
+
<div class="text-(--stuic-color-muted-foreground) mt-4">
|
|
525
|
+
{t("unable_to_preview")}
|
|
526
|
+
</div>
|
|
517
527
|
</div>
|
|
518
528
|
{/if}
|
|
519
529
|
|
|
@@ -521,95 +531,103 @@
|
|
|
521
531
|
<div
|
|
522
532
|
class="absolute inset-0 flex items-center justify-between pointer-events-none"
|
|
523
533
|
>
|
|
524
|
-
|
|
525
|
-
|
|
534
|
+
<!-- class={twMerge("p-4 aspect-square pointer-events-auto", classControls)} -->
|
|
535
|
+
<Button
|
|
536
|
+
class={twMerge(BUTTON_CLS, "ml-4", classControls)}
|
|
526
537
|
onclick={preview_previous}
|
|
527
538
|
type="button"
|
|
539
|
+
{...BUTTON_PROPS}
|
|
528
540
|
>
|
|
529
|
-
<span class="
|
|
530
|
-
|
|
531
|
-
</span>
|
|
532
|
-
</
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
541
|
+
<!-- <span class="stuic-assets-preview-control-nav p-3 block"> -->
|
|
542
|
+
{@html iconPrevious({ size: ICON_SIZE })}
|
|
543
|
+
<!-- </span> -->
|
|
544
|
+
</Button>
|
|
545
|
+
|
|
546
|
+
<!-- class={twMerge("p-4 aspect-square pointer-events-auto", classControls)} -->
|
|
547
|
+
<Button
|
|
548
|
+
class={twMerge(BUTTON_CLS, "mr-4", classControls)}
|
|
536
549
|
onclick={preview_next}
|
|
537
550
|
type="button"
|
|
551
|
+
{...BUTTON_PROPS}
|
|
538
552
|
>
|
|
539
|
-
<span class="
|
|
540
|
-
|
|
541
|
-
</span>
|
|
542
|
-
</
|
|
553
|
+
<!-- <span class="stuic-assets-preview-control-nav p-3 block"> -->
|
|
554
|
+
{@html iconNext({ size: ICON_SIZE })}
|
|
555
|
+
<!-- </span> -->
|
|
556
|
+
</Button>
|
|
543
557
|
</div>
|
|
544
558
|
{/if}
|
|
545
559
|
|
|
546
560
|
<div class="absolute top-4 left-4 right-4 flex items-center justify-between gap-3">
|
|
547
561
|
{#if !noName && previewAsset?.name}
|
|
548
|
-
<span class="truncate
|
|
562
|
+
<span class="stuic-assets-preview-label truncate px-1">
|
|
549
563
|
{previewAsset?.name}
|
|
550
564
|
</span>
|
|
551
565
|
{:else}
|
|
552
566
|
<span></span>
|
|
553
567
|
{/if}
|
|
554
568
|
<div class="flex items-center space-x-3 shrink-0">
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
+
{#if previewAsset.isImage}
|
|
570
|
+
<Button
|
|
571
|
+
class={twMerge(BUTTON_CLS, classControls)}
|
|
572
|
+
type="button"
|
|
573
|
+
onclick={zoomOut}
|
|
574
|
+
disabled={zoomLevelIdx === 0}
|
|
575
|
+
aria-label={t("zoom_out")}
|
|
576
|
+
tooltip={t("zoom_out")}
|
|
577
|
+
{...BUTTON_PROPS}
|
|
578
|
+
>
|
|
579
|
+
{@html iconZoomOut({ size: ICON_SIZE })}
|
|
580
|
+
</Button>
|
|
581
|
+
|
|
582
|
+
<Button
|
|
583
|
+
class={twMerge(BUTTON_CLS, classControls)}
|
|
584
|
+
type="button"
|
|
585
|
+
onclick={zoomIn}
|
|
586
|
+
disabled={zoomLevelIdx === ZOOM_LEVELS.length - 1}
|
|
587
|
+
aria-label={t("zoom_in")}
|
|
588
|
+
tooltip={t("zoom_in")}
|
|
589
|
+
{...BUTTON_PROPS}
|
|
590
|
+
>
|
|
591
|
+
{@html iconZoomIn({ size: ICON_SIZE })}
|
|
592
|
+
</Button>
|
|
593
|
+
{/if}
|
|
594
|
+
|
|
595
|
+
{#if typeof onDelete === "function"}
|
|
596
|
+
<Button
|
|
597
|
+
class={twMerge(BUTTON_CLS, classControls)}
|
|
598
|
+
type="button"
|
|
599
|
+
onclick={() => onDelete(previewAsset, previewIdx, { close })}
|
|
600
|
+
aria-label={t("delete")}
|
|
601
|
+
tooltip={t("delete")}
|
|
602
|
+
{...BUTTON_PROPS}
|
|
603
|
+
>
|
|
604
|
+
{@html iconDelete({ size: ICON_SIZE })}
|
|
605
|
+
</Button>
|
|
606
|
+
{/if}
|
|
607
|
+
|
|
608
|
+
<Button
|
|
609
|
+
class={twMerge(BUTTON_CLS, classControls)}
|
|
569
610
|
type="button"
|
|
570
|
-
onclick={
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
611
|
+
onclick={(e) => {
|
|
612
|
+
e.preventDefault();
|
|
613
|
+
forceDownload(String(previewAsset.url.original), previewAsset?.name || "");
|
|
614
|
+
}}
|
|
615
|
+
aria-label={t("download")}
|
|
616
|
+
tooltip={t("download")}
|
|
617
|
+
{...BUTTON_PROPS}
|
|
574
618
|
>
|
|
575
|
-
{@html
|
|
576
|
-
</
|
|
577
|
-
{/if}
|
|
619
|
+
{@html iconDownload({ size: ICON_SIZE })}
|
|
620
|
+
</Button>
|
|
578
621
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
622
|
+
<Button
|
|
623
|
+
class={twMerge(BUTTON_CLS, classControls)}
|
|
624
|
+
onclick={modal?.close}
|
|
625
|
+
aria-label={t("close")}
|
|
582
626
|
type="button"
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
{@html iconDelete({ class: "size-6" })}
|
|
588
|
-
</button>
|
|
589
|
-
{/if}
|
|
590
|
-
|
|
591
|
-
<button
|
|
592
|
-
class={twMerge(TOP_BUTTON_CLS, classControls)}
|
|
593
|
-
type="button"
|
|
594
|
-
onclick={(e) => {
|
|
595
|
-
e.preventDefault();
|
|
596
|
-
forceDownload(String(previewAsset.url.original), previewAsset?.name || "");
|
|
597
|
-
}}
|
|
598
|
-
aria-label={t("download")}
|
|
599
|
-
use:tooltip
|
|
600
|
-
>
|
|
601
|
-
{@html iconDownload({ class: "size-6" })}
|
|
602
|
-
</button>
|
|
603
|
-
|
|
604
|
-
<button
|
|
605
|
-
class={twMerge(TOP_BUTTON_CLS, classControls)}
|
|
606
|
-
onclick={modal?.close}
|
|
607
|
-
aria-label={t("close")}
|
|
608
|
-
type="button"
|
|
609
|
-
use:tooltip
|
|
610
|
-
>
|
|
611
|
-
<X />
|
|
612
|
-
</button>
|
|
627
|
+
tooltip={t("close")}
|
|
628
|
+
{...BUTTON_PROPS}
|
|
629
|
+
x
|
|
630
|
+
/>
|
|
613
631
|
</div>
|
|
614
632
|
</div>
|
|
615
633
|
|
|
@@ -619,7 +637,7 @@
|
|
|
619
637
|
class="absolute bottom-10 left-0 right-0 text-center"
|
|
620
638
|
transition:fade={{ duration: 100 }}
|
|
621
639
|
>
|
|
622
|
-
<span class="
|
|
640
|
+
<span class="stuic-assets-preview-label p-1">
|
|
623
641
|
{dotTooltip}
|
|
624
642
|
</span>
|
|
625
643
|
</div>
|
|
@@ -630,8 +648,8 @@
|
|
|
630
648
|
<button
|
|
631
649
|
type="button"
|
|
632
650
|
class={twMerge(
|
|
633
|
-
"
|
|
634
|
-
i === previewIdx ? "
|
|
651
|
+
"stuic-assets-preview-dot",
|
|
652
|
+
i === previewIdx ? "active" : ""
|
|
635
653
|
)}
|
|
636
654
|
onclick={() => {
|
|
637
655
|
previewIdx = i;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
ASSETS PREVIEW COMPONENT TOKENS
|
|
3
|
+
Override globally: :root { --stuic-assets-preview-control-bg: var(--stuic-color-muted); }
|
|
4
|
+
Override locally: <AssetsPreview style="--stuic-assets-preview-control-bg: red;">
|
|
5
|
+
============================================================================ */
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
/* Control buttons (zoom, download, close, prev/next) */
|
|
9
|
+
|
|
10
|
+
/* Labels (filename, tooltip) */
|
|
11
|
+
--stuic-assets-preview-label-bg: var(--stuic-color-surface);
|
|
12
|
+
--stuic-assets-preview-label-text: var(--stuic-color-surface-foreground);
|
|
13
|
+
--stuic-assets-preview-label-radius: var(--radius-sm);
|
|
14
|
+
|
|
15
|
+
/* Dot indicators */
|
|
16
|
+
--stuic-assets-preview-dot-size: 0.75rem;
|
|
17
|
+
--stuic-assets-preview-dot-bg: color-mix(
|
|
18
|
+
in srgb,
|
|
19
|
+
var(--stuic-color-surface) 50%,
|
|
20
|
+
transparent
|
|
21
|
+
);
|
|
22
|
+
--stuic-assets-preview-dot-bg-active: var(--stuic-color-surface);
|
|
23
|
+
--stuic-assets-preview-dot-bg-hover: var(--stuic-color-surface-hover);
|
|
24
|
+
--stuic-assets-preview-dot-border: var(--stuic-color-border);
|
|
25
|
+
|
|
26
|
+
/* Transition */
|
|
27
|
+
--stuic-assets-preview-transition: 150ms;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@layer components {
|
|
31
|
+
/* ============================================================================
|
|
32
|
+
LABELS (filename display, tooltip)
|
|
33
|
+
============================================================================ */
|
|
34
|
+
|
|
35
|
+
.stuic-assets-preview-label {
|
|
36
|
+
background: var(--stuic-assets-preview-label-bg);
|
|
37
|
+
color: var(--stuic-assets-preview-label-text);
|
|
38
|
+
border-radius: var(--stuic-assets-preview-label-radius);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* ============================================================================
|
|
42
|
+
DOT INDICATORS (pagination dots)
|
|
43
|
+
============================================================================ */
|
|
44
|
+
|
|
45
|
+
.stuic-assets-preview-dot {
|
|
46
|
+
width: var(--stuic-assets-preview-dot-size);
|
|
47
|
+
height: var(--stuic-assets-preview-dot-size);
|
|
48
|
+
border-radius: 9999px;
|
|
49
|
+
background: var(--stuic-assets-preview-dot-bg);
|
|
50
|
+
border: 1px solid var(--stuic-assets-preview-dot-border);
|
|
51
|
+
transition: background var(--stuic-assets-preview-transition);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.stuic-assets-preview-dot:hover {
|
|
55
|
+
background: var(--stuic-assets-preview-dot-bg-hover);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.stuic-assets-preview-dot.active {
|
|
59
|
+
background: var(--stuic-assets-preview-dot-bg-active);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -60,14 +60,19 @@
|
|
|
60
60
|
el = $bindable(),
|
|
61
61
|
}: Props = $props();
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
// Icon sizes for preset sizes (visual sizes are handled by CSS)
|
|
64
|
+
const ICON_SIZES: Record<string, number> = {
|
|
65
|
+
sm: 16,
|
|
66
|
+
md: 20,
|
|
67
|
+
lg: 28,
|
|
68
|
+
xl: 32,
|
|
69
|
+
"2xl": 36,
|
|
69
70
|
};
|
|
70
71
|
|
|
72
|
+
// Check if size is a known preset
|
|
73
|
+
const isPresetSize = (s: string): s is "sm" | "md" | "lg" | "xl" | "2xl" =>
|
|
74
|
+
s in ICON_SIZES;
|
|
75
|
+
|
|
71
76
|
// Extract initials from input string (email, name, or raw initials)
|
|
72
77
|
function extractInitials(input: string, length: number): string {
|
|
73
78
|
let _input = (input || "").trim();
|
|
@@ -145,8 +150,7 @@
|
|
|
145
150
|
|
|
146
151
|
// Get icon size based on preset or custom size
|
|
147
152
|
let iconSize = $derived.by(() => {
|
|
148
|
-
|
|
149
|
-
if (preset) return preset.icon;
|
|
153
|
+
if (isPresetSize(size)) return ICON_SIZES[size];
|
|
150
154
|
|
|
151
155
|
// For custom sizes, try to parse size-N pattern
|
|
152
156
|
const match = size?.match(/size-(\d+)/);
|
|
@@ -162,25 +166,21 @@
|
|
|
162
166
|
autoColor ? generateAvatarColors(hashSource || initialsProp || "") : null
|
|
163
167
|
);
|
|
164
168
|
|
|
165
|
-
let sizeClass = $derived(SIZE_PRESETS[size]?.container || size);
|
|
166
|
-
|
|
167
169
|
let style = $derived(
|
|
168
170
|
autoColor && colors
|
|
169
171
|
? `background-color: ${colors.bg}; color: ${colors.text};`
|
|
170
172
|
: undefined
|
|
171
173
|
);
|
|
172
174
|
|
|
175
|
+
// Build class string - base class for CSS targeting, allow user overrides via classProp
|
|
173
176
|
let baseClass = $derived(
|
|
174
177
|
twMerge(
|
|
175
178
|
"stuic-avatar",
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"bg-neutral-200 text-neutral-700 dark:bg-neutral-700 dark:text-neutral-200",
|
|
180
|
-
sizeClass,
|
|
179
|
+
// Custom size classes when not using preset (preset sizes handled by data-size in CSS)
|
|
180
|
+
!isPresetSize(size) && size,
|
|
181
|
+
// User-provided Tailwind overrides for colors (only when not using autoColor)
|
|
181
182
|
!autoColor && bg,
|
|
182
183
|
!autoColor && textColor,
|
|
183
|
-
onclick && "select-none cursor-pointer",
|
|
184
184
|
classProp
|
|
185
185
|
)
|
|
186
186
|
);
|
|
@@ -191,7 +191,15 @@
|
|
|
191
191
|
</script>
|
|
192
192
|
|
|
193
193
|
{#if onclick}
|
|
194
|
-
<button
|
|
194
|
+
<button
|
|
195
|
+
bind:this={el}
|
|
196
|
+
type="button"
|
|
197
|
+
class={baseClass}
|
|
198
|
+
{style}
|
|
199
|
+
{onclick}
|
|
200
|
+
data-size={isPresetSize(size) ? size : undefined}
|
|
201
|
+
data-interactive="true"
|
|
202
|
+
>
|
|
195
203
|
{#if renderMode === "photo"}
|
|
196
204
|
<img {src} {alt} class="size-full object-cover" onerror={handleImageError} />
|
|
197
205
|
{:else if renderMode === "initials"}
|
|
@@ -201,7 +209,12 @@
|
|
|
201
209
|
{/if}
|
|
202
210
|
</button>
|
|
203
211
|
{:else}
|
|
204
|
-
<div
|
|
212
|
+
<div
|
|
213
|
+
bind:this={el}
|
|
214
|
+
class={baseClass}
|
|
215
|
+
{style}
|
|
216
|
+
data-size={isPresetSize(size) ? size : undefined}
|
|
217
|
+
>
|
|
205
218
|
{#if renderMode === "photo"}
|
|
206
219
|
<img {src} {alt} class="size-full object-cover" onerror={handleImageError} />
|
|
207
220
|
{:else if renderMode === "initials"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Avatar
|
|
2
|
+
|
|
3
|
+
A flexible avatar component that displays user photos, initials, or icons with automatic fallback handling and optional deterministic color generation.
|
|
4
|
+
|
|
5
|
+
## Props
|
|
6
|
+
|
|
7
|
+
| Prop | Type | Default | Description |
|
|
8
|
+
|------|------|---------|-------------|
|
|
9
|
+
| `src` | `string` | - | Photo URL - when provided, renders in photo mode |
|
|
10
|
+
| `alt` | `string` | - | Alt text for photo mode |
|
|
11
|
+
| `initials` | `string` | - | String to extract initials from. Supports: "AB", "John Doe", or "john.doe@example.com" |
|
|
12
|
+
| `initialsLength` | `number` | `2` | Maximum length of extracted initials |
|
|
13
|
+
| `icon` | `IconFn` | - | Icon function to display - when provided alone, renders in icon mode |
|
|
14
|
+
| `fallback` | `AvatarFallback` | `"icon"` | Fallback when photo fails to load |
|
|
15
|
+
| `hashSource` | `string` | - | String for color hash calculation (e.g., email, user ID). Falls back to `initials` |
|
|
16
|
+
| `size` | `"sm" \| "md" \| "lg" \| "xl" \| "2xl" \| string` | `"md"` | Size preset or custom Tailwind size class |
|
|
17
|
+
| `onclick` | `(event: MouseEvent) => void` | - | Click handler - when provided, renders as a button |
|
|
18
|
+
| `bg` | `string` | - | Background color (Tailwind class). Ignored if `autoColor=true` |
|
|
19
|
+
| `textColor` | `string` | - | Text color (Tailwind class). Ignored if `autoColor=true` |
|
|
20
|
+
| `autoColor` | `boolean` | `false` | Generate deterministic pastel colors from hashSource/initials |
|
|
21
|
+
| `class` | `string` | - | Additional CSS classes |
|
|
22
|
+
| `el` | `HTMLElement` | - | Bindable element reference |
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Basic
|
|
27
|
+
|
|
28
|
+
```svelte
|
|
29
|
+
<script lang="ts">
|
|
30
|
+
import { Avatar } from '@marianmeres/stuic';
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<!-- Photo -->
|
|
34
|
+
<Avatar src="/path/to/photo.jpg" alt="John Doe" />
|
|
35
|
+
|
|
36
|
+
<!-- Initials -->
|
|
37
|
+
<Avatar initials="John Doe" />
|
|
38
|
+
|
|
39
|
+
<!-- Icon (default fallback) -->
|
|
40
|
+
<Avatar />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Sizes
|
|
44
|
+
|
|
45
|
+
```svelte
|
|
46
|
+
<Avatar initials="AB" size="sm" />
|
|
47
|
+
<Avatar initials="AB" size="md" />
|
|
48
|
+
<Avatar initials="AB" size="lg" />
|
|
49
|
+
<Avatar initials="AB" size="xl" />
|
|
50
|
+
<Avatar initials="AB" size="2xl" />
|
|
51
|
+
|
|
52
|
+
<!-- Custom size with Tailwind -->
|
|
53
|
+
<Avatar initials="AB" size="size-20" />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Auto Color
|
|
57
|
+
|
|
58
|
+
Generate deterministic pastel colors based on a hash source:
|
|
59
|
+
|
|
60
|
+
```svelte
|
|
61
|
+
<!-- Same input always produces same color -->
|
|
62
|
+
<Avatar initials="john@example.com" autoColor />
|
|
63
|
+
<Avatar initials="Jane Smith" autoColor hashSource="user-123" />
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Photo with Fallback
|
|
67
|
+
|
|
68
|
+
```svelte
|
|
69
|
+
<!-- Falls back to icon on error -->
|
|
70
|
+
<Avatar src="/maybe-broken.jpg" fallback="icon" />
|
|
71
|
+
|
|
72
|
+
<!-- Falls back to initials on error -->
|
|
73
|
+
<Avatar src="/maybe-broken.jpg" fallback="initials" initials="JD" />
|
|
74
|
+
|
|
75
|
+
<!-- Falls back to specific initials -->
|
|
76
|
+
<Avatar src="/maybe-broken.jpg" fallback={{ initials: "AB" }} />
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### As Button
|
|
80
|
+
|
|
81
|
+
```svelte
|
|
82
|
+
<Avatar
|
|
83
|
+
src="/photo.jpg"
|
|
84
|
+
onclick={() => console.log('clicked')}
|
|
85
|
+
/>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Custom Colors
|
|
89
|
+
|
|
90
|
+
```svelte
|
|
91
|
+
<!-- Using Tailwind classes -->
|
|
92
|
+
<Avatar initials="AB" bg="bg-blue-500" textColor="text-white" />
|
|
93
|
+
|
|
94
|
+
<!-- Using inline style for component tokens -->
|
|
95
|
+
<Avatar
|
|
96
|
+
initials="AB"
|
|
97
|
+
style="--stuic-avatar-bg: #3b82f6; --stuic-avatar-fg: white;"
|
|
98
|
+
/>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## CSS Variables
|
|
102
|
+
|
|
103
|
+
### Component Tokens
|
|
104
|
+
|
|
105
|
+
Override globally in `:root` or locally via `style` prop:
|
|
106
|
+
|
|
107
|
+
| Variable | Default | Description |
|
|
108
|
+
|----------|---------|-------------|
|
|
109
|
+
| `--stuic-avatar-radius` | `9999px` | Border radius (circle by default) |
|
|
110
|
+
| `--stuic-avatar-font-weight` | `--font-weight-medium` | Font weight for initials |
|
|
111
|
+
| `--stuic-avatar-transition` | `150ms` | Transition duration |
|
|
112
|
+
| `--stuic-avatar-bg` | `--stuic-color-muted` | Default background color |
|
|
113
|
+
| `--stuic-avatar-fg` | `--stuic-color-muted-foreground` | Default text/icon color |
|
|
114
|
+
| `--stuic-avatar-ring-width` | `3px` | Focus ring width (button mode) |
|
|
115
|
+
| `--stuic-avatar-ring-color` | `--stuic-color-ring` | Focus ring color |
|
|
116
|
+
|
|
117
|
+
### Size Tokens
|
|
118
|
+
|
|
119
|
+
Each size preset has corresponding tokens (font sizes use Tailwind CSS variables):
|
|
120
|
+
|
|
121
|
+
| Size | Width/Height | Font Size |
|
|
122
|
+
|------|--------------|-----------|
|
|
123
|
+
| `sm` | `--stuic-avatar-size-sm` (2rem) | `--stuic-avatar-font-size-sm` (`--text-xs`) |
|
|
124
|
+
| `md` | `--stuic-avatar-size-md` (2.5rem) | `--stuic-avatar-font-size-md` (`--text-base`) |
|
|
125
|
+
| `lg` | `--stuic-avatar-size-lg` (3rem) | `--stuic-avatar-font-size-lg` (`--text-lg`) |
|
|
126
|
+
| `xl` | `--stuic-avatar-size-xl` (3.5rem) | `--stuic-avatar-font-size-xl` (`--text-xl`) |
|
|
127
|
+
| `2xl` | `--stuic-avatar-size-2xl` (4rem) | `--stuic-avatar-font-size-2xl` (`--text-2xl`) |
|
|
128
|
+
|
|
129
|
+
### Customization Examples
|
|
130
|
+
|
|
131
|
+
```css
|
|
132
|
+
/* Global override - square avatars */
|
|
133
|
+
:root {
|
|
134
|
+
--stuic-avatar-radius: 0.5rem;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Global override - larger default size */
|
|
138
|
+
:root {
|
|
139
|
+
--stuic-avatar-size-md: 3rem;
|
|
140
|
+
--stuic-avatar-font-size-md: 1.25rem;
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```svelte
|
|
145
|
+
<!-- Local override - square avatar -->
|
|
146
|
+
<Avatar initials="AB" style="--stuic-avatar-radius: 0.25rem;" />
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Data Attributes
|
|
150
|
+
|
|
151
|
+
The component uses data attributes for CSS styling:
|
|
152
|
+
|
|
153
|
+
| Attribute | Values | Description |
|
|
154
|
+
|-----------|--------|-------------|
|
|
155
|
+
| `data-size` | `sm`, `md`, `lg`, `xl`, `2xl` | Size preset (only for preset sizes) |
|
|
156
|
+
| `data-interactive` | `true` | Present when `onclick` is provided |
|
|
157
|
+
|
|
158
|
+
## Theming
|
|
159
|
+
|
|
160
|
+
The Avatar component uses theme tokens for default colors:
|
|
161
|
+
|
|
162
|
+
- Background: `--stuic-color-muted`
|
|
163
|
+
- Foreground: `--stuic-color-muted-foreground`
|
|
164
|
+
- Focus ring: `--stuic-color-ring`
|
|
165
|
+
|
|
166
|
+
These automatically adapt to light/dark mode when using the theming system.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
AVATAR COMPONENT TOKENS
|
|
3
|
+
Override globally: :root { --stuic-avatar-radius: 0.5rem; }
|
|
4
|
+
Override locally: <Avatar style="--stuic-avatar-radius: 0;">
|
|
5
|
+
============================================================================ */
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
/* Component-level customization tokens */
|
|
9
|
+
--stuic-avatar-radius: 9999px;
|
|
10
|
+
--stuic-avatar-font-weight: var(--font-weight-medium);
|
|
11
|
+
--stuic-avatar-transition: 150ms;
|
|
12
|
+
|
|
13
|
+
/* Default colors (uses muted role colors) */
|
|
14
|
+
--stuic-avatar-bg: var(--stuic-color-muted);
|
|
15
|
+
--stuic-avatar-fg: var(--stuic-color-muted-foreground);
|
|
16
|
+
|
|
17
|
+
/* Focus ring (for interactive/button mode) */
|
|
18
|
+
--stuic-avatar-ring-width: 3px;
|
|
19
|
+
--stuic-avatar-ring-offset: 0px;
|
|
20
|
+
--stuic-avatar-ring-color: var(--stuic-color-ring);
|
|
21
|
+
|
|
22
|
+
/* Size: sm */
|
|
23
|
+
--stuic-avatar-size-sm: calc(var(--spacing) * 8);
|
|
24
|
+
--stuic-avatar-font-size-sm: var(--text-xs);
|
|
25
|
+
|
|
26
|
+
/* Size: md */
|
|
27
|
+
--stuic-avatar-size-md: calc(var(--spacing) * 10);
|
|
28
|
+
--stuic-avatar-font-size-md: var(--text-base);
|
|
29
|
+
|
|
30
|
+
/* Size: lg */
|
|
31
|
+
--stuic-avatar-size-lg: calc(var(--spacing) * 12);
|
|
32
|
+
--stuic-avatar-font-size-lg: var(--text-lg);
|
|
33
|
+
|
|
34
|
+
/* Size: xl */
|
|
35
|
+
--stuic-avatar-size-xl: calc(var(--spacing) * 14);
|
|
36
|
+
--stuic-avatar-font-size-xl: var(--text-xl);
|
|
37
|
+
|
|
38
|
+
/* Size: 2xl */
|
|
39
|
+
--stuic-avatar-size-2xl: calc(var(--spacing) * 16);
|
|
40
|
+
--stuic-avatar-font-size-2xl: var(--text-2xl);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@layer components {
|
|
44
|
+
/* ============================================================================
|
|
45
|
+
BASE STYLES
|
|
46
|
+
============================================================================ */
|
|
47
|
+
|
|
48
|
+
.stuic-avatar {
|
|
49
|
+
/* Layout */
|
|
50
|
+
display: inline-flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
flex-shrink: 0;
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
|
|
56
|
+
/* Typography */
|
|
57
|
+
font-weight: var(--stuic-avatar-font-weight);
|
|
58
|
+
|
|
59
|
+
/* Box model */
|
|
60
|
+
border-radius: var(--stuic-avatar-radius);
|
|
61
|
+
|
|
62
|
+
/* Colors (can be overridden by autoColor inline styles) */
|
|
63
|
+
background: var(--stuic-avatar-bg);
|
|
64
|
+
color: var(--stuic-avatar-fg);
|
|
65
|
+
|
|
66
|
+
/* Transition for interactive mode */
|
|
67
|
+
transition:
|
|
68
|
+
background var(--stuic-avatar-transition),
|
|
69
|
+
color var(--stuic-avatar-transition),
|
|
70
|
+
opacity var(--stuic-avatar-transition);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* ============================================================================
|
|
74
|
+
SIZE VARIANTS
|
|
75
|
+
============================================================================ */
|
|
76
|
+
|
|
77
|
+
.stuic-avatar[data-size="sm"] {
|
|
78
|
+
width: var(--stuic-avatar-size-sm);
|
|
79
|
+
height: var(--stuic-avatar-size-sm);
|
|
80
|
+
font-size: var(--stuic-avatar-font-size-sm);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.stuic-avatar[data-size="md"] {
|
|
84
|
+
width: var(--stuic-avatar-size-md);
|
|
85
|
+
height: var(--stuic-avatar-size-md);
|
|
86
|
+
font-size: var(--stuic-avatar-font-size-md);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.stuic-avatar[data-size="lg"] {
|
|
90
|
+
width: var(--stuic-avatar-size-lg);
|
|
91
|
+
height: var(--stuic-avatar-size-lg);
|
|
92
|
+
font-size: var(--stuic-avatar-font-size-lg);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.stuic-avatar[data-size="xl"] {
|
|
96
|
+
width: var(--stuic-avatar-size-xl);
|
|
97
|
+
height: var(--stuic-avatar-size-xl);
|
|
98
|
+
font-size: var(--stuic-avatar-font-size-xl);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.stuic-avatar[data-size="2xl"] {
|
|
102
|
+
width: var(--stuic-avatar-size-2xl);
|
|
103
|
+
height: var(--stuic-avatar-size-2xl);
|
|
104
|
+
font-size: var(--stuic-avatar-font-size-2xl);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* ============================================================================
|
|
108
|
+
INTERACTIVE MODE (button)
|
|
109
|
+
============================================================================ */
|
|
110
|
+
|
|
111
|
+
.stuic-avatar[data-interactive] {
|
|
112
|
+
cursor: pointer;
|
|
113
|
+
user-select: none;
|
|
114
|
+
-webkit-tap-highlight-color: transparent;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.stuic-avatar[data-interactive]:hover {
|
|
118
|
+
opacity: 0.85;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.stuic-avatar[data-interactive]:active {
|
|
122
|
+
opacity: 0.75;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Focus styles */
|
|
126
|
+
.stuic-avatar[data-interactive]:focus-visible {
|
|
127
|
+
outline: var(--stuic-avatar-ring-width) solid var(--stuic-avatar-ring-color);
|
|
128
|
+
outline-offset: var(--stuic-avatar-ring-offset);
|
|
129
|
+
}
|
|
130
|
+
}
|