@abide/abide 0.115.0 → 0.116.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/src/ui/Router.ts CHANGED
@@ -5,6 +5,7 @@ import { firstMatch, type Params, partsOf } from '#shared/matchRoute.ts'
5
5
  import { unmounted } from '#shared/mount.ts'
6
6
  import { NAVIGATION_HEADER } from '#shared/NAVIGATION_HEADER.ts'
7
7
  import { OFFLINE_SAVED } from '#shared/OFFLINE_CACHES.ts'
8
+ import { REACHED_MESSAGE } from '#shared/REACHED_MESSAGE.ts'
8
9
  import type { ShareScope } from '#shared/reactive/share.ts'
9
10
  import { arrive, route } from '#shared/route.ts'
10
11
  import { rankRoutes, type Segment } from '#shared/routePattern.ts'
@@ -400,14 +401,18 @@ export class Router {
400
401
  */
401
402
  private writeAddress(target: URL, replace: boolean, left: Scroll): void {
402
403
  const full = `${target.pathname}${target.search}${target.hash}`
403
- if (replace) {
404
- history.replaceState(entryAt(this.index, null), '', full)
405
- return
404
+ // A step to a fragment, or to the address standing, reaches no page the worker lacks.
405
+ const moved =
406
+ target.pathname !== location.pathname ||
407
+ target.search !== location.search
408
+ if (replace) history.replaceState(entryAt(this.index, null), '', full)
409
+ else {
410
+ this.remember(left)
411
+ this.index += 1
412
+ this.scrolls.length = this.index
413
+ history.pushState(entryAt(this.index, null), '', full)
406
414
  }
407
- this.remember(left)
408
- this.index += 1
409
- this.scrolls.length = this.index
410
- history.pushState(entryAt(this.index, null), '', full)
415
+ if (moved) reached(target)
411
416
  }
412
417
 
413
418
  /**
@@ -720,6 +725,22 @@ async function savedOffline(target: URL): Promise<boolean> {
720
725
  return saved !== undefined
721
726
  }
722
727
 
728
+ /**
729
+ * Tells the service worker an address a navigation reached, for it to keep (45.24).
730
+ *
731
+ * POSTED AS THE ADDRESS IS WRITTEN, rather than heard from 30.15's ask: a step within the standing
732
+ * route asks nothing, the ask's redirect lands elsewhere than it asked, and a superseded one writes
733
+ * no address.
734
+ */
735
+ function reached(at: URL): void {
736
+ const controller = navigator.serviceWorker?.controller
737
+ if (controller == null) return
738
+ // A page is kept by its address, which a fragment is no part of.
739
+ const address = new URL(at)
740
+ address.hash = ''
741
+ controller.postMessage({ [REACHED_MESSAGE]: address.href })
742
+ }
743
+
723
744
  /**
724
745
  * Asks the server's `middleware` about an address; `undefined` where the fetch failed.
725
746
  *
@@ -0,0 +1,42 @@
1
+ import { boundValue, writeBound } from '#shared/boundValue.ts'
2
+ import type { Scope } from './Scope.ts'
3
+
4
+ /**
5
+ * Keeps a file input's files in step with a `bind:files` value, both ways.
6
+ *
7
+ * 32.73 — A FILE INPUT'S FILES ARE A LIST, with or without `multiple`, and no markup: the server has
8
+ * none to write. A `FileList` cannot be built, so a list written in is handed over through a
9
+ * `DataTransfer`, and an empty one clears the input through `value`, the one write it takes.
10
+ */
11
+ export function bindFiles(
12
+ element: Element,
13
+ operand: unknown,
14
+ scope: Scope,
15
+ ): void {
16
+ const bound = element as HTMLInputElement
17
+ scope.watch(() => {
18
+ const files = (boundValue(operand) ?? []) as readonly File[]
19
+ if (holdsFiles(bound.files, files)) return
20
+ if (files.length === 0) {
21
+ bound.value = ''
22
+ return
23
+ }
24
+ const transfer = new DataTransfer()
25
+ for (const file of files) transfer.items.add(file)
26
+ bound.files = transfer.files
27
+ })
28
+ bound.addEventListener('change', () => {
29
+ const picked: File[] = []
30
+ for (const file of bound.files ?? []) picked.push(file)
31
+ writeBound(operand, picked)
32
+ })
33
+ }
34
+
35
+ /** Whether an input already holds `files`, the same files in the same order. */
36
+ function holdsFiles(held: FileList | null, files: readonly File[]): boolean {
37
+ const count = held?.length ?? 0
38
+ if (count !== files.length) return false
39
+ for (let index = 0; index < count; index++)
40
+ if ((held as FileList)[index] !== files[index]) return false
41
+ return true
42
+ }
package/src/ui/index.ts CHANGED
@@ -40,6 +40,7 @@ export {
40
40
  textHere,
41
41
  } from './adoptWalk.ts'
42
42
  export { bindElement } from './bindElement.ts'
43
+ export { bindFiles } from './bindFiles.ts'
43
44
  export { bindGroup } from './bindGroup.ts'
44
45
  export { bindProperty } from './bindProperty.ts'
45
46
  export { bindSpread } from './bindSpread.ts'