vintage-frames 0.1.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/LICENSE +21 -0
- package/README.md +262 -0
- package/custom-elements.json +19807 -0
- package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js +9 -0
- package/dist/chrome.d.ts +73 -0
- package/dist/chrome.js +94 -0
- package/dist/components/vf-button-group.d.ts +50 -0
- package/dist/components/vf-button-group.js +68 -0
- package/dist/components/vf-button.d.ts +169 -0
- package/dist/components/vf-button.js +234 -0
- package/dist/components/vf-checkbox.d.ts +60 -0
- package/dist/components/vf-checkbox.js +135 -0
- package/dist/components/vf-container.d.ts +95 -0
- package/dist/components/vf-container.js +86 -0
- package/dist/components/vf-desktop.d.ts +243 -0
- package/dist/components/vf-desktop.js +310 -0
- package/dist/components/vf-dialog.d.ts +105 -0
- package/dist/components/vf-dialog.js +280 -0
- package/dist/components/vf-fieldset.d.ts +34 -0
- package/dist/components/vf-fieldset.js +76 -0
- package/dist/components/vf-grid.d.ts +124 -0
- package/dist/components/vf-grid.js +133 -0
- package/dist/components/vf-icon.d.ts +356 -0
- package/dist/components/vf-icon.js +507 -0
- package/dist/components/vf-img.d.ts +63 -0
- package/dist/components/vf-img.js +65 -0
- package/dist/components/vf-label.d.ts +117 -0
- package/dist/components/vf-label.js +151 -0
- package/dist/components/vf-list-item.d.ts +48 -0
- package/dist/components/vf-list-item.js +98 -0
- package/dist/components/vf-list.d.ts +77 -0
- package/dist/components/vf-list.js +256 -0
- package/dist/components/vf-menu-bar.d.ts +64 -0
- package/dist/components/vf-menu-bar.js +236 -0
- package/dist/components/vf-menu-item.d.ts +91 -0
- package/dist/components/vf-menu-item.js +252 -0
- package/dist/components/vf-menu.d.ts +95 -0
- package/dist/components/vf-menu.js +346 -0
- package/dist/components/vf-number-field.d.ts +60 -0
- package/dist/components/vf-number-field.js +248 -0
- package/dist/components/vf-option.d.ts +49 -0
- package/dist/components/vf-option.js +119 -0
- package/dist/components/vf-paragraph.d.ts +73 -0
- package/dist/components/vf-paragraph.js +66 -0
- package/dist/components/vf-progress-bar.d.ts +69 -0
- package/dist/components/vf-progress-bar.js +272 -0
- package/dist/components/vf-radio-group.d.ts +109 -0
- package/dist/components/vf-radio-group.js +105 -0
- package/dist/components/vf-radio.d.ts +59 -0
- package/dist/components/vf-radio.js +135 -0
- package/dist/components/vf-scroll-area.d.ts +75 -0
- package/dist/components/vf-scroll-area.js +124 -0
- package/dist/components/vf-select.d.ts +334 -0
- package/dist/components/vf-select.js +658 -0
- package/dist/components/vf-separator.d.ts +38 -0
- package/dist/components/vf-separator.js +55 -0
- package/dist/components/vf-slider.d.ts +80 -0
- package/dist/components/vf-slider.js +255 -0
- package/dist/components/vf-stack.d.ts +188 -0
- package/dist/components/vf-stack.js +168 -0
- package/dist/components/vf-swatch.d.ts +98 -0
- package/dist/components/vf-swatch.js +201 -0
- package/dist/components/vf-text-area.d.ts +77 -0
- package/dist/components/vf-text-area.js +121 -0
- package/dist/components/vf-text-field.d.ts +53 -0
- package/dist/components/vf-text-field.js +79 -0
- package/dist/components/vf-window.d.ts +157 -0
- package/dist/components/vf-window.js +377 -0
- package/dist/cursor-art.d.ts +37 -0
- package/dist/cursor-art.js +41 -0
- package/dist/cursor.d.ts +49 -0
- package/dist/cursor.js +171 -0
- package/dist/define.d.ts +45 -0
- package/dist/define.js +15 -0
- package/dist/document-listeners.d.ts +62 -0
- package/dist/document-listeners.js +29 -0
- package/dist/drag.d.ts +49 -0
- package/dist/drag.js +30 -0
- package/dist/events.d.ts +79 -0
- package/dist/events.js +25 -0
- package/dist/focus-modality.d.ts +94 -0
- package/dist/focus-modality.js +56 -0
- package/dist/form-control.d.ts +247 -0
- package/dist/form-control.js +127 -0
- package/dist/glyphs.d.ts +117 -0
- package/dist/glyphs.js +100 -0
- package/dist/grid-snap.d.ts +73 -0
- package/dist/grid-snap.js +135 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.js +71 -0
- package/dist/menu-press.d.ts +60 -0
- package/dist/menu-press.js +97 -0
- package/dist/modal-dialog.d.ts +148 -0
- package/dist/modal-dialog.js +155 -0
- package/dist/motion.d.ts +72 -0
- package/dist/motion.js +22 -0
- package/dist/number.d.ts +24 -0
- package/dist/number.js +12 -0
- package/dist/open-art.d.ts +40 -0
- package/dist/open-art.js +35 -0
- package/dist/pixel-frame.d.ts +102 -0
- package/dist/pixel-frame.js +77 -0
- package/dist/popup-overflow.d.ts +157 -0
- package/dist/popup-overflow.js +45 -0
- package/dist/position.d.ts +187 -0
- package/dist/position.js +74 -0
- package/dist/scale.d.ts +259 -0
- package/dist/scale.js +133 -0
- package/dist/scroll-rail.d.ts +112 -0
- package/dist/scroll-rail.js +194 -0
- package/dist/scroll-state.d.ts +113 -0
- package/dist/scroll-state.js +55 -0
- package/dist/size.d.ts +47 -0
- package/dist/size.js +30 -0
- package/dist/styles/base.d.ts +38 -0
- package/dist/styles/body-font.d.ts +7 -0
- package/dist/styles/body-font.js +9 -0
- package/dist/styles/display-font.d.ts +7 -0
- package/dist/styles/display-font.js +9 -0
- package/dist/styles/recipes/body-face.d.ts +14 -0
- package/dist/styles/recipes/body-face.js +6 -0
- package/dist/styles/recipes/display-face.d.ts +23 -0
- package/dist/styles/recipes/display-face.js +10 -0
- package/dist/styles/recipes/field.d.ts +20 -0
- package/dist/styles/recipes/field.js +68 -0
- package/dist/styles/recipes/focus.d.ts +76 -0
- package/dist/styles/recipes/focus.js +39 -0
- package/dist/styles/recipes/host.d.ts +5 -0
- package/dist/styles/recipes/host.js +62 -0
- package/dist/styles/recipes/pattern.d.ts +80 -0
- package/dist/styles/recipes/pattern.js +82 -0
- package/dist/styles/recipes/scroll-rail.d.ts +69 -0
- package/dist/styles/recipes/scroll-rail.js +222 -0
- package/dist/styles/recipes/shadow.d.ts +13 -0
- package/dist/styles/recipes/shadow.js +5 -0
- package/dist/styles/recipes/static-text.d.ts +24 -0
- package/dist/styles/recipes/static-text.js +17 -0
- package/dist/styles/recipes/surface.d.ts +28 -0
- package/dist/styles/recipes/surface.js +27 -0
- package/dist/styles/recipes/tile.d.ts +122 -0
- package/dist/styles/recipes/tile.js +40 -0
- package/dist/styles/recipes/title-bar.d.ts +49 -0
- package/dist/styles/recipes/title-bar.js +134 -0
- package/dist/styles/recipes/toggle.d.ts +10 -0
- package/dist/styles/recipes/toggle.js +20 -0
- package/dist/styles/register-embedded-font.d.ts +64 -0
- package/dist/styles/register-embedded-font.js +30 -0
- package/dist/text-control.d.ts +131 -0
- package/dist/text-control.js +83 -0
- package/dist/tile-grid.d.ts +111 -0
- package/dist/tile-grid.js +53 -0
- package/dist/toggle-control.d.ts +60 -0
- package/dist/toggle-control.js +41 -0
- package/dist/track-width.d.ts +42 -0
- package/dist/track-width.js +30 -0
- package/dist/type-ahead.d.ts +35 -0
- package/dist/type-ahead.js +20 -0
- package/dist/zoom.d.ts +204 -0
- package/dist/zoom.js +105 -0
- package/docs/SPEC.md +2471 -0
- package/editor/vscode.html-custom-data.json +1320 -0
- package/editor/web-types.json +3237 -0
- package/package.json +118 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { VfTextControlBase } from '../text-control.js';
|
|
2
|
+
declare const VfNumberField_base: (new (...args: any[]) => import("../position.js").VfPositionedInterface) & typeof VfTextControlBase;
|
|
3
|
+
/**
|
|
4
|
+
* `<vf-number-field>` — a System 7 numeric entry field paired with the classic
|
|
5
|
+
* "little arrows" stepper.
|
|
6
|
+
*
|
|
7
|
+
* A form-associated wrapper around a native `<input>` (white well, 1px black
|
|
8
|
+
* border) plus the pixel-exact little-arrows control from the Classic Macintosh
|
|
9
|
+
* UI Kit sprite. Clicking (or press-and-holding, with autorepeat) an arrow
|
|
10
|
+
* steps the value by `step`, clamped to `min`/`max`; the held arrow fills solid
|
|
11
|
+
* black, matching the kit's hollow→filled press convention. Keyboard focus
|
|
12
|
+
* draws the kit's dashed rule under the well alone — the stepper is beside the
|
|
13
|
+
* text, not part of it. The field is a `spinbutton`: ArrowUp/ArrowDown step, Home/End
|
|
14
|
+
* jump to min/max. The shared field skin lives in `vfField`; the value/form
|
|
15
|
+
* scaffolding in {@link VfTextControlBase}.
|
|
16
|
+
*
|
|
17
|
+
* @fires vf-input - On every keystroke. `detail: { value, valueAsNumber }`.
|
|
18
|
+
* @fires vf-change - On commit or step. `detail: { value, valueAsNumber }`.
|
|
19
|
+
* @fires input - The native event: the inner input's own on a keystroke
|
|
20
|
+
* (composed, crosses the boundary itself), dispatched from the host on a
|
|
21
|
+
* step — a native number input's spinner fires it per click too.
|
|
22
|
+
* @fires change - The native commit event, dispatched from the host on a
|
|
23
|
+
* typed commit and on every step, as a native spinner does.
|
|
24
|
+
*
|
|
25
|
+
* The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`,
|
|
26
|
+
* `maxlength`, `spellcheck`, `autocapitalize` — are forwarded from the host
|
|
27
|
+
* onto the inner input; unset, `inputmode` stays the numeric keypad's
|
|
28
|
+
* `decimal` and `autocomplete` stays `off`.
|
|
29
|
+
*
|
|
30
|
+
* @csspart input - The inner native `<input>`.
|
|
31
|
+
* @csspart stepper - The little-arrows control.
|
|
32
|
+
* @cssprop [--vf-control-height=22px] - text fields — `vf-text-field`,
|
|
33
|
+
* `vf-text-area`, the `vf-number-field` well
|
|
34
|
+
* @cssprop [--vf-number-field-width=4em] - width of `vf-number-field`'s input,
|
|
35
|
+
* in its own text (an em, not a system px length — it sizes to the digits)
|
|
36
|
+
* @cssprop [--vf-field-placeholder=#767676] - placeholder text in the editable
|
|
37
|
+
* fields — kept off `--vf-disabled`: a placeholder sits in an *enabled* well
|
|
38
|
+
* and holds AA contrast, where the disabled gray is exempt
|
|
39
|
+
*/
|
|
40
|
+
export declare class VfNumberField extends VfNumberField_base {
|
|
41
|
+
#private;
|
|
42
|
+
static styles: import("lit").CSSResult[];
|
|
43
|
+
/** Minimum allowed value (inclusive). Omit for no lower bound. */
|
|
44
|
+
min?: number;
|
|
45
|
+
/** Maximum allowed value (inclusive). Omit for no upper bound. */
|
|
46
|
+
max?: number;
|
|
47
|
+
/** Increment applied per step / arrow press. Also sets the value's precision. */
|
|
48
|
+
step: number;
|
|
49
|
+
/** Which arrow is currently held (drives the solid pressed glyph). */
|
|
50
|
+
private pressed;
|
|
51
|
+
private input;
|
|
52
|
+
disconnectedCallback(): void;
|
|
53
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
54
|
+
}
|
|
55
|
+
declare global {
|
|
56
|
+
interface HTMLElementTagNameMap {
|
|
57
|
+
'vf-number-field': VfNumberField;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { vfElement as e } from "../define.js";
|
|
2
|
+
import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
|
|
3
|
+
import { VfPositioned as n } from "../position.js";
|
|
4
|
+
import { vfBase as r } from "../styles/recipes/host.js";
|
|
5
|
+
import { vfField as i } from "../styles/recipes/field.js";
|
|
6
|
+
import { emitNative as a } from "../events.js";
|
|
7
|
+
import { STEPPER as o, STEPPER_DOWN_FILL as s, STEPPER_UP_FILL as c } from "../glyphs.js";
|
|
8
|
+
import { VfTextControlBase as l } from "../text-control.js";
|
|
9
|
+
import { decimalsOf as u } from "../number.js";
|
|
10
|
+
import { css as d, html as f, nothing as p } from "lit";
|
|
11
|
+
import { property as m, query as h, state as g } from "lit/decorators.js";
|
|
12
|
+
import { classMap as _ } from "lit/directives/class-map.js";
|
|
13
|
+
import { live as v } from "lit/directives/live.js";
|
|
14
|
+
//#region src/components/vf-number-field.ts
|
|
15
|
+
var y = class extends n(l) {
|
|
16
|
+
constructor(...e) {
|
|
17
|
+
super(...e), this.step = 1, this.pressed = null, this.#l = (e) => {
|
|
18
|
+
if (this.isDisabled || this.readonly) return;
|
|
19
|
+
let t = e.currentTarget.classList.contains("up") ? 1 : -1;
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
try {
|
|
22
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
23
|
+
} catch {}
|
|
24
|
+
this.input?.focus(), this.pressed = t > 0 ? "up" : "down", this.#a(t), this.#s(t);
|
|
25
|
+
}, this.#u = () => {
|
|
26
|
+
this.#c(), this.pressed = null;
|
|
27
|
+
}, this.#d = (e) => {
|
|
28
|
+
if (this.isSubmitEnter(e)) {
|
|
29
|
+
this.requestImplicitSubmit();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!(this.isDisabled || this.readonly) && !(e.isComposing || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) switch (e.key) {
|
|
33
|
+
case "ArrowUp":
|
|
34
|
+
e.preventDefault(), this.#a(1);
|
|
35
|
+
break;
|
|
36
|
+
case "ArrowDown":
|
|
37
|
+
e.preventDefault(), this.#a(-1);
|
|
38
|
+
break;
|
|
39
|
+
case "Home":
|
|
40
|
+
this.min != null && (e.preventDefault(), this.#i(this.min));
|
|
41
|
+
break;
|
|
42
|
+
case "End":
|
|
43
|
+
this.max != null && (e.preventDefault(), this.#i(this.max));
|
|
44
|
+
break;
|
|
45
|
+
case " ": e.preventDefault();
|
|
46
|
+
}
|
|
47
|
+
}, this.#f = (e) => {
|
|
48
|
+
this.value = e.target.value, this.#o("vf-input");
|
|
49
|
+
}, this.#p = (e) => {
|
|
50
|
+
let t = e.target.value, n = parseFloat(t);
|
|
51
|
+
this.value = Number.isNaN(n) ? t : String(Number(this.#r(n).toFixed(u(this.step)))), this.#o("vf-change"), a(this, "change");
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
static {
|
|
55
|
+
this.styles = [
|
|
56
|
+
r,
|
|
57
|
+
i,
|
|
58
|
+
d`
|
|
59
|
+
:host {
|
|
60
|
+
display: inline-flex;
|
|
61
|
+
/* NOT stretch: that sized the well to the stepper's 25px, making this
|
|
62
|
+
the one field that ignored --vf-control-height (see the input rule). */
|
|
63
|
+
align-items: flex-start;
|
|
64
|
+
gap: calc(var(--vf-scale, 1) * 3px);
|
|
65
|
+
}
|
|
66
|
+
/* The flex item is the wrapper, not the input: it boxes the well exactly,
|
|
67
|
+
so the focus rule spans the well and stops short of the stepper beside
|
|
68
|
+
it. Kept a flex container of its own so the input's own em width still
|
|
69
|
+
sets the base size — and the input still grows with it when a consumer
|
|
70
|
+
widens the host. */
|
|
71
|
+
.vf-field-well {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex: 1 1 auto;
|
|
74
|
+
/* Shrinkable, or the stepper leaves the host box: min-width's auto
|
|
75
|
+
floor here is the input's definite 4em width, so a host narrowed to
|
|
76
|
+
4em or less pushed gap + stepper past its own border box (under
|
|
77
|
+
whatever sits beside it) instead of narrowing the well. 0 hands the
|
|
78
|
+
floor to the input's own min-width. */
|
|
79
|
+
min-width: 0;
|
|
80
|
+
/* The 3px difference is odd, so centering the well would land it on a
|
|
81
|
+
half pixel and fringe at every scale. Bias it one whole pixel down
|
|
82
|
+
(1 above / 2 below) — optically centered, still on the device grid. */
|
|
83
|
+
margin-top: calc(var(--vf-scale, 1) * 1px);
|
|
84
|
+
}
|
|
85
|
+
input {
|
|
86
|
+
flex: 1 1 auto;
|
|
87
|
+
width: var(--vf-number-field-width, 4em);
|
|
88
|
+
min-width: 2em;
|
|
89
|
+
/* The reference sheets measure text fields at 22px and the little-arrows
|
|
90
|
+
sprite at 15×25 — the well and the stepper are authentically different
|
|
91
|
+
heights, so the well sits on the shared control token and the
|
|
92
|
+
sprite keeps its native 1:1 size (the host ends up stepper-tall).
|
|
93
|
+
min-, not height, as in vf-text-field: the default line box exactly
|
|
94
|
+
fills it, and a user stylesheet raising line-height grows the well
|
|
95
|
+
instead of clipping the digits. */
|
|
96
|
+
min-height: calc(var(--vf-scale, 1) * var(--vf-control-height, 22px));
|
|
97
|
+
padding: 0 calc(var(--vf-scale, 1) * 6px);
|
|
98
|
+
text-align: right;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* The little-arrows stepper, drawn at its native 15×25 (1:1, crisp). */
|
|
102
|
+
.stepper {
|
|
103
|
+
position: relative;
|
|
104
|
+
flex: none;
|
|
105
|
+
width: calc(var(--vf-scale, 1) * 15px);
|
|
106
|
+
height: calc(var(--vf-scale, 1) * 25px);
|
|
107
|
+
/* Stays solid black even when the field is disabled. */
|
|
108
|
+
color: var(--vf-black, #000);
|
|
109
|
+
cursor: var(--vf-cursor, default);
|
|
110
|
+
}
|
|
111
|
+
.stepper svg {
|
|
112
|
+
display: block;
|
|
113
|
+
width: calc(var(--vf-scale, 1) * 15px);
|
|
114
|
+
height: calc(var(--vf-scale, 1) * 25px);
|
|
115
|
+
}
|
|
116
|
+
.fill {
|
|
117
|
+
display: none;
|
|
118
|
+
}
|
|
119
|
+
.fill.on {
|
|
120
|
+
display: inline;
|
|
121
|
+
}
|
|
122
|
+
/* Transparent hit targets over the top/bottom halves. */
|
|
123
|
+
.hit {
|
|
124
|
+
position: absolute;
|
|
125
|
+
left: 0;
|
|
126
|
+
right: 0;
|
|
127
|
+
height: 50%;
|
|
128
|
+
touch-action: none;
|
|
129
|
+
}
|
|
130
|
+
.hit.up {
|
|
131
|
+
top: 0;
|
|
132
|
+
}
|
|
133
|
+
.hit.down {
|
|
134
|
+
bottom: 0;
|
|
135
|
+
}
|
|
136
|
+
:host([disabled]) .hit,
|
|
137
|
+
:host([readonly]) .hit {
|
|
138
|
+
pointer-events: none;
|
|
139
|
+
}
|
|
140
|
+
`
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
#e;
|
|
144
|
+
#t;
|
|
145
|
+
disconnectedCallback() {
|
|
146
|
+
super.disconnectedCallback(), this.#c();
|
|
147
|
+
}
|
|
148
|
+
#n() {
|
|
149
|
+
return parseFloat(this.value);
|
|
150
|
+
}
|
|
151
|
+
#r(e) {
|
|
152
|
+
return this.min != null && e < this.min ? this.min : this.max != null && e > this.max ? this.max : e;
|
|
153
|
+
}
|
|
154
|
+
#i(e) {
|
|
155
|
+
let t = String(Number(this.#r(e).toFixed(u(this.step))));
|
|
156
|
+
t !== this.value && (this.value = t, this.#o("vf-change"), a(this, "input"), a(this, "change"));
|
|
157
|
+
}
|
|
158
|
+
#a(e) {
|
|
159
|
+
if (this.isDisabled || this.readonly) return;
|
|
160
|
+
let t = this.#n(), n = Number.isNaN(t) ? this.min ?? 0 : t + e * this.step;
|
|
161
|
+
this.#i(n);
|
|
162
|
+
}
|
|
163
|
+
#o(e) {
|
|
164
|
+
this.emitValue(e, {
|
|
165
|
+
value: this.value,
|
|
166
|
+
valueAsNumber: this.#n()
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
#s(e) {
|
|
170
|
+
this.#c(), this.#e = window.setTimeout(() => {
|
|
171
|
+
this.#t = window.setInterval(() => this.#a(e), 60);
|
|
172
|
+
}, 300);
|
|
173
|
+
}
|
|
174
|
+
#c() {
|
|
175
|
+
this.#e !== void 0 && window.clearTimeout(this.#e), this.#t !== void 0 && window.clearInterval(this.#t), this.#e = this.#t = void 0;
|
|
176
|
+
}
|
|
177
|
+
#l;
|
|
178
|
+
#u;
|
|
179
|
+
#d;
|
|
180
|
+
#f;
|
|
181
|
+
#p;
|
|
182
|
+
render() {
|
|
183
|
+
let e = this.isDisabled, t = this.#n();
|
|
184
|
+
return f`
|
|
185
|
+
<div class=${this.wellClass}>
|
|
186
|
+
<input
|
|
187
|
+
part="input"
|
|
188
|
+
class="vf-field"
|
|
189
|
+
type="text"
|
|
190
|
+
inputmode=${this.getAttribute("inputmode") ?? "decimal"}
|
|
191
|
+
role="spinbutton"
|
|
192
|
+
autocomplete=${this.getAttribute("autocomplete") ?? "off"}
|
|
193
|
+
enterkeyhint=${this.forwardedAttr("enterkeyhint")}
|
|
194
|
+
maxlength=${this.forwardedAttr("maxlength")}
|
|
195
|
+
spellcheck=${this.forwardedAttr("spellcheck")}
|
|
196
|
+
autocapitalize=${this.forwardedAttr("autocapitalize")}
|
|
197
|
+
aria-label=${this.label || this.hostLabel || p}
|
|
198
|
+
aria-describedby=${this.describedBy}
|
|
199
|
+
aria-required=${this.required ? "true" : p}
|
|
200
|
+
aria-invalid=${this.validity.valid ? p : "true"}
|
|
201
|
+
aria-valuenow=${Number.isNaN(t) ? p : t}
|
|
202
|
+
aria-valuemin=${this.min ?? p}
|
|
203
|
+
aria-valuemax=${this.max ?? p}
|
|
204
|
+
.value=${v(this.value)}
|
|
205
|
+
placeholder=${this.placeholder}
|
|
206
|
+
?disabled=${e}
|
|
207
|
+
?readonly=${this.readonly}
|
|
208
|
+
@input=${this.#f}
|
|
209
|
+
@change=${this.#p}
|
|
210
|
+
@keydown=${this.#d}
|
|
211
|
+
/>
|
|
212
|
+
</div>
|
|
213
|
+
<span class="stepper vf-snap" part="stepper">
|
|
214
|
+
<svg viewBox="0 0 15 25" shape-rendering="crispEdges" fill="currentColor" aria-hidden="true">
|
|
215
|
+
<path d=${o.d}></path>
|
|
216
|
+
<path class=${_({
|
|
217
|
+
fill: !0,
|
|
218
|
+
on: this.pressed === "up"
|
|
219
|
+
})} d=${c.d}></path>
|
|
220
|
+
<path class=${_({
|
|
221
|
+
fill: !0,
|
|
222
|
+
on: this.pressed === "down"
|
|
223
|
+
})} d=${s.d}></path>
|
|
224
|
+
</svg>
|
|
225
|
+
<span
|
|
226
|
+
class="hit up"
|
|
227
|
+
aria-hidden="true"
|
|
228
|
+
@pointerdown=${this.#l}
|
|
229
|
+
@pointerup=${this.#u}
|
|
230
|
+
@pointercancel=${this.#u}
|
|
231
|
+
@lostpointercapture=${this.#u}
|
|
232
|
+
></span>
|
|
233
|
+
<span
|
|
234
|
+
class="hit down"
|
|
235
|
+
aria-hidden="true"
|
|
236
|
+
@pointerdown=${this.#l}
|
|
237
|
+
@pointerup=${this.#u}
|
|
238
|
+
@pointercancel=${this.#u}
|
|
239
|
+
@lostpointercapture=${this.#u}
|
|
240
|
+
></span>
|
|
241
|
+
</span>
|
|
242
|
+
${this.renderDescription()}
|
|
243
|
+
`;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
t([m({ type: Number })], y.prototype, "min", void 0), t([m({ type: Number })], y.prototype, "max", void 0), t([m({ type: Number })], y.prototype, "step", void 0), t([g()], y.prototype, "pressed", void 0), t([h("input")], y.prototype, "input", void 0), y = t([e("vf-number-field")], y);
|
|
247
|
+
//#endregion
|
|
248
|
+
export { y as VfNumberField };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import type { PropertyValues } from 'lit';
|
|
3
|
+
/**
|
|
4
|
+
* `<vf-option>` — a single choice inside a `<vf-select>` popup menu.
|
|
5
|
+
*
|
|
6
|
+
* A light-DOM child of `<vf-select>` (slotted into the popup panel). Renders
|
|
7
|
+
* its slotted label at menu-item metrics (16px row — the pill's content height,
|
|
8
|
+
* so a selected option overlays the closed pill exactly; the left checkmark gutter is
|
|
9
|
+
* `--vf-select-gutter`, shared with the closed control's left inset so the value
|
|
10
|
+
* doesn't shift on open). The parent select manages `selected` and the transient
|
|
11
|
+
* `active` highlight, and slots this element into its popup panel.
|
|
12
|
+
*
|
|
13
|
+
* The host carries `role="option"` with `aria-selected`/`aria-disabled`.
|
|
14
|
+
*
|
|
15
|
+
* @csspart check - The ✓ checkmark shown in the left gutter when selected.
|
|
16
|
+
* @cssprop [--vf-popup-height=18px] - `vf-select` pill (border box; its 1px
|
|
17
|
+
* hard shadow makes the sheet's 157×19 ink box)
|
|
18
|
+
* @cssprop [--vf-select-gutter=16px] - checkmark column: `vf-select` left inset
|
|
19
|
+
* / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift
|
|
20
|
+
* on open)
|
|
21
|
+
*/
|
|
22
|
+
export declare class VfOption extends LitElement {
|
|
23
|
+
#private;
|
|
24
|
+
static styles: import("lit").CSSResult[];
|
|
25
|
+
private readonly scale;
|
|
26
|
+
/**
|
|
27
|
+
* Submitted/compared value of this option. Falls back to the trimmed text
|
|
28
|
+
* content when empty (like a native `<option>`).
|
|
29
|
+
*/
|
|
30
|
+
value: string;
|
|
31
|
+
/** Disables the option: gray text, not selectable. */
|
|
32
|
+
disabled: boolean;
|
|
33
|
+
/** Whether this option is the select's current value. Managed by the parent. */
|
|
34
|
+
selected: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Transient highlight (hover / keyboard cursor) — full-row inversion.
|
|
37
|
+
* Managed by the parent `<vf-select>`; not part of the authoring API.
|
|
38
|
+
*/
|
|
39
|
+
active: boolean;
|
|
40
|
+
constructor();
|
|
41
|
+
connectedCallback(): void;
|
|
42
|
+
protected updated(changed: PropertyValues<this>): void;
|
|
43
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
44
|
+
}
|
|
45
|
+
declare global {
|
|
46
|
+
interface HTMLElementTagNameMap {
|
|
47
|
+
'vf-option': VfOption;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { vfElement as e } from "../define.js";
|
|
2
|
+
import { ScaleController as t } from "../scale.js";
|
|
3
|
+
import n from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
|
|
4
|
+
import { vfDisplay as r } from "../styles/recipes/display-face.js";
|
|
5
|
+
import { vfBase as i } from "../styles/recipes/host.js";
|
|
6
|
+
import { CHECKMARK as a, glyphSvg as o } from "../glyphs.js";
|
|
7
|
+
import { LitElement as s, css as c, html as l } from "lit";
|
|
8
|
+
import { property as u } from "lit/decorators.js";
|
|
9
|
+
//#region src/components/vf-option.ts
|
|
10
|
+
var d = class extends s {
|
|
11
|
+
static {
|
|
12
|
+
this.styles = [
|
|
13
|
+
i,
|
|
14
|
+
r,
|
|
15
|
+
c`
|
|
16
|
+
:host {
|
|
17
|
+
position: relative;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
/* Row height = the pill's CONTENT height (--vf-popup-height 18px minus
|
|
21
|
+
its two 1px borders), so the selected row's text and whitespace match
|
|
22
|
+
the closed pill exactly when the open list overlays it. Derived, not
|
|
23
|
+
a literal, so re-theming the pill height moves the rows with it. */
|
|
24
|
+
height: calc(var(--vf-scale, 1) * (var(--vf-popup-height, 18px) - 2px));
|
|
25
|
+
/* Lock the line box to the row box. vfDisplay's 1.25 line-height is
|
|
26
|
+
taller than a 16px row (20px), and align-items:center splits the
|
|
27
|
+
excess above AND below — so every row's text spilled 2px past its
|
|
28
|
+
own box and the LAST row's spill pushed the panel into overflow,
|
|
29
|
+
putting a scrollbar on every popup that should never have one. The
|
|
30
|
+
ink doesn't move: a 16px line box centred in a 16px row has the same
|
|
31
|
+
centre as a 20px one. Derived from the same expression as the height
|
|
32
|
+
above so a re-themed pill can't reintroduce the mismatch. */
|
|
33
|
+
line-height: calc(var(--vf-scale, 1) * (var(--vf-popup-height, 18px) - 2px));
|
|
34
|
+
/* Left gutter (--vf-select-gutter) holds the ✓ and matches the closed
|
|
35
|
+
vf-select control's left inset, so a selected option's text lands at
|
|
36
|
+
the same x whether the popup is closed or open. */
|
|
37
|
+
padding: 0 calc(var(--vf-scale, 1) * 20px) 0
|
|
38
|
+
calc(var(--vf-scale, 1) * var(--vf-select-gutter, 16px));
|
|
39
|
+
background: var(--vf-white, #fff);
|
|
40
|
+
color: var(--vf-black, #000);
|
|
41
|
+
white-space: nowrap;
|
|
42
|
+
cursor: var(--vf-cursor, default);
|
|
43
|
+
outline: none;
|
|
44
|
+
}
|
|
45
|
+
/* Hover/keyboard highlight — classic full-row inversion. The parent
|
|
46
|
+
vf-select drives [active] for both pointer and keyboard so only one
|
|
47
|
+
row is ever highlighted at a time. */
|
|
48
|
+
:host([active]) {
|
|
49
|
+
/* Forced colors: exempt the inverted row from the mode's text
|
|
50
|
+
backplate, which would land a Canvas slab on the highlight bar —
|
|
51
|
+
see vf-list-item's forced-colors note. The pair is already the
|
|
52
|
+
user's own via the vfBase token remap. */
|
|
53
|
+
@media (forced-colors: active) {
|
|
54
|
+
forced-color-adjust: none;
|
|
55
|
+
}
|
|
56
|
+
background: var(--vf-highlight, #000);
|
|
57
|
+
color: var(--vf-highlight-text, #fff);
|
|
58
|
+
}
|
|
59
|
+
:host([disabled]) {
|
|
60
|
+
background: var(--vf-white, #fff);
|
|
61
|
+
color: var(--vf-disabled, #c0c0c0);
|
|
62
|
+
}
|
|
63
|
+
/* The ✓ is pinned a whole 3px in from the row's left and top rather than
|
|
64
|
+
centred in the gutter. Centring a 9px glyph in the 16px column (and in
|
|
65
|
+
the 16px row) lands it on a half pixel — 3.5 authored px, i.e. 10.5
|
|
66
|
+
device px at the default 3× — which fringes at every scale. Menus.png
|
|
67
|
+
biases it up-left exactly this way: ink 3px inside the row on both
|
|
68
|
+
axes, leaving the wider gap on the right/bottom. */
|
|
69
|
+
.check {
|
|
70
|
+
position: absolute;
|
|
71
|
+
left: calc(var(--vf-scale, 1) * 3px);
|
|
72
|
+
top: calc(var(--vf-scale, 1) * 3px);
|
|
73
|
+
display: block;
|
|
74
|
+
visibility: hidden;
|
|
75
|
+
color: inherit;
|
|
76
|
+
}
|
|
77
|
+
/* Native 9×9 (1:1, crisp). */
|
|
78
|
+
.check svg {
|
|
79
|
+
display: block;
|
|
80
|
+
width: calc(var(--vf-scale, 1) * 9px);
|
|
81
|
+
height: calc(var(--vf-scale, 1) * 9px);
|
|
82
|
+
}
|
|
83
|
+
:host([selected]) .check {
|
|
84
|
+
visibility: visible;
|
|
85
|
+
}
|
|
86
|
+
`
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
#e;
|
|
90
|
+
constructor() {
|
|
91
|
+
super(), this.scale = new t(this), this.value = "", this.disabled = !1, this.selected = !1, this.active = !1, this.#e = this.attachInternals(), this.#e.role = "option";
|
|
92
|
+
}
|
|
93
|
+
connectedCallback() {
|
|
94
|
+
super.connectedCallback(), this.hasAttribute("tabindex") || this.setAttribute("tabindex", "-1");
|
|
95
|
+
}
|
|
96
|
+
updated(e) {
|
|
97
|
+
e.has("selected") && (this.#e.ariaSelected = this.selected ? "true" : "false"), e.has("disabled") && (this.#e.ariaDisabled = this.disabled ? "true" : null);
|
|
98
|
+
}
|
|
99
|
+
render() {
|
|
100
|
+
return l`
|
|
101
|
+
<span class="check" part="check" aria-hidden="true"
|
|
102
|
+
>${o(a, "checkmark")}</span
|
|
103
|
+
>
|
|
104
|
+
<slot></slot>
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
n([u()], d.prototype, "value", void 0), n([u({
|
|
109
|
+
type: Boolean,
|
|
110
|
+
reflect: !0
|
|
111
|
+
})], d.prototype, "disabled", void 0), n([u({
|
|
112
|
+
type: Boolean,
|
|
113
|
+
reflect: !0
|
|
114
|
+
})], d.prototype, "selected", void 0), n([u({
|
|
115
|
+
type: Boolean,
|
|
116
|
+
reflect: !0
|
|
117
|
+
})], d.prototype, "active", void 0), d = n([e("vf-option")], d);
|
|
118
|
+
//#endregion
|
|
119
|
+
export { d as VfOption };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
declare const VfParagraph_base: (new (...args: any[]) => import("../size.js").VfSizedInterface) & (new (...args: any[]) => import("../position.js").VfPositionedInterface) & typeof LitElement;
|
|
3
|
+
/**
|
|
4
|
+
* `<vf-paragraph>` — a paragraph of copy on the kit's body face and grid.
|
|
5
|
+
*
|
|
6
|
+
* The installer's welcome text, a dialog's explanation, an article's prose:
|
|
7
|
+
* anything a page would otherwise set with a `<p>` and hope the leading landed
|
|
8
|
+
* somewhere sensible. What it adds over that `<p>`:
|
|
9
|
+
*
|
|
10
|
+
* - the **Geneva body face** by default (`face="display"` switches to
|
|
11
|
+
* the Chicago-style chrome face);
|
|
12
|
+
* - a **whole-system-pixel line box at the face's native pitch** —
|
|
13
|
+
* `--vf-paragraph-line-height`: 12px for the body face (Geneva 9's own
|
|
14
|
+
* strike line) and 16px under `face="display"` (Chicago 12's — ascent 12 +
|
|
15
|
+
* descent 3 + leading 1, the pitch a real dialog wrapped its copy on).
|
|
16
|
+
* Whole pixels are the point of the component: line boxes are the single
|
|
17
|
+
* biggest source of off-grid layout, because a ratio resolves to whatever
|
|
18
|
+
* it resolves to (`1.65 × 17px = 28.05px`) and every line of prose nudges
|
|
19
|
+
* everything after it further off the device-pixel grid, smearing 1-bit
|
|
20
|
+
* borders and bitmap glyph stems (docs/SIZING.md rule 2). A
|
|
21
|
+
* whole-pixel line box accumulates whole offsets;
|
|
22
|
+
* - its own {@link GridSnapController}, so it holds its own origin once the
|
|
23
|
+
* page opts in with `applyGridSnap()`;
|
|
24
|
+
* - a **declared box** when the layout wants one — `width`/`height` in whole
|
|
25
|
+
* system px ({@link VfSized}). In flow a paragraph takes its container's
|
|
26
|
+
* width, which is usually right; a *placed* one (`top`/`left`) shrink-wraps
|
|
27
|
+
* its longest line instead — a fractional glyph-run width, wrapped wherever
|
|
28
|
+
* the parent's edge happens to fall — so a DITL-style layout states the
|
|
29
|
+
* measure the copy wraps to, whole and on the grid.
|
|
30
|
+
*
|
|
31
|
+
* The shadow root renders a real `<p>`, so the copy keeps paragraph semantics
|
|
32
|
+
* for assistive tech, and — unlike the kit's chrome — the text is selectable.
|
|
33
|
+
* There is no margin: per SPEC §2 a component adds nothing outside its own box,
|
|
34
|
+
* so paragraph spacing is the page's (a `gap` on the column, or a margin on the
|
|
35
|
+
* host — kept a whole number of pixels, like everything else in the contract).
|
|
36
|
+
*
|
|
37
|
+
* @slot - The paragraph copy.
|
|
38
|
+
* @csspart paragraph - The inner `<p>`.
|
|
39
|
+
* @cssprop --vf-paragraph-line-height - This paragraph kind's own line box,
|
|
40
|
+
* in system px. Unset (the default) the box follows the face tokens below;
|
|
41
|
+
* set, it overrides both faces for paragraphs alone. Keep any override a
|
|
42
|
+
* whole *even* number — a ratio is what puts a page off the grid in the
|
|
43
|
+
* first place, and half the difference to the 16px em is half-leading, so
|
|
44
|
+
* an odd value lands the baseline on a half pixel.
|
|
45
|
+
* @cssprop --vf-line-height - The body face's native line (default `12px`,
|
|
46
|
+
* Geneva 9's) — the face-level knob: retheming the body face to another
|
|
47
|
+
* strike states this alongside `--vf-font-family` / `--vf-font-size`.
|
|
48
|
+
* @cssprop --vf-line-height-display - The display face's native line
|
|
49
|
+
* (default `16px`, Chicago 12's) — the display retheme's third number.
|
|
50
|
+
*/
|
|
51
|
+
export declare class VfParagraph extends VfParagraph_base {
|
|
52
|
+
static styles: import("lit").CSSResult[];
|
|
53
|
+
/** Default-on display scaling (true 72dpi size); see src/scale.ts. */
|
|
54
|
+
private readonly scale;
|
|
55
|
+
/** Device-pixel grid snapping (opt in with applyGridSnap()); see src/grid-snap.ts. */
|
|
56
|
+
private readonly gridSnap;
|
|
57
|
+
/**
|
|
58
|
+
* Which embedded face to set the copy in — `'body'` (Geneva, the
|
|
59
|
+
* default) or `'display'` (the Chicago-style chrome face, for copy that is
|
|
60
|
+
* really dialog chrome). Applied by `vfStaticText`; declared here so it types
|
|
61
|
+
* and reflects.
|
|
62
|
+
*/
|
|
63
|
+
face?: 'display' | 'body';
|
|
64
|
+
/** Greys the copy to `--vf-disabled` — System 7's dimmed static text. */
|
|
65
|
+
dim: boolean;
|
|
66
|
+
protected render(): import("lit-html").TemplateResult<1>;
|
|
67
|
+
}
|
|
68
|
+
declare global {
|
|
69
|
+
interface HTMLElementTagNameMap {
|
|
70
|
+
'vf-paragraph': VfParagraph;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { vfElement as e } from "../define.js";
|
|
2
|
+
import { ScaleController as t } from "../scale.js";
|
|
3
|
+
import n from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
|
|
4
|
+
import { VfPositioned as r } from "../position.js";
|
|
5
|
+
import { vfBase as i } from "../styles/recipes/host.js";
|
|
6
|
+
import { vfStaticText as a } from "../styles/recipes/static-text.js";
|
|
7
|
+
import { GridSnapController as o } from "../grid-snap.js";
|
|
8
|
+
import { VfSized as s } from "../size.js";
|
|
9
|
+
import { LitElement as c, css as l, html as u } from "lit";
|
|
10
|
+
import { property as d } from "lit/decorators.js";
|
|
11
|
+
//#region src/components/vf-paragraph.ts
|
|
12
|
+
var f = class extends s(r(c)) {
|
|
13
|
+
constructor(...e) {
|
|
14
|
+
super(...e), this.scale = new t(this), this.gridSnap = new o(this), this.dim = !1;
|
|
15
|
+
}
|
|
16
|
+
static {
|
|
17
|
+
this.styles = [
|
|
18
|
+
i,
|
|
19
|
+
a,
|
|
20
|
+
l`
|
|
21
|
+
:host {
|
|
22
|
+
display: block;
|
|
23
|
+
/* The body face is already vfBase's host default (vfStaticText's
|
|
24
|
+
face="display" overrides it); what this component fixes is the line
|
|
25
|
+
box — whole system px, never a ratio, at the face's native pitch.
|
|
26
|
+
The pitch is the face's, so it reads the face token
|
|
27
|
+
(--vf-line-height, the retheme knob that travels with
|
|
28
|
+
--vf-font-family/--vf-font-size), under a per-component override.
|
|
29
|
+
12px is Geneva 9's strike line: the 16px em centers into it with
|
|
30
|
+
−2px half-leading (baseline at 10), exactly as vf-icon's name plate
|
|
31
|
+
does, and the strike's 10+2 ink fills the box edge to edge. */
|
|
32
|
+
line-height: calc(
|
|
33
|
+
var(--vf-scale, 1) *
|
|
34
|
+
var(--vf-paragraph-line-height, var(--vf-line-height, 12px))
|
|
35
|
+
);
|
|
36
|
+
/* Prose, not chrome: put back the text selection vfBase suppresses. */
|
|
37
|
+
user-select: text;
|
|
38
|
+
-webkit-user-select: text;
|
|
39
|
+
}
|
|
40
|
+
/* Chrome copy wraps on Chicago 12's native line: ascent 12 + descent 3
|
|
41
|
+
+ leading 1 = 16 — zero half-leading on the 16px em, 7 blank rows
|
|
42
|
+
between a baseline and the caps under it, as a real alert set it.
|
|
43
|
+
Same specificity as vfStaticText's face rule; this sheet is later,
|
|
44
|
+
so the pitch follows the face. */
|
|
45
|
+
:host([face='display']) {
|
|
46
|
+
line-height: calc(
|
|
47
|
+
var(--vf-scale, 1) *
|
|
48
|
+
var(--vf-paragraph-line-height, var(--vf-line-height-display, 16px))
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
p {
|
|
52
|
+
margin: 0;
|
|
53
|
+
}
|
|
54
|
+
`
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
render() {
|
|
58
|
+
return u`<p class="vf-snap" part="paragraph"><slot></slot></p>`;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
n([d({ reflect: !0 })], f.prototype, "face", void 0), n([d({
|
|
62
|
+
type: Boolean,
|
|
63
|
+
reflect: !0
|
|
64
|
+
})], f.prototype, "dim", void 0), f = n([e("vf-paragraph")], f);
|
|
65
|
+
//#endregion
|
|
66
|
+
export { f as VfParagraph };
|