@placeholderco/placeholder-ui 1.0.3 → 1.0.6

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.
Files changed (136) hide show
  1. package/LICENSE +26 -26
  2. package/README.md +179 -179
  3. package/dist/display/Alert.svelte +179 -179
  4. package/dist/display/Avatar.svelte +166 -166
  5. package/dist/display/LinkCollection.svelte +161 -161
  6. package/dist/display/Paper.svelte +118 -118
  7. package/dist/form/Autocomplete.svelte +223 -191
  8. package/dist/form/Autocomplete.svelte.d.ts +3 -1
  9. package/dist/form/AutocompleteMulti.svelte +356 -0
  10. package/dist/form/AutocompleteMulti.svelte.d.ts +28 -0
  11. package/dist/form/Checkbox.svelte +201 -201
  12. package/dist/form/Chips.svelte +128 -128
  13. package/dist/form/ComboBox.svelte +158 -158
  14. package/dist/form/ComboBox.svelte.d.ts +1 -1
  15. package/dist/form/ComboBoxItemBuilder.svelte +460 -460
  16. package/dist/form/ComboBoxMulti.svelte +197 -197
  17. package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
  18. package/dist/form/CronBuilder.svelte +693 -693
  19. package/dist/form/DatePicker.svelte +672 -672
  20. package/dist/form/DateTimePicker.svelte +712 -712
  21. package/dist/form/FileInput.svelte +235 -235
  22. package/dist/form/FormGroup.svelte +68 -68
  23. package/dist/form/Number.svelte +238 -238
  24. package/dist/form/PasswordInput.svelte +252 -252
  25. package/dist/form/RadioGroup.svelte +210 -210
  26. package/dist/form/Rating.svelte +235 -235
  27. package/dist/form/SegmentedControl.svelte +149 -149
  28. package/dist/form/Select.svelte +590 -590
  29. package/dist/form/Select.svelte.d.ts +1 -1
  30. package/dist/form/SelectMulti.svelte +613 -613
  31. package/dist/form/SelectMulti.svelte.d.ts +1 -1
  32. package/dist/form/Slider.svelte +358 -358
  33. package/dist/form/Switch.svelte +147 -147
  34. package/dist/form/TextArea.svelte +148 -148
  35. package/dist/form/Textbox.svelte +228 -228
  36. package/dist/form/TimePicker.svelte +267 -267
  37. package/dist/icon/Icon.svelte +52 -52
  38. package/dist/icon/alert-octagon.svg +5 -5
  39. package/dist/icon/alert-triangle.svg +5 -5
  40. package/dist/icon/archive.svg +1 -1
  41. package/dist/icon/arrow-down.svg +1 -1
  42. package/dist/icon/arrow-left.svg +1 -1
  43. package/dist/icon/arrow-right.svg +1 -1
  44. package/dist/icon/arrow-up.svg +1 -1
  45. package/dist/icon/at.svg +1 -1
  46. package/dist/icon/bell.svg +1 -1
  47. package/dist/icon/bookmark.svg +1 -1
  48. package/dist/icon/calendar.svg +1 -1
  49. package/dist/icon/camera.svg +1 -1
  50. package/dist/icon/chart-bar.svg +1 -1
  51. package/dist/icon/chart-line.svg +1 -1
  52. package/dist/icon/chart-pie.svg +1 -1
  53. package/dist/icon/checkbox.svg +1 -1
  54. package/dist/icon/checklist.svg +1 -1
  55. package/dist/icon/circle-check.svg +1 -1
  56. package/dist/icon/circle-x.svg +1 -1
  57. package/dist/icon/clock.svg +1 -1
  58. package/dist/icon/credit-card.svg +1 -1
  59. package/dist/icon/dots-vertical.svg +1 -1
  60. package/dist/icon/dots.svg +1 -1
  61. package/dist/icon/external-link.svg +1 -1
  62. package/dist/icon/eye-off.svg +1 -1
  63. package/dist/icon/eye.svg +1 -1
  64. package/dist/icon/filter.svg +1 -1
  65. package/dist/icon/fingerprint.svg +1 -1
  66. package/dist/icon/flag.svg +1 -1
  67. package/dist/icon/heart.svg +1 -1
  68. package/dist/icon/home.svg +1 -1
  69. package/dist/icon/key.svg +1 -1
  70. package/dist/icon/list-check.svg +1 -1
  71. package/dist/icon/login.svg +1 -1
  72. package/dist/icon/logout.svg +1 -1
  73. package/dist/icon/map-pin.svg +1 -1
  74. package/dist/icon/maximize.svg +1 -1
  75. package/dist/icon/microphone.svg +1 -1
  76. package/dist/icon/minimize.svg +1 -1
  77. package/dist/icon/note.svg +1 -1
  78. package/dist/icon/player-pause.svg +1 -1
  79. package/dist/icon/printer.svg +1 -1
  80. package/dist/icon/qrcode.svg +1 -1
  81. package/dist/icon/send.svg +1 -1
  82. package/dist/icon/settings.svg +1 -1
  83. package/dist/icon/share.svg +1 -1
  84. package/dist/icon/shopping-cart.svg +1 -1
  85. package/dist/icon/sort-ascending.svg +1 -1
  86. package/dist/icon/sort-descending.svg +1 -1
  87. package/dist/icon/star.svg +1 -1
  88. package/dist/icon/tag.svg +1 -1
  89. package/dist/icon/trending-down.svg +1 -1
  90. package/dist/icon/trending-up.svg +1 -1
  91. package/dist/icon/upload.svg +1 -1
  92. package/dist/icon/volume-off.svg +1 -1
  93. package/dist/icon/volume.svg +1 -1
  94. package/dist/icon/world.svg +1 -1
  95. package/dist/icon/zoom-in.svg +1 -1
  96. package/dist/icon/zoom-out.svg +1 -1
  97. package/dist/index.d.ts +1 -0
  98. package/dist/index.js +1 -0
  99. package/dist/layout/AppShell.svelte +169 -169
  100. package/dist/layout/CustomNavbar.svelte +61 -61
  101. package/dist/layout/Navbar.svelte +206 -206
  102. package/dist/layout/NavbarItemDisplay.svelte +29 -29
  103. package/dist/layout/Sidenav.svelte +712 -712
  104. package/dist/styles/components.css +199 -199
  105. package/dist/styles/dark.css +146 -146
  106. package/dist/styles/index.css +116 -116
  107. package/dist/styles/reset.css +110 -110
  108. package/dist/styles/semantic.css +86 -86
  109. package/dist/styles/tokens.css +203 -197
  110. package/dist/styles/utilities.css +523 -523
  111. package/dist/ui/Accordion.svelte +289 -289
  112. package/dist/ui/ActionIcon.svelte +76 -76
  113. package/dist/ui/Badge.svelte +329 -279
  114. package/dist/ui/Breadcrumbs.svelte +131 -131
  115. package/dist/ui/Button.svelte +432 -370
  116. package/dist/ui/ButtonVariant.d.ts +1 -1
  117. package/dist/ui/Dialog.svelte +307 -307
  118. package/dist/ui/Drawer.svelte +524 -524
  119. package/dist/ui/Dropdown.svelte +97 -97
  120. package/dist/ui/Dropzone.svelte +122 -122
  121. package/dist/ui/Link.svelte +32 -32
  122. package/dist/ui/Loader.svelte +70 -70
  123. package/dist/ui/LoadingOverlay.svelte +53 -53
  124. package/dist/ui/Pagination.svelte +135 -135
  125. package/dist/ui/Popover.svelte +225 -225
  126. package/dist/ui/Progress.svelte +191 -191
  127. package/dist/ui/RingProgress.svelte +141 -141
  128. package/dist/ui/Skeleton.svelte +85 -85
  129. package/dist/ui/Stepper.svelte +355 -355
  130. package/dist/ui/Table.svelte +345 -345
  131. package/dist/ui/Tabs.svelte +146 -146
  132. package/dist/ui/ThemeSwitcher.svelte +39 -39
  133. package/dist/ui/Timeline.svelte +225 -225
  134. package/dist/ui/Toaster.svelte +6 -6
  135. package/dist/ui/Tooltip.svelte +434 -434
  136. package/package.json +14 -14
@@ -1,179 +1,179 @@
1
- <script lang="ts">
2
- import type { Snippet } from "svelte";
3
- import Icon from "../icon/Icon.svelte";
4
- import { iconInfoCircle, iconAlertTriangle, iconCircleCheck, iconCircleX, iconX } from "../icon/index.js";
5
-
6
- type AlertVariant = "default" | "secondary" | "success" | "warning" | "danger" | "info";
7
-
8
- interface Props {
9
- variant?: AlertVariant;
10
- title?: string;
11
- dismissible?: boolean;
12
- children?: Snippet;
13
- ondismiss?: () => void;
14
- class?: string;
15
- }
16
-
17
- let { variant = "default", title, dismissible = false, children, ondismiss, class: className = "" }: Props = $props();
18
-
19
- let visible = $state(true);
20
-
21
- const icons: Record<AlertVariant, string> = {
22
- default: iconInfoCircle,
23
- secondary: iconInfoCircle,
24
- info: iconInfoCircle,
25
- success: iconCircleCheck,
26
- warning: iconAlertTriangle,
27
- danger: iconCircleX,
28
- };
29
-
30
- function dismiss() {
31
- visible = false;
32
- ondismiss?.();
33
- }
34
- </script>
35
-
36
- {#if visible}
37
- <div class="alert alert-{variant} {className}" role="alert">
38
- <div class="alert-icon">
39
- <Icon svg={icons[variant]} size="20px" />
40
- </div>
41
- <div class="alert-content">
42
- {#if title}
43
- <div class="alert-title">{title}</div>
44
- {/if}
45
- {#if children}
46
- <div class="alert-message">
47
- {@render children()}
48
- </div>
49
- {/if}
50
- </div>
51
- {#if dismissible}
52
- <button class="alert-dismiss" onclick={dismiss} aria-label="Dismiss">
53
- <Icon svg={iconX} size="16px" />
54
- </button>
55
- {/if}
56
- </div>
57
- {/if}
58
-
59
- <style>
60
- .alert {
61
- display: flex;
62
- align-items: flex-start;
63
- gap: var(--pui-spacing-3);
64
- padding: var(--pui-spacing-4);
65
- border-radius: var(--pui-radius-md);
66
- border: 1px solid;
67
- }
68
-
69
- .alert-default {
70
- background-color: rgba(122, 74, 153, 0.15);
71
- color: rgb(122, 74, 153);
72
- border-color: var(--pui-color-primary);
73
- }
74
-
75
- .alert-secondary {
76
- background-color: var(--pui-color-gray-50);
77
- border-color: var(--pui-color-gray-200);
78
- color: var(--pui-color-gray-800);
79
- }
80
-
81
- .alert-info {
82
- background-color: var(--pui-alert-info-bg);
83
- border-color: var(--pui-alert-info-border);
84
- color: var(--pui-color-info-dark, var(--pui-color-info));
85
- }
86
-
87
- .alert-success {
88
- background-color: var(--pui-alert-success-bg);
89
- border-color: var(--pui-alert-success-border);
90
- color: var(--pui-color-success-dark, var(--pui-color-success));
91
- }
92
-
93
- .alert-warning {
94
- background-color: var(--pui-alert-warning-bg);
95
- border-color: var(--pui-alert-warning-border);
96
- color: var(--pui-color-warning-dark, var(--pui-color-warning));
97
- }
98
-
99
- .alert-danger {
100
- background-color: var(--pui-alert-danger-bg);
101
- border-color: var(--pui-alert-danger-border);
102
- color: var(--pui-color-danger, var(--pui-color-danger));
103
- }
104
-
105
- :global(.dark) .alert-default {
106
- background-color: rgba(122, 74, 153, 0.15);
107
- color: var(--pui-color-primary-light);
108
- border-color: var(--pui-color-primary);
109
- }
110
-
111
- :global(.dark) .alert-secondary {
112
- background-color: var(--pui-color-dark-200);
113
- border-color: var(--pui-color-dark-border);
114
- color: var(--pui-color-gray-100);
115
- }
116
-
117
- :global(.dark) .alert-info {
118
- background-color: var(--pui-alert-info-bg);
119
- border-color: var(--pui-alert-info-border);
120
- color: #6edff6;
121
- }
122
-
123
- :global(.dark) .alert-success {
124
- background-color: var(--pui-alert-success-bg);
125
- border-color: var(--pui-alert-success-border);
126
- color: #75b798;
127
- }
128
-
129
- :global(.dark) .alert-warning {
130
- background-color: var(--pui-alert-warning-bg);
131
- border-color: var(--pui-alert-warning-border);
132
- color: #ffda6a;
133
- }
134
-
135
- :global(.dark) .alert-danger {
136
- background-color: var(--pui-alert-danger-bg);
137
- border-color: var(--pui-alert-danger-border);
138
- color: #ea868f;
139
- }
140
-
141
- .alert-icon {
142
- flex-shrink: 0;
143
- display: flex;
144
- align-items: center;
145
- }
146
-
147
- .alert-content {
148
- flex: 1;
149
- min-width: 0;
150
- }
151
-
152
- .alert-title {
153
- font-weight: var(--pui-font-weight-semibold);
154
- margin-bottom: var(--pui-spacing-1);
155
- }
156
-
157
- .alert-message {
158
- line-height: var(--pui-line-height-relaxed);
159
- }
160
-
161
- .alert-dismiss {
162
- flex-shrink: 0;
163
- display: flex;
164
- align-items: center;
165
- justify-content: center;
166
- background: none;
167
- border: none;
168
- cursor: pointer;
169
- padding: var(--pui-spacing-1);
170
- border-radius: var(--pui-radius-sm);
171
- color: inherit;
172
- opacity: 0.7;
173
- transition: opacity var(--pui-transition-fast) var(--pui-ease-in-out);
174
- }
175
-
176
- .alert-dismiss:hover {
177
- opacity: 1;
178
- }
179
- </style>
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import Icon from "../icon/Icon.svelte";
4
+ import { iconInfoCircle, iconAlertTriangle, iconCircleCheck, iconCircleX, iconX } from "../icon/index.js";
5
+
6
+ type AlertVariant = "default" | "secondary" | "success" | "warning" | "danger" | "info";
7
+
8
+ interface Props {
9
+ variant?: AlertVariant;
10
+ title?: string;
11
+ dismissible?: boolean;
12
+ children?: Snippet;
13
+ ondismiss?: () => void;
14
+ class?: string;
15
+ }
16
+
17
+ let { variant = "default", title, dismissible = false, children, ondismiss, class: className = "" }: Props = $props();
18
+
19
+ let visible = $state(true);
20
+
21
+ const icons: Record<AlertVariant, string> = {
22
+ default: iconInfoCircle,
23
+ secondary: iconInfoCircle,
24
+ info: iconInfoCircle,
25
+ success: iconCircleCheck,
26
+ warning: iconAlertTriangle,
27
+ danger: iconCircleX,
28
+ };
29
+
30
+ function dismiss() {
31
+ visible = false;
32
+ ondismiss?.();
33
+ }
34
+ </script>
35
+
36
+ {#if visible}
37
+ <div class="alert alert-{variant} {className}" role="alert">
38
+ <div class="alert-icon">
39
+ <Icon svg={icons[variant]} size="20px" />
40
+ </div>
41
+ <div class="alert-content">
42
+ {#if title}
43
+ <div class="alert-title">{title}</div>
44
+ {/if}
45
+ {#if children}
46
+ <div class="alert-message">
47
+ {@render children()}
48
+ </div>
49
+ {/if}
50
+ </div>
51
+ {#if dismissible}
52
+ <button class="alert-dismiss" onclick={dismiss} aria-label="Dismiss">
53
+ <Icon svg={iconX} size="16px" />
54
+ </button>
55
+ {/if}
56
+ </div>
57
+ {/if}
58
+
59
+ <style>
60
+ .alert {
61
+ display: flex;
62
+ align-items: flex-start;
63
+ gap: var(--pui-spacing-3);
64
+ padding: var(--pui-spacing-4);
65
+ border-radius: var(--pui-radius-md);
66
+ border: 1px solid;
67
+ }
68
+
69
+ .alert-default {
70
+ background-color: rgba(122, 74, 153, 0.15);
71
+ color: rgb(122, 74, 153);
72
+ border-color: var(--pui-color-primary);
73
+ }
74
+
75
+ .alert-secondary {
76
+ background-color: var(--pui-color-gray-50);
77
+ border-color: var(--pui-color-gray-200);
78
+ color: var(--pui-color-gray-800);
79
+ }
80
+
81
+ .alert-info {
82
+ background-color: var(--pui-alert-info-bg);
83
+ border-color: var(--pui-alert-info-border);
84
+ color: var(--pui-color-info-dark, var(--pui-color-info));
85
+ }
86
+
87
+ .alert-success {
88
+ background-color: var(--pui-alert-success-bg);
89
+ border-color: var(--pui-alert-success-border);
90
+ color: var(--pui-color-success-dark, var(--pui-color-success));
91
+ }
92
+
93
+ .alert-warning {
94
+ background-color: var(--pui-alert-warning-bg);
95
+ border-color: var(--pui-alert-warning-border);
96
+ color: var(--pui-color-warning-dark, var(--pui-color-warning));
97
+ }
98
+
99
+ .alert-danger {
100
+ background-color: var(--pui-alert-danger-bg);
101
+ border-color: var(--pui-alert-danger-border);
102
+ color: var(--pui-color-danger, var(--pui-color-danger));
103
+ }
104
+
105
+ :global(.dark) .alert-default {
106
+ background-color: rgba(122, 74, 153, 0.15);
107
+ color: var(--pui-color-primary-light);
108
+ border-color: var(--pui-color-primary);
109
+ }
110
+
111
+ :global(.dark) .alert-secondary {
112
+ background-color: var(--pui-color-dark-200);
113
+ border-color: var(--pui-color-dark-border);
114
+ color: var(--pui-color-gray-100);
115
+ }
116
+
117
+ :global(.dark) .alert-info {
118
+ background-color: var(--pui-alert-info-bg);
119
+ border-color: var(--pui-alert-info-border);
120
+ color: #6edff6;
121
+ }
122
+
123
+ :global(.dark) .alert-success {
124
+ background-color: var(--pui-alert-success-bg);
125
+ border-color: var(--pui-alert-success-border);
126
+ color: #75b798;
127
+ }
128
+
129
+ :global(.dark) .alert-warning {
130
+ background-color: var(--pui-alert-warning-bg);
131
+ border-color: var(--pui-alert-warning-border);
132
+ color: #ffda6a;
133
+ }
134
+
135
+ :global(.dark) .alert-danger {
136
+ background-color: var(--pui-alert-danger-bg);
137
+ border-color: var(--pui-alert-danger-border);
138
+ color: #ea868f;
139
+ }
140
+
141
+ .alert-icon {
142
+ flex-shrink: 0;
143
+ display: flex;
144
+ align-items: center;
145
+ }
146
+
147
+ .alert-content {
148
+ flex: 1;
149
+ min-width: 0;
150
+ }
151
+
152
+ .alert-title {
153
+ font-weight: var(--pui-font-weight-semibold);
154
+ margin-bottom: var(--pui-spacing-1);
155
+ }
156
+
157
+ .alert-message {
158
+ line-height: var(--pui-line-height-relaxed);
159
+ }
160
+
161
+ .alert-dismiss {
162
+ flex-shrink: 0;
163
+ display: flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ background: none;
167
+ border: none;
168
+ cursor: pointer;
169
+ padding: var(--pui-spacing-1);
170
+ border-radius: var(--pui-radius-sm);
171
+ color: inherit;
172
+ opacity: 0.7;
173
+ transition: opacity var(--pui-transition-fast) var(--pui-ease-in-out);
174
+ }
175
+
176
+ .alert-dismiss:hover {
177
+ opacity: 1;
178
+ }
179
+ </style>
@@ -1,166 +1,166 @@
1
- <script lang="ts">
2
- import Icon from '../icon/Icon.svelte';
3
-
4
- interface Props {
5
- src: string;
6
- alt?: string;
7
- size?: number;
8
- initials?: string;
9
- fallBackSvg?: string;
10
- class?: string;
11
- href?: string;
12
- backgroundColor?: string;
13
- shape?: 'circle' | 'rounded' | 'square';
14
- onClick?: () => void;
15
- }
16
-
17
- let {
18
- src,
19
- alt = '',
20
- initials = '',
21
- size = 2.5,
22
- class: classes = '',
23
- fallBackSvg,
24
- href,
25
- backgroundColor,
26
- shape = 'circle',
27
- onClick
28
- }: Props = $props();
29
-
30
- let imgExists = $state(true);
31
- const sizeText = $derived(size ? `${size}rem` : '');
32
- const svgSizeText = $derived(size ? `${size - 1}rem` : '');
33
- const initialsSizeText = $derived(size ? `${size / 2}rem` : '');
34
-
35
- function handleError() {
36
- imgExists = false;
37
- }
38
- </script>
39
-
40
- {#snippet img()}
41
- {#if imgExists && src}
42
- <img
43
- {src}
44
- {alt}
45
- class="avatar {shape}"
46
- style="width: {sizeText}; height: {sizeText};"
47
- onerror={handleError}
48
- />
49
- {:else if initials}
50
- <div
51
- class="avatar {shape}"
52
- style="font-size: {initialsSizeText}; width: {sizeText}; height: {sizeText}; {backgroundColor
53
- ? `background-color: ${backgroundColor};`
54
- : ''}"
55
- >
56
- {initials}
57
- </div>
58
- {:else if fallBackSvg}
59
- <div
60
- class="avatar {shape}"
61
- style="width: {sizeText}; height: {sizeText}; {backgroundColor
62
- ? `background-color: ${backgroundColor};`
63
- : ''}"
64
- >
65
- <Icon svg={fallBackSvg} size={svgSizeText} />
66
- </div>
67
- {:else}
68
- <div
69
- class="avatar {shape}"
70
- style="width: {sizeText}; height: {sizeText}; background-color: {backgroundColor
71
- ? backgroundColor
72
- : '#ccc'};"
73
- >
74
- ?
75
- </div>
76
- {/if}
77
- {/snippet}
78
-
79
- <div class="avatar-container {classes}">
80
- {#if onClick}
81
- <button onclick={onClick}>
82
- {@render img()}
83
- </button>
84
- {:else if href}
85
- <a {href}>
86
- {@render img()}
87
- </a>
88
- {:else}
89
- {@render img()}
90
- {/if}
91
- </div>
92
-
93
- <style>
94
- .avatar-container {
95
- display: inline-flex;
96
- position: relative;
97
- }
98
-
99
- .avatar-container button,
100
- .avatar-container a {
101
- display: inline-flex;
102
- background: none;
103
- border: none;
104
- color: var(--pui-color-black);
105
- padding: 0;
106
- transition: all var(--pui-transition-fast) var(--pui-ease-in-out);
107
- text-decoration: none;
108
-
109
- .avatar {
110
- cursor: pointer;
111
-
112
- &:hover {
113
- background: var(--pui-color-primary);
114
- color: var(--pui-text-primary);
115
- }
116
- }
117
-
118
- &:hover {
119
- transform: scale(1.1);
120
- }
121
- }
122
-
123
- .avatar {
124
- object-fit: cover;
125
- background: var(--pui-color-gray-200);
126
- display: flex;
127
- justify-content: center;
128
- align-items: center;
129
- transition: all var(--pui-transition-fast) var(--pui-ease-in-out);
130
- }
131
-
132
- .avatar.circle {
133
- border-radius: var(--pui-radius-full);
134
- }
135
-
136
- .avatar.rounded {
137
- border-radius: var(--pui-radius-lg);
138
- }
139
-
140
- .initials {
141
- display: flex;
142
- align-items: center;
143
- justify-content: center;
144
- color: var(--pui-color-white);
145
- font-weight: var(--pui-font-weight-bold);
146
- border-radius: var(--pui-radius-full);
147
- }
148
-
149
- :global(.dark) {
150
- .avatar-container button,
151
- .avatar-container a {
152
- color: var(--pui-text-primary);
153
-
154
- .avatar {
155
- &:hover {
156
- background: var(--pui-color-secondary);
157
- color: var(--pui-color-black);
158
- }
159
- }
160
- }
161
-
162
- .avatar {
163
- background: var(--pui-color-dark-500);
164
- }
165
- }
166
- </style>
1
+ <script lang="ts">
2
+ import Icon from '../icon/Icon.svelte';
3
+
4
+ interface Props {
5
+ src: string;
6
+ alt?: string;
7
+ size?: number;
8
+ initials?: string;
9
+ fallBackSvg?: string;
10
+ class?: string;
11
+ href?: string;
12
+ backgroundColor?: string;
13
+ shape?: 'circle' | 'rounded' | 'square';
14
+ onClick?: () => void;
15
+ }
16
+
17
+ let {
18
+ src,
19
+ alt = '',
20
+ initials = '',
21
+ size = 2.5,
22
+ class: classes = '',
23
+ fallBackSvg,
24
+ href,
25
+ backgroundColor,
26
+ shape = 'circle',
27
+ onClick
28
+ }: Props = $props();
29
+
30
+ let imgExists = $state(true);
31
+ const sizeText = $derived(size ? `${size}rem` : '');
32
+ const svgSizeText = $derived(size ? `${size - 1}rem` : '');
33
+ const initialsSizeText = $derived(size ? `${size / 2}rem` : '');
34
+
35
+ function handleError() {
36
+ imgExists = false;
37
+ }
38
+ </script>
39
+
40
+ {#snippet img()}
41
+ {#if imgExists && src}
42
+ <img
43
+ {src}
44
+ {alt}
45
+ class="avatar {shape}"
46
+ style="width: {sizeText}; height: {sizeText};"
47
+ onerror={handleError}
48
+ />
49
+ {:else if initials}
50
+ <div
51
+ class="avatar {shape}"
52
+ style="font-size: {initialsSizeText}; width: {sizeText}; height: {sizeText}; {backgroundColor
53
+ ? `background-color: ${backgroundColor};`
54
+ : ''}"
55
+ >
56
+ {initials}
57
+ </div>
58
+ {:else if fallBackSvg}
59
+ <div
60
+ class="avatar {shape}"
61
+ style="width: {sizeText}; height: {sizeText}; {backgroundColor
62
+ ? `background-color: ${backgroundColor};`
63
+ : ''}"
64
+ >
65
+ <Icon svg={fallBackSvg} size={svgSizeText} />
66
+ </div>
67
+ {:else}
68
+ <div
69
+ class="avatar {shape}"
70
+ style="width: {sizeText}; height: {sizeText}; background-color: {backgroundColor
71
+ ? backgroundColor
72
+ : '#ccc'};"
73
+ >
74
+ ?
75
+ </div>
76
+ {/if}
77
+ {/snippet}
78
+
79
+ <div class="avatar-container {classes}">
80
+ {#if onClick}
81
+ <button onclick={onClick}>
82
+ {@render img()}
83
+ </button>
84
+ {:else if href}
85
+ <a {href}>
86
+ {@render img()}
87
+ </a>
88
+ {:else}
89
+ {@render img()}
90
+ {/if}
91
+ </div>
92
+
93
+ <style>
94
+ .avatar-container {
95
+ display: inline-flex;
96
+ position: relative;
97
+ }
98
+
99
+ .avatar-container button,
100
+ .avatar-container a {
101
+ display: inline-flex;
102
+ background: none;
103
+ border: none;
104
+ color: var(--pui-color-black);
105
+ padding: 0;
106
+ transition: all var(--pui-transition-fast) var(--pui-ease-in-out);
107
+ text-decoration: none;
108
+
109
+ .avatar {
110
+ cursor: pointer;
111
+
112
+ &:hover {
113
+ background: var(--pui-color-primary);
114
+ color: var(--pui-text-primary);
115
+ }
116
+ }
117
+
118
+ &:hover {
119
+ transform: scale(1.1);
120
+ }
121
+ }
122
+
123
+ .avatar {
124
+ object-fit: cover;
125
+ background: var(--pui-color-gray-200);
126
+ display: flex;
127
+ justify-content: center;
128
+ align-items: center;
129
+ transition: all var(--pui-transition-fast) var(--pui-ease-in-out);
130
+ }
131
+
132
+ .avatar.circle {
133
+ border-radius: var(--pui-radius-full);
134
+ }
135
+
136
+ .avatar.rounded {
137
+ border-radius: var(--pui-radius-lg);
138
+ }
139
+
140
+ .initials {
141
+ display: flex;
142
+ align-items: center;
143
+ justify-content: center;
144
+ color: var(--pui-color-white);
145
+ font-weight: var(--pui-font-weight-bold);
146
+ border-radius: var(--pui-radius-full);
147
+ }
148
+
149
+ :global(.dark) {
150
+ .avatar-container button,
151
+ .avatar-container a {
152
+ color: var(--pui-text-primary);
153
+
154
+ .avatar {
155
+ &:hover {
156
+ background: var(--pui-color-secondary);
157
+ color: var(--pui-color-black);
158
+ }
159
+ }
160
+ }
161
+
162
+ .avatar {
163
+ background: var(--pui-color-dark-500);
164
+ }
165
+ }
166
+ </style>