vintage-frames 0.6.1 → 0.7.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.
- package/custom-elements.json +1257 -172
- package/dist/components/vf-desktop.d.ts +34 -0
- package/dist/components/vf-desktop.js +66 -43
- package/dist/components/vf-dialog.d.ts +9 -0
- package/dist/components/vf-icon-field.d.ts +144 -0
- package/dist/components/vf-icon-field.js +173 -0
- package/dist/components/vf-icon.d.ts +196 -6
- package/dist/components/vf-icon.js +374 -122
- package/dist/components/vf-number-field.js +1 -1
- package/dist/components/vf-scroll-area.d.ts +25 -6
- package/dist/components/vf-scroll-area.js +34 -28
- package/dist/components/vf-text-field.d.ts +2 -1
- package/dist/components/vf-text-field.js +1 -1
- package/dist/components/vf-window.d.ts +24 -0
- package/dist/components/vf-window.js +66 -59
- package/dist/drag.d.ts +28 -4
- package/dist/drag.js +15 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.js +13 -12
- package/dist/modal-dialog.d.ts +33 -0
- package/dist/modal-dialog.js +66 -36
- package/dist/open-art.d.ts +60 -1
- package/dist/open-art.js +47 -1
- package/dist/position.d.ts +35 -0
- package/dist/position.js +63 -45
- package/dist/text-control.d.ts +8 -1
- package/dist/text-control.js +2 -7
- package/docs/SPEC.md +26 -10
- package/editor/vscode.html-custom-data.json +51 -4
- package/editor/web-types.json +133 -5
- package/package.json +2 -1
package/custom-elements.json
CHANGED
|
@@ -2349,7 +2349,7 @@
|
|
|
2349
2349
|
"declarations": [
|
|
2350
2350
|
{
|
|
2351
2351
|
"kind": "class",
|
|
2352
|
-
"description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its top corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, one whole-surface raster at one image px per system px, 1-bit at\nevery density and zoom (src/pattern-fill.ts).\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).",
|
|
2352
|
+
"description": "`<vf-desktop>` — the full-bleed classic desktop container.\n\nRenders the desktop pattern — the classic 50% dither by default, or any of\nthe standard patterns by name (`pattern`, System 7's General Controls\nsetting) — and manages the stacking order and `active` state of slotted\n`vf-window` children: a `pointerdown` or\n`focusin` (keyboard focus) anywhere inside a window brings it to the front\nand makes it the single active window. The windows' light-DOM order is kept\nin step with the stacking order (bottom-most first, synced in a task once\nany pointer gesture has ended), so tabbing walks the stack the way the eye\ndoes and Shift+Tab is its exact mirror.\n\nUtility windows (`vf-window[variant=\"utility\"]`) stack in a floating tier\nabove every document-tier window, restack only among themselves, and stand\noutside the single-active invariant entirely — clicking a palette neither\ndeactivates the active document window nor greys the palette, exactly as\nSystem 7's floating windoids behaved while their application was frontmost.\nA slotted `vf-menu-bar` (or a free-standing `vf-menu`) sits on a tier\nabove both, so its dropped menus cover palettes and document windows\nalike; only the screen-corner mask is in front of it.\n\n**Deactivation.** On a real System 7 machine clicking the desktop clicked\nthe *Finder* — the frontmost application's windows lost their stripes.\nclearActive is that gesture's handler: it clears `active` from\nthe whole document tier, and **zero active windows is a legal state**,\nheld until a press or keyboard focus re-enters a document window (or a\nnew one is slotted, which activates it — opening a window brings its\napplication forward). The desktop never takes this decision itself: its\nfurniture is slotted light DOM (an icon layer, say), so only the page\nknows which of its children — or which presses on the bare dither — mean\n\"the Finder\", and it routes those through `clearActive()`. Left alone,\nthe classic always-one-active behavior is unchanged. activeWindow\nreads the current holder, and every change of holder — including to and\nfrom none — fires `vf-activate`.\n\nThe desktop is a raster with an explicit size, always: **`width` and\n`height`**, in system px, the way a WIND resource declared a window's —\nthe host box renders at the declared screen plus `2 × bezel` per axis, a\nwhole number of system pixels by construction (default 512×342, the\ncompact Mac's screen). Pure CSS sizing is not supported; the page sets\nthe numbers — directly, or via VfDesktop.fitWithin on\n`resize`/`onScaleChange` for a viewport-filling desktop — and positions\nthe sized box with its own stylesheet, keeping any sub-system-pixel\nslack on its side. `bezel` (system px) draws the black screen surround —\nthe CRT's unlit margin — around the screen, rounding its top corners\nwith the classic corner mask.\n\n**`pattern`** names the desktop pattern — `gray-50` (the dither) by\ndefault, any of the 38 standard patterns (docs/PATTERNS.md), or sixteen\nhex digits stating a custom 8×8 pattern, as on `vf-container`. It is\npainted as the screen's own background: black ink on an opaque white\npaper, one whole-surface raster at one image px per system px, 1-bit at\nevery density and zoom (src/pattern-fill.ts).\n\n**The drag surface.** The Finder dragged an icon as a dotted outline drawn\non the *screen*, clipped at its edge and phase-locked to its raster, so\nthe screen hosts the layer that outline draws on: a full-bleed child of\nthe screen, `pointer-events: none` (never a hit for the page's\n`elementsFromPoint`), whose children paint one tier above the menu bar\nand under the corner mask — the layer itself is deliberately no stacking\ncontext, since a blend inside one would composite against nothing. An\nicon reaches it without an import, the menu-handshake idiom: on a drag's\nfirst step it dispatches `vf-drag-surface-request` (bubbles, non-composed,\n`detail.surface` null), and the nearest desktop on its light-DOM path\nfills the detail with the layer. Not the top layer: stable Safari cannot\nblend an element there against the page (src/cursor.ts), and a second\nkit entry in the top layer would need a re-promotion handshake.\n\nCustom properties:\n- `--vf-desktop-pattern` — a consumer's own tile art in place of the\n pattern (the kit's default is a 1-bit 50% checker, opaque\n black-on-white on a 30-system-px tile). Set, it wins over `pattern` and\n renders as a placed tile grid at that same 30-px geometry\n (src/tile-grid.ts); a token swapped at runtime without touching the\n component wants a `requestUpdate()`.\n- `--vf-desktop` — base color painted *under* the pattern (default\n `#808080`). The pattern's paper is opaque, so this only becomes visible\n when `--vf-desktop-pattern` is overridden with a tile that has\n transparent cells (or with `none`).",
|
|
2353
2353
|
"name": "VfDesktop",
|
|
2354
2354
|
"cssProperties": [
|
|
2355
2355
|
{
|
|
@@ -2439,6 +2439,30 @@
|
|
|
2439
2439
|
],
|
|
2440
2440
|
"description": "Size the screen to the largest whole-system-px raster whose host box —\nbezel included — fits a CSS-px bound, and return what was set. The\npage's half of the sizing contract: it owns the viewport, so it\nmeasures the box and hands it here on `resize` and `onScaleChange`\n(zoom and density moves change how many CSS px a system px costs),\nand the sub-system-pixel remainder stays on the page, behind the\nbezel. The epsilon forgives float error in the division so an\nexact-fit bound never drops a whole system pixel."
|
|
2441
2441
|
},
|
|
2442
|
+
{
|
|
2443
|
+
"kind": "method",
|
|
2444
|
+
"name": "placementAt",
|
|
2445
|
+
"return": {
|
|
2446
|
+
"type": {
|
|
2447
|
+
"text": "{ left: number; top: number }"
|
|
2448
|
+
}
|
|
2449
|
+
},
|
|
2450
|
+
"parameters": [
|
|
2451
|
+
{
|
|
2452
|
+
"name": "clientX",
|
|
2453
|
+
"type": {
|
|
2454
|
+
"text": "number"
|
|
2455
|
+
}
|
|
2456
|
+
},
|
|
2457
|
+
{
|
|
2458
|
+
"name": "clientY",
|
|
2459
|
+
"type": {
|
|
2460
|
+
"text": "number"
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
],
|
|
2464
|
+
"description": "A viewport point (CSS px) as a placement on the screen — `{ left, top }`\nin whole system px on the placement lattice, measured from the raster's\ncorner with the bezel excluded: the pair a child dropped onto the desktop\nis written with. The screen sits in the shadow tree, which is why the\nconversion is a method here."
|
|
2465
|
+
},
|
|
2442
2466
|
{
|
|
2443
2467
|
"kind": "field",
|
|
2444
2468
|
"name": "_windows",
|
|
@@ -2504,6 +2528,16 @@
|
|
|
2504
2528
|
"readonly": true,
|
|
2505
2529
|
"description": "The screen surface the pattern paints on; exists from the first render."
|
|
2506
2530
|
},
|
|
2531
|
+
{
|
|
2532
|
+
"kind": "field",
|
|
2533
|
+
"name": "dragSurface",
|
|
2534
|
+
"type": {
|
|
2535
|
+
"text": "HTMLDivElement"
|
|
2536
|
+
},
|
|
2537
|
+
"privacy": "private",
|
|
2538
|
+
"readonly": true,
|
|
2539
|
+
"description": "The layer a dragged icon's outline draws on; exists from the first render."
|
|
2540
|
+
},
|
|
2507
2541
|
{
|
|
2508
2542
|
"kind": "field",
|
|
2509
2543
|
"name": "patternFill",
|
|
@@ -2707,6 +2741,12 @@
|
|
|
2707
2741
|
"privacy": "private",
|
|
2708
2742
|
"description": "The press ended (or was cancelled) — schedule the DOM-order sync.\nUnconditional rather than only-if-raised: the sync no-ops when order\nalready agrees, and running it after every gesture is what heals the\nstaleness a keyboard-only stretch leaves behind (focus-driven raises\nchange z but never move nodes — see _raise)."
|
|
2709
2743
|
},
|
|
2744
|
+
{
|
|
2745
|
+
"kind": "field",
|
|
2746
|
+
"name": "_onDragSurfaceRequest",
|
|
2747
|
+
"privacy": "private",
|
|
2748
|
+
"description": "A dragged icon asking for the screen's drag surface (see the class\ndoc). Non-composed, so it reaches here only through the light-DOM path\nthe icon is slotted on — a window body included. The nearest desktop\nanswers; an outer one leaves a filled detail alone."
|
|
2749
|
+
},
|
|
2710
2750
|
{
|
|
2711
2751
|
"kind": "field",
|
|
2712
2752
|
"name": "_onFocusIn",
|
|
@@ -2995,7 +3035,7 @@
|
|
|
2995
3035
|
"declarations": [
|
|
2996
3036
|
{
|
|
2997
3037
|
"kind": "class",
|
|
2998
|
-
"description": "`<vf-dialog>` — the System 7 modal dialog shell.\n\nTwo chromes, one modal lifecycle (native `<dialog>` for top-layer rendering\nand focus trapping, with a fully transparent backdrop — no dimming). Both\nare the same dBoxProc double frame — 1px outer rule, 2px gap, 2px inner\nband, no shadow (vfModalFrame):\n\n- **Default:** the movable modal (movableDBoxProc) — the striped title bar\n set into the top of that frame, with a centered title over a white body.\n Drag the title bar to move it. `closable` adds the standard close box\n (left of the bar) — the HIG's own figures disagree on whether a movable\n modal carries one (Figure 5-1 says yes, Figure 6-1 and the Chapter 6 text\n say no), so the component enables either reading rather than enforcing\n one.\n- **`frame=\"plain\"`:** the modal dialog box — the bare frame, no title bar —\n and immovable, like the original. A `heading` renders as a centered\n display-face heading at the top of the body (the reference art's \"Dialog\n title\"); `closable` is ignored, there being no bar to carry the widget.\n\nOpen it with `show()` (or set the `open` attribute/property); close with\n`close()`. Escape closes it and fires `vf-close` with\n`{ reason: 'escape' }`; the close box and programmatic closing fire\n`{ reason: 'close' }`. With `light-dismiss`, a click outside the frame\ncloses it too, with `{ reason: 'outside' }` — for the About box; off by\ndefault, since the classic modal ignored an outside click.",
|
|
3038
|
+
"description": "`<vf-dialog>` — the System 7 modal dialog shell.\n\nTwo chromes, one modal lifecycle (native `<dialog>` for top-layer rendering\nand focus trapping, with a fully transparent backdrop — no dimming). Both\nare the same dBoxProc double frame — 1px outer rule, 2px gap, 2px inner\nband, no shadow (vfModalFrame):\n\n- **Default:** the movable modal (movableDBoxProc) — the striped title bar\n set into the top of that frame, with a centered title over a white body.\n Drag the title bar to move it. `closable` adds the standard close box\n (left of the bar) — the HIG's own figures disagree on whether a movable\n modal carries one (Figure 5-1 says yes, Figure 6-1 and the Chapter 6 text\n say no), so the component enables either reading rather than enforcing\n one.\n- **`frame=\"plain\"`:** the modal dialog box — the bare frame, no title bar —\n and immovable, like the original. A `heading` renders as a centered\n display-face heading at the top of the body (the reference art's \"Dialog\n title\"); `closable` is ignored, there being no bar to carry the widget.\n\nOpen it with `show()` (or set the `open` attribute/property); close with\n`close()`. Escape closes it and fires `vf-close` with\n`{ reason: 'escape' }`; the close box and programmatic closing fire\n`{ reason: 'close' }`. With `light-dismiss`, a click outside the frame\ncloses it too, with `{ reason: 'outside' }` — for the About box; off by\ndefault, since the classic modal ignored an outside click.\n\nKeyboard, the classic Dialog Manager's two rules (VfModalDialog):\non open, focus goes to the first text-entry control — a slotted control\nwith `autofocus` first — or, with none, to the default button\n(`vf-button variant=\"default\"`). Return or Enter activates the default\nbutton from anywhere in the dialog, a focused Cancel included; Space\npresses the focused control. A link keeps its own Enter, and in a\nmulti-line editor Return inserts the newline while the keypad's Enter\nactivates the button.",
|
|
2999
3039
|
"name": "VfDialog",
|
|
3000
3040
|
"cssProperties": [
|
|
3001
3041
|
{
|
|
@@ -3379,11 +3419,64 @@
|
|
|
3379
3419
|
},
|
|
3380
3420
|
{
|
|
3381
3421
|
"kind": "field",
|
|
3382
|
-
"name": "#
|
|
3422
|
+
"name": "#openListeners",
|
|
3383
3423
|
"privacy": "private",
|
|
3384
3424
|
"readonly": true,
|
|
3385
|
-
"default": "new DocumentListenersController(this, () => [ [this._dialog, 'pointerdown', this.#onDialogPointerDown], [this._dialog, 'pointerup', this.#onDialogPointerUp], [this._dialog, 'pointercancel', this.#onDialogPointerCancel], ])",
|
|
3386
|
-
"description": "The
|
|
3425
|
+
"default": "new DocumentListenersController(this, () => [ [this._dialog, 'pointerdown', this.#onDialogPointerDown], [this._dialog, 'pointerup', this.#onDialogPointerUp], [this._dialog, 'pointercancel', this.#onDialogPointerCancel], [this._dialog, 'keydown', this.#onDialogKeydown], ])",
|
|
3426
|
+
"description": "The while-open listeners, on the `<dialog>` itself: the light-dismiss\npointer trio and the Return/Enter routing.\n\nLight dismiss: the `::backdrop` hit-tests as its dialog, and the frame the\nsubclass renders fills the dialog's box, so the dialog is an event's\n*target* only when the pointer was outside the frame — no rect arithmetic,\nno composedPath. Attached whether or not lightDismiss is set: the\nhandlers read the property at the release, so it can be toggled on an\nopen dialog.",
|
|
3427
|
+
"inheritedFrom": {
|
|
3428
|
+
"name": "VfModalDialog",
|
|
3429
|
+
"module": "src/modal-dialog.ts"
|
|
3430
|
+
}
|
|
3431
|
+
},
|
|
3432
|
+
{
|
|
3433
|
+
"kind": "field",
|
|
3434
|
+
"name": "defaultButton",
|
|
3435
|
+
"type": {
|
|
3436
|
+
"text": "HTMLElement | null"
|
|
3437
|
+
},
|
|
3438
|
+
"privacy": "protected",
|
|
3439
|
+
"description": "The dialog's default button — the one wearing the bold ring\n(`vf-button[variant=\"default\"]`) — or null with none, or none enabled.\nLooked for among the slotted content first, then in the shadow tree, so a\nconsumer's own modal that renders its buttons itself is covered too.\nOverride to name it some other way.",
|
|
3440
|
+
"readonly": true,
|
|
3441
|
+
"inheritedFrom": {
|
|
3442
|
+
"name": "VfModalDialog",
|
|
3443
|
+
"module": "src/modal-dialog.ts"
|
|
3444
|
+
}
|
|
3445
|
+
},
|
|
3446
|
+
{
|
|
3447
|
+
"kind": "field",
|
|
3448
|
+
"name": "initialFocusTarget",
|
|
3449
|
+
"type": {
|
|
3450
|
+
"text": "HTMLElement | null"
|
|
3451
|
+
},
|
|
3452
|
+
"privacy": "protected",
|
|
3453
|
+
"description": "Where focus lands on open. The classic Dialog Manager put the insertion\npoint in the first editable text item and gave nothing else focus at all;\nthe web needs a focused control for the keyboard to have anywhere to be,\nso with no text field the default button takes it (which is also what\nmakes Return and Space work there without a Tab). In order:\n\n1. a slotted control carrying `autofocus` — the author's say;\n2. the first enabled text-entry control (`vf-text-field`,\n `vf-number-field`, `vf-text-area`, or a native text input/textarea);\n3. the defaultButton;\n4. null — leave the browser's own choice alone.\n\nLeft to itself, `showModal()` focuses the first focusable thing in flat\ntree order, which for a Cancel/OK row is Cancel, and for a body with a\nlink in it is the link — so Return did the one thing a classic dialog's\nReturn never did. Override to choose differently.",
|
|
3454
|
+
"readonly": true,
|
|
3455
|
+
"inheritedFrom": {
|
|
3456
|
+
"name": "VfModalDialog",
|
|
3457
|
+
"module": "src/modal-dialog.ts"
|
|
3458
|
+
}
|
|
3459
|
+
},
|
|
3460
|
+
{
|
|
3461
|
+
"kind": "method",
|
|
3462
|
+
"name": "#focusInitial",
|
|
3463
|
+
"privacy": "private",
|
|
3464
|
+
"return": {
|
|
3465
|
+
"type": {
|
|
3466
|
+
"text": "void"
|
|
3467
|
+
}
|
|
3468
|
+
},
|
|
3469
|
+
"description": "Hand focus to initialFocusTarget, right after `showModal()`'s own\nfocusing steps have run (so this is the last word, not a race).",
|
|
3470
|
+
"inheritedFrom": {
|
|
3471
|
+
"name": "VfModalDialog",
|
|
3472
|
+
"module": "src/modal-dialog.ts"
|
|
3473
|
+
}
|
|
3474
|
+
},
|
|
3475
|
+
{
|
|
3476
|
+
"kind": "field",
|
|
3477
|
+
"name": "#onDialogKeydown",
|
|
3478
|
+
"privacy": "private",
|
|
3479
|
+
"description": "Return and Enter activate the default button from anywhere in the\ndialog, the way the classic Dialog Manager routed them — including from a\nfocused Cancel button, where Space is the key that presses the focused\ncontrol. Bubble phase, and only an uncancelled press: a control that\ntakes Enter for itself (`vf-select`'s list, `vf-list`, an icon's rename, a\ntext field whose form ran its implicit submission) cancels the keydown\nand is left alone. Two more keep their own Enter: a link, which follows\nit, and a multi-line editor, where Return inserts a newline and only the\nkeypad's Enter key fires the button — the same split a TextEdit item in a\nclassic dialog drew between the two keys.",
|
|
3387
3480
|
"inheritedFrom": {
|
|
3388
3481
|
"name": "VfModalDialog",
|
|
3389
3482
|
"module": "src/modal-dialog.ts"
|
|
@@ -4352,52 +4445,16 @@
|
|
|
4352
4445
|
},
|
|
4353
4446
|
{
|
|
4354
4447
|
"kind": "javascript-module",
|
|
4355
|
-
"path": "src/components/vf-icon.ts",
|
|
4448
|
+
"path": "src/components/vf-icon-field.ts",
|
|
4356
4449
|
"declarations": [
|
|
4357
4450
|
{
|
|
4358
4451
|
"kind": "class",
|
|
4359
|
-
"description": "`<vf-icon>` — a Finder icon: pixel art in a reserved cell with its name on a\nplate below, selectable, movable and renameable in place.\n\nIt is the one composite in the kit whose parts already existed separately —\n`vf-img` draws the art, `vf-list-item` inverts on selection, `vf-window`\ndrags — and the reason to have it anyway is that the *icon* is the unit the\nFinder actually manipulates. A picture and a caption that select together,\nmove together and rename together is a control, not a layout.\n\n### The art arrives by slot\n\nTwo slots, one per resource size — `large` is the 32×32 `ICN#` and `small`\nthe 16×16 `ics#` — each holding a `vf-img` around the consumer's own `<img>`:\n\n```html\n<vf-icon label=\"Macintosh HD\" selectable movable editable>\n <vf-img slot=\"large\"><img src=\"hd-32.png\" alt=\"\"></vf-img>\n <vf-img slot=\"small\"><img src=\"hd-16.png\" alt=\"\"></vf-img>\n</vf-icon>\n```\n\nSlots rather than `src` properties, for the reason `vf-img` exists at all:\nthe kit ships no raster files and never builds an `<img>` on a consumer's\nbehalf, so the graphic stays a real element in the light DOM with its own\n`alt`, `srcset`, loading behavior and asset URLs. A `src` string can express\nnone of that, and it could not hold an inline `<svg>` or a `<canvas>` either.\n`vf-list-item`'s `icon` slot makes the same trade.\n\nThe cost is that both files fetch even though one paints — pay it with two\ndata URIs, or slot only the size that view uses.\n\n### The cell is reserved, the art is registered in it\n\n`size` picks which slot paints *and* the cell it paints in: 32×32 or 16×16,\nheld whether or not there is art for it. A folder is 32×20 of ink and a\ndocument 25×32, and a row of them keeps one baseline only because the cell —\nnot the ink — is the unit, which is what an icon resource always was.\n\n### Everything centered lands on a whole pixel, by parity\n\nThe frame centers two things over one axis — the art cell and the name — and\na centered child sits at `(box − child) / 2`. That is a whole number exactly\nwhen the box and the child have the **same parity**. Half a system pixel is\nwhat fringes 1-bit art: the glyph stems smear across two device columns and\ngo gray, while the plate behind them stays sharp, because backgrounds are\npixel-snapped by the compositor and glyphs are not. A crisp plate under a\ngrey name is the signature of exactly this.\n\nSo the component makes the parities agree instead of correcting afterwards.\nThe cell is 32 or 16, and #measurePlate sizes the name plate to a\nwhole **even** number of system px — measured from the text, rounded up. Both\nchildren are then even, and so is every offset, at every display density and\nwhether or not a `width` is declared. Nothing is snapped, nothing leans on\nthe rasterizer, and `verify:icon` asserts both the geometry and that a field\nof names renders without a single gray pixel — with the kit's normal\nantialiasing left on, since a run on whole pixels gives it nothing to smooth.\n\nYour side of it is one rule: **a declared `width` must be even**, since the\ncomponent cannot round a number you chose. Supply art at the cell size too —\nwhich \"one image pixel is one system pixel\" already implies — because art\ndiffering from its cell by an odd number centers the same way.\n\n### Selection inverts, because the art is a 1-bit mask\n\nA System 7 icon is black ink and opaque white on a transparent surround —\nprecisely an image plus its mask — so inverting it flips ink and fill and\nleaves the surround alone, which is the whole of the classic selected\nappearance. `filter: invert(1)` reproduces it exactly for 1-bit art. Color\nart would invert into a photographic negative, which was never what\nSystem 7 showed: declare `color` and selection **darkens** the art\ninstead — Icon Utilities' ttSelected transform, every color blended\nhalfway toward black (`brightness(0.5)`), whites going gray, the\ntransparent surround still untouched. The declaration is the consumer's\nbecause the component cannot tell color art from a mask by looking at a\nslotted image. It covers whatever the cell shows, the derived open ghost\nincluded, so a color icon has one selected treatment open or closed. The\nlabel plate inverts to the `--vf-highlight` pair either way, sharing one\nselection color with `vf-list-item` — the selected name was inverted\nwhatever the art.\n\n### Open is derived, not shipped\n\nWith `open`, the art redraws as the Finder's open ghost: the outline held\nin solid black, the interior re-filled with the kit's loose 25% dither (the\nscrollbar trough's lattice), the transparent surround untouched. There is\nno second raster and no second fetch — the same alpha channel that makes\nselection an inversion makes the ghost derivable, and `src/open-art.ts`\nderives it from the slotted art by canvas compositing alone. No pixels are\never read back, so a cross-origin image that taints its canvas still works:\ntaint forbids reading, not drawing or displaying.\n\nThe ghost keeps the shape selection expects — ink and opaque white on a\ntransparent surround — so a selected open icon inverts exactly as a closed\none does, with no second treatment. The slot stays in the tree while the\nghost paints, hidden (it is where the art loads, and re-loads, from), and\nart the pipeline cannot draw — nothing slotted yet, a failed load, an\ninline `<svg>` — keeps rendering as itself rather than vanishing behind a\nstate it cannot show.\n\n### `movable`, not `draggable`\n\n`draggable` is a global HTML attribute *and* an `HTMLElement` accessor, so\ndeclaring it would both shadow a platform member and hand the element to the\nbrowser's own drag-and-drop machinery. This is the `align` trap from\n`vf-stack` in a second costume — `align`, `hidden`, `dir`, `draggable` and\n`title` all carry behavior a custom element never asked for. The kit already\nspells this parameter `movable` on `vf-window`, so the icon does too, and it\nmoves the same way: `DragController` tracks the gesture and\n`PlacementController` writes the result into `left`/`top` in whole system px,\nthe art's own unit — the same pair markup places an icon with, so a moved\nicon is still where it was dropped after a zoom.\n\nDragging is a pointer gesture with no keyboard equivalent, which is the kind\nof gap the kit closes rather than inherits (SPEC §1): a focused movable icon\nalso moves under the arrow keys, one system px at a time and eight with\nShift. Focus is what `selectable` grants, so the keyboard half of `movable`\nand `editable` presupposes it — see the role section below.\n\nOpening gets the same treatment. The double-click is the pointer gesture,\nand its keyboard route is ⌘O / ⌘↓ — the System 7 Open shortcuts, with Ctrl\nstanding in for ⌘ off the Mac. Return is deliberately not one of them: the\nFinder's Return renamed, never opened, so on an editable icon it starts the\nedit and on a non-editable one it does nothing at all.\n\n### The name and the art are one target, and the second click decides\n\nA double-click opens the icon *wherever it lands* — the name is as much the\nicon as the picture is, and the Finder never made you aim at the 32 pixels of\nart. But the name is also where a single click renames, so the two gestures\nbegin with the same press and only the second one tells them apart.\n\nSo the rename waits for it. A press on the plate of an already-selected icon\narms the field rather than opening it, and the next press inside\nRENAME_DELAY_MS calls it off — leaving the double-click to open, with\nno rename box flashing up behind it. Nothing needs to *undo* an edit that\nbegan: the press that starts one and the press that opens are the same\npress, so the only thing that can be got right is not committing early.\n\nThe window is generous in the one direction that is cheap. Reading a lone\nclick as a pair costs a wait before the box appears; reading a pair as a lone\nclick renames when the user asked to open. The same reasoning covers a press\nthat turns into a drag, a press elsewhere, and any key — each calls the\npending rename off, because none of them is the click it is waiting for.\n\n### An icon alone is a picture; an icon in a field is an option\n\n`role=\"option\"` is only meaningful inside a `listbox` that owns it. Written\nunconditionally it is not merely untidy — the browser *drops* it, and\n`aria-selected` with it, so a `selectable` icon announced as a bare generic\nand its selection state reached assistive tech nowhere at all.\n\nSo the role follows the container. Owned, the icon is an `option` that names\nitself from its plate and publishes `aria-selected`. Unowned, it degrades to\n`role=\"img\"` with a name — the same vocabulary the derived open ghost uses,\nand true of what it is. Deliberately not `button`: that would promise Enter\nand Space activate, and here Return *renames* while the open route is ⌘O / ⌘↓.\n\nDeclaring the owner is one attribute on whatever already holds the field, and\nit is what buys the selection state back:\n\n```html\n<div role=\"listbox\" aria-label=\"Desktop\" aria-multiselectable=\"true\">\n <vf-icon label=\"Macintosh HD\" selectable movable editable>…</vf-icon>\n <vf-icon label=\"Trash\" selectable movable editable>…</vf-icon>\n</div>\n```\n\nA `vf-desktop` cannot be that container itself: it also holds windows and a\nmenu bar, and a non-`option` child of a listbox is invalid the same way the\norphaned option was. The plain wrapper above is layout-neutral — placed icons\nanchor to the nearest *positioned* ancestor, which is still the desktop's\nraster. One divergence from the APG listbox is deliberate: its options share\na single roving tab stop, while these stay one stop each, the way a Finder\nicon is reached on its own.\n\n**`selectable` is what makes an icon focusable**, and `movable`/`editable`\npresuppose it. That is the Finder's own model — you cannot move or rename\nwhat you have not selected — and the pointer path already assumed it: the\nrename opens on a press on the plate of an *already-selected* icon. A\n`movable`-only icon is a picture you can drag, not a widget.\n\n### The label is a property, because it is editable\n\nThe caption is `label` rather than slotted content: renaming means the\ncomponent owns the string and hands it back on `vf-change`, and it cannot own\ntext that lives in the consumer's DOM. An empty `label` draws no plate at\nall — that *is* the \"no label\" parameter, in preference to a second attribute\nthat could disagree with it. `editable` then lets a click on the plate of an\nalready-selected icon open the rename box a moment later, as the Finder's\ndoes (see above), with Return committing, Escape reverting, and the plate\nwidening as you type.\n\n### A name is never abbreviated, and never folded\n\nThere is no ellipsis, no clipping and no wrapping: one line, always. System 7\nsolved the long-name problem at the *other* end — HFS capped a filename at 31\ncharacters — so the Finder could afford to always draw the name in full, and\ndid. A name wider than its cell simply overflows it, centered, the way a name\nwider than a 32-pixel icon always did.\n\nStaying on one line is also what keeps the name on the grid: a single run has\na single measured width, so the even-plate rule above is one number rather\nthan one per line, each with its own parity. `width` is therefore the cell —\nthe grid pitch — not a bound on the name.\n\n`maxlength` (31) bounds the rename field rather than the `label` property:\nthe name belongs to the consumer's model, and truncating one handed to us\nwould lose data.\n\nThe box hugs its text while you type it, not only once you are done, which is\nwhat keeps the name from moving when the edit commits: the plate is the same\nwidth either side of it, so the glyphs stay exactly where they were.\n\nA file also has to be called *something*, so a rename committed empty — or\nas nothing but spaces — is refused rather than applied, and the previous name\ncomes back, which is what System 7 did. An empty `label` is still a perfectly\ngood state to *start* in, though: a freshly made icon has no name until it is\ngiven one. Such an icon draws no plate but stays selectable, focusable and\nrenameable — Return opens a field, and an edit with nothing in it yet is the\none time the box stops hugging and reserves a cell's width, since a field you\ncannot see is one you cannot type into.",
|
|
4360
|
-
"name": "
|
|
4361
|
-
"cssProperties": [
|
|
4362
|
-
{
|
|
4363
|
-
"description": "Space between the art cell and the name plate",
|
|
4364
|
-
"name": "--vf-icon-gap"
|
|
4365
|
-
},
|
|
4366
|
-
{
|
|
4367
|
-
"description": "The name plate's line box",
|
|
4368
|
-
"name": "--vf-icon-label-height"
|
|
4369
|
-
}
|
|
4370
|
-
],
|
|
4371
|
-
"cssParts": [
|
|
4372
|
-
{
|
|
4373
|
-
"description": "The icon and its label plate, stacked.",
|
|
4374
|
-
"name": "frame"
|
|
4375
|
-
},
|
|
4376
|
-
{
|
|
4377
|
-
"description": "The reserved art cell.",
|
|
4378
|
-
"name": "icon"
|
|
4379
|
-
},
|
|
4380
|
-
{
|
|
4381
|
-
"description": "The name block; its lines are centered under the art.",
|
|
4382
|
-
"name": "label"
|
|
4383
|
-
},
|
|
4384
|
-
{
|
|
4385
|
-
"description": "The inked run behind the name, which each wrapped line gets its own of (inverts when selected).",
|
|
4386
|
-
"name": "plate"
|
|
4387
|
-
},
|
|
4388
|
-
{
|
|
4389
|
-
"description": "The rename field, while editing.",
|
|
4390
|
-
"name": "input"
|
|
4391
|
-
}
|
|
4392
|
-
],
|
|
4452
|
+
"description": "`<vf-icon-field>` — the container a field of `vf-icon`s sits in: a desktop's\nicons, a folder window's contents.\n\nEvery other set in the kit has its container — `vf-list` owns\n`vf-list-item`, `vf-radio-group` owns `vf-radio`, `vf-menu` owns\n`vf-menu-item`, `vf-select` owns `vf-option` — and `vf-icon` was the one\nitem whose container the consumer wrote by hand: a bare\n`<div role=\"listbox\" aria-multiselectable=\"true\">`, because an `option` is\nonly valid inside a `listbox` that owns it, and every kit container is a\npositioning anchor by design, so nothing in the kit was neutral enough to\nstand in. A consumer who forgot the role got icons that silently degraded\nto pictures. This is that container, as kit furniture:\n\n```html\n<vf-desktop width=\"512\" height=\"342\">\n <vf-icon-field label=\"Desktop\" fill-width fill-height>\n <vf-icon label=\"Macintosh HD\" width=\"64\" selectable movable editable left=\"16\" top=\"24\">…</vf-icon>\n <vf-icon label=\"Trash\" width=\"64\" selectable movable editable left=\"16\" top=\"280\">…</vf-icon>\n </vf-icon-field>\n <vf-window heading=\"Documents\" width=\"320\" height=\"201\" scrollbars=\"both\" top=\"40\" left=\"120\">\n <vf-icon-field label=\"Documents\">\n <vf-icon label=\"Read Me\" width=\"64\" selectable movable editable left=\"16\" top=\"16\">…</vf-icon>\n </vf-icon-field>\n </vf-window>\n</vf-desktop>\n```\n\n### Semantics through internals\n\n`role=\"listbox\"` and `aria-multiselectable=\"true\"` always — the Finder was\nalways multi-select, so there is no attribute for it — and `label` as the\naccessible name, the way `vf-scroll-area` takes one. Internals values are\ndefaults (SPEC §2): a consumer's own `role`/`aria-*` on the tag still wins.\nA `selectable` icon inside becomes a real `option` carrying `aria-selected`;\n`vf-icon` matches this tag directly, since a role written through internals\nnever lands as an attribute for `closest()` to find.\n\n### Layout-neutral, exactly what the div was\n\nAn in-flow block with no size, no inset and no position of its own,\npainting nothing — the `vf-stack` posture. A field whose icons are all\nplaced is a zero-height block, so the icons keep anchoring to the desktop's\nraster or the window's plane, stored coordinates mean what they meant, and\na press on the bare dither still reaches the desktop. It takes `top`/`left`\nand `width`/`height` like the other declared-box containers, so a page that\nwants the field to be a box places and sizes it, at which point it becomes\nthe anchor — the placement working, as documented for everything.\n`fill-width`/`fill-height` are the other way to give it a box, read about\nthe host the way `vf-stack` and `vf-container` read them: a filled field\nstays in flow and static, so its icons keep their anchor, and it has a\nsurface to press — which is what the rubber band needs.\n\n### The rubber band\n\nA press on the field's own background — not on an icon — dragged across\nthe field draws the Finder's dotted selection rectangle from the press to\nthe pointer, and the selection follows it live by the Finder's one rule:\n**an icon is selected exactly when it was selected at the press XOR the\nrectangle touches it.** The press fixes that anchor: a plain press has\nalready cleared everything (the way any press outside an icon does), so\nthe rectangle simply selects what it touches and releases what it leaves;\nwith Shift or ⌘ held the selection survives the press and the rectangle\n*toggles* against it — an icon already selected deselects while the\nrectangle covers it and comes back when it leaves, one that was not\nselects. Each change reports `vf-select` as a press would. \"Touches\"\nmeans the icon's art cell or its name plate (VfIcon.touches), not\nthe empty cell beside them, so a rectangle through the gap between two\nicons selects neither. The release keeps the result; Escape or a\n`pointercancel` cancels, the rectangle vanishes and the selection goes\nback to the anchor. A press that never leaves its system pixel draws\nnothing.\n\nThe rectangle is drawn with the drag outline's pen (* paintSelectionRect: the dotted one-pixel outline, `filter: invert(1)`\nunder `mix-blend-mode: difference`) at the field's own level, which is\nwhere the Finder drew its marquee — on the desktop under the windows, in a\nwindow body over that window's content and under any window above it —\npositioned against the viewport from the field's own shadow, since a\nlayout-neutral field has no box of its own to anchor to. It reaches no\nfurther than the field, inside every clip above the field (a window body,\na scroll viewport, the raster's edge), so it never paints outside the\nregion the icons are visible in. Its dots are phased against the field's\nown box; a field at the screen's origin shares the desktop dither's phase.\n\nOnly a field with a box can be pressed: fill it, or place and size it. An\nunplaced, unfilled field of placed icons is zero-height and draws no band.\n`touch-action` is left alone, so on a touch screen a drag on the\nbackground pans a scrolling window as it always did; the band is a mouse\nand pen gesture.\n\n### `size` is the view's\n\n`large` or `small`, written onto every icon in the field when set and\nwhenever it changes, so View → by Small Icon is one attribute instead of a\nloop over every icon. Unset, each icon keeps its own.\n\n### The desktop uses it, and renders none\n\nA desktop's icons sit in a field slotted into `vf-desktop` beside the\nwindows, written by the page; a folder window's icons sit in one slotted\ninto the window body. `vf-desktop` builds no field of its own: its furniture\nis slotted light DOM and the page decides what it is, a desktop with no\nicons should carry no empty listbox, and a field inside the desktop's\nshadow is one `closest()` could not reach.",
|
|
4453
|
+
"name": "VfIconField",
|
|
4393
4454
|
"slots": [
|
|
4394
4455
|
{
|
|
4395
|
-
"description": "The
|
|
4396
|
-
"name": "
|
|
4397
|
-
},
|
|
4398
|
-
{
|
|
4399
|
-
"description": "The 16×16 art, shown under `size=\"small\"`.",
|
|
4400
|
-
"name": "small"
|
|
4456
|
+
"description": "The icons.",
|
|
4457
|
+
"name": ""
|
|
4401
4458
|
}
|
|
4402
4459
|
],
|
|
4403
4460
|
"members": [
|
|
@@ -4408,151 +4465,659 @@
|
|
|
4408
4465
|
"text": "string"
|
|
4409
4466
|
},
|
|
4410
4467
|
"default": "''",
|
|
4411
|
-
"description": "
|
|
4468
|
+
"description": "Accessible name for the field — \"Desktop\", the folder's name — applied as\nthe listbox's `aria-label` through internals, so a consumer's own\n`aria-label` on the tag still wins.",
|
|
4412
4469
|
"attribute": "label"
|
|
4413
4470
|
},
|
|
4414
4471
|
{
|
|
4415
4472
|
"kind": "field",
|
|
4416
4473
|
"name": "size",
|
|
4417
4474
|
"type": {
|
|
4418
|
-
"text": "VfIconSize"
|
|
4475
|
+
"text": "VfIconSize | null | undefined"
|
|
4419
4476
|
},
|
|
4420
|
-
"
|
|
4421
|
-
"description": "Which member of the icon family paints, and the cell it is registered in:\n`large` (32×32) or `small` (16×16).",
|
|
4477
|
+
"description": "The view's icon size, `large` or `small`, written onto every icon in the\nfield — View → by Small Icon as one attribute. Unset, each icon keeps\nits own `size`.",
|
|
4422
4478
|
"attribute": "size",
|
|
4423
4479
|
"reflects": true
|
|
4424
4480
|
},
|
|
4425
4481
|
{
|
|
4426
4482
|
"kind": "field",
|
|
4427
|
-
"name": "
|
|
4428
|
-
"
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
"
|
|
4432
|
-
"description": "Clicking selects. Set `selected` yourself to drive selection some other way.\n\nThis is also the flag that makes an icon focusable and gives it a role, so\nthe keyboard halves of movable and editable presuppose it —\nas the Finder did. A container carrying `role=\"listbox\"` turns the role from\n`img` into a real `option`; see the class doc.",
|
|
4433
|
-
"attribute": "selectable",
|
|
4434
|
-
"reflects": true
|
|
4435
|
-
},
|
|
4436
|
-
{
|
|
4437
|
-
"kind": "field",
|
|
4438
|
-
"name": "selected",
|
|
4439
|
-
"type": {
|
|
4440
|
-
"text": "boolean"
|
|
4441
|
-
},
|
|
4442
|
-
"default": "false",
|
|
4443
|
-
"description": "Whether the icon is selected: the art inverts and the plate goes black.",
|
|
4444
|
-
"attribute": "selected",
|
|
4445
|
-
"reflects": true
|
|
4483
|
+
"name": "scale",
|
|
4484
|
+
"privacy": "private",
|
|
4485
|
+
"readonly": true,
|
|
4486
|
+
"default": "new ScaleController(this)",
|
|
4487
|
+
"description": "Default-on display scaling (true 72dpi size); see src/scale.ts. A lone\nfield with a declared size on a plain page would otherwise resolve that\nsize against the `var(--vf-scale, 1)` fallback while its icons\nself-scaled around it — the vf-stack reasoning."
|
|
4446
4488
|
},
|
|
4447
4489
|
{
|
|
4448
4490
|
"kind": "field",
|
|
4449
|
-
"name": "
|
|
4450
|
-
"
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
"default": "false",
|
|
4454
|
-
"description": "Declares the slotted art a **color icon**, so selection darkens it — the\nttSelected transform, every color blended halfway toward black — instead\nof inverting it into a photographic negative (see the class doc). Yours\nto declare because the component cannot tell color art from a 1-bit mask\nby looking at a slotted image. The label plate inverts either way.",
|
|
4455
|
-
"attribute": "color",
|
|
4456
|
-
"reflects": true
|
|
4491
|
+
"name": "#internals",
|
|
4492
|
+
"privacy": "private",
|
|
4493
|
+
"readonly": true,
|
|
4494
|
+
"description": "ARIA goes through internals, never `setAttribute` on the host: internals\nvalues are *defaults*, so a consumer's own `role`/`aria-*` on the tag wins\n— the platform's own precedence. See SPEC §2."
|
|
4457
4495
|
},
|
|
4458
4496
|
{
|
|
4459
4497
|
"kind": "field",
|
|
4460
|
-
"name": "
|
|
4498
|
+
"name": "#band",
|
|
4499
|
+
"privacy": "private",
|
|
4461
4500
|
"type": {
|
|
4462
|
-
"text": "
|
|
4501
|
+
"text": "Band | null"
|
|
4463
4502
|
},
|
|
4464
|
-
"default": "
|
|
4465
|
-
"description": "The
|
|
4466
|
-
"attribute": "open",
|
|
4467
|
-
"reflects": true
|
|
4503
|
+
"default": "null",
|
|
4504
|
+
"description": "The rubber band in flight, or null between gestures."
|
|
4468
4505
|
},
|
|
4469
4506
|
{
|
|
4470
4507
|
"kind": "field",
|
|
4471
|
-
"name": "
|
|
4472
|
-
"
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
"
|
|
4476
|
-
"description": "Drag to move — `movable`, never `draggable`, which is a platform attribute\nand accessor (see the class doc). Arrow keys move a focused icon too, which\nmeans pairing this with selectable: focus is what that grants.",
|
|
4477
|
-
"attribute": "movable",
|
|
4478
|
-
"reflects": true
|
|
4508
|
+
"name": "#escape",
|
|
4509
|
+
"privacy": "private",
|
|
4510
|
+
"readonly": true,
|
|
4511
|
+
"default": "new DocumentListenersController(this, () => [ [document, 'keydown', this.#onKeyDown, true], ])",
|
|
4512
|
+
"description": "Escape mid-band cancels — the in-flight-gesture idiom `vf-icon`'s drag\nuses, on the document because the field never takes focus. Capture\nphase, and the key is stopped there, so an enclosing `vf-dialog` never\nreads it as a dismissal."
|
|
4479
4513
|
},
|
|
4480
4514
|
{
|
|
4481
4515
|
"kind": "field",
|
|
4482
|
-
"name": "
|
|
4483
|
-
"
|
|
4484
|
-
|
|
4485
|
-
},
|
|
4486
|
-
"default": "false",
|
|
4487
|
-
"description": "The name can be renamed in place: click a selected plate, or press Return.\nPair with selectable — both routes start from a selected icon.\n\nThe pointer route opens the field once the double-click window has passed\n(RENAME_DELAY_MS), so double-clicking the *name* opens the icon the\nway double-clicking its art does. Return opens it at once — a keypress has\nno second half to wait for.",
|
|
4488
|
-
"attribute": "editable",
|
|
4489
|
-
"reflects": true
|
|
4516
|
+
"name": "#onSlotChange",
|
|
4517
|
+
"privacy": "private",
|
|
4518
|
+
"description": "Icons arriving after the view was set take the view's size."
|
|
4490
4519
|
},
|
|
4491
4520
|
{
|
|
4492
|
-
"kind": "
|
|
4493
|
-
"name": "
|
|
4494
|
-
"
|
|
4495
|
-
|
|
4521
|
+
"kind": "method",
|
|
4522
|
+
"name": "#applySize",
|
|
4523
|
+
"privacy": "private",
|
|
4524
|
+
"return": {
|
|
4525
|
+
"type": {
|
|
4526
|
+
"text": "void"
|
|
4527
|
+
}
|
|
4496
4528
|
},
|
|
4497
|
-
"description": "
|
|
4498
|
-
"attribute": "width"
|
|
4529
|
+
"description": "Push the view's size onto every icon in the field. Descendants rather\nthan direct children, so an icon inside a wrapper is one of the field's\ntoo; a property write, which Lit keeps for an icon that has not upgraded\nyet. Unset means hands off."
|
|
4499
4530
|
},
|
|
4500
4531
|
{
|
|
4501
|
-
"kind": "
|
|
4502
|
-
"name": "
|
|
4503
|
-
"
|
|
4504
|
-
|
|
4532
|
+
"kind": "method",
|
|
4533
|
+
"name": "#icons",
|
|
4534
|
+
"privacy": "private",
|
|
4535
|
+
"return": {
|
|
4536
|
+
"type": {
|
|
4537
|
+
"text": "VfIcon[]"
|
|
4538
|
+
}
|
|
4505
4539
|
},
|
|
4506
|
-
"
|
|
4507
|
-
"description": "The most characters the rename field will accept — 31, the HFS filename\nlimit every System 7 name was cut to, which is also why the Finder could\nafford to always draw the whole name. Going past it fires\n`vf-name-too-long` with what was tried, so a host can say so rather than\nlet characters vanish.\n\nIt bounds *typing* only: a `label` set from your own data is displayed as\ngiven and fires nothing, since the name belongs to your model and silently\ntruncating it would lose data.",
|
|
4508
|
-
"attribute": "maxlength"
|
|
4540
|
+
"description": "The icons the band can select: every selectable one in the field."
|
|
4509
4541
|
},
|
|
4510
4542
|
{
|
|
4511
4543
|
"kind": "field",
|
|
4512
|
-
"name": "
|
|
4544
|
+
"name": "#onPointerDown",
|
|
4513
4545
|
"privacy": "private",
|
|
4514
|
-
"
|
|
4515
|
-
"default": "new ScaleController(this)",
|
|
4516
|
-
"description": "Default-on display scaling (true 72dpi size); see src/scale.ts."
|
|
4546
|
+
"description": "A press on the field's own background. `event.target` is the host only\nwhen nothing slotted was hit — a press on an icon reaches here with the\nicon as its target, and that press is the icon's own gesture. The\nanchor is read after the icons' outside-press listeners have run\n(document capture, ahead of this): what they left selected is what\nShift or ⌘ kept — nothing, after a plain press."
|
|
4517
4547
|
},
|
|
4518
4548
|
{
|
|
4519
4549
|
"kind": "field",
|
|
4520
|
-
"name": "
|
|
4550
|
+
"name": "#onPointerMove",
|
|
4521
4551
|
"privacy": "private",
|
|
4522
|
-
"
|
|
4523
|
-
"default": "new GridSnapController(this)",
|
|
4524
|
-
"description": "Device-pixel grid snapping; see src/grid-snap.ts."
|
|
4552
|
+
"description": "A step of the band. The first step whose point differs from the press\nstarts it — a stationary press draws nothing and changes nothing beyond\nwhat the press itself did. Each changed rectangle is redrawn and the\nicons under it re-read."
|
|
4525
4553
|
},
|
|
4526
4554
|
{
|
|
4527
4555
|
"kind": "field",
|
|
4528
|
-
"name": "
|
|
4529
|
-
"privacy": "private"
|
|
4530
|
-
"readonly": true,
|
|
4531
|
-
"default": "new FocusRuleController(this)",
|
|
4532
|
-
"description": "Whether to wear the dashed focus rule; see src/focus-modality.ts."
|
|
4556
|
+
"name": "#onPointerUp",
|
|
4557
|
+
"privacy": "private"
|
|
4533
4558
|
},
|
|
4534
4559
|
{
|
|
4535
4560
|
"kind": "field",
|
|
4536
|
-
"name": "
|
|
4537
|
-
"
|
|
4538
|
-
"text": "boolean"
|
|
4539
|
-
},
|
|
4540
|
-
"privacy": "private",
|
|
4541
|
-
"default": "false",
|
|
4542
|
-
"description": "True while the rename field is open."
|
|
4561
|
+
"name": "#onKeyDown",
|
|
4562
|
+
"privacy": "private"
|
|
4543
4563
|
},
|
|
4544
4564
|
{
|
|
4545
|
-
"kind": "
|
|
4546
|
-
"name": "
|
|
4547
|
-
"type": {
|
|
4548
|
-
"text": "string"
|
|
4549
|
-
},
|
|
4565
|
+
"kind": "method",
|
|
4566
|
+
"name": "#end",
|
|
4550
4567
|
"privacy": "private",
|
|
4551
|
-
"
|
|
4552
|
-
|
|
4568
|
+
"return": {
|
|
4569
|
+
"type": {
|
|
4570
|
+
"text": "void"
|
|
4571
|
+
}
|
|
4572
|
+
},
|
|
4573
|
+
"parameters": [
|
|
4574
|
+
{
|
|
4575
|
+
"name": "cancelled",
|
|
4576
|
+
"type": {
|
|
4577
|
+
"text": "boolean"
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
],
|
|
4581
|
+
"description": "The release, or a cancel. A band that never started (a click) has\nnothing to undo. A release keeps the selection the band made; a cancel\nputs the anchor back."
|
|
4553
4582
|
},
|
|
4554
4583
|
{
|
|
4555
|
-
"kind": "
|
|
4584
|
+
"kind": "method",
|
|
4585
|
+
"name": "#point",
|
|
4586
|
+
"privacy": "private",
|
|
4587
|
+
"return": {
|
|
4588
|
+
"type": {
|
|
4589
|
+
"text": "{ x: number; y: number }"
|
|
4590
|
+
}
|
|
4591
|
+
},
|
|
4592
|
+
"parameters": [
|
|
4593
|
+
{
|
|
4594
|
+
"name": "event",
|
|
4595
|
+
"type": {
|
|
4596
|
+
"text": "PointerEvent"
|
|
4597
|
+
}
|
|
4598
|
+
},
|
|
4599
|
+
{
|
|
4600
|
+
"name": "box",
|
|
4601
|
+
"type": {
|
|
4602
|
+
"text": "DOMRect"
|
|
4603
|
+
}
|
|
4604
|
+
},
|
|
4605
|
+
{
|
|
4606
|
+
"name": "scale",
|
|
4607
|
+
"type": {
|
|
4608
|
+
"text": "number"
|
|
4609
|
+
}
|
|
4610
|
+
},
|
|
4611
|
+
{
|
|
4612
|
+
"name": "reach",
|
|
4613
|
+
"type": {
|
|
4614
|
+
"text": "Band['reach']"
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4617
|
+
],
|
|
4618
|
+
"description": "A pointer position as a point in system px from the field's box, whole\nand held inside the band's reach."
|
|
4619
|
+
},
|
|
4620
|
+
{
|
|
4621
|
+
"kind": "method",
|
|
4622
|
+
"name": "#reach",
|
|
4623
|
+
"privacy": "private",
|
|
4624
|
+
"return": {
|
|
4625
|
+
"type": {
|
|
4626
|
+
"text": "Band['reach']"
|
|
4627
|
+
}
|
|
4628
|
+
},
|
|
4629
|
+
"parameters": [
|
|
4630
|
+
{
|
|
4631
|
+
"name": "box",
|
|
4632
|
+
"type": {
|
|
4633
|
+
"text": "DOMRect"
|
|
4634
|
+
}
|
|
4635
|
+
},
|
|
4636
|
+
{
|
|
4637
|
+
"name": "scale",
|
|
4638
|
+
"type": {
|
|
4639
|
+
"text": "number"
|
|
4640
|
+
}
|
|
4641
|
+
}
|
|
4642
|
+
],
|
|
4643
|
+
"description": "How far the band may reach: the field's own box, intersected with every\nancestor that clips — a window body, a scroll viewport, the desktop's\nraster — found by walking the flat tree, slots and shadow hosts\nincluded. In system px from the field's box, whole, rounded inward."
|
|
4644
|
+
},
|
|
4645
|
+
{
|
|
4646
|
+
"kind": "method",
|
|
4647
|
+
"name": "#makeCanvas",
|
|
4648
|
+
"privacy": "private",
|
|
4649
|
+
"return": {
|
|
4650
|
+
"type": {
|
|
4651
|
+
"text": "HTMLCanvasElement"
|
|
4652
|
+
}
|
|
4653
|
+
},
|
|
4654
|
+
"description": "The band's canvas: the drag outline's XOR pen, positioned against the\nviewport from the field's own shadow — a layout-neutral field has no\npositioned box of its own for it to anchor to — and never a hit."
|
|
4655
|
+
},
|
|
4656
|
+
{
|
|
4657
|
+
"kind": "method",
|
|
4658
|
+
"name": "#paint",
|
|
4659
|
+
"privacy": "private",
|
|
4660
|
+
"return": {
|
|
4661
|
+
"type": {
|
|
4662
|
+
"text": "void"
|
|
4663
|
+
}
|
|
4664
|
+
},
|
|
4665
|
+
"parameters": [
|
|
4666
|
+
{
|
|
4667
|
+
"name": "band",
|
|
4668
|
+
"type": {
|
|
4669
|
+
"text": "Band"
|
|
4670
|
+
}
|
|
4671
|
+
},
|
|
4672
|
+
{
|
|
4673
|
+
"name": "scale",
|
|
4674
|
+
"type": {
|
|
4675
|
+
"text": "number"
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
],
|
|
4679
|
+
"description": "Draw the rectangle where it lands: whole system px from the field's box,\nwhich sits on the device grid, so the band does too. The checker's\nphase follows the rectangle's own corner parity in the field's box."
|
|
4680
|
+
},
|
|
4681
|
+
{
|
|
4682
|
+
"kind": "method",
|
|
4683
|
+
"name": "#selectUnder",
|
|
4684
|
+
"privacy": "private",
|
|
4685
|
+
"return": {
|
|
4686
|
+
"type": {
|
|
4687
|
+
"text": "void"
|
|
4688
|
+
}
|
|
4689
|
+
},
|
|
4690
|
+
"parameters": [
|
|
4691
|
+
{
|
|
4692
|
+
"name": "band",
|
|
4693
|
+
"type": {
|
|
4694
|
+
"text": "Band"
|
|
4695
|
+
}
|
|
4696
|
+
},
|
|
4697
|
+
{
|
|
4698
|
+
"name": "scale",
|
|
4699
|
+
"type": {
|
|
4700
|
+
"text": "number"
|
|
4701
|
+
}
|
|
4702
|
+
}
|
|
4703
|
+
],
|
|
4704
|
+
"description": "The selection under the band — the Finder's rule, re-evaluated for\nevery selectable icon on every changed rectangle: selected exactly when\nit was in the anchor XOR the rectangle touches it (its art cell or its\nplate, the icon's own reading). A plain press left an empty anchor, so\nthe rectangle selects what it touches and releases what it leaves; a\nShift or ⌘ press kept one, so the rectangle toggles against it. Each\nchange goes through the icon's own gesture route so `vf-select` reports\nit."
|
|
4705
|
+
},
|
|
4706
|
+
{
|
|
4707
|
+
"kind": "field",
|
|
4708
|
+
"name": "role",
|
|
4709
|
+
"type": {
|
|
4710
|
+
"text": "string"
|
|
4711
|
+
},
|
|
4712
|
+
"default": "'listbox'"
|
|
4713
|
+
},
|
|
4714
|
+
{
|
|
4715
|
+
"kind": "field",
|
|
4716
|
+
"name": "ariaMultiSelectable",
|
|
4717
|
+
"type": {
|
|
4718
|
+
"text": "string"
|
|
4719
|
+
},
|
|
4720
|
+
"default": "'true'"
|
|
4721
|
+
},
|
|
4722
|
+
{
|
|
4723
|
+
"kind": "field",
|
|
4724
|
+
"name": "width",
|
|
4725
|
+
"type": {
|
|
4726
|
+
"text": "number | null | undefined"
|
|
4727
|
+
},
|
|
4728
|
+
"description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
|
|
4729
|
+
"attribute": "width",
|
|
4730
|
+
"inheritedFrom": {
|
|
4731
|
+
"name": "VfSized",
|
|
4732
|
+
"module": "src/size.ts"
|
|
4733
|
+
}
|
|
4734
|
+
},
|
|
4735
|
+
{
|
|
4736
|
+
"kind": "field",
|
|
4737
|
+
"name": "height",
|
|
4738
|
+
"type": {
|
|
4739
|
+
"text": "number | null | undefined"
|
|
4740
|
+
},
|
|
4741
|
+
"description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
|
|
4742
|
+
"attribute": "height",
|
|
4743
|
+
"inheritedFrom": {
|
|
4744
|
+
"name": "VfSized",
|
|
4745
|
+
"module": "src/size.ts"
|
|
4746
|
+
}
|
|
4747
|
+
},
|
|
4748
|
+
{
|
|
4749
|
+
"kind": "field",
|
|
4750
|
+
"name": "top",
|
|
4751
|
+
"type": {
|
|
4752
|
+
"text": "number | null | undefined"
|
|
4753
|
+
},
|
|
4754
|
+
"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.",
|
|
4755
|
+
"attribute": "top",
|
|
4756
|
+
"inheritedFrom": {
|
|
4757
|
+
"name": "VfPositioned",
|
|
4758
|
+
"module": "src/position.ts"
|
|
4759
|
+
}
|
|
4760
|
+
},
|
|
4761
|
+
{
|
|
4762
|
+
"kind": "field",
|
|
4763
|
+
"name": "left",
|
|
4764
|
+
"type": {
|
|
4765
|
+
"text": "number | null | undefined"
|
|
4766
|
+
},
|
|
4767
|
+
"description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
|
|
4768
|
+
"attribute": "left",
|
|
4769
|
+
"inheritedFrom": {
|
|
4770
|
+
"name": "VfPositioned",
|
|
4771
|
+
"module": "src/position.ts"
|
|
4772
|
+
}
|
|
4773
|
+
},
|
|
4774
|
+
{
|
|
4775
|
+
"kind": "field",
|
|
4776
|
+
"name": "fixed",
|
|
4777
|
+
"type": {
|
|
4778
|
+
"text": "boolean"
|
|
4779
|
+
},
|
|
4780
|
+
"default": "false",
|
|
4781
|
+
"description": "Hold the placement against the visible region of the nearest scrolling\nancestor: the element keeps its stated `top`/`left` while the content\nscrolls under it, and the flag alone places it at (0,0). It comes\nbefore the flow content in its parent. Where nothing scrolls it renders\nexactly as placed.",
|
|
4782
|
+
"attribute": "fixed",
|
|
4783
|
+
"reflects": true,
|
|
4784
|
+
"inheritedFrom": {
|
|
4785
|
+
"name": "VfPositioned",
|
|
4786
|
+
"module": "src/position.ts"
|
|
4787
|
+
}
|
|
4788
|
+
}
|
|
4789
|
+
],
|
|
4790
|
+
"attributes": [
|
|
4791
|
+
{
|
|
4792
|
+
"name": "label",
|
|
4793
|
+
"type": {
|
|
4794
|
+
"text": "string"
|
|
4795
|
+
},
|
|
4796
|
+
"default": "''",
|
|
4797
|
+
"description": "Accessible name for the field — \"Desktop\", the folder's name — applied as\nthe listbox's `aria-label` through internals, so a consumer's own\n`aria-label` on the tag still wins.",
|
|
4798
|
+
"fieldName": "label"
|
|
4799
|
+
},
|
|
4800
|
+
{
|
|
4801
|
+
"name": "size",
|
|
4802
|
+
"type": {
|
|
4803
|
+
"text": "VfIconSize | null | undefined"
|
|
4804
|
+
},
|
|
4805
|
+
"description": "The view's icon size, `large` or `small`, written onto every icon in the\nfield — View → by Small Icon as one attribute. Unset, each icon keeps\nits own `size`.",
|
|
4806
|
+
"fieldName": "size"
|
|
4807
|
+
},
|
|
4808
|
+
{
|
|
4809
|
+
"name": "width",
|
|
4810
|
+
"type": {
|
|
4811
|
+
"text": "number | null | undefined"
|
|
4812
|
+
},
|
|
4813
|
+
"description": "Width in whole system px. What the number means is the component's own\naffair — the measure a paragraph wraps to, a caption column, a window's\nbox — but the mechanics are shared: the length lands on the host as a\nlive `calc(var(--vf-scale, 1) * Npx)`, so the box scales with the\ndisplay and sits on the device-pixel grid by construction. Remove it\nand the width returns to layout.",
|
|
4814
|
+
"fieldName": "width",
|
|
4815
|
+
"inheritedFrom": {
|
|
4816
|
+
"name": "VfSized",
|
|
4817
|
+
"module": "src/size.ts"
|
|
4818
|
+
}
|
|
4819
|
+
},
|
|
4820
|
+
{
|
|
4821
|
+
"name": "height",
|
|
4822
|
+
"type": {
|
|
4823
|
+
"text": "number | null | undefined"
|
|
4824
|
+
},
|
|
4825
|
+
"description": "Height in whole system px; see width. Content that outgrows a\ndeclared height overflows the box rather than growing it — the number\nis the layout, and content that doesn't fit is a number to raise.",
|
|
4826
|
+
"fieldName": "height",
|
|
4827
|
+
"inheritedFrom": {
|
|
4828
|
+
"name": "VfSized",
|
|
4829
|
+
"module": "src/size.ts"
|
|
4830
|
+
}
|
|
4831
|
+
},
|
|
4832
|
+
{
|
|
4833
|
+
"name": "top",
|
|
4834
|
+
"type": {
|
|
4835
|
+
"text": "number | null | undefined"
|
|
4836
|
+
},
|
|
4837
|
+
"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.",
|
|
4838
|
+
"fieldName": "top",
|
|
4839
|
+
"inheritedFrom": {
|
|
4840
|
+
"name": "VfPositioned",
|
|
4841
|
+
"module": "src/position.ts"
|
|
4842
|
+
}
|
|
4843
|
+
},
|
|
4844
|
+
{
|
|
4845
|
+
"name": "left",
|
|
4846
|
+
"type": {
|
|
4847
|
+
"text": "number | null | undefined"
|
|
4848
|
+
},
|
|
4849
|
+
"description": "Offset from the left of the positioning parent, in whole system px.\nSee top.",
|
|
4850
|
+
"fieldName": "left",
|
|
4851
|
+
"inheritedFrom": {
|
|
4852
|
+
"name": "VfPositioned",
|
|
4853
|
+
"module": "src/position.ts"
|
|
4854
|
+
}
|
|
4855
|
+
},
|
|
4856
|
+
{
|
|
4857
|
+
"name": "fixed",
|
|
4858
|
+
"type": {
|
|
4859
|
+
"text": "boolean"
|
|
4860
|
+
},
|
|
4861
|
+
"default": "false",
|
|
4862
|
+
"description": "Hold the placement against the visible region of the nearest scrolling\nancestor: the element keeps its stated `top`/`left` while the content\nscrolls under it, and the flag alone places it at (0,0). It comes\nbefore the flow content in its parent. Where nothing scrolls it renders\nexactly as placed.",
|
|
4863
|
+
"fieldName": "fixed",
|
|
4864
|
+
"inheritedFrom": {
|
|
4865
|
+
"name": "VfPositioned",
|
|
4866
|
+
"module": "src/position.ts"
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
],
|
|
4870
|
+
"mixins": [
|
|
4871
|
+
{
|
|
4872
|
+
"name": "VfSized",
|
|
4873
|
+
"module": "/src/size.js"
|
|
4874
|
+
},
|
|
4875
|
+
{
|
|
4876
|
+
"name": "VfPositioned",
|
|
4877
|
+
"module": "/src/position.js"
|
|
4878
|
+
}
|
|
4879
|
+
],
|
|
4880
|
+
"superclass": {
|
|
4881
|
+
"name": "LitElement",
|
|
4882
|
+
"package": "lit"
|
|
4883
|
+
},
|
|
4884
|
+
"tagName": "vf-icon-field",
|
|
4885
|
+
"customElement": true
|
|
4886
|
+
}
|
|
4887
|
+
],
|
|
4888
|
+
"exports": [
|
|
4889
|
+
{
|
|
4890
|
+
"kind": "js",
|
|
4891
|
+
"name": "VfIconField",
|
|
4892
|
+
"declaration": {
|
|
4893
|
+
"name": "VfIconField",
|
|
4894
|
+
"module": "src/components/vf-icon-field.ts"
|
|
4895
|
+
}
|
|
4896
|
+
},
|
|
4897
|
+
{
|
|
4898
|
+
"kind": "custom-element-definition",
|
|
4899
|
+
"name": "vf-icon-field",
|
|
4900
|
+
"declaration": {
|
|
4901
|
+
"name": "VfIconField",
|
|
4902
|
+
"module": "src/components/vf-icon-field.ts"
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
]
|
|
4906
|
+
},
|
|
4907
|
+
{
|
|
4908
|
+
"kind": "javascript-module",
|
|
4909
|
+
"path": "src/components/vf-icon.ts",
|
|
4910
|
+
"declarations": [
|
|
4911
|
+
{
|
|
4912
|
+
"kind": "class",
|
|
4913
|
+
"description": "`<vf-icon>` — a Finder icon: pixel art in a reserved cell with its name on a\nplate below, selectable, movable and renameable in place.\n\nIt is the one composite in the kit whose parts already existed separately —\n`vf-img` draws the art, `vf-list-item` inverts on selection, `vf-window`\ndrags — and the reason to have it anyway is that the *icon* is the unit the\nFinder actually manipulates. A picture and a caption that select together,\nmove together and rename together is a control, not a layout.\n\n### The art arrives by slot\n\nTwo slots, one per resource size — `large` is the 32×32 `ICN#` and `small`\nthe 16×16 `ics#` — each holding a `vf-img` around the consumer's own `<img>`:\n\n```html\n<vf-icon label=\"Macintosh HD\" selectable movable editable>\n <vf-img slot=\"large\"><img src=\"hd-32.png\" alt=\"\"></vf-img>\n <vf-img slot=\"small\"><img src=\"hd-16.png\" alt=\"\"></vf-img>\n</vf-icon>\n```\n\nSlots rather than `src` properties, for the reason `vf-img` exists at all:\nthe kit ships no raster files and never builds an `<img>` on a consumer's\nbehalf, so the graphic stays a real element in the light DOM with its own\n`alt`, `srcset`, loading behavior and asset URLs. A `src` string can express\nnone of that, and it could not hold an inline `<svg>` or a `<canvas>` either.\n`vf-list-item`'s `icon` slot makes the same trade.\n\nThe cost is that both files fetch even though one paints — pay it with two\ndata URIs, or slot only the size that view uses.\n\n### The cell is reserved, the art is registered in it\n\n`size` picks which slot paints *and* the cell it paints in: 32×32 or 16×16,\nheld whether or not there is art for it. A folder is 32×20 of ink and a\ndocument 25×32, and a row of them keeps one baseline only because the cell —\nnot the ink — is the unit, which is what an icon resource always was.\n\n### Everything centered lands on a whole pixel, by parity\n\nThe frame centers two things over one axis — the art cell and the name — and\na centered child sits at `(box − child) / 2`. That is a whole number exactly\nwhen the box and the child have the **same parity**. Half a system pixel is\nwhat fringes 1-bit art: the glyph stems smear across two device columns and\ngo gray, while the plate behind them stays sharp, because backgrounds are\npixel-snapped by the compositor and glyphs are not. A crisp plate under a\ngrey name is the signature of exactly this.\n\nSo the component makes the parities agree instead of correcting afterwards.\nThe cell is 32 or 16, and #measurePlate sizes the name plate to a\nwhole **even** number of system px — measured from the text, rounded up. Both\nchildren are then even, and so is every offset, at every display density and\nwhether or not a `width` is declared. Nothing is snapped, nothing leans on\nthe rasterizer, and `verify:icon` asserts both the geometry and that a field\nof names renders without a single gray pixel — with the kit's normal\nantialiasing left on, since a run on whole pixels gives it nothing to smooth.\n\nYour side of it is one rule: **a declared `width` must be even**, since the\ncomponent cannot round a number you chose. Supply art at the cell size too —\nwhich \"one image pixel is one system pixel\" already implies — because art\ndiffering from its cell by an odd number centers the same way.\n\n### Selection inverts, because the art is a 1-bit mask\n\nA System 7 icon is black ink and opaque white on a transparent surround —\nprecisely an image plus its mask — so inverting it flips ink and fill and\nleaves the surround alone, which is the whole of the classic selected\nappearance. `filter: invert(1)` reproduces it exactly for 1-bit art. Color\nart would invert into a photographic negative, which was never what\nSystem 7 showed: declare `color` and selection **darkens** the art\ninstead — Icon Utilities' ttSelected transform, every color blended\nhalfway toward black (`brightness(0.5)`), whites going gray, the\ntransparent surround still untouched. The declaration is the consumer's\nbecause the component cannot tell color art from a mask by looking at a\nslotted image. It covers whatever the cell shows, the derived open ghost\nincluded, so a color icon has one selected treatment open or closed. The\nlabel plate inverts to the `--vf-highlight` pair either way, sharing one\nselection color with `vf-list-item` — the selected name was inverted\nwhatever the art.\n\n### Open is derived, not shipped\n\nWith `open`, the art redraws as the Finder's open ghost: the outline held\nin solid black, the interior re-filled with the kit's loose 25% dither (the\nscrollbar trough's lattice), the transparent surround untouched. There is\nno second raster and no second fetch — the same alpha channel that makes\nselection an inversion makes the ghost derivable, and `src/open-art.ts`\nderives it from the slotted art by canvas compositing alone. No pixels are\never read back, so a cross-origin image that taints its canvas still works:\ntaint forbids reading, not drawing or displaying.\n\nThe ghost keeps the shape selection expects — ink and opaque white on a\ntransparent surround — so a selected open icon inverts exactly as a closed\none does, with no second treatment. The slot stays in the tree while the\nghost paints, hidden (it is where the art loads, and re-loads, from), and\nart the pipeline cannot draw — nothing slotted yet, a failed load, an\ninline `<svg>` — keeps rendering as itself rather than vanishing behind a\nstate it cannot show.\n\n### `movable`, not `draggable`\n\n`draggable` is a global HTML attribute *and* an `HTMLElement` accessor, so\ndeclaring it would both shadow a platform member and hand the element to the\nbrowser's own drag-and-drop machinery. This is the `align` trap from\n`vf-stack` in a second costume — `align`, `hidden`, `dir`, `draggable` and\n`title` all carry behavior a custom element never asked for. The kit already\nspells this parameter `movable` on `vf-window`, so the icon does too, and it\nmoves the same way: `DragController` tracks the gesture and\n`PlacementController` writes the result into `left`/`top` in whole system px,\nthe art's own unit — the same pair markup places an icon with, so a moved\nicon is still where it was dropped after a zoom.\n\nDragging is a pointer gesture with no keyboard equivalent, which is the kind\nof gap the kit closes rather than inherits (SPEC §1): a focused movable icon\nalso moves under the arrow keys, one system px at a time and eight with\nShift. Focus is what `selectable` grants, so the keyboard half of `movable`\nand `editable` presupposes it — see the role section below.\n\n### The drag is an outline; the icon stays put\n\nThe Finder dragged an icon as a dotted outline — the mask's boundary and\nthe name's rectangle — drawn over everything with the XOR pen, the icon\nitself staying put until the drop. The kit draws it from what it already\nhas: deriveDragOutline finds the ring the way the open ghost does\n(the silhouette from the art's alpha, eroded by one pixel, the ring the\nerosion removes), adds the plate's rectangle, and dotOutline dots\nit against a 2×2 checker — compositing only, so cross-origin art derives\ntoo, and art the pipeline cannot draw falls back to the cell's rectangle.\nThe canvas paints with the cursor's XOR recipe, `filter: invert(1)` under\n`mix-blend-mode: difference`: a dotted black line over a white window\nbody, and over the desktop dither the composition QuickDraw's pattern pen\ngave, because the checker's phase is taken from where the outline lands\non the *screen* — its dots share the dither's own phase wherever it goes.\n\nIt draws on the desktop's drag surface: a layer in the screen's shadow,\nover windows, palettes and the menu bar alike, clipped at the raster's\nedge like everything else, reached through the `vf-drag-surface-request`\nhandshake (bubbles, non-composed — it travels up the slots the icon is\nassigned to, so it works from inside a window body). With no desktop the\ncanvas lives in the icon's own frame instead, translated by the delta,\nclipped by whatever clips the icon; the gesture and its events are the\nsame either way. Its origin is a whole count of system px from the\nsurface's, written as a live `sysLength()`, so a scale change mid-gesture\ncannot smear it.\n\n### The drag reports, the page files\n\nThe kit ships no filing semantics — folders are the page's own catalog —\nso a drag is a stream of events and a cancelable commit, on the icon:\n\n- `vf-drag-start` `{ left, top }` on the first lattice step away from the\n press (a plain click never starts one, and never fires a drop);\n- `vf-drag` `{ clientX, clientY, left, top, x, y }` on every step that\n changed the snapped proposal;\n- `vf-drop`, the same shape at the release, **cancelable**: its default\n action is the move, the proposal written through `left`/`top` and\n clamped whole in the container measured at the press. `preventDefault()`\n writes nothing — the page re-parents or places the icon itself, the\n outline's origin converted with the destination's `placementAt`;\n- `vf-drag-cancel` `{}` on Escape or a `pointercancel`: nothing written.\n\n`left`/`top` in the detail are the *proposal*, unclamped — the origin plus\nthe lattice-snapped delta — and only the default action clamps, so a page\nthat cancels gets the raw gesture. `x`/`y` are the outline's top-left in\nviewport CSS px: the frame's box at the press translated by the delta,\nthe press offset already inside it, so an icon placed at the\ndestination's `placementAt(x, y)` lands exactly where the outline was.\n\n### The selection travels\n\nA drag that begins on a *selected* icon takes every other selected,\nmovable icon of its field with it — the Finder outlined the whole\nselection under a drag on any member. Each follower draws its own outline\n(followDrag), moved by the same lattice delta as the leader's, and\nthe leader's events carry the set as `detail.icons`, itself first. The\ndefault action of the drop clamps the delta once for the whole group —\nthe arrangement is kept, and the group stops when its outermost member\nmeets the container's edge — then writes each member's origin plus that\ndelta. A cancel takes every outline down and writes nothing. A press on\nan unselected icon makes it the selection first (the outside listeners\nclear the others), so it drags alone; a Shift press that deselects the\npressed icon drags it alone too, the rest staying where they are.\n\nFor the selection to travel, a plain press on one of its members must not\ncollapse it: the other icons' outside-press listeners leave a selection\nalone when the press landed on an icon already selected in the same\nfield. A *click* on such an icon — released with no drag — collapses the\nselection to it on the release, so a click still single-selects and the\nsame press could have dragged them all.\n\nEscape is heard by a document listener scoped to the gesture rather than\nby the icon's own key handler, because a `movable`-only icon never takes\nfocus — an addition in the kit's idiom (a pointer-only gesture with no way\nout is the kind of gap SPEC §1 closes), not a claim about the Finder.\n\n`target` is the destination highlight the page sets on the folder under a\ndrag: the selected treatment of the art alone — inverted, or darkened\nunder `color` — with none of `selected`'s semantics: no event, no outside\nlistener, no `aria-selected`, the plate untouched.\n\nOpening gets the same treatment. The double-click is the pointer gesture,\nand its keyboard route is ⌘O / ⌘↓ — the System 7 Open shortcuts, with Ctrl\nstanding in for ⌘ off the Mac. Return is deliberately not one of them: the\nFinder's Return renamed, never opened, so on an editable icon it starts the\nedit and on a non-editable one it does nothing at all.\n\n### The name and the art are one target, and the second click decides\n\nA double-click opens the icon *wherever it lands* — the name is as much the\nicon as the picture is, and the Finder never made you aim at the 32 pixels of\nart. But the name is also where a single click renames, so the two gestures\nbegin with the same press and only the second one tells them apart.\n\nSo the rename waits for it. A press on the plate of an already-selected icon\narms the field rather than opening it, and the next press inside\nRENAME_DELAY_MS calls it off — leaving the double-click to open, with\nno rename box flashing up behind it. Nothing needs to *undo* an edit that\nbegan: the press that starts one and the press that opens are the same\npress, so the only thing that can be got right is not committing early.\n\nThe window is generous in the one direction that is cheap. Reading a lone\nclick as a pair costs a wait before the box appears; reading a pair as a lone\nclick renames when the user asked to open. The same reasoning covers a press\nthat turns into a drag, a press elsewhere, and any key — each calls the\npending rename off, because none of them is the click it is waiting for.\n\n### An icon alone is a picture; an icon in a field is an option\n\n`role=\"option\"` is only meaningful inside a `listbox` that owns it. Written\nunconditionally it is not merely untidy — the browser *drops* it, and\n`aria-selected` with it, so a `selectable` icon announced as a bare generic\nand its selection state reached assistive tech nowhere at all.\n\nSo the role follows the container. Owned, the icon is an `option` that names\nitself from its plate and publishes `aria-selected`. Unowned, it degrades to\n`role=\"img\"` with a name — the same vocabulary the derived open ghost uses,\nand true of what it is. Deliberately not `button`: that would promise Enter\nand Space activate, and here Return *renames* while the open route is ⌘O / ⌘↓.\n\nThe owner is `vf-icon-field`, the container a field of icons sits in, and\nit is what buys the selection state back:\n\n```html\n<vf-icon-field label=\"Desktop\">\n <vf-icon label=\"Macintosh HD\" selectable movable editable>…</vf-icon>\n <vf-icon label=\"Trash\" selectable movable editable>…</vf-icon>\n</vf-icon-field>\n```\n\nA `vf-desktop` cannot be that container itself: it also holds windows and a\nmenu bar, and a non-`option` child of a listbox is invalid the same way the\norphaned option was. The field is layout-neutral — placed icons anchor to\nthe nearest *positioned* ancestor, which is still the desktop's raster. An\nelement of your own carrying `role=\"listbox\"` still owns an icon the same\nway. One divergence from the APG listbox is deliberate: its options share\na single roving tab stop, while these stay one stop each, the way a Finder\nicon is reached on its own.\n\n**`selectable` is what makes an icon focusable**, and `movable`/`editable`\npresuppose it. That is the Finder's own model — you cannot move or rename\nwhat you have not selected — and the pointer path already assumed it: the\nrename opens on a press on the plate of an *already-selected* icon. A\n`movable`-only icon is a picture you can drag, not a widget.\n\n### The label is a property, because it is editable\n\nThe caption is `label` rather than slotted content: renaming means the\ncomponent owns the string and hands it back on `vf-change`, and it cannot own\ntext that lives in the consumer's DOM. An empty `label` draws no plate at\nall — that *is* the \"no label\" parameter, in preference to a second attribute\nthat could disagree with it. `editable` then lets a click on the plate of an\nalready-selected icon open the rename box a moment later, as the Finder's\ndoes (see above), with Return committing, Escape reverting, and the plate\nwidening as you type.\n\n### A name is never abbreviated, and never folded\n\nThere is no ellipsis, no clipping and no wrapping: one line, always. System 7\nsolved the long-name problem at the *other* end — HFS capped a filename at 31\ncharacters — so the Finder could afford to always draw the name in full, and\ndid. A name wider than its cell simply overflows it, centered, the way a name\nwider than a 32-pixel icon always did.\n\nStaying on one line is also what keeps the name on the grid: a single run has\na single measured width, so the even-plate rule above is one number rather\nthan one per line, each with its own parity. `width` is therefore the cell —\nthe grid pitch — not a bound on the name.\n\n`maxlength` (31) bounds the rename field rather than the `label` property:\nthe name belongs to the consumer's model, and truncating one handed to us\nwould lose data.\n\nThe box hugs its text while you type it, not only once you are done, which is\nwhat keeps the name from moving when the edit commits: the plate is the same\nwidth either side of it, so the glyphs stay exactly where they were.\n\nA file also has to be called *something*, so a rename committed empty — or\nas nothing but spaces — is refused rather than applied, and the previous name\ncomes back, which is what System 7 did. An empty `label` is still a perfectly\ngood state to *start* in, though: a freshly made icon has no name until it is\ngiven one. Such an icon draws no plate but stays selectable, focusable and\nrenameable — Return opens a field, and an edit with nothing in it yet is the\none time the box stops hugging and reserves a cell's width, since a field you\ncannot see is one you cannot type into.",
|
|
4914
|
+
"name": "VfIcon",
|
|
4915
|
+
"cssProperties": [
|
|
4916
|
+
{
|
|
4917
|
+
"description": "Space between the art cell and the name plate",
|
|
4918
|
+
"name": "--vf-icon-gap"
|
|
4919
|
+
},
|
|
4920
|
+
{
|
|
4921
|
+
"description": "The name plate's line box",
|
|
4922
|
+
"name": "--vf-icon-label-height"
|
|
4923
|
+
}
|
|
4924
|
+
],
|
|
4925
|
+
"cssParts": [
|
|
4926
|
+
{
|
|
4927
|
+
"description": "The icon and its label plate, stacked.",
|
|
4928
|
+
"name": "frame"
|
|
4929
|
+
},
|
|
4930
|
+
{
|
|
4931
|
+
"description": "The reserved art cell.",
|
|
4932
|
+
"name": "icon"
|
|
4933
|
+
},
|
|
4934
|
+
{
|
|
4935
|
+
"description": "The name block; its lines are centered under the art.",
|
|
4936
|
+
"name": "label"
|
|
4937
|
+
},
|
|
4938
|
+
{
|
|
4939
|
+
"description": "The inked run behind the name, which each wrapped line gets its own of (inverts when selected).",
|
|
4940
|
+
"name": "plate"
|
|
4941
|
+
},
|
|
4942
|
+
{
|
|
4943
|
+
"description": "The rename field, while editing.",
|
|
4944
|
+
"name": "input"
|
|
4945
|
+
}
|
|
4946
|
+
],
|
|
4947
|
+
"slots": [
|
|
4948
|
+
{
|
|
4949
|
+
"description": "The 32×32 art, normally a `vf-img` around an `<img>`.",
|
|
4950
|
+
"name": "large"
|
|
4951
|
+
},
|
|
4952
|
+
{
|
|
4953
|
+
"description": "The 16×16 art, shown under `size=\"small\"`.",
|
|
4954
|
+
"name": "small"
|
|
4955
|
+
}
|
|
4956
|
+
],
|
|
4957
|
+
"members": [
|
|
4958
|
+
{
|
|
4959
|
+
"kind": "field",
|
|
4960
|
+
"name": "label",
|
|
4961
|
+
"type": {
|
|
4962
|
+
"text": "string"
|
|
4963
|
+
},
|
|
4964
|
+
"default": "''",
|
|
4965
|
+
"description": "The icon's name. Empty draws no plate — that is the \"no label\" setting.",
|
|
4966
|
+
"attribute": "label"
|
|
4967
|
+
},
|
|
4968
|
+
{
|
|
4969
|
+
"kind": "field",
|
|
4970
|
+
"name": "size",
|
|
4971
|
+
"type": {
|
|
4972
|
+
"text": "VfIconSize"
|
|
4973
|
+
},
|
|
4974
|
+
"default": "'large'",
|
|
4975
|
+
"description": "Which member of the icon family paints, and the cell it is registered in:\n`large` (32×32) or `small` (16×16).",
|
|
4976
|
+
"attribute": "size",
|
|
4977
|
+
"reflects": true
|
|
4978
|
+
},
|
|
4979
|
+
{
|
|
4980
|
+
"kind": "field",
|
|
4981
|
+
"name": "selectable",
|
|
4982
|
+
"type": {
|
|
4983
|
+
"text": "boolean"
|
|
4984
|
+
},
|
|
4985
|
+
"default": "false",
|
|
4986
|
+
"description": "Clicking selects. Set `selected` yourself to drive selection some other way.\n\nThis is also the flag that makes an icon focusable and gives it a role, so\nthe keyboard halves of movable and editable presuppose it —\nas the Finder did. A container carrying `role=\"listbox\"` turns the role from\n`img` into a real `option`; see the class doc.",
|
|
4987
|
+
"attribute": "selectable",
|
|
4988
|
+
"reflects": true
|
|
4989
|
+
},
|
|
4990
|
+
{
|
|
4991
|
+
"kind": "field",
|
|
4992
|
+
"name": "selected",
|
|
4993
|
+
"type": {
|
|
4994
|
+
"text": "boolean"
|
|
4995
|
+
},
|
|
4996
|
+
"default": "false",
|
|
4997
|
+
"description": "Whether the icon is selected: the art inverts and the plate goes black.",
|
|
4998
|
+
"attribute": "selected",
|
|
4999
|
+
"reflects": true
|
|
5000
|
+
},
|
|
5001
|
+
{
|
|
5002
|
+
"kind": "field",
|
|
5003
|
+
"name": "target",
|
|
5004
|
+
"type": {
|
|
5005
|
+
"text": "boolean"
|
|
5006
|
+
},
|
|
5007
|
+
"default": "false",
|
|
5008
|
+
"description": "The destination highlight: this icon is the folder under a drag, and its\nart paints the selected treatment — inverted, or darkened under\ncolor — with none of `selected`'s semantics: no `vf-select`, no\noutside-press listener, no `aria-selected`, the plate untouched. The page\nsets it on the folder a `vf-drag` reports the pointer over and clears it\nwhen the pointer leaves or the drop lands.",
|
|
5009
|
+
"attribute": "target",
|
|
5010
|
+
"reflects": true
|
|
5011
|
+
},
|
|
5012
|
+
{
|
|
5013
|
+
"kind": "field",
|
|
5014
|
+
"name": "color",
|
|
5015
|
+
"type": {
|
|
5016
|
+
"text": "boolean"
|
|
5017
|
+
},
|
|
5018
|
+
"default": "false",
|
|
5019
|
+
"description": "Declares the slotted art a **color icon**, so selection darkens it — the\nttSelected transform, every color blended halfway toward black — instead\nof inverting it into a photographic negative (see the class doc). Yours\nto declare because the component cannot tell color art from a 1-bit mask\nby looking at a slotted image. The label plate inverts either way.",
|
|
5020
|
+
"attribute": "color",
|
|
5021
|
+
"reflects": true
|
|
5022
|
+
},
|
|
5023
|
+
{
|
|
5024
|
+
"kind": "field",
|
|
5025
|
+
"name": "open",
|
|
5026
|
+
"type": {
|
|
5027
|
+
"text": "boolean"
|
|
5028
|
+
},
|
|
5029
|
+
"default": "false",
|
|
5030
|
+
"description": "The icon's window is on screen, so the art paints as the Finder's open\nghost — outline held, interior re-filled with the kit's loose dither —\nderived in the client from the slotted art itself (see the class doc).\nSet it when handling `vf-open`, clear it when the window goes away.\nSelection inverts the ghost exactly as it inverts the art.",
|
|
5031
|
+
"attribute": "open",
|
|
5032
|
+
"reflects": true
|
|
5033
|
+
},
|
|
5034
|
+
{
|
|
5035
|
+
"kind": "field",
|
|
5036
|
+
"name": "movable",
|
|
5037
|
+
"type": {
|
|
5038
|
+
"text": "boolean"
|
|
5039
|
+
},
|
|
5040
|
+
"default": "false",
|
|
5041
|
+
"description": "Drag to move — `movable`, never `draggable`, which is a platform attribute\nand accessor (see the class doc). Arrow keys move a focused icon too, which\nmeans pairing this with selectable: focus is what that grants.",
|
|
5042
|
+
"attribute": "movable",
|
|
5043
|
+
"reflects": true
|
|
5044
|
+
},
|
|
5045
|
+
{
|
|
5046
|
+
"kind": "field",
|
|
5047
|
+
"name": "editable",
|
|
5048
|
+
"type": {
|
|
5049
|
+
"text": "boolean"
|
|
5050
|
+
},
|
|
5051
|
+
"default": "false",
|
|
5052
|
+
"description": "The name can be renamed in place: click a selected plate, or press Return.\nPair with selectable — both routes start from a selected icon.\n\nThe pointer route opens the field once the double-click window has passed\n(RENAME_DELAY_MS), so double-clicking the *name* opens the icon the\nway double-clicking its art does. Return opens it at once — a keypress has\nno second half to wait for.",
|
|
5053
|
+
"attribute": "editable",
|
|
5054
|
+
"reflects": true
|
|
5055
|
+
},
|
|
5056
|
+
{
|
|
5057
|
+
"kind": "field",
|
|
5058
|
+
"name": "width",
|
|
5059
|
+
"type": {
|
|
5060
|
+
"text": "number | undefined"
|
|
5061
|
+
},
|
|
5062
|
+
"description": "The cell width in whole system px — the grid pitch, and it must be\n**even**: the component rounds its own plate to keep every centered offset\nwhole, but it cannot round a number you chose (see the class doc).\n\nIt is not a bound on the name. Left off, the box is as wide as the wider of\nthe art cell and the name; set, a longer name overflows it, centered.\nDeclaring one is what keeps a field of icons on a single pitch, and what\nkeeps a text-sized box off a fractional origin (contract rule 3).",
|
|
5063
|
+
"attribute": "width"
|
|
5064
|
+
},
|
|
5065
|
+
{
|
|
5066
|
+
"kind": "field",
|
|
5067
|
+
"name": "maxlength",
|
|
5068
|
+
"type": {
|
|
5069
|
+
"text": "number"
|
|
5070
|
+
},
|
|
5071
|
+
"default": "31",
|
|
5072
|
+
"description": "The most characters the rename field will accept — 31, the HFS filename\nlimit every System 7 name was cut to, which is also why the Finder could\nafford to always draw the whole name. Going past it fires\n`vf-name-too-long` with what was tried, so a host can say so rather than\nlet characters vanish.\n\nIt bounds *typing* only: a `label` set from your own data is displayed as\ngiven and fires nothing, since the name belongs to your model and silently\ntruncating it would lose data.",
|
|
5073
|
+
"attribute": "maxlength"
|
|
5074
|
+
},
|
|
5075
|
+
{
|
|
5076
|
+
"kind": "field",
|
|
5077
|
+
"name": "scale",
|
|
5078
|
+
"privacy": "private",
|
|
5079
|
+
"readonly": true,
|
|
5080
|
+
"default": "new ScaleController(this)",
|
|
5081
|
+
"description": "Default-on display scaling (true 72dpi size); see src/scale.ts."
|
|
5082
|
+
},
|
|
5083
|
+
{
|
|
5084
|
+
"kind": "field",
|
|
5085
|
+
"name": "gridSnap",
|
|
5086
|
+
"privacy": "private",
|
|
5087
|
+
"readonly": true,
|
|
5088
|
+
"default": "new GridSnapController(this)",
|
|
5089
|
+
"description": "Device-pixel grid snapping; see src/grid-snap.ts."
|
|
5090
|
+
},
|
|
5091
|
+
{
|
|
5092
|
+
"kind": "field",
|
|
5093
|
+
"name": "focusRule",
|
|
5094
|
+
"privacy": "private",
|
|
5095
|
+
"readonly": true,
|
|
5096
|
+
"default": "new FocusRuleController(this)",
|
|
5097
|
+
"description": "Whether to wear the dashed focus rule; see src/focus-modality.ts."
|
|
5098
|
+
},
|
|
5099
|
+
{
|
|
5100
|
+
"kind": "field",
|
|
5101
|
+
"name": "_editing",
|
|
5102
|
+
"type": {
|
|
5103
|
+
"text": "boolean"
|
|
5104
|
+
},
|
|
5105
|
+
"privacy": "private",
|
|
5106
|
+
"default": "false",
|
|
5107
|
+
"description": "True while the rename field is open."
|
|
5108
|
+
},
|
|
5109
|
+
{
|
|
5110
|
+
"kind": "field",
|
|
5111
|
+
"name": "_draft",
|
|
5112
|
+
"type": {
|
|
5113
|
+
"text": "string"
|
|
5114
|
+
},
|
|
5115
|
+
"privacy": "private",
|
|
5116
|
+
"default": "''",
|
|
5117
|
+
"description": "The name as typed. The hidden plate renders it, so the box tracks it."
|
|
5118
|
+
},
|
|
5119
|
+
{
|
|
5120
|
+
"kind": "field",
|
|
4556
5121
|
"name": "#plateWidth",
|
|
4557
5122
|
"privacy": "private",
|
|
4558
5123
|
"type": {
|
|
@@ -4595,9 +5160,29 @@
|
|
|
4595
5160
|
"type": {
|
|
4596
5161
|
"text": "boolean"
|
|
4597
5162
|
},
|
|
4598
|
-
"description": "Whether a container is claiming this icon as one item of a set — the\n`vf-menu` `#inBar` idiom. `option` is invalid without a `listbox` that owns\nit: unowned, the browser drops the role and `aria-selected` with it, which\nis how a selectable icon reached assistive tech as a bare `generic` in\nevery configuration the kit shipped.\n\nMatched on the
|
|
5163
|
+
"description": "Whether a container is claiming this icon as one item of a set — the\n`vf-menu` `#inBar` idiom. `option` is invalid without a `listbox` that owns\nit: unowned, the browser drops the role and `aria-selected` with it, which\nis how a selectable icon reached assistive tech as a bare `generic` in\nevery configuration the kit shipped.\n\nMatched on the `vf-icon-field` tag — its `listbox` role lives in\ninternals, which never lands as an attribute for `closest()` to find —\nand on the attribute a consumer's own element writes. A `vf-list` is\ndeliberately not a match: it holds `vf-list-item` rows, and its own\n`listbox` role lives in internals rather than on the tag anyway.",
|
|
5164
|
+
"readonly": true
|
|
5165
|
+
},
|
|
5166
|
+
{
|
|
5167
|
+
"kind": "field",
|
|
5168
|
+
"name": "#field",
|
|
5169
|
+
"privacy": "private",
|
|
5170
|
+
"type": {
|
|
5171
|
+
"text": "Element | null"
|
|
5172
|
+
},
|
|
5173
|
+
"description": "The field this icon is one item of — its selection's boundary — or null.",
|
|
4599
5174
|
"readonly": true
|
|
4600
5175
|
},
|
|
5176
|
+
{
|
|
5177
|
+
"kind": "field",
|
|
5178
|
+
"name": "#collapseOnRelease",
|
|
5179
|
+
"privacy": "private",
|
|
5180
|
+
"type": {
|
|
5181
|
+
"text": "boolean"
|
|
5182
|
+
},
|
|
5183
|
+
"default": "false",
|
|
5184
|
+
"description": "A plain press landed on this icon while it was already selected in a\nfield, and no drag has begun: the selection was kept for the drag's\nsake, and the release with no drag collapses it to this icon (see\n#onOutsidePointerDown)."
|
|
5185
|
+
},
|
|
4601
5186
|
{
|
|
4602
5187
|
"kind": "field",
|
|
4603
5188
|
"name": "#ghostKey",
|
|
@@ -4653,29 +5238,288 @@
|
|
|
4653
5238
|
"description": "Where a moved icon is allowed to end up, in system px. Unlike `vf-window`,\nwhich only keeps a grabbable strip on screen, an icon is small enough to\nhold whole — losing half of one to an edge reads as a bug rather than as a\nwindow pushed aside."
|
|
4654
5239
|
},
|
|
4655
5240
|
{
|
|
4656
|
-
"kind": "field",
|
|
4657
|
-
"name": "#placement",
|
|
5241
|
+
"kind": "field",
|
|
5242
|
+
"name": "#placement",
|
|
5243
|
+
"privacy": "private",
|
|
5244
|
+
"readonly": true,
|
|
5245
|
+
"default": "new PlacementController(this, (x, y, bounds) => this.#keepWhole(x, y, bounds) )",
|
|
5246
|
+
"description": "Drag and nudge placement: the origin lands in `top`/`left` in system px,\nso a moved icon is placed the way an authored one is and holds its spot\nthrough a zoom (see src/position.ts)."
|
|
5247
|
+
},
|
|
5248
|
+
{
|
|
5249
|
+
"kind": "field",
|
|
5250
|
+
"name": "#gesture",
|
|
5251
|
+
"privacy": "private",
|
|
5252
|
+
"type": {
|
|
5253
|
+
"text": "DragGesture | null"
|
|
5254
|
+
},
|
|
5255
|
+
"default": "null",
|
|
5256
|
+
"description": "The in-flight drag, or null between gestures."
|
|
5257
|
+
},
|
|
5258
|
+
{
|
|
5259
|
+
"kind": "field",
|
|
5260
|
+
"name": "#drag",
|
|
5261
|
+
"privacy": "private",
|
|
5262
|
+
"readonly": true,
|
|
5263
|
+
"default": "new DragController(this, { onDragStart: (event: PointerEvent): { x: number; y: number } | null => { if (!this.movable || event.button !== 0 || this._editing) return null this.#warnIfUnplaced() const origin = this.#placement.seed() this.#gesture = { origin, frame: this.#frameRect(), pointer: { clientX: event.clientX, clientY: event.clientY }, proposal: null, outline: null, followers: [], size: this.#sysSize(), } return origin }, onDrag: (x: number, y: number, event: PointerEvent): void => { this.#onDragStep(x, y, event) }, onDragEnd: (event: PointerEvent | undefined, cancelled: boolean): void => { this.#onDragEnd(event, cancelled) }, })",
|
|
5264
|
+
"description": "Drag-to-move, on the same delegate shape as `vf-window`: the placement\ncontroller seeds the origin — from the in-flow offset the first time —\nand the drag controller hands back each step snapped onto the lattice.\nWhat the icon does with a step is #onDragStep; the release is\n#onDragEnd, where the drop is reported and, uncancelled, written."
|
|
5265
|
+
},
|
|
5266
|
+
{
|
|
5267
|
+
"kind": "field",
|
|
5268
|
+
"name": "#escape",
|
|
5269
|
+
"privacy": "private",
|
|
5270
|
+
"readonly": true,
|
|
5271
|
+
"default": "new DocumentListenersController(this, () => [ [document, 'keydown', this.#onDragKeyDown, true], ])",
|
|
5272
|
+
"description": "Escape mid-drag cancels. A document listener scoped to the gesture (the\nin-flight-gesture idiom) rather than the icon's own key handler, because\na `movable`-only icon never takes focus. Capture phase, and the key is\nstopped there, so an enclosing `vf-dialog` never reads it as a dismissal\n— the rule the rename box's Escape follows."
|
|
5273
|
+
},
|
|
5274
|
+
{
|
|
5275
|
+
"kind": "field",
|
|
5276
|
+
"name": "#onDragKeyDown",
|
|
5277
|
+
"privacy": "private"
|
|
5278
|
+
},
|
|
5279
|
+
{
|
|
5280
|
+
"kind": "method",
|
|
5281
|
+
"name": "#frameRect",
|
|
5282
|
+
"privacy": "private",
|
|
5283
|
+
"return": {
|
|
5284
|
+
"type": {
|
|
5285
|
+
"text": "DOMRect"
|
|
5286
|
+
}
|
|
5287
|
+
},
|
|
5288
|
+
"description": "The frame's viewport box — what the outline is a translation of."
|
|
5289
|
+
},
|
|
5290
|
+
{
|
|
5291
|
+
"kind": "method",
|
|
5292
|
+
"name": "#sysSize",
|
|
5293
|
+
"privacy": "private",
|
|
5294
|
+
"return": {
|
|
5295
|
+
"type": {
|
|
5296
|
+
"text": "{ width: number; height: number }"
|
|
5297
|
+
}
|
|
5298
|
+
},
|
|
5299
|
+
"description": "The icon's own box in system px, for the clamp."
|
|
5300
|
+
},
|
|
5301
|
+
{
|
|
5302
|
+
"kind": "method",
|
|
5303
|
+
"name": "followDrag",
|
|
5304
|
+
"return": {
|
|
5305
|
+
"type": {
|
|
5306
|
+
"text": "VfIconDragFollower | null"
|
|
5307
|
+
}
|
|
5308
|
+
},
|
|
5309
|
+
"parameters": [
|
|
5310
|
+
{
|
|
5311
|
+
"name": "surface",
|
|
5312
|
+
"type": {
|
|
5313
|
+
"text": "HTMLElement | null"
|
|
5314
|
+
}
|
|
5315
|
+
}
|
|
5316
|
+
],
|
|
5317
|
+
"description": "Join another icon's drag as a follower (see the class doc): seed the\norigin the drop will add the delta to, draw this icon's own outline on\n`surface` (the desktop's, as the leader's handshake found it) or in its\nown frame, and hand back the handle the leader moves, drops and ends.\nNull when this icon cannot move. Internal to the kit's group drag."
|
|
5318
|
+
},
|
|
5319
|
+
{
|
|
5320
|
+
"kind": "method",
|
|
5321
|
+
"name": "#gatherFollowers",
|
|
5322
|
+
"privacy": "private",
|
|
5323
|
+
"return": {
|
|
5324
|
+
"type": {
|
|
5325
|
+
"text": "VfIconDragFollower[]"
|
|
5326
|
+
}
|
|
5327
|
+
},
|
|
5328
|
+
"parameters": [
|
|
5329
|
+
{
|
|
5330
|
+
"name": "surface",
|
|
5331
|
+
"type": {
|
|
5332
|
+
"text": "HTMLElement | null"
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
],
|
|
5336
|
+
"description": "The icons a drag beginning here carries: this one, then — when it is\nitself selected — every other selected, movable icon of its field, in\ndocument order, each joined as a follower. An icon outside any field,\nor not selected, drags alone."
|
|
5337
|
+
},
|
|
5338
|
+
{
|
|
5339
|
+
"kind": "method",
|
|
5340
|
+
"name": "#dragIcons",
|
|
5341
|
+
"privacy": "private",
|
|
5342
|
+
"return": {
|
|
5343
|
+
"type": {
|
|
5344
|
+
"text": "VfIcon[]"
|
|
5345
|
+
}
|
|
5346
|
+
},
|
|
5347
|
+
"parameters": [
|
|
5348
|
+
{
|
|
5349
|
+
"name": "gesture",
|
|
5350
|
+
"type": {
|
|
5351
|
+
"text": "DragGesture"
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
],
|
|
5355
|
+
"description": "The set a drag's events report: the leader first."
|
|
5356
|
+
},
|
|
5357
|
+
{
|
|
5358
|
+
"kind": "method",
|
|
5359
|
+
"name": "#onDragStep",
|
|
5360
|
+
"privacy": "private",
|
|
5361
|
+
"return": {
|
|
5362
|
+
"type": {
|
|
5363
|
+
"text": "void"
|
|
5364
|
+
}
|
|
5365
|
+
},
|
|
5366
|
+
"parameters": [
|
|
5367
|
+
{
|
|
5368
|
+
"name": "x",
|
|
5369
|
+
"type": {
|
|
5370
|
+
"text": "number"
|
|
5371
|
+
}
|
|
5372
|
+
},
|
|
5373
|
+
{
|
|
5374
|
+
"name": "y",
|
|
5375
|
+
"type": {
|
|
5376
|
+
"text": "number"
|
|
5377
|
+
}
|
|
5378
|
+
},
|
|
5379
|
+
{
|
|
5380
|
+
"name": "event",
|
|
5381
|
+
"type": {
|
|
5382
|
+
"text": "PointerEvent"
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
],
|
|
5386
|
+
"description": "A lattice step of the drag. The first step whose snapped origin differs\nfrom the seed is the start — a stationary press reports jitter, and every\none of those steps snaps back onto the same system px, so a plain click\nnever starts a drag and never fires a drop. Moving an icon is not\nrenaming it, so the start calls off the rename the press armed."
|
|
5387
|
+
},
|
|
5388
|
+
{
|
|
5389
|
+
"kind": "method",
|
|
5390
|
+
"name": "#onDragEnd",
|
|
5391
|
+
"privacy": "private",
|
|
5392
|
+
"return": {
|
|
5393
|
+
"type": {
|
|
5394
|
+
"text": "void"
|
|
5395
|
+
}
|
|
5396
|
+
},
|
|
5397
|
+
"parameters": [
|
|
5398
|
+
{
|
|
5399
|
+
"name": "event",
|
|
5400
|
+
"type": {
|
|
5401
|
+
"text": "PointerEvent | undefined"
|
|
5402
|
+
}
|
|
5403
|
+
},
|
|
5404
|
+
{
|
|
5405
|
+
"name": "cancelled",
|
|
5406
|
+
"type": {
|
|
5407
|
+
"text": "boolean"
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
],
|
|
5411
|
+
"description": "The release, or a cancel. A gesture that never started (a click) reports\nnothing. A cancel — Escape, `pointercancel` — drops every outline and\nsays so; nothing was written. A release reports the drop, and the\ndefault action of an uncancelled `vf-drop` is the move itself, the whole\ngroup by one delta. Dispatched with the outlines gone, the drag state\ncleared and the pointer capture released, so a handler may re-parent\nthe icons freely."
|
|
5412
|
+
},
|
|
5413
|
+
{
|
|
5414
|
+
"kind": "method",
|
|
5415
|
+
"name": "#dropGroup",
|
|
5416
|
+
"privacy": "private",
|
|
5417
|
+
"return": {
|
|
5418
|
+
"type": {
|
|
5419
|
+
"text": "void"
|
|
5420
|
+
}
|
|
5421
|
+
},
|
|
5422
|
+
"parameters": [
|
|
5423
|
+
{
|
|
5424
|
+
"name": "gesture",
|
|
5425
|
+
"type": {
|
|
5426
|
+
"text": "DragGesture"
|
|
5427
|
+
}
|
|
5428
|
+
},
|
|
5429
|
+
{
|
|
5430
|
+
"name": "detail",
|
|
5431
|
+
"type": {
|
|
5432
|
+
"text": "VfIconDragDetail"
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
],
|
|
5436
|
+
"description": "The drop's default action. Alone, the proposal clamped whole by\n#keepWhole. As a group, one delta for all: clamped so that the\nmember nearest each edge stays whole — the group stops when its\noutermost icon meets the container, and the arrangement is kept — then\neach member's origin plus that delta. A group no edge can hold (a member\nwider than the container) is held off the near edge only, the\nsingle-icon rule."
|
|
5437
|
+
},
|
|
5438
|
+
{
|
|
5439
|
+
"kind": "method",
|
|
5440
|
+
"name": "#requestSurface",
|
|
5441
|
+
"privacy": "private",
|
|
5442
|
+
"return": {
|
|
5443
|
+
"type": {
|
|
5444
|
+
"text": "HTMLElement | null"
|
|
5445
|
+
}
|
|
5446
|
+
},
|
|
5447
|
+
"description": "The handshake: a desktop on the light-DOM path hands over its drag\nsurface; null with no desktop, and the outline draws in the frame."
|
|
5448
|
+
},
|
|
5449
|
+
{
|
|
5450
|
+
"kind": "method",
|
|
5451
|
+
"name": "#buildOutline",
|
|
4658
5452
|
"privacy": "private",
|
|
4659
|
-
"
|
|
4660
|
-
|
|
4661
|
-
|
|
5453
|
+
"return": {
|
|
5454
|
+
"type": {
|
|
5455
|
+
"text": "DragOutline | null"
|
|
5456
|
+
}
|
|
5457
|
+
},
|
|
5458
|
+
"parameters": [
|
|
5459
|
+
{
|
|
5460
|
+
"name": "at",
|
|
5461
|
+
"type": {
|
|
5462
|
+
"text": "DOMRect"
|
|
5463
|
+
}
|
|
5464
|
+
},
|
|
5465
|
+
{
|
|
5466
|
+
"name": "surface",
|
|
5467
|
+
"type": {
|
|
5468
|
+
"text": "HTMLElement | null"
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
],
|
|
5472
|
+
"description": "Build this icon's outline for a drag, and put it where it draws: the\ndesktop's drag surface when the leader's handshake found one, else the\nicon's own frame. The geometry is measured off the rendered parts —\nthe frame (`at`, its box at the press), the art as it paints (the ghost\nwhile `open`), the cell and the plate — in system px relative to the\nframe, so the ring lands on the pixels the frame paints. Null when\nthere is nothing to draw with."
|
|
4662
5473
|
},
|
|
4663
5474
|
{
|
|
4664
|
-
"kind": "
|
|
4665
|
-
"name": "#
|
|
5475
|
+
"kind": "method",
|
|
5476
|
+
"name": "#placeOutlineAt",
|
|
4666
5477
|
"privacy": "private",
|
|
4667
|
-
"
|
|
4668
|
-
"
|
|
5478
|
+
"return": {
|
|
5479
|
+
"type": {
|
|
5480
|
+
"text": "void"
|
|
5481
|
+
}
|
|
4669
5482
|
},
|
|
4670
|
-
"
|
|
4671
|
-
|
|
5483
|
+
"parameters": [
|
|
5484
|
+
{
|
|
5485
|
+
"name": "outline",
|
|
5486
|
+
"type": {
|
|
5487
|
+
"text": "DragOutline"
|
|
5488
|
+
}
|
|
5489
|
+
},
|
|
5490
|
+
{
|
|
5491
|
+
"name": "dx",
|
|
5492
|
+
"type": {
|
|
5493
|
+
"text": "number"
|
|
5494
|
+
}
|
|
5495
|
+
},
|
|
5496
|
+
{
|
|
5497
|
+
"name": "dy",
|
|
5498
|
+
"type": {
|
|
5499
|
+
"text": "number"
|
|
5500
|
+
}
|
|
5501
|
+
}
|
|
5502
|
+
],
|
|
5503
|
+
"description": "Move an outline by the drag's delta: the frame's offset plus the delta,\na whole count of system px from the surface's origin, re-dotted at the\nparity that lands on the screen — `x + y` even where the desktop dither\nis inked, so the two share one phase wherever the outline goes."
|
|
4672
5504
|
},
|
|
4673
5505
|
{
|
|
4674
|
-
"kind": "
|
|
4675
|
-
"name": "#
|
|
5506
|
+
"kind": "method",
|
|
5507
|
+
"name": "#dragDetail",
|
|
4676
5508
|
"privacy": "private",
|
|
4677
|
-
"
|
|
4678
|
-
|
|
5509
|
+
"return": {
|
|
5510
|
+
"type": {
|
|
5511
|
+
"text": "VfIconDragDetail"
|
|
5512
|
+
}
|
|
5513
|
+
},
|
|
5514
|
+
"parameters": [
|
|
5515
|
+
{
|
|
5516
|
+
"name": "gesture",
|
|
5517
|
+
"type": {
|
|
5518
|
+
"text": "DragGesture"
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
],
|
|
5522
|
+
"description": "The detail of `vf-drag` and `vf-drop`: the pointer, the proposal in the\ncontainer — unclamped — and the outline's top-left in the viewport: the\nframe's box at the press translated by the proposal's delta."
|
|
4679
5523
|
},
|
|
4680
5524
|
{
|
|
4681
5525
|
"kind": "field",
|
|
@@ -4862,6 +5706,42 @@
|
|
|
4862
5706
|
}
|
|
4863
5707
|
]
|
|
4864
5708
|
},
|
|
5709
|
+
{
|
|
5710
|
+
"kind": "method",
|
|
5711
|
+
"name": "setSelected",
|
|
5712
|
+
"return": {
|
|
5713
|
+
"type": {
|
|
5714
|
+
"text": "void"
|
|
5715
|
+
}
|
|
5716
|
+
},
|
|
5717
|
+
"parameters": [
|
|
5718
|
+
{
|
|
5719
|
+
"name": "next",
|
|
5720
|
+
"type": {
|
|
5721
|
+
"text": "boolean"
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
],
|
|
5725
|
+
"description": "Select or deselect as a press would: the state changes and `vf-select`\nreports it. A `selected` write from code is silent, the way a value set\nfires no `vf-change`; this is the route for a gesture a container runs\non the icon's behalf — `vf-icon-field`'s rubber band. A no-op when the\nstate already matches."
|
|
5726
|
+
},
|
|
5727
|
+
{
|
|
5728
|
+
"kind": "method",
|
|
5729
|
+
"name": "touches",
|
|
5730
|
+
"return": {
|
|
5731
|
+
"type": {
|
|
5732
|
+
"text": "boolean"
|
|
5733
|
+
}
|
|
5734
|
+
},
|
|
5735
|
+
"parameters": [
|
|
5736
|
+
{
|
|
5737
|
+
"name": "box",
|
|
5738
|
+
"type": {
|
|
5739
|
+
"text": "{ left: number; top: number; right: number; bottom: number }"
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5742
|
+
],
|
|
5743
|
+
"description": "Whether a viewport box (CSS px) touches the icon the way the Finder's\nselection rectangle asked: its art cell or its name plate. The empty\ncell around narrower art and the frame's margin beside the plate count\nfor nothing — a rectangle dragged through the gap between two icons\nselects neither. `vf-icon-field`'s rubber band reads it."
|
|
5744
|
+
},
|
|
4865
5745
|
{
|
|
4866
5746
|
"kind": "field",
|
|
4867
5747
|
"name": "#onPointerDown",
|
|
@@ -4873,6 +5753,12 @@
|
|
|
4873
5753
|
"privacy": "private",
|
|
4874
5754
|
"description": "The whole icon opens, name included: the handler is bound to the frame, and\nthe press path is what keeps the plate from having spent the gesture on a\nrename before the second click gets here.\n\nStill guarded on the field: a double-click inside an open rename box is a\nword being selected, not an icon being opened."
|
|
4875
5755
|
},
|
|
5756
|
+
{
|
|
5757
|
+
"kind": "field",
|
|
5758
|
+
"name": "#onPointerUp",
|
|
5759
|
+
"privacy": "private",
|
|
5760
|
+
"description": "The release of a press on this icon. The drag controller hears it first\n(a started drag ends there); then a press that was kept from collapsing\nthe selection — a plain press on an already-selected member of a field —\ncollapses it now, if no drag ever began: a click on one of several\nselected icons selects that one alone, on the release rather than the\npress, so the same press could have dragged them all. A cancel collapses\nnothing."
|
|
5761
|
+
},
|
|
4876
5762
|
{
|
|
4877
5763
|
"kind": "field",
|
|
4878
5764
|
"name": "#onOutsidePointerDown",
|
|
@@ -4979,6 +5865,22 @@
|
|
|
4979
5865
|
{
|
|
4980
5866
|
"description": "A rename was committed with no name in it, so the edit was dropped and the old name put back. `detail: { attempted, kept, reason: 'empty' }`. A `vf-change` is *not* fired alongside it — nothing changed.",
|
|
4981
5867
|
"name": "vf-name-rejected"
|
|
5868
|
+
},
|
|
5869
|
+
{
|
|
5870
|
+
"description": "A drag began: the first lattice step away from the press. `detail: { left, top, icons }` — the origin, whole system px in the icon's container, and every icon the drag carries (this one first).",
|
|
5871
|
+
"name": "vf-drag-start"
|
|
5872
|
+
},
|
|
5873
|
+
{
|
|
5874
|
+
"description": "A step that changed the proposal. `detail: { clientX, clientY, left, top, x, y, icons }` — the pointer in viewport CSS px, the proposed origin in the container (whole system px on the lattice), the outline's top-left in viewport CSS px, and the icons travelling.",
|
|
5875
|
+
"name": "vf-drag"
|
|
5876
|
+
},
|
|
5877
|
+
{
|
|
5878
|
+
"description": "The release; cancelable. The same detail as `vf-drag`. Its default action writes the proposal through `left`/`top` — every icon in `icons` by the same delta, clamped whole in the container as a group; `preventDefault()` writes nothing.",
|
|
5879
|
+
"name": "vf-drop"
|
|
5880
|
+
},
|
|
5881
|
+
{
|
|
5882
|
+
"description": "The drag was abandoned — Escape, or a `pointercancel` — and nothing was written. `detail: {}`.",
|
|
5883
|
+
"name": "vf-drag-cancel"
|
|
4982
5884
|
}
|
|
4983
5885
|
],
|
|
4984
5886
|
"attributes": [
|
|
@@ -5018,6 +5920,15 @@
|
|
|
5018
5920
|
"description": "Whether the icon is selected: the art inverts and the plate goes black.",
|
|
5019
5921
|
"fieldName": "selected"
|
|
5020
5922
|
},
|
|
5923
|
+
{
|
|
5924
|
+
"name": "target",
|
|
5925
|
+
"type": {
|
|
5926
|
+
"text": "boolean"
|
|
5927
|
+
},
|
|
5928
|
+
"default": "false",
|
|
5929
|
+
"description": "The destination highlight: this icon is the folder under a drag, and its\nart paints the selected treatment — inverted, or darkened under\ncolor — with none of `selected`'s semantics: no `vf-select`, no\noutside-press listener, no `aria-selected`, the plate untouched. The page\nsets it on the folder a `vf-drag` reports the pointer over and clears it\nwhen the pointer leaves or the drop lands.",
|
|
5930
|
+
"fieldName": "target"
|
|
5931
|
+
},
|
|
5021
5932
|
{
|
|
5022
5933
|
"name": "color",
|
|
5023
5934
|
"type": {
|
|
@@ -8470,10 +9381,10 @@
|
|
|
8470
9381
|
"privacy": "protected",
|
|
8471
9382
|
"return": {
|
|
8472
9383
|
"type": {
|
|
8473
|
-
"text": "
|
|
9384
|
+
"text": "boolean"
|
|
8474
9385
|
}
|
|
8475
9386
|
},
|
|
8476
|
-
"description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.",
|
|
9387
|
+
"description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
|
|
8477
9388
|
"inheritedFrom": {
|
|
8478
9389
|
"name": "VfTextControlBase",
|
|
8479
9390
|
"module": "src/text-control.ts"
|
|
@@ -11344,7 +12255,7 @@
|
|
|
11344
12255
|
"declarations": [
|
|
11345
12256
|
{
|
|
11346
12257
|
"kind": "class",
|
|
11347
|
-
"description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them,\nand corner reserves that cell on a single-axis rail too — the\nrail stops 15px short of the frame, for a grow box to land in.\n\nSize the host (width/height) from the outside; the viewport fills it.\nContent runs to the frame and the rails — the area adds no inset of its\nown; an inset is the content's (a `vf-stack pad`).\n\nThe scrolled plane sizes to its content — never narrower than the\nviewport, as wide as content that cannot wrap — so the rails follow a row\nthat grows sideways the way they follow copy that grows down, and a\n`slotchange` re-measures. measure covers a scroll range that
|
|
12258
|
+
"description": "`<vf-scroll-area>` — a container whose scrollbars look like System 7.\n\nWhite box with a 1px black frame, an inner scrolling viewport, and scroll\nrails the kit draws itself as shadow DOM (the shared `vfScrollRail` recipe):\nboxed arrow buttons at each end, a loose 1-bit dot-dither trough, and the\nclassic fixed 16px thumb. The native scrollbar is hidden, never the native\nscrolling — wheel, trackpad momentum, keyboard, touch and assistive-tech\nscrolling stay the platform's, and ScrollRailController keeps the\nrail in sync while driving the classic interactions (thumb drag, trough\npaging, arrow stepping with auto-repeat). Every engine renders the same\nrail — there is no Firefox fallback skin anymore.\n\nEach reserved scroll rail is a permanent placeholder: arrow buttons on an\nempty white channel sit in the gutter even when the content fits, the\ndither and thumb filling in only once that axis overflows (System 7 drew\nan active window's no-overflow bar as arrows on a bare channel; driven\nby ScrollStateController). Which rails are reserved is set by\naxis; when both are reserved the bottom-right corner joins them,\nand corner reserves that cell on a single-axis rail too — the\nrail stops 15px short of the frame, for a grow box to land in.\n\nSize the host (width/height) from the outside; the viewport fills it.\nContent runs to the frame and the rails — the area adds no inset of its\nown; an inset is the content's (a `vf-stack pad`).\n\nThe scrolled plane sizes to its content — never narrower than the\nviewport, as wide as content that cannot wrap — so the rails follow a row\nthat grows sideways the way they follow copy that grows down, and a\n`slotchange` re-measures. A placed child moved through `top`/`left` is\nheard too: every such write is announced as `vf-placement-change`\n(src/position.ts), and the area re-measures on it — a drop, an arrow\nnudge, a page's own `icon.left = …`, with no code on the page. * measure covers a scroll range that changes with no box and no placement\nchanging (a transform).\n\nplacementAt converts a viewport point into the plane's own\nplacement coordinates, for a drop that lands in this area from elsewhere.",
|
|
11348
12259
|
"name": "VfScrollArea",
|
|
11349
12260
|
"cssProperties": [
|
|
11350
12261
|
{
|
|
@@ -11458,6 +12369,35 @@
|
|
|
11458
12369
|
"default": "false",
|
|
11459
12370
|
"description": "Whether the content actually overflows the viewport (either axis)."
|
|
11460
12371
|
},
|
|
12372
|
+
{
|
|
12373
|
+
"kind": "field",
|
|
12374
|
+
"name": "#onPlacementChange",
|
|
12375
|
+
"privacy": "private"
|
|
12376
|
+
},
|
|
12377
|
+
{
|
|
12378
|
+
"kind": "method",
|
|
12379
|
+
"name": "placementAt",
|
|
12380
|
+
"return": {
|
|
12381
|
+
"type": {
|
|
12382
|
+
"text": "{ left: number; top: number }"
|
|
12383
|
+
}
|
|
12384
|
+
},
|
|
12385
|
+
"parameters": [
|
|
12386
|
+
{
|
|
12387
|
+
"name": "clientX",
|
|
12388
|
+
"type": {
|
|
12389
|
+
"text": "number"
|
|
12390
|
+
}
|
|
12391
|
+
},
|
|
12392
|
+
{
|
|
12393
|
+
"name": "clientY",
|
|
12394
|
+
"type": {
|
|
12395
|
+
"text": "number"
|
|
12396
|
+
}
|
|
12397
|
+
}
|
|
12398
|
+
],
|
|
12399
|
+
"description": "A viewport point (CSS px) as a placement on the scrolled plane —\n`{ left, top }` in whole system px on the placement lattice, the pair a\nchild dropped here is written with. The plane travels with the scroll,\nso its box already carries the scroll offset and the border-floor\npadding; nothing is left for the caller to add."
|
|
12400
|
+
},
|
|
11461
12401
|
{
|
|
11462
12402
|
"kind": "method",
|
|
11463
12403
|
"name": "focus",
|
|
@@ -11485,7 +12425,7 @@
|
|
|
11485
12425
|
"text": "void"
|
|
11486
12426
|
}
|
|
11487
12427
|
},
|
|
11488
|
-
"description": "Re-measure overflow and re-sync the rails. The area tracks its content's\nbox
|
|
12428
|
+
"description": "Re-measure overflow and re-sync the rails. The area tracks its content's\nbox, its slot and every placement write by itself; call this for a\nscroll range that changes with none of those — a transform — the way the\nkit's own fields re-measure on input."
|
|
11489
12429
|
},
|
|
11490
12430
|
{
|
|
11491
12431
|
"kind": "method",
|
|
@@ -16004,10 +16944,10 @@
|
|
|
16004
16944
|
"privacy": "protected",
|
|
16005
16945
|
"return": {
|
|
16006
16946
|
"type": {
|
|
16007
|
-
"text": "
|
|
16947
|
+
"text": "boolean"
|
|
16008
16948
|
}
|
|
16009
16949
|
},
|
|
16010
|
-
"description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.",
|
|
16950
|
+
"description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
|
|
16011
16951
|
"inheritedFrom": {
|
|
16012
16952
|
"name": "VfTextControlBase",
|
|
16013
16953
|
"module": "src/text-control.ts"
|
|
@@ -16871,7 +17811,7 @@
|
|
|
16871
17811
|
}
|
|
16872
17812
|
}
|
|
16873
17813
|
],
|
|
16874
|
-
"description": "Enter in a single-line field triggers the associated form's implicit\nsubmission. The native `<input>` is shadow-encapsulated, so its form owner\nis null and the browser won't do this itself."
|
|
17814
|
+
"description": "Enter in a single-line field triggers the associated form's implicit\nsubmission. The native `<input>` is shadow-encapsulated, so its form owner\nis null and the browser won't do this itself. A press the form took is\ncancelled, so an enclosing modal does not route it again."
|
|
16875
17815
|
},
|
|
16876
17816
|
{
|
|
16877
17817
|
"kind": "field",
|
|
@@ -17130,10 +18070,10 @@
|
|
|
17130
18070
|
"privacy": "protected",
|
|
17131
18071
|
"return": {
|
|
17132
18072
|
"type": {
|
|
17133
|
-
"text": "
|
|
18073
|
+
"text": "boolean"
|
|
17134
18074
|
}
|
|
17135
18075
|
},
|
|
17136
|
-
"description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.",
|
|
18076
|
+
"description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched.",
|
|
17137
18077
|
"inheritedFrom": {
|
|
17138
18078
|
"name": "VfTextControlBase",
|
|
17139
18079
|
"module": "src/text-control.ts"
|
|
@@ -18185,6 +19125,26 @@
|
|
|
18185
19125
|
"default": "false",
|
|
18186
19126
|
"description": "Whether the `status` slot has assigned content (drives the strip)."
|
|
18187
19127
|
},
|
|
19128
|
+
{
|
|
19129
|
+
"kind": "field",
|
|
19130
|
+
"name": "body",
|
|
19131
|
+
"type": {
|
|
19132
|
+
"text": "HTMLDivElement | null"
|
|
19133
|
+
},
|
|
19134
|
+
"privacy": "private",
|
|
19135
|
+
"readonly": true,
|
|
19136
|
+
"description": "The content region — the placed-child anchor; exists from the first render."
|
|
19137
|
+
},
|
|
19138
|
+
{
|
|
19139
|
+
"kind": "field",
|
|
19140
|
+
"name": "scrollArea",
|
|
19141
|
+
"type": {
|
|
19142
|
+
"text": "VfScrollArea | null"
|
|
19143
|
+
},
|
|
19144
|
+
"privacy": "private",
|
|
19145
|
+
"readonly": true,
|
|
19146
|
+
"description": "The built-in edge scroll area, while `scrollbars` is set."
|
|
19147
|
+
},
|
|
18188
19148
|
{
|
|
18189
19149
|
"kind": "field",
|
|
18190
19150
|
"name": "scale",
|
|
@@ -18269,6 +19229,40 @@
|
|
|
18269
19229
|
"privacy": "private",
|
|
18270
19230
|
"default": "null"
|
|
18271
19231
|
},
|
|
19232
|
+
{
|
|
19233
|
+
"kind": "method",
|
|
19234
|
+
"name": "placementAt",
|
|
19235
|
+
"return": {
|
|
19236
|
+
"type": {
|
|
19237
|
+
"text": "{ left: number; top: number }"
|
|
19238
|
+
}
|
|
19239
|
+
},
|
|
19240
|
+
"parameters": [
|
|
19241
|
+
{
|
|
19242
|
+
"name": "clientX",
|
|
19243
|
+
"type": {
|
|
19244
|
+
"text": "number"
|
|
19245
|
+
}
|
|
19246
|
+
},
|
|
19247
|
+
{
|
|
19248
|
+
"name": "clientY",
|
|
19249
|
+
"type": {
|
|
19250
|
+
"text": "number"
|
|
19251
|
+
}
|
|
19252
|
+
}
|
|
19253
|
+
],
|
|
19254
|
+
"description": "A viewport point (CSS px) as a placement in the window's content region\n— `{ left, top }` in whole system px on the placement lattice, the pair a\nchild dropped into this window is written with: the body's corner, or\nunder `scrollbars` the scrolled plane, scroll offset included. The anchor\nsits in the shadow tree, which is why the conversion is a method here\nrather than the page's own `toSysExact` against a rect."
|
|
19255
|
+
},
|
|
19256
|
+
{
|
|
19257
|
+
"kind": "method",
|
|
19258
|
+
"name": "measure",
|
|
19259
|
+
"return": {
|
|
19260
|
+
"type": {
|
|
19261
|
+
"text": "void"
|
|
19262
|
+
}
|
|
19263
|
+
},
|
|
19264
|
+
"description": "Re-measure the built-in scroll area's overflow and re-sync its rails —\na no-op without `scrollbars`. The area re-measures by itself on its\ncontent's box, its slot and every placement write; this is the escape\nhatch for a scroll range that changes with none of those, the way\n`vf-scroll-area.measure()` is."
|
|
19265
|
+
},
|
|
18272
19266
|
{
|
|
18273
19267
|
"kind": "field",
|
|
18274
19268
|
"name": "#warnedNoSize",
|
|
@@ -19835,10 +20829,10 @@
|
|
|
19835
20829
|
"privacy": "protected",
|
|
19836
20830
|
"return": {
|
|
19837
20831
|
"type": {
|
|
19838
|
-
"text": "
|
|
20832
|
+
"text": "boolean"
|
|
19839
20833
|
}
|
|
19840
20834
|
},
|
|
19841
|
-
"description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML."
|
|
20835
|
+
"description": "Emulates the form's implicit submission for a plain Enter press. HTML\ndefines it as *activating the form's default button* — the first submit\nbutton in tree order — so the submission carries that button's submitter\nidentity and name/value. A bare `requestSubmit()` carries neither\n(`event.submitter === null`), which made Enter and a click on the same\nform produce different payloads. `click()` is the activation: a native\nbutton becomes the real submitter, and a `vf-button` forwards it to its\ninner button, which runs the same transient-proxy path a pointer does. A\ndisabled default button means no submission at all, as in HTML; a form\nwith no submit button falls back to the bare call, also as in HTML.\n\nReturns whether a form owner took the press — true even when a disabled\ndefault button meant no submission, since HTML gave the press to the form\neither way. The callers cancel the keydown on true, so a modal shell\n(`VfModalDialog`) sees the press as spoken for and does not also route it\nto the dialog's default button; with no form the press goes on to the\ndialog untouched."
|
|
19842
20836
|
},
|
|
19843
20837
|
{
|
|
19844
20838
|
"kind": "method",
|
|
@@ -20778,7 +21772,7 @@
|
|
|
20778
21772
|
},
|
|
20779
21773
|
{
|
|
20780
21774
|
"kind": "class",
|
|
20781
|
-
"description": "Base class for the modal shell `vf-dialog` — and for a consumer's own\nmodal (an alert box, say) authored against the kit.\n\nOwns the native `<dialog>` lifecycle every modal shares: `open` sync, `show()` /\n`close()`, the top/left placement (stated or centered) and the\nsingle `close` funnel that drops the written origin and fires `vf-close` with\nthe reason. Because every close path — Escape, `close()`, the opt-in\nlightDismiss click outside — routes through the native `close`\nevent, an Escape-close no longer leaves a stale origin behind, so the next\nopen re-derives it.\n\nRemoving an open modal from the DOM is a close path too. HTML's dialog\n*removing steps* take the element out of the top layer **without** running\nthe close algorithm — no `close` event, no focus restoration — which is\nexactly what the standard framework pattern of unmounting a dialog instead\nof calling `close()` does. `disconnectedCallback` routes that path through\nthe same funnel: the removed element still fires `vf-close` (heard by\nlisteners on the element itself — it has left the tree, so nothing\nbubbles), `open` and the pinned margins reconcile so a re-append mounts it\nclosed and re-centered, and focus returns to the element that was focused\nwhen the modal opened.\n\nSubclasses supply only the frame chrome: a `render()` returning\n`<dialog",
|
|
21775
|
+
"description": "Base class for the modal shell `vf-dialog` — and for a consumer's own\nmodal (an alert box, say) authored against the kit.\n\nOwns the native `<dialog>` lifecycle every modal shares: `open` sync, `show()` /\n`close()`, the top/left placement (stated or centered) and the\nsingle `close` funnel that drops the written origin and fires `vf-close` with\nthe reason. Because every close path — Escape, `close()`, the opt-in\nlightDismiss click outside — routes through the native `close`\nevent, an Escape-close no longer leaves a stale origin behind, so the next\nopen re-derives it.\n\nIt also owns the two keyboard rules every classic dialog followed (see\ndefaultButton and initialFocusTarget): Return or Enter\nactivates the default button from anywhere in the dialog, and the box opens\nwith the insertion point in its first text field — or, with none, focus on\nthe default button.\n\nRemoving an open modal from the DOM is a close path too. HTML's dialog\n*removing steps* take the element out of the top layer **without** running\nthe close algorithm — no `close` event, no focus restoration — which is\nexactly what the standard framework pattern of unmounting a dialog instead\nof calling `close()` does. `disconnectedCallback` routes that path through\nthe same funnel: the removed element still fires `vf-close` (heard by\nlisteners on the element itself — it has left the tree, so nothing\nbubbles), `open` and the pinned margins reconcile so a re-append mounts it\nclosed and re-centered, and focus returns to the element that was focused\nwhen the modal opened.\n\nSubclasses supply only the frame chrome: a `render()` returning\n`<dialog",
|
|
20782
21776
|
"name": "VfModalDialog",
|
|
20783
21777
|
"members": [
|
|
20784
21778
|
{
|
|
@@ -20908,11 +21902,48 @@
|
|
|
20908
21902
|
},
|
|
20909
21903
|
{
|
|
20910
21904
|
"kind": "field",
|
|
20911
|
-
"name": "#
|
|
21905
|
+
"name": "#openListeners",
|
|
20912
21906
|
"privacy": "private",
|
|
20913
21907
|
"readonly": true,
|
|
20914
|
-
"default": "new DocumentListenersController(this, () => [ [this._dialog, 'pointerdown', this.#onDialogPointerDown], [this._dialog, 'pointerup', this.#onDialogPointerUp], [this._dialog, 'pointercancel', this.#onDialogPointerCancel], ])",
|
|
20915
|
-
"description": "The
|
|
21908
|
+
"default": "new DocumentListenersController(this, () => [ [this._dialog, 'pointerdown', this.#onDialogPointerDown], [this._dialog, 'pointerup', this.#onDialogPointerUp], [this._dialog, 'pointercancel', this.#onDialogPointerCancel], [this._dialog, 'keydown', this.#onDialogKeydown], ])",
|
|
21909
|
+
"description": "The while-open listeners, on the `<dialog>` itself: the light-dismiss\npointer trio and the Return/Enter routing.\n\nLight dismiss: the `::backdrop` hit-tests as its dialog, and the frame the\nsubclass renders fills the dialog's box, so the dialog is an event's\n*target* only when the pointer was outside the frame — no rect arithmetic,\nno composedPath. Attached whether or not lightDismiss is set: the\nhandlers read the property at the release, so it can be toggled on an\nopen dialog."
|
|
21910
|
+
},
|
|
21911
|
+
{
|
|
21912
|
+
"kind": "field",
|
|
21913
|
+
"name": "defaultButton",
|
|
21914
|
+
"type": {
|
|
21915
|
+
"text": "HTMLElement | null"
|
|
21916
|
+
},
|
|
21917
|
+
"privacy": "protected",
|
|
21918
|
+
"description": "The dialog's default button — the one wearing the bold ring\n(`vf-button[variant=\"default\"]`) — or null with none, or none enabled.\nLooked for among the slotted content first, then in the shadow tree, so a\nconsumer's own modal that renders its buttons itself is covered too.\nOverride to name it some other way.",
|
|
21919
|
+
"readonly": true
|
|
21920
|
+
},
|
|
21921
|
+
{
|
|
21922
|
+
"kind": "field",
|
|
21923
|
+
"name": "initialFocusTarget",
|
|
21924
|
+
"type": {
|
|
21925
|
+
"text": "HTMLElement | null"
|
|
21926
|
+
},
|
|
21927
|
+
"privacy": "protected",
|
|
21928
|
+
"description": "Where focus lands on open. The classic Dialog Manager put the insertion\npoint in the first editable text item and gave nothing else focus at all;\nthe web needs a focused control for the keyboard to have anywhere to be,\nso with no text field the default button takes it (which is also what\nmakes Return and Space work there without a Tab). In order:\n\n1. a slotted control carrying `autofocus` — the author's say;\n2. the first enabled text-entry control (`vf-text-field`,\n `vf-number-field`, `vf-text-area`, or a native text input/textarea);\n3. the defaultButton;\n4. null — leave the browser's own choice alone.\n\nLeft to itself, `showModal()` focuses the first focusable thing in flat\ntree order, which for a Cancel/OK row is Cancel, and for a body with a\nlink in it is the link — so Return did the one thing a classic dialog's\nReturn never did. Override to choose differently.",
|
|
21929
|
+
"readonly": true
|
|
21930
|
+
},
|
|
21931
|
+
{
|
|
21932
|
+
"kind": "method",
|
|
21933
|
+
"name": "#focusInitial",
|
|
21934
|
+
"privacy": "private",
|
|
21935
|
+
"return": {
|
|
21936
|
+
"type": {
|
|
21937
|
+
"text": "void"
|
|
21938
|
+
}
|
|
21939
|
+
},
|
|
21940
|
+
"description": "Hand focus to initialFocusTarget, right after `showModal()`'s own\nfocusing steps have run (so this is the last word, not a race)."
|
|
21941
|
+
},
|
|
21942
|
+
{
|
|
21943
|
+
"kind": "field",
|
|
21944
|
+
"name": "#onDialogKeydown",
|
|
21945
|
+
"privacy": "private",
|
|
21946
|
+
"description": "Return and Enter activate the default button from anywhere in the\ndialog, the way the classic Dialog Manager routed them — including from a\nfocused Cancel button, where Space is the key that presses the focused\ncontrol. Bubble phase, and only an uncancelled press: a control that\ntakes Enter for itself (`vf-select`'s list, `vf-list`, an icon's rename, a\ntext field whose form ran its implicit submission) cancels the keydown\nand is left alone. Two more keep their own Enter: a link, which follows\nit, and a multi-line editor, where Return inserts a newline and only the\nkeypad's Enter key fires the button — the same split a TextEdit item in a\nclassic dialog drew between the two keys."
|
|
20916
21947
|
},
|
|
20917
21948
|
{
|
|
20918
21949
|
"kind": "field",
|
|
@@ -21271,7 +22302,7 @@
|
|
|
21271
22302
|
},
|
|
21272
22303
|
{
|
|
21273
22304
|
"kind": "mixin",
|
|
21274
|
-
"description": "Explicit placement — `top`/`left` in whole system px, on **every** component.\n\nNo exceptions, deliberately. These are web components, and a consumer may put\none wherever they like; the kit does not get to decide that a `vf-option` is\nonly ever a popup row. The rows a container normally owns (`vf-option`,\n`vf-menu-item`, `vf-list-item`) and a bar's `vf-menu` take the pair on the\nsame terms as anything else — and stating an origin on one *inside* its\nmanaging parent does take it out of that parent's flow layout, which is the\nplacement working, not failing. Each of those components documents what its\ncontainer stops doing for it. `vf-dialog` takes the pair in viewport\ncoordinates, the one difference the platform forces (see modal-dialog.ts).\n\nA DITL resource laid a dialog out as a list of items, each with a rectangle\nin the window's own coordinates; arranging controls by stating where they go\nis as native to System 7 as stacking them. This mixin is that mechanism:\ndeclaring `top` or `left` takes the element out of normal flow and\nabsolutely positions it within its parent, both coordinates in the art's own\nunit so the position scales with the display like every other metric. A\nwindow or dialog body can be laid out either way — a tree of `vf-stack`s, or\npositioned children — with no stylesheet in either case.\n\nMechanics, mirroring how `vf-stack` writes its declared size:\n\n- Setting either property writes `position: absolute` and both offsets onto\n the host's inline style as `calc(var(--vf-scale, 1) * Npx)` — live against\n the display, resolved at paint time. The coordinate left unset is 0.\n- `right`/`bottom` are released to `auto` and `margin` zeroed, so the stated\n offsets are the whole story — an auto-width box with both edges set would\n stretch rather than sit (the `vf-icon` seed logic, generalized).\n- Unsetting both returns the element to normal flow: every inline\n declaration this wrote is removed, and stylesheet values resume.\n\nThe anchor is CSS's own: the nearest positioned ancestor's padding box. The\nkit's containers are all deliberate anchors — a desktop's raster, a window's\ncontent region (the frame's inner edge, below the title bar — the 12px body\ninset governs flow content only, exactly the DITL convention), a dialog's\ncontent area, a stack's box, a fieldset's border box, a scroll area's\nscrolled plane. In a non-kit parent, give the parent `position: relative`,\nthe one line of CSS this feature can't write for you.\n\nThe writing rides a ReactiveController (`hostUpdated`) rather than\nan `updated()` override, for a lifecycle reason worth keeping in the source:\nthe mixin sits *under* each component class, and most components override\n`updated()` without calling `super.updated()` — Lit's base is a no-op, so\nnothing ever forced the call — which would silently shadow a mixin-level\noverride. A controller is invoked by ReactiveElement itself, after every\nupdate, no matter what the subclass does.\n\nA gesture writes **through these properties**: `vf-window`'s title-bar drag\nand `vf-icon`'s drag and arrow nudge hand their new origin to a\nPlacementController, which snaps it onto the placement lattice and\nsets `left`/`top`. A moved element is therefore placed exactly the way an\nauthored one is — a live `calc()` in the art's own unit — and stays where it\nwas dropped when the zoom or the display changes what a system px costs.\n\nIt did not always. Writing the resolved CSS px straight to the inline style\nfroze the coordinate in the wrong unit: `--vf-scale` moved under it at every\nzoom step and the same constant read back as a different number of system px,\nso moved windows and icons slid off the grid the rest of the kit stayed on\n(by `3z / round(3z)` — 10% at 110% zoom, where nothing else moves at all).\n\nSetting a property yourself is still the deliberate way to re-place a moved\nelement. The controller re-applies **only when the values changed**, so an\nunrelated update — a heading change, a desktop toggling `active` — never\nre-asserts a coordinate and costs nothing.\n\n**`fixed`** holds the placement against the *visible* region of the nearest\nscrolling ancestor instead of its scrolled plane — a tool strip over a\ndocument, a column header over rows — and the flag alone places at (0,0).\nThe engine underneath is `position: sticky`, the one way the platform holds\na box against a scrollport from inside it on the compositor (a scroll\nlistener counter-translating lags a frame; a layer outside the scroller\nstops wheel and touch over it). Sticky boxes stay in flow, which is not\nwhat a placement is, so the controller erases the footprint: the host is\nblockified (no line box), shrink-wrapped, and given a negative right/bottom\nmargin equal to its own box — a 0×0 margin box that flow content lays out\nas if it weren't there, measured by a ResizeObserver so a relabel or a zoom\nstep keeps it exact. `z-index: 1` puts it over the plane's placed children,\nwhich is where a fixed strip belongs. Two facts of the engine that the\ndocs state as rules: sticky only ever pushes a box *down* from where the\nflow put it, so a fixed child comes before the flow content in its parent;\nand a scroll container that never scrolls (a plain window body is\n`overflow: hidden`) holds it exactly where placement would.",
|
|
22305
|
+
"description": "Explicit placement — `top`/`left` in whole system px, on **every** component.\n\nNo exceptions, deliberately. These are web components, and a consumer may put\none wherever they like; the kit does not get to decide that a `vf-option` is\nonly ever a popup row. The rows a container normally owns (`vf-option`,\n`vf-menu-item`, `vf-list-item`) and a bar's `vf-menu` take the pair on the\nsame terms as anything else — and stating an origin on one *inside* its\nmanaging parent does take it out of that parent's flow layout, which is the\nplacement working, not failing. Each of those components documents what its\ncontainer stops doing for it. `vf-dialog` takes the pair in viewport\ncoordinates, the one difference the platform forces (see modal-dialog.ts).\n\nA DITL resource laid a dialog out as a list of items, each with a rectangle\nin the window's own coordinates; arranging controls by stating where they go\nis as native to System 7 as stacking them. This mixin is that mechanism:\ndeclaring `top` or `left` takes the element out of normal flow and\nabsolutely positions it within its parent, both coordinates in the art's own\nunit so the position scales with the display like every other metric. A\nwindow or dialog body can be laid out either way — a tree of `vf-stack`s, or\npositioned children — with no stylesheet in either case.\n\nMechanics, mirroring how `vf-stack` writes its declared size:\n\n- Setting either property writes `position: absolute` and both offsets onto\n the host's inline style as `calc(var(--vf-scale, 1) * Npx)` — live against\n the display, resolved at paint time. The coordinate left unset is 0.\n- `right`/`bottom` are released to `auto` and `margin` zeroed, so the stated\n offsets are the whole story — an auto-width box with both edges set would\n stretch rather than sit (the `vf-icon` seed logic, generalized).\n- Unsetting both returns the element to normal flow: every inline\n declaration this wrote is removed, and stylesheet values resume.\n\nThe anchor is CSS's own: the nearest positioned ancestor's padding box. The\nkit's containers are all deliberate anchors — a desktop's raster, a window's\ncontent region (the frame's inner edge, below the title bar — the 12px body\ninset governs flow content only, exactly the DITL convention), a dialog's\ncontent area, a stack's box, a fieldset's border box, a scroll area's\nscrolled plane. In a non-kit parent, give the parent `position: relative`,\nthe one line of CSS this feature can't write for you.\n\nThe writing rides a ReactiveController (`hostUpdated`) rather than\nan `updated()` override, for a lifecycle reason worth keeping in the source:\nthe mixin sits *under* each component class, and most components override\n`updated()` without calling `super.updated()` — Lit's base is a no-op, so\nnothing ever forced the call — which would silently shadow a mixin-level\noverride. A controller is invoked by ReactiveElement itself, after every\nupdate, no matter what the subclass does.\n\nA gesture writes **through these properties**: `vf-window`'s title-bar drag\nand `vf-icon`'s drag and arrow nudge hand their new origin to a\nPlacementController, which snaps it onto the placement lattice and\nsets `left`/`top`. A moved element is therefore placed exactly the way an\nauthored one is — a live `calc()` in the art's own unit — and stays where it\nwas dropped when the zoom or the display changes what a system px costs.\n\nIt did not always. Writing the resolved CSS px straight to the inline style\nfroze the coordinate in the wrong unit: `--vf-scale` moved under it at every\nzoom step and the same constant read back as a different number of system px,\nso moved windows and icons slid off the grid the rest of the kit stayed on\n(by `3z / round(3z)` — 10% at 110% zoom, where nothing else moves at all).\n\nSetting a property yourself is still the deliberate way to re-place a moved\nelement. The controller re-applies **only when the values changed**, so an\nunrelated update — a heading change, a desktop toggling `active` — never\nre-asserts a coordinate and costs nothing.\n\n**Every write it makes is announced** as `vf-placement-change` (bubbles,\n`composed: false`, detail `{ left, top }` — nulls on a return to flow). It\nis an internal coordination event on the terms of the menu handshakes:\nparent and child share one light tree, and a kit protocol must not leak out\nof a consumer's shadow boundary. `vf-scroll-area` listens for it and\nre-measures, because a placed child moved through `top`/`left` changes the\nscroll range with no box changing anywhere a ResizeObserver can see — the\nplane's box is never grown by an absolutely positioned child. One funnel\ncovers a drop, an arrow nudge and a page's own `icon.left = …` alike.\n\n**`fixed`** holds the placement against the *visible* region of the nearest\nscrolling ancestor instead of its scrolled plane — a tool strip over a\ndocument, a column header over rows — and the flag alone places at (0,0).\nThe engine underneath is `position: sticky`, the one way the platform holds\na box against a scrollport from inside it on the compositor (a scroll\nlistener counter-translating lags a frame; a layer outside the scroller\nstops wheel and touch over it). Sticky boxes stay in flow, which is not\nwhat a placement is, so the controller erases the footprint: the host is\nblockified (no line box), shrink-wrapped, and given a negative right/bottom\nmargin equal to its own box — a 0×0 margin box that flow content lays out\nas if it weren't there, measured by a ResizeObserver so a relabel or a zoom\nstep keeps it exact. `z-index: 1` puts it over the plane's placed children,\nwhich is where a fixed strip belongs. Two facts of the engine that the\ndocs state as rules: sticky only ever pushes a box *down* from where the\nflow put it, so a fixed child comes before the flow content in its parent;\nand a scroll container that never scrolls (a plain window body is\n`overflow: hidden`) holds it exactly where placement would.",
|
|
21275
22306
|
"name": "VfPositioned",
|
|
21276
22307
|
"members": [
|
|
21277
22308
|
{
|
|
@@ -21340,6 +22371,43 @@
|
|
|
21340
22371
|
}
|
|
21341
22372
|
]
|
|
21342
22373
|
},
|
|
22374
|
+
{
|
|
22375
|
+
"kind": "function",
|
|
22376
|
+
"name": "placementIn",
|
|
22377
|
+
"return": {
|
|
22378
|
+
"type": {
|
|
22379
|
+
"text": "{ left: number; top: number }"
|
|
22380
|
+
}
|
|
22381
|
+
},
|
|
22382
|
+
"parameters": [
|
|
22383
|
+
{
|
|
22384
|
+
"name": "anchor",
|
|
22385
|
+
"type": {
|
|
22386
|
+
"text": "Element"
|
|
22387
|
+
}
|
|
22388
|
+
},
|
|
22389
|
+
{
|
|
22390
|
+
"name": "clientX",
|
|
22391
|
+
"type": {
|
|
22392
|
+
"text": "number"
|
|
22393
|
+
}
|
|
22394
|
+
},
|
|
22395
|
+
{
|
|
22396
|
+
"name": "clientY",
|
|
22397
|
+
"type": {
|
|
22398
|
+
"text": "number"
|
|
22399
|
+
}
|
|
22400
|
+
},
|
|
22401
|
+
{
|
|
22402
|
+
"name": "scaleAt",
|
|
22403
|
+
"default": "anchor",
|
|
22404
|
+
"type": {
|
|
22405
|
+
"text": "Element"
|
|
22406
|
+
}
|
|
22407
|
+
}
|
|
22408
|
+
],
|
|
22409
|
+
"description": "A viewport point as a placement in `anchor`'s coordinates: `{ left, top }`\nin whole system px on the placement lattice, measured from the anchor's\nbox the way a placed child's `top`/`left` are — the unit and lattice a\ndrag lands on, so the result can be written straight to a child's pair.\n\nThe three containers whose anchor sits in shadow DOM (`vf-window`'s\ncontent region, `vf-desktop`'s screen, `vf-scroll-area`'s plane) expose it\nas `placementAt(clientX, clientY)`; everywhere else the anchor is the\nelement's own padding box, and `toSysExact` against\n`getBoundingClientRect()` is the documented conversion. The anchor is\nexpected to carry no border of its own (the three do not), so its border\nbox is its padding box; `scaleAt` is the element whose `--vf-scale` the\nconversion reads — the component's host."
|
|
22410
|
+
},
|
|
21343
22411
|
{
|
|
21344
22412
|
"kind": "class",
|
|
21345
22413
|
"description": "The gesture half of placement: the piece a *movable* host adds on top of\nVfPositioned, so a drag ends up in the same `top`/`left` system-px\nproperties an author would have written.\n\nTwo things it owns, both consequences of storing the origin in the art's own\nunit rather than in resolved CSS px:\n\n- **Seeding.** A gesture starts from wherever the host already is, which may\n be a coordinate nobody stated in system px — an authored `left: 10%`, a\n `right`-anchored Trash icon, or plain normal flow. seed reads the\n *used* position and converts it once; from then on the properties are the\n whole truth.\n- **The lattice.** Every write is snapped to snapSys — whole art\n pixels, the way QuickDraw moved windows, and the k-system-px run that also\n lands the edge on a whole CSS px (scale.ts explains why that second half\n matters to a scroll rail).\n\nIt holds no state the host's own properties don't already hold, so it takes\nno lifecycle and is not a ReactiveController: the writes it makes are\nordinary property sets, and `VfPositioned`'s controller renders them.\n\nAnd one thing it deliberately does **not** do: re-snap a placed host when the\nscale changes. The lattice moves with the scale (k is 2 at dpr 2, 3 at that\ndisplay's 150%), so a coordinate dropped on one rung sits between two on the\nnext — but re-rounding it there is worse than leaving it. It is lossy, and it\ncompounds: 62 → 63 at 150% → 64 at 200%, a window walking away from where it\nwas dropped one zoom step at a time, which is a milder version of the exact\nbug this class exists to fix (`verify:zoom` group (e) fails on it). Whole\nsystem px is whole *device* px at every rung by the scale contract, so the\nart stays crisp regardless; only the whole-CSS-px edge is given up, and an\nauthored `left=\"10\"` gives that up already. A dropped coordinate is therefore\nas immutable as an authored one — which is the whole claim.",
|
|
@@ -21393,6 +22461,15 @@
|
|
|
21393
22461
|
"description": "Whether a gesture has placed this host (as opposed to markup or CSS).",
|
|
21394
22462
|
"readonly": true
|
|
21395
22463
|
},
|
|
22464
|
+
{
|
|
22465
|
+
"kind": "field",
|
|
22466
|
+
"name": "bounds",
|
|
22467
|
+
"type": {
|
|
22468
|
+
"text": "PlacementBounds"
|
|
22469
|
+
},
|
|
22470
|
+
"description": "The box the gesture is clamped into, in system px: the one seed\nmeasured, or a fresh measurement before any gesture. A group drag reads\nit to clamp a whole selection's delta at once.",
|
|
22471
|
+
"readonly": true
|
|
22472
|
+
},
|
|
21396
22473
|
{
|
|
21397
22474
|
"kind": "method",
|
|
21398
22475
|
"name": "seed",
|
|
@@ -21488,6 +22565,14 @@
|
|
|
21488
22565
|
"module": "src/position.ts"
|
|
21489
22566
|
}
|
|
21490
22567
|
},
|
|
22568
|
+
{
|
|
22569
|
+
"kind": "js",
|
|
22570
|
+
"name": "placementIn",
|
|
22571
|
+
"declaration": {
|
|
22572
|
+
"name": "placementIn",
|
|
22573
|
+
"module": "src/position.ts"
|
|
22574
|
+
}
|
|
22575
|
+
},
|
|
21491
22576
|
{
|
|
21492
22577
|
"kind": "js",
|
|
21493
22578
|
"name": "PlacementController",
|