@lo-tek/interfaces 0.0.0 → 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 +128 -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 +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 +398 -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 +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,56 @@
1
+ import { INPUT_CLASS, input } from "../input/input.js"
2
+ import {
3
+ attributes,
4
+ type FieldState,
5
+ fieldStates,
6
+ isFieldState,
7
+ } from "../shared.js"
8
+
9
+ export const SEARCH_CLASS = "lt-search" as const
10
+ /** The row with the search field and the button next to it. */
11
+ export const SEARCH_ROW_CLASS = "lt-search-row" as const
12
+
13
+ export type SearchOptions = {
14
+ /** Validation state. Default: `default`. */
15
+ state?: FieldState
16
+ }
17
+
18
+ export type SearchAttributes = {
19
+ class: `${typeof INPUT_CLASS} ${typeof SEARCH_CLASS}`
20
+ type: "search"
21
+ "data-state"?: "invalid" | "success"
22
+ "aria-invalid"?: "true"
23
+ }
24
+
25
+ /**
26
+ * The attributes for a search field.
27
+ *
28
+ * The field is an `lt-input` with room for the magnifying glass, so both
29
+ * classes are set. `type="search"` gives the browser's own cross for
30
+ * clearing the field, and on mobile a keyboard with "Search" instead of
31
+ * "Enter".
32
+ *
33
+ * ```ts
34
+ * <input {...search()} name="q" />
35
+ * ```
36
+ */
37
+ export const search = Object.assign(
38
+ ({ state = "default" }: SearchOptions = {}): SearchAttributes => {
39
+ const base = input({ type: "search", state })
40
+ return attributes(
41
+ {
42
+ ...base,
43
+ class: `${INPUT_CLASS} ${SEARCH_CLASS}` as const,
44
+ type: "search" as const,
45
+ "data-variant": undefined,
46
+ },
47
+ base,
48
+ ) as SearchAttributes
49
+ },
50
+ {
51
+ /** The class on the row with the field and the button. */
52
+ row: SEARCH_ROW_CLASS,
53
+ states: fieldStates,
54
+ isState: isFieldState,
55
+ },
56
+ )
@@ -0,0 +1,321 @@
1
+ @layer lotek {
2
+ .lt-select {
3
+ /* The design system cannot assume the consumer has a CSS reset. Without
4
+ border-box, padding and border are added outside the width, and the
5
+ field becomes wider than the box it sits in. */
6
+ box-sizing: border-box;
7
+
8
+ display: block;
9
+ width: var(--lt-select-width, 100%);
10
+ padding: var(--lt-select-padding, var(--lt-spacing-2) var(--lt-spacing-3));
11
+ /* The end side is the room for the arrow, and it is fixed: the arrow is
12
+ drawn a fixed distance from the edge, so less room would put the text
13
+ on top of it. So `--lt-select-padding` applies to everything except
14
+ this side here. With `data-picker="styled"` the arrow takes real
15
+ room, and there the variable applies to all four sides. */
16
+ padding-inline-end: calc(var(--lt-spacing-4) + var(--lt-spacing-2));
17
+ font-family: inherit;
18
+ font-size: var(--lt-font-size-m);
19
+ line-height: var(--lt-line-height-default);
20
+ color: var(--lt-color-neutral-text-strong);
21
+ background-color: var(--lt-color-neutral-canvas);
22
+ border: 1px solid var(--lt-color-neutral-border);
23
+ border-radius: var(--lt-select-radius, var(--lt-spacing-1));
24
+ appearance: none;
25
+ background-image:
26
+ linear-gradient(
27
+ 45deg,
28
+ transparent 50%,
29
+ var(--lt-color-neutral-text-subtle) 50%
30
+ ),
31
+ linear-gradient(
32
+ 135deg,
33
+ var(--lt-color-neutral-text-subtle) 50%,
34
+ transparent 50%
35
+ );
36
+ background-position:
37
+ calc(100% - 1rem) calc(50% - 0.1rem),
38
+ calc(100% - 0.75rem) calc(50% - 0.1rem);
39
+ background-size:
40
+ 0.35rem 0.35rem,
41
+ 0.35rem 0.35rem;
42
+ background-repeat: no-repeat;
43
+ }
44
+
45
+ /* The arrow is two gradients, and `background-position` has no logical
46
+ variant. The mirrored version is the same two pieces, moved to the left
47
+ edge and swapped: the one that was outermost is now innermost. From the
48
+ left the position is given to the piece's left edge, from the right with
49
+ `calc(100% - ...)` to the same point on the other side, so the distances
50
+ are the same numbers. With 0.35rem subtracted the arrow sat 6 pixels
51
+ from the edge instead of 12. */
52
+ .lt-select:dir(rtl) {
53
+ background-position:
54
+ 0.75rem calc(50% - 0.1rem),
55
+ 1rem calc(50% - 0.1rem);
56
+ }
57
+
58
+ .lt-select:hover {
59
+ border-color: var(--lt-color-neutral-border-strong);
60
+ }
61
+
62
+ .lt-select:focus-visible {
63
+ outline: var(--lt-focus-ring);
64
+ outline-offset: 2px;
65
+ border-color: var(--lt-color-accent-border);
66
+ }
67
+
68
+ .lt-select:disabled,
69
+ .lt-select[aria-disabled="true"] {
70
+ background-color: var(--lt-color-disabled-surface);
71
+ color: var(--lt-color-disabled-text);
72
+ border-color: var(--lt-color-disabled-surface);
73
+ /* No pointer-events: none here. The browser already blocks a disabled
74
+ field, and pointer-events would only stop the cursor from showing. The
75
+ element is then not hit by the mouse at all. */
76
+ cursor: not-allowed;
77
+ }
78
+
79
+ /* aria-disabled is only information for screen readers; the element still
80
+ works. Here pointer-events stops the mouse, at the cost of the cursor.
81
+ If the field should really be off, use `disabled` or `readonly`. */
82
+ .lt-select[aria-disabled="true"] {
83
+ pointer-events: none;
84
+ }
85
+
86
+ .lt-select[data-state="invalid"] {
87
+ border-color: var(--lt-color-danger-text);
88
+ background-color: var(--lt-color-danger-surface);
89
+ }
90
+
91
+ .lt-select[data-state="invalid"]:focus-visible {
92
+ outline-color: var(--lt-color-danger-text);
93
+ }
94
+
95
+ .lt-select[data-state="success"] {
96
+ border-color: var(--lt-color-success-text);
97
+ background-color: var(--lt-color-success-surface);
98
+ }
99
+
100
+ .lt-select[data-state="success"]:focus-visible {
101
+ outline-color: var(--lt-color-success-text);
102
+ }
103
+
104
+ /* Disabled wins over a state. `[data-state]` used to come after
105
+ `:disabled` with the same specificity, and an invalid field in a form
106
+ that was disabled during submission looked red, not disabled. */
107
+ .lt-select:is(:disabled, [aria-disabled="true"])[data-state] {
108
+ background-color: var(--lt-color-disabled-surface);
109
+ color: var(--lt-color-disabled-text);
110
+ border-color: var(--lt-color-disabled-surface);
111
+ }
112
+
113
+ /*
114
+ * The dropdown list is a window from the operating system, and no CSS
115
+ * reaches into it. `appearance: base-select` asks the browser to draw it
116
+ * inside the page instead: `::picker(select)` becomes a normal element,
117
+ * and each `<option>` can get the design system's colours and spacing.
118
+ *
119
+ * It is optional, and turned on with `data-picker="styled"`. Firefox 150
120
+ * does not have it, and there the field keeps the browser's own list. It
121
+ * is the same component with two looks, not something that breaks.
122
+ */
123
+ @supports (appearance: base-select) {
124
+ .lt-select[data-picker="styled"],
125
+ .lt-select[data-picker="styled"]::picker(select) {
126
+ appearance: base-select;
127
+ }
128
+
129
+ /* The button now draws its own arrow with `::picker-icon`, so the
130
+ gradients must go. The arrow takes real room in the layout, and the
131
+ room we kept for the drawn arrow would then be there twice. */
132
+ .lt-select[data-picker="styled"] {
133
+ display: flex;
134
+ align-items: center;
135
+ justify-content: space-between;
136
+ gap: var(--lt-spacing-2);
137
+ background-image: none;
138
+ padding: var(
139
+ --lt-select-padding,
140
+ var(--lt-spacing-2) var(--lt-spacing-3)
141
+ );
142
+ }
143
+
144
+ /* The text in the button is the current choice. Long choices should be
145
+ clipped, not push the arrow out of the field. Without
146
+ `min-inline-size: 0` it does not shrink, because flex gives it the
147
+ minimum width of its content.
148
+
149
+ The browser does not create <selectedcontent> by itself. It only
150
+ exists when the author writes
151
+ `<button><selectedcontent></selectedcontent></button>` first in the
152
+ select. Without it the choice is drawn in the browser's own shadow
153
+ DOM, which no rule reaches, and a long choice wraps over several
154
+ lines. `white-space: nowrap` on the select does not help: the text
155
+ does not shrink there, and the arrow is pushed out of the field. */
156
+ /* biome-ignore lint/correctness/noUnknownTypeSelector: <selectedcontent> is
157
+ the element that shows the selected option in a customisable
158
+ dropdown list. Biome does not know it yet. */
159
+ .lt-select[data-picker="styled"] selectedcontent {
160
+ min-inline-size: 0;
161
+ overflow: hidden;
162
+ white-space: nowrap;
163
+ text-overflow: ellipsis;
164
+ }
165
+
166
+ .lt-select[data-picker="styled"]::picker-icon {
167
+ color: var(--lt-color-neutral-text-subtle);
168
+ transition: rotate 120ms ease;
169
+ }
170
+
171
+ .lt-select[data-picker="styled"]:open::picker-icon {
172
+ rotate: 180deg;
173
+ }
174
+
175
+ .lt-select[data-picker="styled"]::picker(select) {
176
+ box-sizing: border-box;
177
+ min-inline-size: anchor-size(self-inline);
178
+ /* The list should fill the room that is actually left down to the
179
+ edge of the window, not stretch beyond it. `stretch` is exactly
180
+ that room, and the margin is subtracted. */
181
+ max-block-size: stretch;
182
+ margin-block: var(--lt-select-viewport-margin, var(--lt-spacing-2));
183
+ padding: 0;
184
+ overflow-y: auto;
185
+ background: var(--lt-color-neutral-canvas);
186
+ border: 1px solid var(--lt-color-neutral-border);
187
+ border-radius: var(--lt-select-radius, var(--lt-spacing-1));
188
+ box-shadow: var(--lt-shadow-overlay);
189
+ /* If there is no room below the field, the list goes above it. */
190
+ position-try-fallbacks: flip-block;
191
+ opacity: 0;
192
+ transition:
193
+ opacity 120ms ease,
194
+ overlay 120ms allow-discrete,
195
+ display 120ms allow-discrete;
196
+ }
197
+
198
+ .lt-select[data-picker="styled"]:open::picker(select) {
199
+ opacity: 1;
200
+ }
201
+
202
+ @starting-style {
203
+ .lt-select[data-picker="styled"]:open::picker(select) {
204
+ opacity: 0;
205
+ }
206
+ }
207
+
208
+ /* The height can only be animated where `calc-size` exists, since `auto`
209
+ is otherwise not a value that can be transitioned to. Without support
210
+ the height stays `auto`, and only the opacity fades. */
211
+ @supports (block-size: calc-size(auto, size)) {
212
+ .lt-select[data-picker="styled"]::picker(select) {
213
+ block-size: 0;
214
+ transition:
215
+ opacity 120ms ease,
216
+ block-size 120ms ease,
217
+ overlay 120ms allow-discrete,
218
+ display 120ms allow-discrete;
219
+ }
220
+
221
+ .lt-select[data-picker="styled"]:open::picker(select) {
222
+ block-size: calc-size(auto, size);
223
+ }
224
+
225
+ @starting-style {
226
+ .lt-select[data-picker="styled"]:open::picker(select) {
227
+ block-size: 0;
228
+ }
229
+ }
230
+ }
231
+
232
+ /* The group heading is not an element we can target when it comes from
233
+ `label=`, only from `<legend>`. So it is set on the group, and
234
+ inherited down: the size and colour set it apart from the options,
235
+ and `text-indent` gives it the same left edge as the text in an
236
+ option. The options set those three back to their own values. */
237
+ .lt-select[data-picker="styled"] optgroup {
238
+ padding-block: var(--lt-spacing-2) var(--lt-spacing-1);
239
+ font-size: var(--lt-font-size-s);
240
+ color: var(--lt-color-neutral-text-subtle);
241
+ text-indent: var(--lt-spacing-3);
242
+ }
243
+
244
+ .lt-select[data-picker="styled"] option {
245
+ text-indent: 0;
246
+ display: flex;
247
+ align-items: center;
248
+ gap: var(--lt-spacing-2);
249
+ padding: var(
250
+ --lt-select-option-padding,
251
+ var(--lt-spacing-2) var(--lt-spacing-3)
252
+ );
253
+ font-family: inherit;
254
+ font-size: var(--lt-font-size-m);
255
+ line-height: var(--lt-line-height-default);
256
+ color: var(--lt-color-neutral-text-strong);
257
+ background: var(--lt-color-neutral-canvas);
258
+ cursor: pointer;
259
+ }
260
+
261
+ /* The tick comes after the text, not before it, so all the options get
262
+ the text in the same place. */
263
+ .lt-select[data-picker="styled"] option::checkmark {
264
+ order: 1;
265
+ margin-inline-start: auto;
266
+ }
267
+
268
+ .lt-select[data-picker="styled"] option:hover {
269
+ background: var(--lt-color-accent-surface);
270
+ }
271
+
272
+ .lt-select[data-picker="styled"] option:checked {
273
+ background: var(--lt-color-accent-fill);
274
+ color: var(--lt-color-accent-content);
275
+ }
276
+
277
+ /* Focus is shown with a border, not with a surface. The arrow keys move
278
+ focus between the options, and the selected one already has its own
279
+ surface. A different background colour would therefore disappear
280
+ exactly where the user is. */
281
+ .lt-select[data-picker="styled"] option:focus {
282
+ outline: var(--lt-focus-ring);
283
+ outline-offset: -2px;
284
+ }
285
+
286
+ .lt-select[data-picker="styled"] option:checked:focus {
287
+ outline-color: var(--lt-color-accent-content);
288
+ }
289
+
290
+ .lt-select[data-picker="styled"] option:disabled {
291
+ color: var(--lt-color-disabled-text);
292
+ background: var(--lt-color-disabled-surface);
293
+ cursor: not-allowed;
294
+ }
295
+
296
+ @media (prefers-reduced-motion: reduce) {
297
+ .lt-select[data-picker="styled"]::picker(select),
298
+ .lt-select[data-picker="styled"]::picker-icon {
299
+ transition: none;
300
+ }
301
+ }
302
+ }
303
+
304
+ /* High contrast: the arrow is two gradients, and they are removed in the
305
+ mode. The browser's own arrow is then better than none. The list drawn
306
+ in the page falls back for the same reason: the browser's own list uses
307
+ the system colours, and they are the only ones guaranteed to be
308
+ readable in the mode. */
309
+ @media (forced-colors: active) {
310
+ .lt-select,
311
+ .lt-select[data-picker="styled"] {
312
+ display: block;
313
+ appearance: auto;
314
+ background-image: none;
315
+ padding: var(
316
+ --lt-select-padding,
317
+ var(--lt-spacing-2) var(--lt-spacing-3)
318
+ );
319
+ }
320
+ }
321
+ }
@@ -0,0 +1,63 @@
1
+ import {
2
+ attributes,
3
+ createGuard,
4
+ type FieldState,
5
+ fieldStates,
6
+ isFieldState,
7
+ type NonDefaultFieldState,
8
+ } from "../shared.js"
9
+
10
+ export const SELECT_CLASS = "lt-select" as const
11
+
12
+ /**
13
+ * Who draws the dropdown list.
14
+ *
15
+ * `auto` is the browser's own list, a window from the operating system that
16
+ * cannot be styled. `styled` asks the browser to draw it inside the page
17
+ * instead, so it gets the design system's colours, spacing and shadow.
18
+ */
19
+ export const selectPickers = ["auto", "styled"] as const
20
+ export type SelectPicker = (typeof selectPickers)[number]
21
+ export type NonDefaultSelectPicker = Exclude<SelectPicker, "auto">
22
+ export const isSelectPicker = createGuard(selectPickers)
23
+
24
+ export type SelectOptions = {
25
+ /** Validation state. Default: `default`. */
26
+ state?: FieldState
27
+ /** Who draws the dropdown list. Default: `auto`. */
28
+ picker?: SelectPicker
29
+ }
30
+
31
+ export type SelectAttributes = {
32
+ class: typeof SELECT_CLASS
33
+ "data-state"?: NonDefaultFieldState
34
+ "data-picker"?: NonDefaultSelectPicker
35
+ "aria-invalid"?: "true"
36
+ }
37
+
38
+ /**
39
+ * The attributes for a dropdown list.
40
+ *
41
+ * ```ts
42
+ * <select {...select({ state: "invalid" })} />
43
+ * <select {...select({ picker: "styled" })} />
44
+ * ```
45
+ */
46
+ export const select = Object.assign(
47
+ ({
48
+ state = "default",
49
+ picker = "auto",
50
+ }: SelectOptions = {}): SelectAttributes =>
51
+ attributes({
52
+ class: SELECT_CLASS,
53
+ "data-state": state === "default" ? undefined : state,
54
+ "data-picker": picker === "auto" ? undefined : picker,
55
+ "aria-invalid": state === "invalid" ? ("true" as const) : undefined,
56
+ }),
57
+ {
58
+ states: fieldStates,
59
+ isState: isFieldState,
60
+ pickers: selectPickers,
61
+ isPicker: isSelectPicker,
62
+ },
63
+ )
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Shared building blocks for the component API.
3
+ *
4
+ * Each component exports one function that takes an object of options and
5
+ * returns the attributes you spread onto the element. The shape is the same
6
+ * for all of them, so you only need to learn it once.
7
+ */
8
+
9
+ /**
10
+ * The attributes each set manages, including those that are off right now.
11
+ *
12
+ * `attributes()` drops the keys without a value, and with them the record of
13
+ * which attributes the builder function has a say in. `setAttributes` needs
14
+ * exactly that: `lt.switch()` manages `disabled` and must be able to remove
15
+ * it, while `lt.button()` has never written it and must leave it alone. The
16
+ * list is kept in a map beside the object, not on it, so spreading, JSON and
17
+ * React see the same as before.
18
+ */
19
+ const managed = new WeakMap<object, readonly string[]>()
20
+
21
+ /** The names the set manages, or `undefined` for a set we did not create. */
22
+ export function managedNames(set: object): readonly string[] | undefined {
23
+ return managed.get(set)
24
+ }
25
+
26
+ /**
27
+ * Gives a set rewritten from another the same managed names.
28
+ *
29
+ * `/react` renames `class` to `className` in a new object, and the new object
30
+ * was not in the map. `lt.setAttributes` from there then only cleaned up
31
+ * `data-*`, and `aria-invalid` stayed after the field was corrected. The
32
+ * names are given as they are spelled in the new set.
33
+ *
34
+ * For `/react` and other entry points that rewrite the sets, not for apps: a
35
+ * set from `lt.*` is already managed.
36
+ */
37
+ export function rememberManaged(set: object, names: readonly string[]): void {
38
+ managed.set(set, names)
39
+ }
40
+
41
+ /**
42
+ * Removes attributes without a value, so `{...spread}` does not set empty
43
+ * fields.
44
+ *
45
+ * When the set builds on another, as `lt.search()` does on `lt.input()`, the
46
+ * other is passed in as `based`. The spread has already lost the keys that
47
+ * were off, and without this the search field only managed `data-state` when
48
+ * it happened to be set: it could be set, but not removed.
49
+ */
50
+ export function attributes<T extends Record<string, unknown>>(
51
+ values: T,
52
+ ...based: object[]
53
+ ): T {
54
+ const result = {} as T
55
+ for (const [name, value] of Object.entries(values)) {
56
+ if (value !== undefined) {
57
+ ;(result as Record<string, unknown>)[name] = value
58
+ }
59
+ }
60
+ const names = new Set(Object.keys(values))
61
+ for (const set of based) {
62
+ for (const name of managed.get(set) ?? []) names.add(name)
63
+ }
64
+ managed.set(result, [...names])
65
+ return result
66
+ }
67
+
68
+ /** Creates a type guard for a list of allowed strings. */
69
+ export function createGuard<T extends string>(allowed: readonly T[]) {
70
+ return (value: string): value is T =>
71
+ (allowed as readonly string[]).includes(value)
72
+ }
73
+
74
+ /**
75
+ * Validation state for form controls.
76
+ *
77
+ * Input, textarea and select each had an identical type of their own. Now
78
+ * there is one, so a value can pass between them without conversion.
79
+ */
80
+ export const fieldStates = ["default", "invalid", "success"] as const
81
+ export type FieldState = (typeof fieldStates)[number]
82
+ export type NonDefaultFieldState = Exclude<FieldState, "default">
83
+ export const isFieldState = createGuard(fieldStates)
84
+
85
+ /** How a required field is marked in its label. */
86
+ export const requiredMarkers = ["symbol", "text"] as const
87
+ export type RequiredMarker = (typeof requiredMarkers)[number]
88
+ export const isRequiredMarker = createGuard(requiredMarkers)
89
+
90
+ /**
91
+ * Warns once per message, in the console.
92
+ *
93
+ * The builder functions are pure functions without an element to attach the
94
+ * message to, so they cannot use `warnAboutMarkup`. The deduplication is
95
+ * needed all the same: an app that renders a list of fields would otherwise
96
+ * say the same thing per field and per render.
97
+ */
98
+ const said = new Set<string>()
99
+
100
+ export function warnOnce(message: string): void {
101
+ if (typeof console === "undefined" || said.has(message)) return
102
+ said.add(message)
103
+ console.warn(message)
104
+ }
105
+
106
+ let idCounter = 0
107
+
108
+ /**
109
+ * Creates an id that is unique within the document.
110
+ *
111
+ * Safe only when the markup is rendered once. When the same field is rendered
112
+ * both on a server and in the browser, the two runs give two different ids,
113
+ * and the wiring is broken until the framework has corrected it.
114
+ */
115
+ export function createFieldId(): string {
116
+ idCounter += 1
117
+ return `lt-field-${idCounter}-${Math.random().toString(36).slice(2, 8)}`
118
+ }
119
+
120
+ /**
121
+ * The keys a builder function creates ids from, checked.
122
+ *
123
+ * A key is the same as `key` in React: a fixed value from the data, such as
124
+ * a record id. The id is the same on every render, and a server update
125
+ * recognises the element. An index is not a key: if the server inserts a row
126
+ * first, the same index points to something else.
127
+ *
128
+ * Throws, instead of warning and creating a fallback as `idOrFallback` does:
129
+ * an empty key, one with whitespace or two identical ones give ids that are
130
+ * empty, invalid or identical, and there is no fallback that is right.
131
+ * Whitespace is what JavaScript counts as whitespace (`\s`), which the
132
+ * Kotlin version spells out.
133
+ */
134
+ export function checkedKeys(builder: string, keys: unknown): string[] {
135
+ if (!Array.isArray(keys)) {
136
+ throw new Error(
137
+ `${builder}: keys is missing. Pass a list with one key per element, taken from the data, such as a record id.`,
138
+ )
139
+ }
140
+ const seen = new Set<string>()
141
+ for (const key of keys) {
142
+ if (typeof key !== "string") {
143
+ throw new Error(
144
+ `${builder}: a key in keys is not a string but ${typeof key}. The keys become ids; turn numbers into strings, as in String(recordId).`,
145
+ )
146
+ }
147
+ if (key === "") {
148
+ throw new Error(
149
+ `${builder}: a key in keys is empty. The keys become ids, and each must be a fixed value from the data.`,
150
+ )
151
+ }
152
+ if (/\s/.test(key)) {
153
+ throw new Error(
154
+ `${builder}: the key "${key}" contains whitespace. The keys become ids, and an id cannot contain whitespace.`,
155
+ )
156
+ }
157
+ if (seen.has(key)) {
158
+ throw new Error(
159
+ `${builder}: the key "${key}" appears twice in keys. The keys become ids, and two equal ids point to the same element.`,
160
+ )
161
+ }
162
+ seen.add(key)
163
+ }
164
+ return keys
165
+ }
166
+
167
+ /**
168
+ * The id the builder function received, or a fallback with a warning.
169
+ *
170
+ * Every builder function that takes an id requires it in the type. That is
171
+ * not enough on its own: a consumer without TypeScript sees no type, and
172
+ * plain HTML with `<script type="module">` is a first-class way to use
173
+ * LO-TEK. Without the fallback the ids became strings like `undefined-list`,
174
+ * `aria-controls` pointed there, and the wiring was broken in a way that
175
+ * looked valid.
176
+ *
177
+ * The empty string counts as no id. `lt.field({ id: "" })` gave `for=""` and
178
+ * `help.id="-help"`, the same problem without anything saying so.
179
+ *
180
+ * The name of the builder function is in the message. Without it the
181
+ * suggestion field said "lt.field()" and sent the developer to the wrong
182
+ * place.
183
+ */
184
+ export function idOrFallback(
185
+ builder: string,
186
+ id: string | undefined,
187
+ option = "id",
188
+ ): string {
189
+ if (typeof id === "string" && id.trim() !== "") return id
190
+
191
+ warnOnce(
192
+ `${builder}: no ${option} given, so a random one is created, and two runs ` +
193
+ "create two different ones. Rendered on a server and in the browser, the " +
194
+ "wiring points to something that does not exist, and a server that sends " +
195
+ "the region again with a new id makes the morph replace the elements and " +
196
+ `lose what the user did in them. Pass ${option}, in React from useId().`,
197
+ )
198
+ return createFieldId()
199
+ }
@@ -0,0 +1,62 @@
1
+ @layer lotek {
2
+ .lt-skeleton {
3
+ box-sizing: border-box;
4
+ display: block;
5
+ inline-size: 100%;
6
+ block-size: var(--lt-skeleton-height, var(--lt-spacing-4));
7
+ border-radius: var(--lt-skeleton-radius, var(--lt-spacing-1));
8
+ background: var(--lt-color-neutral-surface);
9
+ /* The shimmer is a gradient moving across the surface, not an opacity
10
+ that pulses. The contrast against the page surface then stays the
11
+ same all the way. */
12
+ background-image: linear-gradient(
13
+ 90deg,
14
+ transparent 0%,
15
+ var(--lt-color-neutral-canvas) 50%,
16
+ transparent 100%
17
+ );
18
+ background-size: 200% 100%;
19
+ background-repeat: no-repeat;
20
+ animation: lt-skeleton-shimmer 1600ms ease-in-out infinite;
21
+ }
22
+
23
+ /* The text lines are low, so the default radius is smaller. The variable
24
+ applies here too. */
25
+ .lt-skeleton[data-variant="text"] {
26
+ block-size: var(--lt-font-size-m);
27
+ border-radius: var(--lt-skeleton-radius, var(--lt-spacing-0-5));
28
+ }
29
+
30
+ .lt-skeleton[data-variant="circle"] {
31
+ inline-size: var(--lt-skeleton-height, var(--lt-spacing-10));
32
+ block-size: var(--lt-skeleton-height, var(--lt-spacing-10));
33
+ border-radius: 50%;
34
+ }
35
+
36
+ @keyframes lt-skeleton-shimmer {
37
+ from {
38
+ background-position: 150% 0;
39
+ }
40
+ to {
41
+ background-position: -50% 0;
42
+ }
43
+ }
44
+
45
+ @media (prefers-reduced-motion: reduce) {
46
+ .lt-skeleton {
47
+ animation: none;
48
+ background-image: none;
49
+ }
50
+ }
51
+
52
+ /* High contrast: the surface and the shimmer are backgrounds, and both are
53
+ forced to the page surface colour. Without a border the placeholder was
54
+ invisible. */
55
+ @media (forced-colors: active) {
56
+ .lt-skeleton {
57
+ border: 1px solid GrayText;
58
+ animation: none;
59
+ background-image: none;
60
+ }
61
+ }
62
+ }