@a4ui/core 0.35.1 → 0.35.2
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/dist/index.d.ts +1 -1
- package/dist/index.js +1527 -1519
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/ui/InteractiveMap.tsx +63 -10
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// import '@a4ui/core/styles.css'
|
|
9
9
|
// import { Button, Card, Modal } from '@a4ui/core'
|
|
10
10
|
|
|
11
|
-
export const A4UI_VERSION = '0.35.
|
|
11
|
+
export const A4UI_VERSION = '0.35.2'
|
|
12
12
|
|
|
13
13
|
// Helpers (src/lib) — generic, framework-level utilities.
|
|
14
14
|
export { cn } from './lib/cn'
|
|
@@ -51,10 +51,21 @@ interface WorldPixel {
|
|
|
51
51
|
y: number
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* A tile's IDENTITY, with no position in it.
|
|
56
|
+
*
|
|
57
|
+
* `id` and `src` are both pure functions of z/x/y, so a given tile's
|
|
58
|
+
* descriptor never changes while it is on screen — only where it sits does.
|
|
59
|
+
* Keeping position out of this type is what lets the memo below hand `<For>`
|
|
60
|
+
* the same object across a pan, so the `<img>` survives instead of being torn
|
|
61
|
+
* down and rebuilt.
|
|
62
|
+
*/
|
|
54
63
|
interface TileDescriptor {
|
|
55
64
|
id: string
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
/** Tile column, unwrapped — multiplied by TILE_SIZE to place the tile. */
|
|
66
|
+
x: number
|
|
67
|
+
/** Tile row. */
|
|
68
|
+
y: number
|
|
58
69
|
src: string
|
|
59
70
|
}
|
|
60
71
|
|
|
@@ -174,9 +185,33 @@ export function InteractiveMap(props: InteractiveMapProps): JSX.Element {
|
|
|
174
185
|
return { x: c.x - w / 2, y: c.y - h / 2 }
|
|
175
186
|
})
|
|
176
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Live tiles, keyed by id so identity survives a pan.
|
|
190
|
+
*
|
|
191
|
+
* `<For>` in Solid keys by REFERENCE. This memo used to build fresh
|
|
192
|
+
* descriptor objects on every recompute, so every pointer move made every
|
|
193
|
+
* tile a "new" item: the whole grid was removed from the DOM and rebuilt,
|
|
194
|
+
* even though the same tiles were still on screen. Measured on a 20-tile
|
|
195
|
+
* map, one ~500 ms drag produced 340 element insertions and 344 removals.
|
|
196
|
+
*
|
|
197
|
+
* The fade-in below turned that churn into a visible strobe. A rebuilt
|
|
198
|
+
* `<img>` starts at `opacity-0` and only becomes visible when its `load`
|
|
199
|
+
* event re-adds `a4ui-tile-loaded`, so during a pan every tile blinked out
|
|
200
|
+
* and back roughly seventeen times a second — the file header has always
|
|
201
|
+
* claimed tiles are "repositioned (not reloaded) as the user pans", and now
|
|
202
|
+
* they are.
|
|
203
|
+
*
|
|
204
|
+
* Reusing entries also means the cache holds only what is on screen: ids
|
|
205
|
+
* that fall out of range are dropped on the same pass that adds new ones.
|
|
206
|
+
*/
|
|
207
|
+
const tileCache = new Map<string, TileDescriptor>()
|
|
208
|
+
|
|
177
209
|
const tiles = createMemo<TileDescriptor[]>(() => {
|
|
178
210
|
const { w, h } = size()
|
|
179
|
-
if (w <= 0 || h <= 0)
|
|
211
|
+
if (w <= 0 || h <= 0) {
|
|
212
|
+
tileCache.clear()
|
|
213
|
+
return []
|
|
214
|
+
}
|
|
180
215
|
const z = zoom()
|
|
181
216
|
const numTiles = 2 ** z
|
|
182
217
|
const tl = topLeftPx()
|
|
@@ -188,17 +223,29 @@ export function InteractiveMap(props: InteractiveMapProps): JSX.Element {
|
|
|
188
223
|
const maxY = Math.min(numTiles - 1, Math.floor((tl.y + h) / TILE_SIZE) + 1)
|
|
189
224
|
|
|
190
225
|
const list: TileDescriptor[] = []
|
|
226
|
+
const live = new Set<string>()
|
|
227
|
+
|
|
191
228
|
for (let x = minX; x <= maxX; x++) {
|
|
192
229
|
const wrappedX = ((x % numTiles) + numTiles) % numTiles
|
|
193
230
|
for (let y = minY; y <= maxY; y++) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
231
|
+
const id = `${z}/${x}/${y}`
|
|
232
|
+
live.add(id)
|
|
233
|
+
let tile = tileCache.get(id)
|
|
234
|
+
// The url prop can change at runtime, so a cached tile is only
|
|
235
|
+
// reusable while its src still matches.
|
|
236
|
+
const src = buildTileUrl(url, z, wrappedX, y)
|
|
237
|
+
if (!tile || tile.src !== src) {
|
|
238
|
+
tile = { id, x, y, src }
|
|
239
|
+
tileCache.set(id, tile)
|
|
240
|
+
}
|
|
241
|
+
list.push(tile)
|
|
200
242
|
}
|
|
201
243
|
}
|
|
244
|
+
|
|
245
|
+
for (const id of tileCache.keys()) {
|
|
246
|
+
if (!live.has(id)) tileCache.delete(id)
|
|
247
|
+
}
|
|
248
|
+
|
|
202
249
|
return list
|
|
203
250
|
})
|
|
204
251
|
|
|
@@ -309,7 +356,13 @@ export function InteractiveMap(props: InteractiveMapProps): JSX.Element {
|
|
|
309
356
|
!motionReduced() &&
|
|
310
357
|
'opacity-0 transition-opacity duration-150 [&.a4ui-tile-loaded]:opacity-100',
|
|
311
358
|
)}
|
|
312
|
-
|
|
359
|
+
// Read from the reactive origin rather than from the tile, so
|
|
360
|
+
// panning updates two style properties on an element that stays
|
|
361
|
+
// put instead of replacing it.
|
|
362
|
+
style={{
|
|
363
|
+
left: `${tile.x * TILE_SIZE - topLeftPx().x}px`,
|
|
364
|
+
top: `${tile.y * TILE_SIZE - topLeftPx().y}px`,
|
|
365
|
+
}}
|
|
313
366
|
onLoad={(event) => event.currentTarget.classList.add('a4ui-tile-loaded')}
|
|
314
367
|
/>
|
|
315
368
|
)}
|