@lo-tek/interfaces 0.0.0 → 0.38.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 +3389 -0
  2. package/LICENSE +21 -0
  3. package/README.md +129 -1
  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 +41 -0
  161. package/dist/components/default-texts.d.ts.map +1 -0
  162. package/dist/components/default-texts.js +41 -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 +434 -0
  175. package/dist/components/frame/dialog/lt-dialog.js.map +1 -0
  176. package/dist/components/frame/error-summary/error-summary.d.ts +53 -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 +54 -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 +93 -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 +408 -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 +600 -0
  191. package/dist/components/frame/field/lt-field.js.map +1 -0
  192. package/dist/components/frame/popover/lt-popover.d.ts +136 -0
  193. package/dist/components/frame/popover/lt-popover.d.ts.map +1 -0
  194. package/dist/components/frame/popover/lt-popover.js +520 -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 +610 -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 +367 -0
  209. package/dist/components/frame/suggestion/lt-suggestion.d.ts.map +1 -0
  210. package/dist/components/frame/suggestion/lt-suggestion.js +1171 -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 +426 -0
  217. package/dist/components/frame/tabs/lt-tabs.d.ts.map +1 -0
  218. package/dist/components/frame/tabs/lt-tabs.js +1265 -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 +184 -0
  225. package/dist/components/host-element.d.ts.map +1 -0
  226. package/dist/components/host-element.js +440 -0
  227. package/dist/components/host-element.js.map +1 -0
  228. package/dist/components/standalone/connection-status/connection-status.d.ts +32 -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 +26 -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 +86 -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 +240 -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 +171 -0
  245. package/dist/diagnostics/core.d.ts.map +1 -0
  246. package/dist/diagnostics/core.js +84 -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 +494 -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 +1354 -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 +490 -0
  316. package/dist/vocabulary/elements.js.map +1 -0
  317. package/dist/vocabulary/types.d.ts +229 -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 +63 -0
  326. package/manifest/files.json +373 -0
  327. package/manifest/manifest.json +5090 -0
  328. package/manifest/manifest.schema.json +586 -0
  329. package/package.json +399 -5
  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 +41 -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 +513 -0
  407. package/src/components/frame/error-summary/error-summary.css +47 -0
  408. package/src/components/frame/error-summary/error-summary.ts +90 -0
  409. package/src/components/frame/error-summary/lt-error-summary.ts +458 -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 +790 -0
  413. package/src/components/frame/popover/lt-popover.ts +586 -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 +715 -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 +1379 -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 +1395 -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 +493 -0
  426. package/src/components/standalone/connection-status/connection-status.css +41 -0
  427. package/src/components/standalone/connection-status/connection-status.ts +47 -0
  428. package/src/components/standalone/connection-status/lt-connection-status.ts +268 -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 +227 -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 +1356 -0
  453. package/src/vocabulary/elements.ts +492 -0
  454. package/src/vocabulary/types.ts +228 -0
  455. package/src/wasi-host.ts +155 -0
  456. package/web-types.json +800 -0
@@ -0,0 +1,268 @@
1
+ import { defineElement, HostElement, whenUpgraded } from "../../host-element.js"
2
+ import {
3
+ CONNECTION_STATUS_BAR_CLASS,
4
+ CONNECTION_STATUS_CLASS,
5
+ } from "./connection-status.js"
6
+
7
+ export const LT_CONNECTION_STATUS_TAG = "lt-connection-status" as const
8
+
9
+ import { DEFAULT_TEXTS } from "../../default-texts.js"
10
+
11
+ const DEFAULT_OFFLINE = DEFAULT_TEXTS.connectionOffline
12
+ const DEFAULT_ONLINE = DEFAULT_TEXTS.connectionOnline
13
+
14
+ /** How long "back online" stays visible. */
15
+ const RECEIPT_MS = 4000
16
+
17
+ /**
18
+ * Tells the user when the connection to the server is lost.
19
+ *
20
+ * By definition the server cannot tell you this. When it is unreachable,
21
+ * nothing comes from it. So this is a standalone component that creates all
22
+ * of its content itself.
23
+ *
24
+ * It is not decoration in an app that fetches content from the server as it
25
+ * goes. A page that loses its stream looks perfectly normal but is frozen,
26
+ * and a user filling in a form over a dead connection loses everything.
27
+ *
28
+ * `navigator.onLine` only says whether the machine has a network, not whether
29
+ * the server answers. That is why `reportFailure()` and `reportSuccess()`
30
+ * exist: the app calls them when a request fails or succeeds.
31
+ *
32
+ * ```ts
33
+ * const status = document.querySelector("lt-connection-status")
34
+ * try {
35
+ * const response = await fetch("/api/save", { method: "POST", body })
36
+ * if (!response.ok) throw new Error(response.statusText)
37
+ * status.reportSuccess()
38
+ * } catch {
39
+ * status.reportFailure()
40
+ * }
41
+ * ```
42
+ */
43
+ export class LtConnectionStatus extends HostElement {
44
+ static observedAttributes = ["offline-text", "online-text"] as const
45
+
46
+ private bar?: HTMLElement
47
+ private timer?: number
48
+ /** True when the app has reported a failed request, regardless of navigator. */
49
+ private failing = false
50
+
51
+ get offlineText(): string {
52
+ return this.getAttribute("offline-text") ?? DEFAULT_OFFLINE
53
+ }
54
+
55
+ set offlineText(value: string) {
56
+ this.setAttribute("offline-text", value)
57
+ }
58
+
59
+ get onlineText(): string {
60
+ return this.getAttribute("online-text") ?? DEFAULT_ONLINE
61
+ }
62
+
63
+ set onlineText(value: string) {
64
+ this.setAttribute("online-text", value)
65
+ }
66
+
67
+ attributeChangedCallback(): void {
68
+ // If the text changes while the bar is showing, as when the language
69
+ // changes, the bar says the new one. `textContent` is only written when
70
+ // the text differs.
71
+ const bar = this.bar
72
+ if (!bar || bar.textContent === "") return
73
+ const text =
74
+ bar.dataset.state === "offline" ? this.offlineText : this.onlineText
75
+ if (bar.textContent !== text) bar.textContent = text
76
+ }
77
+
78
+ connectedCallback(): void {
79
+ this.classList.add(CONNECTION_STATUS_CLASS)
80
+ /*
81
+ * The host is the live region, from the moment it is connected, so the
82
+ * region exists long before the bar and its text arrive. Only what is
83
+ * missing: a role or politeness the server wrote stays. When the bar was
84
+ * the region, it was inserted together with its text, and Firefox sent
85
+ * no live-region text through AT-SPI at all, so "No connection" was
86
+ * never announced. `check-accessibility-tree.ts` checks it.
87
+ */
88
+ this.markRegion()
89
+ // A morph without `data-ignore-morph` takes both off, and then the next
90
+ // outage is never announced. Writing only what is missing keeps the
91
+ // observer from triggering itself.
92
+ this.regionObserver = new MutationObserver(() => this.markRegion())
93
+ this.regionObserver.observe(this, {
94
+ attributes: true,
95
+ attributeFilter: ["role", "aria-live"],
96
+ })
97
+ window.addEventListener("offline", this.handleOffline)
98
+ window.addEventListener("online", this.handleOnline)
99
+ if (!navigator.onLine) {
100
+ this.showOffline()
101
+ return
102
+ }
103
+ /*
104
+ * The element may have been out of the document, and then both the
105
+ * events and the timer are lost. If the receipt was showing, it stayed
106
+ * for good, and if the bar said "offline" while the network came back,
107
+ * it kept saying so until the next time the network dropped. `failing`
108
+ * is the app's word and not the network's, so it stays.
109
+ */
110
+ if (this.bar?.dataset.state === "online") this.scheduleRemoval(this.bar)
111
+ else if (this.bar?.dataset.state === "offline" && !this.failing) {
112
+ this.showOnline()
113
+ }
114
+ }
115
+
116
+ private regionObserver?: MutationObserver
117
+
118
+ /**
119
+ * The region, as a pair and only when both are missing. A role the server
120
+ * wrote carries its own politeness (`alert` is assertive), and an
121
+ * `aria-live` added next to it would override that.
122
+ */
123
+ private markRegion(): void {
124
+ if (this.hasAttribute("role") || this.hasAttribute("aria-live")) return
125
+ this.setAttribute("role", "status")
126
+ this.setAttribute("aria-live", "polite")
127
+ }
128
+
129
+ disconnectedCallback(): void {
130
+ this.regionObserver?.disconnect()
131
+ this.regionObserver = undefined
132
+ window.removeEventListener("offline", this.handleOffline)
133
+ window.removeEventListener("online", this.handleOnline)
134
+ if (this.timer) window.clearTimeout(this.timer)
135
+ this.timer = undefined
136
+ }
137
+
138
+ /**
139
+ * The bar, created empty first and filled afterwards.
140
+ *
141
+ * A live region must exist before its content arrives, or assistive
142
+ * technology does not announce the first change. The region is the host
143
+ * (see `connectedCallback`), so the bar is content inside a region that is
144
+ * already there; the text is still set in the next frame when the bar is
145
+ * new, so the bar and its text arrive as two changes.
146
+ *
147
+ * The bar is created only when it is needed, not on connection: when it
148
+ * was in the DOM before React hydrated, React found an element it had not
149
+ * rendered.
150
+ */
151
+ private ensureBar(): { bar: HTMLElement; fresh: boolean } {
152
+ if (this.bar) return { bar: this.bar, fresh: false }
153
+
154
+ const bar = document.createElement("div")
155
+ bar.className = CONNECTION_STATUS_BAR_CLASS
156
+ this.append(bar)
157
+ this.bar = bar
158
+ return { bar, fresh: true }
159
+ }
160
+
161
+ private write(
162
+ bar: HTMLElement,
163
+ fresh: boolean,
164
+ state: "offline" | "online",
165
+ ): void {
166
+ // The text is read when it is written, not when it is scheduled. If
167
+ // `offline-text` changed in the frame between, the bar wrote the old one.
168
+ const text = () =>
169
+ state === "offline" ? this.offlineText : this.onlineText
170
+ if (!fresh || typeof requestAnimationFrame === "undefined") {
171
+ bar.textContent = text()
172
+ return
173
+ }
174
+ requestAnimationFrame(() => {
175
+ // The guard is the state, not the bar: if `reportSuccess()` came in
176
+ // the same frame, the bar already says "online", and the offline text
177
+ // must not be written over the receipt.
178
+ if (this.bar === bar && bar.dataset.state === state) {
179
+ bar.textContent = text()
180
+ }
181
+ })
182
+ }
183
+
184
+ private showOffline(): void {
185
+ // Browsers often fire several `offline` events in a row, and the app
186
+ // can report a failure on top of a network that is already gone. If the
187
+ // bar already says "offline", there is nothing new to write or report:
188
+ // each write was announced again, and each report gave the app a new
189
+ // `connection-lost`.
190
+ if (this.bar?.dataset.state === "offline") return
191
+
192
+ if (this.timer) window.clearTimeout(this.timer)
193
+ const { bar, fresh } = this.ensureBar()
194
+ bar.dataset.state = "offline"
195
+ this.write(bar, fresh, "offline")
196
+ this.emit("connection-lost")
197
+ }
198
+
199
+ private showOnline(): void {
200
+ if (!this.bar || this.bar.dataset.state === "online") return
201
+ const bar = this.bar
202
+ bar.dataset.state = "online"
203
+ this.write(bar, false, "online")
204
+ this.emit("connection-restored")
205
+
206
+ this.scheduleRemoval(bar)
207
+ }
208
+
209
+ private scheduleRemoval(bar: HTMLElement): void {
210
+ if (this.timer) window.clearTimeout(this.timer)
211
+ this.timer = window.setTimeout(() => {
212
+ bar.remove()
213
+ this.bar = undefined
214
+ this.timer = undefined
215
+ }, RECEIPT_MS)
216
+ }
217
+
218
+ private handleOffline = (): void => {
219
+ this.showOffline()
220
+ }
221
+
222
+ private handleOnline = (): void => {
223
+ if (this.failing) return
224
+ this.showOnline()
225
+ }
226
+
227
+ private emit(name: string): void {
228
+ this.dispatchEvent(new CustomEvent(name, { bubbles: true, composed: true }))
229
+ }
230
+
231
+ /** Reports that a request to the server failed. */
232
+ reportFailure(): void {
233
+ if (this.failing) return
234
+ this.failing = true
235
+ this.showOffline()
236
+ }
237
+
238
+ /** Reports that a request to the server succeeded again. */
239
+ reportSuccess(): void {
240
+ if (!this.failing) return
241
+ this.failing = false
242
+ this.showOnline()
243
+ }
244
+ }
245
+
246
+ export function defineLtConnectionStatus(
247
+ tagName: string = LT_CONNECTION_STATUS_TAG,
248
+ ): void {
249
+ defineElement(tagName, LtConnectionStatus)
250
+ }
251
+
252
+ /**
253
+ * `element.reportFailure()`, but waits for the registration first.
254
+ *
255
+ * This is where the gap showed up in production: the first message from the
256
+ * event stream came before `defineLtConnectionStatus()`, and the app got
257
+ * "reportSuccess is not a function". See `whenUpgraded`.
258
+ */
259
+ export async function reportFailure(element: Element): Promise<void> {
260
+ const status = await whenUpgraded<LtConnectionStatus>(element)
261
+ status.reportFailure()
262
+ }
263
+
264
+ /** `element.reportSuccess()`, but waits for the registration first. */
265
+ export async function reportSuccess(element: Element): Promise<void> {
266
+ const status = await whenUpgraded<LtConnectionStatus>(element)
267
+ status.reportSuccess()
268
+ }
@@ -0,0 +1,300 @@
1
+ import { DEFAULT_TEXTS } from "../../default-texts.js"
2
+ import { defineElement, HostElement, whenUpgraded } from "../../host-element.js"
3
+ import { TOAST_CLASS, TOAST_CLOSE_CLASS, TOAST_MESSAGE_CLASS } from "./toast.js"
4
+
5
+ export const LT_TOAST_TAG = "lt-toast" as const
6
+
7
+ export const toastColors = ["neutral", "success", "warning", "danger"] as const
8
+ export type ToastColor = (typeof toastColors)[number]
9
+
10
+ export function isToastColor(value: unknown): value is ToastColor {
11
+ return (toastColors as readonly unknown[]).includes(value)
12
+ }
13
+
14
+ const DEFAULT_DURATION = 6000
15
+ const DEFAULT_LABEL = DEFAULT_TEXTS.toastRegion
16
+ const DEFAULT_CLOSE_LABEL = DEFAULT_TEXTS.toastClose
17
+
18
+ export type ShowOptions = {
19
+ /** What the message means. Default: `neutral`. */
20
+ color?: ToastColor
21
+ /** Milliseconds before the message disappears. `0` keeps it. */
22
+ duration?: number
23
+ /**
24
+ * The screen reader name of the close button. Default: `close-label` on
25
+ * the host, and otherwise "Close message".
26
+ */
27
+ closeLabel?: string
28
+ }
29
+
30
+ /**
31
+ * The queue of short messages in the corner of the screen.
32
+ *
33
+ * This is one of the standalone components, and part of the package you call
34
+ * instead of writing markup. The element is the container, not the message:
35
+ * you put it in the app once and call `show()` when there is something to
36
+ * report. The messages do not exist until an event on the client creates
37
+ * them, so there is nothing for the server to render.
38
+ *
39
+ * The server writes the container with `lt.toast()`, which sets
40
+ * `data-ignore-morph`. Without it the Datastar morph tears the messages away
41
+ * at the next patch, because the server's version of the region is empty.
42
+ *
43
+ * The container is a `role="status"` region, not `role="alert"`. A message
44
+ * that appears in the corner should not interrupt what the screen reader is
45
+ * doing; if the message is important enough to interrupt, it belongs in an
46
+ * [Alert](../../css/alert/alert.js) in the page itself. The region has
47
+ * `aria-atomic="false"`: `status` is atomic by default, and then the whole
48
+ * stack was announced again for every new message.
49
+ *
50
+ * Messages that disappear by themselves are an accessibility trap: someone
51
+ * who reads slowly or uses magnification does not have time to read them. So
52
+ * the close button is always there, and the timer stops while the mouse or
53
+ * focus is in the message.
54
+ *
55
+ * ```ts
56
+ * document.querySelector("lt-toast").show("Application sent", {
57
+ * color: "success",
58
+ * })
59
+ * ```
60
+ */
61
+ export class LtToast extends HostElement {
62
+ static observedAttributes = ["duration", "label", "close-label"] as const
63
+
64
+ /**
65
+ * The screen reader name of the close button in each message shown.
66
+ * `closeLabel` in `show()` overrides it for one message.
67
+ */
68
+ get closeLabel(): string {
69
+ return this.getAttribute("close-label") || DEFAULT_CLOSE_LABEL
70
+ }
71
+
72
+ set closeLabel(value: string) {
73
+ this.setAttribute("close-label", value)
74
+ }
75
+
76
+ /** Default lifetime in milliseconds. `0` keeps the messages. */
77
+ get duration(): number {
78
+ // `0` is a valid value and means the messages stay. Without the check
79
+ // against null here they still disappeared after six seconds, and only
80
+ // `show(..., { duration: 0 })` worked. An empty attribute is not null:
81
+ // `Number("")` is 0, and `<lt-toast duration>` kept the messages.
82
+ const raw = this.getAttribute("duration")
83
+ if (raw === null || raw.trim() === "") return DEFAULT_DURATION
84
+ const value = Number(raw)
85
+ return Number.isFinite(value) && value >= 0 ? value : DEFAULT_DURATION
86
+ }
87
+
88
+ set duration(value: number) {
89
+ this.setAttribute("duration", String(value))
90
+ }
91
+
92
+ /**
93
+ * Whether the component wrote `aria-label`. Before, `label` overwrote an
94
+ * `aria-label` the author had set, and when `label` was removed, the old
95
+ * one stayed.
96
+ */
97
+ private ownsLabel = false
98
+ /**
99
+ * The element focus came from before it entered a message. If the last
100
+ * message is removed while it has focus, focus goes back there. Before, it
101
+ * fell to `body`, and the next Tab started at the top of the page. If
102
+ * focus came from a screen reader or F6, the origin is not known, and focus
103
+ * still falls.
104
+ */
105
+ private focusOrigin: HTMLElement | null = null
106
+
107
+ private rememberOrigin = (event: FocusEvent): void => {
108
+ const from = event.relatedTarget
109
+ if (from instanceof HTMLElement && !this.contains(from)) {
110
+ this.focusOrigin = from
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Focus left the region for somewhere else. Then the origin is stale:
116
+ * without this, focus later came back to an element the user left long
117
+ * ago, and the page scrolled there. Not when `relatedTarget` is `null`,
118
+ * because that is also what happens when the message with focus is
119
+ * removed.
120
+ */
121
+ private forgetOrigin = (event: FocusEvent): void => {
122
+ const to = event.relatedTarget
123
+ if (to instanceof Node && !this.contains(to)) this.focusOrigin = null
124
+ }
125
+
126
+ /**
127
+ * Whether focus is in the element. Read from the component's root node,
128
+ * not from `document`: when the region is in a shadow root, as in every
129
+ * preview in the documentation, `document.activeElement` points to the
130
+ * shadow host. Then it looked as if no message had focus, and focus fell
131
+ * to `body` instead of to the neighbouring message or where it came from.
132
+ */
133
+ private holdsFocus(element: Element): boolean {
134
+ const root = this.getRootNode() as Document | ShadowRoot
135
+ return element.contains(root.activeElement)
136
+ }
137
+
138
+ private restoreFocus(): void {
139
+ const origin = this.focusOrigin
140
+ this.focusOrigin = null
141
+ // A restore, not a navigation: the page must not scroll.
142
+ if (origin?.isConnected) origin.focus({ preventScroll: true })
143
+ }
144
+
145
+ disconnectedCallback(): void {
146
+ this.removeEventListener("focusin", this.rememberOrigin)
147
+ this.removeEventListener("focusout", this.forgetOrigin)
148
+ this.focusOrigin = null
149
+ }
150
+
151
+ connectedCallback(): void {
152
+ this.addEventListener("focusin", this.rememberOrigin)
153
+ this.addEventListener("focusout", this.forgetOrigin)
154
+ // If the server wrote the region with lt.toast(), all of this is
155
+ // already there. Here it is only set when missing, so a plain HTML page
156
+ // without a builder function also gets a region the screen reader
157
+ // understands.
158
+ // The class carries the position. It is set here too, so a region
159
+ // without a builder function, or registered under another name, ends up
160
+ // in the corner.
161
+ this.classList.add(TOAST_CLASS)
162
+ if (!this.hasAttribute("role")) this.setAttribute("role", "status")
163
+ if (!this.hasAttribute("aria-live")) {
164
+ this.setAttribute("aria-live", "polite")
165
+ }
166
+ if (!this.hasAttribute("aria-atomic")) {
167
+ this.setAttribute("aria-atomic", "false")
168
+ }
169
+ if (!this.hasAttribute("aria-label")) {
170
+ this.ownsLabel = true
171
+ this.setAttribute(
172
+ "aria-label",
173
+ this.getAttribute("label") ?? DEFAULT_LABEL,
174
+ )
175
+ }
176
+ }
177
+
178
+ attributeChangedCallback(name: string, _old: string, value: string): void {
179
+ // `label` can be set after the element is in the DOM, for example by a
180
+ // framework that fills in the attributes in a later step.
181
+ if (name !== "label" || !this.ownsLabel) return
182
+ this.setAttribute("aria-label", value || DEFAULT_LABEL)
183
+ }
184
+
185
+ /** Shows a message, and returns the element it was put in. */
186
+ show(message: string, options: ShowOptions = {}): HTMLElement {
187
+ const {
188
+ color = "neutral",
189
+ duration = this.duration,
190
+ closeLabel = this.closeLabel,
191
+ } = options
192
+
193
+ const toast = document.createElement("div")
194
+ toast.className = TOAST_MESSAGE_CLASS
195
+ // An unknown colour gives no border, instead of a `data-color` without a rule.
196
+ if (isToastColor(color) && color !== "neutral") toast.dataset.color = color
197
+
198
+ const text = document.createElement("span")
199
+ text.textContent = message
200
+ toast.append(text)
201
+
202
+ const close = document.createElement("button")
203
+ close.type = "button"
204
+ close.className = TOAST_CLOSE_CLASS
205
+ close.setAttribute("aria-label", closeLabel)
206
+ close.textContent = "×"
207
+ close.addEventListener("click", () => this.dismiss(toast))
208
+ toast.append(close)
209
+
210
+ this.prepend(toast)
211
+
212
+ if (duration > 0) {
213
+ let timer = window.setTimeout(() => this.dismiss(toast), duration)
214
+
215
+ /*
216
+ * The timer stops while the user reads or is on the way to the close
217
+ * button, and only runs when neither the mouse nor focus is in the
218
+ * message. The pause used to be two independent pairs, and the mouse
219
+ * leaving restarted the clock while focus was in the message: it
220
+ * disappeared from under the user's fingers.
221
+ */
222
+ const holds = new Set<"pointer" | "focus">()
223
+ const pause = (reason: "pointer" | "focus") => {
224
+ holds.add(reason)
225
+ window.clearTimeout(timer)
226
+ }
227
+ const resume = (reason: "pointer" | "focus") => {
228
+ holds.delete(reason)
229
+ if (holds.size > 0) return
230
+ window.clearTimeout(timer)
231
+ timer = window.setTimeout(() => this.dismiss(toast), duration)
232
+ }
233
+
234
+ toast.addEventListener("mouseenter", () => pause("pointer"))
235
+ toast.addEventListener("mouseleave", () => resume("pointer"))
236
+ toast.addEventListener("focusin", () => pause("focus"))
237
+ toast.addEventListener("focusout", (event) => {
238
+ // Within the message, from the text to the button: still paused.
239
+ const next = event.relatedTarget
240
+ if (next instanceof Node && toast.contains(next)) return
241
+ resume("focus")
242
+ })
243
+ }
244
+
245
+ return toast
246
+ }
247
+
248
+ /** Removes a message. */
249
+ dismiss(toast: HTMLElement): void {
250
+ if (!this.contains(toast)) return
251
+
252
+ /*
253
+ * If focus was in the message, as on the close button after Enter, it
254
+ * would otherwise fall to `body`, and the next Tab starts at the top of
255
+ * the page. The neighbouring message is the nearest place to continue
256
+ * from: the one below, otherwise the one above.
257
+ */
258
+ const hadFocus = this.holdsFocus(toast)
259
+ const neighbour = toast.nextElementSibling ?? toast.previousElementSibling
260
+ toast.remove()
261
+ if (hadFocus) {
262
+ const next = neighbour?.querySelector<HTMLElement>(
263
+ `.${TOAST_CLOSE_CLASS}`,
264
+ )
265
+ if (next) next.focus()
266
+ else this.restoreFocus()
267
+ }
268
+
269
+ this.dispatchEvent(
270
+ new CustomEvent("toast-dismiss", { bubbles: true, composed: true }),
271
+ )
272
+ }
273
+
274
+ /** Removes all the messages. */
275
+ clear(): void {
276
+ if (this.holdsFocus(this)) this.restoreFocus()
277
+ for (const toast of this.querySelectorAll(`.${TOAST_MESSAGE_CLASS}`)) {
278
+ toast.remove()
279
+ }
280
+ }
281
+ }
282
+
283
+ export function defineLtToast(tagName: string = LT_TOAST_TAG): void {
284
+ defineElement(tagName, LtToast)
285
+ }
286
+
287
+ /**
288
+ * `element.show()`, but waits for `defineLtToast()` to have run first.
289
+ *
290
+ * See `whenUpgraded` for why: a call before the registration hit a plain
291
+ * `HTMLElement` without `show`.
292
+ */
293
+ export async function showToast(
294
+ element: Element,
295
+ message: string,
296
+ options: ShowOptions = {},
297
+ ): Promise<HTMLElement> {
298
+ const toast = await whenUpgraded<LtToast>(element)
299
+ return toast.show(message, options)
300
+ }
@@ -0,0 +1,78 @@
1
+ @layer lotek {
2
+ /* The element name and the class. The name keeps the position when a
3
+ morph or a `className` from React tears the class away, and the class
4
+ carries it when the component is registered under another name. */
5
+ lt-toast,
6
+ .lt-toast {
7
+ /* The container is fixed in the corner and lets clicks through, so it
8
+ does not block buttons beneath it when it is empty. */
9
+ position: fixed;
10
+ inset-block-end: var(--lt-toast-space, var(--lt-spacing-4));
11
+ inset-inline-end: var(--lt-toast-space, var(--lt-spacing-4));
12
+ z-index: var(--lt-toast-layer, 60);
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: var(--lt-spacing-2);
16
+ inline-size: var(
17
+ --lt-toast-width,
18
+ min(24rem, calc(100vw - var(--lt-spacing-8)))
19
+ );
20
+ pointer-events: none;
21
+ }
22
+
23
+ .lt-toast__message {
24
+ box-sizing: border-box;
25
+ display: flex;
26
+ align-items: flex-start;
27
+ justify-content: space-between;
28
+ gap: var(--lt-spacing-3);
29
+ padding: var(--lt-toast-padding, var(--lt-spacing-3) var(--lt-spacing-4));
30
+ font-family: inherit;
31
+ font-size: var(--lt-font-size-m);
32
+ line-height: var(--lt-line-height-default);
33
+ color: var(--lt-color-neutral-text-strong);
34
+ background: var(--lt-color-neutral-canvas);
35
+ border: 1px solid var(--lt-color-neutral-border-subtle);
36
+ border-inline-start: var(--lt-spacing-1) solid var(--lt-color-neutral-text);
37
+ border-radius: var(--lt-toast-radius, var(--lt-spacing-1));
38
+ box-shadow: var(--lt-shadow-overlay);
39
+ pointer-events: auto;
40
+ }
41
+
42
+ .lt-toast__message[data-color="success"] {
43
+ border-inline-start-color: var(--lt-color-success-text);
44
+ }
45
+
46
+ .lt-toast__message[data-color="warning"] {
47
+ border-inline-start-color: var(--lt-color-warning-text);
48
+ }
49
+
50
+ .lt-toast__message[data-color="danger"] {
51
+ border-inline-start-color: var(--lt-color-danger-text);
52
+ }
53
+
54
+ .lt-toast__close {
55
+ flex: none;
56
+ min-inline-size: var(--lt-spacing-6);
57
+ min-block-size: var(--lt-spacing-6);
58
+ padding: 0;
59
+ font-family: inherit;
60
+ font-size: var(--lt-font-size-l);
61
+ line-height: 1;
62
+ color: var(--lt-color-neutral-text-subtle);
63
+ background: transparent;
64
+ border: 0;
65
+ border-radius: var(--lt-spacing-1);
66
+ cursor: pointer;
67
+ }
68
+
69
+ .lt-toast__close:hover {
70
+ color: var(--lt-color-neutral-text-strong);
71
+ background: var(--lt-color-neutral-surface);
72
+ }
73
+
74
+ .lt-toast__close:focus-visible {
75
+ outline: var(--lt-focus-ring);
76
+ outline-offset: 2px;
77
+ }
78
+ }