@johnmorrisdotca/kazoku-ui 0.13.1 → 0.15.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 +25 -0
- package/README.md +46 -8
- package/dist/embed-panel/choices.d.ts +42 -0
- package/dist/embed-panel/choices.js +48 -0
- package/dist/embed-panel/define.d.ts +1 -0
- package/dist/embed-panel/define.js +9 -0
- package/dist/embed-panel/element.d.ts +84 -0
- package/dist/embed-panel/element.js +683 -0
- package/dist/embed-panel/index.d.ts +5 -0
- package/dist/embed-panel/index.js +6 -0
- package/dist/embed-panel/snippets.d.ts +94 -0
- package/dist/embed-panel/snippets.js +73 -0
- package/dist/embed-panel/styles.d.ts +1 -0
- package/dist/embed-panel/styles.js +43 -0
- package/dist/embed-panel/words.d.ts +43 -0
- package/dist/embed-panel/words.js +69 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/speed-buttons/define.d.ts +1 -0
- package/dist/speed-buttons/define.js +9 -0
- package/dist/speed-buttons/element.d.ts +34 -0
- package/dist/speed-buttons/element.js +178 -0
- package/dist/speed-buttons/index.d.ts +4 -0
- package/dist/speed-buttons/index.js +5 -0
- package/dist/speed-buttons/steps.d.ts +18 -0
- package/dist/speed-buttons/steps.js +57 -0
- package/dist/speed-buttons/styles.d.ts +1 -0
- package/dist/speed-buttons/styles.js +9 -0
- package/dist/speed-buttons/words.d.ts +9 -0
- package/dist/speed-buttons/words.js +7 -0
- package/dist/view-controls/address.d.ts +3 -3
- package/dist/view-controls/address.js +12 -9
- package/dist/view-controls/element.d.ts +2 -1
- package/dist/view-controls/element.js +55 -26
- package/dist/view-controls/levels.d.ts +1 -1
- package/dist/view-controls/styles.d.ts +1 -1
- package/dist/view-controls/styles.js +6 -6
- package/package.json +43 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.15.0] - 2026-11-03
|
|
6
|
+
|
|
7
|
+
One "Put it in your page" panel for every package of the family.
|
|
8
|
+
|
|
9
|
+
- **`<kazoku-embed-panel>`** (KAZOKU-UI-40 and KAZOKU-UI-30; John, 2026-11-03: "does every package that has embedded have all these options?", "if we do it better elsewhere then we should also use that as a template"). A package fills it with its own choices and its own code; the panel is the same everywhere: a **compact picker** for the thing being embedded (chips by group, a search box from twelve things up, buttons that scroll in their own box; or the package's own in the `picker` slot), a **size** (160, 240, 320, 480 by default), **a tab for each way a page is made** (HTML, ES module, React, Vue, Angular, Svelte, iframe, and any a package adds, such as an image address), **the picture the code makes**, built from the same choices and scaled to fit its box at every size on every screen (wakusei-planets' Venus was cut off on the right), **Copy** with its name on it and a polite "Copied", **Open the embed page**, **Read the docs** and **the credit line**. Slots keep what a package had: `options` (its own choices, between the size and the tabs), `actions` (Save SVG, Download), `picker` and `lede`; `note` and `credits` are a text or a function of the choices. `address` keeps the tab and the size in the link (`?embed=react&size=240`). `lang` (English and Japanese), `theme`, light and dark, 48 px targets, arrow keys, Home and End on the tabs. A package that draws nothing says `no-preview no-size` and still has the tabs, Copy, the docs link and the credit. New part: `@johnmorrisdotca/kazoku-ui/embed-panel`, `/element` and `/define`; the event is `kazoku-embedchange` with `{ kind, size, value, code, reason }`, and `kazoku-embedcopy` follows Copy.
|
|
10
|
+
- **`embedSnippet(kind, spec)`** writes the seven ways from one description of the element (`SnippetSpec`: package, CDN address, tag, entry points, attributes, a name, the iframe), taken from wakusei-planets' generators, byte for byte; with `EMBED_KINDS`, `attrText`, `parseKinds`, `parseSizes`, `nearestSize`, `filterItems`, `groupsOf`, `kindLabel`, the labels in both languages and the defaults. The module imports on a server.
|
|
11
|
+
- **The must-have list has a new item, `embed-panel`**: the demo's "Put it in your page" is kazoku-ui's panel (`templates/check-standard.mjs`, `STANDARD_VERSION` 2026-11-03; `docs/package-standard.md`). A package that cannot say why in `family.notApplicable`. A repo takes the new check when it is next touched; its rollout is a ticket on the modules board.
|
|
12
|
+
- New demo pages `embed-panel.html` (a made-up badge with every part, then a package that draws nothing) and `embed.html` (the badge's embed page), `e2e/embed-panel.demo.mjs` (Chromium and WebKit, desk and phone, light and dark), `test/embed-panel.test.ts`.
|
|
13
|
+
- Fixed: the demo server (`scripts/dev.mjs`) crashed on a file that does not exist after answering 200; it reads first.
|
|
14
|
+
|
|
15
|
+
### Documentation
|
|
16
|
+
|
|
17
|
+
- A guide, `embed-panel.md`, with its Japanese page and a picture; [the survey](docs/embed-panel-survey.md) of every package's panel and which does each part best; the README, getting started, architecture, labels and upgrading pages; the API reference lists the new part.
|
|
18
|
+
|
|
19
|
+
## [0.14.0] - 2026-11-03
|
|
20
|
+
|
|
21
|
+
One speed control for the whole family: the Auto-turn speeds are 0.5×, 1× and 2×, and the same buttons serve a solar system's days a second and a replay bar.
|
|
22
|
+
|
|
23
|
+
- **`<kazoku-speed-buttons>`** (KAZOKU-UI-46, John 2026-11-03: "the same controls throughout all apps"). One button for each speed, no menu: `steps="0.5 1 2"` by default, any numbers (`1 10 100` for days a second, `0.25 0.5 1 2 4` for a replay, `0 1 3600 86400 -3600` for a clock that can stop and run back), `unit` (`×`, or none), `label` (the group's name, shown and read), `no-label`, `address` and `address-key` (the choice kept in the address, the start left off), `lang` and `theme`. The chosen speed is the `value`, an attribute that follows the reader's choice, and the property; `steps` and `step(1)` (the next speed, round at the ends) are properties too; the page hears `kazoku-speedchange` with `{ value, steps, reason }`. It is the Auto-turn control in the view controls and a part of its own: `@johnmorrisdotca/kazoku-ui/speed-buttons`, `/element` and `/define`, with the pure `parseSteps`, `speedText`, `nearestStep`, `startStep`, `stepFrom`, `parseSpeed` and `SPEED_ADDRESS_KEY`. Reduced motion changes nothing in it. New demo page `speed.html`, a guide in English and Japanese, `e2e/speed-buttons.demo.mjs`, `test/speed-buttons.test.ts`.
|
|
24
|
+
- **The view controls' speeds are 0.5×, 1× and 2×**, not Slow, Medium and Fast. The new `pace` attribute is the speed as a multiple of `speed` (1 by default) and follows the buttons; `paces` names other buttons (`paces="1 4 8"`). The address key is now `pace` (`?pace=2`), no longer `speed`, which is a page's own (a clock's, a replay's) and is never read or written by the controls; `?pace=slow` and `?pace=fast` of 0.12 and 0.13 still read as 0.5 and 2. `DEFAULT_KEYS.speed` is `"pace"`, `AddressChoices.pace` a number. `PACES`, `PACE_FACTOR`, `parsePace` and the type `Pace` remain, for the three words. The small speed button of the basic level while the view turns shows `1×` and steps through the speeds.
|
|
25
|
+
|
|
26
|
+
### Documentation
|
|
27
|
+
|
|
28
|
+
- A guide, `speed-buttons.md`, with its Japanese page; the view controls guide, README, getting started, architecture, labels and upgrading pages say `pace`, `paces` and the numbers; the API reference lists the new part; a picture of the buttons three ways (`docs/images/speed-buttons-*.webp`).
|
|
29
|
+
|
|
5
30
|
## [0.13.1] - 2026-11-03
|
|
6
31
|
|
|
7
32
|
The layouts hold at a phone's width and in any narrow panel.
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ The family's shared interface parts, as their own versioned package: one part pe
|
|
|
11
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,
|
|
12
12
|
SVG or CSS 3D applies it.
|
|
13
13
|
|
|
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)
|
|
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) · [Speed buttons](docs/guides/speed-buttons.md) · [Embed panel](docs/guides/embed-panel.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
15
|
|
|
16
16
|
## In 30 seconds
|
|
17
17
|
|
|
@@ -57,6 +57,8 @@ console.log(zoomText(2.44)); // 2.4×
|
|
|
57
57
|
- **View controls** in four layouts, with a zoom slider, a readout, Auto-turn, Centre, keys, touch and the address.
|
|
58
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
59
|
- **Save image** for a canvas, an SVG or any element, as PNG or SVG.
|
|
60
|
+
- **Speed buttons**: one row of 0.5×, 1×, 2× (or the steps you name: a solar system's days a second, a replay's speeds) for the whole family, kept in an attribute and the address.
|
|
61
|
+
- **Embed panel**: the family's one "Put it in your page" panel: a compact picker, a size, a tab for HTML, ES module, React, Vue, Angular, Svelte and iframe (and your own, such as an image), a live picture that always fits its box, Copy, the embed page, the docs and the credit line.
|
|
60
62
|
- **Address state**, with no element: choices kept in the link with `replaceState`.
|
|
61
63
|
- **A shared 3D renderer** and its **freeze checks**: one WebGL context, shared assets, stills, a pixel budget.
|
|
62
64
|
- **Labels in English and Japanese**, light and dark, 48 px touch targets, reduced motion respected.
|
|
@@ -156,15 +158,21 @@ Each its own subpath; later ones: picture-tile pickers, Help, GitHub and npm pil
|
|
|
156
158
|
| `@johnmorrisdotca/kazoku-ui/save-image` | The formats, the file name, the size and the functions that render and download a canvas, an SVG or an element as PNG or SVG. Imports on a server. |
|
|
157
159
|
| `@johnmorrisdotca/kazoku-ui/save-image/element` | The `SaveImageElement` class (needs a browser). |
|
|
158
160
|
| `@johnmorrisdotca/kazoku-ui/save-image/define` | Registers `<kazoku-save-image>` (once, and only if the tag is free). |
|
|
161
|
+
| `@johnmorrisdotca/kazoku-ui/speed-buttons` | The steps, how a speed reads, the nearest step and the address key of the speed buttons. Imports on a server. |
|
|
162
|
+
| `@johnmorrisdotca/kazoku-ui/speed-buttons/element` | The `SpeedButtonsElement` class (needs a browser). |
|
|
163
|
+
| `@johnmorrisdotca/kazoku-ui/speed-buttons/define` | Registers `<kazoku-speed-buttons>` (once, and only if the tag is free). |
|
|
164
|
+
| `@johnmorrisdotca/kazoku-ui/embed-panel` | The seven ways a page is made and their text (`embedSnippet`), the sizes, the picker's search and the labels of the embed panel. Imports on a server. |
|
|
165
|
+
| `@johnmorrisdotca/kazoku-ui/embed-panel/element` | The `EmbedPanelElement` class (needs a browser). |
|
|
166
|
+
| `@johnmorrisdotca/kazoku-ui/embed-panel/define` | Registers `<kazoku-embed-panel>` (once, and only if the tag is free). |
|
|
159
167
|
| `@johnmorrisdotca/kazoku-ui/renderer` | The shared 3D renderer: one WebGL context for the focused view, shared assets, stills, drawing on demand, a pixel budget. three.js is an optional peer, loaded by your `import("three")`. Imports on a server. |
|
|
160
168
|
| `@johnmorrisdotca/kazoku-ui/testing` | The freeze checks for a 3D package's e2e: count contexts, GPU objects, draws and frames; the canvas size against the budget. No test framework named. |
|
|
161
169
|
| `@johnmorrisdotca/kazoku-ui/address-state` | Choices kept in the address: read, write and subscribe to URL parameters with `replaceState`. No element; imports on a server. |
|
|
162
170
|
|
|
163
171
|
## View controls
|
|
164
172
|
|
|
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 `
|
|
173
|
+
- **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 `1×`, and a press steps to the next speed). More (or `controls="advanced"`) adds the tilt slider, the speed buttons (0.5×, 1×, 2×), 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.
|
|
166
174
|
- 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.
|
|
167
|
-
- Auto-turn with play and pause; its speed is
|
|
175
|
+
- Auto-turn with play and pause; its speed is chosen with the family's [speed buttons](docs/guides/speed-buttons.md): 0.5× (half the `speed` attribute), 1× (the `speed` attribute, 30 degrees a second) or 2× (twice it), kept in the `pace` attribute and in the address as `?pace=0.5` or `?pace=2`.
|
|
168
176
|
- **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.
|
|
169
177
|
- Touch: one finger turns at 1x and moves the view when zoomed in; two fingers pinch.
|
|
170
178
|
- Keys on the view: arrows turn and tip it, `+` and `-` zoom, `0` or Home is Front and Fit.
|
|
@@ -186,7 +194,7 @@ The `layout` attribute says where the controls sit; `controls` (`basic` or `adva
|
|
|
186
194
|
|
|
187
195
|
| `layout` | What it looks like | Basic shows | More adds |
|
|
188
196
|
|---|---|---|---|
|
|
189
|
-
| `classic` (the default) | The bar of 0.10 and before, unchanged: one wrapping row at a desk, two rows on a phone. No zoom slider. | Angle, Auto-turn, − readout +, Fit, More | Tilt,
|
|
197
|
+
| `classic` (the default) | The bar of 0.10 and before, unchanged: one wrapping row at a desk, two rows on a phone. No zoom slider. | Angle, Auto-turn, − readout +, Fit, More | Tilt, 0.5×, 1×, 2×, Front; the pan pad once zoomed in |
|
|
190
198
|
| `horizontal` | A tidy bar under the view: the turn on one row (Angle, Auto-turn, More), the **zoom slider** on the next (Zoom, − readout +, Fit). One row when the bar is 1040 px or wider. Under 560 px the labels are read by a screen reader only, and the readout is Fit; under 440 px the zoom slider takes a row of its own, with the buttons under it, so its marks never meet them. Centre, once the view is zoomed or moved, comes in at the end of the zoom row (an icon in the two rows, so Auto-turn and More never move). | Angle, Auto-turn, the zoom slider, − readout +, Fit, More | Tilt and Front, then the speeds, between the two rows; the pan pad last |
|
|
191
199
|
| `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 |
|
|
192
200
|
| `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 |
|
|
@@ -355,13 +363,15 @@ An `init` event is sent once, a moment after the element connects, with the star
|
|
|
355
363
|
| `grab-radius` | Exact grab, for a globe or any turnable piece: its on-screen radius in CSS pixels at Fit, or `fit` for half the smaller side of the view. A drag then turns it by the angle that keeps the touched point under the pointer (a drag of *d* pixels moves the ground at the middle by *d* pixels), and the radius grows with the zoom. Left out, a drag turns by a fixed angle across the view, as before. See [docs/drag-rule.md](docs/drag-rule.md). |
|
|
356
364
|
| `autoplay` | Turn by itself from the start, unless the reader prefers reduced motion. |
|
|
357
365
|
| `autoplay-for` | Seconds of turning after which the `autoplay` turn stops by itself (as Auto-turn pressed). Left out, it never stops. Time spent paused off screen or in a hidden tab is not counted, and a reader's own Auto-turn press has no limit. |
|
|
358
|
-
| `speed` | Degrees a second at
|
|
366
|
+
| `speed` | Degrees a second at a pace of 1 (30). |
|
|
367
|
+
| `pace` | The Auto-turn speed as a multiple of `speed`: 1 by default, 0.5 and 2 on the default buttons. The reader's choice is written here, and kept in the address as `pace`. |
|
|
368
|
+
| `paces` | The speeds the buttons stand for, `0.5 1 2` by default (any numbers: `paces="1 4 8"`). |
|
|
359
369
|
| `controls` | `basic` (the default) or `advanced`: which controls are shown (see above; the zoom readout in both, the speed in both while the view turns). Written in the page's markup it is where the reader starts; what the reader chose with More is remembered under `kazoku-view-controls` in `localStorage` (or the key `storage` names) and wins. Setting the attribute later changes the level at once. The host carries `data-level` with the level now. |
|
|
360
370
|
| `layout` | `classic` (the default), `horizontal`, `vertical` or `compact`: where the controls sit (see [Layouts](#layouts)). Can change at any time. Anything else is `classic`. The host carries `data-layout` with the layout in use. |
|
|
361
371
|
| `storage` | The `localStorage` key the level is remembered under (`kazoku-view-controls`). |
|
|
362
372
|
| `flat` | The view cannot turn: no angle, tilt, Auto-turn or Front, and a drag moves it. |
|
|
363
373
|
| `tilt-max`, `zoom-min`, `zoom-max` | The limits (30, 1, 5). |
|
|
364
|
-
| `address` | Keep the choices in the page's address (`?yaw=40&tilt=10&zoom=2&drag=turn&
|
|
374
|
+
| `address` | Keep the choices in the page's address (`?yaw=40&tilt=10&zoom=2&drag=turn&pace=2`). |
|
|
365
375
|
| `address-keys` | Other keys: `yaw:a,zoom:z`. |
|
|
366
376
|
| `theme` | `light` or `dark`; otherwise the page's colour scheme. |
|
|
367
377
|
| `overlay` | Sit over the bottom of a positioned parent. With `layout="compact"` the row floats 8 px in from the bottom start corner instead of spanning the bottom. |
|
|
@@ -371,7 +381,7 @@ Colours are custom properties: `--kazoku-ink`, `--kazoku-surface`, `--kazoku-lin
|
|
|
371
381
|
|
|
372
382
|
### The address
|
|
373
383
|
|
|
374
|
-
Only what a reader chose is kept: angle, tilt, zoom, the drag choice and the auto-turn speed (`
|
|
384
|
+
Only what a reader chose is kept: angle, tilt, zoom, the drag choice and the auto-turn speed (`pace=0.5` or `pace=2`; 1 leaves the address; a page's own `speed` parameter is never touched, and `?pace=slow` or `fast` of 0.12 and 0.13 still read as 0.5 and 2). The pan is in pixels of one screen and the turning is of a moment, so neither is; while the view turns by itself the angle is left out. A value at its start (Fit, or the `zoom` the page opens at) leaves the address, and other parameters are kept. The address is written at most four times a second (Safari stops a page that changes its address more than 100 times in 30 seconds). Without the element: `parseAddress(search, keys?)` and `formatAddress(search, choices, keys?, leaveYaw?, startZoom?)`.
|
|
375
385
|
|
|
376
386
|
### Why the tag is only defined when it is free
|
|
377
387
|
|
|
@@ -424,6 +434,33 @@ See [docs/address-state.md](docs/address-state.md); the demo's `address.html` sh
|
|
|
424
434
|
|
|
425
435
|
What a picture of an element cannot carry (web fonts, `::before`, cross-origin images), the element-free `renderImage` and `saveImage`, and the one `img-src data:` a strict policy needs are in [docs/save-image.md](docs/save-image.md); the demo's `save-image.html` saves all three kinds.
|
|
426
436
|
|
|
437
|
+
## Speed buttons
|
|
438
|
+
|
|
439
|
+
`<kazoku-speed-buttons>` is one button for each speed (0.5×, 1×, 2× by default, no menu): the same element for Auto-turn in the view controls, a solar system's days a second (`steps="1 10 100"`) and a replay bar. The chosen speed is the `value` attribute, can be kept in the address, and is announced as `kazoku-speedchange`.
|
|
440
|
+
|
|
441
|
+
```html
|
|
442
|
+
<kazoku-speed-buttons steps="0.5 1 2" value="1" address></kazoku-speed-buttons>
|
|
443
|
+
<script type="module">import "@johnmorrisdotca/kazoku-ui/speed-buttons/define";</script>
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
See [the guide](docs/guides/speed-buttons.md) for the attributes, the event and the functions without the element.
|
|
447
|
+
|
|
448
|
+
## Embed panel
|
|
449
|
+
|
|
450
|
+
`<kazoku-embed-panel>` is the "Put it in your page" panel every package of the family shares: a compact picker for the thing being embedded, a size, a tab for each way a page is made, the picture the code makes (scaled to fit its box), Copy with its name on it, Open the embed page, Read the docs and the credit line. The package gives it `generate` (the code of a tab) and `preview` (the picture); `embedSnippet` writes all seven tabs from one description of the element.
|
|
451
|
+
|
|
452
|
+
```html
|
|
453
|
+
<kazoku-embed-panel id="panel" kinds="html module react vue angular svelte iframe" docs-href="docs/"></kazoku-embed-panel>
|
|
454
|
+
<script type="module">
|
|
455
|
+
import "@johnmorrisdotca/kazoku-ui/embed-panel/define";
|
|
456
|
+
import { embedSnippet } from "@johnmorrisdotca/kazoku-ui/embed-panel";
|
|
457
|
+
document.getElementById("panel").generate = ({ kind, size }) =>
|
|
458
|
+
embedSnippet(kind, { package: "@scope/dial", cdn: "https://cdn.jsdelivr.net/npm/@scope/dial@1.0.0", tag: "scope-dial", element: "element", entries: [], attrs: [["size", String(size)]], name: "dial", iframe: { src: "https://example.com/embed.html", title: "Dial", width: size, height: size } });
|
|
459
|
+
</script>
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
See [the guide](docs/guides/embed-panel.md) for the slots (a package's own choices and buttons), the picker, the events and a package that draws nothing, and [the survey](docs/embed-panel-survey.md) for which package's panel does each part best.
|
|
463
|
+
|
|
427
464
|
## Shared 3D renderer
|
|
428
465
|
|
|
429
466
|
A page with many 3D views gets one WebGL context, handed to the focused view; the others are stills made once from the same renderer, kept in a budgeted list. Shared geometries,
|
|
@@ -515,7 +552,7 @@ One part, one subpath; the pure code, the element and the registration are three
|
|
|
515
552
|
|
|
516
553
|
## Demos
|
|
517
554
|
|
|
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).
|
|
555
|
+
[The demo](https://johnmorrisdotca.github.io/kazoku-ui/) has the view controls, the language picker and the theme switch in its header. Beside it: [speed buttons](https://johnmorrisdotca.github.io/kazoku-ui/speed.html), [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
556
|
|
|
520
557
|
<picture>
|
|
521
558
|
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kazoku-ui/main/docs/images/builder-dark.webp">
|
|
@@ -534,6 +571,7 @@ One part, one subpath; the pure code, the element and the registration are three
|
|
|
534
571
|
| [Language picker](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/language-picker.md) | Each language in its own name |
|
|
535
572
|
| [Address state](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/address-state.md) | Choices kept in the link |
|
|
536
573
|
| [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 |
|
|
574
|
+
| [Speed buttons](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/speed-buttons.md) | 0.5×, 1×, 2× or your own steps, kept in an attribute and the address |
|
|
537
575
|
| [The shared renderer](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/renderer.md) | One WebGL context, and the freeze checks |
|
|
538
576
|
| [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
577
|
| [Accessibility and labels](https://github.com/johnmorrisdotca/kazoku-ui/blob/main/docs/guides/accessibility-and-labels.md) | The rules and the words |
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** The sizes (pixels across) the size buttons offer by default. */
|
|
2
|
+
export declare const DEFAULT_SIZES: readonly number[];
|
|
3
|
+
/** The size the panel starts on when the page names none and it is one of the sizes. */
|
|
4
|
+
export declare const DEFAULT_SIZE = 320;
|
|
5
|
+
/** The tabs the panel shows when the page names none: the seven ways a page is made. */
|
|
6
|
+
export declare const DEFAULT_KINDS: readonly string[];
|
|
7
|
+
/** The address parameters the panel keeps its two choices in. */
|
|
8
|
+
export declare const KIND_ADDRESS_KEY = "embed";
|
|
9
|
+
export declare const SIZE_ADDRESS_KEY = "size";
|
|
10
|
+
/** More than this many things to pick from and the picker shows a search box. */
|
|
11
|
+
export declare const SEARCH_FROM = 12;
|
|
12
|
+
/**
|
|
13
|
+
* One thing the compact picker offers: a planet, a jersey, a stadium.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { type PickItem } from "@johnmorrisdotca/kazoku-ui/embed-panel";
|
|
18
|
+
*
|
|
19
|
+
* const mars: PickItem = { id: "mars", label: "Mars", group: "Planets" };
|
|
20
|
+
* console.log(mars.label);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export interface PickItem {
|
|
24
|
+
/** What the page reads back from `value`. */
|
|
25
|
+
id: string;
|
|
26
|
+
/** What the button says. */
|
|
27
|
+
label: string;
|
|
28
|
+
/** The row of choices it is under (chips above the list); items with no group are only under All. */
|
|
29
|
+
group?: string;
|
|
30
|
+
/** More words the search matches (another name, a country). */
|
|
31
|
+
words?: string;
|
|
32
|
+
}
|
|
33
|
+
/** The tab ids from a text such as `html module react`: lower case, once each, in order; no usable id is the default list. */
|
|
34
|
+
export declare function parseKinds(text: string | null | undefined, fallback?: readonly string[]): string[];
|
|
35
|
+
/** The sizes from a text such as `160 240 320`: whole numbers from 16 up to 4096, once each, smallest first; no usable number is the default sizes. */
|
|
36
|
+
export declare function parseSizes(text: string | null | undefined, fallback?: readonly number[]): number[];
|
|
37
|
+
/** The size to use for a wish: the wish if it is one of the sizes, else the sizes' nearest to it, else the one nearest `DEFAULT_SIZE`. */
|
|
38
|
+
export declare function nearestSize(wish: number | string | null | undefined, sizes: readonly number[]): number;
|
|
39
|
+
/** The groups of a list, once each, in the order they first appear. */
|
|
40
|
+
export declare function groupsOf(items: readonly PickItem[]): string[];
|
|
41
|
+
/** The items that are in `group` (all of them when it is empty) and have every word of `query` in their label, id, group or words, ignoring case and accents. */
|
|
42
|
+
export declare function filterItems(items: readonly PickItem[], query?: string, group?: string): PickItem[];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// What the panel's choices are, without the element: the tabs, the sizes and the compact picker's list. Nothing here touches the DOM, so it imports on a server.
|
|
2
|
+
import { EMBED_KINDS } from "./snippets.js";
|
|
3
|
+
/** The sizes (pixels across) the size buttons offer by default. */
|
|
4
|
+
export const DEFAULT_SIZES = [160, 240, 320, 480];
|
|
5
|
+
/** The size the panel starts on when the page names none and it is one of the sizes. */
|
|
6
|
+
export const DEFAULT_SIZE = 320;
|
|
7
|
+
/** The tabs the panel shows when the page names none: the seven ways a page is made. */
|
|
8
|
+
export const DEFAULT_KINDS = EMBED_KINDS;
|
|
9
|
+
/** The address parameters the panel keeps its two choices in. */
|
|
10
|
+
export const KIND_ADDRESS_KEY = "embed";
|
|
11
|
+
export const SIZE_ADDRESS_KEY = "size";
|
|
12
|
+
/** More than this many things to pick from and the picker shows a search box. */
|
|
13
|
+
export const SEARCH_FROM = 12;
|
|
14
|
+
/** The tab ids from a text such as `html module react`: lower case, once each, in order; no usable id is the default list. */
|
|
15
|
+
export function parseKinds(text, fallback = DEFAULT_KINDS) {
|
|
16
|
+
const found = [...new Set((text ?? "").toLowerCase().split(/[\s,]+/).filter((id) => /^[a-z][a-z0-9-]*$/.test(id)))];
|
|
17
|
+
return found.length > 0 ? found : [...fallback];
|
|
18
|
+
}
|
|
19
|
+
/** The sizes from a text such as `160 240 320`: whole numbers from 16 up to 4096, once each, smallest first; no usable number is the default sizes. */
|
|
20
|
+
export function parseSizes(text, fallback = DEFAULT_SIZES) {
|
|
21
|
+
const found = [...new Set((text ?? "").split(/[\s,]+/).map(Number).filter((n) => Number.isInteger(n) && n >= 16 && n <= 4096))].sort((a, b) => a - b);
|
|
22
|
+
return found.length > 0 ? found : [...fallback];
|
|
23
|
+
}
|
|
24
|
+
/** The size to use for a wish: the wish if it is one of the sizes, else the sizes' nearest to it, else the one nearest `DEFAULT_SIZE`. */
|
|
25
|
+
export function nearestSize(wish, sizes) {
|
|
26
|
+
const want = Number(wish);
|
|
27
|
+
const aim = Number.isFinite(want) && wish !== null && wish !== undefined && String(wish).trim() !== "" ? want : DEFAULT_SIZE;
|
|
28
|
+
let best = sizes[0] ?? DEFAULT_SIZE;
|
|
29
|
+
for (const size of sizes)
|
|
30
|
+
if (Math.abs(size - aim) < Math.abs(best - aim))
|
|
31
|
+
best = size;
|
|
32
|
+
return best;
|
|
33
|
+
}
|
|
34
|
+
/** The groups of a list, once each, in the order they first appear. */
|
|
35
|
+
export function groupsOf(items) {
|
|
36
|
+
return [...new Set(items.flatMap((item) => (item.group !== undefined && item.group !== "" ? [item.group] : [])))];
|
|
37
|
+
}
|
|
38
|
+
/** The items that are in `group` (all of them when it is empty) and have every word of `query` in their label, id, group or words, ignoring case and accents. */
|
|
39
|
+
export function filterItems(items, query = "", group = "") {
|
|
40
|
+
const fold = (text) => text.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
|
|
41
|
+
const needles = fold(query).split(/\s+/).filter((word) => word !== "");
|
|
42
|
+
return items.filter((item) => {
|
|
43
|
+
if (group !== "" && item.group !== group)
|
|
44
|
+
return false;
|
|
45
|
+
const hay = fold(`${item.label} ${item.id} ${item.group ?? ""} ${item.words ?? ""}`);
|
|
46
|
+
return needles.every((word) => hay.includes(word));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function defineEmbedPanel(registry?: CustomElementRegistry): boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Importing this registers <kazoku-embed-panel>, unless the tag is already taken (the first definition wins; see view-controls/define.ts for why a taken tag is left alone).
|
|
2
|
+
import { EmbedPanelElement, TAG } from "./element.js";
|
|
3
|
+
export function defineEmbedPanel(registry = customElements) {
|
|
4
|
+
if (registry.get(TAG) !== undefined)
|
|
5
|
+
return false;
|
|
6
|
+
registry.define(TAG, EmbedPanelElement);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
defineEmbedPanel();
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type PickItem } from "./choices.ts";
|
|
2
|
+
import { EVENT, TAG } from "./index.ts";
|
|
3
|
+
import { type PanelWords } from "./words.ts";
|
|
4
|
+
export { EVENT, TAG };
|
|
5
|
+
/** What the page's `generate`, `preview`, `credits` and `note` functions are told. */
|
|
6
|
+
export interface EmbedContext {
|
|
7
|
+
/** The tab: `html`, `module`, `react`, `vue`, `angular`, `svelte`, `iframe`, or one the page added. */
|
|
8
|
+
kind: string;
|
|
9
|
+
/** The size chosen, in pixels across. */
|
|
10
|
+
size: number;
|
|
11
|
+
/** True when the text is for the picture beside the code, not for the reader to copy: a page may use a relative address there so the picture works from where the page is. */
|
|
12
|
+
preview: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** What the `kazoku-embedchange` event carries in `detail`. */
|
|
15
|
+
export interface EmbedChangeDetail {
|
|
16
|
+
kind: string;
|
|
17
|
+
size: number;
|
|
18
|
+
/** The picker's choice (an item's `id`), or an empty text where there is no built-in picker. */
|
|
19
|
+
value: string;
|
|
20
|
+
/** The text to copy now. */
|
|
21
|
+
code: string;
|
|
22
|
+
/** `init` (once, when it comes up), `kind` (a tab), `size` (a size button), `pick` (the picker) or `set` (a property or attribute). */
|
|
23
|
+
reason: "init" | "kind" | "size" | "pick" | "set";
|
|
24
|
+
}
|
|
25
|
+
type Text = string | ((context: EmbedContext) => string);
|
|
26
|
+
/**
|
|
27
|
+
* Attributes: `kinds` (the tabs, `html module react vue angular svelte iframe` by default; add your own such as `image`), `kind` (the tab to start on), `sizes` (`160 240 320 480`) and
|
|
28
|
+
* `size` (the start, 320), `no-size` (no size choice), `value` (the picker's choice), `open-href` (the embed page), `docs-href` (the docs), `aspect` (the picture's height over its width, 1),
|
|
29
|
+
* `no-preview` (no picture), `heading` (show the shared heading), `search` / `no-search` (force the picker's search box on or off), `pick-label` (the picker's name), `address` (keep the tab and
|
|
30
|
+
* the size in the address under `kind-key`, `embed`, and `size-key`, `size`; the starts are left off it), `lang` (`en` or `ja`), `theme` (`light` or `dark`).
|
|
31
|
+
* Slots: `lede` (a line under the heading), `picker` (your own picker in place of the built-in one), `options` (your own choices between the size and the tabs), `actions` (more buttons
|
|
32
|
+
* beside Copy: Save SVG, Download). Parts: `panel`, `title`, `picker`, `size`, `tabs`, `tab`, `preview`, `note`, `code`, `copy`, `open`, `docs`, `credits`.
|
|
33
|
+
* Custom properties: `--kazoku-preview-bg` (behind the picture, the page's surface by default), `--kazoku-preview-width` (the picture's column, 260px).
|
|
34
|
+
*/
|
|
35
|
+
export declare class EmbedPanelElement extends HTMLElement {
|
|
36
|
+
#private;
|
|
37
|
+
static observedAttributes: string[];
|
|
38
|
+
constructor();
|
|
39
|
+
/** The tab now: one of `kinds`. Setting one that is not there is ignored. */
|
|
40
|
+
get kind(): string;
|
|
41
|
+
set kind(kind: string);
|
|
42
|
+
/** The size now, in pixels across: one of `sizes`. Setting a number that is not one takes the nearest. */
|
|
43
|
+
get size(): number;
|
|
44
|
+
set size(size: number | string);
|
|
45
|
+
/** The tabs, in order. Setting a list (or a text such as `html react iframe`) draws them again. */
|
|
46
|
+
get kinds(): readonly string[];
|
|
47
|
+
set kinds(list: readonly string[] | string);
|
|
48
|
+
/** The sizes the buttons offer, smallest first. */
|
|
49
|
+
get sizes(): readonly number[];
|
|
50
|
+
set sizes(list: readonly number[] | string);
|
|
51
|
+
/** The picker's choice: an item's `id`. Setting one that is not in `items` is ignored. */
|
|
52
|
+
get value(): string;
|
|
53
|
+
set value(id: string);
|
|
54
|
+
/** The things the built-in picker offers. Leave it empty and give the `picker` slot for a picker of your own. */
|
|
55
|
+
get items(): readonly PickItem[];
|
|
56
|
+
set items(items: readonly PickItem[]);
|
|
57
|
+
/** How a picker button looks inside: a node (a small picture) or a text. The label by default. */
|
|
58
|
+
set renderItem(render: ((item: PickItem) => Node | string) | undefined);
|
|
59
|
+
/** The text of a tab for the choices as they are. Required: without it the code box is empty. Call `update()` after the page's own choices change. */
|
|
60
|
+
set generate(generate: ((context: EmbedContext) => string) | undefined);
|
|
61
|
+
/** The picture the code makes, as a node (or nothing). It is called when the picture's choices change; the iframe tab draws the iframe itself when this returns nothing for it. */
|
|
62
|
+
set preview(preview: ((context: EmbedContext) => Node | undefined) | undefined);
|
|
63
|
+
/** The credit line shown under the code (and read by a screen reader): a text, or a function of the choices. */
|
|
64
|
+
set credits(credits: Text);
|
|
65
|
+
/** A line above the code for the tab shown ("The embed page is for a site that cannot run script."): a text, or a function of the choices. */
|
|
66
|
+
set note(note: Text);
|
|
67
|
+
/** The names of the tabs the family has no word for: `{ image: "Image address" }`. */
|
|
68
|
+
set labels(labels: Record<string, string>);
|
|
69
|
+
/** Your own words over the language's. */
|
|
70
|
+
set words(words: Partial<PanelWords>);
|
|
71
|
+
/** Where "Open the embed page" goes. Set it again when the choices change the address. */
|
|
72
|
+
get openHref(): string;
|
|
73
|
+
set openHref(href: string);
|
|
74
|
+
/** Where "Read the docs" goes. */
|
|
75
|
+
get docsHref(): string;
|
|
76
|
+
set docsHref(href: string);
|
|
77
|
+
/** The text of the tab shown: what Copy copies. */
|
|
78
|
+
get code(): string;
|
|
79
|
+
/** Reads the page's choices again: the code, the credit, the note, the links, and the picture if its choices changed. Call it whenever the page's own choices change. It tells no one. */
|
|
80
|
+
update(): void;
|
|
81
|
+
connectedCallback(): void;
|
|
82
|
+
disconnectedCallback(): void;
|
|
83
|
+
attributeChangedCallback(name: string, _old: string | null, value: string | null): void;
|
|
84
|
+
}
|