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,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pixel-exact stepped silhouettes for variable-size 1-bit chrome — the
|
|
3
|
+
* `clip-path` analog of the fixed-size sprite glyphs in `glyphs.ts`.
|
|
4
|
+
*
|
|
5
|
+
* A System 7 rounded rectangle is not a curve: it is a staircase of whole
|
|
6
|
+
* pixels. Native `border-radius` renders those corners as antialiased arcs,
|
|
7
|
+
* which is exactly the gray smudge the kit forbids. Instead we author each
|
|
8
|
+
* silhouette as a *corner trace* — the per-row left inset of the shape, in
|
|
9
|
+
* system pixels, read straight off the reference sheet
|
|
10
|
+
* read off the 1x button reference sheet — and compile it into a
|
|
11
|
+
* stepped `polygon()` whose every vertex is a `calc(var(--vf-scale, 1) * Npx)`
|
|
12
|
+
* multiple. Under display scaling each system pixel is a whole number of
|
|
13
|
+
* device pixels, so every edge lands on the device grid and nothing
|
|
14
|
+
* antialiases; the element's own `background` supplies the color, so the
|
|
15
|
+
* result stays `var(--vf-*)`-themeable like everything else (SPEC §1).
|
|
16
|
+
*
|
|
17
|
+
* Corners are anchored to their own edges (left/right via `100% - …`), so one
|
|
18
|
+
* polygon fits any width and height: the straight runs stretch, the traced
|
|
19
|
+
* corners never distort — the CSS equivalent of a 9-slice.
|
|
20
|
+
*
|
|
21
|
+
* A frame is drawn as a *difference of silhouettes*, exactly like QuickDraw's
|
|
22
|
+
* `FrameRoundRect`: paint the outer silhouette in the frame color, then paint
|
|
23
|
+
* the face silhouette (inset one pixel, with its own traced corners) on top.
|
|
24
|
+
* The 1px outline — including the 2px-wide diagonal step pixels at the
|
|
25
|
+
* corners — is simply the region the face doesn't cover. The reference's
|
|
26
|
+
* border pixels reproduce exactly; nothing is stroked.
|
|
27
|
+
*/
|
|
28
|
+
/** A stepped silhouette: per-row corner insets traced from the reference. */
|
|
29
|
+
export interface SteppedProfile {
|
|
30
|
+
/**
|
|
31
|
+
* Left inset (system px) of each corner row, topmost first. Mirrored to all
|
|
32
|
+
* four corners; must be non-increasing (each row steps outward).
|
|
33
|
+
*/
|
|
34
|
+
readonly corner: readonly number[];
|
|
35
|
+
/** Inset of the straight edges once the corner rows are exhausted. */
|
|
36
|
+
readonly edge: number;
|
|
37
|
+
/** Row (system px from the box top) where the silhouette begins. */
|
|
38
|
+
readonly start: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The push button's outer silhouette. Reference rows (80×20 sample, pressed
|
|
42
|
+
* state is solid ink so the trace is unambiguous): y0 inset 3, y1–y2 inset 1,
|
|
43
|
+
* then straight. Identical on the short row-3 buttons, so it holds for any
|
|
44
|
+
* control height.
|
|
45
|
+
*/
|
|
46
|
+
export declare const BUTTON_FRAME: SteppedProfile;
|
|
47
|
+
/**
|
|
48
|
+
* The button's face (the white fill inside the 1px frame). Starts one row
|
|
49
|
+
* down: y1 from x3, y2 from x2, then 1px inside the straight edges. Painting
|
|
50
|
+
* this over {@link BUTTON_FRAME} leaves the reference's exact border pixels —
|
|
51
|
+
* row 0 full-width, `##`/`#` corner steps, 1px sides.
|
|
52
|
+
*/
|
|
53
|
+
export declare const BUTTON_FACE: SteppedProfile;
|
|
54
|
+
/**
|
|
55
|
+
* Outer silhouette of the default-button ring. The reference ring box is the
|
|
56
|
+
* button box outset by {@link RING_INSET} on every side (88×28 around 80×20):
|
|
57
|
+
* y0 inset 5, y1 3, y2 2, y3–y4 1, then straight.
|
|
58
|
+
*/
|
|
59
|
+
export declare const RING_FRAME: SteppedProfile;
|
|
60
|
+
/**
|
|
61
|
+
* The transparent hole inside the ring. The band is 3px thick: the hole opens
|
|
62
|
+
* at row 3 (inset 6, then 4, 4, then 3px inside the straight edges). Kept as
|
|
63
|
+
* a hole — not painted over — so the 1px gap between ring and button shows
|
|
64
|
+
* the surface behind, exactly like the sheet's alpha-0 gap pixels.
|
|
65
|
+
*/
|
|
66
|
+
export declare const RING_HOLE: SteppedProfile;
|
|
67
|
+
/** How far the ring box outsets the button box on each side (system px). */
|
|
68
|
+
export declare const RING_INSET = 4;
|
|
69
|
+
/**
|
|
70
|
+
* The screen-corner mask: per-row width (system px) of the black staircase a
|
|
71
|
+
* compact Mac's ROM painted over the rounded corners of its CRT — what makes
|
|
72
|
+
* the System 7 menu bar's top corners read as rounded. Traced from a 1x
|
|
73
|
+
* screen capture (5×5 corner crop): y0 run 5, y1 3, y2 2, y3–y4 1. The same
|
|
74
|
+
* radius-5 staircase as {@link RING_FRAME}'s corner — QuickDraw had one
|
|
75
|
+
* vocabulary for round corners — but the opposite *object*: this is the ink
|
|
76
|
+
* outside the silhouette, not the silhouette, so it compiles through
|
|
77
|
+
* {@link steppedCornerClip} rather than the rect compilers.
|
|
78
|
+
*/
|
|
79
|
+
export declare const SCREEN_CORNER: readonly number[];
|
|
80
|
+
/** Compile a profile into a stepped `polygon()` clip-path value. */
|
|
81
|
+
export declare const steppedRectClip: (p: SteppedProfile) => string;
|
|
82
|
+
/**
|
|
83
|
+
* Compile a corner-mask run list into the ink polygon for one *top* corner —
|
|
84
|
+
* the corner box minus the rounded silhouette, i.e. the region the screen
|
|
85
|
+
* mask blacked out. Runs are the mask's per-row width, topmost first; `side`
|
|
86
|
+
* mirrors the staircase (via `100% - …`) for the top-right corner. Clip an
|
|
87
|
+
* element `runs[0]` wide × `runs.length` tall (system px), anchored into its
|
|
88
|
+
* corner, and let its `background` supply the ink.
|
|
89
|
+
*/
|
|
90
|
+
export declare const steppedCornerClip: (runs: readonly number[], side: "left" | "right") => string;
|
|
91
|
+
/**
|
|
92
|
+
* Compile an outer profile and a hole profile into a ring (a stepped donut).
|
|
93
|
+
*
|
|
94
|
+
* `polygon()` has no subpaths, so the hole uses the keyhole technique: walk
|
|
95
|
+
* the outer loop clockwise from the top-center, bridge straight down to the
|
|
96
|
+
* hole's top edge, walk the hole counter-clockwise, and bridge back up. The
|
|
97
|
+
* two bridge edges are coincident verticals at x 50%, so with the `evenodd`
|
|
98
|
+
* fill rule they cancel exactly — no visible seam — and the enclosed hole
|
|
99
|
+
* (crossed twice) stays transparent, keeping the classic gap between the
|
|
100
|
+
* default ring and the button see-through.
|
|
101
|
+
*/
|
|
102
|
+
export declare const steppedRingClip: (outer: SteppedProfile, hole: SteppedProfile) => string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//#region src/pixel-frame.ts
|
|
2
|
+
var e = {
|
|
3
|
+
corner: [
|
|
4
|
+
3,
|
|
5
|
+
1,
|
|
6
|
+
1
|
|
7
|
+
],
|
|
8
|
+
edge: 0,
|
|
9
|
+
start: 0
|
|
10
|
+
}, t = {
|
|
11
|
+
corner: [3, 2],
|
|
12
|
+
edge: 1,
|
|
13
|
+
start: 1
|
|
14
|
+
}, n = {
|
|
15
|
+
corner: [
|
|
16
|
+
5,
|
|
17
|
+
3,
|
|
18
|
+
2,
|
|
19
|
+
1,
|
|
20
|
+
1
|
|
21
|
+
],
|
|
22
|
+
edge: 0,
|
|
23
|
+
start: 0
|
|
24
|
+
}, r = {
|
|
25
|
+
corner: [
|
|
26
|
+
6,
|
|
27
|
+
4,
|
|
28
|
+
4
|
|
29
|
+
],
|
|
30
|
+
edge: 3,
|
|
31
|
+
start: 3
|
|
32
|
+
}, i = 4, a = [
|
|
33
|
+
5,
|
|
34
|
+
3,
|
|
35
|
+
2,
|
|
36
|
+
1,
|
|
37
|
+
1
|
|
38
|
+
], o = (e) => e === 0 ? "0" : `calc(var(--vf-scale, 1) * ${e}px)`, s = (e) => e === 0 ? "100%" : `calc(100% - var(--vf-scale, 1) * ${e}px)`, c = (e) => {
|
|
39
|
+
let t = [], n = e.corner[0] ?? e.edge;
|
|
40
|
+
for (let r = 1; r <= e.corner.length; r++) {
|
|
41
|
+
let i = e.corner[r] ?? e.edge;
|
|
42
|
+
i !== n && (t.push({
|
|
43
|
+
y: e.start + r,
|
|
44
|
+
from: n,
|
|
45
|
+
to: i
|
|
46
|
+
}), n = i);
|
|
47
|
+
}
|
|
48
|
+
return t;
|
|
49
|
+
}, l = (e) => {
|
|
50
|
+
let t = e.corner[0] ?? e.edge, n = c(e), r = [...n].reverse(), i = [];
|
|
51
|
+
i.push(`${o(t)} ${o(e.start)}`, `${s(t)} ${o(e.start)}`);
|
|
52
|
+
for (let e of n) i.push(`${s(e.from)} ${o(e.y)}`, `${s(e.to)} ${o(e.y)}`);
|
|
53
|
+
for (let e of r) i.push(`${s(e.to)} ${s(e.y)}`, `${s(e.from)} ${s(e.y)}`);
|
|
54
|
+
i.push(`${s(t)} ${s(e.start)}`, `${o(t)} ${s(e.start)}`);
|
|
55
|
+
for (let e of n) i.push(`${o(e.from)} ${s(e.y)}`, `${o(e.to)} ${s(e.y)}`);
|
|
56
|
+
for (let e of r) i.push(`${o(e.to)} ${o(e.y)}`, `${o(e.from)} ${o(e.y)}`);
|
|
57
|
+
return i;
|
|
58
|
+
}, u = (e) => `polygon(${l(e).join(", ")})`, d = (e, t) => {
|
|
59
|
+
let n = t === "left" ? o : s, r = e[0] ?? 0, i = [`${n(0)} ${o(0)}`, `${n(r)} ${o(0)}`];
|
|
60
|
+
for (let t = 1; t <= e.length; t++) {
|
|
61
|
+
let a = e[t] ?? 0;
|
|
62
|
+
a !== r && (i.push(`${n(r)} ${o(t)}`, `${n(a)} ${o(t)}`), r = a);
|
|
63
|
+
}
|
|
64
|
+
return `polygon(${i.join(", ")})`;
|
|
65
|
+
}, f = (e, t) => {
|
|
66
|
+
let n = l(e), r = l(t), i = `50% ${o(e.start)}`, a = `50% ${o(t.start)}`, s = [...n.slice(1), n[0]], c = [r[0], ...r.slice(1).reverse()];
|
|
67
|
+
return `polygon(evenodd, ${[
|
|
68
|
+
i,
|
|
69
|
+
...s,
|
|
70
|
+
i,
|
|
71
|
+
a,
|
|
72
|
+
...c,
|
|
73
|
+
a
|
|
74
|
+
].join(", ")})`;
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { t as BUTTON_FACE, e as BUTTON_FRAME, n as RING_FRAME, r as RING_HOLE, i as RING_INSET, a as SCREEN_CORNER, d as steppedCornerClip, u as steppedRectClip, f as steppedRingClip };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Popup overflow — the geometry of a menu taller than the screen.
|
|
3
|
+
*
|
|
4
|
+
* System 7 never put a scrollbar on a menu. A popup whose list doesn't fit is
|
|
5
|
+
* **clipped**: the panel is drawn once, the overflowing items are simply not on
|
|
6
|
+
* screen, and the edge slot that has items beyond it shows a solid black arrow
|
|
7
|
+
* in place of a row. Resting the pointer on the arrow rolls the items through
|
|
8
|
+
* the panel one row at a time; once the last item is in view the arrow
|
|
9
|
+
* disappears. A list can overflow both edges at once, and the panel box itself
|
|
10
|
+
* never moves or resizes while open — scrolling rolls the items *inside* it.
|
|
11
|
+
*
|
|
12
|
+
* This module is the arithmetic of that, kept out of the component for the same
|
|
13
|
+
* reason `zoom.ts` is: it is pure, it is testable without a browser, and
|
|
14
|
+
* `vf-menu` will want the identical model when pull-down clamping lands (System
|
|
15
|
+
* 7 scrolled long pull-downs with the very same mechanism).
|
|
16
|
+
*
|
|
17
|
+
* Three ideas carry the whole thing.
|
|
18
|
+
*
|
|
19
|
+
* **The pill lattice.** A popup opens with the selected row's cell laid over
|
|
20
|
+
* the closed pill, which fixes an `idealTop` — the panel top at which that
|
|
21
|
+
* overlay is exact. Every clamp here is quantized to *whole rows* off that
|
|
22
|
+
* lattice, so a panel forced away from the screen edge lands one, two, five
|
|
23
|
+
* rows down rather than an arbitrary number of pixels: the row grid never
|
|
24
|
+
* shifts relative to the pill, and a clamped popup keeps the closed↔open label
|
|
25
|
+
* alignment that an un-quantized pixel clamp destroys.
|
|
26
|
+
*
|
|
27
|
+
* **The panel is as tall as the list wants, not as tall as the items it can
|
|
28
|
+
* currently show.** This is the part that looks wrong until you scroll it. A
|
|
29
|
+
* 12-item list that has to sit somewhere other than where the pill would put it
|
|
30
|
+
* keeps all 12 slots and slides whole rows to fit the screen; the item strip
|
|
31
|
+
* stays welded to the pill, so the slots the strip doesn't reach are drawn as
|
|
32
|
+
* empty white. That blank is not filler — it is the exact travel the list will
|
|
33
|
+
* roll through, so that scrolling to the end lands the strip flush with the
|
|
34
|
+
* panel, *precisely* full, both arrows retired. The kit reproduces it because
|
|
35
|
+
* System 7 did: it is visible in a screenshot of Find File's criteria popup
|
|
36
|
+
* (5 empty rows above `name`) and of the Character Set font menu (2 above
|
|
37
|
+
* `Athens`). Only when the whole list cannot fit the screen band at all does
|
|
38
|
+
* the panel stop at the band's capacity and keep an arrow permanently.
|
|
39
|
+
*
|
|
40
|
+
* **The two directions are one rule.** A panel pushed *up* the screen (the pill
|
|
41
|
+
* is low) reserves its blank at the **top** and rolls down into it; a panel
|
|
42
|
+
* pushed *down* (the pill is high, with a late item selected) reserves it at
|
|
43
|
+
* the **bottom** and rolls up into it. Nothing below distinguishes the cases —
|
|
44
|
+
* the sign of one integer does.
|
|
45
|
+
*
|
|
46
|
+
* **One integer.** `scroll` is how many rows the strip is rolled up past the
|
|
47
|
+
* panel's top edge, and slot `i` shows row `scroll + i`. It is free to leave
|
|
48
|
+
* the range `[0, rowCount − visibleSlots]` that would keep every slot filled,
|
|
49
|
+
* and *that* is the reserved blank: below 0 the first `−scroll` slots are
|
|
50
|
+
* empty, above `rowCount − visibleSlots` the last few are. The up arrow shows
|
|
51
|
+
* when `scroll > 0` (rows really are hidden above), the down arrow when
|
|
52
|
+
* `scroll < rowCount − visibleSlots` (rows really are hidden below).
|
|
53
|
+
* Everything else — which rows are pickable, where the highlight may legally
|
|
54
|
+
* sit, how far a keypress must roll the list — falls out of that. Note what the
|
|
55
|
+
* two conditions say about the opening state: whichever end the blank is on,
|
|
56
|
+
* the arrow pointing back at it is the one that is *off*. The blank is a
|
|
57
|
+
* one-way starting position, not somewhere the user can scroll back to.
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* The fewest row slots a clamped panel is drawn with. With both arrows shown,
|
|
61
|
+
* three slots is the minimum that leaves one *pickable* row between them — at
|
|
62
|
+
* two, the panel offers nothing to choose. A viewport too short for three rows
|
|
63
|
+
* (48 system px) is already outside the kit's world, so the floor is allowed to
|
|
64
|
+
* intrude on the screen-edge insets: a shaved inset beats a menu you cannot
|
|
65
|
+
* pick from.
|
|
66
|
+
*/
|
|
67
|
+
export declare const MIN_VISIBLE_SLOTS = 3;
|
|
68
|
+
/** Inputs to {@link layoutClampedPopup}. Lengths in CSS px, already scaled. */
|
|
69
|
+
export interface PopupClampArgs {
|
|
70
|
+
/** Panel top at which the selected row overlays the pill exactly. */
|
|
71
|
+
idealTop: number;
|
|
72
|
+
/** Height of one option row. */
|
|
73
|
+
rowHeight: number;
|
|
74
|
+
/** The panel's border width (one side). */
|
|
75
|
+
border: number;
|
|
76
|
+
/** How many options the list holds. */
|
|
77
|
+
rowCount: number;
|
|
78
|
+
/** Top of the usable screen band (the top inset). */
|
|
79
|
+
viewTop: number;
|
|
80
|
+
/** Bottom of the usable screen band (viewport height minus the bottom inset). */
|
|
81
|
+
viewBottom: number;
|
|
82
|
+
}
|
|
83
|
+
/** The panel box and the scroll state a clamped popup opens with. */
|
|
84
|
+
export interface PopupLayout {
|
|
85
|
+
/** Panel border-box top, a whole number of rows off the pill lattice. */
|
|
86
|
+
panelTop: number;
|
|
87
|
+
/** Panel border-box height — a whole number of row slots plus both borders. */
|
|
88
|
+
panelHeight: number;
|
|
89
|
+
/** Row slots the panel is drawn with (`R`). */
|
|
90
|
+
visibleSlots: number;
|
|
91
|
+
/** Opening scroll (`S`) — negative when the panel opens with blank above. */
|
|
92
|
+
initialScroll: number;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Fit a popup's panel into the usable screen band, quantized to the pill
|
|
96
|
+
* lattice.
|
|
97
|
+
*
|
|
98
|
+
* The panel wants every row it has (`rowCount` slots) and wants to sit at
|
|
99
|
+
* `idealTop`. Both wishes bend a whole row at a time:
|
|
100
|
+
*
|
|
101
|
+
* - **Position** first. `scroll` *is* the panel's row offset from `idealTop`
|
|
102
|
+
* (`panelTop = idealTop + scroll × rowHeight`), because sliding the box
|
|
103
|
+
* while the strip stays on the lattice is the same act as scrolling the
|
|
104
|
+
* strip while the box stays put. So the search is for the integer `scroll`
|
|
105
|
+
* nearest 0 that puts the whole box inside the band — negative to slide the
|
|
106
|
+
* box up, positive to slide it down, with the reserved blank falling out at
|
|
107
|
+
* whichever end the strip no longer reaches.
|
|
108
|
+
* - **Height** only if position alone can't do it. Each row given up buys one
|
|
109
|
+
* row of freedom for `scroll`, so the loop hands back one slot at a time
|
|
110
|
+
* until an integer fits — which is at most a couple of iterations, and is
|
|
111
|
+
* also how a list far taller than the screen settles at the band's capacity.
|
|
112
|
+
*
|
|
113
|
+
* When neither has to bend — `initialScroll` 0 and `visibleSlots === rowCount`
|
|
114
|
+
* — the result is exactly the unclamped placement the popup has always used, so
|
|
115
|
+
* a list that fits is untouched by any of this.
|
|
116
|
+
*/
|
|
117
|
+
export declare function layoutClampedPopup(args: PopupClampArgs): PopupLayout;
|
|
118
|
+
/**
|
|
119
|
+
* The lowest row index the user can pick right now. An arrow *overlays* the
|
|
120
|
+
* slot it occupies — an opaque white row covering the item beneath, which is
|
|
121
|
+
* the whole mechanism — so a shown up arrow costs the first slot its row. With
|
|
122
|
+
* blank reserved above (`scroll` negative) there is no up arrow and no hidden
|
|
123
|
+
* row, so the first item is simply row 0.
|
|
124
|
+
*/
|
|
125
|
+
export declare function firstPickableRow(scroll: number): number;
|
|
126
|
+
/**
|
|
127
|
+
* The highest row index the user can pick right now — the mirror of the above,
|
|
128
|
+
* ending at the last row when the down arrow is absent (which covers both the
|
|
129
|
+
* flush case and blank reserved below).
|
|
130
|
+
*/
|
|
131
|
+
export declare function lastPickableRow(scroll: number, rowCount: number, visibleSlots: number): number;
|
|
132
|
+
/**
|
|
133
|
+
* The scroll position that brings row `index` into a pickable slot, leaving it
|
|
134
|
+
* one slot clear of whichever arrow will still be showing. Already-pickable
|
|
135
|
+
* rows give the scroll back unchanged — including one outside the flush range,
|
|
136
|
+
* so opening a panel with reserved blank and then merely moving the highlight
|
|
137
|
+
* doesn't scrub that blank away.
|
|
138
|
+
*
|
|
139
|
+
* The two endpoints are the reason for the `+ 2` / `− 1` rather than the
|
|
140
|
+
* obvious `+ 1` / `− 0`: row 0 and row `rowCount − 1` are reached at
|
|
141
|
+
* `scroll = 0` and `scroll = rowCount − visibleSlots`, where the arrow in
|
|
142
|
+
* question has just disappeared and the row may take the edge slot itself.
|
|
143
|
+
*
|
|
144
|
+
* A deliberate move never re-enters the reserved blank at either end — it is a
|
|
145
|
+
* starting position the list rolls out of, not one it can roll back into.
|
|
146
|
+
*/
|
|
147
|
+
export declare function ensureVisibleScroll(index: number, scroll: number, rowCount: number, visibleSlots: number): number;
|
|
148
|
+
/**
|
|
149
|
+
* Hold a scroll inside its legal range. Normally that is `0` (row 0 in slot 0)
|
|
150
|
+
* to `rowCount − visibleSlots` (the last row in the last slot) — but a panel
|
|
151
|
+
* that opened with reserved blank starts *outside* that range at whichever end
|
|
152
|
+
* the blank is on, and the widened bound (`min(from, 0)`, `max(from, …)`) is
|
|
153
|
+
* what lets it roll out of the blank while never letting it roll back in.
|
|
154
|
+
* Symmetric in both directions, and it narrows to the normal range the moment
|
|
155
|
+
* the list reaches it.
|
|
156
|
+
*/
|
|
157
|
+
export declare function clampScroll(scroll: number, from: number, rowCount: number, visibleSlots: number): number;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/popup-overflow.ts
|
|
2
|
+
var e = .001;
|
|
3
|
+
function t(t) {
|
|
4
|
+
let { idealTop: n, rowHeight: r, border: i, rowCount: a, viewTop: o, viewBottom: s } = t;
|
|
5
|
+
if (a <= 0 || r <= 0) return {
|
|
6
|
+
panelTop: n,
|
|
7
|
+
panelHeight: 2 * i,
|
|
8
|
+
visibleSlots: 0,
|
|
9
|
+
initialScroll: 0
|
|
10
|
+
};
|
|
11
|
+
let c = Math.min(a, 3), l = Math.floor((s - o - 2 * i) / r + e), u = Math.min(a, Math.max(l, c)), d = 0;
|
|
12
|
+
for (;;) {
|
|
13
|
+
let t = u * r + 2 * i, a = Math.ceil((o - n) / r - e), l = Math.floor((s - t - n) / r + e);
|
|
14
|
+
if (a <= l) {
|
|
15
|
+
d = Math.min(Math.max(0, a), l);
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
if (u <= c) {
|
|
19
|
+
d = Math.max(0, a);
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
--u;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
panelTop: n + d * r,
|
|
26
|
+
panelHeight: u * r + 2 * i,
|
|
27
|
+
visibleSlots: u,
|
|
28
|
+
initialScroll: d
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function n(e) {
|
|
32
|
+
return e > 0 ? e + 1 : 0;
|
|
33
|
+
}
|
|
34
|
+
function r(e, t, n) {
|
|
35
|
+
return e < t - n ? e + n - 2 : t - 1;
|
|
36
|
+
}
|
|
37
|
+
function i(e, t, i, o) {
|
|
38
|
+
let s = t;
|
|
39
|
+
return e < n(t) ? s = e === 0 ? 0 : e - 1 : e > r(t, i, o) && (s = e === i - 1 ? i - o : e - o + 2), a(s, t, i, o);
|
|
40
|
+
}
|
|
41
|
+
function a(e, t, n, r) {
|
|
42
|
+
return Math.max(Math.min(t, 0), Math.min(e, Math.max(t, n - r)));
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { a as clampScroll, i as ensureVisibleScroll, n as firstPickableRow, r as lastPickableRow, t as layoutClampedPopup };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { LitElement } from 'lit';
|
|
2
|
+
type Constructor<T = object> = new (...args: any[]) => T;
|
|
3
|
+
/**
|
|
4
|
+
* The mixin's surface, as a `declare class` for the same TS4094 reason
|
|
5
|
+
* {@link VfToggleControlInterface} states: TypeScript cannot name the
|
|
6
|
+
* anonymous class a mixin returns when emitting declarations. Keep it in sync
|
|
7
|
+
* with the implementation below.
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class VfPositionedInterface extends LitElement {
|
|
10
|
+
top?: number | null;
|
|
11
|
+
left?: number | null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Explicit placement — `top`/`left` in whole system px, on (nearly) every
|
|
15
|
+
* component.
|
|
16
|
+
*
|
|
17
|
+
* A DITL resource laid a dialog out as a list of items, each with a rectangle
|
|
18
|
+
* in the window's own coordinates; arranging controls by stating where they go
|
|
19
|
+
* is as native to System 7 as stacking them. This mixin is that mechanism:
|
|
20
|
+
* declaring `top` or `left` takes the element out of normal flow and
|
|
21
|
+
* absolutely positions it within its parent, both coordinates in the art's own
|
|
22
|
+
* unit so the position scales with the display like every other metric. A
|
|
23
|
+
* window or dialog body can be laid out either way — a tree of `vf-stack`s, or
|
|
24
|
+
* positioned children — with no stylesheet in either case.
|
|
25
|
+
*
|
|
26
|
+
* Mechanics, mirroring how `vf-stack` writes its declared size:
|
|
27
|
+
*
|
|
28
|
+
* - Setting either property writes `position: absolute` and both offsets onto
|
|
29
|
+
* the host's inline style as `calc(var(--vf-scale, 1) * Npx)` — live against
|
|
30
|
+
* the display, resolved at paint time. The coordinate left unset is 0.
|
|
31
|
+
* - `right`/`bottom` are released to `auto` and `margin` zeroed, so the stated
|
|
32
|
+
* offsets are the whole story — an auto-width box with both edges set would
|
|
33
|
+
* stretch rather than sit (the `vf-icon` seed logic, generalized).
|
|
34
|
+
* - Unsetting both returns the element to normal flow: every inline
|
|
35
|
+
* declaration this wrote is removed, and stylesheet values resume.
|
|
36
|
+
*
|
|
37
|
+
* The anchor is CSS's own: the nearest positioned ancestor's padding box. The
|
|
38
|
+
* kit's containers are all deliberate anchors — a desktop's raster, a window's
|
|
39
|
+
* content region (the frame's inner edge, below the title bar — the 12px body
|
|
40
|
+
* inset governs flow content only, exactly the DITL convention), a dialog's
|
|
41
|
+
* content area, a stack's box, a fieldset's border box, a scroll area's
|
|
42
|
+
* scrolled plane. In a non-kit parent, give the parent `position: relative`,
|
|
43
|
+
* the one line of CSS this feature can't write for you.
|
|
44
|
+
*
|
|
45
|
+
* The writing rides a {@link ReactiveController} (`hostUpdated`) rather than
|
|
46
|
+
* an `updated()` override, for a lifecycle reason worth keeping in the source:
|
|
47
|
+
* the mixin sits *under* each component class, and most components override
|
|
48
|
+
* `updated()` without calling `super.updated()` — Lit's base is a no-op, so
|
|
49
|
+
* nothing ever forced the call — which would silently shadow a mixin-level
|
|
50
|
+
* override. A controller is invoked by ReactiveElement itself, after every
|
|
51
|
+
* update, no matter what the subclass does.
|
|
52
|
+
*
|
|
53
|
+
* A gesture writes **through these properties**: `vf-window`'s title-bar drag
|
|
54
|
+
* and `vf-icon`'s drag and arrow nudge hand their new origin to a
|
|
55
|
+
* {@link PlacementController}, which snaps it onto the placement lattice and
|
|
56
|
+
* sets `left`/`top`. A moved element is therefore placed exactly the way an
|
|
57
|
+
* authored one is — a live `calc()` in the art's own unit — and stays where it
|
|
58
|
+
* was dropped when the zoom or the display changes what a system px costs.
|
|
59
|
+
*
|
|
60
|
+
* It did not always. Writing the resolved CSS px straight to the inline style
|
|
61
|
+
* froze the coordinate in the wrong unit: `--vf-scale` moved under it at every
|
|
62
|
+
* zoom step and the same constant read back as a different number of system px,
|
|
63
|
+
* so moved windows and icons slid off the grid the rest of the kit stayed on
|
|
64
|
+
* (by `3z / round(3z)` — 10% at 110% zoom, where nothing else moves at all).
|
|
65
|
+
*
|
|
66
|
+
* Setting a property yourself is still the deliberate way to re-place a moved
|
|
67
|
+
* element. The controller re-applies **only when the values changed**, so an
|
|
68
|
+
* unrelated update — a heading change, a desktop toggling `active` — never
|
|
69
|
+
* re-asserts a coordinate and costs nothing.
|
|
70
|
+
*/
|
|
71
|
+
export declare const VfPositioned: <T extends Constructor<LitElement>>(Base: T) => Constructor<VfPositionedInterface> & T;
|
|
72
|
+
/** The box a gesture is clamped into, in system px — see {@link PlacementClamp}. */
|
|
73
|
+
export type PlacementBounds = {
|
|
74
|
+
width: number;
|
|
75
|
+
height: number;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* A moved host's own containment rule, in system px, against the box measured
|
|
79
|
+
* once at the start of the gesture.
|
|
80
|
+
*/
|
|
81
|
+
export type PlacementClamp = (x: number, y: number, bounds: PlacementBounds) => {
|
|
82
|
+
x: number;
|
|
83
|
+
y: number;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The gesture half of placement: the piece a *movable* host adds on top of
|
|
87
|
+
* {@link VfPositioned}, so a drag ends up in the same `top`/`left` system-px
|
|
88
|
+
* properties an author would have written.
|
|
89
|
+
*
|
|
90
|
+
* Two things it owns, both consequences of storing the origin in the art's own
|
|
91
|
+
* unit rather than in resolved CSS px:
|
|
92
|
+
*
|
|
93
|
+
* - **Seeding.** A gesture starts from wherever the host already is, which may
|
|
94
|
+
* be a coordinate nobody stated in system px — an authored `left: 10%`, a
|
|
95
|
+
* `right`-anchored Trash icon, or plain normal flow. {@link seed} reads the
|
|
96
|
+
* *used* position and converts it once; from then on the properties are the
|
|
97
|
+
* whole truth.
|
|
98
|
+
* - **The lattice.** Every write is snapped to {@link snapSys} — whole art
|
|
99
|
+
* pixels, the way QuickDraw moved windows, and the k-system-px run that also
|
|
100
|
+
* lands the edge on a whole CSS px (scale.ts explains why that second half
|
|
101
|
+
* matters to a scroll rail).
|
|
102
|
+
*
|
|
103
|
+
* It holds no state the host's own properties don't already hold, so it takes
|
|
104
|
+
* no lifecycle and is not a {@link ReactiveController}: the writes it makes are
|
|
105
|
+
* ordinary property sets, and `VfPositioned`'s controller renders them.
|
|
106
|
+
*
|
|
107
|
+
* And one thing it deliberately does **not** do: re-snap a placed host when the
|
|
108
|
+
* scale changes. The lattice moves with the scale (k is 2 at dpr 2, 3 at that
|
|
109
|
+
* display's 150%), so a coordinate dropped on one rung sits between two on the
|
|
110
|
+
* next — but re-rounding it there is worse than leaving it. It is lossy, and it
|
|
111
|
+
* compounds: 62 → 63 at 150% → 64 at 200%, a window walking away from where it
|
|
112
|
+
* was dropped one zoom step at a time, which is a milder version of the exact
|
|
113
|
+
* bug this class exists to fix (`verify:zoom` group (e) fails on it). Whole
|
|
114
|
+
* system px is whole *device* px at every rung by the scale contract, so the
|
|
115
|
+
* art stays crisp regardless; only the whole-CSS-px edge is given up, and an
|
|
116
|
+
* authored `left="10"` gives that up already. A dropped coordinate is therefore
|
|
117
|
+
* as immutable as an authored one — which is the whole claim.
|
|
118
|
+
*/
|
|
119
|
+
export declare class PlacementController {
|
|
120
|
+
#private;
|
|
121
|
+
constructor(host: HTMLElement & {
|
|
122
|
+
top?: number | null;
|
|
123
|
+
left?: number | null;
|
|
124
|
+
}, clamp: PlacementClamp);
|
|
125
|
+
/** Whether a gesture has placed this host (as opposed to markup or CSS). */
|
|
126
|
+
get placed(): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* The origin a move adds its delta to, in system px.
|
|
129
|
+
*
|
|
130
|
+
* A stated coordinate is authoritative and needs no measuring — including
|
|
131
|
+
* the one this controller wrote last time. Otherwise the host is wherever
|
|
132
|
+
* layout or a stylesheet put it, so the *used* position is read and
|
|
133
|
+
* converted: absolutely positioned hosts through their computed offsets
|
|
134
|
+
* (`left: 10%` and `left: 1em` are perfectly good ways to place one, and
|
|
135
|
+
* both resolve to px here), everything else through its in-flow offset,
|
|
136
|
+
* which is measured against the same padding box `left`/`top` will be.
|
|
137
|
+
*/
|
|
138
|
+
seed(): {
|
|
139
|
+
x: number;
|
|
140
|
+
y: number;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Place the host at a system-px origin, clamped by its own rule and snapped.
|
|
144
|
+
*
|
|
145
|
+
* The clamp runs *here*, during the gesture, which is the moment the user is
|
|
146
|
+
* actually pushing against an edge — and nowhere else. Re-clamping later, on
|
|
147
|
+
* a parent that shrank under a zoom, would move a host nobody moved and would
|
|
148
|
+
* not give the position back when the parent grew again.
|
|
149
|
+
*
|
|
150
|
+
* It runs against the box {@link seed} measured, not a fresh one. The
|
|
151
|
+
* difference only shows when the movable contract is unmet: a host with no
|
|
152
|
+
* stated origin is in normal flow, so its first move takes it out — and an
|
|
153
|
+
* auto-height parent then collapses to whatever is left, which is a box the
|
|
154
|
+
* host never actually sat in. Re-measuring per move would clamp the rest of
|
|
155
|
+
* the gesture into that phantom, walking the host to the parent's origin
|
|
156
|
+
* while the user drags away from it. The box at the moment of the press is
|
|
157
|
+
* the one the user is pushing against.
|
|
158
|
+
*/
|
|
159
|
+
moveTo(x: number, y: number): void;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The movable contract, as a one-time console warning.
|
|
163
|
+
*
|
|
164
|
+
* A host that moves under a gesture states its own origin, and its positioning
|
|
165
|
+
* parent is a box with a size. Neither half is something the component can
|
|
166
|
+
* supply for itself, and both fail quietly rather than loudly:
|
|
167
|
+
*
|
|
168
|
+
* - **Unplaced.** A host still in normal flow has to be taken out of it by its
|
|
169
|
+
* first move — which reflows everything after it and can collapse the very
|
|
170
|
+
* parent the clamp is about to measure. What matters is being out of flow,
|
|
171
|
+
* not which mechanism did it: `top`/`left` are the kit's way and the one that
|
|
172
|
+
* scales, but a stylesheet's own `position: absolute` satisfies it too, and
|
|
173
|
+
* {@link PlacementController.seed} reads that case from the computed offsets.
|
|
174
|
+
* - **Unsized parent.** A positioning parent with no box gives the clamp no
|
|
175
|
+
* range, and the host cannot be dragged away from the parent's origin at all.
|
|
176
|
+
* Only checked for a real ancestor: with no positioned ancestor the host
|
|
177
|
+
* resolves against the initial containing block and the clamp falls back to
|
|
178
|
+
* the viewport, which agree with each other and need no warning.
|
|
179
|
+
*
|
|
180
|
+
* Returns whether it warned, so the caller can latch — one warning per element,
|
|
181
|
+
* not per render.
|
|
182
|
+
*/
|
|
183
|
+
export declare function warnMovableContract(host: HTMLElement & {
|
|
184
|
+
top?: number | null;
|
|
185
|
+
left?: number | null;
|
|
186
|
+
}, what: string, example: string): boolean;
|
|
187
|
+
export {};
|
package/dist/position.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { snapSys as e, sysLength as t, toSysExact as n } from "./scale.js";
|
|
2
|
+
import r from "./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
|
|
3
|
+
import { property as i } from "lit/decorators.js";
|
|
4
|
+
//#region src/position.ts
|
|
5
|
+
var a = (e) => {
|
|
6
|
+
class t extends e {
|
|
7
|
+
constructor(...e) {
|
|
8
|
+
super(...e), new o(this);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return r([i({ type: Number })], t.prototype, "top", void 0), r([i({ type: Number })], t.prototype, "left", void 0), t;
|
|
12
|
+
}, o = class {
|
|
13
|
+
#e;
|
|
14
|
+
#t = null;
|
|
15
|
+
#n = null;
|
|
16
|
+
#r = !1;
|
|
17
|
+
constructor(e) {
|
|
18
|
+
this.#e = e, e.addController(this);
|
|
19
|
+
}
|
|
20
|
+
hostUpdated() {
|
|
21
|
+
let e = this.#e.top ?? null, n = this.#e.left ?? null, r = this.#e.style;
|
|
22
|
+
if (e === null && n === null) {
|
|
23
|
+
if (!this.#r) return;
|
|
24
|
+
this.#r = !1, r.removeProperty("position"), r.removeProperty("top"), r.removeProperty("left"), r.removeProperty("right"), r.removeProperty("bottom"), r.removeProperty("margin");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.#r && e === this.#t && n === this.#n || (this.#r = !0, this.#t = e, this.#n = n, r.position = "absolute", r.top = t(e ?? 0), r.left = t(n ?? 0), r.right = "auto", r.bottom = "auto", r.margin = "0");
|
|
28
|
+
}
|
|
29
|
+
}, s = class {
|
|
30
|
+
#e;
|
|
31
|
+
#t;
|
|
32
|
+
#n = !1;
|
|
33
|
+
#r = null;
|
|
34
|
+
constructor(e, t) {
|
|
35
|
+
this.#e = e, this.#t = t;
|
|
36
|
+
}
|
|
37
|
+
get placed() {
|
|
38
|
+
return this.#n;
|
|
39
|
+
}
|
|
40
|
+
seed() {
|
|
41
|
+
let t = this.#e;
|
|
42
|
+
if (this.#r = this.#i(), t.left != null || t.top != null) return {
|
|
43
|
+
x: t.left ?? 0,
|
|
44
|
+
y: t.top ?? 0
|
|
45
|
+
};
|
|
46
|
+
let r = getComputedStyle(t), i = r.position === "absolute" || r.position === "fixed", a = i ? parseFloat(r.left) || 0 : t.offsetLeft, o = i ? parseFloat(r.top) || 0 : t.offsetTop;
|
|
47
|
+
return {
|
|
48
|
+
x: e(n(a, t), t),
|
|
49
|
+
y: e(n(o, t), t)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
moveTo(t, n) {
|
|
53
|
+
let r = this.#e, i = this.#t(t, n, this.#r ?? this.#i());
|
|
54
|
+
r.left = e(i.x, r), r.top = e(i.y, r), this.#n = !0;
|
|
55
|
+
}
|
|
56
|
+
#i() {
|
|
57
|
+
let e = this.#e, t = e.offsetParent, r = t && t.clientWidth > 0 && t.clientHeight > 0 ? t : null;
|
|
58
|
+
return {
|
|
59
|
+
width: n(r?.clientWidth ?? window.innerWidth, e),
|
|
60
|
+
height: n(r?.clientHeight ?? window.innerHeight, e)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
function c(e, t, n) {
|
|
65
|
+
let r = e.top != null || e.left != null, i = getComputedStyle(e).position, a = i === "absolute" || i === "fixed", o = null;
|
|
66
|
+
if (!r && !a) o = "unplaced";
|
|
67
|
+
else {
|
|
68
|
+
let t = e.offsetParent;
|
|
69
|
+
t && t !== e.ownerDocument.body && (t.clientWidth === 0 || t.clientHeight === 0) && (o = "unsized-parent");
|
|
70
|
+
}
|
|
71
|
+
return o !== null && (console.warn(o === "unplaced" ? `${t}: movable, but in normal flow — so the first drag has to pull it out, reflowing everything after it on the page. State the origin in system px and it never was in flow: ${n}` : `${t}: the positioning parent has no box, so there is nowhere to drag to. Give it a size (and \`position: relative\`, if it is not a kit container) — the clamp is falling back to the viewport meanwhile.`), !0);
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { s as PlacementController, a as VfPositioned, c as warnMovableContract };
|