@abide/abide 0.43.0 → 0.43.1
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 +7 -0
- package/package.json +1 -1
- package/src/lib/ui/compile/compileShadow.ts +19 -5
- package/src/lib/ui/router.ts +42 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# abide
|
|
2
2
|
|
|
3
|
+
## 0.43.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 943402b: restore reload scroll flash-free via native restoration ([`876b99c`](https://github.com/briancray/abide/commit/876b99cd6e285b6a41afe87ca053cf0631099c03))
|
|
8
|
+
- ed96096: abide check: quote hyphenated component prop keys (`aria-label`, `data-*`) so the props shim parses, and treat `on*` callbacks as ordinary declared props (not DOM passthrough) so a passed required `onsave`/`oncancel` no longer reads as missing while an undeclared handler is still caught
|
|
9
|
+
|
|
3
10
|
## 0.43.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -372,12 +372,15 @@ function emitNode(node: TemplateNode, builder: Builder): void {
|
|
|
372
372
|
case 'component': {
|
|
373
373
|
/* The imported tag resolves (via the shadow host) to the child's
|
|
374
374
|
`(props: Props) => …` default, so `Parameters<typeof Child>[0]` is its prop
|
|
375
|
-
shape. `
|
|
376
|
-
(not part of the declared shape), so they are checked
|
|
377
|
-
against its own declared key type — never flagged as
|
|
375
|
+
shape. `bind:` / `class:` / `style:` / `attach` and `{...spread}` are
|
|
376
|
+
framework directives (not part of the declared shape), so they are checked
|
|
377
|
+
leniently — each value against its own declared key type — never flagged as
|
|
378
|
+
excess. `on*` callbacks ARE ordinary declared props on a component (abide does
|
|
379
|
+
not auto-forward events to the DOM), so they go through the completeness check
|
|
380
|
+
with the rest — otherwise a required `onsave`/`oncancel` reads as missing even
|
|
381
|
+
when passed, and an undeclared handler slips by unflagged. */
|
|
378
382
|
const handled = (prop: { name: string; spread?: boolean }): boolean =>
|
|
379
383
|
prop.spread === true ||
|
|
380
|
-
prop.name.startsWith('on') ||
|
|
381
384
|
prop.name.startsWith('bind:') ||
|
|
382
385
|
prop.name.startsWith('class:') ||
|
|
383
386
|
prop.name.startsWith('style:') ||
|
|
@@ -427,12 +430,23 @@ function emitNode(node: TemplateNode, builder: Builder): void {
|
|
|
427
430
|
builder.raw('\n')
|
|
428
431
|
for (const prop of dataProps) {
|
|
429
432
|
/* The key mapped to its source name (excess-prop errors land on the key);
|
|
430
|
-
the value verbatim-mapped (wrong-type errors land on the value).
|
|
433
|
+
the value verbatim-mapped (wrong-type errors land on the value).
|
|
434
|
+
Hyphenated names (`aria-label`, `data-*`) aren't valid identifiers, so
|
|
435
|
+
they're wrapped in raw quotes — a string-literal key the parser accepts.
|
|
436
|
+
The quotes are unmapped; an excess-prop error on the literal starts at the
|
|
437
|
+
opening quote and overlaps the mapped name, clamping back onto it. */
|
|
438
|
+
const needsQuoting = !/^[A-Za-z_$][\w$]*$/.test(prop.name)
|
|
439
|
+
if (needsQuoting) {
|
|
440
|
+
builder.raw('"')
|
|
441
|
+
}
|
|
431
442
|
if (prop.nameLoc !== undefined) {
|
|
432
443
|
builder.mapped(prop.name, prop.nameLoc)
|
|
433
444
|
} else {
|
|
434
445
|
builder.raw(prop.name)
|
|
435
446
|
}
|
|
447
|
+
if (needsQuoting) {
|
|
448
|
+
builder.raw('"')
|
|
449
|
+
}
|
|
436
450
|
builder.raw(': ')
|
|
437
451
|
builder.expr(prop.code, prop.loc)
|
|
438
452
|
builder.raw(',\n')
|
package/src/lib/ui/router.ts
CHANGED
|
@@ -253,9 +253,16 @@ export function router(
|
|
|
253
253
|
}
|
|
254
254
|
const onPageHide = (): void => {
|
|
255
255
|
/* Mirror the live scroll into the active entry's state before it unloads, so a
|
|
256
|
-
reload
|
|
257
|
-
|
|
256
|
+
non-hydratable reload (which tears the SSR DOM down) can recover it from the
|
|
257
|
+
manual bucket. */
|
|
258
258
|
historyEntries.persist()
|
|
259
|
+
/* Re-enable native scroll restoration for the NEXT document load (a reload): the
|
|
260
|
+
browser restores the SSR DOM's offset before paint, flash-free — where the
|
|
261
|
+
manual bucket, gated behind the async chunk import, would re-apply post-paint
|
|
262
|
+
(a visible scroll). The first paint flips back to `manual` for in-session nav. */
|
|
263
|
+
if ('scrollRestoration' in history) {
|
|
264
|
+
history.scrollRestoration = 'auto'
|
|
265
|
+
}
|
|
259
266
|
}
|
|
260
267
|
const onClick = (event: MouseEvent): void => {
|
|
261
268
|
/* Let the browser own anything that isn't a plain primary-button click:
|
|
@@ -297,12 +304,18 @@ export function router(
|
|
|
297
304
|
navigatePath(destination.pathname + destination.search + destination.hash)
|
|
298
305
|
}
|
|
299
306
|
if (typeof window !== 'undefined') {
|
|
300
|
-
/*
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
307
|
+
/* Scroll restoration is the browser's job on a document load (reload / first
|
|
308
|
+
paint): native restoration runs BEFORE paint, so a hydrating in-place adopt
|
|
309
|
+
returns to the reload offset flash-free (`auto` here also self-heals an entry a
|
|
310
|
+
prior session left `manual`). The first paint then flips to `manual` (below) so
|
|
311
|
+
an in-session back/forward — which tears the page down and rebuilds — restores
|
|
312
|
+
against the new DOM instead of letting the browser restore against the
|
|
313
|
+
torn-down one; `onPageHide` flips back to `auto` for the next load. Still adopt
|
|
314
|
+
the initial entry's id (survives a reload) and stamp it onto the landing entry —
|
|
315
|
+
merging so any `scroll` a prior unload persisted stays put for a non-hydratable
|
|
316
|
+
first paint's manual `restore` to recover. */
|
|
304
317
|
if ('scrollRestoration' in history) {
|
|
305
|
-
history.scrollRestoration = '
|
|
318
|
+
history.scrollRestoration = 'auto'
|
|
306
319
|
}
|
|
307
320
|
historyEntries.adopt(entryOf())
|
|
308
321
|
const landingState = (history.state as AbideHistoryState | null) ?? {}
|
|
@@ -421,6 +434,7 @@ export function router(
|
|
|
421
434
|
) {
|
|
422
435
|
divergence += 1
|
|
423
436
|
}
|
|
437
|
+
const firstPaint = first
|
|
424
438
|
const hydrating = first && pageView?.hydratable === true
|
|
425
439
|
first = false
|
|
426
440
|
/* Same page, same layout chain — only params/url differ (e.g. stepping
|
|
@@ -483,11 +497,27 @@ export function router(
|
|
|
483
497
|
)
|
|
484
498
|
/* Reapply the destination entry's scroll once its DOM exists — a
|
|
485
499
|
back/forward restores its offset, a fresh nav scrolls to the `#hash`
|
|
486
|
-
anchor (now built) or the top.
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
500
|
+
anchor (now built) or the top. SKIPPED on a hydrating first paint:
|
|
501
|
+
the SSR DOM is adopted in place, so the browser's native restoration
|
|
502
|
+
already returned the entry to its reload offset before paint — the
|
|
503
|
+
manual bucket, gated behind the async chunk import above, would only
|
|
504
|
+
re-apply post-paint (a visible scroll = the flash). A non-hydratable
|
|
505
|
+
first paint tore the SSR DOM down and rebuilt, so it still needs the
|
|
506
|
+
manual restore (recovered from the persisted `history.state`). */
|
|
507
|
+
if (!hydrating) {
|
|
508
|
+
historyEntries.restore(url.hash)
|
|
509
|
+
}
|
|
510
|
+
/* Take over scroll restoration once abide owns the DOM: a later
|
|
511
|
+
same-document back/forward must restore against the rebuilt page, so
|
|
512
|
+
the browser must not. `onPageHide` flips back to `auto` so the next
|
|
513
|
+
document load restores natively (flash-free) again. */
|
|
514
|
+
if (
|
|
515
|
+
firstPaint &&
|
|
516
|
+
typeof history !== 'undefined' &&
|
|
517
|
+
'scrollRestoration' in history
|
|
518
|
+
) {
|
|
519
|
+
history.scrollRestoration = 'manual'
|
|
520
|
+
}
|
|
491
521
|
}
|
|
492
522
|
/* Build / hydrate is the deterministic surface — a codegen defect or a
|
|
493
523
|
throw in user render code fails the SAME way every load, so reloading
|