vintage-frames 0.19.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-elements.json +239 -51
- package/dist/build/plugins.js +1 -1
- package/dist/components/vf-menu-bar.d.ts +6 -4
- package/dist/components/vf-menu-bar.js +26 -56
- package/dist/components/vf-menu-item.d.ts +33 -5
- package/dist/components/vf-menu-item.js +128 -61
- package/dist/components/vf-menu.d.ts +35 -0
- package/dist/components/vf-menu.js +171 -111
- package/dist/glyphs.d.ts +6 -0
- package/dist/glyphs.js +18 -14
- package/dist/menu-navigation.d.ts +34 -0
- package/dist/menu-navigation.js +53 -0
- package/dist/menu-press.d.ts +25 -7
- package/dist/menu-press.js +78 -44
- package/dist/motion.d.ts +10 -0
- package/dist/shell/catalog.d.ts +40 -2
- package/dist/shell/catalog.js +137 -105
- package/dist/shell/finder.d.ts +4 -3
- package/dist/shell/finder.js +263 -249
- package/dist/shell/index.d.ts +2 -2
- package/dist/shell/index.js +12 -12
- package/dist/shell/pure.js +6 -6
- package/dist/shell/shell.js +24 -19
- package/dist/shell/version.d.ts +1 -1
- package/dist/shell/version.js +1 -1
- package/docs/SPEC.md +10 -6
- package/editor/vscode.html-custom-data.json +10 -5
- package/editor/web-types.json +45 -9
- package/package.json +1 -1
package/dist/shell/index.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ export { finder, FINDER } from './finder.js';
|
|
|
17
17
|
export type { FinderAlert, FinderAlertHandler, FinderAlerts, FinderApi, FinderArt, FinderCommand, FinderCommandSpec, FinderFailedAction, FinderMenu, FinderOpen, FinderOptions, } from './finder.js';
|
|
18
18
|
export { fileByDrag } from './filing.js';
|
|
19
19
|
export type { FilingOptions } from './filing.js';
|
|
20
|
-
export { createCatalog, memoryStorage, aliasName, appIdOf, appleMenuItemsOf, childrenOf, containerOf, copyName, descendantsOf, enclosingFolders, isContainerKind, isInside, isKept, isTrashed, isVolume, itemCount, itemOf, nextFolderName, originalOf, resolve, startupItemsOf, ALIAS_KIND, APPLE_MENU_ITEMS, APP_KIND, DISK, FOLDER, STARTUP_ITEMS, SYSTEM_FOLDER, TRASH, UNTITLED_FOLDER, } from './catalog.js';
|
|
21
|
-
export type { Catalog, CatalogArchive, CatalogOptions, CatalogState, CatalogStorage, Item, KindHooks, NewItem, SystemFolders, Volumes, } from './catalog.js';
|
|
20
|
+
export { createCatalog, memoryStorage, aliasName, appIdOf, appleMenuItemsOf, appleMenuOf, canAlias, childrenOf, containerOf, copyName, descendantsOf, dropTargetOf, enclosingFolders, isContainerKind, isInside, isKept, isTrashed, isVolume, itemCount, itemOf, nextFolderName, originalOf, resolve, startupItemsOf, ALIAS_KIND, APPLE_MENU_ITEMS, APP_KIND, DISK, FOLDER, STARTUP_ITEMS, SYSTEM_FOLDER, TRASH, UNTITLED_FOLDER, } from './catalog.js';
|
|
21
|
+
export type { AppleMenuEntry, Catalog, CatalogArchive, CatalogOptions, CatalogState, CatalogStorage, DropTarget, Item, KindHooks, NewItem, SystemFolders, Volumes, } from './catalog.js';
|
|
22
22
|
export { indexedDbStorage } from './storage.js';
|
|
23
23
|
export { localStorageState, mergeSession, openWindowsOf, readSession } from './state.js';
|
|
24
24
|
export type { LocalStorageStateOptions, SavedSession, SavedWindow, SessionSnapshot, ShellState } from './state.js';
|
package/dist/shell/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { ALIAS_KIND as e, APPLE_MENU_ITEMS as t, APP_KIND as n, DISK as r, FOLDER as i, STARTUP_ITEMS as a, SYSTEM_FOLDER as o, TRASH as s, UNTITLED_FOLDER as c, aliasName as l, appIdOf as u, appleMenuItemsOf as d,
|
|
2
|
-
import { BAND as
|
|
3
|
-
import { createWindowManager as
|
|
4
|
-
import { localStorageState as
|
|
5
|
-
import { DATE_HOLD_MS as
|
|
6
|
-
import { createShell as
|
|
7
|
-
import { fileByDrag as
|
|
8
|
-
import { FINDER as
|
|
9
|
-
import { indexedDbStorage as
|
|
10
|
-
import { VERSION as
|
|
11
|
-
import { APP_API as
|
|
12
|
-
export { e as ALIAS_KIND, t as APPLE_MENU_ITEMS,
|
|
1
|
+
import { ALIAS_KIND as e, APPLE_MENU_ITEMS as t, APP_KIND as n, DISK as r, FOLDER as i, STARTUP_ITEMS as a, SYSTEM_FOLDER as o, TRASH as s, UNTITLED_FOLDER as c, aliasName as l, appIdOf as u, appleMenuItemsOf as d, appleMenuOf as f, canAlias as p, childrenOf as m, containerOf as h, copyName as g, createCatalog as _, descendantsOf as v, dropTargetOf as y, enclosingFolders as b, isContainerKind as x, isInside as S, isKept as C, isTrashed as w, isVolume as T, itemCount as E, itemOf as D, memoryStorage as O, nextFolderName as k, originalOf as A, resolve as j, startupItemsOf as M } from "./catalog.js";
|
|
2
|
+
import { BAND as N, CASCADE_SLOTS as P, CASCADE_STEP as F, ICON_CELL as I, ICON_COLUMN as L, ICON_INSET as R, ICON_ROW as z, NEAR as B, WINDOW_ORIGIN as V, cascadeFrom as H, cascadeSlot as U, cascadedBox as W, centeredBox as G, cleanUp as K, collisions as q, desktopLattice as J, fieldExtent as Y, fillOrder as X, folderLattice as Z, frameOf as Q, freeCellNear as $, isPin as ee, latticeCell as te, latticeSlot as ne, nearBox as re, nextFreeCell as ie, pinOf as ae, pinTo as oe, trashCell as se, windowOrigin as ce } from "./geometry.js";
|
|
3
|
+
import { createWindowManager as le, focusInto as ue } from "./windows.js";
|
|
4
|
+
import { localStorageState as de, mergeSession as fe, openWindowsOf as pe, readSession as me } from "./state.js";
|
|
5
|
+
import { DATE_HOLD_MS as he, formatDate as ge, formatTime as _e, startClock as ve } from "./clock.js";
|
|
6
|
+
import { createShell as ye, defineApp as be } from "./shell.js";
|
|
7
|
+
import { fileByDrag as xe } from "./filing.js";
|
|
8
|
+
import { FINDER as Se, finder as Ce } from "./finder.js";
|
|
9
|
+
import { indexedDbStorage as we } from "./storage.js";
|
|
10
|
+
import { VERSION as Te } from "./version.js";
|
|
11
|
+
import { APP_API as Ee, APP_API_OLDEST as De, APP_FILE_FORMAT as Oe, BOX_SCALE as ke, appRuns as Ae, compareVersions as je, inspectAppFile as Me, readAppFile as Ne, satisfies as Pe } from "./app-file.js";
|
|
12
|
+
export { e as ALIAS_KIND, t as APPLE_MENU_ITEMS, Ee as APP_API, De as APP_API_OLDEST, Oe as APP_FILE_FORMAT, n as APP_KIND, N as BAND, ke as BOX_SCALE, P as CASCADE_SLOTS, F as CASCADE_STEP, he as DATE_HOLD_MS, r as DISK, Se as FINDER, i as FOLDER, I as ICON_CELL, L as ICON_COLUMN, R as ICON_INSET, z as ICON_ROW, B as NEAR, a as STARTUP_ITEMS, o as SYSTEM_FOLDER, s as TRASH, c as UNTITLED_FOLDER, Te as VERSION, V as WINDOW_ORIGIN, l as aliasName, u as appIdOf, Ae as appRuns, d as appleMenuItemsOf, f as appleMenuOf, p as canAlias, H as cascadeFrom, U as cascadeSlot, W as cascadedBox, G as centeredBox, m as childrenOf, K as cleanUp, q as collisions, je as compareVersions, h as containerOf, g as copyName, _ as createCatalog, ye as createShell, le as createWindowManager, be as defineApp, v as descendantsOf, J as desktopLattice, y as dropTargetOf, b as enclosingFolders, Y as fieldExtent, xe as fileByDrag, X as fillOrder, Ce as finder, ue as focusInto, Z as folderLattice, ge as formatDate, _e as formatTime, Q as frameOf, $ as freeCellNear, we as indexedDbStorage, Me as inspectAppFile, x as isContainerKind, S as isInside, C as isKept, ee as isPin, w as isTrashed, T as isVolume, E as itemCount, D as itemOf, te as latticeCell, ne as latticeSlot, de as localStorageState, O as memoryStorage, fe as mergeSession, re as nearBox, k as nextFolderName, ie as nextFreeCell, pe as openWindowsOf, A as originalOf, ae as pinOf, oe as pinTo, Ne as readAppFile, me as readSession, j as resolve, Pe as satisfies, ve as startClock, M as startupItemsOf, se as trashCell, ce as windowOrigin };
|
package/dist/shell/pure.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ALIAS_KIND as e, APPLE_MENU_ITEMS as t, APP_KIND as n, DISK as r, FOLDER as i, STARTUP_ITEMS as a, SYSTEM_FOLDER as o, TRASH as s, UNTITLED_FOLDER as c, aliasName as l, appIdOf as u, appleMenuItemsOf as d,
|
|
2
|
-
import { BAND as
|
|
3
|
-
import { localStorageState as
|
|
4
|
-
import { VERSION as
|
|
5
|
-
import { APP_API as
|
|
6
|
-
export { e as ALIAS_KIND, t as APPLE_MENU_ITEMS,
|
|
1
|
+
import { ALIAS_KIND as e, APPLE_MENU_ITEMS as t, APP_KIND as n, DISK as r, FOLDER as i, STARTUP_ITEMS as a, SYSTEM_FOLDER as o, TRASH as s, UNTITLED_FOLDER as c, aliasName as l, appIdOf as u, appleMenuItemsOf as d, appleMenuOf as f, canAlias as p, childrenOf as m, containerOf as h, copyName as g, createCatalog as _, descendantsOf as v, dropTargetOf as y, enclosingFolders as b, isContainerKind as x, isInside as S, isKept as C, isTrashed as w, isVolume as T, itemCount as E, itemOf as D, memoryStorage as O, nextFolderName as k, originalOf as A, resolve as j, startupItemsOf as M } from "./catalog.js";
|
|
2
|
+
import { BAND as N, CASCADE_SLOTS as P, CASCADE_STEP as F, ICON_CELL as I, ICON_COLUMN as L, ICON_INSET as R, ICON_ROW as z, NEAR as B, WINDOW_ORIGIN as V, cascadeFrom as H, cascadeSlot as U, cascadedBox as W, centeredBox as G, cleanUp as K, collisions as q, desktopLattice as J, fieldExtent as Y, fillOrder as X, folderLattice as Z, frameOf as Q, freeCellNear as $, isPin as ee, latticeCell as te, latticeSlot as ne, nearBox as re, nextFreeCell as ie, pinOf as ae, pinTo as oe, trashCell as se, windowOrigin as ce } from "./geometry.js";
|
|
3
|
+
import { localStorageState as le, mergeSession as ue, openWindowsOf as de, readSession as fe } from "./state.js";
|
|
4
|
+
import { VERSION as pe } from "./version.js";
|
|
5
|
+
import { APP_API as me, APP_API_OLDEST as he, APP_FILE_FORMAT as ge, BOX_SCALE as _e, appRuns as ve, compareVersions as ye, inspectAppFile as be, readAppFile as xe, satisfies as Se } from "./app-file.js";
|
|
6
|
+
export { e as ALIAS_KIND, t as APPLE_MENU_ITEMS, me as APP_API, he as APP_API_OLDEST, ge as APP_FILE_FORMAT, n as APP_KIND, N as BAND, _e as BOX_SCALE, P as CASCADE_SLOTS, F as CASCADE_STEP, r as DISK, i as FOLDER, I as ICON_CELL, L as ICON_COLUMN, R as ICON_INSET, z as ICON_ROW, B as NEAR, a as STARTUP_ITEMS, o as SYSTEM_FOLDER, s as TRASH, c as UNTITLED_FOLDER, pe as VERSION, V as WINDOW_ORIGIN, l as aliasName, u as appIdOf, ve as appRuns, d as appleMenuItemsOf, f as appleMenuOf, p as canAlias, H as cascadeFrom, U as cascadeSlot, W as cascadedBox, G as centeredBox, m as childrenOf, K as cleanUp, q as collisions, ye as compareVersions, h as containerOf, g as copyName, _ as createCatalog, v as descendantsOf, J as desktopLattice, y as dropTargetOf, b as enclosingFolders, Y as fieldExtent, X as fillOrder, Z as folderLattice, Q as frameOf, $ as freeCellNear, be as inspectAppFile, x as isContainerKind, S as isInside, C as isKept, ee as isPin, w as isTrashed, T as isVolume, E as itemCount, D as itemOf, te as latticeCell, ne as latticeSlot, le as localStorageState, O as memoryStorage, ue as mergeSession, re as nearBox, k as nextFolderName, ie as nextFreeCell, de as openWindowsOf, A as originalOf, ae as pinOf, oe as pinTo, xe as readAppFile, fe as readSession, j as resolve, Se as satisfies, M as startupItemsOf, se as trashCell, ce as windowOrigin };
|
package/dist/shell/shell.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { onScaleChange as e } from "../scale.js";
|
|
2
2
|
import { VfWindow as t } from "../components/vf-window.js";
|
|
3
3
|
import "../index.js";
|
|
4
|
-
import { ALIAS_KIND as n, appIdOf as r,
|
|
4
|
+
import { ALIAS_KIND as n, appIdOf as r, appleMenuOf as i, createCatalog as a, resolve as o, startupItemsOf as s } from "./catalog.js";
|
|
5
5
|
import { createWindowManager as c, deepActiveElement as l, desktopRendered as u } from "./windows.js";
|
|
6
6
|
import { openWindowsOf as d } from "./state.js";
|
|
7
7
|
import { startClock as f } from "./clock.js";
|
|
@@ -179,29 +179,34 @@ function h(p, h) {
|
|
|
179
179
|
}
|
|
180
180
|
};
|
|
181
181
|
x.push(E.onFront(B), E.onDialogs(B)), B();
|
|
182
|
-
let V = /* @__PURE__ */ new Map(), H = () => {
|
|
183
|
-
let
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
182
|
+
let V = /* @__PURE__ */ new Map(), H = [], U = "", W = (e) => e.map((e) => e.entries ? `${e.item.id}(${W(e.entries)})` : e.item.id).join(","), G = (e) => e.flatMap((e) => [e.item, ...e.entries ? G(e.entries) : []]), K = (e, t) => {
|
|
183
|
+
for (let { item: n, entries: r } of e) {
|
|
184
|
+
let e = document.createElement("vf-menu-item");
|
|
185
|
+
if (e.setAttribute("value", n.id), e.append(n.name), t.append(e), V.set(e, n.id), H.push(e), r) {
|
|
186
|
+
let t = document.createElement("vf-menu");
|
|
187
|
+
t.slot = "submenu", e.append(t), K(r, t);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}, q = () => {
|
|
191
|
+
let e = j ? i(j.get()) : [], t = W(e);
|
|
192
|
+
if (t === U) {
|
|
193
|
+
G(e).forEach((e, t) => {
|
|
194
|
+
let n = H[t];
|
|
195
|
+
n.labelText !== e.name && n.firstChild.replaceWith(e.name);
|
|
187
196
|
});
|
|
188
197
|
return;
|
|
189
198
|
}
|
|
190
|
-
for (let [e] of
|
|
191
|
-
V.clear();
|
|
192
|
-
|
|
193
|
-
let e = document.createElement("vf-menu-item");
|
|
194
|
-
e.setAttribute("value", t.id), e.textContent = t.name, C.append(e), V.set(e, t.id);
|
|
195
|
-
}
|
|
196
|
-
}, U = (e) => {
|
|
199
|
+
for (let [e] of V) e.remove();
|
|
200
|
+
V.clear(), H = [], U = t, K(e, C);
|
|
201
|
+
}, J = (e) => {
|
|
197
202
|
let t = V.get(e.detail.item);
|
|
198
203
|
t != null && !M() && k(t);
|
|
199
204
|
};
|
|
200
|
-
if (C.addEventListener("vf-menu-select",
|
|
201
|
-
C.removeEventListener("vf-menu-select",
|
|
205
|
+
if (C.addEventListener("vf-menu-select", J), x.push(() => {
|
|
206
|
+
C.removeEventListener("vf-menu-select", J);
|
|
202
207
|
for (let [e] of V) e.remove();
|
|
203
208
|
V.clear();
|
|
204
|
-
}), j && x.push(j.subscribe(
|
|
209
|
+
}), j && x.push(j.subscribe(q)), h.clock !== !1) {
|
|
205
210
|
let e = S.querySelector(":scope > [slot=\"end\"]"), t = !e;
|
|
206
211
|
e || (e = document.createElement("vf-label"), e.slot = "end", S.append(e));
|
|
207
212
|
let n = f(e);
|
|
@@ -209,16 +214,16 @@ function h(p, h) {
|
|
|
209
214
|
n(), t && e.remove();
|
|
210
215
|
});
|
|
211
216
|
}
|
|
212
|
-
let
|
|
217
|
+
let Y = /* @__PURE__ */ new WeakSet(), X = () => {
|
|
213
218
|
if (j) for (let e of p.stackingOrder) {
|
|
214
219
|
if (!(e instanceof t)) continue;
|
|
215
220
|
let n = E.itemOf(e);
|
|
216
221
|
if (n == null) continue;
|
|
217
222
|
let r = j.item(n);
|
|
218
|
-
r ? (
|
|
223
|
+
r ? (Y.add(e), e.heading !== r.name && (e.heading = r.name)) : Y.has(e) && j.get().available && E.close(e, { to: null });
|
|
219
224
|
}
|
|
220
225
|
};
|
|
221
|
-
return j && x.push(j.subscribe(
|
|
226
|
+
return j && x.push(j.subscribe(X), E.onWindows(X)), y?.hold(), {
|
|
222
227
|
desktop: p,
|
|
223
228
|
windows: E,
|
|
224
229
|
catalog: j,
|
package/dist/shell/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The kit's version, as published: package.json's `version`. `npm version` rewrites it (the `version` script). */
|
|
2
|
-
export declare const VERSION = "0.19.
|
|
2
|
+
export declare const VERSION = "0.19.1";
|
package/dist/shell/version.js
CHANGED
package/docs/SPEC.md
CHANGED
|
@@ -365,7 +365,7 @@ The classic popup menu control ("Macintosh HD ▼").
|
|
|
365
365
|
#### `vf-menu-bar` (`VfMenuBar`, vf-menu-bar.ts)
|
|
366
366
|
- **Attributes/props:** `label: string` — accessible name for the menubar, mirrored as host `aria-label` (guarded: a consumer's own `aria-label`/`aria-labelledby` is left alone). `rounded: boolean` (reflected) — draws the System 7 screen-corner mask over the bar's top-left/top-right corners. `shortcuts: boolean` (reflected) — makes every slotted menu's item `shortcut`s live key equivalents, one declaration for the whole bar the way MenuKey() answered for every menu at once (see vf-menu-item for the claim contract). Off by default: key equivalents are page-global, and only *the* menu bar should own them.
|
|
367
367
|
- **Visual:** `display: block/flex`, height `var(--vf-menubar-height, 20px)` — 19 white system px over the 1px black rule, the Menus.png bar strip exactly — white bg, the rule `vfRule`'s `.vf-rule-bottom` on the bar (§4), children laid out horizontally from left. Adjacent titles are pulled 4px into each other (`margin-inline-start: -4px` on every slotted menu, absorbed by 13px of bar start padding — 9px of bar before the first plate plus the first title's 4px share): title ink sits 15px apart while each title's plate runs 10px left / 9px right of its own ink, so neighboring plates *overlap* by 4px. With `rounded`, two 5×5 system-px corner overlays paint the traced stair-step mask (per-row runs 5/3/2/1/1 — `SCREEN_CORNER` + `steppedCornerClip` in `pixel-frame.ts`) in `--vf-black` *over* the bar: on the classic screen the rounding was the ROM's black corner mask sitting on top of the menu bar, not a shape of the bar's own, so it is ink over any backdrop rather than a cutout.
|
|
368
|
-
- **Behavior:** container/controller for slotted `vf-menu` children. Pressing a menu label → opens it (label inverts while open). While any menu is open, hovering another label switches to it (classic behavior). Escape / outside click / item selection closes. `role="menubar"`, behind a first-connect ownership latch so a consumer's own role survives upgrade; the shadow `.bar` is `role="presentation"` and each slotted `vf-menu` host `role="none"`, so the `menubar → menuitem` ownership chain has no generics in it. While a menu is open
|
|
368
|
+
- **Behavior:** container/controller for slotted `vf-menu` children. Pressing a menu label → opens it (label inverts while open). While any menu is open, hovering another label switches to it (classic behavior). Escape / outside click / item selection closes. `role="menubar"`, behind a first-connect ownership latch so a consumer's own role survives upgrade; the shadow `.bar` is `role="presentation"` and each slotted `vf-menu` host `role="none"`, so the `menubar → menuitem` ownership chain has no generics in it. While a menu is open the keyboard is `src/menu-navigation.ts`'s, which a standalone `vf-menu` shares, acting on the deepest open menu holding focus (the APG menubar pattern): ArrowDown/Up walk its items, Home/End jump to its first/last enabled item, and printable keys run the shared Finder first-letter type-ahead over them (`src/type-ahead.ts`; Space stays the focused item's activation key, and the prefix resets on menu switch, a level change or close). ArrowRight on an item that opens a submenu opens it and focuses its first enabled item; on any other item it opens the next menu. ArrowLeft in a submenu closes it and refocuses its item; at the top level it opens the previous menu. Escape closes one level: a submenu back to its item, the menu back to its title. The bar also **owns the press-drag-release gesture** across its menus (`MenuPressController`, `src/menu-press.ts` — see `vf-menu`), since one press may travel over several of them: it binds the opening `pointerdown` and hands the controller its own open/close rules, so the gesture changes *when* a menu opens, never *how*.
|
|
369
369
|
- **Slots:** default (vf-menu elements); `end` — the bar's right end, where a clock goes: a flex row of its own pushed to the end with an auto start margin and held 9px in, mirroring the first title's inset, setting `--vf-label-line-height` to the bar's height so a `vf-label` there rides the titles' baseline. Empty, it is `display: none` and takes no part in the row, so a page that pushes its own item to the end keeps the whole of the slack. `npm run verify:archetypes` (END SLOT). **Parts:** `bar`.
|
|
370
370
|
|
|
371
371
|
#### `vf-menu` (`VfMenu`, vf-menu.ts)
|
|
@@ -374,18 +374,22 @@ The classic popup menu control ("Macintosh HD ▼").
|
|
|
374
374
|
- **Keyboard focus: no ring** — `vfFocusUnderline` (§4) at `--vf-focus-underline-offset: -2px`, a dashed rule one blank system px row under the `.title` box. That box is `line-height: 1`, i.e. the face's own em, whose bottom edge is the descent line — so the one offset clears a descender *and* a slotted 16px `vf-img` (the Apple menu), where the button's baseline-anchored rule is crossed by both. In `currentColor`, so it inverts with the title on an open menu's black cell.
|
|
375
375
|
- **Closed only** (`:host(:not([open]))`). A dropped menu inverts its whole cell, which already shows where focus is; the rule marks the state the inversion can't — focused but not yet open — and drawing both would put a second mark (in white, since it is `currentColor`) under the first. The class stays on through the open state, so the rule returns by itself when the menu closes and hands focus back.
|
|
376
376
|
- Gated on a `.vf-focus-rule` class from the page's input modality, not `:focus-visible` (§4): `MenuPressController` `preventDefault`s the opening `pointerdown` and calls `focusLabel()` itself. The menu also calls the controller's `suppress()` from its own host `pointerdown`, so a press on an already-focused title — which moves no focus, and so fires no `focusin` — still drops the mark, as does mousing into the dropped panel. `npm run verify:focus`.
|
|
377
|
-
- **Behavior:** delegates open-state coordination to parent `vf-menu-bar` when present (only one open at a time; standalone, `#requestToggle` doesn't emit the coordination event at all — see the events convention in §2). Keyboard on the label: Enter, Space and ArrowDown all open **and move focus to the first enabled item** (APG, menubar and menu-button patterns alike — opening without entering would park the keyboard on the title with the panel dropped). While open standalone the menu runs its
|
|
377
|
+
- **Behavior:** delegates open-state coordination to parent `vf-menu-bar` when present (only one open at a time; standalone, `#requestToggle` doesn't emit the coordination event at all — see the events convention in §2). Keyboard on the label: Enter, Space and ArrowDown all open **and move focus to the first enabled item** (APG, menubar and menu-button patterns alike — opening without entering would park the keyboard on the title with the panel dropped). While open standalone the menu runs the bar's keyboard itself (`src/menu-navigation.ts`, see `vf-menu-bar`) over its items and its submenus: ArrowDown/Up wrap, Home/End jump, printable keys run the shared first-letter type-ahead (`src/type-ahead.ts`), ArrowRight and ArrowLeft open and close submenus, Escape closes one level and at the top refocuses the label. Sets `--vf-separator-color: var(--vf-disabled, #c0c0c0)` on its panel so slotted `vf-separator`s render dimmed, each spending one full `--vf-menu-row-height` — the MDEF's divider-as-item — with the rule 8px in (12px under the ink band above, 10 over the one below, per InfiniteMac's System 7.5 Edit menu and the 32px cross-divider ink pitch in `Menus.png`). **Pointer:** the two styles `vf-select` supports, on the same terms and the same `PRESS_HOLD_MS` threshold (`src/motion.ts`) — the menus get theirs from `MenuPressController` (`src/menu-press.ts`), which a standalone menu hosts itself and a `vf-menu-bar` hosts for all of its menus. Opening is on **pointerdown**, and the press is then tracked to its release anywhere:
|
|
378
378
|
- *System 7 press-drag-release* — press the title, slide onto a command, release over it to run it. Sliding sideways onto another title switches menus mid-press. Releasing over a disabled row, a separator, the title, or off the menu closes with nothing chosen (the classic "release outside").
|
|
379
|
-
- *Modern click-to-open* — a quick in-place tap (under `PRESS_HOLD_MS`, 200ms) on a title leaves the menu dropped for a second, independent click; a *held* in-place press closes it, and a press on an already-dropped title closes it. Time is consulted only for an in-place release: any press that travels is a drag-pick however long it took. Hit-testing is by **coordinates**, not event target, so tracking survives touch's implicit pointer capture (every move is delivered to the pressed title)
|
|
379
|
+
- *Modern click-to-open* — a quick in-place tap (under `PRESS_HOLD_MS`, 200ms) on a title leaves the menu dropped for a second, independent click; a *held* in-place press closes it, and a press on an already-dropped title closes it. Time is consulted only for an in-place release: any press that travels is a drag-pick however long it took. Hit-testing is by **coordinates**, not event target, so tracking survives touch's implicit pointer capture (every move is delivered to the pressed title). The trailing `click` the browser synthesises after a press is swallowed by the label and by the row (the same guard `vf-select` uses), so a keyboard/assistive-tech click — which arrives with no preceding pointerdown — is the only click that still toggles or activates.
|
|
380
|
+
- *One pointer model for both.* While a menu is open the pointer is tracked with or without a button down; the button only changes what a release means. Each open menu highlights one row as `vf-menu-item[active]`: the row under the pointer, or, with the pointer elsewhere, the item whose submenu is open. `:hover` paints no row inside a menu, so none shows two. Hit-testing tries the open menus deepest first, since a submenu is painted over its parent.
|
|
381
|
+
- **Submenus.** A `vf-menu` in a `vf-menu-item`'s `submenu` slot is a submenu: no title, only its panel, beside its item. Its host is `role="none"`, its panel `role="menu"`, named by the item's `labelText` unless it has a `label`. The menu holding the item opens it through `expand(item, { focus })`, which closes the submenu already open in that menu and everything under it (`null` closes it); `expandedItem` reads it. The pointer resting on the item for `SUBMENU_OPEN_MS` (150ms) opens it, in either style; on another row of the same menu an open submenu closes after `SUBMENU_CLOSE_MS` (300ms), and never once the pointer reaches it, so a diagonal slide into it can cross the rows between (`src/motion.ts`, the interaction itself, so not reduced-motion gated). A release or a click on the item opens its submenu now and leaves every menu dropped, so the visitor carries on clicking; a finger taps it. A pick at any depth closes every menu through the usual close request, which a submenu passes up; a key equivalent's flash request passes up to the top title too. Closing a menu closes its open submenu. Menus nest to any depth.
|
|
382
|
+
- *Placement*, traced from an Infinite Mac capture of System 7.5's Apple menu at 3×: the submenu's top frame is the row above its item, so its first row is level with the item, and its left frame is 5px left of the parent's right frame, covering the parent's last 4px, frame and shadow, the item's highlight running up to it. The host is a box as wide as the row at the row's top (the item's `.submenu`), and the panel hangs from it by those offsets. It opens toward the side its parent opened toward, the right first, and flips (mirrored: its right frame 5px right of the parent's left frame) where that side has no room and the other has. It is raised by whole system px to fit above the screen's bottom, never past its top. The screen is the closest `vf-desktop`'s `.screen` below its menu bar, within the viewport, else the viewport. It stays inside its parent's panel, so it paints in the parent's stacking context and takes its grid correction.
|
|
380
383
|
- **Slots:** default (vf-menu-item / vf-separator), `label` (replaces the `label` text in the bar — e.g. the Apple menu's `vf-img` apple; the `label` attribute stays set as the accessible name, mirrored to the bar item's `aria-label`). **Parts:** `label`, `panel`.
|
|
381
384
|
- **An icon title.** When the `label` slot holds only art (`vf-img`, `img`, `svg`, `picture` or `canvas`), the title is drawn as an icon: the art goes in a 16×16 cell, 6 system px into its plate and 6 short of the plate's end (`padding-inline` 6/6 in place of the text title's 9/8), on the plate's top row: one row above where centering on the bar's line would put it. Art drawn from x 2, y 1 of its cell, as the apple is (its ink is 11 × 14 at x 2..12, y 1..14), then has 8 px of plate before its ink: as the first title, the cell lands at x 15, its ink at x 17..27 on rows 2..15, and the next title's plate at x 33, its ink at x 43. A text title, from `label` or text in the slot, keeps the text geometry. `npm run verify:archetypes` (ICON TITLE).
|
|
382
385
|
|
|
383
386
|
#### `vf-menu-item` (`VfMenuItem`, vf-menu-item.ts)
|
|
384
|
-
- **Attributes/props:** `disabled`, `checked` (shows ✓ in left gutter), `checkable` (declares a toggle up front — see Behavior), `shortcut: string` (e.g. `"⌘H"`, drawn in the left-aligned shortcut column; a **live key equivalent** under a `shortcuts` grant — see Behavior), `value?: string` (defaults to
|
|
385
|
-
- **
|
|
387
|
+
- **Attributes/props:** `disabled`, `checked` (shows ✓ in left gutter), `checkable` (declares a toggle up front — see Behavior), `shortcut: string` (e.g. `"⌘H"`, drawn in the left-aligned shortcut column; a **live key equivalent** under a `shortcuts` grant — see Behavior), `value?: string` (defaults to `labelText`), `active` (reflect; the highlight under the pointer, or on the item whose submenu is open, managed by the menu — mirrors `vf-option[active]`, not an authoring API), `expanded` (reflect; its submenu is open, managed by the menu, announced as `aria-expanded`). Read-only: `submenu` (the `vf-menu` in the `submenu` slot, or null) and `labelText` (the default slot's text, trimmed, without a submenu's rows: the default value, the menus' type-ahead and a submenu item's name read it).
|
|
388
|
+
- **Submenu items.** An item holding a `vf-menu` in its `submenu` slot draws ▶ (`CARET_RIGHT`, 6×11, `CARET_DOWN` on its side, traced from `Menus.png`'s submenu rows) where a shortcut would go: 8px in from the row's end, as a System 7.5 capture has it too, 2 rows down the 16px row (centered, the odd row below), with an 8px label↔▶ minimum. Its `shortcut` isn't drawn, announced or matched. It is never chosen: a click, Enter, Space or `activate()` opens its submenu and focuses the submenu's first enabled item. It is `menuitem` with `aria-haspopup="menu"` and `aria-expanded`, and its accessible name is its `labelText`, so an open submenu's rows never join it. Events from the submenu pass through it on their way up, and it leaves them alone: a click, press or keydown that came from inside its submenu is that row's.
|
|
389
|
+
- **Visual:** height `var(--vf-menu-row-height, 16px)` — `Menus.png` puts every menu row on a 16px pitch (3px above + the 9px glyph + 4px below), so a pulldown row matches a popup row exactly; the line box is locked to the same expression so an inherited line-height can never overflow the panel. `padding: 0 12px 0 var(--vf-select-gutter, 16px)` (left gutter for ✓, shared with `vf-select`/`vf-option` — `Menus.png` puts a pulldown's label ink at the same inset as a popup's; a shortcut row trades the 12 for 1px, the slot being the clearance there). The shortcut is left-aligned in a right-anchored `--vf-menu-shortcut-column` (23px) slot — every ⌘ at the same x, 23px from the right border, the widest letters running to within ~3px of it (`Menus.png`'s File pulldown) — with an 8px label↔shortcut min gap. `color: var(--vf-disabled)` when disabled. `[active]`, keyboard focus, and hover for a row outside a menu (not disabled): full-width inversion — each with its own `.blink-off` override at matching specificity, so a drag-picked row keeps its flag through the blink and the release reads as the highlight flashing off. Inside a menu the pointer's row is the menu's `[active]`, since the item whose submenu is open stays lit while the pointer is in the submenu. A disabled row dims its ✓ along with the label — a **documented deviation** from the §1 "dim the label, chrome glyphs stay black" rule, because authentic System 7 greyed the whole disabled row.
|
|
386
390
|
- **Behavior:** `role="menuitem"` — or `role="menuitemcheckbox"` with `aria-checked` when the item is *checkable*: either `checkable` is set, or the item has ever been `checked`. Set `checkable` on a toggle that starts **off**, which would otherwise announce as a plain command until its first flip (a boolean `checked` attribute can't express "checkable but off"). The role is re-derived on every connect, so re-parenting a checkable item keeps it; an author-supplied `role` is left alone. On activation — a click, Enter/Space, or the public `activate()` the menu's press gesture calls for the row a drag was released over (which the row's own `click` never sees, since a press that started on the title dispatches its click above both): classic **blink** (invert toggles 3 times over ~250ms via timer; skipped under `prefers-reduced-motion`, selecting at once), then dispatch `vf-menu-select` detail `{ value, item }` and signal ancestors to close the menu — the close signal is skipped when the ancestor menu is already closed (the key-equivalent path below), since the close paths return focus to the bar label and would yank it from wherever the user was typing. Disabling an item mid-blink cancels it and drops the pending `vf-menu-select`.
|
|
387
391
|
- **Key equivalents** (the MenuKey() half of `shortcut`): under an ancestor `vf-menu[shortcuts]` / `vf-menu-bar[shortcuts]` grant, a document-level keydown matching the shortcut activates the item from anywhere on the page, menu open or not — File → Save *shows* ⌘S and *answers* it, no page-side plumbing. Matching is exact on modifiers (⌘=Meta, ⌃=Control, ⌥=Alt, ⇧=Shift — the same mapping `aria-keyshortcuts` announces) with `event.key` compared case-insensitively, except that a declared ⌘ is met by Control too — Ctrl standing in for ⌘ off the Mac, vf-icon's own ⌘O reading — while a shortcut spelling ⌃ itself keeps meaning the Control key alone; the grant is checked per event, so toggling it needs no re-wiring. The claim contract: a `defaultPrevented` stroke is already someone's (a page handler that ran first keeps its key); a match claims with `preventDefault()` and no `stopPropagation`, so the first connected item wins a contested key and later page listeners still observe the claimed event; a **disabled** item claims nothing — the stroke falls through, so a grayed Undo leaves ⌘Z to the focused field's own native undo; auto-repeat strokes are claimed but activate once; a shortcut with no ⌘/⌃/⌥ and a single printable key renders and announces but never matches (a bare letter claimed globally would hijack typing — a named key like "F1" needs no modifier). The arrow glyphs ← ↑ → ↓ name the arrow keys: "⌘→" matches `ArrowRight` and announces as `Meta+ArrowRight`; a bare arrow counts as a single key and never matches, so it can't take the arrows from a list, a menu or a field. Activation is the normal path — blink, `vf-menu-select` — and a **closed** ancestor menu answers by flashing its bar title on the same cadence (MenuKey's acknowledgment; skipped with the blink under `prefers-reduced-motion`): the panel never opens and focus never moves. `npm run verify:menu-keys`.
|
|
388
|
-
- **Slots:** default (label). **Parts:** `item`, `check`, `label`, `shortcut`.
|
|
392
|
+
- **Slots:** default (label), `submenu` (a `vf-menu`). **Parts:** `item`, `check`, `label`, `shortcut`, `arrow`.
|
|
389
393
|
- **Events:** `vf-menu-select` — menu-specific by design. A plain `vf-select` would collide with the `<vf-select>` popup on any delegated ancestor listener, since both bubble and compose while `<vf-select>` itself commits with `vf-change`. Parallels the existing `vf-menu-*` coordination names.
|
|
390
394
|
|
|
391
395
|
#### `vf-list` (`VfList`, vf-list-item children) (vf-list.ts)
|
|
@@ -766,7 +766,7 @@
|
|
|
766
766
|
},
|
|
767
767
|
{
|
|
768
768
|
"name": "vf-menu-bar",
|
|
769
|
-
"description": "`<vf-menu-bar>` — the System 7 menu bar: white strip, 1px black bottom\nrule, slotted `<vf-menu>` children laid out from the left. `rounded` adds\nthe classic screen-corner mask to the top corners.\n\nCoordinates its menus: pressing a label opens that menu (and inverts the\nlabel); while any menu is open, hovering another label switches to it;\nEscape, an outside click, or item selection closes.
|
|
769
|
+
"description": "`<vf-menu-bar>` — the System 7 menu bar: white strip, 1px black bottom\nrule, slotted `<vf-menu>` children laid out from the left. `rounded` adds\nthe classic screen-corner mask to the top corners.\n\nCoordinates its menus: pressing a label opens that menu (and inverts the\nlabel); while any menu is open, hovering another label switches to it;\nEscape, an outside click, or item selection closes. While a menu is open\nthe keyboard is src/menu-navigation.ts's: ArrowDown/ArrowUp move focus\nthrough the open menu's items, Home/End jump to its first/last, typed\nletters run the shared Finder first-letter type-ahead (src/type-ahead.ts),\nArrowRight opens an item's submenu and ArrowLeft closes it, and at the top\nlevel ArrowLeft/ArrowRight move between menus.\n\nThe bar also owns the **press-drag-release** gesture (see src/menu-press.ts)\n— press a title, slide onto a command, release over it — because one press\nmay travel across several of its menus. `vf-select` drives its popup by the\nsame mechanic.\n---\n\n\n### **Slots:**\n - _default_ - `vf-menu` elements.\n- **end** - The bar's right end: a clock, say. Held 9px in from the end, and a `vf-label` there rides the bar's line, so its baseline is the menu titles'.\n\n### **CSS Properties:**\n - **--vf-menubar-height** - `vf-menu-bar` _(default: 20px)_\n\n### **CSS Parts:**\n - **bar** - The horizontal layout container.",
|
|
770
770
|
"attributes": [
|
|
771
771
|
{
|
|
772
772
|
"name": "label",
|
|
@@ -808,7 +808,7 @@
|
|
|
808
808
|
},
|
|
809
809
|
{
|
|
810
810
|
"name": "vf-menu-item",
|
|
811
|
-
"description": "`<vf-menu-item>` — a single command inside a `<vf-menu>` panel.\n\nRenders the classic System 7 menu row: optional ✓ check in the\n`--vf-select-gutter` left column (16px, shared with vf-select/vf-option),\nlabel, and the keyboard shortcut left-aligned in a right-anchored column,\nso every ⌘ lands at the same x. On activation the item performs the classic\n3-blink inversion (~250ms), then dispatches `vf-menu-select` and asks its\nancestors to close the menu.\n\nTakes `top`/`left` like every other element (VfPositioned), for the\nconsumer who wants a row somewhere other than a pulldown. Inside its parent\n`<vf-menu>` the panel is as wide as its widest row and stacks them in flow,\nso stating an origin takes that row out of both: it no longer contributes to\nthe panel's width and the rows below close the gap. The placement doing what\nit says — but not how a pulldown is laid out.\n---\n\n\n### **Events:**\n - **vf-menu-select** - After the blink completes. `detail: { value, item }`. Named for the menu rather than plain `vf-select`, which would collide with the `<vf-select>` popup on any delegated ancestor listener (that component commits with `vf-change`).\n\n### **Slots:**\n - _default_ - The item label.\n\n### **CSS Properties:**\n - **--vf-menu-row-height** - `vf-menu-item` row pitch (`Menus.png`; kept separate from `--vf-popup-height` so re-theming the popup pill doesn't move pulldown rows) _(default: 16px)_\n- **--vf-menu-shortcut-column** - `vf-menu-item` shortcut slot, right-anchored with the text left-aligned in it so every ⌘ lands at the same x (`Menus.png`) — the MDEF reserve, ⌘'s 11px advance + the face's widest letter (M/W, 12px); widen it to line up longer shortcuts (\"⌘⇧S\") _(default: 23px)_\n- **--vf-select-gutter** - checkmark column: `vf-select` left inset / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift on open) _(default: 16px)_\n\n### **CSS Parts:**\n - **item** - The row container.\n- **check** - The ✓ checkmark glyph (rendered when `checked`).\n- **label** - The label wrapper around the default slot.\n- **shortcut** - The shortcut text, left-aligned in the shared column.",
|
|
811
|
+
"description": "`<vf-menu-item>` — a single command inside a `<vf-menu>` panel.\n\nRenders the classic System 7 menu row: optional ✓ check in the\n`--vf-select-gutter` left column (16px, shared with vf-select/vf-option),\nlabel, and the keyboard shortcut left-aligned in a right-anchored column,\nso every ⌘ lands at the same x. On activation the item performs the classic\n3-blink inversion (~250ms), then dispatches `vf-menu-select` and asks its\nancestors to close the menu.\n\nA `<vf-menu>` in the `submenu` slot makes the item open a submenu: it ends\nin ▶ where a shortcut would go, and choosing it opens the submenu beside\nit rather than firing `vf-menu-select`. The item stays highlighted while\nits submenu is open.\n\nTakes `top`/`left` like every other element (VfPositioned), for the\nconsumer who wants a row somewhere other than a pulldown. Inside its parent\n`<vf-menu>` the panel is as wide as its widest row and stacks them in flow,\nso stating an origin takes that row out of both: it no longer contributes to\nthe panel's width and the rows below close the gap. The placement doing what\nit says — but not how a pulldown is laid out.\n---\n\n\n### **Events:**\n - **vf-menu-select** - After the blink completes. `detail: { value, item }`. Named for the menu rather than plain `vf-select`, which would collide with the `<vf-select>` popup on any delegated ancestor listener (that component commits with `vf-change`).\n\n### **Slots:**\n - _default_ - The item label.\n- **submenu** - A `vf-menu`, the submenu the item opens.\n\n### **CSS Properties:**\n - **--vf-menu-row-height** - `vf-menu-item` row pitch (`Menus.png`; kept separate from `--vf-popup-height` so re-theming the popup pill doesn't move pulldown rows) _(default: 16px)_\n- **--vf-menu-shortcut-column** - `vf-menu-item` shortcut slot, right-anchored with the text left-aligned in it so every ⌘ lands at the same x (`Menus.png`) — the MDEF reserve, ⌘'s 11px advance + the face's widest letter (M/W, 12px); widen it to line up longer shortcuts (\"⌘⇧S\") _(default: 23px)_\n- **--vf-select-gutter** - checkmark column: `vf-select` left inset / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift on open) _(default: 16px)_\n\n### **CSS Parts:**\n - **item** - The row container.\n- **check** - The ✓ checkmark glyph (rendered when `checked`).\n- **label** - The label wrapper around the default slot.\n- **shortcut** - The shortcut text, left-aligned in the shared column.\n- **arrow** - The ▶ of an item that opens a submenu.",
|
|
812
812
|
"attributes": [
|
|
813
813
|
{
|
|
814
814
|
"name": "disabled",
|
|
@@ -822,7 +822,12 @@
|
|
|
822
822
|
},
|
|
823
823
|
{
|
|
824
824
|
"name": "active",
|
|
825
|
-
"description": "Transient highlight — the full-row inversion
|
|
825
|
+
"description": "Transient highlight — the full-row inversion on the row under the pointer\nwhile its menu is open, button down or not (`:hover` can't: under touch\nthe pointer is captured by the title the press started on), or on the\nitem whose submenu is open while the pointer is elsewhere. Managed by the\nmenu, mirroring `vf-option[active]`; not part of the authoring API.",
|
|
826
|
+
"values": []
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
"name": "expanded",
|
|
830
|
+
"description": "Whether this item's submenu is open. Reflected, and announced as\n`aria-expanded`. Managed by the menu (VfMenu.expand), like\nactive; not part of the authoring API.",
|
|
826
831
|
"values": []
|
|
827
832
|
},
|
|
828
833
|
{
|
|
@@ -837,7 +842,7 @@
|
|
|
837
842
|
},
|
|
838
843
|
{
|
|
839
844
|
"name": "value",
|
|
840
|
-
"description": "Value reported in the `vf-menu-select` event detail. Defaults to the item's\
|
|
845
|
+
"description": "Value reported in the `vf-menu-select` event detail. Defaults to the item's\nlabelText when unset.",
|
|
841
846
|
"values": []
|
|
842
847
|
},
|
|
843
848
|
{
|
|
@@ -865,7 +870,7 @@
|
|
|
865
870
|
},
|
|
866
871
|
{
|
|
867
872
|
"name": "vf-menu",
|
|
868
|
-
"description": "`<vf-menu>` — a pull-down menu: a bar label plus a dropped panel of\n`<vf-menu-item>` / `<vf-separator>` children.\n\nInside a `<vf-menu-bar>` the bar coordinates open state (only one menu open,\nhover-switching, outside-click/Escape dismissal) and owns the pointer\ngesture, which may travel between its menus. Used standalone, the menu\ntoggles itself on label click and manages its own dismissal, its own press\ngesture and item keyboard navigation (ArrowUp/ArrowDown, Home/End, and the\nshared first-letter type-ahead — src/type-ahead.ts) while open.\n\nPointer — the two styles `vf-select` supports, on the same terms (see\nsrc/menu-press.ts): the System 7 press-drag-release (press the title, slide\nonto a command, release over it) and a modern quick tap that leaves the menu\ndropped for a second click.\n\nTakes `top`/`left` like every other element (VfPositioned) — the\nnatural fit being a standalone menu, which is a free-standing menu button and\nplaces like any other control. The panel is anchored to the host's own box\n(`top: 100%`), so it follows a placed menu down without any further work.\nInside a `<vf-menu-bar>`, the bar lays its titles out in flow — placing one\nlifts it off the bar and the titles beside it close the gap. Slotted straight\ninto a `<vf-desktop>`, a free-standing menu sits on the desktop's menu tier\nabove every window, palettes included, as a bar does.\n---\n\n\n### **Slots:**\n - _default_ - Menu contents: `vf-menu-item` and `vf-separator` elements.\n- **label** - Replaces the `label` text in the bar — e.g. a `vf-img` apple icon for the Apple menu. An icon (`vf-img`, `img`, `svg`, `picture` or `canvas`) is drawn in a 16×16 cell and placed for one: 6 system px into its plate, 6 short of the plate's end, on the plate's top row. Text keeps the text title's place. Keep the `label` attribute set too: it stays the menu's accessible name (the bar item's `aria-label` and the panel's) when the visible title is an image.\n\n### **CSS Properties:**\n - **--vf-menubar-height** - `vf-menu-bar` _(default: 20px)_\n- **--vf-menu-row-height** - `vf-menu-item` row pitch; the panel also spends one of these on every slotted `vf-separator`, the full row the MDEF gave a divider (rule 8px into it — see the panel CSS) _(default: 16px)_\n\n### **CSS Parts:**\n - **label** - The menu title in the bar (inverts while open; flashes when a closed menu's item is activated by its key equivalent).\n- **panel** - The dropped `.vf-panel` containing the items.",
|
|
873
|
+
"description": "`<vf-menu>` — a pull-down menu: a bar label plus a dropped panel of\n`<vf-menu-item>` / `<vf-separator>` children.\n\nInside a `<vf-menu-bar>` the bar coordinates open state (only one menu open,\nhover-switching, outside-click/Escape dismissal) and owns the pointer\ngesture, which may travel between its menus. Used standalone, the menu\ntoggles itself on label click and manages its own dismissal, its own press\ngesture and item keyboard navigation (ArrowUp/ArrowDown, Home/End, and the\nshared first-letter type-ahead — src/type-ahead.ts) while open.\n\nPointer — the two styles `vf-select` supports, on the same terms (see\nsrc/menu-press.ts): the System 7 press-drag-release (press the title, slide\nonto a command, release over it) and a modern quick tap that leaves the menu\ndropped for a second click.\n\nA `vf-menu` in a `vf-menu-item`'s `submenu` slot is a **submenu**: no title,\nonly its panel, opened beside its item by the menu holding the item\n(expand). The pointer resting on the item opens it in either style,\nand so do a click or tap on the item, →, Enter and Space. It overlaps its\nparent as System 7.5's did: its first row level with its item, its frame\n5px inside the parent's right edge, covering the parent's frame and\nshadow. It opens on the side its parent opened toward, the right first,\nflips where that side has no room, and moves up to fit the screen. Menus\nnest to any depth.\n\nTakes `top`/`left` like every other element (VfPositioned) — the\nnatural fit being a standalone menu, which is a free-standing menu button and\nplaces like any other control. The panel is anchored to the host's own box\n(`top: 100%`), so it follows a placed menu down without any further work.\nInside a `<vf-menu-bar>`, the bar lays its titles out in flow — placing one\nlifts it off the bar and the titles beside it close the gap. Slotted straight\ninto a `<vf-desktop>`, a free-standing menu sits on the desktop's menu tier\nabove every window, palettes included, as a bar does.\n---\n\n\n### **Slots:**\n - _default_ - Menu contents: `vf-menu-item` and `vf-separator` elements.\n- **label** - Replaces the `label` text in the bar — e.g. a `vf-img` apple icon for the Apple menu. An icon (`vf-img`, `img`, `svg`, `picture` or `canvas`) is drawn in a 16×16 cell and placed for one: 6 system px into its plate, 6 short of the plate's end, on the plate's top row. Text keeps the text title's place. Keep the `label` attribute set too: it stays the menu's accessible name (the bar item's `aria-label` and the panel's) when the visible title is an image.\n\n### **CSS Properties:**\n - **--vf-menubar-height** - `vf-menu-bar` _(default: 20px)_\n- **--vf-menu-row-height** - `vf-menu-item` row pitch; the panel also spends one of these on every slotted `vf-separator`, the full row the MDEF gave a divider (rule 8px into it — see the panel CSS) _(default: 16px)_\n\n### **CSS Parts:**\n - **label** - The menu title in the bar (inverts while open; flashes when a closed menu's item is activated by its key equivalent).\n- **panel** - The dropped `.vf-panel` containing the items.",
|
|
869
874
|
"attributes": [
|
|
870
875
|
{
|
|
871
876
|
"name": "label",
|
package/editor/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
|
|
3
3
|
"name": "vintage-frames",
|
|
4
|
-
"version": "0.19.
|
|
4
|
+
"version": "0.19.1",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
@@ -1799,7 +1799,7 @@
|
|
|
1799
1799
|
},
|
|
1800
1800
|
{
|
|
1801
1801
|
"name": "vf-menu-bar",
|
|
1802
|
-
"description": "`<vf-menu-bar>` — the System 7 menu bar: white strip, 1px black bottom\nrule, slotted `<vf-menu>` children laid out from the left. `rounded` adds\nthe classic screen-corner mask to the top corners.\n\nCoordinates its menus: pressing a label opens that menu (and inverts the\nlabel); while any menu is open, hovering another label switches to it;\nEscape, an outside click, or item selection closes.
|
|
1802
|
+
"description": "`<vf-menu-bar>` — the System 7 menu bar: white strip, 1px black bottom\nrule, slotted `<vf-menu>` children laid out from the left. `rounded` adds\nthe classic screen-corner mask to the top corners.\n\nCoordinates its menus: pressing a label opens that menu (and inverts the\nlabel); while any menu is open, hovering another label switches to it;\nEscape, an outside click, or item selection closes. While a menu is open\nthe keyboard is src/menu-navigation.ts's: ArrowDown/ArrowUp move focus\nthrough the open menu's items, Home/End jump to its first/last, typed\nletters run the shared Finder first-letter type-ahead (src/type-ahead.ts),\nArrowRight opens an item's submenu and ArrowLeft closes it, and at the top\nlevel ArrowLeft/ArrowRight move between menus.\n\nThe bar also owns the **press-drag-release** gesture (see src/menu-press.ts)\n— press a title, slide onto a command, release over it — because one press\nmay travel across several of its menus. `vf-select` drives its popup by the\nsame mechanic.\n---\n\n\n### **Slots:**\n - _default_ - `vf-menu` elements.\n- **end** - The bar's right end: a clock, say. Held 9px in from the end, and a `vf-label` there rides the bar's line, so its baseline is the menu titles'.\n\n### **CSS Properties:**\n - **--vf-menubar-height** - `vf-menu-bar` _(default: 20px)_\n\n### **CSS Parts:**\n - **bar** - The horizontal layout container.",
|
|
1803
1803
|
"doc-url": "",
|
|
1804
1804
|
"attributes": [
|
|
1805
1805
|
{
|
|
@@ -1889,7 +1889,7 @@
|
|
|
1889
1889
|
},
|
|
1890
1890
|
{
|
|
1891
1891
|
"name": "vf-menu-item",
|
|
1892
|
-
"description": "`<vf-menu-item>` — a single command inside a `<vf-menu>` panel.\n\nRenders the classic System 7 menu row: optional ✓ check in the\n`--vf-select-gutter` left column (16px, shared with vf-select/vf-option),\nlabel, and the keyboard shortcut left-aligned in a right-anchored column,\nso every ⌘ lands at the same x. On activation the item performs the classic\n3-blink inversion (~250ms), then dispatches `vf-menu-select` and asks its\nancestors to close the menu.\n\nTakes `top`/`left` like every other element (VfPositioned), for the\nconsumer who wants a row somewhere other than a pulldown. Inside its parent\n`<vf-menu>` the panel is as wide as its widest row and stacks them in flow,\nso stating an origin takes that row out of both: it no longer contributes to\nthe panel's width and the rows below close the gap. The placement doing what\nit says — but not how a pulldown is laid out.\n---\n\n\n### **Events:**\n - **vf-menu-select** - After the blink completes. `detail: { value, item }`. Named for the menu rather than plain `vf-select`, which would collide with the `<vf-select>` popup on any delegated ancestor listener (that component commits with `vf-change`).\n\n### **Slots:**\n - _default_ - The item label.\n\n### **CSS Properties:**\n - **--vf-menu-row-height** - `vf-menu-item` row pitch (`Menus.png`; kept separate from `--vf-popup-height` so re-theming the popup pill doesn't move pulldown rows) _(default: 16px)_\n- **--vf-menu-shortcut-column** - `vf-menu-item` shortcut slot, right-anchored with the text left-aligned in it so every ⌘ lands at the same x (`Menus.png`) — the MDEF reserve, ⌘'s 11px advance + the face's widest letter (M/W, 12px); widen it to line up longer shortcuts (\"⌘⇧S\") _(default: 23px)_\n- **--vf-select-gutter** - checkmark column: `vf-select` left inset / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift on open) _(default: 16px)_\n\n### **CSS Parts:**\n - **item** - The row container.\n- **check** - The ✓ checkmark glyph (rendered when `checked`).\n- **label** - The label wrapper around the default slot.\n- **shortcut** - The shortcut text, left-aligned in the shared column.",
|
|
1892
|
+
"description": "`<vf-menu-item>` — a single command inside a `<vf-menu>` panel.\n\nRenders the classic System 7 menu row: optional ✓ check in the\n`--vf-select-gutter` left column (16px, shared with vf-select/vf-option),\nlabel, and the keyboard shortcut left-aligned in a right-anchored column,\nso every ⌘ lands at the same x. On activation the item performs the classic\n3-blink inversion (~250ms), then dispatches `vf-menu-select` and asks its\nancestors to close the menu.\n\nA `<vf-menu>` in the `submenu` slot makes the item open a submenu: it ends\nin ▶ where a shortcut would go, and choosing it opens the submenu beside\nit rather than firing `vf-menu-select`. The item stays highlighted while\nits submenu is open.\n\nTakes `top`/`left` like every other element (VfPositioned), for the\nconsumer who wants a row somewhere other than a pulldown. Inside its parent\n`<vf-menu>` the panel is as wide as its widest row and stacks them in flow,\nso stating an origin takes that row out of both: it no longer contributes to\nthe panel's width and the rows below close the gap. The placement doing what\nit says — but not how a pulldown is laid out.\n---\n\n\n### **Events:**\n - **vf-menu-select** - After the blink completes. `detail: { value, item }`. Named for the menu rather than plain `vf-select`, which would collide with the `<vf-select>` popup on any delegated ancestor listener (that component commits with `vf-change`).\n\n### **Slots:**\n - _default_ - The item label.\n- **submenu** - A `vf-menu`, the submenu the item opens.\n\n### **CSS Properties:**\n - **--vf-menu-row-height** - `vf-menu-item` row pitch (`Menus.png`; kept separate from `--vf-popup-height` so re-theming the popup pill doesn't move pulldown rows) _(default: 16px)_\n- **--vf-menu-shortcut-column** - `vf-menu-item` shortcut slot, right-anchored with the text left-aligned in it so every ⌘ lands at the same x (`Menus.png`) — the MDEF reserve, ⌘'s 11px advance + the face's widest letter (M/W, 12px); widen it to line up longer shortcuts (\"⌘⇧S\") _(default: 23px)_\n- **--vf-select-gutter** - checkmark column: `vf-select` left inset / `vf-option` + `vf-menu-item` ✓ column (shared so the value doesn't shift on open) _(default: 16px)_\n\n### **CSS Parts:**\n - **item** - The row container.\n- **check** - The ✓ checkmark glyph (rendered when `checked`).\n- **label** - The label wrapper around the default slot.\n- **shortcut** - The shortcut text, left-aligned in the shared column.\n- **arrow** - The ▶ of an item that opens a submenu.",
|
|
1893
1893
|
"doc-url": "",
|
|
1894
1894
|
"attributes": [
|
|
1895
1895
|
{
|
|
@@ -1904,7 +1904,12 @@
|
|
|
1904
1904
|
},
|
|
1905
1905
|
{
|
|
1906
1906
|
"name": "active",
|
|
1907
|
-
"description": "Transient highlight — the full-row inversion
|
|
1907
|
+
"description": "Transient highlight — the full-row inversion on the row under the pointer\nwhile its menu is open, button down or not (`:hover` can't: under touch\nthe pointer is captured by the title the press started on), or on the\nitem whose submenu is open while the pointer is elsewhere. Managed by the\nmenu, mirroring `vf-option[active]`; not part of the authoring API.",
|
|
1908
|
+
"value": { "type": "boolean", "default": "false" }
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
"name": "expanded",
|
|
1912
|
+
"description": "Whether this item's submenu is open. Reflected, and announced as\n`aria-expanded`. Managed by the menu (VfMenu.expand), like\nactive; not part of the authoring API.",
|
|
1908
1913
|
"value": { "type": "boolean", "default": "false" }
|
|
1909
1914
|
},
|
|
1910
1915
|
{
|
|
@@ -1919,7 +1924,7 @@
|
|
|
1919
1924
|
},
|
|
1920
1925
|
{
|
|
1921
1926
|
"name": "value",
|
|
1922
|
-
"description": "Value reported in the `vf-menu-select` event detail. Defaults to the item's\
|
|
1927
|
+
"description": "Value reported in the `vf-menu-select` event detail. Defaults to the item's\nlabelText when unset.",
|
|
1923
1928
|
"value": { "type": "string | undefined" }
|
|
1924
1929
|
},
|
|
1925
1930
|
{
|
|
@@ -1943,7 +1948,13 @@
|
|
|
1943
1948
|
"value": { "type": "PlacementOrigin | null | undefined" }
|
|
1944
1949
|
}
|
|
1945
1950
|
],
|
|
1946
|
-
"slots": [
|
|
1951
|
+
"slots": [
|
|
1952
|
+
{ "name": "", "description": "The item label." },
|
|
1953
|
+
{
|
|
1954
|
+
"name": "submenu",
|
|
1955
|
+
"description": "A `vf-menu`, the submenu the item opens."
|
|
1956
|
+
}
|
|
1957
|
+
],
|
|
1947
1958
|
"events": [
|
|
1948
1959
|
{
|
|
1949
1960
|
"name": "vf-menu-select",
|
|
@@ -1964,7 +1975,12 @@
|
|
|
1964
1975
|
},
|
|
1965
1976
|
{
|
|
1966
1977
|
"name": "active",
|
|
1967
|
-
"description": "Transient highlight — the full-row inversion
|
|
1978
|
+
"description": "Transient highlight — the full-row inversion on the row under the pointer\nwhile its menu is open, button down or not (`:hover` can't: under touch\nthe pointer is captured by the title the press started on), or on the\nitem whose submenu is open while the pointer is elsewhere. Managed by the\nmenu, mirroring `vf-option[active]`; not part of the authoring API.",
|
|
1979
|
+
"type": "boolean"
|
|
1980
|
+
},
|
|
1981
|
+
{
|
|
1982
|
+
"name": "expanded",
|
|
1983
|
+
"description": "Whether this item's submenu is open. Reflected, and announced as\n`aria-expanded`. Managed by the menu (VfMenu.expand), like\nactive; not part of the authoring API.",
|
|
1968
1984
|
"type": "boolean"
|
|
1969
1985
|
},
|
|
1970
1986
|
{
|
|
@@ -1979,9 +1995,19 @@
|
|
|
1979
1995
|
},
|
|
1980
1996
|
{
|
|
1981
1997
|
"name": "value",
|
|
1982
|
-
"description": "Value reported in the `vf-menu-select` event detail. Defaults to the item's\
|
|
1998
|
+
"description": "Value reported in the `vf-menu-select` event detail. Defaults to the item's\nlabelText when unset.",
|
|
1983
1999
|
"type": "string | undefined"
|
|
1984
2000
|
},
|
|
2001
|
+
{
|
|
2002
|
+
"name": "submenu",
|
|
2003
|
+
"description": "The submenu this item opens: the `vf-menu` in its `submenu` slot, or null.",
|
|
2004
|
+
"type": "VfMenu | null"
|
|
2005
|
+
},
|
|
2006
|
+
{
|
|
2007
|
+
"name": "labelText",
|
|
2008
|
+
"description": "The item's label as text: what its default slot holds, trimmed, without\nthe rows of a submenu. The default value and the menus'\ntype-ahead read it, and it names an item that opens a submenu.",
|
|
2009
|
+
"type": "string"
|
|
2010
|
+
},
|
|
1985
2011
|
{
|
|
1986
2012
|
"name": "top",
|
|
1987
2013
|
"description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
|
|
@@ -2013,7 +2039,7 @@
|
|
|
2013
2039
|
},
|
|
2014
2040
|
{
|
|
2015
2041
|
"name": "vf-menu",
|
|
2016
|
-
"description": "`<vf-menu>` — a pull-down menu: a bar label plus a dropped panel of\n`<vf-menu-item>` / `<vf-separator>` children.\n\nInside a `<vf-menu-bar>` the bar coordinates open state (only one menu open,\nhover-switching, outside-click/Escape dismissal) and owns the pointer\ngesture, which may travel between its menus. Used standalone, the menu\ntoggles itself on label click and manages its own dismissal, its own press\ngesture and item keyboard navigation (ArrowUp/ArrowDown, Home/End, and the\nshared first-letter type-ahead — src/type-ahead.ts) while open.\n\nPointer — the two styles `vf-select` supports, on the same terms (see\nsrc/menu-press.ts): the System 7 press-drag-release (press the title, slide\nonto a command, release over it) and a modern quick tap that leaves the menu\ndropped for a second click.\n\nTakes `top`/`left` like every other element (VfPositioned) — the\nnatural fit being a standalone menu, which is a free-standing menu button and\nplaces like any other control. The panel is anchored to the host's own box\n(`top: 100%`), so it follows a placed menu down without any further work.\nInside a `<vf-menu-bar>`, the bar lays its titles out in flow — placing one\nlifts it off the bar and the titles beside it close the gap. Slotted straight\ninto a `<vf-desktop>`, a free-standing menu sits on the desktop's menu tier\nabove every window, palettes included, as a bar does.\n---\n\n\n### **Slots:**\n - _default_ - Menu contents: `vf-menu-item` and `vf-separator` elements.\n- **label** - Replaces the `label` text in the bar — e.g. a `vf-img` apple icon for the Apple menu. An icon (`vf-img`, `img`, `svg`, `picture` or `canvas`) is drawn in a 16×16 cell and placed for one: 6 system px into its plate, 6 short of the plate's end, on the plate's top row. Text keeps the text title's place. Keep the `label` attribute set too: it stays the menu's accessible name (the bar item's `aria-label` and the panel's) when the visible title is an image.\n\n### **CSS Properties:**\n - **--vf-menubar-height** - `vf-menu-bar` _(default: 20px)_\n- **--vf-menu-row-height** - `vf-menu-item` row pitch; the panel also spends one of these on every slotted `vf-separator`, the full row the MDEF gave a divider (rule 8px into it — see the panel CSS) _(default: 16px)_\n\n### **CSS Parts:**\n - **label** - The menu title in the bar (inverts while open; flashes when a closed menu's item is activated by its key equivalent).\n- **panel** - The dropped `.vf-panel` containing the items.",
|
|
2042
|
+
"description": "`<vf-menu>` — a pull-down menu: a bar label plus a dropped panel of\n`<vf-menu-item>` / `<vf-separator>` children.\n\nInside a `<vf-menu-bar>` the bar coordinates open state (only one menu open,\nhover-switching, outside-click/Escape dismissal) and owns the pointer\ngesture, which may travel between its menus. Used standalone, the menu\ntoggles itself on label click and manages its own dismissal, its own press\ngesture and item keyboard navigation (ArrowUp/ArrowDown, Home/End, and the\nshared first-letter type-ahead — src/type-ahead.ts) while open.\n\nPointer — the two styles `vf-select` supports, on the same terms (see\nsrc/menu-press.ts): the System 7 press-drag-release (press the title, slide\nonto a command, release over it) and a modern quick tap that leaves the menu\ndropped for a second click.\n\nA `vf-menu` in a `vf-menu-item`'s `submenu` slot is a **submenu**: no title,\nonly its panel, opened beside its item by the menu holding the item\n(expand). The pointer resting on the item opens it in either style,\nand so do a click or tap on the item, →, Enter and Space. It overlaps its\nparent as System 7.5's did: its first row level with its item, its frame\n5px inside the parent's right edge, covering the parent's frame and\nshadow. It opens on the side its parent opened toward, the right first,\nflips where that side has no room, and moves up to fit the screen. Menus\nnest to any depth.\n\nTakes `top`/`left` like every other element (VfPositioned) — the\nnatural fit being a standalone menu, which is a free-standing menu button and\nplaces like any other control. The panel is anchored to the host's own box\n(`top: 100%`), so it follows a placed menu down without any further work.\nInside a `<vf-menu-bar>`, the bar lays its titles out in flow — placing one\nlifts it off the bar and the titles beside it close the gap. Slotted straight\ninto a `<vf-desktop>`, a free-standing menu sits on the desktop's menu tier\nabove every window, palettes included, as a bar does.\n---\n\n\n### **Slots:**\n - _default_ - Menu contents: `vf-menu-item` and `vf-separator` elements.\n- **label** - Replaces the `label` text in the bar — e.g. a `vf-img` apple icon for the Apple menu. An icon (`vf-img`, `img`, `svg`, `picture` or `canvas`) is drawn in a 16×16 cell and placed for one: 6 system px into its plate, 6 short of the plate's end, on the plate's top row. Text keeps the text title's place. Keep the `label` attribute set too: it stays the menu's accessible name (the bar item's `aria-label` and the panel's) when the visible title is an image.\n\n### **CSS Properties:**\n - **--vf-menubar-height** - `vf-menu-bar` _(default: 20px)_\n- **--vf-menu-row-height** - `vf-menu-item` row pitch; the panel also spends one of these on every slotted `vf-separator`, the full row the MDEF gave a divider (rule 8px into it — see the panel CSS) _(default: 16px)_\n\n### **CSS Parts:**\n - **label** - The menu title in the bar (inverts while open; flashes when a closed menu's item is activated by its key equivalent).\n- **panel** - The dropped `.vf-panel` containing the items.",
|
|
2017
2043
|
"doc-url": "",
|
|
2018
2044
|
"attributes": [
|
|
2019
2045
|
{
|
|
@@ -2100,6 +2126,16 @@
|
|
|
2100
2126
|
"description": "Viewport rect of the bar title, or `null` before the first render. The\npress gesture hit-tests by coordinates rather than by event target (see\nsrc/menu-press.ts), so it wants the box, not the element.",
|
|
2101
2127
|
"type": "DOMRect | null"
|
|
2102
2128
|
},
|
|
2129
|
+
{
|
|
2130
|
+
"name": "panelRect",
|
|
2131
|
+
"description": "Viewport rect of the dropped panel, or `null` while closed. The press\ngesture hit-tests the open menus by their panels, deepest first.",
|
|
2132
|
+
"type": "DOMRect | null"
|
|
2133
|
+
},
|
|
2134
|
+
{
|
|
2135
|
+
"name": "expandedItem",
|
|
2136
|
+
"description": "The item in this menu whose submenu is open, or `null`.",
|
|
2137
|
+
"type": "VfMenuItem | null"
|
|
2138
|
+
},
|
|
2103
2139
|
{
|
|
2104
2140
|
"name": "top",
|
|
2105
2141
|
"description": "Offset from the top of the positioning parent, in whole system px.\nSetting this (or `left`) absolutely positions the element within its\nparent; the coordinate left unset is 0. Remove both to return the\nelement to normal flow.",
|