@quillmark/svelte 0.26.2 → 0.28.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/NOTICE +4 -4
- package/README.md +26 -8
- package/THEMING.md +40 -4
- package/dist/core/address.d.ts +35 -64
- package/dist/core/address.js +35 -55
- package/dist/core/bloom.d.ts +4 -10
- package/dist/core/bloom.js +14 -27
- package/dist/core/codec/blocks.d.ts +41 -43
- package/dist/core/codec/blocks.js +97 -130
- package/dist/core/codec/code.d.ts +7 -10
- package/dist/core/codec/code.js +37 -46
- package/dist/core/codec/decode.d.ts +19 -23
- package/dist/core/codec/decode.js +76 -111
- package/dist/core/codec/encode.d.ts +11 -15
- package/dist/core/codec/encode.js +60 -67
- package/dist/core/codec/field.d.ts +62 -95
- package/dist/core/codec/field.js +107 -179
- package/dist/core/codec/islands.d.ts +33 -41
- package/dist/core/codec/islands.js +76 -86
- package/dist/core/codec/lists.d.ts +17 -26
- package/dist/core/codec/lists.js +117 -157
- package/dist/core/codec/marks.d.ts +4 -3
- package/dist/core/codec/marks.js +8 -3
- package/dist/core/codec/prose.css +84 -46
- package/dist/core/codec/schema.d.ts +18 -23
- package/dist/core/codec/schema.js +84 -76
- package/dist/core/codec/slash.d.ts +42 -48
- package/dist/core/codec/slash.js +0 -0
- package/dist/core/codec/table-copy.d.ts +4 -3
- package/dist/core/codec/table-copy.js +25 -7
- package/dist/core/codec/table-view.d.ts +12 -12
- package/dist/core/codec/table-view.js +460 -383
- package/dist/core/codec/table.d.ts +94 -61
- package/dist/core/codec/table.js +331 -83
- package/dist/core/codec/tasks.d.ts +6 -0
- package/dist/core/codec/tasks.js +30 -3
- package/dist/core/errors.d.ts +15 -30
- package/dist/core/errors.js +2 -6
- package/dist/core/index.js +7 -12
- package/dist/core/lifecycle.d.ts +7 -16
- package/dist/core/lifecycle.js +10 -21
- package/dist/core/rebind.svelte.d.ts +10 -20
- package/dist/core/rebind.svelte.js +15 -27
- package/dist/core/rungs.d.ts +14 -11
- package/dist/core/rungs.js +17 -19
- package/dist/core/teardown.d.ts +4 -6
- package/dist/core/teardown.js +7 -10
- package/dist/core/theme.css +74 -54
- package/dist/index.js +3 -8
- package/dist/preset/app.css +71 -0
- package/dist/preset/index.css +8 -7
- package/dist/preset/recipes.css +19 -164
- package/dist/preset/scale.css +28 -18
- package/dist/preset/shell.css +157 -0
- package/dist/preview/Preview.svelte +8 -0
- package/dist/preview/bridge.d.ts +18 -22
- package/dist/preview/bridge.js +29 -58
- package/dist/preview/controller.d.ts +21 -29
- package/dist/preview/controller.js +14 -39
- package/dist/preview/geometry.d.ts +13 -34
- package/dist/preview/geometry.js +13 -34
- package/dist/preview/index.js +4 -16
- package/dist/preview/paint.d.ts +3 -3
- package/dist/preview/paint.js +28 -68
- package/dist/preview/strings.d.ts +1 -1
- package/dist/preview/strings.js +2 -14
- package/dist/preview/viewport.js +20 -15
- package/dist/preview/zoom.d.ts +21 -22
- package/dist/preview/zoom.js +53 -30
- package/dist/visual/ArrayField.svelte +77 -16
- package/dist/visual/BooleanField.svelte +28 -15
- package/dist/visual/Card.svelte +66 -51
- package/dist/visual/CardControls.svelte +5 -11
- package/dist/visual/DateField.svelte +20 -4
- package/dist/visual/EnumField.svelte +3 -0
- package/dist/visual/FieldHint.svelte +2 -1
- package/dist/visual/FormatPopover.svelte +82 -15
- package/dist/visual/FormatPopover.svelte.d.ts +4 -0
- package/dist/visual/MatrixAdd.svelte +30 -12
- package/dist/visual/MatrixField.svelte +24 -16
- package/dist/visual/NumberField.svelte +7 -0
- package/dist/visual/ObjectField.svelte +4 -4
- package/dist/visual/ProseField.svelte +0 -5
- package/dist/visual/TipsCard.svelte +11 -10
- package/dist/visual/VariantField.svelte +3 -1
- package/dist/visual/VisualEditorInner.svelte +19 -3
- package/dist/visual/controls.css +7 -2
- package/dist/visual/fonts.d.ts +18 -0
- package/dist/visual/fonts.js +59 -0
- package/dist/visual/icons/nodes.d.ts +0 -5
- package/dist/visual/icons/nodes.js +0 -4
- package/dist/visual/props.d.ts +14 -1
- package/dist/visual/strings.d.ts +7 -0
- package/dist/visual/strings.js +5 -2
- package/dist/visual/structure.d.ts +43 -87
- package/dist/visual/structure.js +42 -83
- package/package.json +2 -2
package/dist/preview/zoom.js
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
|
-
/** CSS px per PDF pt at 100%: 96 CSS px to the inch over 72 pt
|
|
2
|
-
* size. */
|
|
1
|
+
/** CSS px per PDF pt at 100%: 96 CSS px to the inch over 72 pt. */
|
|
3
2
|
export const ACTUAL = 96 / 72;
|
|
4
|
-
/** The default never draws a page larger than this
|
|
5
|
-
* size print would recognise rather than one stretched to the pane. */
|
|
3
|
+
/** The default never draws a page larger than this. */
|
|
6
4
|
const FIT_CAP = 1.25;
|
|
7
5
|
/** The furthest a gesture zooms in. */
|
|
8
6
|
export const MAX_ZOOM = 2;
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
7
|
+
/**
|
|
8
|
+
* How much of the pane the page still fills, on its limiting axis, at the furthest a gesture zooms
|
|
9
|
+
* out.
|
|
10
|
+
*/
|
|
11
11
|
const FLOOR_FILL = 0.9;
|
|
12
|
-
/** What `Mod +` and `Mod −` step through
|
|
13
|
-
* keys land on, so a step lands somewhere a reader has been before. */
|
|
12
|
+
/** What `Mod +` and `Mod −` step through. */
|
|
14
13
|
export const STEPS = [0.67, 0.75, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2];
|
|
15
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* How far, as a log of the zoom's ratio to a hold, a continuous gesture carries past it before it
|
|
16
|
+
* leaves it.
|
|
17
|
+
*/
|
|
16
18
|
const DETENT = 0.04;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
const near = (zoom, hold) => Math.abs(Math.log(zoom / hold)) <= DETENT;
|
|
20
|
+
/** The default: the widest page fits the pane's width, capped at {@link FIT_CAP}. */
|
|
19
21
|
export function fitZoom(pages, pane) {
|
|
20
22
|
const widest = Math.max(...pages.map((p) => p.widthPt));
|
|
21
23
|
return Math.min(pane.width / (widest * ACTUAL), FIT_CAP);
|
|
22
24
|
}
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
25
|
+
/**
|
|
26
|
+
* The furthest a gesture zooms out: every page fits the pane whole with {@link FLOOR_FILL} filled
|
|
27
|
+
* on its limiting axis, and never past the default.
|
|
28
|
+
*/
|
|
26
29
|
export function floorZoom(pages, pane) {
|
|
27
30
|
const whole = Math.min(...pages.map((p) => Math.min(pane.width / p.widthPt, pane.height / p.heightPt)));
|
|
28
31
|
return Math.min((FLOOR_FILL * whole) / ACTUAL, fitZoom(pages, pane));
|
|
@@ -30,23 +33,43 @@ export function floorZoom(pages, pane) {
|
|
|
30
33
|
export function clampZoom(zoom, floor) {
|
|
31
34
|
return Math.min(MAX_ZOOM, Math.max(floor, zoom));
|
|
32
35
|
}
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/**
|
|
37
|
+
* The levels a step lands on: `STEPS` with `fit` replacing any level within {@link DETENT} of it.
|
|
38
|
+
*/
|
|
39
|
+
function stops(fit) {
|
|
40
|
+
return [...STEPS.filter((s) => !near(s, fit)), fit].sort((a, b) => a - b);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The next level in `dir` from `zoom`, skipping levels under `floor`; past the last, the floor.
|
|
44
|
+
* Half a percent of slack, so a zoom shown as a level is at it.
|
|
45
|
+
*/
|
|
46
|
+
export function stepZoom(zoom, dir, floor, fit) {
|
|
47
|
+
const levels = stops(fit);
|
|
38
48
|
const next = dir > 0
|
|
39
|
-
?
|
|
40
|
-
:
|
|
49
|
+
? levels.find((s) => s > zoom + 0.005)
|
|
50
|
+
: levels.reverse().find((s) => s < zoom - 0.005 && s >= floor);
|
|
41
51
|
return clampZoom(next ?? (dir > 0 ? MAX_ZOOM : floor), floor);
|
|
42
52
|
}
|
|
43
|
-
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
53
|
+
/**
|
|
54
|
+
* One event of a continuous gesture, carrying the run to `raw`. It stops at the first of `fit` or
|
|
55
|
+
* 100% it crosses, and leaves a hold once carried {@link DETENT} past. 100% gives way to a `fit`
|
|
56
|
+
* within the detent of it.
|
|
57
|
+
*/
|
|
58
|
+
export function glide(run, raw, floor, fit) {
|
|
59
|
+
const holds = near(fit, 1) ? [fit] : [fit, 1];
|
|
60
|
+
const step = Math.log(raw / run.raw);
|
|
61
|
+
let next;
|
|
62
|
+
if (holds.includes(run.shown)) {
|
|
63
|
+
const slack = run.slack + step;
|
|
64
|
+
if (Math.abs(slack) <= DETENT)
|
|
65
|
+
return { raw, shown: run.shown, slack };
|
|
66
|
+
next = run.shown * Math.exp(slack - Math.sign(slack) * DETENT);
|
|
67
|
+
}
|
|
68
|
+
else
|
|
69
|
+
next = run.shown * Math.exp(step);
|
|
70
|
+
next = clampZoom(next, floor);
|
|
71
|
+
const crossed = holds
|
|
72
|
+
.filter((t) => (run.shown - t) * (next - t) < 0)
|
|
73
|
+
.sort((a, b) => Math.abs(a - run.shown) - Math.abs(b - run.shown));
|
|
74
|
+
return { raw, shown: crossed[0] ?? next, slack: 0 };
|
|
52
75
|
}
|
|
@@ -652,6 +652,27 @@
|
|
|
652
652
|
if (!drag?.engaged || drag.drop !== k || drag.drop === drag.k) return undefined;
|
|
653
653
|
return drag.drop > drag.k ? 'after' : 'before';
|
|
654
654
|
}
|
|
655
|
+
|
|
656
|
+
/** Whether cells stand past the table scroller's end edge, under the pinned remove,
|
|
657
|
+
* which is what the fade before the remove draws on. */
|
|
658
|
+
let tableOverflow = $state(false);
|
|
659
|
+
/** The head row spans every track, so its box is the content width a new row or a
|
|
660
|
+
* longer cell changes. */
|
|
661
|
+
function trackOverflow(el: HTMLElement): () => void {
|
|
662
|
+
const measure = () => {
|
|
663
|
+
tableOverflow = el.scrollWidth - el.clientWidth - Math.abs(el.scrollLeft) > 1;
|
|
664
|
+
};
|
|
665
|
+
measure();
|
|
666
|
+
el.addEventListener('scroll', measure, { passive: true });
|
|
667
|
+
const observer =
|
|
668
|
+
typeof ResizeObserver === 'undefined' ? undefined : new ResizeObserver(measure);
|
|
669
|
+
observer?.observe(el);
|
|
670
|
+
if (el.firstElementChild) observer?.observe(el.firstElementChild);
|
|
671
|
+
return () => {
|
|
672
|
+
el.removeEventListener('scroll', measure);
|
|
673
|
+
observer?.disconnect();
|
|
674
|
+
};
|
|
675
|
+
}
|
|
655
676
|
</script>
|
|
656
677
|
|
|
657
678
|
<div
|
|
@@ -691,7 +712,12 @@
|
|
|
691
712
|
data-default={defaulted ? '' : undefined}
|
|
692
713
|
bind:this={rowsEl}
|
|
693
714
|
>
|
|
694
|
-
<div
|
|
715
|
+
<div
|
|
716
|
+
class="qm-array-table-scroller"
|
|
717
|
+
data-overflow={tableOverflow ? '' : undefined}
|
|
718
|
+
style:--table-cols={columns.length}
|
|
719
|
+
{@attach trackOverflow}
|
|
720
|
+
>
|
|
695
721
|
<!-- The header names the columns, so a cell carries no label of its own: its
|
|
696
722
|
accessible name is the row's and the column's composed (`ObjectField`). The
|
|
697
723
|
guidance affordance rides the header, being the column's rather than any
|
|
@@ -1072,27 +1098,21 @@
|
|
|
1072
1098
|
The distance inside an open row is the list's own, and the row then takes a rung of
|
|
1073
1099
|
air from the rows either side of it, so what it unfolded is nearer the summary it
|
|
1074
1100
|
hangs off than the sibling below. The wider distance on the inside is the inversion:
|
|
1075
|
-
a subform reading as the next row's preamble. A variant
|
|
1076
|
-
|
|
1077
|
-
|
|
1101
|
+
a subform reading as the next row's preamble. A variant takes the same distance and
|
|
1102
|
+
needs no such guard, a field carrying one control and no siblings for its cells to
|
|
1103
|
+
drift toward. */
|
|
1078
1104
|
.qm-element {
|
|
1079
1105
|
row-gap: var(--_qm-space);
|
|
1080
1106
|
}
|
|
1081
1107
|
.qm-element.open {
|
|
1082
1108
|
margin-block: var(--_qm-space);
|
|
1083
1109
|
}
|
|
1084
|
-
/*
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
stands outboard of the chevron that opened it. A variant's cells and a matrix's
|
|
1091
|
-
columns keep the flush edge: those sit beside the discriminant and the tick they are
|
|
1092
|
-
stored with, not under them. */
|
|
1093
|
-
.qm-element > :global(.qm-object) {
|
|
1094
|
-
margin-inline-start: var(--_qm-nest);
|
|
1095
|
-
}
|
|
1110
|
+
/* No inline distance: the subform's vertical hangs at the row's edge, under the
|
|
1111
|
+
summary that unfolded it, as a variant's cells hang at the discriminant's and a
|
|
1112
|
+
matrix's columns at the tick's. Every vertical hangs at the edge of the control it
|
|
1113
|
+
unfolds from, so a step in is one nesting distance wherever it is read, the
|
|
1114
|
+
`--_qm-nest` the subform keeps from its own stroke (ARCHITECTURE §"A plane is a
|
|
1115
|
+
tone"); a second rung here read as a deeper level than the object the row holds. */
|
|
1096
1116
|
/* The slabs measure the head, not the row: an open element is the head plus
|
|
1097
1117
|
everything it unfolded, and the controls belong to the line they sit on rather than
|
|
1098
1118
|
to all of that. Positioned, so `.qm-row-actions` anchors here. */
|
|
@@ -1123,6 +1143,7 @@
|
|
|
1123
1143
|
.qm-element-summary :global(.qm-el-chevron) {
|
|
1124
1144
|
flex-shrink: 0;
|
|
1125
1145
|
display: block;
|
|
1146
|
+
stroke-width: var(--_qm-glyph-stroke);
|
|
1126
1147
|
color: var(--_qm-ink-label);
|
|
1127
1148
|
transform: rotate(0deg);
|
|
1128
1149
|
transform-origin: center;
|
|
@@ -1130,6 +1151,9 @@
|
|
|
1130
1151
|
transform var(--_qm-duration-slow) var(--_qm-ease-reverse),
|
|
1131
1152
|
color var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
1132
1153
|
}
|
|
1154
|
+
.qm-element-summary :global(.qm-el-chevron > *) {
|
|
1155
|
+
vector-effect: non-scaling-stroke;
|
|
1156
|
+
}
|
|
1133
1157
|
.qm-element-summary:hover :global(.qm-el-chevron),
|
|
1134
1158
|
.qm-element.open > .qm-element-head :global(.qm-el-chevron) {
|
|
1135
1159
|
color: var(--_qm-ink);
|
|
@@ -1251,6 +1275,28 @@
|
|
|
1251
1275
|
background: var(--_qm-surface);
|
|
1252
1276
|
box-shadow: 0 0 0 var(--_qm-ring-reach) var(--_qm-surface);
|
|
1253
1277
|
}
|
|
1278
|
+
/* While cells stand past the end edge, the slab fades them in over a step before it
|
|
1279
|
+
rather than cutting them at its edge: the column the cut hides is still there. */
|
|
1280
|
+
.qm-array-table-row > .qm-remove::before,
|
|
1281
|
+
.qm-array-table-head-end::before {
|
|
1282
|
+
content: '';
|
|
1283
|
+
position: absolute;
|
|
1284
|
+
inset-block: calc(-1 * var(--_qm-ring-reach));
|
|
1285
|
+
inset-inline-end: calc(100% + var(--_qm-ring-reach));
|
|
1286
|
+
width: var(--_qm-space-3);
|
|
1287
|
+
background: linear-gradient(to right, transparent, var(--_qm-surface));
|
|
1288
|
+
pointer-events: none;
|
|
1289
|
+
opacity: 0;
|
|
1290
|
+
transition: opacity var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
1291
|
+
}
|
|
1292
|
+
.qm-array-table-row > .qm-remove:dir(rtl)::before,
|
|
1293
|
+
.qm-array-table-head-end:dir(rtl)::before {
|
|
1294
|
+
background: linear-gradient(to left, transparent, var(--_qm-surface));
|
|
1295
|
+
}
|
|
1296
|
+
.qm-array-table-scroller[data-overflow] > .qm-array-table-row > .qm-remove::before,
|
|
1297
|
+
.qm-array-table-scroller[data-overflow] .qm-array-table-head-end::before {
|
|
1298
|
+
opacity: 1;
|
|
1299
|
+
}
|
|
1254
1300
|
/* The glyph stands on the row's first line with the cells, however far the slab
|
|
1255
1301
|
stretches under it. */
|
|
1256
1302
|
.qm-array-table-row > .qm-remove {
|
|
@@ -1261,6 +1307,17 @@
|
|
|
1261
1307
|
border-radius: var(--_qm-radius-inner);
|
|
1262
1308
|
color: var(--_qm-ink-label);
|
|
1263
1309
|
}
|
|
1310
|
+
/* The pad past the slab, flat: the plane's spread above rounds with the slab's
|
|
1311
|
+
corners, and a cell scrolled under them shows through at the clip. */
|
|
1312
|
+
.qm-array-table-row > .qm-remove::after,
|
|
1313
|
+
.qm-array-table-head-end::after {
|
|
1314
|
+
content: '';
|
|
1315
|
+
position: absolute;
|
|
1316
|
+
inset-block: calc(-1 * var(--_qm-ring-reach));
|
|
1317
|
+
inset-inline-start: 100%;
|
|
1318
|
+
width: var(--_qm-ring-reach);
|
|
1319
|
+
background: var(--_qm-surface);
|
|
1320
|
+
}
|
|
1264
1321
|
/* The label line's own type: size, weight and leading are `.qm-field-label`'s, so
|
|
1265
1322
|
the two read as one register. Inner radius: the chip family is the card's, and this
|
|
1266
1323
|
box is a line of type. The chip's tap floor is given back (`.qm-tap-floor`),
|
|
@@ -1273,6 +1330,10 @@
|
|
|
1273
1330
|
line-height: var(--_qm-leading-tight);
|
|
1274
1331
|
border-radius: var(--_qm-radius-inner);
|
|
1275
1332
|
opacity: var(--_qm-opacity-idle);
|
|
1333
|
+
transition:
|
|
1334
|
+
opacity var(--_qm-duration-fast) var(--_qm-ease-reverse),
|
|
1335
|
+
background-color var(--_qm-duration-fast) var(--_qm-ease-reverse),
|
|
1336
|
+
color var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
1276
1337
|
}
|
|
1277
1338
|
.qm-add-el:hover:not(:disabled),
|
|
1278
1339
|
.qm-add-el:focus-visible {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
<!-- Not `.qm-switch`: that name is the preset's pane band, whose narrow-viewport rule
|
|
54
54
|
stands every child of it at `--qmh-tap`, so a track wearing it grows a thumb three times
|
|
55
|
-
its own height over the labels around it (`preset/
|
|
55
|
+
its own height over the labels around it (`preset/shell.css`, held by `check:style`). -->
|
|
56
56
|
<span
|
|
57
57
|
class="qm-toggle-wrap"
|
|
58
58
|
data-unset={unset ? '' : undefined}
|
|
@@ -78,8 +78,13 @@
|
|
|
78
78
|
|
|
79
79
|
<style>
|
|
80
80
|
/* The switch stands on the first line of the boxes beside it: a row of controls tops
|
|
81
|
-
out together, and a mark carries no line of its own to sit on.
|
|
81
|
+
out together, and a mark carries no line of its own to sit on.
|
|
82
|
+
|
|
83
|
+
One length, the thumb's, and the track is two of them around the inset: the travel
|
|
84
|
+
is a thumb's width at every setting of `--qm-space`, where a track sized apart from
|
|
85
|
+
its inset ran the checked thumb into the end cap as the inset grew. */
|
|
82
86
|
.qm-toggle-wrap {
|
|
87
|
+
--thumb: 0.75rem;
|
|
83
88
|
display: flex;
|
|
84
89
|
align-items: center;
|
|
85
90
|
min-height: var(--_qm-box-line);
|
|
@@ -94,15 +99,17 @@
|
|
|
94
99
|
.qm-toggle-wrap :global(.qm-toggle) {
|
|
95
100
|
display: inline-flex;
|
|
96
101
|
align-items: center;
|
|
97
|
-
width:
|
|
98
|
-
height:
|
|
102
|
+
width: calc(2 * var(--thumb) + 2 * var(--_qm-space-half));
|
|
103
|
+
height: calc(var(--thumb) + 2 * var(--_qm-space-half));
|
|
99
104
|
padding: var(--_qm-space-half);
|
|
100
105
|
border: none;
|
|
101
106
|
border-radius: var(--_qm-radius-pill);
|
|
102
107
|
/* The track is a fill and nothing else: an edge around it would be a second
|
|
103
108
|
statement of a shape the tone already draws, and the checked state swaps that
|
|
104
|
-
one fill for the accent (ARCHITECTURE §Styling).
|
|
105
|
-
|
|
109
|
+
one fill for the accent (ARCHITECTURE §Styling). The fill is the band, a
|
|
110
|
+
resting tone: on the hover rung an off switch read as a button under the
|
|
111
|
+
pointer. */
|
|
112
|
+
background: var(--_qm-surface-band);
|
|
106
113
|
cursor: pointer;
|
|
107
114
|
transition: background var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
108
115
|
}
|
|
@@ -113,21 +120,27 @@
|
|
|
113
120
|
background: var(--_qm-accent-default);
|
|
114
121
|
}
|
|
115
122
|
/* The focus ring rides `.qm-focus-ring` on the switch (controls.css). */
|
|
116
|
-
/* The thumb reads on tone alone
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
/* The thumb reads on tone alone, and its tone is what says where it stands: the
|
|
124
|
+
label rung over the track's band unchecked, and the base surface over
|
|
125
|
+
`--_qm-accent` checked. The card's own tone over the band is a step of about
|
|
126
|
+
1.3:1 in light and 1.5:1 in dark, a state indicator under WCAG 1.4.11's 3:1; the
|
|
127
|
+
label rung clears it at both, at 4.6:1 and 3.3:1. No edge of its own: a hairline
|
|
128
|
+
around a 12px pill is a second box inside the track's. */
|
|
119
129
|
.qm-toggle-wrap :global(.qm-toggle-thumb) {
|
|
120
|
-
width:
|
|
121
|
-
height:
|
|
130
|
+
width: var(--thumb);
|
|
131
|
+
height: var(--thumb);
|
|
122
132
|
border-radius: var(--_qm-radius-pill);
|
|
123
|
-
background: var(--_qm-
|
|
124
|
-
transition:
|
|
133
|
+
background: var(--_qm-ink-label);
|
|
134
|
+
transition:
|
|
135
|
+
translate var(--_qm-duration-fast) var(--_qm-ease-reverse),
|
|
136
|
+
background var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
125
137
|
}
|
|
126
138
|
.qm-toggle-wrap :global(.qm-toggle-thumb[data-state='checked']) {
|
|
127
|
-
translate:
|
|
139
|
+
translate: var(--thumb);
|
|
140
|
+
background: var(--_qm-surface);
|
|
128
141
|
}
|
|
129
142
|
/* Unset, the thumb stands at neither end: halfway along its travel. */
|
|
130
143
|
.qm-toggle-wrap[data-unset] :global(.qm-toggle-thumb) {
|
|
131
|
-
translate:
|
|
144
|
+
translate: calc(var(--thumb) / 2);
|
|
132
145
|
}
|
|
133
146
|
</style>
|
package/dist/visual/Card.svelte
CHANGED
|
@@ -122,7 +122,8 @@
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/** Not `--_qm-glyph-control`: that rung centres a glyph in a tap target, where this
|
|
125
|
-
* one stands in a run of text at the body rung and is sized to sit level with it.
|
|
125
|
+
* one stands in a run of text at the body rung and is sized to sit level with it. It
|
|
126
|
+
* strokes at the control glyph's weight (`--_qm-glyph-stroke`). */
|
|
126
127
|
const CHEVRON = 16;
|
|
127
128
|
|
|
128
129
|
const ungrouped = $derived(card.sections.filter((s) => s.group == null));
|
|
@@ -211,12 +212,14 @@
|
|
|
211
212
|
</script>
|
|
212
213
|
|
|
213
214
|
<!-- `data-qm-card` names the island for a floating surface that has to stay inside one
|
|
214
|
-
({@link FieldHint})
|
|
215
|
-
ancestor and nothing else marks the box its guidance belongs in.
|
|
215
|
+
({@link FieldHint}): a surface portals to `[data-qm-root]`, so the card is not its
|
|
216
|
+
ancestor and nothing else marks the box its guidance belongs in. Its value is the card's
|
|
217
|
+
kind, `main` for the main card, which is the one schema-named hook a host's stylesheet
|
|
218
|
+
is promised (THEMING §"What a stylesheet may aim at"). -->
|
|
216
219
|
<section
|
|
217
220
|
bind:this={el}
|
|
218
221
|
class={['qm-card', { 'qm-main': card.isMain, 'qm-unschemable': card.unschemable }]}
|
|
219
|
-
data-qm-card
|
|
222
|
+
data-qm-card={card.kind}
|
|
220
223
|
>
|
|
221
224
|
{#if card.unschemable}
|
|
222
225
|
{@render recoveryShell()}
|
|
@@ -426,15 +429,18 @@
|
|
|
426
429
|
{/snippet}
|
|
427
430
|
|
|
428
431
|
<style>
|
|
429
|
-
/* An island: the card is the base plane
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
half stands a step tighter
|
|
434
|
-
|
|
435
|
-
further for it.
|
|
432
|
+
/* An island: the card is the base plane and the column behind it the sunken one, and
|
|
433
|
+
the step between the two tones is the whole of its edge (ARCHITECTURE §"A plane is a
|
|
434
|
+
tone"). The block half is the same rung as the gap: the payload stands that far off
|
|
435
|
+
the card's edge and off the body below it. The header insets itself to the
|
|
436
|
+
section-heading column (`.qm-card-header`). The inline half stands a step tighter
|
|
437
|
+
than the nesting rung the verticals inside keep: this edge closes the block rather
|
|
438
|
+
than nesting one, and the rules it holds run that much further for it.
|
|
439
|
+
|
|
440
|
+
Inside that inset is the card's one text column (`--_qm-column`): the title, a
|
|
441
|
+
top-level field, the body's lines, the recovery note and the delete glyph's far edge
|
|
442
|
+
all stand on it, so the card reads as one measure at both ends. */
|
|
436
443
|
.qm-card {
|
|
437
|
-
border: var(--_qm-border-width) solid var(--_qm-border);
|
|
438
444
|
border-radius: var(--_qm-radius);
|
|
439
445
|
padding: var(--_qm-space-3) var(--_qm-space-2);
|
|
440
446
|
background: var(--_qm-surface);
|
|
@@ -461,10 +467,9 @@
|
|
|
461
467
|
align-items: center;
|
|
462
468
|
justify-content: space-between;
|
|
463
469
|
gap: var(--_qm-space-2);
|
|
464
|
-
/*
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
padding-inline: calc(var(--_qm-space) + var(--_qm-vertical-width));
|
|
470
|
+
/* On the column, inline only. The block pull is half the card's block inset,
|
|
471
|
+
so the header sits that far off the card's edge and the rule. */
|
|
472
|
+
padding-inline: var(--_qm-column);
|
|
468
473
|
margin-block: calc(-1 * var(--_qm-space-3) / 2);
|
|
469
474
|
}
|
|
470
475
|
.qm-card-header-right {
|
|
@@ -498,9 +503,9 @@
|
|
|
498
503
|
overlaid input tracks its content width. Both carry the same nothing for a box model
|
|
499
504
|
and inherit the same type tokens, which is what makes the two measure alike.
|
|
500
505
|
|
|
501
|
-
The type is the group header's
|
|
502
|
-
|
|
503
|
-
|
|
506
|
+
The type is the group header's: a card title and a section header both name a block
|
|
507
|
+
of fields, so they read as one register and what ranks them is position rather than
|
|
508
|
+
a size step. */
|
|
504
509
|
.qm-card-title-sizer {
|
|
505
510
|
display: inline-grid;
|
|
506
511
|
align-items: center;
|
|
@@ -519,9 +524,9 @@
|
|
|
519
524
|
min-width: 2ch;
|
|
520
525
|
}
|
|
521
526
|
/* The title draws no box in any state: a card title is already the one line that
|
|
522
|
-
reads as the card's name, and the region's `cursor: text`
|
|
523
|
-
|
|
524
|
-
|
|
527
|
+
reads as the card's name, and the region's `cursor: text` answers the pointer. A
|
|
528
|
+
typed title is a value, at the ink, and the automatic one a placeholder, at label
|
|
529
|
+
ink, as in every field. */
|
|
525
530
|
.qm-card-title {
|
|
526
531
|
grid-area: 1 / 1;
|
|
527
532
|
width: 100%;
|
|
@@ -529,18 +534,12 @@
|
|
|
529
534
|
font: inherit;
|
|
530
535
|
/* `font: inherit` does not carry colour: a form control defaults to the UA's own
|
|
531
536
|
text colour rather than the header's ink without this line. */
|
|
532
|
-
color: var(--_qm-ink
|
|
537
|
+
color: var(--_qm-ink);
|
|
533
538
|
border: none;
|
|
534
539
|
padding: 0;
|
|
535
540
|
background: transparent;
|
|
536
|
-
transition: color var(--_qm-duration-fast) var(--_qm-ease-reverse);
|
|
537
541
|
}
|
|
538
|
-
.
|
|
539
|
-
.qm-card-title:focus {
|
|
540
|
-
color: var(--_qm-ink);
|
|
541
|
-
}
|
|
542
|
-
/* The automatic title, at the label ink every placeholder takes rather than the UA's
|
|
543
|
-
grey, and steady under focus, where a typed title steps to the ink. */
|
|
542
|
+
/* The label ink every placeholder takes rather than the UA's grey. */
|
|
544
543
|
.qm-card-title::placeholder {
|
|
545
544
|
color: var(--_qm-ink-label);
|
|
546
545
|
opacity: 1;
|
|
@@ -589,11 +588,10 @@
|
|
|
589
588
|
vertical those two edges hold.
|
|
590
589
|
|
|
591
590
|
No stroke: a vertical would mark a section a reader has no name for. The start
|
|
592
|
-
inset is the
|
|
593
|
-
off it — so a top-level field and a section heading share a column. */
|
|
591
|
+
inset is the column, so a top-level field and a section heading share it. */
|
|
594
592
|
.qm-section {
|
|
595
593
|
padding-block: var(--_qm-space);
|
|
596
|
-
padding-inline-start:
|
|
594
|
+
padding-inline-start: var(--_qm-column);
|
|
597
595
|
}
|
|
598
596
|
/* Where it faces a rule, the rung a heading already stands off one. The fields
|
|
599
597
|
sit on the heading column, not the card's edge. */
|
|
@@ -620,8 +618,8 @@
|
|
|
620
618
|
}
|
|
621
619
|
/* The nesting vertical, one `--_qm-nest` in from the card's edge with the panel's
|
|
622
620
|
fields the same rung off it (ARCHITECTURE §"A plane is a tone"). `--_qm-border` and
|
|
623
|
-
not `--_qm-accent`:
|
|
624
|
-
|
|
621
|
+
not `--_qm-accent`: the verticals and the rules beside them are one stroke at every
|
|
622
|
+
depth, and a tone that changed as it went in would read as unrelated lines rather
|
|
625
623
|
than one figure. A named section is what earns it, which is why the ungrouped block
|
|
626
624
|
above draws none.
|
|
627
625
|
|
|
@@ -682,17 +680,10 @@
|
|
|
682
680
|
the accordion stops looking like structure. Size, weight and leading sit after
|
|
683
681
|
`font`, a shorthand that carries `line-height`. */
|
|
684
682
|
.qm-group-header {
|
|
685
|
-
/* Positioned, and paint is not what it buys: a group's box grows by
|
|
686
|
-
`--stroke-reach` at both ends and hands the growth back as margin, so
|
|
687
|
-
consecutive boxes overlap in a band at every seam, and a closed group's header
|
|
688
|
-
ends inside the next group's box. In flow, the later sibling takes the presses
|
|
689
|
-
that land in the band and the bottom of the header is a dead strip; positioned,
|
|
690
|
-
the control holds its own box, and the band paints the same either way. */
|
|
691
|
-
position: relative;
|
|
692
683
|
display: flex;
|
|
693
684
|
align-items: center;
|
|
694
685
|
gap: var(--_qm-space);
|
|
695
|
-
width:
|
|
686
|
+
min-width: 0;
|
|
696
687
|
border: none;
|
|
697
688
|
background: transparent;
|
|
698
689
|
padding: var(--_qm-space) 0 var(--_qm-space) var(--_qm-space);
|
|
@@ -707,17 +698,26 @@
|
|
|
707
698
|
}
|
|
708
699
|
/* The header row: the press target, and beside it the guidance marker a headed
|
|
709
700
|
section carries (`FieldHint`), which is its own control and so not inside the
|
|
710
|
-
button.
|
|
711
|
-
|
|
701
|
+
button. The button sizes to its text so the marker follows the name, and its
|
|
702
|
+
`::after` spans the row so the whole row still presses it; the marker, positioned
|
|
703
|
+
by its tap floor and later in the row, stands over that plane.
|
|
704
|
+
|
|
705
|
+
Positioned, and paint is not what it buys: a group's box grows by `--stroke-reach`
|
|
706
|
+
at both ends and hands the growth back as margin, so consecutive boxes overlap in a
|
|
707
|
+
band at every seam, and a closed group's header ends inside the next group's box.
|
|
708
|
+
In flow, the later sibling takes the presses that land in the band and the bottom
|
|
709
|
+
of the header is a dead strip; positioned, the row holds its own box, and the band
|
|
710
|
+
paints the same either way. */
|
|
712
711
|
.qm-group-head {
|
|
713
712
|
position: relative;
|
|
714
713
|
display: flex;
|
|
715
714
|
align-items: center;
|
|
716
715
|
gap: var(--_qm-space);
|
|
717
716
|
}
|
|
718
|
-
.qm-group-
|
|
719
|
-
|
|
720
|
-
|
|
717
|
+
.qm-group-header::after {
|
|
718
|
+
content: '';
|
|
719
|
+
position: absolute;
|
|
720
|
+
inset: 0;
|
|
721
721
|
}
|
|
722
722
|
/* The parked description: `FieldLabel`'s recipe, for the one description a
|
|
723
723
|
header rather than a label parks. */
|
|
@@ -732,10 +732,14 @@
|
|
|
732
732
|
.qm-group-header :global(.qm-group-chevron) {
|
|
733
733
|
flex-shrink: 0;
|
|
734
734
|
display: block;
|
|
735
|
+
stroke-width: var(--_qm-glyph-stroke);
|
|
735
736
|
transform: rotate(0deg);
|
|
736
737
|
transform-origin: center;
|
|
737
738
|
transition: transform var(--_qm-duration-slow) var(--_qm-ease-reverse);
|
|
738
739
|
}
|
|
740
|
+
.qm-group-header :global(.qm-group-chevron > *) {
|
|
741
|
+
vector-effect: non-scaling-stroke;
|
|
742
|
+
}
|
|
739
743
|
/* Open shares hover's ink step: a big borderless target draws no box, so the ink is
|
|
740
744
|
the only cue an open section keeps once the pointer leaves it. The chevron's
|
|
741
745
|
rotation is the other cue, and stays open-only below. */
|
|
@@ -790,9 +794,17 @@
|
|
|
790
794
|
flex-direction: column;
|
|
791
795
|
gap: var(--_qm-space);
|
|
792
796
|
}
|
|
793
|
-
/*
|
|
797
|
+
/* Paper's margin is the column. On the editable, so a press in the strip lands a
|
|
798
|
+
caret; carried by the leaf's inset it would be dead width. The diagnostics under the
|
|
799
|
+
body start where its lines do, as a field's start under its well. */
|
|
800
|
+
.qm-body-leaf :global(.ProseMirror),
|
|
801
|
+
.qm-body-leaf > :global(.qm-diag-list) {
|
|
802
|
+
padding-inline: var(--_qm-column);
|
|
803
|
+
}
|
|
804
|
+
/* The one card that draws an edge, and the edge is the state: a dashed stroke is the
|
|
805
|
+
placeholder idiom, which a card the schema cannot project is. */
|
|
794
806
|
.qm-card.qm-unschemable {
|
|
795
|
-
border
|
|
807
|
+
border: var(--_qm-border-width) dashed var(--_qm-border);
|
|
796
808
|
}
|
|
797
809
|
/* One typographic role with the editable title above. */
|
|
798
810
|
.qm-card-title-static {
|
|
@@ -805,6 +817,7 @@
|
|
|
805
817
|
display: flex;
|
|
806
818
|
flex-direction: column;
|
|
807
819
|
gap: var(--_qm-space-2);
|
|
820
|
+
padding-inline: var(--_qm-column);
|
|
808
821
|
}
|
|
809
822
|
.qm-recovery-note {
|
|
810
823
|
margin: 0;
|
|
@@ -818,9 +831,11 @@
|
|
|
818
831
|
font-size: var(--_qm-text-label);
|
|
819
832
|
color: var(--_qm-ink-label);
|
|
820
833
|
}
|
|
821
|
-
/* The one native control the surface draws, on the box's own recipe (controls.css).
|
|
834
|
+
/* The one native control the surface draws, on the box's own recipe (controls.css).
|
|
835
|
+
`font` carries no colour, so the value's ink is restated over the UA's. */
|
|
822
836
|
.qm-recovery-retype select {
|
|
823
837
|
font: inherit;
|
|
838
|
+
color: var(--_qm-ink);
|
|
824
839
|
border: none;
|
|
825
840
|
border-radius: var(--_qm-radius-inner);
|
|
826
841
|
background: var(--_qm-surface-well);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Right-aligned in the header: a move-up/move-down chevron pair (each disabled at its
|
|
4
4
|
edge), then delete, the row revealed while the pointer or the caret is in the card.
|
|
5
5
|
Reorder is buttons, not drag (V1). All three are chips (controls.css): fill arrives
|
|
6
|
-
on hover, and delete
|
|
6
|
+
on hover, and delete rests at the label ink beside them. The card scopes
|
|
7
7
|
that reveal, so its condition lives in the parent's CSS; the row keeps the narrower
|
|
8
8
|
guarantee here, that a focused control is a visible one.
|
|
9
9
|
-->
|
|
@@ -80,18 +80,12 @@
|
|
|
80
80
|
height: var(--_qm-tap-min);
|
|
81
81
|
padding: 0;
|
|
82
82
|
}
|
|
83
|
-
/*
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
}
|
|
83
|
+
/* The delete rests at the chip's label ink, as an array row's and a matrix member's
|
|
84
|
+
remove do, and under the pointer or the focus takes their danger tint and ink in
|
|
85
|
+
place of the chip's hover fill. */
|
|
93
86
|
.qm-card-delete:hover,
|
|
94
87
|
.qm-card-delete:focus-visible {
|
|
88
|
+
background: var(--_qm-danger-tint);
|
|
95
89
|
color: var(--_qm-danger);
|
|
96
90
|
}
|
|
97
91
|
</style>
|