@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,276 @@
1
+ ---
2
+ id: hx-swap
3
+ title: HxSwap
4
+ ---
5
+
6
+ `HxSwap` represents the `hx-swap` attribute, controlling how HTMX replaces DOM content after a successful response. It combines a base strategy (innerHTML, outerHTML, etc.) with optional modifiers that refine timing, animation, scrolling, and focus behavior.
7
+
8
+ Start from one of the predefined strategies, then call modifier methods to customize timing, animation, and scroll behavior. All modifiers are optional, and calling the same modifier twice replaces the earlier value rather than stacking. Here are the core patterns:
9
+
10
+ ```scala
11
+ import zio.http.htmx._
12
+ import scala.concurrent.duration._
13
+
14
+ // Base strategy only
15
+ HxSwap.InnerHTML
16
+
17
+ // With timing modifiers
18
+ HxSwap.OuterHTML.swap(1.second).settle(500.millis)
19
+
20
+ // With animation and scroll
21
+ HxSwap.BeforeEnd.transition.scroll(HxSwap.ScrollPosition.Top)
22
+
23
+ // Full pipeline
24
+ HxSwap.InnerHTML
25
+ .swap(500.millis)
26
+ .settle(250.millis)
27
+ .transition
28
+ .scroll(HxSwap.ScrollPosition.Bottom)
29
+ .show(HxSwap.ShowPosition.Top)
30
+ .focusScroll(true)
31
+ ```
32
+
33
+ ## Swap Strategies
34
+
35
+ Core HTMX strategies define where and how content replaces the DOM:
36
+
37
+ - `HxSwap.InnerHTML` — Replaces the inner content of the target element. This is the default HTMX behavior.
38
+ - `HxSwap.OuterHTML` — Replaces the target element itself, including its opening and closing tags.
39
+ - `HxSwap.TextContent` — Replaces only the text content, leaving the element structure intact.
40
+ - `HxSwap.BeforeBegin` — Inserts the response as a sibling before the target element.
41
+ - `HxSwap.AfterBegin` — Inserts the response as the first child of the target element.
42
+ - `HxSwap.BeforeEnd` — Inserts the response as the last child of the target element.
43
+ - `HxSwap.AfterEnd` — Inserts the response as a sibling after the target element.
44
+ - `HxSwap.Delete` — Removes the target element; response content is discarded.
45
+ - `HxSwap.NoneSwap` — Prevents any content from being swapped into the DOM; the response is ignored.
46
+
47
+ All strategies are available as immutable `HxSwap` values, ready for modifier chaining:
48
+
49
+ ```scala
50
+ import zio.blocks.html._
51
+ import zio.http.htmx._
52
+
53
+ div(hxSwap := HxSwap.InnerHTML)
54
+ div(hxSwap := HxSwap.OuterHTML)
55
+ div(hxSwap := HxSwap.BeforeBegin)
56
+ ```
57
+
58
+ ## Timing Modifiers
59
+
60
+ Control when and how long the swap operation takes:
61
+
62
+ **`swap(duration: FiniteDuration)`** adds a `swap:` delay before the swap begins. Useful for coordinating with request latency or preparing the DOM:
63
+
64
+ ```scala
65
+ import zio.blocks.html._
66
+ import zio.http.htmx._
67
+ import scala.concurrent.duration._
68
+
69
+ // Wait 500ms after response before swapping
70
+ div(hxSwap := HxSwap.InnerHTML.swap(500.millis))
71
+ ```
72
+
73
+ **`settle(duration: FiniteDuration)`** adds a `settle:` delay after the swap before settling (CSS transitions, settling messages). This allows animations to run:
74
+
75
+ ```scala
76
+ import zio.blocks.html._
77
+ import zio.http.htmx._
78
+ import scala.concurrent.duration._
79
+
80
+ // Swap immediately, but wait 250ms for CSS to settle
81
+ div(hxSwap := HxSwap.InnerHTML.settle(250.millis))
82
+ ```
83
+
84
+ Both `swap()` and `settle()` accept any `FiniteDuration` that renders to a valid HTMX duration (milliseconds: `ms`, seconds: `s`, or bare integers treated as milliseconds). Calling either method twice replaces the first value:
85
+
86
+ ```scala
87
+ import zio.http.htmx._
88
+ import scala.concurrent.duration._
89
+
90
+ val swap1 = HxSwap.InnerHTML.swap(1.second)
91
+ val swap2 = swap1.swap(500.millis) // replaces 1.second with 500.millis
92
+ ```
93
+
94
+ ## Animation & Transition
95
+
96
+ **`transition: HxSwap`** enables the `transition:true` modifier, allowing CSS transitions to run during the swap. Useful with timing modifiers to coordinate animation:
97
+
98
+ ```scala
99
+ import zio.blocks.html._
100
+ import zio.http.htmx._
101
+ import scala.concurrent.duration._
102
+
103
+ // Enable transitions and give them 300ms to complete
104
+ div(hxSwap := HxSwap.InnerHTML.transition.settle(300.millis))
105
+ ```
106
+
107
+ ## Scroll Behavior
108
+
109
+ Control where the page scrolls after a swap:
110
+
111
+ **`scroll(position: HxSwap.ScrollPosition)`** adds a `scroll:` modifier. Valid positions are `HxSwap.ScrollPosition.Top` and `HxSwap.ScrollPosition.Bottom`:
112
+
113
+ ```scala
114
+ import zio.blocks.html._
115
+ import zio.http.htmx._
116
+
117
+ // Scroll to top after swapping
118
+ div(hxSwap := HxSwap.InnerHTML.scroll(HxSwap.ScrollPosition.Top))
119
+
120
+ // Scroll to bottom (useful for chat/log appends)
121
+ div(hxSwap := HxSwap.BeforeEnd.scroll(HxSwap.ScrollPosition.Bottom))
122
+ ```
123
+
124
+ **`show(position: HxSwap.ShowPosition)`** adds a `show:` modifier to control where newly swapped content becomes visible. Valid positions are `HxSwap.ShowPosition.Top` and `HxSwap.ShowPosition.Bottom`:
125
+
126
+ ```scala
127
+ import zio.blocks.html._
128
+ import zio.http.htmx._
129
+
130
+ // Show the top of the newly swapped content
131
+ div(hxSwap := HxSwap.InnerHTML.show(HxSwap.ShowPosition.Top))
132
+ ```
133
+
134
+ ## Focus & Title Management
135
+
136
+ **`focusScroll(enabled: Boolean)`** sets the `focusScroll:` modifier to control whether focus moves during swap. Pass `true` to focus the swapped element, `false` to disable focus movement:
137
+
138
+ ```scala
139
+ import zio.blocks.html._
140
+ import zio.http.htmx._
141
+
142
+ // Enable focus scroll (default HTMX behavior)
143
+ div(hxSwap := HxSwap.InnerHTML.focusScroll(true))
144
+
145
+ // Disable automatic focus movement
146
+ div(hxSwap := HxSwap.InnerHTML.focusScroll(false))
147
+ ```
148
+
149
+ **`ignoreTitle: HxSwap`** enables the `ignoreTitle:true` modifier, preventing HTMX from updating the document title if the response contains an `<title>` tag:
150
+
151
+ ```scala
152
+ import zio.blocks.html._
153
+ import zio.http.htmx._
154
+
155
+ // Don't update the page title even if response has one
156
+ div(hxSwap := HxSwap.InnerHTML.ignoreTitle)
157
+ ```
158
+
159
+ ## Rendering & Parsing
160
+
161
+ **`render: String`** produces the literal `hx-swap` attribute value string:
162
+
163
+ ```scala
164
+ import zio.http.htmx._
165
+ import scala.concurrent.duration._
166
+
167
+ val swap = HxSwap.InnerHTML.swap(1.second).settle(500.millis).transition
168
+ swap.render // "innerHTML swap:1s settle:500ms transition:true"
169
+ ```
170
+
171
+ **`HxSwap.parse(value: String): Either[String, HxSwap]`** decodes a rendered HTMX swap string into a typed `HxSwap` value. The parser accepts the same format that `render` produces:
172
+
173
+ ```scala
174
+ import zio.http.htmx._
175
+
176
+ val parsed = HxSwap.parse("outerHTML swap:2s settle:250ms transition:true scroll:bottom")
177
+ // Right(HxSwap.OuterHTML.swap(2 seconds).settle(250 millis).transition.scroll(ScrollPosition.Bottom))
178
+ ```
179
+
180
+ Parse failures return a descriptive `Left` with the error reason:
181
+
182
+ ```scala
183
+ import zio.http.htmx._
184
+
185
+ HxSwap.parse("") // Left("Empty HTMX swap value")
186
+ HxSwap.parse("bogus") // Left("Unknown HTMX swap strategy: bogus")
187
+ HxSwap.parse("innerHTML swap:later") // Left("Unsupported HTMX duration: later")
188
+ HxSwap.parse("innerHTML scroll:middle") // Left("Invalid HTMX scroll position: middle")
189
+ ```
190
+
191
+ ## Common Patterns
192
+
193
+ Effective swap strategies enhance user experience through proper timing and animation. Here are practical usage patterns:
194
+
195
+ ### Fade-In New Content
196
+
197
+ Combine `HxSwap#transition` with `HxSwap#settle` delay to fade in new elements:
198
+
199
+ ```scala
200
+ import zio.blocks.html._
201
+ import zio.http.htmx._
202
+ import scala.concurrent.duration._
203
+
204
+ div(
205
+ hxGet := "/new-items",
206
+ hxSwap := HxSwap.BeforeEnd.transition.settle(300.millis)
207
+ )
208
+ ```
209
+
210
+ ### Scroll New Content Into View
211
+
212
+ Use `HxSwap#scroll` with `BeforeEnd` or `AfterEnd` to append content and scroll to it:
213
+
214
+ ```scala
215
+ import zio.blocks.html._
216
+ import zio.http.htmx._
217
+ import scala.concurrent.duration._
218
+
219
+ div(
220
+ id := "messages",
221
+ hxGet := "/new-messages",
222
+ hxTrigger := HxTrigger.every(2.seconds),
223
+ hxSwap := HxSwap.BeforeEnd.scroll(HxSwap.ScrollPosition.Bottom)
224
+ )
225
+ ```
226
+
227
+ ### Replace and Focus
228
+
229
+ Use `InnerHTML` or `OuterHTML` with `HxSwap#focusScroll` to replace content and move focus:
230
+
231
+ ```scala
232
+ import zio.blocks.html._
233
+ import zio.http.htmx._
234
+
235
+ form(
236
+ hxPost := "/submit",
237
+ hxSwap := HxSwap.OuterHTML.focusScroll(true),
238
+ button("Submit")
239
+ )
240
+ ```
241
+
242
+ ### Preserve Animations While Settling
243
+
244
+ Increase the `HxSwap#settle` delay to let CSS animations complete before HTMX considers the swap done:
245
+
246
+ ```scala
247
+ import zio.blocks.html._
248
+ import zio.http.htmx._
249
+ import scala.concurrent.duration._
250
+
251
+ // CSS animations run for 500ms; settle after they complete
252
+ div(
253
+ hxPost := "/update",
254
+ hxSwap := HxSwap.InnerHTML.transition.settle(500.millis)
255
+ )
256
+ ```
257
+
258
+ ## Integration with Other Module Types
259
+
260
+ `HxSwap` is most often paired with `HxTrigger` (when to request) and `HxTarget` (where to apply the swap):
261
+
262
+ ```scala
263
+ import zio.blocks.html._
264
+ import zio.http.htmx._
265
+ import scala.concurrent.duration._
266
+
267
+ button(
268
+ hxPost := "/api/action",
269
+ hxTrigger := HxTrigger.click.delay(100.millis),
270
+ hxTarget := HxTarget.closest("form"),
271
+ hxSwap := HxSwap.InnerHTML.settle(250.millis),
272
+ "Perform Action"
273
+ )
274
+ ```
275
+
276
+ The `ToHtmxValue[HxSwap]` type class renders automatically, enabling you to assign `HxSwap` values directly using the `:=` operator on the `hxSwap` attribute key.
@@ -0,0 +1,251 @@
1
+ ---
2
+ id: hx-sync
3
+ title: HxSync
4
+ ---
5
+
6
+ `HxSync` represents the `hx-sync` attribute, coordinating multiple HTMX requests by specifying how new requests interact with pending or running requests on a target element. This prevents race conditions and ensures predictable behavior when multiple events fire in quick succession.
7
+
8
+ Create an `HxSync` by pairing an `HxTarget` (which element to synchronize) with a `HxSyncStrategy` (how to handle conflicts):
9
+
10
+ ```scala
11
+ import zio.http.htmx._
12
+
13
+ // Wait for this element's request to finish before starting a new one
14
+ HxSync(HxTarget.This, HxSyncStrategy.Queue)
15
+
16
+ // Replace a pending request with a new one
17
+ HxSync(HxTarget.closest("form"), HxSyncStrategy.Replace)
18
+
19
+ // Drop new requests while one is in flight
20
+ HxSync(HxTarget.This, HxSyncStrategy.Drop)
21
+
22
+ // Abort the current request and start the new one immediately
23
+ HxSync(HxTarget.This, HxSyncStrategy.Abort)
24
+ ```
25
+
26
+ ## Sync Strategies
27
+
28
+ **`HxSyncStrategy.Queue`** queues new requests, executing them after the current request completes. This is the default HTMX behavior when `hx-sync` is not specified. Useful for ensuring requests are processed in order:
29
+
30
+ ```scala
31
+ import zio.blocks.html._
32
+ import zio.http.htmx._
33
+
34
+ input(
35
+ hxPost := "/search",
36
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Queue),
37
+ placeholder := "Search..."
38
+ )
39
+ ```
40
+
41
+ **`HxSyncStrategy.Replace`** cancels any pending request and immediately sends the new one. Useful for searches and filters where the latest value is all that matters:
42
+
43
+ ```scala
44
+ import zio.blocks.html._
45
+ import zio.http.htmx._
46
+
47
+ input(
48
+ hxPost := "/search",
49
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Replace),
50
+ placeholder := "Live Search (only latest request sent)"
51
+ )
52
+ ```
53
+
54
+ **`HxSyncStrategy.Drop`** discards new requests while one is in flight. Only the first request in a burst is sent; others are ignored. Useful for expensive operations that shouldn't be duplicated:
55
+
56
+ ```scala
57
+ import zio.blocks.html._
58
+ import zio.http.htmx._
59
+
60
+ button(
61
+ hxPost := "/expensive-operation",
62
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Drop),
63
+ "Process (only first request sent)"
64
+ )
65
+ ```
66
+
67
+ **`HxSyncStrategy.Abort`** cancels any in-flight request and immediately sends the new one. The old request's response is discarded. Useful for time-sensitive operations:
68
+
69
+ ```scala
70
+ import zio.blocks.html._
71
+ import zio.http.htmx._
72
+
73
+ button(
74
+ hxPost := "/time-sensitive-action",
75
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Abort),
76
+ "Execute (cancels previous)"
77
+ )
78
+ ```
79
+
80
+ ## Target Specification
81
+
82
+ The first parameter of `HxSync` is an `HxTarget` specifying which element to synchronize. This allows coordinating requests across multiple elements:
83
+
84
+ **Synchronize the current element:**
85
+
86
+ Use `HxTarget.This` to synchronize the element that initiates the request:
87
+
88
+ ```scala
89
+ import zio.blocks.html._
90
+ import zio.http.htmx._
91
+
92
+ input(
93
+ hxPost := "/search",
94
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Replace),
95
+ placeholder := "Search..."
96
+ )
97
+ ```
98
+
99
+ **Synchronize a parent form:**
100
+
101
+ Synchronize across all fields in a form using `HxTarget.closest()`:
102
+
103
+ ```scala
104
+ import zio.blocks.html._
105
+ import zio.http.htmx._
106
+
107
+ input(
108
+ hxPost := "/validate",
109
+ hxSync := HxSync(HxTarget.closest("form"), HxSyncStrategy.Queue),
110
+ placeholder := "Email"
111
+ )
112
+ ```
113
+
114
+ **Synchronize multiple fields at once:**
115
+
116
+ Apply the same sync strategy to multiple fields within a form:
117
+
118
+ ```scala
119
+ import zio.blocks.html._
120
+ import zio.http.htmx._
121
+
122
+ form(
123
+ input(
124
+ name := "firstName",
125
+ hxPost := "/validate",
126
+ hxSync := HxSync(HxTarget.closest("form"), HxSyncStrategy.Queue)
127
+ ),
128
+ input(
129
+ name := "email",
130
+ hxPost := "/validate",
131
+ hxSync := HxSync(HxTarget.closest("form"), HxSyncStrategy.Queue)
132
+ )
133
+ )
134
+ ```
135
+
136
+ ## Rendering & Parsing
137
+
138
+ **`render: String`** produces the literal `hx-sync` attribute value string:
139
+
140
+ ```scala
141
+ import zio.http.htmx._
142
+
143
+ HxSync(HxTarget.This, HxSyncStrategy.Queue).render // "this:queue"
144
+ HxSync(HxTarget.closest("form"), HxSyncStrategy.Replace).render // "closest form:replace"
145
+ ```
146
+
147
+ **`HxSync.parse(value: String): Either[String, HxSync]`** parses a rendered string back into a typed `HxSync`:
148
+
149
+ ```scala
150
+ import zio.http.htmx._
151
+
152
+ HxSync.parse("this:queue") // Right(HxSync(HxTarget.This, HxSyncStrategy.Queue))
153
+ HxSync.parse("closest form:replace") // Right(HxSync(HxTarget.closest("form"), HxSyncStrategy.Replace))
154
+ ```
155
+
156
+ ## Common Patterns
157
+
158
+ Synchronization strategies prevent race conditions in interactive forms. Here are practical usage patterns:
159
+
160
+ ### Search with Last-Value-Wins
161
+
162
+ Use `Replace` to ensure only the most recent search is sent when typing quickly:
163
+
164
+ ```scala
165
+ import zio.blocks.html._
166
+ import zio.http.htmx._
167
+ import scala.concurrent.duration._
168
+
169
+ input(
170
+ hxPost := "/search",
171
+ hxTrigger := HxTrigger.input.delay(300.millis),
172
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Replace),
173
+ hxTarget := HxTarget.css("#results"),
174
+ placeholder := "Search (only latest request sent)"
175
+ )
176
+ ```
177
+
178
+ ### Ordered Form Validation
179
+
180
+ Use `Queue` to validate form fields in the order they were changed, preventing race conditions:
181
+
182
+ ```scala
183
+ import zio.blocks.html._
184
+ import zio.http.htmx._
185
+
186
+ form(
187
+ input(
188
+ name := "username",
189
+ hxPost := "/validate/username",
190
+ hxSync := HxSync(HxTarget.closest("form"), HxSyncStrategy.Queue)
191
+ ),
192
+ input(
193
+ name := "email",
194
+ hxPost := "/validate/email",
195
+ hxSync := HxSync(HxTarget.closest("form"), HxSyncStrategy.Queue)
196
+ )
197
+ )
198
+ ```
199
+
200
+ ### Prevent Double-Submit
201
+
202
+ Use `Drop` to prevent duplicate submissions when users click a button repeatedly:
203
+
204
+ ```scala
205
+ import zio.blocks.html._
206
+ import zio.http.htmx._
207
+
208
+ form(
209
+ button(
210
+ hxPost := "/submit",
211
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Drop),
212
+ "Submit (first click only)"
213
+ )
214
+ )
215
+ ```
216
+
217
+ ### Abort Slow Requests
218
+
219
+ Use `Abort` for requests that shouldn't queue up, replacing old requests immediately:
220
+
221
+ ```scala
222
+ import zio.blocks.html._
223
+ import zio.http.htmx._
224
+
225
+ button(
226
+ hxPost := "/fetch-latest",
227
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Abort),
228
+ "Fetch Latest (aborts previous)"
229
+ )
230
+ ```
231
+
232
+ ## Integration with Other Attributes
233
+
234
+ `HxSync` works alongside trigger and timing attributes to coordinate complex request patterns:
235
+
236
+ ```scala
237
+ import zio.blocks.html._
238
+ import zio.http.htmx._
239
+ import scala.concurrent.duration._
240
+
241
+ input(
242
+ hxPost := "/search",
243
+ hxTrigger := HxTrigger.input.delay(500.millis).changed,
244
+ hxSync := HxSync(HxTarget.This, HxSyncStrategy.Replace),
245
+ hxParams := HxParams.only("query"),
246
+ hxTarget := HxTarget.css("#results"),
247
+ placeholder := "Search"
248
+ )
249
+ ```
250
+
251
+ The `ToHtmxValue[HxSync]` instance renders automatically, so `HxSync` values work seamlessly with the `hxSync` attribute key.