@rak200/ui 0.2.14 → 0.2.15
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/README.md +3 -3
- package/dist/field.d.ts.map +1 -1
- package/dist/field.js +22 -1
- package/dist/field.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tooltip.d.ts +69 -0
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/tooltip.js +317 -0
- package/dist/tooltip.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,9 +86,9 @@ components; `docs/` is what describes them.
|
|
|
86
86
|
|
|
87
87
|
## Status
|
|
88
88
|
|
|
89
|
-
**v0.**
|
|
90
|
-
`<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select>`, `<ui-radio-group>`, `<ui-radio
|
|
91
|
-
and `<ui-icon>` — the token layer
|
|
89
|
+
**v0.** Thirteen components — `<ui-button>`, `<ui-card>`, `<ui-field>`, `<ui-dialog>`, `<ui-input>`,
|
|
90
|
+
`<ui-textarea>`, `<ui-checkbox>`, `<ui-switch>`, `<ui-select>`, `<ui-radio-group>`, `<ui-radio>`,
|
|
91
|
+
`<ui-tooltip>` and `<ui-icon>` — the token layer
|
|
92
92
|
under them, and an adopted glyph set, built to the ecosystem's full quality bar rather than
|
|
93
93
|
sketched: type-checked at the strictest available setting, formatted, tested in a real browser and
|
|
94
94
|
**asserted against axe** for WCAG A/AA, 100% coverage and **100% mutation score**, scanned, and
|
package/dist/field.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAMjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,OAAQ,SAAQ,UAAU;;IACnC,gBAAyB,MAAM,0BAuB7B;IAeO,iBAAiB,IAAI,IAAI;IASzB,oBAAoB,IAAI,IAAI;IAK5B,YAAY,IAAI,IAAI;IAIpB,MAAM,IAAI,cAAc;
|
|
1
|
+
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAMjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,OAAQ,SAAQ,UAAU;;IACnC,gBAAyB,MAAM,0BAuB7B;IAeO,iBAAiB,IAAI,IAAI;IASzB,oBAAoB,IAAI,IAAI;IAK5B,YAAY,IAAI,IAAI;IAIpB,MAAM,IAAI,cAAc;CAmLpC;AAQD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,UAAU,EAAE,OAAO,CAAC;KACvB;CACJ"}
|
package/dist/field.js
CHANGED
|
@@ -154,7 +154,14 @@ export class UiField extends LitElement {
|
|
|
154
154
|
// format requirement, which is exactly the error suggestion the user needs in order
|
|
155
155
|
// to recover (WCAG 3.3.3) — dropping it at the moment it becomes useful is the
|
|
156
156
|
// opposite of helping.
|
|
157
|
-
|
|
157
|
+
//
|
|
158
|
+
// And a description this field did not write survives all of it, which is the same
|
|
159
|
+
// courtesy the `id` above gets and for the same reason: something this element
|
|
160
|
+
// cannot see may have put it there. `<ui-tooltip>` is the first to do so, and it
|
|
161
|
+
// was measured being erased — not on the first association, which runs before the
|
|
162
|
+
// tooltip wires, but on the next one, when a re-render moves the help text and the
|
|
163
|
+
// list is rebuilt from what this field alone knows about.
|
|
164
|
+
const described = [error, help, ...this.#foreign(control, uid, [error, help])].filter((id) => id !== undefined);
|
|
158
165
|
if (described.length > 0) {
|
|
159
166
|
control.setAttribute('aria-describedby', described.join(' '));
|
|
160
167
|
}
|
|
@@ -168,6 +175,20 @@ export class UiField extends LitElement {
|
|
|
168
175
|
control.setAttribute('aria-invalid', 'true');
|
|
169
176
|
}
|
|
170
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* The ids already describing `control` that this field is not responsible for.
|
|
180
|
+
*
|
|
181
|
+
* Everything it could have written is excluded rather than remembered: the two ids it
|
|
182
|
+
* generates from `uid`, and whatever the help and error elements are called right now,
|
|
183
|
+
* which may be ids the host supplied. What is left belongs to somebody else and is
|
|
184
|
+
* carried forward in place rather than dropped.
|
|
185
|
+
*/
|
|
186
|
+
#foreign(control, uid, mine) {
|
|
187
|
+
const written = new Set([...mine, `${uid}-help`, `${uid}-error`]);
|
|
188
|
+
return (control.getAttribute('aria-describedby') ?? '')
|
|
189
|
+
.split(' ')
|
|
190
|
+
.filter((id) => id !== '' && !written.has(id));
|
|
191
|
+
}
|
|
171
192
|
/**
|
|
172
193
|
* Gives the control its accessible name from whatever fills the label slot.
|
|
173
194
|
*
|
package/dist/field.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export { UiIcon, register } from './icon.js';
|
|
|
7
7
|
export { UiInput, UiTextarea } from './input.js';
|
|
8
8
|
export { UiRadio, UiRadioGroup, type RadioOrientation } from './radio.js';
|
|
9
9
|
export { UiSelect } from './select.js';
|
|
10
|
+
export { UiTooltip } from './tooltip.js';
|
|
10
11
|
export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, type Token, type DerivedToken, } from './tokens.js';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,EACf,KAAK,KAAK,EACV,KAAK,YAAY,GACpB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,5 +7,6 @@ export { UiIcon, register } from './icon.js';
|
|
|
7
7
|
export { UiInput, UiTextarea } from './input.js';
|
|
8
8
|
export { UiRadio, UiRadioGroup } from './radio.js';
|
|
9
9
|
export { UiSelect } from './select.js';
|
|
10
|
+
export { UiTooltip } from './tooltip.js';
|
|
10
11
|
export { tokens, derivedTokens, defaults, formulas, darkScheme, tokenStyleSheet, } from './tokens.js';
|
|
11
12
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAyB,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAyB,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACH,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,eAAe,GAGlB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { LitElement, type TemplateResult } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* Supplementary text on hover and on keyboard focus.
|
|
4
|
+
*
|
|
5
|
+
* **Everything is slotted**, the way `<ui-field>` slots everything and for the same
|
|
6
|
+
* measured reason: an IDREF does not cross a shadow boundary, so a tip rendered in here
|
|
7
|
+
* could not be the target of the trigger's `aria-describedby`. The trigger is the child
|
|
8
|
+
* with no `slot`; the tip is `slot="tip"`.
|
|
9
|
+
*
|
|
10
|
+
* ## What the platform does, and what is left
|
|
11
|
+
*
|
|
12
|
+
* The tip is a **popover**, which buys the two things a hand-rolled overlay gets wrong:
|
|
13
|
+
* it is promoted to the top layer, so it escapes `overflow: hidden` and every stacking
|
|
14
|
+
* context above it — measured — and it needs no `z-index`, which is why the second overlay
|
|
15
|
+
* in this package brings no layering category either.
|
|
16
|
+
*
|
|
17
|
+
* `popover="manual"` rather than `auto`, and the difference is the point: an `auto`
|
|
18
|
+
* popover light-dismisses, and dismissing one closes the others — so a tooltip appearing
|
|
19
|
+
* over an open menu would close the menu. What `manual` costs is Escape, which it does not
|
|
20
|
+
* handle: measured, a manual popover stays open on Escape. So this element wires it, which
|
|
21
|
+
* WCAG 2.2's **1.4.13 Content on Hover or Focus** requires anyway.
|
|
22
|
+
*
|
|
23
|
+
* ## Why the placement is written here
|
|
24
|
+
*
|
|
25
|
+
* The issue asked whether a clipping tooltip is solved with CSS anchor positioning or with
|
|
26
|
+
* a dependency. Measured in this engine, anchor positioning is **entirely present** —
|
|
27
|
+
* `anchor-name`, `position-anchor`, `position-area` and `position-try-fallbacks` all
|
|
28
|
+
* parse, and an anchored popover really is placed against its anchor rather than merely
|
|
29
|
+
* accepting the rule. It is still not adopted, and the reason is the one `docs/select.md`
|
|
30
|
+
* gives about `appearance: base-select`: a feature one engine has makes a kit look like
|
|
31
|
+
* two kits.
|
|
32
|
+
*
|
|
33
|
+
* **Here that is not a cosmetic difference but a broken component.** Measured: a popover
|
|
34
|
+
* whose anchor rules are ignored does not fall back to somewhere near the trigger — it is
|
|
35
|
+
* `position: fixed` at `inset: 0`, which is the corner of the screen. And the flip at a
|
|
36
|
+
* viewport edge is `position-try-fallbacks`, so an engine without it needs the script
|
|
37
|
+
* anyway. Adopting the CSS would mean writing the placement *and* a second code path for
|
|
38
|
+
* it, which is two answers to one problem.
|
|
39
|
+
*
|
|
40
|
+
* A positioning dependency was the other option the issue named. It solves scroll
|
|
41
|
+
* containers, virtual anchors and continuous auto-update, none of which a tooltip against
|
|
42
|
+
* a real element needs — and it would be this package's second runtime dependency, where
|
|
43
|
+
* RFC 0016's adoption was a *behaviour* library and not a layout one.
|
|
44
|
+
*
|
|
45
|
+
* So the placement is thirty lines here, measured at the edges by a suite that runs in a
|
|
46
|
+
* real browser. When anchor positioning is available broadly it replaces them and the
|
|
47
|
+
* tests stay: they assert where the tip lands, not who put it there.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```html
|
|
51
|
+
* <ui-tooltip>
|
|
52
|
+
* <button type="button">Save</button>
|
|
53
|
+
* <span slot="tip">Saves without closing the dialog.</span>
|
|
54
|
+
* </ui-tooltip>
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare class UiTooltip extends LitElement {
|
|
58
|
+
#private;
|
|
59
|
+
static readonly styles: import("lit").CSSResult;
|
|
60
|
+
connectedCallback(): void;
|
|
61
|
+
disconnectedCallback(): void;
|
|
62
|
+
render(): TemplateResult;
|
|
63
|
+
}
|
|
64
|
+
declare global {
|
|
65
|
+
interface HTMLElementTagNameMap {
|
|
66
|
+
'ui-tooltip': UiTooltip;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=tooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AASjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,qBAAa,SAAU,SAAQ,UAAU;;IACrC,gBAAyB,MAAM,0BAkD7B;IAiDO,iBAAiB,IAAI,IAAI;IAqBzB,oBAAoB,IAAI,IAAI;IAU5B,MAAM,IAAI,cAAc;CAqKpC;AAQD,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,qBAAqB;QAC3B,YAAY,EAAE,SAAS,CAAC;KAC3B;CACJ"}
|
package/dist/tooltip.js
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { LitElement, css, html } from 'lit';
|
|
2
|
+
import { reference } from './reference.js';
|
|
3
|
+
/** Distinguishes one tooltip's generated id from another's. */
|
|
4
|
+
let sequence = 0;
|
|
5
|
+
/** How far the tip sits from the trigger, and which way the script placed it. */
|
|
6
|
+
const side = 'data-side';
|
|
7
|
+
/**
|
|
8
|
+
* Supplementary text on hover and on keyboard focus.
|
|
9
|
+
*
|
|
10
|
+
* **Everything is slotted**, the way `<ui-field>` slots everything and for the same
|
|
11
|
+
* measured reason: an IDREF does not cross a shadow boundary, so a tip rendered in here
|
|
12
|
+
* could not be the target of the trigger's `aria-describedby`. The trigger is the child
|
|
13
|
+
* with no `slot`; the tip is `slot="tip"`.
|
|
14
|
+
*
|
|
15
|
+
* ## What the platform does, and what is left
|
|
16
|
+
*
|
|
17
|
+
* The tip is a **popover**, which buys the two things a hand-rolled overlay gets wrong:
|
|
18
|
+
* it is promoted to the top layer, so it escapes `overflow: hidden` and every stacking
|
|
19
|
+
* context above it — measured — and it needs no `z-index`, which is why the second overlay
|
|
20
|
+
* in this package brings no layering category either.
|
|
21
|
+
*
|
|
22
|
+
* `popover="manual"` rather than `auto`, and the difference is the point: an `auto`
|
|
23
|
+
* popover light-dismisses, and dismissing one closes the others — so a tooltip appearing
|
|
24
|
+
* over an open menu would close the menu. What `manual` costs is Escape, which it does not
|
|
25
|
+
* handle: measured, a manual popover stays open on Escape. So this element wires it, which
|
|
26
|
+
* WCAG 2.2's **1.4.13 Content on Hover or Focus** requires anyway.
|
|
27
|
+
*
|
|
28
|
+
* ## Why the placement is written here
|
|
29
|
+
*
|
|
30
|
+
* The issue asked whether a clipping tooltip is solved with CSS anchor positioning or with
|
|
31
|
+
* a dependency. Measured in this engine, anchor positioning is **entirely present** —
|
|
32
|
+
* `anchor-name`, `position-anchor`, `position-area` and `position-try-fallbacks` all
|
|
33
|
+
* parse, and an anchored popover really is placed against its anchor rather than merely
|
|
34
|
+
* accepting the rule. It is still not adopted, and the reason is the one `docs/select.md`
|
|
35
|
+
* gives about `appearance: base-select`: a feature one engine has makes a kit look like
|
|
36
|
+
* two kits.
|
|
37
|
+
*
|
|
38
|
+
* **Here that is not a cosmetic difference but a broken component.** Measured: a popover
|
|
39
|
+
* whose anchor rules are ignored does not fall back to somewhere near the trigger — it is
|
|
40
|
+
* `position: fixed` at `inset: 0`, which is the corner of the screen. And the flip at a
|
|
41
|
+
* viewport edge is `position-try-fallbacks`, so an engine without it needs the script
|
|
42
|
+
* anyway. Adopting the CSS would mean writing the placement *and* a second code path for
|
|
43
|
+
* it, which is two answers to one problem.
|
|
44
|
+
*
|
|
45
|
+
* A positioning dependency was the other option the issue named. It solves scroll
|
|
46
|
+
* containers, virtual anchors and continuous auto-update, none of which a tooltip against
|
|
47
|
+
* a real element needs — and it would be this package's second runtime dependency, where
|
|
48
|
+
* RFC 0016's adoption was a *behaviour* library and not a layout one.
|
|
49
|
+
*
|
|
50
|
+
* So the placement is thirty lines here, measured at the edges by a suite that runs in a
|
|
51
|
+
* real browser. When anchor positioning is available broadly it replaces them and the
|
|
52
|
+
* tests stay: they assert where the tip lands, not who put it there.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```html
|
|
56
|
+
* <ui-tooltip>
|
|
57
|
+
* <button type="button">Save</button>
|
|
58
|
+
* <span slot="tip">Saves without closing the dialog.</span>
|
|
59
|
+
* </ui-tooltip>
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export class UiTooltip extends LitElement {
|
|
63
|
+
static { this.styles = css `
|
|
64
|
+
/* No box of its own: a wrapper that laid something out would move the trigger it
|
|
65
|
+
wraps, and every call site would have to undo it. */
|
|
66
|
+
:host {
|
|
67
|
+
display: contents;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* The tip is the host's own element, reached through ::slotted — the shape every
|
|
71
|
+
component in this kit has. position: fixed is what the popover already is;
|
|
72
|
+
it is restated because the script writes inset-block-start and
|
|
73
|
+
inset-inline-start against the viewport, and a reader of those two lines
|
|
74
|
+
should not have to know which stylesheet made them mean that.
|
|
75
|
+
|
|
76
|
+
max-content up to a ceiling: a tip is a phrase, and a phrase that wraps at the
|
|
77
|
+
width of whatever it happens to sit near is unreadable. The ceiling is in the
|
|
78
|
+
space token so it scales with everything else a host retunes. */
|
|
79
|
+
slot[name='tip']::slotted(*) {
|
|
80
|
+
position: fixed;
|
|
81
|
+
margin: 0;
|
|
82
|
+
inline-size: max-content;
|
|
83
|
+
max-inline-size: calc(${reference('--ui-space')} * 40);
|
|
84
|
+
padding: calc(${reference('--ui-space')} / 2) ${reference('--ui-space')};
|
|
85
|
+
font-family: ${reference('--ui-font')};
|
|
86
|
+
font-size: 0.875em;
|
|
87
|
+
/* The pair, never half of it: a surface without the colour chosen against it
|
|
88
|
+
inherits whatever the page set, and the contrast measured for that pair
|
|
89
|
+
stops holding. */
|
|
90
|
+
color: ${reference('--ui-color-text')};
|
|
91
|
+
background: ${reference('--ui-color-surface')};
|
|
92
|
+
border: 1px solid ${reference('--ui-color-border')};
|
|
93
|
+
border-radius: ${reference('--ui-radius')};
|
|
94
|
+
box-shadow: ${reference('--ui-elevation-raised')};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* The gap between the tip and its trigger, in CSS because it is a token and the
|
|
98
|
+
script has no way to resolve one: a custom property reads back as it was
|
|
99
|
+
written, so 0.5rem would arrive at a function that needs pixels.
|
|
100
|
+
|
|
101
|
+
It is deliberately small. WCAG 2.2's 1.4.13 asks that a pointer be able to
|
|
102
|
+
travel onto the tip without it vanishing, and it travels across this gap — the
|
|
103
|
+
tip stays open because it is a DOM child of this element, so entering it never
|
|
104
|
+
leaves the host, but only if the pointer does not leave through the gap on the
|
|
105
|
+
way. */
|
|
106
|
+
slot[name='tip']::slotted([data-side='block-start']) {
|
|
107
|
+
translate: 0 calc(${reference('--ui-space')} / -2);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
slot[name='tip']::slotted([data-side='block-end']) {
|
|
111
|
+
translate: 0 calc(${reference('--ui-space')} / 2);
|
|
112
|
+
}
|
|
113
|
+
`; }
|
|
114
|
+
/** Whether the tip is currently shown, which decides what the listeners cost. */
|
|
115
|
+
#shown = false;
|
|
116
|
+
/**
|
|
117
|
+
* What is watching while the tip is open, dropped in one call when it closes.
|
|
118
|
+
*
|
|
119
|
+
* These three are added on show rather than on connection because two of them are
|
|
120
|
+
* per-frame: a page holding fifty tooltips would run fifty no-op scroll handlers for
|
|
121
|
+
* every frame of every scroll, and at most one tooltip is ever open.
|
|
122
|
+
*
|
|
123
|
+
* Built here rather than on the first show so it is never absent — the reason
|
|
124
|
+
* `src/field.ts` builds its observer the same way: a close that has to ask whether the
|
|
125
|
+
* controller exists is a branch no test can reach, because nothing closes what was
|
|
126
|
+
* never opened.
|
|
127
|
+
*/
|
|
128
|
+
#watching = new AbortController();
|
|
129
|
+
/** Re-places the tip under anything that moves the trigger while it is open. */
|
|
130
|
+
#reflow = () => {
|
|
131
|
+
this.#place();
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Dismisses on Escape, which is 1.4.13's first requirement and `manual`'s one gap.
|
|
135
|
+
*
|
|
136
|
+
* On the document rather than on this element: a tip shown by the pointer leaves the
|
|
137
|
+
* focus wherever it was, so a key listener bound here would never hear the key that is
|
|
138
|
+
* supposed to dismiss it.
|
|
139
|
+
*/
|
|
140
|
+
#dismiss = (event) => {
|
|
141
|
+
if (event.key === 'Escape') {
|
|
142
|
+
this.#hide();
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Takes every listener off at once, and the reason is not brevity.
|
|
147
|
+
*
|
|
148
|
+
* Removing them one by one needs the event name repeated at both ends, and half of
|
|
149
|
+
* those repetitions cannot be checked: a leaked `pointerenter` is caught the moment it
|
|
150
|
+
* fires, because it opens a popover that is no longer in a document and that throws —
|
|
151
|
+
* but a leaked `pointerleave` reaches `#hide`, which returns at its own guard, and
|
|
152
|
+
* nothing anywhere can tell it happened. One `abort()` is one thing to get right, and
|
|
153
|
+
* a leak of it is observable through the half that is.
|
|
154
|
+
*/
|
|
155
|
+
#listeners = new AbortController();
|
|
156
|
+
connectedCallback() {
|
|
157
|
+
super.connectedCallback();
|
|
158
|
+
// A fresh one per connection: an aborted signal stays aborted, so a tooltip a
|
|
159
|
+
// framework moves in the tree would come back deaf.
|
|
160
|
+
this.#listeners = new AbortController();
|
|
161
|
+
// One options object for the four, and that is the same argument as the controller
|
|
162
|
+
// itself: written per call, each `{ signal }` is its own thing to get right, and
|
|
163
|
+
// half of them cannot be checked — a leaked `pointerleave` reaches `#hide`, which
|
|
164
|
+
// returns at its own guard, and nothing can tell. Shared, all four ride on the one
|
|
165
|
+
// that does report: a leaked `pointerenter` opens a popover that is no longer in a
|
|
166
|
+
// document, which throws.
|
|
167
|
+
const watched = { signal: this.#listeners.signal };
|
|
168
|
+
this.addEventListener('pointerenter', this.#onPointer, watched);
|
|
169
|
+
this.addEventListener('pointerleave', this.#onLeave, watched);
|
|
170
|
+
this.addEventListener('focusin', this.#onFocus, watched);
|
|
171
|
+
this.addEventListener('focusout', this.#onLeave, watched);
|
|
172
|
+
}
|
|
173
|
+
disconnectedCallback() {
|
|
174
|
+
// Removed while shown, the tip takes the window listeners with it. A tooltip that
|
|
175
|
+
// a framework unmounted mid-hover would otherwise keep re-placing an element that
|
|
176
|
+
// is no longer in a document.
|
|
177
|
+
this.#hide();
|
|
178
|
+
this.#listeners.abort();
|
|
179
|
+
super.disconnectedCallback();
|
|
180
|
+
}
|
|
181
|
+
render() {
|
|
182
|
+
return html `
|
|
183
|
+
<slot></slot>
|
|
184
|
+
<slot name="tip" @slotchange=${this.#associate}></slot>
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
/** The element the tip is about: the one child carrying no `slot` attribute. */
|
|
188
|
+
#trigger() {
|
|
189
|
+
const found = this.querySelector(':scope > :not([slot])');
|
|
190
|
+
return found instanceof HTMLElement ? found : undefined;
|
|
191
|
+
}
|
|
192
|
+
/** The tip itself, which is the host's own element and stays in their tree. */
|
|
193
|
+
#tip() {
|
|
194
|
+
const found = this.querySelector(":scope > [slot='tip']");
|
|
195
|
+
return found instanceof HTMLElement ? found : undefined;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Makes the tip a popover, names it, and points the trigger at it.
|
|
199
|
+
*
|
|
200
|
+
* On `slotchange` as well as at first render, because a framework re-render replaces
|
|
201
|
+
* the element rather than mutating it — and a tip that is only wired the first time is
|
|
202
|
+
* one that stops being announced at a moment nothing reports.
|
|
203
|
+
*
|
|
204
|
+
* A `role` or an `id` the host wrote is never overwritten, the same courtesy
|
|
205
|
+
* `<ui-field>` and `<ui-switch>` extend. `aria-describedby` is **added to** rather than
|
|
206
|
+
* replaced: the trigger may already be described by a field's help text, and a
|
|
207
|
+
* description that silently replaced another is the failure nobody sees.
|
|
208
|
+
*/
|
|
209
|
+
#associate = () => {
|
|
210
|
+
const trigger = this.#trigger();
|
|
211
|
+
const tip = this.#tip();
|
|
212
|
+
if (trigger === undefined || tip === undefined) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (tip.id === '') {
|
|
216
|
+
// Stryker disable next-line UpdateOperator: two tooltips on one page must get
|
|
217
|
+
// different ids, and a decrementing counter delivers that as well as an
|
|
218
|
+
// incrementing one. No input distinguishes them — an equivalent mutant, the
|
|
219
|
+
// same one `src/field.ts` carries beside its own counter.
|
|
220
|
+
tip.id = `ui-tooltip-${String(++sequence)}`;
|
|
221
|
+
}
|
|
222
|
+
if (!tip.hasAttribute('role')) {
|
|
223
|
+
tip.setAttribute('role', 'tooltip');
|
|
224
|
+
}
|
|
225
|
+
tip.setAttribute('popover', 'manual');
|
|
226
|
+
const described = (trigger.getAttribute('aria-describedby') ?? '')
|
|
227
|
+
.split(' ')
|
|
228
|
+
.filter((id) => id !== '');
|
|
229
|
+
if (!described.includes(tip.id)) {
|
|
230
|
+
trigger.setAttribute('aria-describedby', [...described, tip.id].join(' '));
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Shows on the pointer, except the pointer a touch screen reports.
|
|
235
|
+
*
|
|
236
|
+
* There is no hover on a touch screen, so a tap fires `pointerenter` and then focus —
|
|
237
|
+
* and showing on both means the tip arrives, is dismissed by the tap that follows, and
|
|
238
|
+
* arrives again. Focus is the one that survives, so this defers to it.
|
|
239
|
+
*/
|
|
240
|
+
#onPointer = (event) => {
|
|
241
|
+
if (event.pointerType !== 'touch') {
|
|
242
|
+
this.#show();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
#onFocus = () => {
|
|
246
|
+
this.#show();
|
|
247
|
+
};
|
|
248
|
+
#onLeave = () => {
|
|
249
|
+
this.#hide();
|
|
250
|
+
};
|
|
251
|
+
/** Opens the tip and starts watching whatever can move its trigger. */
|
|
252
|
+
#show() {
|
|
253
|
+
const tip = this.#tip();
|
|
254
|
+
if (tip === undefined || this.#shown) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
this.#shown = true;
|
|
258
|
+
tip.showPopover();
|
|
259
|
+
this.#place();
|
|
260
|
+
this.#watching = new AbortController();
|
|
261
|
+
// Shared for the same reason the four above are, and it pays off in the same
|
|
262
|
+
// direction: a leaked `keydown` is invisible — every later Escape returns at
|
|
263
|
+
// `#hide`'s guard — while a leaked `resize` re-places a tip that is not on screen,
|
|
264
|
+
// which a test reads straight off the written inset.
|
|
265
|
+
const watched = { signal: this.#watching.signal };
|
|
266
|
+
// Capturing, because the thing that scrolls is rarely the window: a scroll inside
|
|
267
|
+
// any ancestor moves the trigger and no event reaches the window from it.
|
|
268
|
+
//
|
|
269
|
+
// Not `passive`, and its absence is deliberate rather than an omission: `scroll` is
|
|
270
|
+
// not cancelable, so the flag changes nothing a test could observe — and an option
|
|
271
|
+
// no input can distinguish is one the mutation floor has to be told to ignore. The
|
|
272
|
+
// cheaper answer is not to write it.
|
|
273
|
+
window.addEventListener('scroll', this.#reflow, { capture: true, ...watched });
|
|
274
|
+
window.addEventListener('resize', this.#reflow, watched);
|
|
275
|
+
document.addEventListener('keydown', this.#dismiss, watched);
|
|
276
|
+
}
|
|
277
|
+
/** Closes the tip and stops watching. */
|
|
278
|
+
#hide() {
|
|
279
|
+
if (!this.#shown) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
this.#shown = false;
|
|
283
|
+
this.#tip()?.hidePopover();
|
|
284
|
+
this.#watching.abort();
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Puts the tip above its trigger, below it when there is no room, and inside the
|
|
288
|
+
* viewport either way.
|
|
289
|
+
*
|
|
290
|
+
* Read rather than assumed: the tip's own box is measured after it is shown, because
|
|
291
|
+
* its height depends on how its text wrapped at this width in this font — a number
|
|
292
|
+
* this file cannot know and must not guess.
|
|
293
|
+
*/
|
|
294
|
+
#place() {
|
|
295
|
+
const trigger = this.#trigger();
|
|
296
|
+
const tip = this.#tip();
|
|
297
|
+
if (trigger === undefined || tip === undefined) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const anchor = trigger.getBoundingClientRect();
|
|
301
|
+
const box = tip.getBoundingClientRect();
|
|
302
|
+
const above = anchor.top >= box.height;
|
|
303
|
+
tip.setAttribute(side, above ? 'block-start' : 'block-end');
|
|
304
|
+
tip.style.insetBlockStart = `${String(above ? anchor.top - box.height : anchor.bottom)}px`;
|
|
305
|
+
// Centred on the trigger, then pulled back inside the viewport — in that order,
|
|
306
|
+
// because a tip centred on a trigger near the edge is a tip half off the screen.
|
|
307
|
+
// `Math.max` last so a tip wider than the viewport starts at the leading edge
|
|
308
|
+
// rather than at a negative one.
|
|
309
|
+
tip.style.insetInlineStart = `${String(Math.max(0, Math.min(anchor.left + anchor.width / 2 - box.width / 2, document.documentElement.clientWidth - box.width)))}px`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Stryker disable next-line StringLiteral: the registration runs once, at import, inside
|
|
313
|
+
// the warm process Stryker switches mutants in — so by the time a mutant on this line is
|
|
314
|
+
// active the element is already defined under the original name, and no test can observe
|
|
315
|
+
// the change. Outside the runner's reach, not an equivalent mutant.
|
|
316
|
+
customElements.define('ui-tooltip', UiTooltip);
|
|
317
|
+
//# sourceMappingURL=tooltip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.js","sourceRoot":"","sources":["../src/tooltip.ts"],"names":[],"mappings":"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"}
|