vintage-frames 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +262 -0
- package/custom-elements.json +19807 -0
- package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js +9 -0
- package/dist/chrome.d.ts +73 -0
- package/dist/chrome.js +94 -0
- package/dist/components/vf-button-group.d.ts +50 -0
- package/dist/components/vf-button-group.js +68 -0
- package/dist/components/vf-button.d.ts +169 -0
- package/dist/components/vf-button.js +234 -0
- package/dist/components/vf-checkbox.d.ts +60 -0
- package/dist/components/vf-checkbox.js +135 -0
- package/dist/components/vf-container.d.ts +95 -0
- package/dist/components/vf-container.js +86 -0
- package/dist/components/vf-desktop.d.ts +243 -0
- package/dist/components/vf-desktop.js +310 -0
- package/dist/components/vf-dialog.d.ts +105 -0
- package/dist/components/vf-dialog.js +280 -0
- package/dist/components/vf-fieldset.d.ts +34 -0
- package/dist/components/vf-fieldset.js +76 -0
- package/dist/components/vf-grid.d.ts +124 -0
- package/dist/components/vf-grid.js +133 -0
- package/dist/components/vf-icon.d.ts +356 -0
- package/dist/components/vf-icon.js +507 -0
- package/dist/components/vf-img.d.ts +63 -0
- package/dist/components/vf-img.js +65 -0
- package/dist/components/vf-label.d.ts +117 -0
- package/dist/components/vf-label.js +151 -0
- package/dist/components/vf-list-item.d.ts +48 -0
- package/dist/components/vf-list-item.js +98 -0
- package/dist/components/vf-list.d.ts +77 -0
- package/dist/components/vf-list.js +256 -0
- package/dist/components/vf-menu-bar.d.ts +64 -0
- package/dist/components/vf-menu-bar.js +236 -0
- package/dist/components/vf-menu-item.d.ts +91 -0
- package/dist/components/vf-menu-item.js +252 -0
- package/dist/components/vf-menu.d.ts +95 -0
- package/dist/components/vf-menu.js +346 -0
- package/dist/components/vf-number-field.d.ts +60 -0
- package/dist/components/vf-number-field.js +248 -0
- package/dist/components/vf-option.d.ts +49 -0
- package/dist/components/vf-option.js +119 -0
- package/dist/components/vf-paragraph.d.ts +73 -0
- package/dist/components/vf-paragraph.js +66 -0
- package/dist/components/vf-progress-bar.d.ts +69 -0
- package/dist/components/vf-progress-bar.js +272 -0
- package/dist/components/vf-radio-group.d.ts +109 -0
- package/dist/components/vf-radio-group.js +105 -0
- package/dist/components/vf-radio.d.ts +59 -0
- package/dist/components/vf-radio.js +135 -0
- package/dist/components/vf-scroll-area.d.ts +75 -0
- package/dist/components/vf-scroll-area.js +124 -0
- package/dist/components/vf-select.d.ts +334 -0
- package/dist/components/vf-select.js +658 -0
- package/dist/components/vf-separator.d.ts +38 -0
- package/dist/components/vf-separator.js +55 -0
- package/dist/components/vf-slider.d.ts +80 -0
- package/dist/components/vf-slider.js +255 -0
- package/dist/components/vf-stack.d.ts +188 -0
- package/dist/components/vf-stack.js +168 -0
- package/dist/components/vf-swatch.d.ts +98 -0
- package/dist/components/vf-swatch.js +201 -0
- package/dist/components/vf-text-area.d.ts +77 -0
- package/dist/components/vf-text-area.js +121 -0
- package/dist/components/vf-text-field.d.ts +53 -0
- package/dist/components/vf-text-field.js +79 -0
- package/dist/components/vf-window.d.ts +157 -0
- package/dist/components/vf-window.js +377 -0
- package/dist/cursor-art.d.ts +37 -0
- package/dist/cursor-art.js +41 -0
- package/dist/cursor.d.ts +49 -0
- package/dist/cursor.js +171 -0
- package/dist/define.d.ts +45 -0
- package/dist/define.js +15 -0
- package/dist/document-listeners.d.ts +62 -0
- package/dist/document-listeners.js +29 -0
- package/dist/drag.d.ts +49 -0
- package/dist/drag.js +30 -0
- package/dist/events.d.ts +79 -0
- package/dist/events.js +25 -0
- package/dist/focus-modality.d.ts +94 -0
- package/dist/focus-modality.js +56 -0
- package/dist/form-control.d.ts +247 -0
- package/dist/form-control.js +127 -0
- package/dist/glyphs.d.ts +117 -0
- package/dist/glyphs.js +100 -0
- package/dist/grid-snap.d.ts +73 -0
- package/dist/grid-snap.js +135 -0
- package/dist/index.d.ts +145 -0
- package/dist/index.js +71 -0
- package/dist/menu-press.d.ts +60 -0
- package/dist/menu-press.js +97 -0
- package/dist/modal-dialog.d.ts +148 -0
- package/dist/modal-dialog.js +155 -0
- package/dist/motion.d.ts +72 -0
- package/dist/motion.js +22 -0
- package/dist/number.d.ts +24 -0
- package/dist/number.js +12 -0
- package/dist/open-art.d.ts +40 -0
- package/dist/open-art.js +35 -0
- package/dist/pixel-frame.d.ts +102 -0
- package/dist/pixel-frame.js +77 -0
- package/dist/popup-overflow.d.ts +157 -0
- package/dist/popup-overflow.js +45 -0
- package/dist/position.d.ts +187 -0
- package/dist/position.js +74 -0
- package/dist/scale.d.ts +259 -0
- package/dist/scale.js +133 -0
- package/dist/scroll-rail.d.ts +112 -0
- package/dist/scroll-rail.js +194 -0
- package/dist/scroll-state.d.ts +113 -0
- package/dist/scroll-state.js +55 -0
- package/dist/size.d.ts +47 -0
- package/dist/size.js +30 -0
- package/dist/styles/base.d.ts +38 -0
- package/dist/styles/body-font.d.ts +7 -0
- package/dist/styles/body-font.js +9 -0
- package/dist/styles/display-font.d.ts +7 -0
- package/dist/styles/display-font.js +9 -0
- package/dist/styles/recipes/body-face.d.ts +14 -0
- package/dist/styles/recipes/body-face.js +6 -0
- package/dist/styles/recipes/display-face.d.ts +23 -0
- package/dist/styles/recipes/display-face.js +10 -0
- package/dist/styles/recipes/field.d.ts +20 -0
- package/dist/styles/recipes/field.js +68 -0
- package/dist/styles/recipes/focus.d.ts +76 -0
- package/dist/styles/recipes/focus.js +39 -0
- package/dist/styles/recipes/host.d.ts +5 -0
- package/dist/styles/recipes/host.js +62 -0
- package/dist/styles/recipes/pattern.d.ts +80 -0
- package/dist/styles/recipes/pattern.js +82 -0
- package/dist/styles/recipes/scroll-rail.d.ts +69 -0
- package/dist/styles/recipes/scroll-rail.js +222 -0
- package/dist/styles/recipes/shadow.d.ts +13 -0
- package/dist/styles/recipes/shadow.js +5 -0
- package/dist/styles/recipes/static-text.d.ts +24 -0
- package/dist/styles/recipes/static-text.js +17 -0
- package/dist/styles/recipes/surface.d.ts +28 -0
- package/dist/styles/recipes/surface.js +27 -0
- package/dist/styles/recipes/tile.d.ts +122 -0
- package/dist/styles/recipes/tile.js +40 -0
- package/dist/styles/recipes/title-bar.d.ts +49 -0
- package/dist/styles/recipes/title-bar.js +134 -0
- package/dist/styles/recipes/toggle.d.ts +10 -0
- package/dist/styles/recipes/toggle.js +20 -0
- package/dist/styles/register-embedded-font.d.ts +64 -0
- package/dist/styles/register-embedded-font.js +30 -0
- package/dist/text-control.d.ts +131 -0
- package/dist/text-control.js +83 -0
- package/dist/tile-grid.d.ts +111 -0
- package/dist/tile-grid.js +53 -0
- package/dist/toggle-control.d.ts +60 -0
- package/dist/toggle-control.js +41 -0
- package/dist/track-width.d.ts +42 -0
- package/dist/track-width.js +30 -0
- package/dist/type-ahead.d.ts +35 -0
- package/dist/type-ahead.js +20 -0
- package/dist/zoom.d.ts +204 -0
- package/dist/zoom.js +105 -0
- package/docs/SPEC.md +2471 -0
- package/editor/vscode.html-custom-data.json +1320 -0
- package/editor/web-types.json +3237 -0
- package/package.json +118 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { vfDisplayDecls as e } from "./display-face.js";
|
|
2
|
+
import { css as t } from "lit";
|
|
3
|
+
//#region src/styles/recipes/title-bar.ts
|
|
4
|
+
var n = t`
|
|
5
|
+
.vf-title-bar {
|
|
6
|
+
position: relative;
|
|
7
|
+
/* Inert unless the frame is a flex column (vf-window); on vf-dialog's plain
|
|
8
|
+
block frame the shorthand simply doesn't apply. */
|
|
9
|
+
flex: none;
|
|
10
|
+
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
|
+
display: flex;
|
|
13
|
+
/* flex-start, not center: the title states its own whole-pixel row (see
|
|
14
|
+
the margin below); centering 16 into 17 would land it on a half. */
|
|
15
|
+
align-items: flex-start;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
}
|
|
19
|
+
.vf-title {
|
|
20
|
+
/* Chicago-style title (chrome); slotted body copy keeps the body face. */
|
|
21
|
+
${e}
|
|
22
|
+
position: relative;
|
|
23
|
+
z-index: 1;
|
|
24
|
+
left: var(--vf-title-dx, 0px);
|
|
25
|
+
/* The face's own 16px line box on interior rows 1..16: cap band lands on
|
|
26
|
+
rows 4..12 (4px white above and below the 9px caps — the trace). The
|
|
27
|
+
inherited ratio line-height would give 20px and center fractionally. */
|
|
28
|
+
line-height: calc(var(--vf-scale, 1) * var(--vf-line-height-display, 16px));
|
|
29
|
+
margin-top: calc(var(--vf-scale, 1) * 1px);
|
|
30
|
+
/* 6px + the letters' own 1px side bearing = the traced 7px of white
|
|
31
|
+
between ink and stripes. */
|
|
32
|
+
padding: 0 calc(var(--vf-scale, 1) * 6px);
|
|
33
|
+
max-width: calc(100% - var(--vf-scale, 1) * var(--vf-title-inset, 16px));
|
|
34
|
+
background: var(--vf-white, #fff);
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
text-overflow: ellipsis;
|
|
38
|
+
}
|
|
39
|
+
`, r = t`
|
|
40
|
+
.box {
|
|
41
|
+
position: absolute;
|
|
42
|
+
/* 11×11 box with 3px of clear white above and below it (title-bar
|
|
43
|
+
interior is 17px: 3 + 11 + 3). See SPEC §5 vf-window. */
|
|
44
|
+
top: calc(var(--vf-scale, 1) * 3px);
|
|
45
|
+
z-index: 1;
|
|
46
|
+
width: calc(var(--vf-scale, 1) * 11px);
|
|
47
|
+
height: calc(var(--vf-scale, 1) * 11px);
|
|
48
|
+
padding: 0;
|
|
49
|
+
margin: 0;
|
|
50
|
+
border: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
|
|
51
|
+
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
|
+
font: inherit;
|
|
60
|
+
cursor: var(--vf-cursor, default);
|
|
61
|
+
-webkit-appearance: none;
|
|
62
|
+
appearance: none;
|
|
63
|
+
}
|
|
64
|
+
.close {
|
|
65
|
+
left: calc(var(--vf-scale, 1) * 8px);
|
|
66
|
+
}
|
|
67
|
+
.zoom {
|
|
68
|
+
right: calc(var(--vf-scale, 1) * 8px);
|
|
69
|
+
}
|
|
70
|
+
/* Pressed box (close AND zoom): the interior fills with the classic
|
|
71
|
+
radiating "go-away" sunburst — black 1-bit spokes on the white face
|
|
72
|
+
(4 orthogonal 3px spokes + 4 diagonal 2px ones around an empty center),
|
|
73
|
+
traced pixel-for-pixel from the UI kit's close-button-active-state
|
|
74
|
+
sprite. Both widgets flash the identical graphic while pressed. That
|
|
75
|
+
sprite is the whole 11×11 box; its outer ring is this element's own 1px
|
|
76
|
+
border, so the SVG draws just the 9×9 interior into the padding box. */
|
|
77
|
+
.box:active {
|
|
78
|
+
background-color: var(--vf-white, #ffffff);
|
|
79
|
+
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");
|
|
80
|
+
background-repeat: no-repeat;
|
|
81
|
+
background-position: center;
|
|
82
|
+
background-size: calc(var(--vf-scale, 1) * 9px) calc(var(--vf-scale, 1) * 9px);
|
|
83
|
+
}
|
|
84
|
+
.zoom::after {
|
|
85
|
+
content: '';
|
|
86
|
+
position: absolute;
|
|
87
|
+
/* 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 border, so
|
|
89
|
+
only its right and bottom edges are drawn: a 6×6 box anchored at the
|
|
90
|
+
padding-box origin whose 1px right/bottom borders land the vertical at
|
|
91
|
+
sprite col 6 and the horizontal at row 6. Traced from the UI kit
|
|
92
|
+
zoom-button rest sprite. */
|
|
93
|
+
top: 0;
|
|
94
|
+
left: 0;
|
|
95
|
+
width: calc(var(--vf-scale, 1) * 6px);
|
|
96
|
+
height: calc(var(--vf-scale, 1) * 6px);
|
|
97
|
+
border-right: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
|
|
98
|
+
border-bottom: calc(var(--vf-scale, 1) * 1px) solid var(--vf-black, #000000);
|
|
99
|
+
}
|
|
100
|
+
/* While pressed the zoom box shows the same sunburst as the close box, so
|
|
101
|
+
its inner detail square gives way to it. */
|
|
102
|
+
.zoom:active::after {
|
|
103
|
+
display: none;
|
|
104
|
+
}
|
|
105
|
+
/* Forced colors: everything the widget draws is already token-routed —
|
|
106
|
+
Canvas face, CanvasText border — but its white patch ring is a
|
|
107
|
+
box-shadow, which forced colors never paints, leaving the box pressed
|
|
108
|
+
against the (rescued — see vfStripes) stripes. forced-color-adjust: none
|
|
109
|
+
turns the widget's own paint back on; with the tokens remapped it still
|
|
110
|
+
renders entirely in the user's pair. The one literal left is the pressed
|
|
111
|
+
sunburst tile (url() images are preserved with their black ink), so that
|
|
112
|
+
repaints as a mask over the ink token — into the padding box, where the
|
|
113
|
+
sprite's 9×9 interior already draws. */
|
|
114
|
+
@media (forced-colors: active) {
|
|
115
|
+
.box {
|
|
116
|
+
forced-color-adjust: none;
|
|
117
|
+
}
|
|
118
|
+
.box:active {
|
|
119
|
+
background-image: none;
|
|
120
|
+
}
|
|
121
|
+
.box:active::before {
|
|
122
|
+
content: '';
|
|
123
|
+
position: absolute;
|
|
124
|
+
inset: 0;
|
|
125
|
+
background: var(--vf-black, #000000);
|
|
126
|
+
mask-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");
|
|
127
|
+
mask-repeat: no-repeat;
|
|
128
|
+
mask-position: center;
|
|
129
|
+
mask-size: calc(var(--vf-scale, 1) * 9px) calc(var(--vf-scale, 1) * 9px);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
//#endregion
|
|
134
|
+
export { n as vfTitleBar, r as vfWindowWidgets };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared layout for the two toggle controls (vf-checkbox, vf-radio): the
|
|
3
|
+
* box-and-label row, the host focus suppressed (each control draws
|
|
4
|
+
* {@link vfFocusUnderline} under its own well instead), and the classic "dim
|
|
5
|
+
* the label, not the control" disabled treatment. The Chicago display face
|
|
6
|
+
* comes from {@link vfDisplay} (both controls compose it), so no font is set
|
|
7
|
+
* here. Each component adds only its own well (`.box`/`.circle`), glyphs,
|
|
8
|
+
* press feedback and focus rule.
|
|
9
|
+
*/
|
|
10
|
+
export declare const vfToggle: import("lit").CSSResult;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { css as e } from "lit";
|
|
2
|
+
//#region src/styles/recipes/toggle.ts
|
|
3
|
+
var t = e`
|
|
4
|
+
:host {
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
align-items: center;
|
|
7
|
+
gap: calc(var(--vf-scale, 1) * 6px);
|
|
8
|
+
cursor: var(--vf-cursor, default);
|
|
9
|
+
}
|
|
10
|
+
/* The focus rule is drawn under the box/circle, not around the host. */
|
|
11
|
+
:host(:focus-visible) {
|
|
12
|
+
outline: none;
|
|
13
|
+
}
|
|
14
|
+
/* Disabled dims the label only; the box/circle glyphs stay solid black. */
|
|
15
|
+
.label.dim {
|
|
16
|
+
color: var(--vf-disabled, #c0c0c0);
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { t as vfToggle };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register an embedded bitmap webfont on the document.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS IS JS AND NOT AN `@font-face` RULE: font faces declared inside a
|
|
5
|
+
* component's shadow root are ignored by the browser — `@font-face` resolves
|
|
6
|
+
* only against the document. Every `vf-*` element renders into its own shadow
|
|
7
|
+
* root, so a stylesheet `@font-face` would never take effect. Registering the
|
|
8
|
+
* face imperatively on `document.fonts` makes it visible inside *every* shadow
|
|
9
|
+
* root at once, which preserves the library's "components need no global CSS"
|
|
10
|
+
* promise.
|
|
11
|
+
*
|
|
12
|
+
* Each face is inlined as a base64 WOFF2 data URL by its wrapper module (e.g.
|
|
13
|
+
* `display-font.ts`), so there is no separate asset to ship or to resolve
|
|
14
|
+
* through a consumer's bundler; the font is available the moment the wrapper is
|
|
15
|
+
* imported, with no network round trip.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Vertical-metric overrides for a registered face, applied via the CSS
|
|
19
|
+
* `ascent-override`/`descent-override`/`line-gap-override` descriptors.
|
|
20
|
+
* Percentages of the em (`'75%'`), same as the CSS properties.
|
|
21
|
+
*/
|
|
22
|
+
export interface EmbeddedFontMetrics {
|
|
23
|
+
ascentOverride?: string;
|
|
24
|
+
descentOverride?: string;
|
|
25
|
+
lineGapOverride?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The em box both embedded bitmap faces are drawn on: a 16-design-pixel grid
|
|
29
|
+
* split 12 px above the baseline and 4 px below (the classic Chicago-12
|
|
30
|
+
* ascent/descent), no line gap.
|
|
31
|
+
*
|
|
32
|
+
* WHY OVERRIDE AT ALL: browsers place a line box's baseline from the face's
|
|
33
|
+
* hhea metrics, and the retired converter-artifact faces (ChiKareGo and
|
|
34
|
+
* FindersKeepers, both since replaced by the kit's re-drawn strikes) carried hhea
|
|
35
|
+
* values — ascender 682, descender 192, line gap 92 of the 1024 upm, i.e.
|
|
36
|
+
* 10.66 px / 3 px / 1.44 px at 16px — that are NOT on the 64-unit
|
|
37
|
+
* design-pixel grid the glyphs are drawn on. Half-leading computed from them
|
|
38
|
+
* lands the baseline ~0.5px high in every whole-pixel line box, and
|
|
39
|
+
* rasterization snaps that to a whole device pixel: all chrome/body text sat
|
|
40
|
+
* one device px above its System 7 position (first seen as fractional gaps in
|
|
41
|
+
* the vf-select pill). Today both shipped strikes' own tables carry the
|
|
42
|
+
* grid-clean em (768/256/0 = 12/4/0 px — fonts/manifest-to-font.py writes
|
|
43
|
+
* them); the overrides restate it so every face the kit registers is pinned
|
|
44
|
+
* the same way regardless of provenance or which table a platform prefers.
|
|
45
|
+
*
|
|
46
|
+
* KNOWN LIMIT (dpr 2, scale 1.5): Chrome snaps aliased text baselines to whole
|
|
47
|
+
* ABSOLUTE CSS px. A pill's ideal baseline sits 13 system px (19.5 CSS px at
|
|
48
|
+
* scale 1.5) below its border-box top, so a host at a whole CSS-px position
|
|
49
|
+
* puts it on a half pixel and the snap misses by 1 device px (⅓ system px) —
|
|
50
|
+
* no metric value can reach it; only a host at a half-CSS-px position (still
|
|
51
|
+
* on the device grid) renders it exactly. dpr 1 and 3 land exactly at whole
|
|
52
|
+
* CSS-px positions, the common case.
|
|
53
|
+
*/
|
|
54
|
+
export declare const PIXEL_GRID_METRICS: EmbeddedFontMetrics;
|
|
55
|
+
/**
|
|
56
|
+
* Register `family` from an inlined base64 WOFF2 exactly once per session.
|
|
57
|
+
*
|
|
58
|
+
* Idempotent and environment-safe: it no-ops under SSR (no `document`), when
|
|
59
|
+
* the CSS Font Loading API is unavailable, or when the family is already
|
|
60
|
+
* present (duplicate bundles / HMR). A failed decode releases the family so a
|
|
61
|
+
* later explicit call can retry, and on any failure components simply keep the
|
|
62
|
+
* fallback stack — so this can never break rendering.
|
|
63
|
+
*/
|
|
64
|
+
export declare function registerEmbeddedFont(family: string, woff2Base64: string, metrics?: EmbeddedFontMetrics): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/styles/register-embedded-font.ts
|
|
2
|
+
var e = /* @__PURE__ */ new Set(), t = {
|
|
3
|
+
ascentOverride: "75%",
|
|
4
|
+
descentOverride: "25%",
|
|
5
|
+
lineGapOverride: "0%"
|
|
6
|
+
};
|
|
7
|
+
function n(t, n, r) {
|
|
8
|
+
if (e.has(t) || typeof document > "u" || !("fonts" in document)) return;
|
|
9
|
+
for (let n of document.fonts) if (n.family === t) {
|
|
10
|
+
e.add(t);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
e.add(t);
|
|
14
|
+
let i = `url(data:font/woff2;base64,${n}) format('woff2')`;
|
|
15
|
+
try {
|
|
16
|
+
let n = new FontFace(t, i, {
|
|
17
|
+
style: "normal",
|
|
18
|
+
weight: "100 900",
|
|
19
|
+
display: "swap",
|
|
20
|
+
...r
|
|
21
|
+
});
|
|
22
|
+
document.fonts.add(n), n.load().then(() => {}, () => {
|
|
23
|
+
document.fonts.delete(n), e.delete(t);
|
|
24
|
+
});
|
|
25
|
+
} catch {
|
|
26
|
+
e.delete(t);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { t as PIXEL_GRID_METRICS, n as registerEmbeddedFont };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { nothing } from 'lit';
|
|
2
|
+
import type { PropertyValues } from 'lit';
|
|
3
|
+
import { ScaleController } from './scale.js';
|
|
4
|
+
import { GridSnapController } from './grid-snap.js';
|
|
5
|
+
import { VfShadowRoleControl } from './form-control.js';
|
|
6
|
+
import { FocusRuleController } from './focus-modality.js';
|
|
7
|
+
/**
|
|
8
|
+
* Shared base for the kit's editable text fields — `vf-text-field`,
|
|
9
|
+
* `vf-text-area` and `vf-number-field`. Builds on
|
|
10
|
+
* {@link VfShadowRoleControl} (form association, the disabled-value guard and
|
|
11
|
+
* the name/description bridge every field needs, its role sitting on the inner
|
|
12
|
+
* native control) and adds the value/label scaffolding
|
|
13
|
+
* every field repeated: the reflected `name`, the `value`/`placeholder`/
|
|
14
|
+
* `readonly`/`label` props, the captured default restored on form reset, the
|
|
15
|
+
* `updated()` → {@link VfShadowRoleControl.syncFormValue} funnel, and the display-
|
|
16
|
+
* scaling controller. Pairs with the `vfField` css fragment (the white-well
|
|
17
|
+
* skin) in styles/base.ts.
|
|
18
|
+
*
|
|
19
|
+
* Subclasses render their own native control (`<input>`/`<textarea>` plus any
|
|
20
|
+
* adornments), tag it `class="vf-field"`, and may enrich the emitted event
|
|
21
|
+
* detail via {@link emitValue}.
|
|
22
|
+
*/
|
|
23
|
+
export declare class VfTextControlBase extends VfShadowRoleControl {
|
|
24
|
+
/** Focus delegates into the inner native control. */
|
|
25
|
+
static shadowRootOptions: ShadowRootInit;
|
|
26
|
+
/** Current value. Synced on every keystroke and submitted with forms. */
|
|
27
|
+
value: string;
|
|
28
|
+
/** Placeholder text shown when the field is empty. */
|
|
29
|
+
placeholder: string;
|
|
30
|
+
/** Makes the field read-only (focusable, not editable). */
|
|
31
|
+
readonly: boolean;
|
|
32
|
+
/** Form field name used when submitting the associated form. */
|
|
33
|
+
name: string;
|
|
34
|
+
/**
|
|
35
|
+
* Accessible name, applied as `aria-label` on the inner native control (the
|
|
36
|
+
* element that receives focus and is announced by screen readers — a host
|
|
37
|
+
* attribute cannot reach it on its own). Left empty, the name falls back to
|
|
38
|
+
* whatever the host carries — `aria-labelledby`, `aria-label` or an
|
|
39
|
+
* associated `<label for>` — via {@link VfShadowRoleControl.hostLabel}, so the
|
|
40
|
+
* platform's own labeling vocabulary works on these controls too.
|
|
41
|
+
*/
|
|
42
|
+
label: string;
|
|
43
|
+
/** A field is empty for `required`'s purposes when its value is `''`. */
|
|
44
|
+
protected get valueMissing(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Host attributes forwarded verbatim onto the inner native control — the
|
|
47
|
+
* input-behavior vocabulary the platform only honors on the element that
|
|
48
|
+
* actually takes the input. `<vf-text-field autocomplete="email">` used to
|
|
49
|
+
* put the token on a custom element the browser ignores for autofill while
|
|
50
|
+
* the shadow `<input>` never received it; same story for the mobile-keyboard
|
|
51
|
+
* pair (`inputmode`, `enterkeyhint`) and the rest. These are deliberately
|
|
52
|
+
* *not* reactive properties: four of them are global attributes with IDL
|
|
53
|
+
* accessors already on `HTMLElement` (`spellcheck`, `autocapitalize`,
|
|
54
|
+
* `inputMode`, `enterKeyHint`), and a Lit `@property` would shadow the
|
|
55
|
+
* platform member — the kit's own `align`/`draggable` trap. Instead the
|
|
56
|
+
* attributes are observed (see `observedAttributes`) and read at render
|
|
57
|
+
* time via {@link forwardedAttr}. Subclasses may extend the list
|
|
58
|
+
* (`vf-text-field` adds `pattern`) or override a default in their template
|
|
59
|
+
* (`vf-number-field` keeps `inputmode="decimal"` unless told otherwise).
|
|
60
|
+
*/
|
|
61
|
+
protected static readonly forwardedAttributes: readonly string[];
|
|
62
|
+
static get observedAttributes(): string[];
|
|
63
|
+
attributeChangedCallback(name: string, old: string | null, value: string | null): void;
|
|
64
|
+
/**
|
|
65
|
+
* The forwarded value of a host attribute for the inner control's template
|
|
66
|
+
* binding, or `nothing` (remove the attribute) while the host doesn't
|
|
67
|
+
* carry it.
|
|
68
|
+
*/
|
|
69
|
+
protected forwardedAttr(name: string): string | typeof nothing;
|
|
70
|
+
/** Default-on display scaling (true 72dpi size); see src/scale.ts. */
|
|
71
|
+
protected readonly scale: ScaleController;
|
|
72
|
+
/** Device-pixel grid snapping (opt in with applyGridSnap()); see src/grid-snap.ts. */
|
|
73
|
+
protected readonly gridSnap: GridSnapController;
|
|
74
|
+
/**
|
|
75
|
+
* Whether the well wears the kit's dashed focus rule — keyboard focus only.
|
|
76
|
+
* See {@link FocusRuleController} for why a field cannot get this from
|
|
77
|
+
* `:focus-visible` the way most of the kit does. Typing does NOT reveal the
|
|
78
|
+
* rule after a click: the click already showed where the insertion point
|
|
79
|
+
* went, which is the whole reason a field marks keyboard focus and not every
|
|
80
|
+
* focus, so the field never calls the controller's `reveal()`.
|
|
81
|
+
*/
|
|
82
|
+
protected readonly focusRule: FocusRuleController;
|
|
83
|
+
/**
|
|
84
|
+
* Classes for the `.vf-field-well` wrapper every field renders around its
|
|
85
|
+
* native control: the shared skin hook (`vfField`), the grid-snap target, and
|
|
86
|
+
* the focus rule's own gate. Assembled here so the three fields state the
|
|
87
|
+
* wrapper once and can't drift on what turns the rule on.
|
|
88
|
+
*/
|
|
89
|
+
protected get wellClass(): string;
|
|
90
|
+
connectedCallback(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Re-submit the value only when it (or the disabled state gating it) actually
|
|
93
|
+
* changed. Unrelated re-renders would otherwise re-run this — `vf-number-field`
|
|
94
|
+
* re-renders on every stepper press and release for its `pressed` state alone.
|
|
95
|
+
* On the first update `value` is in `changed` (class-field defaults are, with
|
|
96
|
+
* `undefined` as the old value), so the initial form value is still set.
|
|
97
|
+
*/
|
|
98
|
+
protected updated(changed: PropertyValues<this>): void;
|
|
99
|
+
/** Restores the initial value when the associated form resets. */
|
|
100
|
+
formResetCallback(): void;
|
|
101
|
+
/**
|
|
102
|
+
* True for a plain Enter press (no modifiers, not IME-composing) — the key
|
|
103
|
+
* that triggers a form's implicit submission. The native control is
|
|
104
|
+
* shadow-encapsulated (null form owner), so single-line fields call
|
|
105
|
+
* `internals.form?.requestSubmit()` themselves; this centralises the guard.
|
|
106
|
+
*/
|
|
107
|
+
protected isSubmitEnter(event: KeyboardEvent): boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Emulates the form's implicit submission for a plain Enter press. HTML
|
|
110
|
+
* defines it as *activating the form's default button* — the first submit
|
|
111
|
+
* button in tree order — so the submission carries that button's submitter
|
|
112
|
+
* identity and name/value. A bare `requestSubmit()` carries neither
|
|
113
|
+
* (`event.submitter === null`), which made Enter and a click on the same
|
|
114
|
+
* form produce different payloads. `click()` is the activation: a native
|
|
115
|
+
* button becomes the real submitter, and a `vf-button` forwards it to its
|
|
116
|
+
* inner button, which runs the same transient-proxy path a pointer does. A
|
|
117
|
+
* disabled default button means no submission at all, as in HTML; a form
|
|
118
|
+
* with no submit button falls back to the bare call, also as in HTML.
|
|
119
|
+
*/
|
|
120
|
+
protected requestImplicitSubmit(): void;
|
|
121
|
+
/**
|
|
122
|
+
* Dispatch a bubbling, composed value event (SPEC §4). Text fields use the
|
|
123
|
+
* default `{ value }` detail; `vf-number-field` passes an enriched detail that
|
|
124
|
+
* also carries `valueAsNumber`.
|
|
125
|
+
*/
|
|
126
|
+
protected emitValue(type: 'vf-input' | 'vf-change', detail?: Record<string, unknown>): void;
|
|
127
|
+
/** Mirror the native control's value into `value` and announce a keystroke. */
|
|
128
|
+
protected handleInput(event: Event): void;
|
|
129
|
+
/** Mirror the native control's value into `value` and announce a commit. */
|
|
130
|
+
protected handleChange(event: Event): void;
|
|
131
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ScaleController as e } from "./scale.js";
|
|
2
|
+
import t from "./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.js";
|
|
3
|
+
import { GridSnapController as n } from "./grid-snap.js";
|
|
4
|
+
import { emit as r, emitNative as i } from "./events.js";
|
|
5
|
+
import { VfShadowRoleControl as a } from "./form-control.js";
|
|
6
|
+
import { FocusRuleController as o } from "./focus-modality.js";
|
|
7
|
+
import { nothing as s } from "lit";
|
|
8
|
+
import { property as c } from "lit/decorators.js";
|
|
9
|
+
//#region src/text-control.ts
|
|
10
|
+
var l = class extends a {
|
|
11
|
+
constructor(...t) {
|
|
12
|
+
super(...t), this.value = "", this.placeholder = "", this.readonly = !1, this.name = "", this.label = "", this.scale = new e(this), this.gridSnap = new n(this), this.focusRule = new o(this);
|
|
13
|
+
}
|
|
14
|
+
static {
|
|
15
|
+
this.shadowRootOptions = {
|
|
16
|
+
...a.shadowRootOptions,
|
|
17
|
+
delegatesFocus: !0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
get valueMissing() {
|
|
21
|
+
return this.value === "";
|
|
22
|
+
}
|
|
23
|
+
static {
|
|
24
|
+
this.forwardedAttributes = [
|
|
25
|
+
"autocomplete",
|
|
26
|
+
"inputmode",
|
|
27
|
+
"enterkeyhint",
|
|
28
|
+
"maxlength",
|
|
29
|
+
"spellcheck",
|
|
30
|
+
"autocapitalize"
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
static get observedAttributes() {
|
|
34
|
+
return [...super.observedAttributes, ...this.forwardedAttributes];
|
|
35
|
+
}
|
|
36
|
+
attributeChangedCallback(e, t, n) {
|
|
37
|
+
super.attributeChangedCallback(e, t, n), this.constructor.forwardedAttributes.includes(e) && this.requestUpdate();
|
|
38
|
+
}
|
|
39
|
+
forwardedAttr(e) {
|
|
40
|
+
return this.getAttribute(e) ?? s;
|
|
41
|
+
}
|
|
42
|
+
get wellClass() {
|
|
43
|
+
return `vf-field-well vf-snap${this.focusRule.marked ? " vf-focus-rule" : ""}`;
|
|
44
|
+
}
|
|
45
|
+
connectedCallback() {
|
|
46
|
+
super.connectedCallback(), this.latchFormDefault(this.value);
|
|
47
|
+
}
|
|
48
|
+
updated(e) {
|
|
49
|
+
(e.has("value") || this.disabledChanged(e)) && this.syncFormValue(this.value);
|
|
50
|
+
}
|
|
51
|
+
formResetCallback() {
|
|
52
|
+
this.value = this.formDefault("");
|
|
53
|
+
}
|
|
54
|
+
isSubmitEnter(e) {
|
|
55
|
+
return e.key === "Enter" && !e.isComposing && !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey;
|
|
56
|
+
}
|
|
57
|
+
requestImplicitSubmit() {
|
|
58
|
+
let e = this.internals.form;
|
|
59
|
+
if (!e) return;
|
|
60
|
+
let t = [...e.elements].find((e) => e instanceof HTMLButtonElement && e.type === "submit" || e instanceof HTMLInputElement && (e.type === "submit" || e.type === "image") || e.localName === "vf-button" && e.type?.toLowerCase() === "submit");
|
|
61
|
+
if (t) {
|
|
62
|
+
if (t.matches(":disabled")) return;
|
|
63
|
+
t.click();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
e.requestSubmit();
|
|
67
|
+
}
|
|
68
|
+
emitValue(e, t = { value: this.value }) {
|
|
69
|
+
r(this, e, t);
|
|
70
|
+
}
|
|
71
|
+
handleInput(e) {
|
|
72
|
+
this.value = e.target.value, this.emitValue("vf-input");
|
|
73
|
+
}
|
|
74
|
+
handleChange(e) {
|
|
75
|
+
this.value = e.target.value, this.emitValue("vf-change"), i(this, "change");
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
t([c()], l.prototype, "value", void 0), t([c()], l.prototype, "placeholder", void 0), t([c({
|
|
79
|
+
type: Boolean,
|
|
80
|
+
reflect: !0
|
|
81
|
+
})], l.prototype, "readonly", void 0), t([c({ reflect: !0 })], l.prototype, "name", void 0), t([c()], l.prototype, "label", void 0);
|
|
82
|
+
//#endregion
|
|
83
|
+
export { l as VfTextControlBase };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type CSSResult, type TemplateResult } from 'lit';
|
|
2
|
+
import { type TileRect } from './styles/recipes/tile.js';
|
|
3
|
+
/**
|
|
4
|
+
* The tiled-fill machinery that replaces CSS `background-repeat` on the kit's
|
|
5
|
+
* convertible tiled surfaces (the desktop dither, the windoid dots, the swatch
|
|
6
|
+
* checker, the barber stripes).
|
|
7
|
+
*
|
|
8
|
+
* A CSS repeating fill is ONE paint-snapped box holding N *unsnapped* repeats,
|
|
9
|
+
* each placed at `k × tileSize` where the tile size is a single stored length
|
|
10
|
+
* the engine quantizes to its layout grid (Chromium 1/64 CSS px, Gecko 1/60).
|
|
11
|
+
* The error compounds linearly with `k` until a motif boundary has walked a
|
|
12
|
+
* whole device pixel and the 1-bit art smears to gray. The span construction
|
|
13
|
+
* (`tileSpan`, src/styles/recipes/tile.ts) makes the stored length exact for
|
|
14
|
+
* every scale the *density* ladder derives — but zoom mints scales with
|
|
15
|
+
* arbitrary prime denominators (20/17 at Safari's 85%, 30/23 at its 115%),
|
|
16
|
+
* and no finite lattice can hold those. ZOOM-TILE-DRIFT.md is the analysis;
|
|
17
|
+
* TILE-GRID-PLAN.md the plan this module implements.
|
|
18
|
+
*
|
|
19
|
+
* Two rendering paths, chosen by whether the surface's pattern token is
|
|
20
|
+
* overridden ({@link patternOverride}):
|
|
21
|
+
*
|
|
22
|
+
* **Kit art (token unset): one whole-surface raster** — a single `.vf-tile-raster`
|
|
23
|
+
* element carrying the motif tiled over the surface's declared system-px size
|
|
24
|
+
* as a raster image at one image px per system px (`tileRaster`), stretched
|
|
25
|
+
* `background-size: 100% 100%` under `image-rendering: pixelated`. Exact at
|
|
26
|
+
* every scale, holdable or not, for two measured reasons: nearest-neighbor
|
|
27
|
+
* sampling can only produce source colors, and ONE box has no interior
|
|
28
|
+
* boundaries — Chromium paints an image background to the box's *unsnapped*
|
|
29
|
+
* layout rect with antialiased edges, so any fill assembled from several
|
|
30
|
+
* boxes blends a one-device-px line at every interior seam at a zoom-minted
|
|
31
|
+
* scale, no matter how exactly the boxes were placed. (That measurement is
|
|
32
|
+
* what buried both this plan's original per-tile form and SVG art of any
|
|
33
|
+
* kind: Chromium rasterizes SVG images at the fractional layout size and
|
|
34
|
+
* ignores `image-rendering` for them.) The box's own edge is the one place a
|
|
35
|
+
* partial device pixel remains — the same status every component box already
|
|
36
|
+
* has. The sampling stays exact because the box's stored length errs at most
|
|
37
|
+
* 1/128 CSS px over the WHOLE surface: the relative error shrinks as the
|
|
38
|
+
* surface grows, so the nearest-neighbor grid cannot slip a pixel at any
|
|
39
|
+
* realistic size.
|
|
40
|
+
*
|
|
41
|
+
* **Consumer art (token set): the placed tile grid** — a flat set of
|
|
42
|
+
* absolutely positioned `.vf-tile` boxes, each `T × T` system px at
|
|
43
|
+
* `left: calc(var(--vf-scale, 1) * kT px)`: one single-multiplication length
|
|
44
|
+
* per tile, quantized once, so each box paint-snaps onto the device grid
|
|
45
|
+
* independently and the documented tile geometry (`--vf-*-pattern` art on a
|
|
46
|
+
* 30- or 60-system-px tile) renders verbatim. Positions never accumulate
|
|
47
|
+
* error; at a zoom-minted scale what remains is the bounded per-seam blend
|
|
48
|
+
* above — a hairline per tile boundary, never surface-wide smear. Raster
|
|
49
|
+
* consumer art additionally magnifies nearest-neighbor (the `vf-img` idiom).
|
|
50
|
+
*
|
|
51
|
+
* The trap that must not come back: NEVER lay tiles out with CSS grid,
|
|
52
|
+
* flexbox, inline-block flow, or nested row containers. Track and flow
|
|
53
|
+
* layouts accumulate quantized box sizes — the position of column `k` becomes
|
|
54
|
+
* a *sum* of `k` quantized lengths, error `k/64` CSS px, and the disease
|
|
55
|
+
* returns. Flat absolute placement with one multiplication per tile is the
|
|
56
|
+
* correctness, not a style choice.
|
|
57
|
+
*
|
|
58
|
+
* Wiring: the *container* — the layer the fill used to paint on, or a
|
|
59
|
+
* dedicated child filling it — takes the `vf-tile-grid` class (clip and
|
|
60
|
+
* pointer transparency; it must be positioned, which every kit layer already
|
|
61
|
+
* is). For the grid path it resolves `--_vf-tile-image` — the pattern-token
|
|
62
|
+
* `var()` each tile stretches over its box, stated once so no data URI is
|
|
63
|
+
* repeated per tile — and {@link tileGrid} renders the tiles. For the raster
|
|
64
|
+
* path the component renders a `.vf-tile-raster` child whose
|
|
65
|
+
* `background-image` it writes from `tileRaster(...)`, regenerated only when
|
|
66
|
+
* the declared system-px size changes (density and zoom re-render nothing:
|
|
67
|
+
* the box lengths are live against `--vf-scale`).
|
|
68
|
+
*
|
|
69
|
+
* Forced colors keeps each surface's existing layer-level branches (flat
|
|
70
|
+
* Canvas desktop, span-masked dots and barber, the swatch's
|
|
71
|
+
* forced-color-adjust) — no mask pipeline rasterizes exactly at a zoom-minted
|
|
72
|
+
* scale (image-rendering does not reach masks), so forced-colors-plus-zoom
|
|
73
|
+
* remains the one accepted residual, unchanged from before this work.
|
|
74
|
+
*/
|
|
75
|
+
export declare const vfTileGrid: CSSResult;
|
|
76
|
+
/**
|
|
77
|
+
* The flat set of tiles covering `cols × rows` boxes of `tile` system px each,
|
|
78
|
+
* to render inside a `vf-tile-grid` container — the consumer-pattern path.
|
|
79
|
+
* Each tile carries its own single-multiplication `calc()` position and size,
|
|
80
|
+
* live against `--vf-scale` — so a density or zoom change re-renders nothing,
|
|
81
|
+
* and the count changes only when the caller's *declared system-px size*
|
|
82
|
+
* does. Compute `cols`/`rows` from that declared size with `Math.ceil`; the
|
|
83
|
+
* clipped last row and column are absorbed by the container's clip.
|
|
84
|
+
*/
|
|
85
|
+
export declare function tileGrid({ cols, rows, tile, }: {
|
|
86
|
+
cols: number;
|
|
87
|
+
rows: number;
|
|
88
|
+
tile: number;
|
|
89
|
+
}): TemplateResult[];
|
|
90
|
+
/**
|
|
91
|
+
* The consumer's override of a pattern token, or `''` when the token is unset
|
|
92
|
+
* and the surface should paint its own art — how a converted surface picks
|
|
93
|
+
* between its whole-surface raster (kit art) and its placed tile grid
|
|
94
|
+
* (consumer art at the documented tile geometry).
|
|
95
|
+
*
|
|
96
|
+
* Read with `getComputedStyle` at update time, not observed live: a pattern
|
|
97
|
+
* token swapped at runtime without touching the component wants a nudge (any
|
|
98
|
+
* property write, or `requestUpdate()`) — the same re-check-on-update
|
|
99
|
+
* contract the component's other measured inputs follow.
|
|
100
|
+
*/
|
|
101
|
+
export declare function patternOverride(el: Element, token: string): string;
|
|
102
|
+
/**
|
|
103
|
+
* A one-entry cache for a surface's whole-surface raster, keyed by its
|
|
104
|
+
* system-px size — so every render with an unchanged size (all of them except
|
|
105
|
+
* a declared-size change) reuses the encoded image instead of re-running
|
|
106
|
+
* `tileRaster`'s canvas encode.
|
|
107
|
+
*/
|
|
108
|
+
export declare class TileRasterCache {
|
|
109
|
+
#private;
|
|
110
|
+
for(motifWidth: number, motifHeight: number, rects: readonly TileRect[], width: number, height: number): string;
|
|
111
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { sysLength as e } from "./scale.js";
|
|
2
|
+
import { tileRaster as t } from "./styles/recipes/tile.js";
|
|
3
|
+
import { css as n, html as r } from "lit";
|
|
4
|
+
//#region src/tile-grid.ts
|
|
5
|
+
var i = n`
|
|
6
|
+
.vf-tile-grid {
|
|
7
|
+
/* Clip the overdraw: a fill overdrawn past a box that is not a whole
|
|
8
|
+
number of tiles (or a raster ceiled up to one) crops here. */
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
pointer-events: none;
|
|
11
|
+
}
|
|
12
|
+
.vf-tile,
|
|
13
|
+
.vf-tile-raster {
|
|
14
|
+
position: absolute;
|
|
15
|
+
/* The box IS the size — never the image's intrinsic size, and never an
|
|
16
|
+
intra-box repeat: a consumer tile smaller than its box repeating
|
|
17
|
+
inside it would re-import the accumulation this module removes. */
|
|
18
|
+
background-size: 100% 100%;
|
|
19
|
+
background-repeat: no-repeat;
|
|
20
|
+
/* Raster art samples nearest-neighbor — the exactness mechanism for the
|
|
21
|
+
kit's own tiles and the vf-img idiom for consumer raster art. A no-op
|
|
22
|
+
for SVG (which is why kit art is raster; see tileRaster). */
|
|
23
|
+
image-rendering: pixelated;
|
|
24
|
+
}
|
|
25
|
+
.vf-tile {
|
|
26
|
+
background-image: var(--_vf-tile-image);
|
|
27
|
+
}
|
|
28
|
+
.vf-tile-raster {
|
|
29
|
+
top: 0;
|
|
30
|
+
left: 0;
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
function a({ cols: t, rows: n, tile: i }) {
|
|
34
|
+
let a = e(i), o = [];
|
|
35
|
+
for (let s = 0; s < n; s++) for (let n = 0; n < t; n++) o.push(r`<span
|
|
36
|
+
class="vf-tile"
|
|
37
|
+
style="left:${e(n * i)};top:${e(s * i)};width:${a};height:${a}"
|
|
38
|
+
></span>`);
|
|
39
|
+
return o;
|
|
40
|
+
}
|
|
41
|
+
function o(e, t) {
|
|
42
|
+
return typeof getComputedStyle > "u" ? "" : getComputedStyle(e).getPropertyValue(t).trim();
|
|
43
|
+
}
|
|
44
|
+
var s = class {
|
|
45
|
+
#e = "";
|
|
46
|
+
#t = "";
|
|
47
|
+
for(e, n, r, i, a) {
|
|
48
|
+
let o = `${i}x${a}`;
|
|
49
|
+
return this.#e !== o && (this.#e = o, this.#t = t(e, n, r, i, a)), this.#t;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
export { s as TileRasterCache, o as patternOverride, a as tileGrid, i as vfTileGrid };
|