@lo-tek/interfaces 0.0.0-stage → 0.37.0

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 (456) hide show
  1. package/CHANGELOG.md +3254 -0
  2. package/LICENSE +21 -0
  3. package/README.md +129 -2
  4. package/agent/astro.md +400 -0
  5. package/agent/bundles.md +429 -0
  6. package/agent/datastar.md +409 -0
  7. package/agent/html.md +388 -0
  8. package/agent/react.md +440 -0
  9. package/agent/templates.md +472 -0
  10. package/core/lotek-core.wasm +0 -0
  11. package/core/lotek.wasm +0 -0
  12. package/dist/cli.d.ts +12 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +14 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/components/css/accordion/accordion.d.ts +37 -0
  17. package/dist/components/css/accordion/accordion.d.ts.map +1 -0
  18. package/dist/components/css/accordion/accordion.js +31 -0
  19. package/dist/components/css/accordion/accordion.js.map +1 -0
  20. package/dist/components/css/alert/alert.d.ts +35 -0
  21. package/dist/components/css/alert/alert.d.ts.map +1 -0
  22. package/dist/components/css/alert/alert.js +29 -0
  23. package/dist/components/css/alert/alert.js.map +1 -0
  24. package/dist/components/css/avatar/avatar.d.ts +40 -0
  25. package/dist/components/css/avatar/avatar.d.ts.map +1 -0
  26. package/dist/components/css/avatar/avatar.js +30 -0
  27. package/dist/components/css/avatar/avatar.js.map +1 -0
  28. package/dist/components/css/badge/badge.d.ts +24 -0
  29. package/dist/components/css/badge/badge.d.ts.map +1 -0
  30. package/dist/components/css/badge/badge.js +24 -0
  31. package/dist/components/css/badge/badge.js.map +1 -0
  32. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +26 -0
  33. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts.map +1 -0
  34. package/dist/components/css/breadcrumbs/breadcrumbs.js +24 -0
  35. package/dist/components/css/breadcrumbs/breadcrumbs.js.map +1 -0
  36. package/dist/components/css/button/button.d.ts +26 -0
  37. package/dist/components/css/button/button.d.ts.map +1 -0
  38. package/dist/components/css/button/button.js +25 -0
  39. package/dist/components/css/button/button.js.map +1 -0
  40. package/dist/components/css/card/card.d.ts +36 -0
  41. package/dist/components/css/card/card.d.ts.map +1 -0
  42. package/dist/components/css/card/card.js +28 -0
  43. package/dist/components/css/card/card.js.map +1 -0
  44. package/dist/components/css/checkbox/checkbox.d.ts +32 -0
  45. package/dist/components/css/checkbox/checkbox.d.ts.map +1 -0
  46. package/dist/components/css/checkbox/checkbox.js +27 -0
  47. package/dist/components/css/checkbox/checkbox.js.map +1 -0
  48. package/dist/components/css/divider/divider.d.ts +29 -0
  49. package/dist/components/css/divider/divider.d.ts.map +1 -0
  50. package/dist/components/css/divider/divider.js +23 -0
  51. package/dist/components/css/divider/divider.js.map +1 -0
  52. package/dist/components/css/error-text/error-text.d.ts +24 -0
  53. package/dist/components/css/error-text/error-text.d.ts.map +1 -0
  54. package/dist/components/css/error-text/error-text.js +18 -0
  55. package/dist/components/css/error-text/error-text.js.map +1 -0
  56. package/dist/components/css/fieldset/fieldset.d.ts +55 -0
  57. package/dist/components/css/fieldset/fieldset.d.ts.map +1 -0
  58. package/dist/components/css/fieldset/fieldset.js +39 -0
  59. package/dist/components/css/fieldset/fieldset.js.map +1 -0
  60. package/dist/components/css/file-upload/file-upload.d.ts +41 -0
  61. package/dist/components/css/file-upload/file-upload.d.ts.map +1 -0
  62. package/dist/components/css/file-upload/file-upload.js +32 -0
  63. package/dist/components/css/file-upload/file-upload.js.map +1 -0
  64. package/dist/components/css/heading/heading.d.ts +29 -0
  65. package/dist/components/css/heading/heading.d.ts.map +1 -0
  66. package/dist/components/css/heading/heading.js +23 -0
  67. package/dist/components/css/heading/heading.js.map +1 -0
  68. package/dist/components/css/help-text/help-text.d.ts +24 -0
  69. package/dist/components/css/help-text/help-text.d.ts.map +1 -0
  70. package/dist/components/css/help-text/help-text.js +23 -0
  71. package/dist/components/css/help-text/help-text.js.map +1 -0
  72. package/dist/components/css/icon/icon.d.ts +34 -0
  73. package/dist/components/css/icon/icon.d.ts.map +1 -0
  74. package/dist/components/css/icon/icon.js +25 -0
  75. package/dist/components/css/icon/icon.js.map +1 -0
  76. package/dist/components/css/input/input.d.ts +40 -0
  77. package/dist/components/css/input/input.d.ts.map +1 -0
  78. package/dist/components/css/input/input.js +45 -0
  79. package/dist/components/css/input/input.js.map +1 -0
  80. package/dist/components/css/label/label.d.ts +33 -0
  81. package/dist/components/css/label/label.d.ts.map +1 -0
  82. package/dist/components/css/label/label.js +23 -0
  83. package/dist/components/css/label/label.js.map +1 -0
  84. package/dist/components/css/link/link.d.ts +28 -0
  85. package/dist/components/css/link/link.d.ts.map +1 -0
  86. package/dist/components/css/link/link.js +20 -0
  87. package/dist/components/css/link/link.js.map +1 -0
  88. package/dist/components/css/list/list.d.ts +31 -0
  89. package/dist/components/css/list/list.d.ts.map +1 -0
  90. package/dist/components/css/list/list.js +25 -0
  91. package/dist/components/css/list/list.js.map +1 -0
  92. package/dist/components/css/pagination/pagination.d.ts +27 -0
  93. package/dist/components/css/pagination/pagination.d.ts.map +1 -0
  94. package/dist/components/css/pagination/pagination.js +25 -0
  95. package/dist/components/css/pagination/pagination.js.map +1 -0
  96. package/dist/components/css/paragraph/paragraph.d.ts +36 -0
  97. package/dist/components/css/paragraph/paragraph.d.ts.map +1 -0
  98. package/dist/components/css/paragraph/paragraph.js +26 -0
  99. package/dist/components/css/paragraph/paragraph.js.map +1 -0
  100. package/dist/components/css/progress/progress.d.ts +53 -0
  101. package/dist/components/css/progress/progress.d.ts.map +1 -0
  102. package/dist/components/css/progress/progress.js +38 -0
  103. package/dist/components/css/progress/progress.js.map +1 -0
  104. package/dist/components/css/radio/radio.d.ts +32 -0
  105. package/dist/components/css/radio/radio.d.ts.map +1 -0
  106. package/dist/components/css/radio/radio.js +27 -0
  107. package/dist/components/css/radio/radio.js.map +1 -0
  108. package/dist/components/css/search/search.d.ts +34 -0
  109. package/dist/components/css/search/search.d.ts.map +1 -0
  110. package/dist/components/css/search/search.js +32 -0
  111. package/dist/components/css/search/search.js.map +1 -0
  112. package/dist/components/css/select/select.d.ts +40 -0
  113. package/dist/components/css/select/select.d.ts.map +1 -0
  114. package/dist/components/css/select/select.js +31 -0
  115. package/dist/components/css/select/select.js.map +1 -0
  116. package/dist/components/css/shared.d.ts +90 -0
  117. package/dist/components/css/shared.d.ts.map +1 -0
  118. package/dist/components/css/shared.js +167 -0
  119. package/dist/components/css/shared.js.map +1 -0
  120. package/dist/components/css/skeleton/skeleton.d.ts +29 -0
  121. package/dist/components/css/skeleton/skeleton.d.ts.map +1 -0
  122. package/dist/components/css/skeleton/skeleton.js +23 -0
  123. package/dist/components/css/skeleton/skeleton.js.map +1 -0
  124. package/dist/components/css/skip-link/skip-link.d.ts +18 -0
  125. package/dist/components/css/skip-link/skip-link.d.ts.map +1 -0
  126. package/dist/components/css/skip-link/skip-link.js +16 -0
  127. package/dist/components/css/skip-link/skip-link.js.map +1 -0
  128. package/dist/components/css/spinner/spinner.d.ts +33 -0
  129. package/dist/components/css/spinner/spinner.d.ts.map +1 -0
  130. package/dist/components/css/spinner/spinner.js +25 -0
  131. package/dist/components/css/spinner/spinner.js.map +1 -0
  132. package/dist/components/css/sr-only/sr-only.d.ts +17 -0
  133. package/dist/components/css/sr-only/sr-only.d.ts.map +1 -0
  134. package/dist/components/css/sr-only/sr-only.js +15 -0
  135. package/dist/components/css/sr-only/sr-only.js.map +1 -0
  136. package/dist/components/css/switch/switch.d.ts +37 -0
  137. package/dist/components/css/switch/switch.d.ts.map +1 -0
  138. package/dist/components/css/switch/switch.js +29 -0
  139. package/dist/components/css/switch/switch.js.map +1 -0
  140. package/dist/components/css/table/table.d.ts +76 -0
  141. package/dist/components/css/table/table.d.ts.map +1 -0
  142. package/dist/components/css/table/table.js +59 -0
  143. package/dist/components/css/table/table.js.map +1 -0
  144. package/dist/components/css/tag/tag.d.ts +35 -0
  145. package/dist/components/css/tag/tag.d.ts.map +1 -0
  146. package/dist/components/css/tag/tag.js +27 -0
  147. package/dist/components/css/tag/tag.js.map +1 -0
  148. package/dist/components/css/textarea/textarea.d.ts +23 -0
  149. package/dist/components/css/textarea/textarea.d.ts.map +1 -0
  150. package/dist/components/css/textarea/textarea.js +18 -0
  151. package/dist/components/css/textarea/textarea.js.map +1 -0
  152. package/dist/components/css/toggle-group/toggle-group.d.ts +31 -0
  153. package/dist/components/css/toggle-group/toggle-group.d.ts.map +1 -0
  154. package/dist/components/css/toggle-group/toggle-group.js +29 -0
  155. package/dist/components/css/toggle-group/toggle-group.js.map +1 -0
  156. package/dist/components/css/tooltip/tooltip.d.ts +31 -0
  157. package/dist/components/css/tooltip/tooltip.d.ts.map +1 -0
  158. package/dist/components/css/tooltip/tooltip.js +29 -0
  159. package/dist/components/css/tooltip/tooltip.js.map +1 -0
  160. package/dist/components/default-texts.d.ts +35 -0
  161. package/dist/components/default-texts.d.ts.map +1 -0
  162. package/dist/components/default-texts.js +35 -0
  163. package/dist/components/default-texts.js.map +1 -0
  164. package/dist/components/events.d.ts +77 -0
  165. package/dist/components/events.d.ts.map +1 -0
  166. package/dist/components/events.js +3 -0
  167. package/dist/components/events.js.map +1 -0
  168. package/dist/components/frame/dialog/dialog.d.ts +122 -0
  169. package/dist/components/frame/dialog/dialog.d.ts.map +1 -0
  170. package/dist/components/frame/dialog/dialog.js +112 -0
  171. package/dist/components/frame/dialog/dialog.js.map +1 -0
  172. package/dist/components/frame/dialog/lt-dialog.d.ts +104 -0
  173. package/dist/components/frame/dialog/lt-dialog.d.ts.map +1 -0
  174. package/dist/components/frame/dialog/lt-dialog.js +431 -0
  175. package/dist/components/frame/dialog/lt-dialog.js.map +1 -0
  176. package/dist/components/frame/error-summary/error-summary.d.ts +40 -0
  177. package/dist/components/frame/error-summary/error-summary.d.ts.map +1 -0
  178. package/dist/components/frame/error-summary/error-summary.js +32 -0
  179. package/dist/components/frame/error-summary/error-summary.js.map +1 -0
  180. package/dist/components/frame/error-summary/lt-error-summary.d.ts +82 -0
  181. package/dist/components/frame/error-summary/lt-error-summary.d.ts.map +1 -0
  182. package/dist/components/frame/error-summary/lt-error-summary.js +302 -0
  183. package/dist/components/frame/error-summary/lt-error-summary.js.map +1 -0
  184. package/dist/components/frame/field/field-core.d.ts +92 -0
  185. package/dist/components/frame/field/field-core.d.ts.map +1 -0
  186. package/dist/components/frame/field/field-core.js +63 -0
  187. package/dist/components/frame/field/field-core.js.map +1 -0
  188. package/dist/components/frame/field/lt-field.d.ts +173 -0
  189. package/dist/components/frame/field/lt-field.d.ts.map +1 -0
  190. package/dist/components/frame/field/lt-field.js +580 -0
  191. package/dist/components/frame/field/lt-field.js.map +1 -0
  192. package/dist/components/frame/popover/lt-popover.d.ts +132 -0
  193. package/dist/components/frame/popover/lt-popover.d.ts.map +1 -0
  194. package/dist/components/frame/popover/lt-popover.js +490 -0
  195. package/dist/components/frame/popover/lt-popover.js.map +1 -0
  196. package/dist/components/frame/popover/popover.d.ts +49 -0
  197. package/dist/components/frame/popover/popover.d.ts.map +1 -0
  198. package/dist/components/frame/popover/popover.js +64 -0
  199. package/dist/components/frame/popover/popover.js.map +1 -0
  200. package/dist/components/frame/session-timeout/lt-session-timeout.d.ts +198 -0
  201. package/dist/components/frame/session-timeout/lt-session-timeout.d.ts.map +1 -0
  202. package/dist/components/frame/session-timeout/lt-session-timeout.js +573 -0
  203. package/dist/components/frame/session-timeout/lt-session-timeout.js.map +1 -0
  204. package/dist/components/frame/session-timeout/session-timeout.d.ts +113 -0
  205. package/dist/components/frame/session-timeout/session-timeout.d.ts.map +1 -0
  206. package/dist/components/frame/session-timeout/session-timeout.js +101 -0
  207. package/dist/components/frame/session-timeout/session-timeout.js.map +1 -0
  208. package/dist/components/frame/suggestion/lt-suggestion.d.ts +361 -0
  209. package/dist/components/frame/suggestion/lt-suggestion.d.ts.map +1 -0
  210. package/dist/components/frame/suggestion/lt-suggestion.js +1152 -0
  211. package/dist/components/frame/suggestion/lt-suggestion.js.map +1 -0
  212. package/dist/components/frame/suggestion/suggestion.d.ts +143 -0
  213. package/dist/components/frame/suggestion/suggestion.d.ts.map +1 -0
  214. package/dist/components/frame/suggestion/suggestion.js +127 -0
  215. package/dist/components/frame/suggestion/suggestion.js.map +1 -0
  216. package/dist/components/frame/tabs/lt-tabs.d.ts +404 -0
  217. package/dist/components/frame/tabs/lt-tabs.d.ts.map +1 -0
  218. package/dist/components/frame/tabs/lt-tabs.js +1201 -0
  219. package/dist/components/frame/tabs/lt-tabs.js.map +1 -0
  220. package/dist/components/frame/tabs/tabs.d.ts +112 -0
  221. package/dist/components/frame/tabs/tabs.d.ts.map +1 -0
  222. package/dist/components/frame/tabs/tabs.js +107 -0
  223. package/dist/components/frame/tabs/tabs.js.map +1 -0
  224. package/dist/components/host-element.d.ts +135 -0
  225. package/dist/components/host-element.d.ts.map +1 -0
  226. package/dist/components/host-element.js +366 -0
  227. package/dist/components/host-element.js.map +1 -0
  228. package/dist/components/standalone/connection-status/connection-status.d.ts +28 -0
  229. package/dist/components/standalone/connection-status/connection-status.d.ts.map +1 -0
  230. package/dist/components/standalone/connection-status/connection-status.js +22 -0
  231. package/dist/components/standalone/connection-status/connection-status.js.map +1 -0
  232. package/dist/components/standalone/connection-status/lt-connection-status.d.ts +81 -0
  233. package/dist/components/standalone/connection-status/lt-connection-status.d.ts.map +1 -0
  234. package/dist/components/standalone/connection-status/lt-connection-status.js +216 -0
  235. package/dist/components/standalone/connection-status/lt-connection-status.js.map +1 -0
  236. package/dist/components/standalone/toast/lt-toast.d.ts +109 -0
  237. package/dist/components/standalone/toast/lt-toast.d.ts.map +1 -0
  238. package/dist/components/standalone/toast/lt-toast.js +256 -0
  239. package/dist/components/standalone/toast/lt-toast.js.map +1 -0
  240. package/dist/components/standalone/toast/toast.d.ts +52 -0
  241. package/dist/components/standalone/toast/toast.d.ts.map +1 -0
  242. package/dist/components/standalone/toast/toast.js +42 -0
  243. package/dist/components/standalone/toast/toast.js.map +1 -0
  244. package/dist/diagnostics/core.d.ts +137 -0
  245. package/dist/diagnostics/core.d.ts.map +1 -0
  246. package/dist/diagnostics/core.js +72 -0
  247. package/dist/diagnostics/core.js.map +1 -0
  248. package/dist/diagnostics/default-core.d.ts +10 -0
  249. package/dist/diagnostics/default-core.d.ts.map +1 -0
  250. package/dist/diagnostics/default-core.js +72 -0
  251. package/dist/diagnostics/default-core.js.map +1 -0
  252. package/dist/diagnostics/index.d.ts +84 -0
  253. package/dist/diagnostics/index.d.ts.map +1 -0
  254. package/dist/diagnostics/index.js +106 -0
  255. package/dist/diagnostics/index.js.map +1 -0
  256. package/dist/dom.d.ts +55 -0
  257. package/dist/dom.d.ts.map +1 -0
  258. package/dist/dom.js +162 -0
  259. package/dist/dom.js.map +1 -0
  260. package/dist/index.d.ts +97 -0
  261. package/dist/index.d.ts.map +1 -0
  262. package/dist/index.js +58 -0
  263. package/dist/index.js.map +1 -0
  264. package/dist/jsx/react.d.ts +180 -0
  265. package/dist/jsx/react.d.ts.map +1 -0
  266. package/dist/jsx/react.js +2 -0
  267. package/dist/jsx/react.js.map +1 -0
  268. package/dist/lotek.css +3617 -0
  269. package/dist/lt.d.ts +325 -0
  270. package/dist/lt.d.ts.map +1 -0
  271. package/dist/lt.js +183 -0
  272. package/dist/lt.js.map +1 -0
  273. package/dist/react.d.ts +492 -0
  274. package/dist/react.d.ts.map +1 -0
  275. package/dist/react.js +296 -0
  276. package/dist/react.js.map +1 -0
  277. package/dist/register.d.ts +17 -0
  278. package/dist/register.d.ts.map +1 -0
  279. package/dist/register.js +35 -0
  280. package/dist/register.js.map +1 -0
  281. package/dist/tokens/color.d.ts +43 -0
  282. package/dist/tokens/color.d.ts.map +1 -0
  283. package/dist/tokens/color.js +123 -0
  284. package/dist/tokens/color.js.map +1 -0
  285. package/dist/tokens/contract.d.ts +18 -0
  286. package/dist/tokens/contract.d.ts.map +1 -0
  287. package/dist/tokens/contract.js +18 -0
  288. package/dist/tokens/contract.js.map +1 -0
  289. package/dist/tokens/matrix-cells.d.ts +30 -0
  290. package/dist/tokens/matrix-cells.d.ts.map +1 -0
  291. package/dist/tokens/matrix-cells.js +273 -0
  292. package/dist/tokens/matrix-cells.js.map +1 -0
  293. package/dist/tokens/matrix.d.ts +18 -0
  294. package/dist/tokens/matrix.d.ts.map +1 -0
  295. package/dist/tokens/matrix.js +21 -0
  296. package/dist/tokens/matrix.js.map +1 -0
  297. package/dist/tokens/theme-check.d.ts +42 -0
  298. package/dist/tokens/theme-check.d.ts.map +1 -0
  299. package/dist/tokens/theme-check.js +10 -0
  300. package/dist/tokens/theme-check.js.map +1 -0
  301. package/dist/tokens/theme.d.ts +94 -0
  302. package/dist/tokens/theme.d.ts.map +1 -0
  303. package/dist/tokens/theme.js +14 -0
  304. package/dist/tokens/theme.js.map +1 -0
  305. package/dist/tokens/tokens.d.ts +122 -0
  306. package/dist/tokens/tokens.d.ts.map +1 -0
  307. package/dist/tokens/tokens.js +158 -0
  308. package/dist/tokens/tokens.js.map +1 -0
  309. package/dist/vocabulary/classes.d.ts +3 -0
  310. package/dist/vocabulary/classes.d.ts.map +1 -0
  311. package/dist/vocabulary/classes.js +1320 -0
  312. package/dist/vocabulary/classes.js.map +1 -0
  313. package/dist/vocabulary/elements.d.ts +3 -0
  314. package/dist/vocabulary/elements.d.ts.map +1 -0
  315. package/dist/vocabulary/elements.js +410 -0
  316. package/dist/vocabulary/elements.js.map +1 -0
  317. package/dist/vocabulary/types.d.ts +175 -0
  318. package/dist/vocabulary/types.d.ts.map +1 -0
  319. package/dist/vocabulary/types.js +11 -0
  320. package/dist/vocabulary/types.js.map +1 -0
  321. package/dist/wasi-host.d.ts +24 -0
  322. package/dist/wasi-host.d.ts.map +1 -0
  323. package/dist/wasi-host.js +140 -0
  324. package/dist/wasi-host.js.map +1 -0
  325. package/live-templates.xml +62 -0
  326. package/manifest/files.json +373 -0
  327. package/manifest/manifest.json +4965 -0
  328. package/manifest/manifest.schema.json +513 -0
  329. package/package.json +404 -4
  330. package/src/cli.ts +21 -0
  331. package/src/components/css/accordion/accordion.css +102 -0
  332. package/src/components/css/accordion/accordion.ts +50 -0
  333. package/src/components/css/alert/alert.css +53 -0
  334. package/src/components/css/alert/alert.ts +48 -0
  335. package/src/components/css/avatar/avatar.css +96 -0
  336. package/src/components/css/avatar/avatar.ts +57 -0
  337. package/src/components/css/badge/badge.css +42 -0
  338. package/src/components/css/badge/badge.ts +43 -0
  339. package/src/components/css/breadcrumbs/breadcrumbs.css +62 -0
  340. package/src/components/css/breadcrumbs/breadcrumbs.ts +30 -0
  341. package/src/components/css/button/button.css +128 -0
  342. package/src/components/css/button/button.ts +44 -0
  343. package/src/components/css/card/card.css +65 -0
  344. package/src/components/css/card/card.ts +50 -0
  345. package/src/components/css/checkbox/checkbox.css +114 -0
  346. package/src/components/css/checkbox/checkbox.ts +50 -0
  347. package/src/components/css/divider/divider.css +38 -0
  348. package/src/components/css/divider/divider.ts +42 -0
  349. package/src/components/css/error-text/error-text.css +21 -0
  350. package/src/components/css/error-text/error-text.ts +37 -0
  351. package/src/components/css/fieldset/fieldset.css +72 -0
  352. package/src/components/css/fieldset/fieldset.ts +87 -0
  353. package/src/components/css/file-upload/file-upload.css +102 -0
  354. package/src/components/css/file-upload/file-upload.ts +65 -0
  355. package/src/components/css/heading/heading.css +33 -0
  356. package/src/components/css/heading/heading.ts +42 -0
  357. package/src/components/css/help-text/help-text.css +24 -0
  358. package/src/components/css/help-text/help-text.ts +42 -0
  359. package/src/components/css/icon/icon.css +20 -0
  360. package/src/components/css/icon/icon.ts +44 -0
  361. package/src/components/css/input/input.css +145 -0
  362. package/src/components/css/input/input.ts +77 -0
  363. package/src/components/css/label/label.css +42 -0
  364. package/src/components/css/label/label.ts +51 -0
  365. package/src/components/css/link/link.css +38 -0
  366. package/src/components/css/link/link.ts +38 -0
  367. package/src/components/css/list/list.css +53 -0
  368. package/src/components/css/list/list.ts +44 -0
  369. package/src/components/css/pagination/pagination.css +95 -0
  370. package/src/components/css/pagination/pagination.ts +33 -0
  371. package/src/components/css/paragraph/paragraph.css +31 -0
  372. package/src/components/css/paragraph/paragraph.ts +53 -0
  373. package/src/components/css/progress/progress.css +161 -0
  374. package/src/components/css/progress/progress.ts +78 -0
  375. package/src/components/css/radio/radio.css +94 -0
  376. package/src/components/css/radio/radio.ts +50 -0
  377. package/src/components/css/search/search.css +50 -0
  378. package/src/components/css/search/search.ts +56 -0
  379. package/src/components/css/select/select.css +321 -0
  380. package/src/components/css/select/select.ts +63 -0
  381. package/src/components/css/shared.ts +199 -0
  382. package/src/components/css/skeleton/skeleton.css +62 -0
  383. package/src/components/css/skeleton/skeleton.ts +43 -0
  384. package/src/components/css/skip-link/skip-link.css +41 -0
  385. package/src/components/css/skip-link/skip-link.ts +22 -0
  386. package/src/components/css/spinner/spinner.css +48 -0
  387. package/src/components/css/spinner/spinner.ts +48 -0
  388. package/src/components/css/sr-only/sr-only.css +16 -0
  389. package/src/components/css/sr-only/sr-only.ts +21 -0
  390. package/src/components/css/switch/switch.css +105 -0
  391. package/src/components/css/switch/switch.ts +50 -0
  392. package/src/components/css/table/table.css +159 -0
  393. package/src/components/css/table/table.ts +107 -0
  394. package/src/components/css/tag/tag.css +58 -0
  395. package/src/components/css/tag/tag.ts +49 -0
  396. package/src/components/css/textarea/textarea.css +83 -0
  397. package/src/components/css/textarea/textarea.ts +40 -0
  398. package/src/components/css/toggle-group/toggle-group.css +92 -0
  399. package/src/components/css/toggle-group/toggle-group.ts +37 -0
  400. package/src/components/css/tooltip/tooltip.css +89 -0
  401. package/src/components/css/tooltip/tooltip.ts +37 -0
  402. package/src/components/default-texts.ts +34 -0
  403. package/src/components/events.ts +68 -0
  404. package/src/components/frame/dialog/dialog.css +306 -0
  405. package/src/components/frame/dialog/dialog.ts +166 -0
  406. package/src/components/frame/dialog/lt-dialog.ts +510 -0
  407. package/src/components/frame/error-summary/error-summary.css +47 -0
  408. package/src/components/frame/error-summary/error-summary.ts +56 -0
  409. package/src/components/frame/error-summary/lt-error-summary.ts +344 -0
  410. package/src/components/frame/field/field-core.ts +152 -0
  411. package/src/components/frame/field/field.css +17 -0
  412. package/src/components/frame/field/lt-field.ts +753 -0
  413. package/src/components/frame/popover/lt-popover.ts +555 -0
  414. package/src/components/frame/popover/popover.css +50 -0
  415. package/src/components/frame/popover/popover.ts +102 -0
  416. package/src/components/frame/session-timeout/lt-session-timeout.ts +681 -0
  417. package/src/components/frame/session-timeout/session-timeout.css +50 -0
  418. package/src/components/frame/session-timeout/session-timeout.ts +156 -0
  419. package/src/components/frame/suggestion/lt-suggestion.ts +1358 -0
  420. package/src/components/frame/suggestion/suggestion.css +132 -0
  421. package/src/components/frame/suggestion/suggestion.ts +172 -0
  422. package/src/components/frame/tabs/lt-tabs.ts +1319 -0
  423. package/src/components/frame/tabs/tabs.css +128 -0
  424. package/src/components/frame/tabs/tabs.ts +188 -0
  425. package/src/components/host-element.ts +400 -0
  426. package/src/components/standalone/connection-status/connection-status.css +41 -0
  427. package/src/components/standalone/connection-status/connection-status.ts +41 -0
  428. package/src/components/standalone/connection-status/lt-connection-status.ts +242 -0
  429. package/src/components/standalone/toast/lt-toast.ts +300 -0
  430. package/src/components/standalone/toast/toast.css +78 -0
  431. package/src/components/standalone/toast/toast.ts +70 -0
  432. package/src/diagnostics/core.ts +210 -0
  433. package/src/diagnostics/default-core.ts +81 -0
  434. package/src/diagnostics/index.ts +150 -0
  435. package/src/dom.ts +185 -0
  436. package/src/index.ts +218 -0
  437. package/src/jsx/react.ts +212 -0
  438. package/src/lt.ts +192 -0
  439. package/src/react.ts +411 -0
  440. package/src/register.ts +43 -0
  441. package/src/tailwind/tailwind.css +169 -0
  442. package/src/tokens/boundary.css +104 -0
  443. package/src/tokens/color-contract.json +231 -0
  444. package/src/tokens/color.ts +150 -0
  445. package/src/tokens/contract.ts +24 -0
  446. package/src/tokens/matrix-cells.ts +375 -0
  447. package/src/tokens/matrix.ts +32 -0
  448. package/src/tokens/theme-check.ts +51 -0
  449. package/src/tokens/theme.ts +104 -0
  450. package/src/tokens/tokens.css +569 -0
  451. package/src/tokens/tokens.ts +223 -0
  452. package/src/vocabulary/classes.ts +1322 -0
  453. package/src/vocabulary/elements.ts +412 -0
  454. package/src/vocabulary/types.ts +178 -0
  455. package/src/wasi-host.ts +155 -0
  456. package/web-types.json +800 -0
@@ -0,0 +1,510 @@
1
+ import {
2
+ addClass,
3
+ defineElement,
4
+ HostElement,
5
+ isServerControlled,
6
+ SERVER_CONTROLLED,
7
+ setAttr,
8
+ setFlag,
9
+ uniqueId,
10
+ warnAboutMarkup,
11
+ } from "../../host-element.js"
12
+ import {
13
+ DIALOG_BODY_CLASS,
14
+ DIALOG_CLASS,
15
+ DIALOG_TITLE_CLASS,
16
+ } from "./dialog.js"
17
+
18
+ export const LT_DIALOG_TAG = "lt-dialog" as const
19
+
20
+ /**
21
+ * The parts the dialog finds, as selectors relative to the element. The
22
+ * component finds the parts with these, and the manifest describes them for
23
+ * whoever assembles the markup by hand, so the two cannot say different things.
24
+ */
25
+ export const LT_DIALOG_PARTS = {
26
+ dialog: ":scope > dialog",
27
+ title: "h1, h2, h3, h4, h5, h6",
28
+ body: `.${DIALOG_BODY_CLASS}`,
29
+ } as const
30
+
31
+ /**
32
+ * A modal dialog a server can open.
33
+ *
34
+ * Opening a `<dialog>` is a call and not an attribute. `showModal()` moves
35
+ * focus in, keeps focus inside the dialog, closes on Escape and makes the rest
36
+ * of the page inert. `<dialog open>` does none of that: it is a box on the
37
+ * page.
38
+ *
39
+ * A server that only sends HTML cannot call anything. Without this component
40
+ * an app in Datastar, htmx or a Go template could not open a dialog at all
41
+ * without writing a script of its own alongside. Here the server says instead
42
+ * that the dialog is open, and the component makes the call.
43
+ *
44
+ * When the user closes the dialog, with Escape or with a button in a
45
+ * `<form method="dialog">`, `open` is removed from the host again, so the
46
+ * markup says the same as the screen. The component reports it with
47
+ * `dialog-toggle`, which carries `open` and `returnValue`. A click on the
48
+ * backdrop does not close it: neither does it on a plain `<dialog>`, and the
49
+ * component adds nothing.
50
+ *
51
+ * `open` on the **host** comes from the server. If the server sends the
52
+ * region again with `open` still set, the dialog opens again. `open` on the
53
+ * `<dialog>` itself comes from the browser, set by `showModal()`, and the
54
+ * component puts it back when a patch removes it. The template needs no
55
+ * `data-preserve-attr`.
56
+ *
57
+ * Write `open` on the `<dialog>` itself too when the dialog should be shown.
58
+ * Without JavaScript a `<dialog>` without `open` is hidden, and then the
59
+ * content does not exist at all. `lt.dialog({ open: true })` gives both.
60
+ *
61
+ * `server-controlled` on the host turns off the repair of `open` on the
62
+ * `<dialog>`. Then every patch decides whether the dialog is shown, also when
63
+ * it is in the top layer.
64
+ *
65
+ * ```html
66
+ * <lt-dialog open>
67
+ * <dialog class="lt-dialog" aria-labelledby="title" open>
68
+ * <h2 class="lt-dialog__title" id="title">The decision is registered</h2>
69
+ * <div class="lt-dialog__body">The case has been processed.</div>
70
+ * <form method="dialog" class="lt-dialog__footer">
71
+ * <button class="lt-button" value="close">Close</button>
72
+ * </form>
73
+ * </dialog>
74
+ * </lt-dialog>
75
+ * ```
76
+ */
77
+ /**
78
+ * The dialogs already checked for a close before upgrade.
79
+ *
80
+ * The check applies only the first time a dialog is seen, not every time a
81
+ * host is connected. A morph that moves the host disconnects and reconnects
82
+ * it, and then `returnValue` was left over from a close the user made long
83
+ * ago. The component thought it had been closed before the script arrived,
84
+ * and took `open` from the host the server had just opened. The set lives
85
+ * outside the class, so the same applies to a dialog moved into a new host.
86
+ */
87
+ const upgradeChecked = new WeakSet<HTMLDialogElement>()
88
+
89
+ export class LtDialog extends HostElement {
90
+ static observedAttributes = ["open", SERVER_CONTROLLED] as const
91
+
92
+ private observer?: MutationObserver
93
+ /**
94
+ * A separate observer for `open` on the `<dialog>` itself.
95
+ *
96
+ * It cannot be merged with the other one. When the user closes the dialog,
97
+ * the browser removes the attribute first and sends `close` afterwards, and
98
+ * an observer running all of `sync()` there would have seen a host still
99
+ * saying "open" and a dialog that was not modal, and opened it again before
100
+ * `close` was handled. This one does only one thing: it puts the attribute
101
+ * back on a dialog that is still in the top layer.
102
+ */
103
+ private openObserver?: MutationObserver
104
+ private dialogElement?: HTMLDialogElement
105
+ /**
106
+ * What the component last reported, so it reports only when the state
107
+ * changes.
108
+ *
109
+ * A morph that moves the host or the dialog takes the dialog out of the top
110
+ * layer, and the component opens it again. Before, it then reported
111
+ * `open: true` for a dialog that had been open the whole time. The field
112
+ * survives the host being disconnected and reconnected.
113
+ */
114
+ private lastNotified?: boolean
115
+
116
+ /** Whether the dialog is open. Mirrors the `open` attribute. */
117
+ get open(): boolean {
118
+ return this.hasAttribute("open")
119
+ }
120
+
121
+ set open(value: boolean) {
122
+ setFlag(this, "open", value)
123
+ }
124
+
125
+ connectedCallback(): void {
126
+ /*
127
+ * The server can send the dialog into a region already on the page, and
128
+ * then the `<dialog>` does not exist yet when the component connects. Only
129
+ * a change among the host's direct children runs all of `sync()`: the
130
+ * dialog is always a direct child, and the content inside it changes with
131
+ * every patch without saying anything about which dialog is the
132
+ * component's.
133
+ *
134
+ * `open` on the `<dialog>` itself is included. The browser sets it when
135
+ * `showModal()` is called, and a morph removes it again, since the server
136
+ * did not send it. Without this an open dialog disappeared as soon as
137
+ * something in the region around it was patched, and the template had to
138
+ * write `data-preserve-attr="open"` to prevent it.
139
+ */
140
+ /*
141
+ * The attributes are included for the wiring the component fills in when
142
+ * the markup came without it: the class, the heading's id and
143
+ * `aria-labelledby`. But an attribute record runs only the wiring, not all
144
+ * of `sync()`. `close()` removes `open` synchronously and queues the
145
+ * `close` event as a separate task, and the observer runs before it. When
146
+ * a click changed a class on the button in the same task as it closed the
147
+ * dialog, `sync()` saw a host with `open` and a dialog that was not modal,
148
+ * and opened it again before the close was handled. Every write compares
149
+ * first, or the observer would trigger itself.
150
+ */
151
+ this.observer = new MutationObserver((records) => {
152
+ const swapped = records.some(
153
+ (record) => record.type === "childList" && record.target === this,
154
+ )
155
+ if (swapped) this.sync()
156
+ else this.rewire()
157
+ })
158
+ this.observer.observe(this, {
159
+ childList: true,
160
+ subtree: true,
161
+ attributes: true,
162
+ attributeFilter: ["class", "id", "aria-labelledby"],
163
+ })
164
+ this.sync()
165
+ }
166
+
167
+ disconnectedCallback(): void {
168
+ this.observer?.disconnect()
169
+ this.observer = undefined
170
+ this.openObserver?.disconnect()
171
+ this.openObserver = undefined
172
+ this.dialogElement?.removeEventListener("close", this.handleClose)
173
+ this.dialogElement = undefined
174
+ }
175
+
176
+ attributeChangedCallback(): void {
177
+ this.sync()
178
+ }
179
+
180
+ /**
181
+ * The dialog the component controls.
182
+ *
183
+ * Only a direct child. A `<dialog>` further down the tree can belong to
184
+ * something else, and must not be opened by this component.
185
+ */
186
+ private get dialog(): HTMLDialogElement | null {
187
+ return this.querySelector(LT_DIALOG_PARTS.dialog)
188
+ }
189
+
190
+ /** The id the component gave the heading, so a patch gets the same one back. */
191
+ private titleId?: string
192
+
193
+ /** The wiring alone, for an attribute record. See the observer. */
194
+ private rewire(): void {
195
+ const dialog = this.dialog
196
+ if (dialog?.isConnected) this.wire(dialog)
197
+ }
198
+
199
+ /**
200
+ * The heading that is the dialog's title when the server did not point to
201
+ * one.
202
+ *
203
+ * The first heading in this dialog that is not in the body. A heading in
204
+ * `.lt-dialog__body` is a heading in the content, not the dialog's name: a
205
+ * settings dialog in three sections got the first section's heading as its
206
+ * title, with the class and the styling that come with it. A heading in a
207
+ * nested dialog belongs to the nested one, and the body that counts is only
208
+ * a body in this dialog: if this dialog is itself in the body of another,
209
+ * the outer body is not its body.
210
+ */
211
+ private headingOf(dialog: HTMLDialogElement): HTMLElement | null {
212
+ return (
213
+ [...dialog.querySelectorAll<HTMLElement>(LT_DIALOG_PARTS.title)].find(
214
+ (heading) => {
215
+ if (heading.closest("dialog") !== dialog) return false
216
+ const body = heading.closest(LT_DIALOG_PARTS.body)
217
+ return body === null || !dialog.contains(body)
218
+ },
219
+ ) ?? null
220
+ )
221
+ }
222
+
223
+ /**
224
+ * Whether the dialog has no name: no reference, no `aria-label` and no
225
+ * title. An empty `<dialog>` is a region the server has not filled yet, and
226
+ * does not count.
227
+ */
228
+ private nameless(dialog: HTMLDialogElement): boolean {
229
+ return (
230
+ dialog.childElementCount > 0 &&
231
+ !dialog.hasAttribute("aria-labelledby") &&
232
+ !dialog.hasAttribute("aria-label") &&
233
+ this.headingOf(dialog) === null
234
+ )
235
+ }
236
+
237
+ /**
238
+ * Fills in what a template without JavaScript did not write: the class on
239
+ * the dialog, and the title. The title is what `aria-labelledby` points to
240
+ * when the server wrote it, otherwise the first heading outside the body,
241
+ * and only when the dialog does not already have a name from `aria-label`.
242
+ * The class and the id are written only on the title: a heading in the body
243
+ * of a dialog with `aria-label` is not the title, and must not be styled as
244
+ * one.
245
+ */
246
+ private wire(dialog: HTMLDialogElement): void {
247
+ addClass(dialog, DIALOG_CLASS)
248
+
249
+ const named = dialog.getAttribute("aria-labelledby")
250
+ if (named) {
251
+ // The attribute can list several ids. The title is the first one that
252
+ // is inside the dialog.
253
+ const root = this.getRootNode() as Document | ShadowRoot
254
+ const title = named
255
+ .split(/\s+/)
256
+ .map((id) => root.getElementById?.(id) ?? null)
257
+ .find((element) => element !== null && dialog.contains(element))
258
+ if (title) addClass(title, DIALOG_TITLE_CLASS)
259
+ return
260
+ }
261
+ if (dialog.hasAttribute("aria-label")) return
262
+
263
+ const title = this.headingOf(dialog)
264
+ if (!title) {
265
+ warnAboutMarkup(
266
+ this,
267
+ "the dialog has no name: it has no aria-labelledby, no " +
268
+ "aria-label and no heading outside the body, so a screen reader " +
269
+ 'announces it as "dialog" without saying what it is about. Give ' +
270
+ "it a heading, or aria-label on the <dialog>.",
271
+ () => {
272
+ const now = this.dialog
273
+ return now !== null && this.nameless(now)
274
+ },
275
+ )
276
+ return
277
+ }
278
+ addClass(title, DIALOG_TITLE_CLASS)
279
+ if (!title.id) {
280
+ this.titleId ??= uniqueId("lt-dialog-title")
281
+ setAttr(title, "id", this.titleId)
282
+ }
283
+ setAttr(dialog, "aria-labelledby", title.id)
284
+ }
285
+
286
+ private notify(open: boolean, returnValue = ""): void {
287
+ if (open === this.lastNotified) return
288
+ this.lastNotified = open
289
+ this.dispatchEvent(
290
+ new CustomEvent("dialog-toggle", {
291
+ bubbles: true,
292
+ // Without `composed` the event stops at a shadow root, and a listener
293
+ // outside never learns that the dialog was closed.
294
+ composed: true,
295
+ detail: { open, returnValue },
296
+ }),
297
+ )
298
+ }
299
+
300
+ /**
301
+ * Puts `open` back on a dialog that is still in the top layer.
302
+ *
303
+ * `showModal()` sets the attribute itself, and the server did not send it,
304
+ * so the morph removes it. The browser's own stylesheet then hides a
305
+ * `<dialog>` without `open`, and the dialog disappeared for the user in the
306
+ * middle of what she was doing. Before, the template had to write
307
+ * `data-preserve-attr="open"` to prevent it.
308
+ *
309
+ * The condition is `:modal` and not `this.open`, and that is precise: when
310
+ * the user closes the dialog, it leaves the top layer, and then the missing
311
+ * attribute is real. Only a dialog that is still modal without saying so is
312
+ * a dialog someone has taken the attribute from.
313
+ */
314
+ private repairOpen(): void {
315
+ const dialog = this.dialogElement
316
+ if (!dialog?.isConnected || !dialog.matches(":modal") || dialog.open) {
317
+ return
318
+ }
319
+
320
+ // The attribute must come back either way: without it `close()` does
321
+ // nothing, and the dialog stayed in the top layer, invisible, with the
322
+ // rest of the page inert.
323
+ setAttr(dialog, "open", "")
324
+ if (!isServerControlled(this)) return
325
+
326
+ /*
327
+ * The server decides the state, and the patch took `open` from a modal
328
+ * dialog. Then the message is that it should close, not be put back.
329
+ * Without this the dialog stood with `:modal` and `display: none`, and
330
+ * nothing on the page could be clicked. The host follows along here, so
331
+ * `handleClose` has nothing left to do and the event comes once.
332
+ */
333
+ dialog.close()
334
+ // The event comes whoever removed `open` from the host. If React has
335
+ // already taken it, in its order with children before parent, this is
336
+ // the one time anyone can report it: `sync()` and `handleClose` then see
337
+ // a dialog that is already closed, and stay quiet.
338
+ if (this.open) this.removeAttribute("open")
339
+ this.notify(false, dialog.returnValue)
340
+ }
341
+
342
+ private handleClose = (): void => {
343
+ // `close` is queued, not synchronous. If the server manages to close and
344
+ // open again in the same task, this event no longer applies, and without
345
+ // the guard the component closed the dialog it had just opened.
346
+ if (this.dialog?.matches(":modal")) return
347
+
348
+ // If the server closed it, the host is already in step, and `sync()` has
349
+ // reported it.
350
+ if (!this.open) return
351
+
352
+ this.removeAttribute("open")
353
+ this.notify(false, this.dialog?.returnValue ?? "")
354
+ }
355
+
356
+ private sync(): void {
357
+ const dialog = this.dialog
358
+
359
+ // `showModal()` throws "The element is not in a Document" if the dialog
360
+ // is not on the page. That is not theoretical: a morph builds the
361
+ // server's version in a detached tree before comparing, and a web
362
+ // component is used there too. Without this line the component threw on
363
+ // every single patch that touched the dialog. If it is detached now,
364
+ // `sync()` runs again anyway when it is connected.
365
+ if (!dialog?.isConnected) {
366
+ // Let go of a dialog that is gone. Otherwise the observer and the
367
+ // listener stayed attached to a detached node for as long as the host
368
+ // lived.
369
+ if (!dialog) {
370
+ this.openObserver?.disconnect()
371
+ this.openObserver = undefined
372
+ this.dialogElement?.removeEventListener("close", this.handleClose)
373
+ this.dialogElement = undefined
374
+ }
375
+
376
+ warnAboutMarkup(
377
+ this,
378
+ "found no <dialog> as a direct child. Without it nothing can " +
379
+ "open as a modal, and the content stays on the page as a plain " +
380
+ "box.",
381
+ // If the component is detached, a morph is building the server's
382
+ // version in a separate tree, and there is nothing to report. An
383
+ // empty element is a region the server has not filled yet.
384
+ () => this.childElementCount > 0 && this.dialog === null,
385
+ )
386
+ return
387
+ }
388
+
389
+ this.wire(dialog)
390
+
391
+ const first = dialog !== this.dialogElement
392
+ if (first) {
393
+ this.dialogElement?.removeEventListener("close", this.handleClose)
394
+ this.dialogElement = dialog
395
+ dialog.addEventListener("close", this.handleClose)
396
+
397
+ this.openObserver?.disconnect()
398
+ this.openObserver = new MutationObserver(() => this.repairOpen())
399
+ this.openObserver.observe(dialog, { attributeFilter: ["open"] })
400
+ }
401
+
402
+ this.repairOpen()
403
+
404
+ /*
405
+ * Did the user close the dialog before the component got to run?
406
+ *
407
+ * The server writes `open` in both places, so the content exists without
408
+ * JavaScript, and `<form method="dialog">` closes the box without
409
+ * JavaScript too. If that happens before the script has loaded, there is
410
+ * no listener, and the first `sync()` would see a host saying "open" and a
411
+ * dialog that is closed, and open it again. So the dialog popped up again
412
+ * right after the user had closed it, and on mobile the window was large
413
+ * enough for it to happen every time.
414
+ *
415
+ * `returnValue` tells the two cases apart. The browser sets it to the
416
+ * value of the button that closed the dialog, so a dialog that has never
417
+ * been open has it empty. A template that only wrote `open` on the host,
418
+ * as the documentation showed for a long time, also has it empty, and is
419
+ * opened as before.
420
+ *
421
+ * This works only when the button has a `value`. If the dialog is closed
422
+ * with a button without a value, or with `close()` without an argument,
423
+ * the browser leaves nothing to look for, and the dialog opens again as it
424
+ * did before. That is why every `<form method="dialog">` in the
425
+ * documentation has a `value`, and it is worth keeping.
426
+ */
427
+ if (!upgradeChecked.has(dialog)) {
428
+ upgradeChecked.add(dialog)
429
+ if (this.open && !dialog.open && dialog.returnValue !== "") {
430
+ this.removeAttribute("open")
431
+ return
432
+ }
433
+ }
434
+
435
+ // `:modal` and not `open`. The two are not the same: a `<dialog open>`
436
+ // in the markup, and a dialog that was moved in the DOM while it was
437
+ // open, have `open` without being modal. Then it is a box on the page,
438
+ // without a focus trap and without Escape, which is exactly what the
439
+ // component exists to prevent.
440
+ const modal = dialog.matches(":modal")
441
+
442
+ /*
443
+ * The user has just closed the dialog with a button, and `close` has not
444
+ * arrived yet. The browser removes `open` at once and sends the event in a
445
+ * later task, and the host still says "open" until `handleClose` gets to
446
+ * run. When something wrote `open` on the host in the meantime, such as a
447
+ * framework setting the same value again, the component opened the dialog
448
+ * again and cleared `returnValue`, and a listener on `close` read an empty
449
+ * value instead of "delete". `returnValue` is cleared every time the
450
+ * dialog opens, so a value here comes from a button in this round.
451
+ */
452
+ if (
453
+ this.open &&
454
+ !modal &&
455
+ this.lastNotified === true &&
456
+ dialog.returnValue !== ""
457
+ ) {
458
+ return
459
+ }
460
+
461
+ if (this.open && !modal) {
462
+ /*
463
+ * `showModal()` throws `InvalidStateError` when `open` is already there
464
+ * without the dialog being modal. So the attribute must go first.
465
+ *
466
+ * But not with `close()`. It sends a real `close` event, and since the
467
+ * server now writes `open` on the `<dialog>` itself, that event would
468
+ * have come on every single load of a dialog that is open. An app
469
+ * listening for `close` directly on the `<dialog>`, as the complete
470
+ * example in the documentation does, then got a phantom close before the
471
+ * user had seen the dialog. `removeAttribute` makes room without the
472
+ * event.
473
+ */
474
+ dialog.removeAttribute("open")
475
+ /*
476
+ * Escape and `close()` without an argument leave the previous
477
+ * `returnValue` in place. A dialog the server opened again then
478
+ * reported "delete" from last time when it was closed from the server,
479
+ * and the app that tells "Cancel" from "Delete" by the value deleted
480
+ * something the user never asked for.
481
+ */
482
+ dialog.returnValue = ""
483
+ dialog.showModal()
484
+ this.notify(true)
485
+ } else if (!this.open && (modal || dialog.open)) {
486
+ /*
487
+ * `modal` and not only `dialog.open`: the attribute can be gone while
488
+ * the dialog is still in the top layer.
489
+ *
490
+ * React is the reason. The server writes `open` on the `<dialog>`, so
491
+ * React manages the attribute, and React updates children before the
492
+ * parent. When the app closes the dialog, React first removes `open`
493
+ * from the `<dialog>` and then from `<lt-dialog>`. So we get here with
494
+ * `dialog.open` already false.
495
+ *
496
+ * `close()` does nothing without the attribute, so without this the
497
+ * dialog stayed in the top layer, invisible, with the rest of the page
498
+ * inert. The user was left with a page where nothing could be clicked,
499
+ * and nothing visible to explain why.
500
+ */
501
+ setAttr(dialog, "open", "")
502
+ dialog.close()
503
+ this.notify(false, dialog.returnValue)
504
+ }
505
+ }
506
+ }
507
+
508
+ export function defineLtDialog(tagName: string = LT_DIALOG_TAG): void {
509
+ defineElement(tagName, LtDialog)
510
+ }
@@ -0,0 +1,47 @@
1
+ @import "../../css/list/list.css";
2
+
3
+ @layer lotek {
4
+ lt-error-summary[hidden] {
5
+ display: none;
6
+ }
7
+
8
+ /* The component sets the class on the element itself. The consumer's
9
+ markup stays where it is. */
10
+ .lt-error-summary {
11
+ display: block;
12
+ box-sizing: border-box;
13
+ padding: var(--lt-error-summary-padding, var(--lt-spacing-4));
14
+ font-family: inherit;
15
+ font-size: var(--lt-font-size-m);
16
+ line-height: var(--lt-line-height-default);
17
+ color: var(--lt-color-neutral-text-strong);
18
+ background: var(--lt-color-danger-surface);
19
+ border-inline-start: var(--lt-spacing-1) solid var(--lt-color-danger-text);
20
+ border-radius: var(--lt-error-summary-radius, var(--lt-spacing-1));
21
+ }
22
+
23
+ .lt-error-summary:focus-visible {
24
+ outline: var(--lt-focus-ring);
25
+ outline-color: var(--lt-color-danger-text);
26
+ outline-offset: 2px;
27
+ }
28
+
29
+ .lt-error-summary__title {
30
+ margin: 0 0 var(--lt-spacing-2);
31
+ font-size: var(--lt-font-size-l);
32
+ font-weight: var(--lt-font-weight-semibold);
33
+ line-height: var(--lt-line-height-compact);
34
+ }
35
+
36
+ .lt-error-summary a {
37
+ color: var(--lt-color-danger-text);
38
+ text-decoration: underline;
39
+ text-underline-offset: 0.14em;
40
+ }
41
+
42
+ .lt-error-summary a:focus-visible {
43
+ outline: var(--lt-focus-ring);
44
+ outline-color: var(--lt-color-danger-text);
45
+ outline-offset: 2px;
46
+ }
47
+ }
@@ -0,0 +1,56 @@
1
+ import { attributes } from "../../css/shared.js"
2
+
3
+ export const ERROR_SUMMARY_CLASS = "lt-error-summary" as const
4
+ export const ERROR_SUMMARY_TITLE_CLASS = "lt-error-summary__title" as const
5
+
6
+ export type ErrorSummaryOptions = {
7
+ /** The number of errors the server found. When it is zero, the box is hidden. */
8
+ count?: number
9
+ /** The id of the box, so the form can move focus here after submission. */
10
+ id?: string
11
+ }
12
+
13
+ export type ErrorSummaryAttributes = {
14
+ host: {
15
+ class: typeof ERROR_SUMMARY_CLASS
16
+ role: "alert"
17
+ tabindex: "-1"
18
+ id?: string
19
+ hidden?: true
20
+ }
21
+ title: {
22
+ class: typeof ERROR_SUMMARY_TITLE_CLASS
23
+ }
24
+ }
25
+
26
+ /**
27
+ * The attributes for an error summary.
28
+ *
29
+ * The server writes the whole box, including the heading and the list.
30
+ * `<lt-error-summary>` only moves focus and handles clicks on the links. The
31
+ * component used to create the heading itself, and then it disappeared on
32
+ * every morph in Datastar.
33
+ *
34
+ * ```ts
35
+ * const summary = lt.errorSummary({ count: errors.length, id: "errors" })
36
+ * ```
37
+ * ```html
38
+ * <lt-error-summary {...summary.host}>
39
+ * <h2 {...summary.title}>You need to fix 2 errors</h2>
40
+ * <ul><li><a href="#email">The email address is missing an @</a></li></ul>
41
+ * </lt-error-summary>
42
+ * ```
43
+ */
44
+ export const errorSummary = ({
45
+ count = 0,
46
+ id,
47
+ }: ErrorSummaryOptions = {}): ErrorSummaryAttributes => ({
48
+ host: attributes({
49
+ class: ERROR_SUMMARY_CLASS,
50
+ role: "alert" as const,
51
+ tabindex: "-1" as const,
52
+ id,
53
+ hidden: count === 0 ? (true as const) : undefined,
54
+ }),
55
+ title: attributes({ class: ERROR_SUMMARY_TITLE_CLASS }),
56
+ })