@harmonics/lattice-ui 0.1.0 → 0.5.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 CHANGED
@@ -3,6 +3,51 @@
3
3
  `@harmonics/lattice-ui` follows semantic versioning; while it is `0.x`, a minor version may change
4
4
  an element's inputs, events or tokens, and says so here.
5
5
 
6
+ ## 0.5.0
7
+
8
+ - `<shears-pad>` always ends a stroke. `strokeend` carries `cancelled`: true when the stroke was cut
9
+ short (the pointer cancelled or its capture lost, `disabled` set mid-stroke, the pad taken off
10
+ the page), where before no `strokeend` came at all. `pointercancel` is now `cancelled: true`.
11
+ `stroke` carries `point`, the newest point.
12
+ - **Types:** `stroke` and `strokeend` are typed on `ShearsPadElement` (`ShearsPadEventMap`, and
13
+ `StrokeEndDetail`) instead of on every element's `HTMLElementEventMap`. A listener added on a
14
+ plain `HTMLElement` reference now needs the pad's type, or a cast.
15
+ - `<lattice-face>` idles on animation frames and stops while off screen or in a hidden tab; it
16
+ follows `prefers-reduced-motion` as it changes, not only as it was when first drawn.
17
+ - An element moved on the page keeps its drawing and state; it lets go only once it stays off.
18
+ - `defineX(name)` registers an element under a tag of your own. A tag another element holds is
19
+ left to it with a console warning instead of in silence.
20
+ - `<lattice-shape>` warns once when `parts` is a string that is not JSON (the usual cause: React
21
+ set it before the elements were defined). `parts` read twice is the same array.
22
+ - `@harmonics/lattice-ui/hexes` exports `crossesCell`, `crossesLine` and `crossesRing`.
23
+ - `<hex-sky orient="flat">`: the lattice turned a quarter turn clockwise about its origin, for pages
24
+ whose pieces have flat-topped cells. `x`, `y` and `scale` mean what they did; the default,
25
+ `pointy`, draws exactly what 0.4.0 did.
26
+
27
+ ## 0.4.0
28
+
29
+ - The magnet in `@harmonics/lattice-ui/hexes`: `magnetOf(piece, dx, dy, held)` says where to draw a
30
+ carried piece and which slot it would land in. Near a slot it leans in, nearer the slot catches
31
+ and holds it, and it stays held until tugged past a release distance (`MAGNET` has the four
32
+ distances). `snapFrames(from)` is the click into the slot: Web Animations keyframes that pass it
33
+ once and settle; `stepOnScreen(step)` is a step's move on the screen. Additive: nothing in 0.3.0 changes.
34
+
35
+ ## 0.3.0
36
+
37
+ - Glue in `@harmonics/lattice-ui/hexes`: `glued` (a piece joined to every piece it shares a side
38
+ with, each part keeping its tone), `sharedSides` (the sides that would glue), `seamsOf` (where a
39
+ piece is glued), `apart` (back into its units), `pulledOff` (one part off, the rest as one piece
40
+ or several); with `turned` and `pivotPoint` (a sixth of a turn about the piece's pivot) and
41
+ `unitAt` (a new cell or triangle under a point). Additive: nothing in 0.2.0 changes.
42
+
43
+ ## 0.2.0
44
+
45
+ - `@harmonics/lattice-ui/hexes`: the lattice's geometry, for pages that place their own pieces on
46
+ the sky. Cells, corners and triangles (`boxOf`, `cornerOf`, `cornersOf`, `hexAt`, `cornerAt`,
47
+ `trisOf`), pieces (`pieceOf`, `covered`, `outlineOf`, `touching`), moving and snapping (`moved`,
48
+ `snapDelta`, `stepFor`) and finding room (`fits`, `nearestFit`, `screenArea`). Pure functions, no
49
+ DOM. Additive: nothing in 0.1.0 changes.
50
+
6
51
  ## 0.1.0
7
52
 
8
53
  - `<lattice-face>`: the tree's face, by mood or by expression, idling, with a laugh of relief on
package/README.md CHANGED
@@ -25,7 +25,15 @@ npm install @harmonics/lattice-ui
25
25
 
26
26
  `@harmonics/lattice-ui` itself defines nothing when imported: call `defineLatticeElements()` (or
27
27
  one element's `defineLatticeFace()`, …) in a browser. Both are safe to import in a server render;
28
- there they do nothing. Every input is both an attribute and a property.
28
+ there they do nothing. Every input is both an attribute and a property; a property set does not
29
+ change the attribute, so style by class rather than by `[mood=…]`. A value an input does not know
30
+ falls back to its default.
31
+
32
+ A tag another library already holds is left to it, with a warning. Give the element a tag of your
33
+ own instead: `defineHexSky("my-sky")`, and the same for every `defineX`.
34
+
35
+ An element moved on the page (a list reordered) keeps what it was doing; one taken off the page
36
+ lets go a moment later.
29
37
 
30
38
  ## Elements
31
39
 
@@ -33,7 +41,8 @@ there they do nothing. Every input is both an attribute and a property.
33
41
 
34
42
  A pale hexagon plate raised off the page with a face on it, in 1,536 of the lattice's triangles.
35
43
  It idles (blinks, bobs), laughs with relief when `pulse` goes up, and sinks the old face as a new
36
- one comes. Under `prefers-reduced-motion`, or `still`, it holds still and only changes.
44
+ one comes. Under `prefers-reduced-motion` (followed as it changes), or `still`, it holds still and
45
+ only changes. Off screen, or in a hidden tab, it does not idle.
37
46
 
38
47
  | Input | Type | Default | |
39
48
  |---|---|---|---|
@@ -83,6 +92,7 @@ Decorative.
83
92
  | Input | Type | Default | |
84
93
  |---|---|---|---|
85
94
  | `kind` | `sky` `earth` | `sky` | Earth: cells broken into triangles, on the earth's colour |
95
+ | `orient` | `pointy` `flat` | `pointy` | Flat: the lattice turned a quarter turn clockwise about its origin, so its cells have flat tops and bottoms |
86
96
  | `scale` | number | `1` | The lattice's size |
87
97
  | `x`, `y` | number (pixels) | `0` | Where the lattice's origin sits from the element's top-left, so a sky showing part of a larger one keeps its cells in place |
88
98
 
@@ -93,16 +103,20 @@ cuts is yours to decide. Put it over what can be cut and size it as any block.
93
103
 
94
104
  | Event | `detail` | |
95
105
  |---|---|---|
96
- | `stroke` | `{ points, fresh }` | At every move; `fresh` on a stroke's first point |
97
- | `strokeend` | `{ points }` | When the pointer lifts |
106
+ | `stroke` | `{ points, point, fresh }` | At every move; `point` is the newest, `fresh` on a stroke's first |
107
+ | `strokeend` | `{ points, cancelled }` | Once per stroke: lifted, or `cancelled` when cut short (the pointer cancelled or lost, `disabled` set, the pad taken off the page) |
98
108
 
99
109
  Points are in the element's own pixels from its top-left. `disabled` stops it taking strokes. A
100
110
  cut by pointer has no keyboard of its own: give the same action a button.
101
111
 
102
112
  ```js
103
113
  pad.addEventListener("stroke", (e) => cutAlong(e.detail.points));
114
+ pad.addEventListener("strokeend", (e) => (e.detail.cancelled ? undo() : commit()));
104
115
  ```
105
116
 
117
+ `ShearsPadEventMap` types these on the pad's own `addEventListener`. To find what a stroke cut,
118
+ see [Cutting](#cutting).
119
+
106
120
  ### `<lattice-icon>`
107
121
 
108
122
  `name="shears"` or `name="done"`, in the text's colour, a step square
@@ -145,7 +159,9 @@ them: `lattice-icon::part(done-cell)`, `::part(done-check)`, `::part(shears-blad
145
159
 
146
160
  - **React 19**: use the tags directly; add `import type {} from "@harmonics/lattice-ui/react"`
147
161
  for their props. React 19 sets inputs as properties, so `parts={[…]}` works, and listens with
148
- `onstroke` / `onstrokeend`.
162
+ `onstroke` / `onstrokeend`. Define the elements before React first renders them: until then
163
+ React cannot see the property and sets `parts` as the attribute `"[object Object]"`, which draws
164
+ nothing (the shape warns once in the console when that happens).
149
165
  - **Solid**: `import type {} from "@harmonics/lattice-ui/solid"`; set objects with `prop:parts`;
150
166
  listen with `on:stroke`.
151
167
  - **Vue**: tell the compiler the tags are custom elements
@@ -155,6 +171,99 @@ them: `lattice-icon::part(done-cell)`, `::part(done-check)`, `::part(shears-blad
155
171
 
156
172
  TypeScript knows the tags from `HTMLElementTagNameMap` once the package is imported.
157
173
 
174
+ ## Geometry
175
+
176
+ `@harmonics/lattice-ui/hexes` is the lattice's own geometry, for a page that places pieces on the
177
+ sky itself (a board, a puzzle, an editor). Pure functions, no DOM, safe in a server render.
178
+
179
+ ```js
180
+ import { pieceOf, moved, snapDelta, fits, nearestFit, screenArea } from "@harmonics/lattice-ui/hexes";
181
+
182
+ const piece = pieceOf("a", [{ kind: "cell", at: { q: 2, r: 1 }, tone: 1 }]);
183
+ const carried = moved(piece, snapDelta(piece, dx, dy)); // follow a pointer, snapped to whole cells
184
+ const placed = fits(carried, others, screenArea(width, height))
185
+ ? carried
186
+ : nearestFit(carried, others, screenArea(width, height));
187
+ ```
188
+
189
+ The sky is pointy-top hexagons 44 on a side: a cell is 76.21 across, rows are 66 apart, and each
190
+ row starts half a cell further right. A cell is `{ q, r }` (axial); its box's top-left is
191
+ `boxOf(hex)`. Split into triangles, the sky is also a grid of corners `{ i, j }` (`cornerOf`), each
192
+ the top-left of two triangles `{ i, j, k }`: `k` 0 points right, 1 left. A piece is
193
+ `{ id, parts, pivot }`; make one with `pieceOf`. A piece with a cell moves a whole cell at a time.
194
+
195
+ To lay a `<lattice-shape>` exactly on a `<hex-sky>` of the same `scale` (`s`), give the shape the
196
+ piece's parts and place it at `s × (minX − 2, minY − lift − 2)`, the top-left of every corner it
197
+ covers (`covered`, `cornersOf`, `cornerOf`), less the shape's padding and lift.
198
+
199
+ ### Glue
200
+
201
+ Pieces that share a side (not only a corner) can be glued into one; each part keeps its `tone`, so
202
+ a glued piece still shows its woods, and it moves and turns as one. The functions that split a
203
+ piece take `id`, your own generator of new piece ids.
204
+
205
+ ```js
206
+ import { apart, glued, pulledOff, seamsOf, sharedSides, turned, unitAt } from "@harmonics/lattice-ui/hexes";
207
+
208
+ const sides = others.flatMap((o) => sharedSides(piece, o)); // where G would glue: draw them lit
209
+ const { piece: one, absorbed } = glued(piece, others); // G: drop the absorbed ids from your list
210
+ const seams = seamsOf(one); // the sides between its parts, to draw as hairlines
211
+ const units = apart(one, newId); // Shift+G: every part a piece of its own
212
+ const [part, ...rest] = pulledOff(one, index, newId); // one part off; the rest stay joined
213
+ const turnedOnce = turned(one, 1); // a sixth clockwise about its pivot (pivotPoint(one) on screen)
214
+ const fresh = unitAt("tri", x, y, tone, newId()); // a new cell or triangle under a point
215
+ ```
216
+
217
+ Sides and seams come as pairs of screen points, in the sky's own pixels. Glued pieces are drawn
218
+ with `<lattice-shape>` like any other: give it the piece's parts.
219
+
220
+ ### Magnet
221
+
222
+ A carried piece is pulled into the sky's lattice. Near a slot it leans in. Nearer, the slot
223
+ catches it and it is drawn there exactly. It stays held until it is tugged past a release
224
+ distance, which is further out than the catch, so it never flickers at the edge. `MAGNET` holds
225
+ the four distances, as parts of the piece's unit: `reach`, `capture`, `release` and `lean`.
226
+
227
+ ```js
228
+ import { magnetOf, moved, snapFrames, stepOnScreen } from "@harmonics/lattice-ui/hexes";
229
+
230
+ let held; // the slot it is held in, handed back on every move
231
+ onPointerMove((dx, dy) => {
232
+ const m = magnetOf(piece, dx, dy, held);
233
+ held = m.held;
234
+ draw(piece, m.x, m.y); // where it is drawn, as a move from where it was lifted
235
+ glow(m.step, m.pull); // the slot it would land in, and how hard it pulls (0 to 1)
236
+ });
237
+ onPointerUp((dx, dy) => {
238
+ const m = magnetOf(piece, dx, dy, held);
239
+ const slot = stepOnScreen(m.step); // the slot's move on the screen
240
+ piece = moved(piece, m.step);
241
+ // The click: from where it was let go, past the slot once, to rest in it.
242
+ element.animate(snapFrames({ x: m.x - slot.x, y: m.y - slot.y }), { duration: 260 });
243
+ });
244
+ ```
245
+
246
+ Under reduced motion, draw the piece in its slot and leave out the animation.
247
+
248
+ ### Cutting
249
+
250
+ What a `<shears-pad>` stroke went through. A stroke goes through an outline when it goes in at one
251
+ side and out at another; one that only grazes a corner, or stops inside, does not.
252
+
253
+ ```js
254
+ import { crossesCell, crossesLine, crossesRing } from "@harmonics/lattice-ui/hexes";
255
+
256
+ pad.addEventListener("stroke", (e) => {
257
+ const stroke = e.detail.points; // in the sky's pixels when the pad covers the sky at scale 1
258
+ const isCut = (part) => part.kind === "cell" && crossesCell(stroke, part.at);
259
+ const cut = pieces.filter((p) => p.parts.some(isCut));
260
+ const severed = crossesLine(stroke, branch); // through a line of points
261
+ const through = crossesRing(stroke, outline); // through any closed outline
262
+ });
263
+ ```
264
+
265
+ `crossesCell(stroke, hex, lift)` takes the cell as drawn `lift` above its place.
266
+
158
267
  ## Server rendering
159
268
 
160
269
  Importing either entry on a server does nothing and throws nothing. Render the tags as markup;
package/dist/define.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as e } from "./elements-DMimFemm.js";
1
+ import { t as e } from "./elements-Czlt91Ar.js";
2
2
  //#region src/elements/define.ts
3
3
  e();
4
4
  //#endregion