@signal9/era-ui 30.7.0 → 31.0.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/CHANGELOG.md +20 -0
- package/dist/ai/conversations/conversations-item.svelte +1 -1
- package/dist/ai/plan/plan-header.svelte +1 -1
- package/dist/ai/prompt-input/prompt-input-attachments.svelte +2 -6
- package/dist/ai/swarm/swarm-node.svelte +1 -1
- package/dist/ai/todo/todo-header.svelte +1 -1
- package/dist/ai/todo/todo-item.svelte +1 -1
- package/dist/ai/tool/tool-chip.svelte +1 -1
- package/dist/ai/web-preview/web-preview.svelte +1 -1
- package/dist/dev/audit/audits/wall-is-isotropic.js +1 -1
- package/dist/docs/css-utilities.js +7 -3
- package/dist/era-ui.css +1 -1
- package/dist/generated-docs/badge.md +1 -1
- package/dist/generated-docs/bar.md +22 -4
- package/dist/generated-docs/button.md +38 -4
- package/dist/generated-docs/card.md +35 -5
- package/dist/generated-docs/chip.md +16 -0
- package/dist/generated-docs/llms-full.txt +310 -49
- package/dist/generated-docs/llms.txt +4 -3
- package/dist/generated-docs/manifest.json +45 -21
- package/dist/generated-docs/meter.md +79 -1
- package/dist/generated-docs/nav.md +8 -1
- package/dist/generated-docs/separator.md +18 -0
- package/dist/generated-docs/utilities.json +32 -9
- package/dist/generated-docs/utilities.md +89 -30
- package/dist/styles/density.css +15 -25
- package/dist/styles/index.css +32 -27
- package/dist/ui/accordion/accordion-trigger.svelte +1 -1
- package/dist/ui/alert-dialog/alert-dialog-action.svelte +2 -0
- package/dist/ui/alert-dialog/alert-dialog-cancel.svelte +2 -0
- package/dist/ui/badge/badge.svelte +8 -2
- package/dist/ui/badge/badge.svelte.d.ts +1 -1
- package/dist/ui/bar/bar.svelte +95 -26
- package/dist/ui/bar/bar.svelte.d.ts +71 -31
- package/dist/ui/button/button.svelte +38 -2
- package/dist/ui/button/button.svelte.d.ts +29 -0
- package/dist/ui/button/index.d.ts +2 -0
- package/dist/ui/button/variants.js +4 -9
- package/dist/ui/calendar/calendar-day.svelte +1 -1
- package/dist/ui/card/card.svelte +22 -9
- package/dist/ui/card/card.svelte.d.ts +19 -7
- package/dist/ui/chip/chip.svelte +5 -2
- package/dist/ui/chip/chip.svelte.d.ts +6 -6
- package/dist/ui/collapsible/collapsible-trigger.svelte +1 -1
- package/dist/ui/combobox/combobox-trigger.svelte +1 -1
- package/dist/ui/command/command-item.svelte +1 -1
- package/dist/ui/command/command-link-item.svelte +1 -1
- package/dist/ui/command/command-root.svelte +3 -5
- package/dist/ui/date-field/date-field-segment.svelte +1 -1
- package/dist/ui/date-picker/date-picker-day.svelte +1 -1
- package/dist/ui/date-picker/date-picker-segment.svelte +1 -1
- package/dist/ui/date-picker/date-picker-trigger.svelte +1 -1
- package/dist/ui/date-range-field/date-range-field-segment.svelte +1 -1
- package/dist/ui/date-range-picker/date-range-picker-day.svelte +1 -1
- package/dist/ui/date-range-picker/date-range-picker-segment.svelte +1 -1
- package/dist/ui/date-range-picker/date-range-picker-trigger.svelte +1 -1
- package/dist/ui/dialog/dialog-close.svelte +2 -0
- package/dist/ui/dialog/dialog-content.svelte +19 -1
- package/dist/ui/dropdown-menu/dropdown-menu-trigger.svelte +1 -1
- package/dist/ui/file-upload/file-upload.svelte +1 -1
- package/dist/ui/input/variants.d.ts +6 -6
- package/dist/ui/input/variants.js +2 -2
- package/dist/ui/kv/kv.svelte +1 -1
- package/dist/ui/menubar/menubar-root.svelte +1 -1
- package/dist/ui/menubar/menubar-trigger.svelte +1 -1
- package/dist/ui/nav/nav-bar.svelte +9 -4
- package/dist/ui/nav/nav-bar.svelte.d.ts +2 -1
- package/dist/ui/pane/pane-root.svelte +3 -12
- package/dist/ui/pane/pane-tab.svelte +1 -1
- package/dist/ui/select/select-trigger.svelte +1 -1
- package/dist/ui/slider/slider-thumb.svelte +1 -1
- package/dist/ui/step/step-summary.svelte +1 -1
- package/dist/ui/switch/switch.svelte +2 -2
- package/dist/ui/time-field/time-field-segment.svelte +1 -1
- package/dist/ui/time-range-field/time-range-field-segment.svelte +1 -1
- package/dist/ui/timeline/timeline-step.svelte +1 -1
- package/dist/ui/toggle-group/toggle-group-item.svelte +2 -2
- package/dist/ui/toggle-group/toggle-group-item.svelte.d.ts +6 -6
- package/dist/ui/toggle-group/toggle-group-root.svelte +1 -2
- package/dist/ui/toggle-group/toggle-group-root.svelte.d.ts +3 -3
- package/dist/ui/tooltip/tooltip-content.svelte +1 -1
- package/dist/ui/tree/tree-item.svelte +1 -1
- package/dist/ui/video-player/video-player.svelte +1 -1
- package/dist/utils/tw-merge-config.d.ts +3 -0
- package/dist/utils/tw-merge-config.js +4 -0
- package/package.json +4 -3
- package/skill/SKILL.md +17 -17
package/dist/ui/bar/bar.svelte
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import { cn, tv } from '../../utils/index.js';
|
|
3
3
|
|
|
4
|
+
export type BarSize = 'chip' | 'control' | 'bar';
|
|
5
|
+
export type BarContent = 'icon' | 'pill' | 'chip' | 'control';
|
|
6
|
+
export type BarGeometry =
|
|
7
|
+
| { size: 'chip'; content?: 'icon' | 'pill' }
|
|
8
|
+
| { size: 'control'; content?: 'icon' | 'pill' | 'chip' }
|
|
9
|
+
| { size?: 'bar'; content?: BarContent };
|
|
10
|
+
|
|
11
|
+
const defaultContent: Record<BarSize, BarContent> = {
|
|
12
|
+
chip: 'icon',
|
|
13
|
+
control: 'chip',
|
|
14
|
+
bar: 'control'
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function resolveBarContent(size: BarSize, content?: BarContent): BarContent {
|
|
18
|
+
const resolved = content ?? defaultContent[size];
|
|
19
|
+
const order = ['icon', 'pill', 'chip', 'control', 'bar'];
|
|
20
|
+
if (order.indexOf(resolved) >= order.indexOf(size)) {
|
|
21
|
+
throw new Error(`Bar size="${size}" cannot contain tier "${resolved}"`);
|
|
22
|
+
}
|
|
23
|
+
return resolved;
|
|
24
|
+
}
|
|
25
|
+
|
|
4
26
|
// A Bar is a flex container sized to a tier height, holding smaller controls
|
|
5
27
|
// at the even concentric gap. The height scales with density; pick the tier
|
|
6
28
|
// that fits the content:
|
|
@@ -14,9 +36,9 @@
|
|
|
14
36
|
base: 'flex items-center leading-none',
|
|
15
37
|
variants: {
|
|
16
38
|
size: {
|
|
17
|
-
chip: '
|
|
18
|
-
control: '
|
|
19
|
-
bar: '
|
|
39
|
+
chip: 'tier-chip',
|
|
40
|
+
control: 'tier-control',
|
|
41
|
+
bar: 'tier-bar'
|
|
20
42
|
},
|
|
21
43
|
/**
|
|
22
44
|
* The tier of the CONTROLS inside, because that is what the bar's own
|
|
@@ -26,11 +48,10 @@
|
|
|
26
48
|
* had each restated the two-tier value by hand, and the geometry spec had
|
|
27
49
|
* been rewritten twice chasing which token they were restating.
|
|
28
50
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* differs per size and also comes from the compounds.
|
|
51
|
+
* Name the ACTUAL tier inside. Omitting it means the next canonical rung:
|
|
52
|
+
* chip⊃icon, control⊃chip, bar⊃control. There is deliberately no `tier`
|
|
53
|
+
* shorthand in the public API: it hid which pair supplied the inset and
|
|
54
|
+
* taught the false claim that every adjacent pair had the same value.
|
|
34
55
|
*
|
|
35
56
|
* THIS USED TO BE ONE CLASS FOR ALL THREE SIZES — `px-icon-inset-chip`,
|
|
36
57
|
* on the stated reasoning that "chip⊃icon, control⊃chip and bar⊃control
|
|
@@ -53,8 +74,10 @@
|
|
|
53
74
|
* compared ACROSS the axes. layout/wall-is-isotropic now does.
|
|
54
75
|
*/
|
|
55
76
|
content: {
|
|
56
|
-
|
|
57
|
-
pill: ''
|
|
77
|
+
icon: '',
|
|
78
|
+
pill: '',
|
|
79
|
+
chip: '',
|
|
80
|
+
control: ''
|
|
58
81
|
},
|
|
59
82
|
/**
|
|
60
83
|
* The flush header bar capping a panel — Pane.Handle, Dialog.Header,
|
|
@@ -80,27 +103,34 @@
|
|
|
80
103
|
}
|
|
81
104
|
},
|
|
82
105
|
compoundVariants: [
|
|
83
|
-
// content="tier" — one tier down, each pairing named for itself.
|
|
84
106
|
// chip ⊃ icon = sp/2.
|
|
85
107
|
{
|
|
86
108
|
size: 'chip',
|
|
87
|
-
content: '
|
|
109
|
+
content: 'icon',
|
|
88
110
|
class: 'gap-icon-inset-chip px-icon-inset-chip'
|
|
89
111
|
},
|
|
90
112
|
// control ⊃ chip = (sp−6)/2 — the tight-but-present rung, 1/2/3.
|
|
91
113
|
{
|
|
92
114
|
size: 'control',
|
|
93
|
-
content: '
|
|
115
|
+
content: 'chip',
|
|
94
116
|
class: 'gap-chip-inset-control px-chip-inset-control'
|
|
95
117
|
},
|
|
96
|
-
// bar ⊃ control
|
|
97
|
-
//
|
|
98
|
-
// styles/index.css), so it takes that name rather than borrowing
|
|
99
|
-
// icon-inset-chip's — same number, but a reader can tell why.
|
|
118
|
+
// bar ⊃ control happens to equal the gutter, but it keeps a pair-specific
|
|
119
|
+
// name so geometry never borrows a layout token by numerical coincidence.
|
|
100
120
|
{
|
|
101
121
|
size: 'bar',
|
|
102
|
-
content: '
|
|
103
|
-
class: 'gap-
|
|
122
|
+
content: 'control',
|
|
123
|
+
class: 'gap-control-inset-bar px-control-inset-bar'
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
size: 'control',
|
|
127
|
+
content: 'icon',
|
|
128
|
+
class: 'gap-icon-inset-control px-icon-inset-control'
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
size: 'bar',
|
|
132
|
+
content: 'icon',
|
|
133
|
+
class: 'gap-icon-inset-bar px-icon-inset-bar'
|
|
104
134
|
},
|
|
105
135
|
// control ⊃ pill = (3sp − sp − 10)/2 — the named token.
|
|
106
136
|
{
|
|
@@ -115,6 +145,11 @@
|
|
|
115
145
|
content: 'pill',
|
|
116
146
|
class: 'gap-pill-inset-bar px-pill-inset-bar'
|
|
117
147
|
},
|
|
148
|
+
{
|
|
149
|
+
size: 'bar',
|
|
150
|
+
content: 'chip',
|
|
151
|
+
class: 'gap-chip-inset-bar px-chip-inset-bar'
|
|
152
|
+
},
|
|
118
153
|
// chip ⊃ pill — its own token, not the text inset it used to borrow. The two
|
|
119
154
|
// were the same number only while type was 14px at every density; dense's
|
|
120
155
|
// 12px broke the tie and left this pairing a pixel wide.
|
|
@@ -124,7 +159,7 @@
|
|
|
124
159
|
class: 'gap-pill-inset-chip px-pill-inset-chip'
|
|
125
160
|
}
|
|
126
161
|
],
|
|
127
|
-
defaultVariants: { size: 'bar', content: '
|
|
162
|
+
defaultVariants: { size: 'bar', content: 'control', divider: false }
|
|
128
163
|
});
|
|
129
164
|
</script>
|
|
130
165
|
|
|
@@ -135,8 +170,9 @@
|
|
|
135
170
|
let {
|
|
136
171
|
ref = $bindable(null),
|
|
137
172
|
size = 'bar',
|
|
138
|
-
content
|
|
173
|
+
content,
|
|
139
174
|
divider = false,
|
|
175
|
+
topology = 'nested',
|
|
140
176
|
children,
|
|
141
177
|
class: className,
|
|
142
178
|
...restProps
|
|
@@ -146,16 +182,49 @@
|
|
|
146
182
|
size?: 'chip' | 'control' | 'bar';
|
|
147
183
|
/**
|
|
148
184
|
* Tier of the controls inside — drives the bar's padding and gap so they
|
|
149
|
-
* stay concentric to them.
|
|
150
|
-
* the
|
|
185
|
+
* stay concentric to them. Omit this for the next canonical rung, or name
|
|
186
|
+
* the actual tier when the contents are smaller.
|
|
187
|
+
*/
|
|
188
|
+
content?: BarContent;
|
|
189
|
+
/**
|
|
190
|
+
* The flush cap-a-panel chrome: an outset rule below, and a rounded
|
|
191
|
+
* BOTTOM only — the top defers to the panel's own radius.
|
|
192
|
+
*
|
|
193
|
+
* THE BOTTOM RADIUS IS LOAD-BEARING FOR THE RULE. The divider is an
|
|
194
|
+
* outset `box-shadow`, so it follows the box's corners: the rounded
|
|
195
|
+
* bottom is what curves the line up at the ends and makes the bar read
|
|
196
|
+
* as a long pill. Overriding the radius therefore changes the RULE, not
|
|
197
|
+
* just the corners. Measured against `cn`:
|
|
198
|
+
*
|
|
199
|
+
* - `rounded-none` drops both — a square bar with a dead-straight rule.
|
|
200
|
+
* That is the intended square mode (an in-app Notes bar passes it).
|
|
201
|
+
* - `rounded-b-*` or `rounded-b-none` replaces the curve — this is the
|
|
202
|
+
* one that surprises, because the rule changes shape when the caller
|
|
203
|
+
* thought they were only squaring a corner.
|
|
204
|
+
* - `rounded-t-*` is SAFE: it wins against `rounded-t-none` and leaves
|
|
205
|
+
* `rounded-b-control` standing, so the curve survives.
|
|
206
|
+
*
|
|
207
|
+
* A consumer lost the curve this way. The coupling was already written
|
|
208
|
+
* down — on the variant, inside `<script module>`, where someone
|
|
209
|
+
* hovering this prop never reaches it. Docs are only as good as the
|
|
210
|
+
* place the reader is standing.
|
|
151
211
|
*/
|
|
152
|
-
content?: 'tier' | 'pill';
|
|
153
|
-
/** The flush cap-a-panel chrome: outset divider below, bottom rd-md. */
|
|
154
212
|
divider?: boolean;
|
|
213
|
+
/** `docked` removes one or more exposed curves; strict radius nesting does not apply. */
|
|
214
|
+
topology?: 'nested' | 'docked';
|
|
155
215
|
children?: Snippet;
|
|
156
216
|
} = $props();
|
|
217
|
+
|
|
218
|
+
const resolvedContent = $derived(resolveBarContent(size, content));
|
|
157
219
|
</script>
|
|
158
220
|
|
|
159
|
-
<div
|
|
221
|
+
<div
|
|
222
|
+
bind:this={ref}
|
|
223
|
+
data-era-geometry={divider ? 'docked' : topology}
|
|
224
|
+
data-era-tier={size}
|
|
225
|
+
data-era-contains={resolvedContent}
|
|
226
|
+
class={cn(barVariants({ size, content: resolvedContent, divider }), className)}
|
|
227
|
+
{...restProps}
|
|
228
|
+
>
|
|
160
229
|
{@render children?.()}
|
|
161
230
|
</div>
|
|
@@ -1,8 +1,21 @@
|
|
|
1
|
+
export type BarSize = 'chip' | 'control' | 'bar';
|
|
2
|
+
export type BarContent = 'icon' | 'pill' | 'chip' | 'control';
|
|
3
|
+
export type BarGeometry = {
|
|
4
|
+
size: 'chip';
|
|
5
|
+
content?: 'icon' | 'pill';
|
|
6
|
+
} | {
|
|
7
|
+
size: 'control';
|
|
8
|
+
content?: 'icon' | 'pill' | 'chip';
|
|
9
|
+
} | {
|
|
10
|
+
size?: 'bar';
|
|
11
|
+
content?: BarContent;
|
|
12
|
+
};
|
|
13
|
+
export declare function resolveBarContent(size: BarSize, content?: BarContent): BarContent;
|
|
1
14
|
export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
2
15
|
size: {
|
|
3
|
-
chip: "
|
|
4
|
-
control: "
|
|
5
|
-
bar: "
|
|
16
|
+
chip: "tier-chip";
|
|
17
|
+
control: "tier-control";
|
|
18
|
+
bar: "tier-bar";
|
|
6
19
|
};
|
|
7
20
|
/**
|
|
8
21
|
* The tier of the CONTROLS inside, because that is what the bar's own
|
|
@@ -12,11 +25,10 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
12
25
|
* had each restated the two-tier value by hand, and the geometry spec had
|
|
13
26
|
* been rewritten twice chasing which token they were restating.
|
|
14
27
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* differs per size and also comes from the compounds.
|
|
28
|
+
* Name the ACTUAL tier inside. Omitting it means the next canonical rung:
|
|
29
|
+
* chip⊃icon, control⊃chip, bar⊃control. There is deliberately no `tier`
|
|
30
|
+
* shorthand in the public API: it hid which pair supplied the inset and
|
|
31
|
+
* taught the false claim that every adjacent pair had the same value.
|
|
20
32
|
*
|
|
21
33
|
* THIS USED TO BE ONE CLASS FOR ALL THREE SIZES — `px-icon-inset-chip`,
|
|
22
34
|
* on the stated reasoning that "chip⊃icon, control⊃chip and bar⊃control
|
|
@@ -39,8 +51,10 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
39
51
|
* compared ACROSS the axes. layout/wall-is-isotropic now does.
|
|
40
52
|
*/
|
|
41
53
|
content: {
|
|
42
|
-
|
|
54
|
+
icon: "";
|
|
43
55
|
pill: "";
|
|
56
|
+
chip: "";
|
|
57
|
+
control: "";
|
|
44
58
|
};
|
|
45
59
|
/**
|
|
46
60
|
* The flush header bar capping a panel — Pane.Handle, Dialog.Header,
|
|
@@ -66,9 +80,9 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
66
80
|
};
|
|
67
81
|
}, undefined, "flex items-center leading-none", {
|
|
68
82
|
size: {
|
|
69
|
-
chip: "
|
|
70
|
-
control: "
|
|
71
|
-
bar: "
|
|
83
|
+
chip: "tier-chip";
|
|
84
|
+
control: "tier-control";
|
|
85
|
+
bar: "tier-bar";
|
|
72
86
|
};
|
|
73
87
|
/**
|
|
74
88
|
* The tier of the CONTROLS inside, because that is what the bar's own
|
|
@@ -78,11 +92,10 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
78
92
|
* had each restated the two-tier value by hand, and the geometry spec had
|
|
79
93
|
* been rewritten twice chasing which token they were restating.
|
|
80
94
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* differs per size and also comes from the compounds.
|
|
95
|
+
* Name the ACTUAL tier inside. Omitting it means the next canonical rung:
|
|
96
|
+
* chip⊃icon, control⊃chip, bar⊃control. There is deliberately no `tier`
|
|
97
|
+
* shorthand in the public API: it hid which pair supplied the inset and
|
|
98
|
+
* taught the false claim that every adjacent pair had the same value.
|
|
86
99
|
*
|
|
87
100
|
* THIS USED TO BE ONE CLASS FOR ALL THREE SIZES — `px-icon-inset-chip`,
|
|
88
101
|
* on the stated reasoning that "chip⊃icon, control⊃chip and bar⊃control
|
|
@@ -105,8 +118,10 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
105
118
|
* compared ACROSS the axes. layout/wall-is-isotropic now does.
|
|
106
119
|
*/
|
|
107
120
|
content: {
|
|
108
|
-
|
|
121
|
+
icon: "";
|
|
109
122
|
pill: "";
|
|
123
|
+
chip: "";
|
|
124
|
+
control: "";
|
|
110
125
|
};
|
|
111
126
|
/**
|
|
112
127
|
* The flush header bar capping a panel — Pane.Handle, Dialog.Header,
|
|
@@ -132,9 +147,9 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
132
147
|
};
|
|
133
148
|
}, undefined, import("tailwind-variants").TVReturnTypeLike<{
|
|
134
149
|
size: {
|
|
135
|
-
chip: "
|
|
136
|
-
control: "
|
|
137
|
-
bar: "
|
|
150
|
+
chip: "tier-chip";
|
|
151
|
+
control: "tier-control";
|
|
152
|
+
bar: "tier-bar";
|
|
138
153
|
};
|
|
139
154
|
/**
|
|
140
155
|
* The tier of the CONTROLS inside, because that is what the bar's own
|
|
@@ -144,11 +159,10 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
144
159
|
* had each restated the two-tier value by hand, and the geometry spec had
|
|
145
160
|
* been rewritten twice chasing which token they were restating.
|
|
146
161
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* differs per size and also comes from the compounds.
|
|
162
|
+
* Name the ACTUAL tier inside. Omitting it means the next canonical rung:
|
|
163
|
+
* chip⊃icon, control⊃chip, bar⊃control. There is deliberately no `tier`
|
|
164
|
+
* shorthand in the public API: it hid which pair supplied the inset and
|
|
165
|
+
* taught the false claim that every adjacent pair had the same value.
|
|
152
166
|
*
|
|
153
167
|
* THIS USED TO BE ONE CLASS FOR ALL THREE SIZES — `px-icon-inset-chip`,
|
|
154
168
|
* on the stated reasoning that "chip⊃icon, control⊃chip and bar⊃control
|
|
@@ -171,8 +185,10 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
|
|
|
171
185
|
* compared ACROSS the axes. layout/wall-is-isotropic now does.
|
|
172
186
|
*/
|
|
173
187
|
content: {
|
|
174
|
-
|
|
188
|
+
icon: "";
|
|
175
189
|
pill: "";
|
|
190
|
+
chip: "";
|
|
191
|
+
control: "";
|
|
176
192
|
};
|
|
177
193
|
/**
|
|
178
194
|
* The flush header bar capping a panel — Pane.Handle, Dialog.Header,
|
|
@@ -205,12 +221,36 @@ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
|
205
221
|
size?: 'chip' | 'control' | 'bar';
|
|
206
222
|
/**
|
|
207
223
|
* Tier of the controls inside — drives the bar's padding and gap so they
|
|
208
|
-
* stay concentric to them.
|
|
209
|
-
* the
|
|
224
|
+
* stay concentric to them. Omit this for the next canonical rung, or name
|
|
225
|
+
* the actual tier when the contents are smaller.
|
|
226
|
+
*/
|
|
227
|
+
content?: BarContent;
|
|
228
|
+
/**
|
|
229
|
+
* The flush cap-a-panel chrome: an outset rule below, and a rounded
|
|
230
|
+
* BOTTOM only — the top defers to the panel's own radius.
|
|
231
|
+
*
|
|
232
|
+
* THE BOTTOM RADIUS IS LOAD-BEARING FOR THE RULE. The divider is an
|
|
233
|
+
* outset `box-shadow`, so it follows the box's corners: the rounded
|
|
234
|
+
* bottom is what curves the line up at the ends and makes the bar read
|
|
235
|
+
* as a long pill. Overriding the radius therefore changes the RULE, not
|
|
236
|
+
* just the corners. Measured against `cn`:
|
|
237
|
+
*
|
|
238
|
+
* - `rounded-none` drops both — a square bar with a dead-straight rule.
|
|
239
|
+
* That is the intended square mode (an in-app Notes bar passes it).
|
|
240
|
+
* - `rounded-b-*` or `rounded-b-none` replaces the curve — this is the
|
|
241
|
+
* one that surprises, because the rule changes shape when the caller
|
|
242
|
+
* thought they were only squaring a corner.
|
|
243
|
+
* - `rounded-t-*` is SAFE: it wins against `rounded-t-none` and leaves
|
|
244
|
+
* `rounded-b-control` standing, so the curve survives.
|
|
245
|
+
*
|
|
246
|
+
* A consumer lost the curve this way. The coupling was already written
|
|
247
|
+
* down — on the variant, inside `<script module>`, where someone
|
|
248
|
+
* hovering this prop never reaches it. Docs are only as good as the
|
|
249
|
+
* place the reader is standing.
|
|
210
250
|
*/
|
|
211
|
-
content?: 'tier' | 'pill';
|
|
212
|
-
/** The flush cap-a-panel chrome: outset divider below, bottom rd-md. */
|
|
213
251
|
divider?: boolean;
|
|
252
|
+
/** `docked` removes one or more exposed curves; strict radius nesting does not apply. */
|
|
253
|
+
topology?: 'nested' | 'docked';
|
|
214
254
|
children?: Snippet;
|
|
215
255
|
};
|
|
216
256
|
declare const Bar: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
@@ -13,11 +13,13 @@
|
|
|
13
13
|
active = false,
|
|
14
14
|
icon = false,
|
|
15
15
|
loading = false,
|
|
16
|
+
busy = false,
|
|
16
17
|
loadingLabel,
|
|
17
18
|
disabled,
|
|
18
19
|
lead,
|
|
19
20
|
trail,
|
|
20
21
|
caps = false,
|
|
22
|
+
topology = 'bounded',
|
|
21
23
|
children,
|
|
22
24
|
class: className,
|
|
23
25
|
...restProps
|
|
@@ -47,8 +49,35 @@
|
|
|
47
49
|
*
|
|
48
50
|
* The visible label is therefore HIDDEN in flight — the one thing this
|
|
49
51
|
* trades away, and the reason `loadingLabel` exists.
|
|
52
|
+
*
|
|
53
|
+
* IF YOU DO NOT WANT A SPINNER, use `busy`. Not showing one is a
|
|
54
|
+
* supported posture, not a legacy one — see that prop.
|
|
50
55
|
*/
|
|
51
56
|
loading?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* In flight, WITHOUT a spinner: disables the button and sets `aria-busy`,
|
|
59
|
+
* and changes nothing else. The label stays visible and legible.
|
|
60
|
+
*
|
|
61
|
+
* This exists because "show a spinner" is a taste, not a requirement,
|
|
62
|
+
* and era had only one answer. A consumer whose house style is that
|
|
63
|
+
* spinners should not appear had to spell the posture out themselves —
|
|
64
|
+
* `disabled` plus a restated `aria-busy` — and rely on knowing that
|
|
65
|
+
* `restProps` spreads last, which is a real fact about this component
|
|
66
|
+
* that no reader should have to depend on. Two words for two postures
|
|
67
|
+
* is clearer than one word plus a workaround.
|
|
68
|
+
*
|
|
69
|
+
* `loading` implies this: it is `busy` plus the spinner and the hidden
|
|
70
|
+
* label. What you get from `busy` alone is a control that is visibly
|
|
71
|
+
* disabled (the era-interactive opacity) and announced as busy, with
|
|
72
|
+
* its geometry and its ink untouched — which is also why it needs no
|
|
73
|
+
* `loadingLabel`: nothing about the button changed except that you
|
|
74
|
+
* cannot press it.
|
|
75
|
+
*
|
|
76
|
+
* Reported by a consumer whose user asked for no spinners anywhere.
|
|
77
|
+
* They were right that a fix for the width shift did not answer it: a
|
|
78
|
+
* spinner that no longer moves its neighbours is still a spinner.
|
|
79
|
+
*/
|
|
80
|
+
busy?: boolean;
|
|
52
81
|
/** Screen-reader-only label announced while `loading` is true. */
|
|
53
82
|
loadingLabel?: string;
|
|
54
83
|
/** Icon rendered BEFORE the label, outside it. Use this rather than putting
|
|
@@ -98,10 +127,15 @@
|
|
|
98
127
|
* which is the whole reason those snippets exist.
|
|
99
128
|
*/
|
|
100
129
|
caps?: boolean;
|
|
130
|
+
/** `docked` joins this control to a neighbour and removes one or more curves. */
|
|
131
|
+
topology?: 'bounded' | 'docked';
|
|
101
132
|
children?: Snippet;
|
|
102
133
|
} = $props();
|
|
103
134
|
|
|
104
|
-
|
|
135
|
+
// `loading` is `busy` plus a spinner, so everything that is true of busy is
|
|
136
|
+
// true of loading. Only the spinner and the hidden label read `loading`.
|
|
137
|
+
const inFlight = $derived(loading || busy);
|
|
138
|
+
const effectiveDisabled = $derived(disabled || inFlight);
|
|
105
139
|
</script>
|
|
106
140
|
|
|
107
141
|
<!--
|
|
@@ -122,9 +156,11 @@
|
|
|
122
156
|
<Button.Root
|
|
123
157
|
bind:ref
|
|
124
158
|
disabled={effectiveDisabled}
|
|
125
|
-
aria-busy={
|
|
159
|
+
aria-busy={inFlight || undefined}
|
|
126
160
|
data-state={active ? 'on' : undefined}
|
|
127
161
|
data-size={size}
|
|
162
|
+
data-era-geometry={variant === 'filled' ? topology : undefined}
|
|
163
|
+
data-era-tier={variant === 'filled' ? size : undefined}
|
|
128
164
|
class={cn(
|
|
129
165
|
buttonVariants({ variant, tone, size, icon, active }),
|
|
130
166
|
// Positioned only while loading — the spinner overlay below needs a
|
|
@@ -26,8 +26,35 @@ type $$ComponentProps = Button.RootProps & {
|
|
|
26
26
|
*
|
|
27
27
|
* The visible label is therefore HIDDEN in flight — the one thing this
|
|
28
28
|
* trades away, and the reason `loadingLabel` exists.
|
|
29
|
+
*
|
|
30
|
+
* IF YOU DO NOT WANT A SPINNER, use `busy`. Not showing one is a
|
|
31
|
+
* supported posture, not a legacy one — see that prop.
|
|
29
32
|
*/
|
|
30
33
|
loading?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* In flight, WITHOUT a spinner: disables the button and sets `aria-busy`,
|
|
36
|
+
* and changes nothing else. The label stays visible and legible.
|
|
37
|
+
*
|
|
38
|
+
* This exists because "show a spinner" is a taste, not a requirement,
|
|
39
|
+
* and era had only one answer. A consumer whose house style is that
|
|
40
|
+
* spinners should not appear had to spell the posture out themselves —
|
|
41
|
+
* `disabled` plus a restated `aria-busy` — and rely on knowing that
|
|
42
|
+
* `restProps` spreads last, which is a real fact about this component
|
|
43
|
+
* that no reader should have to depend on. Two words for two postures
|
|
44
|
+
* is clearer than one word plus a workaround.
|
|
45
|
+
*
|
|
46
|
+
* `loading` implies this: it is `busy` plus the spinner and the hidden
|
|
47
|
+
* label. What you get from `busy` alone is a control that is visibly
|
|
48
|
+
* disabled (the era-interactive opacity) and announced as busy, with
|
|
49
|
+
* its geometry and its ink untouched — which is also why it needs no
|
|
50
|
+
* `loadingLabel`: nothing about the button changed except that you
|
|
51
|
+
* cannot press it.
|
|
52
|
+
*
|
|
53
|
+
* Reported by a consumer whose user asked for no spinners anywhere.
|
|
54
|
+
* They were right that a fix for the width shift did not answer it: a
|
|
55
|
+
* spinner that no longer moves its neighbours is still a spinner.
|
|
56
|
+
*/
|
|
57
|
+
busy?: boolean;
|
|
31
58
|
/** Screen-reader-only label announced while `loading` is true. */
|
|
32
59
|
loadingLabel?: string;
|
|
33
60
|
/** Icon rendered BEFORE the label, outside it. Use this rather than putting
|
|
@@ -77,6 +104,8 @@ type $$ComponentProps = Button.RootProps & {
|
|
|
77
104
|
* which is the whole reason those snippets exist.
|
|
78
105
|
*/
|
|
79
106
|
caps?: boolean;
|
|
107
|
+
/** `docked` joins this control to a neighbour and removes one or more curves. */
|
|
108
|
+
topology?: 'bounded' | 'docked';
|
|
80
109
|
children?: Snippet;
|
|
81
110
|
};
|
|
82
111
|
declare const Button: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
@@ -40,27 +40,22 @@ export const buttonVariants = tv({
|
|
|
40
40
|
{
|
|
41
41
|
variant: 'filled',
|
|
42
42
|
size: 'control',
|
|
43
|
-
class: '
|
|
43
|
+
class: 'min-w-control tier-control'
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
variant: 'filled',
|
|
47
47
|
size: 'chip',
|
|
48
|
-
class: '
|
|
48
|
+
class: 'min-w-chip tier-chip'
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
variant: 'filled',
|
|
52
52
|
size: 'pill',
|
|
53
|
-
class: '
|
|
53
|
+
class: 'min-w-pill tier-pill'
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
variant: 'filled',
|
|
57
57
|
size: 'icon',
|
|
58
|
-
class: '
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
variant: 'filled',
|
|
62
|
-
size: 'icon',
|
|
63
|
-
class: 'h-icon min-w-icon rounded-icon'
|
|
58
|
+
class: 'min-w-icon tier-icon'
|
|
64
59
|
},
|
|
65
60
|
// filled insets; icon buttons go square instead. When an icon is present,
|
|
66
61
|
// gap widens to match the wall inset so the icon sits in rhythm with it.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<Calendar.Day
|
|
16
16
|
bind:ref
|
|
17
17
|
class={cn(
|
|
18
|
-
'era-token-ink
|
|
18
|
+
'era-token-ink tier-control w-control cursor-default text-body text-fg hover:bg-highlight focus:outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[outside-month]:pointer-events-none data-[outside-month]:text-muted/40 data-[selected]:bg-hover data-[selected]:text-bright data-[unavailable]:text-muted data-[unavailable]:line-through',
|
|
19
19
|
className
|
|
20
20
|
)}
|
|
21
21
|
{...restProps}
|
package/dist/ui/card/card.svelte
CHANGED
|
@@ -39,10 +39,11 @@
|
|
|
39
39
|
// chisel, that is the difference between a control and a picture of
|
|
40
40
|
// one. Inert on flat (the token is `none`) and on glass (it resolves
|
|
41
41
|
// to the resting shadow), so only the surface that has a pressed face
|
|
42
|
-
// shows it.
|
|
43
|
-
//
|
|
42
|
+
// shows it. Move one library-owned content layer with the face: transforming
|
|
43
|
+
// consumer children separately would pull a card's sections apart, while no
|
|
44
|
+
// wrapper leaves the frame depressed around stationary ink.
|
|
44
45
|
interactive: {
|
|
45
|
-
true: 'cursor-pointer hover:bg-highlight hover:shadow-highlight focus-visible:bg-highlight active:shadow-pressed era-interactive aria-disabled:cursor-not-allowed aria-disabled:opacity-50'
|
|
46
|
+
true: 'cursor-pointer hover:bg-highlight hover:shadow-highlight focus-visible:bg-highlight active:shadow-pressed [&:active>[data-card-content]]:translate-x-(--era-press-sink-x) [&:active>[data-card-content]]:translate-y-(--era-press-sink-y) era-interactive aria-disabled:cursor-not-allowed aria-disabled:opacity-50'
|
|
46
47
|
}
|
|
47
48
|
},
|
|
48
49
|
defaultVariants: { tone: 'elevated', sectioned: false, interactive: false }
|
|
@@ -124,10 +125,22 @@
|
|
|
124
125
|
* A CARD THAT IS PRESSABLE SHOULD USUALLY BE `elevated`. Passing `href`
|
|
125
126
|
* or `onclick` turns on the interactive face — hover, focus, press — but
|
|
126
127
|
* it deliberately does NOT override the tier you asked for, so a
|
|
127
|
-
* `surface` link card
|
|
128
|
-
* static cards around it. That combination was
|
|
129
|
-
* is a choice: a tone that silently changed
|
|
130
|
-
* be a worse one.
|
|
128
|
+
* `surface` link card does not get the raised tier's separation and can
|
|
129
|
+
* read flatter than the static cards around it. That combination was
|
|
130
|
+
* reported as a bug and it is a choice: a tone that silently changed
|
|
131
|
+
* under an event handler would be a worse one.
|
|
132
|
+
*
|
|
133
|
+
* HOW THE TIERS DIFFER IS SURFACE-DEPENDENT, so do not go looking for a
|
|
134
|
+
* fill difference on every surface. `base` mixes the elevated fill 6%
|
|
135
|
+
* toward `--color-bright` and adds a resting shadow, so there the tiers
|
|
136
|
+
* differ in COLOUR. `bevel` sets `--era-surface-bg-elevated` to
|
|
137
|
+
* `--era-surface-bg` outright — elevation is a chisel, not a tone, which
|
|
138
|
+
* is bevel's whole premise — so `surface` and `elevated` paint the
|
|
139
|
+
* IDENTICAL fill there and differ only in relief. A consumer measured
|
|
140
|
+
* exactly that on 30.8.0 bevel: both tones `oklch(0.195 0 none)` in dark,
|
|
141
|
+
* `0.863` in light, one variable, side by side. `glass` splits the tiers
|
|
142
|
+
* its own way again. So the tier is real on every surface; the thing
|
|
143
|
+
* carrying it is not always the fill.
|
|
131
144
|
*/
|
|
132
145
|
tone?: 'elevated' | 'surface' | 'well' | 'flat';
|
|
133
146
|
/** Rendered above the body with a subtle divider. */
|
|
@@ -178,7 +191,7 @@
|
|
|
178
191
|
class={cn(cardVariants({ tone, sectioned, interactive: true }), className)}
|
|
179
192
|
{...restProps}
|
|
180
193
|
>
|
|
181
|
-
{@render body()}
|
|
194
|
+
<div data-card-content>{@render body()}</div>
|
|
182
195
|
</a>
|
|
183
196
|
<!-- eslint-enable svelte/no-navigation-without-resolve -->
|
|
184
197
|
{:else if onclick != null}
|
|
@@ -198,7 +211,7 @@
|
|
|
198
211
|
class={cn(cardVariants({ tone, sectioned, interactive: true }), className)}
|
|
199
212
|
{...restProps}
|
|
200
213
|
>
|
|
201
|
-
{@render body()}
|
|
214
|
+
<div data-card-content>{@render body()}</div>
|
|
202
215
|
</button>
|
|
203
216
|
{:else}
|
|
204
217
|
<div
|