vintage-frames 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/custom-elements.json +184 -20
- package/dist/components/vf-checkbox.d.ts +6 -5
- package/dist/components/vf-checkbox.js +41 -37
- package/dist/components/vf-container.d.ts +14 -14
- package/dist/components/vf-container.js +58 -28
- package/dist/components/vf-desktop.d.ts +114 -12
- package/dist/components/vf-desktop.js +119 -58
- package/dist/components/vf-fieldset.js +30 -22
- package/dist/components/vf-icon-field.d.ts +1 -1
- package/dist/components/vf-icon.js +111 -104
- package/dist/components/vf-list.js +30 -29
- package/dist/components/vf-menu-bar.d.ts +5 -0
- package/dist/components/vf-menu-bar.js +67 -43
- package/dist/components/vf-menu.js +24 -22
- package/dist/components/vf-number-field.js +32 -30
- package/dist/components/vf-progress-bar.js +28 -29
- package/dist/components/vf-radio.js +3 -4
- package/dist/components/vf-scroll-area.js +27 -26
- package/dist/components/vf-select.js +108 -90
- package/dist/components/vf-separator.d.ts +5 -5
- package/dist/components/vf-separator.js +36 -10
- package/dist/components/vf-swatch.js +47 -45
- package/dist/components/vf-text-area.js +37 -35
- package/dist/components/vf-text-field.js +20 -18
- package/dist/components/vf-window.d.ts +13 -0
- package/dist/components/vf-window.js +101 -72
- package/dist/index.d.ts +12 -1
- package/dist/index.js +61 -59
- package/dist/pattern-fill.d.ts +11 -8
- package/dist/pattern-fill.js +2 -4
- package/dist/position.d.ts +25 -7
- package/dist/position.js +6 -3
- package/dist/shell/alert.d.ts +32 -0
- package/dist/shell/alert.js +44 -0
- package/dist/shell/catalog.d.ts +168 -0
- package/dist/shell/catalog.js +278 -0
- package/dist/shell/clock.d.ts +16 -0
- package/dist/shell/clock.js +36 -0
- package/dist/shell/filing.d.ts +39 -0
- package/dist/shell/filing.js +68 -0
- package/dist/shell/finder.d.ts +116 -0
- package/dist/shell/finder.js +564 -0
- package/dist/shell/geometry.d.ts +196 -0
- package/dist/shell/geometry.js +241 -0
- package/dist/shell/index.d.ts +29 -0
- package/dist/shell/index.js +11 -0
- package/dist/shell/shell.d.ts +150 -0
- package/dist/shell/shell.js +213 -0
- package/dist/shell/state.d.ts +88 -0
- package/dist/shell/state.js +103 -0
- package/dist/shell/storage.d.ts +14 -0
- package/dist/shell/storage.js +41 -0
- package/dist/shell/windows.d.ts +215 -0
- package/dist/shell/windows.js +343 -0
- package/dist/styles/base.d.ts +3 -1
- package/dist/styles/recipes/field.d.ts +10 -6
- package/dist/styles/recipes/field.js +8 -7
- package/dist/styles/recipes/focus.d.ts +7 -3
- package/dist/styles/recipes/rule.d.ts +11 -8
- package/dist/styles/recipes/rule.js +31 -13
- package/dist/styles/recipes/scroll-rail.d.ts +7 -6
- package/dist/styles/recipes/scroll-rail.js +18 -18
- package/dist/styles/recipes/shadow.d.ts +10 -6
- package/dist/styles/recipes/shadow.js +4 -2
- package/dist/styles/recipes/stroke.d.ts +85 -0
- package/dist/styles/recipes/stroke.js +53 -0
- package/dist/styles/recipes/surface.d.ts +8 -4
- package/dist/styles/recipes/surface.js +23 -17
- package/dist/styles/recipes/title-bar.d.ts +7 -0
- package/dist/styles/recipes/title-bar.js +25 -22
- package/dist/styles/recipes/toggle.js +3 -3
- package/dist/window-chrome.d.ts +43 -0
- package/dist/window-chrome.js +13 -0
- package/dist/zoom.d.ts +9 -9
- package/docs/SPEC.md +34 -32
- package/editor/vscode.html-custom-data.json +12 -7
- package/editor/web-types.json +50 -14
- package/package.json +10 -2
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
* with no blur and no spread, System 7's only depth cue. The offset is
|
|
4
4
|
* tokenized via `--vf-shadow-offset` and scales with `--vf-scale`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* {
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
|
|
6
|
+
* The shadow value, for a list: a stroked surface passes it to
|
|
7
|
+
* `vfStrokeDecls({ shadows: vfHardShadow })`, which paints it after the
|
|
8
|
+
* stroke. Every raised surface in the kit does — {@link vfPanel} (menus,
|
|
9
|
+
* popups) and {@link vfChromeFrame} (window frames). One value, so the kit
|
|
10
|
+
* can't grow two shadows.
|
|
11
|
+
*/
|
|
12
|
+
export declare const vfHardShadow: import("lit").CSSResult;
|
|
13
|
+
/**
|
|
14
|
+
* {@link vfHardShadow} as the whole `box-shadow` declaration, for a box that
|
|
15
|
+
* casts the shadow and draws no stroke.
|
|
12
16
|
*/
|
|
13
17
|
export declare const vfHardShadowDecls: import("lit").CSSResult;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { unsafeCSS as e } from "lit";
|
|
2
2
|
//#region src/styles/recipes/shadow.ts
|
|
3
|
-
var t = e("
|
|
3
|
+
var t = e("calc(var(--vf-scale, 1) * var(--vf-shadow-offset, 2px))\n calc(var(--vf-scale, 1) * var(--vf-shadow-offset, 2px)) 0 0\n var(--vf-black, #000)"), n = e(`
|
|
4
|
+
box-shadow: ${t};
|
|
5
|
+
`);
|
|
4
6
|
//#endregion
|
|
5
|
-
export { t as vfHardShadowDecls };
|
|
7
|
+
export { t as vfHardShadow, n as vfHardShadowDecls };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type CSSResult } from 'lit';
|
|
2
|
+
import type { RuleEdge } from './rule.js';
|
|
3
|
+
/** One to four values in shorthand order, as top/right/bottom/left. */
|
|
4
|
+
type Sides<T> = T | readonly T[];
|
|
5
|
+
/** The padding under a stroked edge: the element's own plus the stroke's. */
|
|
6
|
+
export declare function strokePadding(own: number | string, width: number): string;
|
|
7
|
+
/** The border a stroked edge becomes under forced colors; 0 wide otherwise. */
|
|
8
|
+
export declare function strokeBorder(width: number): string;
|
|
9
|
+
/** The inset shadow that paints one edge's stroke. */
|
|
10
|
+
export declare function strokeEdgeShadow(edge: RuleEdge, width: number): string;
|
|
11
|
+
/** The forced-colors switch every stroked element carries. */
|
|
12
|
+
export declare const STROKE_FORCED_COLORS = "@media (forced-colors: active) {\n --_vf-stroke-pad: 0;\n }";
|
|
13
|
+
/**
|
|
14
|
+
* The inset shadow of a stroke `width` system px wide on all four edges — the
|
|
15
|
+
* value to restate when only the painted width changes, as vf-checkbox's
|
|
16
|
+
* press does. Reads the ink and forced-colors switch the element's
|
|
17
|
+
* {@link vfStrokeDecls} declares.
|
|
18
|
+
*/
|
|
19
|
+
export declare function vfStrokeShadow(width?: number): CSSResult;
|
|
20
|
+
/** The options of {@link vfStrokeDecls}. */
|
|
21
|
+
export interface VfStrokeOptions {
|
|
22
|
+
/** The stroked edges. Default all four. */
|
|
23
|
+
edges?: readonly RuleEdge[];
|
|
24
|
+
/**
|
|
25
|
+
* The stroke's width in whole system px, one to four values in shorthand
|
|
26
|
+
* order. Default 1.
|
|
27
|
+
*/
|
|
28
|
+
width?: Sides<number>;
|
|
29
|
+
/**
|
|
30
|
+
* The element's own padding, in system px, one to four values in shorthand
|
|
31
|
+
* order; each stroked edge adds its stroke. A value is a number or a
|
|
32
|
+
* system-px CSS length (`'var(--vf-select-gutter, 16px)'`). Omitted, the
|
|
33
|
+
* declarations set padding on the stroked edges alone, to the stroke.
|
|
34
|
+
*/
|
|
35
|
+
padding?: Sides<number | string>;
|
|
36
|
+
/**
|
|
37
|
+
* More shadows, painted with the stroke: the hard drop shadow
|
|
38
|
+
* ({@link vfHardShadow}), a white ring. The stroke comes first in the list.
|
|
39
|
+
*/
|
|
40
|
+
shadows?: string | CSSResult;
|
|
41
|
+
/** The stroke's color. Default `var(--vf-black, #000)`. */
|
|
42
|
+
ink?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A 1-bit stroke that isn't a line width. The declarations for a box whose
|
|
46
|
+
* edges carry a black line of whole system px — every frame, well, rule and
|
|
47
|
+
* box in the kit — for interpolating into the box's rule:
|
|
48
|
+
* `.card { ${vfStrokeDecls({ padding: [4, 8] })} }`.
|
|
49
|
+
*
|
|
50
|
+
* Safari rounds a border width to device px before it applies page zoom, so a
|
|
51
|
+
* `border` of `calc(var(--vf-scale, 1) * 1px)` draws thinner than the art
|
|
52
|
+
* around it at most zoom levels, and moves everything inside the box off the
|
|
53
|
+
* device grid. Padding and box-shadow lengths are not rounded that way. So the
|
|
54
|
+
* stroke's space is padding on each stroked edge, added to the element's own,
|
|
55
|
+
* and the black is an inset `box-shadow`, with `shadows` after it in the same
|
|
56
|
+
* list. Both land on whole device px at every zoom.
|
|
57
|
+
*
|
|
58
|
+
* What changes from a border:
|
|
59
|
+
* - The declarations own `padding` on the stroked edges (on all four, when
|
|
60
|
+
* `padding` is passed) and `box-shadow`. Pass the element's own padding and
|
|
61
|
+
* shadows here; a later `padding` or `box-shadow` on the element replaces
|
|
62
|
+
* the stroke's.
|
|
63
|
+
* - A child placed against the box measures from its outer edge. Offset it by
|
|
64
|
+
* the stroke with {@link vfStrokeInset}.
|
|
65
|
+
* - An `overflow: hidden` box clips at its outer edge, so its content can
|
|
66
|
+
* paint over the stroke. Clip on an inner element instead.
|
|
67
|
+
* - Forced colors paints no `box-shadow`, so there each stroke is a border
|
|
68
|
+
* again and the padding is the element's own: the same box a border draws.
|
|
69
|
+
*
|
|
70
|
+
* The color goes through a private property the declarations set, so pass a
|
|
71
|
+
* `var()` as `ink` to change it from a state rule without restating the
|
|
72
|
+
* shadow list.
|
|
73
|
+
*/
|
|
74
|
+
export declare function vfStrokeDecls(options?: VfStrokeOptions): CSSResult;
|
|
75
|
+
/**
|
|
76
|
+
* A length `base` system px in from a stroke's inner edge, measured from the
|
|
77
|
+
* box's outer edge, on an edge carrying `stroke` system px of it (see
|
|
78
|
+
* {@link vfStrokeDecls}): the inset for a child placed against a stroked box
|
|
79
|
+
* (`right: ${vfStrokeInset(0)}` is flush inside a 1px frame), or a padding
|
|
80
|
+
* that restates the stroke's (`padding: ${vfStrokeInset(0)} ${vfStrokeInset(6)}`).
|
|
81
|
+
* `base` is a number or a system-px CSS length. Under forced colors, where
|
|
82
|
+
* the stroke is a border, it is `base`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function vfStrokeInset(base: number | string, stroke?: number): CSSResult;
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { unsafeCSS as e } from "lit";
|
|
2
|
+
//#region src/styles/recipes/stroke.ts
|
|
3
|
+
var t = [
|
|
4
|
+
"top",
|
|
5
|
+
"right",
|
|
6
|
+
"bottom",
|
|
7
|
+
"left"
|
|
8
|
+
], n = "var(--_vf-stroke-pad, 1)", r = "var(--_vf-stroke-ink)";
|
|
9
|
+
function i(e) {
|
|
10
|
+
let t = Array.isArray(e) ? e : [e], n = t[0], r = t[1] ?? n;
|
|
11
|
+
return [
|
|
12
|
+
n,
|
|
13
|
+
r,
|
|
14
|
+
t[2] ?? n,
|
|
15
|
+
t[3] ?? r
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
var a = (e) => typeof e == "number" ? `${e}px` : e, o = (e) => `calc(var(--vf-scale, 1) * ${e}px * ${n})`;
|
|
19
|
+
function s(e, t) {
|
|
20
|
+
return e === 0 ? o(t) : `calc(var(--vf-scale, 1) * (${a(e)} + ${t}px * ${n}))`;
|
|
21
|
+
}
|
|
22
|
+
function c(e) {
|
|
23
|
+
return `calc(var(--vf-scale, 1) * ${e}px * (1 - ${n})) solid ${r}`;
|
|
24
|
+
}
|
|
25
|
+
function l(e, t) {
|
|
26
|
+
let i = (e) => `calc(var(--vf-scale, 1) * ${e * t}px * ${n})`;
|
|
27
|
+
switch (e) {
|
|
28
|
+
case "top": return `inset 0 ${i(1)} 0 0 ${r}`;
|
|
29
|
+
case "right": return `inset ${i(-1)} 0 0 0 ${r}`;
|
|
30
|
+
case "bottom": return `inset 0 ${i(-1)} 0 0 ${r}`;
|
|
31
|
+
case "left": return `inset ${i(1)} 0 0 0 ${r}`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
var u = "@media (forced-colors: active) {\n --_vf-stroke-pad: 0;\n }";
|
|
35
|
+
function d(t = 1) {
|
|
36
|
+
return e(`inset 0 0 0 ${o(t)} ${r}`);
|
|
37
|
+
}
|
|
38
|
+
function f(n = {}) {
|
|
39
|
+
let r = new Set(n.edges ?? t), o = i(n.width ?? 1), f = i(n.padding ?? 0), p = t.map((e, t) => r.has(e) ? o[t] : 0), m = p.every((e) => e > 0 && e === p[0]), h = [`--_vf-stroke-ink: ${n.ink ?? "var(--vf-black, #000)"};`], g = [];
|
|
40
|
+
return t.forEach((e, t) => {
|
|
41
|
+
let r = p[t];
|
|
42
|
+
if (!r) {
|
|
43
|
+
n.padding !== void 0 && h.push(`padding-${e}: calc(var(--vf-scale, 1) * ${a(f[t])});`);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
h.push(`padding-${e}: ${s(f[t], r)};`), h.push(`border-${e}: ${c(r)};`), m || g.push(l(e, r));
|
|
47
|
+
}), m && g.push(String(d(p[0]))), n.shadows && g.push(String(n.shadows)), h.push(`box-shadow: ${g.length ? g.join(", ") : "none"};`), h.push(u), e(h.join("\n "));
|
|
48
|
+
}
|
|
49
|
+
function p(t, n = 1) {
|
|
50
|
+
return e(s(t, n));
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { u as STROKE_FORCED_COLORS, c as strokeBorder, l as strokeEdgeShadow, s as strokePadding, f as vfStrokeDecls, p as vfStrokeInset, d as vfStrokeShadow };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Panel recipe for menus and popups: white face, 1px black
|
|
2
|
+
* Panel recipe for menus and popups: white face, 1px black frame, hard
|
|
3
3
|
* offset shadow.
|
|
4
4
|
*/
|
|
5
5
|
export declare const vfPanel: import("lit").CSSResult;
|
|
6
6
|
/**
|
|
7
7
|
* Chrome-frame recipe for the desktop window (vf-window): the same white
|
|
8
|
-
* face, 1px black
|
|
8
|
+
* face, 1px black frame and hard offset shadow as {@link vfPanel}. Apply
|
|
9
9
|
* `.vf-frame` to the outer frame element.
|
|
10
10
|
*
|
|
11
11
|
* Skin only — the component adds its own layout (a full-size flex column: the
|
|
@@ -14,7 +14,7 @@ export declare const vfPanel: import("lit").CSSResult;
|
|
|
14
14
|
*/
|
|
15
15
|
export declare const vfChromeFrame: import("lit").CSSResult;
|
|
16
16
|
/**
|
|
17
|
-
* The classic modal-dialog (dBoxProc) frame: 1px outer
|
|
17
|
+
* The classic modal-dialog (dBoxProc) frame: 1px outer rule, 2px white gap,
|
|
18
18
|
* 2px inner band — and NO drop shadow, per the `Windows/modal dialog.png`
|
|
19
19
|
* reference. System 7's alert box drew the
|
|
20
20
|
* *mirror* trace (2px outer, 2px gap, 1px inner rule, with the hard shadow) —
|
|
@@ -30,10 +30,14 @@ export declare const vfChromeFrame: import("lit").CSSResult;
|
|
|
30
30
|
* six stripes / 3 white rows, the title patch's 7px margins) sits directly
|
|
31
31
|
* under the outer rule, inset 2px at either end so its stripes keep the same
|
|
32
32
|
* 2px of white from the outer rule that the inner band keeps; the bar's own
|
|
33
|
-
* 1px rule plus the inner box's 1px top
|
|
33
|
+
* 1px rule plus the inner box's 1px top stroke make the 2px band its floor,
|
|
34
34
|
* which then continues down both sides and along the bottom as the band
|
|
35
35
|
* around the body. No shadow anywhere. Put the `.vf-title-bar` between the
|
|
36
36
|
* two elements, as the first child of `.vf-modal-frame`, and the three rules
|
|
37
37
|
* below do the rest.
|
|
38
|
+
*
|
|
39
|
+
* Both lines are strokes ({@link vfStrokeDecls}): padding holds their space
|
|
40
|
+
* and an inset shadow paints them, so the recipe owns both elements' padding
|
|
41
|
+
* and box-shadow.
|
|
38
42
|
*/
|
|
39
43
|
export declare const vfModalFrame: import("lit").CSSResult;
|
|
@@ -1,42 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { vfHardShadow as e } from "./shadow.js";
|
|
2
|
+
import { vfStrokeDecls as t, vfStrokeInset as n } from "./stroke.js";
|
|
3
|
+
import { css as r, unsafeCSS as i } from "lit";
|
|
3
4
|
//#region src/styles/recipes/surface.ts
|
|
4
|
-
var
|
|
5
|
+
var a = i(`
|
|
5
6
|
background: var(--vf-white, #fff);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
`), i = t`
|
|
7
|
+
${t({ shadows: e })}
|
|
8
|
+
`), o = r`
|
|
9
9
|
.vf-panel {
|
|
10
|
-
${
|
|
10
|
+
${a}
|
|
11
11
|
}
|
|
12
|
-
`,
|
|
12
|
+
`, s = r`
|
|
13
13
|
.vf-frame {
|
|
14
|
-
${
|
|
14
|
+
${a}
|
|
15
15
|
}
|
|
16
|
-
`,
|
|
16
|
+
`, c = r`
|
|
17
17
|
.vf-modal-frame {
|
|
18
18
|
background: var(--vf-white, #fff);
|
|
19
|
-
|
|
19
|
+
${t()}
|
|
20
20
|
}
|
|
21
21
|
.vf-modal-frame-inner {
|
|
22
22
|
margin: calc(var(--vf-scale, 1) * 2px);
|
|
23
|
-
|
|
23
|
+
${t({ width: 2 })}
|
|
24
24
|
}
|
|
25
25
|
/* The bar set into the frame: 2px in from the outer rule at either end, its
|
|
26
26
|
stripes flush to the bar's own edge (vfStripes' 1px side inset is the
|
|
27
|
-
window frame's buffer — here the frame's gap is that buffer).
|
|
27
|
+
window frame's buffer — here the frame's gap is that buffer). The bottom
|
|
28
|
+
inset counts the bar's rule, which is padding (vfTitleBar). */
|
|
28
29
|
.vf-modal-frame > .vf-title-bar {
|
|
29
30
|
margin: 0 calc(var(--vf-scale, 1) * 2px);
|
|
30
31
|
}
|
|
31
32
|
.vf-modal-frame > .vf-title-bar .vf-stripes {
|
|
32
|
-
inset: calc(var(--vf-scale, 1) * 3px) 0;
|
|
33
|
+
inset: calc(var(--vf-scale, 1) * 3px) 0 ${n(3)};
|
|
33
34
|
}
|
|
34
|
-
/* Under a bar, the inner box's top
|
|
35
|
+
/* Under a bar, the inner box's top stroke is the second row of the band —
|
|
35
36
|
the bar's 1px rule is the first — and there is no gap row between. */
|
|
36
37
|
.vf-modal-frame > .vf-title-bar + .vf-modal-frame-inner {
|
|
37
38
|
margin-top: 0;
|
|
38
|
-
|
|
39
|
+
${t({ width: [
|
|
40
|
+
1,
|
|
41
|
+
2,
|
|
42
|
+
2,
|
|
43
|
+
2
|
|
44
|
+
] })}
|
|
39
45
|
}
|
|
40
46
|
`;
|
|
41
47
|
//#endregion
|
|
42
|
-
export {
|
|
48
|
+
export { s as vfChromeFrame, c as vfModalFrame, o as vfPanel };
|
|
@@ -32,6 +32,9 @@
|
|
|
32
32
|
* drag handle, vf-window's only when `[movable]` — suppressing touch scrolling
|
|
33
33
|
* on a bar that can't be dragged would be a real behavior change, so each
|
|
34
34
|
* component keeps that one declaration under its own selector.
|
|
35
|
+
*
|
|
36
|
+
* The rule is a stroke ({@link vfStrokeDecls}): the bottom row is padding, so
|
|
37
|
+
* the texture layers placed inside the bar count it in their bottom insets.
|
|
35
38
|
*/
|
|
36
39
|
export declare const vfTitleBar: import("lit").CSSResult;
|
|
37
40
|
/**
|
|
@@ -45,5 +48,9 @@ export declare const vfTitleBar: import("lit").CSSResult;
|
|
|
45
48
|
* Geometry is the standard 18px bar's; vf-window's utility variant overrides
|
|
46
49
|
* the sizes under its own selector. Host-state rules (hiding widgets on an
|
|
47
50
|
* inactive window) stay with the component — a dialog has no inactive state.
|
|
51
|
+
*
|
|
52
|
+
* Each box's line is a stroke ({@link vfStrokeDecls}), its white patch ring
|
|
53
|
+
* the next shadow in the same list. State rules recolor the line through
|
|
54
|
+
* `--_vf-stroke-ink`.
|
|
48
55
|
*/
|
|
49
56
|
export declare const vfWindowWidgets: import("lit").CSSResult;
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { vfDisplayDecls as e } from "./display-face.js";
|
|
2
|
-
import {
|
|
2
|
+
import { vfStrokeDecls as t, vfStrokeInset as n } from "./stroke.js";
|
|
3
|
+
import { css as r } from "lit";
|
|
3
4
|
//#region src/styles/recipes/title-bar.ts
|
|
4
|
-
var
|
|
5
|
+
var i = r`
|
|
5
6
|
.vf-title-bar {
|
|
6
7
|
position: relative;
|
|
7
8
|
/* Inert unless the frame is a flex column (vf-window); on vf-dialog's plain
|
|
8
9
|
block frame the shorthand simply doesn't apply. */
|
|
9
10
|
flex: none;
|
|
10
11
|
height: calc(var(--vf-scale, 1) * var(--vf-titlebar-height, 18px));
|
|
11
|
-
border-bottom: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
|
|
12
12
|
display: flex;
|
|
13
13
|
/* flex-start, not center: the title states its own whole-pixel row (see
|
|
14
14
|
the margin below); centering 16 into 17 would land it on a half. */
|
|
15
15
|
align-items: flex-start;
|
|
16
16
|
justify-content: center;
|
|
17
17
|
overflow: hidden;
|
|
18
|
+
${t({ edges: ["bottom"] })}
|
|
19
|
+
}
|
|
20
|
+
.vf-title-bar > .vf-stripes {
|
|
21
|
+
bottom: ${n(3)};
|
|
22
|
+
}
|
|
23
|
+
.vf-title-bar > .vf-dots {
|
|
24
|
+
bottom: ${n(2)};
|
|
18
25
|
}
|
|
19
26
|
.vf-title {
|
|
20
27
|
/* Chicago-style title (chrome); slotted body copy keeps the body face. */
|
|
@@ -36,7 +43,7 @@ var n = t`
|
|
|
36
43
|
overflow: hidden;
|
|
37
44
|
text-overflow: ellipsis;
|
|
38
45
|
}
|
|
39
|
-
`,
|
|
46
|
+
`, a = r`
|
|
40
47
|
.box {
|
|
41
48
|
position: absolute;
|
|
42
49
|
/* 11×11 box with 3px of clear white above and below it (title-bar
|
|
@@ -45,21 +52,18 @@ var n = t`
|
|
|
45
52
|
z-index: 1;
|
|
46
53
|
width: calc(var(--vf-scale, 1) * 11px);
|
|
47
54
|
height: calc(var(--vf-scale, 1) * 11px);
|
|
48
|
-
padding: 0;
|
|
49
55
|
margin: 0;
|
|
50
|
-
border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
|
|
51
56
|
background: var(--vf-white, #ffffff);
|
|
52
|
-
/* A 1px white patch ring that interrupts the stripes around the
|
|
53
|
-
box (no bevel — flat 1-bit), same buffer the stripes keep from the
|
|
54
|
-
frame edges. --vf-widget-ring is internal geometry, not a theming
|
|
55
|
-
knob: vf-window's utility variant widens it to 2px, the clearance
|
|
56
|
-
the windoid sheet cuts into its dither. */
|
|
57
|
-
box-shadow: 0 0 0 calc(var(--vf-scale, 1) * var(--vf-widget-ring, 1px))
|
|
58
|
-
var(--vf-white, #ffffff);
|
|
59
57
|
font: inherit;
|
|
60
58
|
cursor: var(--vf-cursor, default);
|
|
61
59
|
-webkit-appearance: none;
|
|
62
60
|
appearance: none;
|
|
61
|
+
/* After the stroke, a 1px white patch ring that interrupts the stripes
|
|
62
|
+
around the box (no bevel — flat 1-bit), same buffer the stripes keep
|
|
63
|
+
from the frame edges. --vf-widget-ring is internal geometry, not a
|
|
64
|
+
theming knob: vf-window's utility variant widens it to 2px, the
|
|
65
|
+
clearance the windoid sheet cuts into its dither. */
|
|
66
|
+
${t({ shadows: "0 0 0 calc(var(--vf-scale, 1) * var(--vf-widget-ring, 1px)) var(--vf-white, #ffffff)" })}
|
|
63
67
|
}
|
|
64
68
|
.close {
|
|
65
69
|
left: calc(var(--vf-scale, 1) * 8px);
|
|
@@ -73,7 +77,7 @@ var n = t`
|
|
|
73
77
|
traced pixel-for-pixel from the UI kit's close-button-active-state
|
|
74
78
|
sprite. Both widgets flash the identical graphic while pressed. That
|
|
75
79
|
sprite is the whole 11×11 box; its outer ring is this element's own 1px
|
|
76
|
-
|
|
80
|
+
stroke, so the SVG draws just the 9×9 interior, centered in the box. */
|
|
77
81
|
.box:active {
|
|
78
82
|
background-color: var(--vf-white, #ffffff);
|
|
79
83
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='9'%3E%3Cpath d='M4 0h1v1h-1zM1 1h1v1h-1zM4 1h1v1h-1zM7 1h1v1h-1zM2 2h1v1h-1zM4 2h1v1h-1zM6 2h1v1h-1zM0 4h3v1h-3zM6 4h3v1h-3zM2 6h1v1h-1zM4 6h1v1h-1zM6 6h1v1h-1zM1 7h1v1h-1zM4 7h1v1h-1zM7 7h1v1h-1zM4 8h1v1h-1z'/%3E%3C/svg%3E");
|
|
@@ -85,17 +89,16 @@ var n = t`
|
|
|
85
89
|
content: '';
|
|
86
90
|
position: absolute;
|
|
87
91
|
/* A small box nested in the TOP-LEFT corner of the widget (classic
|
|
88
|
-
System 7 zoom box). It shares the widget's own top and left
|
|
89
|
-
only its right and bottom edges are drawn: a 6×6 box anchored
|
|
90
|
-
|
|
92
|
+
System 7 zoom box). It shares the widget's own top and left line, so
|
|
93
|
+
only its right and bottom edges are drawn: a 6×6 box anchored inside
|
|
94
|
+
the widget's stroke whose 1px right/bottom strokes land the vertical at
|
|
91
95
|
sprite col 6 and the horizontal at row 6. Traced from the UI kit
|
|
92
96
|
zoom-button rest sprite. */
|
|
93
|
-
top: 0;
|
|
94
|
-
left: 0;
|
|
97
|
+
top: ${n(0)};
|
|
98
|
+
left: ${n(0)};
|
|
95
99
|
width: calc(var(--vf-scale, 1) * 6px);
|
|
96
100
|
height: calc(var(--vf-scale, 1) * 6px);
|
|
97
|
-
|
|
98
|
-
border-bottom: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
|
|
101
|
+
${t({ edges: ["right", "bottom"] })}
|
|
99
102
|
}
|
|
100
103
|
/* While pressed the zoom box shows the same sunburst as the close box, so
|
|
101
104
|
its inner detail square gives way to it. */
|
|
@@ -131,4 +134,4 @@ var n = t`
|
|
|
131
134
|
}
|
|
132
135
|
`;
|
|
133
136
|
//#endregion
|
|
134
|
-
export {
|
|
137
|
+
export { i as vfTitleBar, a as vfWindowWidgets };
|
|
@@ -7,7 +7,7 @@ var t = e`
|
|
|
7
7
|
gap: calc(var(--vf-scale, 1) * 6px);
|
|
8
8
|
cursor: var(--vf-cursor, default);
|
|
9
9
|
}
|
|
10
|
-
/* The well PAINTS 3 system px below the row top. Centering
|
|
10
|
+
/* The radio's 13px well PAINTS 3 system px below the row top. Centering it
|
|
11
11
|
in the 20px row is the layout — kept, because the host's height and its
|
|
12
12
|
exported baseline are consumer-visible (moving them off the well's
|
|
13
13
|
centered bottom reflowed every line holding a toggle) — but centering
|
|
@@ -18,8 +18,8 @@ var t = e`
|
|
|
18
18
|
compose the variable), landing the ink on row 3: ties resolve toward the
|
|
19
19
|
start, as QuickDraw's "div 2" did — the title-bar and vf-stack
|
|
20
20
|
convention. The half-pixel gap between layout box and painted box is the
|
|
21
|
-
documented snap idiom, not a fault.
|
|
22
|
-
|
|
21
|
+
documented snap idiom, not a fault. The checkbox's 12px box centers on
|
|
22
|
+
row 4 exactly and needs no step; both wells end on row 15. */
|
|
23
23
|
.circle {
|
|
24
24
|
top: calc(var(--vf-snap-dy, 0px) - var(--vf-scale, 1) * 0.5px);
|
|
25
25
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A window's chrome as numbers: the insets between its outer box and the
|
|
3
|
+
* region its content lays out in, and the floor its grow box stops at.
|
|
4
|
+
*
|
|
5
|
+
* A page sizing a window to its content, or content to its window, needs the
|
|
6
|
+
* chrome between the two — a folder window's field sized to the scrolled
|
|
7
|
+
* plane, a palette sized to hold its cells exactly. Restating `1 + 18 + 20 +
|
|
8
|
+
* 15 + 1` in the page was the alternative, with a note to re-derive it if
|
|
9
|
+
* the kit's chrome ever changed.
|
|
10
|
+
*/
|
|
11
|
+
/** The grow box's default floors, in system px: a window smaller than this can't be worked. */
|
|
12
|
+
export declare const WINDOW_MIN_WIDTH = 80;
|
|
13
|
+
export declare const WINDOW_MIN_HEIGHT = 54;
|
|
14
|
+
/** The declared options that decide a window's chrome — `vf-window`'s own. */
|
|
15
|
+
export interface VfWindowChromeOptions {
|
|
16
|
+
/** `'utility'` for the windoid's 12px bar; anything else is the 18px one. */
|
|
17
|
+
variant?: 'utility' | null;
|
|
18
|
+
/** The edge scroll rails, as on `vf-window`. */
|
|
19
|
+
scrollbars?: 'vertical' | 'horizontal' | 'both' | null;
|
|
20
|
+
/** The header's height, rule included, while the `header` slot is populated. */
|
|
21
|
+
headerHeight?: number | null;
|
|
22
|
+
/** Whether the `status` slot is populated. */
|
|
23
|
+
status?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Four insets in system px. */
|
|
26
|
+
export interface VfInsets {
|
|
27
|
+
top: number;
|
|
28
|
+
right: number;
|
|
29
|
+
bottom: number;
|
|
30
|
+
left: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The insets, in system px, between a window's outer box (its `width` and
|
|
34
|
+
* `height`) and the region its content lays out in: the body, or under
|
|
35
|
+
* `scrollbars` the scrolled plane's viewport. They go both ways — the content
|
|
36
|
+
* of a window `W` wide is `W − left − right`, and a window that holds content
|
|
37
|
+
* `w` wide is `w + left + right`. The frame's 1px stroke on every side, the
|
|
38
|
+
* title bar (18, or 12 for a utility window) and the header above, the status
|
|
39
|
+
* strip (15) and a horizontal rail (15) below, a vertical rail (15) on the
|
|
40
|
+
* right. The numbers are the default tokens': a page that restyles
|
|
41
|
+
* `--vf-titlebar-height` or `--vf-status-bar-height` restates them.
|
|
42
|
+
*/
|
|
43
|
+
export declare function windowChrome({ variant, scrollbars, headerHeight, status, }?: VfWindowChromeOptions): VfInsets;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/window-chrome.ts
|
|
2
|
+
var e = 80, t = 54;
|
|
3
|
+
function n({ variant: e, scrollbars: t, headerHeight: n, status: r = !1 } = {}) {
|
|
4
|
+
let i = t === "vertical" || t === "both", a = t === "horizontal" || t === "both";
|
|
5
|
+
return {
|
|
6
|
+
top: 1 + (e === "utility" ? 12 : 18) + Math.max(0, n ?? 0),
|
|
7
|
+
right: 1 + (i ? 15 : 0),
|
|
8
|
+
bottom: 1 + (r ? 15 : 0) + (a ? 15 : 0),
|
|
9
|
+
left: 1
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { t as WINDOW_MIN_HEIGHT, e as WINDOW_MIN_WIDTH, n as windowChrome };
|
package/dist/zoom.d.ts
CHANGED
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
*
|
|
18
18
|
* The work is knowing the zoom at all. Zoom multiplies device-px-per-CSS-px in
|
|
19
19
|
* every engine — that is what zoom is — but the engines differ in whether they
|
|
20
|
-
* report it: Chrome and
|
|
21
|
-
* `devicePixelRatio` to the hardware and moves `innerWidth`
|
|
22
|
-
* tracker watches two signals:
|
|
20
|
+
* report it: Chrome, Firefox and Safari 27 fold it into `devicePixelRatio`;
|
|
21
|
+
* older Safari pins `devicePixelRatio` to the hardware and moves `innerWidth`
|
|
22
|
+
* instead. So the tracker watches two signals:
|
|
23
23
|
*
|
|
24
24
|
* 1. `devicePixelRatio`, via a resolution media query re-armed at each new
|
|
25
25
|
* value. A dpr change is *either* zoom or the window moving to a
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* report those members in zoom-affected CSS px) means zoom; anything else
|
|
32
32
|
* means a display change, which rebases the baseline and reports nothing.
|
|
33
33
|
* 2. `innerWidth`/`innerHeight` against `outerWidth`/`outerHeight`, for
|
|
34
|
-
* Safari: zoom rescales the inner (CSS-px) viewport on both axes by the
|
|
34
|
+
* older Safari: zoom rescales the inner (CSS-px) viewport on both axes by the
|
|
35
35
|
* same factor while the outer (screen-px) window holds still. A change in
|
|
36
36
|
* the outer size is a real window resize and rebases; a one-axis inner
|
|
37
37
|
* change (an edge drag, devtools docking, a sidebar) is never zoom and
|
|
@@ -170,12 +170,12 @@ export declare function getZoom(): number;
|
|
|
170
170
|
/**
|
|
171
171
|
* Device px per CSS px *right now* — the number every "snap to the device
|
|
172
172
|
* grid" computation must divide by. `window.devicePixelRatio` is that number
|
|
173
|
-
* only in engines that fold zoom into it; Safari's is simply wrong about
|
|
174
|
-
* current rasterization density at any non-100% zoom. Reading the live dpr
|
|
175
|
-
* folding the tracked zoom back in is correct in both:
|
|
173
|
+
* only in engines that fold zoom into it; older Safari's is simply wrong about
|
|
174
|
+
* the current rasterization density at any non-100% zoom. Reading the live dpr
|
|
175
|
+
* and folding the tracked zoom back in is correct in both:
|
|
176
176
|
*
|
|
177
|
-
* Chrome/Firefox (latched) dpr already includes zoom → dpr
|
|
178
|
-
* Safari (never latches)
|
|
177
|
+
* Chrome/Firefox/Safari 27 (latched) dpr already includes zoom → dpr
|
|
178
|
+
* older Safari (never latches) dpr is the hardware → dpr × zoom
|
|
179
179
|
*
|
|
180
180
|
* The live read matters: a monitor move changes the true density whether or
|
|
181
181
|
* not the tracker is running, and this stays exactly as correct as the old
|