All files / refresh / mod.ts

100.00% Branches 25/25
100.00% Functions 4/4
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
65
 
x4
 
 
 
x4
x4
x4
x4
x13
x4
x4
x242
x63
x63
x242
x4
x4
x64
 
 
x64
x5
x5
x5
x5
 
 
x59
x64
x3
x3
x3
x64
x64
x8
x8
x8
x4
x4
 
x242
x1
x1
x1
x1
x1
 
 
x241
x242
x229
x229
x12
x6
x6
x6
x12
x242
x4
 
 
 
 
x4






























































// Imports
import { type Cache, type Directive, type Optional, 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: undefined }).get(element)!
    if (((cached.interval !== interval) && (cached.id !== undefined)) || (options.state[renderer.internal("refreshing")])) {
      clearTimeout(cached.id)
      cached.id = undefined
    }
  },
  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
    const cached = cache.get(element)
    if ((!cached) || (cached.id !== undefined)) {
      return
    }
    cached.id = setTimeout(() => {
      if (element.isConnected) {
        renderer.render(element as HTMLElement, { ...options, state: { ...options.state, $refresh: true, [renderer.internal("refreshing")]: true } })
      }
    }, cached.interval)
  },
} as const satisfies Directive<{
  Cache: WeakMap<HTMLElement | Comment, { id: Optional<ReturnType<typeof setTimeout>>; interval: number }>
}>

/** Default exports. */
export default _refresh