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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +262 -0
  3. package/custom-elements.json +19807 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js +9 -0
  5. package/dist/chrome.d.ts +73 -0
  6. package/dist/chrome.js +94 -0
  7. package/dist/components/vf-button-group.d.ts +50 -0
  8. package/dist/components/vf-button-group.js +68 -0
  9. package/dist/components/vf-button.d.ts +169 -0
  10. package/dist/components/vf-button.js +234 -0
  11. package/dist/components/vf-checkbox.d.ts +60 -0
  12. package/dist/components/vf-checkbox.js +135 -0
  13. package/dist/components/vf-container.d.ts +95 -0
  14. package/dist/components/vf-container.js +86 -0
  15. package/dist/components/vf-desktop.d.ts +243 -0
  16. package/dist/components/vf-desktop.js +310 -0
  17. package/dist/components/vf-dialog.d.ts +105 -0
  18. package/dist/components/vf-dialog.js +280 -0
  19. package/dist/components/vf-fieldset.d.ts +34 -0
  20. package/dist/components/vf-fieldset.js +76 -0
  21. package/dist/components/vf-grid.d.ts +124 -0
  22. package/dist/components/vf-grid.js +133 -0
  23. package/dist/components/vf-icon.d.ts +356 -0
  24. package/dist/components/vf-icon.js +507 -0
  25. package/dist/components/vf-img.d.ts +63 -0
  26. package/dist/components/vf-img.js +65 -0
  27. package/dist/components/vf-label.d.ts +117 -0
  28. package/dist/components/vf-label.js +151 -0
  29. package/dist/components/vf-list-item.d.ts +48 -0
  30. package/dist/components/vf-list-item.js +98 -0
  31. package/dist/components/vf-list.d.ts +77 -0
  32. package/dist/components/vf-list.js +256 -0
  33. package/dist/components/vf-menu-bar.d.ts +64 -0
  34. package/dist/components/vf-menu-bar.js +236 -0
  35. package/dist/components/vf-menu-item.d.ts +91 -0
  36. package/dist/components/vf-menu-item.js +252 -0
  37. package/dist/components/vf-menu.d.ts +95 -0
  38. package/dist/components/vf-menu.js +346 -0
  39. package/dist/components/vf-number-field.d.ts +60 -0
  40. package/dist/components/vf-number-field.js +248 -0
  41. package/dist/components/vf-option.d.ts +49 -0
  42. package/dist/components/vf-option.js +119 -0
  43. package/dist/components/vf-paragraph.d.ts +73 -0
  44. package/dist/components/vf-paragraph.js +66 -0
  45. package/dist/components/vf-progress-bar.d.ts +69 -0
  46. package/dist/components/vf-progress-bar.js +272 -0
  47. package/dist/components/vf-radio-group.d.ts +109 -0
  48. package/dist/components/vf-radio-group.js +105 -0
  49. package/dist/components/vf-radio.d.ts +59 -0
  50. package/dist/components/vf-radio.js +135 -0
  51. package/dist/components/vf-scroll-area.d.ts +75 -0
  52. package/dist/components/vf-scroll-area.js +124 -0
  53. package/dist/components/vf-select.d.ts +334 -0
  54. package/dist/components/vf-select.js +658 -0
  55. package/dist/components/vf-separator.d.ts +38 -0
  56. package/dist/components/vf-separator.js +55 -0
  57. package/dist/components/vf-slider.d.ts +80 -0
  58. package/dist/components/vf-slider.js +255 -0
  59. package/dist/components/vf-stack.d.ts +188 -0
  60. package/dist/components/vf-stack.js +168 -0
  61. package/dist/components/vf-swatch.d.ts +98 -0
  62. package/dist/components/vf-swatch.js +201 -0
  63. package/dist/components/vf-text-area.d.ts +77 -0
  64. package/dist/components/vf-text-area.js +121 -0
  65. package/dist/components/vf-text-field.d.ts +53 -0
  66. package/dist/components/vf-text-field.js +79 -0
  67. package/dist/components/vf-window.d.ts +157 -0
  68. package/dist/components/vf-window.js +377 -0
  69. package/dist/cursor-art.d.ts +37 -0
  70. package/dist/cursor-art.js +41 -0
  71. package/dist/cursor.d.ts +49 -0
  72. package/dist/cursor.js +171 -0
  73. package/dist/define.d.ts +45 -0
  74. package/dist/define.js +15 -0
  75. package/dist/document-listeners.d.ts +62 -0
  76. package/dist/document-listeners.js +29 -0
  77. package/dist/drag.d.ts +49 -0
  78. package/dist/drag.js +30 -0
  79. package/dist/events.d.ts +79 -0
  80. package/dist/events.js +25 -0
  81. package/dist/focus-modality.d.ts +94 -0
  82. package/dist/focus-modality.js +56 -0
  83. package/dist/form-control.d.ts +247 -0
  84. package/dist/form-control.js +127 -0
  85. package/dist/glyphs.d.ts +117 -0
  86. package/dist/glyphs.js +100 -0
  87. package/dist/grid-snap.d.ts +73 -0
  88. package/dist/grid-snap.js +135 -0
  89. package/dist/index.d.ts +145 -0
  90. package/dist/index.js +71 -0
  91. package/dist/menu-press.d.ts +60 -0
  92. package/dist/menu-press.js +97 -0
  93. package/dist/modal-dialog.d.ts +148 -0
  94. package/dist/modal-dialog.js +155 -0
  95. package/dist/motion.d.ts +72 -0
  96. package/dist/motion.js +22 -0
  97. package/dist/number.d.ts +24 -0
  98. package/dist/number.js +12 -0
  99. package/dist/open-art.d.ts +40 -0
  100. package/dist/open-art.js +35 -0
  101. package/dist/pixel-frame.d.ts +102 -0
  102. package/dist/pixel-frame.js +77 -0
  103. package/dist/popup-overflow.d.ts +157 -0
  104. package/dist/popup-overflow.js +45 -0
  105. package/dist/position.d.ts +187 -0
  106. package/dist/position.js +74 -0
  107. package/dist/scale.d.ts +259 -0
  108. package/dist/scale.js +133 -0
  109. package/dist/scroll-rail.d.ts +112 -0
  110. package/dist/scroll-rail.js +194 -0
  111. package/dist/scroll-state.d.ts +113 -0
  112. package/dist/scroll-state.js +55 -0
  113. package/dist/size.d.ts +47 -0
  114. package/dist/size.js +30 -0
  115. package/dist/styles/base.d.ts +38 -0
  116. package/dist/styles/body-font.d.ts +7 -0
  117. package/dist/styles/body-font.js +9 -0
  118. package/dist/styles/display-font.d.ts +7 -0
  119. package/dist/styles/display-font.js +9 -0
  120. package/dist/styles/recipes/body-face.d.ts +14 -0
  121. package/dist/styles/recipes/body-face.js +6 -0
  122. package/dist/styles/recipes/display-face.d.ts +23 -0
  123. package/dist/styles/recipes/display-face.js +10 -0
  124. package/dist/styles/recipes/field.d.ts +20 -0
  125. package/dist/styles/recipes/field.js +68 -0
  126. package/dist/styles/recipes/focus.d.ts +76 -0
  127. package/dist/styles/recipes/focus.js +39 -0
  128. package/dist/styles/recipes/host.d.ts +5 -0
  129. package/dist/styles/recipes/host.js +62 -0
  130. package/dist/styles/recipes/pattern.d.ts +80 -0
  131. package/dist/styles/recipes/pattern.js +82 -0
  132. package/dist/styles/recipes/scroll-rail.d.ts +69 -0
  133. package/dist/styles/recipes/scroll-rail.js +222 -0
  134. package/dist/styles/recipes/shadow.d.ts +13 -0
  135. package/dist/styles/recipes/shadow.js +5 -0
  136. package/dist/styles/recipes/static-text.d.ts +24 -0
  137. package/dist/styles/recipes/static-text.js +17 -0
  138. package/dist/styles/recipes/surface.d.ts +28 -0
  139. package/dist/styles/recipes/surface.js +27 -0
  140. package/dist/styles/recipes/tile.d.ts +122 -0
  141. package/dist/styles/recipes/tile.js +40 -0
  142. package/dist/styles/recipes/title-bar.d.ts +49 -0
  143. package/dist/styles/recipes/title-bar.js +134 -0
  144. package/dist/styles/recipes/toggle.d.ts +10 -0
  145. package/dist/styles/recipes/toggle.js +20 -0
  146. package/dist/styles/register-embedded-font.d.ts +64 -0
  147. package/dist/styles/register-embedded-font.js +30 -0
  148. package/dist/text-control.d.ts +131 -0
  149. package/dist/text-control.js +83 -0
  150. package/dist/tile-grid.d.ts +111 -0
  151. package/dist/tile-grid.js +53 -0
  152. package/dist/toggle-control.d.ts +60 -0
  153. package/dist/toggle-control.js +41 -0
  154. package/dist/track-width.d.ts +42 -0
  155. package/dist/track-width.js +30 -0
  156. package/dist/type-ahead.d.ts +35 -0
  157. package/dist/type-ahead.js +20 -0
  158. package/dist/zoom.d.ts +204 -0
  159. package/dist/zoom.js +105 -0
  160. package/docs/SPEC.md +2471 -0
  161. package/editor/vscode.html-custom-data.json +1320 -0
  162. package/editor/web-types.json +3237 -0
  163. package/package.json +118 -0
@@ -0,0 +1,256 @@
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 { vfFocusRing as a } from "../styles/recipes/focus.js";
7
+ import { vfScrollRail as o } from "../styles/recipes/scroll-rail.js";
8
+ import { GridSnapController as s } from "../grid-snap.js";
9
+ import { emit as c } from "../events.js";
10
+ import { ScrollStateController as l } from "../scroll-state.js";
11
+ import { ScrollRailController as u, renderScrollRail as d } from "../scroll-rail.js";
12
+ import { TypeAheadBuffer as f } from "../type-ahead.js";
13
+ import { LitElement as p, css as m, html as h, nothing as g } from "lit";
14
+ import { property as _, query as v, queryAssignedElements as y, state as b } from "lit/decorators.js";
15
+ //#region src/components/vf-list.ts
16
+ var x = (e, t) => e.length === t.length && e.every((e, n) => e === t[n]), S = class extends r(p) {
17
+ static {
18
+ this.styles = [
19
+ i,
20
+ o,
21
+ m`
22
+ :host {
23
+ display: block;
24
+ }
25
+ /* Disabled: the item text dims to gray; the black box border stays. */
26
+ :host([disabled]) {
27
+ color: var(--vf-disabled, #c0c0c0);
28
+ }
29
+ /* The snapped wrapper: a real 1px frame (the rail's outer line), the
30
+ [rows | rail] grid — the rail sizes itself to the 15px inside the
31
+ frame — and everything riding the grid-snap offset as one. */
32
+ .box {
33
+ border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
34
+ background: var(--vf-white, #fff);
35
+ display: grid;
36
+ grid-template-columns: 1fr auto;
37
+ }
38
+ .list {
39
+ grid-area: 1 / 1;
40
+ min-width: 0;
41
+ background: var(--vf-white, #fff);
42
+ /* The clamp is the content box's — the .box border adds the 2px frame
43
+ on top, keeping the clamped total where it always was. */
44
+ max-height: calc(var(--vf-scale, 1) * var(--vf-list-max-height, 200px));
45
+ /* Native scrolling; the bar itself is hidden by the recipe
46
+ (.vf-scroll) and the reservation is the rail element, not a native
47
+ gutter. The rows run to the rail on the right.
48
+
49
+ The mod() padding is border-floor compensation: engines floor a
50
+ fractional border-width to whole CSS px (1.5px renders 1px), which
51
+ would put the rows a fraction of a system px inside the frame at
52
+ scale 1.5 or 4/3 — and the rows are LIGHT-DOM components, so their
53
+ origins are the page's device-pixel-grid contract, not just ours.
54
+ Padding is stored exactly, so border + mod(border's fraction)
55
+ restores the exact 1-system-px inset the borderless-scroller
56
+ construction used to give them, at every scale (mod is 0 at whole
57
+ ones). */
58
+ padding: mod(calc(var(--vf-scale, 1) * 1px), 1px);
59
+ padding-right: 0;
60
+ overflow-y: auto;
61
+ }
62
+ .vf-rail--vertical {
63
+ grid-area: 1 / 2;
64
+ }
65
+ /* The viewport is itself the Tab stop while the list is disabled (see
66
+ render()); the ring keeps that stop visible, inset to stay in-box. */
67
+ .list:focus-visible {
68
+ --vf-focus-offset: -2px;
69
+ ${a}
70
+ }
71
+ `
72
+ ];
73
+ }
74
+ #e;
75
+ #t;
76
+ #n;
77
+ #r;
78
+ #i;
79
+ constructor() {
80
+ super(), this.scale = new t(this), this.gridSnap = new s(this), this.scrollState = new l(this, () => this.viewport, () => this.content, (e) => {
81
+ this._scrollable = e.x || e.y;
82
+ }), this.rail = new u(this, {
83
+ getScroll: () => this.viewport,
84
+ getContent: () => this.content
85
+ }), this._scrollable = !1, this.multiple = !1, this.value = "", this.values = [], this.disabled = !1, this.label = "", this.#e = -1, this.#t = -1, this.#n = new f(), this.#r = [], this.#i = this.attachInternals(), this.#l = (e) => {
86
+ if (this.disabled) return;
87
+ let t = e.target?.closest("vf-list-item") ?? null;
88
+ if (!t || t.disabled) return;
89
+ let n = this._items, r = n.indexOf(t);
90
+ if (r < 0) return;
91
+ let i;
92
+ if (this.multiple && e.shiftKey && this.#t >= 0 && this.#t < n.length) {
93
+ let e = Math.min(this.#t, r), t = Math.max(this.#t, r);
94
+ i = n.slice(e, t + 1).filter((e) => !e.disabled).map((e) => e.value);
95
+ } else if (this.multiple && (e.metaKey || e.ctrlKey)) {
96
+ let e = new Set(n.filter((e) => e.selected).map((e) => e.value));
97
+ e.has(t.value) ? e.delete(t.value) : e.add(t.value), i = [...e], this.#t = r;
98
+ } else i = [t.value], this.#t = r;
99
+ this.#e = r, this.#s(i, !0), t.focus();
100
+ }, this.#u = (e) => {
101
+ if (this.disabled) return;
102
+ let t = this._items, n = t.filter((e) => !e.disabled);
103
+ if (n.length === 0) return;
104
+ let r = this.#e;
105
+ if ((r < 0 || r >= t.length || t[r]?.disabled) && (r = t.findIndex((e) => e.selected && !e.disabled), r < 0 && (r = t.indexOf(n[0]))), this.multiple && (e.key === "a" || e.key === "A") && (e.metaKey || e.ctrlKey) && !e.shiftKey && !e.altKey) {
106
+ e.preventDefault(), this.#s(n.map((e) => e.value), !0);
107
+ return;
108
+ }
109
+ switch (e.key) {
110
+ case "ArrowDown":
111
+ case "ArrowUp": {
112
+ e.preventDefault();
113
+ let n = e.key === "ArrowDown" ? 1 : -1;
114
+ if (this.multiple && e.shiftKey) this.#f(this.#p(r, n), r);
115
+ else if (this.multiple && (e.metaKey || e.ctrlKey)) this.#d(this.#p(r, n));
116
+ else {
117
+ let e = t.some((e) => e.selected) ? this.#p(r, n) : r;
118
+ this.#m(e);
119
+ }
120
+ break;
121
+ }
122
+ case "Home":
123
+ case "End": {
124
+ e.preventDefault();
125
+ let i = t.indexOf(e.key === "Home" ? n[0] : n[n.length - 1]);
126
+ this.multiple && e.shiftKey ? this.#f(i, r) : this.multiple ? this.#d(i) : this.#m(i);
127
+ break;
128
+ }
129
+ case " ": {
130
+ e.preventDefault();
131
+ let n = t[r];
132
+ if (!n) return;
133
+ if (this.multiple && e.shiftKey) this.#f(r, r);
134
+ else if (this.multiple) {
135
+ let e = new Set(t.filter((e) => e.selected).map((e) => e.value));
136
+ e.has(n.value) ? e.delete(n.value) : e.add(n.value), this.#e = r, this.#t = r, this.#s([...e], !0);
137
+ } else this.#m(r);
138
+ break;
139
+ }
140
+ default: {
141
+ if (e.key.length !== 1 || e.metaKey || e.ctrlKey || e.altKey) break;
142
+ e.preventDefault();
143
+ let n = this.#n.feed(e.key, r, t.map((e) => ({
144
+ text: e.textContent ?? "",
145
+ disabled: e.disabled
146
+ })));
147
+ if (n < 0) break;
148
+ this.multiple ? this.#d(n) : this.#m(n);
149
+ break;
150
+ }
151
+ }
152
+ }, this.#i.role = "listbox", this.addEventListener("click", this.#l), this.addEventListener("keydown", this.#u), this.addEventListener("vf-list-item-disabled-change", () => {
153
+ this.#c();
154
+ });
155
+ }
156
+ focus(e) {
157
+ if (this.disabled) {
158
+ this.viewport?.focus(e);
159
+ return;
160
+ }
161
+ this._items?.find((e) => e.tabIndex === 0)?.focus(e);
162
+ }
163
+ disconnectedCallback() {
164
+ super.disconnectedCallback(), this.#n.reset();
165
+ }
166
+ updated(e) {
167
+ e.has("multiple") && (this.#i.ariaMultiSelectable = this.multiple ? "true" : null), e.has("disabled") && (this.#i.ariaDisabled = this.disabled ? "true" : null, this.#c()), e.has("label") && (this.#i.ariaLabel = this.label || null), e.has("values") ? (e.get("values") !== void 0 || this.values.length > 0) && this.#a(this.values) : e.has("value") && (e.get("value") !== void 0 || this.value !== "") && this.#a(this.value ? [this.value] : []);
168
+ }
169
+ #a(e) {
170
+ x(e, this.#r) || this.#s(e, !1);
171
+ }
172
+ render() {
173
+ return h`
174
+ <div class="box vf-snap">
175
+ <div
176
+ class="list vf-scroll"
177
+ part="list"
178
+ tabindex=${this.disabled && this._scrollable ? "0" : g}
179
+ >
180
+ <div class="list-content">
181
+ <slot @slotchange=${this.#o}></slot>
182
+ </div>
183
+ </div>
184
+ ${d(this.rail, "vertical")}
185
+ </div>
186
+ `;
187
+ }
188
+ #o() {
189
+ if (this.value || this.values.length > 0) this.#s(this.multiple ? this.values : this.value ? [this.value] : [], !1);
190
+ else {
191
+ let e = this._items.filter((e) => e.selected).map((e) => e.value);
192
+ this.#s(e, !1);
193
+ }
194
+ }
195
+ #s(e, t) {
196
+ let n = this._items, r = this.multiple ? e : e.slice(0, 1), i = n.filter((e) => e.selected).map((e) => e.value);
197
+ for (let e of n) e.selected = r.includes(e.value) && !e.disabled;
198
+ let a = n.filter((e) => e.selected).map((e) => e.value);
199
+ this.#r = [...a];
200
+ let o = a[0] ?? "";
201
+ this.value !== o && (this.value = o), x(this.values, a) || (this.values = a), this.#c(), t && !x(i, a) && c(this, "vf-change", {
202
+ value: this.value,
203
+ values: [...a]
204
+ });
205
+ }
206
+ #c() {
207
+ let e = this._items;
208
+ if (!e) return;
209
+ let t;
210
+ if (!this.disabled) {
211
+ let n = this.#e >= 0 ? e[this.#e] : void 0;
212
+ t = (n && !n.disabled ? n : void 0) ?? e.find((e) => e.selected && !e.disabled) ?? e.find((e) => !e.disabled), this.#e = t ? e.indexOf(t) : -1;
213
+ }
214
+ for (let n of e) n.listDisabled = this.disabled, n.tabIndex = n === t ? 0 : -1;
215
+ }
216
+ #l;
217
+ #u;
218
+ #d(e) {
219
+ e < 0 || (this.#e = e, this.#c(), this.#h(e));
220
+ }
221
+ #f(e, t) {
222
+ let n = this._items;
223
+ if (e < 0) return;
224
+ (this.#t < 0 || this.#t >= n.length) && (this.#t = t);
225
+ let r = Math.min(this.#t, e), i = Math.max(this.#t, e), a = n.slice(r, i + 1).filter((e) => !e.disabled).map((e) => e.value);
226
+ this.#e = e, this.#s(a, !0), this.#h(e);
227
+ }
228
+ #p(e, t) {
229
+ let n = this._items;
230
+ for (let r = e + t; r >= 0 && r < n.length; r += t) {
231
+ let e = n[r];
232
+ if (e && !e.disabled) return r;
233
+ }
234
+ return e;
235
+ }
236
+ #m(e) {
237
+ let t = this._items[e];
238
+ !t || t.disabled || (this.#e = e, this.#t = e, this.#s([t.value], !0), this.#h(e));
239
+ }
240
+ #h(e) {
241
+ let t = this._items[e];
242
+ !t || t.disabled || (t.focus(), t.scrollIntoView({ block: "nearest" }));
243
+ }
244
+ };
245
+ n([v(".list")], S.prototype, "viewport", void 0), n([v(".list-content")], S.prototype, "content", void 0), n([b()], S.prototype, "_scrollable", void 0), n([_({
246
+ type: Boolean,
247
+ reflect: !0
248
+ })], S.prototype, "multiple", void 0), n([_()], S.prototype, "value", void 0), n([_({ attribute: !1 })], S.prototype, "values", void 0), n([_({
249
+ type: Boolean,
250
+ reflect: !0
251
+ })], S.prototype, "disabled", void 0), n([_()], S.prototype, "label", void 0), n([y({
252
+ selector: "vf-list-item",
253
+ flatten: !0
254
+ })], S.prototype, "_items", void 0), S = n([e("vf-list")], S);
255
+ //#endregion
256
+ export { S as VfList };
@@ -0,0 +1,64 @@
1
+ import { LitElement } from 'lit';
2
+ declare const VfMenuBar_base: (new (...args: any[]) => import("../position.js").VfPositionedInterface) & typeof LitElement;
3
+ /**
4
+ * `<vf-menu-bar>` — the System 7 menu bar: white strip, 1px black bottom
5
+ * rule, slotted `<vf-menu>` children laid out from the left. `rounded` adds
6
+ * the classic screen-corner mask to the top corners.
7
+ *
8
+ * Coordinates its menus: pressing a label opens that menu (and inverts the
9
+ * label); while any menu is open, hovering another label switches to it;
10
+ * Escape, an outside click, or item selection closes. ArrowLeft/ArrowRight
11
+ * move between menus while one is open; ArrowDown/ArrowUp move focus through
12
+ * the open menu's items, Home/End jump to its first/last, and typed letters
13
+ * run the shared Finder first-letter type-ahead (src/type-ahead.ts).
14
+ *
15
+ * The bar also owns the **press-drag-release** gesture (see src/menu-press.ts)
16
+ * — press a title, slide onto a command, release over it — because one press
17
+ * may travel across several of its menus. `vf-select` drives its popup by the
18
+ * same mechanic.
19
+ *
20
+ * @slot - `vf-menu` elements.
21
+ * @csspart bar - The horizontal layout container.
22
+ * @cssprop [--vf-menubar-height=20px] - `vf-menu-bar`
23
+ */
24
+ export declare class VfMenuBar extends VfMenuBar_base {
25
+ #private;
26
+ static styles: import("lit").CSSResult[];
27
+ private readonly scale;
28
+ /** Device-pixel grid snapping (opt in with applyGridSnap()); see src/grid-snap.ts. */
29
+ private readonly gridSnap;
30
+ /**
31
+ * Accessible name for the bar, applied as `aria-label` on the host (which
32
+ * carries `role="menubar"`). Without it the menubar computes with an empty
33
+ * name. A consumer-supplied `aria-label`/`aria-labelledby` attribute is
34
+ * left alone.
35
+ */
36
+ label: string;
37
+ /**
38
+ * Rounds the bar's top corners the way System 7's were: not a curve of the
39
+ * bar itself but the black stair-step mask the compact Mac's ROM painted
40
+ * over the CRT's rounded corners, which the menu bar wore by sitting flush
41
+ * with the screen top. Drawn as the traced 5-px staircase in `--vf-black`
42
+ * ink over the bar's own corners — over any backdrop, as the hardware
43
+ * mask was.
44
+ */
45
+ rounded: boolean;
46
+ private _menus;
47
+ connectedCallback(): void;
48
+ disconnectedCallback(): void;
49
+ /**
50
+ * Moves keyboard focus to the bar's single Tab stop — the open menu's label,
51
+ * else the roving one's, else the first. Overridden because the platform's
52
+ * `focus()` was a silent no-op on this host (no `delegatesFocus`, no
53
+ * tabindex), so `vf-label for` and consumer scripts couldn't reach the bar.
54
+ */
55
+ focus(options?: FocusOptions): void;
56
+ protected updated(changed: Map<PropertyKey, unknown>): void;
57
+ protected render(): import("lit-html").TemplateResult<1>;
58
+ }
59
+ declare global {
60
+ interface HTMLElementTagNameMap {
61
+ 'vf-menu-bar': VfMenuBar;
62
+ }
63
+ }
64
+ export {};
@@ -0,0 +1,236 @@
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 { GridSnapController as a } from "../grid-snap.js";
7
+ import { DocumentListenersController as o } from "../document-listeners.js";
8
+ import { SCREEN_CORNER as s, steppedCornerClip as c } from "../pixel-frame.js";
9
+ import { TypeAheadBuffer as l } from "../type-ahead.js";
10
+ import { MenuPressController as u } from "../menu-press.js";
11
+ import { LitElement as d, css as f, html as p, unsafeCSS as m } from "lit";
12
+ import { property as h, queryAssignedElements as g } from "lit/decorators.js";
13
+ //#region src/components/vf-menu-bar.ts
14
+ var _ = class extends r(d) {
15
+ constructor(...e) {
16
+ super(...e), this.scale = new t(this), this.gridSnap = new a(this), this.label = "", this.rounded = !1, this.#e = null, this.#t = this.attachInternals(), this.#n = new l(), this.#r = null, this.#i = new o(this, () => [[
17
+ document,
18
+ "pointerdown",
19
+ this.#h,
20
+ !0
21
+ ], [
22
+ document,
23
+ "keydown",
24
+ this.#g,
25
+ !0
26
+ ]]), this.#a = new u(this, {
27
+ menus: () => this._menus,
28
+ open: (e) => this.#p(e),
29
+ close: () => this.#m()
30
+ }), this.#o = (e) => {
31
+ if (!this.#e) return;
32
+ let t = e.relatedTarget;
33
+ t instanceof Node && (this.contains(t) || this.renderRoot.contains(t)) || this.#m();
34
+ }, this.#u = (e) => {
35
+ let t = e.detail;
36
+ e.preventDefault(), e.stopPropagation(), t.menu.open ? this.#m() : this.#p(t.menu);
37
+ }, this.#d = (e) => {
38
+ let t = e.detail;
39
+ this.#e && t.menu !== this.#e && this.#p(t.menu);
40
+ }, this.#f = () => {
41
+ this.#e?.focus(), this.#m();
42
+ }, this.#h = (e) => {
43
+ e.composedPath().includes(this) || this.#m();
44
+ }, this.#g = (e) => {
45
+ if (!(e.defaultPrevented || !this.#e)) switch (e.key) {
46
+ case "Escape":
47
+ e.preventDefault(), this.#e.focus(), this.#m();
48
+ break;
49
+ case "Tab":
50
+ this.#m();
51
+ break;
52
+ case "ArrowLeft":
53
+ case "ArrowRight":
54
+ e.preventDefault(), this.#v(e.key === "ArrowRight" ? 1 : -1);
55
+ break;
56
+ case "ArrowDown":
57
+ case "ArrowUp":
58
+ e.preventDefault(), this.#y(e.key === "ArrowDown" ? 1 : -1);
59
+ break;
60
+ case "Home":
61
+ case "End": {
62
+ e.preventDefault();
63
+ let t = this.#e.items;
64
+ t[e.key === "Home" ? 0 : t.length - 1]?.focus();
65
+ break;
66
+ }
67
+ default: {
68
+ if (e.key.length !== 1 || e.key === " " || e.metaKey || e.ctrlKey || e.altKey) break;
69
+ e.preventDefault();
70
+ let t = this.#e.items, n = t.indexOf(document.activeElement);
71
+ t[this.#n.feed(e.key, n, t.map((e) => ({
72
+ text: e.textContent ?? "",
73
+ disabled: !1
74
+ })))]?.focus();
75
+ break;
76
+ }
77
+ }
78
+ }, this.#_ = (e) => {
79
+ if (this.#e) return;
80
+ let t = this._menus;
81
+ if (t.length === 0) return;
82
+ let n = e.target;
83
+ if (!(n instanceof HTMLElement)) return;
84
+ let r = t.find((e) => e.contains(n));
85
+ if (!r) return;
86
+ let i;
87
+ switch (e.key) {
88
+ case "ArrowLeft":
89
+ case "ArrowRight":
90
+ i = t[(t.indexOf(r) + (e.key === "ArrowRight" ? 1 : -1) + t.length) % t.length];
91
+ break;
92
+ case "Home":
93
+ i = t[0];
94
+ break;
95
+ case "End":
96
+ i = t[t.length - 1];
97
+ break;
98
+ default: return;
99
+ }
100
+ e.preventDefault(), i && (this.#l(i), i.focus());
101
+ };
102
+ }
103
+ static {
104
+ this.styles = [i, f`
105
+ :host {
106
+ display: block;
107
+ /* 19 white system px over the 1px black rule — Menus.png's bar strip
108
+ exactly (white rows y 69..87 of the sheet). */
109
+ height: calc(var(--vf-scale, 1) * var(--vf-menubar-height, 20px));
110
+ position: relative;
111
+ z-index: 1000;
112
+ }
113
+ .bar {
114
+ /* Paint lives here rather than on the host so it rides the snap offset
115
+ (see .vf-snap in base.ts); border-box sizing keeps the geometry. */
116
+ background: var(--vf-white, #fff);
117
+ border-bottom: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
118
+ display: flex;
119
+ align-items: stretch;
120
+ height: 100%;
121
+ /* 9px of bar before the first title's plate (System 7 put the Apple
122
+ plate 9 system px from the screen edge, clear of the 5px corner
123
+ mask), plus 5px cancelling the first title's share of the overlap
124
+ below. */
125
+ padding-inline-start: calc(var(--vf-scale, 1) * 14px);
126
+ }
127
+ /* Adjacent title plates OVERLAP by 5 system px: Menus.png spaces title
128
+ ink 14px apart while each plate runs 10px left / 9px right of its own
129
+ ink, so neighboring plates share a 5px band. A title's plate is its
130
+ label box (padding in vf-menu), so the overlap is layout: every menu
131
+ pulled 5px into its left neighbor. */
132
+ ::slotted(vf-menu) {
133
+ margin-inline-start: calc(var(--vf-scale, 1) * -5px);
134
+ }
135
+ /* rounded: the screen-corner mask. The corners aren't a shape of the
136
+ bar's own — they're the black staircase the ROM painted over the
137
+ CRT's rounded corners, worn by the menu bar because it sat flush with
138
+ the screen top. So they paint as ink *over* the bar (above slotted
139
+ content, like the hardware mask over anything), not as a cutout that
140
+ would show the backdrop. Stepped per the SCREEN_CORNER trace; the
141
+ --vf-black token keeps it themeable and forced-colors-safe. Absolute
142
+ position anchors to .bar (positioned via .vf-snap), riding the snap
143
+ offset with the paint it masks. */
144
+ :host([rounded]) .bar::before,
145
+ :host([rounded]) .bar::after {
146
+ content: '';
147
+ position: absolute;
148
+ top: 0;
149
+ width: calc(var(--vf-scale, 1) * ${s[0]}px);
150
+ height: calc(var(--vf-scale, 1) * ${s.length}px);
151
+ background: var(--vf-black, #000);
152
+ pointer-events: none;
153
+ }
154
+ :host([rounded]) .bar::before {
155
+ left: 0;
156
+ clip-path: ${m(c(s, "left"))};
157
+ }
158
+ :host([rounded]) .bar::after {
159
+ right: 0;
160
+ clip-path: ${m(c(s, "right"))};
161
+ }
162
+ `];
163
+ }
164
+ #e;
165
+ #t;
166
+ #n;
167
+ #r;
168
+ #i;
169
+ #a;
170
+ connectedCallback() {
171
+ 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);
172
+ }
173
+ disconnectedCallback() {
174
+ 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;
175
+ }
176
+ focus(e) {
177
+ (this.#e ?? this.#r ?? this._menus[0])?.focus(e);
178
+ }
179
+ #o;
180
+ updated(e) {
181
+ e.has("label") && (this.#t.ariaLabel = this.label || null);
182
+ }
183
+ render() {
184
+ return p`
185
+ <div class="bar vf-snap" part="bar" role="presentation">
186
+ <slot @slotchange=${this.#s}></slot>
187
+ </div>
188
+ `;
189
+ }
190
+ #s() {
191
+ this.#e && !this._menus.includes(this.#e) && this.#m(), this.#r && !this._menus.includes(this.#r) && (this.#r = null), this.#c();
192
+ }
193
+ #c() {
194
+ let e = this._menus, t = this.#e ?? this.#r ?? e[0];
195
+ for (let n of e) n.barTabIndex = n === t ? 0 : -1;
196
+ }
197
+ #l(e) {
198
+ this.#r = e, this.#c();
199
+ }
200
+ #u;
201
+ #d;
202
+ #f;
203
+ #p(e) {
204
+ for (let t of this._menus) t !== e && (t.open = !1);
205
+ e.open = !0, this.#e = e, this.#r = e, this.#n.reset(), this.#c(), this.#i.attach();
206
+ }
207
+ #m() {
208
+ for (let e of this._menus) e.open = !1;
209
+ this.#e = null, this.#n.reset(), this.#i.detach(), this.#c();
210
+ }
211
+ #h;
212
+ #g;
213
+ #_;
214
+ #v(e) {
215
+ let t = this._menus;
216
+ if (!this.#e || t.length === 0) return;
217
+ let n = t[(t.indexOf(this.#e) + e + t.length) % t.length];
218
+ n && n !== this.#e && (this.#p(n), n.focus());
219
+ }
220
+ #y(e) {
221
+ if (!this.#e) return;
222
+ let t = this.#e.items;
223
+ if (t.length === 0) return;
224
+ let n = t.indexOf(document.activeElement), r;
225
+ r = n < 0 ? e === 1 ? 0 : t.length - 1 : (n + e + t.length) % t.length, t[r]?.focus();
226
+ }
227
+ };
228
+ n([h()], _.prototype, "label", void 0), n([h({
229
+ type: Boolean,
230
+ reflect: !0
231
+ })], _.prototype, "rounded", void 0), n([g({
232
+ selector: "vf-menu",
233
+ flatten: !0
234
+ })], _.prototype, "_menus", void 0), _ = n([e("vf-menu-bar")], _);
235
+ //#endregion
236
+ export { _ as VfMenuBar };
@@ -0,0 +1,91 @@
1
+ import { LitElement } from 'lit';
2
+ /**
3
+ * `<vf-menu-item>` — a single command inside a `<vf-menu>` panel.
4
+ *
5
+ * Renders the classic System 7 menu row: optional ✓ check in the
6
+ * `--vf-select-gutter` left column (16px, shared with vf-select/vf-option),
7
+ * label, and the keyboard shortcut left-aligned in a right-anchored column,
8
+ * so every ⌘ lands at the same x. On activation the item performs the classic
9
+ * 3-blink inversion (~250ms), then dispatches `vf-menu-select` and asks its
10
+ * ancestors to close the menu.
11
+ *
12
+ * @slot - The item label.
13
+ * @csspart item - The row container.
14
+ * @csspart check - The ✓ checkmark glyph (rendered when `checked`).
15
+ * @csspart label - The label wrapper around the default slot.
16
+ * @csspart shortcut - The shortcut text, left-aligned in the shared column.
17
+ * @fires vf-menu-select - After the blink completes. `detail: { value, item }`.
18
+ * Named for the menu rather than plain `vf-select`, which would collide with
19
+ * the `<vf-select>` popup on any delegated ancestor listener (that component
20
+ * commits with `vf-change`).
21
+ * @cssprop [--vf-menu-row-height=16px] - `vf-menu-item` row pitch (`Menus.png`;
22
+ * kept separate from `--vf-popup-height` so re-theming the popup pill doesn't
23
+ * move pulldown rows)
24
+ * @cssprop [--vf-menu-shortcut-column=23px] - `vf-menu-item` shortcut slot,
25
+ * right-anchored with the text left-aligned in it so every ⌘ lands at the
26
+ * same x (`Menus.png`) — the MDEF reserve, ⌘'s 11px advance + the face's
27
+ * widest letter (M/W, 12px); widen it to line up longer shortcuts ("⌘⇧S")
28
+ * @cssprop [--vf-select-gutter=16px] - checkmark column: `vf-select` left inset
29
+ * / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift
30
+ * on open)
31
+ */
32
+ export declare class VfMenuItem extends LitElement {
33
+ #private;
34
+ static styles: import("lit").CSSResult[];
35
+ private readonly scale;
36
+ /** Disables the item: dimmed text, no highlight, no activation. */
37
+ disabled: boolean;
38
+ /** Shows the classic ✓ checkmark in the left gutter. */
39
+ checked: boolean;
40
+ /**
41
+ * Transient highlight — the full-row inversion the press-drag gesture paints
42
+ * on the row under the pointer (`:hover` can't: under touch the pointer is
43
+ * captured by the title the press started on). Managed by the menu, mirroring
44
+ * `vf-option[active]`; not part of the authoring API.
45
+ */
46
+ active: boolean;
47
+ /**
48
+ * Declares the item a *checkable* toggle up front, so it carries
49
+ * `role="menuitemcheckbox"` and `aria-checked="false"` from the first render
50
+ * rather than only once it has been checked. Set this on a toggle that starts
51
+ * **off** — otherwise it announces as a plain command until the first flip
52
+ * (the `checked` attribute can't express "checkable but off": for a boolean
53
+ * attribute, presence *is* true).
54
+ */
55
+ checkable: boolean;
56
+ /**
57
+ * Keyboard shortcut text, e.g. `"⌘H"`, drawn left-aligned in the panel's
58
+ * shared shortcut column. Display only in the
59
+ * visual sense — the span is `aria-hidden` so the glyphs never concatenate
60
+ * into the item's accessible name ("Print… place of interest sign P") —
61
+ * while the host mirrors it as `aria-keyshortcuts` ("Meta+H"), so AT
62
+ * announces it *as* a shortcut. A consumer's own `aria-keyshortcuts` wins.
63
+ */
64
+ shortcut: string;
65
+ /**
66
+ * Value reported in the `vf-menu-select` event detail. Defaults to the item's
67
+ * trimmed text content when unset.
68
+ */
69
+ value?: string;
70
+ /** `'on' | 'off'` while the selection blink runs, otherwise `null`. */
71
+ private _blinkPhase;
72
+ constructor();
73
+ connectedCallback(): void;
74
+ disconnectedCallback(): void;
75
+ protected updated(changed: Map<PropertyKey, unknown>): void;
76
+ protected render(): import("lit-html").TemplateResult<1>;
77
+ /**
78
+ * Runs the classic 3-blink inversion, then dispatches `vf-menu-select` and an
79
+ * internal `vf-menu-close-request` so ancestor menu/menu-bar close. Public
80
+ * because the menu's press gesture activates the row a drag was released
81
+ * over, which the row's own `click` never sees (a press that started on the
82
+ * title dispatches its click above both of them). No-op while disabled or
83
+ * already blinking.
84
+ */
85
+ activate(): void;
86
+ }
87
+ declare global {
88
+ interface HTMLElementTagNameMap {
89
+ 'vf-menu-item': VfMenuItem;
90
+ }
91
+ }