@lyeve-labs/ui-kit 0.23.0 → 0.23.2
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/CheckboxGroup.svelte +3 -2
- package/dist/components/RadioGroup.svelte +3 -2
- package/dist/components/Table.svelte +25 -3
- package/dist/components/Tooltip.svelte +58 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/choice.d.ts +9 -0
- package/dist/internal/choice.js +10 -1
- package/package.json +1 -1
|
@@ -26,12 +26,13 @@
|
|
|
26
26
|
<script lang="ts">
|
|
27
27
|
import {
|
|
28
28
|
CHOICE_GROUP,
|
|
29
|
+
CHOICE_LEGEND,
|
|
29
30
|
choiceGroupList,
|
|
30
31
|
type ChoiceOrientation,
|
|
31
32
|
type ChoiceSize,
|
|
32
33
|
type ChoiceVariant,
|
|
33
34
|
} from '../internal/choice.js';
|
|
34
|
-
import { FIELD_ERROR, FIELD_HINT,
|
|
35
|
+
import { FIELD_ERROR, FIELD_HINT, describedBy } from '../internal/field.js';
|
|
35
36
|
import Checkbox from './Checkbox.svelte';
|
|
36
37
|
|
|
37
38
|
interface Props {
|
|
@@ -123,7 +124,7 @@
|
|
|
123
124
|
so a reader moving through the page would meet the options with nothing
|
|
124
125
|
ahead of them saying what the set is for.
|
|
125
126
|
-->
|
|
126
|
-
<legend class="{
|
|
127
|
+
<legend class="{CHOICE_LEGEND} {labelHidden ? 'sr-only' : ''}">
|
|
127
128
|
{label}{#if required}<span class="text-danger ms-0.5" aria-hidden="true">*</span><span
|
|
128
129
|
class="sr-only"
|
|
129
130
|
>
|
|
@@ -27,12 +27,13 @@
|
|
|
27
27
|
<script lang="ts">
|
|
28
28
|
import {
|
|
29
29
|
CHOICE_GROUP,
|
|
30
|
+
CHOICE_LEGEND,
|
|
30
31
|
choiceGroupList,
|
|
31
32
|
type ChoiceOrientation,
|
|
32
33
|
type ChoiceSize,
|
|
33
34
|
type ChoiceVariant,
|
|
34
35
|
} from '../internal/choice.js';
|
|
35
|
-
import { FIELD_ERROR, FIELD_HINT,
|
|
36
|
+
import { FIELD_ERROR, FIELD_HINT, describedBy } from '../internal/field.js';
|
|
36
37
|
import Radio from './Radio.svelte';
|
|
37
38
|
|
|
38
39
|
interface Props {
|
|
@@ -108,7 +109,7 @@
|
|
|
108
109
|
so a reader moving through the page would meet the options with nothing
|
|
109
110
|
ahead of them saying what the set is for.
|
|
110
111
|
-->
|
|
111
|
-
<legend class="{
|
|
112
|
+
<legend class="{CHOICE_LEGEND} {labelHidden ? 'sr-only' : ''}">
|
|
112
113
|
{label}{#if required}<span class="text-danger ms-0.5" aria-hidden="true">*</span>{/if}
|
|
113
114
|
</legend>
|
|
114
115
|
|
|
@@ -56,6 +56,23 @@
|
|
|
56
56
|
children: Snippet;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The hover ring, as inset shadows on the cells of the hovered row: top
|
|
61
|
+
* and bottom on every cell, the start edge on the first, the end edge on
|
|
62
|
+
* the last, so the four lines meet as one rectangle and the two outer
|
|
63
|
+
* cells of the last row carry it round the frame's corner. The shadow
|
|
64
|
+
* offsets are physical, which is what a shadow is; the first cell is the
|
|
65
|
+
* start cell in either direction and the two agree in left-to-right text.
|
|
66
|
+
*/
|
|
67
|
+
const HOVER_RING = [
|
|
68
|
+
'[&_tbody_tr:hover]:bg-surface-2/60',
|
|
69
|
+
'[&_tbody_tr:hover>td]:shadow-[inset_0_1px_var(--color-brand),inset_0_-1px_var(--color-brand)]',
|
|
70
|
+
'[&_tbody_tr:hover>td:first-child]:shadow-[inset_1px_1px_var(--color-brand),inset_1px_-1px_var(--color-brand)]',
|
|
71
|
+
'[&_tbody_tr:hover>td:last-child]:shadow-[inset_-1px_1px_var(--color-brand),inset_-1px_-1px_var(--color-brand)]',
|
|
72
|
+
'[&_tbody_tr:hover>td:first-child:last-child]:shadow-[inset_1px_1px_var(--color-brand),inset_-1px_-1px_var(--color-brand)]',
|
|
73
|
+
'[&_tbody_tr]:transition-colors [&_tbody_tr]:duration-150',
|
|
74
|
+
].join(' ');
|
|
75
|
+
|
|
59
76
|
let {
|
|
60
77
|
striped = false,
|
|
61
78
|
hoverable = true,
|
|
@@ -231,6 +248,11 @@
|
|
|
231
248
|
<!--
|
|
232
249
|
border-separate with no spacing, and the row separators on the cells.
|
|
233
250
|
|
|
251
|
+
The last row's outer cells are rounded to the frame's inner radius. A
|
|
252
|
+
row takes no radius, so a ring drawn on it was a square inset in a
|
|
253
|
+
rounded frame and the frame's bottom arcs sat outside it; the ring is
|
|
254
|
+
drawn on the cells now (see HOVER_RING) and follows the corner.
|
|
255
|
+
|
|
234
256
|
The hovered row draws a ring, and a ring is a box-shadow: a browser paints
|
|
235
257
|
no box-shadow on a table row while the table collapses its borders, so the
|
|
236
258
|
same row treatment the panels use would have rendered nothing at all here.
|
|
@@ -251,9 +273,9 @@
|
|
|
251
273
|
[&_tbody_td]:px-4 [&_tbody_td]:py-3 [&_tbody_td]:text-fg [&_tbody_td]:align-middle
|
|
252
274
|
[&_tbody_td]:border-b [&_tbody_td]:border-line [&_tbody_tr:last-child_td]:border-0
|
|
253
275
|
{striped ? '[&_tbody_tr:nth-child(even)]:bg-surface-2/40' : ''}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
276
|
+
[&_tbody_tr:last-child>td:first-child]:rounded-es-[calc(var(--radius-xl)-1px)]
|
|
277
|
+
[&_tbody_tr:last-child>td:last-child]:rounded-ee-[calc(var(--radius-xl)-1px)]
|
|
278
|
+
{hoverable ? HOVER_RING : ''}"
|
|
257
279
|
>
|
|
258
280
|
{@render children()}
|
|
259
281
|
</table>
|
|
@@ -27,13 +27,63 @@
|
|
|
27
27
|
|
|
28
28
|
const id = $props.id();
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* The box is positioned from the trigger's bounding box, fixed to the
|
|
32
|
+
* viewport, rather than absolutely inside the wrapper.
|
|
33
|
+
*
|
|
34
|
+
* Inside the wrapper it took the wrapper's width, so a hint on an icon
|
|
35
|
+
* button broke one character per line, and any ancestor that scrolls cut
|
|
36
|
+
* it off: a Table's scroller is overflow-x auto, which makes its vertical
|
|
37
|
+
* overflow auto as well, so a hint on a row action was clipped at the
|
|
38
|
+
* header. Nothing between a fixed box and the viewport can clip it. The
|
|
39
|
+
* translate carries the half of the geometry that depends on the box's own
|
|
40
|
+
* size, which is not known until it renders.
|
|
41
|
+
*/
|
|
30
42
|
const pos: Record<Position, string> = {
|
|
31
|
-
top: '
|
|
32
|
-
bottom: '
|
|
33
|
-
left: '
|
|
34
|
-
right: '
|
|
43
|
+
top: '-translate-x-1/2 -translate-y-full',
|
|
44
|
+
bottom: '-translate-x-1/2',
|
|
45
|
+
left: '-translate-x-full -translate-y-1/2',
|
|
46
|
+
right: '-translate-y-1/2',
|
|
35
47
|
};
|
|
36
48
|
|
|
49
|
+
/** The distance between the trigger's edge and the box. */
|
|
50
|
+
const GAP = 8;
|
|
51
|
+
|
|
52
|
+
let anchor = $state({ top: 0, left: 0 });
|
|
53
|
+
|
|
54
|
+
function place() {
|
|
55
|
+
const r = wrapper?.getBoundingClientRect();
|
|
56
|
+
if (!r) return;
|
|
57
|
+
switch (position) {
|
|
58
|
+
case 'top':
|
|
59
|
+
anchor = { top: r.top - GAP, left: r.left + r.width / 2 };
|
|
60
|
+
break;
|
|
61
|
+
case 'bottom':
|
|
62
|
+
anchor = { top: r.bottom + GAP, left: r.left + r.width / 2 };
|
|
63
|
+
break;
|
|
64
|
+
case 'left':
|
|
65
|
+
anchor = { top: r.top + r.height / 2, left: r.left - GAP };
|
|
66
|
+
break;
|
|
67
|
+
case 'right':
|
|
68
|
+
anchor = { top: r.top + r.height / 2, left: r.right + GAP };
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function show() {
|
|
74
|
+
place();
|
|
75
|
+
visible = true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// A fixed box does not follow a trigger that scrolls away from under it,
|
|
79
|
+
// so a scroll anywhere closes the hint rather than leaving it stranded.
|
|
80
|
+
$effect(() => {
|
|
81
|
+
if (!visible) return;
|
|
82
|
+
const close = () => (visible = false);
|
|
83
|
+
window.addEventListener('scroll', close, { capture: true, passive: true });
|
|
84
|
+
return () => window.removeEventListener('scroll', close, { capture: true });
|
|
85
|
+
});
|
|
86
|
+
|
|
37
87
|
/**
|
|
38
88
|
* `role="tooltip"` on its own is inert: assistive technology reads a tooltip
|
|
39
89
|
* only because the element it describes points at it. The trigger arrives as
|
|
@@ -68,9 +118,9 @@
|
|
|
68
118
|
<span
|
|
69
119
|
bind:this={wrapper}
|
|
70
120
|
class="relative inline-flex {cls}"
|
|
71
|
-
onmouseenter={
|
|
121
|
+
onmouseenter={show}
|
|
72
122
|
onmouseleave={() => (visible = false)}
|
|
73
|
-
onfocusin={
|
|
123
|
+
onfocusin={show}
|
|
74
124
|
onfocusout={() => (visible = false)}
|
|
75
125
|
{onkeydown}
|
|
76
126
|
>
|
|
@@ -84,7 +134,8 @@
|
|
|
84
134
|
role={describe ? 'tooltip' : undefined}
|
|
85
135
|
aria-hidden={describe ? undefined : 'true'}
|
|
86
136
|
hidden={!visible}
|
|
87
|
-
|
|
137
|
+
style="top: {anchor.top}px; left: {anchor.left}px"
|
|
138
|
+
class="fixed {pos[position]} z-tooltip w-max max-w-[min(16rem,calc(100vw-2rem))]
|
|
88
139
|
rounded-lg border border-line bg-surface-2 px-2.5 py-1.5 text-xs text-fg shadow-xl"
|
|
89
140
|
>
|
|
90
141
|
{text}
|
package/dist/index.d.ts
CHANGED
|
@@ -89,4 +89,4 @@ export type { ConfirmOptions } from './components/dialog/dialog-manager.svelte.j
|
|
|
89
89
|
export type { DialogOptions, DialogEntry, DialogSize } from './components/dialog/types.js';
|
|
90
90
|
export { cn, type ClassValue } from './utils/cn.js';
|
|
91
91
|
export { getTheme, getThemePreference, nextThemePreference, resolveTheme, setTheme, setThemePreference, systemTheme, themeBootScript, toggleTheme, watchSystemTheme, THEME_PREFERENCES, type Theme, type ThemePreference, } from './utils/theme.js';
|
|
92
|
-
export declare const VERSION = "0.23.
|
|
92
|
+
export declare const VERSION = "0.23.2";
|
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.23.
|
|
99
|
+
export const VERSION = '0.23.2';
|
|
@@ -112,6 +112,15 @@ export declare const CHOICE_ICON_PX: Record<ChoiceSize, number>;
|
|
|
112
112
|
* wrapping.
|
|
113
113
|
*/
|
|
114
114
|
export declare const CHOICE_GROUP = "flex min-w-0 flex-col gap-2";
|
|
115
|
+
/**
|
|
116
|
+
* The group's heading. A rendered legend is not a flex item: the browser lays
|
|
117
|
+
* it out as the fieldset's legend, outside the flex formatting context, so
|
|
118
|
+
* the group's gap applies between the list and the hint and never between
|
|
119
|
+
* the legend and the list. The legend carries the distance itself, the same
|
|
120
|
+
* 6px a Field puts between its label and its control. A hidden legend is
|
|
121
|
+
* absolutely positioned and the margin does nothing there.
|
|
122
|
+
*/
|
|
123
|
+
export declare const CHOICE_LEGEND = "text-sm font-medium text-fg mb-1.5";
|
|
115
124
|
/**
|
|
116
125
|
* The row or column the options sit in.
|
|
117
126
|
*
|
package/dist/internal/choice.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Not exported from the package entry point - this is an implementation detail.
|
|
16
16
|
*/
|
|
17
|
-
import { FIELD_HINT } from './field.js';
|
|
17
|
+
import { FIELD_HINT, FIELD_LABEL } from './field.js';
|
|
18
18
|
/**
|
|
19
19
|
* The box and the icon slot share one size map, so the label text starts at the
|
|
20
20
|
* same x whether or not an option carries an icon. 3.5 is 14px, 4 is 16px and 5
|
|
@@ -151,6 +151,15 @@ export const CHOICE_ICON_PX = {
|
|
|
151
151
|
* wrapping.
|
|
152
152
|
*/
|
|
153
153
|
export const CHOICE_GROUP = 'flex min-w-0 flex-col gap-2';
|
|
154
|
+
/**
|
|
155
|
+
* The group's heading. A rendered legend is not a flex item: the browser lays
|
|
156
|
+
* it out as the fieldset's legend, outside the flex formatting context, so
|
|
157
|
+
* the group's gap applies between the list and the hint and never between
|
|
158
|
+
* the legend and the list. The legend carries the distance itself, the same
|
|
159
|
+
* 6px a Field puts between its label and its control. A hidden legend is
|
|
160
|
+
* absolutely positioned and the margin does nothing there.
|
|
161
|
+
*/
|
|
162
|
+
export const CHOICE_LEGEND = `${FIELD_LABEL} mb-1.5`;
|
|
154
163
|
/**
|
|
155
164
|
* The row or column the options sit in.
|
|
156
165
|
*
|
package/package.json
CHANGED