@viniaraujo68/plinth 0.6.0 → 0.8.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/README.md +6 -3
- package/dist/components/Avatar.svelte +81 -0
- package/dist/components/Avatar.svelte.d.ts +29 -0
- package/dist/components/DateRangePicker.svelte +52 -6
- package/dist/components/DateRangePicker.svelte.d.ts +15 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shell/AppShell.svelte +2 -2
- package/dist/shell/TopBar.svelte +2 -2
- package/package.json +1 -1
- package/dist/shell/UserAvatar.svelte +0 -50
- package/dist/shell/UserAvatar.svelte.d.ts +0 -14
- /package/dist/{shell → components}/initials.d.ts +0 -0
- /package/dist/{shell → components}/initials.js +0 -0
package/README.md
CHANGED
|
@@ -300,14 +300,14 @@ never rethrows, so an `{:catch error}` branch that renders it is safe on the ser
|
|
|
300
300
|
| `@viniaraujo68/plinth/virtual` | `Virtualizer`, `VirtualList`, `WindowedData`, `WindowedVirtualizer`, `DataList` |
|
|
301
301
|
| `@viniaraujo68/plinth/url-state` | `createUrlState`, `urlStore`, `SyncedState`, `createParamsCodec` and `param`, the codec contracts |
|
|
302
302
|
|
|
303
|
-
The components entry point ships `AsyncButton`, `Combobox`, `Copyable`, `DateRangePicker`,
|
|
303
|
+
The components entry point ships `AsyncButton`, `Avatar`, `Combobox`, `Copyable`, `DateRangePicker`,
|
|
304
304
|
`Dialog`, `DragHandle`, `Dropdown`, `ErrorDisplay`, `ErrorPage`, `JsonInput`, `JsonView`, `Listbox`, `LoadingButton`, `Logo`,
|
|
305
305
|
`Modal`, `MultiSelect`, `Paginator`, `RefreshButton`, `RequestErrorDisplay`, `SegmentedControl`, `Select`, `Skeleton`, `Tabs`,
|
|
306
306
|
`VirtualJsonView`
|
|
307
307
|
and `Tooltip`. Every one of them has a page in the showcase.
|
|
308
308
|
It also exports the mark's outline as data — `PICK_PATH` and `PICK_CLIP_PATH` — for anything
|
|
309
|
-
that wants the pick as a silhouette of its own. Not for avatars:
|
|
310
|
-
|
|
309
|
+
that wants the pick as a silhouette of its own. Not for avatars: a profile picture is a disc, which
|
|
310
|
+
is what `Avatar` draws.
|
|
311
311
|
|
|
312
312
|
**`Tabs`** switches what one panel shows: a `tablist` with a roving tabindex, where the arrow keys,
|
|
313
313
|
Home and End move the selection and the selection follows the focus. It scrolls sideways on a phone
|
|
@@ -322,6 +322,9 @@ date fields, the pair is `YYYY-MM-DD`, and an empty end leaves that side open, s
|
|
|
322
322
|
"all time" are pairs too. Presets can follow the calendar instead of a duration — `thisMonth`,
|
|
323
323
|
`lastMonth`, `lastMonths(n)` and `allTime` build the usual ones, and any `range(now)` function will
|
|
324
324
|
do. Every preset re-anchors on a timer, and a picker handed a pair opens on the preset that names it.
|
|
325
|
+
`caption` puts visible text before the presets, as on `SegmentedControl`, and `size` (`sm` or `md`)
|
|
326
|
+
sizes the presets and the typed fields together, so the fields line up with the buttons.
|
|
327
|
+
`fieldLabels` puts the field labels `"above"` their fields (the default) or `"beside"` them.
|
|
325
328
|
|
|
326
329
|
`JsonInput` edits a JSON document as text: it reports through a bindable `invalid` whether the text
|
|
327
330
|
parses, shows the parser's message after the first blur, can reindent on blur, and sets the
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ClassValue } from "svelte/elements";
|
|
3
|
+
import { initials } from "./initials.js";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Whose avatar this is. Drawn as initials when there is no photo; never shown in full. */
|
|
7
|
+
name: string;
|
|
8
|
+
/** The photo's URL. Without one, or when it fails to load, the initials stand in for it. */
|
|
9
|
+
src?: string | null;
|
|
10
|
+
/**
|
|
11
|
+
* The disc's diameter, as any CSS length. A prop and not a class because an unlayered
|
|
12
|
+
* component rule beats a Tailwind utility whatever the source order, so `size-10` from the
|
|
13
|
+
* outside could never win. The initials scale with it.
|
|
14
|
+
*/
|
|
15
|
+
size?: string;
|
|
16
|
+
class?: ClassValue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let { name, src, size = "2rem", class: className }: Props = $props();
|
|
20
|
+
|
|
21
|
+
// Remembering which URL failed, rather than a flag, is what lets a new photo be tried again:
|
|
22
|
+
// a changed `src` no longer matches the failed one, with no effect to reset anything.
|
|
23
|
+
let failedSrc = $state<string | null>(null);
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<!--
|
|
27
|
+
@component
|
|
28
|
+
A person on a disc: their photo when there is one, the first letter of their first and last name
|
|
29
|
+
when there is not, and the initials again when the photo fails to load.
|
|
30
|
+
|
|
31
|
+
Hidden from assistive technology: every place that draws it also shows the name next to it, or
|
|
32
|
+
labels the control it sits in, and initials read aloud as letters would only repeat that.
|
|
33
|
+
|
|
34
|
+
```svelte
|
|
35
|
+
<Avatar name="Ada Lovelace" />
|
|
36
|
+
<Avatar name="Ada Lovelace" src={photoUrl} size="6rem" />
|
|
37
|
+
```
|
|
38
|
+
-->
|
|
39
|
+
|
|
40
|
+
<span class={["plinth-avatar", className]} style:--plinth-avatar-size={size} aria-hidden="true">
|
|
41
|
+
{#if src && src !== failedSrc}
|
|
42
|
+
<img {src} alt="" onerror={() => (failedSrc = src)} />
|
|
43
|
+
{:else}
|
|
44
|
+
<span class="plinth-avatar-initials">{initials(name)}</span>
|
|
45
|
+
{/if}
|
|
46
|
+
</span>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
.plinth-avatar {
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-shrink: 0;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
width: var(--plinth-avatar-size);
|
|
55
|
+
height: var(--plinth-avatar-size);
|
|
56
|
+
overflow: hidden;
|
|
57
|
+
border-radius: 9999px;
|
|
58
|
+
/* Solid rather than a wash: on the tinted bars this sits on, a 15% disc all but disappears.
|
|
59
|
+
`--color-primary-content` is the pair the theme guarantees against it in both modes. */
|
|
60
|
+
background-color: var(--color-primary);
|
|
61
|
+
color: var(--color-primary-content);
|
|
62
|
+
font-size: calc(var(--plinth-avatar-size) * 0.375);
|
|
63
|
+
font-weight: 500;
|
|
64
|
+
line-height: 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
img {
|
|
68
|
+
width: 100%;
|
|
69
|
+
height: 100%;
|
|
70
|
+
object-fit: cover;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Centring the line box centres the font's ascent and descent, not the letters: capitals sit
|
|
74
|
+
above the baseline with nothing hanging below, so in a font like Segoe UI Variable they land a
|
|
75
|
+
pixel low on a 2rem disc. Trimmed to cap height and baseline, the box is the letters, and the
|
|
76
|
+
disc centres them whatever the font. The inner element is what gets trimmed, because the
|
|
77
|
+
property applies to block containers and the disc is a flex container. */
|
|
78
|
+
.plinth-avatar-initials {
|
|
79
|
+
text-box: trim-both cap alphabetic;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ClassValue } from "svelte/elements";
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Whose avatar this is. Drawn as initials when there is no photo; never shown in full. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** The photo's URL. Without one, or when it fails to load, the initials stand in for it. */
|
|
6
|
+
src?: string | null;
|
|
7
|
+
/**
|
|
8
|
+
* The disc's diameter, as any CSS length. A prop and not a class because an unlayered
|
|
9
|
+
* component rule beats a Tailwind utility whatever the source order, so `size-10` from the
|
|
10
|
+
* outside could never win. The initials scale with it.
|
|
11
|
+
*/
|
|
12
|
+
size?: string;
|
|
13
|
+
class?: ClassValue;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A person on a disc: their photo when there is one, the first letter of their first and last name
|
|
17
|
+
* when there is not, and the initials again when the photo fails to load.
|
|
18
|
+
*
|
|
19
|
+
* Hidden from assistive technology: every place that draws it also shows the name next to it, or
|
|
20
|
+
* labels the control it sits in, and initials read aloud as letters would only repeat that.
|
|
21
|
+
*
|
|
22
|
+
* ```svelte
|
|
23
|
+
* <Avatar name="Ada Lovelace" />
|
|
24
|
+
* <Avatar name="Ada Lovelace" src={photoUrl} size="6rem" />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
declare const Avatar: import("svelte").Component<Props, {}, "">;
|
|
28
|
+
type Avatar = ReturnType<typeof Avatar>;
|
|
29
|
+
export default Avatar;
|
|
@@ -63,6 +63,21 @@
|
|
|
63
63
|
toLabel?: string;
|
|
64
64
|
/** Shown, as an alert, while the typed pair is out of order. */
|
|
65
65
|
invalidRangeLabel?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Visible text before the presets — "Period:". Passed to the {@link SegmentedControl}, so it
|
|
68
|
+
* sits on the preset row; `label` stays the accessible name.
|
|
69
|
+
*/
|
|
70
|
+
caption?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Sizes the presets and the custom fields together, so the fields line up with the buttons.
|
|
73
|
+
* `sm` is for a toolbar or a card header, where the default height would dominate.
|
|
74
|
+
*/
|
|
75
|
+
size?: "sm" | "md";
|
|
76
|
+
/**
|
|
77
|
+
* Where the custom fields' labels sit: `"above"` stacks each one over its field, `"beside"`
|
|
78
|
+
* puts it before the field on the same line, so the whole picker can stay one row.
|
|
79
|
+
*/
|
|
80
|
+
fieldLabels?: "above" | "beside";
|
|
66
81
|
onchange?: (value: DateRange) => void;
|
|
67
82
|
}
|
|
68
83
|
|
|
@@ -78,6 +93,9 @@
|
|
|
78
93
|
fromLabel = "From",
|
|
79
94
|
toLabel = "To",
|
|
80
95
|
invalidRangeLabel = "The start must not be after the end.",
|
|
96
|
+
caption,
|
|
97
|
+
size = "md",
|
|
98
|
+
fieldLabels = "above",
|
|
81
99
|
onchange,
|
|
82
100
|
}: Props = $props();
|
|
83
101
|
|
|
@@ -106,6 +124,9 @@
|
|
|
106
124
|
|
|
107
125
|
const isCustom = $derived(selection === CUSTOM_PRESET_ID);
|
|
108
126
|
const days = $derived(mode === "date");
|
|
127
|
+
const fieldWrapper = $derived(
|
|
128
|
+
fieldLabels === "beside" ? "flex items-center gap-2" : "flex flex-col gap-1",
|
|
129
|
+
);
|
|
109
130
|
|
|
110
131
|
const options = $derived([
|
|
111
132
|
...presets.map(({ id, label }) => ({ id, label })),
|
|
@@ -251,18 +272,18 @@ buttons are labelled by the list itself, through `DateRangePreset.label`.
|
|
|
251
272
|
```
|
|
252
273
|
-->
|
|
253
274
|
|
|
254
|
-
<div class={["flex flex-wrap items-end gap-x-4 gap-y-3", cls]}>
|
|
275
|
+
<div class={["flex flex-wrap items-end gap-x-4 gap-y-3", cls]} data-size={size}>
|
|
255
276
|
<!-- A segmented control rather than a joined row: a calendar picker easily offers six windows,
|
|
256
277
|
and a joined row cannot wrap onto a second line at phone width. -->
|
|
257
|
-
<SegmentedControl {options} value={selection} {label} onchange={pick} />
|
|
278
|
+
<SegmentedControl {options} value={selection} {label} {caption} {size} onchange={pick} />
|
|
258
279
|
|
|
259
280
|
{#if isCustom}
|
|
260
281
|
<div class="flex flex-wrap items-end gap-x-4 gap-y-3">
|
|
261
|
-
<div class=
|
|
282
|
+
<div class={fieldWrapper}>
|
|
262
283
|
<label class="label text-xs" for={fromId}>{fromLabel}</label>
|
|
263
284
|
<input
|
|
264
285
|
id={fromId}
|
|
265
|
-
class="
|
|
286
|
+
class="plinth-range-field input"
|
|
266
287
|
class:input-error={rangeError !== null}
|
|
267
288
|
type={days ? "date" : "datetime-local"}
|
|
268
289
|
value={customFrom}
|
|
@@ -276,11 +297,11 @@ buttons are labelled by the list itself, through `DateRangePreset.label`.
|
|
|
276
297
|
/>
|
|
277
298
|
</div>
|
|
278
299
|
|
|
279
|
-
<div class=
|
|
300
|
+
<div class={fieldWrapper}>
|
|
280
301
|
<label class="label text-xs" for={toId}>{toLabel}</label>
|
|
281
302
|
<input
|
|
282
303
|
id={toId}
|
|
283
|
-
class="
|
|
304
|
+
class="plinth-range-field input"
|
|
284
305
|
class:input-error={rangeError !== null}
|
|
285
306
|
type={days ? "date" : "datetime-local"}
|
|
286
307
|
value={customTo}
|
|
@@ -300,3 +321,28 @@ buttons are labelled by the list itself, through `DateRangePreset.label`.
|
|
|
300
321
|
<p id={errorId} class="basis-full text-sm text-error" role="alert">{rangeError}</p>
|
|
301
322
|
{/if}
|
|
302
323
|
</div>
|
|
324
|
+
|
|
325
|
+
<style>
|
|
326
|
+
/* Chrome sizes a date field to its text alone, leaving no room for the padding and the picker
|
|
327
|
+
icon, which then covers the last digit. */
|
|
328
|
+
.plinth-range-field {
|
|
329
|
+
height: 2rem;
|
|
330
|
+
min-width: 9rem;
|
|
331
|
+
font-size: 0.78rem;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.plinth-range-field[type="datetime-local"] {
|
|
335
|
+
min-width: 12.5rem;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
[data-size="sm"] .plinth-range-field {
|
|
339
|
+
height: 1.625rem;
|
|
340
|
+
font-size: 0.7rem;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
@media (max-width: 35rem) {
|
|
344
|
+
[data-size="md"] .plinth-range-field {
|
|
345
|
+
height: 2.5rem;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
</style>
|
|
@@ -44,6 +44,21 @@ interface Props {
|
|
|
44
44
|
toLabel?: string;
|
|
45
45
|
/** Shown, as an alert, while the typed pair is out of order. */
|
|
46
46
|
invalidRangeLabel?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Visible text before the presets — "Period:". Passed to the {@link SegmentedControl}, so it
|
|
49
|
+
* sits on the preset row; `label` stays the accessible name.
|
|
50
|
+
*/
|
|
51
|
+
caption?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Sizes the presets and the custom fields together, so the fields line up with the buttons.
|
|
54
|
+
* `sm` is for a toolbar or a card header, where the default height would dominate.
|
|
55
|
+
*/
|
|
56
|
+
size?: "sm" | "md";
|
|
57
|
+
/**
|
|
58
|
+
* Where the custom fields' labels sit: `"above"` stacks each one over its field, `"beside"`
|
|
59
|
+
* puts it before the field on the same line, so the whole picker can stay one row.
|
|
60
|
+
*/
|
|
61
|
+
fieldLabels?: "above" | "beside";
|
|
47
62
|
onchange?: (value: DateRange) => void;
|
|
48
63
|
}
|
|
49
64
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as AsyncButton } from "./AsyncButton.svelte";
|
|
2
|
+
export { default as Avatar } from "./Avatar.svelte";
|
|
2
3
|
export { default as Combobox } from "./Combobox.svelte";
|
|
3
4
|
export { default as Copyable } from "./Copyable.svelte";
|
|
4
5
|
export { default as DateRangePicker } from "./DateRangePicker.svelte";
|
package/dist/components/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as AsyncButton } from "./AsyncButton.svelte";
|
|
2
|
+
export { default as Avatar } from "./Avatar.svelte";
|
|
2
3
|
export { default as Combobox } from "./Combobox.svelte";
|
|
3
4
|
export { default as Copyable } from "./Copyable.svelte";
|
|
4
5
|
export { default as DateRangePicker } from "./DateRangePicker.svelte";
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
* no portable way to import its own manifest, and a consumer that only wants to log a version
|
|
6
6
|
* should not have to bundle one. `version.spec.ts` fails when this drifts from the manifest.
|
|
7
7
|
*/
|
|
8
|
-
export declare const LIBRARY_VERSION = "0.
|
|
8
|
+
export declare const LIBRARY_VERSION = "0.8.0";
|
|
9
9
|
export { AsyncAction, type AsyncActionStatus, type AsyncFn } from "./async-action.svelte.js";
|
|
10
10
|
export { reportError, setErrorReporter, type ErrorReporter } from "./error.js";
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* no portable way to import its own manifest, and a consumer that only wants to log a version
|
|
6
6
|
* should not have to bundle one. `version.spec.ts` fails when this drifts from the manifest.
|
|
7
7
|
*/
|
|
8
|
-
export const LIBRARY_VERSION = "0.
|
|
8
|
+
export const LIBRARY_VERSION = "0.8.0";
|
|
9
9
|
// The root entry carries only what is not tied to one area: the error channel an app installs
|
|
10
10
|
// once, and the async state machine the components are built on. Everything visual is reached
|
|
11
11
|
// through a subpath — `/theme`, `/attachments`, `/components`, `/toast` — so importing a button
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { tooltip } from "../attachments/tooltip.js";
|
|
10
10
|
import { getRoutingContext, resolvePathname, type ResolvedRoute } from "../routing.svelte.js";
|
|
11
11
|
import { getUserContext, type UserData } from "../user/context.js";
|
|
12
|
-
import
|
|
12
|
+
import Avatar from "../components/Avatar.svelte";
|
|
13
13
|
|
|
14
14
|
interface Props {
|
|
15
15
|
/** The routed page. The shell owns the scroll container around it. */
|
|
@@ -557,7 +557,7 @@ shell rather than to the window, and the content area reserves its height plus
|
|
|
557
557
|
|
|
558
558
|
{#snippet userBlock(data: UserData)}
|
|
559
559
|
<div class="user-card">
|
|
560
|
-
<
|
|
560
|
+
<Avatar name={data.name} />
|
|
561
561
|
<span class="user-identity collapsible">
|
|
562
562
|
<span class="user-name">{data.name}</span>
|
|
563
563
|
<span class="user-email">{data.email}</span>
|
package/dist/shell/TopBar.svelte
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { ClassValue } from "svelte/elements";
|
|
4
4
|
import Dropdown from "../components/Dropdown.svelte";
|
|
5
5
|
import { getUserContext } from "../user/context.js";
|
|
6
|
-
import
|
|
6
|
+
import Avatar from "../components/Avatar.svelte";
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
9
|
/** The app's mark and name, at the start of the bar. Usually a link home. */
|
|
@@ -84,7 +84,7 @@ app uses — `--plinth-top-bar-width` caps the bar's content to match it.
|
|
|
84
84
|
title={signedIn.name}
|
|
85
85
|
panelClass="plinth-top-bar-menu dropdown rounded-box bg-base-100 p-2 shadow-lg"
|
|
86
86
|
>
|
|
87
|
-
<
|
|
87
|
+
<Avatar name={signedIn.name} />
|
|
88
88
|
{#snippet content()}
|
|
89
89
|
<!-- The handler only reacts to a click that landed on a link or a button inside, and a
|
|
90
90
|
keyboard activates those with a click event of its own, so nothing is mouse-only. -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viniaraujo68/plinth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A personal Svelte 5 design system: theme, application shell, interface primitives, routing metadata, a fetch wrapper and locale-aware formatters.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import type { ClassValue } from "svelte/elements";
|
|
3
|
-
import { initials } from "./initials.js";
|
|
4
|
-
|
|
5
|
-
interface Props {
|
|
6
|
-
/** Whose avatar this is. Only the initials are drawn; the name itself is the caller's to show. */
|
|
7
|
-
name: string;
|
|
8
|
-
class?: ClassValue;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let { name, class: className }: Props = $props();
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<!--
|
|
15
|
-
@component
|
|
16
|
-
A signed-in user's initials on a disc. Hidden from assistive technology: every place that draws it
|
|
17
|
-
also shows the name next to it, or labels the control it sits in, and initials read aloud as
|
|
18
|
-
letters would only repeat that.
|
|
19
|
-
-->
|
|
20
|
-
<span class={["avatar-initials", className]} aria-hidden="true"
|
|
21
|
-
><span class="avatar-initials-text">{initials(name)}</span></span
|
|
22
|
-
>
|
|
23
|
-
|
|
24
|
-
<style>
|
|
25
|
-
.avatar-initials {
|
|
26
|
-
display: flex;
|
|
27
|
-
flex-shrink: 0;
|
|
28
|
-
align-items: center;
|
|
29
|
-
justify-content: center;
|
|
30
|
-
width: 2rem;
|
|
31
|
-
height: 2rem;
|
|
32
|
-
border-radius: 9999px;
|
|
33
|
-
/* Solid rather than a wash: on the tinted bars this sits on, a 15% disc all but disappears.
|
|
34
|
-
`--color-primary-content` is the pair the theme guarantees against it in both modes. */
|
|
35
|
-
background-color: var(--color-primary);
|
|
36
|
-
color: var(--color-primary-content);
|
|
37
|
-
font-size: 0.75rem;
|
|
38
|
-
font-weight: 500;
|
|
39
|
-
line-height: 1;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/* Centring the line box centres the font's ascent and descent, not the letters: capitals sit
|
|
43
|
-
above the baseline with nothing hanging below, so in a font like Segoe UI Variable they land a
|
|
44
|
-
pixel low on a 2rem disc. Trimmed to cap height and baseline, the box is the letters, and the
|
|
45
|
-
disc centres them whatever the font. The inner element is what gets trimmed, because the
|
|
46
|
-
property applies to block containers and the disc is a flex container. */
|
|
47
|
-
.avatar-initials-text {
|
|
48
|
-
text-box: trim-both cap alphabetic;
|
|
49
|
-
}
|
|
50
|
-
</style>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ClassValue } from "svelte/elements";
|
|
2
|
-
interface Props {
|
|
3
|
-
/** Whose avatar this is. Only the initials are drawn; the name itself is the caller's to show. */
|
|
4
|
-
name: string;
|
|
5
|
-
class?: ClassValue;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* A signed-in user's initials on a disc. Hidden from assistive technology: every place that draws it
|
|
9
|
-
* also shows the name next to it, or labels the control it sits in, and initials read aloud as
|
|
10
|
-
* letters would only repeat that.
|
|
11
|
-
*/
|
|
12
|
-
declare const UserAvatar: import("svelte").Component<Props, {}, "">;
|
|
13
|
-
type UserAvatar = ReturnType<typeof UserAvatar>;
|
|
14
|
-
export default UserAvatar;
|
|
File without changes
|
|
File without changes
|