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.
Files changed (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +262 -0
  3. package/custom-elements.json +19807 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js +9 -0
  5. package/dist/chrome.d.ts +73 -0
  6. package/dist/chrome.js +94 -0
  7. package/dist/components/vf-button-group.d.ts +50 -0
  8. package/dist/components/vf-button-group.js +68 -0
  9. package/dist/components/vf-button.d.ts +169 -0
  10. package/dist/components/vf-button.js +234 -0
  11. package/dist/components/vf-checkbox.d.ts +60 -0
  12. package/dist/components/vf-checkbox.js +135 -0
  13. package/dist/components/vf-container.d.ts +95 -0
  14. package/dist/components/vf-container.js +86 -0
  15. package/dist/components/vf-desktop.d.ts +243 -0
  16. package/dist/components/vf-desktop.js +310 -0
  17. package/dist/components/vf-dialog.d.ts +105 -0
  18. package/dist/components/vf-dialog.js +280 -0
  19. package/dist/components/vf-fieldset.d.ts +34 -0
  20. package/dist/components/vf-fieldset.js +76 -0
  21. package/dist/components/vf-grid.d.ts +124 -0
  22. package/dist/components/vf-grid.js +133 -0
  23. package/dist/components/vf-icon.d.ts +356 -0
  24. package/dist/components/vf-icon.js +507 -0
  25. package/dist/components/vf-img.d.ts +63 -0
  26. package/dist/components/vf-img.js +65 -0
  27. package/dist/components/vf-label.d.ts +117 -0
  28. package/dist/components/vf-label.js +151 -0
  29. package/dist/components/vf-list-item.d.ts +48 -0
  30. package/dist/components/vf-list-item.js +98 -0
  31. package/dist/components/vf-list.d.ts +77 -0
  32. package/dist/components/vf-list.js +256 -0
  33. package/dist/components/vf-menu-bar.d.ts +64 -0
  34. package/dist/components/vf-menu-bar.js +236 -0
  35. package/dist/components/vf-menu-item.d.ts +91 -0
  36. package/dist/components/vf-menu-item.js +252 -0
  37. package/dist/components/vf-menu.d.ts +95 -0
  38. package/dist/components/vf-menu.js +346 -0
  39. package/dist/components/vf-number-field.d.ts +60 -0
  40. package/dist/components/vf-number-field.js +248 -0
  41. package/dist/components/vf-option.d.ts +49 -0
  42. package/dist/components/vf-option.js +119 -0
  43. package/dist/components/vf-paragraph.d.ts +73 -0
  44. package/dist/components/vf-paragraph.js +66 -0
  45. package/dist/components/vf-progress-bar.d.ts +69 -0
  46. package/dist/components/vf-progress-bar.js +272 -0
  47. package/dist/components/vf-radio-group.d.ts +109 -0
  48. package/dist/components/vf-radio-group.js +105 -0
  49. package/dist/components/vf-radio.d.ts +59 -0
  50. package/dist/components/vf-radio.js +135 -0
  51. package/dist/components/vf-scroll-area.d.ts +75 -0
  52. package/dist/components/vf-scroll-area.js +124 -0
  53. package/dist/components/vf-select.d.ts +334 -0
  54. package/dist/components/vf-select.js +658 -0
  55. package/dist/components/vf-separator.d.ts +38 -0
  56. package/dist/components/vf-separator.js +55 -0
  57. package/dist/components/vf-slider.d.ts +80 -0
  58. package/dist/components/vf-slider.js +255 -0
  59. package/dist/components/vf-stack.d.ts +188 -0
  60. package/dist/components/vf-stack.js +168 -0
  61. package/dist/components/vf-swatch.d.ts +98 -0
  62. package/dist/components/vf-swatch.js +201 -0
  63. package/dist/components/vf-text-area.d.ts +77 -0
  64. package/dist/components/vf-text-area.js +121 -0
  65. package/dist/components/vf-text-field.d.ts +53 -0
  66. package/dist/components/vf-text-field.js +79 -0
  67. package/dist/components/vf-window.d.ts +157 -0
  68. package/dist/components/vf-window.js +377 -0
  69. package/dist/cursor-art.d.ts +37 -0
  70. package/dist/cursor-art.js +41 -0
  71. package/dist/cursor.d.ts +49 -0
  72. package/dist/cursor.js +171 -0
  73. package/dist/define.d.ts +45 -0
  74. package/dist/define.js +15 -0
  75. package/dist/document-listeners.d.ts +62 -0
  76. package/dist/document-listeners.js +29 -0
  77. package/dist/drag.d.ts +49 -0
  78. package/dist/drag.js +30 -0
  79. package/dist/events.d.ts +79 -0
  80. package/dist/events.js +25 -0
  81. package/dist/focus-modality.d.ts +94 -0
  82. package/dist/focus-modality.js +56 -0
  83. package/dist/form-control.d.ts +247 -0
  84. package/dist/form-control.js +127 -0
  85. package/dist/glyphs.d.ts +117 -0
  86. package/dist/glyphs.js +100 -0
  87. package/dist/grid-snap.d.ts +73 -0
  88. package/dist/grid-snap.js +135 -0
  89. package/dist/index.d.ts +145 -0
  90. package/dist/index.js +71 -0
  91. package/dist/menu-press.d.ts +60 -0
  92. package/dist/menu-press.js +97 -0
  93. package/dist/modal-dialog.d.ts +148 -0
  94. package/dist/modal-dialog.js +155 -0
  95. package/dist/motion.d.ts +72 -0
  96. package/dist/motion.js +22 -0
  97. package/dist/number.d.ts +24 -0
  98. package/dist/number.js +12 -0
  99. package/dist/open-art.d.ts +40 -0
  100. package/dist/open-art.js +35 -0
  101. package/dist/pixel-frame.d.ts +102 -0
  102. package/dist/pixel-frame.js +77 -0
  103. package/dist/popup-overflow.d.ts +157 -0
  104. package/dist/popup-overflow.js +45 -0
  105. package/dist/position.d.ts +187 -0
  106. package/dist/position.js +74 -0
  107. package/dist/scale.d.ts +259 -0
  108. package/dist/scale.js +133 -0
  109. package/dist/scroll-rail.d.ts +112 -0
  110. package/dist/scroll-rail.js +194 -0
  111. package/dist/scroll-state.d.ts +113 -0
  112. package/dist/scroll-state.js +55 -0
  113. package/dist/size.d.ts +47 -0
  114. package/dist/size.js +30 -0
  115. package/dist/styles/base.d.ts +38 -0
  116. package/dist/styles/body-font.d.ts +7 -0
  117. package/dist/styles/body-font.js +9 -0
  118. package/dist/styles/display-font.d.ts +7 -0
  119. package/dist/styles/display-font.js +9 -0
  120. package/dist/styles/recipes/body-face.d.ts +14 -0
  121. package/dist/styles/recipes/body-face.js +6 -0
  122. package/dist/styles/recipes/display-face.d.ts +23 -0
  123. package/dist/styles/recipes/display-face.js +10 -0
  124. package/dist/styles/recipes/field.d.ts +20 -0
  125. package/dist/styles/recipes/field.js +68 -0
  126. package/dist/styles/recipes/focus.d.ts +76 -0
  127. package/dist/styles/recipes/focus.js +39 -0
  128. package/dist/styles/recipes/host.d.ts +5 -0
  129. package/dist/styles/recipes/host.js +62 -0
  130. package/dist/styles/recipes/pattern.d.ts +80 -0
  131. package/dist/styles/recipes/pattern.js +82 -0
  132. package/dist/styles/recipes/scroll-rail.d.ts +69 -0
  133. package/dist/styles/recipes/scroll-rail.js +222 -0
  134. package/dist/styles/recipes/shadow.d.ts +13 -0
  135. package/dist/styles/recipes/shadow.js +5 -0
  136. package/dist/styles/recipes/static-text.d.ts +24 -0
  137. package/dist/styles/recipes/static-text.js +17 -0
  138. package/dist/styles/recipes/surface.d.ts +28 -0
  139. package/dist/styles/recipes/surface.js +27 -0
  140. package/dist/styles/recipes/tile.d.ts +122 -0
  141. package/dist/styles/recipes/tile.js +40 -0
  142. package/dist/styles/recipes/title-bar.d.ts +49 -0
  143. package/dist/styles/recipes/title-bar.js +134 -0
  144. package/dist/styles/recipes/toggle.d.ts +10 -0
  145. package/dist/styles/recipes/toggle.js +20 -0
  146. package/dist/styles/register-embedded-font.d.ts +64 -0
  147. package/dist/styles/register-embedded-font.js +30 -0
  148. package/dist/text-control.d.ts +131 -0
  149. package/dist/text-control.js +83 -0
  150. package/dist/tile-grid.d.ts +111 -0
  151. package/dist/tile-grid.js +53 -0
  152. package/dist/toggle-control.d.ts +60 -0
  153. package/dist/toggle-control.js +41 -0
  154. package/dist/track-width.d.ts +42 -0
  155. package/dist/track-width.js +30 -0
  156. package/dist/type-ahead.d.ts +35 -0
  157. package/dist/type-ahead.js +20 -0
  158. package/dist/zoom.d.ts +204 -0
  159. package/dist/zoom.js +105 -0
  160. package/docs/SPEC.md +2471 -0
  161. package/editor/vscode.html-custom-data.json +1320 -0
  162. package/editor/web-types.json +3237 -0
  163. package/package.json +118 -0
@@ -0,0 +1,53 @@
1
+ import { VfTextControlBase } from '../text-control.js';
2
+ declare const VfTextField_base: (new (...args: any[]) => import("../position.js").VfPositionedInterface) & typeof VfTextControlBase;
3
+ /**
4
+ * `<vf-text-field>` — a System 7 single-line text entry field.
5
+ *
6
+ * A form-associated wrapper around a native `<input>`: white well, 1px solid
7
+ * black border, no corner radius. Keyboard focus draws the kit's dashed rule
8
+ * one blank row under the well; a click leaves it unmarked (no dotted outline
9
+ * either, per SPEC §1/§5). The shared field skin lives in `vfField`; the
10
+ * value/form scaffolding — and the focus rule's modality gate — in
11
+ * {@link VfTextControlBase}.
12
+ *
13
+ * @fires vf-input - On every keystroke. `detail: { value: string }`.
14
+ * @fires vf-change - On commit (native `change`). `detail: { value: string }`.
15
+ * @fires input - The native keystroke event: the inner input's own, composed,
16
+ * so it crosses the shadow boundary and retargets to the host by itself.
17
+ * @fires change - The native commit event, re-dispatched from the host (the
18
+ * inner one is `composed: false` and never leaves the shadow root), so form
19
+ * delegation and framework bindings hear it.
20
+ *
21
+ * The input-behavior attributes — `autocomplete`, `inputmode`, `enterkeyhint`,
22
+ * `maxlength`, `pattern`, `spellcheck`, `autocapitalize` — are forwarded from
23
+ * the host onto the inner input, where the platform actually honors them.
24
+ *
25
+ * @csspart input - The inner native `<input>` element.
26
+ * @cssprop [--vf-control-height=22px] - text fields — `vf-text-field`,
27
+ * `vf-text-area`, the `vf-number-field` well
28
+ * @cssprop [--vf-field-width=180px] - default width of `vf-text-field` /
29
+ * `vf-text-area`
30
+ * @cssprop [--vf-field-placeholder=#767676] - placeholder text in the editable
31
+ * fields — kept off `--vf-disabled`: a placeholder sits in an *enabled* well
32
+ * and holds AA contrast, where the disabled gray is exempt
33
+ */
34
+ export declare class VfTextField extends VfTextField_base {
35
+ static styles: import("lit").CSSResult[];
36
+ /** Input type, passed through to the native input (e.g. `password`). */
37
+ type: string;
38
+ /** The shared forwarded set plus `pattern`, which only an `<input>` takes. */
39
+ protected static readonly forwardedAttributes: readonly string[];
40
+ /**
41
+ * Enter in a single-line field triggers the associated form's implicit
42
+ * submission. The native `<input>` is shadow-encapsulated, so its form owner
43
+ * is null and the browser won't do this itself.
44
+ */
45
+ private handleKeydown;
46
+ protected render(): import("lit-html").TemplateResult<1>;
47
+ }
48
+ declare global {
49
+ interface HTMLElementTagNameMap {
50
+ 'vf-text-field': VfTextField;
51
+ }
52
+ }
53
+ export {};
@@ -0,0 +1,79 @@
1
+ import { vfElement as e } from "../define.js";
2
+ import t from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
3
+ import { VfPositioned as n } from "../position.js";
4
+ import { vfBase as r } from "../styles/recipes/host.js";
5
+ import { vfField as i } from "../styles/recipes/field.js";
6
+ import { VfTextControlBase as a } from "../text-control.js";
7
+ import { css as o, html as s, nothing as c } from "lit";
8
+ import { property as l } from "lit/decorators.js";
9
+ import { live as u } from "lit/directives/live.js";
10
+ //#region src/components/vf-text-field.ts
11
+ var d = class extends n(a) {
12
+ constructor(...e) {
13
+ super(...e), this.type = "text";
14
+ }
15
+ static {
16
+ this.styles = [
17
+ r,
18
+ i,
19
+ o`
20
+ :host {
21
+ display: inline-block;
22
+ /* A sensible default width (authored system px, scaled) so a bare field
23
+ doesn't collapse; the inner control fills it. Override with a width
24
+ on the host or the --vf-field-width token. */
25
+ width: calc(var(--vf-scale, 1) * var(--vf-field-width, 180px));
26
+ }
27
+ input {
28
+ display: block;
29
+ width: 100%;
30
+ /* min-, not height: the default line box exactly fills the 22px well,
31
+ so nothing moves — but a user stylesheet raising line-height (the
32
+ WCAG 1.4.12 text-spacing condition) grows the well instead of
33
+ clipping the text inside a pinned one. */
34
+ min-height: calc(var(--vf-scale, 1) * var(--vf-control-height, 22px));
35
+ padding: 0 calc(var(--vf-scale, 1) * 6px);
36
+ }
37
+ `
38
+ ];
39
+ }
40
+ static {
41
+ this.forwardedAttributes = [...a.forwardedAttributes, "pattern"];
42
+ }
43
+ handleKeydown(e) {
44
+ this.isSubmitEnter(e) && this.requestImplicitSubmit();
45
+ }
46
+ render() {
47
+ return s`
48
+ <div class=${this.wellClass}>
49
+ <input
50
+ part="input"
51
+ class="vf-field"
52
+ type=${this.type}
53
+ aria-label=${this.label || this.hostLabel || c}
54
+ aria-describedby=${this.describedBy}
55
+ aria-required=${this.required ? "true" : c}
56
+ aria-invalid=${this.validity.valid ? c : "true"}
57
+ autocomplete=${this.forwardedAttr("autocomplete")}
58
+ inputmode=${this.forwardedAttr("inputmode")}
59
+ enterkeyhint=${this.forwardedAttr("enterkeyhint")}
60
+ maxlength=${this.forwardedAttr("maxlength")}
61
+ pattern=${this.forwardedAttr("pattern")}
62
+ spellcheck=${this.forwardedAttr("spellcheck")}
63
+ autocapitalize=${this.forwardedAttr("autocapitalize")}
64
+ .value=${u(this.value)}
65
+ placeholder=${this.placeholder}
66
+ ?disabled=${this.isDisabled}
67
+ ?readonly=${this.readonly}
68
+ @keydown=${this.handleKeydown}
69
+ @input=${this.handleInput}
70
+ @change=${this.handleChange}
71
+ />
72
+ </div>
73
+ ${this.renderDescription()}
74
+ `;
75
+ }
76
+ };
77
+ t([l()], d.prototype, "type", void 0), d = t([e("vf-text-field")], d);
78
+ //#endregion
79
+ export { d as VfTextField };
@@ -0,0 +1,157 @@
1
+ import { LitElement, type PropertyValues } from 'lit';
2
+ import './vf-scroll-area.js';
3
+ declare const VfWindow_base: (new (...args: any[]) => import("../size.js").VfSizedInterface) & (new (...args: any[]) => import("../position.js").VfPositionedInterface) & typeof LitElement;
4
+ /**
5
+ * `<vf-window>` — the System 7 desktop-window shell.
6
+ *
7
+ * Striped title bar with optional close box (left) and zoom box (right), a
8
+ * solid-white frame with a hard offset shadow, an optional grow box for
9
+ * resizing, optional edge scroll rails (`scrollbars`), and the slim windoid
10
+ * chrome (`variant="utility"`). The HIG's window archetypes are parameter
11
+ * recipes over this shell rather than fixed anatomies — the component enables
12
+ * HIG compliance, it doesn't enforce it (see docs/LAYOUT.md "Window archetypes"):
13
+ * the full document window is `closable zoomable movable resizable
14
+ * scrollbars="both"`, a modeless dialog box is `closable movable`, a utility
15
+ * window is `variant="utility" movable`. Place inside `<vf-desktop>` to get
16
+ * click-to-front stacking and automatic `active` management (utility windows
17
+ * float above the document tier).
18
+ *
19
+ * Every recipe also declares `width` AND `height` ({@link VfSized}), in whole
20
+ * system px — the art's own unit, so the window keeps its proportions to the
21
+ * chrome inside it at every display density (a CSS-px size stays put while
22
+ * the components in it triple). A window is a fixed box in both axes, the way
23
+ * the WIND resource carried it: left to layout it takes whatever its
24
+ * container or content hands it, which is how a title bar ends up wider than
25
+ * the screen or a dialog reflows as it moves — and a window that grows with
26
+ * its body is one the user can neither predict nor (via the grow box) own.
27
+ * Content taller than the declared box is clipped at the frame the way the
28
+ * classic content region was; `scrollbars` lets the user reach the rest.
29
+ * Unset, the window still renders — normal block layout, as before — and
30
+ * says so once in the console.
31
+ *
32
+ * @slot - Default slot: window body content.
33
+ * @csspart frame - The outer chrome frame.
34
+ * @csspart title-bar - The striped (or dithered) title bar.
35
+ * @csspart title - The centered title patch (hidden on the utility bar).
36
+ * @csspart close-box - The close widget (left).
37
+ * @csspart zoom-box - The zoom widget (right).
38
+ * @csspart body - The content area.
39
+ * @csspart grow-box - The resize widget (bottom-right, when `resizable`).
40
+ * @csspart viewport - The built-in scroll area's viewport (when `scrollbars`;
41
+ * re-exported from vf-scroll-area).
42
+ * @fires vf-close - Close box clicked. Detail `{ reason: 'close' }` (shape-
43
+ * compatible with vf-dialog's `vf-close`). The window does NOT
44
+ * remove itself; the consumer decides what closing means.
45
+ * @fires vf-zoom - Zoom box clicked. Detail `{}`.
46
+ * @cssprop --vf-dots-pattern - the windoid bar's dot-grid dither — a 2×2 motif,
47
+ * one black pixel at its origin, on a 30-system-px tile (`vfDots`; override
48
+ * the whole tile like `--vf-desktop-pattern` — consumer art renders as a
49
+ * placed tile grid at that same geometry)
50
+ * @cssprop --vf-titlebar-height - window/dialog title bars
51
+ * @cssprop [--vf-titlebar-height-utility=12px] - the slim
52
+ * `vf-window[variant="utility"]` (windoid) bar — 11px interior + 1px bottom
53
+ * rule, traced from `Windows/utility-window.png`
54
+ */
55
+ export declare class VfWindow extends VfWindow_base {
56
+ #private;
57
+ static styles: import("lit").CSSResult[];
58
+ /**
59
+ * Chrome variant. Omit for the standard 18px striped bar; `'utility'` for
60
+ * the slim windoid bar (dot-grid dither, 7×7 widgets, no title patch — the
61
+ * heading still names the widgets). Inside a `vf-desktop`, utility windows
62
+ * float above the document tier and never take the single-active state.
63
+ */
64
+ variant?: 'utility';
65
+ /** Title text shown centered in the title bar. */
66
+ heading: string;
67
+ /**
68
+ * Whether this is the frontmost (active) window: stripes and widgets show.
69
+ * Managed automatically by an enclosing `vf-desktop`.
70
+ */
71
+ active: boolean;
72
+ /** Show the close box (left side of the title bar). */
73
+ closable: boolean;
74
+ /** Show the zoom box (right side of the title bar). */
75
+ zoomable: boolean;
76
+ /** Allow dragging the window by its title bar. */
77
+ movable: boolean;
78
+ /** Show a grow box at the bottom-right corner for resizing. */
79
+ resizable: boolean;
80
+ /** Remove the default 12px body padding. */
81
+ flush: boolean;
82
+ /**
83
+ * Put System 7 scroll rails on the window edge — the classic document
84
+ * window. The body slot renders inside a built-in `vf-scroll-area` pulled
85
+ * one system pixel under the frame on every side, so the rails repaint the
86
+ * border lines and a `resizable` window's grow box lands in the corner
87
+ * cell. Values mirror `vf-scroll-area`'s `axis`; the `heading` names the
88
+ * scroll region; the viewport part is re-exported. Implies `flush` (the
89
+ * viewport carries its own padding). The slotted composition (SPEC §5
90
+ * vf-scroll-area) still works for windows that want an inset well instead.
91
+ */
92
+ scrollbars?: 'vertical' | 'horizontal' | 'both';
93
+ /** Default-on display scaling (true 72dpi size); see src/scale.ts. */
94
+ private readonly scale;
95
+ /** Device-pixel grid snapping (opt in with applyGridSnap()); see src/grid-snap.ts. */
96
+ private readonly gridSnap;
97
+ /** Holds the centered title patch on the placement lattice (src/chrome.ts). */
98
+ private readonly titleCenter;
99
+ /**
100
+ * The consumer's `--vf-dots-pattern` override, or `''` for the kit dots —
101
+ * which exact-fill path the utility bar takes (src/tile-grid.ts). Re-read
102
+ * every update; a runtime token swap wants a `requestUpdate()`.
103
+ */
104
+ private _dotsPattern;
105
+ protected willUpdate(changed: PropertyValues<this>): void;
106
+ /**
107
+ * The exact dots fill rendered into the utility bar's `.vf-dots` layer
108
+ * (src/tile-grid.ts): the bar interior is the declared width minus the
109
+ * frame borders. The kit raster is ceiled to whole 30-px tiles so a
110
+ * grow-box resize only re-encodes the image when it crosses a tile
111
+ * boundary — the layer's clip crops the overdraw; a consumer
112
+ * `--vf-dots-pattern` renders the placed tile grid at the token's
113
+ * documented 30-px geometry instead. A window with no declared width
114
+ * renders neither and the layer keeps its CSS-repeated tile.
115
+ */
116
+ private _dotsTexture;
117
+ /**
118
+ * Drag placement: the origin lands in `top`/`left` in system px, so a moved
119
+ * window is placed the way an authored one is and holds its spot through a
120
+ * zoom (see src/position.ts).
121
+ */
122
+ private readonly _placement;
123
+ /**
124
+ * Title-bar drag-to-move (shared with `vf-dialog` via {@link DragController}).
125
+ * The controller owns the pointer bookkeeping and hands over system px; the
126
+ * placement controller seeds the origin and writes the result.
127
+ */
128
+ private readonly _drag;
129
+ private _resizeState;
130
+ /**
131
+ * Say something the first time a window is opened without a width. The size
132
+ * itself is written by VfSized's controller, from `width`/`height` — which
133
+ * is also what the grow box sets, so a resized window and an authored one
134
+ * are the same declaration and neither can be re-asserted over the other.
135
+ * Controllers run before this hook, so the inline style the warning reads is
136
+ * already written.
137
+ */
138
+ protected updated(): void;
139
+ disconnectedCallback(): void;
140
+ private _onCloseClick;
141
+ private _onZoomClick;
142
+ private _onGrowPointerDown;
143
+ private _onGrowPointerMove;
144
+ /**
145
+ * Ends a grow-box resize on pointerup / pointercancel / lostpointercapture.
146
+ * Idempotent — releasing the capture below re-enters here, and by then
147
+ * `_resizeState` is already null.
148
+ */
149
+ private _onGrowPointerEnd;
150
+ protected render(): unknown;
151
+ }
152
+ declare global {
153
+ interface HTMLElementTagNameMap {
154
+ 'vf-window': VfWindow;
155
+ }
156
+ }
157
+ export {};
@@ -0,0 +1,377 @@
1
+ import { vfElement as e } from "../define.js";
2
+ import { ScaleController as t, snapSys as n, sysLength as r, toSysExact as i } from "../scale.js";
3
+ import a from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
4
+ import { PlacementController as o, VfPositioned as s, warnMovableContract as c } from "../position.js";
5
+ import { vfBase as l } from "../styles/recipes/host.js";
6
+ import { DOT_RECTS as u, DOT_SPAN as d, vfDots as f, vfStripes as p } from "../styles/recipes/pattern.js";
7
+ import { vfChromeFrame as m } from "../styles/recipes/surface.js";
8
+ import { vfTitleBar as h, vfWindowWidgets as g } from "../styles/recipes/title-bar.js";
9
+ import { vfFocus as _ } from "../styles/recipes/focus.js";
10
+ import { TileRasterCache as v, patternOverride as y, tileGrid as b, vfTileGrid as x } from "../tile-grid.js";
11
+ import { GridSnapController as S } from "../grid-snap.js";
12
+ import { VfSized as C } from "../size.js";
13
+ import { DragController as w } from "../drag.js";
14
+ import { TitleCenterController as T, chromeTitleBar as E, closeBox as D, widgetLabel as O, zoomBox as k } from "../chrome.js";
15
+ import { emit as A } from "../events.js";
16
+ import "./vf-scroll-area.js";
17
+ import { LitElement as j, css as M, html as N, nothing as P } from "lit";
18
+ import { property as F } from "lit/decorators.js";
19
+ //#region src/components/vf-window.ts
20
+ var I = 80, L = 54, R = 24, z = 8, B = class extends C(s(j)) {
21
+ constructor(...e) {
22
+ super(...e), this.heading = "", this.active = !0, this.closable = !0, this.zoomable = !1, this.movable = !1, this.resizable = !1, this.flush = !1, this.scale = new t(this), this.gridSnap = new S(this), this.titleCenter = new T(this), this._dotsPattern = "", this.#e = new v(), this.#t = (e, t, n) => {
23
+ let r = i(this.offsetWidth, this);
24
+ return {
25
+ x: Math.min(Math.max(e, R - r), n.width - R),
26
+ y: Math.min(Math.max(t, 0), Math.max(0, n.height - R))
27
+ };
28
+ }, this._placement = new o(this, (e, t, n) => this.#t(e, t, n)), this._drag = new w(this, {
29
+ onDragStart: (e) => !this.movable || e.button !== 0 || e.composedPath().some((e) => e instanceof HTMLElement && e.classList.contains("box")) ? null : (this.#i(), this._placement.seed()),
30
+ onDrag: (e, t) => this._placement.moveTo(e, t)
31
+ }), this._resizeState = null, this.#n = !1, this.#r = !1;
32
+ }
33
+ static {
34
+ this.styles = [
35
+ l,
36
+ p,
37
+ f,
38
+ x,
39
+ _,
40
+ m,
41
+ h,
42
+ g,
43
+ M`
44
+ :host {
45
+ display: block;
46
+ position: relative;
47
+ --vf-surface: var(--vf-white, #ffffff);
48
+ }
49
+ /* Skin from vfChromeFrame; the layout is the window's own — a full-size
50
+ flex column so the body takes the slack the title bar and grow box
51
+ don't. */
52
+ .vf-frame {
53
+ position: relative;
54
+ display: flex;
55
+ flex-direction: column;
56
+ width: 100%;
57
+ height: 100%;
58
+ }
59
+
60
+ /* --- Title bar ------------------------------------------------- */
61
+ /* Clearance for the close/zoom widgets either side of the title patch:
62
+ 8px offset + an 11px box + its 1px white patch ring, doubled, rounded
63
+ up to the classic 30-per-side. Not a theming knob — it's the widgets'
64
+ own geometry — so it's set on the element, like --vf-focus-offset. */
65
+ .vf-title {
66
+ --vf-title-inset: 60px;
67
+ }
68
+ :host([movable]) .vf-title-bar {
69
+ touch-action: none;
70
+ }
71
+ :host(:not([active])) .vf-stripes,
72
+ :host(:not([active])) .vf-dots {
73
+ display: none;
74
+ }
75
+ /* Inactive window: the stripes go away and the widgets stop being
76
+ drawn (see the widget rule below — they keep their tab stops), but
77
+ the title text stays black — classic System 7 never grayed the
78
+ title. */
79
+
80
+ /* --- Window widgets (close / zoom boxes) ----------------------- */
81
+ /* Skin from vfWindowWidgets (shared with vf-dialog); only the
82
+ host-state rule is the window's own — a dialog has no inactive
83
+ state, so blanking widgets with the stripes lives here.
84
+
85
+ Blanked with transparent ink, NOT display/visibility: System 7 drew
86
+ an inactive bar bare, and this reproduces exactly that — but the
87
+ controls stay in the tree and the tab order, a modern affordance the
88
+ kit adds (SPEC §1). A background window whose body holds nothing
89
+ focusable (a static About window, the modeless-dialog recipe, a
90
+ palette of labels) is otherwise unreachable by keyboard entirely:
91
+ Tab must be able to land on "Close Bravo" so vf-desktop's focusin
92
+ raise can activate the window — which repaints the widgets under
93
+ the focus that just arrived. It also means deactivation never yanks
94
+ a focused widget out of the tree, so focus can't be dropped to
95
+ <body> by a click on another window. */
96
+ :host(:not([active])) .box {
97
+ border-color: transparent;
98
+ background-color: transparent;
99
+ box-shadow: none;
100
+ }
101
+ :host(:not([active])) .zoom::after {
102
+ border-color: transparent;
103
+ }
104
+
105
+ /* --- Utility (windoid) variant --------------------------------- */
106
+ /* The slim floating-window bar, traced from Windows/utility-window.png
107
+ : 11px interior over the 1px rule
108
+ (--vf-titlebar-height-utility: 12px), the vf-dots dither instead of
109
+ stripes, and 7×7 widgets at left:7px / right:8px — the art really is
110
+ asymmetric by that pixel. No title patch: the bar is two system px
111
+ shorter than the display face's line box, so the heading feeds the
112
+ widget labels instead (a consumer retheming a taller bar can re-show
113
+ it through ::part(title)). */
114
+ :host([variant='utility']) .vf-title-bar {
115
+ height: calc(
116
+ var(--vf-scale, 1) * var(--vf-titlebar-height-utility, 12px)
117
+ );
118
+ }
119
+ :host([variant='utility']) .vf-title {
120
+ display: none;
121
+ }
122
+ :host([variant='utility']) .box {
123
+ /* 7×7 box with 2px of clear white above and below (bar interior is
124
+ 11px: 2 + 7 + 2). The patch ring stays 2px here where the striped
125
+ bar's is 1px: the windoid sheet deliberately clears two px of
126
+ dither beside its widgets — the flush dot grid lands a dot in the
127
+ column adjacent to the box, and the art blanks it. A custom
128
+ property rather than box-shadow so the
129
+ inactive blanking rule above still wins the cascade. */
130
+ --vf-widget-ring: 2px;
131
+ top: calc(var(--vf-scale, 1) * 2px);
132
+ width: calc(var(--vf-scale, 1) * 7px);
133
+ height: calc(var(--vf-scale, 1) * 7px);
134
+ }
135
+ :host([variant='utility']) .close {
136
+ left: calc(var(--vf-scale, 1) * 7px);
137
+ }
138
+ :host([variant='utility']) .zoom {
139
+ right: calc(var(--vf-scale, 1) * 8px);
140
+ }
141
+ /* Pressed windoid widget: the whole box inverts — black interior under
142
+ a white (invisible, over the patch ring) borderline — rather than the
143
+ big bar's 9×9 sunburst, which cannot land whole on a 5×5 interior. */
144
+ :host([variant='utility']) .box:active {
145
+ border-color: var(--vf-white, #ffffff);
146
+ background-color: var(--vf-black, #000000);
147
+ background-image: none;
148
+ }
149
+ /* The nested zoom square, miniaturized: right/bottom edges land at
150
+ sprite col/row 3 of the 7×7 box (padding col/row 2). */
151
+ :host([variant='utility']) .zoom::after {
152
+ width: calc(var(--vf-scale, 1) * 3px);
153
+ height: calc(var(--vf-scale, 1) * 3px);
154
+ }
155
+
156
+ /* --- Body ------------------------------------------------------ */
157
+ /* Clipped at the frame, the way the classic content region was: the
158
+ window is a fixed box (width and height are both declared), so content
159
+ taller than it is content the user reaches with the scrollbars
160
+ parameter, not something that paints out over the desktop.
161
+
162
+ What this deliberately does NOT clip is a control's drop-open panel.
163
+ vf-select's list is position:fixed, computed from the control's rect,
164
+ precisely so it escapes clipping ancestors (see vf-select.ts) — and it
165
+ still escapes this one, because nothing between it and the viewport
166
+ establishes a containing block for fixed descendants: the grid-snap
167
+ correction is a position:relative left/top offset, never a transform
168
+ (see grid-snap.ts, "Why an offset and not a transform"). So a popup
169
+ opening near the bottom edge still runs past the window border, as it
170
+ should. A vf-menu panel is anchored position:absolute and would clip —
171
+ but a menu bar belongs to the desktop, not inside a window body. */
172
+ .body {
173
+ /* The positioning anchor for slotted children placed with top/left
174
+ (src/position.ts): coordinates measure from the content region's
175
+ corner — the frame's inner edge, below the title bar — exactly the
176
+ DITL convention. CSS anchors absolute children at the padding box,
177
+ so the 12px inset below governs flow content only. */
178
+ position: relative;
179
+ flex: 1 1 auto;
180
+ min-height: 0;
181
+ overflow: hidden;
182
+ padding: calc(var(--vf-scale, 1) * 12px);
183
+ }
184
+ :host([flush]) .body,
185
+ :host([scrollbars]) .body {
186
+ padding: 0;
187
+ }
188
+ /* The edge-rail composition pulls the scroll area one system px OUT of
189
+ the body on every side so its frame border repaints the window's
190
+ border (see .edge-scroll below) — clipping the body would shave
191
+ exactly that overhang off. The scroll area does its own clipping. */
192
+ :host([scrollbars]) .body {
193
+ overflow: visible;
194
+ }
195
+
196
+ /* --- Edge scroll rails (scrollbars) ----------------------------- */
197
+ /* The TeachText composition (SPEC §5 vf-scroll-area), internalized:
198
+ the built-in scroll area is pulled one system pixel under the window
199
+ frame on every side, so its own frame border repaints the window's
200
+ border lines exactly (no doubled frame), the rails run edge to edge,
201
+ and a resizable window's grow box (z-index 1) lands over the rail
202
+ corner cell. */
203
+ .edge-scroll {
204
+ width: calc(100% + var(--vf-scale, 1) * 2px);
205
+ height: calc(100% + var(--vf-scale, 1) * 2px);
206
+ margin: calc(var(--vf-scale, 1) * -1px);
207
+ }
208
+
209
+ /* --- Grow box --------------------------------------------------- */
210
+ .grow {
211
+ position: absolute;
212
+ right: 0;
213
+ bottom: 0;
214
+ z-index: 1;
215
+ width: calc(var(--vf-scale, 1) * 15px);
216
+ height: calc(var(--vf-scale, 1) * 15px);
217
+ border-top: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
218
+ border-left: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
219
+ background: var(--vf-white, #ffffff);
220
+ touch-action: none;
221
+ cursor: var(--vf-cursor, default);
222
+ }
223
+ .grow::before {
224
+ content: '';
225
+ position: absolute;
226
+ right: calc(var(--vf-scale, 1) * 2px);
227
+ bottom: calc(var(--vf-scale, 1) * 2px);
228
+ width: calc(var(--vf-scale, 1) * 9px);
229
+ height: calc(var(--vf-scale, 1) * 9px);
230
+ border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
231
+ }
232
+ .grow::after {
233
+ content: '';
234
+ position: absolute;
235
+ top: calc(var(--vf-scale, 1) * 2px);
236
+ left: calc(var(--vf-scale, 1) * 2px);
237
+ width: calc(var(--vf-scale, 1) * 7px);
238
+ height: calc(var(--vf-scale, 1) * 7px);
239
+ border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
240
+ background: var(--vf-white, #ffffff);
241
+ }
242
+ /* Inactive window: the size box empties with the scroll rails (the same
243
+ HIG no-interactive-UX treatment ScrollStateController drives for the
244
+ bars) — its cell and borders stay, the nested squares go. */
245
+ :host(:not([active])) .grow::before,
246
+ :host(:not([active])) .grow::after {
247
+ display: none;
248
+ }
249
+ `
250
+ ];
251
+ }
252
+ #e;
253
+ willUpdate(e) {
254
+ super.willUpdate(e), this.variant === "utility" && (this._dotsPattern = y(this, "--vf-dots-pattern"));
255
+ }
256
+ _dotsTexture() {
257
+ if (this.width == null) return;
258
+ let e = Math.max(1, this.width - 2);
259
+ if (this._dotsPattern) return b({
260
+ cols: Math.ceil(e / d),
261
+ rows: 1,
262
+ tile: d
263
+ });
264
+ let t = Math.ceil((e + 2) / d) * d, n = z + 2;
265
+ return N`<div
266
+ class="vf-tile-raster"
267
+ style="width:${r(t)};height:${r(n)};background-image:${this.#e.for(2, 2, u, t, n)}"
268
+ ></div>`;
269
+ }
270
+ #t;
271
+ updated() {
272
+ this.#a();
273
+ }
274
+ #n;
275
+ #r;
276
+ #i() {
277
+ this.#r ||= c(this, `vf-window${this.heading ? ` ("${this.heading}")` : ""}`, "<vf-window movable top=\"30\" left=\"40\" width=\"240\" height=\"176\">");
278
+ }
279
+ #a() {
280
+ if (this.#n) return;
281
+ let e = [];
282
+ this.width == null && this.style.width === "" && e.push("width (falling back to block layout)"), this.height == null && this.style.height === "" && e.push("height (falling back to the content)"), e.length !== 0 && (this.#n = !0, console.warn(`vf-window${this.heading ? ` ("${this.heading}")` : ""}: no ${e.join(", no ")}. A window owns its whole box — declare both in system px: <vf-window width="240" height="176">.`));
283
+ }
284
+ disconnectedCallback() {
285
+ super.disconnectedCallback(), this._resizeState = null;
286
+ }
287
+ _onCloseClick() {
288
+ A(this, "vf-close", { reason: "close" });
289
+ }
290
+ _onZoomClick() {
291
+ A(this, "vf-zoom", {});
292
+ }
293
+ _onGrowPointerDown(e) {
294
+ if (!this.resizable || e.button !== 0) return;
295
+ let t = this.getBoundingClientRect();
296
+ this._resizeState = {
297
+ pointerId: e.pointerId,
298
+ startX: e.clientX,
299
+ startY: e.clientY,
300
+ baseWidth: i(t.width, this),
301
+ baseHeight: i(t.height, this)
302
+ }, e.currentTarget.setPointerCapture(e.pointerId), e.preventDefault();
303
+ }
304
+ _onGrowPointerMove(e) {
305
+ let t = this._resizeState;
306
+ if (!t || e.pointerId !== t.pointerId) return;
307
+ let r = Math.max(I, t.baseWidth + i(e.clientX - t.startX, this)), a = Math.max(L, t.baseHeight + i(e.clientY - t.startY, this));
308
+ this.width = n(r, this), this.height = n(a, this);
309
+ }
310
+ _onGrowPointerEnd(e) {
311
+ let t = this._resizeState;
312
+ if (!t || e.pointerId !== t.pointerId) return;
313
+ this._resizeState = null;
314
+ let n = e.currentTarget;
315
+ n.hasPointerCapture(e.pointerId) && n.releasePointerCapture(e.pointerId);
316
+ }
317
+ render() {
318
+ return N`
319
+ <div
320
+ class="vf-frame vf-snap"
321
+ part="frame"
322
+ role="group"
323
+ aria-labelledby=${this.heading ? "title" : P}
324
+ >
325
+ ${E(this._drag, N`
326
+ ${this.closable ? D(O("Close", this.heading), this._onCloseClick) : P}
327
+ <span class="vf-title" part="title" id="title">${this.heading}</span>
328
+ ${this.zoomable ? k(O("Zoom", this.heading), this._onZoomClick) : P}
329
+ `, this.variant === "utility" ? "vf-dots" : "vf-stripes", this.variant === "utility" ? this._dotsTexture() : void 0)}
330
+ <div class="body" part="body">
331
+ ${this.scrollbars ? N`
332
+ <vf-scroll-area
333
+ class="edge-scroll"
334
+ axis=${this.scrollbars}
335
+ label=${this.heading || P}
336
+ exportparts="viewport"
337
+ >
338
+ <slot></slot>
339
+ </vf-scroll-area>
340
+ ` : N`<slot></slot>`}
341
+ </div>
342
+ ${this.resizable ? N`
343
+ <div
344
+ class="grow"
345
+ part="grow-box"
346
+ @pointerdown=${this._onGrowPointerDown}
347
+ @pointermove=${this._onGrowPointerMove}
348
+ @pointerup=${this._onGrowPointerEnd}
349
+ @pointercancel=${this._onGrowPointerEnd}
350
+ @lostpointercapture=${this._onGrowPointerEnd}
351
+ ></div>
352
+ ` : P}
353
+ </div>
354
+ `;
355
+ }
356
+ };
357
+ a([F({ reflect: !0 })], B.prototype, "variant", void 0), a([F()], B.prototype, "heading", void 0), a([F({
358
+ type: Boolean,
359
+ reflect: !0
360
+ })], B.prototype, "active", void 0), a([F({
361
+ type: Boolean,
362
+ reflect: !0
363
+ })], B.prototype, "closable", void 0), a([F({
364
+ type: Boolean,
365
+ reflect: !0
366
+ })], B.prototype, "zoomable", void 0), a([F({
367
+ type: Boolean,
368
+ reflect: !0
369
+ })], B.prototype, "movable", void 0), a([F({
370
+ type: Boolean,
371
+ reflect: !0
372
+ })], B.prototype, "resizable", void 0), a([F({
373
+ type: Boolean,
374
+ reflect: !0
375
+ })], B.prototype, "flush", void 0), a([F({ reflect: !0 })], B.prototype, "scrollbars", void 0), B = a([e("vf-window")], B);
376
+ //#endregion
377
+ export { B as VfWindow };