@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.
Files changed (153) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/README.md +1 -1
  3. package/agent/astro.md +9 -9
  4. package/agent/bundles.md +14 -12
  5. package/agent/datastar.md +65 -65
  6. package/agent/html.md +59 -59
  7. package/agent/react.md +16 -15
  8. package/agent/templates.md +65 -58
  9. package/core/lotek-core.wasm +0 -0
  10. package/core/lotek.wasm +0 -0
  11. package/dist/components/css/accordion/accordion.d.ts +1 -1
  12. package/dist/components/css/accordion/accordion.js +1 -1
  13. package/dist/components/css/alert/alert.d.ts +1 -1
  14. package/dist/components/css/alert/alert.js +1 -1
  15. package/dist/components/css/avatar/avatar.d.ts +1 -1
  16. package/dist/components/css/avatar/avatar.js +1 -1
  17. package/dist/components/css/badge/badge.d.ts +1 -1
  18. package/dist/components/css/badge/badge.js +1 -1
  19. package/dist/components/css/breadcrumbs/breadcrumbs.d.ts +1 -1
  20. package/dist/components/css/breadcrumbs/breadcrumbs.js +1 -1
  21. package/dist/components/css/card/card.d.ts +1 -1
  22. package/dist/components/css/card/card.js +1 -1
  23. package/dist/components/css/file-upload/file-upload.d.ts +1 -1
  24. package/dist/components/css/file-upload/file-upload.js +1 -1
  25. package/dist/components/css/icon/icon.d.ts +1 -1
  26. package/dist/components/css/icon/icon.js +1 -1
  27. package/dist/components/css/label/label.d.ts +1 -1
  28. package/dist/components/css/label/label.js +1 -1
  29. package/dist/components/css/link/link.d.ts +1 -1
  30. package/dist/components/css/link/link.js +1 -1
  31. package/dist/components/css/spinner/spinner.d.ts +7 -5
  32. package/dist/components/css/spinner/spinner.d.ts.map +1 -1
  33. package/dist/components/css/spinner/spinner.js +7 -5
  34. package/dist/components/css/spinner/spinner.js.map +1 -1
  35. package/dist/components/css/tag/tag.d.ts +1 -1
  36. package/dist/components/css/tag/tag.js +1 -1
  37. package/dist/components/css/toggle-group/toggle-group.d.ts +1 -1
  38. package/dist/components/css/toggle-group/toggle-group.js +1 -1
  39. package/dist/components/css/tooltip/tooltip.d.ts +2 -2
  40. package/dist/components/css/tooltip/tooltip.js +2 -2
  41. package/dist/components/frame/dialog/dialog.d.ts +11 -6
  42. package/dist/components/frame/dialog/dialog.d.ts.map +1 -1
  43. package/dist/components/frame/dialog/dialog.js +4 -4
  44. package/dist/components/frame/dialog/dialog.js.map +1 -1
  45. package/dist/components/frame/dialog/lt-dialog.d.ts +1 -1
  46. package/dist/components/frame/dialog/lt-dialog.js +7 -7
  47. package/dist/components/frame/error-summary/error-summary.d.ts +6 -2
  48. package/dist/components/frame/error-summary/error-summary.d.ts.map +1 -1
  49. package/dist/components/frame/error-summary/error-summary.js +1 -1
  50. package/dist/components/frame/error-summary/error-summary.js.map +1 -1
  51. package/dist/components/frame/error-summary/lt-error-summary.d.ts +1 -1
  52. package/dist/components/frame/error-summary/lt-error-summary.js +6 -6
  53. package/dist/components/frame/field/field-core.d.ts +4 -2
  54. package/dist/components/frame/field/field-core.d.ts.map +1 -1
  55. package/dist/components/frame/field/field-core.js.map +1 -1
  56. package/dist/components/frame/field/lt-field.d.ts +5 -4
  57. package/dist/components/frame/field/lt-field.d.ts.map +1 -1
  58. package/dist/components/frame/field/lt-field.js +5 -4
  59. package/dist/components/frame/field/lt-field.js.map +1 -1
  60. package/dist/components/frame/popover/lt-popover.d.ts +3 -3
  61. package/dist/components/frame/popover/lt-popover.js +3 -3
  62. package/dist/components/frame/popover/popover.d.ts +7 -3
  63. package/dist/components/frame/popover/popover.d.ts.map +1 -1
  64. package/dist/components/frame/popover/popover.js +2 -2
  65. package/dist/components/frame/popover/popover.js.map +1 -1
  66. package/dist/components/frame/session-timeout/lt-session-timeout.d.ts +8 -8
  67. package/dist/components/frame/session-timeout/lt-session-timeout.js +7 -7
  68. package/dist/components/frame/session-timeout/session-timeout.d.ts +10 -5
  69. package/dist/components/frame/session-timeout/session-timeout.d.ts.map +1 -1
  70. package/dist/components/frame/session-timeout/session-timeout.js +4 -4
  71. package/dist/components/frame/session-timeout/session-timeout.js.map +1 -1
  72. package/dist/components/frame/suggestion/lt-suggestion.d.ts +12 -11
  73. package/dist/components/frame/suggestion/lt-suggestion.d.ts.map +1 -1
  74. package/dist/components/frame/suggestion/lt-suggestion.js +11 -10
  75. package/dist/components/frame/suggestion/lt-suggestion.js.map +1 -1
  76. package/dist/components/frame/suggestion/suggestion.d.ts +3 -1
  77. package/dist/components/frame/suggestion/suggestion.d.ts.map +1 -1
  78. package/dist/components/frame/suggestion/suggestion.js.map +1 -1
  79. package/dist/components/frame/tabs/lt-tabs.d.ts +3 -3
  80. package/dist/components/frame/tabs/lt-tabs.js +5 -5
  81. package/dist/components/frame/tabs/lt-tabs.js.map +1 -1
  82. package/dist/components/frame/tabs/tabs.d.ts +8 -6
  83. package/dist/components/frame/tabs/tabs.d.ts.map +1 -1
  84. package/dist/components/frame/tabs/tabs.js +4 -4
  85. package/dist/components/frame/tabs/tabs.js.map +1 -1
  86. package/dist/components/host-element.d.ts +3 -0
  87. package/dist/components/host-element.d.ts.map +1 -1
  88. package/dist/components/host-element.js +90 -1
  89. package/dist/components/host-element.js.map +1 -1
  90. package/dist/components/standalone/connection-status/lt-connection-status.d.ts +1 -1
  91. package/dist/components/standalone/connection-status/lt-connection-status.js +1 -1
  92. package/dist/components/standalone/toast/lt-toast.d.ts +1 -1
  93. package/dist/components/standalone/toast/lt-toast.js +1 -1
  94. package/dist/diagnostics/index.d.ts +2 -2
  95. package/dist/diagnostics/index.js +2 -2
  96. package/dist/dom.d.ts +2 -1
  97. package/dist/dom.d.ts.map +1 -1
  98. package/dist/dom.js +2 -1
  99. package/dist/dom.js.map +1 -1
  100. package/dist/lotek.css +20 -1
  101. package/dist/lt.d.ts +5 -5
  102. package/dist/lt.js +5 -5
  103. package/dist/react.d.ts.map +1 -1
  104. package/dist/vocabulary/elements.js +1 -1
  105. package/dist/vocabulary/elements.js.map +1 -1
  106. package/dist/vocabulary/types.d.ts +26 -0
  107. package/dist/vocabulary/types.d.ts.map +1 -1
  108. package/live-templates-kotlin.xml +64 -0
  109. package/live-templates.xml +23 -23
  110. package/manifest/manifest.json +407 -80
  111. package/manifest/manifest.schema.json +61 -7
  112. package/package.json +2 -1
  113. package/src/components/css/accordion/accordion.ts +1 -1
  114. package/src/components/css/alert/alert.ts +1 -1
  115. package/src/components/css/avatar/avatar.ts +1 -1
  116. package/src/components/css/badge/badge.ts +1 -1
  117. package/src/components/css/breadcrumbs/breadcrumbs.ts +1 -1
  118. package/src/components/css/card/card.ts +1 -1
  119. package/src/components/css/file-upload/file-upload.ts +1 -1
  120. package/src/components/css/icon/icon.ts +1 -1
  121. package/src/components/css/input/input.css +21 -0
  122. package/src/components/css/label/label.ts +1 -1
  123. package/src/components/css/link/link.ts +1 -1
  124. package/src/components/css/spinner/spinner.ts +8 -5
  125. package/src/components/css/tag/tag.ts +1 -1
  126. package/src/components/css/toggle-group/toggle-group.ts +1 -1
  127. package/src/components/css/tooltip/tooltip.ts +2 -2
  128. package/src/components/frame/dialog/dialog.ts +11 -6
  129. package/src/components/frame/dialog/lt-dialog.ts +7 -7
  130. package/src/components/frame/error-summary/error-summary.ts +6 -2
  131. package/src/components/frame/error-summary/lt-error-summary.ts +6 -6
  132. package/src/components/frame/field/field-core.ts +4 -2
  133. package/src/components/frame/field/lt-field.ts +5 -4
  134. package/src/components/frame/popover/lt-popover.ts +3 -3
  135. package/src/components/frame/popover/popover.ts +7 -3
  136. package/src/components/frame/session-timeout/lt-session-timeout.ts +8 -8
  137. package/src/components/frame/session-timeout/session-timeout.ts +10 -5
  138. package/src/components/frame/suggestion/lt-suggestion.ts +13 -12
  139. package/src/components/frame/suggestion/suggestion.css +1 -1
  140. package/src/components/frame/suggestion/suggestion.ts +3 -1
  141. package/src/components/frame/tabs/lt-tabs.ts +5 -5
  142. package/src/components/frame/tabs/tabs.css +1 -1
  143. package/src/components/frame/tabs/tabs.ts +8 -6
  144. package/src/components/host-element.ts +97 -1
  145. package/src/components/standalone/connection-status/lt-connection-status.ts +1 -1
  146. package/src/components/standalone/toast/lt-toast.ts +1 -1
  147. package/src/diagnostics/index.ts +2 -2
  148. package/src/dom.ts +2 -1
  149. package/src/lt.ts +5 -5
  150. package/src/tokens/boundary.css +4 -0
  151. package/src/vocabulary/elements.ts +1 -1
  152. package/src/vocabulary/types.ts +23 -0
  153. 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 markup is made with
68
- * JavaScript, `lt.suggestion()` writes the roles, the ids and the wiring.
69
- * When it comes from a template without JavaScript, the template writes
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="city">City</label>
103
- * <input class="lt-input" id="city" role="combobox" aria-controls="city-list"
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="city-status">
106
- * <ul class="lt-suggestion__list" id="city-list" role="listbox" hidden>
107
- * <li class="lt-suggestion__option" id="city-option-0" role="option">Bergen</li>
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="city-status" role="status"></span>
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 Bergen with the
140
- * text "Bodø" in the field. If the text is still there after an update, the
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 Bergen saw "4601".
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 JavaScript arrives without `hidden` on the list,
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 case">
196
- * <li><a href="#application">Application</a></li>
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="application"><h2>Application</h2>…</section>
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
- // `#/case/1`, there is nothing to select either. The server's word is read
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 case", or ' +
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 JavaScript comes without `hidden` on the panels,
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 `["application", "attachments"]`.
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 `#case-attachments`, or from `selected` and `select()` on
75
+ * address, such as `#file-attachments`, or from `selected` and `select()` on
74
76
  * `<lt-tabs>`.
75
77
  *
76
- * A React app uses `lt.tabs()` from `@lo-tek/interfaces/react`, which gives
77
- * the whole state, so React hydrates without mismatches.
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: "case", keys: ["application", "attachments"], label: "Parts of the case" })
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/save", { method: "POST", body })
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 {
@@ -53,7 +53,7 @@ export type ShowOptions = {
53
53
  * focus is in the message.
54
54
  *
55
55
  * ```ts
56
- * document.querySelector("lt-toast").show("Application sent", {
56
+ * document.querySelector("lt-toast").show("Relay 14 is back", {
57
57
  * color: "success",
58
58
  * })
59
59
  * ```
@@ -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 produced without JavaScript, in a Go template, a Kotlin
7
- * string or a Razor view, has no compiler looking at the attributes. A test
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` and `aria-label`. What it
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: "email", required: "text", help: true, error: true, invalid })
101
+ * const field = lt.field({ id: "fixer", required: "text", help: true, error: true, invalid })
102
102
  *
103
- * <label {...field.label}>Email address</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}>We send the receipt here.</p>
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 markup created
110
- * with JavaScript. Then no web component is needed.
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`. */
@@ -91,6 +91,10 @@
91
91
  all: revert-layer;
92
92
  }
93
93
 
94
+ [data-lt-boundary][data-lt-boundary] *::-webkit-date-and-time-value {
95
+ all: revert-layer;
96
+ }
97
+
94
98
  [data-lt-boundary][data-lt-boundary] *::-webkit-progress-bar {
95
99
  all: revert-layer;
96
100
  }
@@ -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 produced without JavaScript. When the markup is produced with JavaScript, use `lt.field()` instead.",
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",
@@ -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.38.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 produced without JavaScript. When the markup is produced with JavaScript, use `lt.field()` instead.",
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
  {