@fusion.dev/helix-headless 0.4.2 → 0.4.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 +39 -39
- package/dist/app-header/root/app-header-root.d.ts +8 -28
- package/dist/avatar/group/avatar-group.d.ts +5 -30
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -28
- package/dist/chat/root/chat-root.d.ts +9 -28
- package/dist/color-picker/root/color-picker-root.d.ts +10 -28
- package/dist/combobox/root/combobox-root.d.ts +18 -60
- package/dist/copy-button/root/copy-button-root.d.ts +6 -21
- package/dist/data-table/root/data-table-root.d.ts +9 -32
- package/dist/date-input/root/date-input-root.d.ts +5 -21
- package/dist/date-picker/root/date-picker-root.d.ts +10 -35
- package/dist/dialog/root/dialog-root.d.ts +7 -29
- package/dist/donut-chart/root/donut-chart-root.d.ts +5 -25
- package/dist/file-input/root/file-input-root.d.ts +5 -22
- package/dist/heatmap/root/heatmap-root.d.ts +13 -36
- package/dist/image/root/image-root.d.ts +5 -22
- package/dist/input/root/input-root.d.ts +11 -29
- package/dist/kbd/root/kbd-root.d.ts +8 -26
- package/dist/legend/root/legend-root.d.ts +6 -24
- package/dist/listbox/root/listbox-root.d.ts +4 -26
- package/dist/megamenu/root/megamenu-root.d.ts +7 -39
- package/dist/meter/root/meter-root.d.ts +6 -24
- package/dist/number-input/root/number-input-root.d.ts +11 -20
- package/dist/outline/root/outline-root.d.ts +8 -27
- package/dist/pagination/root/pagination-root.d.ts +21 -54
- package/dist/pagination/summary/pagination-summary.d.ts +10 -31
- package/dist/progress/root/progress-root.d.ts +4 -21
- package/dist/rating/root/rating-root.d.ts +8 -31
- package/dist/slider/root/slider-root.d.ts +9 -32
- package/dist/splitter/root/splitter-root.d.ts +17 -85
- package/dist/steps/root/steps-root.d.ts +8 -26
- package/dist/steps/step/step.d.ts +11 -41
- package/dist/toast/item/toast-item.d.ts +6 -30
- package/dist/toggle/group/toggle-group.d.ts +7 -25
- package/dist/tooltip/root/tooltip-root.d.ts +14 -36
- package/dist/tour/root/tour-root.d.ts +6 -29
- package/dist/tree/item/tree-item.d.ts +13 -35
- package/dist/video/root/video-root.d.ts +12 -40
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -25
- package/dist/virtual-list/root/virtual-list-root.d.ts +5 -44
- package/package.json +17 -18
- package/{jsx.d.ts → types/jsx.d.ts} +724 -2537
- package/{vue.d.ts → types/vue.d.ts} +39 -39
- /package/{svelte.d.ts → types/svelte.d.ts} +0 -0
|
@@ -10,29 +10,12 @@ declare const HelixHeadlessFileInput_base: (new (...args: any[]) => import("@fus
|
|
|
10
10
|
* replaced.** It is the only way to open a file picker: the dialog is a
|
|
11
11
|
* privileged action a script cannot start on its own, and it opens for a click
|
|
12
12
|
* on the input or on a `<label>` pointing at it. A component that draws its own
|
|
13
|
-
* button
|
|
14
|
-
*
|
|
13
|
+
* button calls `.click()` on a hidden input anyway, and then has two things a
|
|
14
|
+
* screen reader can find where there is one control.
|
|
15
15
|
*
|
|
16
|
-
* So the input stays,
|
|
17
|
-
* and the visible trigger is a `<label for
|
|
18
|
-
* not
|
|
19
|
-
* open the picker because that is what the platform does with a file input.
|
|
20
|
-
*
|
|
21
|
-
* **Dropping is why this is a component rather than an attribute.** A bare file
|
|
22
|
-
* input accepts a drop only on the input itself, which is invisible here, so the
|
|
23
|
-
* field is the drop target and it says when something is over it.
|
|
24
|
-
*
|
|
25
|
-
* **Files are not a value a consumer can hand back.** Every other control here
|
|
26
|
-
* takes the controlled and uncontrolled pair, and this one does not: a `FileList`
|
|
27
|
-
* cannot be constructed from markup, and browsers refuse a consumer writing
|
|
28
|
-
* `value` on a file input outright, because a page that could name a file could
|
|
29
|
-
* read it. So this element owns what was chosen, announces it, and offers
|
|
30
|
-
* `clear()` for the one change a consumer can legitimately ask for.
|
|
31
|
-
*
|
|
32
|
-
* **The list under it is the control's own, not decoration.** A file input with
|
|
33
|
-
* files chosen reports "3 files" and nothing else, which is unusable for
|
|
34
|
-
* checking that the right ones were picked. The names are rendered, and each
|
|
35
|
-
* carries the way to take it back out.
|
|
16
|
+
* So the input stays, holding the label, the name, the validity and the focus,
|
|
17
|
+
* and the visible trigger is a `<label for>`. A label is not focusable and does
|
|
18
|
+
* not need to be: the input is, and Enter and Space on it open the picker.
|
|
36
19
|
*
|
|
37
20
|
* @since 0.1.0
|
|
38
21
|
* @status stable
|
|
@@ -6,49 +6,26 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
|
|
|
6
6
|
* @summary A strip of cells colored by how each one went: uptime, contributions, activity.
|
|
7
7
|
*
|
|
8
8
|
* @description
|
|
9
|
-
* **One family covers both readings
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* discrete levels rather than a gradient, so a cell that takes a tone covers
|
|
13
|
-
* both: `rows="1"` is a status strip, `rows="7"` is a contribution graph, and
|
|
14
|
-
* heatmap is the name of that.
|
|
9
|
+
* **One family covers both readings.** `rows="1"` is a status strip, `rows="7"`
|
|
10
|
+
* is a contribution graph, and a cell that takes a tone covers both: GitHub's
|
|
11
|
+
* contribution graph is five discrete levels rather than a gradient.
|
|
15
12
|
*
|
|
16
13
|
* **What it cannot be is a bar chart with flat values**, and the data model is
|
|
17
|
-
* what closes that
|
|
18
|
-
*
|
|
19
|
-
* cells would be ninety series.
|
|
14
|
+
* what closes that: chart color is per *series* everywhere else here, so ninety
|
|
15
|
+
* differently colored cells would be ninety series.
|
|
20
16
|
*
|
|
21
|
-
* The data is a real `<table>` named by `for
|
|
22
|
-
* in this library uses,
|
|
23
|
-
*
|
|
24
|
-
* already in the markup, and the drawing is a picture of it. Hide it with
|
|
25
|
-
* `data-hidden-visually` and the reading is still there for anyone who needs it.
|
|
17
|
+
* **The data is a real `<table>` named by `for`**, the convention every chart
|
|
18
|
+
* in this library uses, and it is the accessible representation. Hide it with
|
|
19
|
+
* `data-hidden-visually` and the reading is still there.
|
|
26
20
|
*
|
|
27
21
|
* A cell takes its tone from its own `data-tone`, or from `thresholds` and
|
|
28
|
-
* `tones` bucketing its value. The
|
|
29
|
-
*
|
|
30
|
-
* already knows a day was degraded does not have to encode that as a number for
|
|
31
|
-
* this component to decode again.
|
|
22
|
+
* `tones` bucketing its value. The cell's own wins, so a page that already
|
|
23
|
+
* knows a day was degraded does not encode that as a number to be decoded.
|
|
32
24
|
*
|
|
33
25
|
* **A tone is a name this tier never resolves.** `tone="success"` writes
|
|
34
|
-
* `--heatmap-tone: var(--heatmap-tone-success)`
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* ramp, and a consumer on `headless` declares the same names as whatever they
|
|
38
|
-
* like. Reaching for `var(--helix-color-${tone})` here is the dependency the
|
|
39
|
-
* tiers exist to prevent, and it fails loudly rather than quietly: off-tier that
|
|
40
|
-
* property is not undefined, it is *invalid*, so
|
|
41
|
-
* `background-color: var(--heatmap-tone, …)` declines its own fallback and every
|
|
42
|
-
* cell comes out transparent, leaving a blank page under a row of footer text.
|
|
43
|
-
* `shared/chart/series.css` records the same fault in Bar Chart.
|
|
44
|
-
*
|
|
45
|
-
* **On a narrow container it draws fewer cells rather than thinner ones.**
|
|
46
|
-
* Ninety cells in 320 pixels is three and a half pixels each, which is a smear
|
|
47
|
-
* and not a strip. The cells it drops are the oldest, and it reflects `visible`
|
|
48
|
-
* and fires `helix-resize` when the number changes, because **a summary saying
|
|
49
|
-
* "99.2% over 90 days" above a strip drawing 45 of them is telling the reader
|
|
50
|
-
* something false about the picture in front of them** and nothing about the
|
|
51
|
-
* picture says so.
|
|
26
|
+
* `--heatmap-tone: var(--heatmap-tone-success)` and stops, so what a name is
|
|
27
|
+
* worth is the answering tier's to say. Reaching for `var(--helix-color-*)`
|
|
28
|
+
* here is the dependency the tiers exist to prevent.
|
|
52
29
|
*
|
|
53
30
|
* @since 0.1.0
|
|
54
31
|
* @status experimental
|
|
@@ -5,31 +5,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
5
5
|
*
|
|
6
6
|
* @description
|
|
7
7
|
* **An element rather than a recipe, because of the part CSS cannot do.**
|
|
8
|
-
* Reserving the box is `aspect-ratio` and needs nothing from anybody
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* and the two events that move between them, and leaves the box to the
|
|
8
|
+
* Reserving the box is `aspect-ratio` and needs nothing from anybody. What
|
|
9
|
+
* cannot be written as a rule is *when the picture arrived*: a stylesheet has
|
|
10
|
+
* no way to know, and no way to hear that it never will. So this owns three
|
|
11
|
+
* states and the two events that move between them, and leaves the box to the
|
|
13
12
|
* stylesheet.
|
|
14
13
|
*
|
|
15
14
|
* **The placeholder is a slot with nothing in it by default.** A default of
|
|
16
|
-
* `Skeleton` would
|
|
17
|
-
* component, and reaching for one would make the headless package depend on the
|
|
18
|
-
* styled one. The styled tier draws the resting placeholder instead, and a
|
|
19
|
-
* consumer who wants a `Skeleton`, a blurred thumbnail or a solid color slots
|
|
20
|
-
* it.
|
|
21
|
-
*
|
|
22
|
-
* **No source is a loading state, not a fourth one.** A picture whose `src` has
|
|
23
|
-
* not arrived yet is the common case for anything drawn from data, and the
|
|
24
|
-
* useful answer there is the same as for one still on the wire: stand something
|
|
25
|
-
* in its place and keep the ground reserved. So an element with no `src` shows
|
|
26
|
-
* its placeholder and waits, rather than resolving to an empty box that has to
|
|
27
|
-
* be laid out twice.
|
|
28
|
-
*
|
|
29
|
-
* **`alt` is a property and not an afterthought.** An empty string is a real
|
|
30
|
-
* answer, meaning the picture is decoration and a screen reader should skip it,
|
|
31
|
-
* which is why the attribute is always written even when it is empty: omitting
|
|
32
|
-
* it entirely makes a reader announce the file name instead.
|
|
15
|
+
* `Skeleton` would make this tier depend on a component it does not ship.
|
|
33
16
|
*
|
|
34
17
|
* @since 0.1.0
|
|
35
18
|
* @status experimental
|
|
@@ -8,39 +8,21 @@ declare const HelixHeadlessInput_base: (new (...args: any[]) => import("@fusion.
|
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
10
|
* Renders a real `<input>`, so the caret, selection, autofill, spellcheck, the
|
|
11
|
-
* password manager
|
|
12
|
-
* platform.
|
|
13
|
-
*
|
|
11
|
+
* password manager and the on-screen keyboard a phone chooses are all the
|
|
12
|
+
* platform's. It serializes into the server's markup, so the field is usable
|
|
13
|
+
* before any script loads.
|
|
14
14
|
*
|
|
15
|
-
* **The input inside a shadow root submits nothing**,
|
|
15
|
+
* **The input inside a shadow root submits nothing**, the form being on the
|
|
16
16
|
* other side of the boundary. The host is form-associated instead and reports
|
|
17
|
-
* its own value through `ElementInternals`, which
|
|
18
|
-
*
|
|
17
|
+
* its own value through `ElementInternals`, which also carries validity and
|
|
18
|
+
* answers a form reset.
|
|
19
19
|
*
|
|
20
|
-
* `value` and `defaultValue` are separate
|
|
21
|
-
* `defaultChecked` are: `value` is what is in the field now
|
|
22
|
-
*
|
|
23
|
-
* to and is gone the moment anyone types.
|
|
20
|
+
* `value` and `defaultValue` are separate the way `checked` and
|
|
21
|
+
* `defaultChecked` are: `value` is what is in the field now, `defaultValue` is
|
|
22
|
+
* what the markup asked for and where a reset returns to.
|
|
24
23
|
*
|
|
25
|
-
* **Validity is the platform's.**
|
|
26
|
-
* input and its `validity` is copied onto the host
|
|
27
|
-
* what the browser says is not an email rather than what a regular expression
|
|
28
|
-
* here guesses. What this adds is the anchor, so `reportValidity` points the
|
|
29
|
-
* bubble at the input rather than at the host.
|
|
30
|
-
*
|
|
31
|
-
* `pattern`, `minlength` and `maxlength` are forwarded with them.
|
|
32
|
-
*
|
|
33
|
-
* **`clearable` and `reveal` are the field's, not the consumer's.** Neither is
|
|
34
|
-
* a slot's worth of variation: one empties the value, the other flips the type,
|
|
35
|
-
* and both want the same position, the same size and the same disabled
|
|
36
|
-
* behavior. The `end` slot stays for what actually varies, a unit or a prefix.
|
|
37
|
-
*
|
|
38
|
-
* Both opt in rather than following from `type`, and are booleans that default
|
|
39
|
-
* to false. A Lit boolean property that defaults to *true* cannot be turned off
|
|
40
|
-
* from HTML at all, only from script, which is the wrong way round for
|
|
41
|
-
* revealing a password: a consumer should have to ask for it. Drawer's `modal`
|
|
42
|
-
* defaults true for the opposite reason, that losing a focus trap by forgetting
|
|
43
|
-
* a word is worse than having to say so.
|
|
24
|
+
* **Validity is the platform's.** `type` and `required` are set on the real
|
|
25
|
+
* input and its `validity` is copied onto the host.
|
|
44
26
|
*
|
|
45
27
|
* @since 0.1.0
|
|
46
28
|
* @status experimental
|
|
@@ -5,38 +5,20 @@ import { type TemplateResult } from 'lit';
|
|
|
5
5
|
*
|
|
6
6
|
* @description
|
|
7
7
|
* Renders a real `<kbd>`, so the passage still means "press this" to anything
|
|
8
|
-
* reading the markup
|
|
8
|
+
* reading the markup.
|
|
9
9
|
*
|
|
10
|
-
* **One key per element, with `keys` as the stated exception.** A
|
|
10
|
+
* **One key per element, with `keys` as the stated exception.** A sequence is
|
|
11
11
|
* several of these with the separator written between them as ordinary text,
|
|
12
|
-
* because that separator is prose:
|
|
13
|
-
*
|
|
14
|
-
* which. `keys` cannot be guessing, since a chord written `mod+k` has said
|
|
15
|
-
* which it is.
|
|
12
|
+
* because that separator is prose: "then" in a sequence, "plus" in a chord, and
|
|
13
|
+
* a component inserting one would have to guess which.
|
|
16
14
|
*
|
|
17
15
|
* **`keys` exists because the glyphs were being typed by hand and were wrong.**
|
|
18
16
|
* A literal `⌘K` in a template tells a reader on Windows to press a key their
|
|
19
|
-
* keyboard does not have, and nothing catches it
|
|
20
|
-
*
|
|
17
|
+
* keyboard does not have, and nothing catches it. Given `mod` this renders
|
|
18
|
+
* `⌘K` on Apple and `Ctrl+K` everywhere else.
|
|
21
19
|
*
|
|
22
|
-
* **`within` is what binds it, and it is absent by default
|
|
23
|
-
*
|
|
24
|
-
* component wants. The two values are the two honest lifetimes:
|
|
25
|
-
*
|
|
26
|
-
* - `control` listens within the element's own parent, which is the case this
|
|
27
|
-
* is mostly for: a hint sitting inside the control it belongs to, where the
|
|
28
|
-
* shortcut should work while that control has focus and stop existing when
|
|
29
|
-
* the control does.
|
|
30
|
-
* - `document` listens on the document, for a chord that works from anywhere.
|
|
31
|
-
*
|
|
32
|
-
* **A hidden `kbd` is a dead shortcut, and that is the point rather than a
|
|
33
|
-
* trap.** Binding here ties the key to its own advertisement, so a hint that is
|
|
34
|
-
* not rendered is a shortcut that is not offered. A chord that must outlive its
|
|
35
|
-
* label is a chord whose owner is not a label: give it to whatever owns the
|
|
36
|
-
* action and let this element only describe it.
|
|
37
|
-
*
|
|
38
|
-
* Nothing binds until this upgrades, so a shortcut is not available in the
|
|
39
|
-
* moments before hydration.
|
|
20
|
+
* **`within` is what binds it, and it is absent by default**, so this draws a
|
|
21
|
+
* key and listens for nothing unless asked.
|
|
40
22
|
*
|
|
41
23
|
* @since 0.1.0
|
|
42
24
|
* @status stable
|
|
@@ -6,32 +6,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
6
6
|
* @description
|
|
7
7
|
* A legend is a list, so it is a list: not part of a chart, and shared by
|
|
8
8
|
* whichever of them needs one. It takes its entries from the same table the
|
|
9
|
-
* chart reads, through `for`, so the two cannot disagree
|
|
10
|
-
* there are or what they are called.
|
|
9
|
+
* chart reads, through `for`, so the two cannot disagree.
|
|
11
10
|
*
|
|
12
|
-
* `entries` picks which way to read that table
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* and it has no axis to write their names along.
|
|
11
|
+
* `entries` picks which way to read that table. `series` is one entry per data
|
|
12
|
+
* column, named by the header row, which is a line or a bar chart.
|
|
13
|
+
* `categories` is one entry per body row, which is a donut: its slices are the
|
|
14
|
+
* rows and it has no axis to write their names along.
|
|
17
15
|
*
|
|
18
|
-
* **The table is resolved on the client and there is no server pass.**
|
|
19
|
-
* an id and resolving it wants a document, which the server render does not
|
|
20
|
-
* have, so a legend ships empty and fills in on hydration. The table itself is
|
|
21
|
-
* ordinary markup and is served whole, so nothing is missing from the page
|
|
22
|
-
* before then, only from this list.
|
|
23
|
-
*
|
|
24
|
-
* The table is watched, because a legend that goes stale when its data changes
|
|
25
|
-
* is a legend that has to be told, and nothing else in the page would know to
|
|
26
|
-
* tell it.
|
|
27
|
-
*
|
|
28
|
-
* **Not hidden from a screen reader, though the mapping it draws is a color
|
|
29
|
-
* one.** The argument for hiding it is real: the colors mean nothing to a
|
|
30
|
-
* reader who cannot see them, and the names are already in the table. It stays
|
|
31
|
-
* because the cost of being wrong runs one way. A duplicated list of three
|
|
32
|
-
* words is noise; a consumer who took the table out of the accessibility tree
|
|
33
|
-
* and then found the legend hidden too has a chart nobody can read, and neither
|
|
34
|
-
* decision announces itself.
|
|
16
|
+
* **The table is resolved on the client and there is no server pass.**
|
|
35
17
|
*
|
|
36
18
|
* @since 0.1.0
|
|
37
19
|
* @status experimental
|
|
@@ -9,37 +9,15 @@ declare const HelixHeadlessListbox_base: (new (...args: any[]) => import("@fusio
|
|
|
9
9
|
* **The listbox is the control and the options are its vocabulary.** A Select
|
|
10
10
|
* is this with a trigger in front of it, a Combobox is this with a text input,
|
|
11
11
|
* and a command palette is this again: they differ in what opens the list and
|
|
12
|
-
* what filters it, not in what the list is.
|
|
13
|
-
* what stops each of those reimplementing the keyboard and the filtering.
|
|
12
|
+
* what filters it, not in what the list is.
|
|
14
13
|
*
|
|
15
14
|
* **`value` is comma separated when `multiple` is set**, the way
|
|
16
15
|
* `Checkbox.Group`'s is. It stays a string because that is what a form submits
|
|
17
|
-
* and what `MixinControllerValue` holds
|
|
18
|
-
* shape for the same fact, and the two would disagree the first time one was
|
|
19
|
-
* set without the other.
|
|
16
|
+
* and what `MixinControllerValue` holds.
|
|
20
17
|
*
|
|
21
18
|
* **Chosen and active are two different things.** Chosen is the answer; active
|
|
22
|
-
* is where the keyboard is.
|
|
23
|
-
*
|
|
24
|
-
* `multiple` they come apart: the arrows move without choosing and Space
|
|
25
|
-
* chooses, because a reader passing over four options on the way to the fifth
|
|
26
|
-
* did not mean to pick all five.
|
|
27
|
-
*
|
|
28
|
-
* **This does not use `KeyboardNavigationController`, and the reason is where
|
|
29
|
-
* focus lives rather than whether moving chooses.** The controller already
|
|
30
|
-
* separates those, taking `origin: 'focus' | 'selection'`. What it cannot do is
|
|
31
|
-
* leave focus alone: `NavigableItem.focusItem()` moves DOM focus to the item,
|
|
32
|
-
* its model being a roving tab stop. A filterable list cannot have that, focus
|
|
33
|
-
* having to stay in the field or typing stops, so what moves is a virtual
|
|
34
|
-
* cursor and `aria-activedescendant` is what says where it is. No flag
|
|
35
|
-
* reconciles the two.
|
|
36
|
-
*
|
|
37
|
-
* **Filtering hides rather than removes.** An option the filter has hidden is
|
|
38
|
-
* still the consumer's element in the consumer's order, so a filter that clears
|
|
39
|
-
* puts the list back exactly as it was with nothing re-rendered. What counts as
|
|
40
|
-
* a match is a plain case-insensitive substring, and a consumer wanting more
|
|
41
|
-
* hides options itself: `filter` is a convenience over the same `hidden` the
|
|
42
|
-
* component uses.
|
|
19
|
+
* is where the keyboard is. Only the second moves with the arrows, and
|
|
20
|
+
* `aria-activedescendant` names it while focus stays on the list.
|
|
43
21
|
*
|
|
44
22
|
* @since 0.1.0
|
|
45
23
|
* @status experimental
|
|
@@ -8,49 +8,17 @@ declare const HelixHeadlessMegamenu_base: (new (...args: any[]) => import("@fusi
|
|
|
8
8
|
* **A disclosure, not a menu, and that is the whole design.** `role="menu"` and
|
|
9
9
|
* `menuitem` are for an application's commands: they take over the arrow keys,
|
|
10
10
|
* announce "menu" to a reader who asked for navigation, and put every link
|
|
11
|
-
* inside a role that is not a link.
|
|
12
|
-
*
|
|
13
|
-
* gives for site navigation and the one that leaves a middle click, a modified
|
|
11
|
+
* inside a role that is not a link. This is a button that shows and hides a
|
|
12
|
+
* region of the consumer's own anchors, which leaves a middle click, a modified
|
|
14
13
|
* click and the status bar working. `Menu` remains right for a row of commands.
|
|
15
14
|
*
|
|
16
|
-
* **It renders no links
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* **It renders no links.** A router renders its own anchor and cannot render
|
|
16
|
+
* one inside this shadow root, so the panel is a slot and what goes in it is
|
|
17
|
+
* the lists and headings the consumer wrote.
|
|
19
18
|
*
|
|
20
19
|
* **It opens on hover, and a press is always the fallback.** `open-on` says
|
|
21
|
-
* which, and hover is the resting value because a nav bar is
|
|
22
|
-
*
|
|
23
|
-
* read, click again to compare two is the interaction hover exists to save.
|
|
24
|
-
*
|
|
25
|
-
* Hover is not the only way in, and the code is mostly about that. There is no
|
|
26
|
-
* hover on a touch screen, so a tap opens the panel there whatever `open-on`
|
|
27
|
-
* says, which is why the pointer type is checked rather than assumed. Focus
|
|
28
|
-
* still opens nothing, for the same reason a tab through a nav bar should not
|
|
29
|
-
* unfold it, but Enter on the trigger does, and a keyboard press arrives as a
|
|
30
|
-
* click with no pointer behind it.
|
|
31
|
-
*
|
|
32
|
-
* A mouse click on a trigger the pointer is already sitting on is the case
|
|
33
|
-
* worth naming: toggling there would close the panel and the pointer, still on
|
|
34
|
-
* the trigger, would open it again, so the trigger would flicker and never
|
|
35
|
-
* close. In hover mode the mouse's own clicks are left alone and hover governs
|
|
36
|
-
* on its own. Escape and a click away still close it.
|
|
37
|
-
*
|
|
38
|
-
* The delays are intent, not decoration. Opening waits, so a pointer crossing
|
|
39
|
-
* the bar on its way somewhere else opens nothing; closing waits longer, so the
|
|
40
|
-
* diagonal from the trigger down into the panel does not pass through a gap and
|
|
41
|
-
* shut it. Moving along a bar where a panel is already down opens the next one
|
|
42
|
-
* at once, because at that point the reader is reading the bar, not crossing it.
|
|
43
|
-
*
|
|
44
|
-
* **Focus is not trapped and not moved.** A disclosure leaves the reader on the
|
|
45
|
-
* trigger and the panel follows in the tab order, which is what the markup
|
|
46
|
-
* already says, so Tab walks into the links and out the far side without
|
|
47
|
-
* anything managing it. Escape closes and puts focus back, because Escape is
|
|
48
|
-
* the one key that has to return the reader to where they were.
|
|
49
|
-
*
|
|
50
|
-
* The custom property below is written by this component and read by whoever
|
|
51
|
-
* draws it. A consumer never sets it: it carries a number this element computed
|
|
52
|
-
* and CSS cannot, which is what lets a stylesheet on this tier draw the thing
|
|
53
|
-
* without measuring anything itself.
|
|
20
|
+
* which, and hover is the resting value because a nav bar is where a reader
|
|
21
|
+
* expects to sweep along a row and see each section.
|
|
54
22
|
*
|
|
55
23
|
* @since 0.1.0
|
|
56
24
|
* @status experimental
|
|
@@ -9,31 +9,13 @@ export type MeterBand = 'fair' | 'good' | 'poor';
|
|
|
9
9
|
* **Not `Progress`, and the difference is the whole component.** A progress bar
|
|
10
10
|
* reports how far along a task is: it starts empty, ends full, and full is
|
|
11
11
|
* always the good end. A meter reports a level in a range that is not going
|
|
12
|
-
* anywhere, and which end is good is a fact about the thing
|
|
13
|
-
*
|
|
14
|
-
* here: a measurement whose value is unknown is not a measurement.
|
|
12
|
+
* anywhere, and which end is good is a fact about the thing measured. So there
|
|
13
|
+
* is no `indeterminate`: a measurement whose value is unknown is not one.
|
|
15
14
|
*
|
|
16
|
-
* **`low`, `high` and `optimum` are the reason it exists**,
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* to it is fair, and the one two away is poor.
|
|
21
|
-
*
|
|
22
|
-
* **Without thresholds there are no bands, and this is where it parts company
|
|
23
|
-
* with `<meter>`.** The platform defaults `optimum` to the midpoint and both
|
|
24
|
-
* thresholds to the ends, so a bare `<meter value="20" max="100">` computes as
|
|
25
|
-
* optimum and every browser paints it green. A gauge with nothing said about it
|
|
26
|
-
* is not good news; it is a reading. Declared thresholds turn the color on.
|
|
27
|
-
*
|
|
28
|
-
* **And the bands are drawn as well as colored.** A tick sits at `low` and at
|
|
29
|
-
* `high`, so the reading can be placed against them without seeing color at
|
|
30
|
-
* all, which is the same call the carousel's current dot makes by lengthening
|
|
31
|
-
* rather than only changing color.
|
|
32
|
-
*
|
|
33
|
-
* The properties below are written by this component and read by whoever draws
|
|
34
|
-
* it. A consumer sets none of them: they carry a number this element computed
|
|
35
|
-
* and CSS cannot, which is what lets a stylesheet on this tier draw the thing
|
|
36
|
-
* without measuring anything itself.
|
|
15
|
+
* **`low`, `high` and `optimum` are the reason it exists**, following the HTML
|
|
16
|
+
* specification's own rule. The three cut the range into a low band, a middle
|
|
17
|
+
* and a high band, and `optimum` says which is the one to be in; the band next
|
|
18
|
+
* to it is fair and the one two away is poor.
|
|
37
19
|
*
|
|
38
20
|
* @since 0.1.0
|
|
39
21
|
* @status stable
|
|
@@ -5,17 +5,13 @@ declare const HelixHeadlessNumberInput_base: (new (...args: any[]) => import("@f
|
|
|
5
5
|
* @summary A number a reader types or steps to.
|
|
6
6
|
*
|
|
7
7
|
* @description
|
|
8
|
-
* **A family rather than `Input`'s `type="number"
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* to be. This is a different control. It renders two buttons, binds four keys
|
|
12
|
-
* that `Input` does not, and owns clamping and stepping, so it is an alternative
|
|
13
|
-
* and not a variant. Namespacing it under `Input` would say it is contained by
|
|
14
|
-
* one, which is what `IconButton` is top-level for.
|
|
8
|
+
* **A family rather than `Input`'s `type="number"`.** It renders two buttons,
|
|
9
|
+
* binds four keys `Input` does not, and owns clamping and stepping, so it is an
|
|
10
|
+
* alternative and not a variant.
|
|
15
11
|
*
|
|
16
12
|
* **The field is `type="text"` with `inputmode="decimal"`, and that is a
|
|
17
|
-
* measurement
|
|
18
|
-
*
|
|
13
|
+
* measurement.** Asked of a real browser, `type=number` fails three ways this
|
|
14
|
+
* control cannot live with:
|
|
19
15
|
*
|
|
20
16
|
* ```
|
|
21
17
|
* typed "12e" into type=number → .value === ""
|
|
@@ -24,21 +20,16 @@ declare const HelixHeadlessNumberInput_base: (new (...args: any[]) => import("@f
|
|
|
24
20
|
* same keystrokes into type=text → .value === "12e"
|
|
25
21
|
* ```
|
|
26
22
|
*
|
|
27
|
-
* The first is fatal
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* The third means the caret cannot be managed at all, which any correcting or
|
|
31
|
-
* formatting field needs. `inputmode` is what keeps the phone keypad, which is
|
|
23
|
+
* The first is fatal alone: the element cannot see what the reader typed. The
|
|
24
|
+
* second changes a value because a page scrolled past. The third means the
|
|
25
|
+
* caret cannot be managed at all. `inputmode` keeps the phone keypad, which is
|
|
32
26
|
* the one thing `type=number` was giving.
|
|
33
27
|
*
|
|
34
28
|
* Validity is still the platform's, through `min`, `max` and `step` mirrored
|
|
35
|
-
* onto the host's own `setValidity`.
|
|
36
|
-
* `type`, because a text field has no opinion about numbers.
|
|
29
|
+
* onto the host's own `setValidity`.
|
|
37
30
|
*
|
|
38
|
-
* **Stepping is separate from typing
|
|
39
|
-
*
|
|
40
|
-
* range when they step, blur, or the form asks. Clamping on every keystroke is
|
|
41
|
-
* what makes a field that cannot be typed backwards into.
|
|
31
|
+
* **Stepping is separate from typing**, so a reader mid-way through a number is
|
|
32
|
+
* not corrected under their hands.
|
|
42
33
|
*
|
|
43
34
|
* @since 0.1.0
|
|
44
35
|
* @status experimental
|
|
@@ -5,35 +5,16 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
5
5
|
*
|
|
6
6
|
* @description
|
|
7
7
|
* A table of contents, named for the job. It sits beside `Navigation`, which is
|
|
8
|
-
* the same job at the other scale: one navigates a site, the other
|
|
9
|
-
* document. `Contents` and `TableOfContents` are both taken by something else,
|
|
10
|
-
* `display: contents` and the `content` part in one case and the `Table` family
|
|
11
|
-
* in the other.
|
|
8
|
+
* the same job at the other scale: one navigates a site, the other a document.
|
|
12
9
|
*
|
|
13
|
-
* **The links are the consumer's own anchors
|
|
14
|
-
*
|
|
15
|
-
*
|
|
10
|
+
* **The links are the consumer's own anchors.** A router renders its own `<a>`
|
|
11
|
+
* and cannot render one inside this shadow root, and the hrefs are fragments
|
|
12
|
+
* the page already knows.
|
|
16
13
|
*
|
|
17
|
-
* **What is current is decided by position, not by intersection.**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* together several are and it flickers between them. So the observer is only
|
|
22
|
-
* the *trigger*, and the answer is computed from where the headings actually
|
|
23
|
-
* are: the current section is the last one whose top has passed the reading
|
|
24
|
-
* line, or the first heading before any of them has.
|
|
25
|
-
*
|
|
26
|
-
* That rule is what a reader means. Two headings on screen at once are not
|
|
27
|
-
* ambiguous to a person: they are reading under the upper one.
|
|
28
|
-
*
|
|
29
|
-
* **`aria-current="location"`, and it is written onto the consumer's anchor.**
|
|
30
|
-
* This library does not normally write to light-DOM children, because an
|
|
31
|
-
* attribute added after upgrade is one a server's markup never had. It is right
|
|
32
|
-
* here for the reason `Carousel` names its slides: what is current is derived
|
|
33
|
-
* from scroll position, so no server could have rendered it and there is
|
|
34
|
-
* nothing for hydration to disagree with. `location` rather than `page`, which
|
|
35
|
-
* is what `Navigation` uses: `page` means this is the page, and every one of these
|
|
36
|
-
* is the same page.
|
|
14
|
+
* **What is current is decided by position, not by intersection.** An
|
|
15
|
+
* `IntersectionObserver` with a band near the top fails in both directions:
|
|
16
|
+
* with headings far apart nothing is in the band and the marker goes out, and
|
|
17
|
+
* with headings close together several are in it at once.
|
|
37
18
|
*
|
|
38
19
|
* @since 0.1.0
|
|
39
20
|
* @status experimental
|
|
@@ -4,67 +4,34 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
4
4
|
* @summary Moves between pages of a longer result.
|
|
5
5
|
*
|
|
6
6
|
* @description
|
|
7
|
-
* The component is the truncation
|
|
8
|
-
* numbers to show and where the runs break,
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `<helix-pagination page="4" pages="12">` rather than twelve slotted children
|
|
12
|
-
* a consumer has to keep in step with their own query.
|
|
7
|
+
* **The component is the truncation.** Given a page and a total it works out
|
|
8
|
+
* which numbers to show and where the runs break, so this is data-driven rather
|
|
9
|
+
* than composed: `<helix-pagination page="4" pages="12">` rather than twelve
|
|
10
|
+
* slotted children a consumer keeps in step with their own query.
|
|
13
11
|
*
|
|
14
12
|
* **The width does not change as you page.** The run around the current page is
|
|
15
|
-
* `siblings` either side
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* `siblings` either side, and grows to absorb a gap slot at a boundary, so the
|
|
14
|
+
* control holds the same number of slots from the first page to the last.
|
|
15
|
+
* Anything that lets the count vary moves the buttons under the pointer between
|
|
16
|
+
* one click and the next.
|
|
19
17
|
*
|
|
20
|
-
* **A gap is only drawn where it hides more than one page
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* **And a gap is a control.** It goes to the middle of the run it stands over,
|
|
25
|
-
* which is the binary-search step: any page in a set of any size is a handful of
|
|
26
|
-
* presses, where a jump of a fixed size would need forty of them on a set of a
|
|
27
|
-
* hundred and twenty-eight. A menu listing the hidden pages was the other idea
|
|
28
|
-
* and the arithmetic refuses it, for the reasons on `renderGap`.
|
|
18
|
+
* **A gap is only drawn where it hides more than one page, and a gap is a
|
|
19
|
+
* control.** It goes to the middle of the run it stands over, which is the
|
|
20
|
+
* binary-search step: any page in a set of any size is a handful of presses.
|
|
29
21
|
*
|
|
30
22
|
* **`href` is what makes these links.** Set it and every page renders an `<a>`
|
|
31
|
-
* with `{page}` filled in, so the
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* `helix-navigate` first, and `preventDefault()`
|
|
35
|
-
*
|
|
36
|
-
* underneath it.
|
|
37
|
-
*
|
|
38
|
-
* **`helix-navigate` and not `helix-change`**, for the reason the calendar pages
|
|
39
|
-
* with it too. Moving to page four chooses nothing; it moves the view over a
|
|
40
|
-
* result that has not changed. A consumer driving the view has to be able to
|
|
41
|
-
* hear that without being told an answer changed.
|
|
42
|
-
*
|
|
43
|
-
* **The count and the page-size control are their own elements.** A slot can
|
|
44
|
-
* only offer one place: at one end of one row, in one order, on one axis.
|
|
45
|
-
* `Pagination.Summary` and `Pagination.Size` sit beside this one instead, so
|
|
46
|
-
* the direction and the order of a footer are the consumer's, which is what
|
|
47
|
-
* they were always going to want. `total` and `page-size` stay here for the
|
|
48
|
-
* arithmetic below and nothing else.
|
|
49
|
-
*
|
|
50
|
-
* **A single page renders nothing.** A control offering one page offers no
|
|
51
|
-
* navigation, and the row it sits in should close over it rather than hold a
|
|
52
|
-
* space for a thing that cannot be used. Reported through
|
|
53
|
-
* `ElementInternals.states` rather than an attribute on the host, because an
|
|
54
|
-
* attribute written at upgrade is markup a server's output never had and a
|
|
55
|
-
* framework reports the difference; a custom state carries the same fact to a
|
|
56
|
-
* stylesheet and none of that to the DOM.
|
|
23
|
+
* with `{page}` filled in, so the output is crawlable, a middle click opens a
|
|
24
|
+
* tab, and the address bar is the state. Left unset they are buttons, which is
|
|
25
|
+
* right for a table paging in place. Either way the click announces
|
|
26
|
+
* `helix-navigate` first, and `preventDefault()` stops the navigation as well
|
|
27
|
+
* as the change, which is how a router takes over.
|
|
57
28
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* pressed 5 hears the word "5" from the button they were on and nothing at all
|
|
61
|
-
* about having arrived. A polite live region says where they now are. It ships
|
|
62
|
-
* empty and is filled only on a change, because a region populated as it enters
|
|
63
|
-
* the document announces itself on arrival.
|
|
29
|
+
* **`helix-navigate` and not `helix-change`.** Moving to page four chooses
|
|
30
|
+
* nothing; it moves the view over a result that has not changed.
|
|
64
31
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
32
|
+
* **The count and the page-size control are their own elements.**
|
|
33
|
+
* `Pagination.Summary` and `Pagination.Size` sit beside this one, because a
|
|
34
|
+
* slot can only offer one place: at one end of one row, in one order.
|
|
68
35
|
*
|
|
69
36
|
* @since 0.1.0
|
|
70
37
|
* @status experimental
|