@k2b/ui 0.9.1 → 0.11.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 (180) hide show
  1. package/dist/browser/actions/Button.js +15 -7
  2. package/dist/browser/actions/Button.js.map +1 -1
  3. package/dist/browser/actions/ContextMenu.js +55 -15
  4. package/dist/browser/actions/ContextMenu.js.map +1 -1
  5. package/dist/browser/actions/Dropdown.js +39 -15
  6. package/dist/browser/actions/Dropdown.js.map +1 -1
  7. package/dist/browser/actions/FilterChip.js +1 -7
  8. package/dist/browser/actions/FilterChip.js.map +1 -1
  9. package/dist/browser/actions/SplitButton.js +0 -3
  10. package/dist/browser/actions/SplitButton.js.map +1 -1
  11. package/dist/browser/chat/ChatComposer.js +78 -91
  12. package/dist/browser/chat/ChatComposer.js.map +1 -1
  13. package/dist/browser/chat/ChatContextPopup.js +47 -21
  14. package/dist/browser/chat/ChatContextPopup.js.map +1 -1
  15. package/dist/browser/chat/ChatPrimitives.js +221 -232
  16. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  17. package/dist/browser/chat/index.js +3 -2
  18. package/dist/browser/chat/index.js.map +1 -1
  19. package/dist/browser/content/Chart.js +99 -106
  20. package/dist/browser/content/Chart.js.map +1 -1
  21. package/dist/browser/content/ChartExplorer.js +3 -6
  22. package/dist/browser/content/ChartExplorer.js.map +1 -1
  23. package/dist/browser/content/FileBrowser.js +1 -1
  24. package/dist/browser/content/FileBrowser.js.map +1 -1
  25. package/dist/browser/content/FileTree.js +1 -1
  26. package/dist/browser/content/FileTree.js.map +1 -1
  27. package/dist/browser/content/FileView.js +304 -287
  28. package/dist/browser/content/FileView.js.map +1 -1
  29. package/dist/browser/content/Lightbox.js +18 -0
  30. package/dist/browser/content/Lightbox.js.map +1 -1
  31. package/dist/browser/content/PdfPreview.js +68 -43
  32. package/dist/browser/content/PdfPreview.js.map +1 -1
  33. package/dist/browser/content/ZoomPanViewport.js +51 -52
  34. package/dist/browser/content/ZoomPanViewport.js.map +1 -1
  35. package/dist/browser/feedback/HoverPreview.js +391 -0
  36. package/dist/browser/feedback/HoverPreview.js.map +1 -0
  37. package/dist/browser/feedback/Tooltip.js +50 -8
  38. package/dist/browser/feedback/Tooltip.js.map +1 -1
  39. package/dist/browser/feedback/dialog-core.js +21 -3
  40. package/dist/browser/feedback/dialog-core.js.map +1 -1
  41. package/dist/browser/feedback/index.js +1 -0
  42. package/dist/browser/feedback/index.js.map +1 -1
  43. package/dist/browser/inputs/AutocompleteEditor.js +5 -4
  44. package/dist/browser/inputs/AutocompleteEditor.js.map +1 -1
  45. package/dist/browser/inputs/DatePicker.js +10 -0
  46. package/dist/browser/inputs/DatePicker.js.map +1 -1
  47. package/dist/browser/inputs/FileInputs.js +46 -36
  48. package/dist/browser/inputs/FileInputs.js.map +1 -1
  49. package/dist/browser/inputs/SelectChip.js +2 -1
  50. package/dist/browser/inputs/SelectChip.js.map +1 -1
  51. package/dist/browser/inputs/choice.js +5 -1
  52. package/dist/browser/inputs/choice.js.map +1 -1
  53. package/dist/browser/inputs/markdown/MarkdownEditor.js +173 -173
  54. package/dist/browser/inputs/markdown/MarkdownEditor.js.map +1 -1
  55. package/dist/browser/inputs/markdown/Toolbar.js +31 -34
  56. package/dist/browser/inputs/markdown/Toolbar.js.map +1 -1
  57. package/dist/browser/internal/focus-return.js +61 -0
  58. package/dist/browser/internal/focus-return.js.map +1 -0
  59. package/dist/browser/layout/AppOverview.js +1 -1
  60. package/dist/browser/layout/AppOverview.js.map +1 -1
  61. package/dist/browser/layout/AppWorkspace.js +105 -77
  62. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  63. package/dist/browser/layout/DetailPanel.js +16 -15
  64. package/dist/browser/layout/DetailPanel.js.map +1 -1
  65. package/dist/browser/layout/FloatingWindow.js +3 -1
  66. package/dist/browser/layout/FloatingWindow.js.map +1 -1
  67. package/dist/browser/layout/PanelDialog.js +10 -9
  68. package/dist/browser/layout/PanelDialog.js.map +1 -1
  69. package/dist/browser/layout/Panes.js +82 -81
  70. package/dist/browser/layout/Panes.js.map +1 -1
  71. package/dist/browser/layout/SidebarItemPreview.js +48 -136
  72. package/dist/browser/layout/SidebarItemPreview.js.map +1 -1
  73. package/dist/browser/layout/app-workspace-controller.js +35 -4
  74. package/dist/browser/layout/app-workspace-controller.js.map +1 -1
  75. package/dist/browser/surfaces/ProgressBar.js.map +1 -1
  76. package/dist/browser/surfaces/ProgressRing.js +72 -0
  77. package/dist/browser/surfaces/ProgressRing.js.map +1 -0
  78. package/dist/browser/surfaces/index.js +1 -0
  79. package/dist/browser/surfaces/index.js.map +1 -1
  80. package/dist/ssr/actions/Button.js +15 -7
  81. package/dist/ssr/actions/Button.js.map +1 -1
  82. package/dist/ssr/actions/ContextMenu.js +55 -15
  83. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  84. package/dist/ssr/actions/Dropdown.js +31 -12
  85. package/dist/ssr/actions/Dropdown.js.map +1 -1
  86. package/dist/ssr/actions/FilterChip.js +1 -7
  87. package/dist/ssr/actions/FilterChip.js.map +1 -1
  88. package/dist/ssr/actions/SplitButton.js +0 -3
  89. package/dist/ssr/actions/SplitButton.js.map +1 -1
  90. package/dist/ssr/chat/ChatComposer.js +61 -10
  91. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  92. package/dist/ssr/chat/ChatContextPopup.js +19 -5
  93. package/dist/ssr/chat/ChatContextPopup.js.map +1 -1
  94. package/dist/ssr/chat/ChatPrimitives.js +104 -74
  95. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  96. package/dist/ssr/chat/index.js +3 -2
  97. package/dist/ssr/chat/index.js.map +1 -1
  98. package/dist/ssr/content/Chart.js +22 -11
  99. package/dist/ssr/content/Chart.js.map +1 -1
  100. package/dist/ssr/content/ChartExplorer.js +3 -6
  101. package/dist/ssr/content/ChartExplorer.js.map +1 -1
  102. package/dist/ssr/content/FileBrowser.js +1 -1
  103. package/dist/ssr/content/FileBrowser.js.map +1 -1
  104. package/dist/ssr/content/FileTree.js +1 -1
  105. package/dist/ssr/content/FileTree.js.map +1 -1
  106. package/dist/ssr/content/FileView.js +105 -33
  107. package/dist/ssr/content/FileView.js.map +1 -1
  108. package/dist/ssr/content/Lightbox.js +17 -0
  109. package/dist/ssr/content/Lightbox.js.map +1 -1
  110. package/dist/ssr/content/PdfPreview.js +44 -14
  111. package/dist/ssr/content/PdfPreview.js.map +1 -1
  112. package/dist/ssr/content/ZoomPanViewport.js +11 -3
  113. package/dist/ssr/content/ZoomPanViewport.js.map +1 -1
  114. package/dist/ssr/feedback/HoverPreview.js +360 -0
  115. package/dist/ssr/feedback/HoverPreview.js.map +1 -0
  116. package/dist/ssr/feedback/Tooltip.js +50 -8
  117. package/dist/ssr/feedback/Tooltip.js.map +1 -1
  118. package/dist/ssr/feedback/dialog-core.js +21 -3
  119. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  120. package/dist/ssr/feedback/index.js +1 -0
  121. package/dist/ssr/feedback/index.js.map +1 -1
  122. package/dist/ssr/inputs/AutocompleteEditor.js +5 -4
  123. package/dist/ssr/inputs/AutocompleteEditor.js.map +1 -1
  124. package/dist/ssr/inputs/DatePicker.js +5 -0
  125. package/dist/ssr/inputs/DatePicker.js.map +1 -1
  126. package/dist/ssr/inputs/FileInputs.js +29 -11
  127. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  128. package/dist/ssr/inputs/SelectChip.js +2 -1
  129. package/dist/ssr/inputs/SelectChip.js.map +1 -1
  130. package/dist/ssr/inputs/choice.js +5 -1
  131. package/dist/ssr/inputs/choice.js.map +1 -1
  132. package/dist/ssr/inputs/markdown/MarkdownEditor.js +46 -10
  133. package/dist/ssr/inputs/markdown/MarkdownEditor.js.map +1 -1
  134. package/dist/ssr/inputs/markdown/Toolbar.js +30 -2
  135. package/dist/ssr/inputs/markdown/Toolbar.js.map +1 -1
  136. package/dist/ssr/internal/focus-return.js +61 -0
  137. package/dist/ssr/internal/focus-return.js.map +1 -0
  138. package/dist/ssr/layout/AppOverview.js +1 -1
  139. package/dist/ssr/layout/AppOverview.js.map +1 -1
  140. package/dist/ssr/layout/AppWorkspace.js +84 -66
  141. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  142. package/dist/ssr/layout/DetailPanel.js +16 -15
  143. package/dist/ssr/layout/DetailPanel.js.map +1 -1
  144. package/dist/ssr/layout/FloatingWindow.js +3 -1
  145. package/dist/ssr/layout/FloatingWindow.js.map +1 -1
  146. package/dist/ssr/layout/PanelDialog.js +10 -9
  147. package/dist/ssr/layout/PanelDialog.js.map +1 -1
  148. package/dist/ssr/layout/Panes.js +39 -8
  149. package/dist/ssr/layout/Panes.js.map +1 -1
  150. package/dist/ssr/layout/SidebarItemPreview.js +36 -108
  151. package/dist/ssr/layout/SidebarItemPreview.js.map +1 -1
  152. package/dist/ssr/layout/app-workspace-controller.js +35 -4
  153. package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
  154. package/dist/ssr/surfaces/ProgressBar.js.map +1 -1
  155. package/dist/ssr/surfaces/ProgressRing.js +31 -0
  156. package/dist/ssr/surfaces/ProgressRing.js.map +1 -0
  157. package/dist/ssr/surfaces/index.js +1 -0
  158. package/dist/ssr/surfaces/index.js.map +1 -1
  159. package/dist/styles.css +1 -1
  160. package/dist/types/actions/Button.d.ts +5 -0
  161. package/dist/types/actions/Dropdown.d.ts +5 -1
  162. package/dist/types/actions/SplitButton.d.ts +1 -0
  163. package/dist/types/chat/ChatContextPopup.d.ts +8 -0
  164. package/dist/types/chat/index.d.ts +3 -2
  165. package/dist/types/feedback/HoverPreview.d.ts +83 -0
  166. package/dist/types/feedback/Tooltip.d.ts +6 -0
  167. package/dist/types/feedback/index.d.ts +2 -0
  168. package/dist/types/feedback/prompts.d.ts +9 -6
  169. package/dist/types/inputs/SelectChip.d.ts +1 -0
  170. package/dist/types/inputs/completion/overlay.d.ts +3 -1
  171. package/dist/types/internal/focus-return.d.ts +20 -0
  172. package/dist/types/intl/Format.d.ts +16 -8
  173. package/dist/types/layout/AppWorkspace.d.ts +14 -0
  174. package/dist/types/layout/BottomSheet.d.ts +6 -1
  175. package/dist/types/layout/SidebarItemPreview.d.ts +1 -1
  176. package/dist/types/layout/app-workspace-controller.d.ts +5 -0
  177. package/dist/types/surfaces/ProgressBar.d.ts +1 -1
  178. package/dist/types/surfaces/ProgressRing.d.ts +13 -0
  179. package/dist/types/surfaces/index.d.ts +2 -0
  180. package/package.json +3 -2
@@ -7,10 +7,10 @@ import { createComponent as _$createComponent } from "solid-js/web";
7
7
  var _tmpl$ = ["<i", " aria-hidden=\"true\"></i>"],
8
8
  _tmpl$2 = ["<footer class=\"k2b-dialog__actions\">", "</footer>"],
9
9
  _tmpl$3 = ["<div class=\"k2b-dialog__panel k2b-dialog__stack\">", "<div class=\"k2b-zoom-pan-dialog\">", "", "</div></div>"],
10
- _tmpl$4 = ["<button type=\"button\" class=\"k2b-button k2b-icon-button\" data-variant=\"secondary\"", " title=\"", "\"><i class=\"", "\" aria-hidden=\"true\"></i></button>"],
10
+ _tmpl$4 = ["<i class=\"", "\" aria-hidden=\"true\"></i>"],
11
11
  _tmpl$5 = ["<div class=\"", "\" style=\"", "\" role=\"group\"", " tabindex=\"0\"", "><div class=\"k2b-zoom-pan__stage\"", " style=\"", "\">", "</div><div class=\"k2b-zoom-pan__controls\">", "", "", "", "</div></div>"];
12
12
  import { createSignal, For, onCleanup, onMount, Show } from "solid-js";
13
- import { Button } from "../actions/Button";
13
+ import { Button, IconButton } from "../actions/Button";
14
14
  import { dialogCore } from "../feedback/dialog-core";
15
15
  import { DialogHeader } from "../feedback/prompts";
16
16
  import { LocaleProvider, useLocale } from "../intl/locale";
@@ -287,7 +287,15 @@ export function ZoomPanViewport(props) {
287
287
  observer?.disconnect();
288
288
  });
289
289
  });
290
- const control = (label, shortcut, icon, action) => _$ssr(_tmpl$4, _$ssrAttribute("aria-label", _$escape(label, true), false), `${_$escape(label, true)} (${_$escape(shortcut, true)})`, `ti ${_$escape(icon, true)}`);
290
+ const control = (label, shortcut, icon, action) => _$createComponent(IconButton, {
291
+ variant: "secondary",
292
+ label: label,
293
+ tooltip: `${label} (${shortcut})`,
294
+ onClick: action,
295
+ get children() {
296
+ return _$ssr(_tmpl$4, `ti ${_$escape(icon, true)}`);
297
+ }
298
+ });
291
299
  return _$ssr(_tmpl$5, `k2b-zoom-pan ${_$escape(props.class ?? "", true)}`, _$ssrStyle(props.style), _$ssrAttribute("aria-label", _$escape(props.label, true), false) + _$ssrAttribute("aria-description", _$escape(messages().zoomPanKeys, true), false), _$ssrAttribute("data-controls", _$escape(props.controls ?? "visible", true), false) + _$ssrAttribute("data-zoomed", zoomed() ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-dragging", dragging() ? "true" : _$escape(undefined, true), false), _$ssrAttribute("data-smooth", smooth() ? "true" : _$escape(undefined, true), false), _$ssrStyleProperty("transform:", `translate(${_$escape(transform().x, true)}px, ${_$escape(transform().y, true)}px) scale(${_$escape(transform().scale, true)})`), _$escape(props.children), _$escape(control(messages().zoomIn, "+", "ti-plus", () => zoomBy(ZOOM_PAN_STEP))), _$escape(control(messages().zoomOut, "-", "ti-minus", () => zoomBy(1 / ZOOM_PAN_STEP))), _$escape(control(messages().resetZoom, "0", "ti-focus-centered", reset)), _$escape(_$createComponent(Show, {
292
300
  get when() {
293
301
  return props.fullscreen;
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["content/ZoomPanViewport.tsx"],"sourcesContent":["import { createSignal, For, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { Button } from \"../actions/Button\";\nimport { dialogCore } from \"../feedback/dialog-core\";\nimport { DialogHeader } from \"../feedback/prompts\";\nimport { LocaleProvider, useLocale } from \"../intl/locale\";\nimport { useUiMessages } from \"../intl/messages\";\nimport {\n clampZoomPan,\n panZoomPan,\n ZOOM_PAN_FIT,\n ZOOM_PAN_STEP,\n type ZoomPanGeometry,\n type ZoomPanTransform,\n zoomZoomPanAt,\n} from \"./zoom-pan\";\n\nexport type ZoomPanAction = {\n label: string;\n icon?: string;\n /** Runs the action; the button shows a pending state until a returned promise settles. Handle failures here. */\n onSelect: () => void | Promise<void>;\n};\n\nexport type ZoomPanFullscreen = {\n /** Heading and accessible name of the fullscreen dialog. */\n title: string;\n /** Renders a fresh copy of the content for the dialog. Called once per opening. */\n content: () => JSX.Element;\n /** Actions shown in the fullscreen dialog, for example exports. */\n actions?: readonly ZoomPanAction[];\n};\n\nexport type ZoomPanViewportProps = {\n /** Accessible name of the focusable viewport. */\n label: string;\n /** The zoomable content. Its first element is measured to keep it on screen. */\n children: JSX.Element;\n /** Enables the fullscreen control and the `F` shortcut. */\n fullscreen?: ZoomPanFullscreen;\n /** `hover` reveals the controls on hover, on focus, and while zoomed in. Touch screens always show them. */\n controls?: \"visible\" | \"hover\";\n class?: string;\n style?: JSX.CSSProperties;\n};\n\nconst PAN_KEY_STEP = 48;\nconst DRAG_THRESHOLD = 4;\nconst PAN_KEYS: Record<string, readonly [number, number]> = {\n ArrowLeft: [PAN_KEY_STEP, 0],\n ArrowRight: [-PAN_KEY_STEP, 0],\n ArrowUp: [0, PAN_KEY_STEP],\n ArrowDown: [0, -PAN_KEY_STEP],\n};\n\n/** Current screen transform of the stage, including a running CSS transition. */\nconst renderedTransform = (stage: HTMLElement, fallback: ZoomPanTransform): ZoomPanTransform => {\n const match = /^matrix\\(([^)]+)\\)$/.exec(getComputedStyle(stage).transform ?? \"\");\n const values = match?.[1]?.split(\",\").map(Number);\n if (!values || values.length !== 6 || values.some((value) => !Number.isFinite(value)) || !values[0]) return fallback;\n return { scale: values[0], x: values[4] ?? 0, y: values[5] ?? 0 };\n};\n\nconst ActionButton = (props: { action: ZoomPanAction }): JSX.Element => {\n const [pending, setPending] = createSignal(false);\n const run = async () => {\n if (pending()) return;\n setPending(true);\n try {\n await props.action.onSelect();\n } catch (error) {\n console.error(\"Zoom-pan action failed\", error);\n } finally {\n setPending(false);\n }\n };\n return (\n <Button variant=\"secondary\" loading={pending()} onClick={() => void run()}>\n <Show when={props.action.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n {props.action.label}\n </Button>\n );\n};\n\n/**\n * Makes any content zoomable (fit to 8×) and pannable with buttons, Ctrl/Cmd +\n * wheel, pinch, drag, and keys. It applies a CSS transform, so vector content\n * stays crisp and is never re-rendered. The view always starts at fit.\n */\nexport function ZoomPanViewport(props: ZoomPanViewportProps): JSX.Element {\n const messages = useUiMessages();\n const locale = useLocale();\n const [transform, setTransform] = createSignal<ZoomPanTransform>(ZOOM_PAN_FIT);\n const [smooth, setSmooth] = createSignal(false);\n const [dragging, setDragging] = createSignal(false);\n let root: HTMLDivElement | undefined;\n let stage: HTMLDivElement | undefined;\n const pointers = new Map<number, { x: number; y: number }>();\n let drag: { id: number; x: number; y: number; origin: ZoomPanTransform; moved: boolean } | undefined;\n let pinch: { distance: number; center: { x: number; y: number }; origin: ZoomPanTransform } | undefined;\n let suppressClick = false;\n\n const zoomed = () => transform().scale > 1;\n\n const geometry = (): ZoomPanGeometry => {\n const rect = root?.getBoundingClientRect();\n const width = rect?.width ?? 0;\n const height = rect?.height ?? 0;\n const content = stage?.firstElementChild?.getBoundingClientRect();\n if (!rect || !stage || !content || content.width <= 0 || content.height <= 0) {\n return { width, height, content: { left: 0, top: 0, width, height } };\n }\n const current = renderedTransform(stage, transform());\n return {\n width,\n height,\n content: {\n left: (content.left - rect.left - current.x) / current.scale,\n top: (content.top - rect.top - current.y) / current.scale,\n width: content.width / current.scale,\n height: content.height / current.scale,\n },\n };\n };\n\n const apply = (next: ZoomPanTransform, animate: boolean) => {\n setSmooth(animate);\n setTransform(next);\n };\n\n const local = (event: { clientX: number; clientY: number }) => {\n const rect = root?.getBoundingClientRect();\n return { x: event.clientX - (rect?.left ?? 0), y: event.clientY - (rect?.top ?? 0) };\n };\n\n const zoomBy = (factor: number, point?: { x: number; y: number }, animate = true) => {\n const box = geometry();\n const current = transform();\n apply(zoomZoomPanAt(current, current.scale * factor, point ?? { x: box.width / 2, y: box.height / 2 }, box), animate);\n };\n\n const reset = () => apply(ZOOM_PAN_FIT, true);\n\n const openFullscreen = () => {\n const fullscreen = props.fullscreen;\n if (!fullscreen) return;\n const currentLocale = locale();\n void dialogCore.open<void>(\n (close) => (\n <LocaleProvider locale={currentLocale}>\n <div class=\"k2b-dialog__panel k2b-dialog__stack\">\n <DialogHeader title={fullscreen.title} close={() => close()} />\n <div class=\"k2b-zoom-pan-dialog\">\n <ZoomPanViewport label={props.label}>{fullscreen.content()}</ZoomPanViewport>\n <Show when={fullscreen.actions?.length}>\n <footer class=\"k2b-dialog__actions\">\n <For each={fullscreen.actions}>{(action) => <ActionButton action={action} />}</For>\n </footer>\n </Show>\n </div>\n </div>\n </LocaleProvider>\n ),\n {\n panelClassName: \"k2b-dialog k2b-dialog--full\",\n ariaLabel: fullscreen.title,\n initialFocus: (dialog) => dialog.querySelector<HTMLElement>(\".k2b-zoom-pan\"),\n },\n );\n };\n\n const handleKeyDown: JSX.EventHandler<HTMLDivElement, KeyboardEvent> = (event) => {\n if (event.target !== root || event.ctrlKey || event.metaKey || event.altKey) return;\n const key = event.key;\n if (key === \"+\" || key === \"=\") zoomBy(ZOOM_PAN_STEP);\n else if (key === \"-\" || key === \"_\") zoomBy(1 / ZOOM_PAN_STEP);\n else if (key === \"0\") reset();\n else if ((key === \"f\" || key === \"F\") && props.fullscreen) openFullscreen();\n else {\n const delta = PAN_KEYS[key];\n // At fit the arrow keys keep scrolling the page.\n if (!delta || !zoomed()) return;\n apply(panZoomPan(transform(), delta[0], delta[1], geometry()), true);\n }\n event.preventDefault();\n };\n\n // Plain wheel keeps scrolling the page; Ctrl/Cmd + wheel (and trackpad pinch) zooms toward the pointer.\n const handleWheel: JSX.EventHandler<HTMLDivElement, WheelEvent> = (event) => {\n if (!event.ctrlKey && !event.metaKey) return;\n event.preventDefault();\n const lines = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? 400 : 1;\n const delta = Math.max(-50, Math.min(50, event.deltaY * lines));\n zoomBy(Math.exp(-delta / 100), local(event), false);\n };\n\n const handlePointerDown: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (event.button !== 0 || (event.target as Element).closest(\"button, a, input, select, textarea\")) return;\n pointers.set(event.pointerId, local(event));\n if (pointers.size === 2) {\n const [first, second] = [...pointers.values()] as [{ x: number; y: number }, { x: number; y: number }];\n pinch = {\n distance: Math.max(1, Math.hypot(second.x - first.x, second.y - first.y)),\n center: { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 },\n origin: transform(),\n };\n drag = undefined;\n } else if (pointers.size === 1 && zoomed()) {\n // Panning starts only above fit, so clicks and text selection keep working at fit.\n drag = { id: event.pointerId, x: event.clientX, y: event.clientY, origin: transform(), moved: false };\n } else return;\n try {\n event.currentTarget.setPointerCapture(event.pointerId);\n } catch {\n // A synthetic or already released pointer cannot be captured; moves still arrive while inside.\n }\n };\n\n const handlePointerMove: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (!pointers.has(event.pointerId)) return;\n pointers.set(event.pointerId, local(event));\n if (pinch && pointers.size >= 2) {\n const [first, second] = [...pointers.values()] as [{ x: number; y: number }, { x: number; y: number }];\n const distance = Math.hypot(second.x - first.x, second.y - first.y);\n const center = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 };\n const box = geometry();\n const scaled = zoomZoomPanAt(pinch.origin, pinch.origin.scale * (distance / pinch.distance), pinch.center, box);\n apply(panZoomPan(scaled, center.x - pinch.center.x, center.y - pinch.center.y, box), false);\n return;\n }\n if (drag?.id !== event.pointerId) return;\n const dx = event.clientX - drag.x;\n const dy = event.clientY - drag.y;\n if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n drag.moved = true;\n setDragging(true);\n apply(panZoomPan(drag.origin, dx, dy, geometry()), false);\n };\n\n const handlePointerEnd: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n pointers.delete(event.pointerId);\n if (pointers.size < 2) pinch = undefined;\n if (drag?.id !== event.pointerId) return;\n if (drag.moved) {\n // The click that follows this pointerup (if the browser sends one) comes in the same task.\n suppressClick = true;\n setTimeout(() => {\n suppressClick = false;\n });\n }\n drag = undefined;\n setDragging(false);\n };\n\n onMount(() => {\n const element = root;\n if (!element) return;\n // A drag must not also count as a click on the content or its owner.\n const click = (event: MouseEvent) => {\n if (!suppressClick) return;\n suppressClick = false;\n event.preventDefault();\n event.stopPropagation();\n };\n // Keep a two-finger gesture for pinch zoom instead of page panning.\n const touchMove = (event: TouchEvent) => {\n if (event.touches.length > 1) event.preventDefault();\n };\n element.addEventListener(\"click\", click, true);\n element.addEventListener(\"touchmove\", touchMove, { passive: false });\n const observer =\n typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(() => apply(clampZoomPan(transform(), geometry()), false));\n observer?.observe(element);\n onCleanup(() => {\n element.removeEventListener(\"click\", click, true);\n element.removeEventListener(\"touchmove\", touchMove);\n observer?.disconnect();\n });\n });\n\n const control = (label: string, shortcut: string, icon: string, action: () => void) => (\n <button\n type=\"button\"\n class=\"k2b-button k2b-icon-button\"\n data-variant=\"secondary\"\n aria-label={label}\n title={`${label} (${shortcut})`}\n onClick={action}\n >\n <i class={`ti ${icon}`} aria-hidden=\"true\" />\n </button>\n );\n\n return (\n <div\n ref={root}\n class={`k2b-zoom-pan ${props.class ?? \"\"}`}\n style={props.style}\n role=\"group\"\n aria-label={props.label}\n aria-description={messages().zoomPanKeys}\n tabIndex={0}\n data-controls={props.controls ?? \"visible\"}\n data-zoomed={zoomed() ? \"true\" : undefined}\n data-dragging={dragging() ? \"true\" : undefined}\n onKeyDown={handleKeyDown}\n onWheel={handleWheel}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerEnd}\n onPointerCancel={handlePointerEnd}\n >\n <div\n ref={stage}\n class=\"k2b-zoom-pan__stage\"\n data-smooth={smooth() ? \"true\" : undefined}\n style={{ transform: `translate(${transform().x}px, ${transform().y}px) scale(${transform().scale})` }}\n >\n {props.children}\n </div>\n <div class=\"k2b-zoom-pan__controls\">\n {control(messages().zoomIn, \"+\", \"ti-plus\", () => zoomBy(ZOOM_PAN_STEP))}\n {control(messages().zoomOut, \"-\", \"ti-minus\", () => zoomBy(1 / ZOOM_PAN_STEP))}\n {control(messages().resetZoom, \"0\", \"ti-focus-centered\", reset)}\n <Show when={props.fullscreen}>{control(messages().openFullscreen, \"F\", \"ti-maximize\", openFullscreen)}</Show>\n </div>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["content/ZoomPanViewport.tsx"],"sourcesContent":["import { createSignal, For, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { Button, IconButton } from \"../actions/Button\";\nimport { dialogCore } from \"../feedback/dialog-core\";\nimport { DialogHeader } from \"../feedback/prompts\";\nimport { LocaleProvider, useLocale } from \"../intl/locale\";\nimport { useUiMessages } from \"../intl/messages\";\nimport {\n clampZoomPan,\n panZoomPan,\n ZOOM_PAN_FIT,\n ZOOM_PAN_STEP,\n type ZoomPanGeometry,\n type ZoomPanTransform,\n zoomZoomPanAt,\n} from \"./zoom-pan\";\n\nexport type ZoomPanAction = {\n label: string;\n icon?: string;\n /** Runs the action; the button shows a pending state until a returned promise settles. Handle failures here. */\n onSelect: () => void | Promise<void>;\n};\n\nexport type ZoomPanFullscreen = {\n /** Heading and accessible name of the fullscreen dialog. */\n title: string;\n /** Renders a fresh copy of the content for the dialog. Called once per opening. */\n content: () => JSX.Element;\n /** Actions shown in the fullscreen dialog, for example exports. */\n actions?: readonly ZoomPanAction[];\n};\n\nexport type ZoomPanViewportProps = {\n /** Accessible name of the focusable viewport. */\n label: string;\n /** The zoomable content. Its first element is measured to keep it on screen. */\n children: JSX.Element;\n /** Enables the fullscreen control and the `F` shortcut. */\n fullscreen?: ZoomPanFullscreen;\n /** `hover` reveals the controls on hover, on focus, and while zoomed in. Touch screens always show them. */\n controls?: \"visible\" | \"hover\";\n class?: string;\n style?: JSX.CSSProperties;\n};\n\nconst PAN_KEY_STEP = 48;\nconst DRAG_THRESHOLD = 4;\nconst PAN_KEYS: Record<string, readonly [number, number]> = {\n ArrowLeft: [PAN_KEY_STEP, 0],\n ArrowRight: [-PAN_KEY_STEP, 0],\n ArrowUp: [0, PAN_KEY_STEP],\n ArrowDown: [0, -PAN_KEY_STEP],\n};\n\n/** Current screen transform of the stage, including a running CSS transition. */\nconst renderedTransform = (stage: HTMLElement, fallback: ZoomPanTransform): ZoomPanTransform => {\n const match = /^matrix\\(([^)]+)\\)$/.exec(getComputedStyle(stage).transform ?? \"\");\n const values = match?.[1]?.split(\",\").map(Number);\n if (!values || values.length !== 6 || values.some((value) => !Number.isFinite(value)) || !values[0]) return fallback;\n return { scale: values[0], x: values[4] ?? 0, y: values[5] ?? 0 };\n};\n\nconst ActionButton = (props: { action: ZoomPanAction }): JSX.Element => {\n const [pending, setPending] = createSignal(false);\n const run = async () => {\n if (pending()) return;\n setPending(true);\n try {\n await props.action.onSelect();\n } catch (error) {\n console.error(\"Zoom-pan action failed\", error);\n } finally {\n setPending(false);\n }\n };\n return (\n <Button variant=\"secondary\" loading={pending()} onClick={() => void run()}>\n <Show when={props.action.icon}>{(icon) => <i class={icon()} aria-hidden=\"true\" />}</Show>\n {props.action.label}\n </Button>\n );\n};\n\n/**\n * Makes any content zoomable (fit to 8×) and pannable with buttons, Ctrl/Cmd +\n * wheel, pinch, drag, and keys. It applies a CSS transform, so vector content\n * stays crisp and is never re-rendered. The view always starts at fit.\n */\nexport function ZoomPanViewport(props: ZoomPanViewportProps): JSX.Element {\n const messages = useUiMessages();\n const locale = useLocale();\n const [transform, setTransform] = createSignal<ZoomPanTransform>(ZOOM_PAN_FIT);\n const [smooth, setSmooth] = createSignal(false);\n const [dragging, setDragging] = createSignal(false);\n let root: HTMLDivElement | undefined;\n let stage: HTMLDivElement | undefined;\n const pointers = new Map<number, { x: number; y: number }>();\n let drag: { id: number; x: number; y: number; origin: ZoomPanTransform; moved: boolean } | undefined;\n let pinch: { distance: number; center: { x: number; y: number }; origin: ZoomPanTransform } | undefined;\n let suppressClick = false;\n\n const zoomed = () => transform().scale > 1;\n\n const geometry = (): ZoomPanGeometry => {\n const rect = root?.getBoundingClientRect();\n const width = rect?.width ?? 0;\n const height = rect?.height ?? 0;\n const content = stage?.firstElementChild?.getBoundingClientRect();\n if (!rect || !stage || !content || content.width <= 0 || content.height <= 0) {\n return { width, height, content: { left: 0, top: 0, width, height } };\n }\n const current = renderedTransform(stage, transform());\n return {\n width,\n height,\n content: {\n left: (content.left - rect.left - current.x) / current.scale,\n top: (content.top - rect.top - current.y) / current.scale,\n width: content.width / current.scale,\n height: content.height / current.scale,\n },\n };\n };\n\n const apply = (next: ZoomPanTransform, animate: boolean) => {\n setSmooth(animate);\n setTransform(next);\n };\n\n const local = (event: { clientX: number; clientY: number }) => {\n const rect = root?.getBoundingClientRect();\n return { x: event.clientX - (rect?.left ?? 0), y: event.clientY - (rect?.top ?? 0) };\n };\n\n const zoomBy = (factor: number, point?: { x: number; y: number }, animate = true) => {\n const box = geometry();\n const current = transform();\n apply(zoomZoomPanAt(current, current.scale * factor, point ?? { x: box.width / 2, y: box.height / 2 }, box), animate);\n };\n\n const reset = () => apply(ZOOM_PAN_FIT, true);\n\n const openFullscreen = () => {\n const fullscreen = props.fullscreen;\n if (!fullscreen) return;\n const currentLocale = locale();\n void dialogCore.open<void>(\n (close) => (\n <LocaleProvider locale={currentLocale}>\n <div class=\"k2b-dialog__panel k2b-dialog__stack\">\n <DialogHeader title={fullscreen.title} close={() => close()} />\n <div class=\"k2b-zoom-pan-dialog\">\n <ZoomPanViewport label={props.label}>{fullscreen.content()}</ZoomPanViewport>\n <Show when={fullscreen.actions?.length}>\n <footer class=\"k2b-dialog__actions\">\n <For each={fullscreen.actions}>{(action) => <ActionButton action={action} />}</For>\n </footer>\n </Show>\n </div>\n </div>\n </LocaleProvider>\n ),\n {\n panelClassName: \"k2b-dialog k2b-dialog--full\",\n ariaLabel: fullscreen.title,\n initialFocus: (dialog) => dialog.querySelector<HTMLElement>(\".k2b-zoom-pan\"),\n },\n );\n };\n\n const handleKeyDown: JSX.EventHandler<HTMLDivElement, KeyboardEvent> = (event) => {\n if (event.target !== root || event.ctrlKey || event.metaKey || event.altKey) return;\n const key = event.key;\n if (key === \"+\" || key === \"=\") zoomBy(ZOOM_PAN_STEP);\n else if (key === \"-\" || key === \"_\") zoomBy(1 / ZOOM_PAN_STEP);\n else if (key === \"0\") reset();\n else if ((key === \"f\" || key === \"F\") && props.fullscreen) openFullscreen();\n else {\n const delta = PAN_KEYS[key];\n // At fit the arrow keys keep scrolling the page.\n if (!delta || !zoomed()) return;\n apply(panZoomPan(transform(), delta[0], delta[1], geometry()), true);\n }\n event.preventDefault();\n };\n\n // Plain wheel keeps scrolling the page; Ctrl/Cmd + wheel (and trackpad pinch) zooms toward the pointer.\n const handleWheel: JSX.EventHandler<HTMLDivElement, WheelEvent> = (event) => {\n if (!event.ctrlKey && !event.metaKey) return;\n event.preventDefault();\n const lines = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? 400 : 1;\n const delta = Math.max(-50, Math.min(50, event.deltaY * lines));\n zoomBy(Math.exp(-delta / 100), local(event), false);\n };\n\n const handlePointerDown: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (event.button !== 0 || (event.target as Element).closest(\"button, a, input, select, textarea\")) return;\n pointers.set(event.pointerId, local(event));\n if (pointers.size === 2) {\n const [first, second] = [...pointers.values()] as [{ x: number; y: number }, { x: number; y: number }];\n pinch = {\n distance: Math.max(1, Math.hypot(second.x - first.x, second.y - first.y)),\n center: { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 },\n origin: transform(),\n };\n drag = undefined;\n } else if (pointers.size === 1 && zoomed()) {\n // Panning starts only above fit, so clicks and text selection keep working at fit.\n drag = { id: event.pointerId, x: event.clientX, y: event.clientY, origin: transform(), moved: false };\n } else return;\n try {\n event.currentTarget.setPointerCapture(event.pointerId);\n } catch {\n // A synthetic or already released pointer cannot be captured; moves still arrive while inside.\n }\n };\n\n const handlePointerMove: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n if (!pointers.has(event.pointerId)) return;\n pointers.set(event.pointerId, local(event));\n if (pinch && pointers.size >= 2) {\n const [first, second] = [...pointers.values()] as [{ x: number; y: number }, { x: number; y: number }];\n const distance = Math.hypot(second.x - first.x, second.y - first.y);\n const center = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 };\n const box = geometry();\n const scaled = zoomZoomPanAt(pinch.origin, pinch.origin.scale * (distance / pinch.distance), pinch.center, box);\n apply(panZoomPan(scaled, center.x - pinch.center.x, center.y - pinch.center.y, box), false);\n return;\n }\n if (drag?.id !== event.pointerId) return;\n const dx = event.clientX - drag.x;\n const dy = event.clientY - drag.y;\n if (!drag.moved && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;\n drag.moved = true;\n setDragging(true);\n apply(panZoomPan(drag.origin, dx, dy, geometry()), false);\n };\n\n const handlePointerEnd: JSX.EventHandler<HTMLDivElement, PointerEvent> = (event) => {\n pointers.delete(event.pointerId);\n if (pointers.size < 2) pinch = undefined;\n if (drag?.id !== event.pointerId) return;\n if (drag.moved) {\n // The click that follows this pointerup (if the browser sends one) comes in the same task.\n suppressClick = true;\n setTimeout(() => {\n suppressClick = false;\n });\n }\n drag = undefined;\n setDragging(false);\n };\n\n onMount(() => {\n const element = root;\n if (!element) return;\n // A drag must not also count as a click on the content or its owner.\n const click = (event: MouseEvent) => {\n if (!suppressClick) return;\n suppressClick = false;\n event.preventDefault();\n event.stopPropagation();\n };\n // Keep a two-finger gesture for pinch zoom instead of page panning.\n const touchMove = (event: TouchEvent) => {\n if (event.touches.length > 1) event.preventDefault();\n };\n element.addEventListener(\"click\", click, true);\n element.addEventListener(\"touchmove\", touchMove, { passive: false });\n const observer =\n typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(() => apply(clampZoomPan(transform(), geometry()), false));\n observer?.observe(element);\n onCleanup(() => {\n element.removeEventListener(\"click\", click, true);\n element.removeEventListener(\"touchmove\", touchMove);\n observer?.disconnect();\n });\n });\n\n const control = (label: string, shortcut: string, icon: string, action: () => void) => (\n <IconButton variant=\"secondary\" label={label} tooltip={`${label} (${shortcut})`} onClick={action}>\n <i class={`ti ${icon}`} aria-hidden=\"true\" />\n </IconButton>\n );\n\n return (\n <div\n ref={root}\n class={`k2b-zoom-pan ${props.class ?? \"\"}`}\n style={props.style}\n role=\"group\"\n aria-label={props.label}\n aria-description={messages().zoomPanKeys}\n tabIndex={0}\n data-controls={props.controls ?? \"visible\"}\n data-zoomed={zoomed() ? \"true\" : undefined}\n data-dragging={dragging() ? \"true\" : undefined}\n onKeyDown={handleKeyDown}\n onWheel={handleWheel}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerEnd}\n onPointerCancel={handlePointerEnd}\n >\n <div\n ref={stage}\n class=\"k2b-zoom-pan__stage\"\n data-smooth={smooth() ? \"true\" : undefined}\n style={{ transform: `translate(${transform().x}px, ${transform().y}px) scale(${transform().scale})` }}\n >\n {props.children}\n </div>\n <div class=\"k2b-zoom-pan__controls\">\n {control(messages().zoomIn, \"+\", \"ti-plus\", () => zoomBy(ZOOM_PAN_STEP))}\n {control(messages().zoomOut, \"-\", \"ti-minus\", () => zoomBy(1 / ZOOM_PAN_STEP))}\n {control(messages().resetZoom, \"0\", \"ti-focus-centered\", reset)}\n <Show when={props.fullscreen}>{control(messages().openFullscreen, \"F\", \"ti-maximize\", openFullscreen)}</Show>\n </div>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,360 @@
1
+ import { ssr as _$ssr } from "solid-js/web";
2
+ import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
3
+ import { escape as _$escape } from "solid-js/web";
4
+ import { createComponent as _$createComponent } from "solid-js/web";
5
+ var _tmpl$ = ["<div", " popover=\"auto\" role=\"dialog\"", " tabindex=\"-1\"", ">", "</div>"];
6
+ import { createEffect, createSignal, createUniqueId, onCleanup, onMount, Show } from "solid-js";
7
+ import { returnFocus, ringOnReturn } from "../internal/focus-return";
8
+ import { positionTooltipSurface } from "./tooltip-position";
9
+
10
+ /** A resting pointer moving from one anchor to another swaps the card this fast. */
11
+ const SWAP_DELAY = 90;
12
+ /** Grace period for the pointer to travel from an anchor into the card. */
13
+ const CLOSE_DELAY = 180;
14
+ /** Distance to the region and to the boundary edges. */
15
+ const GAP = 8;
16
+ /** Keys the wiring between a controller and its `<HoverPreview>`. */
17
+ const INTERNALS = Symbol("k2b-hover-preview");
18
+
19
+ /**
20
+ * Where the card opens. By default it opens to the right of its anchor,
21
+ * centered on it or aligned with its bottom edge, and flips or clamps to stay
22
+ * in the viewport. With `beside`, it opens right of that region, such as a list
23
+ * column, top-aligned with the anchor and clamped into `within` (default: the
24
+ * viewport); when the card does not fit there, it does not open at all.
25
+ */
26
+
27
+ /** Elements that use Space themselves; Space on them never toggles the card. */
28
+ const OWNS_SPACE = ["button, input, select, textarea, summary, [contenteditable]:not([contenteditable='false'])", ...["button", "checkbox", "switch", "radio", "option", "menuitem", "menuitemcheckbox", "menuitemradio", "tab", "treeitem"].map(role => `[role='${role}']`)].join(", ");
29
+
30
+ /** Open light-dismiss popovers, such as a menu or a date picker, close when another one opens. */
31
+ const AUTO_POPOVER = ":is([popover=''], [popover='auto' i]):popover-open";
32
+
33
+ /**
34
+ * Creates the behavior of one hover preview card shared by a group of anchors,
35
+ * such as the rows of a list. Render the card with `<HoverPreview preview={…}>`.
36
+ */
37
+ export function createHoverPreview(options = {}) {
38
+ const id = `k2b-hover-preview-${createUniqueId()}`;
39
+ const anchors = new Set();
40
+ const [current, setCurrent] = createSignal();
41
+ const [open, setOpen] = createSignal(false);
42
+ let surface;
43
+ let timer;
44
+ /** The anchor whose open delay runs while the card is closed. */
45
+ let pending;
46
+ /** The anchor that Space or `toggle()` opened the card for; only it outlasts the pointer leaving. */
47
+ let pinned;
48
+ let dismissed;
49
+ let ring = true;
50
+ let observer;
51
+ const placement = () => options.placement ?? {};
52
+ const region = () => {
53
+ const value = placement();
54
+ return "beside" in value ? value : undefined;
55
+ };
56
+ const focusTarget = anchor => anchor.trigger?.() ?? anchor.element;
57
+ const anchorOf = node => [...anchors].find(anchor => anchor.element.contains(node));
58
+ const clear = () => {
59
+ clearTimeout(timer);
60
+ timer = undefined;
61
+ if (pending && !open()) document.removeEventListener("keydown", escape);
62
+ pending = undefined;
63
+ };
64
+ /** A passive card never closes a menu or another popover someone opened on purpose. */
65
+ const blocked = () => [...document.querySelectorAll(AUTO_POPOVER)].some(popover => popover !== surface && !(surface && popover.contains(surface)));
66
+
67
+ /** The boundary rectangle, never larger than the viewport. */
68
+ const bounds = within => {
69
+ const rect = within?.getBoundingClientRect();
70
+ return {
71
+ top: Math.max(0, rect?.top ?? 0),
72
+ right: Math.min(window.innerWidth, rect?.right ?? window.innerWidth),
73
+ bottom: Math.min(window.innerHeight, rect?.bottom ?? window.innerHeight)
74
+ };
75
+ };
76
+ /** Room right of the region for the card plus a gap on both sides. Works while the card is hidden. */
77
+ const hasRoom = () => {
78
+ const beside = region();
79
+ if (!beside || !surface) return true;
80
+ const area = beside.beside()?.getBoundingClientRect();
81
+ if (!area) return false;
82
+ const width = surface.getBoundingClientRect().width || Number.parseFloat(getComputedStyle(surface).width) || 0;
83
+ return bounds(beside.within?.()).right - area.right >= width + 2 * GAP;
84
+ };
85
+ const position = () => {
86
+ const anchor = current();
87
+ if (!surface || !anchor) return;
88
+ const beside = region();
89
+ if (beside) {
90
+ const area = beside.beside()?.getBoundingClientRect() ?? anchor.element.getBoundingClientRect();
91
+ const box = bounds(beside.within?.());
92
+ const height = surface.getBoundingClientRect().height;
93
+ const top = anchor.element.getBoundingClientRect().top;
94
+ surface.style.left = `${Math.round(area.right + GAP)}px`;
95
+ surface.style.top = `${Math.round(Math.max(box.top + GAP, Math.min(top, box.bottom - height - GAP)))}px`;
96
+ return;
97
+ }
98
+ positionTooltipSurface(surface, anchor.element, "right");
99
+ const value = placement();
100
+ if (!("beside" in value) && value.align === "end") {
101
+ const height = surface.getBoundingClientRect().height;
102
+ const bottom = anchor.element.getBoundingClientRect().bottom;
103
+ surface.style.top = `${Math.round(Math.max(8, Math.min(bottom - height, window.innerHeight - height - 8)))}px`;
104
+ }
105
+ };
106
+ const escape = event => {
107
+ if (event.key !== "Escape") return;
108
+ if (!open()) {
109
+ // Escape during the open delay works as if the card had opened and closed.
110
+ if (pending) dismissed = pending;
111
+ clear();
112
+ return;
113
+ }
114
+ // Focus in the card goes back to the trigger: handled here, so the browser
115
+ // neither returns it itself nor lights the ring for this key. A card the
116
+ // pointer opened leaves the key to other handlers, such as an open modeless dialog.
117
+ if (surface?.contains(document.activeElement)) event.preventDefault();
118
+ dismiss();
119
+ };
120
+ // A card beside a region belongs to a row of a scrolling list: once the list
121
+ // scrolls, another row sits under the pointer, so the card closes. A card
122
+ // beside its anchor follows it.
123
+ const scroll = event => {
124
+ const target = event.target;
125
+ if (!region()) return position();
126
+ if (!(target instanceof Node) || surface?.contains(target)) return;
127
+ const anchor = current();
128
+ if (!anchor || target.contains(anchor.element)) hide();
129
+ };
130
+ const resize = () => {
131
+ if (hasRoom()) position();else hide();
132
+ };
133
+ const listen = on => {
134
+ if (on) {
135
+ document.addEventListener("keydown", escape);
136
+ window.addEventListener("scroll", scroll, true);
137
+ window.addEventListener("resize", resize);
138
+ return;
139
+ }
140
+ document.removeEventListener("keydown", escape);
141
+ window.removeEventListener("scroll", scroll, true);
142
+ window.removeEventListener("resize", resize);
143
+ };
144
+ const hide = () => {
145
+ clear();
146
+ pinned = undefined;
147
+ if (!open()) return;
148
+ setOpen(false);
149
+ listen(false);
150
+ try {
151
+ surface?.hidePopover();
152
+ } catch {
153
+ // The card may already be disconnected during cleanup.
154
+ }
155
+ };
156
+ const dismiss = () => {
157
+ const anchor = current();
158
+ dismissed = anchor;
159
+ // Before hiding, so the popover does not return focus on its own.
160
+ if (anchor && surface?.contains(document.activeElement)) returnFocus(focusTarget(anchor), ring);
161
+ hide();
162
+ };
163
+ /**
164
+ * Opens the card for `anchor` or moves it there; `pin` keeps it open after
165
+ * the pointer leaves. Moving the card to another anchor drops an earlier pin.
166
+ * Returns whether it is open for `anchor`.
167
+ */
168
+ const show = (anchor, pin) => {
169
+ clear();
170
+ if (!surface || dismissed === anchor) return false;
171
+ if (options.disabled?.(anchor.value) || !hasRoom()) {
172
+ hide();
173
+ return false;
174
+ }
175
+ if (!open() || current() !== anchor) {
176
+ ring = ringOnReturn(focusTarget(anchor));
177
+ setCurrent(anchor);
178
+ if (!open()) {
179
+ try {
180
+ surface.showPopover();
181
+ } catch {
182
+ return false;
183
+ }
184
+ setOpen(true);
185
+ listen(true);
186
+ }
187
+ position();
188
+ pinned = undefined;
189
+ }
190
+ if (pin) pinned = anchor;
191
+ return true;
192
+ };
193
+ /** Shows `anchor` after `delay`; a keyboard swap (`follow`) carries an existing pin along. */
194
+ const schedule = (anchor, delay, follow = false) => {
195
+ clear();
196
+ if (!open()) {
197
+ pending = anchor;
198
+ document.addEventListener("keydown", escape);
199
+ }
200
+ timer = setTimeout(() => {
201
+ if (open()) show(anchor, follow && pinned !== undefined);else if (blocked()) clear();else show(anchor, false);
202
+ }, delay);
203
+ };
204
+ const leave = () => {
205
+ clear();
206
+ dismissed = undefined;
207
+ if (pinned && pinned === current()) return;
208
+ timer = setTimeout(() => {
209
+ // Focus holds the card open inside it, and on the anchor where focus opens it.
210
+ const focused = document.activeElement;
211
+ const held = surface?.contains(focused) || options.keyboard === "focus" && current()?.element.contains(focused);
212
+ if (!held) hide();
213
+ }, CLOSE_DELAY);
214
+ };
215
+ const focusOut = event => {
216
+ const next = event.relatedTarget;
217
+ if (next instanceof Node && (surface?.contains(next) || anchorOf(next))) return;
218
+ pinned = undefined;
219
+ leave();
220
+ };
221
+ createEffect(() => {
222
+ const anchor = current();
223
+ if (open() && anchor && options.disabled?.(anchor.value)) hide();
224
+ });
225
+ // Only an open card holds global listeners, so a server render that
226
+ // disposes the controller touches no browser globals.
227
+ onCleanup(() => {
228
+ hide();
229
+ observer?.disconnect();
230
+ });
231
+ const anchor = (value, trigger) => element => {
232
+ const entry = {
233
+ value,
234
+ element,
235
+ trigger
236
+ };
237
+ anchors.add(entry);
238
+ const pointerEnter = event => {
239
+ // Touch and pen have no resting hover; the card stays a mouse affordance.
240
+ if (event.pointerType !== "mouse") return;
241
+ clear();
242
+ if (open() && current() === entry || dismissed === entry) return;
243
+ schedule(entry, open() ? SWAP_DELAY : options.openDelay ?? 250);
244
+ };
245
+ // The open delay counts from the moment the mouse rests. An open card
246
+ // swaps on entering another anchor and ignores movement inside it.
247
+ const pointerMove = event => {
248
+ if (event.pointerType === "mouse" && !open() && dismissed !== entry) schedule(entry, options.openDelay ?? 250);
249
+ };
250
+ // A press acts on the row, such as opening it or its menu; a card about to open stays closed.
251
+ const pointerDown = () => clear();
252
+ const focusIn = () => {
253
+ if (open()) {
254
+ if (current() === entry) clear();else schedule(entry, SWAP_DELAY, true);
255
+ return;
256
+ }
257
+ clear();
258
+ if (options.keyboard === "focus" && dismissed !== entry) schedule(entry, options.openDelay ?? 250);
259
+ };
260
+ const keyDown = event => {
261
+ if (options.keyboard === "focus" || event.key !== " " || event.defaultPrevented) return;
262
+ // Shift+Space keeps scrolling up; other modifiers belong to shortcuts.
263
+ if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) return;
264
+ if (event.target instanceof Element && event.target.matches(OWNS_SPACE)) return;
265
+ event.preventDefault();
266
+ // A held key toggles once; its repeats only keep the page from scrolling.
267
+ if (event.repeat) return;
268
+ if (open() && current() === entry) {
269
+ dismiss();
270
+ return;
271
+ }
272
+ dismissed = undefined;
273
+ show(entry, true);
274
+ };
275
+ element.addEventListener("pointerenter", pointerEnter);
276
+ element.addEventListener("pointermove", pointerMove);
277
+ element.addEventListener("pointerdown", pointerDown);
278
+ element.addEventListener("pointerleave", leave);
279
+ element.addEventListener("focusin", focusIn);
280
+ element.addEventListener("focusout", focusOut);
281
+ element.addEventListener("keydown", keyDown);
282
+ onCleanup(() => {
283
+ anchors.delete(entry);
284
+ if (current() === entry) hide();
285
+ element.removeEventListener("pointerenter", pointerEnter);
286
+ element.removeEventListener("pointermove", pointerMove);
287
+ element.removeEventListener("pointerdown", pointerDown);
288
+ element.removeEventListener("pointerleave", leave);
289
+ element.removeEventListener("focusin", focusIn);
290
+ element.removeEventListener("focusout", focusOut);
291
+ element.removeEventListener("keydown", keyDown);
292
+ });
293
+ };
294
+ return {
295
+ id,
296
+ anchor,
297
+ active: () => open() ? current()?.value : undefined,
298
+ toggle: value => {
299
+ const entry = [...anchors].find(candidate => candidate.value === value);
300
+ if (!entry || !surface) return;
301
+ clear();
302
+ if (open() && pinned === entry && current() === entry) {
303
+ dismiss();
304
+ return;
305
+ }
306
+ // A hover may have opened the card before this click or key press pinned it.
307
+ ring = ringOnReturn(focusTarget(entry));
308
+ dismissed = undefined;
309
+ if (show(entry, true)) surface.focus();
310
+ },
311
+ close: dismiss,
312
+ [INTERNALS]: {
313
+ current,
314
+ bind: element => {
315
+ surface = element;
316
+ observer = new ResizeObserver(() => {
317
+ if (open()) position();
318
+ });
319
+ observer.observe(element);
320
+ },
321
+ enterSurface: clear,
322
+ leave,
323
+ focusOut,
324
+ toggled: visible => {
325
+ // Light dismissal by the browser: an outside click or another popover.
326
+ // The event of a close this controller made itself arrives later, when
327
+ // the pointer may already wait on the next anchor; that delay stays.
328
+ if (!visible && open()) {
329
+ clear();
330
+ pinned = undefined;
331
+ setOpen(false);
332
+ listen(false);
333
+ }
334
+ options.onOpenChange?.(visible);
335
+ }
336
+ }
337
+ };
338
+ }
339
+ /**
340
+ * A non-modal card that opens beside the anchor a resting mouse points at.
341
+ * Native popover owns the top layer and light dismissal.
342
+ */
343
+ export function HoverPreview(props) {
344
+ const state = props.preview[INTERNALS];
345
+ let surface;
346
+ onMount(() => state.bind(surface));
347
+ const content = () => {
348
+ const children = props.children;
349
+ if (typeof children !== "function") return children;
350
+ return _$createComponent(Show, {
351
+ get when() {
352
+ return state.current();
353
+ },
354
+ keyed: true,
355
+ children: anchor => children(anchor.value)
356
+ });
357
+ };
358
+ return _$ssr(_tmpl$, _$ssrAttribute("id", _$escape(props.preview.id, true), false), _$ssrAttribute("aria-label", _$escape(props.label, true), false), _$ssrAttribute("class", props.class ? `k2b-hover-preview ${_$escape(props.class, true)}` : "k2b-hover-preview", false) + _$ssrAttribute("data-size", _$escape(props.size ?? "content", true), false), _$escape(content()));
359
+ }
360
+ //# sourceMappingURL=HoverPreview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["feedback/HoverPreview.tsx"],"sourcesContent":["import { type Accessor, createEffect, createSignal, createUniqueId, type JSX, onCleanup, onMount, Show } from \"solid-js\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { positionTooltipSurface } from \"./tooltip-position\";\n\n/** A resting pointer moving from one anchor to another swaps the card this fast. */\nconst SWAP_DELAY = 90;\n/** Grace period for the pointer to travel from an anchor into the card. */\nconst CLOSE_DELAY = 180;\n/** Distance to the region and to the boundary edges. */\nconst GAP = 8;\n/** Keys the wiring between a controller and its `<HoverPreview>`. */\nconst INTERNALS = Symbol(\"k2b-hover-preview\");\n\n/**\n * Where the card opens. By default it opens to the right of its anchor,\n * centered on it or aligned with its bottom edge, and flips or clamps to stay\n * in the viewport. With `beside`, it opens right of that region, such as a list\n * column, top-aligned with the anchor and clamped into `within` (default: the\n * viewport); when the card does not fit there, it does not open at all.\n */\nexport type HoverPreviewPlacement =\n | { align?: \"center\" | \"end\" }\n | { beside: () => HTMLElement | null | undefined; within?: () => HTMLElement | null | undefined };\n\nexport type HoverPreviewOptions<T> = {\n /** Milliseconds a resting mouse waits on an anchor before the card opens. Defaults to 250. */\n openDelay?: number;\n /**\n * How keyboard users open the card: `\"space\"` (default) toggles it with Space\n * on the focused anchor and leaves focus there; `\"focus\"` opens it after the\n * open delay whenever an anchor receives focus.\n */\n keyboard?: \"space\" | \"focus\";\n placement?: HoverPreviewPlacement;\n /** Anchors whose value is disabled open no card; an open card closes once its value becomes disabled. */\n disabled?: (value: T) => boolean;\n /** Runs whenever the card opens or closes, including light dismissal by the browser. */\n onOpenChange?: (open: boolean) => void;\n};\n\nexport type HoverPreviewController<T> = {\n /** Id of the card element, for `aria-controls`. */\n readonly id: string;\n /**\n * Registers an anchor element for `value`; use the result as a `ref`.\n * `trigger` is the control that toggles the card for this anchor; Escape\n * returns focus there when focus is inside the card. Defaults to the anchor.\n */\n anchor: (value: T, trigger?: () => HTMLElement | null | undefined) => (element: HTMLElement) => void;\n /** The value whose card is open, or `undefined` while closed. */\n active: Accessor<T | undefined>;\n /** Opens the card for `value` at once and moves focus into it, or closes it when it is already pinned open. */\n toggle: (value: T) => void;\n /** Closes the card; it reopens for the same anchor only after the pointer left it. */\n close: () => void;\n /** Wiring for `<HoverPreview>`; not part of the public contract. */\n readonly [INTERNALS]: Internals<T>;\n};\n\ntype Anchor<T> = { value: T; element: HTMLElement; trigger?: () => HTMLElement | null | undefined };\n\ntype Internals<T> = {\n current: Accessor<Anchor<T> | undefined>;\n bind: (surface: HTMLElement) => void;\n enterSurface: () => void;\n leave: () => void;\n focusOut: (event: FocusEvent) => void;\n toggled: (open: boolean) => void;\n};\n\n/** Elements that use Space themselves; Space on them never toggles the card. */\nconst OWNS_SPACE = [\n \"button, input, select, textarea, summary, [contenteditable]:not([contenteditable='false'])\",\n ...[\"button\", \"checkbox\", \"switch\", \"radio\", \"option\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"tab\", \"treeitem\"].map(\n (role) => `[role='${role}']`,\n ),\n].join(\", \");\n\n/** Open light-dismiss popovers, such as a menu or a date picker, close when another one opens. */\nconst AUTO_POPOVER = \":is([popover=''], [popover='auto' i]):popover-open\";\n\n/**\n * Creates the behavior of one hover preview card shared by a group of anchors,\n * such as the rows of a list. Render the card with `<HoverPreview preview={…}>`.\n */\nexport function createHoverPreview<T>(options: HoverPreviewOptions<T> = {}): HoverPreviewController<T> {\n const id = `k2b-hover-preview-${createUniqueId()}`;\n const anchors = new Set<Anchor<T>>();\n const [current, setCurrent] = createSignal<Anchor<T>>();\n const [open, setOpen] = createSignal(false);\n let surface: HTMLElement | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n /** The anchor whose open delay runs while the card is closed. */\n let pending: Anchor<T> | undefined;\n /** The anchor that Space or `toggle()` opened the card for; only it outlasts the pointer leaving. */\n let pinned: Anchor<T> | undefined;\n let dismissed: Anchor<T> | undefined;\n let ring = true;\n let observer: ResizeObserver | undefined;\n\n const placement = (): HoverPreviewPlacement => options.placement ?? {};\n const region = () => {\n const value = placement();\n return \"beside\" in value ? value : undefined;\n };\n const focusTarget = (anchor: Anchor<T>) => anchor.trigger?.() ?? anchor.element;\n const anchorOf = (node: Node) => [...anchors].find((anchor) => anchor.element.contains(node));\n const clear = () => {\n clearTimeout(timer);\n timer = undefined;\n if (pending && !open()) document.removeEventListener(\"keydown\", escape);\n pending = undefined;\n };\n /** A passive card never closes a menu or another popover someone opened on purpose. */\n const blocked = () =>\n [...document.querySelectorAll(AUTO_POPOVER)].some((popover) => popover !== surface && !(surface && popover.contains(surface)));\n\n /** The boundary rectangle, never larger than the viewport. */\n const bounds = (within: Element | null | undefined) => {\n const rect = within?.getBoundingClientRect();\n return {\n top: Math.max(0, rect?.top ?? 0),\n right: Math.min(window.innerWidth, rect?.right ?? window.innerWidth),\n bottom: Math.min(window.innerHeight, rect?.bottom ?? window.innerHeight),\n };\n };\n /** Room right of the region for the card plus a gap on both sides. Works while the card is hidden. */\n const hasRoom = () => {\n const beside = region();\n if (!beside || !surface) return true;\n const area = beside.beside()?.getBoundingClientRect();\n if (!area) return false;\n const width = surface.getBoundingClientRect().width || Number.parseFloat(getComputedStyle(surface).width) || 0;\n return bounds(beside.within?.()).right - area.right >= width + 2 * GAP;\n };\n const position = () => {\n const anchor = current();\n if (!surface || !anchor) return;\n const beside = region();\n if (beside) {\n const area = beside.beside()?.getBoundingClientRect() ?? anchor.element.getBoundingClientRect();\n const box = bounds(beside.within?.());\n const height = surface.getBoundingClientRect().height;\n const top = anchor.element.getBoundingClientRect().top;\n surface.style.left = `${Math.round(area.right + GAP)}px`;\n surface.style.top = `${Math.round(Math.max(box.top + GAP, Math.min(top, box.bottom - height - GAP)))}px`;\n return;\n }\n positionTooltipSurface(surface, anchor.element, \"right\");\n const value = placement();\n if (!(\"beside\" in value) && value.align === \"end\") {\n const height = surface.getBoundingClientRect().height;\n const bottom = anchor.element.getBoundingClientRect().bottom;\n surface.style.top = `${Math.round(Math.max(8, Math.min(bottom - height, window.innerHeight - height - 8)))}px`;\n }\n };\n\n const escape = (event: KeyboardEvent) => {\n if (event.key !== \"Escape\") return;\n if (!open()) {\n // Escape during the open delay works as if the card had opened and closed.\n if (pending) dismissed = pending;\n clear();\n return;\n }\n // Focus in the card goes back to the trigger: handled here, so the browser\n // neither returns it itself nor lights the ring for this key. A card the\n // pointer opened leaves the key to other handlers, such as an open modeless dialog.\n if (surface?.contains(document.activeElement)) event.preventDefault();\n dismiss();\n };\n // A card beside a region belongs to a row of a scrolling list: once the list\n // scrolls, another row sits under the pointer, so the card closes. A card\n // beside its anchor follows it.\n const scroll = (event: Event) => {\n const target = event.target;\n if (!region()) return position();\n if (!(target instanceof Node) || surface?.contains(target)) return;\n const anchor = current();\n if (!anchor || target.contains(anchor.element)) hide();\n };\n const resize = () => {\n if (hasRoom()) position();\n else hide();\n };\n const listen = (on: boolean) => {\n if (on) {\n document.addEventListener(\"keydown\", escape);\n window.addEventListener(\"scroll\", scroll, true);\n window.addEventListener(\"resize\", resize);\n return;\n }\n document.removeEventListener(\"keydown\", escape);\n window.removeEventListener(\"scroll\", scroll, true);\n window.removeEventListener(\"resize\", resize);\n };\n\n const hide = () => {\n clear();\n pinned = undefined;\n if (!open()) return;\n setOpen(false);\n listen(false);\n try {\n surface?.hidePopover();\n } catch {\n // The card may already be disconnected during cleanup.\n }\n };\n const dismiss = () => {\n const anchor = current();\n dismissed = anchor;\n // Before hiding, so the popover does not return focus on its own.\n if (anchor && surface?.contains(document.activeElement)) returnFocus(focusTarget(anchor), ring);\n hide();\n };\n /**\n * Opens the card for `anchor` or moves it there; `pin` keeps it open after\n * the pointer leaves. Moving the card to another anchor drops an earlier pin.\n * Returns whether it is open for `anchor`.\n */\n const show = (anchor: Anchor<T>, pin: boolean): boolean => {\n clear();\n if (!surface || dismissed === anchor) return false;\n if (options.disabled?.(anchor.value) || !hasRoom()) {\n hide();\n return false;\n }\n if (!open() || current() !== anchor) {\n ring = ringOnReturn(focusTarget(anchor));\n setCurrent(anchor);\n if (!open()) {\n try {\n surface.showPopover();\n } catch {\n return false;\n }\n setOpen(true);\n listen(true);\n }\n position();\n pinned = undefined;\n }\n if (pin) pinned = anchor;\n return true;\n };\n /** Shows `anchor` after `delay`; a keyboard swap (`follow`) carries an existing pin along. */\n const schedule = (anchor: Anchor<T>, delay: number, follow = false) => {\n clear();\n if (!open()) {\n pending = anchor;\n document.addEventListener(\"keydown\", escape);\n }\n timer = setTimeout(() => {\n if (open()) show(anchor, follow && pinned !== undefined);\n else if (blocked()) clear();\n else show(anchor, false);\n }, delay);\n };\n const leave = () => {\n clear();\n dismissed = undefined;\n if (pinned && pinned === current()) return;\n timer = setTimeout(() => {\n // Focus holds the card open inside it, and on the anchor where focus opens it.\n const focused = document.activeElement;\n const held = surface?.contains(focused) || (options.keyboard === \"focus\" && current()?.element.contains(focused));\n if (!held) hide();\n }, CLOSE_DELAY);\n };\n const focusOut = (event: FocusEvent) => {\n const next = event.relatedTarget;\n if (next instanceof Node && (surface?.contains(next) || anchorOf(next))) return;\n pinned = undefined;\n leave();\n };\n\n createEffect(() => {\n const anchor = current();\n if (open() && anchor && options.disabled?.(anchor.value)) hide();\n });\n // Only an open card holds global listeners, so a server render that\n // disposes the controller touches no browser globals.\n onCleanup(() => {\n hide();\n observer?.disconnect();\n });\n\n const anchor =\n (value: T, trigger?: () => HTMLElement | null | undefined) =>\n (element: HTMLElement): void => {\n const entry: Anchor<T> = { value, element, trigger };\n anchors.add(entry);\n const pointerEnter = (event: PointerEvent) => {\n // Touch and pen have no resting hover; the card stays a mouse affordance.\n if (event.pointerType !== \"mouse\") return;\n clear();\n if ((open() && current() === entry) || dismissed === entry) return;\n schedule(entry, open() ? SWAP_DELAY : (options.openDelay ?? 250));\n };\n // The open delay counts from the moment the mouse rests. An open card\n // swaps on entering another anchor and ignores movement inside it.\n const pointerMove = (event: PointerEvent) => {\n if (event.pointerType === \"mouse\" && !open() && dismissed !== entry) schedule(entry, options.openDelay ?? 250);\n };\n // A press acts on the row, such as opening it or its menu; a card about to open stays closed.\n const pointerDown = () => clear();\n const focusIn = () => {\n if (open()) {\n if (current() === entry) clear();\n else schedule(entry, SWAP_DELAY, true);\n return;\n }\n clear();\n if (options.keyboard === \"focus\" && dismissed !== entry) schedule(entry, options.openDelay ?? 250);\n };\n const keyDown = (event: KeyboardEvent) => {\n if (options.keyboard === \"focus\" || event.key !== \" \" || event.defaultPrevented) return;\n // Shift+Space keeps scrolling up; other modifiers belong to shortcuts.\n if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) return;\n if (event.target instanceof Element && event.target.matches(OWNS_SPACE)) return;\n event.preventDefault();\n // A held key toggles once; its repeats only keep the page from scrolling.\n if (event.repeat) return;\n if (open() && current() === entry) {\n dismiss();\n return;\n }\n dismissed = undefined;\n show(entry, true);\n };\n element.addEventListener(\"pointerenter\", pointerEnter);\n element.addEventListener(\"pointermove\", pointerMove);\n element.addEventListener(\"pointerdown\", pointerDown);\n element.addEventListener(\"pointerleave\", leave);\n element.addEventListener(\"focusin\", focusIn);\n element.addEventListener(\"focusout\", focusOut);\n element.addEventListener(\"keydown\", keyDown);\n onCleanup(() => {\n anchors.delete(entry);\n if (current() === entry) hide();\n element.removeEventListener(\"pointerenter\", pointerEnter);\n element.removeEventListener(\"pointermove\", pointerMove);\n element.removeEventListener(\"pointerdown\", pointerDown);\n element.removeEventListener(\"pointerleave\", leave);\n element.removeEventListener(\"focusin\", focusIn);\n element.removeEventListener(\"focusout\", focusOut);\n element.removeEventListener(\"keydown\", keyDown);\n });\n };\n\n return {\n id,\n anchor,\n active: () => (open() ? current()?.value : undefined),\n toggle: (value) => {\n const entry = [...anchors].find((candidate) => candidate.value === value);\n if (!entry || !surface) return;\n clear();\n if (open() && pinned === entry && current() === entry) {\n dismiss();\n return;\n }\n // A hover may have opened the card before this click or key press pinned it.\n ring = ringOnReturn(focusTarget(entry));\n dismissed = undefined;\n if (show(entry, true)) surface.focus();\n },\n close: dismiss,\n [INTERNALS]: {\n current,\n bind: (element) => {\n surface = element;\n observer = new ResizeObserver(() => {\n if (open()) position();\n });\n observer.observe(element);\n },\n enterSurface: clear,\n leave,\n focusOut,\n toggled: (visible) => {\n // Light dismissal by the browser: an outside click or another popover.\n // The event of a close this controller made itself arrives later, when\n // the pointer may already wait on the next anchor; that delay stays.\n if (!visible && open()) {\n clear();\n pinned = undefined;\n setOpen(false);\n listen(false);\n }\n options.onOpenChange?.(visible);\n },\n },\n };\n}\n\nexport type HoverPreviewProps<T> = {\n preview: HoverPreviewController<T>;\n /** Accessible name of the card. */\n label: string;\n /** `\"content\"` (default) sizes the card to its content up to 32rem; `\"fixed\"` keeps one 22rem × 20rem box for every anchor. */\n size?: \"content\" | \"fixed\";\n class?: string;\n /** Plain content stays mounted while the card is closed; a function renders the open anchor's value. */\n children: JSX.Element | ((value: T) => JSX.Element);\n};\n\n/**\n * A non-modal card that opens beside the anchor a resting mouse points at.\n * Native popover owns the top layer and light dismissal.\n */\nexport function HoverPreview<T>(props: HoverPreviewProps<T>): JSX.Element {\n const state = props.preview[INTERNALS];\n let surface!: HTMLDivElement;\n onMount(() => state.bind(surface));\n const content = () => {\n const children = props.children;\n if (typeof children !== \"function\") return children;\n return (\n <Show when={state.current()} keyed>\n {(anchor) => children(anchor.value)}\n </Show>\n );\n };\n return (\n <div\n ref={surface}\n id={props.preview.id}\n popover=\"auto\"\n role=\"dialog\"\n aria-label={props.label}\n tabIndex={-1}\n class={props.class ? `k2b-hover-preview ${props.class}` : \"k2b-hover-preview\"}\n data-size={props.size ?? \"content\"}\n onPointerEnter={() => state.enterSurface()}\n onPointerLeave={() => state.leave()}\n on:focusout={(event) => state.focusOut(event)}\n onToggle={(event) => state.toggled(event.newState === \"open\")}\n >\n {content()}\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}