@threadlabs/looma 0.26.4 → 0.26.6
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/components/ui-badge/ui-badge.html +41 -4
- package/components/ui-button/ui-button.html +50 -5
- package/components/ui-icon-button/ui-icon-button.html +35 -7
- package/components/ui-input-group/ui-input-group.html +9 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-badge.css +40 -1
- package/styles/ui-button.css +53 -5
- package/styles/ui-icon-button.css +35 -7
- package/styles/ui-input-group.css +6 -0
- package/vanilla/UiBadge.d.ts +1 -1
- package/vanilla/UiBadge.js +1 -1
- package/vanilla/UiButton.js +47 -21
- package/vanilla/UiInputGroup.d.ts +1 -0
- package/vanilla/UiInputGroup.js +4 -4
- package/vue/UiBadge.d.ts +3 -3
- package/vue/UiBadge.vue +79 -6
- package/vue/UiButton.d.ts +6 -2
- package/vue/UiButton.vue +56 -6
- package/vue/UiIconButton.vue +40 -7
- package/vue/UiInputGroup.d.ts +9 -0
- package/vue/UiInputGroup.vue +29 -1
- package/vue/chunks/UiBadge.js +3 -2
- package/vue/chunks/UiButton.js +15 -5
- package/vue/chunks/UiIconButton.js +1 -1
- package/vue/chunks/UiInputGroup.js +29 -4
- package/vue/components.css +347 -197
- package/vue/editor/LoomaEditor.d.ts +38 -0
- package/vue/editor/index.d.ts +1 -1
- package/vue/editor/index.js +83 -10
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
aria-hidden when the surrounding control already names that state. square is a fixed rounded square for an icon or single glyph beside a heading; hide a
|
|
14
14
|
decorative icon when adjacent words name it. circle frames a visible glyph, icon or short bounded count in a fixed circle, centered in both
|
|
15
15
|
directions.</prop>
|
|
16
|
-
<prop name="size" type="keyword" values="xs, md, lg" default="md"
|
|
17
|
-
>Scale. xs is compact;
|
|
18
|
-
|
|
16
|
+
<prop name="size" type="keyword" values="xs, sm, md, lg" default="md"
|
|
17
|
+
>Scale. xs is compact; sm keeps the usual badge geometry with compact text, fitting short counts such as 99+ inside a 24px circle. md is the usual text
|
|
18
|
+
badge; lg adds room to fixed marks while retaining the usual text size. A square is 24px at xs, 32px at sm and md, and 40px at lg. A circle is 16px at xs,
|
|
19
|
+
24px at sm and md, and 32px at lg.</prop>
|
|
19
20
|
</defs>
|
|
20
21
|
<span>
|
|
21
22
|
<span class="icon" aria-hidden="true"><slot name="icon"></slot></span>
|
|
@@ -35,6 +36,10 @@
|
|
|
35
36
|
syntax: "*";
|
|
36
37
|
inherits: false;
|
|
37
38
|
}
|
|
39
|
+
@property --ui-badge-circle-opacity {
|
|
40
|
+
syntax: "*";
|
|
41
|
+
inherits: false;
|
|
42
|
+
}
|
|
38
43
|
@property --ui-badge-dot-size {
|
|
39
44
|
syntax: "*";
|
|
40
45
|
inherits: false;
|
|
@@ -191,6 +196,11 @@
|
|
|
191
196
|
line-height: var(--ui-badge-line-height, 1);
|
|
192
197
|
}
|
|
193
198
|
|
|
199
|
+
:host([size="sm"]) {
|
|
200
|
+
font-size: var(--ui-badge-font-size, var(--ui-font-size-2xs));
|
|
201
|
+
line-height: var(--ui-badge-line-height, 1);
|
|
202
|
+
}
|
|
203
|
+
|
|
194
204
|
:host([tone="accent"]) {
|
|
195
205
|
--_badge-surface: var(--ui-accent-strong-surface, var(--ui-accent-solid));
|
|
196
206
|
--_badge-border: var(--ui-accent-strong-surface, var(--ui-accent-solid));
|
|
@@ -272,8 +282,35 @@
|
|
|
272
282
|
background-clip: padding-box;
|
|
273
283
|
}
|
|
274
284
|
|
|
285
|
+
/* A circular count has a tone-tinted surface, a separate lighter rim and readable tone ink.
|
|
286
|
+
Dark controls retain a shaded surface and theme ink with a quieter rim. */
|
|
275
287
|
:host([variant="inverse"][shape="circle"]) {
|
|
276
|
-
|
|
288
|
+
--_badge-color-surface: light-dark(
|
|
289
|
+
color-mix(in srgb, color-mix(in oklab, var(--_ui-badge-color) 0%, var(--ui-surface-default)) var(--ui-badge-circle-opacity, 66%), transparent),
|
|
290
|
+
color-mix(in srgb, color-mix(in oklab, var(--_ui-badge-color) 0%, var(--ui-surface-default)) var(--ui-badge-circle-opacity, 56%), transparent)
|
|
291
|
+
);
|
|
292
|
+
--_badge-color-border: light-dark(
|
|
293
|
+
color-mix(in srgb, color-mix(in oklab, var(--_ui-badge-color) 0%, var(--ui-surface-default)) 88%, transparent),
|
|
294
|
+
color-mix(in srgb, color-mix(in oklab, var(--_ui-badge-color) 0%, var(--ui-surface-default)) 44%, transparent)
|
|
295
|
+
);
|
|
296
|
+
--_badge-color-text: light-dark(
|
|
297
|
+
color-mix(in srgb, var(--_badge-color-ink) 65%, var(--ui-text-primary)),
|
|
298
|
+
color-mix(in oklab, var(--_ui-badge-color) 0%, var(--ui-text-primary))
|
|
299
|
+
);
|
|
300
|
+
--_badge-count-surface: light-dark(
|
|
301
|
+
color-mix(in srgb, var(--_badge-text) var(--ui-badge-circle-opacity, 66%), transparent),
|
|
302
|
+
color-mix(in srgb, var(--_badge-text) var(--ui-badge-circle-opacity, 56%), transparent)
|
|
303
|
+
);
|
|
304
|
+
--_badge-outline-surface: var(--ui-badge-surface, var(--_badge-color-surface, var(--_badge-count-surface)));
|
|
305
|
+
border-color: var(
|
|
306
|
+
--ui-badge-border,
|
|
307
|
+
var(--_badge-color-border, light-dark(color-mix(in srgb, var(--_badge-text) 88%, transparent), color-mix(in srgb, var(--_badge-text) 44%, transparent)))
|
|
308
|
+
);
|
|
309
|
+
color: var(
|
|
310
|
+
--ui-badge-text,
|
|
311
|
+
var(--_badge-color-text, light-dark(color-mix(in srgb, var(--_badge-surface) 65%, var(--ui-text-primary)), var(--ui-text-primary)))
|
|
312
|
+
);
|
|
313
|
+
box-shadow: 0 1px 1px light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.12));
|
|
277
314
|
}
|
|
278
315
|
|
|
279
316
|
/* Neutral reverses a subtle surface/ink pair rather than a strong tone. Keep its original
|
|
@@ -78,6 +78,7 @@
|
|
|
78
78
|
>
|
|
79
79
|
<span $if="$loading or $pending" class="spinner-wrap"><ui-spinner size="sm"></ui-spinner></span>
|
|
80
80
|
<slot></slot>
|
|
81
|
+
<span class="badge"><slot name="badge"></slot></span>
|
|
81
82
|
</a>
|
|
82
83
|
<button
|
|
83
84
|
$else
|
|
@@ -88,6 +89,7 @@
|
|
|
88
89
|
>
|
|
89
90
|
<span $if="$loading or $pending" class="spinner-wrap"><ui-spinner size="sm"></ui-spinner></span>
|
|
90
91
|
<slot></slot>
|
|
92
|
+
<span class="badge"><slot name="badge"></slot></span>
|
|
91
93
|
</button>
|
|
92
94
|
</template>
|
|
93
95
|
<style>
|
|
@@ -166,6 +168,16 @@
|
|
|
166
168
|
}
|
|
167
169
|
|
|
168
170
|
:host {
|
|
171
|
+
/* Optional inherited action-group values keep text and icon actions under the same light.
|
|
172
|
+
One fine top/start highlight follows the bottom/end lowlight at a joined action seam.
|
|
173
|
+
A single ambient lift avoids stacking a face gradient or another inset bevel. */
|
|
174
|
+
--_ui-action-start: 1px;
|
|
175
|
+
--_ui-action-edge: var(
|
|
176
|
+
--ui-action-edge,
|
|
177
|
+
inset var(--_ui-action-start) 1px 0 light-dark(rgb(255 255 255 / 0.08), rgb(255 255 255 / 0.06)),
|
|
178
|
+
inset calc(-1 * var(--_ui-action-start)) -1px 0 light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.08))
|
|
179
|
+
);
|
|
180
|
+
--_ui-action-shadow: var(--_ui-action-edge), 0 1px 2px light-dark(rgb(0 0 0 / 0.10), rgb(0 0 0 / 0.20));
|
|
169
181
|
--_hue: var(--ui-accent);
|
|
170
182
|
--_hue-hover: var(--ui-accent-hover);
|
|
171
183
|
--_hue-active: var(--ui-accent-active);
|
|
@@ -186,6 +198,10 @@
|
|
|
186
198
|
color: var(--_tone-text);
|
|
187
199
|
}
|
|
188
200
|
|
|
201
|
+
:host:dir(rtl) {
|
|
202
|
+
--_ui-action-start: -1px;
|
|
203
|
+
}
|
|
204
|
+
|
|
189
205
|
:host([align="start"]) {
|
|
190
206
|
justify-content: flex-start;
|
|
191
207
|
text-align: start;
|
|
@@ -212,8 +228,8 @@
|
|
|
212
228
|
white-space: var(--ui-button-white-space, inherit);
|
|
213
229
|
line-height: var(--ui-line-height-tight);
|
|
214
230
|
padding: var(--ui-space-2) var(--ui-space-3);
|
|
215
|
-
background-image:
|
|
216
|
-
box-shadow: var(--ui-button-shadow, var(--ui-
|
|
231
|
+
background-image: none;
|
|
232
|
+
box-shadow: var(--ui-button-shadow, var(--ui-action-shadow, var(--_ui-action-shadow)));
|
|
217
233
|
cursor: pointer;
|
|
218
234
|
transition:
|
|
219
235
|
background-color var(--ui-motion-fast) var(--ui-motion-ease),
|
|
@@ -234,6 +250,22 @@
|
|
|
234
250
|
vertical-align: middle;
|
|
235
251
|
}
|
|
236
252
|
|
|
253
|
+
/* A count is an adornment, not another line of label text. Keep the standard control height
|
|
254
|
+
with enough inset for a 24px mark; large content can still grow the button naturally. */
|
|
255
|
+
:host:not([variant="link"]):has(.badge:not(:empty)) {
|
|
256
|
+
padding-block: var(--ui-space-0-5);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.badge {
|
|
260
|
+
display: inline-flex;
|
|
261
|
+
align-items: center;
|
|
262
|
+
flex: none;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.badge:empty {
|
|
266
|
+
display: none;
|
|
267
|
+
}
|
|
268
|
+
|
|
237
269
|
@supports (text-box-trim: trim-both) {
|
|
238
270
|
:host {
|
|
239
271
|
text-box-trim: trim-both;
|
|
@@ -307,6 +339,15 @@
|
|
|
307
339
|
background-color: var(--ui-button-hover-surface, oklch(from var(--_tone) l c h / 12%));
|
|
308
340
|
}
|
|
309
341
|
|
|
342
|
+
/* A neutral card encloses content quietly; ordinary outlines keep their action-tone edge. */
|
|
343
|
+
:host([variant="card"][tone="neutral"]) {
|
|
344
|
+
border-color: var(--ui-button-border, var(--ui-border));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
:host([variant="card"][tone="neutral"]):hover:not(:disabled, [aria-disabled="true"]) {
|
|
348
|
+
border-color: var(--ui-button-hover-border, var(--ui-border-strong));
|
|
349
|
+
}
|
|
350
|
+
|
|
310
351
|
/* Pressed reads as pressed in: the highlight goes, and the shadow moves inside. */
|
|
311
352
|
:host:active:not(:disabled, [aria-disabled="true"]) {
|
|
312
353
|
background-color: var(--ui-button-active-surface, oklch(from var(--_tone) l c h / 18%));
|
|
@@ -318,7 +359,7 @@
|
|
|
318
359
|
border-color: var(--_tone);
|
|
319
360
|
background-color: var(--_tone);
|
|
320
361
|
color: var(--_on-tone);
|
|
321
|
-
box-shadow: var(--ui-button-solid-shadow, var(--ui-
|
|
362
|
+
box-shadow: var(--ui-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow)));
|
|
322
363
|
}
|
|
323
364
|
|
|
324
365
|
:is(:host([variant="solid"]), :host([variant="danger"])):hover:not(:disabled, [aria-disabled="true"]) {
|
|
@@ -400,16 +441,20 @@
|
|
|
400
441
|
:host:focus-visible {
|
|
401
442
|
outline: none;
|
|
402
443
|
box-shadow:
|
|
403
|
-
var(--ui-button-shadow, var(--ui-
|
|
444
|
+
var(--ui-button-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))),
|
|
404
445
|
var(--ui-focus-halo);
|
|
405
446
|
}
|
|
406
447
|
|
|
407
448
|
:host([variant="solid"]):focus-visible {
|
|
408
449
|
box-shadow:
|
|
409
|
-
var(--ui-button-solid-shadow, var(--ui-
|
|
450
|
+
var(--ui-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))),
|
|
410
451
|
var(--ui-focus-halo);
|
|
411
452
|
}
|
|
412
453
|
|
|
454
|
+
:is(:host([variant="ghost"]), :host([variant="link"]), :host([variant="card"])):focus-visible {
|
|
455
|
+
box-shadow: var(--ui-focus-halo);
|
|
456
|
+
}
|
|
457
|
+
|
|
413
458
|
/* A loading action stays visually active; only an explicitly disabled action takes the
|
|
414
459
|
neutral disabled palette. Its flat surface and muted ink make the state unambiguous. */
|
|
415
460
|
:host:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
|
|
@@ -116,6 +116,16 @@
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
:host {
|
|
119
|
+
/* Optional inherited action-group values keep text and icon actions under the same light.
|
|
120
|
+
One fine top/start highlight follows the bottom/end lowlight at a joined action seam.
|
|
121
|
+
A single ambient lift avoids stacking a face gradient or another inset bevel. */
|
|
122
|
+
--_ui-action-start: 1px;
|
|
123
|
+
--_ui-action-edge: var(
|
|
124
|
+
--ui-action-edge,
|
|
125
|
+
inset var(--_ui-action-start) 1px 0 light-dark(rgb(255 255 255 / 0.08), rgb(255 255 255 / 0.06)),
|
|
126
|
+
inset calc(-1 * var(--_ui-action-start)) -1px 0 light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.08))
|
|
127
|
+
);
|
|
128
|
+
--_ui-action-shadow: var(--_ui-action-edge), 0 1px 2px light-dark(rgb(0 0 0 / 0.10), rgb(0 0 0 / 0.20));
|
|
119
129
|
/* Hooks do not inherit, so the parts below read them from the root. */
|
|
120
130
|
--_ui-icon-button-icon-size: var(--ui-icon-button-icon-size);
|
|
121
131
|
--_ui-icon-button-icon-stroke-width: var(--ui-icon-button-icon-stroke-width);
|
|
@@ -124,6 +134,10 @@
|
|
|
124
134
|
color: var(--ui-text-secondary);
|
|
125
135
|
}
|
|
126
136
|
|
|
137
|
+
:host:dir(rtl) {
|
|
138
|
+
--_ui-action-start: -1px;
|
|
139
|
+
}
|
|
140
|
+
|
|
127
141
|
:host([tone="accent"]) {
|
|
128
142
|
--_icon-button-border: var(--ui-accent-solid);
|
|
129
143
|
--_icon-button-hover-border: var(--ui-accent-hover);
|
|
@@ -297,19 +311,16 @@
|
|
|
297
311
|
:host([variant="outline"]) {
|
|
298
312
|
border-color: var(--ui-icon-button-border, var(--_icon-button-border, var(--ui-control-border, var(--ui-border-strong))));
|
|
299
313
|
background-color: var(--ui-icon-button-outline-surface, var(--_icon-button-outline-surface, var(--ui-surface-default)));
|
|
300
|
-
background-image:
|
|
314
|
+
background-image: none;
|
|
301
315
|
color: var(--ui-icon-button-outline-text, var(--_icon-button-text, var(--ui-text-primary)));
|
|
302
|
-
box-shadow: var(--ui-
|
|
316
|
+
box-shadow: var(--ui-action-shadow, var(--_ui-action-shadow));
|
|
303
317
|
}
|
|
304
318
|
|
|
305
319
|
:host([variant="solid"]) {
|
|
306
320
|
background-color: var(--ui-icon-button-solid-surface, var(--_icon-button-solid-surface, var(--ui-accent-solid)));
|
|
307
|
-
background-image:
|
|
321
|
+
background-image: none;
|
|
308
322
|
color: var(--ui-icon-button-solid-text, var(--_icon-button-solid-text, var(--ui-text-on-accent)));
|
|
309
|
-
box-shadow: var(
|
|
310
|
-
--ui-icon-button-solid-shadow,
|
|
311
|
-
0 1px 2px color-mix(in srgb, var(--ui-accent-solid) 18%, transparent)
|
|
312
|
-
);
|
|
323
|
+
box-shadow: var(--ui-icon-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow)));
|
|
313
324
|
}
|
|
314
325
|
|
|
315
326
|
:host:hover:enabled:not([aria-disabled="true"]) {
|
|
@@ -373,6 +384,23 @@
|
|
|
373
384
|
box-shadow: var(--ui-focus-halo);
|
|
374
385
|
}
|
|
375
386
|
|
|
387
|
+
:host([variant="outline"]):focus-visible {
|
|
388
|
+
box-shadow:
|
|
389
|
+
var(--ui-action-shadow, var(--_ui-action-shadow)),
|
|
390
|
+
var(--ui-focus-halo);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
:host([variant="solid"]):focus-visible {
|
|
394
|
+
box-shadow:
|
|
395
|
+
var(--ui-icon-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))),
|
|
396
|
+
var(--ui-focus-halo);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
:host:is(:host([variant="outline"]), :host([variant="solid"])):active:enabled:not([aria-disabled="true"]) {
|
|
400
|
+
background-image: none;
|
|
401
|
+
box-shadow: var(--ui-pressed);
|
|
402
|
+
}
|
|
403
|
+
|
|
376
404
|
:host([variant="outline"])[aria-pressed="true"]:enabled:focus-visible {
|
|
377
405
|
box-shadow:
|
|
378
406
|
var(--ui-pressed),
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template component="ui-input-group" controller="./ui-input-group.js">
|
|
2
2
|
<defs>
|
|
3
|
+
<prop name="variant" type="keyword" values="outline, subtle" default="outline"
|
|
4
|
+
>outline uses the standard field edge and surface. subtle uses a quiet decorative edge and muted surface for a multiline composer. Both retain the same
|
|
5
|
+
focus, invalid and disabled states, and the field inside shares the group's one frame.</prop>
|
|
3
6
|
<prop name="multiline" type="boolean" default="false">Groups a Textarea with an action below its text inside the shared frame.</prop>
|
|
4
7
|
<prop name="actionPosition" type="keyword" values="below, top-end" default="below"
|
|
5
8
|
>In a multiline group, below keeps the action under the text. top-end places it inside the textarea at its upper end and reserves text padding for its
|
|
@@ -79,6 +82,12 @@
|
|
|
79
82
|
transition: border-color var(--ui-motion-fast) var(--ui-motion-ease), box-shadow var(--ui-motion-fast) var(--ui-motion-ease);
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
:host([variant="subtle"]) {
|
|
86
|
+
border-color: var(--ui-border);
|
|
87
|
+
background: var(--ui-surface-muted);
|
|
88
|
+
box-shadow: none;
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
/* The frame takes its input's size, so a small group lines up with small buttons and fields. */
|
|
83
92
|
:host:has(.field [data-ui-input-state~="size=sm"]) {
|
|
84
93
|
min-block-size: var(--ui-control-size-sm);
|