@lo-tek/interfaces 0.0.0-stage → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. package/CHANGELOG.md +3254 -0
  2. package/LICENSE +21 -0
  3. package/README.md +129 -2
  4. package/agent/astro.md +400 -0
  5. package/agent/bundles.md +429 -0
  6. package/agent/datastar.md +409 -0
  7. package/agent/html.md +388 -0
  8. package/agent/react.md +440 -0
  9. package/agent/templates.md +472 -0
  10. package/core/lotek-core.wasm +0 -0
  11. package/core/lotek.wasm +0 -0
  12. package/dist/cli.d.ts +12 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +14 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/components/css/accordion/accordion.d.ts +37 -0
  17. package/dist/components/css/accordion/accordion.d.ts.map +1 -0
  18. package/dist/components/css/accordion/accordion.js +31 -0
  19. package/dist/components/css/accordion/accordion.js.map +1 -0
  20. package/dist/components/css/alert/alert.d.ts +35 -0
  21. package/dist/components/css/alert/alert.d.ts.map +1 -0
  22. package/dist/components/css/alert/alert.js +29 -0
  23. package/dist/components/css/alert/alert.js.map +1 -0
  24. package/dist/components/css/avatar/avatar.d.ts +40 -0
  25. package/dist/components/css/avatar/avatar.d.ts.map +1 -0
  26. package/dist/components/css/avatar/avatar.js +30 -0
  27. package/dist/components/css/avatar/avatar.js.map +1 -0
  28. package/dist/components/css/badge/badge.d.ts +24 -0
  29. package/dist/components/css/badge/badge.d.ts.map +1 -0
  30. package/dist/components/css/badge/badge.js +24 -0
  31. package/dist/components/css/badge/badge.js.map +1 -0
  32. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +26 -0
  33. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts.map +1 -0
  34. package/dist/components/css/breadcrumbs/breadcrumbs.js +24 -0
  35. package/dist/components/css/breadcrumbs/breadcrumbs.js.map +1 -0
  36. package/dist/components/css/button/button.d.ts +26 -0
  37. package/dist/components/css/button/button.d.ts.map +1 -0
  38. package/dist/components/css/button/button.js +25 -0
  39. package/dist/components/css/button/button.js.map +1 -0
  40. package/dist/components/css/card/card.d.ts +36 -0
  41. package/dist/components/css/card/card.d.ts.map +1 -0
  42. package/dist/components/css/card/card.js +28 -0
  43. package/dist/components/css/card/card.js.map +1 -0
  44. package/dist/components/css/checkbox/checkbox.d.ts +32 -0
  45. package/dist/components/css/checkbox/checkbox.d.ts.map +1 -0
  46. package/dist/components/css/checkbox/checkbox.js +27 -0
  47. package/dist/components/css/checkbox/checkbox.js.map +1 -0
  48. package/dist/components/css/divider/divider.d.ts +29 -0
  49. package/dist/components/css/divider/divider.d.ts.map +1 -0
  50. package/dist/components/css/divider/divider.js +23 -0
  51. package/dist/components/css/divider/divider.js.map +1 -0
  52. package/dist/components/css/error-text/error-text.d.ts +24 -0
  53. package/dist/components/css/error-text/error-text.d.ts.map +1 -0
  54. package/dist/components/css/error-text/error-text.js +18 -0
  55. package/dist/components/css/error-text/error-text.js.map +1 -0
  56. package/dist/components/css/fieldset/fieldset.d.ts +55 -0
  57. package/dist/components/css/fieldset/fieldset.d.ts.map +1 -0
  58. package/dist/components/css/fieldset/fieldset.js +39 -0
  59. package/dist/components/css/fieldset/fieldset.js.map +1 -0
  60. package/dist/components/css/file-upload/file-upload.d.ts +41 -0
  61. package/dist/components/css/file-upload/file-upload.d.ts.map +1 -0
  62. package/dist/components/css/file-upload/file-upload.js +32 -0
  63. package/dist/components/css/file-upload/file-upload.js.map +1 -0
  64. package/dist/components/css/heading/heading.d.ts +29 -0
  65. package/dist/components/css/heading/heading.d.ts.map +1 -0
  66. package/dist/components/css/heading/heading.js +23 -0
  67. package/dist/components/css/heading/heading.js.map +1 -0
  68. package/dist/components/css/help-text/help-text.d.ts +24 -0
  69. package/dist/components/css/help-text/help-text.d.ts.map +1 -0
  70. package/dist/components/css/help-text/help-text.js +23 -0
  71. package/dist/components/css/help-text/help-text.js.map +1 -0
  72. package/dist/components/css/icon/icon.d.ts +34 -0
  73. package/dist/components/css/icon/icon.d.ts.map +1 -0
  74. package/dist/components/css/icon/icon.js +25 -0
  75. package/dist/components/css/icon/icon.js.map +1 -0
  76. package/dist/components/css/input/input.d.ts +40 -0
  77. package/dist/components/css/input/input.d.ts.map +1 -0
  78. package/dist/components/css/input/input.js +45 -0
  79. package/dist/components/css/input/input.js.map +1 -0
  80. package/dist/components/css/label/label.d.ts +33 -0
  81. package/dist/components/css/label/label.d.ts.map +1 -0
  82. package/dist/components/css/label/label.js +23 -0
  83. package/dist/components/css/label/label.js.map +1 -0
  84. package/dist/components/css/link/link.d.ts +28 -0
  85. package/dist/components/css/link/link.d.ts.map +1 -0
  86. package/dist/components/css/link/link.js +20 -0
  87. package/dist/components/css/link/link.js.map +1 -0
  88. package/dist/components/css/list/list.d.ts +31 -0
  89. package/dist/components/css/list/list.d.ts.map +1 -0
  90. package/dist/components/css/list/list.js +25 -0
  91. package/dist/components/css/list/list.js.map +1 -0
  92. package/dist/components/css/pagination/pagination.d.ts +27 -0
  93. package/dist/components/css/pagination/pagination.d.ts.map +1 -0
  94. package/dist/components/css/pagination/pagination.js +25 -0
  95. package/dist/components/css/pagination/pagination.js.map +1 -0
  96. package/dist/components/css/paragraph/paragraph.d.ts +36 -0
  97. package/dist/components/css/paragraph/paragraph.d.ts.map +1 -0
  98. package/dist/components/css/paragraph/paragraph.js +26 -0
  99. package/dist/components/css/paragraph/paragraph.js.map +1 -0
  100. package/dist/components/css/progress/progress.d.ts +53 -0
  101. package/dist/components/css/progress/progress.d.ts.map +1 -0
  102. package/dist/components/css/progress/progress.js +38 -0
  103. package/dist/components/css/progress/progress.js.map +1 -0
  104. package/dist/components/css/radio/radio.d.ts +32 -0
  105. package/dist/components/css/radio/radio.d.ts.map +1 -0
  106. package/dist/components/css/radio/radio.js +27 -0
  107. package/dist/components/css/radio/radio.js.map +1 -0
  108. package/dist/components/css/search/search.d.ts +34 -0
  109. package/dist/components/css/search/search.d.ts.map +1 -0
  110. package/dist/components/css/search/search.js +32 -0
  111. package/dist/components/css/search/search.js.map +1 -0
  112. package/dist/components/css/select/select.d.ts +40 -0
  113. package/dist/components/css/select/select.d.ts.map +1 -0
  114. package/dist/components/css/select/select.js +31 -0
  115. package/dist/components/css/select/select.js.map +1 -0
  116. package/dist/components/css/shared.d.ts +90 -0
  117. package/dist/components/css/shared.d.ts.map +1 -0
  118. package/dist/components/css/shared.js +167 -0
  119. package/dist/components/css/shared.js.map +1 -0
  120. package/dist/components/css/skeleton/skeleton.d.ts +29 -0
  121. package/dist/components/css/skeleton/skeleton.d.ts.map +1 -0
  122. package/dist/components/css/skeleton/skeleton.js +23 -0
  123. package/dist/components/css/skeleton/skeleton.js.map +1 -0
  124. package/dist/components/css/skip-link/skip-link.d.ts +18 -0
  125. package/dist/components/css/skip-link/skip-link.d.ts.map +1 -0
  126. package/dist/components/css/skip-link/skip-link.js +16 -0
  127. package/dist/components/css/skip-link/skip-link.js.map +1 -0
  128. package/dist/components/css/spinner/spinner.d.ts +33 -0
  129. package/dist/components/css/spinner/spinner.d.ts.map +1 -0
  130. package/dist/components/css/spinner/spinner.js +25 -0
  131. package/dist/components/css/spinner/spinner.js.map +1 -0
  132. package/dist/components/css/sr-only/sr-only.d.ts +17 -0
  133. package/dist/components/css/sr-only/sr-only.d.ts.map +1 -0
  134. package/dist/components/css/sr-only/sr-only.js +15 -0
  135. package/dist/components/css/sr-only/sr-only.js.map +1 -0
  136. package/dist/components/css/switch/switch.d.ts +37 -0
  137. package/dist/components/css/switch/switch.d.ts.map +1 -0
  138. package/dist/components/css/switch/switch.js +29 -0
  139. package/dist/components/css/switch/switch.js.map +1 -0
  140. package/dist/components/css/table/table.d.ts +76 -0
  141. package/dist/components/css/table/table.d.ts.map +1 -0
  142. package/dist/components/css/table/table.js +59 -0
  143. package/dist/components/css/table/table.js.map +1 -0
  144. package/dist/components/css/tag/tag.d.ts +35 -0
  145. package/dist/components/css/tag/tag.d.ts.map +1 -0
  146. package/dist/components/css/tag/tag.js +27 -0
  147. package/dist/components/css/tag/tag.js.map +1 -0
  148. package/dist/components/css/textarea/textarea.d.ts +23 -0
  149. package/dist/components/css/textarea/textarea.d.ts.map +1 -0
  150. package/dist/components/css/textarea/textarea.js +18 -0
  151. package/dist/components/css/textarea/textarea.js.map +1 -0
  152. package/dist/components/css/toggle-group/toggle-group.d.ts +31 -0
  153. package/dist/components/css/toggle-group/toggle-group.d.ts.map +1 -0
  154. package/dist/components/css/toggle-group/toggle-group.js +29 -0
  155. package/dist/components/css/toggle-group/toggle-group.js.map +1 -0
  156. package/dist/components/css/tooltip/tooltip.d.ts +31 -0
  157. package/dist/components/css/tooltip/tooltip.d.ts.map +1 -0
  158. package/dist/components/css/tooltip/tooltip.js +29 -0
  159. package/dist/components/css/tooltip/tooltip.js.map +1 -0
  160. package/dist/components/default-texts.d.ts +35 -0
  161. package/dist/components/default-texts.d.ts.map +1 -0
  162. package/dist/components/default-texts.js +35 -0
  163. package/dist/components/default-texts.js.map +1 -0
  164. package/dist/components/events.d.ts +77 -0
  165. package/dist/components/events.d.ts.map +1 -0
  166. package/dist/components/events.js +3 -0
  167. package/dist/components/events.js.map +1 -0
  168. package/dist/components/frame/dialog/dialog.d.ts +122 -0
  169. package/dist/components/frame/dialog/dialog.d.ts.map +1 -0
  170. package/dist/components/frame/dialog/dialog.js +112 -0
  171. package/dist/components/frame/dialog/dialog.js.map +1 -0
  172. package/dist/components/frame/dialog/lt-dialog.d.ts +104 -0
  173. package/dist/components/frame/dialog/lt-dialog.d.ts.map +1 -0
  174. package/dist/components/frame/dialog/lt-dialog.js +431 -0
  175. package/dist/components/frame/dialog/lt-dialog.js.map +1 -0
  176. package/dist/components/frame/error-summary/error-summary.d.ts +40 -0
  177. package/dist/components/frame/error-summary/error-summary.d.ts.map +1 -0
  178. package/dist/components/frame/error-summary/error-summary.js +32 -0
  179. package/dist/components/frame/error-summary/error-summary.js.map +1 -0
  180. package/dist/components/frame/error-summary/lt-error-summary.d.ts +82 -0
  181. package/dist/components/frame/error-summary/lt-error-summary.d.ts.map +1 -0
  182. package/dist/components/frame/error-summary/lt-error-summary.js +302 -0
  183. package/dist/components/frame/error-summary/lt-error-summary.js.map +1 -0
  184. package/dist/components/frame/field/field-core.d.ts +92 -0
  185. package/dist/components/frame/field/field-core.d.ts.map +1 -0
  186. package/dist/components/frame/field/field-core.js +63 -0
  187. package/dist/components/frame/field/field-core.js.map +1 -0
  188. package/dist/components/frame/field/lt-field.d.ts +173 -0
  189. package/dist/components/frame/field/lt-field.d.ts.map +1 -0
  190. package/dist/components/frame/field/lt-field.js +580 -0
  191. package/dist/components/frame/field/lt-field.js.map +1 -0
  192. package/dist/components/frame/popover/lt-popover.d.ts +132 -0
  193. package/dist/components/frame/popover/lt-popover.d.ts.map +1 -0
  194. package/dist/components/frame/popover/lt-popover.js +490 -0
  195. package/dist/components/frame/popover/lt-popover.js.map +1 -0
  196. package/dist/components/frame/popover/popover.d.ts +49 -0
  197. package/dist/components/frame/popover/popover.d.ts.map +1 -0
  198. package/dist/components/frame/popover/popover.js +64 -0
  199. package/dist/components/frame/popover/popover.js.map +1 -0
  200. package/dist/components/frame/session-timeout/lt-session-timeout.d.ts +198 -0
  201. package/dist/components/frame/session-timeout/lt-session-timeout.d.ts.map +1 -0
  202. package/dist/components/frame/session-timeout/lt-session-timeout.js +573 -0
  203. package/dist/components/frame/session-timeout/lt-session-timeout.js.map +1 -0
  204. package/dist/components/frame/session-timeout/session-timeout.d.ts +113 -0
  205. package/dist/components/frame/session-timeout/session-timeout.d.ts.map +1 -0
  206. package/dist/components/frame/session-timeout/session-timeout.js +101 -0
  207. package/dist/components/frame/session-timeout/session-timeout.js.map +1 -0
  208. package/dist/components/frame/suggestion/lt-suggestion.d.ts +361 -0
  209. package/dist/components/frame/suggestion/lt-suggestion.d.ts.map +1 -0
  210. package/dist/components/frame/suggestion/lt-suggestion.js +1152 -0
  211. package/dist/components/frame/suggestion/lt-suggestion.js.map +1 -0
  212. package/dist/components/frame/suggestion/suggestion.d.ts +143 -0
  213. package/dist/components/frame/suggestion/suggestion.d.ts.map +1 -0
  214. package/dist/components/frame/suggestion/suggestion.js +127 -0
  215. package/dist/components/frame/suggestion/suggestion.js.map +1 -0
  216. package/dist/components/frame/tabs/lt-tabs.d.ts +404 -0
  217. package/dist/components/frame/tabs/lt-tabs.d.ts.map +1 -0
  218. package/dist/components/frame/tabs/lt-tabs.js +1201 -0
  219. package/dist/components/frame/tabs/lt-tabs.js.map +1 -0
  220. package/dist/components/frame/tabs/tabs.d.ts +112 -0
  221. package/dist/components/frame/tabs/tabs.d.ts.map +1 -0
  222. package/dist/components/frame/tabs/tabs.js +107 -0
  223. package/dist/components/frame/tabs/tabs.js.map +1 -0
  224. package/dist/components/host-element.d.ts +135 -0
  225. package/dist/components/host-element.d.ts.map +1 -0
  226. package/dist/components/host-element.js +366 -0
  227. package/dist/components/host-element.js.map +1 -0
  228. package/dist/components/standalone/connection-status/connection-status.d.ts +28 -0
  229. package/dist/components/standalone/connection-status/connection-status.d.ts.map +1 -0
  230. package/dist/components/standalone/connection-status/connection-status.js +22 -0
  231. package/dist/components/standalone/connection-status/connection-status.js.map +1 -0
  232. package/dist/components/standalone/connection-status/lt-connection-status.d.ts +81 -0
  233. package/dist/components/standalone/connection-status/lt-connection-status.d.ts.map +1 -0
  234. package/dist/components/standalone/connection-status/lt-connection-status.js +216 -0
  235. package/dist/components/standalone/connection-status/lt-connection-status.js.map +1 -0
  236. package/dist/components/standalone/toast/lt-toast.d.ts +109 -0
  237. package/dist/components/standalone/toast/lt-toast.d.ts.map +1 -0
  238. package/dist/components/standalone/toast/lt-toast.js +256 -0
  239. package/dist/components/standalone/toast/lt-toast.js.map +1 -0
  240. package/dist/components/standalone/toast/toast.d.ts +52 -0
  241. package/dist/components/standalone/toast/toast.d.ts.map +1 -0
  242. package/dist/components/standalone/toast/toast.js +42 -0
  243. package/dist/components/standalone/toast/toast.js.map +1 -0
  244. package/dist/diagnostics/core.d.ts +137 -0
  245. package/dist/diagnostics/core.d.ts.map +1 -0
  246. package/dist/diagnostics/core.js +72 -0
  247. package/dist/diagnostics/core.js.map +1 -0
  248. package/dist/diagnostics/default-core.d.ts +10 -0
  249. package/dist/diagnostics/default-core.d.ts.map +1 -0
  250. package/dist/diagnostics/default-core.js +72 -0
  251. package/dist/diagnostics/default-core.js.map +1 -0
  252. package/dist/diagnostics/index.d.ts +84 -0
  253. package/dist/diagnostics/index.d.ts.map +1 -0
  254. package/dist/diagnostics/index.js +106 -0
  255. package/dist/diagnostics/index.js.map +1 -0
  256. package/dist/dom.d.ts +55 -0
  257. package/dist/dom.d.ts.map +1 -0
  258. package/dist/dom.js +162 -0
  259. package/dist/dom.js.map +1 -0
  260. package/dist/index.d.ts +97 -0
  261. package/dist/index.d.ts.map +1 -0
  262. package/dist/index.js +58 -0
  263. package/dist/index.js.map +1 -0
  264. package/dist/jsx/react.d.ts +180 -0
  265. package/dist/jsx/react.d.ts.map +1 -0
  266. package/dist/jsx/react.js +2 -0
  267. package/dist/jsx/react.js.map +1 -0
  268. package/dist/lotek.css +3617 -0
  269. package/dist/lt.d.ts +325 -0
  270. package/dist/lt.d.ts.map +1 -0
  271. package/dist/lt.js +183 -0
  272. package/dist/lt.js.map +1 -0
  273. package/dist/react.d.ts +492 -0
  274. package/dist/react.d.ts.map +1 -0
  275. package/dist/react.js +296 -0
  276. package/dist/react.js.map +1 -0
  277. package/dist/register.d.ts +17 -0
  278. package/dist/register.d.ts.map +1 -0
  279. package/dist/register.js +35 -0
  280. package/dist/register.js.map +1 -0
  281. package/dist/tokens/color.d.ts +43 -0
  282. package/dist/tokens/color.d.ts.map +1 -0
  283. package/dist/tokens/color.js +123 -0
  284. package/dist/tokens/color.js.map +1 -0
  285. package/dist/tokens/contract.d.ts +18 -0
  286. package/dist/tokens/contract.d.ts.map +1 -0
  287. package/dist/tokens/contract.js +18 -0
  288. package/dist/tokens/contract.js.map +1 -0
  289. package/dist/tokens/matrix-cells.d.ts +30 -0
  290. package/dist/tokens/matrix-cells.d.ts.map +1 -0
  291. package/dist/tokens/matrix-cells.js +273 -0
  292. package/dist/tokens/matrix-cells.js.map +1 -0
  293. package/dist/tokens/matrix.d.ts +18 -0
  294. package/dist/tokens/matrix.d.ts.map +1 -0
  295. package/dist/tokens/matrix.js +21 -0
  296. package/dist/tokens/matrix.js.map +1 -0
  297. package/dist/tokens/theme-check.d.ts +42 -0
  298. package/dist/tokens/theme-check.d.ts.map +1 -0
  299. package/dist/tokens/theme-check.js +10 -0
  300. package/dist/tokens/theme-check.js.map +1 -0
  301. package/dist/tokens/theme.d.ts +94 -0
  302. package/dist/tokens/theme.d.ts.map +1 -0
  303. package/dist/tokens/theme.js +14 -0
  304. package/dist/tokens/theme.js.map +1 -0
  305. package/dist/tokens/tokens.d.ts +122 -0
  306. package/dist/tokens/tokens.d.ts.map +1 -0
  307. package/dist/tokens/tokens.js +158 -0
  308. package/dist/tokens/tokens.js.map +1 -0
  309. package/dist/vocabulary/classes.d.ts +3 -0
  310. package/dist/vocabulary/classes.d.ts.map +1 -0
  311. package/dist/vocabulary/classes.js +1320 -0
  312. package/dist/vocabulary/classes.js.map +1 -0
  313. package/dist/vocabulary/elements.d.ts +3 -0
  314. package/dist/vocabulary/elements.d.ts.map +1 -0
  315. package/dist/vocabulary/elements.js +410 -0
  316. package/dist/vocabulary/elements.js.map +1 -0
  317. package/dist/vocabulary/types.d.ts +175 -0
  318. package/dist/vocabulary/types.d.ts.map +1 -0
  319. package/dist/vocabulary/types.js +11 -0
  320. package/dist/vocabulary/types.js.map +1 -0
  321. package/dist/wasi-host.d.ts +24 -0
  322. package/dist/wasi-host.d.ts.map +1 -0
  323. package/dist/wasi-host.js +140 -0
  324. package/dist/wasi-host.js.map +1 -0
  325. package/live-templates.xml +62 -0
  326. package/manifest/files.json +373 -0
  327. package/manifest/manifest.json +4965 -0
  328. package/manifest/manifest.schema.json +513 -0
  329. package/package.json +404 -4
  330. package/src/cli.ts +21 -0
  331. package/src/components/css/accordion/accordion.css +102 -0
  332. package/src/components/css/accordion/accordion.ts +50 -0
  333. package/src/components/css/alert/alert.css +53 -0
  334. package/src/components/css/alert/alert.ts +48 -0
  335. package/src/components/css/avatar/avatar.css +96 -0
  336. package/src/components/css/avatar/avatar.ts +57 -0
  337. package/src/components/css/badge/badge.css +42 -0
  338. package/src/components/css/badge/badge.ts +43 -0
  339. package/src/components/css/breadcrumbs/breadcrumbs.css +62 -0
  340. package/src/components/css/breadcrumbs/breadcrumbs.ts +30 -0
  341. package/src/components/css/button/button.css +128 -0
  342. package/src/components/css/button/button.ts +44 -0
  343. package/src/components/css/card/card.css +65 -0
  344. package/src/components/css/card/card.ts +50 -0
  345. package/src/components/css/checkbox/checkbox.css +114 -0
  346. package/src/components/css/checkbox/checkbox.ts +50 -0
  347. package/src/components/css/divider/divider.css +38 -0
  348. package/src/components/css/divider/divider.ts +42 -0
  349. package/src/components/css/error-text/error-text.css +21 -0
  350. package/src/components/css/error-text/error-text.ts +37 -0
  351. package/src/components/css/fieldset/fieldset.css +72 -0
  352. package/src/components/css/fieldset/fieldset.ts +87 -0
  353. package/src/components/css/file-upload/file-upload.css +102 -0
  354. package/src/components/css/file-upload/file-upload.ts +65 -0
  355. package/src/components/css/heading/heading.css +33 -0
  356. package/src/components/css/heading/heading.ts +42 -0
  357. package/src/components/css/help-text/help-text.css +24 -0
  358. package/src/components/css/help-text/help-text.ts +42 -0
  359. package/src/components/css/icon/icon.css +20 -0
  360. package/src/components/css/icon/icon.ts +44 -0
  361. package/src/components/css/input/input.css +145 -0
  362. package/src/components/css/input/input.ts +77 -0
  363. package/src/components/css/label/label.css +42 -0
  364. package/src/components/css/label/label.ts +51 -0
  365. package/src/components/css/link/link.css +38 -0
  366. package/src/components/css/link/link.ts +38 -0
  367. package/src/components/css/list/list.css +53 -0
  368. package/src/components/css/list/list.ts +44 -0
  369. package/src/components/css/pagination/pagination.css +95 -0
  370. package/src/components/css/pagination/pagination.ts +33 -0
  371. package/src/components/css/paragraph/paragraph.css +31 -0
  372. package/src/components/css/paragraph/paragraph.ts +53 -0
  373. package/src/components/css/progress/progress.css +161 -0
  374. package/src/components/css/progress/progress.ts +78 -0
  375. package/src/components/css/radio/radio.css +94 -0
  376. package/src/components/css/radio/radio.ts +50 -0
  377. package/src/components/css/search/search.css +50 -0
  378. package/src/components/css/search/search.ts +56 -0
  379. package/src/components/css/select/select.css +321 -0
  380. package/src/components/css/select/select.ts +63 -0
  381. package/src/components/css/shared.ts +199 -0
  382. package/src/components/css/skeleton/skeleton.css +62 -0
  383. package/src/components/css/skeleton/skeleton.ts +43 -0
  384. package/src/components/css/skip-link/skip-link.css +41 -0
  385. package/src/components/css/skip-link/skip-link.ts +22 -0
  386. package/src/components/css/spinner/spinner.css +48 -0
  387. package/src/components/css/spinner/spinner.ts +48 -0
  388. package/src/components/css/sr-only/sr-only.css +16 -0
  389. package/src/components/css/sr-only/sr-only.ts +21 -0
  390. package/src/components/css/switch/switch.css +105 -0
  391. package/src/components/css/switch/switch.ts +50 -0
  392. package/src/components/css/table/table.css +159 -0
  393. package/src/components/css/table/table.ts +107 -0
  394. package/src/components/css/tag/tag.css +58 -0
  395. package/src/components/css/tag/tag.ts +49 -0
  396. package/src/components/css/textarea/textarea.css +83 -0
  397. package/src/components/css/textarea/textarea.ts +40 -0
  398. package/src/components/css/toggle-group/toggle-group.css +92 -0
  399. package/src/components/css/toggle-group/toggle-group.ts +37 -0
  400. package/src/components/css/tooltip/tooltip.css +89 -0
  401. package/src/components/css/tooltip/tooltip.ts +37 -0
  402. package/src/components/default-texts.ts +34 -0
  403. package/src/components/events.ts +68 -0
  404. package/src/components/frame/dialog/dialog.css +306 -0
  405. package/src/components/frame/dialog/dialog.ts +166 -0
  406. package/src/components/frame/dialog/lt-dialog.ts +510 -0
  407. package/src/components/frame/error-summary/error-summary.css +47 -0
  408. package/src/components/frame/error-summary/error-summary.ts +56 -0
  409. package/src/components/frame/error-summary/lt-error-summary.ts +344 -0
  410. package/src/components/frame/field/field-core.ts +152 -0
  411. package/src/components/frame/field/field.css +17 -0
  412. package/src/components/frame/field/lt-field.ts +753 -0
  413. package/src/components/frame/popover/lt-popover.ts +555 -0
  414. package/src/components/frame/popover/popover.css +50 -0
  415. package/src/components/frame/popover/popover.ts +102 -0
  416. package/src/components/frame/session-timeout/lt-session-timeout.ts +681 -0
  417. package/src/components/frame/session-timeout/session-timeout.css +50 -0
  418. package/src/components/frame/session-timeout/session-timeout.ts +156 -0
  419. package/src/components/frame/suggestion/lt-suggestion.ts +1358 -0
  420. package/src/components/frame/suggestion/suggestion.css +132 -0
  421. package/src/components/frame/suggestion/suggestion.ts +172 -0
  422. package/src/components/frame/tabs/lt-tabs.ts +1319 -0
  423. package/src/components/frame/tabs/tabs.css +128 -0
  424. package/src/components/frame/tabs/tabs.ts +188 -0
  425. package/src/components/host-element.ts +400 -0
  426. package/src/components/standalone/connection-status/connection-status.css +41 -0
  427. package/src/components/standalone/connection-status/connection-status.ts +41 -0
  428. package/src/components/standalone/connection-status/lt-connection-status.ts +242 -0
  429. package/src/components/standalone/toast/lt-toast.ts +300 -0
  430. package/src/components/standalone/toast/toast.css +78 -0
  431. package/src/components/standalone/toast/toast.ts +70 -0
  432. package/src/diagnostics/core.ts +210 -0
  433. package/src/diagnostics/default-core.ts +81 -0
  434. package/src/diagnostics/index.ts +150 -0
  435. package/src/dom.ts +185 -0
  436. package/src/index.ts +218 -0
  437. package/src/jsx/react.ts +212 -0
  438. package/src/lt.ts +192 -0
  439. package/src/react.ts +411 -0
  440. package/src/register.ts +43 -0
  441. package/src/tailwind/tailwind.css +169 -0
  442. package/src/tokens/boundary.css +104 -0
  443. package/src/tokens/color-contract.json +231 -0
  444. package/src/tokens/color.ts +150 -0
  445. package/src/tokens/contract.ts +24 -0
  446. package/src/tokens/matrix-cells.ts +375 -0
  447. package/src/tokens/matrix.ts +32 -0
  448. package/src/tokens/theme-check.ts +51 -0
  449. package/src/tokens/theme.ts +104 -0
  450. package/src/tokens/tokens.css +569 -0
  451. package/src/tokens/tokens.ts +223 -0
  452. package/src/vocabulary/classes.ts +1322 -0
  453. package/src/vocabulary/elements.ts +412 -0
  454. package/src/vocabulary/types.ts +178 -0
  455. package/src/wasi-host.ts +155 -0
  456. package/web-types.json +800 -0
@@ -0,0 +1,555 @@
1
+ import {
2
+ addClass,
3
+ defineElement,
4
+ HostElement,
5
+ isServerControlled,
6
+ SERVER_CONTROLLED,
7
+ setAttr,
8
+ setFlag,
9
+ warnAboutMarkup,
10
+ } from "../../host-element.js"
11
+ import {
12
+ isPopoverPlacement,
13
+ POPOVER_CLASS,
14
+ type PopoverPlacement,
15
+ } from "./popover.js"
16
+
17
+ export const LT_POPOVER_TAG = "lt-popover" as const
18
+
19
+ /**
20
+ * The parts the popover finds, as selectors relative to the element. The
21
+ * component finds the parts with these, and the manifest describes them for
22
+ * whoever assembles the markup by hand, so the two cannot say different things.
23
+ */
24
+ export const LT_POPOVER_PARTS = {
25
+ trigger: "button, [role='button']",
26
+ panel: `[popover], .${POPOVER_CLASS}`,
27
+ } as const
28
+
29
+ /**
30
+ * A panel that hangs below a button, and closes itself.
31
+ *
32
+ * The panel uses the browser's own `popover`, so it goes into the top layer
33
+ * and lies above everything else without `z-index`. `lt.popover()` writes
34
+ * `popover="manual"`, so Escape and clicks outside are handled by the
35
+ * component: with `auto` the browser closed the panel before the button got
36
+ * its click, and the button could not be used to close it again. Handwritten
37
+ * markup with a bare `popover` is `auto`, and then the component follows along
38
+ * when the browser closes it.
39
+ *
40
+ * What the top layer does not do is place the panel. `position-anchor` is not
41
+ * yet in every browser, so the position is worked out here.
42
+ *
43
+ * When the markup is made with JavaScript, `lt.popover()` writes the wiring:
44
+ * `aria-controls` on the button, and the class, the id and `popover` on the
45
+ * panel. When the markup comes from a template without JavaScript, the
46
+ * template writes a button and a panel with the class `lt-popover` and an id:
47
+ * the component sets `popover="manual"` and writes `aria-controls` on the
48
+ * button from the id. Only what is missing, so what the server wrote stays.
49
+ * The component sets `open` on the host, `aria-expanded` on the button and the
50
+ * position on the panel, and puts back everything it has written when a patch
51
+ * removes it. The template needs no `data-preserve-attr`.
52
+ *
53
+ * The component never writes an id on the panel. Datastar and idiomorph
54
+ * recognise an element by its id, and a panel with an id the server did not
55
+ * send was replaced on the next patch, along with focus and what the user had
56
+ * done in it. Without an id the panel is kept, but the button cannot be
57
+ * connected to it with `aria-controls`, and the component says so in the
58
+ * console.
59
+ *
60
+ * The repair works one way: if someone has asked for the popover to be open,
61
+ * it stays open through a patch. If the server sends `open`, it opens, since
62
+ * that is something the server actually said.
63
+ *
64
+ * For the server to decide the state, set `server-controlled` on the host.
65
+ * That is also the answer when the page controls `open` with an attribute from
66
+ * outside, as with Datastar's `data-attr:open`: a removed attribute cannot be
67
+ * told apart from a morph, while `menu.open = false` is a message the
68
+ * component can see.
69
+ *
70
+ * ```html
71
+ * <lt-popover placement="bottom-end">
72
+ * <button class="lt-button" aria-controls="menu" aria-expanded="false">Actions</button>
73
+ * <ul id="menu" class="lt-popover" popover="manual">…</ul>
74
+ * </lt-popover>
75
+ * ```
76
+ */
77
+ export class LtPopover extends HostElement {
78
+ static observedAttributes = ["open", "placement", SERVER_CONTROLLED] as const
79
+
80
+ private panel?: HTMLElement
81
+ private triggerElement?: HTMLElement
82
+ private observer?: MutationObserver
83
+ /**
84
+ * What the component was last asked for, through the `open` property.
85
+ *
86
+ * `open` lives on the host, and a morph removes everything that is not in
87
+ * the server's HTML. Without anything more, every single patch closed a
88
+ * popover the user had just opened.
89
+ *
90
+ * A removed attribute looks the same whoever removed it, so the component
91
+ * cannot tell a morph from an app. The distinction is instead **how** the app
92
+ * says it: if it goes through the property, as in `menu.open = false`,
93
+ * `hide()` or `toggle()`, that is a message, and it is followed. If something
94
+ * sets the attribute directly, as Datastar's `data-attr:open` does, it cannot
95
+ * be told apart from a morph, and then the page should say
96
+ * `server-controlled` and let the server decide the state.
97
+ */
98
+ private wantsOpen = false
99
+
100
+ /** Whether the panel is open. Mirrored, so CSS can match the state. */
101
+ get open(): boolean {
102
+ return this.hasAttribute("open")
103
+ }
104
+
105
+ set open(value: boolean) {
106
+ // The message is noted here, and not in `show()` and `hide()`. Both go
107
+ // through the setter, but so does `menu.open = false` from an app, and
108
+ // without this the component set the attribute straight back again.
109
+ //
110
+ // No memory when the server decides the state. Without that guard the
111
+ // popover popped open by itself the moment `server-controlled` was removed.
112
+ this.wantsOpen = isServerControlled(this) ? false : value
113
+ setFlag(this, "open", value)
114
+ }
115
+
116
+ /** Which edge the panel hangs from. Default: `bottom-start`. */
117
+ get placement(): PopoverPlacement {
118
+ const value = this.getAttribute("placement")
119
+ return isPopoverPlacement(value) ? value : "bottom-start"
120
+ }
121
+
122
+ set placement(value: PopoverPlacement) {
123
+ setAttr(this, "placement", value)
124
+ }
125
+
126
+ connectedCallback(): void {
127
+ window.addEventListener("resize", this.reposition)
128
+ window.addEventListener("scroll", this.reposition, true)
129
+ /*
130
+ * The attributes are included, not only the children. `aria-expanded` on the
131
+ * button and the position on the panel are something the component works
132
+ * out, so if a patch removes them, they must come back. Every write in
133
+ * `sync()` compares first, or the observer would trigger itself.
134
+ */
135
+ this.observer = new MutationObserver((records) => {
136
+ // `type` concerns only the button. A password field in the panel that
137
+ // switches between `password` and `text` must not run all of `sync()`.
138
+ const onlyOtherTypes = records.every(
139
+ (record) =>
140
+ record.attributeName === "type" &&
141
+ record.target !== this.triggerElement,
142
+ )
143
+ if (!onlyOtherTypes) this.sync()
144
+ })
145
+ this.observer.observe(this, {
146
+ childList: true,
147
+ subtree: true,
148
+ attributes: true,
149
+ attributeFilter: [
150
+ "aria-expanded",
151
+ "style",
152
+ // The wiring the component fills in when the markup came without it.
153
+ "popover",
154
+ "id",
155
+ "aria-controls",
156
+ "class",
157
+ // A patch from a template without `type` removes it, and then the
158
+ // button submits the form again.
159
+ "type",
160
+ ],
161
+ })
162
+ this.sync()
163
+ }
164
+
165
+ disconnectedCallback(): void {
166
+ window.removeEventListener("resize", this.reposition)
167
+ window.removeEventListener("scroll", this.reposition, true)
168
+ document.removeEventListener("click", this.handleOutsideClick, true)
169
+ document.removeEventListener("keydown", this.handleKeydown)
170
+ this.observer?.disconnect()
171
+ this.observer = undefined
172
+ this.triggerElement?.removeEventListener("click", this.handleTriggerClick)
173
+ this.panel?.removeEventListener("toggle", this.handlePanelToggle)
174
+ // The reference must be reset, or `sync()` sees that the button is the
175
+ // same when the element is inserted again, and skips attaching the listener
176
+ // again. Then the panel can no longer be opened.
177
+ this.triggerElement = undefined
178
+ this.panel = undefined
179
+ }
180
+
181
+ attributeChangedCallback(name: string): void {
182
+ // `server-controlled` turned on midway: the component lets go, and the
183
+ // next patch decides.
184
+ if (name === SERVER_CONTROLLED && isServerControlled(this)) {
185
+ this.wantsOpen = false
186
+ }
187
+
188
+ if (this.isConnected) this.sync()
189
+ }
190
+
191
+ private sync(): void {
192
+ /*
193
+ * Puts `open` back when a patch took it.
194
+ *
195
+ * One way only: if `open` is there, the popover is open, and then either the
196
+ * user or the server has said so. If it is gone while the user opened it, the
197
+ * morph took it, and then it comes back.
198
+ */
199
+ if (this.wantsOpen && !this.open && !isServerControlled(this)) {
200
+ /*
201
+ * Wait until the whole patch has landed before opening the popover again.
202
+ *
203
+ * A morph sets one attribute at a time, and `open` comes before
204
+ * `server-controlled` in document order. When the component repaired at
205
+ * once, it put `open` back while the server was in the middle of saying it
206
+ * was taking over the state, and the popover stayed open afterwards.
207
+ * `queueMicrotask` runs after the whole patch is done, and the condition is
208
+ * checked again there.
209
+ */
210
+ queueMicrotask(() => {
211
+ if (!this.isConnected || isServerControlled(this)) return
212
+ if (this.wantsOpen && !this.open) setFlag(this, "open", true)
213
+ })
214
+ }
215
+
216
+ // The parts are recognised by the wiring that must be there anyway: the
217
+ // panel is the one with `popover`, and the button is the one pointing to the
218
+ // panel with `aria-controls`. Before, the button had `slot="trigger"`, a
219
+ // leftover from the time the component had shadow DOM. Without a shadow
220
+ // root `slot` does nothing in HTML, so the attribute was a label that looked
221
+ // like something it was not.
222
+ const panel = this.findPanel()
223
+ if (panel) {
224
+ // Only what is missing. A template may have written `popover` without a
225
+ // value, and that is `auto`; then the component follows along when the
226
+ // browser closes it.
227
+ addClass(panel, POPOVER_CLASS)
228
+ if (!panel.hasAttribute("popover")) setAttr(panel, "popover", "manual")
229
+ // Never an id on the panel: see the comment at the top. Without an id there
230
+ // is nothing for `aria-controls` to point to.
231
+ warnAboutMarkup(
232
+ this,
233
+ "the panel has no id, so the button cannot be connected to it " +
234
+ "with aria-controls. Write an id on the panel in the template, " +
235
+ 'such as id="menu", and the same value in aria-controls on the ' +
236
+ "button.",
237
+ () => {
238
+ const found = this.findPanel()
239
+ return found !== null && !found.id
240
+ },
241
+ )
242
+ }
243
+ const trigger = panel ? this.findTrigger(panel) : null
244
+ if (panel?.id && trigger && !trigger.hasAttribute("aria-controls")) {
245
+ setAttr(trigger, "aria-controls", panel.id)
246
+ }
247
+ // A `<button>` without `type` is a submit button, and so is one with an
248
+ // empty or unknown `type`, which a template can write. A help popover next
249
+ // to a field sits inside the form, and a click to open it submitted the
250
+ // form, or brought up the error summary, instead of showing the panel. A
251
+ // button that says `submit` itself is left alone.
252
+ if (
253
+ trigger instanceof HTMLButtonElement &&
254
+ trigger.type === "submit" &&
255
+ trigger.getAttribute("type")?.toLowerCase() !== "submit"
256
+ ) {
257
+ setAttr(trigger, "type", "button")
258
+ }
259
+
260
+ if (!trigger || !panel) {
261
+ /*
262
+ * Two different faults, and a message for each, so the developer looks in
263
+ * the right place. An empty element is a region the server has not filled
264
+ * yet, and that is not a fault in the markup.
265
+ */
266
+ const isEmpty = () => this.childElementCount === 0
267
+
268
+ if (!panel) {
269
+ warnAboutMarkup(
270
+ this,
271
+ "found no panel: an element with popover or the class " +
272
+ "lt-popover. Without one there is nothing to open or position.",
273
+ () => !isEmpty() && this.findPanel() === null,
274
+ )
275
+ } else {
276
+ warnAboutMarkup(
277
+ this,
278
+ "found no button: neither one with aria-controls pointing to " +
279
+ "the panel nor a <button> outside the panel. Without it the " +
280
+ "component does not know what opens the popover.",
281
+ () => {
282
+ const found = this.findPanel()
283
+ return !isEmpty() && found !== null && !this.findTrigger(found)
284
+ },
285
+ )
286
+ }
287
+
288
+ /*
289
+ * Let go of what we had. When a patch removed the panel, the component
290
+ * otherwise held on to a detached node, and `reposition()` kept working out
291
+ * the position of something that is not on the page.
292
+ *
293
+ * The listeners on `document` must go too. Without that they stayed in the
294
+ * capture phase for as long as the markup was broken, and a click anywhere
295
+ * on the page gave the app a `popover-toggle` it had not asked for.
296
+ */
297
+ this.triggerElement?.removeEventListener("click", this.handleTriggerClick)
298
+ this.triggerElement = undefined
299
+ this.panel?.removeEventListener("toggle", this.handlePanelToggle)
300
+ this.panel = undefined
301
+ document.removeEventListener("click", this.handleOutsideClick, true)
302
+ document.removeEventListener("keydown", this.handleKeydown)
303
+ return
304
+ }
305
+
306
+ if (this.triggerElement !== trigger) {
307
+ this.triggerElement?.removeEventListener("click", this.handleTriggerClick)
308
+ trigger.addEventListener("click", this.handleTriggerClick)
309
+ this.triggerElement = trigger
310
+ }
311
+ if (this.panel !== panel) {
312
+ if (this.panel) this.warnAboutReplacedPanel(this.panel, panel)
313
+ /*
314
+ * The state is in the markup as `:popover-open`, and is read from there.
315
+ * When the browser closes a `popover="auto"` itself, because another one
316
+ * opens or the user clicks outside, `toggle` reports it. Without the
317
+ * listener the host kept `open` and the button `aria-expanded="true"` over a
318
+ * closed panel, and the next click did nothing visible.
319
+ */
320
+ this.panel?.removeEventListener("toggle", this.handlePanelToggle)
321
+ panel.addEventListener("toggle", this.handlePanelToggle)
322
+ this.panel = panel
323
+ }
324
+
325
+ // Written by the component, and set again if a patch took it.
326
+ setAttr(trigger, "aria-expanded", String(this.open))
327
+
328
+ if (this.open) {
329
+ if (!panel.matches(":popover-open")) panel.showPopover()
330
+ this.reposition()
331
+ document.addEventListener("click", this.handleOutsideClick, true)
332
+ document.addEventListener("keydown", this.handleKeydown)
333
+ } else {
334
+ if (panel.matches(":popover-open")) panel.hidePopover()
335
+ document.removeEventListener("click", this.handleOutsideClick, true)
336
+ document.removeEventListener("keydown", this.handleKeydown)
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Says so when a patch replaced the open panel because the id from the
342
+ * server changed.
343
+ *
344
+ * Datastar and idiomorph recognise an element by its id, and a new id is a
345
+ * new element to them. The component opens the new panel again, but focus,
346
+ * and what the user typed in a field in it, are gone. It happens when the
347
+ * template builds the id from something that changes between renders, such
348
+ * as a random number or a counter.
349
+ *
350
+ * The message comes only when the popover was open and both panels had an id
351
+ * from the server, with different values. A panel without an id is kept by
352
+ * the morph, and a server that deliberately sends another panel while the
353
+ * popover is closed has nothing to fix.
354
+ */
355
+ private warnAboutReplacedPanel(lost: HTMLElement, panel: HTMLElement): void {
356
+ if (lost.isConnected || !this.wantsOpen) return
357
+ if (!lost.id || !panel.id || lost.id === panel.id) return
358
+
359
+ warnAboutMarkup(
360
+ this,
361
+ "a server update replaced the panel while it was open, because " +
362
+ "the panel's id was different from last time and the update did " +
363
+ "not recognise it. The panel is opened again, but focus and " +
364
+ "what the user did in it are lost; build the id from something " +
365
+ "that is the same every time, not from a random number or a " +
366
+ "counter.",
367
+ // The replacement has happened and cannot undo itself, unless a new
368
+ // update has already replaced the panel again.
369
+ () => !lost.isConnected && this.findPanel() === panel,
370
+ )
371
+ }
372
+
373
+ /** The panel: the one with `popover`, otherwise the one with the class. */
374
+ private findPanel(): HTMLElement | null {
375
+ return this.querySelector<HTMLElement>(LT_POPOVER_PARTS.panel)
376
+ }
377
+
378
+ /**
379
+ * The button: the one pointing to the panel with `aria-controls`, otherwise
380
+ * the first button outside the panel that does not point to anything else. A
381
+ * button with an `aria-controls` pointing elsewhere is not a fallback: then
382
+ * the server has said something else, and that is not to be guessed around.
383
+ */
384
+ private findTrigger(panel: HTMLElement): HTMLElement | null {
385
+ if (panel.id) {
386
+ const pointing = this.querySelector<HTMLElement>(
387
+ `[aria-controls="${CSS.escape(panel.id)}"]`,
388
+ )
389
+ if (pointing) return pointing
390
+ }
391
+ return (
392
+ [...this.querySelectorAll<HTMLElement>(LT_POPOVER_PARTS.trigger)].find(
393
+ (button) =>
394
+ !panel.contains(button) && !button.hasAttribute("aria-controls"),
395
+ ) ?? null
396
+ )
397
+ }
398
+
399
+ private handleTriggerClick = (): void => {
400
+ this.toggle()
401
+ }
402
+
403
+ private handlePanelToggle = (event: Event): void => {
404
+ const state = (event as ToggleEvent).newState
405
+ /*
406
+ * The event applies only when it matches the panel. `toggle` is delivered as
407
+ * a separate task, after everything that happened in the same round, and can
408
+ * describe a state that has already changed. A patch that takes `open` on
409
+ * the host and `popover` on the panel in the same morph closes the panel
410
+ * here in `sync()`, reopens it in the microtask afterwards, and only then
411
+ * does "closed" arrive from the close. Before, it was taken as the user's,
412
+ * and the popover the user had open stayed closed after a patch that said
413
+ * nothing about it.
414
+ *
415
+ * The component's own `showPopover()` and `hidePopover()` also fire the
416
+ * event, but then `open` already matches, and nothing happens.
417
+ */
418
+ const showing = (event.currentTarget as Element).matches(":popover-open")
419
+ if (state === "closed" && this.open && !showing) this.hide()
420
+ else if (state === "open" && !this.open && showing) this.show()
421
+ }
422
+
423
+ private handleOutsideClick = (event: Event): void => {
424
+ // `composedPath()` and not `target`: when the component is in a shadow
425
+ // root, `target` is retargeted to the shadow host, and a click in the panel
426
+ // itself looked like a click outside.
427
+ const target = event.composedPath()[0] as Node
428
+ if (this.contains(target) || this.panel?.contains(target)) return
429
+ this.hide()
430
+ }
431
+
432
+ private handleKeydown = (event: KeyboardEvent): void => {
433
+ if (event.key !== "Escape" || !this.open) return
434
+ // Something else has already used the key press, such as a suggestion list
435
+ // that closed. Without this one press closed both the list and the popover.
436
+ if (event.defaultPrevented) return
437
+
438
+ // `composedPath()` and not `contains`: a field in a shadow root in the
439
+ // panel is not in the tree `contains` sees, and a press there closed nothing.
440
+ const path = event.composedPath()
441
+ for (const node of path) {
442
+ if (node === this) break
443
+ // An open popover inside this one is closer. It takes the press, and this
444
+ // one closes on the next. The listeners are both on `document`, and the
445
+ // outer one came first: before, it closed and left the inner one open.
446
+ if (node instanceof LtPopover && node.open) return
447
+ // A modal dialog opened on top of the popover is closed by its own press.
448
+ if (
449
+ node instanceof HTMLDialogElement &&
450
+ node.matches(":modal") &&
451
+ !node.contains(this)
452
+ ) {
453
+ return
454
+ }
455
+ }
456
+
457
+ /*
458
+ * Focus is moved back only when it was in the popover, or nowhere else: on
459
+ * `body`, or on a dialog or a `<main tabindex="-1">` the popover is in. A
460
+ * mouse click does not give the button focus in Safari, and inside a modal
461
+ * dialog the dialog gets the focus. If focus was in another field, it stays
462
+ * there. Before, it jumped to the button.
463
+ */
464
+ const target = path[0]
465
+ const fromWindow =
466
+ path.includes(this) || (target instanceof Node && target.contains(this))
467
+
468
+ // Used here. Without this it also closed a modal dialog the popover was
469
+ // in: one press took both, and the user lost the dialog.
470
+ event.preventDefault()
471
+ this.hide()
472
+ // Without this focus is on a panel that no longer exists, and the next
473
+ // key press starts at the top of the page.
474
+ if (fromWindow) this.triggerElement?.focus()
475
+ }
476
+
477
+ /** Works out where the panel goes, against the button's place on screen. */
478
+ private reposition = (): void => {
479
+ if (!this.open || !this.panel || !this.triggerElement) return
480
+
481
+ const anchor = this.triggerElement.getBoundingClientRect()
482
+ const panel = this.panel.getBoundingClientRect()
483
+ const space = 4
484
+
485
+ const below = this.placement.startsWith("bottom")
486
+ // `start` and `end` follow the reading direction: in a right-to-left
487
+ // document `start` is the button's right edge. Without this the panel was on
488
+ // the wrong side of the button in RTL. `:dir(rtl)` and not `direction` from
489
+ // `getComputedStyle`: the stylesheet flips the value with `:dir(rtl)`, so
490
+ // both read the HTML direction from `dir`. A page that sets
491
+ // `direction: rtl` in CSS without `dir` is not flipped, neither here nor in
492
+ // the stylesheet, as with the background icons.
493
+ const rtl = this.panel.matches(":dir(rtl)")
494
+ const alignRight = this.placement.endsWith("end") !== rtl
495
+
496
+ let top = below ? anchor.bottom + space : anchor.top - panel.height - space
497
+ let left = alignRight ? anchor.right - panel.width : anchor.left
498
+
499
+ // The panel must not end up off screen. If it goes out at the side, it is
500
+ // moved in. If there is no room on the side of the button the placement asks
501
+ // for, it goes on the other side, and is then clamped into the viewport
502
+ // either way. `top-*` lacked the last part, and a panel at the top of the
503
+ // page lay entirely off screen, with no way to scroll it into view.
504
+ left = Math.max(
505
+ space,
506
+ Math.min(left, window.innerWidth - panel.width - space),
507
+ )
508
+ if (below && top + panel.height > window.innerHeight) {
509
+ top = anchor.top - panel.height - space
510
+ } else if (!below && top < space) {
511
+ top = anchor.bottom + space
512
+ }
513
+ top = Math.max(
514
+ space,
515
+ Math.min(top, window.innerHeight - panel.height - space),
516
+ )
517
+
518
+ this.panel.style.setProperty("--lt-popover-top", `${Math.round(top)}px`)
519
+ this.panel.style.setProperty("--lt-popover-left", `${Math.round(left)}px`)
520
+ }
521
+
522
+ private emit(open: boolean): void {
523
+ this.dispatchEvent(
524
+ new CustomEvent("popover-toggle", {
525
+ detail: { open },
526
+ bubbles: true,
527
+ composed: true,
528
+ }),
529
+ )
530
+ }
531
+
532
+ /** Opens the panel. */
533
+ show(): void {
534
+ if (this.open) return
535
+ this.open = true
536
+ this.emit(true)
537
+ }
538
+
539
+ /** Closes the panel. */
540
+ hide(): void {
541
+ if (!this.open) return
542
+ this.open = false
543
+ this.emit(false)
544
+ }
545
+
546
+ /** Opens or closes. */
547
+ toggle(): void {
548
+ if (this.open) this.hide()
549
+ else this.show()
550
+ }
551
+ }
552
+
553
+ export function defineLtPopover(tagName: string = LT_POPOVER_TAG): void {
554
+ defineElement(tagName, LtPopover)
555
+ }
@@ -0,0 +1,50 @@
1
+ @layer lotek {
2
+ lt-popover {
3
+ display: contents;
4
+ }
5
+
6
+ .lt-popover {
7
+ /* The panel is in the top layer, so the position is relative to the
8
+ viewport. The values are set by the component, which works them out
9
+ against the button. */
10
+ position: fixed;
11
+ inset: auto;
12
+ inset-block-start: var(--lt-popover-top, 0);
13
+ inset-inline-start: var(--lt-popover-left, 0);
14
+ margin: 0;
15
+ /* The panel is often a `<ul>` of actions. The reset belongs here and
16
+ not with the consumer: `data-variant="plain"` matched no rule on
17
+ `.lt-popover`, so the list kept the browser's bullets. The indent was
18
+ already gone, since `padding` is set in the same rule. */
19
+ list-style: none;
20
+ box-sizing: border-box;
21
+ inline-size: max-content;
22
+ max-inline-size: var(
23
+ --lt-popover-width,
24
+ min(20rem, calc(100vw - var(--lt-spacing-8)))
25
+ );
26
+ padding: var(--lt-popover-padding, var(--lt-spacing-3));
27
+ font-family: inherit;
28
+ font-size: var(--lt-font-size-m);
29
+ line-height: var(--lt-line-height-default);
30
+ color: var(--lt-color-neutral-text-strong);
31
+ background: var(--lt-color-neutral-canvas);
32
+ border: 1px solid var(--lt-color-neutral-border-subtle);
33
+ border-radius: var(--lt-popover-radius, var(--lt-spacing-1));
34
+ box-shadow: var(--lt-shadow-overlay);
35
+ }
36
+
37
+ /* The position is physical: `--lt-popover-left` is the distance from the
38
+ left edge of the viewport, worked out against the button.
39
+ `inset-inline-start` is the right edge in a right-to-left document, so
40
+ there it is flipped, the way background icons are flipped with
41
+ `:dir(rtl)`. */
42
+ .lt-popover:dir(rtl) {
43
+ inset-inline-start: auto;
44
+ inset-inline-end: var(--lt-popover-left, 0);
45
+ }
46
+
47
+ .lt-popover:not(:popover-open) {
48
+ display: none;
49
+ }
50
+ }