@fusion.dev/helix 0.143.0 → 0.145.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.
Files changed (211) hide show
  1. package/custom-elements.json +19817 -1238
  2. package/dist/accordion/item/accordion-item.css.js +14 -3
  3. package/dist/accordion/item/accordion-item.d.ts +2 -0
  4. package/dist/aura/palette.d.ts +38 -0
  5. package/dist/aura/palette.js +22 -0
  6. package/dist/aura/root/aura-root.css.js +24 -3
  7. package/dist/aura/root/aura-root.d.ts +42 -7
  8. package/dist/aura/root/aura-root.js +31 -25
  9. package/dist/badge/root/badge-root.css.js +35 -0
  10. package/dist/badge/root/badge-root.d.ts +16 -0
  11. package/dist/badge/root/badge-root.js +3 -0
  12. package/dist/blockquote/root/blockquote-root.css.js +5 -5
  13. package/dist/blockquote/root/blockquote-root.d.ts +5 -0
  14. package/dist/breadcrumbs/breadcrumb/breadcrumb.css.js +7 -7
  15. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +5 -0
  16. package/dist/breadcrumbs/root/breadcrumbs-root.css.js +1 -1
  17. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +3 -0
  18. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -0
  19. package/dist/button/root/button-root.css.js +38 -1
  20. package/dist/button/root/button-root.d.ts +5 -0
  21. package/dist/button/root/button-root.js +2 -1
  22. package/dist/button-group/root/button-group-root.d.ts +3 -0
  23. package/dist/callout/root/callout-root.css.js +69 -11
  24. package/dist/callout/root/callout-root.d.ts +25 -2
  25. package/dist/callout/root/callout-root.js +4 -0
  26. package/dist/canvas/root/canvas-root.css.js +1 -0
  27. package/dist/card/root/card-root.css.js +26 -1
  28. package/dist/card/root/card-root.d.ts +11 -0
  29. package/dist/carousel/root/carousel-root.css.js +17 -1
  30. package/dist/checkbox-group/root/checkbox-group-root.d.ts +2 -0
  31. package/dist/checkbox-group/root/checkbox-group-root.js +2 -0
  32. package/dist/code-block/root/code-block-root.css.js +23 -0
  33. package/dist/code-block/root/code-block-root.d.ts +15 -0
  34. package/dist/code-block/root/code-block-root.js +8 -0
  35. package/dist/color-picker/root/color-picker-root.css.js +64 -0
  36. package/dist/color-picker/root/color-picker-root.d.ts +2 -0
  37. package/dist/copy-button/root/copy-button-root.css.js +5 -0
  38. package/dist/data-table/root/data-table-root.css.js +63 -0
  39. package/dist/data-table/root/data-table-root.d.ts +1 -0
  40. package/dist/date-input/root/date-input-root.css.js +19 -0
  41. package/dist/date-input/root/date-input-root.d.ts +8 -0
  42. package/dist/date-input/root/date-input-root.js +2 -0
  43. package/dist/date-picker/root/date-picker-root.css.js +68 -0
  44. package/dist/date-picker/root/date-picker-root.d.ts +1 -0
  45. package/dist/description-list/item/description-list-item.css.js +11 -0
  46. package/dist/dialog/root/dialog-root.css.js +47 -0
  47. package/dist/divider/root/divider-root.css.js +8 -3
  48. package/dist/divider/root/divider-root.d.ts +17 -1
  49. package/dist/divider/root/divider-root.js +4 -0
  50. package/dist/drawer/root/drawer-root.css.js +28 -1
  51. package/dist/drilldown/item/drilldown-item.css.js +2 -1
  52. package/dist/drilldown/root/drilldown-root.css.js +2 -1
  53. package/dist/element-events.generated.js +66 -14
  54. package/dist/empty/root/empty-root.css.js +9 -9
  55. package/dist/empty/root/empty-root.d.ts +9 -0
  56. package/dist/file-input/root/file-input-root.js +2 -0
  57. package/dist/filter-input/root/filter-input-root.js +2 -0
  58. package/dist/flex/root/flex-root.css.js +134 -9
  59. package/dist/flex/root/flex-root.d.ts +51 -5
  60. package/dist/flex/root/flex-root.js +17 -1
  61. package/dist/grid/root/grid-root.css.js +114 -11
  62. package/dist/grid/root/grid-root.d.ts +37 -0
  63. package/dist/grid/root/grid-root.js +13 -1
  64. package/dist/heatmap/root/heatmap-root.css.js +33 -5
  65. package/dist/heatmap/root/heatmap-root.d.ts +4 -1
  66. package/dist/icon/index.d.ts +1 -0
  67. package/dist/icon/index.js +1 -0
  68. package/dist/icon/root/icon-root.d.ts +1 -1
  69. package/dist/icon/root/icon-root.js +1 -1
  70. package/dist/icon/root/index.d.ts +2 -0
  71. package/dist/icon/root/index.js +2 -0
  72. package/dist/icon-button/root/icon-button-root.css.js +37 -0
  73. package/dist/icon-button/root/icon-button-root.d.ts +4 -0
  74. package/dist/icon-button/root/icon-button-root.js +2 -1
  75. package/dist/input/root/input-root.css.js +3 -3
  76. package/dist/input/root/input-root.d.ts +3 -0
  77. package/dist/legend/root/legend-root.css.js +40 -13
  78. package/dist/legend/root/legend-root.d.ts +6 -0
  79. package/dist/legend/root/legend-root.js +1 -1
  80. package/dist/lightbox/root/lightbox-root.css.js +84 -0
  81. package/dist/lightbox/root/lightbox-root.d.ts +1 -0
  82. package/dist/list/item/list-item.css.js +21 -1
  83. package/dist/list/item/list-item.d.ts +3 -0
  84. package/dist/list/root/list-root.css.js +13 -1
  85. package/dist/loader/root/loader-root.css.js +10 -0
  86. package/dist/loader/root/loader-root.d.ts +3 -0
  87. package/dist/menu/index.d.ts +1 -0
  88. package/dist/menu/index.js +1 -0
  89. package/dist/menu/item/menu-item.css.js +15 -1
  90. package/dist/menu/item/menu-item.d.ts +3 -0
  91. package/dist/menu/item/menu-item.js +2 -0
  92. package/dist/menu/section/menu-section.css.d.ts +1 -0
  93. package/dist/menu/section/menu-section.css.js +14 -0
  94. package/dist/menu/section/menu-section.d.ts +24 -0
  95. package/dist/menu/section/menu-section.js +16 -0
  96. package/dist/meter/root/meter-root.css.js +23 -1
  97. package/dist/meter/root/meter-root.d.ts +5 -0
  98. package/dist/navigation/root/navigation-root.css.js +3 -2
  99. package/dist/navigation/root/navigation-root.d.ts +6 -0
  100. package/dist/notifications/notification/notification.css.js +5 -0
  101. package/dist/notifications/root/notifications-root.css.js +24 -1
  102. package/dist/notifications/root/notifications-root.d.ts +1 -0
  103. package/dist/number-input/root/number-input-root.css.js +3 -3
  104. package/dist/number-input/root/number-input-root.d.ts +3 -0
  105. package/dist/object-inspector/root/object-inspector-root.css.js +36 -1
  106. package/dist/object-inspector/root/object-inspector-root.d.ts +4 -1
  107. package/dist/object-inspector/root/object-inspector-root.js +2 -1
  108. package/dist/orb/index.d.ts +1 -1
  109. package/dist/orb/index.js +1 -1
  110. package/dist/orb/root/orb-root.d.ts +31 -5
  111. package/dist/orb/root/orb-root.js +33 -4
  112. package/dist/outline/root/outline-root.css.js +10 -0
  113. package/dist/outline/root/outline-root.d.ts +7 -0
  114. package/dist/pin-input/root/pin-input-root.css.js +12 -5
  115. package/dist/pin-input/root/pin-input-root.d.ts +5 -0
  116. package/dist/popover/root/popover-root.css.js +36 -0
  117. package/dist/popover/root/popover-root.d.ts +1 -0
  118. package/dist/progress/root/progress-root.css.js +22 -1
  119. package/dist/progress/root/progress-root.d.ts +5 -0
  120. package/dist/quote/root/quote-root.css.js +1 -1
  121. package/dist/quote/root/quote-root.d.ts +1 -0
  122. package/dist/sandbox/frame.d.ts +63 -0
  123. package/dist/sandbox/frame.js +132 -0
  124. package/dist/sandbox/index.d.ts +1 -0
  125. package/dist/sandbox/index.js +1 -0
  126. package/dist/sandbox/levels.d.ts +10 -0
  127. package/dist/sandbox/levels.js +5 -0
  128. package/dist/sandbox/root/sandbox-root.d.ts +3 -0
  129. package/dist/sandbox/root/sandbox-root.js +13 -3
  130. package/dist/sandbox/source/sandbox-source.d.ts +1 -0
  131. package/dist/sandbox/source/sandbox-source.js +7 -3
  132. package/dist/sandbox/terminal/sandbox-terminal.css.js +39 -0
  133. package/dist/sandbox/terminal/sandbox-terminal.d.ts +42 -1
  134. package/dist/sandbox/terminal/sandbox-terminal.js +47 -3
  135. package/dist/scroll-area/root/scroll-area-root.css.js +83 -1
  136. package/dist/scroll-area/root/scroll-area-root.d.ts +6 -0
  137. package/dist/shared/chart/tooltip.css.js +25 -2
  138. package/dist/shared/field/field-segments.css.d.ts +1 -0
  139. package/dist/shared/field/field-segments.css.js +40 -0
  140. package/dist/skeleton/root/skeleton-root.css.js +56 -5
  141. package/dist/skeleton/root/skeleton-root.d.ts +17 -1
  142. package/dist/skeleton/root/skeleton-root.js +3 -0
  143. package/dist/skip-link/root/skip-link-root.css.js +8 -8
  144. package/dist/skip-link/root/skip-link-root.d.ts +9 -0
  145. package/dist/speed-dial/item/speed-dial-item.css.js +11 -4
  146. package/dist/speed-dial/item/speed-dial-item.d.ts +4 -0
  147. package/dist/speed-dial/root/speed-dial-root.css.js +7 -3
  148. package/dist/speed-dial/root/speed-dial-root.d.ts +9 -0
  149. package/dist/speed-dial/root/speed-dial-root.js +4 -0
  150. package/dist/stat/root/stat-root.css.js +7 -7
  151. package/dist/stat/root/stat-root.d.ts +8 -0
  152. package/dist/steps/step/step.js +2 -1
  153. package/dist/switch/root/switch-root.css.js +13 -5
  154. package/dist/switch/root/switch-root.d.ts +25 -1
  155. package/dist/switch/root/switch-root.js +7 -0
  156. package/dist/table/root/table-root.css.js +18 -0
  157. package/dist/table/root/table-root.d.ts +6 -0
  158. package/dist/table/root/table-root.js +2 -0
  159. package/dist/table/row/table-row.css.js +18 -0
  160. package/dist/table/row/table-row.d.ts +2 -0
  161. package/dist/table/row/table-row.js +2 -0
  162. package/dist/tabs/panel/tab-panel.css.js +5 -1
  163. package/dist/tabs/root/tabs-root.css.js +56 -1
  164. package/dist/tabs/tab/tab.css.js +5 -1
  165. package/dist/tag/group/tag-group.css.d.ts +1 -0
  166. package/dist/tag/group/tag-group.css.js +6 -0
  167. package/dist/tag/group/tag-group.d.ts +25 -0
  168. package/dist/tag/group/tag-group.js +16 -0
  169. package/dist/tag/index.d.ts +1 -0
  170. package/dist/tag/index.js +1 -0
  171. package/dist/tag/root/tag-root.css.js +50 -0
  172. package/dist/tag/root/tag-root.d.ts +4 -0
  173. package/dist/text/clamp.d.ts +8 -0
  174. package/dist/text/clamp.js +1 -0
  175. package/dist/text/root/text-root.css.js +85 -45
  176. package/dist/text/root/text-root.d.ts +48 -7
  177. package/dist/text/root/text-root.js +17 -11
  178. package/dist/textarea/root/textarea-root.css.js +12 -4
  179. package/dist/textarea/root/textarea-root.d.ts +4 -0
  180. package/dist/time-input/root/time-input-root.css.js +19 -0
  181. package/dist/time-input/root/time-input-root.d.ts +8 -0
  182. package/dist/time-input/root/time-input-root.js +2 -0
  183. package/dist/timeline/group/timeline-group.css.d.ts +1 -0
  184. package/dist/timeline/group/timeline-group.css.js +32 -0
  185. package/dist/timeline/group/timeline-group.d.ts +25 -0
  186. package/dist/timeline/group/timeline-group.js +16 -0
  187. package/dist/timeline/index.d.ts +1 -0
  188. package/dist/timeline/index.js +1 -0
  189. package/dist/timeline/item/timeline-item.css.js +43 -16
  190. package/dist/timeline/root/timeline-root.css.js +56 -0
  191. package/dist/timeline/root/timeline-root.d.ts +26 -0
  192. package/dist/timeline/root/timeline-root.js +7 -0
  193. package/dist/toast/item/toast-item.css.js +16 -0
  194. package/dist/toggle/group/toggle-group.css.js +16 -0
  195. package/dist/toolbar/root/toolbar-root.css.js +3 -0
  196. package/dist/toolbar/root/toolbar-root.d.ts +3 -0
  197. package/dist/tour/root/tour-root.css.js +8 -0
  198. package/dist/tour/stop/tour-stop.css.js +32 -0
  199. package/dist/tree/item/tree-item.css.js +72 -0
  200. package/dist/tree/item/tree-item.d.ts +10 -0
  201. package/dist/trend/root/trend-root.css.js +12 -5
  202. package/dist/trend/root/trend-root.d.ts +6 -1
  203. package/dist/video/root/video-root.css.js +6 -0
  204. package/dist/video/root/video-root.d.ts +3 -0
  205. package/dist/virtual-list/root/virtual-list-root.css.js +5 -0
  206. package/dist/virtual-list/root/virtual-list-root.d.ts +4 -3
  207. package/lint.json +1 -1
  208. package/package.json +6 -6
  209. package/types/jsx.d.ts +8620 -813
  210. package/types/svelte.d.ts +3 -0
  211. package/types/vue.d.ts +2372 -213
@@ -0,0 +1,63 @@
1
+ import type { PreviewRender } from './preview/sandbox-preview.js';
2
+ /** How `servePreview` serves a frame document. */
3
+ export interface ServePreviewOptions {
4
+ /**
5
+ * The element each example is written into. Defaults to `#preview-root`,
6
+ * and to the body where there is none.
7
+ */
8
+ root?: HTMLElement;
9
+ /**
10
+ * Whether the frame adds the rules for the four `layout` values, `padded`,
11
+ * `centered`, `hug` and `fullscreen`, to its own head. Defaults to `true`.
12
+ * Set `false` where your frame's stylesheet places the root itself.
13
+ */
14
+ layouts?: boolean;
15
+ /** Called before new markup replaces the old, to take down what it mounted. */
16
+ onReplace?: (root: HTMLElement) => void;
17
+ /** Called after each render, with whether the markup was new or only restyled. */
18
+ onRender?: (root: HTMLElement, fresh: boolean) => void;
19
+ }
20
+ /**
21
+ * The rules that place the root for each `layout` a preview sends.
22
+ *
23
+ * `centered` gives the example a measure and centers that box, and `hug`
24
+ * shrinks each top-level child to its own content and centers it, which is
25
+ * the difference for a component whose root is a full-width block. They
26
+ * select the root by its `data-preview-root` attribute, so they reach nothing
27
+ * an example draws inside it.
28
+ */
29
+ export declare const LAYOUT_RULES = "\n:root { --preview-measure: 48rem; }\n[data-preview-root][data-layout=\"padded\"] { padding: var(--helix-space-lg, 1.5rem); }\n[data-preview-root][data-layout=\"centered\"], [data-preview-root][data-layout=\"hug\"] {\n align-content: center; display: block; inline-size: min(100%, var(--preview-measure));\n margin-inline: auto; min-block-size: 100dvh; padding: var(--helix-space-lg, 1.5rem);\n}\n[data-preview-root][data-layout=\"centered\"] { text-align: center; }\n[data-preview-root][data-layout=\"centered\"] > * { margin-inline: auto; text-align: start; }\n[data-preview-root][data-layout=\"hug\"] > * {\n inline-size: fit-content; margin-inline: auto; max-inline-size: 100%;\n}\n[data-preview-root][data-layout=\"fullscreen\"] { min-block-size: 100dvh; padding: 0; }\n";
30
+ /** Whether `data` is a render message, as the preview sends one. */
31
+ export declare const isRender: (data: unknown) => data is PreviewRender;
32
+ /**
33
+ * The markup an example renders, without the stylesheet links it carries.
34
+ *
35
+ * An example that needs CSS says so the way a consumer would, with a `<link>`
36
+ * beside it, and the stylesheet arrives in the render message already read.
37
+ * The frame is on an opaque origin, so a request for the link would fail and
38
+ * log an error into the reader's console on every render.
39
+ */
40
+ export declare const withoutStylesheets: (source: string) => string;
41
+ /** A logged value as text, without throwing on a cycle. */
42
+ export declare const loggedText: (value: unknown) => string;
43
+ /**
44
+ * Serves the frame side of a sandbox preview, in the document its `src` loads.
45
+ *
46
+ * Call it once from that document, after the elements your examples use are
47
+ * defined. It does what the preview's protocol asks of a frame:
48
+ *
49
+ * - Posts `helix-preview-ready` once it listens.
50
+ * - Writes each `helix-preview-render` into the root: the markup, the
51
+ * stylesheet, the direction, the layout and the color mode. Scripts in the
52
+ * markup run.
53
+ * - Posts every console call and uncaught error as `helix-preview-console`.
54
+ * - Posts `helix-preview-size` whenever what was drawn changes height.
55
+ * - Posts `helix-preview-press` on every press, so menus outside close.
56
+ *
57
+ * Only a message from the parent window is rendered. Answers go to `*`, which
58
+ * is the only target a sandboxed frame can name, and carry only what the
59
+ * example itself wrote.
60
+ *
61
+ * @returns A function that stops serving and puts the console back.
62
+ */
63
+ export declare const servePreview: (options?: ServePreviewOptions) => (() => void);
@@ -0,0 +1,132 @@
1
+ export const LAYOUT_RULES = `
2
+ :root { --preview-measure: 48rem; }
3
+ [data-preview-root][data-layout="padded"] { padding: var(--helix-space-lg, 1.5rem); }
4
+ [data-preview-root][data-layout="centered"], [data-preview-root][data-layout="hug"] {
5
+ align-content: center; display: block; inline-size: min(100%, var(--preview-measure));
6
+ margin-inline: auto; min-block-size: 100dvh; padding: var(--helix-space-lg, 1.5rem);
7
+ }
8
+ [data-preview-root][data-layout="centered"] { text-align: center; }
9
+ [data-preview-root][data-layout="centered"] > * { margin-inline: auto; text-align: start; }
10
+ [data-preview-root][data-layout="hug"] > * {
11
+ inline-size: fit-content; margin-inline: auto; max-inline-size: 100%;
12
+ }
13
+ [data-preview-root][data-layout="fullscreen"] { min-block-size: 100dvh; padding: 0; }
14
+ `;
15
+ export const isRender = (data) => typeof data === 'object' &&
16
+ data !== null &&
17
+ data.type === 'helix-preview-render';
18
+ export const withoutStylesheets = (source) => source.replace(/<link\b[^>]*\brel=(["'])stylesheet\1[^>]*>\s*/gi, '');
19
+ export const loggedText = (value) => {
20
+ if (typeof value === 'string') {
21
+ return value;
22
+ }
23
+ try {
24
+ return JSON.stringify(value) ?? String(value);
25
+ }
26
+ catch {
27
+ return String(value);
28
+ }
29
+ };
30
+ const runScripts = (root) => {
31
+ for (const original of [...root.querySelectorAll('script')]) {
32
+ const script = document.createElement('script');
33
+ for (const { name, value } of original.attributes) {
34
+ script.setAttribute(name, value);
35
+ }
36
+ if (!script.type) {
37
+ script.type = 'module';
38
+ }
39
+ script.text = original.text;
40
+ original.replaceWith(script);
41
+ }
42
+ };
43
+ const drawn = (child) => {
44
+ const own = child.getBoundingClientRect();
45
+ if (own.width > 0 || own.height > 0) {
46
+ return [own];
47
+ }
48
+ return [...child.querySelectorAll('*')]
49
+ .filter((node) => getComputedStyle(node).position !== 'fixed')
50
+ .map((node) => node.getBoundingClientRect())
51
+ .filter((box) => box.width > 0 || box.height > 0);
52
+ };
53
+ export const servePreview = (options = {}) => {
54
+ const root = options.root ?? document.getElementById('preview-root') ?? document.body;
55
+ root.toggleAttribute('data-preview-root', true);
56
+ root.dataset.layout ??= 'padded';
57
+ if (options.layouts !== false) {
58
+ const rules = document.createElement('style');
59
+ rules.textContent = LAYOUT_RULES;
60
+ document.head.prepend(rules);
61
+ }
62
+ const post = (message) => window.parent?.postMessage(message, '*');
63
+ const levels = ['log', 'info', 'warn', 'error'];
64
+ const originals = levels.map((level) => console[level]);
65
+ for (const level of levels) {
66
+ const original = console[level];
67
+ console[level] = (...args) => {
68
+ original(...args);
69
+ post({ level, text: args.map(loggedText).join(' '), type: 'helix-preview-console' });
70
+ };
71
+ }
72
+ const failed = (event) => {
73
+ post({ level: 'error', text: event.message, type: 'helix-preview-console' });
74
+ };
75
+ const measure = () => {
76
+ const boxes = [...root.children].flatMap(drawn);
77
+ if (boxes.length === 0) {
78
+ return;
79
+ }
80
+ const top = Math.min(...boxes.map((box) => box.top));
81
+ const bottom = Math.max(...boxes.map((box) => box.bottom));
82
+ const style = getComputedStyle(root);
83
+ const padding = (Number.parseFloat(style.paddingBlockStart) || 0) +
84
+ (Number.parseFloat(style.paddingBlockEnd) || 0);
85
+ post({ size: Math.ceil(bottom - top + padding), type: 'helix-preview-size' });
86
+ };
87
+ let rendered;
88
+ const render = (event) => {
89
+ if (!isRender(event.data) || event.source !== window.parent) {
90
+ return;
91
+ }
92
+ const { colorMode, direction = 'ltr', layout = 'padded', source = '', styles = '' } = event.data;
93
+ document.documentElement.dir = direction;
94
+ root.dataset.layout = layout;
95
+ if (colorMode) {
96
+ document.documentElement.dataset.colorMode = colorMode;
97
+ document.documentElement.style.colorScheme = colorMode;
98
+ }
99
+ const fresh = source !== rendered;
100
+ if (fresh) {
101
+ options.onReplace?.(root);
102
+ root.innerHTML = withoutStylesheets(source);
103
+ runScripts(root);
104
+ rendered = source;
105
+ }
106
+ let sheet = document.getElementById('preview-styles');
107
+ if (!sheet) {
108
+ sheet = document.createElement('style');
109
+ sheet.id = 'preview-styles';
110
+ document.head.append(sheet);
111
+ }
112
+ sheet.textContent = styles;
113
+ options.onRender?.(root, fresh);
114
+ measure();
115
+ };
116
+ const press = () => post({ type: 'helix-preview-press' });
117
+ const watcher = new ResizeObserver(measure);
118
+ watcher.observe(root);
119
+ window.addEventListener('error', failed);
120
+ window.addEventListener('message', render);
121
+ document.addEventListener('pointerdown', press, { capture: true, passive: true });
122
+ post({ type: 'helix-preview-ready' });
123
+ return () => {
124
+ levels.forEach((level, index) => {
125
+ console[level] = originals[index];
126
+ });
127
+ watcher.disconnect();
128
+ window.removeEventListener('error', failed);
129
+ window.removeEventListener('message', render);
130
+ document.removeEventListener('pointerdown', press, { capture: true });
131
+ };
132
+ };
@@ -12,6 +12,7 @@
12
12
  * hardcodes `sandbox="allow-scripts allow-forms"` on its frame and never
13
13
  * `allow-same-origin`, which is the pairing that would undo it.
14
14
  */
15
+ export { isRender, LAYOUT_RULES, loggedText, type ServePreviewOptions, servePreview, withoutStylesheets, } from './frame.js';
15
16
  export * from './preview/sandbox-preview.js';
16
17
  export * from './root/sandbox-root.js';
17
18
  export * from './source/sandbox-source.js';
@@ -1,3 +1,4 @@
1
+ export { isRender, LAYOUT_RULES, loggedText, servePreview, withoutStylesheets, } from './frame.js';
1
2
  export * from './preview/sandbox-preview.js';
2
3
  export * from './root/sandbox-root.js';
3
4
  export * from './source/sandbox-source.js';
@@ -0,0 +1,10 @@
1
+ import type { ConsoleDetail } from '@fusion.dev/helix-core';
2
+ /** A console level, from the quietest to the loudest. */
3
+ export type ConsoleLevel = ConsoleDetail['level'];
4
+ /** The four levels in order, the quietest first. */
5
+ export declare const LEVELS: readonly ConsoleLevel[];
6
+ /**
7
+ * Whether a line at `level` shows under a terminal's `level`, which is the
8
+ * quietest it shows. No floor, or one that names no level, shows every line.
9
+ */
10
+ export declare const shows: (level: ConsoleLevel, floor: string | undefined) => boolean;
@@ -0,0 +1,5 @@
1
+ export const LEVELS = ['log', 'info', 'warn', 'error'];
2
+ export const shows = (level, floor) => {
3
+ const from = LEVELS.indexOf(floor);
4
+ return from < 0 || LEVELS.indexOf(level) >= from;
5
+ };
@@ -36,7 +36,10 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
36
36
  * @event {HelixBeforeSplitEvent} helix-before-split - Fired before the panes' divider is released, with the lead pane's share. Cancelable: cancel it to keep the current split.
37
37
  * @event {HelixCaretEvent} helix-caret - Fired when the cursor moves in the source. The source pane dispatches it, and you can listen for it on the sandbox.
38
38
  * @event {HelixConsoleEvent} helix-console - Fired once per console call in the preview frame. The preview pane dispatches it.
39
+ * @event {HelixEditEvent} helix-edit - Fired after the source was edited, with the new source. The source pane dispatches it, and you can listen for it on the sandbox.
40
+ * @event {HelixPanelResizeEvent} helix-panel-resize - Fired after the panel's divider settled at a new height, with the height in pixels.
39
41
  * @event {HelixResizeEvent} helix-resize - Fired by the preview pane when the height of the frame's content changes, with the new height.
42
+ * @event {HelixSplitEvent} helix-split - Fired after the panes' divider settled somewhere new, with the lead pane's share.
40
43
  *
41
44
  * @csspart body - The splitter holding the panes above the panel.
42
45
  * @csspart corner - The handle where the two dividers cross.
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixBeforePanelResizeEvent, HelixBeforeSplitEvent, HelixElement, requestChange, } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixBeforePanelResizeEvent, HelixBeforeSplitEvent, HelixElement, HelixPanelResizeEvent, HelixSplitEvent, requestChange, } from '@fusion.dev/helix-core';
8
8
  import '../../splitter/index.js';
9
9
  import { html } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
@@ -164,7 +164,8 @@ let HelixSandbox = class HelixSandbox extends HelixElement {
164
164
  return this.orientation ?? 'horizontal';
165
165
  }
166
166
  requestSplit(next) {
167
- return requestChange({
167
+ const was = this.split;
168
+ const accepted = requestChange({
168
169
  apply: (value) => {
169
170
  this.split = value;
170
171
  },
@@ -172,9 +173,14 @@ let HelixSandbox = class HelixSandbox extends HelixElement {
172
173
  host: this,
173
174
  next,
174
175
  });
176
+ if (accepted && this.split !== was) {
177
+ this.dispatchEvent(new HelixSplitEvent({ value: String(this.split) }));
178
+ }
179
+ return accepted;
175
180
  }
176
181
  requestPanelSize(next) {
177
- return requestChange({
182
+ const was = this.panelSize;
183
+ const accepted = requestChange({
178
184
  apply: (value) => {
179
185
  this.panelSize = value;
180
186
  },
@@ -182,6 +188,10 @@ let HelixSandbox = class HelixSandbox extends HelixElement {
182
188
  host: this,
183
189
  next,
184
190
  });
191
+ if (accepted && this.panelSize !== was) {
192
+ this.dispatchEvent(new HelixPanelResizeEvent({ value: String(this.panelSize) }));
193
+ }
194
+ return accepted;
185
195
  }
186
196
  answer(divider, position) {
187
197
  const target = divider;
@@ -18,6 +18,7 @@ import type { CSSResultGroup, TemplateResult } from 'lit';
18
18
  *
19
19
  * @event {HelixBeforeEditEvent} helix-before-edit - Fired before the source is edited. Cancelable: cancel it to keep the source unchanged.
20
20
  * @event {HelixCaretEvent} helix-caret - Fired when the cursor moves.
21
+ * @event {HelixEditEvent} helix-edit - Fired after the source was edited, with the new source.
21
22
  *
22
23
  * @csspart source - The editable region.
23
24
  *
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixBeforeEditEvent, HelixCaretEvent, HelixElement, onUnlessFalse, restoreValue, } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixBeforeEditEvent, HelixCaretEvent, HelixEditEvent, HelixElement, onUnlessFalse, restoreValue, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { styles } from './sandbox-source.css.js';
@@ -13,7 +13,7 @@ let HelixSandboxSource = class HelixSandboxSource extends HelixElement {
13
13
  constructor() {
14
14
  super(...arguments);
15
15
  this.fieldLabel = 'Example source';
16
- this.keysDescription = 'Tab inserts two spaces. To leave, press Escape and then Tab.';
16
+ this.keysDescription = 'Tab inserts two spaces. To leave, press Escape and then Tab, or press Shift+Tab to go back.';
17
17
  this.name = 'html';
18
18
  this.tabIndents = true;
19
19
  this.value = '';
@@ -55,10 +55,14 @@ let HelixSandboxSource = class HelixSandboxSource extends HelixElement {
55
55
  }
56
56
  edit(field, next) {
57
57
  const request = new HelixBeforeEditEvent({ value: next });
58
- if (this.dispatchEvent(request)) {
58
+ const accepted = this.dispatchEvent(request);
59
+ if (accepted) {
59
60
  this.value = next;
60
61
  }
61
62
  restoreValue(field, this.value);
63
+ if (accepted) {
64
+ this.dispatchEvent(new HelixEditEvent({ value: next }));
65
+ }
62
66
  }
63
67
  render() {
64
68
  return html `<textarea
@@ -7,6 +7,45 @@ grid-template-rows: minmax(0, 1fr);
7
7
  min-block-size: 0;
8
8
  }
9
9
 
10
+ :host([controls]) {
11
+ grid-template-rows: auto minmax(0, 1fr);
12
+ }
13
+
14
+ [part="controls"] {
15
+ align-items: center;
16
+ border-block-end: var(--helix-border-thin) solid var(--helix-color-border);
17
+ display: flex;
18
+ font-family: var(--helix-font-sans);
19
+ font-size: var(--helix-text-xs);
20
+ gap: var(--helix-space-xs);
21
+ justify-content: space-between;
22
+ padding: var(--helix-space-2xs) var(--helix-space-sm);
23
+ }
24
+
25
+ [part="filter"],
26
+ [part="clear"] {
27
+ background-color: transparent;
28
+ border: var(--helix-border-thin) solid var(--helix-color-border);
29
+ border-radius: var(--helix-radii-sm);
30
+ color: var(--helix-color-foreground);
31
+ font: inherit;
32
+ padding: var(--helix-space-2xs) var(--helix-space-xs);
33
+ }
34
+
35
+ [part="clear"] {
36
+ cursor: pointer;
37
+ }
38
+
39
+ [part="clear"]:hover {
40
+ background-color: var(--helix-color-surface-sunken);
41
+ }
42
+
43
+ [part="filter"]:focus-visible,
44
+ [part="clear"]:focus-visible {
45
+ outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);
46
+ outline-offset: var(--helix-size-focus-offset);
47
+ }
48
+
10
49
  [part="log"] {
11
50
  font-family: var(--helix-font-mono);
12
51
  font-size: var(--helix-text-xs);
@@ -1,5 +1,6 @@
1
1
  import { type ConsoleDetail, HelixElement } from '@fusion.dev/helix-core';
2
2
  import type { CSSResultGroup, TemplateResult } from 'lit';
3
+ import { type ConsoleLevel } from '../levels.js';
3
4
  /**
4
5
  * @summary A panel that shows what the previewed example wrote to the console.
5
6
  *
@@ -17,14 +18,21 @@ import type { CSSResultGroup, TemplateResult } from 'lit';
17
18
  * level is read before it, except a plain `log`, so an error does not sound
18
19
  * like any other line.
19
20
  *
21
+ * **`controls` adds a clear button and a level filter** above the log. Both
22
+ * are also yours to drive without them: `clear()` empties the log, and
23
+ * `level` hides the quieter lines.
24
+ *
20
25
  * @since 0.1.0
21
26
  * @status stable
22
27
  * @aliases Console, Output
23
28
  * @documentation https://fusion.dev/helix/components/sandbox
24
29
  *
30
+ * @csspart clear - The button that empties the log, under `controls`.
31
+ * @csspart controls - The row holding the clear button and the level filter, under `controls`.
25
32
  * @csspart count - How many times a repeated line was written.
26
- * @csspart empty - Shown while nothing has been written.
33
+ * @csspart empty - Shown while nothing has been written, or while the level filter hides every line.
27
34
  * @csspart entry - One line.
35
+ * @csspart filter - The level filter, a native select, under `controls`.
28
36
  * @csspart level - The console level a line was written at.
29
37
  * @csspart log - The scrolling region.
30
38
  * @csspart message - The text of a line.
@@ -32,6 +40,17 @@ import type { CSSResultGroup, TemplateResult } from 'lit';
32
40
  * @cssproperty --sandbox-terminal-size - Height of the scrolling region.
33
41
  */
34
42
  export declare class HelixSandboxTerminal extends HelixElement {
43
+ /**
44
+ * The clear button's text, under `controls`.
45
+ */
46
+ clearLabel: string;
47
+ /**
48
+ * Shows a clear button and a level filter above the log.
49
+ *
50
+ * Off by default, because a sandbox that puts the terminal in a panel often
51
+ * has a toolbar of its own for them.
52
+ */
53
+ controls: boolean;
35
54
  /**
36
55
  * Text shown before anything has been written.
37
56
  */
@@ -40,6 +59,24 @@ export declare class HelixSandboxTerminal extends HelixElement {
40
59
  * The log's name, read by screen readers.
41
60
  */
42
61
  label: string;
62
+ /**
63
+ * The quietest level shown: `info` hides plain logs, `warn` shows warnings
64
+ * and errors, and `error` shows errors only. Absent shows every line.
65
+ *
66
+ * Lines it hides are kept, so lowering it shows them again. The filter under
67
+ * `controls` sets it.
68
+ */
69
+ level?: ConsoleLevel | undefined;
70
+ /**
71
+ * The level filter's name, read by screen readers, under `controls`.
72
+ */
73
+ levelLabel: string;
74
+ /**
75
+ * What each choice of the level filter reads, for a page in another
76
+ * language, keyed by level, such as `{ warn: '…' }`. A level left out keeps
77
+ * its English words. As an attribute it is that object as JSON.
78
+ */
79
+ levelWords: Partial<Record<ConsoleLevel, string>>;
43
80
  private entries;
44
81
  /**
45
82
  * Whether the reader is at the bottom, and so wants to follow new output.
@@ -61,6 +98,10 @@ export declare class HelixSandboxTerminal extends HelixElement {
61
98
  /** Empties the log. */
62
99
  clear(): void;
63
100
  protected updated(): void;
101
+ /** Takes the level the reader chose in the filter. */
102
+ private readonly handleLevel;
103
+ /** The clear button and the level filter, under `controls`. */
104
+ private renderControls;
64
105
  protected render(): TemplateResult;
65
106
  static styles: CSSResultGroup;
66
107
  }
@@ -7,15 +7,30 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  import { customElement, HelixElement } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property, state } from 'lit/decorators.js';
10
+ import { LEVELS, shows } from '../levels.js';
10
11
  import { styles } from './sandbox-terminal.css.js';
12
+ const LEVEL_WORDS = {
13
+ error: 'Errors',
14
+ info: 'Info and above',
15
+ log: 'All levels',
16
+ warn: 'Warnings and errors',
17
+ };
11
18
  const LIMIT = 200;
12
19
  let HelixSandboxTerminal = class HelixSandboxTerminal extends HelixElement {
13
20
  constructor() {
14
21
  super(...arguments);
22
+ this.clearLabel = 'Clear';
23
+ this.controls = false;
15
24
  this.emptyLabel = 'Nothing written to the console yet.';
16
25
  this.label = 'Console output';
26
+ this.levelLabel = 'Levels shown';
27
+ this.levelWords = {};
17
28
  this.entries = [];
18
29
  this.following = true;
30
+ this.handleLevel = (event) => {
31
+ const chosen = event.target.value;
32
+ this.level = chosen === 'log' ? undefined : chosen;
33
+ };
19
34
  }
20
35
  get log() {
21
36
  return this.renderRoot.querySelector('[part="log"]');
@@ -44,11 +59,25 @@ let HelixSandboxTerminal = class HelixSandboxTerminal extends HelixElement {
44
59
  }
45
60
  }
46
61
  }
62
+ renderControls() {
63
+ if (!this.controls) {
64
+ return nothing;
65
+ }
66
+ return html `<div part="controls">
67
+ <select part="filter" aria-label=${this.levelLabel} @change=${this.handleLevel}>
68
+ ${LEVELS.map((one) => html `<option value=${one} ?selected=${(this.level ?? 'log') === one}>
69
+ ${this.levelWords[one] ?? LEVEL_WORDS[one]}
70
+ </option>`)}
71
+ </select>
72
+ <button part="clear" type="button" @click=${() => this.clear()}>${this.clearLabel}</button>
73
+ </div>`;
74
+ }
47
75
  render() {
48
- return html `<div part="log" role="log" aria-label=${this.label} tabindex="0">
49
- ${this.entries.length === 0
76
+ const shown = this.entries.filter((entry) => shows(entry.level, this.level));
77
+ return html `${this.renderControls()}<div part="log" role="log" aria-label=${this.label} tabindex="0">
78
+ ${shown.length === 0
50
79
  ? html `<p part="empty">${this.emptyLabel}</p>`
51
- : this.entries.map((entry) => html `<div part="entry" data-level=${entry.level}>
80
+ : shown.map((entry) => html `<div part="entry" data-level=${entry.level}>
52
81
  <span part="level" aria-hidden=${entry.level === 'log' ? 'true' : nothing}
53
82
  >${entry.level}</span
54
83
  >
@@ -63,12 +92,27 @@ let HelixSandboxTerminal = class HelixSandboxTerminal extends HelixElement {
63
92
  }
64
93
  static { this.styles = styles; }
65
94
  };
95
+ __decorate([
96
+ property({ attribute: 'clear-label' })
97
+ ], HelixSandboxTerminal.prototype, "clearLabel", void 0);
98
+ __decorate([
99
+ property({ reflect: true, type: Boolean })
100
+ ], HelixSandboxTerminal.prototype, "controls", void 0);
66
101
  __decorate([
67
102
  property({ attribute: 'empty-label' })
68
103
  ], HelixSandboxTerminal.prototype, "emptyLabel", void 0);
69
104
  __decorate([
70
105
  property()
71
106
  ], HelixSandboxTerminal.prototype, "label", void 0);
107
+ __decorate([
108
+ property()
109
+ ], HelixSandboxTerminal.prototype, "level", void 0);
110
+ __decorate([
111
+ property({ attribute: 'level-label' })
112
+ ], HelixSandboxTerminal.prototype, "levelLabel", void 0);
113
+ __decorate([
114
+ property({ attribute: 'level-words', type: Object })
115
+ ], HelixSandboxTerminal.prototype, "levelWords", void 0);
72
116
  __decorate([
73
117
  state()
74
118
  ], HelixSandboxTerminal.prototype, "entries", void 0);
@@ -21,6 +21,87 @@ var(--scroll-area-track-color, transparent);
21
21
  scrollbar-width: var(--scroll-area-scrollbar, thin);
22
22
  }
23
23
 
24
+ :host([scrollbars]) [part="viewport"] {
25
+ scrollbar-width: none;
26
+ }
27
+
28
+ [part~="scrollbar"] {
29
+ --_scroll-area-thumb: max(
30
+ var(--scroll-area-thumb-min-size, var(--helix-space-lg)),
31
+ var(--scroll-area-thumb-fraction, 1) *
32
+ 100%
33
+ );
34
+ background-color: var(--scroll-area-track-color, transparent);
35
+ opacity: 0;
36
+ position: absolute;
37
+ touch-action: none;
38
+ transition: opacity var(--helix-duration-fast) var(--helix-easing-standard);
39
+ user-select: none;
40
+
41
+ z-index: calc(var(--helix-layer-raised) + 1);
42
+ }
43
+
44
+ [part~="scrollbar-visible"] {
45
+ opacity: 1;
46
+ }
47
+
48
+ [part~="scrollbar-vertical"] {
49
+ inline-size: var(--scroll-area-scrollbar-size, var(--helix-space-sm));
50
+ inset-block: 0;
51
+ inset-inline-end: 0;
52
+ }
53
+
54
+ [part~="scrollbar-horizontal"] {
55
+ block-size: var(--scroll-area-scrollbar-size, var(--helix-space-sm));
56
+ inset-block-end: 0;
57
+ inset-inline: 0;
58
+ }
59
+
60
+ :host(:state(scrollbar-horizontal)) [part~="scrollbar-vertical"] {
61
+ inset-block-end: var(--scroll-area-scrollbar-size, var(--helix-space-sm));
62
+ }
63
+
64
+ :host(:state(scrollbar-vertical)) [part~="scrollbar-horizontal"] {
65
+ inset-inline-end: var(--scroll-area-scrollbar-size, var(--helix-space-sm));
66
+ }
67
+
68
+ [part~="thumb"] {
69
+ background-color: var(--scroll-area-thumb-color, var(--helix-color-border));
70
+ border-radius: var(--helix-radii-full);
71
+ position: absolute;
72
+ }
73
+
74
+ [part~="thumb"]:hover {
75
+ background-color: var(--scroll-area-thumb-hover-color, var(--helix-color-foreground-subtle));
76
+ }
77
+
78
+ [part~="thumb-vertical"] {
79
+ block-size: var(--_scroll-area-thumb);
80
+ inset-block-start: calc(
81
+ var(--scroll-area-scroll-fraction, 0) *
82
+ (100% - var(--_scroll-area-thumb))
83
+ );
84
+ inset-inline: var(--helix-border-thick);
85
+ }
86
+
87
+ [part~="thumb-horizontal"] {
88
+ inline-size: var(--_scroll-area-thumb);
89
+ inset-block: var(--helix-border-thick);
90
+ inset-inline-start: calc(
91
+ var(--scroll-area-scroll-fraction, 0) *
92
+ (100% - var(--_scroll-area-thumb))
93
+ );
94
+ }
95
+
96
+ [part="corner"] {
97
+ background-color: var(--scroll-area-track-color, transparent);
98
+ block-size: var(--scroll-area-scrollbar-size, var(--helix-space-sm));
99
+ inline-size: var(--scroll-area-scrollbar-size, var(--helix-space-sm));
100
+ inset-block-end: 0;
101
+ inset-inline-end: 0;
102
+ position: absolute;
103
+ }
104
+
24
105
  :host::before,
25
106
  :host::after {
26
107
  content: "";
@@ -110,7 +191,8 @@ opacity: 1;
110
191
  :host::before,
111
192
  :host::after,
112
193
  [part="edges"]::before,
113
- [part="edges"]::after {
194
+ [part="edges"]::after,
195
+ [part~="scrollbar"] {
114
196
  transition: none;
115
197
  }
116
198
  }
@@ -8,6 +8,9 @@ import type { CSSResultGroup } from 'lit';
8
8
  * styled to match the design system. The base class measures which edges have
9
9
  * content past them.
10
10
  *
11
+ * Under `scrollbars`, the browser's scrollbar is hidden and the component's own
12
+ * are drawn over the content at the edge, in the same colors.
13
+ *
11
14
  * @since 0.1.0
12
15
  * @status stable
13
16
  * @category layout
@@ -21,7 +24,10 @@ import type { CSSResultGroup } from 'lit';
21
24
  * @cssproperty --scroll-area-fade-inline-end - The depth of the fade at the inline end, or `0` to remove it.
22
25
  * @cssproperty --scroll-area-fade-inline-start - The depth of the fade at the inline start, or `0` to remove it.
23
26
  * @cssproperty --scroll-area-scrollbar - The scrollbar width, as a `scrollbar-width` keyword.
27
+ * @cssproperty --scroll-area-scrollbar-size - The thickness of each scrollbar drawn under `scrollbars`, and the size of the corner.
24
28
  * @cssproperty --scroll-area-thumb-color - The color of the scrollbar's thumb.
29
+ * @cssproperty --scroll-area-thumb-hover-color - The color of a thumb drawn under `scrollbars` while the pointer is over it.
30
+ * @cssproperty --scroll-area-thumb-min-size - The shortest a thumb drawn under `scrollbars` gets, however long the content.
25
31
  * @cssproperty --scroll-area-track-color - The color of the scrollbar's track.
26
32
  */
27
33
  export declare class HelixScrollArea extends HelixHeadlessScrollArea {