@zio.dev/zio-blocks 0.0.33 → 0.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/guides/compile-time-resource-safety-with-scope.md +16 -17
  2. package/guides/getting-started-with-mux.md +1507 -0
  3. package/guides/query-dsl-extending.md +161 -102
  4. package/guides/query-dsl-fluent-builder.md +217 -157
  5. package/guides/query-dsl-reified-optics.md +12 -10
  6. package/guides/query-dsl-sql.md +246 -165
  7. package/guides/telemetry-guide.md +1069 -0
  8. package/guides/zio-schema-migration.md +29 -22
  9. package/index.md +292 -50
  10. package/package.json +1 -1
  11. package/plans/config-follow-up-prs.md +188 -0
  12. package/plans/config-pr-assessment-roadmap.md +310 -0
  13. package/reference/MuxDataFlow.jsx +250 -0
  14. package/reference/async.md +651 -0
  15. package/reference/chunk.md +3533 -308
  16. package/reference/codegen/case-class.md +436 -0
  17. package/reference/codegen/emitter-config.md +383 -0
  18. package/reference/codegen/examples.md +664 -0
  19. package/reference/codegen/field.md +316 -0
  20. package/reference/codegen/index.md +317 -0
  21. package/reference/codegen/scala-emitter.md +392 -0
  22. package/reference/codegen/scala-file.md +276 -0
  23. package/reference/codegen/sealed-trait.md +408 -0
  24. package/reference/codegen/type-definition.md +340 -0
  25. package/reference/codegen/type-ref.md +201 -0
  26. package/reference/combinators.md +347 -117
  27. package/reference/config.md +158 -0
  28. package/reference/context.md +4 -4
  29. package/reference/datastar.md +346 -0
  30. package/reference/docs.md +1461 -345
  31. package/reference/endpoint/auth-type.md +146 -0
  32. package/reference/endpoint/endpoint.md +297 -0
  33. package/reference/endpoint/http-codec.md +249 -0
  34. package/reference/endpoint/index.md +825 -0
  35. package/reference/endpoint/path-codec.md +237 -0
  36. package/reference/endpoint/route-pattern.md +196 -0
  37. package/reference/endpoint/route-tree.md +111 -0
  38. package/reference/endpoint/segment-codec.md +212 -0
  39. package/reference/html.md +1120 -0
  40. package/reference/htmx/attribute-values.md +359 -0
  41. package/reference/htmx/hx-encoding.md +111 -0
  42. package/reference/htmx/hx-params.md +204 -0
  43. package/reference/htmx/hx-swap.md +276 -0
  44. package/reference/htmx/hx-sync.md +251 -0
  45. package/reference/htmx/hx-target.md +314 -0
  46. package/reference/htmx/hx-trigger.md +457 -0
  47. package/reference/htmx/hx-url-update.md +239 -0
  48. package/reference/htmx/index.md +855 -0
  49. package/reference/http-model/index.md +47 -0
  50. package/reference/http-model/model.md +1481 -0
  51. package/reference/http-model/schema.md +747 -0
  52. package/reference/maybe.md +826 -0
  53. package/reference/media-type.md +2 -2
  54. package/reference/mux.mdx +823 -0
  55. package/reference/openapi.md +1351 -0
  56. package/reference/resource-management/defer-handle.md +1 -1
  57. package/reference/resource-management/resource.md +31 -2
  58. package/reference/resource-management/scope.md +28 -12
  59. package/reference/resource-management/wire.md +3 -7
  60. package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
  61. package/reference/ringbuffer/MpscDiagram.jsx +618 -0
  62. package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
  63. package/reference/ringbuffer/SpscDiagram.jsx +677 -0
  64. package/reference/ringbuffer/advanced.mdx +109 -0
  65. package/reference/ringbuffer/index.mdx +145 -0
  66. package/reference/ringbuffer/mpmc.mdx +151 -0
  67. package/reference/ringbuffer/mpsc.mdx +132 -0
  68. package/reference/ringbuffer/spmc.mdx +108 -0
  69. package/reference/ringbuffer/spsc.mdx +344 -0
  70. package/reference/{allows.md → schema/allows.md} +4 -4
  71. package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
  72. package/reference/{binding.md → schema/binding.md} +2 -3
  73. package/reference/schema/built-in-codecs/avro.md +451 -0
  74. package/reference/schema/built-in-codecs/bson.md +480 -0
  75. package/reference/schema/built-in-codecs/csv.md +564 -0
  76. package/reference/schema/built-in-codecs/index.md +77 -0
  77. package/reference/schema/built-in-codecs/json/index.md +295 -0
  78. package/reference/schema/built-in-codecs/json/json-config.md +217 -0
  79. package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
  80. package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
  81. package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
  82. package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
  83. package/reference/schema/built-in-codecs/messagepack.md +508 -0
  84. package/reference/schema/built-in-codecs/thrift.md +433 -0
  85. package/reference/schema/built-in-codecs/toon.md +1078 -0
  86. package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
  87. package/reference/schema/built-in-codecs/yaml.md +552 -0
  88. package/reference/{codec.md → schema/codec.md} +10 -10
  89. package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
  90. package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
  91. package/reference/schema/format.md +92 -0
  92. package/reference/schema/index.md +50 -0
  93. package/reference/schema/migration.md +297 -0
  94. package/reference/{modifier.md → schema/modifier.md} +58 -7
  95. package/reference/{optics.md → schema/optics.md} +2 -2
  96. package/reference/{patch.md → schema/patch.md} +1 -1
  97. package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
  98. package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
  99. package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
  100. package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
  101. package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
  102. package/reference/{schema.md → schema/schema.md} +12 -0
  103. package/reference/{structural-types.md → schema/structural-types.md} +1 -1
  104. package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
  105. package/reference/smithy.md +533 -0
  106. package/reference/sql/db-codec-deriver.md +71 -0
  107. package/reference/sql/db-codec.md +687 -0
  108. package/reference/sql/db-con.md +271 -0
  109. package/reference/sql/db-connection.md +153 -0
  110. package/reference/sql/db-param-writer.md +77 -0
  111. package/reference/sql/db-param.md +66 -0
  112. package/reference/sql/db-result-reader.md +146 -0
  113. package/reference/sql/db-tx.md +82 -0
  114. package/reference/sql/db-value.md +41 -0
  115. package/reference/sql/ddl.md +85 -0
  116. package/reference/sql/frag.md +254 -0
  117. package/reference/sql/index.md +341 -0
  118. package/reference/sql/repo.md +600 -0
  119. package/reference/sql/sql-dialect.md +73 -0
  120. package/reference/sql/sql-logger.md +62 -0
  121. package/reference/sql/sql-name-mapper.md +70 -0
  122. package/reference/sql/table-metadata.md +134 -0
  123. package/reference/sql/table.md +448 -0
  124. package/reference/sql/transactor-zio.md +399 -0
  125. package/reference/sql/transactor.md +353 -0
  126. package/reference/sql-zio.md +112 -0
  127. package/reference/streams/concurrent-operators.md +106 -0
  128. package/reference/streams/index.md +653 -0
  129. package/reference/streams/pipeline.md +718 -0
  130. package/reference/streams/reader.md +1284 -0
  131. package/reference/streams/scala-2-compatibility.md +55 -0
  132. package/reference/streams/sink.md +1426 -0
  133. package/reference/streams/stream.md +2526 -0
  134. package/reference/streams/writer.md +1045 -0
  135. package/reference/streams/zero-boxing.md +275 -0
  136. package/reference/telemetry.md +693 -0
  137. package/reference/typeid.md +5 -19
  138. package/sidebars.js +238 -43
  139. package/reference/formats.md +0 -694
  140. package/reference/http-model.md +0 -1716
  141. package/reference/streams.md +0 -989
  142. package/ringbuffer.md +0 -249
  143. /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
  144. /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
  145. /package/reference/{lazy.md → schema/lazy.md} +0 -0
  146. /package/reference/{reflect.md → schema/reflect.md} +0 -0
  147. /package/reference/{registers.md → schema/registers.md} +0 -0
  148. /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
  149. /package/reference/{syntax.md → schema/syntax.md} +0 -0
  150. /package/reference/{validation.md → schema/validation.md} +0 -0
@@ -0,0 +1,457 @@
1
+ ---
2
+ id: hx-trigger
3
+ title: HxTrigger
4
+ ---
5
+
6
+ `HxTrigger` represents the `hx-trigger` attribute, declaring which event fires an HTMX request. It combines an event name with optional modifiers that refine timing (delay, throttle), state (once, changed), source (from), queue strategy, and filtering. `HxTriggerSet` composes multiple triggers in a comma-separated list for complex event handling.
7
+
8
+ Start from a predefined trigger like `HxTrigger.click` or construct one with `HxTrigger("eventName")`. Add modifiers by chaining methods. Modifiers within the same group (e.g., two `delay` calls) replace earlier values; unrelated modifiers accumulate. Here are the core patterns:
9
+
10
+ ```scala
11
+ import zio.http.htmx._
12
+ import scala.concurrent.duration._
13
+
14
+ // Predefined trigger
15
+ HxTrigger.click
16
+
17
+ // Custom event name
18
+ HxTrigger("myEvent")
19
+
20
+ // With modifiers
21
+ HxTrigger.input.delay(500.millis).changed
22
+
23
+ // Multiple triggers
24
+ HxTrigger(HxTrigger.click, HxTrigger.load)
25
+
26
+ // Polling
27
+ HxTrigger.every(2.seconds)
28
+ ```
29
+
30
+ ## Predefined Triggers
31
+
32
+ Common HTMX events are available as case objects:
33
+
34
+ - `HxTrigger.click` — Fires on element click.
35
+ - `HxTrigger.submit` — Fires on form submission.
36
+ - `HxTrigger.load` — Fires when the element is first loaded and added to the DOM.
37
+ - `HxTrigger.change` — Fires when an input/select/textarea value changes.
38
+ - `HxTrigger.input` — Fires on each keystroke in an input/textarea.
39
+ - `HxTrigger.revealed` — Fires when the element scrolls into view (requires Intersection Observer).
40
+ - `HxTrigger.intersect` — Fires when the element enters the intersection observer threshold.
41
+
42
+ All predefined triggers are ready for modifier chaining:
43
+
44
+ ```scala
45
+ import zio.blocks.html._
46
+ import zio.http.htmx._
47
+
48
+ input(hxPost := "/search", hxTrigger := HxTrigger.input)
49
+ form(hxPost := "/submit", hxTrigger := HxTrigger.submit)
50
+ div(hxGet := "/status", hxTrigger := HxTrigger.load)
51
+ ```
52
+
53
+ ## Custom Events
54
+
55
+ Construct a trigger from an arbitrary event name using `HxTrigger("eventName")`:
56
+
57
+ ```scala
58
+ import zio.blocks.html._
59
+ import zio.http.htmx._
60
+
61
+ // Custom event (must be fired by JavaScript)
62
+ div(hxPost := "/api/action", hxTrigger := HxTrigger("custom-event"))
63
+ ```
64
+
65
+ The module validates that the event name is non-empty but otherwise accepts any string. Custom events must be fired by your JavaScript code or other HTMX event sources.
66
+
67
+ ## Timing Modifiers
68
+
69
+ Control when the request fires relative to the triggering event:
70
+
71
+ **`delay(duration: FiniteDuration)`** adds a `delay:` modifier, waiting before firing the request. Useful for debouncing high-frequency events like typing:
72
+
73
+ ```scala
74
+ import zio.blocks.html._
75
+ import zio.http.htmx._
76
+ import scala.concurrent.duration._
77
+
78
+ // Wait 500ms after typing stops before firing
79
+ input(
80
+ hxPost := "/search",
81
+ hxTrigger := HxTrigger.input.delay(500.millis)
82
+ )
83
+ ```
84
+
85
+ **`throttle(duration: FiniteDuration)`** adds a `throttle:` modifier, firing at most once per duration. Useful for rate-limiting requests on frequent events:
86
+
87
+ ```scala
88
+ import zio.blocks.html._
89
+ import zio.http.htmx._
90
+ import scala.concurrent.duration._
91
+
92
+ // Fire at most once per second
93
+ input(
94
+ hxPost := "/search",
95
+ hxTrigger := HxTrigger.input.throttle(1.second)
96
+ )
97
+ ```
98
+
99
+ Both modifiers accept any `FiniteDuration`. Calling `delay()` or `throttle()` twice replaces the earlier value:
100
+
101
+ ```scala
102
+ import zio.http.htmx._
103
+ import scala.concurrent.duration._
104
+
105
+ val trigger1 = HxTrigger.input.delay(1.second)
106
+ val trigger2 = trigger1.delay(500.millis) // replaces 1.second
107
+ ```
108
+
109
+ ## State Modifiers
110
+
111
+ Refine which events fire the request based on element state:
112
+
113
+ **`once: HxTrigger`** adds the `once` modifier, firing the request exactly once and then disabling the trigger:
114
+
115
+ ```scala
116
+ import zio.blocks.html._
117
+ import zio.http.htmx._
118
+
119
+ // Load data only on first page visit
120
+ div(hxGet := "/initial-data", hxTrigger := HxTrigger.load.once)
121
+ ```
122
+
123
+ **`changed: HxTrigger`** adds the `changed` modifier, firing only when the element's value actually changes (not on every event):
124
+
125
+ ```scala
126
+ import zio.blocks.html._
127
+ import zio.http.htmx._
128
+ import scala.concurrent.duration._
129
+
130
+ // Fire only if the value is different from the last value
131
+ input(
132
+ hxPost := "/validate",
133
+ hxTrigger := HxTrigger.input.changed
134
+ )
135
+ ```
136
+
137
+ Combine these with timing modifiers for fine-grained control:
138
+
139
+ ```scala
140
+ import zio.http.htmx._
141
+ import scala.concurrent.duration._
142
+
143
+ HxTrigger.input.delay(500.millis).changed
144
+ ```
145
+
146
+ ## Source & Target Control
147
+
148
+ Direct where the request originates and how it affects other elements:
149
+
150
+ **`from(selector: String)` or `from(target: HxTarget)`** adds a `from:` modifier, listening for the trigger on a different element. The request still fires on the original element, but it listens for the trigger event on the specified source:
151
+
152
+ ```scala
153
+ import zio.blocks.html._
154
+ import zio.http.htmx._
155
+
156
+ // Button fires a request, but listens for clicks on the input
157
+ input(id := "search")
158
+ button(
159
+ "Go",
160
+ hxPost := "/search",
161
+ hxTrigger := HxTrigger.click.from("#search")
162
+ )
163
+ ```
164
+
165
+ **`target(selector: String)`** adds a `target:` modifier, restricting the trigger to events from specific descendant elements. Useful for event delegation:
166
+
167
+ ```scala
168
+ import zio.blocks.html._
169
+ import zio.http.htmx._
170
+
171
+ // Only fire on clicks within .clickable items
172
+ div(
173
+ hxPost := "/item-selected",
174
+ hxTrigger := HxTrigger.click.target(".clickable"),
175
+ div(className := "clickable", "Item 1"),
176
+ div(className := "clickable", "Item 2")
177
+ )
178
+ ```
179
+
180
+ ## Queue Strategy
181
+
182
+ Control how requests queue when multiple triggers fire in quick succession:
183
+
184
+ **`queue(strategy: HxTrigger.QueueStrategy)`** adds a `queue:` modifier. Valid strategies are:
185
+
186
+ - `HxTrigger.QueueStrategy.First` — Queue the first request only; discard later ones until it completes.
187
+ - `HxTrigger.QueueStrategy.Last` — Keep the most recent request; discard earlier queued ones.
188
+ - `HxTrigger.QueueStrategy.All` — Queue all requests and fire them in order (default HTMX behavior).
189
+ - `HxTrigger.QueueStrategy.None` — Abort pending requests and fire the new one immediately.
190
+
191
+ Here are examples using different queue strategies:
192
+
193
+ ```scala
194
+ import zio.blocks.html._
195
+ import zio.http.htmx._
196
+ import scala.concurrent.duration._
197
+
198
+ // Only fire the most recent request
199
+ input(
200
+ hxPost := "/search",
201
+ hxTrigger := HxTrigger.input.delay(500.millis).queue(HxTrigger.QueueStrategy.Last)
202
+ )
203
+
204
+ // Fire only the first request in a burst
205
+ button(
206
+ hxPost := "/submit",
207
+ hxTrigger := HxTrigger.click.queue(HxTrigger.QueueStrategy.First)
208
+ )
209
+ ```
210
+
211
+ ## Intersection Observer
212
+
213
+ **`threshold(value: Double)`** adds a `threshold:` modifier for Intersection Observer-based triggers (e.g., `HxTrigger.intersect`). The value is a fraction between 0.0 and 1.0:
214
+
215
+ ```scala
216
+ import zio.blocks.html._
217
+ import zio.http.htmx._
218
+
219
+ // Load when 50% of the element is visible
220
+ div(
221
+ hxGet := "/load-more",
222
+ hxTrigger := HxTrigger.intersect.threshold(0.5)
223
+ )
224
+ ```
225
+
226
+ **`root(selector: String)`** adds a `root:` modifier, specifying the container for Intersection Observer calculations:
227
+
228
+ ```scala
229
+ import zio.blocks.html._
230
+ import zio.http.htmx._
231
+
232
+ // Observe intersection within a scrollable container
233
+ div(
234
+ id := "scrollable-list",
235
+ div(
236
+ hxGet := "/item",
237
+ hxTrigger := HxTrigger.intersect.root(".scrollable-list")
238
+ )
239
+ )
240
+ ```
241
+
242
+ ## JavaScript Filtering
243
+
244
+ **`filter(expression: Js)`** adds a JavaScript expression that must return true for the request to fire. The `Js` type is intentionally raw—do not build it from unsanitized user input:
245
+
246
+ ```scala
247
+ import zio.blocks.html._
248
+ import zio.http.htmx._
249
+
250
+ // Only POST if input has 3+ characters
251
+ input(
252
+ hxPost := "/search",
253
+ hxTrigger := HxTrigger.input.filter(Js("event.target.value.length > 2"))
254
+ )
255
+ ```
256
+
257
+ The filter expression has access to the native JavaScript `event` object and the element's JavaScript context.
258
+
259
+ ## Request Consumption
260
+
261
+ **`consume: HxTrigger`** adds the `consume` modifier, preventing the triggering event from bubbling to parent handlers:
262
+
263
+ ```scala
264
+ import zio.blocks.html._
265
+ import zio.http.htmx._
266
+
267
+ // Click on button fires request and stops event propagation
268
+ button(
269
+ hxPost := "/action",
270
+ hxTrigger := HxTrigger.click.consume
271
+ )
272
+ ```
273
+
274
+ ## Polling
275
+
276
+ **`HxTrigger.every(duration: FiniteDuration)`** creates a special polling trigger that fires every N milliseconds/seconds:
277
+
278
+ ```scala
279
+ import zio.blocks.html._
280
+ import zio.http.htmx._
281
+ import scala.concurrent.duration._
282
+
283
+ // Poll every 3 seconds
284
+ div(
285
+ hxGet := "/status",
286
+ hxTrigger := HxTrigger.every(3.seconds)
287
+ )
288
+ ```
289
+
290
+ The duration can be any `FiniteDuration`. Polling triggers can be paused and resumed by HTMX via the `htmx:beforeRequest` and `htmx:afterRequest` events.
291
+
292
+ ## Rendering & Parsing
293
+
294
+ **`render: String`** produces the literal `hx-trigger` attribute value string:
295
+
296
+ ```scala
297
+ import zio.http.htmx._
298
+ import scala.concurrent.duration._
299
+
300
+ val trigger = HxTrigger.input.delay(500.millis).changed
301
+ trigger.render // "input delay:500ms changed"
302
+ ```
303
+
304
+ ## Multiple Triggers (HxTriggerSet)
305
+
306
+ Use the `HxTrigger` constructor to combine multiple triggers in a comma-separated list:
307
+
308
+ ```scala
309
+ import zio.http.htmx._
310
+ import scala.concurrent.duration._
311
+
312
+ // Fire on click or every 5 seconds
313
+ val triggers = HxTrigger(
314
+ HxTrigger.click,
315
+ HxTrigger.every(5.seconds)
316
+ )
317
+ triggers.render // "click, every 5s"
318
+ ```
319
+
320
+ Assign `HxTriggerSet` directly to the `hxTrigger` attribute:
321
+
322
+ ```scala
323
+ import zio.blocks.html._
324
+ import zio.http.htmx._
325
+ import scala.concurrent.duration._
326
+
327
+ div(
328
+ hxGet := "/update",
329
+ hxTrigger := HxTrigger(
330
+ HxTrigger.click,
331
+ HxTrigger.load,
332
+ HxTrigger.every(10.seconds)
333
+ )
334
+ )
335
+ ```
336
+
337
+ ## Common Patterns
338
+
339
+ `HxTrigger` enables sophisticated event handling through modifiers. Here are practical usage patterns:
340
+
341
+ ### Debounced Search
342
+
343
+ Delay firing to let the user finish typing, and only fire if the value changed:
344
+
345
+ ```scala
346
+ import zio.blocks.html._
347
+ import zio.http.htmx._
348
+ import scala.concurrent.duration._
349
+
350
+ input(
351
+ placeholder := "Search...",
352
+ hxPost := "/api/search",
353
+ hxTrigger := HxTrigger.input.delay(500.millis).changed
354
+ )
355
+ ```
356
+
357
+ ### Rate-Limited Live Updates
358
+
359
+ Throttle rapid events to avoid overwhelming the server:
360
+
361
+ ```scala
362
+ import zio.blocks.html._
363
+ import zio.http.htmx._
364
+ import scala.concurrent.duration._
365
+
366
+ div(
367
+ hxPost := "/analytics",
368
+ hxTrigger := HxTrigger.input.throttle(1.second)
369
+ )
370
+ ```
371
+
372
+ ### Load Once
373
+
374
+ Fire the request exactly once when the page loads:
375
+
376
+ ```scala
377
+ import zio.blocks.html._
378
+ import zio.http.htmx._
379
+
380
+ div(
381
+ hxGet := "/welcome-message",
382
+ hxTrigger := HxTrigger.load.once
383
+ )
384
+ ```
385
+
386
+ ### Lazy Loading with Intersection Observer
387
+
388
+ Load content when it scrolls into view:
389
+
390
+ ```scala
391
+ import zio.blocks.html._
392
+ import zio.http.htmx._
393
+
394
+ // Load when 10% of the image is visible
395
+ img(
396
+ src := "/placeholder.jpg",
397
+ hxGet := "/lazy-image",
398
+ hxSwap := HxSwap.OuterHTML,
399
+ hxTrigger := HxTrigger.intersect.threshold(0.1)
400
+ )
401
+ ```
402
+
403
+ ### Dual Triggers: Click or Automatic
404
+
405
+ Fire on click or every 30 seconds:
406
+
407
+ ```scala
408
+ import zio.blocks.html._
409
+ import zio.http.htmx._
410
+ import scala.concurrent.duration._
411
+
412
+ button(
413
+ hxPost := "/refresh",
414
+ hxTrigger := HxTrigger(
415
+ HxTrigger.click,
416
+ HxTrigger.every(30.seconds)
417
+ ),
418
+ "Refresh Now"
419
+ )
420
+ ```
421
+
422
+ ### Event Delegation
423
+
424
+ Parent element listens for clicks on child items:
425
+
426
+ ```scala
427
+ import zio.blocks.html._
428
+ import zio.http.htmx._
429
+
430
+ ul(
431
+ hxPost := "/item-selected",
432
+ hxTrigger := HxTrigger.click.target("li"),
433
+ li("Item 1"),
434
+ li("Item 2"),
435
+ li("Item 3")
436
+ )
437
+ ```
438
+
439
+ ## Integration with Other Module Types
440
+
441
+ `HxTrigger` pairs with `HxTarget` (where to swap) and `HxSwap` (how to swap):
442
+
443
+ ```scala
444
+ import zio.blocks.html._
445
+ import zio.http.htmx._
446
+ import scala.concurrent.duration._
447
+
448
+ button(
449
+ hxPost := "/api/action",
450
+ hxTrigger := HxTrigger.click.delay(100.millis),
451
+ hxTarget := HxTarget.closest("form"),
452
+ hxSwap := HxSwap.InnerHTML.settle(250.millis),
453
+ "Submit"
454
+ )
455
+ ```
456
+
457
+ The `ToHtmxValue` type class handles both `HxTrigger` (single trigger) and `HxTriggerSet` (multiple triggers), so you can assign either directly to the `hxTrigger` attribute key.
@@ -0,0 +1,239 @@
1
+ ---
2
+ id: hx-url-update
3
+ title: HxUrlUpdate
4
+ ---
5
+
6
+ `HxUrlUpdate` represents the `hx-push-url` and `hx-replace-url` attributes, controlling whether and how the browser's URL bar updates after an HTMX request. You can enable/disable URL updates as booleans or specify a custom URL to push/replace.
7
+
8
+ Use `HxUrlUpdate.Enabled` or `HxUrlUpdate(true)` to update the URL with the response URL, or pass a custom URL string/Path/URL to update to a different address. Use `HxUrlUpdate.Disabled` or `HxUrlUpdate(false)` to prevent updates. Here are the core patterns:
9
+
10
+ ```scala
11
+ import zio.http.htmx._
12
+ import zio.http.Path
13
+
14
+ // Enable URL update (default)
15
+ HxUrlUpdate.Enabled
16
+
17
+ // Disable URL update
18
+ HxUrlUpdate.Disabled
19
+
20
+ // Custom URL string
21
+ HxUrlUpdate("/custom-url")
22
+
23
+ // Custom URL using Path
24
+ HxUrlUpdate(Path("/custom-url"))
25
+
26
+ // Boolean shorthand
27
+ HxUrlUpdate(true)
28
+ HxUrlUpdate(false)
29
+ ```
30
+
31
+ ## Strategies
32
+
33
+ **`HxUrlUpdate.Enabled`** updates the browser URL with the response URL. This is the default behavior when `hxPushUrl` or `hxReplaceUrl` are set:
34
+
35
+ ```scala
36
+ import zio.blocks.html._
37
+ import zio.http.htmx._
38
+
39
+ a(
40
+ href := "/page-2",
41
+ hxPushUrl := HxUrlUpdate.Enabled,
42
+ "Page 2"
43
+ )
44
+ ```
45
+
46
+ **`HxUrlUpdate.Disabled`** prevents the URL from updating, even though the page content changes:
47
+
48
+ ```scala
49
+ import zio.blocks.html._
50
+ import zio.http.htmx._
51
+
52
+ form(
53
+ hxPost := "/search",
54
+ hxPushUrl := HxUrlUpdate.Disabled,
55
+ "Search (URL won't change)"
56
+ )
57
+ ```
58
+
59
+ **`HxUrlUpdate(customUrl: String)`** updates the URL to a specific custom address instead of the response URL:
60
+
61
+ ```scala
62
+ import zio.blocks.html._
63
+ import zio.http.htmx._
64
+
65
+ form(
66
+ hxPost := "/api/search-internal",
67
+ hxPushUrl := HxUrlUpdate("/results"),
68
+ "Search (URL becomes /results)"
69
+ )
70
+ ```
71
+
72
+ **`HxUrlUpdate(path: Path)` or `HxUrlUpdate(url: URL)`** accepts typed paths and URLs, which are then encoded to strings:
73
+
74
+ ```scala
75
+ import zio.blocks.html._
76
+ import zio.http.htmx._
77
+ import zio.http.Path
78
+
79
+ form(
80
+ hxPost := "/api/search",
81
+ hxPushUrl := HxUrlUpdate(Path("/search-results")),
82
+ button("Search")
83
+ )
84
+ ```
85
+
86
+ ## Boolean Constructors
87
+
88
+ Convenient overloads let you use booleans directly:
89
+
90
+ ```scala
91
+ import zio.blocks.html._
92
+ import zio.http.htmx._
93
+
94
+ form(
95
+ hxPost := "/search",
96
+ hxPushUrl := HxUrlUpdate(true), // same as HxUrlUpdate.Enabled
97
+ button("Search")
98
+ )
99
+
100
+ button(
101
+ hxPost := "/action",
102
+ hxPushUrl := HxUrlUpdate(false), // same as HxUrlUpdate.Disabled
103
+ "Action (URL unchanged)"
104
+ )
105
+ ```
106
+
107
+ ## Rendering & Parsing
108
+
109
+ **`render: String`** produces the literal attribute value string:
110
+
111
+ ```scala
112
+ import zio.http.htmx._
113
+
114
+ HxUrlUpdate.Enabled.render // "true"
115
+ HxUrlUpdate.Disabled.render // "false"
116
+ HxUrlUpdate("/results").render // "/results"
117
+ ```
118
+
119
+ **`HxUrlUpdate.parse(value: String): Either[String, HxUrlUpdate]`** parses a rendered string back into a typed `HxUrlUpdate`:
120
+
121
+ ```scala
122
+ import zio.http.htmx._
123
+
124
+ HxUrlUpdate.parse("true") // Right(HxUrlUpdate.Enabled)
125
+ HxUrlUpdate.parse("false") // Right(HxUrlUpdate.Disabled)
126
+ HxUrlUpdate.parse("/custom") // Right(HxUrlUpdate("/custom"))
127
+ ```
128
+
129
+ ## Common Patterns
130
+
131
+ URL updates enable seamless integration with browser history and bookmarking. Here are representative usage patterns:
132
+
133
+ ### Progressive Enhancement with URL Updates
134
+
135
+ Keep the URL in sync with SPA-like navigation while using server-side rendering:
136
+
137
+ ```scala
138
+ import zio.blocks.html._
139
+ import zio.http.htmx._
140
+
141
+ nav(
142
+ a(
143
+ href := "/users",
144
+ hxPushUrl := HxUrlUpdate.Enabled,
145
+ hxTarget := HxTarget.css("#content"),
146
+ "Users"
147
+ ),
148
+ a(
149
+ href := "/settings",
150
+ hxPushUrl := HxUrlUpdate.Enabled,
151
+ hxTarget := HxTarget.css("#content"),
152
+ "Settings"
153
+ )
154
+ )
155
+ ```
156
+
157
+ ### API-Only Requests Without URL Change
158
+
159
+ Make internal API calls that don't affect the URL:
160
+
161
+ ```scala
162
+ import zio.blocks.html._
163
+ import zio.http.htmx._
164
+
165
+ button(
166
+ hxPost := "/api/save-draft",
167
+ hxPushUrl := HxUrlUpdate.Disabled,
168
+ "Save Draft"
169
+ )
170
+ ```
171
+
172
+ ### Redirect to a Different URL
173
+
174
+ Use an internal API endpoint but show a different user-friendly URL:
175
+
176
+ ```scala
177
+ import zio.blocks.html._
178
+ import zio.http.htmx._
179
+
180
+ form(
181
+ hxPost := "/api/checkout-internal",
182
+ hxPushUrl := HxUrlUpdate("/order-confirmation"),
183
+ hxTarget := HxTarget.css("#main"),
184
+ button("Complete Order")
185
+ )
186
+ ```
187
+
188
+ ### Search with Query Parameter URL
189
+
190
+ Push a URL with query parameters to reflect the search state:
191
+
192
+ ```scala
193
+ import zio.blocks.html._
194
+ import zio.http.htmx._
195
+
196
+ input(
197
+ name := "query",
198
+ hxPost := "/search",
199
+ hxPushUrl := HxUrlUpdate("/search?query=..."), // placeholder; dynamically set by server
200
+ placeholder := "Search..."
201
+ )
202
+ ```
203
+
204
+ ## Replace vs Push
205
+
206
+ Use `hxReplaceUrl` instead of `hxPushUrl` to replace the current history entry rather than adding a new one:
207
+
208
+ ```scala
209
+ import zio.blocks.html._
210
+ import zio.http.htmx._
211
+
212
+ // This request replaces the current history entry
213
+ form(
214
+ hxPost := "/search",
215
+ hxReplaceUrl := HxUrlUpdate("/search-results"),
216
+ button("Search")
217
+ )
218
+ ```
219
+
220
+ Both attributes accept the same `HxUrlUpdate` type. Use `replace` for searches, filters, and state changes that shouldn't create browser history entries. Use `push` for navigation to new pages.
221
+
222
+ ## Integration with Other Attributes
223
+
224
+ Combine `HxUrlUpdate` with `hxPushUrl` and `hxReplaceUrl` to control history and URL bar updates:
225
+
226
+ ```scala
227
+ import zio.blocks.html._
228
+ import zio.http.htmx._
229
+
230
+ form(
231
+ hxPost := "/api/search",
232
+ hxTarget := HxTarget.css("#results"),
233
+ hxSwap := HxSwap.InnerHTML,
234
+ hxReplaceUrl := HxUrlUpdate("/search?q=example"),
235
+ input(placeholder := "Search...")
236
+ )
237
+ ```
238
+
239
+ The `ToHtmxValue[HxUrlUpdate]` instance renders automatically, so `HxUrlUpdate` values work seamlessly with both attribute keys.