All files / refresh / mod.ts

100.00% Branches 17/17
100.00% Lines 50/50
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
 
x4
 
 
 
x4
 
x4
x4
x4
x4
x17
x4
x4
x250
x942
x314
x750
x4
x4
x69
 
 
x69
x74
x74
x74
x74
 
 
x129
x69
x72
x72
x72
x286
x69
x77
x77
x77
x4
x4
 
x250
x251
x251
x251
x251
x251
 
 
x250
x483
x483
x262
x268
x2144
x268
x262
x250
x4
 
 
x4





























































// Imports
import { type Cache, type Directive, Phase } from "@mizu/internal/engine"
export type * from "@mizu/internal/engine"

/** `*refresh` directive. */
export const _refresh: Directive<{
  Cache: WeakMap<HTMLElement | Comment, { id: number; interval: number }>
}> = {
  name: "*refresh",
  phase: Phase.POSTPROCESSING,
  init(this: typeof _refresh, renderer) {
    renderer.cache<Cache<typeof this>>(this.name, new WeakMap())
  },
  setup(this: typeof _refresh, _, __, { state }) {
    if (!("$refresh" in state)) {
      Object.assign(state, { $refresh: false })
    }
    return { state }
  },
  async execute(this: typeof _refresh, renderer, element, { attributes: [attribute], cache, ...options }) {
    const value = await renderer.evaluate(element, attribute.value, options) as string

    // Clear interval if value is null
    if (value === null) {
      clearTimeout(cache.get(element)?.id)
      cache.delete(element)
      return
    }

    // Setup interval configuration for later use
    const interval = Number.parseInt(`${1000 * Number(value)}`)
    if ((Number.isNaN(interval)) || (interval <= 0)) {
      renderer.warn(`[${this.name}] expects a finite positive number but got ${value}, ignoring`, element)
      return
    }
    const cached = cache.get(element) ?? cache.set(element, { interval, id: NaN }).get(element)!
    if (((cached.interval !== interval) && (!Number.isNaN(cached.id))) || (options.state[renderer.internal("refreshing")])) {
      clearTimeout(cached.id)
      cached.id = NaN
    }
  },
  cleanup(this: typeof _refresh, renderer, element, { cache, ...options }) {
    // Cleanup interval from commented out elements
    if ((renderer.isComment(element)) && (cache.has(renderer.cache("*").get(element)!))) {
      element = renderer.cache("*").get(element)!
      clearTimeout(cache.get(element)?.id)
      cache.delete(element)
      return
    }

    // Setup interval if needed
    if (!Number.isNaN(cache.get(element)?.id)) {
      return
    }
    cache.get(element)!.id = setTimeout(() => {
      if (element.isConnected) {
        renderer.render(element as HTMLElement, { ...options, state: { ...options.state, $refresh: true, [renderer.internal("refreshing")]: true } })
      }
    }, cache.get(element)!.interval)
  },
}

/** Default exports. */
export default _refresh