@fusion.dev/helix-core 0.0.0-dev → 0.0.3
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/custom-elements.json +156 -124
- package/dist/controllers/aria-forward-controller/aria-forward-controller.d.ts +2 -0
- package/dist/controllers/aria-forward-controller/aria-forward-controller.js +44 -17
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +5 -6
- package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +5 -6
- package/dist/controllers/command-controller/command-controller.d.ts +5 -7
- package/dist/controllers/command-controller/command-controller.js +5 -7
- package/dist/controllers/copy-controller/copy-controller.d.ts +2 -4
- package/dist/controllers/copy-controller/copy-controller.js +7 -9
- package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.d.ts +10 -17
- package/dist/controllers/disabled-cascade-controller/disabled-cascade-controller.js +11 -18
- package/dist/controllers/dismiss-controller/dismiss-controller.d.ts +44 -6
- package/dist/controllers/dismiss-controller/dismiss-controller.js +77 -10
- package/dist/controllers/drilldown-controller/drilldown-controller.d.ts +3 -6
- package/dist/controllers/drilldown-controller/drilldown-controller.js +3 -6
- package/dist/controllers/floating-controller/floating-controller.d.ts +7 -8
- package/dist/controllers/floating-controller/floating-controller.js +69 -38
- package/dist/controllers/focus-trap-controller/focus-trap-controller.js +7 -10
- package/dist/controllers/indicator-controller/indicator-controller.d.ts +17 -29
- package/dist/controllers/indicator-controller/indicator-controller.js +11 -21
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +3 -13
- package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +11 -24
- package/dist/controllers/popover-controller/popover-controller.d.ts +2 -7
- package/dist/controllers/popover-controller/popover-controller.js +3 -9
- package/dist/controllers/source-controller/source-controller.d.ts +2 -5
- package/dist/controllers/source-controller/source-controller.js +2 -5
- package/dist/controllers/token-color-controller/token-color-controller.d.ts +2 -4
- package/dist/controllers/token-color-controller/token-color-controller.js +2 -4
- package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +1 -1
- package/dist/controllers/virtualize-controller/virtualize-controller.js +15 -15
- package/dist/events/element-events.js +0 -5
- package/dist/events/events.d.ts +55 -93
- package/dist/events/events.js +68 -105
- package/dist/helix-element/helix-element.d.ts +26 -27
- package/dist/helix-element/helix-element.js +83 -100
- package/dist/helix-form-element/helix-form-element.d.ts +11 -17
- package/dist/helix-form-element/helix-form-element.js +15 -21
- package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +6 -14
- package/dist/mixins/mixin-chart-source/mixin-chart-source.js +12 -25
- package/dist/mixins/mixin-collection/mixin-collection.d.ts +1 -7
- package/dist/mixins/mixin-collection/mixin-collection.js +16 -23
- package/dist/mixins/mixin-controller-active/mixin-controller-active.d.ts +15 -16
- package/dist/mixins/mixin-controller-active/mixin-controller-active.js +28 -42
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +15 -17
- package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +33 -45
- package/dist/mixins/mixin-controller-open/mixin-controller-open.d.ts +4 -4
- package/dist/mixins/mixin-controller-open/mixin-controller-open.js +29 -45
- package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +10 -18
- package/dist/mixins/mixin-controller-value/mixin-controller-value.js +14 -31
- package/dist/mixins/mixin-dismissible/mixin-dismissible.d.ts +10 -15
- package/dist/mixins/mixin-dismissible/mixin-dismissible.js +10 -15
- package/dist/mixins/mixin-field/mixin-field.d.ts +10 -12
- package/dist/mixins/mixin-field/mixin-field.js +48 -64
- package/dist/utils/capture-pointer/capture-pointer.d.ts +1 -4
- package/dist/utils/capture-pointer/capture-pointer.js +1 -4
- package/dist/utils/closest-instance/closest-instance.d.ts +6 -10
- package/dist/utils/closest-instance/closest-instance.js +6 -10
- package/dist/utils/create-sheet/create-sheet.d.ts +1 -1
- package/dist/utils/create-sheet/create-sheet.js +1 -1
- package/dist/utils/custom-element/custom-element.d.ts +0 -21
- package/dist/utils/custom-element/custom-element.js +1 -1
- package/dist/utils/draw-icon/draw-icon.d.ts +3 -3
- package/dist/utils/draw-icon/draw-icon.js +3 -3
- package/dist/utils/elapsed-time/elapsed-time.js +1 -3
- package/dist/utils/focusable-elements/focusable-elements.d.ts +3 -4
- package/dist/utils/focusable-elements/focusable-elements.js +9 -11
- package/dist/utils/format-numbers/format-numbers.js +1 -4
- package/dist/utils/host-aria/host-aria.d.ts +2 -2
- package/dist/utils/host-aria/host-aria.js +2 -2
- package/dist/utils/line-path/line-path.js +1 -1
- package/dist/utils/on-unless-false/on-unless-false.d.ts +0 -5
- package/dist/utils/on-unless-false/on-unless-false.js +0 -5
- package/dist/utils/read-series/read-series.d.ts +26 -0
- package/dist/utils/read-series/read-series.js +26 -29
- package/dist/utils/relative-time/relative-time.js +5 -6
- package/dist/utils/request-change/request-change.d.ts +9 -20
- package/dist/utils/resolve-active-item/resolve-active-item.d.ts +3 -3
- package/dist/utils/resolve-active-item/resolve-active-item.js +3 -3
- package/dist/utils/restore-value/restore-value.js +1 -6
- package/dist/utils/scroll-item-into-view/scroll-item-into-view.js +4 -5
- package/dist/utils/slot-filled/slot-filled.d.ts +2 -4
- package/dist/utils/slot-filled/slot-filled.js +2 -4
- package/dist/utils/tell-disclosure/tell-disclosure.d.ts +15 -18
- package/dist/utils/tell-disclosure/tell-disclosure.js +15 -18
- package/dist/utils/tone-marks/tone-marks.d.ts +10 -16
- package/dist/utils/tone-marks/tone-marks.js +6 -10
- package/dist/utils/value-as/value-as.d.ts +0 -28
- package/dist/utils/value-as/value-as.js +5 -10
- package/package.json +6 -6
|
@@ -17,9 +17,9 @@ export declare class MixinControllerValueInterface {
|
|
|
17
17
|
/**
|
|
18
18
|
* What the control holds now, and what its form submits.
|
|
19
19
|
*
|
|
20
|
-
* Reading gives the real value,
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* Reading gives the real value. Writing always applies, and is a seed and a
|
|
21
|
+
* correction rather than a claim of ownership: a consumer who wants the
|
|
22
|
+
* decision refuses the `helix-before-change`.
|
|
23
23
|
*/
|
|
24
24
|
value: string;
|
|
25
25
|
/**
|
|
@@ -32,8 +32,7 @@ export declare class MixinControllerValueInterface {
|
|
|
32
32
|
*
|
|
33
33
|
* It selects the event rather than the timing. `requestValue` is called
|
|
34
34
|
* whenever the value moves either way; this decides whether the control sends
|
|
35
|
-
* `input` alone or `input` and `change` together
|
|
36
|
-
* name and know how often to expect it.
|
|
35
|
+
* `input` alone or `input` and `change` together.
|
|
37
36
|
*/
|
|
38
37
|
protected readonly announcesInput: boolean;
|
|
39
38
|
/** Requests a new value, and reports whether the request stood. */
|
|
@@ -46,23 +45,16 @@ export declare class MixinControllerValueInterface {
|
|
|
46
45
|
/**
|
|
47
46
|
* Grafts `value`, `defaultValue`, and the two callbacks that answer a form.
|
|
48
47
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* **Seeded in `willUpdate`, not `connectedCallback`.** SSR never calls the
|
|
56
|
-
* latter, so a control with a `default-value` would serve empty and fill itself
|
|
57
|
-
* at hydration, which is a flash for a reader and a mismatch for React. Seeding
|
|
58
|
-
* once, before the first render, is what makes the server and the client agree.
|
|
48
|
+
* **Derived on read, never seeded in a lifecycle hook.** `value` falls back to
|
|
49
|
+
* `defaultValue`, so the server and the client agree without either running a
|
|
50
|
+
* hook. Seeding in `connectedCallback` is the trap: SSR never calls it, so a
|
|
51
|
+
* control with a `default-value` would serve empty and fill itself at
|
|
52
|
+
* hydration, a flash for a reader and a mismatch for React.
|
|
59
53
|
*
|
|
60
54
|
* **What it does not own is reporting.** `setFormValue` stays with the
|
|
61
55
|
* component, because what an empty value means is the component's business: a
|
|
62
56
|
* text field submits its emptiness, since `""` is an answer and the field was
|
|
63
|
-
* on the form, while a group with nothing chosen submits nothing at all.
|
|
64
|
-
* mixin cannot hold both without a flag, and a flag would be this rule written
|
|
65
|
-
* down twice.
|
|
57
|
+
* on the form, while a group with nothing chosen submits nothing at all.
|
|
66
58
|
*
|
|
67
59
|
* `Checkbox` deliberately does not take this. Its `value` is what it submits
|
|
68
60
|
* *when it is on* rather than what it holds, so it is configuration rather than
|
|
@@ -10,23 +10,16 @@ import { requestChange } from '../../utils/request-change/request-change.js';
|
|
|
10
10
|
/**
|
|
11
11
|
* Grafts `value`, `defaultValue`, and the two callbacks that answer a form.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* **Seeded in `willUpdate`, not `connectedCallback`.** SSR never calls the
|
|
20
|
-
* latter, so a control with a `default-value` would serve empty and fill itself
|
|
21
|
-
* at hydration, which is a flash for a reader and a mismatch for React. Seeding
|
|
22
|
-
* once, before the first render, is what makes the server and the client agree.
|
|
13
|
+
* **Derived on read, never seeded in a lifecycle hook.** `value` falls back to
|
|
14
|
+
* `defaultValue`, so the server and the client agree without either running a
|
|
15
|
+
* hook. Seeding in `connectedCallback` is the trap: SSR never calls it, so a
|
|
16
|
+
* control with a `default-value` would serve empty and fill itself at
|
|
17
|
+
* hydration, a flash for a reader and a mismatch for React.
|
|
23
18
|
*
|
|
24
19
|
* **What it does not own is reporting.** `setFormValue` stays with the
|
|
25
20
|
* component, because what an empty value means is the component's business: a
|
|
26
21
|
* text field submits its emptiness, since `""` is an answer and the field was
|
|
27
|
-
* on the form, while a group with nothing chosen submits nothing at all.
|
|
28
|
-
* mixin cannot hold both without a flag, and a flag would be this rule written
|
|
29
|
-
* down twice.
|
|
22
|
+
* on the form, while a group with nothing chosen submits nothing at all.
|
|
30
23
|
*
|
|
31
24
|
* `Checkbox` deliberately does not take this. Its `value` is what it submits
|
|
32
25
|
* *when it is on* rather than what it holds, so it is configuration rather than
|
|
@@ -54,12 +47,10 @@ export const MixinControllerValue = (Base) => {
|
|
|
54
47
|
/**
|
|
55
48
|
* Whether a reader edits this continuously, which decides the native name.
|
|
56
49
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* while a reader is editing against `change` when they are done, which is
|
|
62
|
-
* the distinction the platform already draws. See 294.
|
|
50
|
+
* The request event is `helix-before-change` whatever the answer; this
|
|
51
|
+
* decides which native event announces the result, `input` while a reader
|
|
52
|
+
* is editing against `change` when they are done, which is the distinction
|
|
53
|
+
* the platform already draws.
|
|
63
54
|
*
|
|
64
55
|
* Discrete by default: only a control the reader edits continuously says
|
|
65
56
|
* otherwise.
|
|
@@ -69,17 +60,10 @@ export const MixinControllerValue = (Base) => {
|
|
|
69
60
|
/**
|
|
70
61
|
* What the control holds now, and what its form submits.
|
|
71
62
|
*
|
|
72
|
-
* One property, not two.
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* means in React: the value is theirs and the control waits to be told.
|
|
77
|
-
* `default-value` is how markup seeds one it does not want to own.
|
|
78
|
-
*
|
|
79
|
-
* The same shape as the other three mixins, which is the point of them
|
|
80
|
-
* being four of a kind rather than four of three kinds.
|
|
81
|
-
*
|
|
82
|
-
* helix-input
|
|
63
|
+
* One property, not two, so `input.value` answers the question anyone
|
|
64
|
+
* asking it meant. Writing always applies; `<helix-input value="x">` is the
|
|
65
|
+
* same statement in markup, and `default-value` is how markup seeds a value
|
|
66
|
+
* a reset should return to.
|
|
83
67
|
*/
|
|
84
68
|
get value() {
|
|
85
69
|
return this.valueState ?? this.defaultValue;
|
|
@@ -117,7 +101,6 @@ export const MixinControllerValue = (Base) => {
|
|
|
117
101
|
* itself, whether or not a consumer declared `value`: a declared property
|
|
118
102
|
* is a seed and a correction, the way `<input value>` is, and a consumer
|
|
119
103
|
* who wants the change for themselves refuses it and assigns their own.
|
|
120
|
-
* See 294.
|
|
121
104
|
*/
|
|
122
105
|
requestValue(next) {
|
|
123
106
|
if (this.value === next) {
|
|
@@ -17,25 +17,20 @@ export declare class DismissibleElement {
|
|
|
17
17
|
/**
|
|
18
18
|
* Grafts `dismissible`, its label, and the event that says so.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* light-DOM descendant including the ones assigned elsewhere. On `Announcement`
|
|
28
|
-
* and `Tag` that is the same thing, because they have one slot. On `Toast.Item`
|
|
29
|
-
* it swept in the actions: a message reading "Could not reach the server" with a
|
|
30
|
-
* `Retry` button announced itself as "Could not reach the server Retry", and the
|
|
31
|
-
* consumer removing it from their list got that string to match on.
|
|
20
|
+
* **The label reads the default slot, never `this.textContent`**, which is
|
|
21
|
+
* every light-DOM descendant including the ones assigned elsewhere. On
|
|
22
|
+
* `Announcement` and `Tag` the two are the same thing, because they have one
|
|
23
|
+
* slot. On `Toast.Item` the second sweeps in the actions: a message reading
|
|
24
|
+
* "Could not reach the server" with a `Retry` button announces itself as "Could
|
|
25
|
+
* not reach the server Retry", and the consumer removing it from their list
|
|
26
|
+
* gets that string to match on.
|
|
32
27
|
*
|
|
33
28
|
* **`dismissEvent()` is separate from `dismiss()` for the one that needs it.**
|
|
34
29
|
* `Announcement` and `Tag` dispatch and are done: they own no state, so a
|
|
35
30
|
* refusal is the whole of the consumer's answer. `Toast.Item` holds an `open`
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
31
|
+
* and applies the change itself, so its event goes through `requestChange` with
|
|
32
|
+
* an `apply` beside it. Giving it the event rather than the method lets it keep
|
|
33
|
+
* the contract and skip the default.
|
|
39
34
|
*/
|
|
40
35
|
export declare const MixinDismissible: <T extends Constructor<LitElement>>(Base: T) => Constructor<DismissibleElement> & T;
|
|
41
36
|
export {};
|
|
@@ -9,25 +9,20 @@ import { HelixDismissEvent } from '../../events/events.js';
|
|
|
9
9
|
/**
|
|
10
10
|
* Grafts `dismissible`, its label, and the event that says so.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* light-DOM descendant including the ones assigned elsewhere. On `Announcement`
|
|
20
|
-
* and `Tag` that is the same thing, because they have one slot. On `Toast.Item`
|
|
21
|
-
* it swept in the actions: a message reading "Could not reach the server" with a
|
|
22
|
-
* `Retry` button announced itself as "Could not reach the server Retry", and the
|
|
23
|
-
* consumer removing it from their list got that string to match on.
|
|
12
|
+
* **The label reads the default slot, never `this.textContent`**, which is
|
|
13
|
+
* every light-DOM descendant including the ones assigned elsewhere. On
|
|
14
|
+
* `Announcement` and `Tag` the two are the same thing, because they have one
|
|
15
|
+
* slot. On `Toast.Item` the second sweeps in the actions: a message reading
|
|
16
|
+
* "Could not reach the server" with a `Retry` button announces itself as "Could
|
|
17
|
+
* not reach the server Retry", and the consumer removing it from their list
|
|
18
|
+
* gets that string to match on.
|
|
24
19
|
*
|
|
25
20
|
* **`dismissEvent()` is separate from `dismiss()` for the one that needs it.**
|
|
26
21
|
* `Announcement` and `Tag` dispatch and are done: they own no state, so a
|
|
27
22
|
* refusal is the whole of the consumer's answer. `Toast.Item` holds an `open`
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
23
|
+
* and applies the change itself, so its event goes through `requestChange` with
|
|
24
|
+
* an `apply` beside it. Giving it the event rather than the method lets it keep
|
|
25
|
+
* the contract and skip the default.
|
|
31
26
|
*/
|
|
32
27
|
export const MixinDismissible = (Base) => {
|
|
33
28
|
class Dismissible extends Base {
|
|
@@ -16,7 +16,7 @@ export declare class MixinFieldInterface {
|
|
|
16
16
|
*/
|
|
17
17
|
hint: string;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Id of the description node, for `aria-describedby` on the control.
|
|
20
20
|
*
|
|
21
21
|
* `undefined` when there is nothing to point at, so the attribute is absent
|
|
22
22
|
* rather than pointing at an id that renders nothing.
|
|
@@ -68,7 +68,7 @@ export declare class MixinFieldInterface {
|
|
|
68
68
|
*
|
|
69
69
|
* `empty` is the control's own answer to whether it holds nothing, `message`
|
|
70
70
|
* is what to say about it, and `anchor` names the part the browser should
|
|
71
|
-
* point its bubble at.
|
|
71
|
+
* point its bubble at.
|
|
72
72
|
*/
|
|
73
73
|
protected reportRequired(empty: boolean, message: string, anchor: string): void;
|
|
74
74
|
/** The message actually shown: the consumer's, or the platform's, or none. */
|
|
@@ -129,17 +129,15 @@ export declare class MixinFieldInterface {
|
|
|
129
129
|
* uses. Measured the same way, and the failure is silent either way: the
|
|
130
130
|
* description simply never reaches the accessibility tree.
|
|
131
131
|
*
|
|
132
|
-
*
|
|
133
132
|
* **The label, the hint and the feedback are documented on each component that
|
|
134
|
-
* applies this, rather than here.**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* the inner class in the area rules is about.
|
|
133
|
+
* applies this, rather than here.** Declaring them once on this mixin is the
|
|
134
|
+
* obvious fix and does not work: the analyzer reads `@csspart` off a custom
|
|
135
|
+
* element class, so a class-level tag here reaches the manifest as nothing at
|
|
136
|
+
* all, on this JSDoc and on the inner class alike. Measured both ways, and the
|
|
137
|
+
* inner-class version is the dangerous one, since it satisfies the rule while
|
|
138
|
+
* every API page stays exactly as wrong as before. Members are different and
|
|
139
|
+
* do inherit, which is what the note about the inner class in the area rules
|
|
140
|
+
* is about.
|
|
143
141
|
*/
|
|
144
142
|
export declare const MixinField: <T extends Constructor<HelixElement>>(Base: T) => Constructor<MixinFieldInterface> & T;
|
|
145
143
|
export {};
|
|
@@ -65,17 +65,15 @@ const FEEDBACK_ID = 'helix-feedback';
|
|
|
65
65
|
* uses. Measured the same way, and the failure is silent either way: the
|
|
66
66
|
* description simply never reaches the accessibility tree.
|
|
67
67
|
*
|
|
68
|
-
*
|
|
69
68
|
* **The label, the hint and the feedback are documented on each component that
|
|
70
|
-
* applies this, rather than here.**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* the inner class in the area rules is about.
|
|
69
|
+
* applies this, rather than here.** Declaring them once on this mixin is the
|
|
70
|
+
* obvious fix and does not work: the analyzer reads `@csspart` off a custom
|
|
71
|
+
* element class, so a class-level tag here reaches the manifest as nothing at
|
|
72
|
+
* all, on this JSDoc and on the inner class alike. Measured both ways, and the
|
|
73
|
+
* inner-class version is the dangerous one, since it satisfies the rule while
|
|
74
|
+
* every API page stays exactly as wrong as before. Members are different and
|
|
75
|
+
* do inherit, which is what the note about the inner class in the area rules
|
|
76
|
+
* is about.
|
|
79
77
|
*/
|
|
80
78
|
export const MixinField = (Base) => {
|
|
81
79
|
// JSDoc is duplicated from MixinFieldInterface deliberately. The interface is
|
|
@@ -84,14 +82,14 @@ export const MixinField = (Base) => {
|
|
|
84
82
|
class FieldElement extends Base {
|
|
85
83
|
constructor() {
|
|
86
84
|
super(...arguments);
|
|
87
|
-
/**
|
|
88
|
-
* Help under the control, shown while there is nothing wrong.
|
|
89
|
-
*/
|
|
90
|
-
this.hint = '';
|
|
91
85
|
/**
|
|
92
86
|
* What is wrong, in the reader's words rather than the browser's.
|
|
93
87
|
*/
|
|
94
88
|
this.feedback = '';
|
|
89
|
+
/**
|
|
90
|
+
* Help under the control, shown while there is nothing wrong.
|
|
91
|
+
*/
|
|
92
|
+
this.hint = '';
|
|
95
93
|
/**
|
|
96
94
|
* The accessible name, and the fallback for the `label` slot.
|
|
97
95
|
*/
|
|
@@ -103,15 +101,14 @@ export const MixinField = (Base) => {
|
|
|
103
101
|
/**
|
|
104
102
|
* Whether the reader has had a go at the field yet.
|
|
105
103
|
*
|
|
106
|
-
*
|
|
107
|
-
* field wrong *yet*" and
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* the
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* both times.
|
|
104
|
+
* **Not `:user-invalid`**, which is the platform's own answer to "is this
|
|
105
|
+
* field wrong *yet*" and cannot work here, for a structural reason rather
|
|
106
|
+
* than a browser bug: it tracks interaction with the element it is on, and
|
|
107
|
+
* nobody interacts with this element. The reader types into an `<input>`
|
|
108
|
+
* inside its shadow root, so the host's interaction flag is never set and
|
|
109
|
+
* the selector never matches. Measured after submitting an invalid required
|
|
110
|
+
* field and again after editing one and leaving it: false both times, while
|
|
111
|
+
* `:invalid` matched both times.
|
|
115
112
|
*
|
|
116
113
|
* So the moments are listened for instead, and both of them demonstrably
|
|
117
114
|
* reach the host: `invalid` when a form is submitted with this control
|
|
@@ -122,10 +119,10 @@ export const MixinField = (Base) => {
|
|
|
122
119
|
* **A composite control has to construct its own `change`, and this mixin
|
|
123
120
|
* deliberately does not do it for them.** A native `change` is
|
|
124
121
|
* `composed: false`, so a control built from several inner inputs never
|
|
125
|
-
* relays one to its host: `Input` gets this free from its one control,
|
|
126
|
-
* `PinInput`
|
|
127
|
-
* entered, so a half-entered code
|
|
128
|
-
* what
|
|
122
|
+
* relays one to its host: `Input` gets this free from its one control,
|
|
123
|
+
* while a `PinInput` of four boxes has no inner `change` that means the
|
|
124
|
+
* code is entered, so a half-entered code is never `touched` and can never
|
|
125
|
+
* say what is wrong with it.
|
|
129
126
|
*
|
|
130
127
|
* **The obvious shared fix is for `touched` to take a `focusout`, and it is
|
|
131
128
|
* the wrong one**, because it is the paragraph above undone: a `focusout`
|
|
@@ -134,15 +131,15 @@ export const MixinField = (Base) => {
|
|
|
134
131
|
* focus moment, it is the native meaning of `change`, "edited, then left",
|
|
135
132
|
* and the editing half is the part a mixin up here cannot see.
|
|
136
133
|
*
|
|
137
|
-
* **Nor is there one shape to extract yet.** The two composites
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
134
|
+
* **Nor is there one shape to extract yet.** The two composites solve it
|
|
135
|
+
* differently and both are right: `PinInput` is focus-driven, taking a
|
|
136
|
+
* baseline when focus arrives from outside the row and announcing on the
|
|
137
|
+
* way out if it moved, and `DateInput` is value-driven, stopping its inner
|
|
138
|
+
* `change` and announcing once the date itself moves. Only one of them has
|
|
139
|
+
* a leaving moment at all, so generalizing from the pair is generalizing
|
|
140
|
+
* from one. The trigger for lifting it is a third control that needs the
|
|
141
|
+
* focus-driven shape, at which point what moves up is the
|
|
142
|
+
* baseline-and-commit pair rather than `touched`.
|
|
146
143
|
*/
|
|
147
144
|
this.touched = false;
|
|
148
145
|
/** What the last render put on screen, so a change can be noticed. */
|
|
@@ -230,10 +227,8 @@ export const MixinField = (Base) => {
|
|
|
230
227
|
* that the label is rendered and clipped, so the name arrives through the
|
|
231
228
|
* same `<label for>` a visible one does.
|
|
232
229
|
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
* label, `aria-label` means a name, and neither means an unnamed field a
|
|
236
|
-
* screen reader announces as "edit text"
|
|
230
|
+
* The rule is that `label` means a label, `aria-label` means a name, and
|
|
231
|
+
* neither means an unnamed field a screen reader announces as "edit text".
|
|
237
232
|
*/
|
|
238
233
|
renderLabel() {
|
|
239
234
|
const hidden = this.label === '' ? this.getAttribute('aria-label') : null;
|
|
@@ -323,17 +318,10 @@ export const MixinField = (Base) => {
|
|
|
323
318
|
/**
|
|
324
319
|
* Reports "required and empty" and refreshes the message, or clears both.
|
|
325
320
|
*
|
|
326
|
-
*
|
|
327
|
-
* anchor lookup, the same `refreshFeedback` under the same comment about
|
|
328
|
-
* validity moving without a property moving. Seven copies of a five-line
|
|
329
|
-
* shape is the threshold at which this mixin itself was extracted, and two
|
|
330
|
-
* of them had already drifted, `checkbox-group` having no validity at all
|
|
331
|
-
* until yesterday.
|
|
332
|
-
*
|
|
333
|
-
* What differs between them is exactly two things, so those are the
|
|
321
|
+
* Two things differ between the controls that call this, so those are the
|
|
334
322
|
* arguments: whether the control is empty, which only it can say, and the
|
|
335
323
|
* sentence to say about it, which is different for a box, a switch and a
|
|
336
|
-
* set. The anchor is looked up here because every one of them
|
|
324
|
+
* set. The anchor is looked up here because every one of them anchors on a
|
|
337
325
|
* rendered part and the browser needs somewhere to point the bubble.
|
|
338
326
|
*
|
|
339
327
|
* The message is not derived from the control's kind, deliberately. "Tick
|
|
@@ -354,13 +342,12 @@ export const MixinField = (Base) => {
|
|
|
354
342
|
* Records "wrong, and the reader knows it" as the custom state `invalid`.
|
|
355
343
|
*
|
|
356
344
|
* The styled tier selects on this rather than on `:host(:user-invalid)`,
|
|
357
|
-
* which cannot match for the reason above,
|
|
358
|
-
*
|
|
359
|
-
* construction.
|
|
345
|
+
* which cannot match for the reason above. One state, and the message and
|
|
346
|
+
* the border agree by construction.
|
|
360
347
|
*
|
|
361
|
-
* **A state and not `data-invalid
|
|
362
|
-
*
|
|
363
|
-
*
|
|
348
|
+
* **A state and not `data-invalid`.** A field served with a `feedback`
|
|
349
|
+
* message already failing is written invalid on its first update, which
|
|
350
|
+
* would be an attribute the consumer's server never sent.
|
|
364
351
|
* `HelixElement.toggleState` carries the argument.
|
|
365
352
|
*
|
|
366
353
|
* In `willUpdate`, which is where the resolved message is first known.
|
|
@@ -369,11 +356,9 @@ export const MixinField = (Base) => {
|
|
|
369
356
|
super.willUpdate(changed);
|
|
370
357
|
this.shownFeedback = this.resolvedFeedback;
|
|
371
358
|
this.toggleState('invalid', this.shownFeedback !== '');
|
|
372
|
-
/* And say it to the accessibility tree
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
* that message through `aria-describedby`, and was never announced as
|
|
376
|
-
* being *wrong*: a reader heard the message as though it were help text.
|
|
359
|
+
/* And say it to the accessibility tree. Describing a field with its
|
|
360
|
+
* `feedback` message through `aria-describedby` never announces it as
|
|
361
|
+
* being *wrong*: a reader hears the message as though it were help text.
|
|
377
362
|
* `aria-invalid` is the one word for it, and this is the one place that
|
|
378
363
|
* knows, so every field in the library gets it from here. */
|
|
379
364
|
this.internals.ariaInvalid = this.shownFeedback === '' ? null : 'true';
|
|
@@ -395,10 +380,10 @@ export const MixinField = (Base) => {
|
|
|
395
380
|
}
|
|
396
381
|
__decorate([
|
|
397
382
|
property()
|
|
398
|
-
], FieldElement.prototype, "
|
|
383
|
+
], FieldElement.prototype, "feedback", void 0);
|
|
399
384
|
__decorate([
|
|
400
385
|
property()
|
|
401
|
-
], FieldElement.prototype, "
|
|
386
|
+
], FieldElement.prototype, "hint", void 0);
|
|
402
387
|
__decorate([
|
|
403
388
|
property()
|
|
404
389
|
], FieldElement.prototype, "label", void 0);
|
|
@@ -417,7 +402,6 @@ export const MixinField = (Base) => {
|
|
|
417
402
|
|
|
418
403
|
The contract is enforced by use rather than by the cast. Every helper is
|
|
419
404
|
called from `Input` or `Checkbox`, so an interface that drifts from the
|
|
420
|
-
class fails their builds
|
|
421
|
-
were found. */
|
|
405
|
+
class fails their builds. */
|
|
422
406
|
return FieldElement;
|
|
423
407
|
};
|
|
@@ -11,10 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* That is reachable outside a test: a pointer released between the event being
|
|
13
13
|
* queued and the handler running is gone by the time this is called, and a
|
|
14
|
-
* synthetic `pointerdown` has no active pointer at all.
|
|
15
|
-
* reported four of those as uncaught page errors, and three splitter specs had
|
|
16
|
-
* each stubbed the method to get past it, which is the shape of a workaround
|
|
17
|
-
* that has been in place long enough to read as setup.
|
|
14
|
+
* synthetic `pointerdown` has no active pointer at all.
|
|
18
15
|
*
|
|
19
16
|
* Returns whether it took, for the callers that then ask `hasPointerCapture`
|
|
20
17
|
* before believing a move belongs to them.
|
|
@@ -11,10 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* That is reachable outside a test: a pointer released between the event being
|
|
13
13
|
* queued and the handler running is gone by the time this is called, and a
|
|
14
|
-
* synthetic `pointerdown` has no active pointer at all.
|
|
15
|
-
* reported four of those as uncaught page errors, and three splitter specs had
|
|
16
|
-
* each stubbed the method to get past it, which is the shape of a workaround
|
|
17
|
-
* that has been in place long enough to read as setup.
|
|
14
|
+
* synthetic `pointerdown` has no active pointer at all.
|
|
18
15
|
*
|
|
19
16
|
* Returns whether it took, for the callers that then ask `hasPointerCapture`
|
|
20
17
|
* before believing a move belongs to them.
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The nearest ancestor that is an instance of a class, crossing shadow roots.
|
|
3
3
|
*
|
|
4
|
-
* **Identity rather than tag name
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* this tier to the styled one." Nine headless families resolve their
|
|
8
|
-
* relationships that way; two did it by tag, and both named `helix-*` tags that
|
|
9
|
-
* only `@fusion.dev/helix` registers, which is a headless package depending on a
|
|
10
|
-
* styled one by string.
|
|
4
|
+
* **Identity rather than tag name.** Matching `helix-accordion-item` ties the
|
|
5
|
+
* headless tier to the styled one by string, and only `@fusion.dev/helix`
|
|
6
|
+
* registers a `helix-*` tag.
|
|
11
7
|
*
|
|
12
8
|
* The consequence is silent rather than loud. Subclassing a headless element under
|
|
13
9
|
* a different tag is exactly what the headless tier exists to allow, and it is
|
|
14
|
-
* what `helix/styled` itself does. Under a third tag
|
|
15
|
-
* so an accordion's `mode`
|
|
16
|
-
* with no error anywhere.
|
|
10
|
+
* what `helix/styled` itself does. Under a third tag a lookup by tag returns
|
|
11
|
+
* `null`, so an accordion's `mode` is ignored and every section behaves as
|
|
12
|
+
* `multiple` with no error anywhere.
|
|
17
13
|
*
|
|
18
14
|
* **`closest()` alone is not enough**, which is why this walks rather than
|
|
19
15
|
* delegating. `closest()` stops at a shadow boundary: a component slotted into
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The nearest ancestor that is an instance of a class, crossing shadow roots.
|
|
3
3
|
*
|
|
4
|
-
* **Identity rather than tag name
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* this tier to the styled one." Nine headless families resolve their
|
|
8
|
-
* relationships that way; two did it by tag, and both named `helix-*` tags that
|
|
9
|
-
* only `@fusion.dev/helix` registers, which is a headless package depending on a
|
|
10
|
-
* styled one by string.
|
|
4
|
+
* **Identity rather than tag name.** Matching `helix-accordion-item` ties the
|
|
5
|
+
* headless tier to the styled one by string, and only `@fusion.dev/helix`
|
|
6
|
+
* registers a `helix-*` tag.
|
|
11
7
|
*
|
|
12
8
|
* The consequence is silent rather than loud. Subclassing a headless element under
|
|
13
9
|
* a different tag is exactly what the headless tier exists to allow, and it is
|
|
14
|
-
* what `helix/styled` itself does. Under a third tag
|
|
15
|
-
* so an accordion's `mode`
|
|
16
|
-
* with no error anywhere.
|
|
10
|
+
* what `helix/styled` itself does. Under a third tag a lookup by tag returns
|
|
11
|
+
* `null`, so an accordion's `mode` is ignored and every section behaves as
|
|
12
|
+
* `multiple` with no error anywhere.
|
|
17
13
|
*
|
|
18
14
|
* **`closest()` alone is not enough**, which is why this walks rather than
|
|
19
15
|
* delegating. `closest()` stops at a shadow boundary: a component slotted into
|
|
@@ -4,7 +4,7 @@ import { type CSSResult } from 'lit';
|
|
|
4
4
|
*
|
|
5
5
|
* Component styles live in real `.css` files rather than `css` tagged
|
|
6
6
|
* templates, so they get CSS tooling: syntax highlighting, formatters, the
|
|
7
|
-
* token linter, and a diff that reads as CSS. `
|
|
7
|
+
* token linter, and a diff that reads as CSS. `turbo run styles` compiles each
|
|
8
8
|
* one into a sibling `.css.ts` that calls this.
|
|
9
9
|
*
|
|
10
10
|
* The `unsafe` in `unsafeCSS` warns about interpolating untrusted input into a
|
|
@@ -4,7 +4,7 @@ import { unsafeCSS } from 'lit';
|
|
|
4
4
|
*
|
|
5
5
|
* Component styles live in real `.css` files rather than `css` tagged
|
|
6
6
|
* templates, so they get CSS tooling: syntax highlighting, formatters, the
|
|
7
|
-
* token linter, and a diff that reads as CSS. `
|
|
7
|
+
* token linter, and a diff that reads as CSS. `turbo run styles` compiles each
|
|
8
8
|
* one into a sibling `.css.ts` that calls this.
|
|
9
9
|
*
|
|
10
10
|
* The `unsafe` in `unsafeCSS` warns about interpolating untrusted input into a
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Registers a custom element, once.
|
|
3
|
-
*
|
|
4
|
-
* A drop-in replacement for Lit's `@customElement` that does not throw when the
|
|
5
|
-
* tag is already registered.
|
|
6
|
-
*
|
|
7
|
-
* The duplicate is not hypothetical. Next.js evaluates a module in both the
|
|
8
|
-
* server and client graphs, so a component imported by a server component and a
|
|
9
|
-
* client component runs its registration twice against one registry, and
|
|
10
|
-
* `customElements.define` throws:
|
|
11
|
-
*
|
|
12
|
-
* > Failed to execute 'define': the name "helix-card" has already been used
|
|
13
|
-
*
|
|
14
|
-
* That failure takes down the whole prerender, and it appears only once an app
|
|
15
|
-
* mixes server and client usage of the same component, which is exactly what a
|
|
16
|
-
* real app does.
|
|
17
|
-
*
|
|
18
|
-
* The analyzer detects `@customElement` **by decorator name**, not by where it
|
|
19
|
-
* is imported from, so the manifest still records `tagName` and nothing
|
|
20
|
-
* downstream changes.
|
|
21
|
-
*/
|
|
22
1
|
/** Every tag registered twice by different classes, in registration order. */
|
|
23
2
|
export declare const duplicateRegistrations: () => string[];
|
|
24
3
|
/** Every tag registered through `customElement`, whoever imported it first. */
|
|
@@ -15,15 +15,15 @@ export interface DrawIconOptions {
|
|
|
15
15
|
* every consumer of any component that renders one ships all of the generated
|
|
16
16
|
* path data: measured at 50,814 bytes raw and 15,010 gzipped for 102 icons, and
|
|
17
17
|
* growing about 147 gzipped bytes per icon added. A named import ships one path
|
|
18
|
-
* and is checked when the package compiles rather than
|
|
18
|
+
* and is checked when the package compiles rather than by a name lookup later.
|
|
19
19
|
*
|
|
20
20
|
* That element still earns its place as *public* API, where a consumer writes a
|
|
21
21
|
* name in markup and a lookup is the only thing that can work. What it should
|
|
22
22
|
* not be is how this library draws its own marks.
|
|
23
23
|
*
|
|
24
24
|
* **There is no `part` option, and there cannot be.** A part name behind an
|
|
25
|
-
* interpolation is a part the manifest cannot see, so
|
|
26
|
-
*
|
|
25
|
+
* interpolation is a part the manifest cannot see, so the component's own
|
|
26
|
+
* stylesheet matches nothing and the part stops being
|
|
27
27
|
* API. `Trend` needs one and therefore writes its `<svg>` out with a literal
|
|
28
28
|
* `part="icon"`, which is the honest cost: a helper can carry everything except
|
|
29
29
|
* the thing static analysis has to read.
|