@marianmeres/stuic 2.51.0 → 2.52.0
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/dist/components/Avatar/Avatar.svelte +237 -0
- package/dist/components/Avatar/Avatar.svelte.d.ts +40 -0
- package/dist/components/Avatar/index.d.ts +1 -0
- package/dist/components/Avatar/index.js +1 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/components/AvatarInitials/AvatarInitials.svelte +0 -113
- package/dist/components/AvatarInitials/AvatarInitials.svelte.d.ts +0 -23
- package/dist/components/AvatarInitials/README.md +0 -169
- package/dist/components/AvatarInitials/index.d.ts +0 -1
- package/dist/components/AvatarInitials/index.js +0 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
export type IconFn = (opts?: { size?: number; class?: string }) => string;
|
|
3
|
+
export type AvatarFallback = "icon" | "initials" | { icon: IconFn } | { initials: string };
|
|
4
|
+
|
|
5
|
+
export interface Props {
|
|
6
|
+
/** Photo URL - when provided, renders in photo mode */
|
|
7
|
+
src?: string;
|
|
8
|
+
/** Alt text for photo mode */
|
|
9
|
+
alt?: string;
|
|
10
|
+
/** String to extract initials from. Supports: "AB", "John Doe", or "john.doe@example.com" */
|
|
11
|
+
initials?: string;
|
|
12
|
+
/** Icon function to display - when provided alone, renders in icon mode */
|
|
13
|
+
icon?: IconFn;
|
|
14
|
+
/** Fallback when photo fails to load. Defaults to "icon" */
|
|
15
|
+
fallback?: AvatarFallback;
|
|
16
|
+
/** Optional string for color hash calculation (e.g., email, user ID). Falls back to `initials` */
|
|
17
|
+
hashSource?: string;
|
|
18
|
+
/** Size preset or custom Tailwind size class */
|
|
19
|
+
size?: "sm" | "md" | "lg" | "xl" | string;
|
|
20
|
+
/** Click handler - when provided, renders as a button */
|
|
21
|
+
onclick?: (event: MouseEvent) => void;
|
|
22
|
+
/** Background color (Tailwind class). Ignored if autoColor=true */
|
|
23
|
+
bg?: string;
|
|
24
|
+
/** Text color (Tailwind class). Ignored if autoColor=true */
|
|
25
|
+
textColor?: string;
|
|
26
|
+
/** Generate deterministic pastel colors from hashSource/initials */
|
|
27
|
+
autoColor?: boolean;
|
|
28
|
+
/** CSS class override */
|
|
29
|
+
class?: string;
|
|
30
|
+
/** Bindable element reference */
|
|
31
|
+
el?: HTMLDivElement | HTMLButtonElement;
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import { twMerge } from "../../utils/tw-merge.js";
|
|
37
|
+
import { generateAvatarColors } from "../../utils/avatar-colors.js";
|
|
38
|
+
import { iconUser as defaultIconUser } from "../../icons/index.js";
|
|
39
|
+
|
|
40
|
+
let {
|
|
41
|
+
src,
|
|
42
|
+
alt,
|
|
43
|
+
initials: initialsProp,
|
|
44
|
+
icon,
|
|
45
|
+
fallback = "icon",
|
|
46
|
+
hashSource,
|
|
47
|
+
size = "md",
|
|
48
|
+
onclick,
|
|
49
|
+
bg,
|
|
50
|
+
textColor,
|
|
51
|
+
autoColor = false,
|
|
52
|
+
class: classProp,
|
|
53
|
+
el = $bindable(),
|
|
54
|
+
}: Props = $props();
|
|
55
|
+
|
|
56
|
+
const SIZE_PRESETS: Record<string, { container: string; icon: number }> = {
|
|
57
|
+
sm: { container: "size-8 text-xs", icon: 16 },
|
|
58
|
+
md: { container: "size-10 text-sm", icon: 20 },
|
|
59
|
+
lg: { container: "size-14 text-base", icon: 28 },
|
|
60
|
+
xl: { container: "size-16 text-lg", icon: 32 },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Image loading state
|
|
64
|
+
let imageError = $state(false);
|
|
65
|
+
|
|
66
|
+
// Reset image state when src changes
|
|
67
|
+
$effect(() => {
|
|
68
|
+
if (src) {
|
|
69
|
+
imageError = false;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Extract initials from string
|
|
74
|
+
let extractedInitials = $derived.by(() => {
|
|
75
|
+
let _input = (initialsProp || "").trim();
|
|
76
|
+
|
|
77
|
+
if (!_input) return "?";
|
|
78
|
+
|
|
79
|
+
// Check if input looks like an email
|
|
80
|
+
if (_input.includes("@")) {
|
|
81
|
+
const username = _input.split("@")[0];
|
|
82
|
+
// Split by common separators (., _, -)
|
|
83
|
+
const parts = username.split(/[._+-]/).filter(Boolean);
|
|
84
|
+
if (parts.length > 1) {
|
|
85
|
+
_input = parts.map((p) => p.charAt(0)).join("");
|
|
86
|
+
} else {
|
|
87
|
+
_input = username;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Check if input looks like a full name (multiple words)
|
|
91
|
+
else if (_input.length > 2 && /\s/.test(_input)) {
|
|
92
|
+
_input = _input
|
|
93
|
+
.split(/\s/)
|
|
94
|
+
.map((v) => v.trim())
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
.map((v) => v.charAt(0))
|
|
97
|
+
.join("");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Extract first 2 chars, uppercase
|
|
101
|
+
return _input.slice(0, 2).toUpperCase();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Determine the current render mode
|
|
105
|
+
let renderMode = $derived.by((): "photo" | "initials" | "icon" => {
|
|
106
|
+
// Photo mode (if src provided and no error)
|
|
107
|
+
if (src && !imageError) return "photo";
|
|
108
|
+
|
|
109
|
+
// Photo error - determine fallback
|
|
110
|
+
if (src && imageError) {
|
|
111
|
+
if (fallback === "initials") return "initials";
|
|
112
|
+
if (typeof fallback === "object" && "initials" in fallback) return "initials";
|
|
113
|
+
return "icon";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// No src - determine from other props
|
|
117
|
+
if (initialsProp) return "initials";
|
|
118
|
+
return "icon";
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Get fallback initials (from fallback prop or initialsProp)
|
|
122
|
+
let fallbackInitials = $derived.by(() => {
|
|
123
|
+
if (typeof fallback === "object" && "initials" in fallback) {
|
|
124
|
+
const _input = (fallback.initials || "").trim();
|
|
125
|
+
if (!_input) return "?";
|
|
126
|
+
|
|
127
|
+
// Apply same extraction logic
|
|
128
|
+
let result = _input;
|
|
129
|
+
if (_input.includes("@")) {
|
|
130
|
+
const username = _input.split("@")[0];
|
|
131
|
+
const parts = username.split(/[._+-]/).filter(Boolean);
|
|
132
|
+
if (parts.length > 1) {
|
|
133
|
+
result = parts.map((p) => p.charAt(0)).join("");
|
|
134
|
+
} else {
|
|
135
|
+
result = username;
|
|
136
|
+
}
|
|
137
|
+
} else if (_input.length > 2 && /\s/.test(_input)) {
|
|
138
|
+
result = _input
|
|
139
|
+
.split(/\s/)
|
|
140
|
+
.map((v) => v.trim())
|
|
141
|
+
.filter(Boolean)
|
|
142
|
+
.map((v) => v.charAt(0))
|
|
143
|
+
.join("");
|
|
144
|
+
}
|
|
145
|
+
return result.slice(0, 2).toUpperCase();
|
|
146
|
+
}
|
|
147
|
+
return extractedInitials;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Get the icon to render
|
|
151
|
+
let iconToRender = $derived.by(() => {
|
|
152
|
+
// If fallback specifies a custom icon
|
|
153
|
+
if (imageError && typeof fallback === "object" && "icon" in fallback) {
|
|
154
|
+
return fallback.icon;
|
|
155
|
+
}
|
|
156
|
+
// Use provided icon or default
|
|
157
|
+
return icon || defaultIconUser;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Get icon size based on preset or custom size
|
|
161
|
+
let iconSize = $derived.by(() => {
|
|
162
|
+
const preset = SIZE_PRESETS[size];
|
|
163
|
+
if (preset) return preset.icon;
|
|
164
|
+
|
|
165
|
+
// For custom sizes, try to parse size-N pattern
|
|
166
|
+
const match = size?.match(/size-(\d+)/);
|
|
167
|
+
if (match) {
|
|
168
|
+
// size-N = N * 4px, icon should be ~50%
|
|
169
|
+
return parseInt(match[1]) * 2;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return 20; // Default fallback
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
let colors = $derived(
|
|
176
|
+
autoColor ? generateAvatarColors(hashSource || initialsProp || "") : null
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
let sizeClass = $derived(SIZE_PRESETS[size]?.container || size);
|
|
180
|
+
|
|
181
|
+
let style = $derived(
|
|
182
|
+
autoColor && colors
|
|
183
|
+
? `background-color: ${colors.bg}; color: ${colors.text};`
|
|
184
|
+
: undefined
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
let baseClass = $derived(
|
|
188
|
+
twMerge(
|
|
189
|
+
"stuic-avatar",
|
|
190
|
+
"inline-flex items-center justify-center",
|
|
191
|
+
"rounded-full font-medium shrink-0 overflow-hidden",
|
|
192
|
+
!autoColor &&
|
|
193
|
+
"bg-neutral-200 text-neutral-700 dark:bg-neutral-700 dark:text-neutral-200",
|
|
194
|
+
sizeClass,
|
|
195
|
+
!autoColor && bg,
|
|
196
|
+
!autoColor && textColor,
|
|
197
|
+
onclick && "select-none cursor-pointer",
|
|
198
|
+
classProp
|
|
199
|
+
)
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
function handleImageError() {
|
|
203
|
+
imageError = true;
|
|
204
|
+
}
|
|
205
|
+
</script>
|
|
206
|
+
|
|
207
|
+
{#if onclick}
|
|
208
|
+
<button bind:this={el} type="button" class={baseClass} {style} {onclick}>
|
|
209
|
+
{#if renderMode === "photo"}
|
|
210
|
+
<img
|
|
211
|
+
{src}
|
|
212
|
+
{alt}
|
|
213
|
+
class="size-full object-cover"
|
|
214
|
+
onerror={handleImageError}
|
|
215
|
+
/>
|
|
216
|
+
{:else if renderMode === "initials"}
|
|
217
|
+
{fallbackInitials}
|
|
218
|
+
{:else}
|
|
219
|
+
{@html iconToRender({ size: iconSize })}
|
|
220
|
+
{/if}
|
|
221
|
+
</button>
|
|
222
|
+
{:else}
|
|
223
|
+
<div bind:this={el} class={baseClass} {style}>
|
|
224
|
+
{#if renderMode === "photo"}
|
|
225
|
+
<img
|
|
226
|
+
{src}
|
|
227
|
+
{alt}
|
|
228
|
+
class="size-full object-cover"
|
|
229
|
+
onerror={handleImageError}
|
|
230
|
+
/>
|
|
231
|
+
{:else if renderMode === "initials"}
|
|
232
|
+
{fallbackInitials}
|
|
233
|
+
{:else}
|
|
234
|
+
{@html iconToRender({ size: iconSize })}
|
|
235
|
+
{/if}
|
|
236
|
+
</div>
|
|
237
|
+
{/if}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type IconFn = (opts?: {
|
|
2
|
+
size?: number;
|
|
3
|
+
class?: string;
|
|
4
|
+
}) => string;
|
|
5
|
+
export type AvatarFallback = "icon" | "initials" | {
|
|
6
|
+
icon: IconFn;
|
|
7
|
+
} | {
|
|
8
|
+
initials: string;
|
|
9
|
+
};
|
|
10
|
+
export interface Props {
|
|
11
|
+
/** Photo URL - when provided, renders in photo mode */
|
|
12
|
+
src?: string;
|
|
13
|
+
/** Alt text for photo mode */
|
|
14
|
+
alt?: string;
|
|
15
|
+
/** String to extract initials from. Supports: "AB", "John Doe", or "john.doe@example.com" */
|
|
16
|
+
initials?: string;
|
|
17
|
+
/** Icon function to display - when provided alone, renders in icon mode */
|
|
18
|
+
icon?: IconFn;
|
|
19
|
+
/** Fallback when photo fails to load. Defaults to "icon" */
|
|
20
|
+
fallback?: AvatarFallback;
|
|
21
|
+
/** Optional string for color hash calculation (e.g., email, user ID). Falls back to `initials` */
|
|
22
|
+
hashSource?: string;
|
|
23
|
+
/** Size preset or custom Tailwind size class */
|
|
24
|
+
size?: "sm" | "md" | "lg" | "xl" | string;
|
|
25
|
+
/** Click handler - when provided, renders as a button */
|
|
26
|
+
onclick?: (event: MouseEvent) => void;
|
|
27
|
+
/** Background color (Tailwind class). Ignored if autoColor=true */
|
|
28
|
+
bg?: string;
|
|
29
|
+
/** Text color (Tailwind class). Ignored if autoColor=true */
|
|
30
|
+
textColor?: string;
|
|
31
|
+
/** Generate deterministic pastel colors from hashSource/initials */
|
|
32
|
+
autoColor?: boolean;
|
|
33
|
+
/** CSS class override */
|
|
34
|
+
class?: string;
|
|
35
|
+
/** Bindable element reference */
|
|
36
|
+
el?: HTMLDivElement | HTMLButtonElement;
|
|
37
|
+
}
|
|
38
|
+
declare const Avatar: import("svelte").Component<Props, {}, "el">;
|
|
39
|
+
type Avatar = ReturnType<typeof Avatar>;
|
|
40
|
+
export default Avatar;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Avatar, type Props as AvatarProps, type IconFn, type AvatarFallback, } from "./Avatar.svelte";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Avatar, } from "./Avatar.svelte";
|
package/dist/icons/index.d.ts
CHANGED
|
@@ -34,3 +34,4 @@ export { iconLucideCheck as iconCheck } from "@marianmeres/icons-fns/lucide/icon
|
|
|
34
34
|
export { iconLucideCircle as iconCircle } from "@marianmeres/icons-fns/lucide/iconLucideCircle.js";
|
|
35
35
|
export { iconLucideSquare as iconSquare } from "@marianmeres/icons-fns/lucide/iconLucideSquare.js";
|
|
36
36
|
export { iconLucideMenu as iconMenu } from "@marianmeres/icons-fns/lucide/iconLucideMenu.js";
|
|
37
|
+
export { iconLucideUser as iconUser } from "@marianmeres/icons-fns/lucide/iconLucideUser.js";
|
package/dist/icons/index.js
CHANGED
|
@@ -38,3 +38,4 @@ export { iconLucideCheck as iconCheck } from "@marianmeres/icons-fns/lucide/icon
|
|
|
38
38
|
export { iconLucideCircle as iconCircle } from "@marianmeres/icons-fns/lucide/iconLucideCircle.js";
|
|
39
39
|
export { iconLucideSquare as iconSquare } from "@marianmeres/icons-fns/lucide/iconLucideSquare.js";
|
|
40
40
|
export { iconLucideMenu as iconMenu } from "@marianmeres/icons-fns/lucide/iconLucideMenu.js";
|
|
41
|
+
export { iconLucideUser as iconUser } from "@marianmeres/icons-fns/lucide/iconLucideUser.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export * from "./components/AlertConfirmPrompt/index.js";
|
|
|
25
25
|
export * from "./components/AnimatedElipsis/index.js";
|
|
26
26
|
export * from "./components/AppShell/index.js";
|
|
27
27
|
export * from "./components/AssetsPreview/index.js";
|
|
28
|
-
export * from "./components/
|
|
28
|
+
export * from "./components/Avatar/index.js";
|
|
29
29
|
export * from "./components/Backdrop/index.js";
|
|
30
30
|
export * from "./components/Button/index.js";
|
|
31
31
|
export * from "./components/ButtonGroupRadio/index.js";
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ export * from "./components/AlertConfirmPrompt/index.js";
|
|
|
26
26
|
export * from "./components/AnimatedElipsis/index.js";
|
|
27
27
|
export * from "./components/AppShell/index.js";
|
|
28
28
|
export * from "./components/AssetsPreview/index.js";
|
|
29
|
-
export * from "./components/
|
|
29
|
+
export * from "./components/Avatar/index.js";
|
|
30
30
|
export * from "./components/Backdrop/index.js";
|
|
31
31
|
export * from "./components/Button/index.js";
|
|
32
32
|
export * from "./components/ButtonGroupRadio/index.js";
|
package/package.json
CHANGED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
export interface Props {
|
|
3
|
-
/** String to extract initials from. Supports: "AB", "John Doe", or "john.doe@example.com" */
|
|
4
|
-
input: string;
|
|
5
|
-
/** Optional string for color hash calculation (e.g., email, user ID). Falls back to `input` */
|
|
6
|
-
hashSource?: string;
|
|
7
|
-
/** Size preset or custom Tailwind size class */
|
|
8
|
-
size?: "sm" | "md" | "lg" | "xl" | string;
|
|
9
|
-
/** Click handler - when provided, renders as a button */
|
|
10
|
-
onclick?: (event: MouseEvent) => void;
|
|
11
|
-
/** Background color (Tailwind class). Ignored if autoColor=true */
|
|
12
|
-
bg?: string;
|
|
13
|
-
/** Text color (Tailwind class). Ignored if autoColor=true */
|
|
14
|
-
textColor?: string;
|
|
15
|
-
/** Generate deterministic pastel colors from hashSource/input */
|
|
16
|
-
autoColor?: boolean;
|
|
17
|
-
/** CSS class override */
|
|
18
|
-
class?: string;
|
|
19
|
-
/** Bindable element reference */
|
|
20
|
-
el?: HTMLDivElement | HTMLButtonElement;
|
|
21
|
-
}
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<script lang="ts">
|
|
25
|
-
import { twMerge } from "../../utils/tw-merge.js";
|
|
26
|
-
import { generateAvatarColors } from "../../utils/avatar-colors.js";
|
|
27
|
-
|
|
28
|
-
let {
|
|
29
|
-
input,
|
|
30
|
-
hashSource,
|
|
31
|
-
size = "md",
|
|
32
|
-
onclick,
|
|
33
|
-
bg,
|
|
34
|
-
textColor,
|
|
35
|
-
autoColor = false,
|
|
36
|
-
class: classProp,
|
|
37
|
-
el = $bindable(),
|
|
38
|
-
}: Props = $props();
|
|
39
|
-
|
|
40
|
-
const SIZE_PRESETS: Record<string, string> = {
|
|
41
|
-
sm: "size-8 text-xs",
|
|
42
|
-
md: "size-10 text-sm",
|
|
43
|
-
lg: "size-14 text-base",
|
|
44
|
-
xl: "size-16 text-lg",
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
let initials = $derived.by(() => {
|
|
48
|
-
let _input = (input || "").trim();
|
|
49
|
-
|
|
50
|
-
if (!_input) return "?";
|
|
51
|
-
|
|
52
|
-
// Check if input looks like an email
|
|
53
|
-
if (_input.includes("@")) {
|
|
54
|
-
const username = _input.split("@")[0];
|
|
55
|
-
// Split by common separators (., _, -)
|
|
56
|
-
const parts = username.split(/[._+-]/).filter(Boolean);
|
|
57
|
-
if (parts.length > 1) {
|
|
58
|
-
_input = parts.map((p) => p.charAt(0)).join("");
|
|
59
|
-
} else {
|
|
60
|
-
_input = username;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// Check if input looks like a full name (multiple words)
|
|
64
|
-
else if (_input.length > 2 && /\s/.test(_input)) {
|
|
65
|
-
_input = _input
|
|
66
|
-
.split(/\s/)
|
|
67
|
-
.map((v) => v.trim())
|
|
68
|
-
.filter(Boolean)
|
|
69
|
-
.map((v) => v.charAt(0))
|
|
70
|
-
.join("");
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Extract first 2 chars, uppercase
|
|
74
|
-
return _input.slice(0, 2).toUpperCase();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
let colors = $derived(
|
|
78
|
-
autoColor ? generateAvatarColors(hashSource || input || "") : null
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
let sizeClass = $derived(SIZE_PRESETS[size] || size);
|
|
82
|
-
|
|
83
|
-
let style = $derived(
|
|
84
|
-
autoColor && colors
|
|
85
|
-
? `background-color: ${colors.bg}; color: ${colors.text};`
|
|
86
|
-
: undefined
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
let baseClass = $derived(
|
|
90
|
-
twMerge(
|
|
91
|
-
"stuic-avatar-initials",
|
|
92
|
-
"inline-flex items-center justify-center",
|
|
93
|
-
"rounded-full font-medium shrink-0",
|
|
94
|
-
!autoColor &&
|
|
95
|
-
"bg-neutral-200 text-neutral-700 dark:bg-neutral-700 dark:text-neutral-200",
|
|
96
|
-
sizeClass,
|
|
97
|
-
!autoColor && bg,
|
|
98
|
-
!autoColor && textColor,
|
|
99
|
-
onclick && "select-none cursor-pointer",
|
|
100
|
-
classProp
|
|
101
|
-
)
|
|
102
|
-
);
|
|
103
|
-
</script>
|
|
104
|
-
|
|
105
|
-
{#if onclick}
|
|
106
|
-
<button bind:this={el} type="button" class={baseClass} {style} {onclick}>
|
|
107
|
-
{initials}
|
|
108
|
-
</button>
|
|
109
|
-
{:else}
|
|
110
|
-
<div bind:this={el} class={baseClass} {style}>
|
|
111
|
-
{initials}
|
|
112
|
-
</div>
|
|
113
|
-
{/if}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface Props {
|
|
2
|
-
/** String to extract initials from. Supports: "AB", "John Doe", or "john.doe@example.com" */
|
|
3
|
-
input: string;
|
|
4
|
-
/** Optional string for color hash calculation (e.g., email, user ID). Falls back to `input` */
|
|
5
|
-
hashSource?: string;
|
|
6
|
-
/** Size preset or custom Tailwind size class */
|
|
7
|
-
size?: "sm" | "md" | "lg" | "xl" | string;
|
|
8
|
-
/** Click handler - when provided, renders as a button */
|
|
9
|
-
onclick?: (event: MouseEvent) => void;
|
|
10
|
-
/** Background color (Tailwind class). Ignored if autoColor=true */
|
|
11
|
-
bg?: string;
|
|
12
|
-
/** Text color (Tailwind class). Ignored if autoColor=true */
|
|
13
|
-
textColor?: string;
|
|
14
|
-
/** Generate deterministic pastel colors from hashSource/input */
|
|
15
|
-
autoColor?: boolean;
|
|
16
|
-
/** CSS class override */
|
|
17
|
-
class?: string;
|
|
18
|
-
/** Bindable element reference */
|
|
19
|
-
el?: HTMLDivElement | HTMLButtonElement;
|
|
20
|
-
}
|
|
21
|
-
declare const AvatarInitials: import("svelte").Component<Props, {}, "el">;
|
|
22
|
-
type AvatarInitials = ReturnType<typeof AvatarInitials>;
|
|
23
|
-
export default AvatarInitials;
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
# AvatarInitials
|
|
2
|
-
|
|
3
|
-
A circular avatar component displaying initials extracted from names or emails, with optional auto-generated colors and size presets.
|
|
4
|
-
|
|
5
|
-
## Props
|
|
6
|
-
|
|
7
|
-
| Prop | Type | Default | Description |
|
|
8
|
-
|------|------|---------|-------------|
|
|
9
|
-
| `input` | `string` | - | String to extract initials from (name, initials, or email) |
|
|
10
|
-
| `hashSource` | `string` | - | Optional string for color hash calculation (falls back to `input`) |
|
|
11
|
-
| `size` | `"sm" \| "md" \| "lg" \| "xl" \| string` | `"md"` | Size preset or custom Tailwind class |
|
|
12
|
-
| `onclick` | `(event: MouseEvent) => void` | - | Click handler (renders as button when provided) |
|
|
13
|
-
| `bg` | `string` | - | Background color Tailwind class (ignored if autoColor) |
|
|
14
|
-
| `textColor` | `string` | - | Text color Tailwind class (ignored if autoColor) |
|
|
15
|
-
| `autoColor` | `boolean` | `false` | Generate deterministic pastel colors from input |
|
|
16
|
-
| `class` | `string` | - | Additional CSS classes |
|
|
17
|
-
| `el` | `HTMLDivElement \| HTMLButtonElement` | - | Element reference (bindable) |
|
|
18
|
-
|
|
19
|
-
## Size Presets
|
|
20
|
-
|
|
21
|
-
| Size | Dimensions | Font Size |
|
|
22
|
-
|------|------------|-----------|
|
|
23
|
-
| `sm` | 32px (size-8) | text-xs |
|
|
24
|
-
| `md` | 40px (size-10) | text-sm |
|
|
25
|
-
| `lg` | 56px (size-14) | text-base |
|
|
26
|
-
| `xl` | 64px (size-16) | text-lg |
|
|
27
|
-
|
|
28
|
-
Custom sizes can be passed as Tailwind classes: `size="size-20 text-2xl"`
|
|
29
|
-
|
|
30
|
-
## Initials Extraction Logic
|
|
31
|
-
|
|
32
|
-
The component intelligently extracts up to 2 characters from the input:
|
|
33
|
-
|
|
34
|
-
1. **Email addresses** (`john.doe@example.com`):
|
|
35
|
-
- Splits username by `.`, `_`, `+`, `-`
|
|
36
|
-
- Takes first letter of each part
|
|
37
|
-
- Result: `JD`
|
|
38
|
-
|
|
39
|
-
2. **Full names** (`John Doe`):
|
|
40
|
-
- Splits by whitespace
|
|
41
|
-
- Takes first letter of each word
|
|
42
|
-
- Result: `JD`
|
|
43
|
-
|
|
44
|
-
3. **Short strings** (`AB` or `Jo`):
|
|
45
|
-
- Takes first 2 characters
|
|
46
|
-
- Result: `AB` or `JO`
|
|
47
|
-
|
|
48
|
-
4. **Empty input**:
|
|
49
|
-
- Returns `?`
|
|
50
|
-
|
|
51
|
-
All initials are uppercase.
|
|
52
|
-
|
|
53
|
-
## Auto Color Generation
|
|
54
|
-
|
|
55
|
-
When `autoColor` is enabled, the component generates deterministic pastel colors:
|
|
56
|
-
|
|
57
|
-
- Colors are derived from a hash of `hashSource` or `input`
|
|
58
|
-
- Same input always produces the same color
|
|
59
|
-
- Colors are designed as accessible pastel tones
|
|
60
|
-
- Text color automatically contrasts with background
|
|
61
|
-
|
|
62
|
-
## Usage
|
|
63
|
-
|
|
64
|
-
### Basic Display
|
|
65
|
-
|
|
66
|
-
```svelte
|
|
67
|
-
<script lang="ts">
|
|
68
|
-
import { AvatarInitials } from 'stuic';
|
|
69
|
-
</script>
|
|
70
|
-
|
|
71
|
-
<AvatarInitials input="John Doe" />
|
|
72
|
-
<AvatarInitials input="jane.smith@example.com" />
|
|
73
|
-
<AvatarInitials input="AB" />
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Size Variants
|
|
77
|
-
|
|
78
|
-
```svelte
|
|
79
|
-
<AvatarInitials input="JD" size="sm" />
|
|
80
|
-
<AvatarInitials input="JD" size="md" />
|
|
81
|
-
<AvatarInitials input="JD" size="lg" />
|
|
82
|
-
<AvatarInitials input="JD" size="xl" />
|
|
83
|
-
|
|
84
|
-
<!-- Custom size -->
|
|
85
|
-
<AvatarInitials input="JD" size="size-24 text-3xl" />
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### Auto Color (Deterministic)
|
|
89
|
-
|
|
90
|
-
```svelte
|
|
91
|
-
<!-- Same email always produces same color -->
|
|
92
|
-
<AvatarInitials input="john@example.com" autoColor />
|
|
93
|
-
<AvatarInitials input="jane@example.com" autoColor />
|
|
94
|
-
|
|
95
|
-
<!-- Use ID for consistent color regardless of display name -->
|
|
96
|
-
<AvatarInitials input="John Doe" hashSource="user-123" autoColor />
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Custom Colors
|
|
100
|
-
|
|
101
|
-
```svelte
|
|
102
|
-
<AvatarInitials
|
|
103
|
-
input="JD"
|
|
104
|
-
bg="bg-blue-500"
|
|
105
|
-
textColor="text-white"
|
|
106
|
-
/>
|
|
107
|
-
|
|
108
|
-
<AvatarInitials
|
|
109
|
-
input="AB"
|
|
110
|
-
bg="bg-gradient-to-br from-purple-500 to-pink-500"
|
|
111
|
-
textColor="text-white"
|
|
112
|
-
/>
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Clickable Avatar
|
|
116
|
-
|
|
117
|
-
```svelte
|
|
118
|
-
<script lang="ts">
|
|
119
|
-
function handleClick() {
|
|
120
|
-
console.log('Avatar clicked');
|
|
121
|
-
}
|
|
122
|
-
</script>
|
|
123
|
-
|
|
124
|
-
<AvatarInitials
|
|
125
|
-
input="john@example.com"
|
|
126
|
-
autoColor
|
|
127
|
-
onclick={handleClick}
|
|
128
|
-
/>
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### In Header Dropdown
|
|
132
|
-
|
|
133
|
-
```svelte
|
|
134
|
-
<script lang="ts">
|
|
135
|
-
import { AvatarInitials, DropdownMenu } from 'stuic';
|
|
136
|
-
</script>
|
|
137
|
-
|
|
138
|
-
<DropdownMenu
|
|
139
|
-
items={[
|
|
140
|
-
{ type: "action", id: "profile", label: "View Profile" },
|
|
141
|
-
{ type: "action", id: "logout", label: "Logout" },
|
|
142
|
-
]}
|
|
143
|
-
>
|
|
144
|
-
{#snippet trigger({ toggle })}
|
|
145
|
-
<AvatarInitials
|
|
146
|
-
input={userEmail}
|
|
147
|
-
onclick={toggle}
|
|
148
|
-
autoColor
|
|
149
|
-
class="cursor-pointer hover:ring-2 hover:ring-blue-500"
|
|
150
|
-
/>
|
|
151
|
-
{/snippet}
|
|
152
|
-
</DropdownMenu>
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Avatar List
|
|
156
|
-
|
|
157
|
-
```svelte
|
|
158
|
-
<div class="flex -space-x-2">
|
|
159
|
-
{#each users as user}
|
|
160
|
-
<AvatarInitials
|
|
161
|
-
input={user.email}
|
|
162
|
-
hashSource={user.id}
|
|
163
|
-
autoColor
|
|
164
|
-
size="sm"
|
|
165
|
-
class="ring-2 ring-white"
|
|
166
|
-
/>
|
|
167
|
-
{/each}
|
|
168
|
-
</div>
|
|
169
|
-
```
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as AvatarInitials, type Props as AvatarInitialsProps, } from "./AvatarInitials.svelte";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as AvatarInitials, } from "./AvatarInitials.svelte";
|