@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.
- package/CHANGELOG.md +3254 -0
- package/LICENSE +21 -0
- package/README.md +129 -2
- package/agent/astro.md +400 -0
- package/agent/bundles.md +429 -0
- package/agent/datastar.md +409 -0
- package/agent/html.md +388 -0
- package/agent/react.md +440 -0
- package/agent/templates.md +472 -0
- package/core/lotek-core.wasm +0 -0
- package/core/lotek.wasm +0 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -0
- package/dist/components/css/accordion/accordion.d.ts +37 -0
- package/dist/components/css/accordion/accordion.d.ts.map +1 -0
- package/dist/components/css/accordion/accordion.js +31 -0
- package/dist/components/css/accordion/accordion.js.map +1 -0
- package/dist/components/css/alert/alert.d.ts +35 -0
- package/dist/components/css/alert/alert.d.ts.map +1 -0
- package/dist/components/css/alert/alert.js +29 -0
- package/dist/components/css/alert/alert.js.map +1 -0
- package/dist/components/css/avatar/avatar.d.ts +40 -0
- package/dist/components/css/avatar/avatar.d.ts.map +1 -0
- package/dist/components/css/avatar/avatar.js +30 -0
- package/dist/components/css/avatar/avatar.js.map +1 -0
- package/dist/components/css/badge/badge.d.ts +24 -0
- package/dist/components/css/badge/badge.d.ts.map +1 -0
- package/dist/components/css/badge/badge.js +24 -0
- package/dist/components/css/badge/badge.js.map +1 -0
- package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +26 -0
- package/dist/components/css/breadcrumbs/breadcrumbs.d.ts.map +1 -0
- package/dist/components/css/breadcrumbs/breadcrumbs.js +24 -0
- package/dist/components/css/breadcrumbs/breadcrumbs.js.map +1 -0
- package/dist/components/css/button/button.d.ts +26 -0
- package/dist/components/css/button/button.d.ts.map +1 -0
- package/dist/components/css/button/button.js +25 -0
- package/dist/components/css/button/button.js.map +1 -0
- package/dist/components/css/card/card.d.ts +36 -0
- package/dist/components/css/card/card.d.ts.map +1 -0
- package/dist/components/css/card/card.js +28 -0
- package/dist/components/css/card/card.js.map +1 -0
- package/dist/components/css/checkbox/checkbox.d.ts +32 -0
- package/dist/components/css/checkbox/checkbox.d.ts.map +1 -0
- package/dist/components/css/checkbox/checkbox.js +27 -0
- package/dist/components/css/checkbox/checkbox.js.map +1 -0
- package/dist/components/css/divider/divider.d.ts +29 -0
- package/dist/components/css/divider/divider.d.ts.map +1 -0
- package/dist/components/css/divider/divider.js +23 -0
- package/dist/components/css/divider/divider.js.map +1 -0
- package/dist/components/css/error-text/error-text.d.ts +24 -0
- package/dist/components/css/error-text/error-text.d.ts.map +1 -0
- package/dist/components/css/error-text/error-text.js +18 -0
- package/dist/components/css/error-text/error-text.js.map +1 -0
- package/dist/components/css/fieldset/fieldset.d.ts +55 -0
- package/dist/components/css/fieldset/fieldset.d.ts.map +1 -0
- package/dist/components/css/fieldset/fieldset.js +39 -0
- package/dist/components/css/fieldset/fieldset.js.map +1 -0
- package/dist/components/css/file-upload/file-upload.d.ts +41 -0
- package/dist/components/css/file-upload/file-upload.d.ts.map +1 -0
- package/dist/components/css/file-upload/file-upload.js +32 -0
- package/dist/components/css/file-upload/file-upload.js.map +1 -0
- package/dist/components/css/heading/heading.d.ts +29 -0
- package/dist/components/css/heading/heading.d.ts.map +1 -0
- package/dist/components/css/heading/heading.js +23 -0
- package/dist/components/css/heading/heading.js.map +1 -0
- package/dist/components/css/help-text/help-text.d.ts +24 -0
- package/dist/components/css/help-text/help-text.d.ts.map +1 -0
- package/dist/components/css/help-text/help-text.js +23 -0
- package/dist/components/css/help-text/help-text.js.map +1 -0
- package/dist/components/css/icon/icon.d.ts +34 -0
- package/dist/components/css/icon/icon.d.ts.map +1 -0
- package/dist/components/css/icon/icon.js +25 -0
- package/dist/components/css/icon/icon.js.map +1 -0
- package/dist/components/css/input/input.d.ts +40 -0
- package/dist/components/css/input/input.d.ts.map +1 -0
- package/dist/components/css/input/input.js +45 -0
- package/dist/components/css/input/input.js.map +1 -0
- package/dist/components/css/label/label.d.ts +33 -0
- package/dist/components/css/label/label.d.ts.map +1 -0
- package/dist/components/css/label/label.js +23 -0
- package/dist/components/css/label/label.js.map +1 -0
- package/dist/components/css/link/link.d.ts +28 -0
- package/dist/components/css/link/link.d.ts.map +1 -0
- package/dist/components/css/link/link.js +20 -0
- package/dist/components/css/link/link.js.map +1 -0
- package/dist/components/css/list/list.d.ts +31 -0
- package/dist/components/css/list/list.d.ts.map +1 -0
- package/dist/components/css/list/list.js +25 -0
- package/dist/components/css/list/list.js.map +1 -0
- package/dist/components/css/pagination/pagination.d.ts +27 -0
- package/dist/components/css/pagination/pagination.d.ts.map +1 -0
- package/dist/components/css/pagination/pagination.js +25 -0
- package/dist/components/css/pagination/pagination.js.map +1 -0
- package/dist/components/css/paragraph/paragraph.d.ts +36 -0
- package/dist/components/css/paragraph/paragraph.d.ts.map +1 -0
- package/dist/components/css/paragraph/paragraph.js +26 -0
- package/dist/components/css/paragraph/paragraph.js.map +1 -0
- package/dist/components/css/progress/progress.d.ts +53 -0
- package/dist/components/css/progress/progress.d.ts.map +1 -0
- package/dist/components/css/progress/progress.js +38 -0
- package/dist/components/css/progress/progress.js.map +1 -0
- package/dist/components/css/radio/radio.d.ts +32 -0
- package/dist/components/css/radio/radio.d.ts.map +1 -0
- package/dist/components/css/radio/radio.js +27 -0
- package/dist/components/css/radio/radio.js.map +1 -0
- package/dist/components/css/search/search.d.ts +34 -0
- package/dist/components/css/search/search.d.ts.map +1 -0
- package/dist/components/css/search/search.js +32 -0
- package/dist/components/css/search/search.js.map +1 -0
- package/dist/components/css/select/select.d.ts +40 -0
- package/dist/components/css/select/select.d.ts.map +1 -0
- package/dist/components/css/select/select.js +31 -0
- package/dist/components/css/select/select.js.map +1 -0
- package/dist/components/css/shared.d.ts +90 -0
- package/dist/components/css/shared.d.ts.map +1 -0
- package/dist/components/css/shared.js +167 -0
- package/dist/components/css/shared.js.map +1 -0
- package/dist/components/css/skeleton/skeleton.d.ts +29 -0
- package/dist/components/css/skeleton/skeleton.d.ts.map +1 -0
- package/dist/components/css/skeleton/skeleton.js +23 -0
- package/dist/components/css/skeleton/skeleton.js.map +1 -0
- package/dist/components/css/skip-link/skip-link.d.ts +18 -0
- package/dist/components/css/skip-link/skip-link.d.ts.map +1 -0
- package/dist/components/css/skip-link/skip-link.js +16 -0
- package/dist/components/css/skip-link/skip-link.js.map +1 -0
- package/dist/components/css/spinner/spinner.d.ts +33 -0
- package/dist/components/css/spinner/spinner.d.ts.map +1 -0
- package/dist/components/css/spinner/spinner.js +25 -0
- package/dist/components/css/spinner/spinner.js.map +1 -0
- package/dist/components/css/sr-only/sr-only.d.ts +17 -0
- package/dist/components/css/sr-only/sr-only.d.ts.map +1 -0
- package/dist/components/css/sr-only/sr-only.js +15 -0
- package/dist/components/css/sr-only/sr-only.js.map +1 -0
- package/dist/components/css/switch/switch.d.ts +37 -0
- package/dist/components/css/switch/switch.d.ts.map +1 -0
- package/dist/components/css/switch/switch.js +29 -0
- package/dist/components/css/switch/switch.js.map +1 -0
- package/dist/components/css/table/table.d.ts +76 -0
- package/dist/components/css/table/table.d.ts.map +1 -0
- package/dist/components/css/table/table.js +59 -0
- package/dist/components/css/table/table.js.map +1 -0
- package/dist/components/css/tag/tag.d.ts +35 -0
- package/dist/components/css/tag/tag.d.ts.map +1 -0
- package/dist/components/css/tag/tag.js +27 -0
- package/dist/components/css/tag/tag.js.map +1 -0
- package/dist/components/css/textarea/textarea.d.ts +23 -0
- package/dist/components/css/textarea/textarea.d.ts.map +1 -0
- package/dist/components/css/textarea/textarea.js +18 -0
- package/dist/components/css/textarea/textarea.js.map +1 -0
- package/dist/components/css/toggle-group/toggle-group.d.ts +31 -0
- package/dist/components/css/toggle-group/toggle-group.d.ts.map +1 -0
- package/dist/components/css/toggle-group/toggle-group.js +29 -0
- package/dist/components/css/toggle-group/toggle-group.js.map +1 -0
- package/dist/components/css/tooltip/tooltip.d.ts +31 -0
- package/dist/components/css/tooltip/tooltip.d.ts.map +1 -0
- package/dist/components/css/tooltip/tooltip.js +29 -0
- package/dist/components/css/tooltip/tooltip.js.map +1 -0
- package/dist/components/default-texts.d.ts +35 -0
- package/dist/components/default-texts.d.ts.map +1 -0
- package/dist/components/default-texts.js +35 -0
- package/dist/components/default-texts.js.map +1 -0
- package/dist/components/events.d.ts +77 -0
- package/dist/components/events.d.ts.map +1 -0
- package/dist/components/events.js +3 -0
- package/dist/components/events.js.map +1 -0
- package/dist/components/frame/dialog/dialog.d.ts +122 -0
- package/dist/components/frame/dialog/dialog.d.ts.map +1 -0
- package/dist/components/frame/dialog/dialog.js +112 -0
- package/dist/components/frame/dialog/dialog.js.map +1 -0
- package/dist/components/frame/dialog/lt-dialog.d.ts +104 -0
- package/dist/components/frame/dialog/lt-dialog.d.ts.map +1 -0
- package/dist/components/frame/dialog/lt-dialog.js +431 -0
- package/dist/components/frame/dialog/lt-dialog.js.map +1 -0
- package/dist/components/frame/error-summary/error-summary.d.ts +40 -0
- package/dist/components/frame/error-summary/error-summary.d.ts.map +1 -0
- package/dist/components/frame/error-summary/error-summary.js +32 -0
- package/dist/components/frame/error-summary/error-summary.js.map +1 -0
- package/dist/components/frame/error-summary/lt-error-summary.d.ts +82 -0
- package/dist/components/frame/error-summary/lt-error-summary.d.ts.map +1 -0
- package/dist/components/frame/error-summary/lt-error-summary.js +302 -0
- package/dist/components/frame/error-summary/lt-error-summary.js.map +1 -0
- package/dist/components/frame/field/field-core.d.ts +92 -0
- package/dist/components/frame/field/field-core.d.ts.map +1 -0
- package/dist/components/frame/field/field-core.js +63 -0
- package/dist/components/frame/field/field-core.js.map +1 -0
- package/dist/components/frame/field/lt-field.d.ts +173 -0
- package/dist/components/frame/field/lt-field.d.ts.map +1 -0
- package/dist/components/frame/field/lt-field.js +580 -0
- package/dist/components/frame/field/lt-field.js.map +1 -0
- package/dist/components/frame/popover/lt-popover.d.ts +132 -0
- package/dist/components/frame/popover/lt-popover.d.ts.map +1 -0
- package/dist/components/frame/popover/lt-popover.js +490 -0
- package/dist/components/frame/popover/lt-popover.js.map +1 -0
- package/dist/components/frame/popover/popover.d.ts +49 -0
- package/dist/components/frame/popover/popover.d.ts.map +1 -0
- package/dist/components/frame/popover/popover.js +64 -0
- package/dist/components/frame/popover/popover.js.map +1 -0
- package/dist/components/frame/session-timeout/lt-session-timeout.d.ts +198 -0
- package/dist/components/frame/session-timeout/lt-session-timeout.d.ts.map +1 -0
- package/dist/components/frame/session-timeout/lt-session-timeout.js +573 -0
- package/dist/components/frame/session-timeout/lt-session-timeout.js.map +1 -0
- package/dist/components/frame/session-timeout/session-timeout.d.ts +113 -0
- package/dist/components/frame/session-timeout/session-timeout.d.ts.map +1 -0
- package/dist/components/frame/session-timeout/session-timeout.js +101 -0
- package/dist/components/frame/session-timeout/session-timeout.js.map +1 -0
- package/dist/components/frame/suggestion/lt-suggestion.d.ts +361 -0
- package/dist/components/frame/suggestion/lt-suggestion.d.ts.map +1 -0
- package/dist/components/frame/suggestion/lt-suggestion.js +1152 -0
- package/dist/components/frame/suggestion/lt-suggestion.js.map +1 -0
- package/dist/components/frame/suggestion/suggestion.d.ts +143 -0
- package/dist/components/frame/suggestion/suggestion.d.ts.map +1 -0
- package/dist/components/frame/suggestion/suggestion.js +127 -0
- package/dist/components/frame/suggestion/suggestion.js.map +1 -0
- package/dist/components/frame/tabs/lt-tabs.d.ts +404 -0
- package/dist/components/frame/tabs/lt-tabs.d.ts.map +1 -0
- package/dist/components/frame/tabs/lt-tabs.js +1201 -0
- package/dist/components/frame/tabs/lt-tabs.js.map +1 -0
- package/dist/components/frame/tabs/tabs.d.ts +112 -0
- package/dist/components/frame/tabs/tabs.d.ts.map +1 -0
- package/dist/components/frame/tabs/tabs.js +107 -0
- package/dist/components/frame/tabs/tabs.js.map +1 -0
- package/dist/components/host-element.d.ts +135 -0
- package/dist/components/host-element.d.ts.map +1 -0
- package/dist/components/host-element.js +366 -0
- package/dist/components/host-element.js.map +1 -0
- package/dist/components/standalone/connection-status/connection-status.d.ts +28 -0
- package/dist/components/standalone/connection-status/connection-status.d.ts.map +1 -0
- package/dist/components/standalone/connection-status/connection-status.js +22 -0
- package/dist/components/standalone/connection-status/connection-status.js.map +1 -0
- package/dist/components/standalone/connection-status/lt-connection-status.d.ts +81 -0
- package/dist/components/standalone/connection-status/lt-connection-status.d.ts.map +1 -0
- package/dist/components/standalone/connection-status/lt-connection-status.js +216 -0
- package/dist/components/standalone/connection-status/lt-connection-status.js.map +1 -0
- package/dist/components/standalone/toast/lt-toast.d.ts +109 -0
- package/dist/components/standalone/toast/lt-toast.d.ts.map +1 -0
- package/dist/components/standalone/toast/lt-toast.js +256 -0
- package/dist/components/standalone/toast/lt-toast.js.map +1 -0
- package/dist/components/standalone/toast/toast.d.ts +52 -0
- package/dist/components/standalone/toast/toast.d.ts.map +1 -0
- package/dist/components/standalone/toast/toast.js +42 -0
- package/dist/components/standalone/toast/toast.js.map +1 -0
- package/dist/diagnostics/core.d.ts +137 -0
- package/dist/diagnostics/core.d.ts.map +1 -0
- package/dist/diagnostics/core.js +72 -0
- package/dist/diagnostics/core.js.map +1 -0
- package/dist/diagnostics/default-core.d.ts +10 -0
- package/dist/diagnostics/default-core.d.ts.map +1 -0
- package/dist/diagnostics/default-core.js +72 -0
- package/dist/diagnostics/default-core.js.map +1 -0
- package/dist/diagnostics/index.d.ts +84 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +106 -0
- package/dist/diagnostics/index.js.map +1 -0
- package/dist/dom.d.ts +55 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +162 -0
- package/dist/dom.js.map +1 -0
- package/dist/index.d.ts +97 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx/react.d.ts +180 -0
- package/dist/jsx/react.d.ts.map +1 -0
- package/dist/jsx/react.js +2 -0
- package/dist/jsx/react.js.map +1 -0
- package/dist/lotek.css +3617 -0
- package/dist/lt.d.ts +325 -0
- package/dist/lt.d.ts.map +1 -0
- package/dist/lt.js +183 -0
- package/dist/lt.js.map +1 -0
- package/dist/react.d.ts +492 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +296 -0
- package/dist/react.js.map +1 -0
- package/dist/register.d.ts +17 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +35 -0
- package/dist/register.js.map +1 -0
- package/dist/tokens/color.d.ts +43 -0
- package/dist/tokens/color.d.ts.map +1 -0
- package/dist/tokens/color.js +123 -0
- package/dist/tokens/color.js.map +1 -0
- package/dist/tokens/contract.d.ts +18 -0
- package/dist/tokens/contract.d.ts.map +1 -0
- package/dist/tokens/contract.js +18 -0
- package/dist/tokens/contract.js.map +1 -0
- package/dist/tokens/matrix-cells.d.ts +30 -0
- package/dist/tokens/matrix-cells.d.ts.map +1 -0
- package/dist/tokens/matrix-cells.js +273 -0
- package/dist/tokens/matrix-cells.js.map +1 -0
- package/dist/tokens/matrix.d.ts +18 -0
- package/dist/tokens/matrix.d.ts.map +1 -0
- package/dist/tokens/matrix.js +21 -0
- package/dist/tokens/matrix.js.map +1 -0
- package/dist/tokens/theme-check.d.ts +42 -0
- package/dist/tokens/theme-check.d.ts.map +1 -0
- package/dist/tokens/theme-check.js +10 -0
- package/dist/tokens/theme-check.js.map +1 -0
- package/dist/tokens/theme.d.ts +94 -0
- package/dist/tokens/theme.d.ts.map +1 -0
- package/dist/tokens/theme.js +14 -0
- package/dist/tokens/theme.js.map +1 -0
- package/dist/tokens/tokens.d.ts +122 -0
- package/dist/tokens/tokens.d.ts.map +1 -0
- package/dist/tokens/tokens.js +158 -0
- package/dist/tokens/tokens.js.map +1 -0
- package/dist/vocabulary/classes.d.ts +3 -0
- package/dist/vocabulary/classes.d.ts.map +1 -0
- package/dist/vocabulary/classes.js +1320 -0
- package/dist/vocabulary/classes.js.map +1 -0
- package/dist/vocabulary/elements.d.ts +3 -0
- package/dist/vocabulary/elements.d.ts.map +1 -0
- package/dist/vocabulary/elements.js +410 -0
- package/dist/vocabulary/elements.js.map +1 -0
- package/dist/vocabulary/types.d.ts +175 -0
- package/dist/vocabulary/types.d.ts.map +1 -0
- package/dist/vocabulary/types.js +11 -0
- package/dist/vocabulary/types.js.map +1 -0
- package/dist/wasi-host.d.ts +24 -0
- package/dist/wasi-host.d.ts.map +1 -0
- package/dist/wasi-host.js +140 -0
- package/dist/wasi-host.js.map +1 -0
- package/live-templates.xml +62 -0
- package/manifest/files.json +373 -0
- package/manifest/manifest.json +4965 -0
- package/manifest/manifest.schema.json +513 -0
- package/package.json +404 -4
- package/src/cli.ts +21 -0
- package/src/components/css/accordion/accordion.css +102 -0
- package/src/components/css/accordion/accordion.ts +50 -0
- package/src/components/css/alert/alert.css +53 -0
- package/src/components/css/alert/alert.ts +48 -0
- package/src/components/css/avatar/avatar.css +96 -0
- package/src/components/css/avatar/avatar.ts +57 -0
- package/src/components/css/badge/badge.css +42 -0
- package/src/components/css/badge/badge.ts +43 -0
- package/src/components/css/breadcrumbs/breadcrumbs.css +62 -0
- package/src/components/css/breadcrumbs/breadcrumbs.ts +30 -0
- package/src/components/css/button/button.css +128 -0
- package/src/components/css/button/button.ts +44 -0
- package/src/components/css/card/card.css +65 -0
- package/src/components/css/card/card.ts +50 -0
- package/src/components/css/checkbox/checkbox.css +114 -0
- package/src/components/css/checkbox/checkbox.ts +50 -0
- package/src/components/css/divider/divider.css +38 -0
- package/src/components/css/divider/divider.ts +42 -0
- package/src/components/css/error-text/error-text.css +21 -0
- package/src/components/css/error-text/error-text.ts +37 -0
- package/src/components/css/fieldset/fieldset.css +72 -0
- package/src/components/css/fieldset/fieldset.ts +87 -0
- package/src/components/css/file-upload/file-upload.css +102 -0
- package/src/components/css/file-upload/file-upload.ts +65 -0
- package/src/components/css/heading/heading.css +33 -0
- package/src/components/css/heading/heading.ts +42 -0
- package/src/components/css/help-text/help-text.css +24 -0
- package/src/components/css/help-text/help-text.ts +42 -0
- package/src/components/css/icon/icon.css +20 -0
- package/src/components/css/icon/icon.ts +44 -0
- package/src/components/css/input/input.css +145 -0
- package/src/components/css/input/input.ts +77 -0
- package/src/components/css/label/label.css +42 -0
- package/src/components/css/label/label.ts +51 -0
- package/src/components/css/link/link.css +38 -0
- package/src/components/css/link/link.ts +38 -0
- package/src/components/css/list/list.css +53 -0
- package/src/components/css/list/list.ts +44 -0
- package/src/components/css/pagination/pagination.css +95 -0
- package/src/components/css/pagination/pagination.ts +33 -0
- package/src/components/css/paragraph/paragraph.css +31 -0
- package/src/components/css/paragraph/paragraph.ts +53 -0
- package/src/components/css/progress/progress.css +161 -0
- package/src/components/css/progress/progress.ts +78 -0
- package/src/components/css/radio/radio.css +94 -0
- package/src/components/css/radio/radio.ts +50 -0
- package/src/components/css/search/search.css +50 -0
- package/src/components/css/search/search.ts +56 -0
- package/src/components/css/select/select.css +321 -0
- package/src/components/css/select/select.ts +63 -0
- package/src/components/css/shared.ts +199 -0
- package/src/components/css/skeleton/skeleton.css +62 -0
- package/src/components/css/skeleton/skeleton.ts +43 -0
- package/src/components/css/skip-link/skip-link.css +41 -0
- package/src/components/css/skip-link/skip-link.ts +22 -0
- package/src/components/css/spinner/spinner.css +48 -0
- package/src/components/css/spinner/spinner.ts +48 -0
- package/src/components/css/sr-only/sr-only.css +16 -0
- package/src/components/css/sr-only/sr-only.ts +21 -0
- package/src/components/css/switch/switch.css +105 -0
- package/src/components/css/switch/switch.ts +50 -0
- package/src/components/css/table/table.css +159 -0
- package/src/components/css/table/table.ts +107 -0
- package/src/components/css/tag/tag.css +58 -0
- package/src/components/css/tag/tag.ts +49 -0
- package/src/components/css/textarea/textarea.css +83 -0
- package/src/components/css/textarea/textarea.ts +40 -0
- package/src/components/css/toggle-group/toggle-group.css +92 -0
- package/src/components/css/toggle-group/toggle-group.ts +37 -0
- package/src/components/css/tooltip/tooltip.css +89 -0
- package/src/components/css/tooltip/tooltip.ts +37 -0
- package/src/components/default-texts.ts +34 -0
- package/src/components/events.ts +68 -0
- package/src/components/frame/dialog/dialog.css +306 -0
- package/src/components/frame/dialog/dialog.ts +166 -0
- package/src/components/frame/dialog/lt-dialog.ts +510 -0
- package/src/components/frame/error-summary/error-summary.css +47 -0
- package/src/components/frame/error-summary/error-summary.ts +56 -0
- package/src/components/frame/error-summary/lt-error-summary.ts +344 -0
- package/src/components/frame/field/field-core.ts +152 -0
- package/src/components/frame/field/field.css +17 -0
- package/src/components/frame/field/lt-field.ts +753 -0
- package/src/components/frame/popover/lt-popover.ts +555 -0
- package/src/components/frame/popover/popover.css +50 -0
- package/src/components/frame/popover/popover.ts +102 -0
- package/src/components/frame/session-timeout/lt-session-timeout.ts +681 -0
- package/src/components/frame/session-timeout/session-timeout.css +50 -0
- package/src/components/frame/session-timeout/session-timeout.ts +156 -0
- package/src/components/frame/suggestion/lt-suggestion.ts +1358 -0
- package/src/components/frame/suggestion/suggestion.css +132 -0
- package/src/components/frame/suggestion/suggestion.ts +172 -0
- package/src/components/frame/tabs/lt-tabs.ts +1319 -0
- package/src/components/frame/tabs/tabs.css +128 -0
- package/src/components/frame/tabs/tabs.ts +188 -0
- package/src/components/host-element.ts +400 -0
- package/src/components/standalone/connection-status/connection-status.css +41 -0
- package/src/components/standalone/connection-status/connection-status.ts +41 -0
- package/src/components/standalone/connection-status/lt-connection-status.ts +242 -0
- package/src/components/standalone/toast/lt-toast.ts +300 -0
- package/src/components/standalone/toast/toast.css +78 -0
- package/src/components/standalone/toast/toast.ts +70 -0
- package/src/diagnostics/core.ts +210 -0
- package/src/diagnostics/default-core.ts +81 -0
- package/src/diagnostics/index.ts +150 -0
- package/src/dom.ts +185 -0
- package/src/index.ts +218 -0
- package/src/jsx/react.ts +212 -0
- package/src/lt.ts +192 -0
- package/src/react.ts +411 -0
- package/src/register.ts +43 -0
- package/src/tailwind/tailwind.css +169 -0
- package/src/tokens/boundary.css +104 -0
- package/src/tokens/color-contract.json +231 -0
- package/src/tokens/color.ts +150 -0
- package/src/tokens/contract.ts +24 -0
- package/src/tokens/matrix-cells.ts +375 -0
- package/src/tokens/matrix.ts +32 -0
- package/src/tokens/theme-check.ts +51 -0
- package/src/tokens/theme.ts +104 -0
- package/src/tokens/tokens.css +569 -0
- package/src/tokens/tokens.ts +223 -0
- package/src/vocabulary/classes.ts +1322 -0
- package/src/vocabulary/elements.ts +412 -0
- package/src/vocabulary/types.ts +178 -0
- package/src/wasi-host.ts +155 -0
- package/web-types.json +800 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The core: LO-TEK's check and theme generator, written in Rust and built to
|
|
3
|
+
* a single WebAssembly module.
|
|
4
|
+
*
|
|
5
|
+
* The same module runs here, in VS Code, on the JVM (Chicory) and in
|
|
6
|
+
* IntelliJ, and gives the same answer everywhere. The module has no imports,
|
|
7
|
+
* so it needs no glue: the loader writes the text in as UTF-8 and reads the
|
|
8
|
+
* answer out as JSON, as every host language does.
|
|
9
|
+
*
|
|
10
|
+
* The core carries no data. The vocabulary and the colour contract are given
|
|
11
|
+
* to it with `loadManifest` and `loadContract`, from the files in the package.
|
|
12
|
+
*
|
|
13
|
+
* `diagnoseMarkup` and `diagnosePage` in `index.ts` load the module and the
|
|
14
|
+
* files from the package themselves. `loadCore` is for anyone who has the
|
|
15
|
+
* module some other way, such as an extension that bundles it, or a browser:
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* const core = loadCore(await WebAssembly.compileStreaming(fetch(url)))
|
|
19
|
+
* core.loadManifest(await (await fetch(manifestUrl)).text())
|
|
20
|
+
* core.diagnosePage(html)
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Synchronous, because Node, Bun and VS Code allow it. In the browser the
|
|
24
|
+
* module must be compiled with `WebAssembly.compile` first, and the compiled
|
|
25
|
+
* module passed here.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export type Severity = "error" | "warning"
|
|
29
|
+
|
|
30
|
+
/** One edit in a fix: replace the text from `start` to `end` with `text`. */
|
|
31
|
+
export type Edit = {
|
|
32
|
+
start: number
|
|
33
|
+
end: number
|
|
34
|
+
text: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A fix the editor can offer: one or more edits that belong together, such
|
|
39
|
+
* as `id` on the field and `for` on the label. They are in text order and do
|
|
40
|
+
* not overlap; apply them from the end, so the positions hold. `preferred`
|
|
41
|
+
* marks the safe one, such as `onlinetext` to `online-text`: the same
|
|
42
|
+
* letters, only written differently. A suggestion at a distance is a
|
|
43
|
+
* suggestion.
|
|
44
|
+
*/
|
|
45
|
+
export type Fix = {
|
|
46
|
+
title: string
|
|
47
|
+
edits: Edit[]
|
|
48
|
+
preferred?: boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type Finding = {
|
|
52
|
+
/** Where the finding starts, as an index into the text (UTF-16, like a `string`). */
|
|
53
|
+
start: number
|
|
54
|
+
end: number
|
|
55
|
+
/** The line the finding starts on, from 1. */
|
|
56
|
+
line: number
|
|
57
|
+
/** The column the finding starts at, from 1, in UTF-16 units. */
|
|
58
|
+
column: number
|
|
59
|
+
message: string
|
|
60
|
+
severity: Severity
|
|
61
|
+
/** The name of the rule, such as `unknown-class`. That is what `lotek-ignore-next` takes. */
|
|
62
|
+
rule: string
|
|
63
|
+
/** The component page, as a link in the message. */
|
|
64
|
+
link: string
|
|
65
|
+
fix?: Fix
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type CoreVersion = {
|
|
69
|
+
/** The version of the core. */
|
|
70
|
+
core: string
|
|
71
|
+
/** The version of `@lo-tek/interfaces` the manifest was written from, or `null` without a manifest. */
|
|
72
|
+
manifest: string | null
|
|
73
|
+
/** The manifest format the core understands. */
|
|
74
|
+
schemaVersion: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A promise in the colour contract that does not hold. */
|
|
78
|
+
export type Violation = {
|
|
79
|
+
family: string
|
|
80
|
+
promise: string
|
|
81
|
+
ratio: number
|
|
82
|
+
required: number
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The theme `buildTheme` returns: the tokens per appearance, the promises that break, and the CSS. */
|
|
86
|
+
export type CoreTheme = {
|
|
87
|
+
light: Record<string, string>
|
|
88
|
+
dark: Record<string, string>
|
|
89
|
+
/** Promises that do not hold. Empty when the theme is sound. */
|
|
90
|
+
violations: Violation[]
|
|
91
|
+
css: string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** What `inspectTheme` found in a theme the consumer has written by hand. */
|
|
95
|
+
export type CoreThemeReport = {
|
|
96
|
+
problems: { selector: string; message: string }[]
|
|
97
|
+
/** Blocks that were actually read. */
|
|
98
|
+
blocks: number
|
|
99
|
+
/** Values the consumer wrote, and that were understood. */
|
|
100
|
+
declarations: number
|
|
101
|
+
/** Promises that were actually checked. */
|
|
102
|
+
promises: number
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** What `inspectStyles` found in a stylesheet. */
|
|
106
|
+
export type CoreStyles = {
|
|
107
|
+
/** Every class that appears in a selector. */
|
|
108
|
+
classes: string[]
|
|
109
|
+
/** Every class with an attribute, and the value when the selector requires one. */
|
|
110
|
+
attributes: { class: string; attribute: string; value?: string }[]
|
|
111
|
+
/** The URLs in `@import`. */
|
|
112
|
+
imports: string[]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type Core = {
|
|
116
|
+
/** Checks a template or a fragment. Throws without a manifest. */
|
|
117
|
+
diagnoseMarkup(html: string): Finding[]
|
|
118
|
+
/** Checks a whole page, including the wiring. Throws without a manifest. */
|
|
119
|
+
diagnosePage(html: string): Finding[]
|
|
120
|
+
/** Gives the core the vocabulary. Throws with the core's explanation if it cannot be read. */
|
|
121
|
+
loadManifest(json: string): void
|
|
122
|
+
/** Gives the core the colour contract. Throws with the core's explanation if it cannot be read. */
|
|
123
|
+
loadContract(json: string): void
|
|
124
|
+
version(): CoreVersion
|
|
125
|
+
/** Builds a theme from the recipe. Throws without a colour contract, or when the recipe cannot be used. */
|
|
126
|
+
buildTheme(recipe: unknown): CoreTheme
|
|
127
|
+
/** Checks every promise in a theme written as CSS. Throws without a colour contract. */
|
|
128
|
+
inspectTheme(css: string): CoreThemeReport
|
|
129
|
+
/** The classes and attribute values a stylesheet styles, read with the core's CSS reader. */
|
|
130
|
+
inspectStyles(css: string): CoreStyles
|
|
131
|
+
/**
|
|
132
|
+
* The check with the stylesheets as well: a class none of them styles, and
|
|
133
|
+
* a value without a rule. `page` checks as `diagnosePage`, otherwise as
|
|
134
|
+
* `diagnoseMarkup`. Throws without a manifest.
|
|
135
|
+
*/
|
|
136
|
+
diagnoseStyled(html: string, css: string[], page: boolean): Finding[]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
type Exports = {
|
|
140
|
+
memory: WebAssembly.Memory
|
|
141
|
+
alloc(length: number): number
|
|
142
|
+
diagnose_markup_raw(pointer: number, length: number): number
|
|
143
|
+
diagnose_page_raw(pointer: number, length: number): number
|
|
144
|
+
load_manifest_raw(pointer: number, length: number): number
|
|
145
|
+
load_contract_raw(pointer: number, length: number): number
|
|
146
|
+
version_raw(): void
|
|
147
|
+
build_theme_raw(pointer: number, length: number): number
|
|
148
|
+
inspect_theme_raw(pointer: number, length: number): number
|
|
149
|
+
inspect_styles_raw(pointer: number, length: number): void
|
|
150
|
+
diagnose_styled_raw(pointer: number, length: number): number
|
|
151
|
+
result_ptr(): number
|
|
152
|
+
result_len(): number
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Loads the core from the module's bytes or an already compiled module. */
|
|
156
|
+
export function loadCore(source: BufferSource | WebAssembly.Module): Core {
|
|
157
|
+
const module =
|
|
158
|
+
source instanceof WebAssembly.Module
|
|
159
|
+
? source
|
|
160
|
+
: new WebAssembly.Module(source)
|
|
161
|
+
const e = new WebAssembly.Instance(module, {}).exports as unknown as Exports
|
|
162
|
+
const encoder = new TextEncoder()
|
|
163
|
+
const decoder = new TextDecoder()
|
|
164
|
+
|
|
165
|
+
/** Writes the text into the module's memory and returns the pointer and length. */
|
|
166
|
+
const write = (text: string): [number, number] => {
|
|
167
|
+
const bytes = encoder.encode(text)
|
|
168
|
+
const pointer = e.alloc(bytes.length)
|
|
169
|
+
// The memory can grow on every call, so the view is created after `alloc`.
|
|
170
|
+
new Uint8Array(e.memory.buffer, pointer, bytes.length).set(bytes)
|
|
171
|
+
return [pointer, bytes.length]
|
|
172
|
+
}
|
|
173
|
+
const read = <T>(): T =>
|
|
174
|
+
JSON.parse(
|
|
175
|
+
decoder.decode(
|
|
176
|
+
new Uint8Array(e.memory.buffer, e.result_ptr(), e.result_len()),
|
|
177
|
+
),
|
|
178
|
+
) as T
|
|
179
|
+
/** The answer from a call that can be rejected: the value, or the core's explanation as an error. */
|
|
180
|
+
const answer = <T>(status: number): T => {
|
|
181
|
+
if (status !== 0) throw new Error(read<{ error: string }>().error)
|
|
182
|
+
return read<T>()
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
diagnoseMarkup: (html) => answer(e.diagnose_markup_raw(...write(html))),
|
|
187
|
+
diagnosePage: (html) => answer(e.diagnose_page_raw(...write(html))),
|
|
188
|
+
loadManifest: (json) => {
|
|
189
|
+
answer(e.load_manifest_raw(...write(json)))
|
|
190
|
+
},
|
|
191
|
+
loadContract: (json) => {
|
|
192
|
+
answer(e.load_contract_raw(...write(json)))
|
|
193
|
+
},
|
|
194
|
+
version: () => {
|
|
195
|
+
e.version_raw()
|
|
196
|
+
return read()
|
|
197
|
+
},
|
|
198
|
+
buildTheme: (recipe) =>
|
|
199
|
+
answer(e.build_theme_raw(...write(JSON.stringify(recipe)))),
|
|
200
|
+
inspectTheme: (css) => answer(e.inspect_theme_raw(...write(css))),
|
|
201
|
+
inspectStyles: (css) => {
|
|
202
|
+
e.inspect_styles_raw(...write(css))
|
|
203
|
+
return read()
|
|
204
|
+
},
|
|
205
|
+
diagnoseStyled: (html, css, page) =>
|
|
206
|
+
answer(
|
|
207
|
+
e.diagnose_styled_raw(...write(JSON.stringify({ html, css, page }))),
|
|
208
|
+
),
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The core from the package itself, loaded the first time it is needed, with
|
|
3
|
+
* the package's manifest and colour contract, which sit next to the module.
|
|
4
|
+
*
|
|
5
|
+
* Both the check (`@lo-tek/interfaces/diagnostics`) and the theme
|
|
6
|
+
* (`@lo-tek/interfaces/theme` and `theme-check`) use the same instance.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { type Core, loadCore } from "./core.js"
|
|
10
|
+
|
|
11
|
+
/** The files in the package, seen from both `src/diagnostics/` and `dist/diagnostics/`. */
|
|
12
|
+
const MODULE = new URL("../../core/lotek-core.wasm", import.meta.url)
|
|
13
|
+
const MANIFEST = new URL("../../manifest/manifest.json", import.meta.url)
|
|
14
|
+
const CONTRACT = new URL(
|
|
15
|
+
"../../src/tokens/color-contract.json",
|
|
16
|
+
import.meta.url,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
let core: Core | undefined
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* Loaded the first time it is needed, not when the module is imported:
|
|
23
|
+
* anyone who only wants the types, or `loadCore`, should not pay for it.
|
|
24
|
+
*
|
|
25
|
+
* `node:fs` is fetched with `process.getBuiltinModule` instead of an import,
|
|
26
|
+
* so the file can be bundled for the browser without the bundler looking
|
|
27
|
+
* for `fs`.
|
|
28
|
+
*/
|
|
29
|
+
export function defaultCore(): Core {
|
|
30
|
+
if (core) return core
|
|
31
|
+
const fs = globalThis.process?.getBuiltinModule?.("node:fs") as
|
|
32
|
+
| typeof import("node:fs")
|
|
33
|
+
| undefined
|
|
34
|
+
if (!fs) {
|
|
35
|
+
// `getBuiltinModule` arrived in Node 20.16 and 22.3, so Node 21 and 22.0
|
|
36
|
+
// to 22.2 are excluded even though they are newer than 20.16. `engines`
|
|
37
|
+
// in package.json says the same. A Node without it is still Node, and
|
|
38
|
+
// should be told that the version is wrong, with both limits, so Node
|
|
39
|
+
// 22.1 is not told it is older than 20.16.
|
|
40
|
+
const node = globalThis.process?.versions?.node
|
|
41
|
+
throw new Error(
|
|
42
|
+
node
|
|
43
|
+
? `LO-TEK requires Node 20.16 or later in the 20 series, or Node 22.3 or later, and this is Node ${node}.`
|
|
44
|
+
: "LO-TEK reads the core from the package, which requires Node, Bun or Deno. In the browser, load lotek-core.wasm yourself, pass it to loadCore, and give the core manifest.json with loadManifest and color-contract.json with loadContract.",
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
let bytes: Uint8Array
|
|
48
|
+
try {
|
|
49
|
+
bytes = fs.readFileSync(MODULE)
|
|
50
|
+
} catch {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Could not find the core at ${MODULE.pathname}. In the repository it is built with "bun run core" in interfaces/, which requires Rust.`,
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
// The manifest is only needed by the check, and the colour contract only
|
|
56
|
+
// by the theme. Each is read the first time a call needs it.
|
|
57
|
+
const loaded = loadCore(bytes)
|
|
58
|
+
let manifest = false
|
|
59
|
+
let contract = false
|
|
60
|
+
const withManifest = () => {
|
|
61
|
+
if (!manifest) loaded.loadManifest(fs.readFileSync(MANIFEST, "utf8"))
|
|
62
|
+
manifest = true
|
|
63
|
+
return loaded
|
|
64
|
+
}
|
|
65
|
+
const withContract = () => {
|
|
66
|
+
if (!contract) loaded.loadContract(fs.readFileSync(CONTRACT, "utf8"))
|
|
67
|
+
contract = true
|
|
68
|
+
return loaded
|
|
69
|
+
}
|
|
70
|
+
core = {
|
|
71
|
+
...loaded,
|
|
72
|
+
diagnoseMarkup: (html) => withManifest().diagnoseMarkup(html),
|
|
73
|
+
diagnosePage: (html) => withManifest().diagnosePage(html),
|
|
74
|
+
diagnoseStyled: (html, css, page) =>
|
|
75
|
+
withManifest().diagnoseStyled(html, css, page),
|
|
76
|
+
version: () => withManifest().version(),
|
|
77
|
+
buildTheme: (recipe) => withContract().buildTheme(recipe),
|
|
78
|
+
inspectTheme: (css) => withContract().inspectTheme(css),
|
|
79
|
+
}
|
|
80
|
+
return core
|
|
81
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks markup against LO-TEK, outside the browser.
|
|
3
|
+
*
|
|
4
|
+
* `diagnoseMarkup(html)` is the same check the editor extensions run while
|
|
5
|
+
* you type, and that `lotek check` runs on the command line. It is here
|
|
6
|
+
* because markup produced without JavaScript, in a Go template, a Kotlin
|
|
7
|
+
* string or a Razor view, has no compiler looking at the attributes. A test
|
|
8
|
+
* in the app can then run it over the HTML the server actually sends:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { diagnosePage } from "@lo-tek/interfaces/diagnostics"
|
|
12
|
+
*
|
|
13
|
+
* expect(diagnosePage(html)).toEqual([])
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* The check is the core, written in Rust and built to WebAssembly (see
|
|
17
|
+
* `core.ts`). The module ships with the package, and is loaded the first
|
|
18
|
+
* time one of the functions is called, in Node, Bun and Deno. In the
|
|
19
|
+
* browser, or where the module is bundled some other way, `loadCore` loads
|
|
20
|
+
* it.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { Finding } from "./core.js"
|
|
24
|
+
|
|
25
|
+
export type { Manifest } from "../vocabulary/types.js"
|
|
26
|
+
|
|
27
|
+
import { defaultCore } from "./default-core.js"
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
type Core,
|
|
31
|
+
type CoreStyles,
|
|
32
|
+
type CoreTheme,
|
|
33
|
+
type CoreThemeReport,
|
|
34
|
+
type CoreVersion,
|
|
35
|
+
type Finding,
|
|
36
|
+
type Fix,
|
|
37
|
+
loadCore,
|
|
38
|
+
type Severity,
|
|
39
|
+
type Violation,
|
|
40
|
+
} from "./core.js"
|
|
41
|
+
|
|
42
|
+
/** Options for the check. */
|
|
43
|
+
export type DiagnoseOptions = {
|
|
44
|
+
/**
|
|
45
|
+
* The stylesheets the page loads, as text. With them the check also
|
|
46
|
+
* reports a class none of them styles, as when the component's stylesheet
|
|
47
|
+
* is not loaded, and a value without a rule, such as a variant added to a
|
|
48
|
+
* taken-over component without the CSS getting it.
|
|
49
|
+
*/
|
|
50
|
+
css?: string[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Every finding in the text: the vocabulary, for a template or a fragment of a page. */
|
|
54
|
+
export function diagnoseMarkup(
|
|
55
|
+
text: string,
|
|
56
|
+
options: DiagnoseOptions = {},
|
|
57
|
+
): Finding[] {
|
|
58
|
+
return options.css
|
|
59
|
+
? defaultCore().diagnoseStyled(text, options.css, false)
|
|
60
|
+
: defaultCore().diagnoseMarkup(text)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Every finding on a whole page: what `diagnoseMarkup` finds, and also that
|
|
65
|
+
* every `for`, `aria-describedby`, `aria-labelledby` and `aria-controls`
|
|
66
|
+
* points to an id that exists, that no id appears twice, and that every
|
|
67
|
+
* LO-TEK field and every help text and error text is wired.
|
|
68
|
+
*
|
|
69
|
+
* Use it on the HTML the server sends, not on a template: in a template split
|
|
70
|
+
* into pieces, the id can be in another file.
|
|
71
|
+
*
|
|
72
|
+
* ```ts
|
|
73
|
+
* const html = await (await fetch("http://localhost:8080/form")).text()
|
|
74
|
+
* expect(diagnosePage(html)).toEqual([])
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function diagnosePage(
|
|
78
|
+
text: string,
|
|
79
|
+
options: DiagnoseOptions = {},
|
|
80
|
+
): Finding[] {
|
|
81
|
+
return options.css
|
|
82
|
+
? defaultCore().diagnoseStyled(text, options.css, true)
|
|
83
|
+
: defaultCore().diagnosePage(text)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** What `diagnoseRendered` needs from a page: `evaluate`, as in Playwright. */
|
|
87
|
+
export type RenderedPage = {
|
|
88
|
+
evaluate<T>(script: () => Promise<T>): Promise<T>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Reads the rendered page in the browser: the DOM as it stands now, and the
|
|
93
|
+
* text of every stylesheet, with what they import. A stylesheet from another
|
|
94
|
+
* origin, such as a CDN, cannot be read through the CSSOM without
|
|
95
|
+
* `crossorigin`, and is then fetched again with `fetch`.
|
|
96
|
+
*
|
|
97
|
+
* The function runs in the browser, and cannot use anything outside itself.
|
|
98
|
+
*/
|
|
99
|
+
export const READ_RENDERED_PAGE = async (): Promise<{
|
|
100
|
+
html: string
|
|
101
|
+
css: string[]
|
|
102
|
+
}> => {
|
|
103
|
+
const sheets: CSSStyleSheet[] = [
|
|
104
|
+
...document.styleSheets,
|
|
105
|
+
...document.adoptedStyleSheets,
|
|
106
|
+
]
|
|
107
|
+
const css: string[] = []
|
|
108
|
+
const read = async (sheet: CSSStyleSheet): Promise<void> => {
|
|
109
|
+
let rules: CSSRuleList
|
|
110
|
+
try {
|
|
111
|
+
rules = sheet.cssRules
|
|
112
|
+
} catch {
|
|
113
|
+
if (!sheet.href) return
|
|
114
|
+
const answer = await fetch(sheet.href)
|
|
115
|
+
if (!answer.ok)
|
|
116
|
+
throw new Error(
|
|
117
|
+
`The stylesheet ${sheet.href} could not be read, and answered ${answer.status} when it was fetched again.`,
|
|
118
|
+
)
|
|
119
|
+
css.push(await answer.text())
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
for (const rule of rules)
|
|
123
|
+
if (rule instanceof CSSImportRule && rule.styleSheet)
|
|
124
|
+
await read(rule.styleSheet)
|
|
125
|
+
css.push([...rules].map((rule) => rule.cssText).join("\n"))
|
|
126
|
+
}
|
|
127
|
+
for (const sheet of sheets) await read(sheet)
|
|
128
|
+
const doctype = document.doctype ? `<!DOCTYPE ${document.doctype.name}>` : ""
|
|
129
|
+
return { html: doctype + document.documentElement.outerHTML, css }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Checks the page as the browser has rendered it, with the stylesheets it
|
|
134
|
+
* has loaded: what `diagnosePage` finds, and what the stylesheets do not
|
|
135
|
+
* style. Markup the web components have added, and a class JavaScript has
|
|
136
|
+
* set, are included.
|
|
137
|
+
*
|
|
138
|
+
* ```ts
|
|
139
|
+
* import { diagnoseRendered } from "@lo-tek/interfaces/diagnostics"
|
|
140
|
+
*
|
|
141
|
+
* await page.goto("http://localhost:8080/form")
|
|
142
|
+
* expect(await diagnoseRendered(page)).toEqual([])
|
|
143
|
+
* ```
|
|
144
|
+
*
|
|
145
|
+
* `page` is a Playwright page, or anything with `evaluate`.
|
|
146
|
+
*/
|
|
147
|
+
export async function diagnoseRendered(page: RenderedPage): Promise<Finding[]> {
|
|
148
|
+
const { html, css } = await page.evaluate(READ_RENDERED_PAGE)
|
|
149
|
+
return defaultCore().diagnoseStyled(html, css, true)
|
|
150
|
+
}
|
package/src/dom.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applying the attributes to an element in the plain DOM.
|
|
3
|
+
*
|
|
4
|
+
* In JSX you spread the object straight onto the element. Without JSX you
|
|
5
|
+
* would have to loop through it yourself, and take care to remove attributes
|
|
6
|
+
* from the previous state. When a field goes from invalid to valid, the
|
|
7
|
+
* builder function leaves out `data-state`, and a `setAttribute` alone would
|
|
8
|
+
* have left the old value in place.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { managedNames } from "./components/css/shared.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* What gets cleaned up for a set without a builder function behind it.
|
|
15
|
+
*
|
|
16
|
+
* A handwritten or spread set is not in the map of what it manages, and then
|
|
17
|
+
* the `data-*` names are the only ones the function can safely remove: they
|
|
18
|
+
* belong to the system. A set straight from a builder function does not use
|
|
19
|
+
* this list. It cleans up exactly what the builder function manages, so
|
|
20
|
+
* `lt.popover().trigger` on a secondary button leaves `data-variant` alone.
|
|
21
|
+
*/
|
|
22
|
+
const SAFE_FOR_UNKNOWN = [
|
|
23
|
+
"data-variant",
|
|
24
|
+
"data-state",
|
|
25
|
+
"data-color",
|
|
26
|
+
"data-size",
|
|
27
|
+
"data-picker",
|
|
28
|
+
"data-required",
|
|
29
|
+
"data-optional",
|
|
30
|
+
"data-interactive",
|
|
31
|
+
"data-hoverable",
|
|
32
|
+
"data-selectable",
|
|
33
|
+
"data-autofocus",
|
|
34
|
+
] as const
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Names that are never removed, not even when the builder function manages
|
|
38
|
+
* them.
|
|
39
|
+
*
|
|
40
|
+
* `id` and `for` are connections other elements point to. `lt.errorSummary()`
|
|
41
|
+
* takes an optional `id`, and a call without it removed the id the consumer
|
|
42
|
+
* had written on the box: every link and every `aria-*` pointing there broke.
|
|
43
|
+
*/
|
|
44
|
+
const NEVER_REMOVED: readonly string[] = ["id", "for"]
|
|
45
|
+
|
|
46
|
+
let warnedAboutUnknownSet = false
|
|
47
|
+
|
|
48
|
+
/** The classes that belong to the system. The consumer's classes are kept. */
|
|
49
|
+
const IS_SYSTEM_CLASS = /^lt-/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The values a builder function can output.
|
|
53
|
+
*
|
|
54
|
+
* `true` and `false` are included because several of them are boolean HTML
|
|
55
|
+
* attributes: `disabled` on a switch, `multiple` on a file field, `hidden` on
|
|
56
|
+
* an error text. In HTML such an attribute is true as long as it exists,
|
|
57
|
+
* whatever its value, so `true` is set as the empty string and `false`
|
|
58
|
+
* removes it.
|
|
59
|
+
*/
|
|
60
|
+
export type Attributes = Record<string, string | boolean | undefined>
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Sets the attributes from an `lt` function on an element.
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* import { lt } from "@lo-tek/interfaces"
|
|
67
|
+
*
|
|
68
|
+
* const field = document.querySelector("input")
|
|
69
|
+
* lt.setAttributes(field, lt.input({ type: "email", state: "invalid" }))
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* Call it again to change state. What the builder function manages and is
|
|
73
|
+
* not in the new set is removed, and the `lt-` classes are replaced. A
|
|
74
|
+
* builder function manages the attributes it has an option for:
|
|
75
|
+
* `lt.spinner()` has `label`, and so manages `role` and `aria-label`. What it
|
|
76
|
+
* has no option for stays as it is: `lt.button()` has never written
|
|
77
|
+
* `disabled`, so a disabled button is still disabled afterwards. `id` and
|
|
78
|
+
* `for` are never removed.
|
|
79
|
+
*
|
|
80
|
+
* When two sets go on the same element, pass them separately, not spread
|
|
81
|
+
* together:
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* lt.setAttributes(field, lt.input({ type: "email" }), wiring.control)
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* A spread object is a new object, and then the function does not know which
|
|
88
|
+
* builder function it came from. It is set correctly, but only `data-*` is
|
|
89
|
+
* cleaned up. An explicit `false` in a handwritten set removes the attribute.
|
|
90
|
+
*
|
|
91
|
+
* It accepts `null`, and then does nothing. `document.querySelector()`
|
|
92
|
+
* returns `Element | null`, so without that every call site in a `strict` app
|
|
93
|
+
* would need a guard or an exclamation mark around a lookup that almost
|
|
94
|
+
* always succeeds. It is the same trade-off as `element?.classList`.
|
|
95
|
+
*/
|
|
96
|
+
export function setAttributes(
|
|
97
|
+
element: Element | null | undefined,
|
|
98
|
+
...sets: Attributes[]
|
|
99
|
+
): void {
|
|
100
|
+
if (!element || sets.length === 0) return
|
|
101
|
+
|
|
102
|
+
const merged: Attributes = {}
|
|
103
|
+
const removable = new Set<string>()
|
|
104
|
+
const classes: string[] = []
|
|
105
|
+
|
|
106
|
+
for (const set of sets) {
|
|
107
|
+
const names = managedNames(set)
|
|
108
|
+
if (names) {
|
|
109
|
+
for (const name of names) removable.add(name)
|
|
110
|
+
} else {
|
|
111
|
+
for (const name of SAFE_FOR_UNKNOWN) removable.add(name)
|
|
112
|
+
warnAboutUnknownSet(set)
|
|
113
|
+
}
|
|
114
|
+
for (const [name, value] of Object.entries(set)) {
|
|
115
|
+
if (name === "class") {
|
|
116
|
+
if (typeof value === "string") classes.push(...value.split(/\s+/))
|
|
117
|
+
} else if (value === false) {
|
|
118
|
+
// An explicit `false` in a handwritten set is a request.
|
|
119
|
+
merged[name] = false
|
|
120
|
+
removable.add(name)
|
|
121
|
+
} else if (value !== undefined) {
|
|
122
|
+
// `undefined` does not override an earlier set: the builder functions
|
|
123
|
+
// drop such keys themselves, and a handwritten set must not be able to
|
|
124
|
+
// take `aria-invalid` from `lt.input()` by naming it without a value.
|
|
125
|
+
merged[name] = value
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
for (const name of NEVER_REMOVED) removable.delete(name)
|
|
130
|
+
|
|
131
|
+
if (classes.length > 0) {
|
|
132
|
+
const ownClasses = [...element.classList].filter(
|
|
133
|
+
(token) => !IS_SYSTEM_CLASS.test(token),
|
|
134
|
+
)
|
|
135
|
+
// `setAttribute` and not `className`: on an `<svg>`, `className` is a
|
|
136
|
+
// read-only `SVGAnimatedString`, and `lt.icon()` belongs on an svg.
|
|
137
|
+
element.setAttribute(
|
|
138
|
+
"class",
|
|
139
|
+
[...new Set([...classes, ...ownClasses])].filter(Boolean).join(" "),
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const name of removable) {
|
|
144
|
+
if (name === "class") continue
|
|
145
|
+
setOrRemove(element, name, merged[name])
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const [name, value] of Object.entries(merged)) {
|
|
149
|
+
if (removable.has(name)) continue
|
|
150
|
+
if (value === undefined || value === false) continue
|
|
151
|
+
element.setAttribute(name, value === true ? "" : value)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Warns, once, about a set that looks like two spread together.
|
|
157
|
+
*
|
|
158
|
+
* `{ ...lt.input(), ...field.control }` is set correctly, but is a new object
|
|
159
|
+
* without a builder function behind it. The next call then does not remove
|
|
160
|
+
* `aria-invalid` when the field becomes valid, and nothing else would have
|
|
161
|
+
* said so.
|
|
162
|
+
*/
|
|
163
|
+
function warnAboutUnknownSet(set: Attributes): void {
|
|
164
|
+
if (warnedAboutUnknownSet) return
|
|
165
|
+
if (typeof set.class !== "string" || !/(^|\s)lt-/.test(set.class)) return
|
|
166
|
+
warnedAboutUnknownSet = true
|
|
167
|
+
console.warn(
|
|
168
|
+
"lt.setAttributes(): the set does not come straight from a builder " +
|
|
169
|
+
"function, so only the data attributes are cleaned up. Pass the sets " +
|
|
170
|
+
"separately instead of spreading them: " +
|
|
171
|
+
"lt.setAttributes(element, lt.input(), field.control).",
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function setOrRemove(
|
|
176
|
+
element: Element,
|
|
177
|
+
name: string,
|
|
178
|
+
value: string | boolean | undefined,
|
|
179
|
+
): void {
|
|
180
|
+
if (value === undefined || value === false) {
|
|
181
|
+
element.removeAttribute(name)
|
|
182
|
+
} else {
|
|
183
|
+
element.setAttribute(name, value === true ? "" : value)
|
|
184
|
+
}
|
|
185
|
+
}
|