@johnmorrisdotca/kazoku-ui 0.12.0 → 0.13.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/CHANGELOG.md +14 -0
- package/README.md +235 -14
- package/dist/view-controls/controller.d.ts +2 -2
- package/dist/view-controls/controller.js +22 -13
- package/dist/view-controls/element.d.ts +1 -1
- package/dist/view-controls/element.js +1 -1
- package/dist/view-controls/index.d.ts +1 -1
- package/dist/view-controls/index.js +1 -1
- package/dist/view-controls/math.d.ts +8 -2
- package/dist/view-controls/math.js +11 -2
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.13.0] - 2026-11-03
|
|
6
|
+
|
|
7
|
+
The wheel over the view zooms it, in every family package that uses the view controls.
|
|
8
|
+
|
|
9
|
+
- **The wheel zooms** (KAZOKU-UI-45). The wheel over the view now zooms it about the pointer, like a map, about 1.2 times a notch; before, the plain wheel tipped a focused view and only Ctrl or Command with the wheel zoomed. A trackpad's pinch (Ctrl and the wheel) still zooms. Shift and the wheel, or a sideways swipe, turns the view. Tipping is a drag, the arrow keys or the tilt slider. With the pointer anywhere else the wheel scrolls the page; a wheel that starts while the zoom is already at its end that way (turned down at Fit, or up at the most) scrolls the page as well, so it is never trapped, while the steps of a turn that reaches the end part-way through stay with the view, so the page does not jump. A flat view leaves Shift and the wheel and a sideways swipe to the page. `wheelZoomFactor` takes a third argument, `"pinch"` (the default, as before) or `"wheel"`, and `WHEEL_GESTURE_MS` (300) is exported. `key-tilt="wheel"` still swaps the up and down keys.
|
|
10
|
+
- The README opens with the package's thumbnail, and the demo carries it as its link preview (og:image) and publishes the card, a dark card and an icon on Pages at `thumbnail.png`, `thumbnail-dark.png` and `icon.png`.
|
|
11
|
+
|
|
12
|
+
### Documentation
|
|
13
|
+
|
|
14
|
+
- **Fourteen guides, each with a Japanese page, and a generated API reference.** `docs/guides/` has getting started, view controls, layouts and the zoom slider, the view controller and its arithmetic, the theme switch, the language picker, address state, save image, the shared renderer, frameworks and CSP, accessibility and labels, architecture, troubleshooting and upgrading; each has its Japanese page of the same name in `docs/guides/ja/`. `docs/API.md` is written from the built declarations (`pnpm docs:api`). The README is rewritten into sections around the same material, and no longer says the package is unpublished.
|
|
15
|
+
- **Every example runs, and the documentation is checked.** `pnpm test:readme` runs each `ts`, `js` and `html` block of the README and the guides (Node for the scripts, headless Chromium in software for the HTML); `pnpm docs:check` fails when `docs/API.md` is out of date, a guide has no Japanese page or a different set of code blocks, a link or picture is missing, or an export is named nowhere. Both run in `.github/workflows/docs.yml`.
|
|
16
|
+
- **Twelve more pictures** in `docs/images/` (the layouts on a phone, the header's switches, address state, save image, the shared renderer and the builder, light and dark), made by `pnpm screenshots`. The hero and the layouts pictures were taken again, because the demo's links now include the builder.
|
|
17
|
+
- New demo page: `demo/builder.html`, to choose a part, set its options, see it live and copy the code, in English and in Japanese, with `e2e/builder.demo.mjs`.
|
|
18
|
+
|
|
5
19
|
## [0.12.0] - 2026-11-03
|
|
6
20
|
|
|
7
21
|
The view controls in four layouts, chosen by one attribute, and the zoom as a slider with marks. A page that names no layout looks exactly as before.
|
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<p align="center"><a href="https://johnmorrisdotca.github.io/kazoku-ui/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kazoku-ui/main/docs/images/thumbnail-dark.png"><img src="https://raw.githubusercontent.com/johnmorrisdotca/kazoku-ui/main/docs/images/thumbnail.png" alt="Kazoku UI: a card with a blue circle and a black triangle, turned and tipped in 3D, over the view controls that turn it." width="1200"></picture></a></p>
|
|
2
|
+
|
|
1
3
|
# Kazoku UI 家族
|
|
2
4
|
|
|
3
5
|
<picture>
|
|
@@ -9,13 +11,136 @@ The family's shared interface parts, as their own versioned package: one part pe
|
|
|
9
11
|
The first part is the **view controls** for any 3D or zoomable view. They never draw the scene: they tell your page what the reader chose, and your canvas, WebGL,
|
|
10
12
|
SVG or CSS 3D applies it.
|
|
11
13
|
|
|
12
|
-
[Demo](https://johnmorrisdotca.github.io/kazoku-ui/) · [Layouts demo](https://johnmorrisdotca.github.io/kazoku-ui/layouts.html) · [Strict-CSP demo](https://johnmorrisdotca.github.io/kazoku-ui/csp.html) · [Theme switch](docs/theme-switch.md) · [Language picker](docs/language-picker.md) · [Address state](docs/address-state.md) · [Save image](docs/save-image.md) · [Shared 3D renderer](docs/renderer.md) · [The drag rule](docs/drag-rule.md) · [Content-Security-Policy](docs/csp.md) · [Labels, English and 日本語](docs/labels.md) · [Changelog](CHANGELOG.md)
|
|
14
|
+
[Demo](https://johnmorrisdotca.github.io/kazoku-ui/) · [Layouts demo](https://johnmorrisdotca.github.io/kazoku-ui/layouts.html) · [Strict-CSP demo](https://johnmorrisdotca.github.io/kazoku-ui/csp.html) · [Theme switch](docs/theme-switch.md) · [Language picker](docs/language-picker.md) · [Address state](docs/address-state.md) · [Save image](docs/save-image.md) · [Shared 3D renderer](docs/renderer.md) · [The drag rule](docs/drag-rule.md) · [Content-Security-Policy](docs/csp.md) · [Labels, English and 日本語](docs/labels.md) · [Changelog](CHANGELOG.md) · [Builder](https://johnmorrisdotca.github.io/kazoku-ui/builder.html) · [API reference](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/API.md) · [Guides](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides)
|
|
15
|
+
|
|
16
|
+
## In 30 seconds
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install @johnmorrisdotca/kazoku-ui
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<div id="scene" tabindex="0" style="height: 200px">Your scene</div>
|
|
24
|
+
<kazoku-view-controls for="scene" address></kazoku-view-controls>
|
|
25
|
+
<script type="module">
|
|
26
|
+
import "@johnmorrisdotca/kazoku-ui/view-controls/define";
|
|
27
|
+
|
|
28
|
+
document.querySelector("kazoku-view-controls").addEventListener("kazoku-viewchange", (event) => {
|
|
29
|
+
const { yaw, tilt, zoom, panX, panY } = event.detail;
|
|
30
|
+
document.getElementById("scene").style.transform = `translate(${panX}px, ${panY}px) scale(${zoom}) rotateX(${tilt}deg) rotateY(${yaw}deg)`;
|
|
31
|
+
});
|
|
32
|
+
</script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
And the pure functions, with no page at all:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
import { formatAddress, parseAddress, zoomMarks, zoomText } from "@johnmorrisdotca/kazoku-ui/view-controls";
|
|
39
|
+
|
|
40
|
+
console.log(formatAddress("?x=1", { yaw: 40, tilt: 10, zoom: 2, drag: "turn" })); // ?x=1&yaw=40&tilt=10&zoom=2&drag=turn
|
|
41
|
+
console.log(JSON.stringify(parseAddress("?yaw=40&zoom=2"))); // {"yaw":40,"zoom":2}
|
|
42
|
+
console.log(zoomMarks(1, 5).map((mark) => mark.zoom).join(" ")); // 1 2 3 5
|
|
43
|
+
console.log(zoomText(2.44)); // 2.4×
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Who it is for
|
|
47
|
+
|
|
48
|
+
- A page or a package that shows a 3D or zoomable view (a card, a globe, a rocket, a map) and wants the reader to turn, tip, zoom and move it, without writing the controls again.
|
|
49
|
+
- A site with a language choice and a Light, Dark or Auto switch that should look and behave the same across every page and every package of a family.
|
|
50
|
+
- A page that wants Save PNG and Save SVG for a canvas, an SVG or a piece of the page.
|
|
51
|
+
- A page of many 3D views that must stay inside one WebGL context and a pixel budget.
|
|
52
|
+
- A framework app: the parts are plain custom elements with plain modules beside them.
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- **No runtime dependencies, one part to a subpath.** A page loads only what it uses.
|
|
57
|
+
- **View controls** in four layouts, with a zoom slider, a readout, Auto-turn, Centre, keys, touch and the address.
|
|
58
|
+
- **Theme switch** (Light, Dark, Auto) and **language picker** (each language in its own name) that write `data-theme` and `lang` and tell the page.
|
|
59
|
+
- **Save image** for a canvas, an SVG or any element, as PNG or SVG.
|
|
60
|
+
- **Address state**, with no element: choices kept in the link with `replaceState`.
|
|
61
|
+
- **A shared 3D renderer** and its **freeze checks**: one WebGL context, shared assets, stills, a pixel budget.
|
|
62
|
+
- **Labels in English and Japanese**, light and dark, 48 px touch targets, reduced motion respected.
|
|
63
|
+
- **A strict Content-Security-Policy is fine:** styles are adopted as constructable stylesheets.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
13
66
|
|
|
14
67
|
```sh
|
|
15
68
|
npm install @johnmorrisdotca/kazoku-ui
|
|
16
69
|
```
|
|
17
70
|
|
|
18
|
-
|
|
71
|
+
```sh
|
|
72
|
+
pnpm add @johnmorrisdotca/kazoku-ui
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Node 22 or later builds and tests it; the parts run in any current browser. Each part is its own subpath, and the `define` file of an element registers its tag, once, and only if the tag is free.
|
|
76
|
+
|
|
77
|
+
## Use it in your project
|
|
78
|
+
|
|
79
|
+
In a bundler or a framework, import the `define` file of each element you use once, in code that runs in the browser, and import the pure modules wherever you like:
|
|
80
|
+
|
|
81
|
+
```ts no-run
|
|
82
|
+
import "@johnmorrisdotca/kazoku-ui/view-controls/define";
|
|
83
|
+
import "@johnmorrisdotca/kazoku-ui/theme-switch/define";
|
|
84
|
+
import { createAddressState } from "@johnmorrisdotca/kazoku-ui/address-state";
|
|
85
|
+
import type { ViewChangeDetail } from "@johnmorrisdotca/kazoku-ui/view-controls/element";
|
|
86
|
+
|
|
87
|
+
export const state = createAddressState({ defaults: { size: "m" } });
|
|
88
|
+
export const draw = (detail: ViewChangeDetail) => console.log(detail.yaw, detail.zoom);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Use it anywhere
|
|
92
|
+
|
|
93
|
+
On a page with no bundler, an import map tells the browser where each subpath lives, and the `dist` folder is served as it is:
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<script type="importmap">
|
|
97
|
+
{ "imports": {
|
|
98
|
+
"@johnmorrisdotca/kazoku-ui/theme-switch/define": "/node_modules/@johnmorrisdotca/kazoku-ui/dist/theme-switch/define.js"
|
|
99
|
+
} }
|
|
100
|
+
</script>
|
|
101
|
+
<kazoku-theme-switch address storage></kazoku-theme-switch>
|
|
102
|
+
<script type="module">import "@johnmorrisdotca/kazoku-ui/theme-switch/define";</script>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Examples
|
|
106
|
+
|
|
107
|
+
A theme switch and a language picker side by side, with the page answering their events:
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<kazoku-theme-switch address storage></kazoku-theme-switch>
|
|
111
|
+
<kazoku-language-picker languages="en ja" address storage></kazoku-language-picker>
|
|
112
|
+
<script type="module">
|
|
113
|
+
import "@johnmorrisdotca/kazoku-ui/theme-switch/define";
|
|
114
|
+
import "@johnmorrisdotca/kazoku-ui/language-picker/define";
|
|
115
|
+
|
|
116
|
+
document.addEventListener("kazoku-themechange", ({ detail }) => console.log(detail.choice, detail.scheme));
|
|
117
|
+
document.addEventListener("kazoku-languagechange", ({ detail }) => console.log(detail.lang));
|
|
118
|
+
</script>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Save a canvas as a PNG:
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<canvas id="art" width="360" height="200"></canvas>
|
|
125
|
+
<kazoku-save-image for="art" name="my-picture"></kazoku-save-image>
|
|
126
|
+
<script type="module">import "@johnmorrisdotca/kazoku-ui/save-image/define";</script>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The same idea in code, with the address and the theme as plain functions:
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
import { applyParams } from "@johnmorrisdotca/kazoku-ui/address-state";
|
|
133
|
+
import { resolveTheme } from "@johnmorrisdotca/kazoku-ui/theme-switch";
|
|
134
|
+
import { filenameFor } from "@johnmorrisdotca/kazoku-ui/save-image";
|
|
135
|
+
|
|
136
|
+
console.log(applyParams("?a=1&size=m", { size: "l", a: null }, { size: "m" })); // ?size=l
|
|
137
|
+
console.log(resolveTheme("auto", true)); // dark
|
|
138
|
+
console.log(filenameFor("My Card/x", "png")); // My Card-x.png
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Parts
|
|
142
|
+
|
|
143
|
+
Each its own subpath; later ones: picture-tile pickers, Help, GitHub and npm pills, the family's backgrounds.
|
|
19
144
|
|
|
20
145
|
| Subpath | What it is |
|
|
21
146
|
|---|---|
|
|
@@ -40,7 +165,7 @@ Parts (each its own subpath; later ones: picture-tile pickers, Help, GitHub and
|
|
|
40
165
|
- **Two levels, so the bar stays simple.** The default, `controls="basic"`, shows the angle slider, Auto-turn, zoom out and in with a **zoom readout** (`2.4×`; tap it to go back to Fit), Fit, and a small More button; while the view turns, one small **speed** button beside Auto-turn (it shows the word Speed over `Medium`, and a press steps to the next speed). More (or `controls="advanced"`) adds the tilt slider, all three speeds (Slow, Medium, Fast), Front, and, once zoomed in, the four-arrow pan pad and the Move or Turn choice for what a drag does. The reader's choice with More is remembered in `localStorage` and beats the attribute.
|
|
41
166
|
- The zoom readout is the view's `zoom` (1× is Fit, whatever `fit-scale` is), one decimal, the one zoom number on the bar: it follows the buttons, the wheel, a pinch, a double press, Fit, the keys, the address, `set` and a changed limit. A screen reader is told "Zoom level 2.4×" once the zoom has settled for a moment, not for every step of a pinch.
|
|
42
167
|
- Auto-turn with play and pause; its speed is Slow (half the `speed` attribute), Medium (the `speed` attribute, 30 degrees a second) or Fast (twice it), kept in the address as `?speed=slow` or `?speed=fast`.
|
|
43
|
-
-
|
|
168
|
+
- **The wheel zooms.** The wheel over the view zooms it about the pointer, like a map; a trackpad's pinch (and Ctrl or Command with the wheel) zooms too. Shift and the wheel, or a sideways swipe, turns it. Tipping is a drag, the arrow keys or the tilt slider. With the pointer anywhere else the wheel scrolls the page, and a wheel that starts while the zoom is already at its end that way (turned down at Fit) scrolls the page too, so the page is never trapped.
|
|
44
169
|
- Touch: one finger turns at 1x and moves the view when zoomed in; two fingers pinch.
|
|
45
170
|
- Keys on the view: arrows turn and tip it, `+` and `-` zoom, `0` or Home is Front and Fit.
|
|
46
171
|
- The choices are kept in the address, with keys you can rename.
|
|
@@ -66,7 +191,7 @@ The `layout` attribute says where the controls sit; `controls` (`basic` or `adva
|
|
|
66
191
|
| `vertical` | A column beside the view, `--kazoku-column-width` wide (16rem). Auto-turn and More at the top, so More does not move when it opens; then the Angle slider, the zoom slider, and − readout + and Fit. On a narrow screen put it under the view and give it `width: auto`. | Auto-turn, More, Angle, the zoom slider, − readout +, Fit | Tilt, the speeds, Front, the pan pad, in the column |
|
|
67
192
|
| `compact` | The least room, for a card or a phone: **one row** of Auto-turn, −, the readout, + and More (icons, named for a screen reader). It never takes a second row until More is pressed; while it turns, the speed is in More, and Centre is there with Fit. | Auto-turn, − readout +, More | Everything else, one press away, under the row: the zoom slider and Fit, Angle, Tilt, the speeds, Front, the pan pad |
|
|
68
193
|
|
|
69
|
-
```html
|
|
194
|
+
```html no-check
|
|
70
195
|
<!-- Horizontal: under the view -->
|
|
71
196
|
<div id="view" tabindex="0">...your scene...</div>
|
|
72
197
|
<kazoku-view-controls for="view" layout="horizontal"></kazoku-view-controls>
|
|
@@ -136,7 +261,7 @@ export function Viewer({ draw }) {
|
|
|
136
261
|
|
|
137
262
|
`createViewController` is the same hands and state with no markup of its own, for a page that draws its own buttons. It touches the DOM only in `bind`.
|
|
138
263
|
|
|
139
|
-
```js
|
|
264
|
+
```js no-run
|
|
140
265
|
import { createViewController } from "@johnmorrisdotca/kazoku-ui/view-controls";
|
|
141
266
|
|
|
142
267
|
const view = createViewController({
|
|
@@ -164,7 +289,7 @@ The hands capture the pointer on the `for` element, so a press on a button or li
|
|
|
164
289
|
|
|
165
290
|
The zoom buttons and keys have no pointer, so they zoom about the view's middle. If your scene's middle is not the view's (a piece standing to one side, say), tell the controls where it is:
|
|
166
291
|
|
|
167
|
-
```js
|
|
292
|
+
```js no-run
|
|
168
293
|
controls.anchor = { x: 60, y: -30 }; // pixels from the view's middle when it is at Fit
|
|
169
294
|
```
|
|
170
295
|
|
|
@@ -180,13 +305,13 @@ In every turnable view the surface under the pointer follows the pointer, the ar
|
|
|
180
305
|
|
|
181
306
|
### Keys, the wheel and the turn
|
|
182
307
|
|
|
183
|
-
Arrow Up tips like a drag up (`tilt` gets larger)
|
|
308
|
+
The wheel over the view zooms it about the pointer, and the steps of one turn of the wheel or one swipe stay with the view even when the zoom reaches its end part-way through, so the page does not jump. A new turn of the wheel at that end scrolls the page. Shift and the wheel, or a sideways swipe, turns the view (on a flat view they are left to the page). Arrow Up tips like a drag up (`tilt` gets larger); `key-tilt="wheel"` makes it tip the other way (`tilt` gets smaller), the way the wheel turned up tipped the view before 0.13.0, and Arrow Down the other way. `stop-turn` stops the automatic turn when the reader takes hold of the view with a press, the wheel or a key (a `pause` event is sent first); the defaults keep it turning, and a button or slider never stops it. `tilt-step="1"` makes a drag give whole degrees of tilt.
|
|
184
309
|
|
|
185
310
|
### Opening at the scene's natural size
|
|
186
311
|
|
|
187
312
|
Zoom 1 is Fit, whatever size that is on the page. A page that draws its scene at `fitScale` (for example 0.4 of natural size when it fits the view) and wants to open at natural size sets `zoom="1"` and tells the controls the scale:
|
|
188
313
|
|
|
189
|
-
```js
|
|
314
|
+
```js no-run
|
|
190
315
|
const controls = document.querySelector("kazoku-view-controls");
|
|
191
316
|
controls.setAttribute("zoom", "1"); // natural size
|
|
192
317
|
controls.setFitScale(measure()); // the scale the scene is drawn at when it fits, for example 0.4
|
|
@@ -223,7 +348,7 @@ An `init` event is sent once, a moment after the element connects, with the star
|
|
|
223
348
|
| `fit-scale` | The scale your page draws the scene at when it is at Fit (event `zoom` 1): what zoom 1 means. 1 by default. Or call `element.setFitScale(n)` once the scene is measured. |
|
|
224
349
|
| `ignore` | A CSS selector for what inside the view a pointer press leaves alone, so its click goes through: `button, a, input, select, textarea, label, [data-kazoku-ignore]` by default. A bare `ignore=""` leaves nothing alone. |
|
|
225
350
|
| `anchor` | `x,y`: the point the + and - buttons, the 2x button and the + and - keys zoom about, in CSS pixels from the middle of the view when it is at Fit (zoom 1, no pan). It is a point on the picture, so it moves with it and stays fixed on screen while the buttons zoom. Left out, they zoom about the view's middle, as before. The wheel, a pinch and a double tap still zoom about the pointer. Or set `element.anchor = { x, y }` (`undefined` clears it). |
|
|
226
|
-
| `key-tilt` | Which way Arrow Up tips the view: `drag` (the default: like a drag up) or `wheel` (the way
|
|
351
|
+
| `key-tilt` | Which way Arrow Up tips the view: `drag` (the default: like a drag up) or `wheel` (the other way, as the wheel turned up tipped it before 0.13.0; the wheel zooms now). Arrow Down goes the other way from Up. |
|
|
227
352
|
| `stop-turn` | A press on the view, the wheel over it or a key it handles stops the automatic turn. Off by default, so the turn carries on. Buttons and sliders never stop it. |
|
|
228
353
|
| `tilt-step` | Round the tilt of a drag to a multiple of this many degrees (`1` gives whole degrees). 0 by default: not rounded. |
|
|
229
354
|
| `object-size` | `w,h`: the object's size at Fit in CSS pixels, when it is smaller than the view. A pan then keeps at least a quarter of the object in view, however far it is dragged; left out, the object is the picture, which keeps covering the view. Or set `element.objectSize = { width, height }`. |
|
|
@@ -278,7 +403,7 @@ See [docs/language-picker.md](docs/language-picker.md). The demo's own English a
|
|
|
278
403
|
|
|
279
404
|
Choices kept in the address, without an element: `createAddressState({ defaults })` with `get`, `set`, `remove`, `subscribe` and `flush`, writing with `replaceState` (no history entry, one write for the changes of a quarter of a second, a value at its default leaves the address, other parameters and the hash kept), and hearing back and forward. The theme switch, the language picker and the view controls keep their choices through it.
|
|
280
405
|
|
|
281
|
-
```js
|
|
406
|
+
```js no-run
|
|
282
407
|
import { createAddressState } from "@johnmorrisdotca/kazoku-ui/address-state";
|
|
283
408
|
|
|
284
409
|
const state = createAddressState({ defaults: { size: "m" } });
|
|
@@ -306,7 +431,7 @@ materials, textures and models are counted and freed when the last view lets go.
|
|
|
306
431
|
requested at a time, and a canvas never passes 6 million pixels or 4096 a side. Context loss is handled in one place. **three.js is a peer dependency and is never bundled**:
|
|
307
432
|
your page passes `load: () => import("three")`, so it loads only when the first view goes live. How a package uses it, the API and the freeze checks: [docs/renderer.md](docs/renderer.md). Demo: `renderer.html`.
|
|
308
433
|
|
|
309
|
-
```ts
|
|
434
|
+
```ts no-check
|
|
310
435
|
import { createSharedRenderer } from "@johnmorrisdotca/kazoku-ui/renderer";
|
|
311
436
|
const renderer = createSharedRenderer({ load: () => import("three") });
|
|
312
437
|
const view = renderer.view(host, item); // item: { key, build({ three, assets, width, height }) => { scene, camera } }
|
|
@@ -341,8 +466,104 @@ Gzipped, unbundled. View controls: the element and its definition about 9.3 KB,
|
|
|
341
466
|
|
|
342
467
|
`pnpm check` (lint, types, 122 unit tests of the maths, the address, the controller and the package, the build and the packed package), and `pnpm test:demo` (the demo in Chromium and WebKit at a phone's width and a desk's: the controls in the first screen, a toggle during Auto-turn leaves the angle advancing, the frame loop counted at `requestAnimationFrame` (nothing out of sight or hidden, one callback a page frame otherwise, a moved element too), the zoom readout against the state after every way of zooming, the speed button adding no row, each layout's controls, rows and sizes (compact one row on a phone, turning adding no row), the zoom slider by keys, mouse and touch, its marks for several ranges and always the true zoom, the layouts page, a button or link inside the view still clicks while a drag turns it, the opening zoom and fit scale, zoom about the pointer, zoom about an anchor (`?anchor=pin`), Arrow Up either way (`?key-tilt=wheel`), `?stop-turn`, `?tilt-step=1`, pan when zoomed, touch, the address, the labels, reduced motion, the controls styled under a strict `style-src` with no violation, `demo/csp.html`, the theme switch and the language picker in each browser and size, the address state, back and forward and no history added, on `demo/address.html`, and the save buttons: each file read back from disk, on `demo/save-image.html`, and the shared 3D renderer on `demo/renderer.html`: one WebGL context over fifty focus changes, nothing drawn or requested at rest, the GPU objects and heap flat, the canvas within budget at 5K, context loss, out of sight, the stills' byte budget, and three.js fetched only when a view goes live).
|
|
343
468
|
|
|
344
|
-
##
|
|
469
|
+
## Theming
|
|
470
|
+
|
|
471
|
+
Every part follows the page's colours: `data-theme="light"` or `"dark"` on the page forces them, and without it the device decides. `theme="light"` or `theme="dark"` on one element gives it a look of its own. The colours are custom properties on the element: `--kazoku-ink`, `--kazoku-surface`, `--kazoku-line` and `--kazoku-accent`; the parts are `bar`, `field`, `slider` and `button` (and `group` for the others).
|
|
472
|
+
|
|
473
|
+
```html
|
|
474
|
+
<style>
|
|
475
|
+
kazoku-theme-switch, kazoku-language-picker { --kazoku-accent: #2457d6; --kazoku-line: #c9cdd6; }
|
|
476
|
+
</style>
|
|
477
|
+
<kazoku-theme-switch></kazoku-theme-switch>
|
|
478
|
+
<script type="module">import "@johnmorrisdotca/kazoku-ui/theme-switch/define";</script>
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Accessibility
|
|
482
|
+
|
|
483
|
+
Controls are on or right beside the view and visible without scrolling; changing a control never stops or resets the turn; every control is 48 px; the choices are radio groups with one tab stop and the arrow keys; the zoom is announced politely once it has settled; each language button carries its own `lang`; reduced motion gets no automatic turn. The rules and the labels are in [Accessibility and labels](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/accessibility-and-labels.md).
|
|
484
|
+
|
|
485
|
+
## Limits
|
|
486
|
+
|
|
487
|
+
- **The Japanese labels have not yet been read by a native speaker.**
|
|
488
|
+
- **The demo's scene is a CSS 3D card;** the package has no opinion about what draws.
|
|
489
|
+
- **A picture of an element** (save image) cannot carry web fonts, `::before` or `::after`, or a cross-origin image: draw those into a canvas and save the canvas.
|
|
490
|
+
- **Several visible live 3D views at once are not supported** by the shared renderer: that is its point.
|
|
491
|
+
- **WebKit has no software GL switch,** so off CI its tests skip the pages that draw WebGL.
|
|
492
|
+
- **Early development (0.x):** a minor version can add to the interface. [Upgrading](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/upgrading.md) lists what asks something of you.
|
|
493
|
+
|
|
494
|
+
## Browser and runtime support
|
|
495
|
+
|
|
496
|
+
The elements need custom elements, shadow DOM and constructable stylesheets (Chrome, Edge, Firefox and Safari 16.4 or later; older Safari falls back to a `<style>` that carries the element's `nonce`). The demo is played in Chromium and WebKit, at a phone's width and a desk's. Node 22 or later builds and tests it (22.18 or later to run the TypeScript tests directly); the pure modules import on a server. To load an element only where it can run:
|
|
497
|
+
|
|
498
|
+
```js no-run
|
|
499
|
+
if ("customElements" in window) {
|
|
500
|
+
await import("@johnmorrisdotca/kazoku-ui/theme-switch/define");
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## Languages
|
|
505
|
+
|
|
506
|
+
Every label is in English and in Japanese, and a unit test holds that every key is in both. `lang="ja"` (on the element or on a parent) chooses Japanese; anything else is English. Your own words go over either set: `element.words = { front: "Home" }`, or `wordsFor("ja", { front: "ホーム" })`. The language picker names each language in its own language. The guides have a Japanese page each, in [docs/guides/ja](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/ja).
|
|
507
|
+
|
|
508
|
+
## Roadmap
|
|
509
|
+
|
|
510
|
+
Parts that arrive later: picture-tile pickers, Help, GitHub and npm pills and the family's backgrounds, each a new subpath that changes nothing that exists. Next for what is here: the Japanese labels read by a native speaker, and the family's other packages moving to the shared parts.
|
|
511
|
+
|
|
512
|
+
## Architecture
|
|
513
|
+
|
|
514
|
+
One part, one subpath; the pure code, the element and the registration are three files; a part never draws the page's scene; the shared internals (styles, adoption, scheme, store, target) are not exported. See the [architecture guide](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/architecture.md).
|
|
515
|
+
|
|
516
|
+
## Demos
|
|
517
|
+
|
|
518
|
+
[The demo](https://johnmorrisdotca.github.io/kazoku-ui/) has the view controls, the language picker and the theme switch in its header. Beside it: [layouts](https://johnmorrisdotca.github.io/kazoku-ui/layouts.html), [the builder](https://johnmorrisdotca.github.io/kazoku-ui/builder.html), [address state](https://johnmorrisdotca.github.io/kazoku-ui/address.html), [save image](https://johnmorrisdotca.github.io/kazoku-ui/save-image.html), [the shared renderer](https://johnmorrisdotca.github.io/kazoku-ui/renderer.html), [exact grab](https://johnmorrisdotca.github.io/kazoku-ui/grab.html) and [a strict Content-Security-Policy](https://johnmorrisdotca.github.io/kazoku-ui/csp.html).
|
|
519
|
+
|
|
520
|
+
<picture>
|
|
521
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kazoku-ui/main/docs/images/builder-dark.webp">
|
|
522
|
+
<img alt="The builder: the part chosen in a row of buttons, its options beside the live preview, and the code to copy under them" src="https://raw.githubusercontent.com/johnmorrisdotca/kazoku-ui/main/docs/images/builder-light.webp">
|
|
523
|
+
</picture>
|
|
524
|
+
|
|
525
|
+
## Documentation
|
|
526
|
+
|
|
527
|
+
| Guide | What it covers |
|
|
528
|
+
|---|---|
|
|
529
|
+
| [Getting started](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/getting-started.md) | The parts in a few minutes |
|
|
530
|
+
| [View controls](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/view-controls.md) | Every attribute, the event, the keys, the address |
|
|
531
|
+
| [Layouts and the zoom slider](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/layouts-and-zoom.md) | The four layouts and the slider's scale |
|
|
532
|
+
| [The view controller and its arithmetic](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/view-controller.md) | The controller without the element |
|
|
533
|
+
| [Theme switch](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/theme-switch.md) | Light, Dark and Auto |
|
|
534
|
+
| [Language picker](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/language-picker.md) | Each language in its own name |
|
|
535
|
+
| [Address state](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/address-state.md) | Choices kept in the link |
|
|
536
|
+
| [Save image](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/save-image.md) | A canvas, an SVG or an element as a picture |
|
|
537
|
+
| [The shared renderer](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/renderer.md) | One WebGL context, and the freeze checks |
|
|
538
|
+
| [Frameworks and CSP](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/frameworks-and-csp.md) | React, Vue, Svelte, Angular, servers, strict policies |
|
|
539
|
+
| [Accessibility and labels](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/accessibility-and-labels.md) | The rules and the words |
|
|
540
|
+
| [Architecture](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/architecture.md) | The parts and the rules |
|
|
541
|
+
| [Troubleshooting and FAQ](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/troubleshooting.md) | A bar that does nothing, an unknown tag |
|
|
542
|
+
| [Upgrading](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/upgrading.md) | What each version asks of you |
|
|
543
|
+
|
|
544
|
+
Each guide has a Japanese page of the same name in [docs/guides/ja](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/ja). The reference for every export is [docs/API.md](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/API.md), written from the built declarations. The repository's own notes: [theme switch](docs/theme-switch.md), [language picker](docs/language-picker.md), [address state](docs/address-state.md), [save image](docs/save-image.md), [renderer](docs/renderer.md), [the drag rule](docs/drag-rule.md), [CSP](docs/csp.md), [labels](docs/labels.md), [the package standard](docs/package-standard.md).
|
|
545
|
+
|
|
546
|
+
## Development
|
|
547
|
+
|
|
548
|
+
```sh
|
|
549
|
+
pnpm check # lint, types, tests, the build and the packed package
|
|
550
|
+
pnpm test:package # pack it as npm does, install it in an empty project, import every entry
|
|
551
|
+
pnpm test:demo # build the demo and play it in Chromium and WebKit
|
|
552
|
+
pnpm test:readme # every example of the README and the guides, run in Node and in a browser
|
|
553
|
+
pnpm docs:api # rewrite docs/API.md from the built declarations
|
|
554
|
+
pnpm docs:check # the reference is current, every guide has its Japanese page, every link and picture is there
|
|
555
|
+
pnpm screenshots # the pictures of the README and the guides (docs/images)
|
|
556
|
+
pnpm dev # the demo on http://127.0.0.1:8400/
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
## Contributing
|
|
560
|
+
|
|
561
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md): no runtime dependencies, one part to a subpath, a part never draws the page's scene, labels in both languages, no marks, and tests for what you change. Add a line to `CHANGELOG.md` under **Unreleased** for a change a user would notice. `templates/` and `docs/package-standard.md` are what the family's other packages copy unchanged.
|
|
562
|
+
|
|
563
|
+
## Changes
|
|
564
|
+
|
|
565
|
+
Every release, dated, is in [CHANGELOG.md](CHANGELOG.md).
|
|
345
566
|
|
|
346
|
-
|
|
567
|
+
## Licence
|
|
347
568
|
|
|
348
|
-
|
|
569
|
+
MIT. No brand, league or sponsor mark is in this package.
|
|
@@ -14,7 +14,7 @@ export interface ChangeInfo {
|
|
|
14
14
|
}
|
|
15
15
|
/** What `bind` leaves alone by default: the page's own controls inside the view. */
|
|
16
16
|
export declare const DEFAULT_IGNORE = "button, a, input, select, textarea, label, [data-kazoku-ignore]";
|
|
17
|
-
/** Which way Arrow Up tips the view: like a drag up (`"drag"`, the default) or the way the wheel turned up
|
|
17
|
+
/** Which way Arrow Up tips the view: like a drag up (`"drag"`, the default) or the other way (`"wheel"`, the way the wheel turned up tipped it before 0.13.0; the wheel zooms now). */
|
|
18
18
|
export type KeyTilt = "drag" | "wheel";
|
|
19
19
|
export interface Clock {
|
|
20
20
|
request(callback: (now: number) => void): number;
|
|
@@ -35,7 +35,7 @@ export interface ViewControllerOptions {
|
|
|
35
35
|
ignore?: string;
|
|
36
36
|
/** The point the zoom buttons and keys zoom about, in content coordinates: CSS pixels from the view's middle when it is at Fit (zoom 1, no pan). It moves with the picture, so the point stays where it is on screen while the view zooms. Left out, they zoom about the view's middle. */
|
|
37
37
|
anchor?: Point;
|
|
38
|
-
/** Which way Arrow Up tips the view: `"drag"` (the default, as a drag up) or `"wheel"` (as
|
|
38
|
+
/** Which way Arrow Up tips the view: `"drag"` (the default, as a drag up) or `"wheel"` (the other way, as the wheel turned up tipped it before 0.13.0; the wheel zooms now). */
|
|
39
39
|
keyTilt?: KeyTilt;
|
|
40
40
|
/** True to stop the automatic turn when the reader presses the view, uses the wheel on it or presses a key it handles. False (the default) lets it carry on. Buttons and sliders never stop it. */
|
|
41
41
|
stopTurn?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { turnOnlyInSight } from "./sight.js";
|
|
2
|
-
import { between, clamp, clampPan, DEFAULT_LIMITS, dragTurn, PAN_STEP, pinchTo, wheelZoomFactor, wrapYaw, ZOOMED, zoomAbout } from "./math.js";
|
|
2
|
+
import { between, clamp, clampPan, DEFAULT_LIMITS, dragTurn, PAN_STEP, pinchTo, WHEEL_GESTURE_MS, wheelZoomFactor, wrapYaw, ZOOMED, zoomAbout } from "./math.js";
|
|
3
3
|
/** What `bind` leaves alone by default: the page's own controls inside the view. */
|
|
4
4
|
export const DEFAULT_IGNORE = "button, a, input, select, textarea, label, [data-kazoku-ignore]";
|
|
5
5
|
/** How long a move to the middle takes. */
|
|
@@ -390,27 +390,36 @@ export function createViewController(options = {}) {
|
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
};
|
|
393
|
+
// The wheel over the view zooms it about the pointer, as a map does; the page scrolls when the pointer is anywhere else. Ctrl or Command
|
|
394
|
+
// and the wheel (a trackpad's pinch) zooms too. Shift and the wheel, or a sideways swipe, turns it. Tipping is left to a drag, the keys
|
|
395
|
+
// and the buttons. A wheel that starts while the zoom is already at its end that way (zooming out at Fit) goes on to scroll the page,
|
|
396
|
+
// so the page is never trapped; one that reaches the end part-way through stops there rather than flinging the page.
|
|
397
|
+
let wheelTaken = Number.NEGATIVE_INFINITY;
|
|
393
398
|
const onWheel = (e) => {
|
|
394
|
-
|
|
395
|
-
|
|
399
|
+
const pinch = e.ctrlKey || e.metaKey;
|
|
400
|
+
const now = Number.isFinite(e.timeStamp) ? e.timeStamp : 0;
|
|
401
|
+
const continuing = now - wheelTaken < WHEEL_GESTURE_MS && now >= wheelTaken;
|
|
402
|
+
const horizontal = Math.abs(e.deltaX) > Math.abs(e.deltaY);
|
|
403
|
+
if (!pinch && (horizontal || e.shiftKey)) {
|
|
404
|
+
if (!canTurn())
|
|
405
|
+
return;
|
|
396
406
|
e.preventDefault();
|
|
407
|
+
wheelTaken = now;
|
|
397
408
|
grab();
|
|
398
|
-
controller.
|
|
409
|
+
controller.set({ yaw: state.yaw + (e.deltaX || e.deltaY) * 0.3 }, true, "wheel");
|
|
399
410
|
return;
|
|
400
411
|
}
|
|
401
|
-
const
|
|
402
|
-
const
|
|
403
|
-
//
|
|
404
|
-
if (!
|
|
412
|
+
const factor = wheelZoomFactor(e.deltaY, e.deltaMode, pinch ? "pinch" : "wheel");
|
|
413
|
+
const atEnd = factor >= 1 ? state.zoom >= limits.zoomMax : state.zoom <= limits.zoomMin;
|
|
414
|
+
// Ctrl and the wheel is always the view's, or the browser would zoom the whole page.
|
|
415
|
+
if (!pinch && atEnd && !continuing)
|
|
405
416
|
return;
|
|
406
417
|
e.preventDefault();
|
|
407
|
-
|
|
418
|
+
wheelTaken = now;
|
|
419
|
+
if (atEnd)
|
|
408
420
|
return;
|
|
409
421
|
grab();
|
|
410
|
-
|
|
411
|
-
controller.set({ yaw: state.yaw + (e.deltaX || e.deltaY) * 0.3 }, true, "wheel");
|
|
412
|
-
else
|
|
413
|
-
controller.set({ tilt: Math.round(state.tilt + e.deltaY * 0.15) }, true, "wheel");
|
|
422
|
+
controller.zoomAt(state.zoom * factor, { x: e.clientX, y: e.clientY });
|
|
414
423
|
};
|
|
415
424
|
const onKey = (e) => {
|
|
416
425
|
if (e.ctrlKey || e.metaKey || e.altKey)
|
|
@@ -12,7 +12,7 @@ export interface ViewChangeDetail extends ViewState {
|
|
|
12
12
|
* Attributes: `for` (the id of the view to bind), `lang` (en or ja; else the page's), `yaw`, `tilt` (the starting view), `zoom` (`fit`, the default, or a scale where 1
|
|
13
13
|
* is the scene's natural size), `fit-scale` (the page's scale at Fit: what zoom 1 means), `ignore` (a selector for what inside the view a pointer press leaves alone;
|
|
14
14
|
* buttons, links and form controls by default), `autoplay` (turn by itself from the start, unless the reader prefers reduced motion), `autoplay-for` (stop that turn by itself after this many seconds of turning), `speed` (degrees a second),
|
|
15
|
-
* `anchor` (`x,y`: the point the zoom buttons and keys zoom about, in pixels from the view's middle at Fit), `key-tilt` (`drag`, the default, or `wheel`: which way Arrow Up tips),
|
|
15
|
+
* `anchor` (`x,y`: the point the zoom buttons and keys zoom about, in pixels from the view's middle at Fit), `key-tilt` (`drag`, the default, or `wheel`: which way Arrow Up tips; the wheel itself zooms),
|
|
16
16
|
* `controls` (`basic`, the default, or `advanced`: which controls are shown, the zoom readout in both, and the speed in both while the view turns; the reader's own choice with the More button is remembered in localStorage, under `kazoku-view-controls` or the key `storage` names, and wins),
|
|
17
17
|
* `layout` (`classic`, the default, `horizontal`, `vertical` or `compact`: where the controls sit; the horizontal, vertical and compact layouts have the zoom slider),
|
|
18
18
|
* `stop-turn` (a press, the wheel or a key on the view stops the automatic turn), `tilt-step` (round a drag's tilt to a multiple of this many degrees), `object-size` (`w,h`: the object's size at Fit in pixels when it is smaller than the view, so a pan keeps a quarter of it in view), `grab-radius` (exact grab: the on-screen radius of the piece in pixels, or `fit`; a drag then keeps the touched point under the pointer), `flat` (the view cannot turn), `tilt-max`, `zoom-min`, `zoom-max`, `address` (keep the choices in the page's address), `address-keys` (`yaw:a,tilt:b`), `theme` (light
|
|
@@ -66,7 +66,7 @@ const HTML = `
|
|
|
66
66
|
* Attributes: `for` (the id of the view to bind), `lang` (en or ja; else the page's), `yaw`, `tilt` (the starting view), `zoom` (`fit`, the default, or a scale where 1
|
|
67
67
|
* is the scene's natural size), `fit-scale` (the page's scale at Fit: what zoom 1 means), `ignore` (a selector for what inside the view a pointer press leaves alone;
|
|
68
68
|
* buttons, links and form controls by default), `autoplay` (turn by itself from the start, unless the reader prefers reduced motion), `autoplay-for` (stop that turn by itself after this many seconds of turning), `speed` (degrees a second),
|
|
69
|
-
* `anchor` (`x,y`: the point the zoom buttons and keys zoom about, in pixels from the view's middle at Fit), `key-tilt` (`drag`, the default, or `wheel`: which way Arrow Up tips),
|
|
69
|
+
* `anchor` (`x,y`: the point the zoom buttons and keys zoom about, in pixels from the view's middle at Fit), `key-tilt` (`drag`, the default, or `wheel`: which way Arrow Up tips; the wheel itself zooms),
|
|
70
70
|
* `controls` (`basic`, the default, or `advanced`: which controls are shown, the zoom readout in both, and the speed in both while the view turns; the reader's own choice with the More button is remembered in localStorage, under `kazoku-view-controls` or the key `storage` names, and wins),
|
|
71
71
|
* `layout` (`classic`, the default, `horizontal`, `vertical` or `compact`: where the controls sit; the horizontal, vertical and compact layouts have the zoom slider),
|
|
72
72
|
* `stop-turn` (a press, the wheel or a key on the view stops the automatic turn), `tilt-step` (round a drag's tilt to a multiple of this many degrees), `object-size` (`w,h`: the object's size at Fit in pixels when it is smaller than the view, so a pan keeps a quarter of it in view), `grab-radius` (exact grab: the on-screen radius of the piece in pixels, or `fit`; a drag then keeps the touched point under the pointer), `flat` (the view cannot turn), `tilt-max`, `zoom-min`, `zoom-max`, `address` (keep the choices in the page's address), `address-keys` (`yaw:a,tilt:b`), `theme` (light
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { DEFAULT_KEYS, formatAddress, parseAddress, parseKeys, type AddressChoices, type AddressKeys, type DragMode } from "./address.ts";
|
|
2
2
|
export { createViewController, DEFAULT_IGNORE, type ChangeInfo, type Clock, type ViewController, type ViewControllerOptions, type ViewState } from "./controller.ts";
|
|
3
|
-
export { between, clamp, clampPan, DEFAULT_LIMITS, degreesPerPixel, dragTurn, PAN_STEP, pinchTo, TILT_ACROSS, TURN_ACROSS, wheelZoomFactor, wrapYaw, ZOOM_STEP, ZOOMED, zoomAbout, type Camera, type Limits, type PinchStart, type Point, type Size } from "./math.ts";
|
|
3
|
+
export { between, clamp, clampPan, DEFAULT_LIMITS, degreesPerPixel, dragTurn, PAN_STEP, pinchTo, TILT_ACROSS, TURN_ACROSS, WHEEL_GESTURE_MS, wheelZoomFactor, wrapYaw, ZOOM_STEP, ZOOMED, zoomAbout, type Camera, type Limits, type PinchStart, type Point, type Size } from "./math.ts";
|
|
4
4
|
export { languageOf, WORDS, wordsFor, type Language, type ViewWords } from "./words.ts";
|
|
5
5
|
export declare const EVENT = "kazoku-viewchange";
|
|
6
6
|
export declare const TAG = "kazoku-view-controls";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The view controls without the element: the controller, the arithmetic, the address and the labels. Nothing here touches the DOM until `bind` is called, so it imports on a server.
|
|
2
2
|
export { DEFAULT_KEYS, formatAddress, parseAddress, parseKeys } from "./address.js";
|
|
3
3
|
export { createViewController, DEFAULT_IGNORE } from "./controller.js";
|
|
4
|
-
export { between, clamp, clampPan, DEFAULT_LIMITS, degreesPerPixel, dragTurn, PAN_STEP, pinchTo, TILT_ACROSS, TURN_ACROSS, wheelZoomFactor, wrapYaw, ZOOM_STEP, ZOOMED, zoomAbout } from "./math.js";
|
|
4
|
+
export { between, clamp, clampPan, DEFAULT_LIMITS, degreesPerPixel, dragTurn, PAN_STEP, pinchTo, TILT_ACROSS, TURN_ACROSS, WHEEL_GESTURE_MS, wheelZoomFactor, wrapYaw, ZOOM_STEP, ZOOMED, zoomAbout } from "./math.js";
|
|
5
5
|
export { languageOf, WORDS, wordsFor } from "./words.js";
|
|
6
6
|
export const EVENT = "kazoku-viewchange";
|
|
7
7
|
export const TAG = "kazoku-view-controls";
|
|
@@ -56,5 +56,11 @@ export declare const degreesPerPixel: (radius: number) => number;
|
|
|
56
56
|
export declare function dragTurn(start: Camera, dx: number, dy: number, size: Size, tips: boolean, limits?: Limits, step?: number, grabRadius?: number): Pick<Camera, "yaw" | "tilt">;
|
|
57
57
|
/** The camera `fraction` of the way (0 to 1) from `from` to `to`, eased out: quick at first, slow at the end. */
|
|
58
58
|
export declare function between(from: Camera, to: Camera, fraction: number): Camera;
|
|
59
|
-
/**
|
|
60
|
-
|
|
59
|
+
/**
|
|
60
|
+
* The factor a wheel turn zooms by: up (a negative `deltaY`) zooms in. `deltaMode` 1 is lines and 2 is pages. `kind` `"pinch"` (the default) is a
|
|
61
|
+
* trackpad's pinch, which arrives as Ctrl and the wheel in small steps; `"wheel"` is a plain wheel or two-finger scroll, whose steps are larger
|
|
62
|
+
* (about 100 pixels a notch), so one notch zooms about 1.2 times, close to the + key, and one such event never zooms more than 2 times either way.
|
|
63
|
+
*/
|
|
64
|
+
export declare const wheelZoomFactor: (deltaY: number, deltaMode: number, kind?: "pinch" | "wheel") => number;
|
|
65
|
+
/** Wheel steps closer together than this, in milliseconds, belong to one turn of the wheel or one swipe (with its glide). */
|
|
66
|
+
export declare const WHEEL_GESTURE_MS = 300;
|
|
@@ -60,5 +60,14 @@ export function between(from, to, fraction) {
|
|
|
60
60
|
const mix = (a, b) => a + (b - a) * t;
|
|
61
61
|
return { yaw: mix(from.yaw, to.yaw), tilt: mix(from.tilt, to.tilt), zoom: mix(from.zoom, to.zoom), panX: mix(from.panX, to.panX), panY: mix(from.panY, to.panY) };
|
|
62
62
|
}
|
|
63
|
-
/**
|
|
64
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The factor a wheel turn zooms by: up (a negative `deltaY`) zooms in. `deltaMode` 1 is lines and 2 is pages. `kind` `"pinch"` (the default) is a
|
|
65
|
+
* trackpad's pinch, which arrives as Ctrl and the wheel in small steps; `"wheel"` is a plain wheel or two-finger scroll, whose steps are larger
|
|
66
|
+
* (about 100 pixels a notch), so one notch zooms about 1.2 times, close to the + key, and one such event never zooms more than 2 times either way.
|
|
67
|
+
*/
|
|
68
|
+
export const wheelZoomFactor = (deltaY, deltaMode, kind = "pinch") => {
|
|
69
|
+
const pixels = deltaMode === 1 ? deltaY * (kind === "pinch" ? 5 : 33) : deltaMode === 2 ? deltaY * 800 : deltaY;
|
|
70
|
+
return kind === "pinch" ? Math.exp(-pixels * 0.01) : clamp(Math.exp(-pixels * 0.002), 0.5, 2);
|
|
71
|
+
};
|
|
72
|
+
/** Wheel steps closer together than this, in milliseconds, belong to one turn of the wheel or one swipe (with its glide). */
|
|
73
|
+
export const WHEEL_GESTURE_MS = 300;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johnmorrisdotca/kazoku-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Kazoku UI 家族, the family's shared interface parts as their own versioned package: view controls for any 3D or zoomable view (angle, tilt, auto-turn, zoom about the pointer, pan, touch, address state) a light, dark and auto theme switch, a language picker, choices kept in the address and a save-as-image button, each a framework-free custom element and a plain controller. Zero dependencies; the shared 3D renderer takes three.js as an optional peer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"view-controls",
|
|
@@ -163,7 +163,10 @@
|
|
|
163
163
|
"site": "pnpm build && node scripts/site.mjs",
|
|
164
164
|
"test:demo": "pnpm site && playwright test",
|
|
165
165
|
"test:freeze": "pnpm site && playwright test e2e/renderer.demo.mjs --project=chromium-desk",
|
|
166
|
-
"screenshots": "pnpm site && node scripts/screenshots.mjs"
|
|
166
|
+
"screenshots": "pnpm site && node scripts/screenshots.mjs",
|
|
167
|
+
"docs:api": "pnpm build && node scripts/docs.mjs api",
|
|
168
|
+
"docs:check": "pnpm build && node scripts/docs.mjs check",
|
|
169
|
+
"test:readme": "pnpm build && node scripts/check-readme-examples.mjs"
|
|
167
170
|
},
|
|
168
171
|
"devDependencies": {
|
|
169
172
|
"@eslint/js": "^9.0.0",
|