vintage-frames 0.2.0 → 0.3.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.
@@ -62,12 +62,18 @@ export declare class VfMenuItem extends VfMenuItem_base {
62
62
  */
63
63
  checkable: boolean;
64
64
  /**
65
- * Keyboard shortcut text, e.g. `"⌘H"`, drawn left-aligned in the panel's
66
- * shared shortcut column. Display only in the
67
- * visual sense the span is `aria-hidden` so the glyphs never concatenate
68
- * into the item's accessible name ("Print… place of interest sign P")
69
- * while the host mirrors it as `aria-keyshortcuts` ("Meta+H"), so AT
70
- * announces it *as* a shortcut. A consumer's own `aria-keyshortcuts` wins.
65
+ * Keyboard shortcut, e.g. `"⌘H"`, drawn left-aligned in the panel's
66
+ * shared shortcut column. The span is `aria-hidden` so the glyphs never
67
+ * concatenate into the item's accessible name ("Print… place of interest
68
+ * sign P") the host mirrors it as `aria-keyshortcuts` ("Meta+H") instead,
69
+ * so AT announces it *as* a shortcut. A consumer's own `aria-keyshortcuts`
70
+ * wins.
71
+ *
72
+ * Inside a `vf-menu`/`vf-menu-bar` that declares `shortcuts`, this is a
73
+ * LIVE key equivalent, not a legend: a matching keydown anywhere on the
74
+ * page activates the item — menu open or not — claiming the stroke with
75
+ * `preventDefault()` (see the document keydown handler below for the full
76
+ * contract).
71
77
  */
72
78
  shortcut: string;
73
79
  /**
@@ -24,7 +24,20 @@ function b(e) {
24
24
  let r = t.join("");
25
25
  return r ? [...n, r].join("+") : "";
26
26
  }
27
- var x = class extends r(f) {
27
+ function x(e, t) {
28
+ let n = [...t.trim()], r = {
29
+ Meta: !1,
30
+ Control: !1,
31
+ Alt: !1,
32
+ Shift: !1
33
+ };
34
+ for (; n.length > 0 && y.has(n[0]);) r[y.get(n.shift())] = !0;
35
+ let i = n.join("");
36
+ if (!i || !r.Meta && !r.Control && !r.Alt && [...i].length === 1) return !1;
37
+ let a = r.Meta ? e.metaKey || e.ctrlKey : !e.metaKey, o = r.Control ? e.ctrlKey : r.Meta || !e.ctrlKey;
38
+ return a && o && e.altKey === r.Alt && e.shiftKey === r.Shift && e.key.toLowerCase() === i.toLowerCase();
39
+ }
40
+ var S = class extends r(f) {
28
41
  static {
29
42
  this.styles = [
30
43
  a,
@@ -161,19 +174,21 @@ var x = class extends r(f) {
161
174
  #r;
162
175
  #i;
163
176
  constructor() {
164
- super(), this.scale = new t(this), this.disabled = !1, this.checked = !1, this.active = !1, this.checkable = !1, this.shortcut = "", this._blinkPhase = null, this.#t = !1, this.#n = !1, this.#r = this.attachInternals(), this.#i = !1, this.addEventListener("keydown", this.#u), this.addEventListener("pointerdown", this.#c), this.addEventListener("click", this.#l);
177
+ super(), this.scale = new t(this), this.disabled = !1, this.checked = !1, this.active = !1, this.checkable = !1, this.shortcut = "", this._blinkPhase = null, this.#t = !1, this.#n = !1, this.#r = this.attachInternals(), this.#i = !1, this.#d = (e) => {
178
+ e.defaultPrevented || e.isComposing || this.disabled || !this.shortcut || this.closest("vf-menu[shortcuts], vf-menu-bar[shortcuts]") && x(e, this.shortcut) && (e.preventDefault(), !(e.repeat || this.#t) && (c(this, "vf-menu-flash-request", { item: this }, { composed: !1 }), this.activate()));
179
+ }, this.addEventListener("keydown", this.#u), this.addEventListener("pointerdown", this.#c), this.addEventListener("click", this.#l);
165
180
  }
166
181
  get #a() {
167
182
  return this.checkable || this.#n;
168
183
  }
169
184
  connectedCallback() {
170
- super.connectedCallback(), this.#s(), this.#o(), this.hasAttribute("tabindex") || (this.tabIndex = -1);
185
+ super.connectedCallback(), this.#s(), this.#o(), this.hasAttribute("tabindex") || (this.tabIndex = -1), document.addEventListener("keydown", this.#d);
171
186
  }
172
187
  disconnectedCallback() {
173
- super.disconnectedCallback(), this.#d();
188
+ super.disconnectedCallback(), document.removeEventListener("keydown", this.#d), this.#f();
174
189
  }
175
190
  updated(e) {
176
- e.has("disabled") && (this.#r.ariaDisabled = this.disabled ? "true" : null, this.disabled && this.#d()), e.has("checked") && this.checked && (this.#n = !0), (e.has("checked") || e.has("checkable")) && this.#s(), e.has("shortcut") && this.#o();
191
+ e.has("disabled") && (this.#r.ariaDisabled = this.disabled ? "true" : null, this.disabled && this.#f()), e.has("checked") && this.checked && (this.#n = !0), (e.has("checked") || e.has("checkable")) && this.#s(), e.has("shortcut") && this.#o();
177
192
  }
178
193
  #o() {
179
194
  this.#r.ariaKeyShortcuts = b(this.shortcut) || null;
@@ -215,39 +230,42 @@ var x = class extends r(f) {
215
230
  #u(e) {
216
231
  (e.key === "Enter" || e.key === " ") && (e.preventDefault(), e.stopPropagation(), this.activate());
217
232
  }
233
+ #d;
218
234
  activate() {
219
235
  if (this.disabled || this.#t) return;
220
236
  this.#t = !0;
221
237
  let e = l((e) => {
222
238
  this._blinkPhase = e ? "on" : "off";
223
239
  }, () => {
224
- this._blinkPhase = null, this.#t = !1, this.#e = void 0, this.#f();
240
+ this._blinkPhase = null, this.#t = !1, this.#e = void 0, this.#p();
225
241
  });
226
242
  this.#t && (this.#e = e);
227
243
  }
228
- #d() {
244
+ #f() {
229
245
  this.#t && (this.#e?.cancel(), this.#e = void 0, this.#t = !1, this._blinkPhase = null);
230
246
  }
231
- #f() {
247
+ #p() {
232
248
  let e = this.value ?? (this.textContent ?? "").trim();
233
249
  c(this, "vf-menu-select", {
234
250
  value: e,
235
251
  item: this
236
- }), c(this, "vf-menu-close-request", { item: this }, { composed: !1 });
252
+ });
253
+ let t = this.closest("vf-menu");
254
+ (!t || t.hasAttribute("open")) && c(this, "vf-menu-close-request", { item: this }, { composed: !1 });
237
255
  }
238
256
  };
239
257
  n([g({
240
258
  type: Boolean,
241
259
  reflect: !0
242
- })], x.prototype, "disabled", void 0), n([g({
260
+ })], S.prototype, "disabled", void 0), n([g({
243
261
  type: Boolean,
244
262
  reflect: !0
245
- })], x.prototype, "checked", void 0), n([g({
263
+ })], S.prototype, "checked", void 0), n([g({
246
264
  type: Boolean,
247
265
  reflect: !0
248
- })], x.prototype, "active", void 0), n([g({
266
+ })], S.prototype, "active", void 0), n([g({
249
267
  type: Boolean,
250
268
  reflect: !0
251
- })], x.prototype, "checkable", void 0), n([g()], x.prototype, "shortcut", void 0), n([g()], x.prototype, "value", void 0), n([_()], x.prototype, "_blinkPhase", void 0), x = n([e("vf-menu-item")], x);
269
+ })], S.prototype, "checkable", void 0), n([g()], S.prototype, "shortcut", void 0), n([g()], S.prototype, "value", void 0), n([_()], S.prototype, "_blinkPhase", void 0), S = n([e("vf-menu-item")], S);
252
270
  //#endregion
253
- export { x as VfMenuItem };
271
+ export { S as VfMenuItem };
@@ -29,7 +29,8 @@ declare const VfMenu_base: (new (...args: any[]) => import("../position.js").VfP
29
29
  * icon for the Apple menu. Keep the `label` attribute set too: it stays the
30
30
  * menu's accessible name (the bar item's `aria-label` and the panel's) when
31
31
  * the visible title is an image.
32
- * @csspart label - The menu title in the bar (inverts while open).
32
+ * @csspart label - The menu title in the bar (inverts while open; flashes
33
+ * when a closed menu's item is activated by its key equivalent).
33
34
  * @csspart panel - The dropped `.vf-panel` containing the items.
34
35
  * @cssprop [--vf-menubar-height=20px] - `vf-menu-bar`
35
36
  * @cssprop [--vf-menu-row-height=16px] - `vf-menu-item` row pitch; the panel
@@ -53,6 +54,16 @@ export declare class VfMenu extends VfMenu_base {
53
54
  * `vf-menu-bar` when present, otherwise by the menu itself.
54
55
  */
55
56
  open: boolean;
57
+ /**
58
+ * Makes the items' `shortcut`s live key equivalents: a matching keydown
59
+ * anywhere on the page activates the item — menu open or not — and a
60
+ * closed menu answers by flashing its bar title, MenuKey's acknowledgment.
61
+ * Off by default because key equivalents are page-global and a page may
62
+ * hold several menus of which only one is *the* menu bar; on a bar, declare
63
+ * it there once for every menu in it. The full claim contract lives on
64
+ * `vf-menu-item.shortcut`.
65
+ */
66
+ shortcuts: boolean;
56
67
  /**
57
68
  * The bar label's tabindex. A parent `vf-menu-bar` owns a roving tabindex
58
69
  * across its menus and sets this to 0 on the active menu, -1 on the rest,
@@ -77,6 +88,8 @@ export declare class VfMenu extends VfMenu_base {
77
88
  * src/menu-press.ts), so it wants the box, not the element.
78
89
  */
79
90
  get labelRect(): DOMRect | null;
91
+ /** `true` while the key-equivalent flash paints the title inverted. */
92
+ private _flashOn;
80
93
  connectedCallback(): void;
81
94
  disconnectedCallback(): void;
82
95
  protected updated(changed: Map<PropertyKey, unknown>): void;
@@ -9,15 +9,16 @@ import { vfFocusUnderline as s } from "../styles/recipes/focus.js";
9
9
  import { GridSnapController as c } from "../grid-snap.js";
10
10
  import { DocumentListenersController as l, releaseAfterGesture as u } from "../document-listeners.js";
11
11
  import { deferActivation as d, emit as f } from "../events.js";
12
- import { FocusRuleController as p } from "../focus-modality.js";
13
- import { TypeAheadBuffer as m } from "../type-ahead.js";
14
- import { MenuPressController as h } from "../menu-press.js";
15
- import { LitElement as g, css as _, html as v, nothing as y } from "lit";
16
- import { property as b, query as x, queryAssignedElements as S } from "lit/decorators.js";
12
+ import { runSelectionBlink as p } from "../motion.js";
13
+ import { FocusRuleController as m } from "../focus-modality.js";
14
+ import { TypeAheadBuffer as h } from "../type-ahead.js";
15
+ import { MenuPressController as g } from "../menu-press.js";
16
+ import { LitElement as _, css as v, html as y, nothing as b } from "lit";
17
+ import { property as x, query as S, queryAssignedElements as C, state as w } from "lit/decorators.js";
17
18
  //#region src/components/vf-menu.ts
18
- var C = class extends r(g) {
19
+ var T = class extends r(_) {
19
20
  constructor(...e) {
20
- super(...e), this.scale = new t(this), this.gridSnap = new c(this), this.#e = new p(this), this.label = "", this.open = !1, this.barTabIndex = 0, this.#t = new h(this, {
21
+ super(...e), this.scale = new t(this), this.gridSnap = new c(this), this.#e = new m(this), this.label = "", this.open = !1, this.shortcuts = !1, this.barTabIndex = 0, this.#t = new g(this, {
21
22
  menus: () => [this],
22
23
  open: (e) => {
23
24
  e.open = !0;
@@ -25,11 +26,17 @@ var C = class extends r(g) {
25
26
  close: () => {
26
27
  this.open = !1;
27
28
  }
28
- }), this.#r = this.attachInternals(), this.#i = new m(), this.#a = !1, this.#o = () => {
29
+ }), this.#r = this.attachInternals(), this.#i = new h(), this.#a = !1, this.#o = () => {
29
30
  this.#n || this.focus(), this.open = !1;
30
- }, this.#s = (e) => {
31
+ }, this._flashOn = !1, this.#c = () => {
32
+ this.open || (this.#l(), this.#s = p((e) => {
33
+ this._flashOn = e;
34
+ }, () => {
35
+ this._flashOn = !1, this.#s = void 0;
36
+ }));
37
+ }, this.#u = (e) => {
31
38
  e.composedPath().includes(this) || (this.open = !1);
32
- }, this.#c = (e) => {
39
+ }, this.#d = (e) => {
33
40
  if (!e.defaultPrevented) switch (e.key) {
34
41
  case "Escape":
35
42
  e.preventDefault(), this.open = !1, this.focus();
@@ -39,7 +46,7 @@ var C = class extends r(g) {
39
46
  break;
40
47
  case "ArrowDown":
41
48
  case "ArrowUp":
42
- e.preventDefault(), this.#l(e.key === "ArrowDown" ? 1 : -1);
49
+ e.preventDefault(), this.#f(e.key === "ArrowDown" ? 1 : -1);
43
50
  break;
44
51
  case "Home":
45
52
  case "End": {
@@ -59,21 +66,21 @@ var C = class extends r(g) {
59
66
  break;
60
67
  }
61
68
  }
62
- }, this.#u = new l(this, () => [[
69
+ }, this.#p = new l(this, () => [[
63
70
  document,
64
71
  "pointerdown",
65
- this.#s,
72
+ this.#u,
66
73
  !0
67
74
  ], [
68
75
  document,
69
76
  "keydown",
70
- this.#c,
77
+ this.#d,
71
78
  !0
72
- ]]), this.#f = (e) => {
79
+ ]]), this.#h = (e) => {
73
80
  if (!this.open || this.#n) return;
74
81
  let t = e.relatedTarget;
75
82
  t instanceof Node && (this.contains(t) || this.renderRoot.contains(t)) || (this.open = !1);
76
- }, this.#p = (e) => {
83
+ }, this.#g = (e) => {
77
84
  this.#e.suppress(), !this.#n && this.#t.onPointerDown(e);
78
85
  };
79
86
  }
@@ -82,7 +89,7 @@ var C = class extends r(g) {
82
89
  a,
83
90
  i,
84
91
  o,
85
- _`
92
+ v`
86
93
  :host {
87
94
  display: inline-block;
88
95
  position: relative;
@@ -105,7 +112,12 @@ var C = class extends r(g) {
105
112
  touch-action: none;
106
113
  cursor: var(--vf-cursor, default);
107
114
  }
108
- :host([open]) .label {
115
+ /* .flash-on: the key-equivalent acknowledgment — a closed menu whose
116
+ item was activated by its shortcut flashes the title on the item
117
+ blink's own cadence, the way MenuKey() flashed the bar. Shares the
118
+ open inversion so the two can't drift apart. */
119
+ :host([open]) .label,
120
+ .label.flash-on {
109
121
  /* Forced colors: exempt the inverted title from the mode's text
110
122
  backplate, which would land a Canvas slab on the highlight bar —
111
123
  see vf-list-item's forced-colors note. The pair is already the
@@ -240,30 +252,35 @@ var C = class extends r(g) {
240
252
  #o;
241
253
  #s;
242
254
  #c;
243
- #l(e) {
255
+ #l() {
256
+ this.#s?.cancel(), this.#s = void 0, this._flashOn = !1;
257
+ }
258
+ #u;
259
+ #d;
260
+ #f(e) {
244
261
  let t = this.items;
245
262
  if (t.length === 0) return;
246
263
  let n = t.indexOf(document.activeElement), r;
247
264
  r = n < 0 ? e === 1 ? 0 : t.length - 1 : (n + e + t.length) % t.length, t[r]?.focus();
248
265
  }
249
- #u;
266
+ #p;
250
267
  connectedCallback() {
251
- super.connectedCallback(), this.#d(), this.requestUpdate(), this.addEventListener("vf-menu-close-request", this.#o), this.addEventListener("pointerdown", this.#p), this.addEventListener("focusout", this.#f);
268
+ 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);
252
269
  }
253
- #d() {
270
+ #m() {
254
271
  this.#r.role = this.#n ? "none" : null;
255
272
  }
256
273
  disconnectedCallback() {
257
- super.disconnectedCallback(), this.removeEventListener("vf-menu-close-request", this.#o), this.removeEventListener("pointerdown", this.#p), this.removeEventListener("focusout", this.#f);
274
+ 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();
258
275
  }
259
- #f;
276
+ #h;
260
277
  updated(e) {
261
278
  if (e.has("open")) {
262
- if (this.open && !this.#n ? this.#u.attach() : this.#u.detach(), !this.open) for (let e of this.allItems) e.active = !1;
279
+ 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;
263
280
  this.open || this.#i.reset();
264
281
  }
265
282
  }
266
- #p;
283
+ #g;
267
284
  focus(e) {
268
285
  this._labelEl?.focus(e);
269
286
  }
@@ -271,19 +288,19 @@ var C = class extends r(g) {
271
288
  this.focus();
272
289
  }
273
290
  render() {
274
- return v`
291
+ return y`
275
292
  <div
276
- class="label vf-snap ${this.#e.marked ? "vf-focus-rule" : ""}"
293
+ class="label vf-snap ${this.#e.marked ? "vf-focus-rule" : ""} ${this._flashOn ? "flash-on" : ""}"
277
294
  part="label"
278
295
  role=${this.#n ? "menuitem" : "button"}
279
296
  tabindex=${this.barTabIndex}
280
297
  aria-haspopup="menu"
281
298
  aria-expanded=${this.open ? "true" : "false"}
282
- aria-label=${this.label || y}
283
- @pointerdown=${this.#h}
284
- @click=${this.#g}
285
- @pointerenter=${this.#_}
286
- @keydown=${this.#v}
299
+ aria-label=${this.label || b}
300
+ @pointerdown=${this.#v}
301
+ @click=${this.#y}
302
+ @pointerenter=${this.#b}
303
+ @keydown=${this.#x}
287
304
  >
288
305
  <span class="title"><slot name="label">${this.label}</slot></span>
289
306
  </div>
@@ -291,13 +308,13 @@ var C = class extends r(g) {
291
308
  class="panel vf-panel"
292
309
  part="panel"
293
310
  role="menu"
294
- aria-label=${this.label || y}
311
+ aria-label=${this.label || b}
295
312
  >
296
313
  <slot></slot>
297
314
  </div>
298
315
  `;
299
316
  }
300
- #m() {
317
+ #_() {
301
318
  if (!this.#n) {
302
319
  this.open = !this.open;
303
320
  return;
@@ -307,41 +324,44 @@ var C = class extends r(g) {
307
324
  composed: !1
308
325
  }) && (this.open = !this.open);
309
326
  }
310
- #h() {
327
+ #v() {
311
328
  this.#a = !0, u(() => {
312
329
  this.#a = !1;
313
330
  });
314
331
  }
315
- #g(e) {
332
+ #y(e) {
316
333
  if (this.#a) {
317
334
  this.#a = !1;
318
335
  return;
319
336
  }
320
- d(this, e, () => this.#m());
337
+ d(this, e, () => this.#_());
321
338
  }
322
- #_() {
339
+ #b() {
323
340
  f(this, "vf-menu-hover", { menu: this }, { composed: !1 });
324
341
  }
325
- #v(e) {
342
+ #x(e) {
326
343
  if (e.key === "Enter" || e.key === " ") {
327
344
  e.preventDefault();
328
345
  let t = this.open;
329
- this.#m(), !t && this.open && this.#y();
330
- } else e.key === "ArrowDown" && !this.open && (e.preventDefault(), this.#m(), this.open && this.#y());
346
+ this.#_(), !t && this.open && this.#S();
347
+ } else e.key === "ArrowDown" && !this.open && (e.preventDefault(), this.#_(), this.open && this.#S());
331
348
  }
332
- async #y() {
349
+ async #S() {
333
350
  await this.updateComplete, this.items[0]?.focus();
334
351
  }
335
352
  };
336
- n([b()], C.prototype, "label", void 0), n([b({
353
+ n([x()], T.prototype, "label", void 0), n([x({
354
+ type: Boolean,
355
+ reflect: !0
356
+ })], T.prototype, "open", void 0), n([x({
337
357
  type: Boolean,
338
358
  reflect: !0
339
- })], C.prototype, "open", void 0), n([b({
359
+ })], T.prototype, "shortcuts", void 0), n([x({
340
360
  type: Number,
341
361
  attribute: !1
342
- })], C.prototype, "barTabIndex", void 0), n([x(".label")], C.prototype, "_labelEl", void 0), n([S({
362
+ })], T.prototype, "barTabIndex", void 0), n([S(".label")], T.prototype, "_labelEl", void 0), n([C({
343
363
  selector: "vf-menu-item",
344
364
  flatten: !0
345
- })], C.prototype, "_assignedItems", void 0), C = n([e("vf-menu")], C);
365
+ })], T.prototype, "_assignedItems", void 0), n([w()], T.prototype, "_flashOn", void 0), T = n([e("vf-menu")], T);
346
366
  //#endregion
347
- export { C as VfMenu };
367
+ export { T as VfMenu };
@@ -13,11 +13,14 @@ declare const VfRadio_base: (new (...args: any[]) => import("../position.js").Vf
13
13
  * unselects its siblings.
14
14
  *
15
15
  * @slot - The label, rendered to the right of the circle with a 6px gap.
16
+ * Left empty, the label and its gap collapse: the control is the bare
17
+ * 13×13 circle.
16
18
  * @csspart circle - The 13×13 radio circle.
17
19
  * @csspart label - The label wrapper around the slot.
18
20
  * @fires vf-change - When selected by user interaction. `detail: { value: string }`.
19
21
  */
20
22
  export declare class VfRadio extends VfRadio_base {
23
+ #private;
21
24
  static styles: import("lit").CSSResult[];
22
25
  /** Whether this radio is selected. Managed by the containing group. */
23
26
  checked: boolean;
@@ -30,6 +33,8 @@ export declare class VfRadio extends VfRadio_base {
30
33
  * group — not intended to be set by consumers.
31
34
  */
32
35
  groupDisabled: boolean;
36
+ /** Whether the slot holds non-whitespace content; gates the label row. */
37
+ private _hasLabel;
33
38
  private readonly internals;
34
39
  constructor();
35
40
  /**
@@ -9,10 +9,10 @@ import { emit as s } from "../events.js";
9
9
  import { RADIO_DOT as c, RADIO_FACE as l, RADIO_RING as u, RADIO_RING_PRESSED as d } from "../glyphs.js";
10
10
  import { VfToggleControl as f } from "../toggle-control.js";
11
11
  import { LitElement as p, css as m, html as h } from "lit";
12
- import { property as g } from "lit/decorators.js";
13
- import { classMap as _ } from "lit/directives/class-map.js";
12
+ import { property as g, state as _ } from "lit/decorators.js";
13
+ import { classMap as v } from "lit/directives/class-map.js";
14
14
  //#region src/components/vf-radio.ts
15
- var v = class extends n(f(p)) {
15
+ var y = class extends n(f(p)) {
16
16
  static {
17
17
  this.styles = [
18
18
  i,
@@ -82,7 +82,7 @@ var v = class extends n(f(p)) {
82
82
  ];
83
83
  }
84
84
  constructor() {
85
- super(), this.checked = !1, this.disabled = !1, this.value = "", this.groupDisabled = !1, this.internals = this.attachInternals(), this.internals.role = "radio";
85
+ super(), this.checked = !1, this.disabled = !1, this.value = "", this.groupDisabled = !1, this._hasLabel = !1, this.internals = this.attachInternals(), this.internals.role = "radio";
86
86
  }
87
87
  get toggleInternals() {
88
88
  return this.internals;
@@ -97,7 +97,7 @@ var v = class extends n(f(p)) {
97
97
  let e = this.toggleDisabled;
98
98
  return h`
99
99
  <span
100
- class=${_({
100
+ class=${v({
101
101
  circle: !0,
102
102
  dim: e,
103
103
  "vf-snap": !0
@@ -112,14 +112,19 @@ var v = class extends n(f(p)) {
112
112
  <path class="dot" d=${c.d}></path>
113
113
  </svg>
114
114
  </span>
115
- <span class=${_({
115
+ <span class=${v({
116
116
  label: !0,
117
- dim: e
117
+ dim: e,
118
+ empty: !this._hasLabel
118
119
  })} part="label">
119
- <slot></slot>
120
+ <slot @slotchange=${this.#e}></slot>
120
121
  </span>
121
122
  `;
122
123
  }
124
+ #e(e) {
125
+ let t = e.target;
126
+ this._hasLabel = t.assignedNodes({ flatten: !0 }).some((e) => e.nodeType === Node.ELEMENT_NODE || !!e.textContent?.trim());
127
+ }
123
128
  activate() {
124
129
  this.checked || (this.externallyCoordinated || (this.checked = !0), this.focus(), s(this, "vf-change", { value: this.value }));
125
130
  }
@@ -127,9 +132,9 @@ var v = class extends n(f(p)) {
127
132
  t([g({
128
133
  type: Boolean,
129
134
  reflect: !0
130
- })], v.prototype, "checked", void 0), t([g({
135
+ })], y.prototype, "checked", void 0), t([g({
131
136
  type: Boolean,
132
137
  reflect: !0
133
- })], v.prototype, "disabled", void 0), t([g()], v.prototype, "value", void 0), t([g({ attribute: !1 })], v.prototype, "groupDisabled", void 0), v = t([e("vf-radio")], v);
138
+ })], y.prototype, "disabled", void 0), t([g()], y.prototype, "value", void 0), t([g({ attribute: !1 })], y.prototype, "groupDisabled", void 0), t([_()], y.prototype, "_hasLabel", void 0), y = t([e("vf-radio")], y);
134
139
  //#endregion
135
- export { v as VfRadio };
140
+ export { y as VfRadio };
@@ -30,12 +30,18 @@ declare const VfWindow_base: (new (...args: any[]) => import("../size.js").VfSiz
30
30
  * says so once in the console.
31
31
  *
32
32
  * @slot - Default slot: window body content.
33
+ * @slot status - Optional status-bar content — the classic bottom readout
34
+ * strip ("40px x 40px"): a 1px rule over a 15px white band under the body,
35
+ * body-face text on its native line. Takes no space until populated; a
36
+ * `resizable` window's grow box sits flush in its right end.
33
37
  * @csspart frame - The outer chrome frame.
34
38
  * @csspart title-bar - The striped (or dithered) title bar.
35
39
  * @csspart title - The centered title patch (hidden on the utility bar).
36
40
  * @csspart close-box - The close widget (left).
37
41
  * @csspart zoom-box - The zoom widget (right).
38
42
  * @csspart body - The content area.
43
+ * @csspart status-bar - The bottom status strip (when the `status` slot is
44
+ * populated).
39
45
  * @csspart grow-box - The resize widget (bottom-right, when `resizable`).
40
46
  * @csspart viewport - The built-in scroll area's viewport (when `scrollbars`;
41
47
  * re-exported from vf-scroll-area).
@@ -43,6 +49,13 @@ declare const VfWindow_base: (new (...args: any[]) => import("../size.js").VfSiz
43
49
  * compatible with vf-dialog's `vf-close`). The window does NOT
44
50
  * remove itself; the consumer decides what closing means.
45
51
  * @fires vf-zoom - Zoom box clicked. Detail `{}`.
52
+ * @fires vf-resize - The grow box resized the window. Detail `{ width,
53
+ * height, commit }`, sizes in whole system px: one event per size the drag
54
+ * writes (`commit: false`), fired after the new box is applied so a handler
55
+ * that measures reads the resized layout, then a final `commit: true` as
56
+ * the gesture settles — only when it changed the size. Fired by the gesture
57
+ * alone: a programmatic `width`/`height` write fires nothing, the way a
58
+ * value set fires no `vf-change`.
46
59
  * @cssprop --vf-dots-pattern - the windoid bar's dot-grid dither — a 2×2 motif,
47
60
  * one black pixel at its origin, on a 30-system-px tile (`vfDots`; override
48
61
  * the whole tile like `--vf-desktop-pattern` — consumer art renders as a
@@ -51,6 +64,10 @@ declare const VfWindow_base: (new (...args: any[]) => import("../size.js").VfSiz
51
64
  * @cssprop [--vf-titlebar-height-utility=12px] - the slim
52
65
  * `vf-window[variant="utility"]` (windoid) bar — 11px interior + 1px bottom
53
66
  * rule, traced from `Windows/utility-window.png`
67
+ * @cssprop [--vf-status-bar-height=15px] - the status strip: 1px rule + 14px
68
+ * interior — the grow box's own height, so the two compose flush
69
+ * @cssprop [--vf-line-height=12px] - the body face's native line, which the
70
+ * status strip's text rides (whole-pixel centered in the 14px interior)
54
71
  */
55
72
  export declare class VfWindow extends VfWindow_base {
56
73
  #private;
@@ -90,6 +107,8 @@ export declare class VfWindow extends VfWindow_base {
90
107
  * vf-scroll-area) still works for windows that want an inset well instead.
91
108
  */
92
109
  scrollbars?: 'vertical' | 'horizontal' | 'both';
110
+ /** Whether the `status` slot has assigned content (drives the strip). */
111
+ private _hasStatus;
93
112
  /** Default-on display scaling (true 72dpi size); see src/scale.ts. */
94
113
  private readonly scale;
95
114
  /** Device-pixel grid snapping; see src/grid-snap.ts. */
@@ -134,9 +153,16 @@ export declare class VfWindow extends VfWindow_base {
134
153
  * are the same declaration and neither can be re-asserted over the other.
135
154
  * Controllers run before this hook, so the inline style the warning reads is
136
155
  * already written.
156
+ *
157
+ * The stream half of `vf-resize` also fires here, not from the pointermove
158
+ * that wrote the size: by this hook the controller has applied the box, so
159
+ * a handler that measures reads the resized layout — the contract that lets
160
+ * window content follow the grow box without a ResizeObserver.
137
161
  */
138
162
  protected updated(): void;
139
163
  disconnectedCallback(): void;
164
+ /** The `.empty` gate: the strip renders only while the slot is populated. */
165
+ private _onStatusSlotChange;
140
166
  private _onCloseClick;
141
167
  private _onZoomClick;
142
168
  private _onGrowPointerDown;