/ src / lightspeed / component / helpers.gleam
helpers.gleam
 1  //// Ergonomic helpers for Lightspeed components.
 2  
 3  import lightspeed/agent/isa
 4  import lightspeed/component
 5  
 6  /// Construct a component from init/update/render functions.
 7  pub fn make(
 8    init init: fn() -> model,
 9    update update: fn(model, msg) -> #(model, List(component.Command(msg))),
10    render render: fn(model) -> component.Rendered,
11  ) -> component.Component(model, msg) {
12    component.Component(init: init, update: update, render: render)
13  }
14  
15  /// Render markup with an explicit fingerprint.
16  pub fn html_with_fingerprint(
17    markup: String,
18    fingerprint: String,
19  ) -> component.Rendered {
20    component.Rendered(html: markup, fingerprint: fingerprint)
21  }
22  
23  /// Return an update result with no commands.
24  pub fn no_effect(model: model) -> #(model, List(component.Command(msg))) {
25    #(model, [])
26  }
27  
28  /// Construct a dispatch command.
29  pub fn dispatch(msg: msg) -> component.Command(msg) {
30    component.Dispatch(msg)
31  }
32  
33  /// Construct a pushed ISA instruction command.
34  pub fn push(instruction: isa.Instruction) -> component.Command(msg) {
35    component.Push(instruction)
36  }
37  
38  /// Construct a navigate command.
39  pub fn navigate(to: String) -> component.Command(msg) {
40    push(isa.Navigate(to: to))
41  }
42  
43  /// Construct a patch command.
44  pub fn patch(target: String, html: String) -> component.Command(msg) {
45    push(isa.Patch(target: target, html: html))
46  }
47  
48  /// Construct a subscription command.
49  pub fn subscribe(topic: String) -> component.Command(msg) {
50    push(isa.Subscribe(topic: topic))
51  }
52  
53  /// Construct an unsubscription command.
54  pub fn unsubscribe(topic: String) -> component.Command(msg) {
55    push(isa.Unsubscribe(topic: topic))
56  }
57  
58  /// Construct a shutdown command.
59  pub fn shutdown(reason: String) -> component.Command(msg) {
60    push(isa.Shutdown(reason: reason))
61  }
62  
63  /// Map the message type inside a component command.
64  pub fn map_command(
65    command: component.Command(inner),
66    with: fn(inner) -> outer,
67  ) -> component.Command(outer) {
68    case command {
69      component.NoCommand -> component.NoCommand
70      component.Dispatch(inner) -> component.Dispatch(with(inner))
71      component.Push(instruction) -> component.Push(instruction)
72    }
73  }
74  
75  /// Map message type for a list of commands.
76  pub fn map_commands(
77    commands: List(component.Command(inner)),
78    with: fn(inner) -> outer,
79  ) -> List(component.Command(outer)) {
80    case commands {
81      [] -> []
82      [command, ..rest] -> [
83        map_command(command, with),
84        ..map_commands(rest, with)
85      ]
86    }
87  }