vintage-frames 0.14.0 → 0.15.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 (55) hide show
  1. package/README.md +16 -0
  2. package/custom-elements.json +271 -27
  3. package/dist/components/vf-button.d.ts +5 -1
  4. package/dist/components/vf-button.js +1 -1
  5. package/dist/components/vf-container.d.ts +3 -1
  6. package/dist/components/vf-container.js +34 -16
  7. package/dist/components/vf-desktop.d.ts +114 -12
  8. package/dist/components/vf-desktop.js +119 -58
  9. package/dist/components/vf-dialog.d.ts +7 -1
  10. package/dist/components/vf-icon-field.d.ts +1 -1
  11. package/dist/components/vf-icon.js +68 -61
  12. package/dist/components/vf-menu-bar.d.ts +5 -0
  13. package/dist/components/vf-menu-bar.js +67 -43
  14. package/dist/components/vf-window.d.ts +13 -0
  15. package/dist/components/vf-window.js +10 -1
  16. package/dist/events.d.ts +3 -2
  17. package/dist/events.js +3 -3
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.js +44 -43
  20. package/dist/modal-dialog.d.ts +31 -4
  21. package/dist/modal-dialog.js +98 -82
  22. package/dist/pattern-fill.d.ts +11 -8
  23. package/dist/pattern-fill.js +2 -4
  24. package/dist/position.d.ts +25 -7
  25. package/dist/position.js +31 -23
  26. package/dist/shell/alert.d.ts +40 -0
  27. package/dist/shell/alert.js +41 -0
  28. package/dist/shell/catalog.d.ts +168 -0
  29. package/dist/shell/catalog.js +278 -0
  30. package/dist/shell/clock.d.ts +16 -0
  31. package/dist/shell/clock.js +36 -0
  32. package/dist/shell/filing.d.ts +39 -0
  33. package/dist/shell/filing.js +68 -0
  34. package/dist/shell/finder.d.ts +156 -0
  35. package/dist/shell/finder.js +598 -0
  36. package/dist/shell/geometry.d.ts +196 -0
  37. package/dist/shell/geometry.js +241 -0
  38. package/dist/shell/index.d.ts +27 -0
  39. package/dist/shell/index.js +10 -0
  40. package/dist/shell/shell.d.ts +164 -0
  41. package/dist/shell/shell.js +234 -0
  42. package/dist/shell/state.d.ts +88 -0
  43. package/dist/shell/state.js +103 -0
  44. package/dist/shell/storage.d.ts +14 -0
  45. package/dist/shell/storage.js +41 -0
  46. package/dist/shell/windows.d.ts +234 -0
  47. package/dist/shell/windows.js +387 -0
  48. package/dist/text-control.d.ts +6 -0
  49. package/dist/text-control.js +2 -2
  50. package/dist/window-chrome.d.ts +43 -0
  51. package/dist/window-chrome.js +13 -0
  52. package/docs/SPEC.md +17 -16
  53. package/editor/vscode.html-custom-data.json +11 -6
  54. package/editor/web-types.json +55 -14
  55. package/package.json +10 -2
@@ -19,7 +19,7 @@ import { property as A, state as j } from "lit/decorators.js";
19
19
  var M, N = {
20
20
  large: 32,
21
21
  small: 16
22
- }, P = 1, F = 8, I = (e, t) => t <= 0 ? Math.max(e, 0) : Math.min(Math.max(e, 0), t), L = class extends c(E) {
22
+ }, P = 1, F = 8, I = (e, t, n) => n <= t ? Math.max(e, t) : Math.min(Math.max(e, t), n), L = "vf-icon-field, [role=\"listbox\"]", R = (e) => e instanceof Element && e.matches(L), z = class extends c(E) {
23
23
  static {
24
24
  M = this;
25
25
  }
@@ -27,14 +27,14 @@ var M, N = {
27
27
  super(...t), this.label = "", this.size = "large", this.selectable = !1, this.selected = !1, this.target = !1, this.color = !1, this.open = !1, this.movable = !1, this.editable = !1, this.maxlength = 31, this.scale = new n(this), this.gridSnap = new m(this), this.focusRule = new T(this), this._editing = !1, this._draft = "", this.#e = 0, this._ghost = null, this.#t = null, this.#n = this.attachInternals(), this.#a = !1, this.#o = "", this.#s = "", this.#c = 0, this.#l = -Infinity, this.#u = !1, this.#d = null, this.#f = null, this.#p = new h(this, () => [[
28
28
  document,
29
29
  "pointerdown",
30
- this.#te,
30
+ this.#ne,
31
31
  !0
32
32
  ]]), this.#m = (e, t, n) => this.#h() ? {
33
33
  x: Math.max(e, 0),
34
- y: Math.max(t, 0)
34
+ y: Math.max(t, n.top)
35
35
  } : {
36
- x: I(e, n.width - a(this.offsetWidth, this)),
37
- y: I(t, n.height - a(this.offsetHeight, this))
36
+ x: I(e, 0, n.width - a(this.offsetWidth, this)),
37
+ y: I(t, n.top, n.height - a(this.offsetHeight, this))
38
38
  }, this.#g = new s(this, (e, t, n) => this.#m(e, t, n)), this.#_ = null, this.#v = new g(this, {
39
39
  onDragStart: (e) => {
40
40
  if (!this.movable || e.button !== 0 || this._editing) return null;
@@ -86,10 +86,16 @@ var M, N = {
86
86
  }, this.#w = (e) => {
87
87
  e.key !== "Escape" || !this.#x || (e.preventDefault(), e.stopPropagation(), this.#x.finish());
88
88
  }, this.#I = !1, this.#B = () => {
89
- this.#H(), this.#K();
89
+ this.#H(), this.#q();
90
90
  }, this.#V = () => {
91
91
  this.#U();
92
- }, this.#Z = (e) => {
92
+ }, this.#K = () => {
93
+ queueMicrotask(() => {
94
+ if (!this._editing) return;
95
+ let e = this.renderRoot.querySelector("input");
96
+ e && this.renderRoot.activeElement === e || this.commitEditing();
97
+ });
98
+ }, this.#Q = (e) => {
93
99
  if (e.button !== 0 || this._editing) return;
94
100
  let t = e.composedPath().some((e) => e instanceof HTMLElement && e.classList.contains("label")), n = this.selected, r = e.timeStamp - this.#l < 800;
95
101
  this.#l = e.timeStamp, this.#G();
@@ -102,43 +108,43 @@ var M, N = {
102
108
  second: i !== null && e.timeStamp - i.time <= 500 && Math.hypot(e.clientX - i.x, e.clientY - i.y) <= 24
103
109
  } : null;
104
110
  let a = !e.shiftKey && !e.metaKey;
105
- this.selectable && (a ? this.#X(!0) : this.#X(!this.selected)), this.#a = a && n && this.#i !== null, this.editable && t && n && !r && !e.shiftKey && !e.metaKey && this.#W(), this.#J && (this.focus({ preventScroll: !0 }), this.focusRule.suppress()), this.#v.onPointerDown(e);
106
- }, this.#Q = () => {
111
+ this.selectable && (a ? this.#Z(!0) : this.#Z(!this.selected)), this.#a = a && n && this.#i !== null, this.editable && t && n && !r && !e.shiftKey && !e.metaKey && this.#W(), this.#Y && (this.focus({ preventScroll: !0 }), this.focusRule.suppress()), this.#v.onPointerDown(e);
112
+ }, this.#$ = () => {
107
113
  this._editing || this.#u || e(this, "vf-open", {});
108
- }, this.#$ = (t) => {
114
+ }, this.#ee = (t) => {
109
115
  let n = this.#_?.proposal != null;
110
116
  this.#v.onPointerUp(t);
111
- let r = this.#ee(t, n);
117
+ let r = this.#te(t, n);
112
118
  if (this.#a) {
113
119
  this.#a = !1;
114
120
  let e = t.type === "pointercancel" ? null : this.#i;
115
121
  for (let t of e?.querySelectorAll("vf-icon") ?? []) t !== this && t.selected && t.setSelected(!1);
116
122
  }
117
123
  r && e(this, "vf-open", {});
118
- }, this.#te = (e) => {
124
+ }, this.#ne = (e) => {
119
125
  let t = e.composedPath();
120
126
  if (t.includes(this) || (this.#G(), this.#f = null, e.shiftKey || e.metaKey)) return;
121
127
  let n = this.#i;
122
128
  if (n !== null) {
123
129
  let e = t.find((e) => e instanceof M);
124
- if (e?.selected && e.#i === n) return;
130
+ if (!e && !t.some(R) || e?.selected && e.#i === n) return;
125
131
  }
126
- this.#X(!1);
127
- }, this.#ne = (e) => {
132
+ this.#Z(!1);
133
+ }, this.#re = (e) => {
128
134
  let t = e.target, n = e.data ?? e.dataTransfer?.getData("text") ?? "";
129
135
  if (n === "") return;
130
136
  let r = t.selectionStart ?? t.value.length, i = t.selectionEnd ?? r, a = t.value.slice(0, r) + n + t.value.slice(i);
131
- a.length > this.maxlength && this.#ie(a);
132
- }, this.#re = (e) => {
137
+ a.length > this.maxlength && this.#ae(a);
138
+ }, this.#ie = (e) => {
133
139
  let t = e.target;
134
140
  if (t.value.length > this.maxlength) {
135
141
  let e = t.value, n = Math.min(t.selectionStart ?? this.maxlength, this.maxlength);
136
- t.value = e.slice(0, this.maxlength), t.setSelectionRange(n, n), this.#ie(e);
142
+ t.value = e.slice(0, this.maxlength), t.setSelectionRange(n, n), this.#ae(e);
137
143
  }
138
144
  this._draft = t.value;
139
- }, this.#ae = (e) => {
145
+ }, this.#oe = (e) => {
140
146
  e.key === "Enter" ? (e.preventDefault(), this.commitEditing(), this.focus()) : e.key === "Escape" && (e.preventDefault(), e.stopPropagation(), this.cancelEditing(), this.focus());
141
- }, this.#oe = (t) => {
147
+ }, this.#se = (t) => {
142
148
  if (this.#G(), !(this._editing || t.defaultPrevented)) {
143
149
  if ((t.metaKey || t.ctrlKey) && !t.altKey && (t.key.toLowerCase() === "o" || t.key === "ArrowDown")) {
144
150
  t.preventDefault(), e(this, "vf-open", {});
@@ -151,7 +157,7 @@ var M, N = {
151
157
  return;
152
158
  case " ":
153
159
  if (!this.selectable) return;
154
- t.preventDefault(), this.focusRule.reveal(), this.#X(!this.selected);
160
+ t.preventDefault(), this.focusRule.reveal(), this.#Z(!this.selected);
155
161
  return;
156
162
  case "ArrowLeft":
157
163
  case "ArrowRight":
@@ -405,7 +411,7 @@ var M, N = {
405
411
  return this.#i !== null;
406
412
  }
407
413
  get #i() {
408
- return this.closest("vf-icon-field, [role=\"listbox\"]");
414
+ return this.closest(L);
409
415
  }
410
416
  #a;
411
417
  #o;
@@ -533,11 +539,11 @@ var M, N = {
533
539
  }, ...e.followers.map((e) => ({
534
540
  corner: r(e.origin, e.offset),
535
541
  size: e.size
536
- }))], a = this.#h(), o = (e, t, n, r) => {
537
- let o = -Infinity, s = Infinity;
538
- for (let e of i) o = Math.max(o, -t(e)), a || (s = Math.min(s, r - n(e) - t(e)));
539
- return s < o && (s = Infinity), Math.min(Math.max(e, o), s);
540
- }, s = o(t.left - e.origin.x, (e) => e.corner.x, (e) => e.size.width, n.width), c = o(t.top - e.origin.y, (e) => e.corner.y, (e) => e.size.height, n.height);
542
+ }))], a = this.#h(), o = (e, t, n, r, o) => {
543
+ let s = -Infinity, c = Infinity;
544
+ for (let e of i) s = Math.max(s, r - t(e)), a || (c = Math.min(c, o - n(e) - t(e)));
545
+ return c < s && (c = Infinity), Math.min(Math.max(e, s), c);
546
+ }, s = o(t.left - e.origin.x, (e) => e.corner.x, (e) => e.size.width, 0, n.width), c = o(t.top - e.origin.y, (e) => e.corner.y, (e) => e.size.height, n.top, n.height);
541
547
  this.#g.moveTo(e.origin.x + s, e.origin.y + c);
542
548
  for (let t of e.followers) t.drop(s, c);
543
549
  }
@@ -601,15 +607,15 @@ var M, N = {
601
607
  };
602
608
  }
603
609
  connectedCallback() {
604
- super.connectedCallback(), this.#Y(), this.#K(), this.selectable && this.selected && this.#p.attach(), this.addEventListener("keydown", this.#oe);
610
+ super.connectedCallback(), this.#X(), this.#q(), this.selectable && this.selected && this.#p.attach(), this.addEventListener("keydown", this.#se);
605
611
  }
606
612
  disconnectedCallback() {
607
- super.disconnectedCallback(), this.removeEventListener("keydown", this.#oe), this.#G(), this.#_?.outline?.canvas.remove();
613
+ super.disconnectedCallback(), this.removeEventListener("keydown", this.#se), this.#G(), this.#_?.outline?.canvas.remove();
608
614
  for (let e of this.#_?.followers ?? []) e.end();
609
615
  this.#_ = null, this.#x?.finish();
610
616
  }
611
617
  updated(e) {
612
- if (e.has("selectable") && this.#Y(), (e.has("selectable") || e.has("selected") || e.has("label")) && this.#K(), (e.has("selected") || e.has("selectable")) && (this.selectable && this.selected ? this.#p.attach() : this.#p.detach()), e.has("_editing") && this._editing) {
618
+ if (e.has("selectable") && this.#X(), (e.has("selectable") || e.has("selected") || e.has("label")) && this.#q(), (e.has("selected") || e.has("selectable")) && (this.selectable && this.selected ? this.#p.attach() : this.#p.detach()), e.has("_editing") && this._editing) {
613
619
  let e = this.renderRoot.querySelector("input");
614
620
  e?.focus(), e?.select();
615
621
  }
@@ -696,6 +702,7 @@ var M, N = {
696
702
  previous: t
697
703
  }));
698
704
  }
705
+ #K;
699
706
  cancelEditing() {
700
707
  this._editing && (this._editing = !1, this._draft = this.#s);
701
708
  }
@@ -705,12 +712,12 @@ var M, N = {
705
712
  class="frame vf-snap"
706
713
  part="frame"
707
714
  style=${o}
708
- @pointerdown=${this.#Z}
715
+ @pointerdown=${this.#Q}
709
716
  @pointermove=${this.#v.onPointerMove}
710
- @pointerup=${this.#$}
711
- @pointercancel=${this.#$}
712
- @lostpointercapture=${this.#$}
713
- @dblclick=${this.#Q}
717
+ @pointerup=${this.#ee}
718
+ @pointercancel=${this.#ee}
719
+ @lostpointercapture=${this.#ee}
720
+ @dblclick=${this.#$}
714
721
  >
715
722
  <div class=${a} part="icon">
716
723
  ${this.size === "small" ? O`<slot name="small" @slotchange=${this.#B}></slot>` : O`<slot name="large" @slotchange=${this.#B}></slot>`}${this._ghost ?? k}
@@ -731,16 +738,16 @@ var M, N = {
731
738
  type="text"
732
739
  maxlength=${this.maxlength}
733
740
  .value=${this._draft}
734
- @beforeinput=${this.#ne}
735
- @input=${this.#re}
736
- @keydown=${this.#ae}
737
- @blur=${this.commitEditing}
741
+ @beforeinput=${this.#re}
742
+ @input=${this.#ie}
743
+ @keydown=${this.#oe}
744
+ @blur=${this.#K}
738
745
  />
739
746
  </div>` : k}
740
747
  </div>` : k}
741
748
  </div>`;
742
749
  }
743
- #K() {
750
+ #q() {
744
751
  if (!this.selectable) {
745
752
  this.#n.role = null, this.#n.ariaSelected = null, this.#n.ariaLabel = null;
746
753
  return;
@@ -749,22 +756,22 @@ var M, N = {
749
756
  this.#n.role = "option", this.#n.ariaSelected = this.selected ? "true" : "false", this.#n.ariaLabel = null;
750
757
  return;
751
758
  }
752
- this.#n.role = "img", this.#n.ariaSelected = null, this.#n.ariaLabel = this.label || this.#q || null;
759
+ this.#n.role = "img", this.#n.ariaSelected = null, this.#n.ariaLabel = this.label || this.#J || null;
753
760
  }
754
- get #q() {
761
+ get #J() {
755
762
  return this.#t instanceof HTMLImageElement ? this.#t.alt : "";
756
763
  }
757
- get #J() {
764
+ get #Y() {
758
765
  return this.selectable;
759
766
  }
760
- #Y() {
761
- this.hasAttribute("tabindex") || (this.#J ? this.tabIndex = 0 : this.removeAttribute("tabindex"));
767
+ #X() {
768
+ this.hasAttribute("tabindex") || (this.#Y ? this.tabIndex = 0 : this.removeAttribute("tabindex"));
762
769
  }
763
- #X(t) {
770
+ #Z(t) {
764
771
  t !== this.selected && (this.selected = t, e(this, "vf-select", { selected: t }));
765
772
  }
766
773
  setSelected(e) {
767
- this.#X(e);
774
+ this.#Z(e);
768
775
  }
769
776
  touches(e) {
770
777
  let t = this.renderRoot;
@@ -794,10 +801,10 @@ var M, N = {
794
801
  }), this.#x = t, this.#S.attach();
795
802
  })) : (this.#g.moveTo(r.x, r.y), !1);
796
803
  }
797
- #Z;
798
804
  #Q;
799
805
  #$;
800
- #ee(e, t) {
806
+ #ee;
807
+ #te(e, t) {
801
808
  let n = this.#d;
802
809
  return !n || n.pointerId !== e.pointerId || (this.#d = null, !(e.type !== "pointercancel" && !t && !this._editing && Math.hypot(e.clientX - n.x, e.clientY - n.y) < _(e))) ? !1 : n.second ? !0 : (this.#f = {
803
810
  time: n.time,
@@ -805,40 +812,40 @@ var M, N = {
805
812
  y: n.y
806
813
  }, !1);
807
814
  }
808
- #te;
809
815
  #ne;
810
816
  #re;
811
- #ie(t) {
817
+ #ie;
818
+ #ae(t) {
812
819
  e(this, "vf-name-too-long", {
813
820
  attempted: t,
814
821
  accepted: t.slice(0, this.maxlength),
815
822
  limit: this.maxlength
816
823
  });
817
824
  }
818
- #ae;
819
825
  #oe;
826
+ #se;
820
827
  };
821
- o([A()], L.prototype, "label", void 0), o([A({ reflect: !0 })], L.prototype, "size", void 0), o([A({
828
+ o([A()], z.prototype, "label", void 0), o([A({ reflect: !0 })], z.prototype, "size", void 0), o([A({
822
829
  type: Boolean,
823
830
  reflect: !0
824
- })], L.prototype, "selectable", void 0), o([A({
831
+ })], z.prototype, "selectable", void 0), o([A({
825
832
  type: Boolean,
826
833
  reflect: !0
827
- })], L.prototype, "selected", void 0), o([A({
834
+ })], z.prototype, "selected", void 0), o([A({
828
835
  type: Boolean,
829
836
  reflect: !0
830
- })], L.prototype, "target", void 0), o([A({
837
+ })], z.prototype, "target", void 0), o([A({
831
838
  type: Boolean,
832
839
  reflect: !0
833
- })], L.prototype, "color", void 0), o([A({
840
+ })], z.prototype, "color", void 0), o([A({
834
841
  type: Boolean,
835
842
  reflect: !0
836
- })], L.prototype, "open", void 0), o([A({
843
+ })], z.prototype, "open", void 0), o([A({
837
844
  type: Boolean,
838
845
  reflect: !0
839
- })], L.prototype, "movable", void 0), o([A({
846
+ })], z.prototype, "movable", void 0), o([A({
840
847
  type: Boolean,
841
848
  reflect: !0
842
- })], L.prototype, "editable", void 0), o([A({ type: Number })], L.prototype, "width", void 0), o([A({ type: Number })], L.prototype, "maxlength", void 0), o([j()], L.prototype, "_editing", void 0), o([j()], L.prototype, "_draft", void 0), o([j()], L.prototype, "_ghost", void 0), L = M = o([t("vf-icon")], L);
849
+ })], z.prototype, "editable", void 0), o([A({ type: Number })], z.prototype, "width", void 0), o([A({ type: Number })], z.prototype, "maxlength", void 0), o([j()], z.prototype, "_editing", void 0), o([j()], z.prototype, "_draft", void 0), o([j()], z.prototype, "_ghost", void 0), z = M = o([t("vf-icon")], z);
843
850
  //#endregion
844
- export { L as VfIcon };
851
+ export { z as VfIcon };
@@ -18,6 +18,9 @@ declare const VfMenuBar_base: (new (...args: any[]) => import("../position.js").
18
18
  * same mechanic.
19
19
  *
20
20
  * @slot - `vf-menu` elements.
21
+ * @slot end - The bar's right end: a clock, say. Held 9px in from the end,
22
+ * and a `vf-label` there rides the bar's line, so its baseline is the
23
+ * menu titles'.
21
24
  * @csspart bar - The horizontal layout container.
22
25
  * @cssprop [--vf-menubar-height=20px] - `vf-menu-bar`
23
26
  */
@@ -52,6 +55,8 @@ export declare class VfMenuBar extends VfMenuBar_base {
52
55
  */
53
56
  shortcuts: boolean;
54
57
  private _menus;
58
+ /** Whether the `end` slot has assigned content (drives its `.empty` gate). */
59
+ private _hasEnd;
55
60
  connectedCallback(): void;
56
61
  disconnectedCallback(): void;
57
62
  /**
@@ -10,53 +10,53 @@ import { SCREEN_CORNER as c, steppedCornerClip as l } from "../pixel-frame.js";
10
10
  import { TypeAheadBuffer as u } from "../type-ahead.js";
11
11
  import { MenuPressController as d } from "../menu-press.js";
12
12
  import { LitElement as f, css as p, html as m, unsafeCSS as h } from "lit";
13
- import { property as g, queryAssignedElements as _ } from "lit/decorators.js";
13
+ import { property as g, queryAssignedElements as _, state as v } from "lit/decorators.js";
14
14
  //#region src/components/vf-menu-bar.ts
15
- var v = class extends r(f) {
15
+ var y = class extends r(f) {
16
16
  constructor(...e) {
17
- super(...e), this.scale = new t(this), this.gridSnap = new o(this), this.label = "", this.rounded = !1, this.shortcuts = !1, this.#e = null, this.#t = this.attachInternals(), this.#n = new u(), this.#r = null, this.#i = new s(this, () => [[
17
+ super(...e), this.scale = new t(this), this.gridSnap = new o(this), this.label = "", this.rounded = !1, this.shortcuts = !1, this._hasEnd = !1, this.#e = null, this.#t = this.attachInternals(), this.#n = new u(), this.#r = null, this.#i = new s(this, () => [[
18
18
  document,
19
19
  "pointerdown",
20
- this.#h,
20
+ this.#g,
21
21
  !0
22
22
  ], [
23
23
  document,
24
24
  "keydown",
25
- this.#g,
25
+ this.#_,
26
26
  !0
27
27
  ]]), this.#a = new d(this, {
28
28
  menus: () => this._menus,
29
- open: (e) => this.#p(e),
30
- close: () => this.#m()
29
+ open: (e) => this.#m(e),
30
+ close: () => this.#h()
31
31
  }), this.#o = (e) => {
32
32
  if (!this.#e) return;
33
33
  let t = e.relatedTarget;
34
- t instanceof Node && (this.contains(t) || this.renderRoot.contains(t)) || this.#m();
35
- }, this.#u = (e) => {
36
- let t = e.detail;
37
- e.preventDefault(), e.stopPropagation(), t.menu.open ? this.#m() : this.#p(t.menu);
34
+ t instanceof Node && (this.contains(t) || this.renderRoot.contains(t)) || this.#h();
38
35
  }, this.#d = (e) => {
39
36
  let t = e.detail;
40
- this.#e && t.menu !== this.#e && this.#p(t.menu);
41
- }, this.#f = () => {
42
- this.#e?.focus(), this.#m();
43
- }, this.#h = (e) => {
44
- e.composedPath().includes(this) || this.#m();
37
+ e.preventDefault(), e.stopPropagation(), t.menu.open ? this.#h() : this.#m(t.menu);
38
+ }, this.#f = (e) => {
39
+ let t = e.detail;
40
+ this.#e && t.menu !== this.#e && this.#m(t.menu);
41
+ }, this.#p = () => {
42
+ this.#e?.focus(), this.#h();
45
43
  }, this.#g = (e) => {
44
+ e.composedPath().includes(this) || this.#h();
45
+ }, this.#_ = (e) => {
46
46
  if (!(e.defaultPrevented || !this.#e)) switch (e.key) {
47
47
  case "Escape":
48
- e.preventDefault(), this.#e.focus(), this.#m();
48
+ e.preventDefault(), this.#e.focus(), this.#h();
49
49
  break;
50
50
  case "Tab":
51
- this.#m();
51
+ this.#h();
52
52
  break;
53
53
  case "ArrowLeft":
54
54
  case "ArrowRight":
55
- e.preventDefault(), this.#v(e.key === "ArrowRight" ? 1 : -1);
55
+ e.preventDefault(), this.#y(e.key === "ArrowRight" ? 1 : -1);
56
56
  break;
57
57
  case "ArrowDown":
58
58
  case "ArrowUp":
59
- e.preventDefault(), this.#y(e.key === "ArrowDown" ? 1 : -1);
59
+ e.preventDefault(), this.#b(e.key === "ArrowDown" ? 1 : -1);
60
60
  break;
61
61
  case "Home":
62
62
  case "End": {
@@ -76,7 +76,7 @@ var v = class extends r(f) {
76
76
  break;
77
77
  }
78
78
  }
79
- }, this.#_ = (e) => {
79
+ }, this.#v = (e) => {
80
80
  if (this.#e) return;
81
81
  let t = this._menus;
82
82
  if (t.length === 0) return;
@@ -98,7 +98,7 @@ var v = class extends r(f) {
98
98
  break;
99
99
  default: return;
100
100
  }
101
- e.preventDefault(), i && (this.#l(i), i.focus());
101
+ e.preventDefault(), i && (this.#u(i), i.focus());
102
102
  };
103
103
  }
104
104
  static {
@@ -141,6 +141,22 @@ var v = class extends r(f) {
141
141
  ::slotted(vf-menu) {
142
142
  margin-inline-start: calc(var(--vf-scale, 1) * -5px);
143
143
  }
144
+ /* The bar's right end (slot="end"): a clock, say. A row of its own,
145
+ pushed to the end and held 9px in, mirroring the first title's
146
+ inset. A caption in it rides the bar's line, so its baseline is the
147
+ titles'. */
148
+ slot[name='end'] {
149
+ display: flex;
150
+ align-items: stretch;
151
+ margin-inline-start: auto;
152
+ padding-inline-end: calc(var(--vf-scale, 1) * 9px);
153
+ --vf-label-line-height: var(--vf-menubar-height, 20px);
154
+ }
155
+ /* Empty, it takes no part in the row: a page that pushes its own item
156
+ to the end with an auto margin keeps the whole of the slack. */
157
+ slot[name='end'].empty {
158
+ display: none;
159
+ }
144
160
  /* rounded: the screen-corner mask. The corners aren't a shape of the
145
161
  bar's own — they're the black staircase the ROM painted over the
146
162
  CRT's rounded corners, worn by the menu bar because it sat flush with
@@ -178,10 +194,10 @@ var v = class extends r(f) {
178
194
  #i;
179
195
  #a;
180
196
  connectedCallback() {
181
- super.connectedCallback(), this.#t.role = "menubar", this.addEventListener("vf-menu-toggle-request", this.#u), this.addEventListener("vf-menu-hover", this.#d), this.addEventListener("vf-menu-close-request", this.#f), this.addEventListener("keydown", this.#_), this.addEventListener("pointerdown", this.#a.onPointerDown), this.addEventListener("focusout", this.#o);
197
+ super.connectedCallback(), this.#t.role = "menubar", this.addEventListener("vf-menu-toggle-request", this.#d), this.addEventListener("vf-menu-hover", this.#f), this.addEventListener("vf-menu-close-request", this.#p), this.addEventListener("keydown", this.#v), this.addEventListener("pointerdown", this.#a.onPointerDown), this.addEventListener("focusout", this.#o);
182
198
  }
183
199
  disconnectedCallback() {
184
- super.disconnectedCallback(), this.removeEventListener("vf-menu-toggle-request", this.#u), this.removeEventListener("vf-menu-hover", this.#d), this.removeEventListener("vf-menu-close-request", this.#f), this.removeEventListener("keydown", this.#_), this.removeEventListener("pointerdown", this.#a.onPointerDown), this.removeEventListener("focusout", this.#o), this.#e = null, this.#r = null;
200
+ super.disconnectedCallback(), this.removeEventListener("vf-menu-toggle-request", this.#d), this.removeEventListener("vf-menu-hover", this.#f), this.removeEventListener("vf-menu-close-request", this.#p), this.removeEventListener("keydown", this.#v), this.removeEventListener("pointerdown", this.#a.onPointerDown), this.removeEventListener("focusout", this.#o), this.#e = null, this.#r = null;
185
201
  }
186
202
  focus(e) {
187
203
  (this.#e ?? this.#r ?? this._menus[0])?.focus(e);
@@ -193,41 +209,49 @@ var v = class extends r(f) {
193
209
  render() {
194
210
  return m`
195
211
  <div class="bar vf-snap vf-rule-bottom" part="bar" role="presentation">
196
- <slot @slotchange=${this.#s}></slot>
212
+ <slot @slotchange=${this.#c}></slot>
213
+ <slot
214
+ name="end"
215
+ class=${this._hasEnd ? "" : "empty"}
216
+ @slotchange=${this.#s}
217
+ ></slot>
197
218
  </div>
198
219
  `;
199
220
  }
200
- #s() {
201
- this.#e && !this._menus.includes(this.#e) && this.#m(), this.#r && !this._menus.includes(this.#r) && (this.#r = null), this.#c();
221
+ #s(e) {
222
+ this._hasEnd = e.target.assignedElements().length > 0;
202
223
  }
203
224
  #c() {
225
+ this.#e && !this._menus.includes(this.#e) && this.#h(), this.#r && !this._menus.includes(this.#r) && (this.#r = null), this.#l();
226
+ }
227
+ #l() {
204
228
  let e = this._menus, t = this.#e ?? this.#r ?? e[0];
205
229
  for (let n of e) n.barTabIndex = n === t ? 0 : -1;
206
230
  }
207
- #l(e) {
208
- this.#r = e, this.#c();
231
+ #u(e) {
232
+ this.#r = e, this.#l();
209
233
  }
210
- #u;
211
234
  #d;
212
235
  #f;
213
- #p(e) {
236
+ #p;
237
+ #m(e) {
214
238
  for (let t of this._menus) t !== e && (t.open = !1);
215
- e.open = !0, this.#e = e, this.#r = e, this.#n.reset(), this.#c(), this.#i.attach();
239
+ e.open = !0, this.#e = e, this.#r = e, this.#n.reset(), this.#l(), this.#i.attach();
216
240
  }
217
- #m() {
241
+ #h() {
218
242
  for (let e of this._menus) e.open = !1;
219
- this.#e = null, this.#n.reset(), this.#i.detach(), this.#c();
243
+ this.#e = null, this.#n.reset(), this.#i.detach(), this.#l();
220
244
  }
221
- #h;
222
245
  #g;
223
246
  #_;
224
- #v(e) {
247
+ #v;
248
+ #y(e) {
225
249
  let t = this._menus;
226
250
  if (!this.#e || t.length === 0) return;
227
251
  let n = t[(t.indexOf(this.#e) + e + t.length) % t.length];
228
- n && n !== this.#e && (this.#p(n), n.focus());
252
+ n && n !== this.#e && (this.#m(n), n.focus());
229
253
  }
230
- #y(e) {
254
+ #b(e) {
231
255
  if (!this.#e) return;
232
256
  let t = this.#e.items;
233
257
  if (t.length === 0) return;
@@ -235,15 +259,15 @@ var v = class extends r(f) {
235
259
  r = n < 0 ? e === 1 ? 0 : t.length - 1 : (n + e + t.length) % t.length, t[r]?.focus();
236
260
  }
237
261
  };
238
- n([g()], v.prototype, "label", void 0), n([g({
262
+ n([g()], y.prototype, "label", void 0), n([g({
239
263
  type: Boolean,
240
264
  reflect: !0
241
- })], v.prototype, "rounded", void 0), n([g({
265
+ })], y.prototype, "rounded", void 0), n([g({
242
266
  type: Boolean,
243
267
  reflect: !0
244
- })], v.prototype, "shortcuts", void 0), n([_({
268
+ })], y.prototype, "shortcuts", void 0), n([_({
245
269
  selector: "vf-menu",
246
270
  flatten: !0
247
- })], v.prototype, "_menus", void 0), v = n([e("vf-menu-bar")], v);
271
+ })], y.prototype, "_menus", void 0), n([v()], y.prototype, "_hasEnd", void 0), y = n([e("vf-menu-bar")], y);
248
272
  //#endregion
249
- export { v as VfMenuBar };
273
+ export { y as VfMenuBar };
@@ -190,6 +190,19 @@ export declare class VfWindow extends VfWindow_base {
190
190
  maxWidth?: number | null;
191
191
  /** See {@link minWidth}. Unbounded by default. */
192
192
  maxHeight?: number | null;
193
+ /**
194
+ * The sizeRect as it applies, in whole system px: `min-width` and
195
+ * `min-height`, or the 80×54 floor where unset, and `max-width` and
196
+ * `max-height`, `Infinity` where unbounded — the range the grow box drags
197
+ * the window in. A page that sizes the window itself, re-fitting it to a
198
+ * smaller screen say, reads it to stay inside the same range.
199
+ */
200
+ get sizeLimits(): {
201
+ minWidth: number;
202
+ minHeight: number;
203
+ maxWidth: number;
204
+ maxHeight: number;
205
+ };
193
206
  /**
194
207
  * Put System 7 scroll rails on the window edge — the classic document
195
208
  * window. The body slot renders inside a built-in `vf-scroll-area` pulled
@@ -18,6 +18,7 @@ import { DragController as j } from "../drag.js";
18
18
  import { TitleCenterController as M, chromeTitleBar as N, closeBox as P, widgetLabel as F, zoomBox as I } from "../chrome.js";
19
19
  import { prefersReducedMotion as L, windowRectFraction as R } from "../motion.js";
20
20
  import { paintSelectionRect as z, penPhase as B, xorPenCanvas as V } from "../open-art.js";
21
+ import "../window-chrome.js";
21
22
  import "./vf-scroll-area.js";
22
23
  import { LitElement as H, css as U, html as W, nothing as G } from "lit";
23
24
  import { property as K, query as q, state as J } from "lit/decorators.js";
@@ -37,7 +38,7 @@ var Q = 24, $ = class extends A(l(H)) {
37
38
  let r = o(this.offsetWidth, this);
38
39
  return {
39
40
  x: Math.min(Math.max(e, Q - r), n.width - Q),
40
- y: Math.min(Math.max(t, 0), Math.max(0, n.height - Q))
41
+ y: Math.min(Math.max(t, n.top), Math.max(n.top, n.height - Q))
41
42
  };
42
43
  }, this._placement = new c(this, (e, t, n) => this.#t(e, t, n)), this._drag = new j(this, {
43
44
  onDragStart: (e) => {
@@ -376,6 +377,14 @@ var Q = 24, $ = class extends A(l(H)) {
376
377
  `
377
378
  ];
378
379
  }
380
+ get sizeLimits() {
381
+ return {
382
+ minWidth: this.minWidth ?? Y,
383
+ minHeight: this.minHeight ?? X,
384
+ maxWidth: this.maxWidth ?? Infinity,
385
+ maxHeight: this.maxHeight ?? Infinity
386
+ };
387
+ }
379
388
  #e;
380
389
  willUpdate(e) {
381
390
  super.willUpdate(e), this.variant === "utility" && (this._dotsPattern = m(this, "--vf-dots-pattern"));
package/dist/events.d.ts CHANGED
@@ -46,7 +46,8 @@ export declare function emitNative(host: EventTarget, type: 'input' | 'change'):
46
46
  /**
47
47
  * Run a control's **activation behavior** the way HTML runs a native one's: at
48
48
  * the *end* of the click's propagation, and not at all if anything on the path
49
- * called `preventDefault()`.
49
+ * called `preventDefault()`. A form's `submit` defers the same way: the
50
+ * listener waits on the event's own type.
50
51
  *
51
52
  * This is what makes `preventDefault()` on a native checkbox — or on any
52
53
  * ancestor — actually stop it from checking. A handler that acts where it sits
@@ -73,7 +74,7 @@ export declare function emitNative(host: EventTarget, type: 'input' | 'change'):
73
74
  * submit proxy's re-entrancy guard woven through it.
74
75
  *
75
76
  * @param host the element whose ownerDocument supplies the window to defer on
76
- * @param event the click being deferred; its `defaultPrevented` is the verdict
77
+ * @param event the click (or submit) being deferred; its `defaultPrevented` is the verdict
77
78
  * @param action run once, at the end of the path, unless cancelled
78
79
  */
79
80
  export declare function deferActivation(host: Element, event: Event, action: () => void): void;
package/dist/events.js CHANGED
@@ -16,10 +16,10 @@ function t(e, t) {
16
16
  function n(e, t, n) {
17
17
  let r = e.ownerDocument.defaultView;
18
18
  if (!r) return;
19
- let i = 0, a = () => {
20
- r.removeEventListener("click", a), r.clearTimeout(i), !t.defaultPrevented && n();
19
+ let i = t.type, a = 0, o = () => {
20
+ r.removeEventListener(i, o), r.clearTimeout(a), !t.defaultPrevented && n();
21
21
  };
22
- r.addEventListener("click", a), i = r.setTimeout(a, 0);
22
+ r.addEventListener(i, o), a = r.setTimeout(o, 0);
23
23
  }
24
24
  //#endregion
25
25
  export { n as deferActivation, e as emit, t as emitNative };
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@
13
13
  export { VfDesktop } from './components/vf-desktop.js';
14
14
  export { VfWindow } from './components/vf-window.js';
15
15
  export type { VfViewportBox, VfWindowHideOptions, VfWindowShowOptions, } from './components/vf-window.js';
16
+ export { windowChrome, WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT } from './window-chrome.js';
17
+ export type { VfInsets, VfWindowChromeOptions } from './window-chrome.js';
16
18
  export { VfDialog } from './components/vf-dialog.js';
17
19
  export { VfSeparator } from './components/vf-separator.js';
18
20
  export { VfButton } from './components/vf-button.js';
@@ -172,7 +174,7 @@ export type { PlacementBounds, PlacementClamp, PlacementOrigin, VfPositionedInte
172
174
  export { VfSized } from './size.js';
173
175
  export type { VfSizedInterface } from './size.js';
174
176
  export { VfModalDialog, modalDialogStyles } from './modal-dialog.js';
175
- export type { VfCloseReason } from './modal-dialog.js';
177
+ export type { VfCloseDetail, VfCloseReason } from './modal-dialog.js';
176
178
  /** The embedded System 7 bitmap faces and the registration helper. */
177
179
  export { registerEmbeddedFont, PIXEL_GRID_METRICS } from './styles/register-embedded-font.js';
178
180
  export type { EmbeddedFontMetrics } from './styles/register-embedded-font.js';