vintage-frames 0.6.2 → 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 +1230 -235
- package/dist/components/vf-desktop.d.ts +34 -0
- package/dist/components/vf-desktop.js +66 -43
- 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-scroll-area.d.ts +25 -6
- package/dist/components/vf-scroll-area.js +34 -28
- 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/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/docs/SPEC.md +24 -8
- package/editor/vscode.html-custom-data.json +50 -3
- package/editor/web-types.json +132 -4
- 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",
|
|
@@ -4405,52 +4445,16 @@
|
|
|
4405
4445
|
},
|
|
4406
4446
|
{
|
|
4407
4447
|
"kind": "javascript-module",
|
|
4408
|
-
"path": "src/components/vf-icon.ts",
|
|
4448
|
+
"path": "src/components/vf-icon-field.ts",
|
|
4409
4449
|
"declarations": [
|
|
4410
4450
|
{
|
|
4411
4451
|
"kind": "class",
|
|
4412
|
-
"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.",
|
|
4413
|
-
"name": "
|
|
4414
|
-
"cssProperties": [
|
|
4415
|
-
{
|
|
4416
|
-
"description": "Space between the art cell and the name plate",
|
|
4417
|
-
"name": "--vf-icon-gap"
|
|
4418
|
-
},
|
|
4419
|
-
{
|
|
4420
|
-
"description": "The name plate's line box",
|
|
4421
|
-
"name": "--vf-icon-label-height"
|
|
4422
|
-
}
|
|
4423
|
-
],
|
|
4424
|
-
"cssParts": [
|
|
4425
|
-
{
|
|
4426
|
-
"description": "The icon and its label plate, stacked.",
|
|
4427
|
-
"name": "frame"
|
|
4428
|
-
},
|
|
4429
|
-
{
|
|
4430
|
-
"description": "The reserved art cell.",
|
|
4431
|
-
"name": "icon"
|
|
4432
|
-
},
|
|
4433
|
-
{
|
|
4434
|
-
"description": "The name block; its lines are centered under the art.",
|
|
4435
|
-
"name": "label"
|
|
4436
|
-
},
|
|
4437
|
-
{
|
|
4438
|
-
"description": "The inked run behind the name, which each wrapped line gets its own of (inverts when selected).",
|
|
4439
|
-
"name": "plate"
|
|
4440
|
-
},
|
|
4441
|
-
{
|
|
4442
|
-
"description": "The rename field, while editing.",
|
|
4443
|
-
"name": "input"
|
|
4444
|
-
}
|
|
4445
|
-
],
|
|
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",
|
|
4446
4454
|
"slots": [
|
|
4447
4455
|
{
|
|
4448
|
-
"description": "The
|
|
4449
|
-
"name": "
|
|
4450
|
-
},
|
|
4451
|
-
{
|
|
4452
|
-
"description": "The 16×16 art, shown under `size=\"small\"`.",
|
|
4453
|
-
"name": "small"
|
|
4456
|
+
"description": "The icons.",
|
|
4457
|
+
"name": ""
|
|
4454
4458
|
}
|
|
4455
4459
|
],
|
|
4456
4460
|
"members": [
|
|
@@ -4461,274 +4465,1061 @@
|
|
|
4461
4465
|
"text": "string"
|
|
4462
4466
|
},
|
|
4463
4467
|
"default": "''",
|
|
4464
|
-
"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.",
|
|
4465
4469
|
"attribute": "label"
|
|
4466
4470
|
},
|
|
4467
4471
|
{
|
|
4468
4472
|
"kind": "field",
|
|
4469
4473
|
"name": "size",
|
|
4470
4474
|
"type": {
|
|
4471
|
-
"text": "VfIconSize"
|
|
4475
|
+
"text": "VfIconSize | null | undefined"
|
|
4472
4476
|
},
|
|
4473
|
-
"
|
|
4474
|
-
"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`.",
|
|
4475
4478
|
"attribute": "size",
|
|
4476
4479
|
"reflects": true
|
|
4477
4480
|
},
|
|
4478
|
-
{
|
|
4479
|
-
"kind": "field",
|
|
4480
|
-
"name": "selectable",
|
|
4481
|
-
"type": {
|
|
4482
|
-
"text": "boolean"
|
|
4483
|
-
},
|
|
4484
|
-
"default": "false",
|
|
4485
|
-
"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.",
|
|
4486
|
-
"attribute": "selectable",
|
|
4487
|
-
"reflects": true
|
|
4488
|
-
},
|
|
4489
|
-
{
|
|
4490
|
-
"kind": "field",
|
|
4491
|
-
"name": "selected",
|
|
4492
|
-
"type": {
|
|
4493
|
-
"text": "boolean"
|
|
4494
|
-
},
|
|
4495
|
-
"default": "false",
|
|
4496
|
-
"description": "Whether the icon is selected: the art inverts and the plate goes black.",
|
|
4497
|
-
"attribute": "selected",
|
|
4498
|
-
"reflects": true
|
|
4499
|
-
},
|
|
4500
|
-
{
|
|
4501
|
-
"kind": "field",
|
|
4502
|
-
"name": "color",
|
|
4503
|
-
"type": {
|
|
4504
|
-
"text": "boolean"
|
|
4505
|
-
},
|
|
4506
|
-
"default": "false",
|
|
4507
|
-
"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.",
|
|
4508
|
-
"attribute": "color",
|
|
4509
|
-
"reflects": true
|
|
4510
|
-
},
|
|
4511
|
-
{
|
|
4512
|
-
"kind": "field",
|
|
4513
|
-
"name": "open",
|
|
4514
|
-
"type": {
|
|
4515
|
-
"text": "boolean"
|
|
4516
|
-
},
|
|
4517
|
-
"default": "false",
|
|
4518
|
-
"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.",
|
|
4519
|
-
"attribute": "open",
|
|
4520
|
-
"reflects": true
|
|
4521
|
-
},
|
|
4522
|
-
{
|
|
4523
|
-
"kind": "field",
|
|
4524
|
-
"name": "movable",
|
|
4525
|
-
"type": {
|
|
4526
|
-
"text": "boolean"
|
|
4527
|
-
},
|
|
4528
|
-
"default": "false",
|
|
4529
|
-
"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.",
|
|
4530
|
-
"attribute": "movable",
|
|
4531
|
-
"reflects": true
|
|
4532
|
-
},
|
|
4533
|
-
{
|
|
4534
|
-
"kind": "field",
|
|
4535
|
-
"name": "editable",
|
|
4536
|
-
"type": {
|
|
4537
|
-
"text": "boolean"
|
|
4538
|
-
},
|
|
4539
|
-
"default": "false",
|
|
4540
|
-
"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.",
|
|
4541
|
-
"attribute": "editable",
|
|
4542
|
-
"reflects": true
|
|
4543
|
-
},
|
|
4544
|
-
{
|
|
4545
|
-
"kind": "field",
|
|
4546
|
-
"name": "width",
|
|
4547
|
-
"type": {
|
|
4548
|
-
"text": "number | undefined"
|
|
4549
|
-
},
|
|
4550
|
-
"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).",
|
|
4551
|
-
"attribute": "width"
|
|
4552
|
-
},
|
|
4553
|
-
{
|
|
4554
|
-
"kind": "field",
|
|
4555
|
-
"name": "maxlength",
|
|
4556
|
-
"type": {
|
|
4557
|
-
"text": "number"
|
|
4558
|
-
},
|
|
4559
|
-
"default": "31",
|
|
4560
|
-
"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.",
|
|
4561
|
-
"attribute": "maxlength"
|
|
4562
|
-
},
|
|
4563
4481
|
{
|
|
4564
4482
|
"kind": "field",
|
|
4565
4483
|
"name": "scale",
|
|
4566
4484
|
"privacy": "private",
|
|
4567
4485
|
"readonly": true,
|
|
4568
4486
|
"default": "new ScaleController(this)",
|
|
4569
|
-
"description": "Default-on display scaling (true 72dpi size); see src/scale.ts."
|
|
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."
|
|
4570
4488
|
},
|
|
4571
4489
|
{
|
|
4572
4490
|
"kind": "field",
|
|
4573
|
-
"name": "
|
|
4491
|
+
"name": "#internals",
|
|
4574
4492
|
"privacy": "private",
|
|
4575
4493
|
"readonly": true,
|
|
4576
|
-
"
|
|
4577
|
-
"description": "Device-pixel grid snapping; see src/grid-snap.ts."
|
|
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."
|
|
4578
4495
|
},
|
|
4579
4496
|
{
|
|
4580
4497
|
"kind": "field",
|
|
4581
|
-
"name": "
|
|
4498
|
+
"name": "#band",
|
|
4582
4499
|
"privacy": "private",
|
|
4583
|
-
"readonly": true,
|
|
4584
|
-
"default": "new FocusRuleController(this)",
|
|
4585
|
-
"description": "Whether to wear the dashed focus rule; see src/focus-modality.ts."
|
|
4586
|
-
},
|
|
4587
|
-
{
|
|
4588
|
-
"kind": "field",
|
|
4589
|
-
"name": "_editing",
|
|
4590
4500
|
"type": {
|
|
4591
|
-
"text": "
|
|
4501
|
+
"text": "Band | null"
|
|
4592
4502
|
},
|
|
4593
|
-
"
|
|
4594
|
-
"
|
|
4595
|
-
"description": "True while the rename field is open."
|
|
4503
|
+
"default": "null",
|
|
4504
|
+
"description": "The rubber band in flight, or null between gestures."
|
|
4596
4505
|
},
|
|
4597
4506
|
{
|
|
4598
4507
|
"kind": "field",
|
|
4599
|
-
"name": "
|
|
4600
|
-
"type": {
|
|
4601
|
-
"text": "string"
|
|
4602
|
-
},
|
|
4508
|
+
"name": "#escape",
|
|
4603
4509
|
"privacy": "private",
|
|
4604
|
-
"
|
|
4605
|
-
"
|
|
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."
|
|
4606
4513
|
},
|
|
4607
4514
|
{
|
|
4608
4515
|
"kind": "field",
|
|
4609
|
-
"name": "#
|
|
4516
|
+
"name": "#onSlotChange",
|
|
4610
4517
|
"privacy": "private",
|
|
4611
|
-
"
|
|
4612
|
-
"text": "number"
|
|
4613
|
-
},
|
|
4614
|
-
"default": "0",
|
|
4615
|
-
"description": "The plate's width in whole, EVEN system px — 0 until the text is measured.\nSee #measurePlate; this is what keeps every name on the grid.\n\nDeliberately NOT `@state()`. The width can only be known by rendering the\nname and measuring it, so a reactive field would make every icon render\ntwice — once to measure, once to deliver one number back — and Lit's\nchange-in-update warning is exactly that round trip being spotted. It\nfeeds a single inline width and nothing else, so #measurePlate\nwrites it straight to the plate instead, the ownership the grid snapper\nhas over `--vf-snap-dx`/`-dy`. The template still reads it so a caption\nLit re-creates (one leaving `_editing`, say) is born at the settled width\nrather than flashing through the unmeasured one."
|
|
4518
|
+
"description": "Icons arriving after the view was set take the view's size."
|
|
4616
4519
|
},
|
|
4617
4520
|
{
|
|
4618
|
-
"kind": "
|
|
4619
|
-
"name": "
|
|
4620
|
-
"type": {
|
|
4621
|
-
"text": "HTMLCanvasElement | null"
|
|
4622
|
-
},
|
|
4521
|
+
"kind": "method",
|
|
4522
|
+
"name": "#applySize",
|
|
4623
4523
|
"privacy": "private",
|
|
4624
|
-
"
|
|
4625
|
-
|
|
4524
|
+
"return": {
|
|
4525
|
+
"type": {
|
|
4526
|
+
"text": "void"
|
|
4527
|
+
}
|
|
4528
|
+
},
|
|
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."
|
|
4626
4530
|
},
|
|
4627
4531
|
{
|
|
4628
|
-
"kind": "
|
|
4629
|
-
"name": "#
|
|
4532
|
+
"kind": "method",
|
|
4533
|
+
"name": "#icons",
|
|
4630
4534
|
"privacy": "private",
|
|
4631
|
-
"
|
|
4632
|
-
"
|
|
4535
|
+
"return": {
|
|
4536
|
+
"type": {
|
|
4537
|
+
"text": "VfIcon[]"
|
|
4538
|
+
}
|
|
4633
4539
|
},
|
|
4634
|
-
"
|
|
4635
|
-
"description": "The art element the ghost derives from, so a rehome moves the listener."
|
|
4540
|
+
"description": "The icons the band can select: every selectable one in the field."
|
|
4636
4541
|
},
|
|
4637
4542
|
{
|
|
4638
4543
|
"kind": "field",
|
|
4639
|
-
"name": "#
|
|
4544
|
+
"name": "#onPointerDown",
|
|
4640
4545
|
"privacy": "private",
|
|
4641
|
-
"
|
|
4642
|
-
"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."
|
|
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."
|
|
4643
4547
|
},
|
|
4644
4548
|
{
|
|
4645
4549
|
"kind": "field",
|
|
4646
|
-
"name": "#
|
|
4550
|
+
"name": "#onPointerMove",
|
|
4647
4551
|
"privacy": "private",
|
|
4648
|
-
"
|
|
4649
|
-
"text": "boolean"
|
|
4650
|
-
},
|
|
4651
|
-
"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 attribute because that is what the recipe writes\n(`<div role=\"listbox\">` — see the class doc). A `vf-list` is deliberately\nnot a match: it holds `vf-list-item` rows, and its own `listbox` role now\nlives in internals rather than on the tag anyway.",
|
|
4652
|
-
"readonly": true
|
|
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."
|
|
4653
4553
|
},
|
|
4654
4554
|
{
|
|
4655
4555
|
"kind": "field",
|
|
4656
|
-
"name": "#
|
|
4657
|
-
"privacy": "private"
|
|
4658
|
-
"type": {
|
|
4659
|
-
"text": "string"
|
|
4660
|
-
},
|
|
4661
|
-
"default": "''",
|
|
4662
|
-
"description": "What the current ghost was derived from, so a no-op refresh is free."
|
|
4556
|
+
"name": "#onPointerUp",
|
|
4557
|
+
"privacy": "private"
|
|
4663
4558
|
},
|
|
4664
4559
|
{
|
|
4665
4560
|
"kind": "field",
|
|
4666
|
-
"name": "#
|
|
4667
|
-
"privacy": "private"
|
|
4668
|
-
"type": {
|
|
4669
|
-
"text": "string"
|
|
4670
|
-
},
|
|
4671
|
-
"default": "''",
|
|
4672
|
-
"description": "The name editing started from, for Escape to put back."
|
|
4561
|
+
"name": "#onKeyDown",
|
|
4562
|
+
"privacy": "private"
|
|
4673
4563
|
},
|
|
4674
4564
|
{
|
|
4675
|
-
"kind": "
|
|
4676
|
-
"name": "#
|
|
4565
|
+
"kind": "method",
|
|
4566
|
+
"name": "#end",
|
|
4677
4567
|
"privacy": "private",
|
|
4678
|
-
"
|
|
4679
|
-
"
|
|
4568
|
+
"return": {
|
|
4569
|
+
"type": {
|
|
4570
|
+
"text": "void"
|
|
4571
|
+
}
|
|
4680
4572
|
},
|
|
4681
|
-
"
|
|
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."
|
|
4582
|
+
},
|
|
4583
|
+
{
|
|
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",
|
|
5121
|
+
"name": "#plateWidth",
|
|
5122
|
+
"privacy": "private",
|
|
5123
|
+
"type": {
|
|
5124
|
+
"text": "number"
|
|
5125
|
+
},
|
|
5126
|
+
"default": "0",
|
|
5127
|
+
"description": "The plate's width in whole, EVEN system px — 0 until the text is measured.\nSee #measurePlate; this is what keeps every name on the grid.\n\nDeliberately NOT `@state()`. The width can only be known by rendering the\nname and measuring it, so a reactive field would make every icon render\ntwice — once to measure, once to deliver one number back — and Lit's\nchange-in-update warning is exactly that round trip being spotted. It\nfeeds a single inline width and nothing else, so #measurePlate\nwrites it straight to the plate instead, the ownership the grid snapper\nhas over `--vf-snap-dx`/`-dy`. The template still reads it so a caption\nLit re-creates (one leaving `_editing`, say) is born at the settled width\nrather than flashing through the unmeasured one."
|
|
5128
|
+
},
|
|
5129
|
+
{
|
|
5130
|
+
"kind": "field",
|
|
5131
|
+
"name": "_ghost",
|
|
5132
|
+
"type": {
|
|
5133
|
+
"text": "HTMLCanvasElement | null"
|
|
5134
|
+
},
|
|
5135
|
+
"privacy": "private",
|
|
5136
|
+
"default": "null",
|
|
5137
|
+
"description": "The derived open ghost, or null while there is nothing to derive from."
|
|
5138
|
+
},
|
|
5139
|
+
{
|
|
5140
|
+
"kind": "field",
|
|
5141
|
+
"name": "#art",
|
|
5142
|
+
"privacy": "private",
|
|
5143
|
+
"type": {
|
|
5144
|
+
"text": "HTMLImageElement | HTMLCanvasElement | null"
|
|
5145
|
+
},
|
|
5146
|
+
"default": "null",
|
|
5147
|
+
"description": "The art element the ghost derives from, so a rehome moves the listener."
|
|
5148
|
+
},
|
|
5149
|
+
{
|
|
5150
|
+
"kind": "field",
|
|
5151
|
+
"name": "#internals",
|
|
5152
|
+
"privacy": "private",
|
|
5153
|
+
"readonly": true,
|
|
5154
|
+
"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."
|
|
5155
|
+
},
|
|
5156
|
+
{
|
|
5157
|
+
"kind": "field",
|
|
5158
|
+
"name": "#inListbox",
|
|
5159
|
+
"privacy": "private",
|
|
5160
|
+
"type": {
|
|
5161
|
+
"text": "boolean"
|
|
5162
|
+
},
|
|
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.",
|
|
5174
|
+
"readonly": true
|
|
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
|
+
},
|
|
5186
|
+
{
|
|
5187
|
+
"kind": "field",
|
|
5188
|
+
"name": "#ghostKey",
|
|
5189
|
+
"privacy": "private",
|
|
5190
|
+
"type": {
|
|
5191
|
+
"text": "string"
|
|
5192
|
+
},
|
|
5193
|
+
"default": "''",
|
|
5194
|
+
"description": "What the current ghost was derived from, so a no-op refresh is free."
|
|
5195
|
+
},
|
|
5196
|
+
{
|
|
5197
|
+
"kind": "field",
|
|
5198
|
+
"name": "#committed",
|
|
5199
|
+
"privacy": "private",
|
|
5200
|
+
"type": {
|
|
5201
|
+
"text": "string"
|
|
5202
|
+
},
|
|
5203
|
+
"default": "''",
|
|
5204
|
+
"description": "The name editing started from, for Escape to put back."
|
|
5205
|
+
},
|
|
5206
|
+
{
|
|
5207
|
+
"kind": "field",
|
|
5208
|
+
"name": "#renameTimer",
|
|
5209
|
+
"privacy": "private",
|
|
5210
|
+
"type": {
|
|
5211
|
+
"text": "number"
|
|
5212
|
+
},
|
|
5213
|
+
"default": "0",
|
|
4682
5214
|
"description": "A rename waiting out the double-click window; 0 when none is pending."
|
|
4683
5215
|
},
|
|
4684
5216
|
{
|
|
4685
|
-
"kind": "field",
|
|
4686
|
-
"name": "#lastPressTime",
|
|
5217
|
+
"kind": "field",
|
|
5218
|
+
"name": "#lastPressTime",
|
|
5219
|
+
"privacy": "private",
|
|
5220
|
+
"type": {
|
|
5221
|
+
"text": "number"
|
|
5222
|
+
},
|
|
5223
|
+
"default": "-Infinity",
|
|
5224
|
+
"description": "When the last press on this icon landed, so the next one can tell whether\nit is the second half of a double-click. `-Infinity` until there is one,\nwhich is what keeps the very first press from reading as a pair."
|
|
5225
|
+
},
|
|
5226
|
+
{
|
|
5227
|
+
"kind": "field",
|
|
5228
|
+
"name": "#outside",
|
|
5229
|
+
"privacy": "private",
|
|
5230
|
+
"readonly": true,
|
|
5231
|
+
"default": "new DocumentListenersController(this, () => [ [document, 'pointerdown', this.#onOutsidePointerDown, true], ])",
|
|
5232
|
+
"description": "Clicking elsewhere deselects, the way it does on a real desktop. Attached\nonly while this icon is both selectable and selected, so an unselected\nfield of icons costs nothing. Capture phase, so a handler that stops the\npress still deselects — and so the icon being clicked *into* selects after\nthis runs, which is what makes plain clicks single-select with no container\nmanaging the set. A container that wants multi-select suppresses it by\nowning `selected` and leaving `selectable` off."
|
|
5233
|
+
},
|
|
5234
|
+
{
|
|
5235
|
+
"kind": "field",
|
|
5236
|
+
"name": "#keepWhole",
|
|
5237
|
+
"privacy": "private",
|
|
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."
|
|
5239
|
+
},
|
|
5240
|
+
{
|
|
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",
|
|
4687
5416
|
"privacy": "private",
|
|
4688
|
-
"
|
|
4689
|
-
"
|
|
5417
|
+
"return": {
|
|
5418
|
+
"type": {
|
|
5419
|
+
"text": "void"
|
|
5420
|
+
}
|
|
4690
5421
|
},
|
|
4691
|
-
"
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
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."
|
|
4701
5437
|
},
|
|
4702
5438
|
{
|
|
4703
|
-
"kind": "
|
|
4704
|
-
"name": "#
|
|
5439
|
+
"kind": "method",
|
|
5440
|
+
"name": "#requestSurface",
|
|
4705
5441
|
"privacy": "private",
|
|
4706
|
-
"
|
|
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."
|
|
4707
5448
|
},
|
|
4708
5449
|
{
|
|
4709
|
-
"kind": "
|
|
4710
|
-
"name": "#
|
|
5450
|
+
"kind": "method",
|
|
5451
|
+
"name": "#buildOutline",
|
|
4711
5452
|
"privacy": "private",
|
|
4712
|
-
"
|
|
4713
|
-
|
|
4714
|
-
|
|
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."
|
|
4715
5473
|
},
|
|
4716
5474
|
{
|
|
4717
|
-
"kind": "
|
|
4718
|
-
"name": "#
|
|
5475
|
+
"kind": "method",
|
|
5476
|
+
"name": "#placeOutlineAt",
|
|
4719
5477
|
"privacy": "private",
|
|
4720
|
-
"
|
|
4721
|
-
"
|
|
5478
|
+
"return": {
|
|
5479
|
+
"type": {
|
|
5480
|
+
"text": "void"
|
|
5481
|
+
}
|
|
4722
5482
|
},
|
|
4723
|
-
"
|
|
4724
|
-
|
|
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."
|
|
4725
5504
|
},
|
|
4726
5505
|
{
|
|
4727
|
-
"kind": "
|
|
4728
|
-
"name": "#
|
|
5506
|
+
"kind": "method",
|
|
5507
|
+
"name": "#dragDetail",
|
|
4729
5508
|
"privacy": "private",
|
|
4730
|
-
"
|
|
4731
|
-
|
|
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."
|
|
4732
5523
|
},
|
|
4733
5524
|
{
|
|
4734
5525
|
"kind": "field",
|
|
@@ -4915,6 +5706,42 @@
|
|
|
4915
5706
|
}
|
|
4916
5707
|
]
|
|
4917
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
|
+
},
|
|
4918
5745
|
{
|
|
4919
5746
|
"kind": "field",
|
|
4920
5747
|
"name": "#onPointerDown",
|
|
@@ -4926,6 +5753,12 @@
|
|
|
4926
5753
|
"privacy": "private",
|
|
4927
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."
|
|
4928
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
|
+
},
|
|
4929
5762
|
{
|
|
4930
5763
|
"kind": "field",
|
|
4931
5764
|
"name": "#onOutsidePointerDown",
|
|
@@ -5032,6 +5865,22 @@
|
|
|
5032
5865
|
{
|
|
5033
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.",
|
|
5034
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"
|
|
5035
5884
|
}
|
|
5036
5885
|
],
|
|
5037
5886
|
"attributes": [
|
|
@@ -5071,6 +5920,15 @@
|
|
|
5071
5920
|
"description": "Whether the icon is selected: the art inverts and the plate goes black.",
|
|
5072
5921
|
"fieldName": "selected"
|
|
5073
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
|
+
},
|
|
5074
5932
|
{
|
|
5075
5933
|
"name": "color",
|
|
5076
5934
|
"type": {
|
|
@@ -11397,7 +12255,7 @@
|
|
|
11397
12255
|
"declarations": [
|
|
11398
12256
|
{
|
|
11399
12257
|
"kind": "class",
|
|
11400
|
-
"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.",
|
|
11401
12259
|
"name": "VfScrollArea",
|
|
11402
12260
|
"cssProperties": [
|
|
11403
12261
|
{
|
|
@@ -11511,6 +12369,35 @@
|
|
|
11511
12369
|
"default": "false",
|
|
11512
12370
|
"description": "Whether the content actually overflows the viewport (either axis)."
|
|
11513
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
|
+
},
|
|
11514
12401
|
{
|
|
11515
12402
|
"kind": "method",
|
|
11516
12403
|
"name": "focus",
|
|
@@ -11538,7 +12425,7 @@
|
|
|
11538
12425
|
"text": "void"
|
|
11539
12426
|
}
|
|
11540
12427
|
},
|
|
11541
|
-
"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."
|
|
11542
12429
|
},
|
|
11543
12430
|
{
|
|
11544
12431
|
"kind": "method",
|
|
@@ -18238,6 +19125,26 @@
|
|
|
18238
19125
|
"default": "false",
|
|
18239
19126
|
"description": "Whether the `status` slot has assigned content (drives the strip)."
|
|
18240
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
|
+
},
|
|
18241
19148
|
{
|
|
18242
19149
|
"kind": "field",
|
|
18243
19150
|
"name": "scale",
|
|
@@ -18322,6 +19229,40 @@
|
|
|
18322
19229
|
"privacy": "private",
|
|
18323
19230
|
"default": "null"
|
|
18324
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
|
+
},
|
|
18325
19266
|
{
|
|
18326
19267
|
"kind": "field",
|
|
18327
19268
|
"name": "#warnedNoSize",
|
|
@@ -21361,7 +22302,7 @@
|
|
|
21361
22302
|
},
|
|
21362
22303
|
{
|
|
21363
22304
|
"kind": "mixin",
|
|
21364
|
-
"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.",
|
|
21365
22306
|
"name": "VfPositioned",
|
|
21366
22307
|
"members": [
|
|
21367
22308
|
{
|
|
@@ -21430,6 +22371,43 @@
|
|
|
21430
22371
|
}
|
|
21431
22372
|
]
|
|
21432
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
|
+
},
|
|
21433
22411
|
{
|
|
21434
22412
|
"kind": "class",
|
|
21435
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.",
|
|
@@ -21483,6 +22461,15 @@
|
|
|
21483
22461
|
"description": "Whether a gesture has placed this host (as opposed to markup or CSS).",
|
|
21484
22462
|
"readonly": true
|
|
21485
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
|
+
},
|
|
21486
22473
|
{
|
|
21487
22474
|
"kind": "method",
|
|
21488
22475
|
"name": "seed",
|
|
@@ -21578,6 +22565,14 @@
|
|
|
21578
22565
|
"module": "src/position.ts"
|
|
21579
22566
|
}
|
|
21580
22567
|
},
|
|
22568
|
+
{
|
|
22569
|
+
"kind": "js",
|
|
22570
|
+
"name": "placementIn",
|
|
22571
|
+
"declaration": {
|
|
22572
|
+
"name": "placementIn",
|
|
22573
|
+
"module": "src/position.ts"
|
|
22574
|
+
}
|
|
22575
|
+
},
|
|
21581
22576
|
{
|
|
21582
22577
|
"kind": "js",
|
|
21583
22578
|
"name": "PlacementController",
|