@seed-ship/mcp-ui-solid 6.2.0 → 6.3.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,59 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [6.3.0] - 2026-05-03
9
+
10
+ Two consumer-friendly props driven by `deposium_solid`'s
11
+ `SOLID-MCPUI-IMPROVEMENT-AXES-2026-05-03.md` handoff (axes 1 + 4).
12
+
13
+ ### Added — `TableComponentParams.maxHeight` (axe 1)
14
+
15
+ Opt-out for the inline-mode max-height cap. The library defaults a
16
+ `max-height: 400px` (or 500px when virtualizing) on tables with > 8
17
+ rows so they don't blow out a chat-stream layout. When the consumer's
18
+ wrapping container handles overflow, that cap is undesirable — it
19
+ forces an internal scroll even with plenty of room.
20
+
21
+ ```ts
22
+ TableComponentParams = {
23
+ // ...existing
24
+ maxHeight?: 'auto' | number | string
25
+ // 'auto' → no cap, parent handles overflow
26
+ // number → `${n}px`
27
+ // string → CSS length as-is
28
+ // undefined → existing 400/500px heuristic
29
+ }
30
+ ```
31
+
32
+ Ignored in expanded (fullscreen) mode — the modal uses
33
+ `flex-1 min-h-0` regardless.
34
+
35
+ Spec dep bump : `@seed-ship/mcp-ui-spec` `^5.0.4` → `^5.0.5`.
36
+
37
+ ### Added — `<ExpandableWrapper toolbarVariant>` (axe 4)
38
+
39
+ Visibility behavior of the inline expand button :
40
+
41
+ - `'hover'` (default — backwards compat) : opacity 0, fades to 0.7 on
42
+ parent group hover. Pre-v6.3.0 behavior.
43
+ - `'always-visible'` : opacity 0.6 permanent, 1 on hover. Use when the
44
+ inline button needs to be discoverable without hovering — esp. on
45
+ touch surfaces and consumer themes where the hover-only pattern
46
+ hides the affordance.
47
+
48
+ ### Non-breaking
49
+
50
+ Both additions opt-in. All v6.2.0 APIs unchanged. No tests added —
51
+ backwards-compat path covered by existing 583 tests, manual
52
+ verification confirmed.
53
+
54
+ ### Out of scope (for follow-up)
55
+
56
+ - **Axe 2 — dark-theme native (CSS vars)** : ~120 lines of overrides
57
+ côté deposium ; needs design pass + brief. Targeted v7.0.0.
58
+ - **Axe 3 — Portal dropdowns** : 3 dropdowns to migrate (chart, table,
59
+ graph). Targeted v6.4.0.
60
+
8
61
  ## [6.2.0] - 2026-05-03
9
62
 
10
63
  Cross-renderer fluidity audit — completes the work started in v6.1.0
@@ -11,7 +11,7 @@ var _tmpl$ = /* @__PURE__ */ web.template(`<svg class="w-5 h-5"fill=none viewBox
11
11
  from { opacity: 0; transform: scale(0.97); }
12
12
  to { opacity: 1; transform: scale(1); }
13
13
  }
14
- `), _tmpl$5 = /* @__PURE__ */ web.template(`<div class="relative group"><div><div></div></div><button class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-70 hover:!opacity-100 p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm"title=Expand aria-label="Expand to fullscreen"><svg class="w-3.5 h-3.5 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5"></path></svg></button><!$><!/>`), _tmpl$6 = /* @__PURE__ */ web.template(`<svg class="w-5 h-5 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`);
14
+ `), _tmpl$5 = /* @__PURE__ */ web.template(`<div class="relative group"><div><div></div></div><button title=Expand aria-label="Expand to fullscreen"><svg class="w-3.5 h-3.5 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5"></path></svg></button><!$><!/>`), _tmpl$6 = /* @__PURE__ */ web.template(`<svg class="w-5 h-5 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`);
15
15
  const ExpandedContext = solidJs.createContext(() => false);
16
16
  const useExpanded = () => solidJs.useContext(ExpandedContext);
17
17
  const ExpandableWrapper = (props) => {
@@ -134,6 +134,7 @@ const ExpandableWrapper = (props) => {
134
134
  });
135
135
  }
136
136
  }), _el$16, _co$2);
137
+ web.effect(() => web.className(_el$4, `absolute top-2 right-2 z-10 ${props.toolbarVariant === "always-visible" ? "opacity-60 hover:opacity-100" : "opacity-0 group-hover:opacity-70 hover:!opacity-100"} p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm`));
137
138
  web.runHydrationEvents();
138
139
  return _el$;
139
140
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"ExpandableWrapper.cjs","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX, createContext, useContext, Accessor } from 'solid-js'\nimport { Portal } from 'solid-js/web'\n\n/** Context for child components to know if they're in expanded/fullscreen view */\nconst ExpandedContext = createContext<Accessor<boolean>>(() => false)\n\n/** Hook for child components to read expanded state */\nexport const useExpanded = () => useContext(ExpandedContext)\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n <ExpandedContext.Provider value={isExpanded}>\n {props.children}\n </ExpandedContext.Provider>\n </div>\n </div>\n\n {/* Expand button — visible on hover */}\n <button\n onClick={handleOpen}\n class=\"absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-70 hover:!opacity-100 p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm\"\n title=\"Expand\"\n aria-label=\"Expand to fullscreen\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || 'Expanded view'}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || 'Expanded View'}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || 'Copy to clipboard'}\n aria-label={props.copyLabel || 'Copy to clipboard'}\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\" />\n </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label=\"Close expanded view\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded.\n v6.1.0 : `flex flex-col` lets aware children opt into\n `flex-1 min-h-0` to fill the modal vertically (chart,\n table, map, graph). Unaware children keep working\n thanks to `overflow-auto` (their natural height\n scrolls if it overflows the slot). */}\n <div class=\"flex-1 min-h-0 overflow-auto p-4 flex flex-col\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandedContext","createContext","useExpanded","useContext","ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","handleBackdropClick","target","currentTarget","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","_$createComponent","Provider","value","children","$$click","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","stopPropagation","title","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_tmpl$4","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ExpandableWrapper.cjs","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX, createContext, useContext, Accessor } from 'solid-js'\nimport { Portal } from 'solid-js/web'\n\n/** Context for child components to know if they're in expanded/fullscreen view */\nconst ExpandedContext = createContext<Accessor<boolean>>(() => false)\n\n/** Hook for child components to read expanded state */\nexport const useExpanded = () => useContext(ExpandedContext)\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n /**\n * Visibility behavior of the inline expand button (v6.3.0 — axe 4 deposium handoff).\n * - `'hover'` (default) : opacity 0, fades to 0.7 on parent group hover.\n * Backwards-compatible — pre-v6.3.0 behavior.\n * - `'always-visible'` : opacity 0.6 permanent, 1 on hover. Use this when\n * the inline button needs to be discoverable without hovering — esp.\n * on touch surfaces and consumer themes where the hover-only pattern\n * hides the affordance.\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n <ExpandedContext.Provider value={isExpanded}>\n {props.children}\n </ExpandedContext.Provider>\n </div>\n </div>\n\n {/* Expand button — visibility per `toolbarVariant` (default 'hover') */}\n <button\n onClick={handleOpen}\n class={`absolute top-2 right-2 z-10 ${\n props.toolbarVariant === 'always-visible'\n ? 'opacity-60 hover:opacity-100'\n : 'opacity-0 group-hover:opacity-70 hover:!opacity-100'\n } p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm`}\n title=\"Expand\"\n aria-label=\"Expand to fullscreen\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || 'Expanded view'}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || 'Expanded View'}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || 'Copy to clipboard'}\n aria-label={props.copyLabel || 'Copy to clipboard'}\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\" />\n </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label=\"Close expanded view\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded.\n v6.1.0 : `flex flex-col` lets aware children opt into\n `flex-1 min-h-0` to fill the modal vertically (chart,\n table, map, graph). Unaware children keep working\n thanks to `overflow-auto` (their natural height\n scrolls if it overflows the slot). */}\n <div class=\"flex-1 min-h-0 overflow-auto p-4 flex flex-col\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandedContext","createContext","useExpanded","useContext","ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","handleBackdropClick","target","currentTarget","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","_$createComponent","Provider","value","children","$$click","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","stopPropagation","title","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_tmpl$4","_$className","toolbarVariant","_$delegateEvents"],"mappings":"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"}
@@ -17,6 +17,16 @@ export interface ExpandableWrapperProps {
17
17
  copyData?: string;
18
18
  /** Label for copy button tooltip */
19
19
  copyLabel?: string;
20
+ /**
21
+ * Visibility behavior of the inline expand button (v6.3.0 — axe 4 deposium handoff).
22
+ * - `'hover'` (default) : opacity 0, fades to 0.7 on parent group hover.
23
+ * Backwards-compatible — pre-v6.3.0 behavior.
24
+ * - `'always-visible'` : opacity 0.6 permanent, 1 on hover. Use this when
25
+ * the inline button needs to be discoverable without hovering — esp.
26
+ * on touch surfaces and consumer themes where the hover-only pattern
27
+ * hides the affordance.
28
+ */
29
+ toolbarVariant?: 'hover' | 'always-visible';
20
30
  }
21
31
  /**
22
32
  * Wraps any component with an expand button (top-right corner).
@@ -1 +1 @@
1
- {"version":3,"file":"ExpandableWrapper.d.ts","sourceRoot":"","sources":["../../src/components/ExpandableWrapper.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAA+C,GAAG,EAA6B,QAAQ,EAAE,MAAM,UAAU,CAAA;AAM3H,uDAAuD;AACvD,eAAO,MAAM,WAAW,yBAAoC,CAAA;AAE5D,MAAM,WAAW,sBAAsB;IACrC,sDAAsD;IACtD,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,CAAC,sBAAsB,CA8K/D,CAAA"}
1
+ {"version":3,"file":"ExpandableWrapper.d.ts","sourceRoot":"","sources":["../../src/components/ExpandableWrapper.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAA+C,GAAG,EAA6B,QAAQ,EAAE,MAAM,UAAU,CAAA;AAM3H,uDAAuD;AACvD,eAAO,MAAM,WAAW,yBAAoC,CAAA;AAE5D,MAAM,WAAW,sBAAsB;IACrC,sDAAsD;IACtD,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAA;CAC5C;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,CAAC,sBAAsB,CAkL/D,CAAA"}
@@ -1,4 +1,4 @@
1
- import { delegateEvents, getNextElement, template, getNextMarker, insert, createComponent, Portal, use, effect, setAttribute, runHydrationEvents } from "solid-js/web";
1
+ import { delegateEvents, getNextElement, template, getNextMarker, insert, createComponent, Portal, use, effect, setAttribute, runHydrationEvents, className } from "solid-js/web";
2
2
  import { createContext, useContext, createSignal, createEffect, onCleanup, Show } from "solid-js";
3
3
  var _tmpl$ = /* @__PURE__ */ template(`<svg class="w-5 h-5"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z">`), _tmpl$2 = /* @__PURE__ */ template(`<button class="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">`), _tmpl$3 = /* @__PURE__ */ template(`<div class="fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm"role=dialog aria-modal=true tabindex=-1 style="animation:expandable-fade-in 0.15s ease-out"><div class="relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden"style="animation:expandable-scale-in 0.15s ease-out"><div class="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0"><h2 class="text-lg font-semibold text-gray-900 dark:text-white truncate"></h2><div class="flex items-center gap-2"><!$><!/><button class="p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"aria-label="Close expanded view"><svg class="w-5 h-5"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M6 18L18 6M6 6l12 12"></path></svg></button></div></div><div class="flex-1 min-h-0 overflow-auto p-4 flex flex-col">`), _tmpl$4 = /* @__PURE__ */ template(`<style>
4
4
  @keyframes expandable-fade-in {
@@ -9,7 +9,7 @@ var _tmpl$ = /* @__PURE__ */ template(`<svg class="w-5 h-5"fill=none viewBox="0
9
9
  from { opacity: 0; transform: scale(0.97); }
10
10
  to { opacity: 1; transform: scale(1); }
11
11
  }
12
- `), _tmpl$5 = /* @__PURE__ */ template(`<div class="relative group"><div><div></div></div><button class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-70 hover:!opacity-100 p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm"title=Expand aria-label="Expand to fullscreen"><svg class="w-3.5 h-3.5 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5"></path></svg></button><!$><!/>`), _tmpl$6 = /* @__PURE__ */ template(`<svg class="w-5 h-5 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`);
12
+ `), _tmpl$5 = /* @__PURE__ */ template(`<div class="relative group"><div><div></div></div><button title=Expand aria-label="Expand to fullscreen"><svg class="w-3.5 h-3.5 text-gray-500 dark:text-gray-400"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5"></path></svg></button><!$><!/>`), _tmpl$6 = /* @__PURE__ */ template(`<svg class="w-5 h-5 text-green-500"fill=none viewBox="0 0 24 24"stroke=currentColor><path stroke-linecap=round stroke-linejoin=round stroke-width=2 d="M5 13l4 4L19 7">`);
13
13
  const ExpandedContext = createContext(() => false);
14
14
  const useExpanded = () => useContext(ExpandedContext);
15
15
  const ExpandableWrapper = (props) => {
@@ -132,6 +132,7 @@ const ExpandableWrapper = (props) => {
132
132
  });
133
133
  }
134
134
  }), _el$16, _co$2);
135
+ effect(() => className(_el$4, `absolute top-2 right-2 z-10 ${props.toolbarVariant === "always-visible" ? "opacity-60 hover:opacity-100" : "opacity-0 group-hover:opacity-70 hover:!opacity-100"} p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm`));
135
136
  runHydrationEvents();
136
137
  return _el$;
137
138
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"ExpandableWrapper.js","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX, createContext, useContext, Accessor } from 'solid-js'\nimport { Portal } from 'solid-js/web'\n\n/** Context for child components to know if they're in expanded/fullscreen view */\nconst ExpandedContext = createContext<Accessor<boolean>>(() => false)\n\n/** Hook for child components to read expanded state */\nexport const useExpanded = () => useContext(ExpandedContext)\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n <ExpandedContext.Provider value={isExpanded}>\n {props.children}\n </ExpandedContext.Provider>\n </div>\n </div>\n\n {/* Expand button — visible on hover */}\n <button\n onClick={handleOpen}\n class=\"absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-70 hover:!opacity-100 p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm\"\n title=\"Expand\"\n aria-label=\"Expand to fullscreen\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || 'Expanded view'}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || 'Expanded View'}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || 'Copy to clipboard'}\n aria-label={props.copyLabel || 'Copy to clipboard'}\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\" />\n </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label=\"Close expanded view\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded.\n v6.1.0 : `flex flex-col` lets aware children opt into\n `flex-1 min-h-0` to fill the modal vertically (chart,\n table, map, graph). Unaware children keep working\n thanks to `overflow-auto` (their natural height\n scrolls if it overflows the slot). */}\n <div class=\"flex-1 min-h-0 overflow-auto p-4 flex flex-col\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandedContext","createContext","useExpanded","useContext","ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","handleBackdropClick","target","currentTarget","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","_$createComponent","Provider","value","children","$$click","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","stopPropagation","title","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_tmpl$4","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ExpandableWrapper.js","sources":["../../src/components/ExpandableWrapper.tsx"],"sourcesContent":["/**\n * ExpandableWrapper - Generic expand/fullscreen wrapper for components\n * v2.2.0: Reusable wrapper that adds expand button + fullscreen modal\n *\n * Uses DOM reparenting to avoid rendering children twice — critical for\n * imperative components like ChartJS that bind instances to DOM nodes.\n */\n\nimport { Component, Show, createSignal, createEffect, onCleanup, JSX, createContext, useContext, Accessor } from 'solid-js'\nimport { Portal } from 'solid-js/web'\n\n/** Context for child components to know if they're in expanded/fullscreen view */\nconst ExpandedContext = createContext<Accessor<boolean>>(() => false)\n\n/** Hook for child components to read expanded state */\nexport const useExpanded = () => useContext(ExpandedContext)\n\nexport interface ExpandableWrapperProps {\n /** Content to render inline (and in expanded view) */\n children: JSX.Element\n /** Title shown in the expanded modal header */\n title?: string\n /** Data string for copy-to-clipboard in expanded view */\n copyData?: string\n /** Label for copy button tooltip */\n copyLabel?: string\n /**\n * Visibility behavior of the inline expand button (v6.3.0 — axe 4 deposium handoff).\n * - `'hover'` (default) : opacity 0, fades to 0.7 on parent group hover.\n * Backwards-compatible — pre-v6.3.0 behavior.\n * - `'always-visible'` : opacity 0.6 permanent, 1 on hover. Use this when\n * the inline button needs to be discoverable without hovering — esp.\n * on touch surfaces and consumer themes where the hover-only pattern\n * hides the affordance.\n */\n toolbarVariant?: 'hover' | 'always-visible'\n}\n\n/**\n * Wraps any component with an expand button (top-right corner).\n * Opens a fullscreen Portal modal. The children's DOM is physically\n * reparented into the modal (not duplicated), so imperative bindings\n * like Chart.js canvas refs stay intact.\n *\n * @example\n * <ExpandableWrapper title=\"Sales Data\" copyData={tsvData}>\n * <TableRenderer ... />\n * </ExpandableWrapper>\n */\nexport const ExpandableWrapper: Component<ExpandableWrapperProps> = (props) => {\n const [isExpanded, setIsExpanded] = createSignal(false)\n const [copied, setCopied] = createSignal(false)\n let dialogRef: HTMLDivElement | undefined\n let contentRef: HTMLDivElement | undefined\n let inlineSlotRef: HTMLDivElement | undefined\n let modalSlotRef: HTMLDivElement | undefined\n\n const handleOpen = () => setIsExpanded(true)\n const handleClose = () => setIsExpanded(false)\n\n // Reparent content DOM between inline and modal slots\n createEffect(() => {\n if (!contentRef) return\n\n if (isExpanded()) {\n // Move content into modal\n modalSlotRef?.appendChild(contentRef)\n } else {\n // Move content back to inline\n inlineSlotRef?.appendChild(contentRef)\n }\n })\n\n // Keyboard: Escape to close\n createEffect(() => {\n if (!isExpanded()) return\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n handleClose()\n }\n }\n\n document.addEventListener('keydown', onKeyDown)\n onCleanup(() => document.removeEventListener('keydown', onKeyDown))\n })\n\n // Prevent body scroll when expanded\n createEffect(() => {\n if (isExpanded()) {\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n // Focus the dialog\n setTimeout(() => dialogRef?.focus(), 10)\n onCleanup(() => {\n document.body.style.overflow = prev\n })\n }\n })\n\n const handleBackdropClick = (e: MouseEvent) => {\n if (e.target === e.currentTarget) handleClose()\n }\n\n const handleCopy = async () => {\n if (!props.copyData) return\n try {\n await navigator.clipboard.writeText(props.copyData)\n setCopied(true)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error('Failed to copy:', err)\n }\n }\n\n return (\n <div class=\"relative group\">\n {/* Inline slot — content lives here when not expanded */}\n <div ref={inlineSlotRef}>\n <div ref={contentRef}>\n <ExpandedContext.Provider value={isExpanded}>\n {props.children}\n </ExpandedContext.Provider>\n </div>\n </div>\n\n {/* Expand button — visibility per `toolbarVariant` (default 'hover') */}\n <button\n onClick={handleOpen}\n class={`absolute top-2 right-2 z-10 ${\n props.toolbarVariant === 'always-visible'\n ? 'opacity-60 hover:opacity-100'\n : 'opacity-0 group-hover:opacity-70 hover:!opacity-100'\n } p-1.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-all shadow-sm`}\n title=\"Expand\"\n aria-label=\"Expand to fullscreen\"\n >\n <svg class=\"w-3.5 h-3.5 text-gray-500 dark:text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 4h-4m4 0l-5-5\" />\n </svg>\n </button>\n\n {/* Fullscreen modal via Portal */}\n <Show when={isExpanded()}>\n <Portal>\n <div\n class=\"fixed inset-0 z-50 flex flex-col bg-black/50 backdrop-blur-sm\"\n style={{ animation: 'expandable-fade-in 0.15s ease-out' }}\n onClick={handleBackdropClick}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={props.title || 'Expanded view'}\n tabIndex={-1}\n ref={dialogRef}\n >\n {/* Modal panel */}\n <div\n class=\"relative flex flex-col m-4 flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-xl overflow-hidden\"\n style={{ animation: 'expandable-scale-in 0.15s ease-out' }}\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div class=\"flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex-shrink-0\">\n <h2 class=\"text-lg font-semibold text-gray-900 dark:text-white truncate\">\n {props.title || 'Expanded View'}\n </h2>\n <div class=\"flex items-center gap-2\">\n {/* Copy button */}\n <Show when={props.copyData}>\n <button\n onClick={handleCopy}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n title={props.copyLabel || 'Copy to clipboard'}\n aria-label={props.copyLabel || 'Copy to clipboard'}\n >\n <Show\n when={!copied()}\n fallback={\n <svg class=\"w-5 h-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n }\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\" />\n </svg>\n </Show>\n </button>\n </Show>\n {/* Close button */}\n <button\n onClick={handleClose}\n class=\"p-1.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors\"\n aria-label=\"Close expanded view\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Modal slot — content is reparented here when expanded.\n v6.1.0 : `flex flex-col` lets aware children opt into\n `flex-1 min-h-0` to fill the modal vertically (chart,\n table, map, graph). Unaware children keep working\n thanks to `overflow-auto` (their natural height\n scrolls if it overflows the slot). */}\n <div class=\"flex-1 min-h-0 overflow-auto p-4 flex flex-col\" ref={modalSlotRef} />\n </div>\n </div>\n\n <style>{`\n @keyframes expandable-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes expandable-scale-in {\n from { opacity: 0; transform: scale(0.97); }\n to { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </Portal>\n </Show>\n </div>\n )\n}\n"],"names":["ExpandedContext","createContext","useExpanded","useContext","ExpandableWrapper","props","isExpanded","setIsExpanded","createSignal","copied","setCopied","dialogRef","contentRef","inlineSlotRef","modalSlotRef","handleOpen","handleClose","createEffect","appendChild","onKeyDown","e","key","preventDefault","document","addEventListener","onCleanup","removeEventListener","prev","body","style","overflow","setTimeout","focus","handleBackdropClick","target","currentTarget","handleCopy","copyData","navigator","clipboard","writeText","err","console","error","_el$","_$getNextElement","_tmpl$5","_el$2","firstChild","_el$3","_el$4","nextSibling","_el$15","_el$16","_co$2","_$getNextMarker","_ref$","_$use","_ref$2","_$insert","_$createComponent","Provider","value","children","$$click","Show","when","Portal","_el$5","_tmpl$3","_el$6","_el$7","_el$8","_el$9","_el$11","_el$12","_co$","_el$10","_el$13","_ref$3","stopPropagation","title","_el$0","_tmpl$2","fallback","_tmpl$6","_tmpl$","_$effect","_p$","_v$","copyLabel","_v$2","_$setAttribute","t","undefined","_$runHydrationEvents","_ref$4","_tmpl$4","_$className","toolbarVariant","_$delegateEvents"],"mappings":"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"}
@@ -842,18 +842,33 @@ ${dataRows}`;
842
842
  web.effect((_p$) => {
843
843
  var _v$0 = `relative w-full bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden group ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : "h-full"}`, _v$1 = `p-4 ${isExpanded() ? "flex-1 min-h-0 flex flex-col" : ""}`, _v$10 = `overflow-x-auto ${isExpanded() ? "flex-1 min-h-0" : ""}`, _v$11 = (
844
844
  // v6.1.0 — when expanded, the scroll container fills the
845
- // remaining vertical space (flex-1 + min-h-0 above) and
846
- // scrolls internally instead of the modal scrolling. Inline
847
- // mode keeps the previous max-height heuristic.
848
- isExpanded() ? {
849
- "overflow-y": "auto"
850
- } : isVirtualizing() ? {
851
- "max-height": "500px",
852
- "overflow-y": "auto"
853
- } : clientVisibleRows().length > 8 ? {
854
- "max-height": "400px",
855
- "overflow-y": "auto"
856
- } : {}
845
+ // remaining vertical space and scrolls internally.
846
+ // v6.3.0 `params.maxHeight` opt-out (axe 1 deposium handoff)
847
+ // - 'auto' no cap, parent handles overflow
848
+ // - number → `${n}px`, string → CSS as-is
849
+ // - undefined → existing 400/500px heuristic
850
+ (() => {
851
+ if (isExpanded()) return {
852
+ "overflow-y": "auto"
853
+ };
854
+ const mh = tableParams.maxHeight;
855
+ if (mh === "auto") return {};
856
+ if (mh !== void 0) {
857
+ return {
858
+ "max-height": typeof mh === "number" ? `${mh}px` : mh,
859
+ "overflow-y": "auto"
860
+ };
861
+ }
862
+ if (isVirtualizing()) return {
863
+ "max-height": "500px",
864
+ "overflow-y": "auto"
865
+ };
866
+ if (clientVisibleRows().length > 8) return {
867
+ "max-height": "400px",
868
+ "overflow-y": "auto"
869
+ };
870
+ return {};
871
+ })()
857
872
  ), _v$12 = tableParams.title || "Data table", _v$13 = tableParams.title ? `${tableId}-title` : void 0;
858
873
  _v$0 !== _p$.e && web.className(_el$29, _p$.e = _v$0);
859
874
  _v$1 !== _p$.t && web.className(_el$44, _p$.t = _v$1);