All files / refresh / mod.ts

100.00% Branches 17/17
100.00% Lines 49/49
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
 
x4
 
 
 
x4
x4
x4
x4
x17
x4
x4
x266
x1002
x334
x798
x4
x4
x73
 
 
x73
x78
x78
x78
x78
 
 
x137
x73
x76
x76
x76
x302
x73
x81
x81
x81
x4
x4
 
x266
x267
x267
x267
x267
x267
 
 
x266
x515
x515
x278
x284
x2272
x284
x278
x266
x4
 
 
x4



























































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

/** `*refresh` directive. */
export const _refresh = {
  name: "*refresh",
  phase: Phase.POSTPROCESSING,
  init(renderer) {
    renderer.cache<Cache<typeof _refresh>>(this.name, new WeakMap())
  },
  setup(_, __, { state }) {
    if (!("$refresh" in state)) {
      Object.assign(state, { $refresh: false })
    }
    return { state }
  },
  async execute(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(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)
  },
} as Directive<WeakMap<HTMLElement | Comment, { id: number; interval: number }>>

/** Default exports. */
export default _refresh