@lo-tek/interfaces 0.39.0 → 0.41.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 +178 -0
- package/README.md +1 -1
- package/agent/astro.md +16 -14
- package/agent/bundles.md +18 -16
- package/agent/datastar.md +70 -68
- package/agent/html.md +63 -61
- package/agent/kotlin.md +622 -0
- package/agent/react.md +18 -16
- package/agent/templates.md +72 -68
- package/core/lotek-core.wasm +0 -0
- package/core/lotek.wasm +0 -0
- package/dist/components/css/accordion/accordion.d.ts +1 -1
- package/dist/components/css/accordion/accordion.js +1 -1
- package/dist/components/css/alert/alert.d.ts +1 -1
- package/dist/components/css/alert/alert.js +1 -1
- package/dist/components/css/avatar/avatar.d.ts +1 -1
- package/dist/components/css/avatar/avatar.js +1 -1
- package/dist/components/css/badge/badge.d.ts +1 -1
- package/dist/components/css/badge/badge.js +1 -1
- package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +1 -1
- package/dist/components/css/breadcrumbs/breadcrumbs.js +1 -1
- package/dist/components/css/card/card.d.ts +1 -1
- package/dist/components/css/card/card.js +1 -1
- package/dist/components/css/file-upload/file-upload.d.ts +1 -1
- package/dist/components/css/file-upload/file-upload.js +1 -1
- package/dist/components/css/icon/icon.d.ts +1 -1
- package/dist/components/css/icon/icon.js +1 -1
- package/dist/components/css/label/label.d.ts +1 -1
- package/dist/components/css/label/label.js +1 -1
- package/dist/components/css/link/link.d.ts +1 -1
- package/dist/components/css/link/link.js +1 -1
- package/dist/components/css/spinner/spinner.d.ts +7 -5
- package/dist/components/css/spinner/spinner.d.ts.map +1 -1
- package/dist/components/css/spinner/spinner.js +7 -5
- package/dist/components/css/spinner/spinner.js.map +1 -1
- package/dist/components/css/steps/steps.d.ts +23 -0
- package/dist/components/css/steps/steps.d.ts.map +1 -0
- package/dist/components/css/steps/steps.js +21 -0
- package/dist/components/css/steps/steps.js.map +1 -0
- package/dist/components/css/tag/tag.d.ts +1 -1
- package/dist/components/css/tag/tag.js +1 -1
- package/dist/components/css/toggle-group/toggle-group.d.ts +1 -1
- package/dist/components/css/toggle-group/toggle-group.js +1 -1
- package/dist/components/css/tooltip/tooltip.d.ts +2 -2
- package/dist/components/css/tooltip/tooltip.js +2 -2
- package/dist/components/events.d.ts +3 -1
- package/dist/components/events.d.ts.map +1 -1
- package/dist/components/frame/dialog/dialog.d.ts +11 -6
- package/dist/components/frame/dialog/dialog.d.ts.map +1 -1
- package/dist/components/frame/dialog/dialog.js +4 -4
- package/dist/components/frame/dialog/dialog.js.map +1 -1
- package/dist/components/frame/dialog/lt-dialog.d.ts +1 -1
- package/dist/components/frame/dialog/lt-dialog.js +7 -7
- package/dist/components/frame/error-summary/error-summary.d.ts +6 -2
- package/dist/components/frame/error-summary/error-summary.d.ts.map +1 -1
- package/dist/components/frame/error-summary/error-summary.js +1 -1
- package/dist/components/frame/error-summary/error-summary.js.map +1 -1
- package/dist/components/frame/error-summary/lt-error-summary.d.ts +1 -1
- package/dist/components/frame/error-summary/lt-error-summary.js +6 -6
- package/dist/components/frame/field/field-core.d.ts +4 -2
- package/dist/components/frame/field/field-core.d.ts.map +1 -1
- package/dist/components/frame/field/field-core.js.map +1 -1
- package/dist/components/frame/field/lt-field.d.ts +5 -4
- package/dist/components/frame/field/lt-field.d.ts.map +1 -1
- package/dist/components/frame/field/lt-field.js +5 -4
- package/dist/components/frame/field/lt-field.js.map +1 -1
- package/dist/components/frame/popover/lt-popover.d.ts +3 -3
- package/dist/components/frame/popover/lt-popover.d.ts.map +1 -1
- package/dist/components/frame/popover/lt-popover.js +7 -22
- package/dist/components/frame/popover/lt-popover.js.map +1 -1
- package/dist/components/frame/popover/popover.d.ts +7 -3
- package/dist/components/frame/popover/popover.d.ts.map +1 -1
- package/dist/components/frame/popover/popover.js +2 -2
- package/dist/components/frame/popover/popover.js.map +1 -1
- package/dist/components/frame/session-timeout/lt-session-timeout.d.ts +8 -8
- package/dist/components/frame/session-timeout/lt-session-timeout.js +7 -7
- package/dist/components/frame/session-timeout/session-timeout.d.ts +10 -5
- package/dist/components/frame/session-timeout/session-timeout.d.ts.map +1 -1
- package/dist/components/frame/session-timeout/session-timeout.js +4 -4
- package/dist/components/frame/session-timeout/session-timeout.js.map +1 -1
- package/dist/components/frame/suggestion/lt-suggestion.d.ts +12 -11
- package/dist/components/frame/suggestion/lt-suggestion.d.ts.map +1 -1
- package/dist/components/frame/suggestion/lt-suggestion.js +11 -10
- package/dist/components/frame/suggestion/lt-suggestion.js.map +1 -1
- package/dist/components/frame/suggestion/suggestion.d.ts +3 -1
- package/dist/components/frame/suggestion/suggestion.d.ts.map +1 -1
- package/dist/components/frame/suggestion/suggestion.js.map +1 -1
- package/dist/components/frame/tabs/lt-tabs.d.ts +3 -3
- package/dist/components/frame/tabs/lt-tabs.js +5 -5
- package/dist/components/frame/tabs/lt-tabs.js.map +1 -1
- package/dist/components/frame/tabs/tabs.d.ts +8 -6
- package/dist/components/frame/tabs/tabs.d.ts.map +1 -1
- package/dist/components/frame/tabs/tabs.js +4 -4
- package/dist/components/frame/tabs/tabs.js.map +1 -1
- package/dist/components/frame/term/lt-term.d.ts +114 -0
- package/dist/components/frame/term/lt-term.d.ts.map +1 -0
- package/dist/components/frame/term/lt-term.js +530 -0
- package/dist/components/frame/term/lt-term.js.map +1 -0
- package/dist/components/frame/term/term.d.ts +55 -0
- package/dist/components/frame/term/term.d.ts.map +1 -0
- package/dist/components/frame/term/term.js +55 -0
- package/dist/components/frame/term/term.js.map +1 -0
- package/dist/components/host-element.d.ts +19 -0
- package/dist/components/host-element.d.ts.map +1 -1
- package/dist/components/host-element.js +37 -0
- package/dist/components/host-element.js.map +1 -1
- package/dist/components/standalone/connection-status/lt-connection-status.d.ts +1 -1
- package/dist/components/standalone/connection-status/lt-connection-status.js +1 -1
- package/dist/components/standalone/toast/lt-toast.d.ts +1 -1
- package/dist/components/standalone/toast/lt-toast.js +1 -1
- package/dist/diagnostics/index.d.ts +2 -2
- package/dist/diagnostics/index.js +2 -2
- package/dist/dom.d.ts +2 -1
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +2 -1
- package/dist/dom.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/jsx/react.d.ts +3 -1
- package/dist/jsx/react.d.ts.map +1 -1
- package/dist/lotek.css +370 -1
- package/dist/lt.d.ts +13 -6
- package/dist/lt.d.ts.map +1 -1
- package/dist/lt.js +14 -6
- package/dist/lt.js.map +1 -1
- package/dist/react.d.ts +19 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +14 -0
- package/dist/react.js.map +1 -1
- package/dist/register.d.ts.map +1 -1
- package/dist/register.js +2 -0
- package/dist/register.js.map +1 -1
- package/dist/vocabulary/classes.d.ts.map +1 -1
- package/dist/vocabulary/classes.js +60 -6
- package/dist/vocabulary/classes.js.map +1 -1
- package/dist/vocabulary/elements.d.ts.map +1 -1
- package/dist/vocabulary/elements.js +16 -3
- package/dist/vocabulary/elements.js.map +1 -1
- package/dist/vocabulary/types.d.ts +28 -3
- package/dist/vocabulary/types.d.ts.map +1 -1
- package/live-templates-kotlin.xml +69 -0
- package/live-templates.xml +29 -23
- package/manifest/files.json +18 -0
- package/manifest/manifest.json +510 -87
- package/manifest/manifest.schema.json +54 -7
- package/package.json +16 -5
- package/src/components/css/accordion/accordion.ts +1 -1
- package/src/components/css/alert/alert.ts +1 -1
- package/src/components/css/avatar/avatar.ts +1 -1
- package/src/components/css/badge/badge.ts +1 -1
- package/src/components/css/breadcrumbs/breadcrumbs.ts +1 -1
- package/src/components/css/card/card.ts +1 -1
- package/src/components/css/file-upload/file-upload.ts +1 -1
- package/src/components/css/icon/icon.ts +1 -1
- package/src/components/css/input/input.css +21 -0
- package/src/components/css/label/label.ts +1 -1
- package/src/components/css/link/link.ts +1 -1
- package/src/components/css/spinner/spinner.ts +8 -5
- package/src/components/css/steps/steps.css +240 -0
- package/src/components/css/steps/steps.ts +26 -0
- package/src/components/css/tag/tag.ts +1 -1
- package/src/components/css/toggle-group/toggle-group.ts +1 -1
- package/src/components/css/tooltip/tooltip.ts +2 -2
- package/src/components/events.ts +3 -1
- package/src/components/frame/dialog/dialog.ts +11 -6
- package/src/components/frame/dialog/lt-dialog.ts +7 -7
- package/src/components/frame/error-summary/error-summary.ts +6 -2
- package/src/components/frame/error-summary/lt-error-summary.ts +6 -6
- package/src/components/frame/field/field-core.ts +4 -2
- package/src/components/frame/field/lt-field.ts +5 -4
- package/src/components/frame/popover/lt-popover.ts +7 -27
- package/src/components/frame/popover/popover.ts +7 -3
- package/src/components/frame/session-timeout/lt-session-timeout.ts +8 -8
- package/src/components/frame/session-timeout/session-timeout.ts +10 -5
- package/src/components/frame/suggestion/lt-suggestion.ts +13 -12
- package/src/components/frame/suggestion/suggestion.css +1 -1
- package/src/components/frame/suggestion/suggestion.ts +3 -1
- package/src/components/frame/tabs/lt-tabs.ts +5 -5
- package/src/components/frame/tabs/tabs.css +1 -1
- package/src/components/frame/tabs/tabs.ts +8 -6
- package/src/components/frame/term/lt-term.ts +573 -0
- package/src/components/frame/term/term.css +177 -0
- package/src/components/frame/term/term.ts +87 -0
- package/src/components/host-element.ts +46 -0
- package/src/components/standalone/connection-status/lt-connection-status.ts +1 -1
- package/src/components/standalone/toast/lt-toast.ts +1 -1
- package/src/diagnostics/index.ts +2 -2
- package/src/dom.ts +2 -1
- package/src/index.ts +4 -0
- package/src/jsx/react.ts +4 -0
- package/src/lt.ts +14 -6
- package/src/react.ts +15 -0
- package/src/register.ts +2 -0
- package/src/tokens/boundary.css +4 -0
- package/src/tokens/tokens.css +4 -0
- package/src/vocabulary/classes.ts +60 -6
- package/src/vocabulary/elements.ts +16 -3
- package/src/vocabulary/types.ts +25 -3
- package/web-types.json +32 -11
|
@@ -25,7 +25,12 @@ export const dialogColors = [
|
|
|
25
25
|
export type DialogColor = (typeof dialogColors)[number]
|
|
26
26
|
|
|
27
27
|
export type DialogOptions = {
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* Id of the heading. The dialog takes its name from it with
|
|
30
|
+
* `aria-labelledby`. Make it from a key in the data, never from the loop
|
|
31
|
+
* index or a counter, so it is unique on the page and the same on every
|
|
32
|
+
* render.
|
|
33
|
+
*/
|
|
29
34
|
titleId: string
|
|
30
35
|
/**
|
|
31
36
|
* The dialog is open. Default: closed.
|
|
@@ -36,7 +41,7 @@ export type DialogOptions = {
|
|
|
36
41
|
*/
|
|
37
42
|
open?: boolean
|
|
38
43
|
/**
|
|
39
|
-
* Colour of the header. Default: none.
|
|
44
|
+
* Colour of the header. Default: `neutral`, which gives none.
|
|
40
45
|
*
|
|
41
46
|
* The colour applies to `header`, so the header must be in the markup for
|
|
42
47
|
* it to show: `<div {...box.header}>` around the heading. Without a header
|
|
@@ -94,15 +99,15 @@ export type DialogAttributes = {
|
|
|
94
99
|
* turns it into a modal.
|
|
95
100
|
*
|
|
96
101
|
* ```ts
|
|
97
|
-
* const box = lt.dialog({ titleId: "
|
|
102
|
+
* const box = lt.dialog({ titleId: "cancel-title", open: true })
|
|
98
103
|
* ```
|
|
99
104
|
* ```html
|
|
100
105
|
* <lt-dialog {...box.host}>
|
|
101
106
|
* <dialog {...box.dialog}>
|
|
102
|
-
* <h2 {...box.title}>
|
|
103
|
-
* <div {...box.body}>The
|
|
107
|
+
* <h2 {...box.title}>Cancel job 222-0481?</h2>
|
|
108
|
+
* <div {...box.body}>The crew keeps the advance. This cannot be undone.</div>
|
|
104
109
|
* <form method="dialog" {...box.footer}>
|
|
105
|
-
* <button class="lt-button" value="
|
|
110
|
+
* <button class="lt-button" value="keep">Keep the job</button>
|
|
106
111
|
* </form>
|
|
107
112
|
* </dialog>
|
|
108
113
|
* </lt-dialog>
|
|
@@ -65,8 +65,8 @@ export const LT_DIALOG_PARTS = {
|
|
|
65
65
|
* ```html
|
|
66
66
|
* <lt-dialog open>
|
|
67
67
|
* <dialog class="lt-dialog" aria-labelledby="title" open>
|
|
68
|
-
* <h2 class="lt-dialog__title" id="title">The
|
|
69
|
-
* <div class="lt-dialog__body">
|
|
68
|
+
* <h2 class="lt-dialog__title" id="title">The order is placed</h2>
|
|
69
|
+
* <div class="lt-dialog__body">Job 222-0481 is open to crews.</div>
|
|
70
70
|
* <form method="dialog" class="lt-dialog__footer">
|
|
71
71
|
* <button class="lt-button" value="close">Close</button>
|
|
72
72
|
* </form>
|
|
@@ -237,7 +237,7 @@ export class LtDialog extends HostElement {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
/**
|
|
240
|
-
* Fills in what a template without
|
|
240
|
+
* Fills in what a template without a builder function did not write: the class on
|
|
241
241
|
* the dialog, and the title. The title is what `aria-labelledby` points to
|
|
242
242
|
* when the server wrote it, otherwise the first heading outside the body,
|
|
243
243
|
* and only when the dialog does not already have a name from `aria-label`.
|
|
@@ -449,7 +449,7 @@ export class LtDialog extends HostElement {
|
|
|
449
449
|
* run. When something wrote `open` on the host in the meantime, such as a
|
|
450
450
|
* framework setting the same value again, the component opened the dialog
|
|
451
451
|
* again and cleared `returnValue`, and a listener on `close` read an empty
|
|
452
|
-
* value instead of "
|
|
452
|
+
* value instead of "cancel". `returnValue` is cleared every time the
|
|
453
453
|
* dialog opens, so a value here comes from a button in this round.
|
|
454
454
|
*/
|
|
455
455
|
if (
|
|
@@ -478,9 +478,9 @@ export class LtDialog extends HostElement {
|
|
|
478
478
|
/*
|
|
479
479
|
* Escape and `close()` without an argument leave the previous
|
|
480
480
|
* `returnValue` in place. A dialog the server opened again then
|
|
481
|
-
* reported "
|
|
482
|
-
* and the app that tells "
|
|
483
|
-
* something the user never asked for.
|
|
481
|
+
* reported "cancel" from last time when it was closed from the server,
|
|
482
|
+
* and the app that tells "Keep the job" from "Cancel the job" by the
|
|
483
|
+
* value cancelled something the user never asked for.
|
|
484
484
|
*/
|
|
485
485
|
dialog.returnValue = ""
|
|
486
486
|
dialog.showModal()
|
|
@@ -6,7 +6,11 @@ export const ERROR_SUMMARY_TITLE_CLASS = "lt-error-summary__title" as const
|
|
|
6
6
|
export type ErrorSummaryOptions = {
|
|
7
7
|
/** The number of errors the server found. When it is zero, the box is hidden. */
|
|
8
8
|
count?: number
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* The id of the box, so the form can move focus here after submission.
|
|
11
|
+
* Make it from a key in the data, never from the loop index or a counter,
|
|
12
|
+
* so it is unique on the page and the same on every render.
|
|
13
|
+
*/
|
|
10
14
|
id?: string
|
|
11
15
|
/**
|
|
12
16
|
* The id of the heading. The box takes its name from it with
|
|
@@ -50,7 +54,7 @@ export type ErrorSummaryAttributes = {
|
|
|
50
54
|
* ```html
|
|
51
55
|
* <lt-error-summary {...summary.host}>
|
|
52
56
|
* <h2 {...summary.title}>You need to fix 2 errors</h2>
|
|
53
|
-
* <ul><li><a href="#
|
|
57
|
+
* <ul><li><a href="#fixer">The fixer's address is missing an @</a></li></ul>
|
|
54
58
|
* </lt-error-summary>
|
|
55
59
|
* ```
|
|
56
60
|
*/
|
|
@@ -26,10 +26,10 @@ export const LT_ERROR_SUMMARY_PARTS = {
|
|
|
26
26
|
* Takes the user from an error in the summary to the field that failed.
|
|
27
27
|
*
|
|
28
28
|
* The server writes the content: the heading and the list of links. When the
|
|
29
|
-
* markup is written with
|
|
30
|
-
* class, `role="alert"` and `tabindex="-1"` on the box, and the component
|
|
31
|
-
* leaves them alone. When the markup comes from a template without
|
|
32
|
-
*
|
|
29
|
+
* markup is written with a builder function, `lt.errorSummary()` also writes
|
|
30
|
+
* the class, `role="alert"` and `tabindex="-1"` on the box, and the component
|
|
31
|
+
* leaves them alone. When the markup comes from a template without one, the
|
|
32
|
+
* component fills in those three and the class on the
|
|
33
33
|
* heading, and sets them back when a patch removes them. Either way, a box
|
|
34
34
|
* without a name gets `aria-labelledby` pointing at its heading. The component
|
|
35
35
|
* creates no nodes: it used to, and then the heading disappeared on the
|
|
@@ -49,7 +49,7 @@ export const LT_ERROR_SUMMARY_PARTS = {
|
|
|
49
49
|
* <lt-error-summary>
|
|
50
50
|
* <h2>The form has two errors</h2>
|
|
51
51
|
* <ul class="lt-list">
|
|
52
|
-
* <li><a href="#
|
|
52
|
+
* <li><a href="#fixer">Enter the fixer's address with an @</a></li>
|
|
53
53
|
* </ul>
|
|
54
54
|
* </lt-error-summary>
|
|
55
55
|
* ```
|
|
@@ -185,7 +185,7 @@ export class LtErrorSummary extends HostElement {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
|
-
* Fills in what a template without
|
|
188
|
+
* Fills in what a template without a builder function did not write. Only what is
|
|
189
189
|
* missing: a role the server chose stays, and so does a `tabindex`.
|
|
190
190
|
*/
|
|
191
191
|
private wire(): void {
|
|
@@ -17,10 +17,12 @@ export { createFieldId }
|
|
|
17
17
|
|
|
18
18
|
export type FieldOptions = {
|
|
19
19
|
/**
|
|
20
|
-
* The id of the control. Required.
|
|
20
|
+
* The id of the control. Required. Make it from a key in the data, never
|
|
21
|
+
* from the loop index or a counter, so it is unique on the page and the
|
|
22
|
+
* same on every render.
|
|
21
23
|
*
|
|
22
24
|
* In React it comes from `useId()`. Otherwise the field's name is usually
|
|
23
|
-
* the obvious choice. If you know for certain that the markup is rendered
|
|
25
|
+
* the obvious choice, and in a loop the name with the row's key. If you know for certain that the markup is rendered
|
|
24
26
|
* once, call `createFieldId()` yourself.
|
|
25
27
|
*
|
|
26
28
|
* It used to be optional, and the function created one when it was
|
|
@@ -124,10 +124,11 @@ function readControlWord(control: Element): ControlWord {
|
|
|
124
124
|
/**
|
|
125
125
|
* Connects label, control, help text and error text in plain DOM.
|
|
126
126
|
*
|
|
127
|
-
* The component is for markup
|
|
128
|
-
* PHP file, a Razor view or handwritten HTML. When the
|
|
129
|
-
*
|
|
130
|
-
* attributes instead, and this element is not
|
|
127
|
+
* The component is for markup from code that cannot call a builder function:
|
|
128
|
+
* a Go template, a PHP file, a Razor view or handwritten HTML. When the code
|
|
129
|
+
* can, in TypeScript wherever it runs, or in Kotlin or Java, `lt.field()` or
|
|
130
|
+
* `Lt.field()` should write the attributes instead, and this element is not
|
|
131
|
+
* needed.
|
|
131
132
|
*
|
|
132
133
|
* The component renders nothing. It used to insert a `<slot>` element into
|
|
133
134
|
* plain DOM, and since the server did not know about it, Datastar's morph
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
defineElement,
|
|
4
4
|
HostElement,
|
|
5
5
|
isServerControlled,
|
|
6
|
+
place,
|
|
6
7
|
SERVER_CONTROLLED,
|
|
7
8
|
setAttr,
|
|
8
9
|
setFlag,
|
|
@@ -40,9 +41,9 @@ export const LT_POPOVER_PARTS = {
|
|
|
40
41
|
* What the top layer does not do is place the panel. `position-anchor` is not
|
|
41
42
|
* yet in every browser, so the position is worked out here.
|
|
42
43
|
*
|
|
43
|
-
* When the markup is made with
|
|
44
|
-
* `aria-controls` on the button, and the class, the id and `popover`
|
|
45
|
-
* panel. When the markup comes from a template without
|
|
44
|
+
* When the markup is made with a builder function, `lt.popover()` writes the
|
|
45
|
+
* wiring: `aria-controls` on the button, and the class, the id and `popover`
|
|
46
|
+
* on the panel. When the markup comes from a template without one, the
|
|
46
47
|
* template writes a button and a panel with the class `lt-popover` and an id:
|
|
47
48
|
* the component sets `popover="manual"` and writes `aria-controls` on the
|
|
48
49
|
* button from the id. Only what is missing, so what the server wrote stays.
|
|
@@ -507,7 +508,6 @@ export class LtPopover extends HostElement {
|
|
|
507
508
|
|
|
508
509
|
const anchor = this.triggerElement.getBoundingClientRect()
|
|
509
510
|
const panel = this.panel.getBoundingClientRect()
|
|
510
|
-
const space = 4
|
|
511
511
|
|
|
512
512
|
const below = this.placement.startsWith("bottom")
|
|
513
513
|
// `start` and `end` follow the reading direction: in a right-to-left
|
|
@@ -520,30 +520,10 @@ export class LtPopover extends HostElement {
|
|
|
520
520
|
const rtl = this.panel.matches(":dir(rtl)")
|
|
521
521
|
const alignRight = this.placement.endsWith("end") !== rtl
|
|
522
522
|
|
|
523
|
-
|
|
524
|
-
let left = alignRight ? anchor.right - panel.width : anchor.left
|
|
525
|
-
|
|
526
|
-
// The panel must not end up off screen. If it goes out at the side, it is
|
|
527
|
-
// moved in. If there is no room on the side of the button the placement asks
|
|
528
|
-
// for, it goes on the other side, and is then clamped into the viewport
|
|
529
|
-
// either way. `top-*` lacked the last part, and a panel at the top of the
|
|
530
|
-
// page lay entirely off screen, with no way to scroll it into view.
|
|
531
|
-
left = Math.max(
|
|
532
|
-
space,
|
|
533
|
-
Math.min(left, window.innerWidth - panel.width - space),
|
|
534
|
-
)
|
|
535
|
-
if (below && top + panel.height > window.innerHeight) {
|
|
536
|
-
top = anchor.top - panel.height - space
|
|
537
|
-
} else if (!below && top < space) {
|
|
538
|
-
top = anchor.bottom + space
|
|
539
|
-
}
|
|
540
|
-
top = Math.max(
|
|
541
|
-
space,
|
|
542
|
-
Math.min(top, window.innerHeight - panel.height - space),
|
|
543
|
-
)
|
|
523
|
+
const { top, left } = place(anchor, panel, below, alignRight)
|
|
544
524
|
|
|
545
|
-
this.panel.style.setProperty("--lt-popover-top", `${
|
|
546
|
-
this.panel.style.setProperty("--lt-popover-left", `${
|
|
525
|
+
this.panel.style.setProperty("--lt-popover-top", `${top}px`)
|
|
526
|
+
this.panel.style.setProperty("--lt-popover-left", `${left}px`)
|
|
547
527
|
}
|
|
548
528
|
|
|
549
529
|
private emit(open: boolean): void {
|
|
@@ -16,7 +16,11 @@ export function isPopoverPlacement(value: unknown): value is PopoverPlacement {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export type PopoverOptions = {
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Id of the panel. Connects the button to the panel with `aria-controls`.
|
|
21
|
+
* Make it from a key in the data, never from the loop index or a counter,
|
|
22
|
+
* so it is unique on the page and the same on every render.
|
|
23
|
+
*/
|
|
20
24
|
id: string
|
|
21
25
|
/** The panel is open. Default: closed. */
|
|
22
26
|
open?: boolean
|
|
@@ -52,8 +56,8 @@ export type PopoverAttributes = {
|
|
|
52
56
|
* ```
|
|
53
57
|
* ```html
|
|
54
58
|
* <lt-popover>
|
|
55
|
-
* <button {...box.trigger}>What
|
|
56
|
-
* <div {...box.panel}>
|
|
59
|
+
* <button {...box.trigger}>What is the late fee?</button>
|
|
60
|
+
* <div {...box.panel}>EB 100 a night past the return date. No exceptions.</div>
|
|
57
61
|
* </lt-popover>
|
|
58
62
|
* ```
|
|
59
63
|
*/
|
|
@@ -141,9 +141,9 @@ function deepActiveElement(): HTMLElement | null {
|
|
|
141
141
|
* Warns before a signed-in session expires.
|
|
142
142
|
*
|
|
143
143
|
* This is the thing every large organisation builds itself and gets wrong.
|
|
144
|
-
* Either no warning comes, and the user loses a half-filled
|
|
145
|
-
*
|
|
146
|
-
*
|
|
144
|
+
* Either no warning comes, and the user loses a half-filled form, or the
|
|
145
|
+
* dialog steals focus in the middle of a sentence, or the screen reader
|
|
146
|
+
* reads the countdown every second.
|
|
147
147
|
*
|
|
148
148
|
* The renderer writes the dialog and the text, so the warning is in the
|
|
149
149
|
* language the app uses, with the translation tool the app already has. The
|
|
@@ -163,14 +163,14 @@ function deepActiveElement(): HTMLElement | null {
|
|
|
163
163
|
* ```html
|
|
164
164
|
* <lt-session-timeout warn-at="1500" expires-at="1800" data-ignore-morph>
|
|
165
165
|
* <dialog class="lt-session-timeout__dialog" role="alertdialog" aria-labelledby="session">
|
|
166
|
-
* <h2 class="lt-session-timeout__title" id="session">
|
|
166
|
+
* <h2 class="lt-session-timeout__title" id="session">Your session is ending</h2>
|
|
167
167
|
* <p class="lt-session-timeout__text">
|
|
168
|
-
*
|
|
168
|
+
* Your session on the Lattice ends in <span class="lt-session-timeout__count" aria-hidden="true"></span>. Unsaved routes will be lost.
|
|
169
169
|
* </p>
|
|
170
170
|
* <span class="lt-sr-only" role="status"></span>
|
|
171
171
|
* <form method="dialog" class="lt-session-timeout__actions">
|
|
172
|
-
* <button class="lt-button" value="extend">Stay
|
|
173
|
-
* <button class="lt-button" data-variant="secondary" value="logout">
|
|
172
|
+
* <button class="lt-button" value="extend">Stay connected</button>
|
|
173
|
+
* <button class="lt-button" data-variant="secondary" value="logout">Log out</button>
|
|
174
174
|
* </form>
|
|
175
175
|
* </dialog>
|
|
176
176
|
* </lt-session-timeout>
|
|
@@ -224,7 +224,7 @@ export class LtSessionTimeout extends HostElement {
|
|
|
224
224
|
* `close` arrives in a later task than the click that closed the dialog.
|
|
225
225
|
* When the clock ticked in between, `tick()` opened the dialog again and
|
|
226
226
|
* cleared `returnValue`, and the queued event then read `""` and extended
|
|
227
|
-
* the session: the user pressed "
|
|
227
|
+
* the session: the user pressed "Log out", and the app got
|
|
228
228
|
* `session-extend`.
|
|
229
229
|
*/
|
|
230
230
|
private shownDialog?: HTMLDialogElement
|
|
@@ -15,7 +15,12 @@ export const SESSION_EXTEND = "extend" as const
|
|
|
15
15
|
export const SESSION_LOGOUT = "logout" as const
|
|
16
16
|
|
|
17
17
|
export type SessionTimeoutOptions = {
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Id of the heading. The dialog takes its name from it with
|
|
20
|
+
* `aria-labelledby`. Make it from a key in the data, never from the loop
|
|
21
|
+
* index or a counter, so it is unique on the page and the same on every
|
|
22
|
+
* render.
|
|
23
|
+
*/
|
|
19
24
|
titleId: string
|
|
20
25
|
/** Seconds without activity before the warning appears. Default: 25 minutes. */
|
|
21
26
|
warnAt?: number
|
|
@@ -76,14 +81,14 @@ export type SessionTimeoutAttributes = {
|
|
|
76
81
|
* ```html
|
|
77
82
|
* <lt-session-timeout {...session.host}>
|
|
78
83
|
* <dialog {...session.dialog}>
|
|
79
|
-
* <h2 {...session.title}>
|
|
84
|
+
* <h2 {...session.title}>Your session is ending</h2>
|
|
80
85
|
* <p {...session.text}>
|
|
81
|
-
*
|
|
86
|
+
* Your session on the Lattice ends in <span {...session.count}></span>. Unsaved routes will be lost.
|
|
82
87
|
* </p>
|
|
83
88
|
* <span {...session.live}></span>
|
|
84
89
|
* <form {...session.actions}>
|
|
85
|
-
* <button {...session.extend}>Stay
|
|
86
|
-
* <button {...session.logout}>
|
|
90
|
+
* <button {...session.extend}>Stay connected</button>
|
|
91
|
+
* <button {...session.logout}>Log out</button>
|
|
87
92
|
* </form>
|
|
88
93
|
* </dialog>
|
|
89
94
|
* </lt-session-timeout>
|
|
@@ -64,9 +64,10 @@ const OPTION_SELECTOR = LT_SUGGESTION_PARTS.option
|
|
|
64
64
|
/**
|
|
65
65
|
* The keyboard and the filtering in a field with a suggestion list.
|
|
66
66
|
*
|
|
67
|
-
* The server writes the field and the list. When the
|
|
68
|
-
*
|
|
69
|
-
* When it comes from a template
|
|
67
|
+
* The server writes the field and the list. When the code that renders the
|
|
68
|
+
* markup can call a builder function, `lt.suggestion()` or `Lt.suggestion()`
|
|
69
|
+
* writes the roles, the ids and the wiring. When it comes from a template that
|
|
70
|
+
* cannot, the template writes
|
|
70
71
|
* `for` on the label and `id` on the field, or wraps the field in the label,
|
|
71
72
|
* which HTML requires for the field to have a name without JavaScript. The
|
|
72
73
|
* rest is enough: a `.lt-suggestion__list` with `<li>` and a
|
|
@@ -99,15 +100,15 @@ const OPTION_SELECTOR = LT_SUGGESTION_PARTS.option
|
|
|
99
100
|
*
|
|
100
101
|
* ```html
|
|
101
102
|
* <lt-suggestion>
|
|
102
|
-
* <label class="lt-label" for="
|
|
103
|
-
* <input class="lt-input" id="
|
|
103
|
+
* <label class="lt-label" for="breaker">Breaker</label>
|
|
104
|
+
* <input class="lt-input" id="breaker" role="combobox" aria-controls="breaker-list"
|
|
104
105
|
* aria-expanded="false" aria-autocomplete="list" autocomplete="off"
|
|
105
|
-
* aria-describedby="
|
|
106
|
-
* <ul class="lt-suggestion__list" id="
|
|
107
|
-
* <li class="lt-suggestion__option" id="
|
|
106
|
+
* aria-describedby="breaker-status">
|
|
107
|
+
* <ul class="lt-suggestion__list" id="breaker-list" role="listbox" hidden>
|
|
108
|
+
* <li class="lt-suggestion__option" id="breaker-option-0" role="option">Crowbar</li>
|
|
108
109
|
* </ul>
|
|
109
110
|
* <p class="lt-suggestion__empty">No matches</p>
|
|
110
|
-
* <span class="lt-sr-only" id="
|
|
111
|
+
* <span class="lt-sr-only" id="breaker-status" role="status"></span>
|
|
111
112
|
* </lt-suggestion>
|
|
112
113
|
* ```
|
|
113
114
|
*/
|
|
@@ -136,8 +137,8 @@ export class LtSuggestion extends HostElement {
|
|
|
136
137
|
*
|
|
137
138
|
* The pair is what the code in the value field applies to. If the user
|
|
138
139
|
* changes the text, the code no longer applies, and the value field is
|
|
139
|
-
* emptied; otherwise the form would submit the code for
|
|
140
|
-
* text "
|
|
140
|
+
* emptied; otherwise the form would submit the code for Crowbar with the
|
|
141
|
+
* text "Needle" in the field. If the text is still there after an update, the
|
|
141
142
|
* code is set back: the value field's `value` is an attribute, and a morph
|
|
142
143
|
* writes the server's over what the web component set.
|
|
143
144
|
*/
|
|
@@ -1196,7 +1197,7 @@ export class LtSuggestion extends HostElement {
|
|
|
1196
1197
|
}
|
|
1197
1198
|
// The field shows the text. The code goes to the value field, when the
|
|
1198
1199
|
// server has written one, and in the event. `data-value` used to end up in
|
|
1199
|
-
// the field, and the user who chose
|
|
1200
|
+
// the field, and the user who chose Crowbar saw "LDG-117".
|
|
1200
1201
|
if (setter) setter.call(this.control, label)
|
|
1201
1202
|
else this.control.value = label
|
|
1202
1203
|
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
display: none;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
/* Markup written without
|
|
53
|
+
/* Markup written without a builder function arrives without `hidden` on the list,
|
|
54
54
|
and the web component only closes it once it is registered. Until then
|
|
55
55
|
it is hidden, so it does not stand open below a field nobody has
|
|
56
56
|
touched. */
|
|
@@ -14,7 +14,9 @@ export const SUGGESTION_STATUS_CLASS = "lt-sr-only" as const
|
|
|
14
14
|
export type SuggestionOptions = Omit<FieldOptions, "id"> & {
|
|
15
15
|
/**
|
|
16
16
|
* Shared id stem. The control becomes `${id}`, the list `${id}-list`, and
|
|
17
|
-
* the options `${id}-option-${key}`.
|
|
17
|
+
* the options `${id}-option-${key}`. Make it from a key in the data, never
|
|
18
|
+
* from the loop index or a counter, so it is unique on the page and the
|
|
19
|
+
* same on every render.
|
|
18
20
|
*/
|
|
19
21
|
id: string
|
|
20
22
|
/**
|
|
@@ -192,11 +192,11 @@ function markGone(element: Element, name: string, word: Word): boolean {
|
|
|
192
192
|
*
|
|
193
193
|
* ```html
|
|
194
194
|
* <lt-tabs>
|
|
195
|
-
* <ul class="lt-tabs__list" aria-label="Parts of the
|
|
196
|
-
* <li><a href="#
|
|
195
|
+
* <ul class="lt-tabs__list" aria-label="Parts of the claim">
|
|
196
|
+
* <li><a href="#claim">Claim</a></li>
|
|
197
197
|
* <li><a href="#attachments">Attachments</a></li>
|
|
198
198
|
* </ul>
|
|
199
|
-
* <section class="lt-tabs__panel" id="
|
|
199
|
+
* <section class="lt-tabs__panel" id="claim"><h2>Claim</h2>…</section>
|
|
200
200
|
* <section class="lt-tabs__panel" id="attachments"><h2>Attachments</h2>…</section>
|
|
201
201
|
* </lt-tabs>
|
|
202
202
|
* ```
|
|
@@ -759,7 +759,7 @@ export class LtTabs extends HostElement {
|
|
|
759
759
|
// the user asked for. Waiting for every tab to have a panel was not
|
|
760
760
|
// enough: with one link pointing to nothing, the tab list never loaded.
|
|
761
761
|
// If the address points to something that never comes, such as
|
|
762
|
-
// `#/
|
|
762
|
+
// `#/claim/1`, there is nothing to select either. The server's word is read
|
|
763
763
|
// through the memory, so a new connection does not take the web
|
|
764
764
|
// component's writes for the server's.
|
|
765
765
|
const tabs = this.tabs
|
|
@@ -1066,7 +1066,7 @@ export class LtTabs extends HostElement {
|
|
|
1066
1066
|
warnAboutMarkup(
|
|
1067
1067
|
this,
|
|
1068
1068
|
"has a tab list without a name. Write aria-label on " +
|
|
1069
|
-
'.lt-tabs__list, such as aria-label="Parts of the
|
|
1069
|
+
'.lt-tabs__list, such as aria-label="Parts of the claim", or ' +
|
|
1070
1070
|
"aria-labelledby pointing to a heading; without a name a screen " +
|
|
1071
1071
|
'reader only says "tab list".',
|
|
1072
1072
|
() => {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
cursor: not-allowed;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
/* Markup written without
|
|
61
|
+
/* Markup written without a builder function comes without `hidden` on the panels,
|
|
62
62
|
and the web component sets it only once it is registered. Until then
|
|
63
63
|
only one panel is shown, the one the web component is going to select,
|
|
64
64
|
so the content does not jump when the rest are hidden:
|
|
@@ -6,11 +6,13 @@ export const TABS_PANEL_CLASS = "lt-tabs__panel" as const
|
|
|
6
6
|
export type TabsOptions = {
|
|
7
7
|
/**
|
|
8
8
|
* The shared id stem. The panels become `${id}-${key}`, and the headings in
|
|
9
|
-
* them `${id}-${key}-heading`.
|
|
9
|
+
* them `${id}-${key}-heading`. Make it from a key in the data, never from
|
|
10
|
+
* the loop index or a counter, so it is unique on the page and the same on
|
|
11
|
+
* every render.
|
|
10
12
|
*/
|
|
11
13
|
id: string
|
|
12
14
|
/**
|
|
13
|
-
* One key per tab, from the data, such as `["
|
|
15
|
+
* One key per tab, from the data, such as `["claim", "attachments"]`.
|
|
14
16
|
* The key gives the panel its id, and the link points to it. An index is
|
|
15
17
|
* not a key: if the server inserts a tab first, the same index points to
|
|
16
18
|
* another panel. An empty key, whitespace and two identical keys throw.
|
|
@@ -70,14 +72,14 @@ function prepare(
|
|
|
70
72
|
* behave like one, and hidden the panels.
|
|
71
73
|
*
|
|
72
74
|
* The first tab is selected when the page loads. Another one comes from the
|
|
73
|
-
* address, such as `#
|
|
75
|
+
* address, such as `#file-attachments`, or from `selected` and `select()` on
|
|
74
76
|
* `<lt-tabs>`.
|
|
75
77
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
+
* In React, use `lt.tabs()` from `@lo-tek/interfaces/react`, which gives the
|
|
79
|
+
* whole state, so React hydrates without mismatches.
|
|
78
80
|
*
|
|
79
81
|
* ```ts
|
|
80
|
-
* const tabs = lt.tabs({ id: "
|
|
82
|
+
* const tabs = lt.tabs({ id: "file", keys: ["claim", "attachments"], label: "Parts of the claim" })
|
|
81
83
|
* ```
|
|
82
84
|
*/
|
|
83
85
|
export const tabs = (options: TabsOptions): TabsAttributes => {
|