@quillmark/svelte 0.7.3 → 0.9.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/dist/core/codec/blocks.d.ts +5 -1
- package/dist/core/codec/blocks.js +121 -0
- package/dist/core/codec/breaks.d.ts +10 -0
- package/dist/core/codec/breaks.js +37 -0
- package/dist/core/codec/code.d.ts +3 -1
- package/dist/core/codec/code.js +62 -4
- package/dist/core/codec/decode.js +11 -10
- package/dist/core/codec/encode.js +84 -16
- package/dist/core/codec/field.d.ts +5 -4
- package/dist/core/codec/field.js +11 -8
- package/dist/core/codec/islands.d.ts +24 -4
- package/dist/core/codec/islands.js +140 -15
- package/dist/core/codec/keymap.js +2 -1
- package/dist/core/codec/lists.d.ts +5 -1
- package/dist/core/codec/lists.js +77 -14
- package/dist/core/codec/prose.css +6 -12
- package/dist/core/codec/schema.js +150 -9
- package/dist/core/codec/table-view.js +2 -2
- package/dist/core/theme.css +16 -16
- package/dist/visual/ArrayField.svelte +14 -22
- package/dist/visual/BooleanField.svelte +6 -1
- package/dist/visual/Card.svelte +114 -61
- package/dist/visual/CardControls.svelte +31 -26
- package/dist/visual/EnumField.svelte +17 -2
- package/dist/visual/ObjectField.svelte +1 -1
- package/dist/visual/controls.css +63 -28
- package/dist/visual/ext.d.ts +2 -3
- package/dist/visual/ext.js +2 -3
- package/dist/visual/icons/Icon.svelte +2 -3
- package/dist/visual/icons/Icon.svelte.d.ts +2 -3
- package/dist/visual/strings.d.ts +1 -1
- package/package.json +2 -2
package/dist/core/theme.css
CHANGED
|
@@ -280,14 +280,19 @@
|
|
|
280
280
|
--_qm-reveal: 500ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
281
281
|
|
|
282
282
|
/* ── Geometry ──────────────────────────────────────────────────────────*/
|
|
283
|
-
/* One stroke width for every edge the chrome draws
|
|
284
|
-
|
|
285
|
-
rhythm choice, like the tap floor: a hairline is a hairline at
|
|
286
|
-
`--qm-space`, so
|
|
287
|
-
|
|
288
|
-
and a subform's are one stroke at three depths, and `check:style`'s
|
|
289
|
-
border-width axis can only hold that against a token. */
|
|
283
|
+
/* One stroke width for every edge the chrome draws and every rule it ranks
|
|
284
|
+
with: a card's, a control's, the two closing a card's payload. A threshold
|
|
285
|
+
rather than a rhythm choice, like the tap floor: a hairline is a hairline at
|
|
286
|
+
any `--qm-space`, so neither derives from a dial. Both exist as rungs because
|
|
287
|
+
`check:style`'s border-width axis can only hold a stroke against a token. */
|
|
290
288
|
--_qm-border-width: 1px;
|
|
289
|
+
/* Half a step over it, for the verticals alone — a section's, a subform's, a
|
|
290
|
+
blockquote's. What a vertical states is depth, and depth is the one thing a
|
|
291
|
+
reader has to find in a dense card; the strokes that close a block or rank two
|
|
292
|
+
stay at the hairline, so the ladder is what carries the extra weight. Half,
|
|
293
|
+
because a whole step reads as a different kind of line rather than the same
|
|
294
|
+
one doing more work. */
|
|
295
|
+
--_qm-vertical-width: 1.5px;
|
|
291
296
|
--_qm-radius: var(--qm-radius, 8px);
|
|
292
297
|
/* The interior corner, and half is the relationship rather than the number: a
|
|
293
298
|
corner inside a corner reads as nested only while it is visibly tighter, and
|
|
@@ -324,17 +329,12 @@
|
|
|
324
329
|
--_qm-tail: 40vh;
|
|
325
330
|
/* WCAG 2.5.8's 24x24 pointer-target floor. A rung rather than a sixth step on
|
|
326
331
|
the space scale: it is a threshold the spec fixes, not a rhythm choice, so
|
|
327
|
-
it does not move when `--qm-space` does.
|
|
332
|
+
it does not move when `--qm-space` does. Every button family reads it, so the
|
|
328
333
|
floor is one number instead of one expression per control. */
|
|
329
334
|
--_qm-tap-min: 24px;
|
|
330
|
-
/* The
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
card's gutter is an arithmetic of the two, and a literal on either side is a
|
|
334
|
-
balance that silently breaks when the glyph moves. Sized in CSS rather than
|
|
335
|
-
through the icon's `size`, so the hang and the glyph read one number. Scoped to
|
|
336
|
-
the control pair: the hint and the format marks take their own sizes, and a
|
|
337
|
-
rung claiming to be every glyph would be false at three of them. */
|
|
335
|
+
/* The chrome glyph: a chip's, and the in-well remove's. Sized in CSS rather than
|
|
336
|
+
through the icon's `size`, so a stylesheet beats the SVG presentation attribute
|
|
337
|
+
and one rung is the whole of what a control draws. */
|
|
338
338
|
--_qm-glyph-control: 14px;
|
|
339
339
|
|
|
340
340
|
/* ── Type ────────────────────────────────────────────────────────────────
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
by `items.type`: `richtext` / `plaintext` → a prose element
|
|
6
6
|
({@link ProseValue}), `object` → a summary row that opens onto a subform,
|
|
7
7
|
everything else → a text input. The
|
|
8
|
-
add
|
|
8
|
+
add chip sits in the label header row (space-between with the field label);
|
|
9
9
|
{@link Field} skips its own label for array controls and hands this component the
|
|
10
10
|
label track with it.
|
|
11
11
|
|
|
@@ -352,7 +352,7 @@
|
|
|
352
352
|
{/if}
|
|
353
353
|
<button
|
|
354
354
|
type="button"
|
|
355
|
-
class="qm-add-el qm-
|
|
355
|
+
class="qm-add-el qm-chip qm-focus-ring qm-tap-floor"
|
|
356
356
|
bind:this={addEl}
|
|
357
357
|
onclick={add}>{t.strings.arrayAdd}</button
|
|
358
358
|
>
|
|
@@ -442,10 +442,9 @@
|
|
|
442
442
|
gap: var(--_qm-space);
|
|
443
443
|
}
|
|
444
444
|
/* The array's first line is the row's label line: this component owns the label
|
|
445
|
-
track (Field.svelte), so the
|
|
446
|
-
|
|
447
|
-
label
|
|
448
|
-
below. */
|
|
445
|
+
track (Field.svelte). Arrays never pack (`packable`), so the chip's tap floor is
|
|
446
|
+
free to size the header; a caption on this line would have had to match
|
|
447
|
+
`.qm-field-label-row`. */
|
|
449
448
|
.qm-array-header {
|
|
450
449
|
display: flex;
|
|
451
450
|
align-items: center;
|
|
@@ -584,29 +583,22 @@
|
|
|
584
583
|
color: var(--_qm-ink-label);
|
|
585
584
|
font-style: italic;
|
|
586
585
|
}
|
|
587
|
-
/*
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
target-sized box would stand the header 8px taller than the `.qm-field-label-row`
|
|
593
|
-
beside it in a shared row and put the two labels on different lines.
|
|
594
|
-
|
|
595
|
-
The label rung in place of the family's body rung, which the family invites: it reads
|
|
596
|
-
as one more thing on the label line rather than as a control from the row below. */
|
|
586
|
+
/* The label line's own type: size, weight and leading are `.qm-field-label`'s, so
|
|
587
|
+
the two read as one register. Inner radius: the chip family is the card's, and this
|
|
588
|
+
box is a line of type. The chip's tap floor is given back (`.qm-tap-floor`),
|
|
589
|
+
and the padding with it, so the drawn box is the line and the target is the floor.
|
|
590
|
+
It rests dim and comes up under the pointer; empty, it is the way in. */
|
|
597
591
|
.qm-add-el {
|
|
598
592
|
padding: 0 var(--_qm-space);
|
|
599
|
-
font-
|
|
593
|
+
font-weight: var(--_qm-weight-mid);
|
|
594
|
+
line-height: var(--_qm-leading-tight);
|
|
595
|
+
border-radius: var(--_qm-radius-inner);
|
|
600
596
|
opacity: var(--_qm-opacity-idle);
|
|
601
597
|
}
|
|
602
|
-
.qm-
|
|
598
|
+
.qm-add-el:hover,
|
|
603
599
|
.qm-add-el:focus-visible {
|
|
604
600
|
opacity: 1;
|
|
605
601
|
}
|
|
606
|
-
/* Empty, the rows box is `display: none` and the field is a label and this trigger:
|
|
607
|
-
nothing left for it to compete with, and nothing else in the field to act on. So
|
|
608
|
-
it rests full-strength there and takes the recede back once it heads a list. A
|
|
609
|
-
receded trigger beside no content reads as a caption rather than as a way in. */
|
|
610
602
|
.qm-array.empty .qm-add-el {
|
|
611
603
|
opacity: 1;
|
|
612
604
|
}
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
<span class="qm-switch-wrap">
|
|
40
40
|
<Switch.Root
|
|
41
|
-
class="qm-switch qm-focus-ring"
|
|
41
|
+
class="qm-switch qm-focus-ring qm-tap-floor"
|
|
42
42
|
checked={local.value}
|
|
43
43
|
{id}
|
|
44
44
|
aria-label={id ? undefined : label}
|
|
@@ -55,6 +55,11 @@
|
|
|
55
55
|
<style>
|
|
56
56
|
/* A primitive renders its own element, which a scoped selector cannot reach:
|
|
57
57
|
styled through the wrapper with `:global`. */
|
|
58
|
+
/* The track is the drawn box and the target is `.qm-tap-floor`'s (controls.css): a
|
|
59
|
+
switch is a mark standing in a field's row, so its height is the track's, where WCAG
|
|
60
|
+
2.5.8's floor is half again that. The `<label for>` beside it forwards a press but
|
|
61
|
+
stands at the label rung, so it is no taller: without the floor no region on the
|
|
62
|
+
surface reaches this control at the size every other control on it holds. */
|
|
58
63
|
.qm-switch-wrap :global(.qm-switch) {
|
|
59
64
|
display: inline-flex;
|
|
60
65
|
align-items: center;
|
package/dist/visual/Card.svelte
CHANGED
|
@@ -408,33 +408,54 @@
|
|
|
408
408
|
|
|
409
409
|
<style>
|
|
410
410
|
/* An island: the card is the base plane, the column behind it the sunken one, and the
|
|
411
|
-
hairline closes it (ARCHITECTURE §Styling). The
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
411
|
+
hairline closes it (ARCHITECTURE §Styling). The block half is the same rung as the
|
|
412
|
+
gap: the payload stands that far off the card's edge and off the body below it. The
|
|
413
|
+
header insets itself to the section-heading column (`.qm-card-header`). The inline
|
|
414
|
+
half stands a step tighter than the nesting rung the verticals inside keep: this
|
|
415
|
+
edge closes the block rather than nesting one, and the rules it holds run that much
|
|
416
|
+
further for it. */
|
|
415
417
|
.qm-card {
|
|
416
418
|
border: var(--_qm-border-width) solid var(--_qm-border);
|
|
417
419
|
border-radius: var(--_qm-radius);
|
|
418
|
-
padding: var(--_qm-space-3) var(--_qm-
|
|
420
|
+
padding: var(--_qm-space-3) var(--_qm-space-2);
|
|
419
421
|
background: var(--_qm-surface);
|
|
420
422
|
display: flex;
|
|
421
423
|
flex-direction: column;
|
|
422
424
|
gap: var(--_qm-space-3);
|
|
423
425
|
}
|
|
426
|
+
/* Headerless, the payload faces that edge with nothing between. A field row
|
|
427
|
+
leading it stands the edge's own inset off the top, as it does off the side:
|
|
428
|
+
this rung plus the section's block padding is the inline inset. A card that
|
|
429
|
+
opens on its body keeps the full inset — prose is neither a heading nor a
|
|
430
|
+
field row. */
|
|
431
|
+
.qm-card.qm-main:has(> .qm-card-body > .qm-card-meta:first-child) {
|
|
432
|
+
padding-top: var(--_qm-space);
|
|
433
|
+
}
|
|
434
|
+
/* A section heading leading it: the vertical starts at the content's
|
|
435
|
+
top-left, so the top inset is the inline one and the tip is a square
|
|
436
|
+
corner of the card. */
|
|
437
|
+
.qm-card.qm-main:has(> .qm-card-body > .qm-card-meta:first-child > .qm-groups:first-child) {
|
|
438
|
+
padding-top: var(--_qm-space-2);
|
|
439
|
+
}
|
|
424
440
|
.qm-card-header {
|
|
425
441
|
display: flex;
|
|
426
442
|
align-items: center;
|
|
427
443
|
justify-content: space-between;
|
|
428
444
|
gap: var(--_qm-space-2);
|
|
445
|
+
/* The group's vertical plus the rung the chevron stands off it: the title
|
|
446
|
+
shares that column. Inline only. The block pull is half the card's block
|
|
447
|
+
inset, so the header sits that far off the hairline and the rule. */
|
|
448
|
+
padding-inline: calc(var(--_qm-space) + var(--_qm-vertical-width));
|
|
449
|
+
margin-block: calc(-1 * var(--_qm-space-3) / 2);
|
|
429
450
|
}
|
|
430
|
-
/* The overhang is the tap target's own centring given back, so the glyphs sit on the
|
|
431
|
-
card's gutter rather than the targets around them. It is the fill that crosses the
|
|
432
|
-
line, not a box: an icon button rests unboxed and fills only under the pointer. */
|
|
433
451
|
.qm-card-header-right {
|
|
434
452
|
display: flex;
|
|
435
453
|
align-items: center;
|
|
436
454
|
gap: var(--_qm-space-2);
|
|
437
|
-
|
|
455
|
+
/* The overhang is the tap target's own centring given back, so the delete
|
|
456
|
+
glyph sits on the header's inset rather than the pad around it. The fill is
|
|
457
|
+
what crosses the line. */
|
|
458
|
+
margin-inline-end: calc(-1 * (var(--_qm-tap-min) - var(--_qm-glyph-control)) / 2);
|
|
438
459
|
}
|
|
439
460
|
/* Focus as well as hover, so a caret in any leaf, the title, or a chevron holds the
|
|
440
461
|
reveal: hover alone would strand the pair on keyboard and touch. */
|
|
@@ -445,7 +466,7 @@
|
|
|
445
466
|
/* The rename hit region is the header's free width at full height, so a press
|
|
446
467
|
anywhere left of the controls enters the title edit. `align-self: stretch` beats the
|
|
447
468
|
header's centring, which is also what keeps the target legal with no padding: WCAG
|
|
448
|
-
2.5.8's floor is the row's, which the
|
|
469
|
+
2.5.8's floor is the row's, which the chips hold at `--_qm-tap-min`. */
|
|
449
470
|
.qm-card-rename {
|
|
450
471
|
flex: 1;
|
|
451
472
|
min-width: 0;
|
|
@@ -504,24 +525,21 @@
|
|
|
504
525
|
flex-direction: column;
|
|
505
526
|
gap: var(--_qm-space-3);
|
|
506
527
|
}
|
|
507
|
-
/* Where two sections meet the distance is three of one rung — the cap closing the one
|
|
508
|
-
above, this gap, the cap opening the one below — the same whether either side is a
|
|
509
|
-
group or the ungrouped block. That total is what the fields inside a section keep
|
|
510
|
-
between their rows (`.qm-fields` row-gap), so a section boundary stands no further
|
|
511
|
-
apart than a field boundary: what ranks them is the stroke, not the air. */
|
|
512
528
|
.qm-card-meta,
|
|
513
529
|
.qm-groups {
|
|
514
530
|
display: flex;
|
|
515
531
|
flex-direction: column;
|
|
516
|
-
gap: var(--_qm-space);
|
|
517
532
|
}
|
|
518
|
-
/* One rung
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
533
|
+
/* One rung between the payload's two kinds of section, and none inside the accordion:
|
|
534
|
+
what stands one disclosure row off the next is the two headers' own block padding and
|
|
535
|
+
nothing else, so the rows tile and their targets with them. A run of one-line rows
|
|
536
|
+
wants the tighter rhythm — the `--_qm-space-3` the fields keep between their own rows
|
|
537
|
+
is calibrated for a label over a well, and reads as a float between two lines of text.
|
|
538
|
+
|
|
539
|
+
`--stroke-reach` is what a box standing a field row off a rule has left to spend: the
|
|
540
|
+
rung is `--_qm-nest`, of which its own block padding is already `--_qm-space`. */
|
|
524
541
|
.qm-card-meta {
|
|
542
|
+
gap: var(--_qm-space);
|
|
525
543
|
--stroke-reach: calc(var(--_qm-nest) - var(--_qm-space));
|
|
526
544
|
}
|
|
527
545
|
/* The card's two horizontals, and ranking is the one thing a horizontal is for: they
|
|
@@ -530,27 +548,35 @@
|
|
|
530
548
|
conditional on what it ranks against: `main` is headerless, and a card with no body
|
|
531
549
|
has nothing under the payload to divide it from.
|
|
532
550
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
551
|
+
Neither insets: a rule is drawn on the edge of the box it faces, and the air is that
|
|
552
|
+
box's own. So the section carries what its ink stands off a rule — the inset of the
|
|
553
|
+
ink facing it, which is what squares the corner: `--_qm-nest` for a field row,
|
|
554
|
+
`--_qm-space` for a heading, each the rung that ink stands off a vertical inline. The
|
|
555
|
+
air outside the pair is the `--_qm-space-3` the card spends between its blocks. */
|
|
536
556
|
.qm-meta-top {
|
|
537
557
|
border-top: var(--_qm-border-width) solid var(--_qm-border);
|
|
538
|
-
padding-top: var(--stroke-reach);
|
|
539
558
|
}
|
|
540
559
|
.qm-meta-bottom {
|
|
541
560
|
border-bottom: var(--_qm-border-width) solid var(--_qm-border);
|
|
542
|
-
padding-bottom: var(--stroke-reach);
|
|
543
561
|
}
|
|
544
|
-
/* A section's share of the boundary between sections
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
edges hold.
|
|
562
|
+
/* A section's share of the boundary between sections, one rung at each end. The
|
|
563
|
+
accordion spends the same rung from its header and its panel, where it also caps the
|
|
564
|
+
vertical those two edges hold.
|
|
548
565
|
|
|
549
|
-
No stroke
|
|
550
|
-
|
|
551
|
-
|
|
566
|
+
No stroke: a vertical would mark a section a reader has no name for. The start
|
|
567
|
+
inset is the heading's — the group's vertical plus the rung the chevron stands
|
|
568
|
+
off it — so a top-level field and a section heading share a column. */
|
|
552
569
|
.qm-section {
|
|
553
570
|
padding-block: var(--_qm-space);
|
|
571
|
+
padding-inline-start: calc(var(--_qm-space) + var(--_qm-vertical-width));
|
|
572
|
+
}
|
|
573
|
+
/* Where it faces a rule, the rung a heading already stands off one. The fields
|
|
574
|
+
sit on the heading column, not the card's edge. */
|
|
575
|
+
.qm-meta-top > .qm-section:first-child {
|
|
576
|
+
padding-top: var(--_qm-space-2);
|
|
577
|
+
}
|
|
578
|
+
.qm-meta-bottom > .qm-section:last-child {
|
|
579
|
+
padding-bottom: var(--_qm-space-2);
|
|
554
580
|
}
|
|
555
581
|
/* Two query containers, one per kind of section, so capacity follows the width the
|
|
556
582
|
fields actually get: a panel's inset makes that narrower than the card. */
|
|
@@ -597,47 +623,72 @@
|
|
|
597
623
|
than absent, so a toggle moves no text — and closed, there is nothing under the header
|
|
598
624
|
for it to gather.
|
|
599
625
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
boundary keeps the three rungs it stood at. What the stroke reaches into is the air of
|
|
605
|
-
that boundary, or, where the section is the payload's first or last, exactly the inset
|
|
606
|
-
its rule took — so it lands on the rule at a corner with nothing drawn longer for it.
|
|
626
|
+
A free end of the stroke caps at `--_qm-space`, the rung the subform's own vertical
|
|
627
|
+
caps at (`ObjectField`), grown as padding and handed straight back as margin so the
|
|
628
|
+
stroke lengthens and no boundary moves. Above, that is also the rung the header stands
|
|
629
|
+
off it, so the top end is the same number whether it caps free or lands on a rule.
|
|
607
630
|
One section open at a time, so no two strokes are ever visible to meet in between. */
|
|
608
631
|
.qm-group {
|
|
609
632
|
display: flex;
|
|
610
633
|
flex-direction: column;
|
|
611
|
-
border-left: var(--_qm-
|
|
612
|
-
padding-
|
|
613
|
-
margin-
|
|
614
|
-
transition:
|
|
615
|
-
|
|
634
|
+
border-left: var(--_qm-vertical-width) solid transparent;
|
|
635
|
+
padding-bottom: var(--_qm-space);
|
|
636
|
+
margin-bottom: calc(-1 * var(--_qm-space));
|
|
637
|
+
transition:
|
|
638
|
+
border-color var(--_qm-duration-slow) var(--_qm-ease-reverse),
|
|
639
|
+
padding-bottom var(--_qm-duration-slow) var(--_qm-ease-reverse),
|
|
640
|
+
margin-bottom var(--_qm-duration-slow) var(--_qm-ease-reverse);
|
|
641
|
+
}
|
|
642
|
+
/* Open, the cap is air as well as stroke: a section ending on a field row stands the
|
|
643
|
+
longer rung off what follows, and three of `--_qm-space` are what that comes to — the
|
|
644
|
+
panel's own, this cap, and the next header's. Shut, a section ends on its header and
|
|
645
|
+
stands the header's rung, so there is nothing to cap and the padding is handed back. */
|
|
616
646
|
.qm-group.qm-open {
|
|
617
647
|
border-left-color: var(--_qm-border);
|
|
648
|
+
margin-bottom: 0;
|
|
649
|
+
}
|
|
650
|
+
/* The one end that lands on a rule, where the padding is real and the rule is this
|
|
651
|
+
box's own bottom edge — so the stroke ends on the rule at every frame of a fold rather
|
|
652
|
+
than at the two ends of one, and the fields inside stand `--_qm-nest` off it. Shut,
|
|
653
|
+
the box ends on the header's block padding and the rule takes the heading's rung
|
|
654
|
+
there, the same one the rule above it takes. */
|
|
655
|
+
.qm-meta-bottom > .qm-groups:last-child > .qm-group:last-child {
|
|
656
|
+
padding-bottom: 0;
|
|
657
|
+
margin-bottom: 0;
|
|
658
|
+
}
|
|
659
|
+
.qm-meta-bottom > .qm-groups:last-child > .qm-group:last-child.qm-open {
|
|
660
|
+
padding-bottom: var(--stroke-reach);
|
|
618
661
|
}
|
|
619
|
-
/*
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
between ink and stroke, and the rotation swaps which bearing
|
|
662
|
+
/* One rung on three sides and zero on the end, the end because the row is the target
|
|
663
|
+
and an inset there is target given back. The rung is `--_qm-space` in both axes, and
|
|
664
|
+
neither is free: the block padding is the tightest that still clears WCAG 2.5.8's
|
|
665
|
+
24×24 floor, and it is the whole air a heading opening the payload keeps under its
|
|
666
|
+
rule, which a rule cannot inset past — so the inline inset is that same number or the
|
|
667
|
+
corner is square on one axis alone. The heading rides the vertical there, one
|
|
668
|
+
nesting step out from the fields under it, which is what states that it holds them.
|
|
669
|
+
What the glyph gives up for the step is the gutter: it stands in the clearance itself,
|
|
670
|
+
with only its own bearing between ink and stroke, and the rotation swaps which bearing
|
|
671
|
+
faces which.
|
|
628
672
|
|
|
629
673
|
`font: inherit` because a UA button inherits no face, then the body rung, one step
|
|
630
674
|
over the field labels beneath it: at the label rung the two read as one register and
|
|
631
675
|
the accordion stops looking like structure. Size, weight and leading sit after
|
|
632
676
|
`font`, a shorthand that carries `line-height`. */
|
|
633
677
|
.qm-group-header {
|
|
678
|
+
/* Positioned, and paint is not what it buys: a group's box grows by
|
|
679
|
+
`--stroke-reach` at both ends and hands the growth back as margin, so
|
|
680
|
+
consecutive boxes overlap in a band at every seam, and a closed group's header
|
|
681
|
+
ends inside the next group's box. In flow, the later sibling takes the presses
|
|
682
|
+
that land in the band and the bottom of the header is a dead strip; positioned,
|
|
683
|
+
the control holds its own box, and the band paints the same either way. */
|
|
684
|
+
position: relative;
|
|
634
685
|
display: flex;
|
|
635
686
|
align-items: center;
|
|
636
687
|
gap: var(--_qm-space);
|
|
637
688
|
width: 100%;
|
|
638
689
|
border: none;
|
|
639
690
|
background: transparent;
|
|
640
|
-
padding: var(--_qm-space) 0 var(--_qm-space) var(--_qm-
|
|
691
|
+
padding: var(--_qm-space) 0 var(--_qm-space) var(--_qm-space);
|
|
641
692
|
font: inherit;
|
|
642
693
|
font-size: var(--_qm-text-body);
|
|
643
694
|
font-weight: var(--_qm-weight-mid);
|
|
@@ -672,11 +723,13 @@
|
|
|
672
723
|
.qm-group.qm-open .qm-group-panel {
|
|
673
724
|
grid-template-rows: 1fr;
|
|
674
725
|
}
|
|
675
|
-
/*
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
the
|
|
679
|
-
|
|
726
|
+
/* What a toggle moves is the panel's track, and the box's own bottom edge where the
|
|
727
|
+
last section is the one toggling. A reveal's landing measures its target one flush
|
|
728
|
+
after asking — where either, mid-flight, still reads its start value — so nothing
|
|
729
|
+
about the accordion animates for it. Both panels of the move are inside the group box,
|
|
730
|
+
so the opening one and the closing one above it stand still together (`revealLeaf`). */
|
|
731
|
+
.qm-groups.qm-instant .qm-group-panel,
|
|
732
|
+
.qm-groups.qm-instant .qm-group {
|
|
680
733
|
transition: none;
|
|
681
734
|
}
|
|
682
735
|
/* Two boxes because the axes clip differently, and the track is the only thing that
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
Card controls, composable cards only.
|
|
3
3
|
Right-aligned in the header: a move-up/move-down chevron pair revealed while the
|
|
4
|
-
pointer or the caret is in the card (each disabled at its edge), then
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
to do it: the size is the stylesheet's below, never an attribute.
|
|
4
|
+
pointer or the caret is in the card (each disabled at its edge), then delete.
|
|
5
|
+
Reorder is buttons, not drag (V1). All three are chips (controls.css): fill arrives
|
|
6
|
+
on hover, and delete rests faded danger, full under the pointer. The card scopes
|
|
7
|
+
that reveal, so its condition lives in the parent's CSS; the pair keeps the narrower
|
|
8
|
+
guarantee here, that a focused chevron is a visible one.
|
|
10
9
|
-->
|
|
11
10
|
<script lang="ts">
|
|
12
11
|
import { wording } from './strings.js';
|
|
@@ -31,14 +30,14 @@
|
|
|
31
30
|
<div class="qm-card-reorder">
|
|
32
31
|
<button
|
|
33
32
|
type="button"
|
|
34
|
-
class="qm-
|
|
33
|
+
class="qm-chip qm-focus-ring"
|
|
35
34
|
title={t.strings.cardMoveUp}
|
|
36
35
|
disabled={isFirst}
|
|
37
36
|
onclick={onMoveUp}><Icon name="chevron-up" /></button
|
|
38
37
|
>
|
|
39
38
|
<button
|
|
40
39
|
type="button"
|
|
41
|
-
class="qm-
|
|
40
|
+
class="qm-chip qm-focus-ring"
|
|
42
41
|
title={t.strings.cardMoveDown}
|
|
43
42
|
disabled={isLast}
|
|
44
43
|
onclick={onMoveDown}><Icon name="chevron-down" /></button
|
|
@@ -46,9 +45,9 @@
|
|
|
46
45
|
</div>
|
|
47
46
|
<button
|
|
48
47
|
type="button"
|
|
49
|
-
class="qm-
|
|
48
|
+
class="qm-chip qm-card-delete qm-focus-ring"
|
|
50
49
|
title={t.strings.cardDelete}
|
|
51
|
-
onclick={onDelete}><Icon name="
|
|
50
|
+
onclick={onDelete}><Icon name="trash-2" /></button
|
|
52
51
|
>
|
|
53
52
|
</div>
|
|
54
53
|
|
|
@@ -56,20 +55,14 @@
|
|
|
56
55
|
.qm-card-controls {
|
|
57
56
|
display: flex;
|
|
58
57
|
align-items: center;
|
|
59
|
-
gap: var(--_qm-space);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
breaks silently when the two disagree. A stylesheet beats an SVG presentation
|
|
65
|
-
attribute, so the rung wins without the attribute being removed from the icon. */
|
|
66
|
-
.qm-card-controls :global(svg) {
|
|
67
|
-
width: var(--_qm-glyph-control);
|
|
68
|
-
height: var(--_qm-glyph-control);
|
|
58
|
+
gap: var(--_qm-space-half);
|
|
59
|
+
/* One compositor layer for the row. Opacity on the pair otherwise snaps
|
|
60
|
+
that box off the header's half-pixel, against delete which never leaves
|
|
61
|
+
the document paint. */
|
|
62
|
+
transform: translateZ(0);
|
|
69
63
|
}
|
|
70
64
|
.qm-card-reorder {
|
|
71
65
|
display: flex;
|
|
72
|
-
gap: var(--_qm-space-half);
|
|
73
66
|
opacity: 0;
|
|
74
67
|
transition: opacity var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
75
68
|
}
|
|
@@ -80,13 +73,25 @@
|
|
|
80
73
|
.qm-card-reorder:focus-within {
|
|
81
74
|
opacity: 1;
|
|
82
75
|
}
|
|
83
|
-
/*
|
|
84
|
-
is
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
/* Squares: the chip's inline pad is for a label. Width and height are one rung
|
|
77
|
+
so the hover fill is a square and the glyph sits in the middle. */
|
|
78
|
+
.qm-card-controls button {
|
|
79
|
+
width: var(--_qm-tap-min);
|
|
80
|
+
height: var(--_qm-tap-min);
|
|
81
|
+
padding: 0;
|
|
88
82
|
}
|
|
83
|
+
/* Hover fill comes from `.qm-chip` (controls.css). Idle recede is on the ink,
|
|
84
|
+
not the box: a layer on the chip is two rasters of the same stroke, and the
|
|
85
|
+
fill has to stay opaque. Danger mixed to the idle rung; full under the pointer. */
|
|
89
86
|
.qm-card-delete {
|
|
87
|
+
color: color-mix(
|
|
88
|
+
in oklab,
|
|
89
|
+
var(--_qm-danger) calc(100% * var(--_qm-opacity-idle)),
|
|
90
|
+
var(--_qm-surface)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
.qm-card-delete:hover,
|
|
94
|
+
.qm-card-delete:focus-visible {
|
|
90
95
|
color: var(--_qm-danger);
|
|
91
96
|
}
|
|
92
97
|
</style>
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
aria-describedby={describedBy}
|
|
105
105
|
data-ghosted={unset ? '' : undefined}
|
|
106
106
|
>
|
|
107
|
-
{shown}
|
|
108
|
-
<Icon name="chevron-down" size={14} />
|
|
107
|
+
<span class="qm-select-shown">{shown}</span>
|
|
108
|
+
<Icon name="chevron-down" class="qm-select-chevron" size={14} />
|
|
109
109
|
</Select.Trigger>
|
|
110
110
|
<Select.Portal to={portalTarget}>
|
|
111
111
|
<Select.Content collisionBoundary={portalTarget ?? []} sideOffset={4}>
|
|
@@ -176,6 +176,21 @@
|
|
|
176
176
|
text-align: left;
|
|
177
177
|
cursor: pointer;
|
|
178
178
|
}
|
|
179
|
+
/* The value is one line however long it runs, and the glyph beside it keeps its rung:
|
|
180
|
+
the box is an inset plus one line box (controls.css), which a value that wraps
|
|
181
|
+
stands two lines tall, and a flex glyph with nothing holding it gives its width up
|
|
182
|
+
to the text first and is gone by the time the row is tight. The pair is the same
|
|
183
|
+
guard the element summary takes over its own title, and `min-width` is the half
|
|
184
|
+
that lets the text shrink at all. */
|
|
185
|
+
.qm-select-shown {
|
|
186
|
+
min-width: 0;
|
|
187
|
+
overflow: hidden;
|
|
188
|
+
text-overflow: ellipsis;
|
|
189
|
+
white-space: nowrap;
|
|
190
|
+
}
|
|
191
|
+
.qm-select-wrap :global(.qm-select-chevron) {
|
|
192
|
+
flex-shrink: 0;
|
|
193
|
+
}
|
|
179
194
|
/* The focus ring rides `.qm-focus-ring` on the trigger (controls.css). */
|
|
180
195
|
/* Shown-never-written: the closed control reads muted while unset, matching the
|
|
181
196
|
ghosted placeholder the text/number controls show. */
|
|
@@ -260,7 +260,7 @@
|
|
|
260
260
|
display: grid;
|
|
261
261
|
grid-template-columns: repeat(var(--cols), 1fr);
|
|
262
262
|
gap: var(--_qm-space-2);
|
|
263
|
-
border-inline-start: var(--_qm-
|
|
263
|
+
border-inline-start: var(--_qm-vertical-width) solid var(--_qm-border);
|
|
264
264
|
padding-inline-start: var(--_qm-nest);
|
|
265
265
|
padding-block: var(--_qm-space);
|
|
266
266
|
}
|