@fusion.dev/helix 0.183.0 → 0.185.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.
@@ -1 +1,2 @@
1
+ export { type CodeBlockHighlighter, registerHighlighter, } from '@fusion.dev/helix-headless/code-block';
1
2
  export * from './root/code-block-root.js';
@@ -1 +1,2 @@
1
+ export { registerHighlighter, } from '@fusion.dev/helix-headless/code-block';
1
2
  export * from './root/code-block-root.js';
@@ -1,2 +1,2 @@
1
1
  import { createSheet } from '@fusion.dev/helix-core';
2
- export const styles = createSheet(`:host {display: block}[part="toolbar"], [part="table"], [part="footer"] {grid-column: 1 / -1}[part="table"] {display: grid;grid-template-columns: var(--data-table-columns, var(--data-table-template, minmax(0, 1fr)));inline-size: 100%;overflow-x: auto}[part="table"] {--_data-table-fade: to right;animation: data-table-edges linear both;animation-timeline: scroll(self inline);mask-image: linear-gradient(var(--_data-table-fade), transparent, #000 var(--_data-table-fade-start, 0rem), #000 calc(100% - var(--_data-table-fade-end, 0rem)), transparent)}:host(:dir(rtl)) [part="table"] {--_data-table-fade: to left}@keyframes data-table-edges {0% {--_data-table-fade-end: var(--helix-space-xl);--_data-table-fade-start: 0rem}5%, 95% {--_data-table-fade-end: var(--helix-space-xl);--_data-table-fade-start: var(--helix-space-xl)}100% {--_data-table-fade-end: 0rem;--_data-table-fade-start: var(--helix-space-xl)}}@media (prefers-reduced-motion: reduce) {[part="table"] {animation: none}}[part="head"], [part~="row"] {display: contents}[part~="cell"] {border-block-end: var(--helix-border-thin) solid var(--data-table-rule-color, var(--helix-color-border));color: var(--data-table-color, var(--helix-color-foreground));font-family: var(--helix-font-sans);font-size: var(--data-table-text, var(--helix-text-sm));padding-block: var(--data-table-padding-block, var(--helix-space-sm));padding-inline: var(--data-table-padding-inline, var(--helix-space-none) var(--helix-space-md));text-align: var(--data-table-cell-align)}[part~="row"]:last-of-type [part~="cell"] {border-block-end-color: transparent}::slotted(helix-table-column) {--_table-heading-color: var(--data-table-heading-color);--_table-heading-padding-block: var(--data-table-padding-block);--_table-heading-padding-inline: var(--data-table-padding-inline);--_table-heading-rule-color: var(--data-table-rule-color);--_table-heading-text: var(--data-table-text)}::slotted(helix-table-column[sortable]) {cursor: pointer}[part~="cell"]:focus-visible, [part="more-cell"]:focus-visible, [part~="select"]:focus-visible, [part="select-all"]:focus-visible, [part="row-grip"]:focus-visible, [part="grip-head"]:focus-visible {outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);outline-offset: calc(var(--helix-size-focus-offset) * -1)}[part~="select"], [part="select-all"], [part="row-grip"], [part="grip-head"] {align-items: center;border-block-end: var(--helix-border-thin) solid var(--data-table-rule-color, var(--helix-color-border));display: flex;justify-content: center;padding-block: var(--data-table-padding-block, var(--helix-space-sm));padding-inline: var(--data-table-padding-inline, var(--helix-space-sm))}[part~="row"]:last-of-type [part~="select"], [part~="row"]:last-of-type [part="row-grip"] {border-block-end-color: transparent}[part~="row"][aria-selected="true"] > * {background-color: var(--data-table-selected-background-color, color-mix(in srgb, var(--helix-color-primary) 10%, var(--helix-color-background)))}[part="row-grip-button"] {all: unset;align-items: center;border-radius: var(--helix-radii-sm);box-sizing: border-box;color: var(--data-table-grip-color, var(--helix-color-foreground-muted));cursor: grab;display: inline-flex;touch-action: none}[part="row-grip-button"]:active {cursor: grabbing}[part="row-grip-button"][aria-disabled="true"] {cursor: default;opacity: var(--data-table-grip-disabled-opacity, 0.4)}[part="row-grip-mark"] {block-size: 1em;inline-size: 1em}[part~="row"][part~="row-dragging"] > * {opacity: var(--data-table-dragging-opacity, 0.5)}[part~="row"][part~="row-drop-before"] > * {box-shadow: inset 0 var(--data-table-drop-indicator-size, var(--helix-border-thick)) 0 0 var(--data-table-drop-indicator-color, var(--helix-color-primary))}[part~="row"][part~="row-drop-after"] > * {box-shadow: inset 0 calc(var(--data-table-drop-indicator-size, var(--helix-border-thick)) * -1) 0 0 var(--data-table-drop-indicator-color, var(--helix-color-primary))}[part="toolbar"][data-empty], [part="footer"][data-empty] {display: none}[part="toolbar"] {padding-block-end: var(--data-table-toolbar-gap, var(--helix-space-sm))}[part="footer"] {align-items: center;display: flex;flex-wrap: wrap;gap: var(--data-table-footer-item-gap, var(--helix-space-md));padding-block-start: var(--data-table-footer-gap, var(--helix-space-sm))}[part="pagination"] {margin-inline-start: auto}[part="empty"], [part="loading"] {grid-column: 1 / -1}[part="error"] {color: var(--data-table-error-color, var(--helix-color-foreground));font-family: var(--helix-font-sans);font-size: var(--helix-text-sm);grid-column: 1 / -1;margin: var(--helix-space-none);padding-block: var(--helix-space-md);padding-inline: var(--helix-space-sm);text-align: center}:host([child-field]) [part~="cell-header"] {--_data-table-gutter: calc(var(--data-table-toggle-size, var(--helix-size-icon-tight)) + var(--data-table-toggle-gap, var(--helix-space-2xs)));padding-inline-start: calc((var(--data-table-level, 1) - 1) * var(--data-table-indent, var(--_data-table-gutter)))}:host([child-field]) [part~="cell-header"]:not(:has([part~="toggle"])) {padding-inline-start: calc((var(--data-table-level, 1) - 1) * var(--data-table-indent, var(--_data-table-gutter)) + var(--_data-table-gutter))}[part~="toggle"] {align-items: center;background: none;block-size: var(--data-table-toggle-size, var(--helix-size-icon-tight));border: none;color: var(--data-table-toggle-color, var(--helix-color-foreground-muted));cursor: pointer;display: inline-flex;inline-size: var(--data-table-toggle-size, var(--helix-size-icon-tight));justify-content: center;margin-inline-end: var(--data-table-toggle-gap, var(--helix-space-2xs));padding: 0;vertical-align: middle}[part="toggle-mark"] {block-size: 100%;inline-size: 100%}[part="more"] {display: contents}[part="more-cell"] {display: flex;grid-column: 1 / -1;padding-block: var(--data-table-padding-block, var(--helix-space-sm))}[part="more-button"] {background: none;border: var(--helix-border-thin) solid var(--data-table-more-border-color, var(--helix-color-border));border-radius: var(--data-table-more-radius, var(--helix-radii-sm));color: var(--data-table-color, var(--helix-color-foreground));cursor: pointer;font-family: var(--helix-font-sans);font-size: var(--data-table-text, var(--helix-text-sm));padding-block: var(--data-table-more-padding-block, var(--helix-space-2xs));padding-inline: var(--data-table-more-padding-inline, var(--helix-space-sm))}[part="more-button"]:hover {background-color: var(--data-table-more-hover-background-color, var(--helix-color-surface-raised))}:host([windowed]) [part="table"] {block-size: var(--data-table-block-size, 24em);overflow-y: auto;overscroll-behavior: contain}:host([windowed]) ::slotted(helix-table-column) {--_table-heading-background-color: var(--data-table-heading-background-color, var(--helix-color-background));--_table-heading-layer: var(--helix-layer-sticky);--_table-heading-position: sticky}:host([windowed]) [part="select-all"], :host([windowed]) [part="grip-head"] {background-color: var(--data-table-heading-background-color, var(--helix-color-background));inset-block-start: 0;position: sticky;z-index: var(--helix-layer-sticky)}[part="before"], [part="after"] {grid-column: 1 / -1}:host([loading]) [part="table"] {opacity: 0.5}[part="drag-preview"] {background-color: var(--data-table-drag-preview-background-color, var(--helix-color-surface-raised));border: var(--data-table-drag-preview-border-width, var(--helix-border-floating)) solid var(--data-table-drag-preview-border-color, var(--helix-color-border));border-radius: var(--data-table-drag-preview-radius, var(--helix-radii-sm));box-shadow: var(--data-table-drag-preview-shadow, var(--helix-shadow-floating));opacity: var(--data-table-drag-preview-opacity, 0.9)}@media print {:host {background-color: transparent}[part="table"] {overflow-x: visible}:host([windowed]) [part="table"] {block-size: auto;overflow-y: visible}[part="footer"] {display: none}}`);
2
+ export const styles = createSheet(`:host {display: block}[part="toolbar"], [part="table"], [part="footer"] {grid-column: 1 / -1}[part="table"] {display: grid;grid-template-columns: var(--data-table-columns, var(--data-table-template, minmax(0, 1fr)));inline-size: 100%;overflow-x: auto}[part="table"] {--_data-table-fade: to right;animation: data-table-edges linear both;animation-timeline: scroll(self inline);mask-image: linear-gradient(var(--_data-table-fade), transparent, #000 var(--_data-table-fade-start, 0rem), #000 calc(100% - var(--_data-table-fade-end, 0rem)), transparent)}:host(:dir(rtl)) [part="table"] {--_data-table-fade: to left}@keyframes data-table-edges {0% {--_data-table-fade-end: var(--helix-space-xl);--_data-table-fade-start: 0rem}5%, 95% {--_data-table-fade-end: var(--helix-space-xl);--_data-table-fade-start: var(--helix-space-xl)}100% {--_data-table-fade-end: 0rem;--_data-table-fade-start: var(--helix-space-xl)}}@media (prefers-reduced-motion: reduce) {[part="table"] {animation: none}}[part="head"], [part~="row"] {display: contents}[part~="cell"] {border-block-end: var(--helix-border-thin) solid var(--data-table-rule-color, var(--helix-color-border));color: var(--data-table-color, var(--helix-color-foreground));font-family: var(--helix-font-sans);font-size: var(--data-table-text, var(--helix-text-sm));padding-block: var(--data-table-padding-block, var(--helix-space-sm));padding-inline: var(--data-table-padding-inline, var(--helix-space-none) var(--helix-space-md));text-align: var(--data-table-cell-align)}[part~="row"]:last-of-type [part~="cell"] {border-block-end-color: transparent}::slotted(helix-table-column) {--_table-heading-color: var(--data-table-heading-color);--_table-heading-padding-block: var(--data-table-padding-block);--_table-heading-padding-inline: var(--data-table-padding-inline);--_table-heading-rule-color: var(--data-table-rule-color);--_table-heading-text: var(--data-table-text)}::slotted(helix-table-column[sortable]) {cursor: pointer}[part~="cell"]:focus-visible, [part="more-cell"]:focus-visible, [part~="select"]:focus-visible, [part="select-all"]:focus-visible, [part="row-grip"]:focus-visible, [part="grip-head"]:focus-visible {outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);outline-offset: calc(var(--helix-size-focus-offset) * -1)}[part~="select"], [part="select-all"], [part="row-grip"], [part="grip-head"] {align-items: center;border-block-end: var(--helix-border-thin) solid var(--data-table-rule-color, var(--helix-color-border));display: flex;justify-content: center;padding-block: var(--data-table-padding-block, var(--helix-space-sm));padding-inline: var(--data-table-padding-inline, var(--helix-space-sm))}[part="grip-head"]::before {content: "";inline-size: 1em}[part~="row"]:last-of-type [part~="select"], [part~="row"]:last-of-type [part="row-grip"] {border-block-end-color: transparent}[part~="row"][aria-selected="true"] > * {background-color: var(--data-table-selected-background-color, color-mix(in srgb, var(--helix-color-primary) 10%, var(--helix-color-background)))}[part="row-grip-button"] {all: unset;align-items: center;border-radius: var(--helix-radii-sm);box-sizing: border-box;color: var(--data-table-grip-color, var(--helix-color-foreground-muted));cursor: grab;display: inline-flex;touch-action: none}[part="row-grip-button"]:active {cursor: grabbing}[part="row-grip-button"][aria-disabled="true"] {cursor: default;opacity: var(--data-table-grip-disabled-opacity, 0.4)}[part="row-grip-mark"] {block-size: 1em;inline-size: 1em}[part~="row"][part~="row-dragging"] > * {opacity: var(--data-table-dragging-opacity, 0.5)}[part~="row"][part~="row-drop-before"] > * {box-shadow: inset 0 var(--data-table-drop-indicator-size, var(--helix-border-thick)) 0 0 var(--data-table-drop-indicator-color, var(--helix-color-primary))}[part~="row"][part~="row-drop-after"] > * {box-shadow: inset 0 calc(var(--data-table-drop-indicator-size, var(--helix-border-thick)) * -1) 0 0 var(--data-table-drop-indicator-color, var(--helix-color-primary))}[part="toolbar"][data-empty], [part="footer"][data-empty] {display: none}[part="toolbar"] {padding-block-end: var(--data-table-toolbar-gap, var(--helix-space-sm))}[part="footer"] {align-items: center;display: flex;flex-wrap: wrap;gap: var(--data-table-footer-item-gap, var(--helix-space-md));padding-block-start: var(--data-table-footer-gap, var(--helix-space-sm))}[part="pagination"] {margin-inline-start: auto}[part="footer"][data-held] {min-block-size: calc(var(--pagination-size, var(--helix-size-control-tight)) + var(--data-table-footer-gap, var(--helix-space-sm)))}[part="footer"][data-held] [part="pagination"] {visibility: hidden}:host(:not(:defined))::before, :host(:not(:defined))::after {content: none !important}[part="empty"], [part="loading"] {grid-column: 1 / -1}[part="error"] {color: var(--data-table-error-color, var(--helix-color-foreground));font-family: var(--helix-font-sans);font-size: var(--helix-text-sm);grid-column: 1 / -1;margin: var(--helix-space-none);padding-block: var(--helix-space-md);padding-inline: var(--helix-space-sm);text-align: center}:host([child-field]) [part~="cell-header"] {--_data-table-gutter: calc(var(--data-table-toggle-size, var(--helix-size-icon-tight)) + var(--data-table-toggle-gap, var(--helix-space-2xs)));padding-inline-start: calc((var(--data-table-level, 1) - 1) * var(--data-table-indent, var(--_data-table-gutter)))}:host([child-field]) [part~="cell-header"]:not(:has([part~="toggle"])) {padding-inline-start: calc((var(--data-table-level, 1) - 1) * var(--data-table-indent, var(--_data-table-gutter)) + var(--_data-table-gutter))}[part~="toggle"] {align-items: center;background: none;block-size: var(--data-table-toggle-size, var(--helix-size-icon-tight));border: none;color: var(--data-table-toggle-color, var(--helix-color-foreground-muted));cursor: pointer;display: inline-flex;inline-size: var(--data-table-toggle-size, var(--helix-size-icon-tight));justify-content: center;margin-inline-end: var(--data-table-toggle-gap, var(--helix-space-2xs));padding: 0;vertical-align: middle}[part="toggle-mark"] {block-size: 100%;inline-size: 100%}[part="more"] {display: contents}[part="more-cell"] {display: flex;grid-column: 1 / -1;padding-block: var(--data-table-padding-block, var(--helix-space-sm))}[part="more-button"] {background: none;border: var(--helix-border-thin) solid var(--data-table-more-border-color, var(--helix-color-border));border-radius: var(--data-table-more-radius, var(--helix-radii-sm));color: var(--data-table-color, var(--helix-color-foreground));cursor: pointer;font-family: var(--helix-font-sans);font-size: var(--data-table-text, var(--helix-text-sm));padding-block: var(--data-table-more-padding-block, var(--helix-space-2xs));padding-inline: var(--data-table-more-padding-inline, var(--helix-space-sm))}[part="more-button"]:hover {background-color: var(--data-table-more-hover-background-color, var(--helix-color-surface-raised))}:host([windowed]) [part="table"] {block-size: var(--data-table-block-size, 24em);overflow-y: auto;overscroll-behavior: contain}:host([windowed]) ::slotted(helix-table-column) {--_table-heading-background-color: var(--data-table-heading-background-color, var(--helix-color-background));--_table-heading-layer: var(--helix-layer-sticky);--_table-heading-position: sticky}:host([windowed]) [part="select-all"], :host([windowed]) [part="grip-head"] {background-color: var(--data-table-heading-background-color, var(--helix-color-background));inset-block-start: 0;position: sticky;z-index: var(--helix-layer-sticky)}[part="before"], [part="after"] {grid-column: 1 / -1}:host([loading]) [part="table"] {opacity: 0.5}[part="drag-preview"] {background-color: var(--data-table-drag-preview-background-color, var(--helix-color-surface-raised));border: var(--data-table-drag-preview-border-width, var(--helix-border-floating)) solid var(--data-table-drag-preview-border-color, var(--helix-color-border));border-radius: var(--data-table-drag-preview-radius, var(--helix-radii-sm));box-shadow: var(--data-table-drag-preview-shadow, var(--helix-shadow-floating));opacity: var(--data-table-drag-preview-opacity, 0.9)}@media print {:host {background-color: transparent}[part="table"] {overflow-x: visible}:host([windowed]) [part="table"] {block-size: auto;overflow-y: visible}[part="footer"] {display: none}}`);
@@ -22,7 +22,9 @@ import '../../pagination/index.js';
22
22
  * Write it under the columns as `helix-table-row` and `helix-table-cell`
23
23
  * elements. `@fusion.dev/helix-styles/helix.css` lays them out as the table
24
24
  * draws them before it upgrades, and the table shows them until `rows` holds a
25
- * row.
25
+ * row. It holds the checkbox column and the pagination's row from the first
26
+ * paint, and takes the widths from `columns`, so nothing moves when the table
27
+ * upgrades or draws its own rows.
26
28
  *
27
29
  * @since 0.1.0
28
30
  * @status stable
package/dist/index.d.ts CHANGED
@@ -62,6 +62,7 @@ export * from './listbox/index.js';
62
62
  export * from './loader/index.js';
63
63
  export * from './map/index.js';
64
64
  export * from './megamenu/index.js';
65
+ export * from './menu/filter/index.js';
65
66
  export * from './menu/index.js';
66
67
  export * from './meter/index.js';
67
68
  export * from './navigation/index.js';
package/dist/index.js CHANGED
@@ -57,6 +57,7 @@ export * from './listbox/index.js';
57
57
  export * from './loader/index.js';
58
58
  export * from './map/index.js';
59
59
  export * from './megamenu/index.js';
60
+ export * from './menu/filter/index.js';
60
61
  export * from './menu/index.js';
61
62
  export * from './meter/index.js';
62
63
  export * from './navigation/index.js';
@@ -0,0 +1 @@
1
+ export { filterMenu, MenuFilterController } from '@fusion.dev/helix-headless/menu/filter';
@@ -0,0 +1,5 @@
1
+ import { filterMenu } from '@fusion.dev/helix-headless/menu/filter';
2
+ import { HelixMenu } from '../root/menu-root.js';
3
+ import { styles } from './menu-filter.css.js';
4
+ filterMenu(HelixMenu, styles);
5
+ export { filterMenu, MenuFilterController } from '@fusion.dev/helix-headless/menu/filter';
@@ -0,0 +1 @@
1
+ export declare const styles: import("lit").CSSResult;
@@ -0,0 +1,2 @@
1
+ import { createSheet } from '@fusion.dev/helix-core';
2
+ export const styles = createSheet(`input {appearance: none;background-color: var(--menu-background-color, var(--helix-color-surface-overlay));border: none;border-block-end: var(--helix-border-thin) solid var(--helix-color-border);box-sizing: border-box;color: var(--helix-color-foreground);display: block;font-family: var(--helix-font-sans);font-size: var(--helix-text-xs);font-weight: var(--helix-weight-medium);inline-size: 100%;inset-block-start: calc(var(--helix-space-xs) * -1);margin-block: calc(var(--helix-space-xs) * -1) var(--helix-space-xs);min-block-size: var(--helix-size-control);padding-inline: var(--helix-space-sm);position: sticky;z-index: 1}input:focus-visible {outline: none}[data-filtered] slot::slotted(helix-divider) {display: none}`);
@@ -1,2 +1,2 @@
1
1
  import { createSheet } from '@fusion.dev/helix-core';
2
- export const styles = createSheet(`:host {display: contents}[part="surface"] {--helix-size-control: var(--helix-size-control-sm);--helix-size-control-loose: calc(var(--helix-size-control) * 1.2);--helix-size-control-tight: calc(var(--helix-size-control) * 0.8);--helix-size-icon: var(--helix-size-icon-sm);--helix-size-icon-loose: calc(var(--helix-size-icon) * 1.2);--helix-size-icon-tight: calc(var(--helix-size-icon) * 0.8);--menu-item-start: 0px;--menu-item-end: 0px;max-block-size: var(--menu-max-block-size, var(--floating-available-block-size, none));overflow-y: auto;--_floating-background-color: var(--menu-background-color, var(--helix-color-surface-overlay));--_floating-border-color: var(--menu-border-color, var(--helix-color-border));--_floating-border-width: var(--menu-border-width, var(--helix-border-floating));--_floating-shadow: var(--menu-shadow, var(--helix-shadow-floating));min-inline-size: var(--menu-min-inline-size, 12rem);padding-block: var(--helix-space-xs);padding-inline: var(--helix-space-none)}:host(:state(start)) [part="surface"] {--menu-item-start: calc(var(--helix-size-icon) + var(--helix-space-xs))}:host(:state(mark)) [part="surface"] {--menu-item-end: calc(var(--helix-size-icon) + var(--helix-space-xs))}[part="surface"][data-placement^="left"] {transform-origin: 100% var(--floating-arrow-y, 50%)}[part="surface"][data-placement^="right"] {transform-origin: 0 var(--floating-arrow-y, 50%)}[part="header"] {align-items: center;border-block-end: var(--helix-border-thin) solid var(--helix-color-border);display: flex;gap: var(--helix-space-2xs);margin-block-end: var(--helix-space-2xs);padding: var(--helix-space-2xs) var(--helix-space-xs) var(--helix-space-xs)}[part="back"] {align-items: center;background: none;border: none;border-radius: var(--helix-radii-sm);color: var(--helix-color-foreground-muted);cursor: pointer;display: inline-flex;justify-content: center;block-size: calc(var(--helix-size-icon) + var(--helix-space-xs));inline-size: calc(var(--helix-size-icon) + var(--helix-space-xs));padding: var(--helix-space-none)}[part="back"] svg {block-size: var(--helix-size-icon);inline-size: var(--helix-size-icon)}[part="back"]:hover {background-color: color-mix(in oklab, var(--helix-color-foreground) 8%, transparent);color: var(--helix-color-foreground)}[part="back"]:focus-visible {outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);outline-offset: calc(var(--helix-size-focus-offset) * -1)}[part="title"] {color: var(--helix-color-foreground);font-size: var(--helix-text-xs);font-weight: var(--helix-weight-medium)}::slotted(helix-divider) {margin-block: var(--helix-space-2xs)}[part="surface"][data-drilled] slot::slotted(:not(helix-menu-item)) {display: none}[part="search-field"] {appearance: none;background-color: var(--menu-background-color, var(--helix-color-surface-overlay));border: none;border-block-end: var(--helix-border-thin) solid var(--helix-color-border);box-sizing: border-box;color: var(--helix-color-foreground);display: block;font-family: var(--helix-font-sans);font-size: var(--helix-text-xs);font-weight: var(--helix-weight-medium);inline-size: 100%;inset-block-start: calc(var(--helix-space-xs) * -1);margin-block: calc(var(--helix-space-xs) * -1) var(--helix-space-xs);min-block-size: var(--helix-size-control);padding-inline: var(--helix-space-sm);position: sticky;z-index: 1}[part="search-field"]:focus-visible {outline: none}[part="surface"][data-filtered] slot::slotted(helix-divider) {display: none}:host([loading]) [part="surface"] {opacity: 0.5}@media print {:host {display: none}}`);
2
+ export const styles = createSheet(`:host {display: contents}[part="surface"] {--helix-size-control: var(--helix-size-control-sm);--helix-size-control-loose: calc(var(--helix-size-control) * 1.2);--helix-size-control-tight: calc(var(--helix-size-control) * 0.8);--helix-size-icon: var(--helix-size-icon-sm);--helix-size-icon-loose: calc(var(--helix-size-icon) * 1.2);--helix-size-icon-tight: calc(var(--helix-size-icon) * 0.8);--menu-item-start: 0px;--menu-item-end: 0px;max-block-size: var(--menu-max-block-size, var(--floating-available-block-size, none));overflow-y: auto;--_floating-background-color: var(--menu-background-color, var(--helix-color-surface-overlay));--_floating-border-color: var(--menu-border-color, var(--helix-color-border));--_floating-border-width: var(--menu-border-width, var(--helix-border-floating));--_floating-shadow: var(--menu-shadow, var(--helix-shadow-floating));min-inline-size: var(--menu-min-inline-size, 12rem);padding-block: var(--helix-space-xs);padding-inline: var(--helix-space-none)}:host(:state(start)) [part="surface"] {--menu-item-start: calc(var(--helix-size-icon) + var(--helix-space-xs))}:host(:state(mark)) [part="surface"] {--menu-item-end: calc(var(--helix-size-icon) + var(--helix-space-xs))}[part="surface"][data-placement^="left"] {transform-origin: 100% var(--floating-arrow-y, 50%)}[part="surface"][data-placement^="right"] {transform-origin: 0 var(--floating-arrow-y, 50%)}[part="header"] {align-items: center;border-block-end: var(--helix-border-thin) solid var(--helix-color-border);display: flex;gap: var(--helix-space-2xs);margin-block-end: var(--helix-space-2xs);padding: var(--helix-space-2xs) var(--helix-space-xs) var(--helix-space-xs)}[part="back"] {align-items: center;background: none;border: none;border-radius: var(--helix-radii-sm);color: var(--helix-color-foreground-muted);cursor: pointer;display: inline-flex;justify-content: center;block-size: calc(var(--helix-size-icon) + var(--helix-space-xs));inline-size: calc(var(--helix-size-icon) + var(--helix-space-xs));padding: var(--helix-space-none)}[part="back"] svg {block-size: var(--helix-size-icon);inline-size: var(--helix-size-icon)}[part="back"]:hover {background-color: color-mix(in oklab, var(--helix-color-foreground) 8%, transparent);color: var(--helix-color-foreground)}[part="back"]:focus-visible {outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);outline-offset: calc(var(--helix-size-focus-offset) * -1)}[part="title"] {color: var(--helix-color-foreground);font-size: var(--helix-text-xs);font-weight: var(--helix-weight-medium)}::slotted(helix-divider) {margin-block: var(--helix-space-2xs)}[part="surface"][data-drilled] slot::slotted(:not(helix-menu-item)) {display: none}:host([loading]) [part="surface"] {opacity: 0.5}@media print {:host {display: none}}`);
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The compiler a module file in TypeScript or JSX runs through before the
3
+ * frame loads it, by its npm name and its pinned version.
4
+ *
5
+ * **Sucrase, measured against esbuild-wasm on October 8, 2026**, on a TSX
6
+ * module of 241 lines with the automatic JSX runtime:
7
+ *
8
+ * | | Sucrase 3.35 | esbuild-wasm 0.28 |
9
+ * | --- | --- | --- |
10
+ * | Download, gzip | 48 KB, and 9 KB of `buffer` esm.sh adds | 3.2 MB of WebAssembly, and 19 KB |
11
+ * | First compile | 14 ms | 188 ms, the WebAssembly starting |
12
+ * | Each after | 0.8 ms | 4.6 ms |
13
+ * | Automatic JSX runtime | yes | yes |
14
+ * | Source map | yes | yes |
15
+ *
16
+ * Both compile what a sandbox holds, and neither checks types. esbuild-wasm
17
+ * downloads 56 times as much before the first module runs, which a reader
18
+ * waits through on the first edit of every page. Sucrase also keeps every line
19
+ * where it was, so a line number in an error in the frame is the line in the
20
+ * pane even where a browser ignores the source map.
21
+ *
22
+ * **Loaded the first time a module needs it**, from the preview's `cdn` as a
23
+ * package in `dependencies` is, so the published package carries no compiler
24
+ * and a sandbox with no TypeScript loads none.
25
+ *
26
+ * **In a worker of its own**, which has no access to the page and keeps a
27
+ * long compile off the page's thread. The worker's script is text, so the
28
+ * `import()` of an address known only at run time is one no bundler reads:
29
+ * written in this module, Turbopack refused to build a page that imported
30
+ * it, the TypeScript build having dropped the comments that tell bundlers to
31
+ * leave it alone. The compiler reads the source as text, and the example
32
+ * itself still runs only in the frame.
33
+ */
34
+ export declare const COMPILER = "sucrase@3.35.1";
35
+ /** Whether a module file is TypeScript or JSX, which a browser cannot run as written. */
36
+ export declare const COMPILED: RegExp;
37
+ /** Sucrase's `transform`, as the worker runs it: rejects with what failed. */
38
+ type Transform = (source: string, options: Record<string, unknown>) => Promise<{
39
+ code: string;
40
+ sourceMap?: Record<string, unknown>;
41
+ }>;
42
+ /**
43
+ * The compiler for each `cdn`, so each is set up once a page. A spec puts a
44
+ * stand-in here, for an address of its own.
45
+ */
46
+ export declare const compilers: Map<string, Transform>;
47
+ /**
48
+ * A module file compiled to JavaScript, with its source map inline, for the
49
+ * frame to load as its other modules.
50
+ *
51
+ * `.ts` is read as TypeScript, `.tsx` as TypeScript with JSX, and `.jsx` as
52
+ * JSX. JSX compiles to the automatic runtime, `react/jsx-runtime`, which an
53
+ * example lists in `dependencies`, or to the runtime a
54
+ * `@jsxImportSource preact` comment in the file names.
55
+ *
56
+ * **What fails to compile still runs, as a module that throws** what the
57
+ * compiler said, such as `Error transforming app.tsx: Unexpected token (2:1)`.
58
+ * The frame reports it as it reports any error an example does not catch, so
59
+ * it shows over the example in the preview's `error` part and in the
60
+ * terminal, and only where the example imports the file.
61
+ */
62
+ export declare const compile: (cdn: string, path: string, source: string) => Promise<string>;
63
+ export {};
@@ -0,0 +1,52 @@
1
+ export const COMPILER = 'sucrase@3.35.1';
2
+ export const COMPILED = /\.(?:[cm]?ts|[jt]sx)$/;
3
+ const WORKER = 'const l={};onmessage=async({data:[i,a,s,o]})=>{let c;try{c=await(l[a]??=import(a))}catch(e){delete l[a];return postMessage([i,,o.filePath+" needs "+a+", which did not load: "+e])}try{postMessage([i,c.transform(s,o)])}catch(e){postMessage([i,,String(e?.message)])}}';
4
+ let worker;
5
+ let sent = 0;
6
+ const owed = new Map();
7
+ const inWorker = (address) => (source, options) => {
8
+ if (!worker) {
9
+ worker = new Worker(URL.createObjectURL(new Blob([WORKER], { type: 'text/javascript' })), {
10
+ type: 'module',
11
+ });
12
+ worker.onmessage = ({ data }) => {
13
+ owed.get(data[0])?.(data);
14
+ owed.delete(data[0]);
15
+ };
16
+ }
17
+ sent += 1;
18
+ const id = sent;
19
+ worker.postMessage([id, address, source, options]);
20
+ return new Promise((resolve, reject) => {
21
+ owed.set(id, ([, result, error]) => (result ? resolve(result) : reject(new Error(error))));
22
+ });
23
+ };
24
+ export const compilers = new Map();
25
+ const compilerAt = (cdn) => {
26
+ let compiler = compilers.get(cdn);
27
+ if (!compiler) {
28
+ compiler = inWorker(`${cdn}${COMPILER}`);
29
+ compilers.set(cdn, compiler);
30
+ }
31
+ return compiler;
32
+ };
33
+ export const compile = async (cdn, path, source) => {
34
+ try {
35
+ const { code, sourceMap } = await compilerAt(cdn)(source, {
36
+ filePath: path,
37
+ jsxImportSource: /@jsxImportSource\s+(\S+)/.exec(source)?.[1],
38
+ jsxRuntime: 'automatic',
39
+ production: true,
40
+ sourceMapOptions: { compiledFilename: path },
41
+ transforms: [
42
+ ...(/tsx?$/.test(path) ? ['typescript'] : []),
43
+ ...(/x$/.test(path) ? ['jsx'] : []),
44
+ ],
45
+ });
46
+ const map = JSON.stringify({ ...sourceMap, sourcesContent: [source] });
47
+ return `${code}\n
48
+ }
49
+ catch (error) {
50
+ return `throw new SyntaxError(${JSON.stringify(error.message)})`;
51
+ }
52
+ };
@@ -73,6 +73,15 @@ export type PreviewMessage = PreviewConsole | PreviewPress | PreviewReady | Prev
73
73
  * in the `js` pane or in another module loads it. A sandbox writes each pane
74
74
  * named for a module file here.
75
75
  *
76
+ * **A module in TypeScript or JSX runs too**: a path ending in `.ts`, `.tsx`
77
+ * or `.jsx` is compiled to JavaScript first, and imported by its path or by
78
+ * its path without the extension. The compiler, Sucrase, loads from `cdn` the
79
+ * first time a module needs it, so the package carries none and a preview
80
+ * with no such module loads none. JSX compiles to the automatic runtime,
81
+ * `react/jsx-runtime`, from `dependencies`, or to the one a
82
+ * `@jsxImportSource` comment names. What fails to compile shows in the
83
+ * `error` part as an error the example threw, when the example imports it.
84
+ *
76
85
  * **Examples render in a frame**, so an example's styles and scripts do not
77
86
  * affect the page around the sandbox, and `position: fixed` stays inside the
78
87
  * pane. The frame starts empty, with its own stylesheets and its own custom
@@ -233,6 +242,16 @@ export declare class HelixSandboxPreview extends HelixElement {
233
242
  private failedSource;
234
243
  /** What the example threw and did not catch, while the preview is `failed`. */
235
244
  private failure;
245
+ /** Each module file compiled to JavaScript, by its path and source. */
246
+ private compiled;
247
+ /** The module files being compiled, by their path and source. */
248
+ private readonly compiling;
249
+ /**
250
+ * The import map the built-in document loads with: the last one with every
251
+ * module file compiled, so a frame never loads without one an example
252
+ * imports.
253
+ */
254
+ private map;
236
255
  /** Says the preview failed with `text`, or, with none, that it has not. */
237
256
  private fail;
238
257
  /** What the frame is sent as its source: the markup, and the script after it. */
@@ -244,6 +263,11 @@ export declare class HelixSandboxPreview extends HelixElement {
244
263
  * `dependencies` and the paths under it, and `imports` over all of them.
245
264
  */
246
265
  private get importMap();
266
+ /**
267
+ * Compiles a module file, once, then draws again with it. Not on a server,
268
+ * which loads no frame.
269
+ */
270
+ private compileModule;
247
271
  /**
248
272
  * The frame's document has finished loading, so its listener exists, the
249
273
  * example is sent and the preview is no longer `loading`. The frame saying
@@ -322,7 +346,10 @@ export declare class HelixSandboxPreview extends HelixElement {
322
346
  private scheduleSend;
323
347
  /** What the frame is sent: the source, its stylesheet and its settings. */
324
348
  private get message();
325
- /** Sends the current source to the frame, once it is listening. */
349
+ /**
350
+ * Sends the current source to the frame, once it is listening and no
351
+ * module it may import is still compiling.
352
+ */
326
353
  private send;
327
354
  /**
328
355
  * Opens the example in a new tab, and returns the tab's window, or `null`
@@ -362,6 +389,7 @@ export declare class HelixSandboxPreview extends HelixElement {
362
389
  * nothing has failed in it. The built-in document loads too.
363
390
  */
364
391
  private unready;
392
+ protected willUpdate(changed: PropertyValues): void;
365
393
  protected updated(changed: PropertyValues): void;
366
394
  /** The reload and open buttons, under `controls`. */
367
395
  private renderControls;
@@ -4,6 +4,7 @@ import { customElement, DelayController, HelixConsoleEvent, HelixElement, HelixR
4
4
  const SETTLE = 120;
5
5
  import { html, nothing } from 'lit';
6
6
  import { property, state } from 'lit/decorators.js';
7
+ import { COMPILED, compile } from '../compile.js';
7
8
  import { defaultFrame, withScript } from '../frame.js';
8
9
  import { styles as sheet } from './sandbox-preview.css.js';
9
10
  const SANDBOX = 'allow-scripts allow-forms';
@@ -22,6 +23,9 @@ let HelixSandboxPreview = class HelixSandboxPreview extends HelixElement {
22
23
  this.src = '';
23
24
  this.styles = '';
24
25
  this.ready = false;
26
+ this.compiled = new Map();
27
+ this.compiling = new Set();
28
+ this.map = {};
25
29
  this.handleFrameLoad = () => {
26
30
  this.ready = true;
27
31
  this.toggleState('loading', false);
@@ -72,11 +76,39 @@ let HelixSandboxPreview = class HelixSandboxPreview extends HelixElement {
72
76
  map[name] = address;
73
77
  map[`${name}/`] = `${address}/`;
74
78
  }
79
+ const compiled = new Map();
75
80
  for (const [given, source] of Object.entries(this.modules ?? {})) {
76
81
  const path = given.replace(/^\.?\//, '');
77
- map[`./${path}`] = map[`sandbox:/${path}`] = moduleText(path, source);
82
+ if (!COMPILED.test(path)) {
83
+ map[`./${path}`] = map[`sandbox:/${path}`] = moduleText(path, source);
84
+ continue;
85
+ }
86
+ const key = `${path}\u0000${source}`;
87
+ const code = this.compiled.get(key);
88
+ if (code === undefined) {
89
+ this.compileModule(key, path, source);
90
+ continue;
91
+ }
92
+ compiled.set(key, code);
93
+ const text = moduleText(path, code);
94
+ const bare = path.replace(COMPILED, '');
95
+ map[`./${path}`] = map[`sandbox:/${path}`] = text;
96
+ map[`./${bare}`] ??= map[`sandbox:/${bare}`] ??= text;
78
97
  }
79
- return { ...map, ...this.imports };
98
+ this.compiled = compiled;
99
+ return this.compiling.size > 0 ? undefined : { ...map, ...this.imports };
100
+ }
101
+ compileModule(key, path, source) {
102
+ if (this.compiling.has(key) || typeof document === 'undefined') {
103
+ return;
104
+ }
105
+ this.compiling.add(key);
106
+ void compile(this.cdn, path, source).then((code) => {
107
+ this.compiling.delete(key);
108
+ this.compiled.set(key, code);
109
+ this.requestUpdate();
110
+ void this.updateComplete.then(() => this.send());
111
+ });
80
112
  }
81
113
  static { this.target = '*'; }
82
114
  scheduleSend() {
@@ -98,7 +130,7 @@ let HelixSandboxPreview = class HelixSandboxPreview extends HelixElement {
98
130
  };
99
131
  }
100
132
  send() {
101
- if (!this.ready) {
133
+ if (!this.ready || this.compiling.size > 0) {
102
134
  return;
103
135
  }
104
136
  this.frame?.contentWindow?.postMessage(this.message, HelixSandboxPreview_1.target);
@@ -118,7 +150,7 @@ let HelixSandboxPreview = class HelixSandboxPreview extends HelixElement {
118
150
  frame.title = this.label;
119
151
  frame.style.cssText = 'block-size: 100vh; border: none; display: block; inline-size: 100vw';
120
152
  if (this.src === '') {
121
- frame.srcdoc = defaultFrame(this.importMap);
153
+ frame.srcdoc = defaultFrame(this.map);
122
154
  }
123
155
  else {
124
156
  frame.src = this.frame?.src || this.src;
@@ -144,7 +176,7 @@ frame.onload = () => frame.contentWindow.postMessage(${JSON.stringify(this.messa
144
176
  }
145
177
  this.unready();
146
178
  if (this.src === '') {
147
- frame.srcdoc = defaultFrame(this.importMap);
179
+ frame.srcdoc = defaultFrame(this.map);
148
180
  return;
149
181
  }
150
182
  frame.src = this.src;
@@ -156,9 +188,15 @@ frame.onload = () => frame.contentWindow.postMessage(${JSON.stringify(this.messa
156
188
  this.fail();
157
189
  this.toggleState('loading', true);
158
190
  }
191
+ willUpdate(changed) {
192
+ super.willUpdate(changed);
193
+ if (this.src === '') {
194
+ this.map = this.importMap ?? this.map;
195
+ }
196
+ }
159
197
  updated(changed) {
160
198
  super.updated(changed);
161
- const map = this.src === '' ? JSON.stringify(this.importMap) : '';
199
+ const map = this.src === '' ? JSON.stringify(this.map) : '';
162
200
  const remapped = map !== this.loadedMap;
163
201
  this.loadedMap = map;
164
202
  if (changed.has('src') || remapped) {
@@ -189,7 +227,7 @@ frame.onload = () => frame.contentWindow.postMessage(${JSON.stringify(this.messa
189
227
  part="frame"
190
228
  @load=${this.handleFrameLoad}
191
229
  src=${this.src || nothing}
192
- srcdoc=${this.src === '' ? defaultFrame(this.importMap) : nothing}
230
+ srcdoc=${this.src === '' ? defaultFrame(this.map) : nothing}
193
231
  title=${this.label}
194
232
  aria-describedby=${this.failure === undefined ? nothing : 'error'}
195
233
  allow="clipboard-write *; fullscreen *"
@@ -21,6 +21,10 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
21
21
  * - `js` sets a script that runs as a module after the markup.
22
22
  * - A module file name ending in `.js` or `.mjs`, such as `utils.js`, is a
23
23
  * module the other scripts import by its path, `./utils.js`.
24
+ * - A module file name ending in `.ts`, `.tsx` or `.jsx`, such as `app.tsx`,
25
+ * is one too, which the preview compiles to JavaScript first. The `js` pane
26
+ * runs it with `import './app.tsx'`. A file that does not compile shows
27
+ * why over the preview, as an error the example threw.
24
28
  * - `package.json` sets the npm packages the scripts import by name, from its
25
29
  * `dependencies`, which load from the preview's `cdn`.
26
30
  *
@@ -8,7 +8,7 @@ const DEFAULT_SPLIT = 50;
8
8
  const DEFAULT_PANEL = 160;
9
9
  const PANEL_MAX = 0.8;
10
10
  const PANE_KEYS = { css: 'styles', html: 'source', js: 'script' };
11
- const MODULE = /\.m?js$/;
11
+ const MODULE = /\.(?:m?js|[cm]?ts|[jt]sx)$/;
12
12
  const MANIFEST = 'package.json';
13
13
  const isPaneName = (name) => typeof name === 'string' &&
14
14
  (Object.hasOwn(PANE_KEYS, name) || MODULE.test(name) || name === MANIFEST);
@@ -40,7 +40,7 @@ import { type StrandsVariant } from '../path.js';
40
40
  *
41
41
  * The ribbon stops moving in these cases:
42
42
  * - `prefers-reduced-motion` is set. The element shows one still frame, and in
43
- * `travel` still follows the scroll, without easing.
43
+ * `travel` still follows the scroll.
44
44
  * - The element is off screen or in a background tab.
45
45
  *
46
46
  * It shows a gradient from the same tokens in these cases, and matches
@@ -185,7 +185,7 @@ export declare class HelixStrands extends HelixElement {
185
185
  private readonly quietRects;
186
186
  /** In `travel`, the path's `y` at even steps of its parameter. */
187
187
  private table;
188
- /** In `travel`, the scroll position the ribbon is drawn at, easing toward the page's. */
188
+ /** In `travel`, the scroll position the ribbon was last drawn at. */
189
189
  private offset;
190
190
  /** Whether the opening frame has been drawn. Aura's settle: see `open`. */
191
191
  private opened;
@@ -256,8 +256,8 @@ export declare class HelixStrands extends HelixElement {
256
256
  /** A layout on the next frame, once, however many changes asked for one. */
257
257
  private readonly relayout;
258
258
  /**
259
- * The page scrolled. A running loop eases toward it on its own; a stopped
260
- * one, for a reader who asked for no motion, jumps there and paints once.
259
+ * The page scrolled. A running loop draws the new position on its next
260
+ * frame; a stopped one, for a reader who asked for no motion, paints once.
261
261
  */
262
262
  private readonly handleScroll;
263
263
  private paint;
@@ -10,7 +10,6 @@ import { styles } from './strands-root.css.js';
10
10
  const MAX_CANVAS_EDGE = 4096;
11
11
  const MAX_CANVAS_PIXELS = 4_194_304;
12
12
  const OPENING_PATIENCE = 500;
13
- const TRAVEL_EASE = 0.09;
14
13
  const PACE = 0.25;
15
14
  let HelixStrands = class HelixStrands extends HelixElement {
16
15
  constructor() {
@@ -465,11 +464,7 @@ let HelixStrands = class HelixStrands extends HelixElement {
465
464
  this.held = 0;
466
465
  this.elapsed += step * PACE * Math.max(0, this.speed);
467
466
  if (this.travel) {
468
- const target = window.scrollY;
469
- this.offset += (target - this.offset) * (1 - Math.exp(-step / TRAVEL_EASE));
470
- if (Math.abs(target - this.offset) < 0.5) {
471
- this.offset = target;
472
- }
467
+ this.offset = window.scrollY;
473
468
  }
474
469
  this.paint();
475
470
  }