All files / render / client / client.ts

100.00% Branches 3/3
100.00% Lines 32/32
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 
 
x2
x2
 
 
 
 
 
 
x2
 
 
 
 
 
x2
x4
x4
x4
 
x4
x4
x4
 
 
x4
x28
x35
 
x7
x7
 
 
x4
 
 
x4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x4
x5
x5
 
 
 
 
 
 
 
 
 
x4
x8
x8
x9
x9
x80
x8
 
 
x4
x5
x5
 
 
x2
x2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 











































































































// Imports
import type { Arg, Directive, RendererOptions, RendererRenderOptions } from "@mizu/internal/engine"
import { Context, Renderer } from "@mizu/internal/engine"
import defaults from "./defaults.ts"
export type * from "@mizu/internal/engine"

/**
 * Client side renderer.
 * @module
 */
export class Client {
  /**
   * Default options for {@linkcode Client}.
   *
   * These default options are merged with the provided options when creating a new {@linkcode Client} instance.
   */
  static defaults = {
    window: globalThis.window,
    directives: defaults,
    context: {},
    // deno-lint-ignore no-console
    warn: console.warn,
    debug: undefined,
  } as unknown as Required<ClientOptions>

  /** {@linkcode Client} constructor. */
  constructor(options?: ClientOptions) {
    const { directives, window, warn, debug, context } = { ...Client.defaults, ...options }
    this.#renderer = new Renderer(window, { directives, warn, debug })
    // deno-lint-ignore no-explicit-any
    this.#context = new Context<any>(context)
  }

  /** Linked {@linkcode Renderer}. */
  readonly #renderer

  /** Linked {@linkcode Context}. */
  readonly #context

  /**
   * Rendering context.
   *
   * All properties assigned to this object are available during rendering.
   *
   * Changes to this object are reactive and will trigger a re-render of related elements.
   * This is achieved using {@linkcode Context}, which leverages {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy | Proxy} handlers.
   *
   * > [!NOTE]
   * > You cannot reassign this property directly to ensure reactivity is maintained.
   * > To achieve a similar effect, use {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign | Object.assign()}.
   */
  // deno-lint-ignore no-explicit-any
  get context(): Record<PropertyKey, any> {
    return this.#context.target
  }

  /**
   * Start rendering all subtrees marked with the {@linkcode https://mizu.sh/#mizu | *mizu} attribute.
   *
   * ```ts ignore
   * const mizu = new Client({ context: { foo: "bar" } })
   * await mizu.render()
   * ```
   */
  render<T extends Arg<Renderer["render"]>>(element = this.#renderer.document.documentElement as T, options?: ClientRenderOptions): Promise<T> {
    let context = this.#context
    if (options?.context) {
      context = context.with(options.context)
    }
    return this.#renderer.render(element, { implicit: false, reactive: true, ...options, context, state: { $renderer: "client", ...options?.state } })
  }

  /** Flush the reactive render queue of {@linkcode Renderer}.  */
  flush(): Promise<void> {
    return this.#renderer.flushReactiveRenderQueue()
  }

  /** Default {@linkcode Client} instance. */
  static readonly default = new Client() as Client
}

/** {@linkcode Client} options. */
export type ClientOptions = Pick<RendererOptions, "warn" | "debug"> & {
  /** Default directives. */
  directives?: Array<Partial<Directive> | string>
  /**
   * Initial rendering {@linkcode Context}.
   *
   * It can be modified later using the {@linkcode Client.context} property.
   */
  context?: ConstructorParameters<typeof Context>[0]
  /** Window object. */
  window?: Renderer["window"]
}

/** {@linkcode Client.render} options. */
export type ClientRenderOptions = Pick<RendererRenderOptions, "implicit" | "reactive" | "throw"> & {
  /**
   * Rendering context.
   *
   * Values from {@linkcode Client.context} are inherited.
   */
  context?: Arg<Context["with"]>
  /**
   * Initial state.
   *
   * It is populated with `$renderer: "client"` by default.
   */
  state?: Arg<Renderer["render"], 1, true>["state"]
}