@lo-tek/interfaces 0.38.0 → 0.40.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 +123 -0
- package/README.md +1 -1
- package/agent/astro.md +9 -9
- package/agent/bundles.md +14 -12
- package/agent/datastar.md +65 -65
- package/agent/html.md +59 -59
- package/agent/react.md +16 -15
- package/agent/templates.md +65 -58
- 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/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/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.js +3 -3
- 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/host-element.d.ts +3 -0
- package/dist/components/host-element.d.ts.map +1 -1
- package/dist/components/host-element.js +90 -1
- 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/lotek.css +20 -1
- package/dist/lt.d.ts +5 -5
- package/dist/lt.js +5 -5
- package/dist/react.d.ts.map +1 -1
- package/dist/vocabulary/elements.js +1 -1
- package/dist/vocabulary/elements.js.map +1 -1
- package/dist/vocabulary/types.d.ts +26 -0
- package/dist/vocabulary/types.d.ts.map +1 -1
- package/live-templates-kotlin.xml +64 -0
- package/live-templates.xml +23 -23
- package/manifest/manifest.json +407 -80
- package/manifest/manifest.schema.json +61 -7
- package/package.json +2 -1
- 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/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/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 +3 -3
- 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/host-element.ts +97 -1
- 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/lt.ts +5 -5
- package/src/tokens/boundary.css +4 -0
- package/src/vocabulary/elements.ts +1 -1
- package/src/vocabulary/types.ts +23 -0
- package/web-types.json +2 -2
|
@@ -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 => {
|
|
@@ -12,12 +12,108 @@
|
|
|
12
12
|
* without `customElements` nothing is registered, and without registration
|
|
13
13
|
* the browser never creates an instance. It only exists so the module can
|
|
14
14
|
* load.
|
|
15
|
+
*
|
|
16
|
+
* In the browser the base does one thing: it adopts the properties a script
|
|
17
|
+
* set before the registration, see `adoptPropertiesSetBeforeUpgrade()`.
|
|
15
18
|
*/
|
|
16
19
|
|
|
17
20
|
export const HostElement =
|
|
18
21
|
typeof HTMLElement === "undefined"
|
|
19
22
|
? (class {} as unknown as typeof HTMLElement)
|
|
20
|
-
: HTMLElement
|
|
23
|
+
: (class extends HTMLElement {
|
|
24
|
+
constructor() {
|
|
25
|
+
super()
|
|
26
|
+
// Here the own properties are exactly what a script set before the
|
|
27
|
+
// upgrade: the component's own fields are assigned after `super()`
|
|
28
|
+
// returns. An element created after the registration has none, and
|
|
29
|
+
// costs nothing.
|
|
30
|
+
const names = Object.getOwnPropertyNames(this)
|
|
31
|
+
if (names.length === 0) return
|
|
32
|
+
const pending = holdPropertiesSetBeforeUpgrade(this, names)
|
|
33
|
+
if (pending.size === 0) return
|
|
34
|
+
// Written in a microtask, not here: the field initialisers run
|
|
35
|
+
// after `super()`, and a setter that notes something on the
|
|
36
|
+
// instance, as `<lt-popover>` does with `open`, would have been
|
|
37
|
+
// overwritten by them. By the microtask the whole constructor and
|
|
38
|
+
// the upgrade's `connectedCallback` are done.
|
|
39
|
+
queueMicrotask(() => {
|
|
40
|
+
for (const [name, value] of pending) {
|
|
41
|
+
Reflect.deleteProperty(this, name)
|
|
42
|
+
;(this as unknown as Record<string, unknown>)[name] = value
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
} as unknown as typeof HTMLElement)
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Takes the properties a script set on the element before it was upgraded out
|
|
50
|
+
* of the way, so they can be written through the component's setters.
|
|
51
|
+
*
|
|
52
|
+
* `dialog.open = true` from a script that runs before `defineLt()` lands on a
|
|
53
|
+
* plain `HTMLElement`, and becomes an own data property. When the element is
|
|
54
|
+
* upgraded, that property shadows the accessor on the prototype: the
|
|
55
|
+
* attribute is never set, and every later write, `dialog.open = false`
|
|
56
|
+
* included, goes to the shadow and never reaches the setter. Tested against
|
|
57
|
+
* `dist` on 2026-11-01: the setters were dead for the rest of the element's
|
|
58
|
+
* life.
|
|
59
|
+
*
|
|
60
|
+
* Each such property is replaced by a stand-in for the time until the
|
|
61
|
+
* microtask that writes the value. A read goes to the getter, so the
|
|
62
|
+
* component's first `sync()` sees the attribute and not the script's value:
|
|
63
|
+
* when it saw the value, it opened a dialog the script closed again right
|
|
64
|
+
* after the registration, and the write of `false` then removed an attribute
|
|
65
|
+
* that was never there, so nothing closed the dialog (found by GitHub's
|
|
66
|
+
* review). A write goes to the setter and cancels the adoption, so the last
|
|
67
|
+
* write wins. The values returned are what the microtask writes.
|
|
68
|
+
*
|
|
69
|
+
* Only a property with a setter somewhere on the prototype chain is taken;
|
|
70
|
+
* anything else a script put on the element stays as it is. A property set
|
|
71
|
+
* after the upgrade is never an own property, so there is nothing to do for
|
|
72
|
+
* an element created once the registration is there. A property someone made
|
|
73
|
+
* non-configurable cannot be moved out of the way, and is left shadowing the
|
|
74
|
+
* setter, as before. (Documented: the "lazy properties" pattern in Google's
|
|
75
|
+
* custom elements best practices.)
|
|
76
|
+
*/
|
|
77
|
+
function holdPropertiesSetBeforeUpgrade(
|
|
78
|
+
element: HTMLElement,
|
|
79
|
+
names: readonly string[],
|
|
80
|
+
): Map<string, unknown> {
|
|
81
|
+
const pending = new Map<string, unknown>()
|
|
82
|
+
for (const name of names) {
|
|
83
|
+
const own = Object.getOwnPropertyDescriptor(element, name)
|
|
84
|
+
if (!own || !("value" in own) || !own.configurable) continue
|
|
85
|
+
const accessor = accessorOnPrototype(element, name)
|
|
86
|
+
if (!accessor?.set) continue
|
|
87
|
+
const { get, set } = accessor
|
|
88
|
+
pending.set(name, own.value)
|
|
89
|
+
Object.defineProperty(element, name, {
|
|
90
|
+
configurable: true,
|
|
91
|
+
enumerable: true,
|
|
92
|
+
get: () => get?.call(element),
|
|
93
|
+
set: (value: unknown) => {
|
|
94
|
+
pending.delete(name)
|
|
95
|
+
Reflect.deleteProperty(element, name)
|
|
96
|
+
set.call(element, value)
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
return pending
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function accessorOnPrototype(
|
|
104
|
+
element: object,
|
|
105
|
+
name: string,
|
|
106
|
+
): PropertyDescriptor | undefined {
|
|
107
|
+
for (
|
|
108
|
+
let proto = Object.getPrototypeOf(element);
|
|
109
|
+
proto !== null;
|
|
110
|
+
proto = Object.getPrototypeOf(proto)
|
|
111
|
+
) {
|
|
112
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, name)
|
|
113
|
+
if (descriptor) return descriptor
|
|
114
|
+
}
|
|
115
|
+
return undefined
|
|
116
|
+
}
|
|
21
117
|
|
|
22
118
|
/*
|
|
23
119
|
* The types of the events, such as `dialog-toggle` with `detail.open`. Every
|
|
@@ -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/lt.ts
CHANGED
|
@@ -98,16 +98,16 @@ export const lt = {
|
|
|
98
98
|
* Connects the label, field, help text and error text.
|
|
99
99
|
*
|
|
100
100
|
* ```ts
|
|
101
|
-
* const field = lt.field({ id: "
|
|
101
|
+
* const field = lt.field({ id: "fixer", required: "text", help: true, error: true, invalid })
|
|
102
102
|
*
|
|
103
|
-
* <label {...field.label}>
|
|
103
|
+
* <label {...field.label}>Fixer's address</label>
|
|
104
104
|
* <input {...lt.input({ type: "email", state: field.state })} {...field.control} />
|
|
105
|
-
* <p {...lt.helpText()} {...field.help}>
|
|
105
|
+
* <p {...lt.helpText()} {...field.help}>The board writes to the fixer, never to the crew.</p>
|
|
106
106
|
* <p {...lt.errorText()} {...field.error}>Enter a valid address.</p>
|
|
107
107
|
* ```
|
|
108
108
|
*
|
|
109
|
-
* The same calculation `<lt-field>` does, but as data, for
|
|
110
|
-
*
|
|
109
|
+
* The same calculation `<lt-field>` does in the browser, but as data, for
|
|
110
|
+
* the code that renders the markup. Then no web component is needed.
|
|
111
111
|
*/
|
|
112
112
|
field: computeFieldAttributes,
|
|
113
113
|
/** A group of controls that belong together. Its label is `lt.legend`. */
|
package/src/tokens/boundary.css
CHANGED
|
@@ -4,7 +4,7 @@ import type { Elements } from "./types.js"
|
|
|
4
4
|
export const elements: Elements = {
|
|
5
5
|
"lt-field": {
|
|
6
6
|
"link": "https://lo-tek.sobernetics.no/components/field/",
|
|
7
|
-
"description": "Wires label, field, help text and error text together: sets `aria-describedby` and `aria-invalid` on the control and an id on the help text and the error text. Never writes an id on the control: the template writes `for` and `id`, or wraps the field in the label. The field is invalid when the error text is there without `hidden`. Exists for markup
|
|
7
|
+
"description": "Wires label, field, help text and error text together: sets `aria-describedby` and `aria-invalid` on the control and an id on the help text and the error text. Never writes an id on the control: the template writes `for` and `id`, or wraps the field in the label. The field is invalid when the error text is there without `hidden`. Exists for markup from code that cannot call a builder function. When it can, in TypeScript, Kotlin or Java, use `lt.field()` or `Lt.field()` instead.",
|
|
8
8
|
"attributes": {
|
|
9
9
|
"disabled": {
|
|
10
10
|
"type": "flag",
|
package/src/vocabulary/types.ts
CHANGED
|
@@ -190,13 +190,36 @@ export type BuilderReturns = { optional?: boolean } & (
|
|
|
190
190
|
*/
|
|
191
191
|
export type Builder = {
|
|
192
192
|
name: string
|
|
193
|
+
/**
|
|
194
|
+
* The comment on the function, without the code examples. Missing in a
|
|
195
|
+
* manifest from before 0.40, where the schema version was already 1.
|
|
196
|
+
*/
|
|
197
|
+
description?: string
|
|
198
|
+
/** The code examples in the comment, in TypeScript. */
|
|
199
|
+
example?: string
|
|
200
|
+
/** The page the function is documented on. */
|
|
201
|
+
docs?: string
|
|
202
|
+
/**
|
|
203
|
+
* Which part of a frame component each set in the result fills, as
|
|
204
|
+
* `field.control` fills the control of `<lt-field>`. A set that fills no
|
|
205
|
+
* part is left out.
|
|
206
|
+
*/
|
|
207
|
+
parts?: Record<string, { element: string; part: string }>
|
|
193
208
|
options: {
|
|
194
209
|
name: string
|
|
195
210
|
required: boolean
|
|
196
211
|
type: BuilderOptionType
|
|
197
212
|
description?: string
|
|
213
|
+
/** The value that gives the same answer as leaving the option out. */
|
|
214
|
+
default?: string
|
|
198
215
|
}[]
|
|
199
216
|
returns: BuilderReturns
|
|
217
|
+
/**
|
|
218
|
+
* The classes for the parts the template writes itself, as `lt.card.title`:
|
|
219
|
+
* the name on the function and the class. Only on the builder functions
|
|
220
|
+
* that have one.
|
|
221
|
+
*/
|
|
222
|
+
classes?: Record<string, string>
|
|
200
223
|
}
|
|
201
224
|
|
|
202
225
|
/**
|
package/web-types.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
|
|
3
3
|
"name": "@lo-tek/interfaces",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.40.0",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
8
8
|
"elements": [
|
|
9
9
|
{
|
|
10
10
|
"name": "lt-field",
|
|
11
|
-
"description": "Wires label, field, help text and error text together: sets `aria-describedby` and `aria-invalid` on the control and an id on the help text and the error text. Never writes an id on the control: the template writes `for` and `id`, or wraps the field in the label. The field is invalid when the error text is there without `hidden`. Exists for markup
|
|
11
|
+
"description": "Wires label, field, help text and error text together: sets `aria-describedby` and `aria-invalid` on the control and an id on the help text and the error text. Never writes an id on the control: the template writes `for` and `id`, or wraps the field in the label. The field is invalid when the error text is there without `hidden`. Exists for markup from code that cannot call a builder function. When it can, in TypeScript, Kotlin or Java, use `lt.field()` or `Lt.field()` instead.",
|
|
12
12
|
"doc-url": "https://lo-tek.sobernetics.no/components/field/",
|
|
13
13
|
"attributes": [
|
|
14
14
|
{
|