@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.
Files changed (44) hide show
  1. package/custom-elements.json +39 -39
  2. package/dist/app-header/root/app-header-root.d.ts +8 -28
  3. package/dist/avatar/group/avatar-group.d.ts +5 -30
  4. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -28
  5. package/dist/chat/root/chat-root.d.ts +9 -28
  6. package/dist/color-picker/root/color-picker-root.d.ts +10 -28
  7. package/dist/combobox/root/combobox-root.d.ts +18 -60
  8. package/dist/copy-button/root/copy-button-root.d.ts +6 -21
  9. package/dist/data-table/root/data-table-root.d.ts +9 -32
  10. package/dist/date-input/root/date-input-root.d.ts +5 -21
  11. package/dist/date-picker/root/date-picker-root.d.ts +10 -35
  12. package/dist/dialog/root/dialog-root.d.ts +7 -29
  13. package/dist/donut-chart/root/donut-chart-root.d.ts +5 -25
  14. package/dist/file-input/root/file-input-root.d.ts +5 -22
  15. package/dist/heatmap/root/heatmap-root.d.ts +13 -36
  16. package/dist/image/root/image-root.d.ts +5 -22
  17. package/dist/input/root/input-root.d.ts +11 -29
  18. package/dist/kbd/root/kbd-root.d.ts +8 -26
  19. package/dist/legend/root/legend-root.d.ts +6 -24
  20. package/dist/listbox/root/listbox-root.d.ts +4 -26
  21. package/dist/megamenu/root/megamenu-root.d.ts +7 -39
  22. package/dist/meter/root/meter-root.d.ts +6 -24
  23. package/dist/number-input/root/number-input-root.d.ts +11 -20
  24. package/dist/outline/root/outline-root.d.ts +8 -27
  25. package/dist/pagination/root/pagination-root.d.ts +21 -54
  26. package/dist/pagination/summary/pagination-summary.d.ts +10 -31
  27. package/dist/progress/root/progress-root.d.ts +4 -21
  28. package/dist/rating/root/rating-root.d.ts +8 -31
  29. package/dist/slider/root/slider-root.d.ts +9 -32
  30. package/dist/splitter/root/splitter-root.d.ts +17 -85
  31. package/dist/steps/root/steps-root.d.ts +8 -26
  32. package/dist/steps/step/step.d.ts +11 -41
  33. package/dist/toast/item/toast-item.d.ts +6 -30
  34. package/dist/toggle/group/toggle-group.d.ts +7 -25
  35. package/dist/tooltip/root/tooltip-root.d.ts +14 -36
  36. package/dist/tour/root/tour-root.d.ts +6 -29
  37. package/dist/tree/item/tree-item.d.ts +13 -35
  38. package/dist/video/root/video-root.d.ts +12 -40
  39. package/dist/video-playlist/menu/video-playlist-menu.d.ts +3 -25
  40. package/dist/virtual-list/root/virtual-list-root.d.ts +5 -44
  41. package/package.json +17 -18
  42. package/{jsx.d.ts → types/jsx.d.ts} +724 -2537
  43. package/{vue.d.ts → types/vue.d.ts} +39 -39
  44. /package/{svelte.d.ts → types/svelte.d.ts} +0 -0
@@ -11,36 +11,13 @@ import type { HelixHeadlessTourStop } from '../stop/tour-stop.js';
11
11
  *
12
12
  * **The spotlight is a shadow, not a hole.** The scrim is a box the size of the
13
13
  * current target with an enormous spread on its `box-shadow`, so the darkness
14
- * is what the shadow paints and the target is simply where the box is. A
15
- * `clip-path` on a full-screen scrim is the other way to do it and is worse
16
- * here: it needs the viewport in the same coordinate space as the cut, which
17
- * stops being true the moment anything between them is transformed.
14
+ * is what the shadow paints. A `clip-path` on a full-screen scrim needs the
15
+ * viewport in the same coordinate space as the cut, which stops being true the
16
+ * moment anything between them is transformed.
18
17
  *
19
- * **It measures, and this is a case where measuring is the only option.** The
20
- * target is an element somewhere else on the page whose position nothing can
21
- * know statically. That is different from a trail folding by width, where the
22
- * design rule was expressible and the measurement was answering a question
23
- * nobody had asked. Here the question is "where is that button", and there is
24
- * no other way to ask it.
25
- *
26
- * **Nothing is measured on the server and nothing needs to be.** A tour starts
27
- * closed and a closed tour draws nothing, so the server renders the stops as
28
- * inert markup and the first frame is correct. This is also what makes it
29
- * acceptable for the root to write `current` onto stops it does not own: there
30
- * is no server state for the write to disagree with.
31
- *
32
- * **It cannot point inside a modal, and nothing can fix that here.**
33
- * `showModal()` makes everything outside the dialog's own subtree inert, so a
34
- * panel anywhere else is unpressable however the top layer is ordered: this is
35
- * what modal means rather than a stacking accident. Measured with a modal
36
- * `Drawer` open, `elementsFromPoint` over the tour's own Done button returned
37
- * the drawer, the press light-dismissed the drawer, and the tour heard nothing
38
- * at all. Anything a tour points into has to stay reachable, which for `Drawer`
39
- * and `Dialog` means not modal while the tour is running.
40
- *
41
- * **Focus is not trapped.** A reader has to be able to look at, and reach, the
42
- * thing being pointed at. `Escape` leaves, which is the one binding a tour
43
- * needs and the one every reader already tries.
18
+ * **It measures, and the target is an element elsewhere on the page whose
19
+ * position nothing can know statically.** Nothing is measured on the server and
20
+ * nothing needs to be: a tour starts when a reader asks for it.
44
21
  *
45
22
  * @since 0.1.0
46
23
  * @status experimental
@@ -5,45 +5,23 @@ declare const HelixHeadlessTreeItem_base: (new (...args: any[]) => import("@fusi
5
5
  * @summary One node in a tree.
6
6
  *
7
7
  * @description
8
- * **`open`, on `MixinControllerOpen`, rather than a bare boolean of its own.** A
9
- * branch showing its children is the same state a disclosure holds, and
10
- * `AccordionItem` is on that mixin too. A hand-written toggle announces nothing,
11
- * so a consumer can neither know a branch opened nor own the state.
12
- * `aria-expanded` is still what the row renders, because that is the attribute a
13
- * tree uses; the property is what differs, not the semantics.
8
+ * **`open`, on `MixinControllerOpen`, rather than a bare boolean of its own.**
9
+ * A branch showing its children is the same state a disclosure holds. A
10
+ * hand-written toggle announces nothing, so a consumer could neither know a
11
+ * branch opened nor own the state. `aria-expanded` is still what the row
12
+ * renders: the property differs, not the semantics.
14
13
  *
15
- * An element rather than a slot because it owns state: whether it is open,
16
- * whether it is selected, and whether it is the one holding focus.
14
+ * An element rather than a slot, because it owns state: whether it is open,
15
+ * selected, and whether it holds focus.
17
16
  *
18
17
  * A branch is any item with something slotted into `children`, detected in
19
- * `firstUpdated` rather than declared by the consumer. That is a client-side
20
- * enhancement by necessity, since the server cannot inspect children, so the
21
- * server renders the node as a leaf and the twisty appears on hydration. It
22
- * appears rather than disappearing, which is the harmless direction.
18
+ * `firstUpdated` because the server cannot inspect children. The server renders
19
+ * the node as a leaf and the twisty appears on hydration, which is the harmless
20
+ * direction.
23
21
  *
24
- * **`href` makes the row itself the link.** A tree used as navigation is a list
25
- * of links, and the row is what a reader presses: with the anchor slotted into
26
- * the name instead, everything outside the words is dead, Enter does nothing,
27
- * and there is no middle-click, no status bar and no open-in-new-tab. Given an
28
- * `href` the row *is* an `<a>`, so the platform provides all of that and the
29
- * keyboard needs no handler at all.
30
- *
31
- * It changes the shape of the ARIA, which is why it is a property and not the
32
- * default. An anchor cannot contain the group, since the group holds more
33
- * anchors and nesting them is invalid, so the row carries `treeitem` and the
34
- * group becomes its sibling under a presentational wrapper. That is exactly the
35
- * navigation flavor of the tree pattern, and the arrangement without an `href`
36
- * stays as it was: `treeitem` containing `group`, which is what an `<li>`
37
- * holding a `<ul>` is.
38
- *
39
- * A consumer whose framework owns routing slots its own link instead and leaves
40
- * `href` unset. The row follows that one too, so the whole row works either
41
- * way; what the anchor adds is what only a real anchor can give.
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.
22
+ * **`href` makes the row itself the link.** With the anchor slotted into the
23
+ * name instead, everything outside the words is dead: no Enter, no middle
24
+ * click, no status bar. Given an `href` the row *is* an `<a>`.
47
25
  *
48
26
  * @since 0.1.0
49
27
  * @status experimental
@@ -7,57 +7,29 @@ import '../../menu/index.js';
7
7
  *
8
8
  * @description
9
9
  * **The picture is a real `<video>`.** Decoding, buffering, captions, Picture
10
- * in Picture, AirPlay and the codecs a device happens to have are all the
11
- * platform's, and none of them can be had from anything else. What is replaced
12
- * is the control bar, which is the one part of a video element that cannot be
13
- * styled and looks different in every browser.
10
+ * in Picture, AirPlay and whatever codecs a device has are the platform's. What
11
+ * is replaced is the control bar, which is the one part that cannot be styled.
14
12
  *
15
13
  * **Every control is a real control.** The scrubber and the volume are
16
- * `<input type="range">`, so a reader gets arrow keys, Home and End, the
17
- * platform's own touch target and its screen reader announcement without this
18
- * element implementing any of it. Drawing a div and listening for pointer moves
19
- * is how a media player ends up unusable from a keyboard.
14
+ * `<input type="range">`, so arrow keys, Home and End, the platform's touch
15
+ * target and its screen reader announcement all come for free.
20
16
  *
21
17
  * **Native controls are the fallback, not the default.** Where a consumer sets
22
- * `controls`, the browser's bar is used and this one is not drawn: two sets of
23
- * controls over one video is worse than either alone, and a consumer who asked
24
- * for the native bar has a reason.
18
+ * `controls` the browser's bar is used and this one is not drawn, and the keys
19
+ * below are not bound: two handlers on one press is a video that seeks twice.
25
20
  *
26
21
  * The keys are the ones every player uses: Space and `k` to play or pause, the
27
- * arrows to seek and to change the volume, `m` to mute, `f` for fullscreen.
28
- * They are bound on this element rather than on the document, because a video
29
- * is not a palette: keys belong to it only while a reader is on it. Where
30
- * `controls` is set they are not bound at all, since two handlers on one press
31
- * is a video that seeks twice, and the focus stop goes with them.
22
+ * arrows to seek and to change volume, `m` to mute, `f` for fullscreen. They
23
+ * are bound on this element rather than on the document, because a video is not
24
+ * a palette.
32
25
  *
33
26
  * **`<source>` and `<track>` are copied into the video, never slotted into it.**
34
27
  * A media element runs resource selection over its own child *nodes*, and a slot
35
28
  * is one node, so slotted sources reach it as nothing: no text tracks, no
36
29
  * current source, `readyState` zero. A video given sources that way does not
37
- * merely lack captions, it does not play.
38
- *
39
- * They are copied rather than moved, because the consumer's light DOM is theirs
40
- * and a framework that re-renders it would find its children gone. The copies
41
- * are rebuilt whenever the slot changes and the video is asked to load again,
42
- * which is what makes a `<track>` added later take effect.
43
- *
44
- * **The lock screen and a headset's buttons work, and one player owns them.**
45
- * There is one `navigator.mediaSession` per page, so the rule is stated rather
46
- * than left to render order: whichever player was started last owns it, which
47
- * is what a reader means by "this one" when they press play on their
48
- * headphones. The title comes from `label` and the artwork from `poster`.
49
- *
50
- * **`remote` sends the picture somewhere else.** The Remote Playback API is
51
- * what Safari implements for AirPlay and Chrome for Cast, so one control covers
52
- * both and the platform supplies the device list. It is offered only once
53
- * something has answered `watchAvailability`: a button that opens an empty list
54
- * is worse than no button, and a consumer who wants none can set
55
- * `disableremoteplayback` on this element.
56
- *
57
- * The properties below are written by this component and read by whoever draws
58
- * it. A consumer sets none of them: they carry a number this element computed
59
- * and CSS cannot, which is what lets a stylesheet on this tier draw the thing
60
- * without measuring anything itself.
30
+ * merely lack captions, it does not play. They are copied rather than moved,
31
+ * because the consumer's light DOM is theirs, and rebuilt whenever the slot
32
+ * changes.
61
33
  *
62
34
  * @since 0.1.0
63
35
  * @status experimental
@@ -7,35 +7,13 @@ import '../../menu/index.js';
7
7
  * @description
8
8
  * The entries again, reachable without leaving the picture. A playlist beside
9
9
  * the player is a list a reader scrolls to; the same list in the control bar is
10
- * one they open over the video, which is what a full-screen player has and what
11
- * a phone has room for.
10
+ * one they open over the video.
12
11
  *
13
12
  * **It is placed by the consumer, not rendered by the playlist**, and that is
14
- * forced rather than chosen. A shadow root cannot project into a sibling's
13
+ * forced rather than chosen: a shadow root cannot project into a sibling's
15
14
  * light DOM, so nothing the playlist renders can land inside the player's
16
15
  * control bar. What can is an element the consumer writes there, which is what
17
- * `Video`'s `controls` slot is for:
18
- *
19
- * ```html
20
- * <helix-video-playlist>
21
- * <helix-video slot="player" controls>
22
- * <helix-video-playlist-menu slot="controls"></helix-video-playlist-menu>
23
- * </helix-video>
24
- * <helix-video-playlist-item value="one" src="/one.mp4">One</helix-video-playlist-item>
25
- * </helix-video-playlist>
26
- * ```
27
- *
28
- * So it finds its playlist upward rather than being handed one, and that chain
29
- * crosses a shadow boundary in the ordinary arrangement, which is why it is
30
- * `closestInstance` and not `closest`.
31
- *
32
- * **Choosing dispatches `helix-select`, the same event an entry dispatches.**
33
- * It never writes `active` on the playlist, which looks like the shorter path
34
- * and skips the cancelable request: an assignment applies the change with no
35
- * `helix-before-activate` for anyone to refuse. Announcing is what an entry
36
- * does, the playlist already listens for it, and a consumer refusing a
37
- * selection gets `preventDefault()` on one event rather than two paths that
38
- * behave differently.
16
+ * `Video`'s `controls` slot is for.
39
17
  *
40
18
  * @since 0.1.0
41
19
  * @status experimental
@@ -10,58 +10,19 @@ import { type PropertyValues, type TemplateResult } from 'lit';
10
10
  * `<template>` child says what one row looks like: elements carrying
11
11
  * `data-field="name"` are filled with that field of the item.
12
12
  *
13
- * Both halves are markup, and that is the whole reason for the shape.
14
- * `VirtualizeController` says why the obvious alternatives are shut: an `items`
13
+ * Both halves are markup, and that is the reason for the shape. An `items`
15
14
  * property cannot survive React hydration, which skips every property it would
16
15
  * have to apply imperatively, and light-DOM children exist by the time anything
17
- * could decide not to create them. Text in the document is neither. A server
18
- * emits it, React re-emits it as a child rather than applying it, and it is one
19
- * node whatever the row count.
16
+ * could decide not to create them. Text in the document is neither, and it is
17
+ * one node whatever the row count.
20
18
  *
21
19
  * **The accessible version is not a `<table>`**, the way it is for the charts
22
20
  * that share this `for="<id>"` shape: ten thousand rows in one is the cost this
23
- * exists to avoid. It is the rendered window plus `aria-setsize` and
24
- * `aria-posinset`, which is what says "four thousand of ten thousand" rather
25
- * than "four of twenty".
21
+ * exists to avoid. It is the rendered window plus `aria-setsize`.
26
22
  *
27
- * **Rows are light DOM, deliberately.** They are cloned from the consumer's
28
- * template and appended to the host, so a document rule reaches them, the base
29
- * layer applies, and a class in the template means what it says. Cloned into
30
- * the shadow root they would be markup the consumer wrote and could not style,
31
- * which is the one thing a row template must not be.
32
- *
33
- * **Reading the source is client-only**, because a server has no document to
34
- * query. The window renders empty from the server and fills on the first update,
35
- * which is the same client-side enhancement rule every tree-reading component
36
- * here follows.
37
- *
38
- * **On this tier the consumer gives the viewport its box**, and the component
39
- * cannot do it for them: `helix/headless` ships no stylesheet, and a height is a
40
- * decision rather than a fact this could publish. So a base list wants two rules
41
- * before it is a list at all:
42
- *
43
- * ```css
44
- * helix-headless-virtual-list::part(viewport) {
45
- * block-size: 12rem;
46
- * overflow-y: auto;
47
- * }
48
- * ```
49
- *
50
- * **Without them it is not a scroller**, and the failure is worse than it
51
- * sounds. `scrollTop` is always zero, so nothing ever windows; and the box has
52
- * no height of its own, so `clientHeight` is a function of the rows just drawn.
53
- * Measuring then moves what is being measured, and the two need not converge:
54
- * measured in Chromium at 500 rows, it settled into a two-cycle between 378 and
55
- * 379 rows and stayed there, at two and a half thousand passes in eight seconds.
56
- * `VirtualizeController` gives up after ten passes and says so, so this now
57
- * draws a bad window rather than stopping the page, but a bad window is still
58
- * the answer until the rules are written.
59
- *
60
- * The styled tier writes them for you, with `--virtual-list-block-size`.
61
- *
62
- * @category layout
63
23
  * @since 0.1.0
64
24
  * @status experimental
25
+ * @category layout
65
26
  * @documentation https://helix.fusion.dev/components/virtual-list
66
27
  *
67
28
  * @csspart after - The space standing in for rows below the window.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-headless",
3
3
  "description": "Headless component tier. Structure, accessibility, state, and form participation. No styling.",
4
- "version": "0.4.2",
4
+ "version": "0.4.3",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -21,41 +21,40 @@
21
21
  "types": "./dist/*/index.d.ts",
22
22
  "default": "./dist/*/index.js"
23
23
  },
24
- "./jsx": "./jsx.d.ts",
25
- "./svelte": "./svelte.d.ts",
26
- "./vue": "./vue.d.ts"
24
+ "./custom-elements.json": "./custom-elements.json",
25
+ "./jsx": "./types/jsx.d.ts",
26
+ "./svelte": "./types/svelte.d.ts",
27
+ "./vue": "./types/vue.d.ts"
27
28
  },
28
29
  "files": [
29
30
  "custom-elements.json",
30
31
  "dist",
31
- "jsx.d.ts",
32
- "svelte.d.ts",
33
- "vue.d.ts"
32
+ "types"
34
33
  ],
35
34
  "customElements": "custom-elements.json",
36
- "sideEffects": [
37
- "./dist/**/*.js"
38
- ],
35
+ "sideEffects": true,
39
36
  "scripts": {
40
- "analyze": "cem analyze --config custom-elements-manifest.config.ts",
41
- "build": "tsc -p tsconfig.json && tsc -p tsconfig.types.json",
42
- "test": "bun run test:pure && bun run test:dom && bun run test:ssr",
37
+ "analyze": "bun --bun cem analyze --config cem.config.ts",
38
+ "build": "bun run element-events && tsc -p tsconfig.build.json && tsc -p tsconfig.types.json",
39
+ "element-events": "build-element-events --package headless",
40
+ "test": "bun run element-events && bun run test:pure && bun run test:dom && bun run test:ssr",
43
41
  "test:dom": "bun test dom.spec",
44
42
  "test:pure": "bun test pure.spec",
45
43
  "test:ssr": "bun test ssr.spec",
46
- "typecheck": "tsc -p tsconfig.json --noEmit"
44
+ "typecheck": "bun run element-events && tsc -p tsconfig.json --noEmit"
47
45
  },
48
46
  "bugs": "https://github.com/project-fusion/fusion/issues",
49
47
  "dependencies": {
50
- "@fusion.dev/helix-core": "0.4.2",
51
- "@fusion.dev/helix-icons": "0.4.2",
48
+ "@fusion.dev/helix-core": "0.4.3",
49
+ "@fusion.dev/helix-icons": "0.4.3",
52
50
  "uqr": "^0.1.3"
53
51
  },
54
52
  "devDependencies": {
55
53
  "@custom-elements-manifest/analyzer": "^0.11.0",
54
+ "@fusion.dev/helix-cem": "0.4.3",
55
+ "@fusion.dev/helix-testing": "0.4.3",
56
+ "@fusion.dev/scripts": "0.0.0-dev",
56
57
  "@happy-dom/global-registrator": "^20.0.0",
57
- "@fusion.dev/helix-cem": "0.4.2",
58
- "@fusion.dev/helix-testing": "0.4.2",
59
58
  "@lit-labs/ssr": "^3.3.0",
60
59
  "@types/bun": "^1.3.0",
61
60
  "lit": "^3.2.1"