@fusion.dev/helix-headless 0.99.0 → 0.101.0

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.
@@ -20,7 +20,7 @@ declare const HelixHeadlessDialog_base: (new (...args: any[]) => import("@fusion
20
20
  * @since 0.1.0
21
21
  * @status stable
22
22
  * @category overlays
23
- * @aliases Modal, Lightbox, Sheet
23
+ * @aliases Modal, Sheet
24
24
  * @documentation https://fusion.dev/helix/components/dialog
25
25
  *
26
26
  * @slot - The dialog's content.
@@ -76,6 +76,14 @@ registerElementEvents({
76
76
  'helix-headless-image': ['error', 'load'],
77
77
  'helix-headless-input': ['change', 'helix-before-change', 'input'],
78
78
  'helix-headless-kbd': ['helix-shortcut'],
79
+ 'helix-headless-lightbox': [
80
+ 'close',
81
+ 'helix-activate',
82
+ 'helix-before-activate',
83
+ 'helix-before-close',
84
+ 'helix-before-open',
85
+ 'toggle',
86
+ ],
79
87
  'helix-headless-listbox': ['change', 'helix-before-change', 'helix-filter', 'input'],
80
88
  'helix-headless-megamenu': ['helix-before-close', 'helix-before-open', 'toggle'],
81
89
  'helix-headless-menu': ['helix-before-close', 'helix-before-open', 'helix-select', 'toggle'],
package/dist/index.d.ts CHANGED
@@ -53,6 +53,7 @@ export * from './image/index.js';
53
53
  export * from './input/index.js';
54
54
  export * from './kbd/index.js';
55
55
  export * from './legend/index.js';
56
+ export * from './lightbox/index.js';
56
57
  export * from './line-chart/index.js';
57
58
  export * from './list/index.js';
58
59
  export * from './listbox/index.js';
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ export * from './image/index.js';
44
44
  export * from './input/index.js';
45
45
  export * from './kbd/index.js';
46
46
  export * from './legend/index.js';
47
+ export * from './lightbox/index.js';
47
48
  export * from './line-chart/index.js';
48
49
  export * from './list/index.js';
49
50
  export * from './listbox/index.js';
@@ -0,0 +1,2 @@
1
+ export { HelixHeadlessLightboxItem } from './item/lightbox-item.js';
2
+ export { HelixHeadlessLightbox } from './root/lightbox-root.js';
@@ -0,0 +1,2 @@
1
+ export { HelixHeadlessLightboxItem } from './item/lightbox-item.js';
2
+ export { HelixHeadlessLightbox } from './root/lightbox-root.js';
@@ -0,0 +1,60 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
+ /**
4
+ * @summary One thing a lightbox can show.
5
+ *
6
+ * @description
7
+ * A wrapper around whatever the consumer opened, which is the whole of what it
8
+ * is: a picture, a frame of video, a chart. The family shows one at a time and
9
+ * this is how it says which, so the content stays the consumer's and the
10
+ * arithmetic stays the root's.
11
+ *
12
+ * Hidden rather than unmounted while another item is current, for the reason
13
+ * `TabPanel` gives: what is in the markup is what a crawler reads and what a
14
+ * server sent, and an `<img>` that has already decoded does not decode again
15
+ * when the reader walks back to it.
16
+ *
17
+ * @since 0.100.0
18
+ * @status stable
19
+ * @category media
20
+ * @aliases LightboxSlide, GalleryItem
21
+ * @documentation https://fusion.dev/helix/components/lightbox
22
+ *
23
+ * @slot - What is being shown.
24
+ * @slot description - A line under it saying what it is.
25
+ *
26
+ * @csspart item - The box the content is shown in.
27
+ *
28
+ * @cssstate current - While this is the item the lightbox is showing.
29
+ */
30
+ export declare class HelixHeadlessLightboxItem extends HelixElement {
31
+ /**
32
+ * Whether this is the one being shown.
33
+ *
34
+ * Written by the root, which is the only thing that sees the set.
35
+ */
36
+ current: boolean;
37
+ /**
38
+ * What this item is called, for the reading that says where the reader is.
39
+ */
40
+ label: string;
41
+ /**
42
+ * What names this item, and what `active` on the root is compared against.
43
+ */
44
+ value: string;
45
+ /**
46
+ * Mirrors the shown state where a stylesheet can select on it.
47
+ *
48
+ * A custom state and never a reflected attribute, for the reason `TabPanel`
49
+ * writes out at length: the parent writes this, so reflecting it would put
50
+ * the element's own bookkeeping into markup a consumer never sent, which is a
51
+ * hydration mismatch in React and a choice nobody made in a serialized tree.
52
+ */
53
+ protected willUpdate(changed: PropertyValues): void;
54
+ protected render(): TemplateResult;
55
+ }
56
+ declare global {
57
+ interface HTMLElementTagNameMap {
58
+ 'helix-headless-lightbox-item': HelixHeadlessLightboxItem;
59
+ }
60
+ }
@@ -0,0 +1,45 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { customElement, HelixElement } from '@fusion.dev/helix-core';
8
+ import { html, nothing } from 'lit';
9
+ import { property } from 'lit/decorators.js';
10
+ let HelixHeadlessLightboxItem = class HelixHeadlessLightboxItem extends HelixElement {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.current = false;
14
+ this.label = '';
15
+ this.value = '';
16
+ }
17
+ willUpdate(changed) {
18
+ super.willUpdate(changed);
19
+ this.toggleState('current', this.current);
20
+ }
21
+ render() {
22
+ return html `<div
23
+ part="item"
24
+ role="group"
25
+ aria-label=${this.label || nothing}
26
+ ?hidden=${!this.current}
27
+ >
28
+ <slot></slot>
29
+ <slot name="description"></slot>
30
+ </div>`;
31
+ }
32
+ };
33
+ __decorate([
34
+ property({ type: Boolean })
35
+ ], HelixHeadlessLightboxItem.prototype, "current", void 0);
36
+ __decorate([
37
+ property()
38
+ ], HelixHeadlessLightboxItem.prototype, "label", void 0);
39
+ __decorate([
40
+ property()
41
+ ], HelixHeadlessLightboxItem.prototype, "value", void 0);
42
+ HelixHeadlessLightboxItem = __decorate([
43
+ customElement('helix-headless-lightbox-item')
44
+ ], HelixHeadlessLightboxItem);
45
+ export { HelixHeadlessLightboxItem };
@@ -0,0 +1,176 @@
1
+ import { CommandController, HelixElement } from '@fusion.dev/helix-core';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
+ declare const HelixHeadlessLightbox_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerActiveInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & typeof HelixElement;
4
+ /**
5
+ * @summary A viewer that opens one thing over the page.
6
+ *
7
+ * @description
8
+ * **A lightbox is chrome around a picture rather than a picture.** It draws
9
+ * nothing of its own: what it shows is whatever the consumer opened, so this
10
+ * earns its place on the frame, the keyboard and the reading that says where in
11
+ * the set the reader is.
12
+ *
13
+ * **The modal half is the platform's.** A real `<dialog>` opened with
14
+ * `showModal()` brings the top layer, a backdrop, Escape and `inert` behind it,
15
+ * and this repository has already removed one hand-written focus trap for being
16
+ * worse than that: `inert` on the rest of the document is something no
17
+ * JavaScript trap can achieve. Every way it can close comes back through
18
+ * `requestOpen`, which is what `Dialog` and `Drawer` both do and for the reason
19
+ * they both give.
20
+ *
21
+ * **The set is this component's own and not `Carousel`'s.** A carousel is a
22
+ * track: a scroller with snap points, per-view arithmetic and stops it can rest
23
+ * between. A lightbox shows one thing at a time and has no track, so what it
24
+ * needs is which item is current, which is `active` and the same pair `Tabs`
25
+ * uses. The arrow keys move the picture under a reader whose focus stays in the
26
+ * dialog, which is also why `KeyboardNavigationController` is not the right
27
+ * tool: that one moves focus between focusable items, and these are pictures.
28
+ *
29
+ * **Chrome hides while the reader is still**, on `IdleController`, and comes
30
+ * back on a pointer move or a focus. `fullscreen` is `FullscreenController`, the
31
+ * same pair `Video` holds and the only two it shares.
32
+ *
33
+ * @since 0.100.0
34
+ * @status stable
35
+ * @category media
36
+ * @aliases Photo viewer, Image viewer, Gallery overlay
37
+ * @documentation https://fusion.dev/helix/components/lightbox
38
+ *
39
+ * @slot - The items, which are `<helix-headless-lightbox-item>` elements.
40
+ * @slot footer - Chrome below the item, such as a counter or a thumbnail strip.
41
+ * @slot header - Chrome above the item, such as a title and a close button.
42
+ *
43
+ * @event {Event} close - Relayed, because the platform's own does not escape.
44
+ * @event {HelixActivateEvent} helix-activate - Fired once the shown item has moved, so the element already holds it.
45
+ * @event {HelixBeforeActivateEvent} helix-before-activate - Fired before the shown item changes, and cancelable. The only way to refuse one.
46
+ * @event {HelixBeforeCloseEvent} helix-before-close - Fired before it closes, and cancelable.
47
+ * @event {HelixBeforeOpenEvent} helix-before-open - Fired before it opens, and cancelable.
48
+ * @event {Event} toggle - Says the state moved, carrying `newState` and `oldState`.
49
+ *
50
+ * @csspart dialog - The native dialog element.
51
+ * @csspart live - The region that says which item is shown.
52
+ * @csspart viewport - The box the current item is shown in.
53
+ *
54
+ * @cssstate fullscreen - While the viewer fills the screen.
55
+ * @cssstate idle - While the reader has been still long enough to hide the chrome.
56
+ * @cssstate open - While the viewer is open.
57
+ */
58
+ export declare class HelixHeadlessLightbox extends HelixHeadlessLightbox_base {
59
+ /**
60
+ * How long the reader may be still before the chrome hides, in milliseconds.
61
+ *
62
+ * Zero keeps it up, which is what a viewer with a caption worth reading
63
+ * wants. The wait is only ever about idling: whether anything else should
64
+ * keep the chrome up is the consumer's to decide with this.
65
+ */
66
+ idleDelay: number;
67
+ /**
68
+ * Accessible name for the viewer.
69
+ */
70
+ label: string;
71
+ /**
72
+ * Whether walking past the end comes back to the beginning.
73
+ */
74
+ loop: boolean;
75
+ /**
76
+ * The reading that says where in the set the reader is, with `{index}` and
77
+ * `{count}` filled in.
78
+ *
79
+ * Announced rather than only drawn, because the picture changing under a
80
+ * reader who cannot see it is otherwise silent. A consumer who has their own
81
+ * counter in the chrome can empty this and say it themselves.
82
+ */
83
+ statusLabel: string;
84
+ /** Whether the reader has been still long enough to hide the chrome. */
85
+ private resting;
86
+ /** Whether the viewer is filling the screen. */
87
+ private filled;
88
+ /** What the live region is currently saying. */
89
+ private announcement;
90
+ private readonly fullscreen;
91
+ private readonly idle;
92
+ protected readonly commands: CommandController;
93
+ /** The items, read from the host so the server has them too. */
94
+ private get items();
95
+ /** The native dialog, or nothing where there is no DOM to hold one. */
96
+ private get dialog();
97
+ /** Opens the viewer, which makes the rest of the page inert. */
98
+ show(): void;
99
+ /** Asks to close, which a consumer may refuse. */
100
+ close(): void;
101
+ /** Shows the next item, or the first where it is past the end and looping. */
102
+ next(): void;
103
+ /** Shows the previous item, or the last where it is past the start and looping. */
104
+ previous(): void;
105
+ /** Fills the screen with the viewer, or comes back. */
106
+ toggleFullscreen(): void;
107
+ /**
108
+ * Moves the shown item, through `requestActive` so a consumer can refuse.
109
+ *
110
+ * Clamped rather than wrapped by default: a viewer that silently returns to
111
+ * the first picture tells a reader they have seen everything only if they
112
+ * were counting, and `loop` is how a consumer asks for the other reading.
113
+ */
114
+ private step;
115
+ /**
116
+ * Escape, intercepted so it is a request rather than an act.
117
+ *
118
+ * The native `cancel` fires before the dialog closes, so preventing it leaves
119
+ * the viewer open and lets `requestOpen` decide. Without this a reader
120
+ * pressing Escape would close a viewer its consumer had said was open, and the
121
+ * two would disagree until something else moved.
122
+ */
123
+ private readonly handleCancel;
124
+ /**
125
+ * A close that happened anyway, reflected back and relayed.
126
+ *
127
+ * `close` on a `<dialog>` neither bubbles nor is composed, so the one the
128
+ * browser fires reaches nobody outside this shadow root.
129
+ */
130
+ private readonly handleClose;
131
+ /**
132
+ * The arrow keys, which move the picture rather than the focus.
133
+ *
134
+ * `inlineStep` rather than a comparison against `ArrowLeft`, so the reader on
135
+ * a right-to-left page gets the next picture from the key that points the way
136
+ * their set runs. Up and down are bound as well, the set having one axis and
137
+ * a reader having no way to know which one this component thinks it is.
138
+ */
139
+ private readonly handleKeydown;
140
+ /** Home and End, which are an index rather than a step. */
141
+ private toEnd;
142
+ /**
143
+ * A press on the backdrop, which lands on the dialog element itself.
144
+ *
145
+ * The point is compared against the panel's box rather than trusting the
146
+ * target alone, for the reason `Dialog` gives: a modal `<dialog>` attributes
147
+ * backdrop presses to itself, so any padding the styled tier gives the panel
148
+ * is a strip where a press on the panel looks like a press outside it.
149
+ */
150
+ private readonly handleBackdrop;
151
+ protected willUpdate(changed: PropertyValues): void;
152
+ protected updated(changed: PropertyValues): void;
153
+ /**
154
+ * Tells each item whether it is the one being shown.
155
+ *
156
+ * `resolveActiveItem` rather than a comparison, so a set whose `active` names
157
+ * nothing still shows something: every set before its consumer has chosen, and
158
+ * every set whose current item has just been removed.
159
+ */
160
+ private mark;
161
+ /**
162
+ * Says where the reader is, once the reader has moved.
163
+ *
164
+ * Empty until then, because a live region populated as it enters the document
165
+ * announces itself on arrival, which for a component that upgrades on load is
166
+ * every page load.
167
+ */
168
+ private say;
169
+ protected render(): TemplateResult;
170
+ }
171
+ declare global {
172
+ interface HTMLElementTagNameMap {
173
+ 'helix-headless-lightbox': HelixHeadlessLightbox;
174
+ }
175
+ }
176
+ export {};
@@ -0,0 +1,212 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { CommandController, customElement, FullscreenController, HelixElement, hostAria, IdleController, inlineStep, MixinControllerActive, MixinControllerOpen, resolveActiveItem, } from '@fusion.dev/helix-core';
8
+ import { html, nothing } from 'lit';
9
+ import { property, state } from 'lit/decorators.js';
10
+ import { HelixHeadlessLightboxItem } from '../item/lightbox-item.js';
11
+ const fill = (template, index, count) => template.replaceAll('{index}', String(index)).replaceAll('{count}', String(count));
12
+ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerActive(MixinControllerOpen(HelixElement)) {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.idleDelay = 0;
16
+ this.label = '';
17
+ this.loop = false;
18
+ this.statusLabel = '{index} of {count}';
19
+ this.resting = false;
20
+ this.filled = false;
21
+ this.announcement = '';
22
+ this.fullscreen = new FullscreenController(this, (full) => {
23
+ this.filled = full;
24
+ });
25
+ this.idle = new IdleController(this, () => this.idleDelay, (resting) => {
26
+ this.resting = resting;
27
+ });
28
+ this.commands = new CommandController(this, {
29
+ commands: {
30
+ '--close': () => this.close(),
31
+ '--fullscreen': () => this.toggleFullscreen(),
32
+ '--next': () => this.next(),
33
+ '--previous': () => this.previous(),
34
+ '--show': () => this.show(),
35
+ '--toggle': () => this.toggleOpen(),
36
+ },
37
+ });
38
+ this.handleCancel = (event) => {
39
+ event.preventDefault();
40
+ this.requestOpen(false);
41
+ };
42
+ this.handleClose = (event) => {
43
+ if (this.open) {
44
+ this.requestOpen(false);
45
+ }
46
+ this.relayNativeEvent(event);
47
+ };
48
+ this.handleKeydown = (event) => {
49
+ const along = inlineStep(this, event.key);
50
+ if (along === 1 || event.key === 'ArrowDown') {
51
+ event.preventDefault();
52
+ this.next();
53
+ }
54
+ else if (along === -1 || event.key === 'ArrowUp') {
55
+ event.preventDefault();
56
+ this.previous();
57
+ }
58
+ else if (event.key === 'Home') {
59
+ event.preventDefault();
60
+ this.toEnd(0);
61
+ }
62
+ else if (event.key === 'End') {
63
+ event.preventDefault();
64
+ this.toEnd(this.items.length - 1);
65
+ }
66
+ };
67
+ this.handleBackdrop = (event) => {
68
+ const panel = this.dialog;
69
+ if (!panel || event.target !== panel) {
70
+ return;
71
+ }
72
+ const box = panel.getBoundingClientRect();
73
+ if (event.clientX >= box.left &&
74
+ event.clientX <= box.right &&
75
+ event.clientY >= box.top &&
76
+ event.clientY <= box.bottom) {
77
+ return;
78
+ }
79
+ this.requestOpen(false);
80
+ };
81
+ }
82
+ get items() {
83
+ return [...this.children].filter((child) => child instanceof HelixHeadlessLightboxItem);
84
+ }
85
+ get dialog() {
86
+ return this.renderRoot?.querySelector('dialog') ?? null;
87
+ }
88
+ show() {
89
+ this.requestOpen(true);
90
+ }
91
+ close() {
92
+ this.requestOpen(false);
93
+ }
94
+ next() {
95
+ this.step(1);
96
+ }
97
+ previous() {
98
+ this.step(-1);
99
+ }
100
+ toggleFullscreen() {
101
+ void this.fullscreen.toggle();
102
+ }
103
+ step(by) {
104
+ const items = this.items;
105
+ if (items.length === 0) {
106
+ return;
107
+ }
108
+ const current = items.findIndex((item) => item.value === this.active);
109
+ const from = current === -1 ? 0 : current;
110
+ const last = items.length - 1;
111
+ const next = this.loop
112
+ ? (from + by + items.length) % items.length
113
+ : Math.min(Math.max(from + by, 0), last);
114
+ const target = items[next];
115
+ if (target && target.value !== this.active) {
116
+ this.requestActive(target.value);
117
+ }
118
+ }
119
+ toEnd(at) {
120
+ const target = this.items[at];
121
+ if (target && target.value !== this.active) {
122
+ this.requestActive(target.value);
123
+ }
124
+ }
125
+ willUpdate(changed) {
126
+ super.willUpdate(changed);
127
+ hostAria(this.internals, { label: this.label });
128
+ this.toggleState('fullscreen', this.filled);
129
+ this.toggleState('idle', this.resting);
130
+ this.say(changed);
131
+ }
132
+ updated(changed) {
133
+ super.updated(changed);
134
+ this.mark();
135
+ const dialog = this.dialog;
136
+ if (!dialog) {
137
+ return;
138
+ }
139
+ if (this.open && !dialog.open) {
140
+ dialog.showModal();
141
+ this.idle.stir();
142
+ }
143
+ else if (!this.open && dialog.open) {
144
+ dialog.close();
145
+ }
146
+ }
147
+ mark() {
148
+ const items = this.items;
149
+ const current = resolveActiveItem(this.active, items);
150
+ for (const item of items) {
151
+ item.current = item === current;
152
+ }
153
+ }
154
+ say(changed) {
155
+ if (!changed.has('active') && !changed.has('open')) {
156
+ return;
157
+ }
158
+ if (!this.open || this.statusLabel === '') {
159
+ this.announcement = '';
160
+ return;
161
+ }
162
+ const items = this.items;
163
+ const current = resolveActiveItem(this.active, items);
164
+ const at = current ? items.indexOf(current) : -1;
165
+ this.announcement = at === -1 ? '' : fill(this.statusLabel, at + 1, items.length);
166
+ }
167
+ render() {
168
+ return html `<dialog
169
+ part="dialog"
170
+ aria-label=${this.label || 'Lightbox'}
171
+ @cancel=${this.handleCancel}
172
+ @click=${this.handleBackdrop}
173
+ @close=${this.handleClose}
174
+ @focusin=${this.idle.stir}
175
+ @keydown=${this.handleKeydown}
176
+ @pointerleave=${this.idle.leave}
177
+ @pointermove=${this.idle.stir}
178
+ >
179
+ <slot name="header"></slot>
180
+ <div part="viewport">
181
+ <slot></slot>
182
+ </div>
183
+ <slot name="footer"></slot>
184
+ <span part="live" role="status" aria-live="polite">${this.announcement || nothing}</span>
185
+ </dialog>`;
186
+ }
187
+ };
188
+ __decorate([
189
+ property({ attribute: 'idle-delay', type: Number })
190
+ ], HelixHeadlessLightbox.prototype, "idleDelay", void 0);
191
+ __decorate([
192
+ property()
193
+ ], HelixHeadlessLightbox.prototype, "label", void 0);
194
+ __decorate([
195
+ property({ reflect: true, type: Boolean })
196
+ ], HelixHeadlessLightbox.prototype, "loop", void 0);
197
+ __decorate([
198
+ property({ attribute: 'status-label' })
199
+ ], HelixHeadlessLightbox.prototype, "statusLabel", void 0);
200
+ __decorate([
201
+ state()
202
+ ], HelixHeadlessLightbox.prototype, "resting", void 0);
203
+ __decorate([
204
+ state()
205
+ ], HelixHeadlessLightbox.prototype, "filled", void 0);
206
+ __decorate([
207
+ state()
208
+ ], HelixHeadlessLightbox.prototype, "announcement", void 0);
209
+ HelixHeadlessLightbox = __decorate([
210
+ customElement('helix-headless-lightbox')
211
+ ], HelixHeadlessLightbox);
212
+ export { HelixHeadlessLightbox };
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.99.0",
4
+ "version": "0.101.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -46,13 +46,13 @@
46
46
  },
47
47
  "bugs": "https://github.com/project-fusion/fusion/issues",
48
48
  "dependencies": {
49
- "@fusion.dev/helix-core": "0.99.0",
50
- "@fusion.dev/helix-icons": "0.99.0",
49
+ "@fusion.dev/helix-core": "0.101.0",
50
+ "@fusion.dev/helix-icons": "0.101.0",
51
51
  "uqr": "^0.1.3"
52
52
  },
53
53
  "devDependencies": {
54
- "@fusion.dev/helix-cem": "0.99.0",
55
- "@fusion.dev/helix-testing": "0.99.0",
54
+ "@fusion.dev/helix-cem": "0.101.0",
55
+ "@fusion.dev/helix-testing": "0.101.0",
56
56
  "@fusion.dev/scripts": "0.0.0-dev",
57
57
  "@happy-dom/global-registrator": "^20.0.0",
58
58
  "@lit-labs/ssr": "^4.1.0",