acrylblends 0.6.2 → 0.6.3

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 (181) hide show
  1. package/README.md +4 -1
  2. package/docs/framework/README.md +3 -2
  3. package/docs/framework/UPSTREAM.md +1 -1
  4. package/docs/framework/cordis/acryl-in-a-cordis-app.md +6 -0
  5. package/docs/framework/getting-started/basic.md +1 -0
  6. package/docs/framework/getting-started/desktop.md +41 -0
  7. package/docs/framework/guides/jsx-dom.md +0 -3
  8. package/docs/framework/guides/jsx.md +0 -3
  9. package/docs/framework/helpers/css.md +0 -3
  10. package/docs/framework/upstream.json +3 -3
  11. package/lib/accepts.d.ts +1 -1
  12. package/lib/acryl-base.d.ts +1 -1
  13. package/lib/adapter.d.ts +1 -1
  14. package/lib/{app-BdrVHBlJ.d.ts → app-wFwpg-bt.d.ts} +1 -1
  15. package/lib/base-BpQy2Rk7.d.ts +783 -0
  16. package/lib/base-ddiVmiE7.js +721 -0
  17. package/lib/basic-auth.d.ts +1 -1
  18. package/lib/bearer-auth.d.ts +1 -1
  19. package/lib/body-limit.d.ts +1 -1
  20. package/lib/cache.d.ts +1 -1
  21. package/lib/client.d.ts +2 -2
  22. package/lib/combine.d.ts +1 -1
  23. package/lib/common-D8Z4JRCw.d.ts +46 -0
  24. package/lib/components-Ce5FRyHU.js +268 -0
  25. package/lib/components-DYZdxJIJ.js +24 -0
  26. package/lib/compress.d.ts +1 -1
  27. package/lib/conninfo.d.ts +2 -2
  28. package/lib/constants-BR9jM9BC.js +9 -0
  29. package/lib/{context-B5Y1lLha.d.ts → context-DVQBjuns.d.ts} +1 -1
  30. package/lib/context-DxFdY_un.d.ts +250 -0
  31. package/lib/context-storage.d.ts +1 -1
  32. package/lib/{cookie-D9_nQbsM.d.ts → cookie-CCelEqky.d.ts} +1 -1
  33. package/lib/cookie.d.ts +3 -3
  34. package/lib/cors.d.ts +1 -1
  35. package/lib/csrf.d.ts +1 -1
  36. package/lib/css-D2jb7X8b.js +320 -0
  37. package/lib/css.d.ts +82 -0
  38. package/lib/css.js +134 -0
  39. package/lib/dev.d.ts +2 -2
  40. package/lib/etag.d.ts +1 -1
  41. package/lib/factory.d.ts +2 -2
  42. package/lib/hooks-Bq5q9IMl.js +762 -0
  43. package/lib/html-SllAe0YT.d.ts +46 -0
  44. package/lib/html.d.ts +1 -1
  45. package/lib/index-C850MeBA.d.ts +94 -0
  46. package/lib/{index-m12zcmph.d.ts → index-CUTfU69R.d.ts} +3 -3
  47. package/lib/{index-DtEoak1i.d.ts → index-Cqcdluyn.d.ts} +2 -2
  48. package/lib/{index-Kqou8Fjh.d.ts → index-CsvB-F9j.d.ts} +2 -2
  49. package/lib/index.d.ts +4 -4
  50. package/lib/ip-restriction.d.ts +2 -2
  51. package/lib/jsx/dom/client.d.ts +30 -0
  52. package/lib/jsx/dom/client.js +49 -0
  53. package/lib/jsx/dom/css.d.ts +84 -0
  54. package/lib/jsx/dom/css.js +2 -0
  55. package/lib/jsx/dom/jsx-dev-runtime.d.ts +3 -0
  56. package/lib/jsx/dom/jsx-dev-runtime.js +2 -0
  57. package/lib/jsx/dom/jsx-runtime.d.ts +3 -0
  58. package/lib/jsx/dom/jsx-runtime.js +2 -0
  59. package/lib/jsx/dom/server.d.ts +105 -0
  60. package/lib/jsx/dom/server.js +37 -0
  61. package/lib/jsx/dom.d.ts +5 -0
  62. package/lib/jsx/dom.js +74 -0
  63. package/lib/jsx/jsx-dev-runtime.d.ts +6 -0
  64. package/lib/jsx/jsx-dev-runtime.js +14 -0
  65. package/lib/jsx/jsx-runtime.d.ts +10 -0
  66. package/lib/jsx/jsx-runtime.js +30 -0
  67. package/lib/jsx/streaming.d.ts +2 -0
  68. package/lib/jsx/streaming.js +2 -0
  69. package/lib/jsx-dev-runtime-B22O-Npk.d.ts +7 -0
  70. package/lib/jsx-dev-runtime-kAhR_4C7.js +354 -0
  71. package/lib/jsx-runtime-BVVgDSdq.d.ts +1 -0
  72. package/lib/jsx.d.ts +3 -0
  73. package/lib/jsx.js +47 -0
  74. package/lib/jwk.d.ts +4 -4
  75. package/lib/{jwt-BA65QhnJ.d.ts → jwt-BARhotDz.d.ts} +1 -1
  76. package/lib/jwt.d.ts +4 -4
  77. package/lib/language.d.ts +1 -1
  78. package/lib/logger.d.ts +1 -1
  79. package/lib/loopback-only.d.ts +1 -1
  80. package/lib/method-not-allowed.d.ts +2 -2
  81. package/lib/method-override.d.ts +2 -2
  82. package/lib/mount.d.ts +1 -1
  83. package/lib/node-server/conninfo.d.ts +1 -1
  84. package/lib/node-server/serve-static.d.ts +1 -1
  85. package/lib/node-server/serve-static.js +1 -1
  86. package/lib/node-server.d.ts +1 -1
  87. package/lib/node-server.js +1 -1
  88. package/lib/powered-by.d.ts +1 -1
  89. package/lib/pretty-json.d.ts +1 -1
  90. package/lib/proxy.d.ts +1 -1
  91. package/lib/quick.d.ts +1 -1
  92. package/lib/request-id.d.ts +1 -1
  93. package/lib/request.d.ts +1 -1
  94. package/lib/route.d.ts +1 -1
  95. package/lib/schema-validator.d.ts +1 -1
  96. package/lib/secure-headers.d.ts +1 -1
  97. package/lib/{serve-static-Dw_m6KxR.js → serve-static-Cw23shXv.js} +1 -0
  98. package/lib/serve-static.d.ts +1 -1
  99. package/lib/streaming.d.ts +1 -1
  100. package/lib/testing.d.ts +4 -4
  101. package/lib/timeout.d.ts +1 -1
  102. package/lib/timing.d.ts +1 -1
  103. package/lib/tiny.d.ts +1 -1
  104. package/lib/trailing-slash.d.ts +1 -1
  105. package/lib/{types-Bd29gh2o.d.ts → types-CQiGmsWW.d.ts} +3 -3
  106. package/lib/{types-D9DEXMjE.d.ts → types-Lqx5BxQF.d.ts} +8 -1
  107. package/lib/{types-ZM9gIiKh.d.ts → types-nEL8VPDC.d.ts} +1 -1
  108. package/lib/types.d.ts +1 -1
  109. package/lib/utils/body.d.ts +1 -1
  110. package/lib/utils/cookie.d.ts +1 -1
  111. package/lib/utils/crypto.d.ts +1 -1
  112. package/lib/utils/html.d.ts +1 -45
  113. package/lib/utils/ipaddr.d.ts +1 -1
  114. package/lib/utils/jwt.d.ts +1 -1
  115. package/lib/utils/types.d.ts +2 -2
  116. package/lib/validator.d.ts +2 -2
  117. package/lib/ws.d.ts +1 -1
  118. package/package.json +45 -1
  119. package/plugins/acryl-inspect/upstream.json +1 -1
  120. package/plugins/acryl-node-server/upstream.json +1 -1
  121. package/plugins/acryl-router/src/core/helper/css/common.case.test.tsx +506 -0
  122. package/plugins/acryl-router/src/core/helper/css/common.ts +337 -0
  123. package/plugins/acryl-router/src/core/helper/css/index.test.tsx +678 -0
  124. package/plugins/acryl-router/src/core/helper/css/index.ts +262 -0
  125. package/plugins/acryl-router/src/core/helper/streaming/sse.test.tsx +557 -0
  126. package/plugins/acryl-router/src/core/jsx/base.test.tsx +97 -0
  127. package/plugins/acryl-router/src/core/jsx/base.ts +490 -0
  128. package/plugins/acryl-router/src/core/jsx/children.test.ts +52 -0
  129. package/plugins/acryl-router/src/core/jsx/children.ts +20 -0
  130. package/plugins/acryl-router/src/core/jsx/components.test.tsx +936 -0
  131. package/plugins/acryl-router/src/core/jsx/components.ts +257 -0
  132. package/plugins/acryl-router/src/core/jsx/constants.ts +6 -0
  133. package/plugins/acryl-router/src/core/jsx/context-isolation.test.ts +655 -0
  134. package/plugins/acryl-router/src/core/jsx/context.ts +266 -0
  135. package/plugins/acryl-router/src/core/jsx/dom/client.test.tsx +128 -0
  136. package/plugins/acryl-router/src/core/jsx/dom/client.ts +89 -0
  137. package/plugins/acryl-router/src/core/jsx/dom/components.test.tsx +330 -0
  138. package/plugins/acryl-router/src/core/jsx/dom/components.ts +39 -0
  139. package/plugins/acryl-router/src/core/jsx/dom/context.test.tsx +184 -0
  140. package/plugins/acryl-router/src/core/jsx/dom/context.ts +52 -0
  141. package/plugins/acryl-router/src/core/jsx/dom/css.test.tsx +149 -0
  142. package/plugins/acryl-router/src/core/jsx/dom/css.ts +263 -0
  143. package/plugins/acryl-router/src/core/jsx/dom/hooks/index.test.tsx +172 -0
  144. package/plugins/acryl-router/src/core/jsx/dom/hooks/index.ts +91 -0
  145. package/plugins/acryl-router/src/core/jsx/dom/index.test.tsx +3681 -0
  146. package/plugins/acryl-router/src/core/jsx/dom/index.ts +170 -0
  147. package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.test.tsx +1344 -0
  148. package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.ts +438 -0
  149. package/plugins/acryl-router/src/core/jsx/dom/jsx-dev-runtime.ts +23 -0
  150. package/plugins/acryl-router/src/core/jsx/dom/jsx-runtime.ts +8 -0
  151. package/plugins/acryl-router/src/core/jsx/dom/render.ts +879 -0
  152. package/plugins/acryl-router/src/core/jsx/dom/server.test.tsx +199 -0
  153. package/plugins/acryl-router/src/core/jsx/dom/server.ts +76 -0
  154. package/plugins/acryl-router/src/core/jsx/dom/utils.ts +7 -0
  155. package/plugins/acryl-router/src/core/jsx/hooks/dom.test.tsx +859 -0
  156. package/plugins/acryl-router/src/core/jsx/hooks/index.ts +437 -0
  157. package/plugins/acryl-router/src/core/jsx/hooks/string.test.tsx +40 -0
  158. package/plugins/acryl-router/src/core/jsx/index.test.tsx +1744 -0
  159. package/plugins/acryl-router/src/core/jsx/index.ts +115 -0
  160. package/plugins/acryl-router/src/core/jsx/intrinsic-element/common.ts +23 -0
  161. package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.test.tsx +666 -0
  162. package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.ts +210 -0
  163. package/plugins/acryl-router/src/core/jsx/intrinsic-elements.ts +938 -0
  164. package/plugins/acryl-router/src/core/jsx/jsx-dev-runtime.ts +26 -0
  165. package/plugins/acryl-router/src/core/jsx/jsx-runtime.test.tsx +85 -0
  166. package/plugins/acryl-router/src/core/jsx/jsx-runtime.ts +51 -0
  167. package/plugins/acryl-router/src/core/jsx/streaming.test.tsx +1281 -0
  168. package/plugins/acryl-router/src/core/jsx/streaming.ts +231 -0
  169. package/plugins/acryl-router/src/core/jsx/types.ts +41 -0
  170. package/plugins/acryl-router/src/core/jsx/utils.test.ts +366 -0
  171. package/plugins/acryl-router/src/core/jsx/utils.ts +202 -0
  172. package/plugins/acryl-router/src/core/utils/types.ts +6 -0
  173. package/plugins/acryl-router/src/helpers/index.ts +1 -0
  174. package/plugins/acryl-router/tests/jsx.spec.tsx +63 -0
  175. package/plugins/acryl-router/upstream.json +3 -3
  176. package/plugins/acryl-router-http/upstream.json +1 -1
  177. package/plugins/acryl-state/upstream.json +1 -1
  178. package/plugins/desktop/upstream.json +1 -1
  179. package/plugins/desktop-web/upstream.json +1 -1
  180. package/plugins/save/plugin.json +1 -1
  181. package/templates/todo/upstream.json +1 -1
@@ -0,0 +1,438 @@
1
+ import type { Props } from '../../base.ts'
2
+ import { useContext } from '../../context.ts'
3
+ import { use, useCallback, useMemo, useState } from '../../hooks/index.ts'
4
+ import {
5
+ dataPrecedenceAttr,
6
+ deDupeKeyMap,
7
+ domRenderers,
8
+ isStylesheetLinkWithPrecedence,
9
+ shouldDeDupeByKey,
10
+ } from '../../intrinsic-element/common.ts'
11
+ import type { IntrinsicElements } from '../../intrinsic-elements.ts'
12
+ import type { FC, JSXNode, PropsWithChildren, RefObject } from '../../types.ts'
13
+ import { FormContext, registerAction } from '../hooks/index.ts'
14
+ import type { PreserveNodeType } from '../render.ts'
15
+ import { createPortal, getNameSpaceContext } from '../render.ts'
16
+
17
+ // this function is a testing utility and should not be exported to the user
18
+ export const clearCache = () => {
19
+ blockingPromiseMap = Object.create(null)
20
+ createdElements = Object.create(null)
21
+ }
22
+
23
+ // this function is exported for testing and should not be used by the user
24
+ export const composeRef = <T>(
25
+ ref: RefObject<T | null> | Function | undefined,
26
+ cb: (e: T) => void | (() => void)
27
+ ): ((e: T) => () => void) => {
28
+ return useMemo(
29
+ () => (e: T) => {
30
+ let refCleanup: (() => void) | undefined
31
+ if (ref) {
32
+ if (typeof ref === 'function') {
33
+ refCleanup =
34
+ ref(e) ||
35
+ (() => {
36
+ ref(null)
37
+ })
38
+ } else if (ref && 'current' in ref) {
39
+ ref.current = e
40
+ refCleanup = () => {
41
+ ref.current = null
42
+ }
43
+ }
44
+ }
45
+
46
+ const cbCleanup = cb(e)
47
+ return () => {
48
+ cbCleanup?.()
49
+ refCleanup?.()
50
+ }
51
+ },
52
+ [ref]
53
+ )
54
+ }
55
+
56
+ let blockingPromiseMap: Record<string, Promise<Event> | undefined> = Object.create(null)
57
+ let createdElements: Record<string, HTMLElement> = Object.create(null)
58
+ const documentMetadataTag = (
59
+ tag: string,
60
+ props: Props,
61
+ preserveNodeType: PreserveNodeType | undefined,
62
+ supportSort: boolean,
63
+ supportBlocking: boolean
64
+ ) => {
65
+ if (props?.itemProp) {
66
+ return {
67
+ tag,
68
+ props,
69
+ type: tag,
70
+ ref: props.ref,
71
+ }
72
+ }
73
+
74
+ const head = document.head
75
+
76
+ let { onLoad, onError, precedence, blocking, ...restProps } = props
77
+ let element: HTMLElement | null = null
78
+ let created = false
79
+
80
+ const deDupeKeys = deDupeKeyMap[tag]
81
+ const deDupeByKey = shouldDeDupeByKey(tag, supportSort)
82
+ const isDeDupeCandidateLink = (e: HTMLElement) =>
83
+ e.getAttribute('rel') === 'stylesheet' && e.getAttribute(dataPrecedenceAttr) !== null
84
+ let existingElements: NodeListOf<HTMLElement> | undefined = undefined
85
+ if (deDupeByKey) {
86
+ const tags = head.querySelectorAll<HTMLElement>(tag)
87
+ LOOP: for (const e of tags) {
88
+ if (tag === 'link' && !isDeDupeCandidateLink(e)) {
89
+ continue
90
+ }
91
+ for (const key of deDupeKeys) {
92
+ if (e.getAttribute(key) === props[key]) {
93
+ element = e
94
+ break LOOP
95
+ }
96
+ }
97
+ }
98
+
99
+ if (!element) {
100
+ const cacheKey = deDupeKeys.reduce(
101
+ (acc, key) => (props[key] === undefined ? acc : `${acc}-${key}-${props[key]}`),
102
+ tag
103
+ )
104
+ created = !createdElements[cacheKey]
105
+ element = createdElements[cacheKey] ||= (() => {
106
+ const e = document.createElement(tag)
107
+ for (const key of deDupeKeys) {
108
+ if (props[key] !== undefined) {
109
+ e.setAttribute(key, props[key] as string)
110
+ }
111
+ }
112
+ if (props.rel) {
113
+ e.setAttribute('rel', props.rel)
114
+ }
115
+ return e
116
+ })()
117
+ }
118
+ } else {
119
+ existingElements = head.querySelectorAll<HTMLElement>(tag)
120
+ }
121
+
122
+ precedence = supportSort ? (precedence ?? '') : undefined
123
+ if (supportSort) {
124
+ restProps[dataPrecedenceAttr] = precedence
125
+ }
126
+
127
+ const insert = useCallback(
128
+ (e: HTMLElement) => {
129
+ if (deDupeByKey) {
130
+ if (tag === 'link' && precedence !== undefined) {
131
+ let found = false
132
+ for (const existingElement of head.querySelectorAll<HTMLElement>(tag)) {
133
+ const existingPrecedence = existingElement.getAttribute(dataPrecedenceAttr)
134
+ if (existingPrecedence === null) {
135
+ head.insertBefore(e, existingElement)
136
+ return
137
+ }
138
+ if (found && existingPrecedence !== precedence) {
139
+ head.insertBefore(e, existingElement)
140
+ return
141
+ }
142
+ if (existingPrecedence === precedence) {
143
+ found = true
144
+ }
145
+ }
146
+
147
+ // if sentinel is not found, append to the end
148
+ head.appendChild(e)
149
+ return
150
+ }
151
+
152
+ let found = false
153
+ for (const existingElement of head.querySelectorAll<HTMLElement>(tag)) {
154
+ if (found && existingElement.getAttribute(dataPrecedenceAttr) !== precedence) {
155
+ head.insertBefore(e, existingElement)
156
+ return
157
+ }
158
+ if (existingElement.getAttribute(dataPrecedenceAttr) === precedence) {
159
+ found = true
160
+ }
161
+ }
162
+
163
+ // if sentinel is not found, append to the end
164
+ head.appendChild(e)
165
+ } else if (tag === 'link') {
166
+ if (!head.contains(e)) {
167
+ head.appendChild(e)
168
+ }
169
+ } else if (existingElements) {
170
+ let found = false
171
+ for (const existingElement of existingElements!) {
172
+ if (existingElement === e) {
173
+ found = true
174
+ break
175
+ }
176
+ }
177
+ if (!found) {
178
+ // newly created element
179
+ head.insertBefore(
180
+ e,
181
+ head.contains(existingElements[0]) ? existingElements[0] : head.querySelector(tag)
182
+ )
183
+ }
184
+ existingElements = undefined
185
+ }
186
+ },
187
+ [deDupeByKey, precedence, tag]
188
+ )
189
+
190
+ const ref = composeRef(props.ref, (e: HTMLElement) => {
191
+ const key = deDupeKeys[0]
192
+
193
+ if (preserveNodeType === 2) {
194
+ e.innerHTML = ''
195
+ }
196
+
197
+ if (created || existingElements) {
198
+ insert(e)
199
+ }
200
+
201
+ if (!onError && !onLoad) {
202
+ return
203
+ }
204
+ if (!key) {
205
+ return
206
+ }
207
+
208
+ let promise = (blockingPromiseMap[e.getAttribute(key) as string] ||= new Promise<Event>(
209
+ (resolve, reject) => {
210
+ e.addEventListener('load', resolve)
211
+ e.addEventListener('error', reject)
212
+ }
213
+ ))
214
+ if (onLoad) {
215
+ promise = promise.then(onLoad)
216
+ }
217
+ if (onError) {
218
+ promise = promise.catch(onError)
219
+ }
220
+ promise.catch(() => {})
221
+ })
222
+
223
+ if (supportBlocking && blocking === 'render') {
224
+ const key = deDupeKeyMap[tag][0]
225
+ if (key && props[key]) {
226
+ const value = props[key]
227
+ const promise = (blockingPromiseMap[value] ||= new Promise<Event>((resolve, reject) => {
228
+ insert(element as HTMLElement)
229
+ element!.addEventListener('load', resolve)
230
+ element!.addEventListener('error', reject)
231
+ }))
232
+ use(promise)
233
+ }
234
+ }
235
+
236
+ const jsxNode = {
237
+ tag,
238
+ type: tag,
239
+ props: {
240
+ ...restProps,
241
+ ref,
242
+ },
243
+ ref,
244
+ } as unknown as JSXNode & { e?: HTMLElement; p?: PreserveNodeType }
245
+
246
+ jsxNode.p = preserveNodeType // preserve for unmounting
247
+ if (element) {
248
+ jsxNode.e = element
249
+ }
250
+
251
+ return createPortal(
252
+ jsxNode,
253
+ head
254
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
255
+ ) as any
256
+ }
257
+ export const title: FC<PropsWithChildren> = (props) => {
258
+ const nameSpaceContext = getNameSpaceContext()
259
+ const ns = nameSpaceContext && useContext(nameSpaceContext)
260
+ if (ns?.endsWith('svg')) {
261
+ return {
262
+ tag: 'title',
263
+ props,
264
+ type: 'title',
265
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
266
+ ref: (props as any).ref,
267
+ } as unknown as JSXNode
268
+ }
269
+ return documentMetadataTag('title', props, undefined, false, false)
270
+ }
271
+
272
+ export const script: FC<PropsWithChildren<IntrinsicElements['script']>> = (props) => {
273
+ if (!props || ['src', 'async'].some((k) => !props[k])) {
274
+ return {
275
+ tag: 'script',
276
+ props,
277
+ type: 'script',
278
+ ref: props.ref,
279
+ } as unknown as JSXNode
280
+ }
281
+ return documentMetadataTag('script', props, 1, false, true)
282
+ }
283
+
284
+ export const style: FC<PropsWithChildren<IntrinsicElements['style']>> = (props) => {
285
+ if (!props || !['href', 'precedence'].every((k) => k in props)) {
286
+ return {
287
+ tag: 'style',
288
+ props,
289
+ type: 'style',
290
+ ref: props.ref,
291
+ } as unknown as JSXNode
292
+ }
293
+ props['data-href'] = props.href
294
+ delete props.href
295
+ return documentMetadataTag('style', props, 2, true, true)
296
+ }
297
+
298
+ export const link: FC<PropsWithChildren<IntrinsicElements['link']>> = (props) => {
299
+ if (
300
+ !props ||
301
+ ['onLoad', 'onError'].some((k) => k in props) ||
302
+ (props.rel === 'stylesheet' && (!('precedence' in props) || 'disabled' in props))
303
+ ) {
304
+ return {
305
+ tag: 'link',
306
+ props,
307
+ type: 'link',
308
+ ref: props.ref,
309
+ } as unknown as JSXNode
310
+ }
311
+ return documentMetadataTag('link', props, 1, isStylesheetLinkWithPrecedence(props), true)
312
+ }
313
+
314
+ export const meta: FC<PropsWithChildren> = (props) => {
315
+ return documentMetadataTag('meta', props, undefined, false, false)
316
+ }
317
+
318
+ const customEventFormAction = Symbol()
319
+ export const form: FC<
320
+ PropsWithChildren<{
321
+ action?: Function | string
322
+ method?: 'get' | 'post'
323
+ ref?: RefObject<HTMLFormElement | null> | ((e: HTMLFormElement | null) => void | (() => void))
324
+ }>
325
+ > = (props) => {
326
+ const { action, ...restProps } = props
327
+ if (typeof action !== 'function') {
328
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
329
+ ;(restProps as any).action = action
330
+ }
331
+
332
+ const [state, setState] = useState<[FormData | null, boolean]>([null, false]) // [FormData, isDirty]
333
+ const onSubmit = useCallback<(ev: SubmitEvent | CustomEvent) => void>(
334
+ async (ev: SubmitEvent | CustomEvent) => {
335
+ const currentAction = ev.isTrusted
336
+ ? action
337
+ : (ev as CustomEvent).detail[customEventFormAction]
338
+ if (typeof currentAction !== 'function') {
339
+ return
340
+ }
341
+
342
+ ev.preventDefault()
343
+ const formData = new FormData(ev.target as HTMLFormElement)
344
+ setState([formData, true])
345
+ const actionRes = currentAction(formData)
346
+ if (actionRes instanceof Promise) {
347
+ registerAction(actionRes)
348
+ await actionRes
349
+ }
350
+ setState([null, true])
351
+ },
352
+ []
353
+ )
354
+
355
+ const ref = composeRef(props.ref, (el: HTMLFormElement) => {
356
+ el.addEventListener('submit', onSubmit)
357
+ return () => {
358
+ el.removeEventListener('submit', onSubmit)
359
+ }
360
+ })
361
+
362
+ const [data, isDirty] = state
363
+ state[1] = false
364
+ return {
365
+ tag: FormContext as unknown as Function,
366
+ props: {
367
+ value: {
368
+ pending: data !== null,
369
+ data,
370
+ method: data ? 'post' : null,
371
+ action: data ? action : null,
372
+ },
373
+ children: {
374
+ tag: 'form',
375
+ props: {
376
+ ...restProps,
377
+ ref,
378
+ },
379
+ type: 'form',
380
+ ref,
381
+ },
382
+ },
383
+ f: isDirty,
384
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
385
+ } as any
386
+ }
387
+
388
+ const formActionableElement = (
389
+ tag: string,
390
+ {
391
+ formAction,
392
+ ...props
393
+ }: {
394
+ formAction?: Function | string
395
+ ref?: RefObject<HTMLInputElement | null> | ((e: HTMLInputElement) => void | (() => void))
396
+ }
397
+ ) => {
398
+ if (typeof formAction === 'function') {
399
+ const onClick = useCallback<(ev: MouseEvent) => void>((ev: MouseEvent) => {
400
+ ev.preventDefault()
401
+ ;(ev.currentTarget! as HTMLInputElement).form!.dispatchEvent(
402
+ new CustomEvent('submit', { detail: { [customEventFormAction]: formAction } })
403
+ )
404
+ }, [])
405
+
406
+ props.ref = composeRef(props.ref, (el: HTMLInputElement) => {
407
+ el.addEventListener('click', onClick)
408
+ return () => {
409
+ el.removeEventListener('click', onClick)
410
+ }
411
+ })
412
+ }
413
+
414
+ return {
415
+ tag,
416
+ props,
417
+ type: tag,
418
+ ref: props.ref,
419
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
420
+ } as any
421
+ }
422
+
423
+ export const input: FC<PropsWithChildren<IntrinsicElements['input']>> = (props) =>
424
+ formActionableElement('input', props)
425
+
426
+ export const button: FC<PropsWithChildren<IntrinsicElements['button']>> = (props) =>
427
+ formActionableElement('button', props)
428
+
429
+ Object.assign(domRenderers, {
430
+ title,
431
+ script,
432
+ style,
433
+ link,
434
+ meta,
435
+ form,
436
+ input,
437
+ button,
438
+ })
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @module
3
+ * This module provides the `acryl-router/jsx/dom` dev runtime.
4
+ */
5
+
6
+ import type { JSXNode, Props } from '../base.ts'
7
+ export type { JSX } from '../base.ts'
8
+ import * as intrinsicElementTags from './intrinsic-element/components.ts'
9
+
10
+ export const jsxDEV = (tag: string | Function, props: Props, key?: string): JSXNode => {
11
+ if (typeof tag === 'string' && intrinsicElementTags[tag as keyof typeof intrinsicElementTags]) {
12
+ tag = intrinsicElementTags[tag as keyof typeof intrinsicElementTags]
13
+ }
14
+ return {
15
+ tag,
16
+ type: tag,
17
+ props,
18
+ key,
19
+ ref: props.ref,
20
+ } as JSXNode
21
+ }
22
+
23
+ export const Fragment = (props: Record<string, unknown>): JSXNode => jsxDEV('', props, undefined)
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @module
3
+ * This module provides the `acryl-router/jsx/dom` runtime.
4
+ */
5
+
6
+ export { jsxDEV as jsx, Fragment } from './jsx-dev-runtime.ts'
7
+ export { jsxDEV as jsxs } from './jsx-dev-runtime.ts'
8
+ export type { JSX } from './jsx-dev-runtime.ts'