@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
|
@@ -5,37 +5,17 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
5
5
|
*
|
|
6
6
|
* @description
|
|
7
7
|
* Tracks whether the page has scrolled and carries it as the custom state
|
|
8
|
-
* `scrolled`, so the styled tier can lift the bar
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* part-way down corrects on hydration.
|
|
8
|
+
* `scrolled`, so the styled tier can lift the bar without a scroll handler of
|
|
9
|
+
* its own. It is read on connect rather than derived: a fresh load starts at
|
|
10
|
+
* the top, which is what the server rendered, and a reload part-way down
|
|
11
|
+
* corrects on hydration.
|
|
13
12
|
*
|
|
14
13
|
* **A custom state and never `data-scrolled`.** An attribute the element writes
|
|
15
14
|
* onto its own host is one the server did not send, so a hydrating framework
|
|
16
|
-
* reports
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* for a hydration pass to disagree with, and `:state(scrolled)` selects it. The
|
|
21
|
-
* base class made the same move for `data-settled` and its note explains the
|
|
22
|
-
* rest.
|
|
23
|
-
*
|
|
24
|
-
* The listener is passive and reads `scrollY` only, so it never blocks
|
|
25
|
-
* scrolling.
|
|
26
|
-
*
|
|
27
|
-
* Three regions, each in its own wrapper, because the row is a grid and a grid
|
|
28
|
-
* needs three items to put the middle one in the middle. A bare slot is
|
|
29
|
-
* `display: contents`, so its assigned elements become grid items themselves:
|
|
30
|
-
* two actions would take two tracks and push the navigation off center. These
|
|
31
|
-
* wrappers earn their boxes, which is the test; they carry no inset or fill of
|
|
32
|
-
* their own, so an empty one still paints nothing.
|
|
33
|
-
*
|
|
34
|
-
* The host is the banner and spans the viewport; the row inside it is what gets
|
|
35
|
-
* centered. A banner that is itself narrowed leaves its background stopping
|
|
36
|
-
* short of the window edges, which reads as a floating bar rather than as the
|
|
37
|
-
* top of the page. That needs one element and one row, not a wrapper as well:
|
|
38
|
-
* the host carries the material and `content` carries the measure.
|
|
15
|
+
* reports a mismatch, intermittently: only when the reader reloads far enough
|
|
16
|
+
* down the page. `ElementInternals.states` is not in the DOM, so there is
|
|
17
|
+
* nothing for a hydration pass to disagree with, and `:state(scrolled)` selects
|
|
18
|
+
* it.
|
|
39
19
|
*
|
|
40
20
|
* @since 0.1.0
|
|
41
21
|
* @status stable
|
|
@@ -15,40 +15,15 @@ export declare const FOLDS_UP_TO = 12;
|
|
|
15
15
|
*
|
|
16
16
|
* @description
|
|
17
17
|
* A part of `Avatar` rather than a family of its own: it arranges avatars and
|
|
18
|
-
* is about nothing else
|
|
19
|
-
* not have to explain.
|
|
18
|
+
* is about nothing else.
|
|
20
19
|
*
|
|
21
20
|
* **The overflow count is the group's, not a slot.** Which avatars are hidden
|
|
22
|
-
* is a fact the group works out from `max
|
|
23
|
-
*
|
|
24
|
-
* agree with a list they do not control.
|
|
21
|
+
* is a fact the group works out from `max`, so a consumer writing the "+3"
|
|
22
|
+
* would be writing a number that has to agree with a list they do not control.
|
|
25
23
|
*
|
|
26
|
-
* **Hidden, not removed.** Past `max
|
|
24
|
+
* **Hidden, not removed.** Past `max` the extra avatars stay in the DOM and
|
|
27
25
|
* stop being shown, so nothing re-renders when `max` changes and the accessible
|
|
28
|
-
* name
|
|
29
|
-
* gets; the group's own name is what everyone else gets, and "Seven people" is
|
|
30
|
-
* more useful than seven names read in a row.
|
|
31
|
-
*
|
|
32
|
-
* **And hidden by the stylesheet, not by this class.** Hiding from `updated` is
|
|
33
|
-
* one update too late and one element too far out: Lit renders on a microtask,
|
|
34
|
-
* so an avatar appended past the limit is laid out and painted before anything
|
|
35
|
-
* runs to hide it, and marking each one means writing attributes onto markup a
|
|
36
|
-
* consumer owns. The styled tier's
|
|
37
|
-
* `:host([max="N"]) ::slotted(:nth-child(n + N+1))` is right at the first style
|
|
38
|
-
* resolution and writes nothing.
|
|
39
|
-
*
|
|
40
|
-
* **The fold is finite, and the count agrees with it.** `An+B` takes a literal,
|
|
41
|
-
* so there is one selector per value of `max` and the range stops at twelve.
|
|
42
|
-
* Past that the group shows everyone, counts nothing and says so, because
|
|
43
|
-
* showing everyone is a defensible answer and showing everyone beside a "+5" is
|
|
44
|
-
* not. `max` above twelve is a list the consumer should be slicing themselves.
|
|
45
|
-
*
|
|
46
|
-
* **It is a list.** A row of faces is a set of separate people, and a group
|
|
47
|
-
* with no role announces as one run of text. `role="list"` needs its children
|
|
48
|
-
* to be `listitem`, which they cannot be told to become without writing an
|
|
49
|
-
* attribute onto a consumer's element, so the group carries `role="group"` and
|
|
50
|
-
* its name instead: one announcement, correct, and nothing written to anyone
|
|
51
|
-
* else's markup.
|
|
26
|
+
* name is still built from all of them.
|
|
52
27
|
*
|
|
53
28
|
* @since 0.1.0
|
|
54
29
|
* @status stable
|
|
@@ -4,37 +4,18 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
4
4
|
* @summary Shows the path to the current page.
|
|
5
5
|
*
|
|
6
6
|
* @description
|
|
7
|
-
* The host is the landmark, so `aria-label` on it names the trail directly
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The host is the landmark, so `aria-label` on it names the trail directly. A
|
|
8
|
+
* page may hold more than one navigation, and landmarks are how a
|
|
9
|
+
* screen-reader user chooses between them.
|
|
10
10
|
*
|
|
11
|
-
* `role="list"` is set explicitly on the `<ol>` even though it is
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `role="list"` is set explicitly on the `<ol>` even though it is one:
|
|
12
|
+
* `list-style: none` strips list semantics in Safari, and a breadcrumb trail
|
|
13
|
+
* always removes its markers.
|
|
14
14
|
*
|
|
15
|
-
* Items are slotted rather than wrapped, so they are `<ol>` children in the
|
|
16
|
-
* tree and each carries `role="listitem"` itself.
|
|
15
|
+
* Items are slotted rather than wrapped, so they are `<ol>` children in the
|
|
16
|
+
* flat tree and each carries `role="listitem"` itself.
|
|
17
17
|
*
|
|
18
|
-
* **The trail does not measure itself, and the overflow is markup.**
|
|
19
|
-
* how many crumbs fit can never reach the server: there is no width without a
|
|
20
|
-
* DOM, so the server ships every crumb and the client removes some after
|
|
21
|
-
* hydrating, and a trail that collapses paints wrong on every single load. Nor
|
|
22
|
-
* does anybody specify a breadcrumb in units of fit. Designs say "keep the root
|
|
23
|
-
* and the current page, fold the middle", which is a fact about depth, or "fold
|
|
24
|
-
* below this width", which is a fact CSS knows.
|
|
25
|
-
*
|
|
26
|
-
* So the trail lays out crumbs and separators, and the consumer writes what
|
|
27
|
-
* folds: a `helix-menu` of `helix-menu-item`s in a crumb, where the gap
|
|
28
|
-
* belongs. Server rendered, no observer, no stylesheet, and correct before the
|
|
29
|
-
* first script runs. A trail that should fold only at some widths is a
|
|
30
|
-
* container query in the consumer's own sheet, which is theirs to write and
|
|
31
|
-
* theirs to leave out.
|
|
32
|
-
*
|
|
33
|
-
* **And the crumbs carry no marker for any of it.** CSS can already see it: the
|
|
34
|
-
* crumb with the menu in it is `:has(helix-menu)`, and the middle is
|
|
35
|
-
* `:not(:first-child, [current])`. A marker would be the design rule written
|
|
36
|
-
* twice, once in the HTML and once in the sheet, with nothing keeping the two
|
|
37
|
-
* honest.
|
|
18
|
+
* **The trail does not measure itself, and the overflow is markup.**
|
|
38
19
|
*
|
|
39
20
|
* @since 0.1.0
|
|
40
21
|
* @status stable
|
|
@@ -7,35 +7,16 @@ declare const HelixHeadlessChat_base: (new (...args: any[]) => import("@fusion.d
|
|
|
7
7
|
*
|
|
8
8
|
* @description
|
|
9
9
|
* **It has no opinion about where it sits.** A chat is asked for as a floating
|
|
10
|
-
* button, a page, a palette, a sidebar, a modal and an inline block, and none
|
|
11
|
-
* those is a variant of a conversation: they are containers
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* and nothing here owns a trigger, because a trigger opens a container and
|
|
16
|
-
* containers own their own. If a form factor needs a mode on this element, the
|
|
17
|
-
* split is wrong.
|
|
10
|
+
* button, a page, a palette, a sidebar, a modal and an inline block, and none
|
|
11
|
+
* of those is a variant of a conversation: they are containers, and the
|
|
12
|
+
* containers already exist. Nothing here is positioned, nothing has a backdrop,
|
|
13
|
+
* and nothing owns a trigger. If a form factor needs a mode on this element,
|
|
14
|
+
* the split is wrong.
|
|
18
15
|
*
|
|
19
|
-
* **Scroll ownership is the behavior everything else depends on
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* bottom, release the moment they leave it, and offer a way back. **Never scroll
|
|
24
|
-
* a reader who is reading.**
|
|
25
|
-
*
|
|
26
|
-
* The element owns it, because it is the only party that knows a message arrived
|
|
27
|
-
* and where the viewport was when it did. `pinned` announces before it moves, so
|
|
28
|
-
* a consumer who wants to own it refuses the request and assigns their own.
|
|
29
|
-
*
|
|
30
|
-
* **The live region announces on completion, not on arrival.** The transcript is
|
|
31
|
-
* `role="log"` with `aria-live="polite"`, and a streaming message carries
|
|
32
|
-
* `aria-busy` while it arrives: a screen reader reading a stream token by token
|
|
33
|
-
* is unusable, and holding the announcement until the flag clears is the only
|
|
34
|
-
* arrangement that works. Somebody typing is a separate status region, so it
|
|
35
|
-
* never interleaves with what was said.
|
|
36
|
-
*
|
|
37
|
-
* **Not a client.** No transport, no protocol, no model. It renders a
|
|
38
|
-
* conversation and reports what the reader did.
|
|
16
|
+
* **Scroll ownership is the behavior everything else depends on.** A transcript
|
|
17
|
+
* that grows wants to stay at the bottom, except when the reader has scrolled
|
|
18
|
+
* up to read, where pinning is hostile. The rule: pin while the reader is
|
|
19
|
+
* within `threshold` of the bottom, and let go the moment they leave it.
|
|
39
20
|
*
|
|
40
21
|
* @since 0.1.0
|
|
41
22
|
* @status experimental
|
|
@@ -9,38 +9,20 @@ declare const HelixHeadlessColorPicker_base: (new (...args: any[]) => import("@f
|
|
|
9
9
|
* @summary Picks a color, from a square and two ramps.
|
|
10
10
|
*
|
|
11
11
|
* @description
|
|
12
|
-
* **The picker itself, with no field around it.** Color Input is this element
|
|
13
|
-
* a panel behind a text field
|
|
14
|
-
*
|
|
15
|
-
* sitting flat on the page with nothing to open. Neither should be the other one
|
|
16
|
-
* with parts hidden.
|
|
12
|
+
* **The picker itself, with no field around it.** Color Input is this element
|
|
13
|
+
* in a panel behind a text field; a settings pane or a toolbar wants the picker
|
|
14
|
+
* flat on the page with nothing to open.
|
|
17
15
|
*
|
|
18
|
-
* **The square is two real range inputs.** Saturation and brightness are each
|
|
19
|
-
* `<input type="range">`, laid over the square and made invisible, and the
|
|
20
|
-
* handle is drawn from their values. The
|
|
21
|
-
*
|
|
22
|
-
* sliders that announce a name and a number. All of it is the platform's, and
|
|
23
|
-
* nothing here implements a key handler. There is no `role="application"`, which
|
|
24
|
-
* is the usual way this control is built and amounts to asking a screen reader
|
|
25
|
-
* to stop helping.
|
|
16
|
+
* **The square is two real range inputs.** Saturation and brightness are each
|
|
17
|
+
* an `<input type="range">`, laid over the square and made invisible, and the
|
|
18
|
+
* handle is drawn from their values. The keyboard, the announcements and the
|
|
19
|
+
* touch targets are the platform's, and there is no `role="application"`.
|
|
26
20
|
*
|
|
27
21
|
* **The working model is HSB, and the value is derived from it.** The reverse
|
|
28
22
|
* loses colors: black is `brightness: 0`, whose hue and saturation are both
|
|
29
|
-
* undefined, so
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* model once, which is the only moment the conversion runs in that direction.
|
|
33
|
-
*
|
|
34
|
-
* **Every syntax is read; the one `format` names is written.** `#f00`,
|
|
35
|
-
* `#ff0000`, `rgb(255 0 0)`, `rgba(255,0,0,.5)` and `hsl(0 100% 50%)` are all
|
|
36
|
-
* colors. Named colors are the exception, and deliberately: resolving
|
|
37
|
-
* `rebeccapurple` means shipping a table of a hundred and forty or asking the
|
|
38
|
-
* browser, and there is no browser during a server render.
|
|
39
|
-
*
|
|
40
|
-
* The properties below are written by this component and read by whoever draws
|
|
41
|
-
* it. A consumer sets none of them: they carry a number this element computed
|
|
42
|
-
* and CSS cannot, which is what lets a stylesheet on this tier draw the thing
|
|
43
|
-
* without measuring anything itself.
|
|
23
|
+
* undefined, so dragging to the bottom of the square and back would give gray
|
|
24
|
+
* where there had been red. A value arriving from outside is read into the
|
|
25
|
+
* model once.
|
|
44
26
|
*
|
|
45
27
|
* @since 0.1.0
|
|
46
28
|
* @status experimental
|
|
@@ -7,73 +7,31 @@ declare const HelixHeadlessCombobox_base: (new (...args: any[]) => import("@fusi
|
|
|
7
7
|
* @summary A field that filters a list as it is typed into.
|
|
8
8
|
*
|
|
9
9
|
* @description
|
|
10
|
-
* **The list is `helix-listbox`, and the filtering is its own.** It
|
|
11
|
-
*
|
|
12
|
-
* scrolling an option into view.
|
|
13
|
-
*
|
|
14
|
-
* traffic between them.
|
|
10
|
+
* **The list is `helix-listbox`, and the filtering is its own.** It owns
|
|
11
|
+
* narrowing, the empty state, the highlight, `aria-activedescendant` and
|
|
12
|
+
* scrolling an option into view. What is left for this element is the field,
|
|
13
|
+
* the panel and the traffic between them.
|
|
15
14
|
*
|
|
16
|
-
* **The text and the value are two things.** What a reader types is a filter
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* component exists.
|
|
22
|
-
*
|
|
23
|
-
* That is also why the field is emptied of its filter on close rather than left
|
|
24
|
-
* holding it: a control reopened still narrowed by the last visit's search is a
|
|
25
|
-
* control whose second use starts from the first one's leftovers. Select
|
|
26
|
-
* records the same.
|
|
15
|
+
* **The text and the value are two things.** What a reader types is a filter:
|
|
16
|
+
* it narrows the list and is thrown away. The value is whichever option they
|
|
17
|
+
* then choose, and the field is rewritten to that option's words once they
|
|
18
|
+
* have. The filter is cleared on close, so a reopened control does not start
|
|
19
|
+
* from the last visit's leftovers.
|
|
27
20
|
*
|
|
28
21
|
* **Focus stays in the field, which is the whole of what a combobox is.** The
|
|
29
|
-
* arrows move the list without leaving the text,
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* does them.
|
|
33
|
-
*
|
|
34
|
-
* `role="combobox"` on the input rather than on a wrapper, because the thing
|
|
35
|
-
* the pattern describes is the text field, and `aria-controls` can name the
|
|
36
|
-
* listbox because both are in this shadow root. The active option cannot be
|
|
37
|
-
* named that way, being the consumer's light DOM, so it is linked by element
|
|
38
|
-
* reference for the reason Listbox records at length.
|
|
39
|
-
*
|
|
40
|
-
* **With `multiple`, the chosen options are tags in the field.** A field that
|
|
41
|
-
* says "Rust, Ruby, Go" is a sentence the reader has to parse to find out what
|
|
42
|
-
* is in it, and offers nothing to press to take one out: Select does that today
|
|
43
|
-
* and it is the weakest part of it. A tag apiece is one object per answer, each
|
|
44
|
-
* with its own remove control, which is what the reader already believes they
|
|
45
|
-
* are looking at.
|
|
22
|
+
* arrows move the list without leaving the text, through the listbox's public
|
|
23
|
+
* `navigate`. `role="combobox"` is on the input rather than on a wrapper,
|
|
24
|
+
* because the thing the pattern describes is the text field.
|
|
46
25
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* filter clears on each choice
|
|
50
|
-
*
|
|
26
|
+
* **With `multiple`, the chosen options are tags in the field**, one object per
|
|
27
|
+
* answer, each with its own remove control. The panel stays open while more
|
|
28
|
+
* than one can be chosen and the filter clears on each choice, so the next pick
|
|
29
|
+
* starts from the whole list.
|
|
51
30
|
*
|
|
52
31
|
* **`creatable` announces, and does not write.** The options are the consumer's
|
|
53
32
|
* light DOM and nothing here puts an element into it: a component that grew the
|
|
54
|
-
* consumer's markup would be
|
|
55
|
-
*
|
|
56
|
-
* reader presses it, and say so with `helix-create`. A consumer who has to
|
|
57
|
-
* validate first refuses the event and adds nothing.
|
|
58
|
-
*
|
|
59
|
-
* Which leaves a value naming no option, and that is what the words fall back
|
|
60
|
-
* to: for anything chosen the field shows the option's text if there is one and
|
|
61
|
-
* the value itself otherwise. No copy is stored, so a consumer who then adds a
|
|
62
|
-
* real option with that value has its words take over on the next render.
|
|
63
|
-
*
|
|
64
|
-
* **`suggest` finishes the word, and only ever forwards.** The field completes
|
|
65
|
-
* to the first option the typing begins, with the part the reader did not type
|
|
66
|
-
* selected, so the next character replaces it and Delete removes it. Applied on
|
|
67
|
-
* insertion alone: completing after a deletion would put back what the reader
|
|
68
|
-
* just took out, and a field that cannot be shortened is a field that cannot be
|
|
69
|
-
* corrected. Measured as the difference between `insertText` and the several
|
|
70
|
-
* `delete*` input types, which is what the platform already says about which
|
|
71
|
-
* happened.
|
|
72
|
-
*
|
|
73
|
-
* The filter stays what was typed rather than what was completed. Filtering on
|
|
74
|
-
* the completion would narrow the list to the one option it came from, so a
|
|
75
|
-
* reader typing "ru" would be shown "Rust" alone and never learn Ruby was
|
|
76
|
-
* there.
|
|
33
|
+
* consumer's markup would be one their framework re-renders over. It offers the
|
|
34
|
+
* row and fires the event; adding the option is the consumer's.
|
|
77
35
|
*
|
|
78
36
|
* @since 0.1.0
|
|
79
37
|
* @status experimental
|
|
@@ -7,29 +7,14 @@ import '../../tooltip/index.js';
|
|
|
7
7
|
* @description
|
|
8
8
|
* **The confirmation is the component.** Writing to the clipboard is three
|
|
9
9
|
* lines; what makes this worth an element is that the platform gives a reader
|
|
10
|
-
* no feedback at all
|
|
11
|
-
* failed
|
|
12
|
-
* press is indistinguishable from the first.
|
|
10
|
+
* no feedback at all, so a press that copies looks identical to one that
|
|
11
|
+
* failed.
|
|
13
12
|
*
|
|
14
13
|
* **Announced after the write, never before.** A clipboard write is a
|
|
15
|
-
* permission the browser may refuse,
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `HelixCopyEvent` is not cancelable for the same reason: by the time there is
|
|
20
|
-
* anything to say, it has already happened.
|
|
21
|
-
*
|
|
22
|
-
* **An icon button, extending IconButton.** Both of this button's faces are
|
|
23
|
-
* icons and it never carries a word, so it is the square control rather than a
|
|
24
|
-
* text button that happens to hold a glyph: it inherits the square box, and
|
|
25
|
-
* with it the rule that an icon-only control must be named. `label` is not
|
|
26
|
-
* optional here for the same reason it is not optional there, and the two
|
|
27
|
-
* faces make it sharper: the accessible name has to describe the press, not
|
|
28
|
-
* whichever picture is showing when a reader arrives.
|
|
29
|
-
*
|
|
30
|
-
* `data-copied`, not a reflected `copied`. State never reflects here: a
|
|
31
|
-
* serialized `copied` attribute would come back from a paste as a button
|
|
32
|
-
* claiming to have copied something it never held.
|
|
14
|
+
* permission the browser may refuse, for real reasons: an insecure origin, a
|
|
15
|
+
* document that is not focused, a gesture that did not qualify. The
|
|
16
|
+
* confirmation and the event both wait for the promise, and a refusal leaves
|
|
17
|
+
* the button exactly as it was.
|
|
33
18
|
*
|
|
34
19
|
* @since 0.1.0
|
|
35
20
|
* @status experimental
|
|
@@ -9,10 +9,9 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
|
|
|
9
9
|
*
|
|
10
10
|
* @description
|
|
11
11
|
* **The schema is markup and the data is a property.** Columns are declared as
|
|
12
|
-
* `helix-table-column` children, because a schema is small
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* cannot window them.
|
|
12
|
+
* `helix-table-column` children, because a schema is small and something a
|
|
13
|
+
* reader should see in the HTML; rows are handed over as an array, because a
|
|
14
|
+
* table that draws ten thousand elements cannot window them.
|
|
16
15
|
*
|
|
17
16
|
* ```html
|
|
18
17
|
* <helix-data-table .rows=${rows} sort="name:asc" page-size="20">
|
|
@@ -25,35 +24,13 @@ declare const HelixHeadlessDataTable_base: (new (...args: any[]) => import("@fus
|
|
|
25
24
|
* twelve things a person wrote out.
|
|
26
25
|
*
|
|
27
26
|
* **Sort and filter apply to the whole set, never the page.** Sorting the drawn
|
|
28
|
-
* window is correct on the first screen and wrong on every other one
|
|
29
|
-
* how it survives being looked at. The arithmetic lives in `order.ts` where
|
|
30
|
-
* nothing can see a window, and its spec asserts the composition rather than the
|
|
31
|
-
* three steps, because each step alone passes either way.
|
|
27
|
+
* window is correct on the first screen and wrong on every other one.
|
|
32
28
|
*
|
|
33
|
-
* **
|
|
34
|
-
* do
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* a slow connection see a table of the right shape with nothing in it, which is
|
|
39
|
-
* strictly less than `helix-table` serves for a table whose rows are markup.
|
|
40
|
-
*
|
|
41
|
-
* That is the cost of "the schema is markup and the data is a property". Use
|
|
42
|
-
* `helix-table` where the content has to be in the served HTML and the result is
|
|
43
|
-
* small enough to write out; use this where the result is large, and accept that
|
|
44
|
-
* the first paint is a shell. The obvious workaround, a `columns` property
|
|
45
|
-
* beside the markup columns, is deliberately not offered: it splits one source
|
|
46
|
-
* of truth for the columns into two that nothing checks against each other, and
|
|
47
|
-
* a table rendering one way on the server and another in the browser is that
|
|
48
|
-
* failure with a hydration mismatch on top.
|
|
49
|
-
*
|
|
50
|
-
* **Every axis is controlled or uncontrolled, and that is what makes a server
|
|
51
|
-
* mode fall out rather than be bolted on.** `sort`, `filter`, `page` and
|
|
52
|
-
* `page-size` each defer to the consumer when set and seed themselves from a
|
|
53
|
-
* `default-*` when not. A consumer who answers all four holds the data flow:
|
|
54
|
-
* they hear the reader's request, fetch, and hand back a new `rows` with a
|
|
55
|
-
* `total`. Retrofitting that inverts the flow and is a rewrite, so it is here
|
|
56
|
-
* from the start.
|
|
29
|
+
* **The cell values do not server-render.** The headings do, and the row
|
|
30
|
+
* elements do, but reading a cell needs the columns, and the columns are markup
|
|
31
|
+
* children Lit SSR does not hand this element. So a crawler sees a table of the
|
|
32
|
+
* right shape with nothing in it. That is the cost of the first paragraph: use
|
|
33
|
+
* `helix-table` where the served HTML has to carry the values.
|
|
57
34
|
*
|
|
58
35
|
* @since 0.1.0
|
|
59
36
|
* @status experimental
|
|
@@ -9,29 +9,13 @@ declare const HelixHeadlessDateInput_base: (new (...args: any[]) => import("@fus
|
|
|
9
9
|
* @description
|
|
10
10
|
* **The calendar is `helix-date-picker`, not a second one written here.** It
|
|
11
11
|
* already knows about months, ranges, disabled days, the first day of the week
|
|
12
|
-
* and every keyboard rule a grid owes a reader
|
|
13
|
-
*
|
|
14
|
-
* element is the field, the overlay, and the translation between them.
|
|
12
|
+
* and every keyboard rule a grid owes a reader. This element is the field, the
|
|
13
|
+
* overlay, and the translation between them.
|
|
15
14
|
*
|
|
16
|
-
* **Typed as well as picked, and the typing is
|
|
15
|
+
* **Typed as well as picked, and the typing is why it is not
|
|
17
16
|
* `<input type="date">`.** The native control gives a free picker and a free
|
|
18
|
-
* locale and takes almost all styling with it
|
|
19
|
-
*
|
|
20
|
-
* the native one is the part worth keeping: a real text field, so a reader who
|
|
21
|
-
* knows the date types it and never opens anything.
|
|
22
|
-
*
|
|
23
|
-
* **The value is `YYYY-MM-DD`, always.** That is what a form submits, what
|
|
24
|
-
* `min` and `max` compare against, and what the calendar speaks. What the field
|
|
25
|
-
* *shows* is the same string: a locale-formatted display would have to be parsed
|
|
26
|
-
* back on every keystroke, and half-typed input does not survive a round trip
|
|
27
|
-
* through a formatter. The same reasoning the sandbox records for its two
|
|
28
|
-
* syntaxes, which is that a document being edited must not be a rendering of
|
|
29
|
-
* something else.
|
|
30
|
-
*
|
|
31
|
-
* **Opening does not commit.** The calendar announces a date, this element takes
|
|
32
|
-
* it, and the overlay shuts, because a single date has nothing more to say. A
|
|
33
|
-
* consumer who owns `value` answers the announcement the way they answer any
|
|
34
|
-
* other control here.
|
|
17
|
+
* locale and takes almost all styling with it. What is kept from it is the part
|
|
18
|
+
* worth keeping: a real text field.
|
|
35
19
|
*
|
|
36
20
|
* @since 0.1.0
|
|
37
21
|
* @status experimental
|
|
@@ -6,44 +6,19 @@ declare const HelixHeadlessDatePicker_base: (new (...args: any[]) => import("@fu
|
|
|
6
6
|
*
|
|
7
7
|
* @description
|
|
8
8
|
* A grid of days, per the APG date grid: the whole grid is one tab stop, arrows
|
|
9
|
-
* move by day and week, `PageUp` and `PageDown`
|
|
10
|
-
*
|
|
11
|
-
* sequence.
|
|
9
|
+
* move by day and week, `PageUp` and `PageDown` by month, `Home` and `End` to
|
|
10
|
+
* the ends of the week. Only the focused cell is in the tab sequence.
|
|
12
11
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* **Dates are `YYYY-MM-DD` strings and every calculation is in UTC.** A
|
|
13
|
+
* local-time `Date` shifts across a timezone or a daylight-saving boundary.
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* `month` is what the grid shows, separately from `value`, so paging does not
|
|
16
|
+
* select anything and a consumer can drive the view. Left unset it follows the
|
|
17
|
+
* first selected date, then the current month.
|
|
19
18
|
*
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* `mode` decides how many dates are held. Absent takes one. `range` takes two
|
|
25
|
-
* and marks everything between them, starting over on the third click rather
|
|
26
|
-
* than replacing an end at random. `multiple` toggles, so clicking a chosen
|
|
27
|
-
* date takes it out. All three read and write the same comma-separated
|
|
28
|
-
* `value`.
|
|
29
|
-
*
|
|
30
|
-
* `locale` is worth setting where the output is compared across environments.
|
|
31
|
-
* Left unset, month and day names come from the runtime's own locale, and a
|
|
32
|
-
* server and a browser need not agree on what that is.
|
|
33
|
-
*
|
|
34
|
-
* A control whose value changes dispatches `helix-before-change`:
|
|
35
|
-
* `Input`, `Select`, `Listbox`, `Radio.Group` and `Toggle.Group` all do, and a
|
|
36
|
-
* calendar submitting a date is the same kind of thing. `helix-select` stays
|
|
37
|
-
* what it is elsewhere, which is a row or an item being chosen in something
|
|
38
|
-
* that reports no value at all, such as `Menu` or `Tree`.
|
|
39
|
-
*
|
|
40
|
-
* Paging is `helix-navigate` and not either of those, because it chooses
|
|
41
|
-
* nothing: the view moves and the answer does not.
|
|
42
|
-
*
|
|
43
|
-
* The properties below are written by this component and read by whoever draws
|
|
44
|
-
* it. A consumer sets none of them: they carry a number this element computed
|
|
45
|
-
* and CSS cannot, which is what lets a stylesheet on this tier draw the thing
|
|
46
|
-
* without measuring anything itself.
|
|
19
|
+
* `mode` decides how many dates are held: absent takes one, `range` takes two
|
|
20
|
+
* and marks everything between them, `multiple` toggles. All three read and
|
|
21
|
+
* write the same comma-separated `value`.
|
|
47
22
|
*
|
|
48
23
|
* @since 0.1.0
|
|
49
24
|
* @status experimental
|
|
@@ -7,37 +7,15 @@ declare const HelixHeadlessDialog_base: (new (...args: any[]) => import("@fusion
|
|
|
7
7
|
* @description
|
|
8
8
|
* A real `<dialog>` opened with `showModal()`, not a div with a role and a
|
|
9
9
|
* hand-built trap. The platform gives four things that are each a day's work to
|
|
10
|
-
* reimplement
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* by pointer, keyboard, or a screen reader's virtual cursor.
|
|
10
|
+
* reimplement: the top layer, so the dialog is above everything whatever
|
|
11
|
+
* `z-index` anyone wrote; a real backdrop; Escape; and inert-behind, so the
|
|
12
|
+
* page underneath is unreachable by pointer, keyboard or virtual cursor.
|
|
14
13
|
*
|
|
15
14
|
* **`open` drives `showModal()`, and every way it can close comes back through
|
|
16
|
-
* `requestOpen`.** A native dialog
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* is intercepted and routed through the request, so `preventDefault()` on the
|
|
21
|
-
* `helix-before-close` refuses it; a close that happens anyway is reflected back.
|
|
22
|
-
*
|
|
23
|
-
* **Nothing here traps focus.** `showModal()` does it, and better: `inert` on
|
|
24
|
-
* the rest of the document is something no JavaScript trap can achieve.
|
|
25
|
-
* `FocusTrapController` stays for the surfaces that are not modal.
|
|
26
|
-
*
|
|
27
|
-
* The regions are bare slots rather than wrappers, for the reason the card's
|
|
28
|
-
* are: an unassigned `<slot>` is `display: contents` and generates no box, so a
|
|
29
|
-
* dialog with no footer has no empty band where one would be.
|
|
30
|
-
*
|
|
31
|
-
* Opened declaratively with the platform's invoker attributes:
|
|
32
|
-
*
|
|
33
|
-
* ```html
|
|
34
|
-
* <helix-button commandfor="confirm" command="--show">Delete…</helix-button>
|
|
35
|
-
* <helix-dialog id="confirm">…</helix-dialog>
|
|
36
|
-
* ```
|
|
37
|
-
*
|
|
38
|
-
* `--show`, `--close` and `--toggle`. Custom commands rather than the built-in
|
|
39
|
-
* `show-modal`, because those are defined against native elements and do
|
|
40
|
-
* nothing when pointed at a custom one.
|
|
15
|
+
* `requestOpen`.** A native dialog closes from three directions: the property,
|
|
16
|
+
* Escape, and a `<form method="dialog">` submit. Left alone, two of them would
|
|
17
|
+
* shut the dialog while the consumer's state still said open, which is the bug
|
|
18
|
+
* that makes a modal impossible to reopen.
|
|
41
19
|
*
|
|
42
20
|
* @since 0.1.0
|
|
43
21
|
* @status stable
|
|
@@ -9,32 +9,12 @@ declare const HelixHeadlessDonutChart_base: typeof HelixElement & (new (...args:
|
|
|
9
9
|
* The chart with the narrowest honest use, so the component is narrow too.
|
|
10
10
|
*
|
|
11
11
|
* **It reads one column and treats the rows as the parts.** Every other chart
|
|
12
|
-
* here takes a column per series; a ring has one whole to divide,
|
|
13
|
-
*
|
|
14
|
-
* way. So the first column of numbers is the series and each row is a slice.
|
|
12
|
+
* here takes a column per series; a ring has one whole to divide, so the first
|
|
13
|
+
* column of numbers is the series and each row is a slice.
|
|
15
14
|
*
|
|
16
|
-
* **It refuses negative values rather than drawing them.**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* and the total is of what is left, which is the only reading that stays true.
|
|
20
|
-
*
|
|
21
|
-
* **The shape is drawn with a dashed stroke, not with arc commands.** One
|
|
22
|
-
* `<circle>` per slice, its dash set to the slice's share of the circumference
|
|
23
|
-
* and its offset to where the slice begins. Arc paths need the sweep flag and a
|
|
24
|
-
* large-arc flag, both of which flip at values a reader never sees coming, and
|
|
25
|
-
* a slice of exactly one whole becomes a path with no length at all.
|
|
26
|
-
*
|
|
27
|
-
* **And it keeps its aspect ratio**, alone among the charts here. The others
|
|
28
|
-
* set `preserveAspectRatio="none"` so one drawing fills any box and rescue
|
|
29
|
-
* their strokes with `vector-effect`; that rescues strokes and not geometry, so
|
|
30
|
-
* a ring drawn the same way is an ellipse in anything but a square.
|
|
31
|
-
*
|
|
32
|
-
* The table is the accessible representation and this drawing is `aria-hidden`.
|
|
33
|
-
*
|
|
34
|
-
* The properties below are written by this component and read by whoever draws
|
|
35
|
-
* it. A consumer sets none of them: they carry a number this element computed
|
|
36
|
-
* and CSS cannot, which is what lets a stylesheet on this tier draw the thing
|
|
37
|
-
* without measuring anything itself.
|
|
15
|
+
* **It refuses negative values rather than drawing them.** An arc for a
|
|
16
|
+
* negative number either runs backwards over its neighbors or is silently
|
|
17
|
+
* dropped. Those rows are skipped and the total is of what is left.
|
|
38
18
|
*
|
|
39
19
|
* @since 0.1.0
|
|
40
20
|
* @status experimental
|