@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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
@layer lotek {
|
|
2
|
+
/* The word. It is a link, but reads as part of the sentence: the colour
|
|
3
|
+
is the text's, and the dotted line says it has an explanation. */
|
|
4
|
+
.lt-term {
|
|
5
|
+
color: inherit;
|
|
6
|
+
text-decoration-line: underline;
|
|
7
|
+
text-decoration-style: dotted;
|
|
8
|
+
text-decoration-color: var(
|
|
9
|
+
--lt-term-underline-color,
|
|
10
|
+
var(--lt-color-accent-fill)
|
|
11
|
+
);
|
|
12
|
+
text-decoration-thickness: 0.1em;
|
|
13
|
+
text-underline-offset: 0.2em;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Only where there is a pointer that hovers. iOS keeps `:hover` on a
|
|
17
|
+
link after a tap, and the line stayed solid until the next tap
|
|
18
|
+
somewhere else. */
|
|
19
|
+
@media (hover: hover) {
|
|
20
|
+
.lt-term:hover {
|
|
21
|
+
text-decoration-style: solid;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* The word whose card is open, so the reader sees what the card belongs
|
|
26
|
+
to. It matters most on a narrow screen, where the card is a sheet at
|
|
27
|
+
the edge of the window and not next to the word. */
|
|
28
|
+
.lt-term[aria-expanded="true"] {
|
|
29
|
+
text-decoration-style: solid;
|
|
30
|
+
background: var(--lt-color-accent-surface);
|
|
31
|
+
border-radius: var(--lt-spacing-0-5);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.lt-term:focus-visible {
|
|
35
|
+
outline: var(--lt-focus-ring);
|
|
36
|
+
outline-offset: 2px;
|
|
37
|
+
border-radius: var(--lt-spacing-0-5);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* The card is the glossary entry itself, in the top layer while
|
|
41
|
+
`<lt-term>` has it open. Every rule stands on `:popover-open`, so the
|
|
42
|
+
entry looks as the page made it everywhere else. */
|
|
43
|
+
.lt-term__entry:popover-open {
|
|
44
|
+
/* The position is relative to the viewport. The values are set by the
|
|
45
|
+
web component, which works them out against the word. */
|
|
46
|
+
position: fixed;
|
|
47
|
+
inset: auto;
|
|
48
|
+
inset-block-start: var(--lt-term-top, 0);
|
|
49
|
+
inset-inline-start: var(--lt-term-left, 0);
|
|
50
|
+
margin: 0;
|
|
51
|
+
/* The space between the word and the card. The web component reads it
|
|
52
|
+
from here, where the browser has turned it into pixels, and places the
|
|
53
|
+
card with it. The card is never scrolled to, so the property does
|
|
54
|
+
nothing else. */
|
|
55
|
+
scroll-margin-block-start: var(--lt-term-gap, var(--lt-spacing-2));
|
|
56
|
+
box-sizing: border-box;
|
|
57
|
+
inline-size: max-content;
|
|
58
|
+
max-inline-size: var(
|
|
59
|
+
--lt-term-width,
|
|
60
|
+
min(22rem, calc(100vw - var(--lt-spacing-8)))
|
|
61
|
+
);
|
|
62
|
+
padding: var(--lt-term-padding, var(--lt-spacing-3) var(--lt-spacing-4));
|
|
63
|
+
font-family: inherit;
|
|
64
|
+
font-size: var(--lt-font-size-m);
|
|
65
|
+
line-height: var(--lt-line-height-default);
|
|
66
|
+
color: var(--lt-color-neutral-text-strong);
|
|
67
|
+
background: var(--lt-color-neutral-surface);
|
|
68
|
+
border: 1px solid var(--lt-color-neutral-border-strong);
|
|
69
|
+
border-inline-start: var(--lt-spacing-1) solid
|
|
70
|
+
var(--lt-term-accent-color, var(--lt-color-accent-fill));
|
|
71
|
+
border-radius: var(--lt-term-radius, var(--lt-spacing-1));
|
|
72
|
+
box-shadow: var(--lt-shadow-overlay);
|
|
73
|
+
/* An explanation taller than the window scrolls in the card rather
|
|
74
|
+
than running off the screen. */
|
|
75
|
+
max-block-size: calc(100dvh - var(--lt-spacing-4));
|
|
76
|
+
overflow-y: auto;
|
|
77
|
+
/* Scrolling to the end of the explanation stops there. Otherwise it
|
|
78
|
+
went on to the page behind, the word left the view, and the card
|
|
79
|
+
closed while the reader was still in it. */
|
|
80
|
+
overscroll-behavior: contain;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* `--lt-term-left` is the distance from the left edge of the viewport.
|
|
84
|
+
`inset-inline-start` is the right edge in a right-to-left document, so
|
|
85
|
+
there it is flipped, as on the popover. */
|
|
86
|
+
.lt-term__entry:popover-open:dir(rtl) {
|
|
87
|
+
inset-inline-start: auto;
|
|
88
|
+
inset-inline-end: var(--lt-term-left, 0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* On a narrow screen the card is a sheet across the window, at the
|
|
92
|
+
bottom, or at the top when the word is down where the sheet would
|
|
93
|
+
cover it. A card by the word covered the lines the reader was about to
|
|
94
|
+
read, and closing it meant reaching for the middle of the screen.
|
|
95
|
+
`--lt-term-sheet` tells the web component which form the card has; the
|
|
96
|
+
stylesheet is the one place that decides. The width is `sm` in the
|
|
97
|
+
breakpoints in `tokens.ts`: a media query cannot read a variable. */
|
|
98
|
+
@media (width < 640px) {
|
|
99
|
+
.lt-term__entry:popover-open {
|
|
100
|
+
--lt-term-sheet: 1;
|
|
101
|
+
inset-inline: 0;
|
|
102
|
+
inline-size: auto;
|
|
103
|
+
max-inline-size: none;
|
|
104
|
+
max-block-size: var(--lt-term-sheet-height, 60dvh);
|
|
105
|
+
padding-block-end: calc(
|
|
106
|
+
var(--lt-spacing-3) +
|
|
107
|
+
env(safe-area-inset-bottom, 0px)
|
|
108
|
+
);
|
|
109
|
+
border-radius: 0;
|
|
110
|
+
border-inline-end: none;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* The direction does not matter across the whole width. Without this
|
|
114
|
+
the rule above for right to left won, and the sheet hung from the
|
|
115
|
+
word. */
|
|
116
|
+
.lt-term__entry:popover-open:dir(rtl) {
|
|
117
|
+
inset-inline: 0;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* The label over the term. CSS cannot import a text, so it is read from a
|
|
122
|
+
variable with the English text as the fallback. */
|
|
123
|
+
.lt-term__entry:popover-open::before {
|
|
124
|
+
content: var(--lt-term-label-text, "Glossary");
|
|
125
|
+
display: block;
|
|
126
|
+
margin-block-end: var(--lt-spacing-1);
|
|
127
|
+
font-family: var(--lt-term-label-font, inherit);
|
|
128
|
+
font-size: var(--lt-font-size-xs);
|
|
129
|
+
line-height: var(--lt-line-height-compact);
|
|
130
|
+
letter-spacing: 0.08em;
|
|
131
|
+
text-transform: uppercase;
|
|
132
|
+
color: var(--lt-color-accent-text);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.lt-term__entry:popover-open > dt {
|
|
136
|
+
margin: 0;
|
|
137
|
+
font-weight: var(--lt-font-weight-semibold);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.lt-term__entry:popover-open > dd {
|
|
141
|
+
margin: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@starting-style {
|
|
145
|
+
.lt-term__entry:popover-open {
|
|
146
|
+
opacity: 0;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
151
|
+
.lt-term__entry:popover-open {
|
|
152
|
+
transition: opacity 120ms ease;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* More contrast: the line under the word is solid and in the text's own
|
|
157
|
+
colour, and the card's edge is as strong as the text. */
|
|
158
|
+
@media (prefers-contrast: more) {
|
|
159
|
+
.lt-term {
|
|
160
|
+
text-decoration-style: solid;
|
|
161
|
+
text-decoration-color: currentColor;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.lt-term__entry:popover-open {
|
|
165
|
+
border-block-color: var(--lt-color-neutral-text-strong);
|
|
166
|
+
border-inline-end-color: var(--lt-color-neutral-text-strong);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* High contrast: the shadow is removed and the surface is the page's, so
|
|
171
|
+
the edge is what holds the card apart from the text under it. */
|
|
172
|
+
@media (forced-colors: active) {
|
|
173
|
+
.lt-term__entry:popover-open {
|
|
174
|
+
border-color: CanvasText;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { attributes } from "../../css/shared.js"
|
|
2
|
+
|
|
3
|
+
export const TERM_CLASS = "lt-term" as const
|
|
4
|
+
export const TERM_ENTRY_CLASS = "lt-term__entry" as const
|
|
5
|
+
|
|
6
|
+
export type TermOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Id of the entry in the glossary: the `<div>` around the `<dt>` and
|
|
9
|
+
* `<dd>`. The word links to it, and the web component shows the entry
|
|
10
|
+
* itself as the card. Make it from the term, such as `term-signal`, so it
|
|
11
|
+
* is the same on every render.
|
|
12
|
+
*/
|
|
13
|
+
id: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type TermAttributes = {
|
|
17
|
+
word: {
|
|
18
|
+
class: typeof TERM_CLASS
|
|
19
|
+
href: string
|
|
20
|
+
"aria-details": string
|
|
21
|
+
}
|
|
22
|
+
entry: {
|
|
23
|
+
class: typeof TERM_ENTRY_CLASS
|
|
24
|
+
id: string
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The attributes for a word in the text that is explained in a glossary on
|
|
30
|
+
* the same page.
|
|
31
|
+
*
|
|
32
|
+
* Only what holds without JavaScript: the word is a link to the entry, and
|
|
33
|
+
* `aria-details` says the entry explains it. `<lt-term>` around the link
|
|
34
|
+
* opens the entry as a card by the word, and writes `aria-expanded`.
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* const signal = lt.term({ id: "term-signal" })
|
|
38
|
+
* ```
|
|
39
|
+
* ```html
|
|
40
|
+
* <p>Datastar keeps <lt-term><a {...signal.word}>signals</a></lt-term> in the browser.</p>
|
|
41
|
+
* <dl>
|
|
42
|
+
* <div {...signal.entry}><dt>Signal</dt><dd>A value Datastar keeps in the browser.</dd></div>
|
|
43
|
+
* </dl>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function term({ id }: TermOptions): TermAttributes {
|
|
47
|
+
// A made-up id would point at nothing: the entry is in the glossary, and
|
|
48
|
+
// only the template knows its id.
|
|
49
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
50
|
+
throw new Error(
|
|
51
|
+
"lt.term(): id is required. It is the id of the entry in the glossary, " +
|
|
52
|
+
'such as "term-signal".',
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
word: attributes({
|
|
57
|
+
class: TERM_CLASS,
|
|
58
|
+
href: `#${id}`,
|
|
59
|
+
"aria-details": id,
|
|
60
|
+
}),
|
|
61
|
+
entry: attributes({
|
|
62
|
+
class: TERM_ENTRY_CLASS,
|
|
63
|
+
id,
|
|
64
|
+
}),
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* `lt.term()` with the state `<lt-term>` writes, for `/react`.
|
|
70
|
+
*
|
|
71
|
+
* React reports a mismatch for every attribute a web component writes on an
|
|
72
|
+
* element React has rendered. `aria-expanded="false"` is there from the
|
|
73
|
+
* start, so the web component has nothing to write before React hydrates.
|
|
74
|
+
*/
|
|
75
|
+
export function termWithState(options: TermOptions): {
|
|
76
|
+
word: TermAttributes["word"] & { "aria-expanded": "false" }
|
|
77
|
+
entry: TermAttributes["entry"]
|
|
78
|
+
} {
|
|
79
|
+
const built = term(options)
|
|
80
|
+
return {
|
|
81
|
+
word: attributes(
|
|
82
|
+
{ ...built.word, "aria-expanded": "false" as const },
|
|
83
|
+
built.word,
|
|
84
|
+
),
|
|
85
|
+
entry: built.entry,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -587,3 +587,49 @@ export async function whenUpgraded<T extends HTMLElement>(
|
|
|
587
587
|
customElements.upgrade(element)
|
|
588
588
|
return element as T
|
|
589
589
|
}
|
|
590
|
+
|
|
591
|
+
/** The space between a panel and the window's edge. */
|
|
592
|
+
const PLACE_EDGE = 4
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Where a panel in the top layer goes next to the element that opened it,
|
|
596
|
+
* as distances from the window's top and left edge.
|
|
597
|
+
*
|
|
598
|
+
* `below` asks for the panel under the anchor, otherwise over it, and
|
|
599
|
+
* `alignRight` lines the panel's right edge up with the anchor's. `gap` is
|
|
600
|
+
* the space between the anchor and the panel. The panel must not end up off
|
|
601
|
+
* screen. If it goes out at the side, it is moved in. If there is no room on
|
|
602
|
+
* the side of the anchor that was asked for, it goes on the other side, and
|
|
603
|
+
* is then clamped into the viewport either way.
|
|
604
|
+
*
|
|
605
|
+
* `<lt-popover>` and `<lt-term>` place their panels with it, and write the
|
|
606
|
+
* answer as two variables the stylesheet reads. It lives here so a copy from
|
|
607
|
+
* `lotek takeover` finds it through `@lo-tek/interfaces/host-element`.
|
|
608
|
+
*/
|
|
609
|
+
export function place(
|
|
610
|
+
anchor: DOMRect,
|
|
611
|
+
panel: DOMRect,
|
|
612
|
+
below: boolean,
|
|
613
|
+
alignRight: boolean,
|
|
614
|
+
gap = 4,
|
|
615
|
+
): { top: number; left: number } {
|
|
616
|
+
// The viewport a fixed box lives in, without a classic scrollbar:
|
|
617
|
+
// `innerWidth` counts it, and a panel pushed in from the right edge sat
|
|
618
|
+
// under the scrollbar on Windows and Linux.
|
|
619
|
+
const width = document.documentElement.clientWidth || window.innerWidth
|
|
620
|
+
const height = document.documentElement.clientHeight || window.innerHeight
|
|
621
|
+
let top = below ? anchor.bottom + gap : anchor.top - panel.height - gap
|
|
622
|
+
let left = alignRight ? anchor.right - panel.width : anchor.left
|
|
623
|
+
|
|
624
|
+
// `top-*` lacked the vertical clamp once, and a panel at the top of the
|
|
625
|
+
// page lay entirely off screen, with no way to scroll it into view.
|
|
626
|
+
left = Math.max(PLACE_EDGE, Math.min(left, width - panel.width - PLACE_EDGE))
|
|
627
|
+
if (below && top + panel.height > height) {
|
|
628
|
+
top = anchor.top - panel.height - gap
|
|
629
|
+
} else if (!below && top < PLACE_EDGE) {
|
|
630
|
+
top = anchor.bottom + gap
|
|
631
|
+
}
|
|
632
|
+
top = Math.max(PLACE_EDGE, Math.min(top, height - panel.height - PLACE_EDGE))
|
|
633
|
+
|
|
634
|
+
return { top: Math.round(top), left: Math.round(left) }
|
|
635
|
+
}
|
|
@@ -32,7 +32,7 @@ const RECEIPT_MS = 4000
|
|
|
32
32
|
* ```ts
|
|
33
33
|
* const status = document.querySelector("lt-connection-status")
|
|
34
34
|
* try {
|
|
35
|
-
* const response = await fetch("/api/
|
|
35
|
+
* const response = await fetch("/api/routes", { method: "POST", body })
|
|
36
36
|
* if (!response.ok) throw new Error(response.statusText)
|
|
37
37
|
* status.reportSuccess()
|
|
38
38
|
* } catch {
|
package/src/diagnostics/index.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `diagnoseMarkup(html)` is the same check the editor extensions run while
|
|
5
5
|
* you type, and that `lotek check` runs on the command line. It is here
|
|
6
|
-
* because markup
|
|
7
|
-
*
|
|
6
|
+
* because markup from a template, in Go, a Kotlin string or a Razor view, has
|
|
7
|
+
* no compiler looking at the attributes. A test
|
|
8
8
|
* in the app can then run it over the HTML the server actually sends:
|
|
9
9
|
*
|
|
10
10
|
* ```ts
|
package/src/dom.ts
CHANGED
|
@@ -72,7 +72,8 @@ export type Attributes = Record<string, string | boolean | undefined>
|
|
|
72
72
|
* Call it again to change state. What the builder function manages and is
|
|
73
73
|
* not in the new set is removed, and the `lt-` classes are replaced. A
|
|
74
74
|
* builder function manages the attributes it has an option for:
|
|
75
|
-
* `lt.spinner()` has `label`, and so manages `role
|
|
75
|
+
* `lt.spinner()` has `label`, and so manages `role`, `aria-label` and
|
|
76
|
+
* `aria-hidden`. What it
|
|
76
77
|
* has no option for stays as it is: `lt.button()` has never written
|
|
77
78
|
* `disabled`, so a disabled button is still disabled afterwards. `id` and
|
|
78
79
|
* `for` are never removed.
|
package/src/index.ts
CHANGED
|
@@ -155,6 +155,8 @@ export type {
|
|
|
155
155
|
export { spinner } from "./components/css/spinner/spinner.js"
|
|
156
156
|
export type { SrOnlyAttributes } from "./components/css/sr-only/sr-only.js"
|
|
157
157
|
export { srOnly } from "./components/css/sr-only/sr-only.js"
|
|
158
|
+
export type { StepsAttributes } from "./components/css/steps/steps.js"
|
|
159
|
+
export { steps } from "./components/css/steps/steps.js"
|
|
158
160
|
export type {
|
|
159
161
|
SwitchAttributes,
|
|
160
162
|
SwitchOptions,
|
|
@@ -207,6 +209,8 @@ export * from "./components/frame/suggestion/lt-suggestion.js"
|
|
|
207
209
|
export * from "./components/frame/suggestion/suggestion.js"
|
|
208
210
|
export * from "./components/frame/tabs/lt-tabs.js"
|
|
209
211
|
export * from "./components/frame/tabs/tabs.js"
|
|
212
|
+
export * from "./components/frame/term/lt-term.js"
|
|
213
|
+
export * from "./components/frame/term/term.js"
|
|
210
214
|
export * from "./components/standalone/connection-status/connection-status.js"
|
|
211
215
|
export * from "./components/standalone/connection-status/lt-connection-status.js"
|
|
212
216
|
export * from "./components/standalone/toast/lt-toast.js"
|
package/src/jsx/react.ts
CHANGED
|
@@ -158,6 +158,8 @@ type LtPopoverAttributes = Host &
|
|
|
158
158
|
placement?: "bottom-start" | "bottom-end" | "top-start" | "top-end"
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
type LtTermAttributes = Host
|
|
162
|
+
|
|
161
163
|
type LtSessionTimeoutAttributes = Host & {
|
|
162
164
|
/** Seconds without activity before the warning appears. */
|
|
163
165
|
"warn-at"?: number | string
|
|
@@ -192,6 +194,7 @@ declare module "react" {
|
|
|
192
194
|
"lt-popover": LtPopoverAttributes
|
|
193
195
|
"lt-suggestion": LtSuggestionAttributes
|
|
194
196
|
"lt-tabs": LtTabsAttributes
|
|
197
|
+
"lt-term": LtTermAttributes
|
|
195
198
|
"lt-session-timeout": LtSessionTimeoutAttributes
|
|
196
199
|
"lt-connection-status": LtConnectionStatusAttributes
|
|
197
200
|
"lt-toast": LtToastAttributes
|
|
@@ -208,5 +211,6 @@ export type {
|
|
|
208
211
|
LtSessionTimeoutAttributes,
|
|
209
212
|
LtSuggestionAttributes,
|
|
210
213
|
LtTabsAttributes,
|
|
214
|
+
LtTermAttributes,
|
|
211
215
|
LtToastAttributes,
|
|
212
216
|
}
|
package/src/lt.ts
CHANGED
|
@@ -33,6 +33,7 @@ import { skeleton } from "./components/css/skeleton/skeleton.js"
|
|
|
33
33
|
import { skipLink } from "./components/css/skip-link/skip-link.js"
|
|
34
34
|
import { spinner } from "./components/css/spinner/spinner.js"
|
|
35
35
|
import { srOnly } from "./components/css/sr-only/sr-only.js"
|
|
36
|
+
import { steps } from "./components/css/steps/steps.js"
|
|
36
37
|
import { switchControl } from "./components/css/switch/switch.js"
|
|
37
38
|
import { table, tableSort } from "./components/css/table/table.js"
|
|
38
39
|
import { tag } from "./components/css/tag/tag.js"
|
|
@@ -46,6 +47,7 @@ import { popover } from "./components/frame/popover/popover.js"
|
|
|
46
47
|
import { sessionTimeout } from "./components/frame/session-timeout/session-timeout.js"
|
|
47
48
|
import { suggestion } from "./components/frame/suggestion/suggestion.js"
|
|
48
49
|
import { tabs } from "./components/frame/tabs/tabs.js"
|
|
50
|
+
import { term } from "./components/frame/term/term.js"
|
|
49
51
|
import { connectionStatus } from "./components/standalone/connection-status/connection-status.js"
|
|
50
52
|
import { toast } from "./components/standalone/toast/toast.js"
|
|
51
53
|
import { setAttributes } from "./dom.js"
|
|
@@ -98,16 +100,16 @@ export const lt = {
|
|
|
98
100
|
* Connects the label, field, help text and error text.
|
|
99
101
|
*
|
|
100
102
|
* ```ts
|
|
101
|
-
* const field = lt.field({ id: "
|
|
103
|
+
* const field = lt.field({ id: "fixer", required: "text", help: true, error: true, invalid })
|
|
102
104
|
*
|
|
103
|
-
* <label {...field.label}>
|
|
105
|
+
* <label {...field.label}>Fixer's address</label>
|
|
104
106
|
* <input {...lt.input({ type: "email", state: field.state })} {...field.control} />
|
|
105
|
-
* <p {...lt.helpText()} {...field.help}>
|
|
106
|
-
* <p {...lt.errorText()} {...field.error}>Enter
|
|
107
|
+
* <p {...lt.helpText()} {...field.help}>The board writes to the fixer, never to the crew.</p>
|
|
108
|
+
* <p {...lt.errorText()} {...field.error}>Enter the fixer's address, for example fixer@relay.example.</p>
|
|
107
109
|
* ```
|
|
108
110
|
*
|
|
109
|
-
* The same calculation `<lt-field>` does, but as data, for
|
|
110
|
-
*
|
|
111
|
+
* The same calculation `<lt-field>` does in the browser, but as data, for
|
|
112
|
+
* the code that renders the markup. Then no web component is needed.
|
|
111
113
|
*/
|
|
112
114
|
field: computeFieldAttributes,
|
|
113
115
|
/** A group of controls that belong together. Its label is `lt.legend`. */
|
|
@@ -143,6 +145,7 @@ export const lt = {
|
|
|
143
145
|
spinner,
|
|
144
146
|
/** Text only screen readers get. Requires `sr-only.css`. */
|
|
145
147
|
srOnly,
|
|
148
|
+
steps,
|
|
146
149
|
/**
|
|
147
150
|
* The field and the suggestion list. `<lt-suggestion>` handles the
|
|
148
151
|
* keyboard and the filtering, the server writes the markup.
|
|
@@ -169,6 +172,11 @@ export const lt = {
|
|
|
169
172
|
*/
|
|
170
173
|
tableSort,
|
|
171
174
|
tag,
|
|
175
|
+
/**
|
|
176
|
+
* A word in the text, linked to its entry in a glossary on the same page.
|
|
177
|
+
* `<lt-term>` around the link shows the entry as a card by the word.
|
|
178
|
+
*/
|
|
179
|
+
term,
|
|
172
180
|
textarea,
|
|
173
181
|
|
|
174
182
|
/**
|
package/src/react.ts
CHANGED
|
@@ -35,6 +35,7 @@ import { skeleton } from "./components/css/skeleton/skeleton.js"
|
|
|
35
35
|
import { skipLink } from "./components/css/skip-link/skip-link.js"
|
|
36
36
|
import { spinner } from "./components/css/spinner/spinner.js"
|
|
37
37
|
import { srOnly } from "./components/css/sr-only/sr-only.js"
|
|
38
|
+
import { steps } from "./components/css/steps/steps.js"
|
|
38
39
|
import { switchControl } from "./components/css/switch/switch.js"
|
|
39
40
|
import { table, tableSort } from "./components/css/table/table.js"
|
|
40
41
|
import { tag } from "./components/css/tag/tag.js"
|
|
@@ -55,6 +56,7 @@ import {
|
|
|
55
56
|
type TabsWithStateOptions,
|
|
56
57
|
tabsWithState,
|
|
57
58
|
} from "./components/frame/tabs/tabs.js"
|
|
59
|
+
import { termWithState } from "./components/frame/term/term.js"
|
|
58
60
|
import { connectionStatus } from "./components/standalone/connection-status/connection-status.js"
|
|
59
61
|
import { toast } from "./components/standalone/toast/toast.js"
|
|
60
62
|
import { type Attributes, setAttributes as setHtmlAttributes } from "./dom.js"
|
|
@@ -261,6 +263,7 @@ export const lt = {
|
|
|
261
263
|
progress: forReact(progress),
|
|
262
264
|
spinner: forReact(spinner),
|
|
263
265
|
srOnly: forReact(srOnly),
|
|
266
|
+
steps: forReact(steps),
|
|
264
267
|
switch: forReact(switchControl),
|
|
265
268
|
toggleGroup: forReact(toggleGroup),
|
|
266
269
|
tooltip: forReact(tooltip),
|
|
@@ -354,6 +357,18 @@ export const lt = {
|
|
|
354
357
|
}
|
|
355
358
|
},
|
|
356
359
|
|
|
360
|
+
/**
|
|
361
|
+
* With `aria-expanded`, which `<lt-term>` writes: React reports a mismatch
|
|
362
|
+
* for an attribute it did not render. See `termWithState`.
|
|
363
|
+
*/
|
|
364
|
+
term: (options: Parameters<typeof termWithState>[0]) => {
|
|
365
|
+
const built = termWithState(options)
|
|
366
|
+
return {
|
|
367
|
+
word: toReactAttributes(built.word),
|
|
368
|
+
entry: toReactAttributes(built.entry),
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
|
|
357
372
|
suggestion: (options: Parameters<typeof suggestion>[0]) => {
|
|
358
373
|
const built = suggestion(options)
|
|
359
374
|
return {
|
package/src/register.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { defineLtPopover } from "./components/frame/popover/lt-popover.js"
|
|
|
20
20
|
import { defineLtSessionTimeout } from "./components/frame/session-timeout/lt-session-timeout.js"
|
|
21
21
|
import { defineLtSuggestion } from "./components/frame/suggestion/lt-suggestion.js"
|
|
22
22
|
import { defineLtTabs } from "./components/frame/tabs/lt-tabs.js"
|
|
23
|
+
import { defineLtTerm } from "./components/frame/term/lt-term.js"
|
|
23
24
|
import { defineLtConnectionStatus } from "./components/standalone/connection-status/lt-connection-status.js"
|
|
24
25
|
import { defineLtToast } from "./components/standalone/toast/lt-toast.js"
|
|
25
26
|
|
|
@@ -40,4 +41,5 @@ export function defineLt(): void {
|
|
|
40
41
|
defineLtToast()
|
|
41
42
|
defineLtSessionTimeout()
|
|
42
43
|
defineLtConnectionStatus()
|
|
44
|
+
defineLtTerm()
|
|
43
45
|
}
|
package/src/tokens/boundary.css
CHANGED
package/src/tokens/tokens.css
CHANGED
|
@@ -527,6 +527,7 @@
|
|
|
527
527
|
.lt-skip-link,
|
|
528
528
|
.lt-spinner,
|
|
529
529
|
.lt-sr-only,
|
|
530
|
+
.lt-steps,
|
|
530
531
|
.lt-suggestion__empty,
|
|
531
532
|
.lt-suggestion__list,
|
|
532
533
|
.lt-suggestion__option,
|
|
@@ -539,6 +540,8 @@
|
|
|
539
540
|
.lt-tabs__list,
|
|
540
541
|
.lt-tabs__panel,
|
|
541
542
|
.lt-tag,
|
|
543
|
+
.lt-term,
|
|
544
|
+
.lt-term__entry,
|
|
542
545
|
.lt-textarea,
|
|
543
546
|
.lt-toast,
|
|
544
547
|
.lt-toast__close,
|
|
@@ -551,6 +554,7 @@
|
|
|
551
554
|
lt-tabs,
|
|
552
555
|
lt-error-summary,
|
|
553
556
|
lt-popover,
|
|
557
|
+
lt-term,
|
|
554
558
|
lt-suggestion,
|
|
555
559
|
lt-dialog,
|
|
556
560
|
lt-toast,
|