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,658 @@
1
+ import { vfElement as e } from "../define.js";
2
+ import { ScaleController as t, sys as n } from "../scale.js";
3
+ import r from "../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
4
+ import { VfPositioned as i } from "../position.js";
5
+ import { vfDisplay as a } from "../styles/recipes/display-face.js";
6
+ import { vfBase as o } from "../styles/recipes/host.js";
7
+ import { vfPanel as s } from "../styles/recipes/surface.js";
8
+ import { vfFocusUnderline as c } from "../styles/recipes/focus.js";
9
+ import { GridSnapController as l } from "../grid-snap.js";
10
+ import { DocumentListenersController as u } from "../document-listeners.js";
11
+ import { emit as d, emitNative as f } from "../events.js";
12
+ import { runSelectionBlink as p } from "../motion.js";
13
+ import { CARET_DOWN as m, CARET_UP as h, glyphSvg as g } from "../glyphs.js";
14
+ import { VfShadowRoleControl as _ } from "../form-control.js";
15
+ import { FocusRuleController as v } from "../focus-modality.js";
16
+ import { VfOption as y } from "./vf-option.js";
17
+ import { clampScroll as b, ensureVisibleScroll as x, firstPickableRow as S, lastPickableRow as C, layoutClampedPopup as w } from "../popup-overflow.js";
18
+ import { TypeAheadBuffer as T } from "../type-ahead.js";
19
+ import { css as E, html as D, nothing as O } from "lit";
20
+ import { property as k, query as A, queryAssignedElements as j, state as M } from "lit/decorators.js";
21
+ import { classMap as N } from "lit/directives/class-map.js";
22
+ //#region src/components/vf-select.ts
23
+ var P, F = class extends i(_) {
24
+ static {
25
+ P = this;
26
+ }
27
+ static {
28
+ this.ITEM_HEIGHT = 16;
29
+ }
30
+ static {
31
+ this.styles = [
32
+ o,
33
+ a,
34
+ s,
35
+ E`
36
+ :host {
37
+ /* A popup menu is sized to its widest option — never stretched to fill
38
+ its container. fit-content holds that intrinsic width even inside a
39
+ stretching flex/grid parent (align-items / justify-items: stretch act
40
+ only on auto sizes), while still shrinking if the container is genuinely
41
+ too narrow. Authors opt into filling via flex:1 / width / align-self. */
42
+ display: inline-block;
43
+ width: fit-content;
44
+ }
45
+ .control {
46
+ /* Also the anchor the focus rule below hangs from. */
47
+ position: relative;
48
+ display: flex;
49
+ align-items: center;
50
+ gap: calc(var(--vf-scale, 1) * 8px);
51
+ /* No intrinsic min-width: the control hugs the WIDEST option (via the
52
+ label sizer below). Authors wanting a floor set min-width on the host,
53
+ or grow it in their own layout (e.g. flex: 1). */
54
+ width: 100%;
55
+ /* 18px, not the 22px fields: the reference pill measures 156×18 plus
56
+ its 1px hard shadow (the 157×19 ink box on the sheet). */
57
+ height: calc(var(--vf-scale, 1) * var(--vf-popup-height, 18px));
58
+ /* Left inset = the checkmark gutter (--vf-select-gutter), so the selected
59
+ label sits at the SAME x it will occupy in the open list (where the ✓
60
+ fills that gutter). The right inset stays the small 8px. */
61
+ padding: 0 calc(var(--vf-scale, 1) * 8px) 0
62
+ calc(var(--vf-scale, 1) * var(--vf-select-gutter, 16px));
63
+ background: var(--vf-white, #fff);
64
+ border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000);
65
+ border-radius: 0;
66
+ box-shadow: calc(var(--vf-scale, 1) * 1px) calc(var(--vf-scale, 1) * 1px)
67
+ 0 0 var(--vf-black, #000);
68
+ /* The press-drag gesture owns pointer moves while the button is held;
69
+ suppress the browser's own touch panning/scrolling so a touch drag
70
+ tracks the list instead of scrolling the page. */
71
+ touch-action: none;
72
+ cursor: var(--vf-cursor, default);
73
+ }
74
+ /* Keyboard focus is the kit's dashed rule under the pill, not a ring
75
+ around it (see vfFocusUnderline). It goes BELOW the whole box rather
76
+ than inside the face the way vf-button underlines its label: the pill
77
+ has one line and the label already shares it with the ▼.
78
+
79
+ The offset counts every row of ink below the pseudo-element's padding
80
+ box before the blank row and the rule itself — the 1px border, then
81
+ the 1px hard shadow: −(1 + 1 + 1 + 1). The ±1px sides widen it from
82
+ that same padding box to the border box, which is the shape the pill
83
+ reads as (the shadow is a depth cue, not part of the silhouette).
84
+
85
+ Gated on a class, not :focus-visible — the same problem the editable
86
+ fields have, arrived at from the other direction. The pill suppresses
87
+ the browser's own mouse focus (the press-drag gesture owns it) and
88
+ calls focus() itself: on pointerdown to open, and again when a release
89
+ closes the list. Blink reads a scripted focus as a visible one, so
90
+ :focus-visible is TRUE after a pure mouse round-trip through the menu,
91
+ where on a button it is false. FocusRuleController consults the page's
92
+ last input modality instead (see src/focus-modality.ts), and render()
93
+ holds the class off while the list is open — see there. */
94
+ .control:focus-visible {
95
+ outline: none;
96
+ }
97
+ .control.vf-focus-rule::after {
98
+ --vf-focus-underline-offset: -4px;
99
+ ${c}
100
+ left: calc(var(--vf-scale, 1) * -1px);
101
+ right: calc(var(--vf-scale, 1) * -1px);
102
+ }
103
+ /* The label is a 1×1 grid: the visible value and an invisible stack of
104
+ every option's text share the one cell, so the cell — and thus the
105
+ control and the open panel — is sized to the WIDEST option. The closed
106
+ pill and the open list are therefore always exactly the same width, and
107
+ the value never shifts as the selection changes. */
108
+ .label {
109
+ flex: 1 1 auto;
110
+ display: grid;
111
+ min-width: 0;
112
+ text-align: left;
113
+ }
114
+ .label > .value,
115
+ .label > .sizer {
116
+ grid-area: 1 / 1;
117
+ min-width: 0;
118
+ white-space: nowrap;
119
+ }
120
+ .value {
121
+ overflow: hidden;
122
+ text-overflow: ellipsis;
123
+ }
124
+ /* Sizer contributes width only: collapsed to zero height and clipped so it
125
+ never adds a row, but its widest line still drives the grid cell width. */
126
+ .sizer {
127
+ display: flex;
128
+ flex-direction: column;
129
+ height: 0;
130
+ overflow: hidden;
131
+ visibility: hidden;
132
+ pointer-events: none;
133
+ }
134
+ .arrow {
135
+ flex: none;
136
+ display: flex;
137
+ align-items: center;
138
+ /* Stays solid black even when disabled — only the label dims. */
139
+ color: var(--vf-black, #000);
140
+ }
141
+ .arrow svg {
142
+ display: block;
143
+ width: calc(var(--vf-scale, 1) * 11px);
144
+ height: calc(var(--vf-scale, 1) * 6px);
145
+ }
146
+ /* Disabled: only the value label dims; the box, hard shadow and ▼ arrow
147
+ stay solid black (System 7 dims the label, not the control). */
148
+ :host([disabled]) .label,
149
+ .control.disabled .label {
150
+ color: var(--vf-disabled, #c0c0c0);
151
+ }
152
+ .panel {
153
+ display: none;
154
+ position: fixed;
155
+ z-index: 10000;
156
+ margin: 0;
157
+ padding: 0;
158
+ /* Match the closed pill's 1px hard shadow — the shared .vf-panel recipe
159
+ defaults to the 2px menu shadow, which would overhang the pill's shadow
160
+ by 1px on the right and bottom. */
161
+ --vf-shadow-offset: 1px;
162
+ /* A CLIP, never a scroll surface: positionPanel gives the panel a whole
163
+ number of row slots and the rows are rolled by transform inside it.
164
+ System 7 put no scrollbar on a menu, and an un-quantized native scroll
165
+ would break the row lattice the pill overlay is built on. */
166
+ overflow: hidden;
167
+ /* The screen-edge reserve, resolved here and read back by
168
+ positionPanel: the clamp is JS geometry, so the tokens can't be spent
169
+ in a declaration, but their defaults and the cascade still belong in
170
+ the stylesheet with every other token. Authored (unscaled) system px,
171
+ like --vf-popup-height. Internal names — the knobs are
172
+ --vf-popup-inset-top / --vf-popup-inset-bottom. */
173
+ --vf-popup-clamp-top: var(--vf-popup-inset-top, 4px);
174
+ --vf-popup-clamp-bottom: var(--vf-popup-inset-bottom, 4px);
175
+ }
176
+ .panel.open {
177
+ display: block;
178
+ }
179
+ /* The rolling strip — a box for the options to ride, whose translateY
180
+ positionPanel and the arrow timer write. Transform rather than
181
+ scrollTop: both keep
182
+ getBoundingClientRect() truthful for hit-testing, but a transform can't
183
+ be hijacked — the browser will scroll an overflow:hidden box on its own
184
+ initiative (focus without preventScroll, find-in-page, an outside
185
+ scrollIntoView), and any un-quantized scroll lands the rows off the
186
+ lattice. The offset is a whole count of rows, and a row is a whole count
187
+ of device px by the layout contract, so rolled rows stay on the grid. */
188
+ .rows {
189
+ display: block;
190
+ }
191
+ /* An arrow OVERLAYS the edge slot — an opaque white row covering the item
192
+ beneath, which is the mechanism rather than a side effect: the row is
193
+ still there, still in the accessibility tree, just not pickable while
194
+ the arrow is on it. */
195
+ .arrow-slot {
196
+ display: none;
197
+ position: absolute;
198
+ left: 0;
199
+ right: 0;
200
+ height: calc(var(--vf-scale, 1) * (var(--vf-popup-height, 18px) - 2px));
201
+ background: var(--vf-white, #fff);
202
+ color: var(--vf-black, #000);
203
+ cursor: var(--vf-cursor, default);
204
+ }
205
+ .arrow-slot.shown {
206
+ display: block;
207
+ }
208
+ .arrow-slot.up {
209
+ top: 0;
210
+ }
211
+ .arrow-slot.down {
212
+ bottom: 0;
213
+ }
214
+ /* Pinned at whole offsets rather than centred, the way vf-option pins its
215
+ ✓ at 3,3. Traced from a real System 7 popup clipped at the screen edge
216
+ (Find File's criteria menu under Infinite Mac, 2×): the 11×6 triangle's
217
+ ink sits 13px in from the panel's content edge and 5px down its 16px row
218
+ — which is the row's exact vertical centre, and 5 is whole, so the
219
+ centring costs nothing. Whole px at every scale is the point; a glyph
220
+ centred in an odd-width panel would land on a half pixel and fringe. */
221
+ .arrow-slot svg {
222
+ position: absolute;
223
+ left: calc(var(--vf-scale, 1) * 13px);
224
+ top: calc(var(--vf-scale, 1) * 5px);
225
+ display: block;
226
+ width: calc(var(--vf-scale, 1) * 11px);
227
+ height: calc(var(--vf-scale, 1) * 6px);
228
+ }
229
+ `
230
+ ];
231
+ }
232
+ get valueMissing() {
233
+ return this.value === "";
234
+ }
235
+ get valueMissingMessage() {
236
+ return "Please select an item in the list.";
237
+ }
238
+ get showUpArrow() {
239
+ return this.rowScroll > 0;
240
+ }
241
+ get showDownArrow() {
242
+ return this.rowScroll < this.optionItems.length - this.visibleSlots;
243
+ }
244
+ constructor() {
245
+ super(), this.value = "", this.name = "", this.label = "", this.open = !1, this.cachedOptions = [], this.scale = new t(this), this.gridSnap = new l(this), this.focusRule = new v(this), this.panelListeners = new u(this, () => [
246
+ [
247
+ document,
248
+ "pointerdown",
249
+ this.handleDocumentPointerDown,
250
+ !0
251
+ ],
252
+ [
253
+ window,
254
+ "scroll",
255
+ this.handleWindowScroll,
256
+ !0
257
+ ],
258
+ [
259
+ window,
260
+ "resize",
261
+ this.handleWindowResize
262
+ ]
263
+ ]), this.pressListeners = new u(this, () => [
264
+ [
265
+ document,
266
+ "pointermove",
267
+ this.handlePressPointerMove,
268
+ !0
269
+ ],
270
+ [
271
+ document,
272
+ "pointerup",
273
+ this.handlePressPointerUp,
274
+ !0
275
+ ],
276
+ [
277
+ document,
278
+ "pointercancel",
279
+ this.handlePressCancel,
280
+ !0
281
+ ]
282
+ ]), this.activeIndex = -1, this.rowScroll = 0, this.visibleSlots = 0, this.rowHeight = 0, this.arrowScroll = null, this.pressStartArrow = null, this.pressing = !1, this.pressOpenedPanel = !1, this.pressMoved = !1, this.pressStartOption = null, this.pressDownTime = 0, this.pressDownX = 0, this.pressDownY = 0, this.swallowClick = !1, this.blinking = !1, this.typeAhead = new T(), this.handleSlotChange = () => {
283
+ if (this.cachedOptions = this.assignedOptions ?? [], this.value === "") {
284
+ let e = this.optionItems.find((e) => !e.disabled);
285
+ e && (this.value = this.optionValue(e));
286
+ }
287
+ this.optionItems.length && this.latchFormDefault(this.value), this.applySelection();
288
+ }, this.handleArrowEnter = (e) => {
289
+ !this.open || this.blinking || this.pressing || this.hoverArrowAt(e.clientX, e.clientY);
290
+ }, this.handleArrowLeave = () => {
291
+ this.pressing || this.stopArrowScroll();
292
+ }, this.handleHostClick = (e) => {
293
+ if (this.isDisabled) return;
294
+ if (this.swallowClick) {
295
+ this.swallowClick = !1;
296
+ return;
297
+ }
298
+ if (this.blinking) return;
299
+ let t = this.optionFromEvent(e);
300
+ if (t) {
301
+ this.open && this.selectOption(t);
302
+ return;
303
+ }
304
+ this.open ? this.closePanel(!0) : (this.controlEl?.focus(), this.openPanel());
305
+ }, this.handleHostPointerDown = (e) => {
306
+ this.isDisabled || this.blinking || e.button > 0 || !e.isPrimary || (this.swallowClick = !0, this.pressing = !0, this.pressMoved = !1, this.pressStartOption = null, this.pressStartArrow = null, this.pressDownTime = e.timeStamp, this.pressDownX = e.clientX, this.pressDownY = e.clientY, this.pressOpenedPanel = !this.open, e.preventDefault(), this.pressListeners.attach(), this.pressOpenedPanel ? (this.controlEl?.focus(), this.openPanel().then(() => {
307
+ this.pressing && this.pressStartOption === null && (this.pressStartArrow = this.arrowZoneAtPoint(this.pressDownX, this.pressDownY), this.pressStartOption = this.optionAtPoint(this.pressDownX, this.pressDownY));
308
+ })) : (this.pressStartArrow = this.arrowZoneAtPoint(e.clientX, e.clientY), this.pressStartOption = this.optionAtPoint(e.clientX, e.clientY)));
309
+ }, this.handlePressPointerMove = (e) => {
310
+ if (!this.pressing) return;
311
+ e.preventDefault();
312
+ let t = this.arrowZoneAtPoint(e.clientX, e.clientY);
313
+ if (t) {
314
+ this.startArrowScroll(t === "up" ? -1 : 1), this.activeIndex !== -1 && this.clearActive();
315
+ return;
316
+ }
317
+ this.stopArrowScroll();
318
+ let n = this.optionAtPoint(e.clientX, e.clientY);
319
+ this.pressStartOption === null ? this.pressStartOption = n : !this.pressMoved && n !== this.pressStartOption && (this.pressMoved = !0), this.trackHighlight(n);
320
+ }, this.handlePressPointerUp = (e) => {
321
+ if (!this.pressing) return;
322
+ let t = this.arrowZoneAtPoint(e.clientX, e.clientY) ? null : this.optionAtPoint(e.clientX, e.clientY), n = this.pressStartArrow !== null, r = this.pressOpenedPanel, i = !this.pressMoved, a = e.timeStamp - this.pressDownTime < 200;
323
+ if (this.endPress(), n || r && i && a) {
324
+ this.hoverArrowAt(e.clientX, e.clientY);
325
+ return;
326
+ }
327
+ this.stopArrowScroll(), this.resolveRelease(t);
328
+ }, this.handlePressCancel = () => {
329
+ this.stopArrowScroll(), this.endPress();
330
+ }, this.handleHostKeyDown = (e) => {
331
+ if (!this.isDisabled) {
332
+ if (this.blinking) {
333
+ e.preventDefault();
334
+ return;
335
+ }
336
+ if (!this.open) {
337
+ (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") && (e.preventDefault(), this.openPanel());
338
+ return;
339
+ }
340
+ switch (e.key) {
341
+ case "ArrowDown":
342
+ e.preventDefault(), this.moveActive(1);
343
+ break;
344
+ case "ArrowUp":
345
+ e.preventDefault(), this.moveActive(-1);
346
+ break;
347
+ case "Home": {
348
+ e.preventDefault();
349
+ let t = this.firstEnabledIndex();
350
+ t !== -1 && this.setActive(t);
351
+ break;
352
+ }
353
+ case "End": {
354
+ e.preventDefault();
355
+ let t = this.lastEnabledIndex();
356
+ t !== -1 && this.setActive(t);
357
+ break;
358
+ }
359
+ case "Enter":
360
+ case " ": {
361
+ e.preventDefault();
362
+ let t = this.optionItems[this.activeIndex];
363
+ t && this.selectOption(t);
364
+ break;
365
+ }
366
+ case "Escape":
367
+ e.preventDefault(), this.closePanel(!0);
368
+ break;
369
+ case "Tab":
370
+ this.closePanel(!1);
371
+ break;
372
+ default: {
373
+ if (e.key.length !== 1 || e.metaKey || e.ctrlKey || e.altKey) break;
374
+ e.preventDefault();
375
+ let t = this.typeAhead.feed(e.key, this.activeIndex, this.optionItems.map((e) => ({
376
+ text: e.textContent ?? "",
377
+ disabled: e.disabled
378
+ })));
379
+ t !== -1 && this.setActive(t);
380
+ break;
381
+ }
382
+ }
383
+ }
384
+ }, this.handleHostPointerOver = (e) => {
385
+ if (!this.open || this.blinking || this.pressing) return;
386
+ let t = this.optionFromEvent(e);
387
+ if (t && !t.disabled) {
388
+ let e = this.optionItems.indexOf(t);
389
+ e !== -1 && e !== this.activeIndex && this.setActive(e);
390
+ }
391
+ }, this.handleHostFocusOut = (e) => {
392
+ if (!this.open || this.blinking) return;
393
+ let t = e.relatedTarget;
394
+ t instanceof Node && (this.contains(t) || this.renderRoot.contains(t)) || this.closePanel(!1);
395
+ }, this.handleDocumentPointerDown = (e) => {
396
+ this.blinking || e.composedPath().includes(this) || this.closePanel(!1);
397
+ }, this.handleWindowScroll = () => {
398
+ this.blinking || this.closePanel(!1);
399
+ }, this.handleWindowResize = () => {
400
+ this.blinking || this.closePanel(!1);
401
+ }, this.addEventListener("pointerdown", this.handleHostPointerDown), this.addEventListener("click", this.handleHostClick), this.addEventListener("keydown", this.handleHostKeyDown), this.addEventListener("pointerover", this.handleHostPointerOver), this.addEventListener("focusout", this.handleHostFocusOut);
402
+ }
403
+ disconnectedCallback() {
404
+ super.disconnectedCallback(), this.cancelBlink(), this.stopArrowScroll(), this.clearActive(), this.endPress(), this.open = !1;
405
+ }
406
+ focus(e) {
407
+ this.controlEl?.focus(e);
408
+ }
409
+ updated(e) {
410
+ e.has("value") && this.applySelection(), (e.has("value") || e.has("disabled")) && this.syncFormValue(this.value), e.has("disabled") && this.disabled && this.open && this.closePanel(!1);
411
+ }
412
+ formDisabledCallback(e) {
413
+ super.formDisabledCallback(e), this.syncFormValue(this.value), e && this.open && this.closePanel(!1);
414
+ }
415
+ formResetCallback() {
416
+ this.value = this.formDefault("");
417
+ }
418
+ get optionItems() {
419
+ return this.cachedOptions;
420
+ }
421
+ optionValue(e) {
422
+ return e.value === "" ? (e.textContent ?? "").trim() : e.value;
423
+ }
424
+ applySelection() {
425
+ for (let e of this.optionItems) e.selected = this.optionValue(e) === this.value;
426
+ }
427
+ async openPanel() {
428
+ if (this.open || this.isDisabled) return;
429
+ this.open = !0, this.panelListeners.attach(), await this.updateComplete;
430
+ let e = this.optionItems.findIndex((e) => e.selected && !e.disabled);
431
+ e === -1 && (e = this.firstEnabledIndex()), this.positionPanel(Math.max(e, 0)), e !== -1 && this.setActive(e);
432
+ }
433
+ positionPanel(e) {
434
+ let t = this.controlEl, r = this.panelEl;
435
+ if (!t || !r) return;
436
+ let i = t.getBoundingClientRect(), a = this.optionItems[0]?.getBoundingClientRect()?.height || n(P.ITEM_HEIGHT, this), o = getComputedStyle(r), s = parseFloat(o.borderTopWidth) || 0, c = (e) => n(parseFloat(o.getPropertyValue(e)) || 0, this), l = w({
437
+ idealTop: i.top - e * a,
438
+ rowHeight: a,
439
+ border: s,
440
+ rowCount: this.optionItems.length,
441
+ viewTop: c("--vf-popup-clamp-top"),
442
+ viewBottom: window.innerHeight - c("--vf-popup-clamp-bottom")
443
+ }), u = i.left;
444
+ r.style.minWidth = `${i.width}px`, r.style.top = `${l.panelTop}px`, r.style.left = `${u}px`, r.style.height = `${l.panelHeight}px`, this.rowHeight = a, this.visibleSlots = l.visibleSlots, this.rowScroll = l.initialScroll, this.applyScroll();
445
+ }
446
+ closePanel(e) {
447
+ this.open && (this.open = !1, this.cancelBlink(), this.stopArrowScroll(), this.endPress(), this.clearActive(), this.typeAhead.reset(), this.panelListeners.detach(), e && this.controlEl?.focus());
448
+ }
449
+ applyScroll() {
450
+ let e = this.rowScroll * this.rowHeight;
451
+ this.rowsEl && (this.rowsEl.style.transform = e ? `translateY(${-e}px)` : ""), this.upArrowEl?.classList.toggle("shown", this.showUpArrow), this.downArrowEl?.classList.toggle("shown", this.showDownArrow);
452
+ }
453
+ revealRow(e) {
454
+ let t = x(e, this.rowScroll, this.optionItems.length, this.visibleSlots);
455
+ t !== this.rowScroll && (this.rowScroll = t, this.applyScroll());
456
+ }
457
+ stepArrowScroll(e) {
458
+ let t = b(this.rowScroll + e, this.rowScroll, this.optionItems.length, this.visibleSlots);
459
+ return t !== this.rowScroll && (this.rowScroll = t, this.applyScroll(), !0);
460
+ }
461
+ startArrowScroll(e) {
462
+ if (this.arrowScroll?.dir === e || (this.stopArrowScroll(), !this.stepArrowScroll(e))) return;
463
+ let t = window.setInterval(() => {
464
+ this.stepArrowScroll(e) || this.stopArrowScroll();
465
+ }, 66);
466
+ this.arrowScroll = {
467
+ dir: e,
468
+ timer: t
469
+ };
470
+ }
471
+ stopArrowScroll() {
472
+ this.arrowScroll &&= (window.clearInterval(this.arrowScroll.timer), null);
473
+ }
474
+ arrowZoneAtPoint(e, t) {
475
+ let n = this.panelEl;
476
+ if (!this.open || !n) return null;
477
+ let r = n.getBoundingClientRect();
478
+ if (e < r.left || e >= r.right) return null;
479
+ let i = (r.height - this.visibleSlots * this.rowHeight) / 2;
480
+ return this.showUpArrow && t < r.top + i + this.rowHeight ? "up" : this.showDownArrow && t >= r.bottom - i - this.rowHeight ? "down" : null;
481
+ }
482
+ arrowSlotAtPoint(e, t) {
483
+ let n = [["up", this.upArrowEl], ["down", this.downArrowEl]];
484
+ for (let [r, i] of n) {
485
+ if (!i || !i.classList.contains("shown")) continue;
486
+ let n = i.getBoundingClientRect();
487
+ if (e >= n.left && e < n.right && t >= n.top && t < n.bottom) return r;
488
+ }
489
+ return null;
490
+ }
491
+ hoverArrowAt(e, t) {
492
+ let n = this.arrowSlotAtPoint(e, t);
493
+ if (!n) {
494
+ this.stopArrowScroll();
495
+ return;
496
+ }
497
+ this.startArrowScroll(n === "up" ? -1 : 1), this.activeIndex !== -1 && this.clearActive();
498
+ }
499
+ setActive(e, t = !0) {
500
+ this.activeIndex = e, this.optionItems.forEach((t, n) => {
501
+ t.active = n === e;
502
+ }), this.revealRow(e), t && this.optionItems[e]?.focus({ preventScroll: !0 });
503
+ }
504
+ clearActive() {
505
+ this.activeIndex = -1;
506
+ for (let e of this.optionItems) e.active = !1;
507
+ }
508
+ moveActive(e) {
509
+ let t = this.optionItems, n = this.activeIndex;
510
+ for (let r = 0; r < t.length; r += 1) {
511
+ if (n += e, n < 0 || n >= t.length) return;
512
+ let r = t[n];
513
+ if (r && !r.disabled) {
514
+ this.setActive(n);
515
+ return;
516
+ }
517
+ }
518
+ }
519
+ firstEnabledIndex() {
520
+ return this.optionItems.findIndex((e) => !e.disabled);
521
+ }
522
+ lastEnabledIndex() {
523
+ let e = this.optionItems;
524
+ for (let t = e.length - 1; t >= 0; --t) {
525
+ let n = e[t];
526
+ if (n && !n.disabled) return t;
527
+ }
528
+ return -1;
529
+ }
530
+ selectOption(e) {
531
+ if (e.disabled || this.blinking) return;
532
+ let t = this.optionItems.indexOf(e);
533
+ this.setActive(t, !1), this.blinking = !0;
534
+ let n = p((t) => {
535
+ e.active = t;
536
+ }, () => {
537
+ this.commit(e);
538
+ });
539
+ this.blinking && (this.blinkHandle = n);
540
+ }
541
+ cancelBlink() {
542
+ this.blinkHandle?.cancel(), this.blinkHandle = void 0, this.blinking = !1;
543
+ }
544
+ commit(e) {
545
+ this.closePanel(!0);
546
+ let t = this.optionValue(e);
547
+ this.value = t, d(this, "vf-change", { value: t }), f(this, "input"), f(this, "change");
548
+ }
549
+ optionFromEvent(e) {
550
+ return e.composedPath().find((e) => e instanceof y);
551
+ }
552
+ endPress() {
553
+ this.pressing && (this.pressing = !1, this.pressStartOption = null, this.pressStartArrow = null, this.pressListeners.detach());
554
+ }
555
+ optionAtPoint(e, t) {
556
+ let n = this.panelEl;
557
+ if (!this.open || !n) return null;
558
+ let r = n.getBoundingClientRect();
559
+ if (e < r.left || e >= r.right || t < r.top || t >= r.bottom) return null;
560
+ let i = S(this.rowScroll), a = C(this.rowScroll, this.optionItems.length, this.visibleSlots);
561
+ for (let n = i; n <= a; n += 1) {
562
+ let r = this.optionItems[n];
563
+ if (!r) continue;
564
+ let i = r.getBoundingClientRect();
565
+ if (e >= i.left && e < i.right && t >= i.top && t < i.bottom) return r;
566
+ }
567
+ return null;
568
+ }
569
+ trackHighlight(e) {
570
+ if (e && !e.disabled) {
571
+ let t = this.optionItems.indexOf(e);
572
+ t !== -1 && t !== this.activeIndex && this.setActive(t, !1);
573
+ } else this.activeIndex !== -1 && this.clearActive();
574
+ }
575
+ resolveRelease(e) {
576
+ if (!e || e.disabled || this.optionValue(e) === this.value) {
577
+ this.closePanel(!0);
578
+ return;
579
+ }
580
+ this.selectOption(e);
581
+ }
582
+ render() {
583
+ let e = this.optionItems.find((e) => this.optionValue(e) === this.value), t = e ? (e.textContent ?? "").trim() : "", n = this.isDisabled;
584
+ return D`
585
+ <div
586
+ class=${N({
587
+ control: !0,
588
+ "vf-snap": !0,
589
+ "vf-focus-rule": this.focusRule.marked && !this.open,
590
+ disabled: n
591
+ })}
592
+ part="control"
593
+ role="combobox"
594
+ aria-haspopup="listbox"
595
+ aria-expanded=${this.open ? "true" : "false"}
596
+ aria-disabled=${n ? "true" : "false"}
597
+ aria-controls="listbox"
598
+ aria-label=${this.label || this.hostLabel || O}
599
+ aria-describedby=${this.describedBy}
600
+ aria-required=${this.required ? "true" : O}
601
+ aria-invalid=${this.validity.valid ? O : "true"}
602
+ tabindex=${n ? "-1" : "0"}
603
+ >
604
+ <span class="label" part="label">
605
+ <span class="value">${t}</span>
606
+ <span class="sizer" aria-hidden="true">
607
+ ${this.optionItems.map((e) => D`<span>${(e.textContent ?? "").trim()}</span>`)}
608
+ </span>
609
+ </span>
610
+ <span class="arrow" part="arrow" aria-hidden="true"
611
+ >${g(m, "caret")}</span
612
+ >
613
+ </div>
614
+ <div
615
+ id="listbox"
616
+ class="panel vf-panel ${this.open ? "open" : ""}"
617
+ part="panel"
618
+ role="listbox"
619
+ aria-label=${this.label || this.hostLabel || O}
620
+ aria-hidden=${this.open ? "false" : "true"}
621
+ >
622
+ <!-- role="presentation" so the strip that carries the roll doesn't
623
+ stand between the listbox and the options it owns. -->
624
+ <div class="rows" role="presentation">
625
+ <slot @slotchange=${this.handleSlotChange}></slot>
626
+ </div>
627
+ <!-- Pointer affordances only, hence aria-hidden: the clipped options
628
+ stay in the accessibility tree un-hidden (clipping is presentation,
629
+ and a native <select> exposes its whole list too), and keyboard and
630
+ AT users reach every row with the arrows, Home/End and type-ahead. -->
631
+ <div
632
+ class="arrow-slot up"
633
+ part="scroll-arrow"
634
+ aria-hidden="true"
635
+ @pointerenter=${this.handleArrowEnter}
636
+ @pointermove=${this.handleArrowEnter}
637
+ @pointerleave=${this.handleArrowLeave}
638
+ >
639
+ ${g(h, "caret")}
640
+ </div>
641
+ <div
642
+ class="arrow-slot down"
643
+ part="scroll-arrow"
644
+ aria-hidden="true"
645
+ @pointerenter=${this.handleArrowEnter}
646
+ @pointermove=${this.handleArrowEnter}
647
+ @pointerleave=${this.handleArrowLeave}
648
+ >
649
+ ${g(m, "caret")}
650
+ </div>
651
+ </div>
652
+ ${this.renderDescription()}
653
+ `;
654
+ }
655
+ };
656
+ r([k()], F.prototype, "value", void 0), r([k({ reflect: !0 })], F.prototype, "name", void 0), r([k()], F.prototype, "label", void 0), r([M()], F.prototype, "open", void 0), r([A(".control")], F.prototype, "controlEl", void 0), r([A(".panel")], F.prototype, "panelEl", void 0), r([A(".rows")], F.prototype, "rowsEl", void 0), r([A(".arrow-slot.up")], F.prototype, "upArrowEl", void 0), r([A(".arrow-slot.down")], F.prototype, "downArrowEl", void 0), r([j({ selector: "vf-option" })], F.prototype, "assignedOptions", void 0), r([M()], F.prototype, "cachedOptions", void 0), F = P = r([e("vf-select")], F);
657
+ //#endregion
658
+ export { F as VfSelect };
@@ -0,0 +1,38 @@
1
+ import { LitElement } from 'lit';
2
+ import type { PropertyValues } from 'lit';
3
+ declare const VfSeparator_base: (new (...args: any[]) => import("../position.js").VfPositionedInterface) & typeof LitElement;
4
+ /**
5
+ * `<vf-separator>` — a 1px System 7 rule.
6
+ *
7
+ * Horizontal by default (1px tall, full width); set the `vertical` attribute
8
+ * for a 1px-wide vertical rule (give it a height, or let a flex parent
9
+ * stretch it).
10
+ *
11
+ * Containers may restyle it via custom properties:
12
+ * - `--vf-separator-color` — line color (default `var(--vf-black, #000)`).
13
+ * - `--vf-separator-style` — line style, e.g. `dotted` (default `solid`).
14
+ * `vf-menu` sets these so slotted separators render as the classic dimmed
15
+ * dotted menu rule (see Menus.png).
16
+ * @cssprop --vf-separator-color - `vf-separator` rule color — `vf-menu` sets it
17
+ * to `--vf-disabled` for the dimmed menu rule
18
+ * @cssprop [--vf-separator-style=solid] - `vf-separator` rule style — `vf-menu`
19
+ * sets `dotted` (see `Menus.png`)
20
+ */
21
+ export declare class VfSeparator extends VfSeparator_base {
22
+ #private;
23
+ static styles: import("lit").CSSResult[];
24
+ private readonly scale;
25
+ /** Device-pixel grid snapping (opt in with applyGridSnap()); see src/grid-snap.ts. */
26
+ private readonly gridSnap;
27
+ /** Render as a vertical rule (1px wide) instead of horizontal. */
28
+ vertical: boolean;
29
+ constructor();
30
+ protected updated(changed: PropertyValues<this>): void;
31
+ protected render(): unknown;
32
+ }
33
+ declare global {
34
+ interface HTMLElementTagNameMap {
35
+ 'vf-separator': VfSeparator;
36
+ }
37
+ }
38
+ export {};