@lyeve-labs/ui-kit 0.18.0 → 0.20.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 +11 -4
- package/dist/components/AccordionItem.svelte +1 -1
- package/dist/components/AppShell.svelte +3 -3
- package/dist/components/Autocomplete.svelte +4 -4
- package/dist/components/AvatarGroup.svelte +2 -2
- package/dist/components/Breadcrumb.svelte +1 -1
- package/dist/components/Button.svelte +34 -9
- package/dist/components/ButtonGroup.svelte +3 -3
- package/dist/components/Card.svelte +1 -1
- package/dist/components/Checkbox.svelte +1 -1
- package/dist/components/CheckboxGroup.svelte +1 -1
- package/dist/components/Collapsible.svelte +3 -3
- package/dist/components/CopyButton.svelte +1 -1
- package/dist/components/DatePicker.svelte +2 -2
- package/dist/components/DateTimePicker.svelte +1 -1
- package/dist/components/Drawer.svelte +1 -1
- package/dist/components/Dropdown.svelte +1 -1
- package/dist/components/Field.svelte +1 -1
- package/dist/components/Input.svelte +1 -1
- package/dist/components/Label.svelte +1 -1
- package/dist/components/MultiSelect.svelte +4 -4
- package/dist/components/NumberInput.svelte +3 -3
- package/dist/components/Pagination.svelte +194 -65
- package/dist/components/Pagination.svelte.d.ts +52 -2
- package/dist/components/PasswordInput.svelte +4 -4
- package/dist/components/Radio.svelte +1 -1
- package/dist/components/RadioGroup.svelte +1 -1
- package/dist/components/SearchInput.svelte +4 -4
- package/dist/components/SegmentedControl.svelte +3 -1
- package/dist/components/Select.svelte +6 -6
- package/dist/components/SidebarNav.svelte +20 -5
- package/dist/components/Table.svelte +114 -1
- package/dist/components/Table.svelte.d.ts +37 -0
- package/dist/components/Tabs.svelte +1 -1
- package/dist/components/Tag.svelte +1 -1
- package/dist/components/Textarea.svelte +1 -1
- package/dist/components/ThemeToggle.svelte +1 -1
- package/dist/components/TimePicker.svelte +2 -2
- package/dist/components/Toaster.svelte +2 -2
- package/dist/components/Toggle.svelte +10 -4
- package/dist/components/TreeView.svelte +4 -4
- package/dist/components/dialog/Dialog.svelte +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/internal/href.d.ts +13 -0
- package/dist/internal/href.js +27 -0
- package/dist/internal/number.d.ts +26 -0
- package/dist/internal/number.js +35 -0
- package/dist/internal/panel.js +1 -1
- package/dist/styles/theme.css +53 -0
- package/package.json +3 -4
- package/src/lib/styles/theme.css +53 -0
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/**
|
|
3
|
+
* How a body cell treats content wider than its column.
|
|
4
|
+
*
|
|
5
|
+
* `wrap` breaks anywhere it has to. `truncate` keeps the cell to one line and
|
|
6
|
+
* ends it with an ellipsis. `auto` is the browser's own table algorithm, with
|
|
7
|
+
* no cap of any kind.
|
|
8
|
+
*/
|
|
9
|
+
export type CellFit = 'auto' | 'wrap' | 'truncate';
|
|
10
|
+
</script>
|
|
11
|
+
|
|
1
12
|
<script lang="ts">
|
|
2
13
|
import type { Snippet } from 'svelte';
|
|
3
14
|
|
|
@@ -12,6 +23,35 @@
|
|
|
12
23
|
* else, which is a landmark the reader has to enter to identify.
|
|
13
24
|
*/
|
|
14
25
|
label?: string;
|
|
26
|
+
/**
|
|
27
|
+
* What a body cell does with content too wide for its column.
|
|
28
|
+
*
|
|
29
|
+
* `wrap` by default, and the default is not `auto`. A table constrained
|
|
30
|
+
* nothing: one cell holding an unbroken token - an API key, a signed URL,
|
|
31
|
+
* a base64 payload - has no break opportunity in it, so the auto table
|
|
32
|
+
* algorithm sizes the column to the whole string and the table grows to
|
|
33
|
+
* whatever that comes to. One measured page took its container from no
|
|
34
|
+
* overflow at all to 3,086px of it, and the audit log overran its own
|
|
35
|
+
* container by 419px at 390px wide.
|
|
36
|
+
*
|
|
37
|
+
* `wrap` fixes exactly that and nothing else. It is `overflow-wrap:
|
|
38
|
+
* anywhere`, which is the one value that shrinks the column's min-content
|
|
39
|
+
* width, so a paragraph still breaks at its spaces and only a token with
|
|
40
|
+
* no spaces in it is broken mid-string. Prose in a cell renders as it
|
|
41
|
+
* always did.
|
|
42
|
+
*
|
|
43
|
+
* The default wraps rather than truncates because wrapping is the choice
|
|
44
|
+
* that cannot lose anything. Truncation hides, and a table that hides is
|
|
45
|
+
* worse than one that scrolls, so it is opted into.
|
|
46
|
+
*/
|
|
47
|
+
cell?: CellFit;
|
|
48
|
+
/**
|
|
49
|
+
* The width a truncating cell stops at. Any CSS length.
|
|
50
|
+
*
|
|
51
|
+
* Read through a custom property, so a single column can disagree with the
|
|
52
|
+
* table by setting `--cell-truncate` in its own style attribute.
|
|
53
|
+
*/
|
|
54
|
+
truncateAt?: string;
|
|
15
55
|
class?: string;
|
|
16
56
|
children: Snippet;
|
|
17
57
|
}
|
|
@@ -21,6 +61,8 @@
|
|
|
21
61
|
hoverable = true,
|
|
22
62
|
fixed = false,
|
|
23
63
|
label = undefined,
|
|
64
|
+
cell = 'wrap',
|
|
65
|
+
truncateAt = '20rem',
|
|
24
66
|
class: cls = '',
|
|
25
67
|
children,
|
|
26
68
|
}: Props = $props();
|
|
@@ -99,6 +141,73 @@
|
|
|
99
141
|
* for the same reason and it reads in both themes.
|
|
100
142
|
*/
|
|
101
143
|
const EDGE = 'pointer-events-none absolute inset-y-px w-6';
|
|
144
|
+
|
|
145
|
+
/*
|
|
146
|
+
* The table-wide treatment, applied only to cells that did not ask for their
|
|
147
|
+
* own.
|
|
148
|
+
*
|
|
149
|
+
* `:not([data-cell])` is what keeps the two apart. Without it a column that
|
|
150
|
+
* opted out still matched the table's rule for the same property, and which
|
|
151
|
+
* of the two won came down to the order Tailwind happened to emit them in.
|
|
152
|
+
*/
|
|
153
|
+
const CELL_FIT: Record<CellFit, string> = {
|
|
154
|
+
auto: '',
|
|
155
|
+
wrap: '[&_tbody_td:not([data-cell])]:[overflow-wrap:anywhere]',
|
|
156
|
+
truncate:
|
|
157
|
+
'[&_tbody_td:not([data-cell])]:max-w-[var(--cell-truncate)] [&_tbody_td:not([data-cell])]:truncate',
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/*
|
|
161
|
+
* The per-cell opt-in, always present so a caller can mix treatments down one
|
|
162
|
+
* row: `data-cell="nowrap"` on a timestamp, `data-cell="truncate"` on a
|
|
163
|
+
* description, nothing at all on the rest.
|
|
164
|
+
*/
|
|
165
|
+
const CELL_NAMED =
|
|
166
|
+
'[&_tbody_td[data-cell=wrap]]:[overflow-wrap:anywhere] ' +
|
|
167
|
+
'[&_tbody_td[data-cell=nowrap]]:whitespace-nowrap ' +
|
|
168
|
+
'[&_tbody_td[data-cell=truncate]]:max-w-[var(--cell-truncate)] ' +
|
|
169
|
+
'[&_tbody_td[data-cell=truncate]]:truncate';
|
|
170
|
+
|
|
171
|
+
/** Marks a title this component put there, so it can take it back again. */
|
|
172
|
+
const OWNED = 'data-cell-title';
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Gives a clipped cell its full value back.
|
|
176
|
+
*
|
|
177
|
+
* The text is never removed from the document, so a screen reader already
|
|
178
|
+
* reads the whole of it and it is the sighted pointer user who loses the tail
|
|
179
|
+
* to the ellipsis. The title that returns it is attached when a pointer or
|
|
180
|
+
* the keyboard actually reaches the cell, from one listener on the scroll
|
|
181
|
+
* box, rather than measured for every cell as the table renders: a tenant
|
|
182
|
+
* with 10,000 rows would pay for 10,000 measurements and 10,000 title
|
|
183
|
+
* attributes to answer a question asked of about three of them.
|
|
184
|
+
*/
|
|
185
|
+
function reveal(event: Event) {
|
|
186
|
+
const from = event.target;
|
|
187
|
+
if (!(from instanceof Element)) return;
|
|
188
|
+
const td = from.closest('td');
|
|
189
|
+
if (!(td instanceof HTMLElement)) return;
|
|
190
|
+
|
|
191
|
+
const named = td.dataset.cell;
|
|
192
|
+
const clipping = named === 'truncate' || (named === undefined && cell === 'truncate');
|
|
193
|
+
if (!clipping) return;
|
|
194
|
+
|
|
195
|
+
const ours = td.hasAttribute(OWNED);
|
|
196
|
+
// A title the caller wrote is theirs. It may well say more than the cell
|
|
197
|
+
// holds, and replacing it with the visible text would be a downgrade.
|
|
198
|
+
if (td.hasAttribute('title') && !ours) return;
|
|
199
|
+
|
|
200
|
+
const full = (td.textContent ?? '').trim();
|
|
201
|
+
if (full && td.scrollWidth - td.clientWidth > 1) {
|
|
202
|
+
td.setAttribute('title', full);
|
|
203
|
+
td.setAttribute(OWNED, '');
|
|
204
|
+
} else if (ours) {
|
|
205
|
+
// The column widened, or the row now holds something shorter. A tooltip
|
|
206
|
+
// repeating text the reader can already see is noise.
|
|
207
|
+
td.removeAttribute('title');
|
|
208
|
+
td.removeAttribute(OWNED);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
102
211
|
</script>
|
|
103
212
|
|
|
104
213
|
<div data-testid="table-frame" class="relative w-full rounded-xl border border-line {cls}">
|
|
@@ -109,6 +218,8 @@
|
|
|
109
218
|
<div
|
|
110
219
|
bind:this={scroller}
|
|
111
220
|
onscroll={measure}
|
|
221
|
+
onpointerover={reveal}
|
|
222
|
+
onfocusin={reveal}
|
|
112
223
|
data-print="unclip"
|
|
113
224
|
data-testid="table-scroll"
|
|
114
225
|
class="w-full overflow-x-auto rounded-xl"
|
|
@@ -126,8 +237,10 @@
|
|
|
126
237
|
same place.
|
|
127
238
|
-->
|
|
128
239
|
<table
|
|
129
|
-
|
|
240
|
+
style="--cell-truncate: {truncateAt}"
|
|
241
|
+
class="w-full border-separate border-spacing-0 text-sm text-start
|
|
130
242
|
{fixed ? 'table-fixed' : ''}
|
|
243
|
+
{CELL_FIT[cell]} {CELL_NAMED}
|
|
131
244
|
[&_thead_th]:border-b [&_thead_th]:border-line [&_thead]:bg-surface-2/40
|
|
132
245
|
[&_thead_th]:px-4 [&_thead_th]:py-3 [&_thead_th]:text-xs
|
|
133
246
|
[&_thead_th]:font-medium [&_thead_th]:text-faint
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a body cell treats content wider than its column.
|
|
3
|
+
*
|
|
4
|
+
* `wrap` breaks anywhere it has to. `truncate` keeps the cell to one line and
|
|
5
|
+
* ends it with an ellipsis. `auto` is the browser's own table algorithm, with
|
|
6
|
+
* no cap of any kind.
|
|
7
|
+
*/
|
|
8
|
+
export type CellFit = 'auto' | 'wrap' | 'truncate';
|
|
1
9
|
import type { Snippet } from 'svelte';
|
|
2
10
|
interface Props {
|
|
3
11
|
striped?: boolean;
|
|
@@ -10,6 +18,35 @@ interface Props {
|
|
|
10
18
|
* else, which is a landmark the reader has to enter to identify.
|
|
11
19
|
*/
|
|
12
20
|
label?: string;
|
|
21
|
+
/**
|
|
22
|
+
* What a body cell does with content too wide for its column.
|
|
23
|
+
*
|
|
24
|
+
* `wrap` by default, and the default is not `auto`. A table constrained
|
|
25
|
+
* nothing: one cell holding an unbroken token - an API key, a signed URL,
|
|
26
|
+
* a base64 payload - has no break opportunity in it, so the auto table
|
|
27
|
+
* algorithm sizes the column to the whole string and the table grows to
|
|
28
|
+
* whatever that comes to. One measured page took its container from no
|
|
29
|
+
* overflow at all to 3,086px of it, and the audit log overran its own
|
|
30
|
+
* container by 419px at 390px wide.
|
|
31
|
+
*
|
|
32
|
+
* `wrap` fixes exactly that and nothing else. It is `overflow-wrap:
|
|
33
|
+
* anywhere`, which is the one value that shrinks the column's min-content
|
|
34
|
+
* width, so a paragraph still breaks at its spaces and only a token with
|
|
35
|
+
* no spaces in it is broken mid-string. Prose in a cell renders as it
|
|
36
|
+
* always did.
|
|
37
|
+
*
|
|
38
|
+
* The default wraps rather than truncates because wrapping is the choice
|
|
39
|
+
* that cannot lose anything. Truncation hides, and a table that hides is
|
|
40
|
+
* worse than one that scrolls, so it is opted into.
|
|
41
|
+
*/
|
|
42
|
+
cell?: CellFit;
|
|
43
|
+
/**
|
|
44
|
+
* The width a truncating cell stops at. Any CSS length.
|
|
45
|
+
*
|
|
46
|
+
* Read through a custom property, so a single column can disagree with the
|
|
47
|
+
* table by setting `--cell-truncate` in its own style attribute.
|
|
48
|
+
*/
|
|
49
|
+
truncateAt?: string;
|
|
13
50
|
class?: string;
|
|
14
51
|
children: Snippet;
|
|
15
52
|
}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
{tab.label}
|
|
28
28
|
{#if tab.count !== undefined}
|
|
29
29
|
<span
|
|
30
|
-
class="
|
|
30
|
+
class="ms-1.5 inline-flex items-center justify-center min-w-4.5 h-4.5 px-1
|
|
31
31
|
rounded-full text-[10px] font-semibold
|
|
32
32
|
{active === tab.id ? 'bg-brand/40 text-brand' : 'bg-surface-2 text-faint'}"
|
|
33
33
|
>
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<div class="{FIELD_WRAP} {cls}">
|
|
55
55
|
{#if label}
|
|
56
56
|
<label for={fieldId} class={FIELD_LABEL}>
|
|
57
|
-
{label}{#if required}<span class="text-danger
|
|
57
|
+
{label}{#if required}<span class="text-danger ms-0.5" aria-hidden="true">*</span>{/if}
|
|
58
58
|
</label>
|
|
59
59
|
{/if}
|
|
60
60
|
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
data-print="hide"
|
|
82
82
|
data-theme-preference={preference}
|
|
83
83
|
aria-label="Theme: {LABEL[preference]}. Switch to {LABEL[upcoming]}."
|
|
84
|
-
class="rounded-lg p-2 text-faint transition-colors duration-150 hover:bg-surface-2 hover:text-fg {klass}"
|
|
84
|
+
class="rounded-lg p-2 text-faint transition-colors duration-150 hover:bg-surface-2 hover:text-fg active:bg-line active:text-fg {klass}"
|
|
85
85
|
>
|
|
86
86
|
<!-- The icon shows the state the control is in, not the one it moves to. It
|
|
87
87
|
showed the destination while the label named the same destination, which
|
|
@@ -401,7 +401,7 @@
|
|
|
401
401
|
takes its name from this same element. The hour keeps its own aria-label,
|
|
402
402
|
which is what a screen reader reads for it. -->
|
|
403
403
|
<label id="{fieldId}-label" for="{fieldId}-hour" class={FIELD_LABEL}>
|
|
404
|
-
{label}{#if required}<span class="
|
|
404
|
+
{label}{#if required}<span class="ms-0.5 text-danger" aria-hidden="true">*</span>{/if}
|
|
405
405
|
</label>
|
|
406
406
|
{/if}
|
|
407
407
|
|
|
@@ -459,7 +459,7 @@
|
|
|
459
459
|
aria-required={required ? 'true' : undefined}
|
|
460
460
|
onchange={onMeridiemChange}
|
|
461
461
|
onkeydown={onMeridiemKeydown}
|
|
462
|
-
class="{CONTROL_SEGMENT}
|
|
462
|
+
class="{CONTROL_SEGMENT} ms-1"
|
|
463
463
|
>
|
|
464
464
|
<option value="AM">AM</option>
|
|
465
465
|
<option value="PM">PM</option>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
change. -->
|
|
19
19
|
<div
|
|
20
20
|
data-print="hide"
|
|
21
|
-
class="pointer-events-none fixed bottom-4
|
|
21
|
+
class="pointer-events-none fixed bottom-4 end-4 z-toast flex w-80 max-w-[calc(100vw-2rem)] flex-col gap-2"
|
|
22
22
|
role="status"
|
|
23
23
|
aria-live="polite"
|
|
24
24
|
aria-atomic="false"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
{#each toast.items as t (t.id)}
|
|
27
27
|
<div
|
|
28
28
|
class="pointer-events-auto flex items-start gap-3 overflow-hidden rounded-lg border border-line
|
|
29
|
-
bg-surface
|
|
29
|
+
bg-surface ps-0 pe-3 py-3 shadow-xl animate-[toast-in_var(--duration-toast-in)_ease-out]"
|
|
30
30
|
>
|
|
31
31
|
<span class="w-1 self-stretch shrink-0 {styles[statusTone(t.tone)].bar}"></span>
|
|
32
32
|
<span
|
|
@@ -27,13 +27,19 @@
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const thumb: Record<'sm' | 'md', string> = {
|
|
30
|
-
sm: 'w-3 h-3 top-0.5
|
|
31
|
-
md: 'w-4 h-4 top-0.5
|
|
30
|
+
sm: 'w-3 h-3 top-0.5 start-0.5',
|
|
31
|
+
md: 'w-4 h-4 top-0.5 start-0.5',
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
/*
|
|
35
|
+
* The travel is mirrored by hand. `start-0.5` moves the resting knob to the
|
|
36
|
+
* right-hand end of the track in a right-to-left page, and a positive
|
|
37
|
+
* translate from there would carry it straight off the track; there is no
|
|
38
|
+
* logical translate utility, so the sign is flipped instead.
|
|
39
|
+
*/
|
|
34
40
|
const thumbOn: Record<'sm' | 'md', string> = {
|
|
35
|
-
sm: 'translate-x-4',
|
|
36
|
-
md: 'translate-x-5',
|
|
41
|
+
sm: 'translate-x-4 rtl:-translate-x-4',
|
|
42
|
+
md: 'translate-x-5 rtl:-translate-x-5',
|
|
37
43
|
};
|
|
38
44
|
|
|
39
45
|
function handleClick() {
|
|
@@ -270,11 +270,11 @@
|
|
|
270
270
|
scan, so no rule is generated and every row draws flush left.
|
|
271
271
|
-->
|
|
272
272
|
<div
|
|
273
|
-
class="flex w-full items-center gap-2 rounded-md py-1.5
|
|
273
|
+
class="flex w-full items-center gap-2 rounded-md py-1.5 pe-2 text-sm transition-colors duration-150 {rowPaint(
|
|
274
274
|
selected === node.id,
|
|
275
275
|
!!node.disabled,
|
|
276
276
|
)}"
|
|
277
|
-
style="padding-
|
|
277
|
+
style="padding-inline-start: calc(var(--spacing-inline) + {depth} * var(--spacing-stack))"
|
|
278
278
|
>
|
|
279
279
|
{#if isBranch(node)}
|
|
280
280
|
<span
|
|
@@ -283,7 +283,7 @@
|
|
|
283
283
|
>
|
|
284
284
|
<ChevronRight
|
|
285
285
|
size={14}
|
|
286
|
-
class="transition-transform duration-150 {open ? 'rotate-90' : ''}"
|
|
286
|
+
class="transition-transform duration-150 {open ? 'rotate-90' : 'rtl:rotate-180'}"
|
|
287
287
|
/>
|
|
288
288
|
</span>
|
|
289
289
|
{:else}
|
|
@@ -321,7 +321,7 @@
|
|
|
321
321
|
<span class="truncate">{node.label}</span>
|
|
322
322
|
|
|
323
323
|
{#if node.badge !== undefined}
|
|
324
|
-
<span class="
|
|
324
|
+
<span class="ms-auto shrink-0 ps-2 text-xs text-faint">{node.badge}</span>
|
|
325
325
|
{/if}
|
|
326
326
|
</div>
|
|
327
327
|
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
|
|
154
154
|
{#if !entry.options.persistent}
|
|
155
155
|
<button
|
|
156
|
-
class="inline-flex items-center justify-center w-8 h-8 -
|
|
156
|
+
class="inline-flex items-center justify-center w-8 h-8 -me-2 rounded-lg
|
|
157
157
|
text-muted hover:text-fg hover:bg-surface-2 transition-colors duration-150 shrink-0"
|
|
158
158
|
onclick={() => handleClose()}
|
|
159
159
|
aria-label="Close"
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export { default as Table } from './components/Table.svelte';
|
|
|
20
20
|
export { default as DescriptionList } from './components/DescriptionList.svelte';
|
|
21
21
|
export { default as Toolbar } from './components/Toolbar.svelte';
|
|
22
22
|
export { default as TreeView } from './components/TreeView.svelte';
|
|
23
|
+
export type { CellFit } from './components/Table.svelte';
|
|
23
24
|
export type { PageWidth, SectionVariant } from './internal/layout.js';
|
|
24
25
|
export type { TreeNode, TriState } from './internal/tree.js';
|
|
25
26
|
export { default as Button } from './components/Button.svelte';
|
|
@@ -88,4 +89,4 @@ export type { ConfirmOptions } from './components/dialog/dialog-manager.svelte.j
|
|
|
88
89
|
export type { DialogOptions, DialogEntry, DialogSize } from './components/dialog/types.js';
|
|
89
90
|
export { cn, type ClassValue } from './utils/cn.js';
|
|
90
91
|
export { getTheme, getThemePreference, nextThemePreference, resolveTheme, setTheme, setThemePreference, systemTheme, themeBootScript, toggleTheme, watchSystemTheme, THEME_PREFERENCES, type Theme, type ThemePreference, } from './utils/theme.js';
|
|
91
|
-
export declare const VERSION = "0.
|
|
92
|
+
export declare const VERSION = "0.20.0";
|
package/dist/index.js
CHANGED
|
@@ -96,4 +96,4 @@ export { getTheme, getThemePreference, nextThemePreference, resolveTheme, setThe
|
|
|
96
96
|
// ── Version ────────────────────────────────────────────────────────────────
|
|
97
97
|
// Generated from package.json by `pnpm version:sync`. Bump package.json, never
|
|
98
98
|
// this line; the build and the test suite fail when the two disagree.
|
|
99
|
-
export const VERSION = '0.
|
|
99
|
+
export const VERSION = '0.20.0';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one rule the kit applies to an href it did not write itself.
|
|
3
|
+
*
|
|
4
|
+
* A component takes a URL from its caller, and a caller takes it from data, so
|
|
5
|
+
* `javascript:` and `data:` arrive as markup and leave as a script the page
|
|
6
|
+
* never wrote. Button carried this rule inline and Pagination now builds hrefs
|
|
7
|
+
* from a caller supplied function, and a rule kept in two places is a rule that
|
|
8
|
+
* gets fixed in one of them.
|
|
9
|
+
*
|
|
10
|
+
* Not exported from the package entry point - this is an implementation detail.
|
|
11
|
+
*/
|
|
12
|
+
/** The href as given, or nothing when its scheme is one no component will emit. */
|
|
13
|
+
export declare function safeHref(href: string | undefined | null): string | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one rule the kit applies to an href it did not write itself.
|
|
3
|
+
*
|
|
4
|
+
* A component takes a URL from its caller, and a caller takes it from data, so
|
|
5
|
+
* `javascript:` and `data:` arrive as markup and leave as a script the page
|
|
6
|
+
* never wrote. Button carried this rule inline and Pagination now builds hrefs
|
|
7
|
+
* from a caller supplied function, and a rule kept in two places is a rule that
|
|
8
|
+
* gets fixed in one of them.
|
|
9
|
+
*
|
|
10
|
+
* Not exported from the package entry point - this is an implementation detail.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The characters a browser drops before it reads the scheme.
|
|
14
|
+
*
|
|
15
|
+
* The URL parser trims leading and trailing ASCII whitespace and strips tab,
|
|
16
|
+
* line feed and carriage return from anywhere in the value, so a tab written
|
|
17
|
+
* inside the word `javascript` still navigates while an anchored scheme test on
|
|
18
|
+
* the raw string sees an unknown scheme and allows it through.
|
|
19
|
+
*/
|
|
20
|
+
const IGNORED = /[\u0000-\u0020]/g;
|
|
21
|
+
const DENIED = /^(?:javascript|data|vbscript):/i;
|
|
22
|
+
/** The href as given, or nothing when its scheme is one no component will emit. */
|
|
23
|
+
export function safeHref(href) {
|
|
24
|
+
if (!href)
|
|
25
|
+
return undefined;
|
|
26
|
+
return DENIED.test(href.replace(IGNORED, '')) ? undefined : href;
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Digit grouping for a figure a reader is meant to take in at a glance.
|
|
3
|
+
*
|
|
4
|
+
* The locale is fixed rather than the runtime's. `toLocaleString()` with no
|
|
5
|
+
* argument formats by whatever locale the environment reports, and the server
|
|
6
|
+
* that renders a page and the browser that hydrates it do not have to report
|
|
7
|
+
* the same one: the same figure then arrives as `4,210` in the HTML and as
|
|
8
|
+
* `4.210` or `4210` after hydration, which is a mismatch the framework warns
|
|
9
|
+
* about and a number the reader watches change under them. A component library
|
|
10
|
+
* has no way to know which locale a host has configured either, so the
|
|
11
|
+
* separator has to be a decision the kit makes once.
|
|
12
|
+
*
|
|
13
|
+
* `en-US` because that is the language every string in the kit is written in,
|
|
14
|
+
* and an English sentence carrying a German separator is the worse of the two
|
|
15
|
+
* inconsistencies.
|
|
16
|
+
*
|
|
17
|
+
* Not exported from the package entry point - this is an implementation detail.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* A count, grouped in threes.
|
|
21
|
+
*
|
|
22
|
+
* A value that is not a finite number reads as zero rather than reaching the
|
|
23
|
+
* formatter, which spells infinity as a glyph no summary line should carry and
|
|
24
|
+
* NaN as a word no reader can act on.
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatCount(n: number): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Digit grouping for a figure a reader is meant to take in at a glance.
|
|
3
|
+
*
|
|
4
|
+
* The locale is fixed rather than the runtime's. `toLocaleString()` with no
|
|
5
|
+
* argument formats by whatever locale the environment reports, and the server
|
|
6
|
+
* that renders a page and the browser that hydrates it do not have to report
|
|
7
|
+
* the same one: the same figure then arrives as `4,210` in the HTML and as
|
|
8
|
+
* `4.210` or `4210` after hydration, which is a mismatch the framework warns
|
|
9
|
+
* about and a number the reader watches change under them. A component library
|
|
10
|
+
* has no way to know which locale a host has configured either, so the
|
|
11
|
+
* separator has to be a decision the kit makes once.
|
|
12
|
+
*
|
|
13
|
+
* `en-US` because that is the language every string in the kit is written in,
|
|
14
|
+
* and an English sentence carrying a German separator is the worse of the two
|
|
15
|
+
* inconsistencies.
|
|
16
|
+
*
|
|
17
|
+
* Not exported from the package entry point - this is an implementation detail.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Built at module load, not per call.
|
|
21
|
+
*
|
|
22
|
+
* Constructing an `Intl` formatter is the expensive half of using one, and a
|
|
23
|
+
* pager re-renders on every keystroke that filters the list behind it.
|
|
24
|
+
*/
|
|
25
|
+
const GROUPED = new Intl.NumberFormat('en-US');
|
|
26
|
+
/**
|
|
27
|
+
* A count, grouped in threes.
|
|
28
|
+
*
|
|
29
|
+
* A value that is not a finite number reads as zero rather than reaching the
|
|
30
|
+
* formatter, which spells infinity as a glyph no summary line should carry and
|
|
31
|
+
* NaN as a word no reader can act on.
|
|
32
|
+
*/
|
|
33
|
+
export function formatCount(n) {
|
|
34
|
+
return Number.isFinite(n) ? GROUPED.format(n) : '0';
|
|
35
|
+
}
|
package/dist/internal/panel.js
CHANGED
|
@@ -55,7 +55,7 @@ export const PANEL_LIST = 'max-h-panel-max overflow-y-auto overscroll-contain py
|
|
|
55
55
|
* No background and no text colour of its own: it inherits both from the
|
|
56
56
|
* surface, which leaves each state below a single utility to override.
|
|
57
57
|
*/
|
|
58
|
-
export const PANEL_OPTION = 'flex w-full items-center gap-2.5 px-3 py-2 text-
|
|
58
|
+
export const PANEL_OPTION = 'flex w-full items-center gap-2.5 px-3 py-2 text-start text-sm ' +
|
|
59
59
|
'transition-colors duration-150 outline-none hover:bg-surface-2 ' +
|
|
60
60
|
'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand';
|
|
61
61
|
/**
|
package/dist/styles/theme.css
CHANGED
|
@@ -151,6 +151,8 @@
|
|
|
151
151
|
* Body 16px 1.55 0 400 Default body copy *
|
|
152
152
|
* Caption 13px 1.50 0 400 Labels, metadata *
|
|
153
153
|
* Mono 18px 1.50 0 500 Code, eyebrows *
|
|
154
|
+
* The ramp the guideline states, for marketing surfaces. *
|
|
155
|
+
* The scale the console renders is declared below it. *
|
|
154
156
|
* */
|
|
155
157
|
--text-display: 4rem; /* 64px */
|
|
156
158
|
--text-h1: 2.75rem; /* 44px */
|
|
@@ -173,6 +175,57 @@
|
|
|
173
175
|
--tracking-h2: -0.02em;
|
|
174
176
|
--tracking-h3: -0.015em;
|
|
175
177
|
|
|
178
|
+
/* -- Typography - console type scale --------------------------- *
|
|
179
|
+
* Token Size Line Where it renders *
|
|
180
|
+
* text-xs 12px 16px labels, table heads, metadata, hints*
|
|
181
|
+
* text-sm 14px 20px body copy and every control *
|
|
182
|
+
* text-base 16px 24px a card title, the large button *
|
|
183
|
+
* text-lg 18px 28px a dialog title, a small stat value *
|
|
184
|
+
* text-xl 20px 28px the large wordmark *
|
|
185
|
+
* text-2xl 24px 32px a stat value, an empty-state glyph *
|
|
186
|
+
*
|
|
187
|
+
* Two scales, on purpose, and this is the one the components
|
|
188
|
+
* are drawn in.
|
|
189
|
+
*
|
|
190
|
+
* The ramp above is the brand guideline's, and it is a marketing
|
|
191
|
+
* ramp: it runs 64 / 44 / 32 / 22 / 16 / 13, holds nothing
|
|
192
|
+
* between 16px and 22px, and has no step at 14px at all. A
|
|
193
|
+
* console's body copy is 14px and its labels are 12px, both
|
|
194
|
+
* below the ramp's floor, so adopting the ramp wholesale would
|
|
195
|
+
* have meant restyling every screen to fit a scale drawn for a
|
|
196
|
+
* landing page. The ramp keeps its steps for the surfaces it was
|
|
197
|
+
* drawn for, and the two overlap at exactly the two steps a
|
|
198
|
+
* console does reach: PageHeader takes H2 for the page title and
|
|
199
|
+
* SectionHeading takes H3 for a section head.
|
|
200
|
+
*
|
|
201
|
+
* These are Tailwind's own steps at Tailwind's own values,
|
|
202
|
+
* stated here rather than inherited silently. Nothing renders
|
|
203
|
+
* one pixel differently for it. What changes is that the scale
|
|
204
|
+
* the components are built from is now declared beside the one
|
|
205
|
+
* they are not, so a designer reading this file sees both, and
|
|
206
|
+
* moving console body copy to 15px is one line here instead of
|
|
207
|
+
* 49 class attributes. Restated rather than renamed: an app
|
|
208
|
+
* writing `text-sm` in its own markup and a component writing it
|
|
209
|
+
* in the kit's must mean the same size, and a private spelling
|
|
210
|
+
* of the same 14px would be a second name for one thing.
|
|
211
|
+
*
|
|
212
|
+
* A step is declared with its leading, because a --text-* token
|
|
213
|
+
* sets font-size and nothing else. Only the six steps the kit
|
|
214
|
+
* actually renders are declared; the larger ones a marketing
|
|
215
|
+
* page reaches for stay as Tailwind ships them. */
|
|
216
|
+
--text-xs: 0.75rem; /* 12px */
|
|
217
|
+
--text-xs--line-height: calc(1 / 0.75); /* 16px */
|
|
218
|
+
--text-sm: 0.875rem; /* 14px */
|
|
219
|
+
--text-sm--line-height: calc(1.25 / 0.875); /* 20px */
|
|
220
|
+
--text-base: 1rem; /* 16px */
|
|
221
|
+
--text-base--line-height: calc(1.5 / 1); /* 24px */
|
|
222
|
+
--text-lg: 1.125rem; /* 18px */
|
|
223
|
+
--text-lg--line-height: calc(1.75 / 1.125); /* 28px */
|
|
224
|
+
--text-xl: 1.25rem; /* 20px */
|
|
225
|
+
--text-xl--line-height: calc(1.75 / 1.25); /* 28px */
|
|
226
|
+
--text-2xl: 1.5rem; /* 24px */
|
|
227
|
+
--text-2xl--line-height: calc(2 / 1.5); /* 32px */
|
|
228
|
+
|
|
176
229
|
/* ── Spacing grid · 4px base (semantic shortcuts) ────────────── *
|
|
177
230
|
* Tailwind's default 0.25rem (4px) grid is unchanged. *
|
|
178
231
|
* These tokens add named slots for common component spacing. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lyeve-labs/ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "A clean, accessible, palette-aware Svelte 5 component library. The design system behind LyEve.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "LyEve Labs <hello@lyeve.com>",
|
|
@@ -44,16 +44,15 @@
|
|
|
44
44
|
"./package.json": "./package.json"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
+
"@lucide/svelte": ">=0.511.0 <2",
|
|
47
48
|
"svelte": "^5.0.0",
|
|
48
49
|
"tailwindcss": "^4"
|
|
49
50
|
},
|
|
50
51
|
"publishConfig": {
|
|
51
52
|
"access": "public"
|
|
52
53
|
},
|
|
53
|
-
"dependencies": {
|
|
54
|
-
"@lucide/svelte": "^0.511.0"
|
|
55
|
-
},
|
|
56
54
|
"devDependencies": {
|
|
55
|
+
"@lucide/svelte": "^0.511.0",
|
|
57
56
|
"@sveltejs/package": "^2.5.7",
|
|
58
57
|
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
|
59
58
|
"@testing-library/svelte": "^5.3.1",
|
package/src/lib/styles/theme.css
CHANGED
|
@@ -151,6 +151,8 @@
|
|
|
151
151
|
* Body 16px 1.55 0 400 Default body copy *
|
|
152
152
|
* Caption 13px 1.50 0 400 Labels, metadata *
|
|
153
153
|
* Mono 18px 1.50 0 500 Code, eyebrows *
|
|
154
|
+
* The ramp the guideline states, for marketing surfaces. *
|
|
155
|
+
* The scale the console renders is declared below it. *
|
|
154
156
|
* */
|
|
155
157
|
--text-display: 4rem; /* 64px */
|
|
156
158
|
--text-h1: 2.75rem; /* 44px */
|
|
@@ -173,6 +175,57 @@
|
|
|
173
175
|
--tracking-h2: -0.02em;
|
|
174
176
|
--tracking-h3: -0.015em;
|
|
175
177
|
|
|
178
|
+
/* -- Typography - console type scale --------------------------- *
|
|
179
|
+
* Token Size Line Where it renders *
|
|
180
|
+
* text-xs 12px 16px labels, table heads, metadata, hints*
|
|
181
|
+
* text-sm 14px 20px body copy and every control *
|
|
182
|
+
* text-base 16px 24px a card title, the large button *
|
|
183
|
+
* text-lg 18px 28px a dialog title, a small stat value *
|
|
184
|
+
* text-xl 20px 28px the large wordmark *
|
|
185
|
+
* text-2xl 24px 32px a stat value, an empty-state glyph *
|
|
186
|
+
*
|
|
187
|
+
* Two scales, on purpose, and this is the one the components
|
|
188
|
+
* are drawn in.
|
|
189
|
+
*
|
|
190
|
+
* The ramp above is the brand guideline's, and it is a marketing
|
|
191
|
+
* ramp: it runs 64 / 44 / 32 / 22 / 16 / 13, holds nothing
|
|
192
|
+
* between 16px and 22px, and has no step at 14px at all. A
|
|
193
|
+
* console's body copy is 14px and its labels are 12px, both
|
|
194
|
+
* below the ramp's floor, so adopting the ramp wholesale would
|
|
195
|
+
* have meant restyling every screen to fit a scale drawn for a
|
|
196
|
+
* landing page. The ramp keeps its steps for the surfaces it was
|
|
197
|
+
* drawn for, and the two overlap at exactly the two steps a
|
|
198
|
+
* console does reach: PageHeader takes H2 for the page title and
|
|
199
|
+
* SectionHeading takes H3 for a section head.
|
|
200
|
+
*
|
|
201
|
+
* These are Tailwind's own steps at Tailwind's own values,
|
|
202
|
+
* stated here rather than inherited silently. Nothing renders
|
|
203
|
+
* one pixel differently for it. What changes is that the scale
|
|
204
|
+
* the components are built from is now declared beside the one
|
|
205
|
+
* they are not, so a designer reading this file sees both, and
|
|
206
|
+
* moving console body copy to 15px is one line here instead of
|
|
207
|
+
* 49 class attributes. Restated rather than renamed: an app
|
|
208
|
+
* writing `text-sm` in its own markup and a component writing it
|
|
209
|
+
* in the kit's must mean the same size, and a private spelling
|
|
210
|
+
* of the same 14px would be a second name for one thing.
|
|
211
|
+
*
|
|
212
|
+
* A step is declared with its leading, because a --text-* token
|
|
213
|
+
* sets font-size and nothing else. Only the six steps the kit
|
|
214
|
+
* actually renders are declared; the larger ones a marketing
|
|
215
|
+
* page reaches for stay as Tailwind ships them. */
|
|
216
|
+
--text-xs: 0.75rem; /* 12px */
|
|
217
|
+
--text-xs--line-height: calc(1 / 0.75); /* 16px */
|
|
218
|
+
--text-sm: 0.875rem; /* 14px */
|
|
219
|
+
--text-sm--line-height: calc(1.25 / 0.875); /* 20px */
|
|
220
|
+
--text-base: 1rem; /* 16px */
|
|
221
|
+
--text-base--line-height: calc(1.5 / 1); /* 24px */
|
|
222
|
+
--text-lg: 1.125rem; /* 18px */
|
|
223
|
+
--text-lg--line-height: calc(1.75 / 1.125); /* 28px */
|
|
224
|
+
--text-xl: 1.25rem; /* 20px */
|
|
225
|
+
--text-xl--line-height: calc(1.75 / 1.25); /* 28px */
|
|
226
|
+
--text-2xl: 1.5rem; /* 24px */
|
|
227
|
+
--text-2xl--line-height: calc(2 / 1.5); /* 32px */
|
|
228
|
+
|
|
176
229
|
/* ── Spacing grid · 4px base (semantic shortcuts) ────────────── *
|
|
177
230
|
* Tailwind's default 0.25rem (4px) grid is unchanged. *
|
|
178
231
|
* These tokens add named slots for common component spacing. */
|