vintage-frames 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +262 -0
- package/custom-elements.json +19807 -0
- package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js +9 -0
- package/dist/chrome.d.ts +73 -0
- package/dist/chrome.js +94 -0
- package/dist/components/vf-button-group.d.ts +50 -0
- package/dist/components/vf-button-group.js +68 -0
- package/dist/components/vf-button.d.ts +169 -0
- package/dist/components/vf-button.js +234 -0
- package/dist/components/vf-checkbox.d.ts +60 -0
- package/dist/components/vf-checkbox.js +135 -0
- package/dist/components/vf-container.d.ts +95 -0
- package/dist/components/vf-container.js +86 -0
- package/dist/components/vf-desktop.d.ts +243 -0
- package/dist/components/vf-desktop.js +310 -0
- package/dist/components/vf-dialog.d.ts +105 -0
- package/dist/components/vf-dialog.js +280 -0
- package/dist/components/vf-fieldset.d.ts +34 -0
- package/dist/components/vf-fieldset.js +76 -0
- package/dist/components/vf-grid.d.ts +124 -0
- package/dist/components/vf-grid.js +133 -0
- package/dist/components/vf-icon.d.ts +356 -0
- package/dist/components/vf-icon.js +507 -0
- package/dist/components/vf-img.d.ts +63 -0
- package/dist/components/vf-img.js +65 -0
- package/dist/components/vf-label.d.ts +117 -0
- package/dist/components/vf-label.js +151 -0
- package/dist/components/vf-list-item.d.ts +48 -0
- package/dist/components/vf-list-item.js +98 -0
- package/dist/components/vf-list.d.ts +77 -0
- package/dist/components/vf-list.js +256 -0
- package/dist/components/vf-menu-bar.d.ts +64 -0
- package/dist/components/vf-menu-bar.js +236 -0
- package/dist/components/vf-menu-item.d.ts +91 -0
- package/dist/components/vf-menu-item.js +252 -0
- package/dist/components/vf-menu.d.ts +95 -0
- package/dist/components/vf-menu.js +346 -0
- package/dist/components/vf-number-field.d.ts +60 -0
- package/dist/components/vf-number-field.js +248 -0
- package/dist/components/vf-option.d.ts +49 -0
- package/dist/components/vf-option.js +119 -0
- package/dist/components/vf-paragraph.d.ts +73 -0
- package/dist/components/vf-paragraph.js +66 -0
- package/dist/components/vf-progress-bar.d.ts +69 -0
- package/dist/components/vf-progress-bar.js +272 -0
- package/dist/components/vf-radio-group.d.ts +109 -0
- package/dist/components/vf-radio-group.js +105 -0
- package/dist/components/vf-radio.d.ts +59 -0
- package/dist/components/vf-radio.js +135 -0
- package/dist/components/vf-scroll-area.d.ts +75 -0
- package/dist/components/vf-scroll-area.js +124 -0
- package/dist/components/vf-select.d.ts +334 -0
- package/dist/components/vf-select.js +658 -0
- package/dist/components/vf-separator.d.ts +38 -0
- package/dist/components/vf-separator.js +55 -0
- package/dist/components/vf-slider.d.ts +80 -0
- package/dist/components/vf-slider.js +255 -0
- package/dist/components/vf-stack.d.ts +188 -0
- package/dist/components/vf-stack.js +168 -0
- package/dist/components/vf-swatch.d.ts +98 -0
- package/dist/components/vf-swatch.js +201 -0
- package/dist/components/vf-text-area.d.ts +77 -0
- package/dist/components/vf-text-area.js +121 -0
- package/dist/components/vf-text-field.d.ts +53 -0
- package/dist/components/vf-text-field.js +79 -0
- package/dist/components/vf-window.d.ts +157 -0
- package/dist/components/vf-window.js +377 -0
- package/dist/cursor-art.d.ts +37 -0
- package/dist/cursor-art.js +41 -0
- package/dist/cursor.d.ts +49 -0
- package/dist/cursor.js +171 -0
- package/dist/define.d.ts +45 -0
- package/dist/define.js +15 -0
- package/dist/document-listeners.d.ts +62 -0
- package/dist/document-listeners.js +29 -0
- package/dist/drag.d.ts +49 -0
- package/dist/drag.js +30 -0
- package/dist/events.d.ts +79 -0
- package/dist/events.js +25 -0
- package/dist/focus-modality.d.ts +94 -0
- package/dist/focus-modality.js +56 -0
- package/dist/form-control.d.ts +247 -0
- package/dist/form-control.js +127 -0
- package/dist/glyphs.d.ts +117 -0
- package/dist/glyphs.js +100 -0
- package/dist/grid-snap.d.ts +73 -0
- package/dist/grid-snap.js +135 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.js +71 -0
- package/dist/menu-press.d.ts +60 -0
- package/dist/menu-press.js +97 -0
- package/dist/modal-dialog.d.ts +148 -0
- package/dist/modal-dialog.js +155 -0
- package/dist/motion.d.ts +72 -0
- package/dist/motion.js +22 -0
- package/dist/number.d.ts +24 -0
- package/dist/number.js +12 -0
- package/dist/open-art.d.ts +40 -0
- package/dist/open-art.js +35 -0
- package/dist/pixel-frame.d.ts +102 -0
- package/dist/pixel-frame.js +77 -0
- package/dist/popup-overflow.d.ts +157 -0
- package/dist/popup-overflow.js +45 -0
- package/dist/position.d.ts +187 -0
- package/dist/position.js +74 -0
- package/dist/scale.d.ts +259 -0
- package/dist/scale.js +133 -0
- package/dist/scroll-rail.d.ts +112 -0
- package/dist/scroll-rail.js +194 -0
- package/dist/scroll-state.d.ts +113 -0
- package/dist/scroll-state.js +55 -0
- package/dist/size.d.ts +47 -0
- package/dist/size.js +30 -0
- package/dist/styles/base.d.ts +38 -0
- package/dist/styles/body-font.d.ts +7 -0
- package/dist/styles/body-font.js +9 -0
- package/dist/styles/display-font.d.ts +7 -0
- package/dist/styles/display-font.js +9 -0
- package/dist/styles/recipes/body-face.d.ts +14 -0
- package/dist/styles/recipes/body-face.js +6 -0
- package/dist/styles/recipes/display-face.d.ts +23 -0
- package/dist/styles/recipes/display-face.js +10 -0
- package/dist/styles/recipes/field.d.ts +20 -0
- package/dist/styles/recipes/field.js +68 -0
- package/dist/styles/recipes/focus.d.ts +76 -0
- package/dist/styles/recipes/focus.js +39 -0
- package/dist/styles/recipes/host.d.ts +5 -0
- package/dist/styles/recipes/host.js +62 -0
- package/dist/styles/recipes/pattern.d.ts +80 -0
- package/dist/styles/recipes/pattern.js +82 -0
- package/dist/styles/recipes/scroll-rail.d.ts +69 -0
- package/dist/styles/recipes/scroll-rail.js +222 -0
- package/dist/styles/recipes/shadow.d.ts +13 -0
- package/dist/styles/recipes/shadow.js +5 -0
- package/dist/styles/recipes/static-text.d.ts +24 -0
- package/dist/styles/recipes/static-text.js +17 -0
- package/dist/styles/recipes/surface.d.ts +28 -0
- package/dist/styles/recipes/surface.js +27 -0
- package/dist/styles/recipes/tile.d.ts +122 -0
- package/dist/styles/recipes/tile.js +40 -0
- package/dist/styles/recipes/title-bar.d.ts +49 -0
- package/dist/styles/recipes/title-bar.js +134 -0
- package/dist/styles/recipes/toggle.d.ts +10 -0
- package/dist/styles/recipes/toggle.js +20 -0
- package/dist/styles/register-embedded-font.d.ts +64 -0
- package/dist/styles/register-embedded-font.js +30 -0
- package/dist/text-control.d.ts +131 -0
- package/dist/text-control.js +83 -0
- package/dist/tile-grid.d.ts +111 -0
- package/dist/tile-grid.js +53 -0
- package/dist/toggle-control.d.ts +60 -0
- package/dist/toggle-control.js +41 -0
- package/dist/track-width.d.ts +42 -0
- package/dist/track-width.js +30 -0
- package/dist/type-ahead.d.ts +35 -0
- package/dist/type-ahead.js +20 -0
- package/dist/zoom.d.ts +204 -0
- package/dist/zoom.js +105 -0
- package/docs/SPEC.md +2471 -0
- package/editor/vscode.html-custom-data.json +1320 -0
- package/editor/web-types.json +3237 -0
- package/package.json +118 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The embedded System 7 pointer set — GENERATED by `npm run embed:cursors`
|
|
3
|
+
* from `cursors/*.png`; edit the art there (or the hotspot/XOR manifest in
|
|
4
|
+
* `scripts/embed-cursor-art.mjs`) and re-run rather than editing this file.
|
|
5
|
+
*
|
|
6
|
+
* Like the bitmap faces, the art ships base64-embedded so the package needs
|
|
7
|
+
* no asset files: these are the defaults `applyCursor()` uses, exported so a
|
|
8
|
+
* consumer can pass one explicitly, remap it to another kind, or mix the set
|
|
9
|
+
* with art of their own. XOR art carries a `staticSrc` variant — the same
|
|
10
|
+
* ink under a one-pixel white halo — for the engines whose top layer cannot
|
|
11
|
+
* blend; supply `cursors/<name>-static.png` to replace the generated halo
|
|
12
|
+
* with hand-drawn art.
|
|
13
|
+
*/
|
|
14
|
+
import type { VfCursorArt } from './cursor.js';
|
|
15
|
+
/**
|
|
16
|
+
* The System 7 arrow. Hotspot on the black tip at (1,1), inside the
|
|
17
|
+
* white outline whose corner is (0,0); the outline is what keeps it legible
|
|
18
|
+
* on any background, so it draws as-is rather than with the XOR pen.
|
|
19
|
+
*/
|
|
20
|
+
export declare const CURSOR_ARROW: VfCursorArt;
|
|
21
|
+
/**
|
|
22
|
+
* The text-entry I-beam. Hotspot mid-stem, per the classic CURS
|
|
23
|
+
* resource; draws with the XOR pen, as System 7 drew it, with a white-halo
|
|
24
|
+
* static variant for the engines that cannot (see `staticSrc`).
|
|
25
|
+
*/
|
|
26
|
+
export declare const CURSOR_I_BEAM: VfCursorArt;
|
|
27
|
+
/**
|
|
28
|
+
* The crosshair. Hotspot on the intersection; draws with the XOR pen,
|
|
29
|
+
* as System 7 drew it, with a white-halo static variant for the engines
|
|
30
|
+
* that cannot (see `staticSrc`).
|
|
31
|
+
*/
|
|
32
|
+
export declare const CURSOR_CROSSHAIR: VfCursorArt;
|
|
33
|
+
/**
|
|
34
|
+
* The wristwatch, 8 frames of turning hand. Opaque white face, so it
|
|
35
|
+
* reads on any background drawn as-is.
|
|
36
|
+
*/
|
|
37
|
+
export declare const CURSOR_WAIT: VfCursorArt;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/cursor-art.ts
|
|
2
|
+
var e = {
|
|
3
|
+
src: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAQCAYAAADAvYV+AAAAS0lEQVR42q2SMQ4AMAgC/f+n20WTliowlNW7RA2xMuEkQU8o2BJOWAoIU6GDR2GCW4HBj6DgS2A7Y7o/6wNrSAXshoS/VcAuGK64ASXf4R9SU/euAAAAAElFTkSuQmCC",
|
|
4
|
+
width: 11,
|
|
5
|
+
height: 16,
|
|
6
|
+
hotspotX: 1,
|
|
7
|
+
hotspotY: 1
|
|
8
|
+
}, t = {
|
|
9
|
+
src: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAASCAYAAACJgPRIAAAAJElEQVR42mNgIAH8h2JCYnAJBlyS2BSOKhpaivBGMElJBSsAAPlIF+m2BB32AAAAAElFTkSuQmCC",
|
|
10
|
+
staticSrc: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAASCAYAAACJgPRIAAAAOklEQVR42mP4jwYYGBgYMMSgEv/R5ZDFGNAlkGlk0xmQrUBWwIANoJnGMKqIlorwRgtREUxyUsGV6AB8JyboJ0V9MQAAAABJRU5ErkJggg==",
|
|
11
|
+
width: 9,
|
|
12
|
+
height: 18,
|
|
13
|
+
hotspotX: 4,
|
|
14
|
+
hotspotY: 8,
|
|
15
|
+
invert: !0
|
|
16
|
+
}, n = {
|
|
17
|
+
src: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAHElEQVR42mNgoDH4P2oAHQ34TyYeDcSRYQBBAAA+6BrmCTIofgAAAABJRU5ErkJggg==",
|
|
18
|
+
staticSrc: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMElEQVR42mNgwAH+IwEGcgBU46gBxBjwHw8gIA1RA1NIDoYbQJELRqNxsBtATHYGAOqhCgWyPk7UAAAAAElFTkSuQmCC",
|
|
19
|
+
width: 16,
|
|
20
|
+
height: 16,
|
|
21
|
+
hotspotX: 7,
|
|
22
|
+
hotspotY: 7,
|
|
23
|
+
invert: !0
|
|
24
|
+
}, r = {
|
|
25
|
+
src: [
|
|
26
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAANElEQVR42mNgwA7+48BEgyFuwH9cgBhDsGoixRC6GfCfoAHIgUeWAeQGJMUGUByNIzEvAAD+3h7wGXnswAAAAABJRU5ErkJggg==",
|
|
27
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOUlEQVR42mNgwA7+48BEgyFuwH9cgBhD/hMChAzBq5FsA5DFCYUHNoXYvPCf/mFAlZggKS2MoLwAABZGIe0u7mf5AAAAAElFTkSuQmCC",
|
|
28
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMklEQVR42mNgwA7+48BEgyFuwH9cgBhD/hMChAyhmwH/CRqALRBJMmDAwoDiaByJeQEAXRsh7d6Ez7cAAAAASUVORK5CYII=",
|
|
29
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAANElEQVR42mNgwA7+48BEgyFuwH9cgBhD/hMChAyhmwH/CRqAHHhkGYCuiW5hQHE0jsS8AAC0GCTqSlaRPQAAAABJRU5ErkJggg==",
|
|
30
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAANklEQVR42mNgwA7+48BEgyFuwH9cgBhD/hMChAyhmwH/CRqAHHhkGYCsgSIvkGoAxdE4EvMCAIAUIe3WmwVMAAAAAElFTkSuQmCC",
|
|
31
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOUlEQVR42mNgwA7+48BEgyFuwH9cgBhD/hMChAyhmwH/CRqAHHhkGQBTTLYXcHmHopggKS2MoLwAAFw4Ie372GT7AAAAAElFTkSuQmCC",
|
|
32
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMklEQVR42mNgwA7+48BEgyFuwH9cgBhD/hMChAyhmwH/8RqAHngkGzCgYUBxNI7EvAAAwfwk6qpQGA8AAAAASUVORK5CYII=",
|
|
33
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAOElEQVR42mNgwA7+48BEgyFuwH9cgBhD/hMChAxBUUiRAbgMIRQe2BSSZwDFYUCTmCApLYygvAAAzn8h7Z3ZHmwAAAAASUVORK5CYII="
|
|
34
|
+
],
|
|
35
|
+
width: 16,
|
|
36
|
+
height: 16,
|
|
37
|
+
hotspotX: 8,
|
|
38
|
+
hotspotY: 8
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
export { e as CURSOR_ARROW, n as CURSOR_CROSSHAIR, t as CURSOR_I_BEAM, r as CURSOR_WAIT };
|
package/dist/cursor.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface VfCursorArt {
|
|
2
|
+
/** Frame URL(s), drawn at one image px = one system px; several animate. */
|
|
3
|
+
src: string | string[];
|
|
4
|
+
/** Art width in image px. */
|
|
5
|
+
width: number;
|
|
6
|
+
/** Art height in image px. */
|
|
7
|
+
height: number;
|
|
8
|
+
/** The art pixel that lands on the cell under the pointer (tip, stem, …). */
|
|
9
|
+
hotspotX?: number;
|
|
10
|
+
hotspotY?: number;
|
|
11
|
+
/** Draw with the XOR pen — per-pixel inversion of whatever is beneath. */
|
|
12
|
+
invert?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The variant an engine that cannot XOR draws as-is (stable Safari — see
|
|
15
|
+
* `XOR_UNAVAILABLE`): same box and hotspot as `src`, but carrying its own
|
|
16
|
+
* legibility, e.g. a white outline. Only consulted when `invert` is set;
|
|
17
|
+
* without one, such an engine draws `src` itself, un-inverted.
|
|
18
|
+
*/
|
|
19
|
+
staticSrc?: string | string[];
|
|
20
|
+
}
|
|
21
|
+
export type VfCursorKind = 'arrow' | 'text' | 'crosshair' | 'wait';
|
|
22
|
+
export interface VfCursorOptions {
|
|
23
|
+
/** The default pointer. Defaults to the embedded System 7 arrow. */
|
|
24
|
+
arrow?: VfCursorArt;
|
|
25
|
+
/** Over editable text. Defaults to the embedded I-beam; `null` disables. */
|
|
26
|
+
text?: VfCursorArt | null;
|
|
27
|
+
/** For `data-vf-cursor="crosshair"` claims. Defaults to the embedded
|
|
28
|
+
* crosshair; `null` disables. */
|
|
29
|
+
crosshair?: VfCursorArt | null;
|
|
30
|
+
/** While `aria-busy`. Defaults to the embedded wristwatch; `null`
|
|
31
|
+
* disables. */
|
|
32
|
+
wait?: VfCursorArt | null;
|
|
33
|
+
/**
|
|
34
|
+
* Element whose top-left corner anchors the system-pixel lattice the
|
|
35
|
+
* cursor snaps to. Defaults to the page's `vf-desktop` (else the document
|
|
36
|
+
* root), so on a faux desktop the cursor's pixels land on the same grid
|
|
37
|
+
* as the raster's dither.
|
|
38
|
+
*/
|
|
39
|
+
anchor?: HTMLElement;
|
|
40
|
+
/** ms per `wait` frame (default 250 — a full turn of 8 hands in 2s). */
|
|
41
|
+
waitFrameMs?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Replace the native pointer with the kit's system-pixel-locked cursor art
|
|
45
|
+
* (or the consumer's own, per kind). Strictly opt-in — call it once from
|
|
46
|
+
* your app. Returns a cleanup function that removes the overlay and
|
|
47
|
+
* restores the native pointer.
|
|
48
|
+
*/
|
|
49
|
+
export declare function applyCursor(options?: VfCursorOptions): () => void;
|
package/dist/cursor.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { truePixelRatio as e } from "./zoom.js";
|
|
2
|
+
import { effectiveScale as t, onScaleChange as n } from "./scale.js";
|
|
3
|
+
import { prefersReducedMotion as r } from "./motion.js";
|
|
4
|
+
import { VfModalDialog as i } from "./modal-dialog.js";
|
|
5
|
+
import { CURSOR_ARROW as a, CURSOR_CROSSHAIR as o, CURSOR_I_BEAM as s, CURSOR_WAIT as c } from "./cursor-art.js";
|
|
6
|
+
//#region src/cursor.ts
|
|
7
|
+
var l = [
|
|
8
|
+
"arrow",
|
|
9
|
+
"text",
|
|
10
|
+
"crosshair",
|
|
11
|
+
"wait"
|
|
12
|
+
], u = typeof navigator < "u" && (navigator.vendor ?? "").startsWith("Apple"), d = /* @__PURE__ */ new Set([
|
|
13
|
+
"text",
|
|
14
|
+
"password",
|
|
15
|
+
"search",
|
|
16
|
+
"email",
|
|
17
|
+
"url",
|
|
18
|
+
"tel",
|
|
19
|
+
"number"
|
|
20
|
+
]);
|
|
21
|
+
function f(e) {
|
|
22
|
+
return e instanceof HTMLTextAreaElement ? !e.disabled : e instanceof HTMLInputElement ? !e.disabled && d.has(e.type) : e instanceof HTMLElement && e.isContentEditable;
|
|
23
|
+
}
|
|
24
|
+
function p(e, t) {
|
|
25
|
+
let n = document.elementFromPoint(e, t);
|
|
26
|
+
for (; n?.shadowRoot;) {
|
|
27
|
+
let r = n.shadowRoot.elementFromPoint(e, t);
|
|
28
|
+
if (!r || r === n) break;
|
|
29
|
+
n = r;
|
|
30
|
+
}
|
|
31
|
+
return n;
|
|
32
|
+
}
|
|
33
|
+
function m(e) {
|
|
34
|
+
let t = [];
|
|
35
|
+
for (; e;) {
|
|
36
|
+
t.push(e);
|
|
37
|
+
let n = e.getRootNode();
|
|
38
|
+
e = e.parentElement ?? (n instanceof ShadowRoot ? n.host : null);
|
|
39
|
+
}
|
|
40
|
+
return t;
|
|
41
|
+
}
|
|
42
|
+
function h() {
|
|
43
|
+
for (let e of document.querySelectorAll("[aria-busy=\"true\"]")) if (e instanceof i && e.open) return !0;
|
|
44
|
+
return !1;
|
|
45
|
+
}
|
|
46
|
+
function g(d = {}) {
|
|
47
|
+
let { anchor: g = document.querySelector("vf-desktop") ?? document.documentElement, waitFrameMs: _ = 250, arrow: v = a, text: y = s, crosshair: b = o, wait: x = c } = d, S = {
|
|
48
|
+
arrow: v,
|
|
49
|
+
text: y,
|
|
50
|
+
crosshair: b,
|
|
51
|
+
wait: x
|
|
52
|
+
}, C = document.documentElement, w = new AbortController(), { signal: T } = w, E = document.createElement("style");
|
|
53
|
+
E.textContent = "* { cursor: none !important }";
|
|
54
|
+
let D = document.createElement("img");
|
|
55
|
+
D.alt = "", D.setAttribute("aria-hidden", "true");
|
|
56
|
+
let O = "showPopover" in D;
|
|
57
|
+
O && D.setAttribute("popover", "manual"), Object.assign(D.style, {
|
|
58
|
+
position: "fixed",
|
|
59
|
+
inset: "0 auto auto 0",
|
|
60
|
+
margin: "0",
|
|
61
|
+
padding: "0",
|
|
62
|
+
border: "0",
|
|
63
|
+
background: "transparent",
|
|
64
|
+
overflow: "visible",
|
|
65
|
+
pointerEvents: "none",
|
|
66
|
+
imageRendering: "pixelated",
|
|
67
|
+
visibility: "hidden",
|
|
68
|
+
zIndex: "2147483647"
|
|
69
|
+
});
|
|
70
|
+
let k = /* @__PURE__ */ new Map();
|
|
71
|
+
for (let e of l) {
|
|
72
|
+
let t = S[e];
|
|
73
|
+
if (!t) continue;
|
|
74
|
+
let n = t.invert && u ? t.staticSrc ?? t.src : t.src, r = Array.isArray(n) ? n : [n], i = r.map((e) => {
|
|
75
|
+
let t = new Image();
|
|
76
|
+
return t.src = e, t;
|
|
77
|
+
});
|
|
78
|
+
k.set(e, {
|
|
79
|
+
art: t,
|
|
80
|
+
frames: r,
|
|
81
|
+
xor: !!t.invert && !u,
|
|
82
|
+
images: i,
|
|
83
|
+
ready: !1
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
let A = (e) => Promise.all(e.images.map((e) => e.decode())).then(() => {
|
|
87
|
+
e.ready = !0;
|
|
88
|
+
}), j = 1, M = 3, N = 0, P = 0, F = -1, I = -1, L = "arrow", R = k.get("arrow"), z = 0, B = 0, V = () => {
|
|
89
|
+
if (F < 0) return;
|
|
90
|
+
let { hotspotX: e = 0, hotspotY: t = 0 } = R.art, n = Math.floor((F * j - N) / M), r = Math.floor((I * j - P) / M), i = (N + (n - e) * M) / j, a = (P + (r - t) * M) / j;
|
|
91
|
+
D.style.transform = `translate(${i}px, ${a}px)`;
|
|
92
|
+
}, H = () => {
|
|
93
|
+
j = e() || 1, M = Math.max(1, Math.round(t(g) * j));
|
|
94
|
+
let n = g.getBoundingClientRect();
|
|
95
|
+
N = Math.round(n.left * j), P = Math.round(n.top * j), D.style.width = `${R.art.width * M / j}px`, D.style.height = `${R.art.height * M / j}px`, V();
|
|
96
|
+
}, U = (e) => {
|
|
97
|
+
let t = k.get(e), n = t?.ready ? t : k.get("arrow");
|
|
98
|
+
(e !== L || n !== R) && (L = e, R = n, D.dataset.kind = e, window.clearInterval(B), z = 0, D.src = R.frames[0], D.style.filter = R.xor ? "invert(1)" : "", D.style.mixBlendMode = R.xor ? "difference" : "", R.frames.length > 1 && !r() && (B = window.setInterval(() => {
|
|
99
|
+
z = (z + 1) % R.frames.length, D.src = R.frames[z];
|
|
100
|
+
}, _)), H());
|
|
101
|
+
}, W = () => {
|
|
102
|
+
if (h()) return "wait";
|
|
103
|
+
let e = m(p(F, I));
|
|
104
|
+
if (e.some((e) => e.getAttribute("aria-busy") === "true")) return "wait";
|
|
105
|
+
for (let t of e) {
|
|
106
|
+
let e = t.getAttribute("data-vf-cursor");
|
|
107
|
+
if (e && l.includes(e)) return e;
|
|
108
|
+
}
|
|
109
|
+
return e[0] && f(e[0]) ? "text" : "arrow";
|
|
110
|
+
}, G = () => {
|
|
111
|
+
F < 0 || U(W());
|
|
112
|
+
}, K = 0, q = () => {
|
|
113
|
+
cancelAnimationFrame(K), K = requestAnimationFrame(() => {
|
|
114
|
+
K = requestAnimationFrame(H);
|
|
115
|
+
});
|
|
116
|
+
}, J = () => {
|
|
117
|
+
if (!(!O || !D.isConnected)) try {
|
|
118
|
+
D.hidePopover(), D.showPopover();
|
|
119
|
+
} catch {}
|
|
120
|
+
}, Y = new MutationObserver((e) => {
|
|
121
|
+
for (let t of e) {
|
|
122
|
+
let e = t.target;
|
|
123
|
+
if (t.attributeName === "open" && e instanceof i && e.open) {
|
|
124
|
+
J();
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
G();
|
|
129
|
+
}), X = () => {
|
|
130
|
+
D.style.visibility = "hidden";
|
|
131
|
+
}, Z = (e) => {
|
|
132
|
+
if (e.pointerType === "touch") {
|
|
133
|
+
X();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
F = e.clientX, I = e.clientY, G(), V(), D.style.visibility = "visible";
|
|
137
|
+
}, Q = !1, $ = () => {
|
|
138
|
+
if (Q) return;
|
|
139
|
+
document.body.append(D), O && D.showPopover(), D.dataset.kind = "arrow", H(), document.head.append(E), C.style.setProperty("--vf-cursor", "none");
|
|
140
|
+
let e = {
|
|
141
|
+
capture: !0,
|
|
142
|
+
passive: !0,
|
|
143
|
+
signal: T
|
|
144
|
+
};
|
|
145
|
+
window.addEventListener("pointermove", Z, e), window.addEventListener("pointerdown", Z, e), window.addEventListener("pointercancel", X, e), document.addEventListener("mouseleave", X, { signal: T }), window.addEventListener("blur", X, { signal: T }), document.addEventListener("visibilitychange", () => {
|
|
146
|
+
document.hidden && X();
|
|
147
|
+
}, { signal: T }), window.addEventListener("resize", q, { signal: T }), Y.observe(document.body, {
|
|
148
|
+
subtree: !0,
|
|
149
|
+
attributes: !0,
|
|
150
|
+
attributeFilter: [
|
|
151
|
+
"open",
|
|
152
|
+
"aria-busy",
|
|
153
|
+
"data-vf-cursor"
|
|
154
|
+
]
|
|
155
|
+
});
|
|
156
|
+
}, ee = n(q);
|
|
157
|
+
A(k.get("arrow")).then(() => (D.src = R.frames[0], D.decode())).then($, () => {
|
|
158
|
+
console.warn("applyCursor: could not load the arrow art; keeping the native pointer");
|
|
159
|
+
});
|
|
160
|
+
for (let [e, t] of k) e !== "arrow" && A(t).then(() => G(), () => {
|
|
161
|
+
console.warn(`applyCursor: could not load the "${e}" art; it will fall back to the arrow`);
|
|
162
|
+
});
|
|
163
|
+
return () => {
|
|
164
|
+
if (Q = !0, w.abort(), Y.disconnect(), ee(), cancelAnimationFrame(K), window.clearInterval(B), O && D.isConnected) try {
|
|
165
|
+
D.hidePopover();
|
|
166
|
+
} catch {}
|
|
167
|
+
D.remove(), E.remove(), C.style.removeProperty("--vf-cursor");
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
171
|
+
export { g as applyCursor };
|
package/dist/define.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element registration, guarded against a second copy of the library.
|
|
3
|
+
*
|
|
4
|
+
* `customElements.define()` throws `NotSupportedError` if the name is taken,
|
|
5
|
+
* and an uncaught throw during module evaluation takes the whole import graph
|
|
6
|
+
* with it — one duplicated dependency and the consumer's page is blank. That is
|
|
7
|
+
* a plausible thing to happen by accident: a bundle that loads the kit
|
|
8
|
+
* alongside a micro-frontend that also bundles it, an app pinned to two
|
|
9
|
+
* versions through a nested dependency, a `<script type="module">` on a page
|
|
10
|
+
* whose framework already imported the package.
|
|
11
|
+
*
|
|
12
|
+
* So registration checks first and warns instead. The first copy wins — it owns
|
|
13
|
+
* the tag and the elements already upgraded against it — and the page keeps
|
|
14
|
+
* working. The warning matters as much as the guard, because a duplicated copy
|
|
15
|
+
* is not benign even once the page survives it: the scaling, grid-snapping and
|
|
16
|
+
* focus-modality registries are all module-scoped singletons, so the second
|
|
17
|
+
* copy's `applyGridSnap()` reaches none of the first copy's components and the
|
|
18
|
+
* two disagree silently. Better to say so than to let it look fine.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Register `ctor` under `tagName` unless the name is already taken.
|
|
22
|
+
*
|
|
23
|
+
* Re-registering the identical constructor (the same module evaluated twice) is
|
|
24
|
+
* a silent no-op; a *different* constructor warns and leaves the incumbent in
|
|
25
|
+
* place. Use it directly when you define a control without the decorator:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* defineElement('my-control', MyControl)
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare const defineElement: (tagName: string, ctor: CustomElementConstructor) => void;
|
|
32
|
+
/**
|
|
33
|
+
* The kit's replacement for Lit's `@customElement`, registering through
|
|
34
|
+
* {@link defineElement} so a duplicated copy warns rather than throwing.
|
|
35
|
+
* Identical to use:
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* @vfElement('my-control')
|
|
39
|
+
* export class MyControl extends LitElement {}
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* Handles both decorator dialects the way Lit's own does, so it keeps working
|
|
43
|
+
* if the project moves off `experimentalDecorators`.
|
|
44
|
+
*/
|
|
45
|
+
export declare const vfElement: (tagName: string) => <T extends CustomElementConstructor>(classOrTarget: T, context?: ClassDecoratorContext<T>) => T;
|
package/dist/define.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/define.ts
|
|
2
|
+
var e = (e, t) => {
|
|
3
|
+
let n = customElements.get(e);
|
|
4
|
+
if (n !== t) {
|
|
5
|
+
if (n !== void 0) {
|
|
6
|
+
console.warn(`[vintage-frames] <${e}> is already registered on this page, so this second registration was skipped and the first one kept. Two copies of the library also mean two of every module-scoped singleton — applyGridSnap(), applyScale() and the focus-modality tracker each only reach the components built from their own copy. Dedupe vintage-frames to a single version.`);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
customElements.define(e, t);
|
|
10
|
+
}
|
|
11
|
+
}, t = (t) => (n, r) => (r === void 0 ? e(t, n) : r.addInitializer(() => {
|
|
12
|
+
e(t, n);
|
|
13
|
+
}), n);
|
|
14
|
+
//#endregion
|
|
15
|
+
export { e as defineElement, t as vfElement };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* One managed listener: target, event type, handler, and capture/options —
|
|
4
|
+
* exactly the arguments the add/remove pair is made with. The handler's event
|
|
5
|
+
* parameter is typed `never` (the bottom type) so a handler narrowed to its
|
|
6
|
+
* real event (`(event: PointerEvent) => void`) is accepted without a cast;
|
|
7
|
+
* the single widening cast lives inside the controller.
|
|
8
|
+
*/
|
|
9
|
+
export type DocumentListenerSpec = readonly [
|
|
10
|
+
target: EventTarget,
|
|
11
|
+
type: string,
|
|
12
|
+
handler: (event: never) => void,
|
|
13
|
+
options?: boolean | AddEventListenerOptions
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Document-level listeners scoped to a transient state — an open panel, an
|
|
17
|
+
* in-flight gesture.
|
|
18
|
+
*
|
|
19
|
+
* The kit's transient surfaces (`vf-menu`, `vf-menu-bar`, `vf-select`) listen
|
|
20
|
+
* outside their own shadow root while open: capture-phase `pointerdown` for
|
|
21
|
+
* outside dismissal, `scroll`/`resize` to keep a fixed-position panel honest,
|
|
22
|
+
* document-wide keyboard navigation, a press gesture tracked to its release
|
|
23
|
+
* anywhere on the page. Each of them hand-rolled the same bookkeeping —
|
|
24
|
+
* add/remove calls that must mirror each other exactly, an attached guard
|
|
25
|
+
* making both idempotent, and a disconnect cleanup so a host torn down
|
|
26
|
+
* mid-open can't leak listeners onto the document. This controller owns that
|
|
27
|
+
* bookkeeping; the handlers, and when to {@link attach}, stay the host's.
|
|
28
|
+
*
|
|
29
|
+
* The spec list is a thunk, evaluated at attach time, so a controller field
|
|
30
|
+
* may sit above the handler fields it references without capturing their
|
|
31
|
+
* pre-initialization `undefined`. {@link detach} removes exactly the specs
|
|
32
|
+
* the last {@link attach} added.
|
|
33
|
+
*
|
|
34
|
+
* Listeners are detached on host disconnect and are NOT re-attached on
|
|
35
|
+
* reconnect: the state that warranted them (an open panel, a pressed pointer)
|
|
36
|
+
* is itself torn down by the host on disconnect.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Runs `release` once the in-flight press gesture is over — its trailing
|
|
40
|
+
* `click`, wherever that lands, or a `pointercancel` if no click ever comes.
|
|
41
|
+
*
|
|
42
|
+
* A swallow-click latch armed on `pointerdown` cannot be cleared by the
|
|
43
|
+
* element's own click handler alone: per UI Events, the click after a
|
|
44
|
+
* press-drag-release is dispatched at the nearest common inclusive ancestor of
|
|
45
|
+
* the pointerdown and pointerup targets, so a press that releases elsewhere
|
|
46
|
+
* lands it *above* the pressed element, the clearing handler never runs, and
|
|
47
|
+
* the stuck latch silently swallows the next synthetic (assistive-tech)
|
|
48
|
+
* click. One-shot and bubble-phase, so the element's own click handler — the
|
|
49
|
+
* swallow itself — always runs first.
|
|
50
|
+
*/
|
|
51
|
+
export declare function releaseAfterGesture(release: () => void): void;
|
|
52
|
+
export declare class DocumentListenersController implements ReactiveController {
|
|
53
|
+
private readonly listeners;
|
|
54
|
+
/** The specs currently attached; null while detached. */
|
|
55
|
+
private active;
|
|
56
|
+
constructor(host: ReactiveControllerHost, listeners: () => readonly DocumentListenerSpec[]);
|
|
57
|
+
/** Add every listener. A no-op while already attached. */
|
|
58
|
+
attach(): void;
|
|
59
|
+
/** Remove every listener the last {@link attach} added. A no-op while detached. */
|
|
60
|
+
detach(): void;
|
|
61
|
+
hostDisconnected(): void;
|
|
62
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/document-listeners.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
let t = () => {
|
|
4
|
+
document.removeEventListener("click", t), document.removeEventListener("pointercancel", t), e();
|
|
5
|
+
};
|
|
6
|
+
document.addEventListener("click", t), document.addEventListener("pointercancel", t);
|
|
7
|
+
}
|
|
8
|
+
var t = class {
|
|
9
|
+
constructor(e, t) {
|
|
10
|
+
this.listeners = t, this.active = null, e.addController(this);
|
|
11
|
+
}
|
|
12
|
+
attach() {
|
|
13
|
+
if (!this.active) {
|
|
14
|
+
this.active = this.listeners();
|
|
15
|
+
for (let [e, t, n, r] of this.active) e.addEventListener(t, n, r);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
detach() {
|
|
19
|
+
if (this.active) {
|
|
20
|
+
for (let [e, t, n, r] of this.active) e.removeEventListener(t, n, r);
|
|
21
|
+
this.active = null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
hostDisconnected() {
|
|
25
|
+
this.detach();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { t as DocumentListenersController, e as releaseAfterGesture };
|
package/dist/drag.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* The moving side of a pointer drag. A component supplies these callbacks; the
|
|
4
|
+
* {@link DragController} owns the pointer bookkeeping (which button, capture,
|
|
5
|
+
* delta tracking, system-pixel snapping) so window, dialog and icon dragging
|
|
6
|
+
* stay identical.
|
|
7
|
+
*
|
|
8
|
+
* Every coordinate here is in **system px** — the art's own unit, and the unit
|
|
9
|
+
* a placement is stored in, so that a dropped host is still where it was
|
|
10
|
+
* dropped after a zoom. The controller does the one conversion: pointer events
|
|
11
|
+
* arrive in CSS px, and only the *delta* is converted.
|
|
12
|
+
*/
|
|
13
|
+
export interface DragTarget {
|
|
14
|
+
/**
|
|
15
|
+
* A press landed on the drag handle. Return the origin in system px, the
|
|
16
|
+
* space {@link onDrag} writes back in, to begin the drag — or `null` to
|
|
17
|
+
* ignore this press (wrong button, landed on a widget, the host isn't
|
|
18
|
+
* movable). Seed any positioning state here before returning the origin.
|
|
19
|
+
*/
|
|
20
|
+
onDragStart(event: PointerEvent): {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
} | null;
|
|
24
|
+
/** Apply a moved origin, in system px, already snapped onto the lattice. */
|
|
25
|
+
onDrag(x: number, y: number): void;
|
|
26
|
+
/** Optional: the drag ended (pointer released or cancelled). */
|
|
27
|
+
onDragEnd?(): void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Drag-to-move for 1-bit chrome. Bind {@link onPointerDown} / {@link onPointerMove}
|
|
31
|
+
* / {@link onPointerUp} to a handle element (`vf-window`'s title bar,
|
|
32
|
+
* `vf-dialog`'s title bar); the controller captures the pointer on that handle,
|
|
33
|
+
* tracks the delta from the press point, and hands the {@link DragTarget} a new
|
|
34
|
+
* origin in system px, snapped onto the placement lattice — whole art pixels,
|
|
35
|
+
* the way QuickDraw moved windows, which is also what keeps the pixel art
|
|
36
|
+
* inside fringe-free (see {@link snapSys}). The target decides how to apply
|
|
37
|
+
* it; all three write it through `top`/`left` (`src/position.ts`).
|
|
38
|
+
*/
|
|
39
|
+
export declare class DragController implements ReactiveController {
|
|
40
|
+
#private;
|
|
41
|
+
private readonly host;
|
|
42
|
+
private readonly target;
|
|
43
|
+
constructor(host: ReactiveControllerHost & Element, target: DragTarget);
|
|
44
|
+
/** Abandon an in-flight drag if the host is torn down mid-gesture. */
|
|
45
|
+
hostDisconnected(): void;
|
|
46
|
+
onPointerDown: (event: PointerEvent) => void;
|
|
47
|
+
onPointerMove: (event: PointerEvent) => void;
|
|
48
|
+
onPointerUp: (event: PointerEvent) => void;
|
|
49
|
+
}
|
package/dist/drag.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { effectiveScale as e, snapSys as t } from "./scale.js";
|
|
2
|
+
//#region src/drag.ts
|
|
3
|
+
var n = class {
|
|
4
|
+
#e;
|
|
5
|
+
#t;
|
|
6
|
+
#n;
|
|
7
|
+
#r;
|
|
8
|
+
#i;
|
|
9
|
+
constructor(n, r) {
|
|
10
|
+
this.host = n, this.target = r, this.#e = null, this.#t = 0, this.#n = 0, this.#r = 0, this.#i = 0, this.onPointerDown = (e) => {
|
|
11
|
+
if (this.#e !== null) return;
|
|
12
|
+
let t = this.target.onDragStart(e);
|
|
13
|
+
t && (this.#e = e.pointerId, this.#t = e.clientX, this.#n = e.clientY, this.#r = t.x, this.#i = t.y, e.currentTarget.setPointerCapture(e.pointerId), e.preventDefault());
|
|
14
|
+
}, this.onPointerMove = (n) => {
|
|
15
|
+
if (n.pointerId !== this.#e) return;
|
|
16
|
+
let r = e(this.host);
|
|
17
|
+
this.target.onDrag(t(this.#r + (n.clientX - this.#t) / r, this.host), t(this.#i + (n.clientY - this.#n) / r, this.host));
|
|
18
|
+
}, this.onPointerUp = (e) => {
|
|
19
|
+
if (e.pointerId !== this.#e) return;
|
|
20
|
+
this.#e = null;
|
|
21
|
+
let t = e.currentTarget;
|
|
22
|
+
t.hasPointerCapture(e.pointerId) && t.releasePointerCapture(e.pointerId), this.target.onDragEnd?.();
|
|
23
|
+
}, n.addController(this);
|
|
24
|
+
}
|
|
25
|
+
hostDisconnected() {
|
|
26
|
+
this.#e = null;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
//#endregion
|
|
30
|
+
export { n as DragController };
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch a `vf-*` CustomEvent following the kit's SPEC §4 convention: every
|
|
3
|
+
* public component event bubbles and crosses shadow roots (`bubbles: true`,
|
|
4
|
+
* `composed: true`) so an ancestor listener in the light DOM can hear it.
|
|
5
|
+
*
|
|
6
|
+
* Centralises the boilerplate that was hand-rolled at 30+ call sites (and in
|
|
7
|
+
* three near-identical private `#emit`/`_emit` wrappers). Returns
|
|
8
|
+
* `dispatchEvent`'s result — `false` when a `cancelable` event was canceled
|
|
9
|
+
* (used by the internal `vf-menu-toggle-request` handshake), otherwise `true`.
|
|
10
|
+
*
|
|
11
|
+
* The *internal* coordination events (the menu handshakes, the list-item
|
|
12
|
+
* disabled notice) pass `composed: false`: parent and child share one light
|
|
13
|
+
* tree, so crossing shadow roots buys them nothing — and leaking a private,
|
|
14
|
+
* cancelable protocol out of a consumer's own shadow boundary hands their
|
|
15
|
+
* delegated listeners an event they can break the kit by cancelling.
|
|
16
|
+
*
|
|
17
|
+
* @param host element to dispatch from (the event's `target`)
|
|
18
|
+
* @param name event type, e.g. `'vf-change'`
|
|
19
|
+
* @param detail optional `event.detail` payload
|
|
20
|
+
* @param options set `cancelable` to allow a listener to `preventDefault()`;
|
|
21
|
+
* set `composed: false` to keep an internal event inside the
|
|
22
|
+
* dispatching tree
|
|
23
|
+
*/
|
|
24
|
+
export declare function emit<T = unknown>(host: EventTarget, name: string, detail?: T, options?: {
|
|
25
|
+
cancelable?: boolean;
|
|
26
|
+
composed?: boolean;
|
|
27
|
+
}): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Dispatch the *native* `input`/`change` event a form control's host owes the
|
|
30
|
+
* platform, alongside the kit's `vf-input`/`vf-change`.
|
|
31
|
+
*
|
|
32
|
+
* A form-associated custom element that contributes to `FormData` but never
|
|
33
|
+
* fires `change` is half a native control: `form.addEventListener('change')`
|
|
34
|
+
* delegation hears nothing, and framework bindings (React's `onChange`, Vue's
|
|
35
|
+
* `v-model`) have nothing to bind to. Each control calls this from its **user
|
|
36
|
+
* interaction** paths only — a programmatic `value` set fires nothing, exactly
|
|
37
|
+
* as a native control fires nothing.
|
|
38
|
+
*
|
|
39
|
+
* The flags mirror the platform's own: both bubble; `input` is composed and
|
|
40
|
+
* `change` is not (UI Events). That composed split is also why the kit's
|
|
41
|
+
* *fields* only bridge `change`: their inner native control's own `input`
|
|
42
|
+
* already crosses the shadow boundary and retargets to the host, so
|
|
43
|
+
* re-dispatching it would double-fire every keystroke.
|
|
44
|
+
*/
|
|
45
|
+
export declare function emitNative(host: EventTarget, type: 'input' | 'change'): void;
|
|
46
|
+
/**
|
|
47
|
+
* Run a control's **activation behavior** the way HTML runs a native one's: at
|
|
48
|
+
* the *end* of the click's propagation, and not at all if anything on the path
|
|
49
|
+
* called `preventDefault()`.
|
|
50
|
+
*
|
|
51
|
+
* This is what makes `preventDefault()` on a native checkbox — or on any
|
|
52
|
+
* ancestor — actually stop it from checking. A handler that acts where it sits
|
|
53
|
+
* cannot reproduce it: a listener on the control (or, worse, one registered in
|
|
54
|
+
* the constructor, which is first in the host's own listener list) beats every
|
|
55
|
+
* listener a consumer can write, so only a capture-phase cancel ever landed —
|
|
56
|
+
* and even that needs someone to *read* `defaultPrevented`, which is the half
|
|
57
|
+
* that is easy to forget.
|
|
58
|
+
*
|
|
59
|
+
* The deferral works because each node's listener list is read as that node is
|
|
60
|
+
* reached, so a listener added to the window *during* dispatch still runs when
|
|
61
|
+
* the event gets there — by which point `defaultPrevented` is final. The one
|
|
62
|
+
* ordering HTML has that this doesn't: a window listener registered before this
|
|
63
|
+
* one still runs before the action.
|
|
64
|
+
*
|
|
65
|
+
* `stopPropagation()` cancels nothing in HTML — a native button still submits —
|
|
66
|
+
* but it does stop the event ever reaching the window, so a task picks the
|
|
67
|
+
* action up in that case. A task and **never** a microtask: microtasks
|
|
68
|
+
* interleave *between* the listeners of a trusted dispatch, which would put the
|
|
69
|
+
* action back before the path is done.
|
|
70
|
+
*
|
|
71
|
+
* Callers keep their own guards (disabled, re-entrancy, gesture latches) — this
|
|
72
|
+
* owns the timing alone. `vf-button` runs the same shape inline, with the
|
|
73
|
+
* submit proxy's re-entrancy guard woven through it.
|
|
74
|
+
*
|
|
75
|
+
* @param host the element whose ownerDocument supplies the window to defer on
|
|
76
|
+
* @param event the click being deferred; its `defaultPrevented` is the verdict
|
|
77
|
+
* @param action run once, at the end of the path, unless cancelled
|
|
78
|
+
*/
|
|
79
|
+
export declare function deferActivation(host: Element, event: Event, action: () => void): void;
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/events.ts
|
|
2
|
+
function e(e, t, n, r) {
|
|
3
|
+
return e.dispatchEvent(new CustomEvent(t, {
|
|
4
|
+
detail: n,
|
|
5
|
+
bubbles: !0,
|
|
6
|
+
composed: r?.composed ?? !0,
|
|
7
|
+
cancelable: r?.cancelable ?? !1
|
|
8
|
+
}));
|
|
9
|
+
}
|
|
10
|
+
function t(e, t) {
|
|
11
|
+
e.dispatchEvent(new Event(t, {
|
|
12
|
+
bubbles: !0,
|
|
13
|
+
composed: t === "input"
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
function n(e, t, n) {
|
|
17
|
+
let r = e.ownerDocument.defaultView;
|
|
18
|
+
if (!r) return;
|
|
19
|
+
let i = 0, a = () => {
|
|
20
|
+
r.removeEventListener("click", a), r.clearTimeout(i), !t.defaultPrevented && n();
|
|
21
|
+
};
|
|
22
|
+
r.addEventListener("click", a), i = r.setTimeout(a, 0);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { n as deferActivation, e as emit, t as emitNative };
|