vintage-frames 0.15.0 → 0.16.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.
Files changed (47) hide show
  1. package/custom-elements.json +681 -65
  2. package/dist/chrome.d.ts +10 -1
  3. package/dist/chrome.js +8 -3
  4. package/dist/components/vf-desktop.d.ts +5 -2
  5. package/dist/components/vf-desktop.js +5 -5
  6. package/dist/components/vf-dialog.d.ts +7 -2
  7. package/dist/components/vf-dialog.js +27 -27
  8. package/dist/components/vf-menu-bar.js +9 -10
  9. package/dist/components/vf-menu-item.js +18 -13
  10. package/dist/components/vf-menu.d.ts +8 -3
  11. package/dist/components/vf-menu.js +85 -61
  12. package/dist/components/vf-number-field.d.ts +4 -0
  13. package/dist/components/vf-number-field.js +28 -25
  14. package/dist/components/vf-paragraph.d.ts +3 -1
  15. package/dist/components/vf-paragraph.js +7 -0
  16. package/dist/components/vf-scroll-area.d.ts +16 -1
  17. package/dist/components/vf-scroll-area.js +39 -29
  18. package/dist/components/vf-select.d.ts +5 -4
  19. package/dist/components/vf-select.js +26 -17
  20. package/dist/components/vf-text-field.d.ts +5 -5
  21. package/dist/components/vf-text-field.js +1 -1
  22. package/dist/components/vf-window.d.ts +22 -5
  23. package/dist/components/vf-window.js +113 -98
  24. package/dist/cursor.d.ts +8 -0
  25. package/dist/cursor.js +70 -73
  26. package/dist/index.d.ts +2 -1
  27. package/dist/modal-dialog.d.ts +20 -2
  28. package/dist/modal-dialog.js +63 -51
  29. package/dist/shell/catalog.d.ts +1 -1
  30. package/dist/shell/catalog.js +32 -18
  31. package/dist/shell/finder.d.ts +6 -1
  32. package/dist/shell/finder.js +182 -176
  33. package/dist/shell/index.d.ts +1 -1
  34. package/dist/shell/pure.d.ts +10 -0
  35. package/dist/shell/pure.js +4 -0
  36. package/dist/shell/shell.d.ts +16 -3
  37. package/dist/shell/shell.js +141 -126
  38. package/dist/shell/windows.d.ts +48 -10
  39. package/dist/shell/windows.js +142 -85
  40. package/dist/text-control.d.ts +24 -1
  41. package/dist/text-control.js +25 -4
  42. package/dist/top-layer.d.ts +13 -0
  43. package/dist/top-layer.js +16 -0
  44. package/docs/SPEC.md +23 -22
  45. package/editor/vscode.html-custom-data.json +21 -11
  46. package/editor/web-types.json +51 -23
  47. package/package.json +5 -1
@@ -16,9 +16,18 @@ import { MenuPressController as g } from "../menu-press.js";
16
16
  import { LitElement as _, css as v, html as y, nothing as b } from "lit";
17
17
  import { property as x, query as S, queryAssignedElements as C, state as w } from "lit/decorators.js";
18
18
  //#region src/components/vf-menu.ts
19
- var T = class extends a(_) {
19
+ var T = /* @__PURE__ */ new Set([
20
+ "vf-img",
21
+ "img",
22
+ "svg",
23
+ "picture",
24
+ "canvas"
25
+ ]), E = class extends a(_) {
20
26
  constructor(...e) {
21
- super(...e), this.scale = new r(this), this.gridSnap = new u(this), this.#e = new m(this), this.label = "", this.open = !1, this.shortcuts = !1, this.barTabIndex = 0, this.#t = new g(this, {
27
+ super(...e), this.scale = new r(this), this.gridSnap = new u(this), this.#e = new m(this), this.label = "", this.open = !1, this.shortcuts = !1, this.barTabIndex = 0, this._artTitle = !1, this.#t = (e) => {
28
+ let t = e.target.assignedElements();
29
+ this._artTitle = t.length > 0 && t.every((e) => T.has(e.localName));
30
+ }, this.#n = new g(this, {
22
31
  menus: () => [this],
23
32
  open: (e) => {
24
33
  e.open = !0;
@@ -26,17 +35,17 @@ var T = class extends a(_) {
26
35
  close: () => {
27
36
  this.open = !1;
28
37
  }
29
- }), this.#r = this.attachInternals(), this.#i = new h(), this.#a = !1, this.#o = () => {
30
- this.#n || this.focus(), this.open = !1;
31
- }, this._flashOn = !1, this.#c = () => {
32
- this.open || (this.#l(), this.#s = p((e) => {
38
+ }), this.#i = this.attachInternals(), this.#a = new h(), this.#o = !1, this.#s = () => {
39
+ this.#r || this.focus(), this.open = !1;
40
+ }, this._flashOn = !1, this.#l = () => {
41
+ this.open || (this.#u(), this.#c = p((e) => {
33
42
  this._flashOn = e;
34
43
  }, () => {
35
- this._flashOn = !1, this.#s = void 0;
44
+ this._flashOn = !1, this.#c = void 0;
36
45
  }));
37
- }, this.#u = (e) => {
38
- e.composedPath().includes(this) || (this.open = !1);
39
46
  }, this.#d = (e) => {
47
+ e.composedPath().includes(this) || (this.open = !1);
48
+ }, this.#f = (e) => {
40
49
  if (!e.defaultPrevented) switch (e.key) {
41
50
  case "Escape":
42
51
  e.preventDefault(), this.open = !1, this.focus();
@@ -46,7 +55,7 @@ var T = class extends a(_) {
46
55
  break;
47
56
  case "ArrowDown":
48
57
  case "ArrowUp":
49
- e.preventDefault(), this.#f(e.key === "ArrowDown" ? 1 : -1);
58
+ e.preventDefault(), this.#p(e.key === "ArrowDown" ? 1 : -1);
50
59
  break;
51
60
  case "Home":
52
61
  case "End": {
@@ -59,29 +68,29 @@ var T = class extends a(_) {
59
68
  if (e.key.length !== 1 || e.key === " " || e.metaKey || e.ctrlKey || e.altKey) break;
60
69
  e.preventDefault();
61
70
  let t = this.items, n = t.indexOf(document.activeElement);
62
- t[this.#i.feed(e.key, n, t.map((e) => ({
71
+ t[this.#a.feed(e.key, n, t.map((e) => ({
63
72
  text: e.textContent ?? "",
64
73
  disabled: !1
65
74
  })))]?.focus();
66
75
  break;
67
76
  }
68
77
  }
69
- }, this.#p = new d(this, () => [[
78
+ }, this.#m = new d(this, () => [[
70
79
  document,
71
80
  "pointerdown",
72
- this.#u,
81
+ this.#d,
73
82
  !0
74
83
  ], [
75
84
  document,
76
85
  "keydown",
77
- this.#d,
86
+ this.#f,
78
87
  !0
79
- ]]), this.#h = (e) => {
80
- if (!this.open || this.#n) return;
88
+ ]]), this.#g = (e) => {
89
+ if (!this.open || this.#r) return;
81
90
  let t = e.relatedTarget;
82
91
  t instanceof Node && (this.contains(t) || this.renderRoot.contains(t)) || (this.open = !1);
83
- }, this.#g = (e) => {
84
- this.#e.suppress(), !this.#n && this.#t.onPointerDown(e);
92
+ }, this.#_ = (e) => {
93
+ this.#e.suppress(), !this.#r && this.#n.onPointerDown(e);
85
94
  };
86
95
  }
87
96
  static {
@@ -102,7 +111,7 @@ var T = class extends a(_) {
102
111
  Menus.png puts the plate 10px left / 9px right of the title ink.
103
112
  Chicago carries a 1px bearing on each side inside the text box, so
104
113
  9/8 in layout lands the plate at ink −10/+9. In a bar, vf-menu-bar
105
- overlaps adjacent boxes 5px on top of this — the plates share a
114
+ overlaps adjacent boxes 4px on top of this — the plates share a
106
115
  band, as the originals did. */
107
116
  padding-inline: calc(var(--vf-scale, 1) * 9px) calc(var(--vf-scale, 1) * 8px);
108
117
  white-space: nowrap;
@@ -177,6 +186,18 @@ var T = class extends a(_) {
177
186
  is for the state the inversion can't express: focused, not yet open.
178
187
  The class stays on through the open state, so the rule comes back by
179
188
  itself when the menu closes and hands focus back to the title. */
189
+ /* An art title: an icon in the label slot. Its 16×16 cell sits 6 system
190
+ px into the plate and 6 short of the plate's end, on the plate's top
191
+ row (one row above where centering would put it). Art drawn from x 2,
192
+ y 1 of its cell, as the apple is, then has 8 px of plate before its
193
+ ink, and its ink runs from the bar's row 2. */
194
+ .label.art {
195
+ align-items: flex-start;
196
+ padding-inline: calc(var(--vf-scale, 1) * 6px);
197
+ }
198
+ .label.art .title {
199
+ margin-block-start: calc(var(--vf-scale, 1) * 1px);
200
+ }
180
201
  .label:focus {
181
202
  outline: none;
182
203
  }
@@ -236,6 +257,7 @@ var T = class extends a(_) {
236
257
  }
237
258
  #e;
238
259
  #t;
260
+ #n;
239
261
  get items() {
240
262
  return this.allItems.filter((e) => !e.disabled);
241
263
  }
@@ -245,44 +267,44 @@ var T = class extends a(_) {
245
267
  get labelRect() {
246
268
  return this._labelEl?.getBoundingClientRect() ?? null;
247
269
  }
248
- get #n() {
270
+ get #r() {
249
271
  return this.closest("vf-menu-bar") !== null;
250
272
  }
251
- #r;
252
273
  #i;
253
274
  #a;
254
275
  #o;
255
276
  #s;
256
277
  #c;
257
- #l() {
258
- this.#s?.cancel(), this.#s = void 0, this._flashOn = !1;
278
+ #l;
279
+ #u() {
280
+ this.#c?.cancel(), this.#c = void 0, this._flashOn = !1;
259
281
  }
260
- #u;
261
282
  #d;
262
- #f(e) {
283
+ #f;
284
+ #p(e) {
263
285
  let t = this.items;
264
286
  if (t.length === 0) return;
265
287
  let n = t.indexOf(document.activeElement), r;
266
288
  r = n < 0 ? e === 1 ? 0 : t.length - 1 : (n + e + t.length) % t.length, t[r]?.focus();
267
289
  }
268
- #p;
290
+ #m;
269
291
  connectedCallback() {
270
- super.connectedCallback(), this.#m(), this.requestUpdate(), this.addEventListener("vf-menu-close-request", this.#o), this.addEventListener("vf-menu-flash-request", this.#c), this.addEventListener("pointerdown", this.#g), this.addEventListener("focusout", this.#h);
292
+ super.connectedCallback(), this.#h(), this.requestUpdate(), this.addEventListener("vf-menu-close-request", this.#s), this.addEventListener("vf-menu-flash-request", this.#l), this.addEventListener("pointerdown", this.#_), this.addEventListener("focusout", this.#g);
271
293
  }
272
- #m() {
273
- this.#r.role = this.#n ? "none" : null;
294
+ #h() {
295
+ this.#i.role = this.#r ? "none" : null;
274
296
  }
275
297
  disconnectedCallback() {
276
- super.disconnectedCallback(), this.removeEventListener("vf-menu-close-request", this.#o), this.removeEventListener("vf-menu-flash-request", this.#c), this.removeEventListener("pointerdown", this.#g), this.removeEventListener("focusout", this.#h), this.#l();
298
+ super.disconnectedCallback(), this.removeEventListener("vf-menu-close-request", this.#s), this.removeEventListener("vf-menu-flash-request", this.#l), this.removeEventListener("pointerdown", this.#_), this.removeEventListener("focusout", this.#g), this.#u();
277
299
  }
278
- #h;
300
+ #g;
279
301
  updated(e) {
280
302
  if (e.has("open")) {
281
- if (this.open && this.#l(), this.open && !this.#n ? this.#p.attach() : this.#p.detach(), !this.open) for (let e of this.allItems) e.active = !1;
282
- this.open || this.#i.reset();
303
+ if (this.open && this.#u(), this.open && !this.#r ? this.#m.attach() : this.#m.detach(), !this.open) for (let e of this.allItems) e.active = !1;
304
+ this.open || this.#a.reset();
283
305
  }
284
306
  }
285
- #g;
307
+ #_;
286
308
  focus(e) {
287
309
  this._labelEl?.focus(e);
288
310
  }
@@ -292,19 +314,21 @@ var T = class extends a(_) {
292
314
  render() {
293
315
  return y`
294
316
  <div
295
- class="label vf-snap ${this.#e.marked ? "vf-focus-rule" : ""} ${this._flashOn ? "flash-on" : ""}"
317
+ class="label vf-snap ${this.#e.marked ? "vf-focus-rule" : ""} ${this._flashOn ? "flash-on" : ""} ${this._artTitle ? "art" : ""}"
296
318
  part="label"
297
- role=${this.#n ? "menuitem" : "button"}
319
+ role=${this.#r ? "menuitem" : "button"}
298
320
  tabindex=${this.barTabIndex}
299
321
  aria-haspopup="menu"
300
322
  aria-expanded=${this.open ? "true" : "false"}
301
323
  aria-label=${this.label || b}
302
- @pointerdown=${this.#v}
303
- @click=${this.#y}
304
- @pointerenter=${this.#b}
305
- @keydown=${this.#x}
324
+ @pointerdown=${this.#y}
325
+ @click=${this.#b}
326
+ @pointerenter=${this.#x}
327
+ @keydown=${this.#S}
306
328
  >
307
- <span class="title"><slot name="label">${this.label}</slot></span>
329
+ <span class="title"
330
+ ><slot name="label" @slotchange=${this.#t}>${this.label}</slot></span
331
+ >
308
332
  </div>
309
333
  <div
310
334
  class="panel vf-panel"
@@ -316,8 +340,8 @@ var T = class extends a(_) {
316
340
  </div>
317
341
  `;
318
342
  }
319
- #_() {
320
- if (!this.#n) {
343
+ #v() {
344
+ if (!this.#r) {
321
345
  this.open = !this.open;
322
346
  return;
323
347
  }
@@ -326,44 +350,44 @@ var T = class extends a(_) {
326
350
  composed: !1
327
351
  }) && (this.open = !this.open);
328
352
  }
329
- #v() {
330
- this.#a = !0, f(() => {
331
- this.#a = !1;
353
+ #y() {
354
+ this.#o = !0, f(() => {
355
+ this.#o = !1;
332
356
  });
333
357
  }
334
- #y(t) {
335
- if (this.#a) {
336
- this.#a = !1;
358
+ #b(t) {
359
+ if (this.#o) {
360
+ this.#o = !1;
337
361
  return;
338
362
  }
339
- e(this, t, () => this.#_());
363
+ e(this, t, () => this.#v());
340
364
  }
341
- #b() {
365
+ #x() {
342
366
  t(this, "vf-menu-hover", { menu: this }, { composed: !1 });
343
367
  }
344
- #x(e) {
368
+ #S(e) {
345
369
  if (e.key === "Enter" || e.key === " ") {
346
370
  e.preventDefault();
347
371
  let t = this.open;
348
- this.#_(), !t && this.open && this.#S();
349
- } else e.key === "ArrowDown" && !this.open && (e.preventDefault(), this.#_(), this.open && this.#S());
372
+ this.#v(), !t && this.open && this.#C();
373
+ } else e.key === "ArrowDown" && !this.open && (e.preventDefault(), this.#v(), this.open && this.#C());
350
374
  }
351
- async #S() {
375
+ async #C() {
352
376
  await this.updateComplete, this.items[0]?.focus();
353
377
  }
354
378
  };
355
- i([x()], T.prototype, "label", void 0), i([x({
379
+ i([x()], E.prototype, "label", void 0), i([x({
356
380
  type: Boolean,
357
381
  reflect: !0
358
- })], T.prototype, "open", void 0), i([x({
382
+ })], E.prototype, "open", void 0), i([x({
359
383
  type: Boolean,
360
384
  reflect: !0
361
- })], T.prototype, "shortcuts", void 0), i([x({
385
+ })], E.prototype, "shortcuts", void 0), i([x({
362
386
  type: Number,
363
387
  attribute: !1
364
- })], T.prototype, "barTabIndex", void 0), i([S(".label")], T.prototype, "_labelEl", void 0), i([C({
388
+ })], E.prototype, "barTabIndex", void 0), i([S(".label")], E.prototype, "_labelEl", void 0), i([w()], E.prototype, "_artTitle", void 0), i([C({
365
389
  selector: "vf-menu-item",
366
390
  flatten: !0
367
- })], T.prototype, "_assignedItems", void 0), i([w()], T.prototype, "_flashOn", void 0), T = i([n("vf-menu")], T);
391
+ })], E.prototype, "_assignedItems", void 0), i([w()], E.prototype, "_flashOn", void 0), E = i([n("vf-menu")], E);
368
392
  //#endregion
369
- export { T as VfMenu };
393
+ export { E as VfMenu };
@@ -50,6 +50,10 @@ export declare class VfNumberField extends VfNumberField_base {
50
50
  private pressed;
51
51
  private input;
52
52
  disconnectedCallback(): void;
53
+ /** Typed text clamps and rounds when it is a number; otherwise it stays as typed. */
54
+ protected committedText(raw: string): string;
55
+ /** Every value event carries `valueAsNumber` too. */
56
+ protected emitValue(type: 'vf-input' | 'vf-change', detail?: Record<string, unknown>): void;
53
57
  protected render(): import("lit-html").TemplateResult<1>;
54
58
  }
55
59
  declare global {
@@ -14,42 +14,39 @@ import { classMap as v } from "lit/directives/class-map.js";
14
14
  import { live as y } from "lit/directives/live.js";
15
15
  //#region src/components/vf-number-field.ts
16
16
  var b = class extends r(u) {
17
- constructor(...t) {
18
- super(...t), this.step = 1, this.pressed = null, this.#l = (e) => {
17
+ constructor(...e) {
18
+ super(...e), this.step = 1, this.pressed = null, this.#l = (e) => {
19
19
  if (this.isDisabled || this.readonly) return;
20
20
  let t = e.currentTarget.classList.contains("up") ? 1 : -1;
21
21
  e.preventDefault();
22
22
  try {
23
23
  e.currentTarget.setPointerCapture(e.pointerId);
24
24
  } catch {}
25
- this.input?.focus(), this.pressed = t > 0 ? "up" : "down", this.#a(t), this.#s(t);
25
+ this.input?.focus(), this.pressed = t > 0 ? "up" : "down", this.#o(t), this.#s(t);
26
26
  }, this.#u = () => {
27
27
  this.#c(), this.pressed = null;
28
28
  }, this.#d = (e) => {
29
29
  if (this.isSubmitEnter(e)) {
30
- this.requestImplicitSubmit() && e.preventDefault();
30
+ this.submitOnEnter(e);
31
31
  return;
32
32
  }
33
33
  if (!(this.isDisabled || this.readonly) && !(e.isComposing || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) switch (e.key) {
34
34
  case "ArrowUp":
35
- e.preventDefault(), this.#a(1);
35
+ e.preventDefault(), this.#o(1);
36
36
  break;
37
37
  case "ArrowDown":
38
- e.preventDefault(), this.#a(-1);
38
+ e.preventDefault(), this.#o(-1);
39
39
  break;
40
40
  case "Home":
41
- this.min != null && (e.preventDefault(), this.#i(this.min));
41
+ this.min != null && (e.preventDefault(), this.#a(this.min));
42
42
  break;
43
43
  case "End":
44
- this.max != null && (e.preventDefault(), this.#i(this.max));
44
+ this.max != null && (e.preventDefault(), this.#a(this.max));
45
45
  break;
46
46
  case " ": e.preventDefault();
47
47
  }
48
48
  }, this.#f = (e) => {
49
- this.value = e.target.value, this.#o("vf-input");
50
- }, this.#p = (t) => {
51
- let n = t.target.value, r = parseFloat(n);
52
- this.value = Number.isNaN(r) ? n : String(Number(this.#r(r).toFixed(d(this.step)))), this.#o("vf-change"), e(this, "change");
49
+ this.value = e.target.value, this.emitValue("vf-input");
53
50
  };
54
51
  }
55
52
  static {
@@ -153,24 +150,31 @@ var b = class extends r(u) {
153
150
  #r(e) {
154
151
  return this.min != null && e < this.min ? this.min : this.max != null && e > this.max ? this.max : e;
155
152
  }
156
- #i(t) {
157
- let n = String(Number(this.#r(t).toFixed(d(this.step))));
158
- n !== this.value && (this.value = n, this.#o("vf-change"), e(this, "input"), e(this, "change"));
153
+ #i(e) {
154
+ return String(Number(this.#r(e).toFixed(d(this.step))));
159
155
  }
160
- #a(e) {
156
+ committedText(e) {
157
+ let t = parseFloat(e);
158
+ return Number.isNaN(t) ? e : this.#i(t);
159
+ }
160
+ #a(t) {
161
+ let n = this.#i(t);
162
+ n !== this.value && (this.value = n, this.committed = n, this.emitValue("vf-change"), e(this, "input"), e(this, "change"));
163
+ }
164
+ #o(e) {
161
165
  if (this.isDisabled || this.readonly) return;
162
166
  let t = this.#n(), n = Number.isNaN(t) ? this.min ?? 0 : t + e * this.step;
163
- this.#i(n);
167
+ this.#a(n);
164
168
  }
165
- #o(e) {
166
- this.emitValue(e, {
167
- value: this.value,
168
- valueAsNumber: this.#n()
169
- });
169
+ emitValue(e, t = {
170
+ value: this.value,
171
+ valueAsNumber: this.#n()
172
+ }) {
173
+ super.emitValue(e, t);
170
174
  }
171
175
  #s(e) {
172
176
  this.#c(), this.#e = window.setTimeout(() => {
173
- this.#t = window.setInterval(() => this.#a(e), 60);
177
+ this.#t = window.setInterval(() => this.#o(e), 60);
174
178
  }, 300);
175
179
  }
176
180
  #c() {
@@ -180,7 +184,6 @@ var b = class extends r(u) {
180
184
  #u;
181
185
  #d;
182
186
  #f;
183
- #p;
184
187
  render() {
185
188
  let e = this.isDisabled, t = this.#n();
186
189
  return p`
@@ -208,7 +211,7 @@ var b = class extends r(u) {
208
211
  ?disabled=${e}
209
212
  ?readonly=${this.readonly}
210
213
  @input=${this.#f}
211
- @change=${this.#p}
214
+ @change=${this.handleChange}
212
215
  @keydown=${this.#d}
213
216
  />
214
217
  </div>
@@ -29,7 +29,9 @@ declare const VfParagraph_base: (new (...args: any[]) => import("../size.js").Vf
29
29
  * measure the copy wraps to, whole and on the grid.
30
30
  *
31
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.
32
+ * for assistive tech, and — unlike the kit's chrome — the text is selectable,
33
+ * selected text inverting to the 1-bit highlight (`--vf-highlight` /
34
+ * `--vf-highlight-text`) as a field's does.
33
35
  * There is no margin: per SPEC §2 a component adds nothing outside its own box,
34
36
  * so paragraph spacing is the page's (a `gap` on the column, or a margin on the
35
37
  * host — kept a whole number of pixels, like everything else in the contract).
@@ -37,6 +37,13 @@ var f = class extends s(r(c)) {
37
37
  user-select: text;
38
38
  -webkit-user-select: text;
39
39
  }
40
+ /* Selected copy inverts to the 1-bit highlight, as a field's does. The
41
+ copy is the host's own light-DOM text, so the rule is the host's;
42
+ text inside slotted elements (a <b>, a link) inherits it. */
43
+ :host::selection {
44
+ background-color: var(--vf-highlight, #000);
45
+ color: var(--vf-highlight-text, #fff);
46
+ }
40
47
  /* Chrome copy wraps on Chicago 12's native line: ascent 12 + descent 3
41
48
  + leading 1 = 16 — zero half-leading on the 16px em, 7 blank rows
42
49
  between a baseline and the caps under it, as a real alert set it.
@@ -1,4 +1,5 @@
1
1
  import { LitElement } from 'lit';
2
+ import type { PropertyValues } from 'lit';
2
3
  declare const VfScrollArea_base: (new (...args: any[]) => import("../position.js").VfPositionedInterface) & typeof LitElement;
3
4
  /**
4
5
  * `<vf-scroll-area>` — a container whose scrollbars look like System 7.
@@ -24,7 +25,8 @@ declare const VfScrollArea_base: (new (...args: any[]) => import("../position.js
24
25
  *
25
26
  * Size the host (width/height) from the outside; the viewport fills it.
26
27
  * Content runs to the frame and the rails — the area adds no inset of its
27
- * own; an inset is the content's (a `vf-stack pad`).
28
+ * own; an inset is the content's (a `vf-stack pad`). `pattern` fills the
29
+ * scrolled plane with a 1-bit pattern, as on `vf-container`.
28
30
  *
29
31
  * The scrolled plane sizes to its content — never narrower than the
30
32
  * viewport, as wide as content that cannot wrap — so the rails follow a row
@@ -75,8 +77,21 @@ export declare class VfScrollArea extends VfScrollArea_base {
75
77
  * unnamed region is inert) and role-less when not.
76
78
  */
77
79
  label: string;
80
+ /**
81
+ * A 1-bit fill for the scrolled plane, as on `vf-container`: a library
82
+ * pattern by name (docs/PATTERNS.md) or sixteen hex digits. It fills the
83
+ * visible area, runs on past the content, and scrolls with it, its phase
84
+ * anchored at the content's top-left. Unset, the plane stays white; an
85
+ * unrecognized value paints nothing and warns once.
86
+ */
87
+ pattern?: string | null;
88
+ /** `pattern`, resolved — what the fill paints; null paints nothing. */
89
+ private _pattern;
78
90
  private viewport;
79
91
  private content;
92
+ /** The pattern fill, painted on the scrolled plane (src/pattern-fill.ts). */
93
+ private readonly patternFill;
94
+ protected willUpdate(changed: PropertyValues<this>): void;
80
95
  /** Reports overflow per axis so the reserved rail(s) activate on overflow. */
81
96
  private readonly scrollState;
82
97
  /** Syncs the drawn rails to the viewport and drives their interactions. */
@@ -3,27 +3,36 @@ import { ScaleController as t } from "../scale.js";
3
3
  import n from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
4
4
  import { VfPositioned as r, placementIn as i } from "../position.js";
5
5
  import { vfBase as a } from "../styles/recipes/host.js";
6
- import { vfStrokeDecls as o } from "../styles/recipes/stroke.js";
7
- import { vfFocusRing as s } from "../styles/recipes/focus.js";
8
- import { vfScrollRail as c } from "../styles/recipes/scroll-rail.js";
9
- import { GridSnapController as l } from "../grid-snap.js";
10
- import { ScrollStateController as u } from "../scroll-state.js";
11
- import { ScrollRailController as d, renderScrollRail as f } from "../scroll-rail.js";
12
- import { LitElement as p, css as m, html as h, nothing as g } from "lit";
13
- import { property as _, query as v, state as y } from "lit/decorators.js";
6
+ import { parsePattern as o } from "../patterns.js";
7
+ import { vfStrokeDecls as s } from "../styles/recipes/stroke.js";
8
+ import { vfFocusRing as c } from "../styles/recipes/focus.js";
9
+ import { vfScrollRail as l } from "../styles/recipes/scroll-rail.js";
10
+ import { PatternFillController as u, vfPatternFill as d } from "../pattern-fill.js";
11
+ import { GridSnapController as f } from "../grid-snap.js";
12
+ import { ScrollStateController as p } from "../scroll-state.js";
13
+ import { ScrollRailController as m, renderScrollRail as h } from "../scroll-rail.js";
14
+ import { LitElement as g, css as _, html as v, nothing as y } from "lit";
15
+ import { property as b, query as x, state as S } from "lit/decorators.js";
14
16
  //#region src/components/vf-scroll-area.ts
15
- var b = class extends r(p) {
17
+ var C = class extends r(g) {
18
+ #e;
19
+ willUpdate(e) {
20
+ super.willUpdate(e), e.has("pattern") && (this._pattern = o(this.pattern), this._pattern === null && this.pattern?.trim() && !this.#e && (this.#e = !0, console.warn(`vf-scroll-area: unknown pattern "${this.pattern}" — a library name (docs/PATTERNS.md) or sixteen hex digits. Painting nothing.`)));
21
+ }
16
22
  constructor() {
17
- super(), this.scale = new t(this), this.gridSnap = new l(this), this.axis = "vertical", this.corner = !1, this.label = "", this.scrollState = new u(this, () => this.viewport, () => this.content, (e) => {
23
+ super(), this.scale = new t(this), this.gridSnap = new f(this), this.axis = "vertical", this.corner = !1, this.label = "", this._pattern = null, this.#e = !1, this.patternFill = new u(this, {
24
+ getBox: () => this.content,
25
+ getPattern: () => this._pattern
26
+ }), this.scrollState = new p(this, () => this.viewport, () => this.content, (e) => {
18
27
  this._scrollable = e.x || e.y;
19
- }), this.rail = new d(this, {
28
+ }), this.rail = new m(this, {
20
29
  getScroll: () => this.viewport,
21
30
  getContent: () => this.content
22
- }), this._scrollable = !1, this.#e = () => {
31
+ }), this._scrollable = !1, this.#t = () => {
23
32
  this.measure();
24
- }, this.addEventListener("vf-placement-change", this.#e);
33
+ }, this.addEventListener("vf-placement-change", this.#t);
25
34
  }
26
- #e;
35
+ #t;
27
36
  placementAt(e, t, n) {
28
37
  return i(this.content ?? this, e, t, this, n);
29
38
  }
@@ -39,8 +48,9 @@ var b = class extends r(p) {
39
48
  static {
40
49
  this.styles = [
41
50
  a,
42
- c,
43
- m`
51
+ l,
52
+ d,
53
+ _`
44
54
  :host {
45
55
  display: block;
46
56
  }
@@ -55,7 +65,7 @@ var b = class extends r(p) {
55
65
  grid-template-columns: 1fr;
56
66
  grid-template-rows: 1fr;
57
67
  background: var(--vf-white, #fff);
58
- ${o()}
68
+ ${s()}
59
69
  }
60
70
  :host(:not([axis])) .box,
61
71
  :host([axis='vertical']) .box,
@@ -135,36 +145,36 @@ var b = class extends r(p) {
135
145
  /* Focusable so keyboard users can scroll; inset ring to stay in-box. */
136
146
  .viewport:focus-visible {
137
147
  --vf-focus-offset: -2px;
138
- ${s}
148
+ ${c}
139
149
  }
140
150
  `
141
151
  ];
142
152
  }
143
153
  render() {
144
154
  let e = this.axis !== "horizontal", t = this.axis === "horizontal" || this.axis === "both", n = e && t || this.corner;
145
- return h`
155
+ return v`
146
156
  <div class="box vf-snap">
147
157
  <div
148
158
  class="viewport vf-scroll"
149
159
  part="viewport"
150
- tabindex=${this._scrollable ? "0" : g}
151
- role=${this.label ? "region" : this._scrollable ? "group" : g}
152
- aria-label=${this.label || g}
160
+ tabindex=${this._scrollable ? "0" : y}
161
+ role=${this.label ? "region" : this._scrollable ? "group" : y}
162
+ aria-label=${this.label || y}
153
163
  >
154
- <div class="content">
164
+ <div class="content vf-pattern-fill${this._pattern ? " vf-patterned" : ""}">
155
165
  <slot @slotchange=${this._onSlotChange}></slot>
156
166
  </div>
157
167
  </div>
158
- ${e ? f(this.rail, "vertical") : g}
159
- ${t ? f(this.rail, "horizontal") : g}
160
- ${n ? h`<div class="vf-rail-corner" aria-hidden="true"></div>` : g}
168
+ ${e ? h(this.rail, "vertical") : y}
169
+ ${t ? h(this.rail, "horizontal") : y}
170
+ ${n ? v`<div class="vf-rail-corner" aria-hidden="true"></div>` : y}
161
171
  </div>
162
172
  `;
163
173
  }
164
174
  };
165
- n([_({ reflect: !0 })], b.prototype, "axis", void 0), n([_({
175
+ n([b({ reflect: !0 })], C.prototype, "axis", void 0), n([b({
166
176
  type: Boolean,
167
177
  reflect: !0
168
- })], b.prototype, "corner", void 0), n([_()], b.prototype, "label", void 0), n([v(".viewport")], b.prototype, "viewport", void 0), n([v(".content")], b.prototype, "content", void 0), n([y()], b.prototype, "_scrollable", void 0), b = n([e("vf-scroll-area")], b);
178
+ })], C.prototype, "corner", void 0), n([b()], C.prototype, "label", void 0), n([b()], C.prototype, "pattern", void 0), n([x(".viewport")], C.prototype, "viewport", void 0), n([x(".content")], C.prototype, "content", void 0), n([S()], C.prototype, "_scrollable", void 0), C = n([e("vf-scroll-area")], C);
169
179
  //#endregion
170
- export { b as VfScrollArea };
180
+ export { C as VfScrollArea };
@@ -9,10 +9,11 @@ declare const VfSelect_base: (new (...args: any[]) => import("../position.js").V
9
9
  * Children are `<vf-option>` elements in the default slot. The closed control
10
10
  * is a white box with a 1px black border, the small 1px hard shadow, the
11
11
  * selected option's label on the left and a solid black ▼ triangle on the
12
- * right. The open panel uses the shared `.vf-panel` recipe and is positioned
13
- * `position: fixed` (computed from `getBoundingClientRect()`) so it escapes
14
- * clipping containers; when possible the currently-selected item opens
15
- * directly over the control, like the real popup menu.
12
+ * right. The open panel uses the shared `.vf-panel` recipe and is a manual
13
+ * popover in the top layer, positioned `position: fixed` (computed from
14
+ * `getBoundingClientRect()`), so it escapes clipping containers and paints
15
+ * over every window, palettes included; when possible the currently-selected
16
+ * item opens directly over the control, like the real popup menu.
16
17
  *
17
18
  * Pointer — two interaction styles are supported and disambiguated by the
18
19
  * gesture itself, resolved at the first pointer release: