@k2b/ui 0.27.0 → 0.28.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 (191) hide show
  1. package/README.md +1 -1
  2. package/dist/base.css +147 -2
  3. package/dist/browser/actions/ContextMenu.js +30 -4
  4. package/dist/browser/actions/ContextMenu.js.map +1 -1
  5. package/dist/browser/actions/Dropdown.js +184 -158
  6. package/dist/browser/actions/Dropdown.js.map +1 -1
  7. package/dist/browser/actions/GestureMenu.js +71 -138
  8. package/dist/browser/actions/GestureMenu.js.map +1 -1
  9. package/dist/browser/actions/Toolbar.js +379 -48
  10. package/dist/browser/actions/Toolbar.js.map +1 -1
  11. package/dist/browser/actions/index.js.map +1 -1
  12. package/dist/browser/actions/menu-rows.js +15 -0
  13. package/dist/browser/actions/menu-rows.js.map +1 -0
  14. package/dist/browser/actions/menu-sheet.js +154 -0
  15. package/dist/browser/actions/menu-sheet.js.map +1 -0
  16. package/dist/browser/actions/toolbar-messages.js +20 -0
  17. package/dist/browser/actions/toolbar-messages.js.map +1 -0
  18. package/dist/browser/chart-inspection.js +3 -0
  19. package/dist/browser/chart-inspection.js.map +1 -0
  20. package/dist/browser/chat/ChatComposer.js +581 -397
  21. package/dist/browser/chat/ChatComposer.js.map +1 -1
  22. package/dist/browser/chat/chat-messages.js +19 -1
  23. package/dist/browser/chat/chat-messages.js.map +1 -1
  24. package/dist/browser/chat/command-search.js +3 -0
  25. package/dist/browser/chat/command-search.js.map +1 -1
  26. package/dist/browser/chat/index.js +2 -0
  27. package/dist/browser/chat/index.js.map +1 -1
  28. package/dist/browser/chat/reaction-people.js +15 -0
  29. package/dist/browser/chat/reaction-people.js.map +1 -0
  30. package/dist/browser/content/Chart.js +6 -15
  31. package/dist/browser/content/Chart.js.map +1 -1
  32. package/dist/browser/content/ChartSnapshotView.js +7 -10
  33. package/dist/browser/content/ChartSnapshotView.js.map +1 -1
  34. package/dist/browser/content/MessageRow.js +330 -448
  35. package/dist/browser/content/MessageRow.js.map +1 -1
  36. package/dist/browser/content/ReactionBar.js +419 -0
  37. package/dist/browser/content/ReactionBar.js.map +1 -0
  38. package/dist/browser/content/chart-inspection-core.js +557 -0
  39. package/dist/browser/content/chart-inspection-core.js.map +1 -0
  40. package/dist/browser/content/chart-inspection.js +10 -445
  41. package/dist/browser/content/chart-inspection.js.map +1 -1
  42. package/dist/browser/content/chart-line-gaps.js +1 -1
  43. package/dist/browser/content/chart-line-gaps.js.map +1 -1
  44. package/dist/browser/content/chart-snapshot.js +1 -1
  45. package/dist/browser/content/chart-snapshot.js.map +1 -1
  46. package/dist/browser/content/index.js +1 -0
  47. package/dist/browser/content/index.js.map +1 -1
  48. package/dist/browser/feedback/dialog-core.js +135 -23
  49. package/dist/browser/feedback/dialog-core.js.map +1 -1
  50. package/dist/browser/index.js +1 -0
  51. package/dist/browser/index.js.map +1 -1
  52. package/dist/browser/input-mode.js +40 -0
  53. package/dist/browser/input-mode.js.map +1 -0
  54. package/dist/browser/inputs/FileDropTarget.js +24 -9
  55. package/dist/browser/inputs/FileDropTarget.js.map +1 -1
  56. package/dist/browser/inputs/FileInputs.js +2 -2
  57. package/dist/browser/inputs/FileInputs.js.map +1 -1
  58. package/dist/browser/inputs/SelectChip.js +9 -0
  59. package/dist/browser/inputs/SelectChip.js.map +1 -1
  60. package/dist/browser/inputs/emoji/EmojiPicker.js +13 -0
  61. package/dist/browser/inputs/emoji/EmojiPicker.js.map +1 -1
  62. package/dist/browser/inputs/file-drop.js.map +1 -1
  63. package/dist/browser/layout/DetailPanel.js +283 -197
  64. package/dist/browser/layout/DetailPanel.js.map +1 -1
  65. package/dist/browser/layout/Discussion.js +233 -185
  66. package/dist/browser/layout/Discussion.js.map +1 -1
  67. package/dist/browser/layout/TableOfContents.js +10 -2
  68. package/dist/browser/layout/TableOfContents.js.map +1 -1
  69. package/dist/browser/layout/detail-panel-messages.js +35 -0
  70. package/dist/browser/layout/detail-panel-messages.js.map +1 -0
  71. package/dist/browser/surfaces/Avatar.js +50 -20
  72. package/dist/browser/surfaces/Avatar.js.map +1 -1
  73. package/dist/browser/surfaces/AvatarStack.js +406 -0
  74. package/dist/browser/surfaces/AvatarStack.js.map +1 -0
  75. package/dist/browser/surfaces/avatar-messages.js +8 -2
  76. package/dist/browser/surfaces/avatar-messages.js.map +1 -1
  77. package/dist/browser/surfaces/index.js +1 -0
  78. package/dist/browser/surfaces/index.js.map +1 -1
  79. package/dist/ssr/actions/ContextMenu.js +21 -3
  80. package/dist/ssr/actions/ContextMenu.js.map +1 -1
  81. package/dist/ssr/actions/Dropdown.js +140 -72
  82. package/dist/ssr/actions/Dropdown.js.map +1 -1
  83. package/dist/ssr/actions/GestureMenu.js +52 -106
  84. package/dist/ssr/actions/GestureMenu.js.map +1 -1
  85. package/dist/ssr/actions/Toolbar.js +294 -14
  86. package/dist/ssr/actions/Toolbar.js.map +1 -1
  87. package/dist/ssr/actions/index.js.map +1 -1
  88. package/dist/ssr/actions/menu-rows.js +15 -0
  89. package/dist/ssr/actions/menu-rows.js.map +1 -0
  90. package/dist/ssr/actions/menu-sheet.js +138 -0
  91. package/dist/ssr/actions/menu-sheet.js.map +1 -0
  92. package/dist/ssr/actions/toolbar-messages.js +20 -0
  93. package/dist/ssr/actions/toolbar-messages.js.map +1 -0
  94. package/dist/ssr/chart-inspection.js +3 -0
  95. package/dist/ssr/chart-inspection.js.map +1 -0
  96. package/dist/ssr/chat/ChatComposer.js +263 -89
  97. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  98. package/dist/ssr/chat/chat-messages.js +19 -1
  99. package/dist/ssr/chat/chat-messages.js.map +1 -1
  100. package/dist/ssr/chat/command-search.js +3 -0
  101. package/dist/ssr/chat/command-search.js.map +1 -1
  102. package/dist/ssr/chat/index.js +2 -0
  103. package/dist/ssr/chat/index.js.map +1 -1
  104. package/dist/ssr/chat/reaction-people.js +15 -0
  105. package/dist/ssr/chat/reaction-people.js.map +1 -0
  106. package/dist/ssr/content/Chart.js +6 -15
  107. package/dist/ssr/content/Chart.js.map +1 -1
  108. package/dist/ssr/content/ChartSnapshotView.js +7 -10
  109. package/dist/ssr/content/ChartSnapshotView.js.map +1 -1
  110. package/dist/ssr/content/MessageRow.js +98 -152
  111. package/dist/ssr/content/MessageRow.js.map +1 -1
  112. package/dist/ssr/content/ReactionBar.js +304 -0
  113. package/dist/ssr/content/ReactionBar.js.map +1 -0
  114. package/dist/ssr/content/chart-inspection-core.js +557 -0
  115. package/dist/ssr/content/chart-inspection-core.js.map +1 -0
  116. package/dist/ssr/content/chart-inspection.js +10 -445
  117. package/dist/ssr/content/chart-inspection.js.map +1 -1
  118. package/dist/ssr/content/chart-line-gaps.js +1 -1
  119. package/dist/ssr/content/chart-line-gaps.js.map +1 -1
  120. package/dist/ssr/content/chart-snapshot.js +1 -1
  121. package/dist/ssr/content/chart-snapshot.js.map +1 -1
  122. package/dist/ssr/content/index.js +1 -0
  123. package/dist/ssr/content/index.js.map +1 -1
  124. package/dist/ssr/feedback/dialog-core.js +135 -23
  125. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  126. package/dist/ssr/index.js +1 -0
  127. package/dist/ssr/index.js.map +1 -1
  128. package/dist/ssr/input-mode.js +40 -0
  129. package/dist/ssr/input-mode.js.map +1 -0
  130. package/dist/ssr/inputs/FileDropTarget.js +17 -5
  131. package/dist/ssr/inputs/FileDropTarget.js.map +1 -1
  132. package/dist/ssr/inputs/FileInputs.js +2 -2
  133. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  134. package/dist/ssr/inputs/SelectChip.js +9 -0
  135. package/dist/ssr/inputs/SelectChip.js.map +1 -1
  136. package/dist/ssr/inputs/emoji/EmojiPicker.js +12 -0
  137. package/dist/ssr/inputs/emoji/EmojiPicker.js.map +1 -1
  138. package/dist/ssr/inputs/file-drop.js.map +1 -1
  139. package/dist/ssr/layout/DetailPanel.js +112 -58
  140. package/dist/ssr/layout/DetailPanel.js.map +1 -1
  141. package/dist/ssr/layout/Discussion.js +63 -36
  142. package/dist/ssr/layout/Discussion.js.map +1 -1
  143. package/dist/ssr/layout/TableOfContents.js +10 -2
  144. package/dist/ssr/layout/TableOfContents.js.map +1 -1
  145. package/dist/ssr/layout/detail-panel-messages.js +35 -0
  146. package/dist/ssr/layout/detail-panel-messages.js.map +1 -0
  147. package/dist/ssr/surfaces/Avatar.js +20 -2
  148. package/dist/ssr/surfaces/Avatar.js.map +1 -1
  149. package/dist/ssr/surfaces/AvatarStack.js +286 -0
  150. package/dist/ssr/surfaces/AvatarStack.js.map +1 -0
  151. package/dist/ssr/surfaces/avatar-messages.js +8 -2
  152. package/dist/ssr/surfaces/avatar-messages.js.map +1 -1
  153. package/dist/ssr/surfaces/index.js +1 -0
  154. package/dist/ssr/surfaces/index.js.map +1 -1
  155. package/dist/styles.css +1 -1
  156. package/dist/types/actions/ContextMenu.d.ts +8 -0
  157. package/dist/types/actions/Dropdown.d.ts +12 -5
  158. package/dist/types/actions/GestureMenu.d.ts +6 -4
  159. package/dist/types/actions/Toolbar.d.ts +20 -0
  160. package/dist/types/actions/index.d.ts +1 -1
  161. package/dist/types/actions/menu-rows.d.ts +5 -0
  162. package/dist/types/actions/menu-sheet.d.ts +22 -0
  163. package/dist/types/actions/toolbar-messages.d.ts +8 -0
  164. package/dist/types/chart-inspection.d.ts +2 -0
  165. package/dist/types/chat/ChatComposer.d.ts +7 -0
  166. package/dist/types/chat/chat-messages.d.ts +10 -2
  167. package/dist/types/chat/command-search.d.ts +2 -0
  168. package/dist/types/chat/index.d.ts +2 -0
  169. package/dist/types/chat/reaction-people.d.ts +9 -0
  170. package/dist/types/content/Chart.d.ts +2 -2
  171. package/dist/types/content/MessageRow.d.ts +3 -10
  172. package/dist/types/content/ReactionBar.d.ts +32 -0
  173. package/dist/types/content/chart-inspection-core.d.ts +61 -0
  174. package/dist/types/content/chart-inspection.d.ts +4 -20
  175. package/dist/types/content/chart-snapshot.d.ts +1 -1
  176. package/dist/types/content/index.d.ts +2 -1
  177. package/dist/types/feedback/dialog-core.d.ts +16 -1
  178. package/dist/types/index.d.ts +1 -0
  179. package/dist/types/input-mode.d.ts +7 -0
  180. package/dist/types/inputs/FileDropTarget.d.ts +6 -3
  181. package/dist/types/inputs/SelectChip.d.ts +2 -0
  182. package/dist/types/inputs/file-drop.d.ts +2 -0
  183. package/dist/types/layout/DetailPanel.d.ts +11 -0
  184. package/dist/types/layout/Discussion.d.ts +8 -0
  185. package/dist/types/layout/TableOfContents.d.ts +2 -0
  186. package/dist/types/layout/detail-panel-messages.d.ts +15 -0
  187. package/dist/types/surfaces/Avatar.d.ts +12 -0
  188. package/dist/types/surfaces/AvatarStack.d.ts +41 -0
  189. package/dist/types/surfaces/avatar-messages.d.ts +3 -0
  190. package/dist/types/surfaces/index.d.ts +3 -1
  191. package/package.json +6 -1
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["content/ReactionBar.tsx"],"sourcesContent":["import { type Accessor, createEffect, createMemo, createSignal, For, type JSX, on, onCleanup, onMount, Show } from \"solid-js\";\nimport { IconButton } from \"../actions/Button\";\nimport { useChatMessages } from \"../chat/chat-messages\";\nimport { positionTooltipSurface } from \"../feedback/tooltip-position\";\nimport { ScrollArea } from \"../layout/ScrollArea\";\n\nexport type ReactionBarItem = {\n /** Stable identity of the reaction, passed to `onToggle`. */\n key: string;\n emoji: string;\n count: number;\n /** The reader reacted this way; the chip shows pressed. */\n own?: boolean;\n /** Who reacted, for example from `reactionPeople`. Read by screen readers and shown in the names hint. */\n label?: string;\n /** A change of the reader's own reaction is still being saved; the chip carries `aria-busy`. */\n busy?: boolean;\n};\nexport type ReactionBarProps = {\n reactions: readonly ReactionBarItem[];\n /** Called with a reaction's key when the reader presses its chip. Without it, the chips are not controls. */\n onToggle?: (key: string) => void;\n /** Called by \"Add reaction\" with the button to anchor a picker to, such as `EmojiPicker.Popover`. Without it there is no button. */\n onAdd?: (anchor: HTMLElement) => void;\n /** \"default\": 26 px chips (chat, item detail). \"compact\": 24 px chips (cards). */\n density?: \"default\" | \"compact\";\n /** \"scroll\" (default): one line of fixed height that scrolls sideways. \"wrap\": every chip shows, lines wrap. */\n overflow?: \"scroll\" | \"wrap\";\n /** Shows at most this many chips and then \"+n\", n being the number of further emoji. The shown chips do not scroll. */\n max?: number;\n /** Screen-reader text of \"+n\"; defaults to \"{n} more reactions\". */\n moreLabel?: (count: number) => string;\n /** \"always\" (default) or \"progressive\": shown on hover and focus with a mouse or trackpad (k2b-progressive-action, inside the caller's k2b-progressive-row), kept while its picker is open, hidden on touch. Its place stays reserved. */\n add?: \"always\" | \"progressive\";\n class?: string;\n};\n\nconst HOVER_MS = 250;\nconst HOLD_MS = 500;\nconst MOVE_TOLERANCE = 8;\n\nexport function ReactionBar(props: ReactionBarProps): JSX.Element {\n const chatMessages = useChatMessages();\n // A max uses the wrapping layout, so the shown chips, +n, and add button wrap together.\n const overflow = () => (props.max !== undefined ? \"wrap\" : (props.overflow ?? \"scroll\"));\n const shown = createMemo(() => (props.max === undefined ? props.reactions : props.reactions.slice(0, props.max)));\n const keys = createMemo(() => shown().map((reaction) => reaction.key));\n const byKey = createMemo(() => new Map(shown().map((reaction) => [reaction.key, reaction])));\n const more = () => props.reactions.length - shown().length;\n /** The reaction whose names the hint shows; it follows that reaction while the caller updates it. */\n const [hintKey, setHintKey] = createSignal<string>();\n const hinted = () => {\n const key = hintKey();\n const reaction = key === undefined ? undefined : byKey().get(key);\n return reaction?.label?.trim() ? reaction : undefined;\n };\n const hint = () => {\n const reaction = hinted();\n return reaction ? `${reaction.emoji} ${reaction.label}` : \"\";\n };\n const [touch, setTouch] = createSignal(false);\n let root: HTMLDivElement | undefined;\n let surface: HTMLSpanElement | undefined;\n\n onMount(() => {\n if (!root || !surface) return;\n const bar = root;\n const tooltip = surface;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let openChip: HTMLElement | undefined;\n let swallowClick = false;\n let press: { chip: HTMLElement; id: number; x: number; y: number; held: boolean; moved: boolean } | undefined;\n\n const chipOf = (target: EventTarget | null): HTMLElement | undefined => {\n const chip = target instanceof Element ? target.closest<HTMLElement>(\"[data-reaction-key]\") : null;\n return chip && chip.closest(\".k2b-reaction-bar\") === bar ? chip : undefined;\n };\n const clearTimer = () => {\n clearTimeout(timer);\n timer = undefined;\n };\n const close = () => {\n clearTimer();\n openChip = undefined;\n try {\n tooltip.hidePopover();\n } catch {\n /* The surface may already be disconnected. */\n }\n document.removeEventListener(\"pointerdown\", dismiss, true);\n document.removeEventListener(\"keydown\", escape);\n window.removeEventListener(\"scroll\", close, true);\n window.removeEventListener(\"resize\", close);\n };\n const dismiss = () => close();\n const escape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") close();\n };\n /** Opens the names hint over a chip; false when the chip has no names to show. */\n const open = (chip: HTMLElement, fromTouch: boolean): boolean => {\n clearTimer();\n const reaction = byKey().get(chip.dataset.reactionKey ?? \"\");\n if (!reaction?.label?.trim() || !chip.isConnected) return false;\n setHintKey(reaction.key);\n setTouch(fromTouch);\n if (!tooltip.matches(\":popover-open\")) {\n try {\n tooltip.showPopover();\n } catch {\n return false;\n }\n }\n openChip = chip;\n positionTooltipSurface(tooltip, chip, \"top\");\n document.addEventListener(\"pointerdown\", dismiss, true);\n document.addEventListener(\"keydown\", escape);\n window.addEventListener(\"scroll\", close, true);\n window.addEventListener(\"resize\", close);\n return true;\n };\n // An open hint follows its reaction: new names show at once, and it closes when the reaction, its names, or its\n // chip go away.\n createEffect(\n on(hinted, (reaction) => {\n if (!openChip) return;\n if (!reaction || !openChip.isConnected) close();\n else positionTooltipSurface(tooltip, openChip, \"top\");\n }),\n );\n const delayed = (chip: HTMLElement) => {\n if (touch() && openChip) return;\n if (\n !byKey()\n .get(chip.dataset.reactionKey ?? \"\")\n ?.label?.trim()\n ) {\n close();\n return;\n }\n clearTimer();\n if (openChip) open(chip, false);\n else timer = setTimeout(() => open(chip, false), HOVER_MS);\n };\n const enter = (event: PointerEvent) => {\n const chip = chipOf(event.target);\n if (chip && event.target === chip && event.pointerType !== \"touch\") delayed(chip);\n };\n const leave = (event: PointerEvent) => {\n if (event.target !== chipOf(event.target) || (touch() && openChip)) return;\n // Moving directly to another chip keeps an open hint available for its immediate update.\n const next = chipOf(event.relatedTarget);\n if (\n !next ||\n !byKey()\n .get(next.dataset.reactionKey ?? \"\")\n ?.label?.trim()\n )\n close();\n };\n const focusIn = (event: FocusEvent) => {\n const chip = chipOf(event.target);\n if (chip?.matches(\":focus-visible\")) delayed(chip);\n };\n const focusOut = (event: FocusEvent) => {\n if (chipOf(event.target) && !chipOf(event.relatedTarget) && !(touch() && openChip)) close();\n };\n const resetSwallow = () => {\n swallowClick = false;\n document.removeEventListener(\"pointerdown\", resetSwallow, true);\n };\n const endPress = () => {\n clearTimer();\n press = undefined;\n document.removeEventListener(\"pointermove\", move, true);\n document.removeEventListener(\"pointerup\", up, true);\n document.removeEventListener(\"pointercancel\", cancel, true);\n };\n const down = (event: PointerEvent) => {\n resetSwallow();\n close();\n endPress();\n const chip = chipOf(event.target);\n if (!chip || (event.pointerType !== \"touch\" && event.pointerType !== \"pen\")) return;\n press = { chip, id: event.pointerId, x: event.clientX, y: event.clientY, held: false, moved: false };\n timer = setTimeout(() => {\n timer = undefined;\n if (!press || press.moved) return;\n // Only a hold that shows names takes the press; on a chip without names it stays an ordinary press.\n press.held = open(press.chip, true);\n }, HOLD_MS);\n document.addEventListener(\"pointermove\", move, true);\n document.addEventListener(\"pointerup\", up, true);\n document.addEventListener(\"pointercancel\", cancel, true);\n };\n const move = (event: PointerEvent) => {\n if (!press || event.pointerId !== press.id) return;\n if (Math.hypot(event.clientX - press.x, event.clientY - press.y) > MOVE_TOLERANCE) {\n press.moved = true;\n clearTimer();\n }\n };\n const up = (event: PointerEvent) => {\n if (!press || event.pointerId !== press.id) return;\n if (press.held) {\n swallowClick = true;\n // Some browsers send no click after a hold. Never consume the next real tap.\n document.addEventListener(\"pointerdown\", resetSwallow, true);\n } else if (!press.moved && !props.onToggle) open(press.chip, true);\n endPress();\n };\n const cancel = (event: PointerEvent) => {\n if (event.pointerId === press?.id) endPress();\n };\n const touchEnd = (event: TouchEvent) => {\n if (!swallowClick || !event.cancelable) return;\n // A cancelled touchend sends no click, so nothing is left to swallow.\n event.preventDefault();\n resetSwallow();\n };\n // Only the click of the held finger or pen is swallowed; a keyboard's click has no detail and always goes through.\n const click = (event: MouseEvent) => {\n if (!swallowClick || event.detail === 0) return;\n event.preventDefault();\n event.stopPropagation();\n resetSwallow();\n };\n const contextMenu = (event: MouseEvent) => {\n if (chipOf(event.target) && (press || swallowClick || (touch() && openChip))) event.preventDefault();\n };\n\n bar.addEventListener(\"pointerenter\", enter, true);\n bar.addEventListener(\"pointerleave\", leave, true);\n bar.addEventListener(\"focusin\", focusIn);\n bar.addEventListener(\"focusout\", focusOut);\n bar.addEventListener(\"pointerdown\", down);\n bar.addEventListener(\"touchend\", touchEnd, { passive: false });\n bar.addEventListener(\"click\", click, true);\n bar.addEventListener(\"contextmenu\", contextMenu);\n onCleanup(() => {\n close();\n endPress();\n resetSwallow();\n bar.removeEventListener(\"pointerenter\", enter, true);\n bar.removeEventListener(\"pointerleave\", leave, true);\n bar.removeEventListener(\"focusin\", focusIn);\n bar.removeEventListener(\"focusout\", focusOut);\n bar.removeEventListener(\"pointerdown\", down);\n bar.removeEventListener(\"touchend\", touchEnd);\n bar.removeEventListener(\"click\", click, true);\n bar.removeEventListener(\"contextmenu\", contextMenu);\n });\n });\n\n const chip = (key: string, reaction: Accessor<ReactionBarItem>) => {\n const label = (own: boolean) =>\n chatMessages().messageReaction({ emoji: reaction().emoji, count: reaction().count, names: reaction().label, own });\n return (\n <Show\n when={props.onToggle}\n fallback={\n <span\n class=\"k2b-reaction-bar__chip\"\n role=\"img\"\n aria-label={label(Boolean(reaction().own))}\n aria-busy={reaction().busy ? \"true\" : undefined}\n data-own={reaction().own ? \"\" : undefined}\n data-reaction-key={key}\n data-gesture-ignore\n >\n <span aria-hidden=\"true\">{reaction().emoji}</span>\n <span aria-hidden=\"true\">{reaction().count}</span>\n </span>\n }\n >\n {(toggle) => (\n <button\n type=\"button\"\n class=\"k2b-reaction-bar__chip\"\n aria-pressed={reaction().own ? \"true\" : \"false\"}\n aria-label={label(false)}\n aria-busy={reaction().busy ? \"true\" : undefined}\n data-own={reaction().own ? \"\" : undefined}\n data-reaction-key={key}\n onClick={() => toggle()(key)}\n >\n <span aria-hidden=\"true\">{reaction().emoji}</span>\n <span aria-hidden=\"true\">{reaction().count}</span>\n </button>\n )}\n </Show>\n );\n };\n const chips = () => <For each={keys()}>{(key) => <Show when={byKey().get(key)}>{(reaction) => chip(key, reaction)}</Show>}</For>;\n\n return (\n <div\n ref={root}\n class={props.class ? `k2b-reaction-bar ${props.class}` : \"k2b-reaction-bar\"}\n role=\"group\"\n aria-label={chatMessages().messageReactions}\n data-density={props.density ?? \"default\"}\n data-overflow={overflow()}\n data-add={props.onAdd ? (props.add ?? \"always\") : undefined}\n data-empty={props.reactions.length === 0 ? \"\" : undefined}\n >\n <Show when={overflow() === \"scroll\"} fallback={<div class=\"k2b-reaction-bar__list\">{chips()}</div>}>\n <Show when={shown().length > 0}>\n <ScrollArea orientation=\"horizontal\" class=\"k2b-reaction-bar__scroll\" tabindex={props.onToggle ? undefined : 0}>\n {chips()}\n </ScrollArea>\n </Show>\n </Show>\n <Show when={more() > 0}>\n <span class=\"k2b-reaction-bar__more\">\n <span aria-hidden=\"true\">+{more()}</span>\n <span class=\"k2b-sr-only\">{props.moreLabel?.(more()) ?? chatMessages().moreReactions({ count: more() })}</span>\n </span>\n </Show>\n <Show when={props.onAdd}>\n {(add) => (\n <span class={`k2b-reaction-bar__add${props.add === \"progressive\" ? \" k2b-progressive-action\" : \"\"}`}>\n <IconButton\n label={chatMessages().messageAddReaction}\n size=\"xs\"\n class=\"k2b-reaction-bar__add-button\"\n onClick={(event) => add()(event.currentTarget)}\n >\n <i class=\"ti ti-mood-plus\" aria-hidden=\"true\" />\n </IconButton>\n </span>\n )}\n </Show>\n <span\n ref={surface}\n class=\"k2b-tooltip k2b-reaction-bar__hint\"\n popover=\"manual\"\n role=\"tooltip\"\n aria-hidden=\"true\"\n data-touch={touch() ? \"\" : undefined}\n >\n {hint()}\n </span>\n </div>\n );\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,557 @@
1
+ import { positionTooltipSurface } from "../feedback/tooltip-position";
2
+ export const sameChartDatum = (a, b) => a.role === b.role && a.index === b.index && a.seriesIndex === b.seriesIndex;
3
+ const object = value => typeof value === "object" && value !== null;
4
+ const pair = value => Array.isArray(value) && value.length === 2 && value.every(n => typeof n === "number" && Number.isFinite(n));
5
+ const roles = new Set(["point", "item", "bin", "box", "outlier", "value", "cell", "interval"]);
6
+ export function isChartDatum(value) {
7
+ return object(value) && Number.isInteger(value.index) && typeof value.index === "number" && value.index >= 0 && (value.seriesIndex === undefined || typeof value.seriesIndex === "number" && Number.isInteger(value.seriesIndex) && value.seriesIndex >= 0) && typeof value.role === "string" && roles.has(value.role) && pair(value.anchor) && (value.grid === undefined || pair(value.grid)) && (value.label === undefined || typeof value.label === "string") && Array.isArray(value.values) && value.values.every(field => object(field) && typeof field.key === "string" && (typeof field.value === "string" || typeof field.value === "number" && Number.isFinite(field.value)) && (field.formatted === undefined || typeof field.formatted === "string"));
8
+ }
9
+
10
+ /** Adds or removes only the tooltip id, keeping descriptions the host put on the container. */
11
+ const describe = (container, id, on) => {
12
+ const ids = (container.getAttribute("aria-describedby") ?? "").split(/\s+/).filter(token => token && token !== id);
13
+ if (on) ids.push(id);
14
+ if (ids.length) container.setAttribute("aria-describedby", ids.join(" "));else container.removeAttribute("aria-describedby");
15
+ };
16
+
17
+ /** Enhances existing SVG nodes. Pointer/focus changes never rebuild the chart. */
18
+ export function createChartInspector(options) {
19
+ const source = Symbol("chart cursor");
20
+ let entries;
21
+ let sortedPoints = [];
22
+ const byElement = new Map();
23
+ let active;
24
+ let highlighted = [];
25
+ let pinned = false;
26
+ let pointerStart;
27
+ let moved = false;
28
+ let pending;
29
+ let frame;
30
+ // The cursor this chart is subscribed to. Closing reads it instead of the cursor prop: a chart also closes
31
+ // while it unmounts, when a parent `<Show>` may already have dropped the value the prop came from.
32
+ let linkedCursor;
33
+ const records = () => {
34
+ if (!entries || entries.length > 0 && !entries[0].element.isConnected) {
35
+ entries = [];
36
+ byElement.clear();
37
+ sortedPoints = [];
38
+ for (const element of Array.from(options.container()?.querySelectorAll("[data-chart-datum]") ?? [])) {
39
+ try {
40
+ const value = JSON.parse(element.getAttribute("data-chart-datum") ?? "null");
41
+ if (isChartDatum(value)) {
42
+ const entry = {
43
+ element,
44
+ datum: value
45
+ };
46
+ entries.push(entry);
47
+ byElement.set(element, entry);
48
+ }
49
+ } catch {
50
+ /* Only valid renderer metadata participates in inspection. */
51
+ }
52
+ }
53
+ sortedPoints = entries.filter(entry => entry.datum.role === "point").sort((a, b) => a.datum.anchor[0] - b.datum.anchor[0]);
54
+ }
55
+ return entries;
56
+ };
57
+ const highlightColor = item => {
58
+ // Read the actual paint before applying inspection styles, including
59
+ // custom series and threshold colors. Invisible points use their line.
60
+ const point = item.element.querySelector(".stdlib-chart-inspection-point");
61
+ const source = point ? options.container()?.querySelector(options.kind() === "line" ? `.stdlib-chart-line.stdlib-chart-series-${(item.datum.seriesIndex ?? 0) % 8}` : ".stdlib-chart-sparkline, .stdlib-chart-stat-sparkline") : item.element.querySelector(".stdlib-chart-bar-gauge-fill, .stdlib-chart-gauge-fill, .stdlib-chart-stat-value") ?? item.element.querySelector("rect, path, circle");
62
+ const paint = source ? source.ownerDocument.defaultView?.getComputedStyle(source) : undefined;
63
+ const color = (point || item.element.hasAttribute("data-chart-reference") ? paint?.stroke : paint?.fill) || `var(--stdlib-chart-c${(item.datum.seriesIndex ?? 0) % 8 + 1})`;
64
+ return `color-mix(in srgb, ${color} 72%, var(--k2b-chart-highlight-mix))`;
65
+ };
66
+ const selectData = selected => {
67
+ for (const entry of records()) {
68
+ const matches = selected.some(datum => sameChartDatum(datum, entry.datum));
69
+ if (matches) entry.element.style.setProperty("--k2b-chart-selection-color", highlightColor(entry));else entry.element.style.removeProperty("--k2b-chart-selection-color");
70
+ entry.element.toggleAttribute("data-selected", matches);
71
+ }
72
+ };
73
+ const screenPoint = entry => {
74
+ const matrix = entry.element.getScreenCTM?.();
75
+ const [x, y] = entry.datum.anchor;
76
+ if (matrix) return {
77
+ x: matrix.a * x + matrix.c * y + matrix.e,
78
+ y: matrix.b * x + matrix.d * y + matrix.f
79
+ };
80
+ const rect = entry.element.getBoundingClientRect();
81
+ return {
82
+ x: rect.x + rect.width / 2,
83
+ y: rect.y + rect.height / 2
84
+ };
85
+ };
86
+ const visibleRecords = () => {
87
+ const nodes = records();
88
+ if (options.kind() !== "map") return nodes;
89
+ const viewport = options.container()?.querySelector(".stdlib-chart-map-viewport");
90
+ if (!viewport?.getScreenCTM?.()) return nodes;
91
+ const root = viewport.ownerSVGElement?.getBoundingClientRect();
92
+ if (!root?.width || !root.height) return nodes;
93
+ const width = viewport?.width?.baseVal.value;
94
+ const height = viewport?.height?.baseVal.value;
95
+ if (!width || !height) return nodes;
96
+ const left = root.left + viewport.x.baseVal.value;
97
+ const top = root.top + viewport.y.baseVal.value;
98
+ const clip = {
99
+ left,
100
+ top,
101
+ right: left + width,
102
+ bottom: top + height
103
+ };
104
+ return nodes.filter(entry => {
105
+ const point = screenPoint(entry);
106
+ return point.x >= clip.left && point.x <= clip.right && point.y >= clip.top && point.y <= clip.bottom;
107
+ });
108
+ };
109
+ const rawX = entry => entry.datum.values.find(field => field.key === "x")?.value;
110
+ const seriesChart = () => options.kind() === "line" || options.kind() === "sparkline";
111
+ const clearHighlight = () => {
112
+ for (const entry of highlighted) {
113
+ entry.element.removeAttribute("data-inspected");
114
+ entry.element.style.removeProperty("--k2b-chart-inspection-color");
115
+ if (!entry.element.getAttribute("style")) entry.element.removeAttribute("style");
116
+ }
117
+ highlighted = [];
118
+ };
119
+ const close = (broadcast = true, cancelPending = true) => {
120
+ const state = linkedCursor?.read();
121
+ if (broadcast && state && state.source !== source) return;
122
+ if (broadcast) linkedCursor?.clear(source);
123
+ options.container()?.removeAttribute("data-cursor-source");
124
+ pinned = false;
125
+ active = undefined;
126
+ clearHighlight();
127
+ const container = options.container(),
128
+ id = options.tooltip()?.id;
129
+ if (container && id) describe(container, id, false);
130
+ options.container()?.removeAttribute("data-inspecting");
131
+ const tooltip = options.tooltip();
132
+ if (tooltip) {
133
+ try {
134
+ if (tooltip.matches(":popover-open")) tooltip.hidePopover();
135
+ } catch {
136
+ /* Detached during navigation. */
137
+ }
138
+ }
139
+ if (cancelPending) {
140
+ if (frame !== undefined) cancelAnimationFrame(frame);
141
+ frame = undefined;
142
+ pending = undefined;
143
+ }
144
+ };
145
+ const invalidate = () => {
146
+ options.cursor?.()?.clear(source);
147
+ close(false);
148
+ entries = undefined;
149
+ sortedPoints = [];
150
+ byElement.clear();
151
+ receive(options.cursor?.()?.read() ?? null);
152
+ };
153
+ const show = (entry, broadcast = true) => {
154
+ const x = rawX(entry);
155
+ if (broadcast && options.kind() === "line" && typeof x === "number") options.cursor?.()?.move(source, x);
156
+ const tooltip = options.tooltip(),
157
+ anchor = options.anchor(),
158
+ container = options.container();
159
+ if (!tooltip || !anchor || !container || !options.enabled()) return;
160
+ if (active === entry && tooltip.matches(":popover-open")) return;
161
+ clearHighlight();
162
+ active = entry;
163
+ highlighted = options.kind() === "line" ? records().filter(candidate => rawX(candidate) === rawX(entry)) : [entry];
164
+ for (const item of highlighted) {
165
+ item.element.style.setProperty("--k2b-chart-inspection-color", highlightColor(item));
166
+ item.element.setAttribute("data-inspected", "true");
167
+ }
168
+ const document = tooltip.ownerDocument;
169
+ const contents = highlighted.map(item => ({
170
+ item,
171
+ content: options.format({
172
+ kind: options.kind(),
173
+ datum: item.datum
174
+ }),
175
+ missing: false
176
+ }));
177
+ if (options.cursor?.() && options.kind() === "line") {
178
+ const present = new Set(highlighted.map(item => item.datum.seriesIndex));
179
+ for (const item of records()) {
180
+ if (present.has(item.datum.seriesIndex)) continue;
181
+ present.add(item.datum.seriesIndex);
182
+ contents.push({
183
+ item,
184
+ content: options.format({
185
+ kind: options.kind(),
186
+ datum: item.datum
187
+ }),
188
+ missing: true
189
+ });
190
+ }
191
+ contents.sort((a, b) => (a.item.datum.seriesIndex ?? 0) - (b.item.datum.seriesIndex ?? 0));
192
+ }
193
+ tooltip.replaceChildren();
194
+ const sharedTitle = typeof x === "number" ? options.cursor?.()?.formatX?.(x) : undefined;
195
+ const commonTitle = sharedTitle ?? (contents.every(({
196
+ content
197
+ }) => content.title === contents[0]?.content.title) ? contents[0]?.content.title : undefined);
198
+ if (commonTitle) {
199
+ const title = document.createElement("strong");
200
+ title.className = "k2b-chart__tooltip-title";
201
+ title.textContent = commonTitle;
202
+ tooltip.append(title);
203
+ }
204
+ for (const {
205
+ item,
206
+ content,
207
+ missing
208
+ } of contents) {
209
+ if (content.title && !commonTitle) {
210
+ const title = document.createElement("strong");
211
+ title.className = "k2b-chart__tooltip-title";
212
+ title.textContent = content.title;
213
+ tooltip.append(title);
214
+ }
215
+ for (const row of content.rows) {
216
+ const line = document.createElement("span");
217
+ line.className = "k2b-chart__tooltip-row";
218
+ if (options.kind() === "line") {
219
+ const swatch = document.createElement("i");
220
+ swatch.setAttribute("aria-hidden", "true");
221
+ const series = container.querySelector(`.stdlib-chart-line.stdlib-chart-series-${(item.datum.seriesIndex ?? 0) % 8}`);
222
+ swatch.style.background = series ? getComputedStyle(series).stroke : `var(--stdlib-chart-c${(item.datum.seriesIndex ?? 0) % 8 + 1})`;
223
+ line.append(swatch);
224
+ }
225
+ const label = document.createElement("span");
226
+ label.textContent = `${row.label}: `;
227
+ const value = document.createElement("b");
228
+ value.textContent = missing ? "—" : row.value;
229
+ line.append(label, value);
230
+ tooltip.append(line);
231
+ }
232
+ }
233
+ const rect = container.getBoundingClientRect(),
234
+ point = screenPoint(entry);
235
+ anchor.style.left = `${point.x - rect.left}px`;
236
+ anchor.style.top = `${point.y - rect.top}px`;
237
+ updatePlotBounds();
238
+ container.style.setProperty("--k2b-chart-inspection-x", `${point.x - rect.left}px`);
239
+ container.setAttribute("data-inspecting", seriesChart() ? "series" : "datum");
240
+ describe(container, tooltip.id, true);
241
+ try {
242
+ if (!tooltip.matches(":popover-open")) tooltip.showPopover();
243
+ positionTooltipSurface(tooltip, anchor);
244
+ } catch {
245
+ /* SVG titles remain available without the Popover API. */
246
+ }
247
+ };
248
+ const updatePlotBounds = () => {
249
+ const container = options.container();
250
+ if (!container) return;
251
+ const axes = Array.from(container.querySelectorAll(".stdlib-chart-axis")).map(axis => axis.getBoundingClientRect());
252
+ if (axes.length < 2) return;
253
+ const left = Math.min(...axes.map(a => a.left)),
254
+ right = Math.max(...axes.map(a => a.right));
255
+ const top = Math.min(...axes.map(a => a.top)),
256
+ bottom = Math.max(...axes.map(a => a.bottom));
257
+ if (right <= left || bottom <= top) return;
258
+ const rect = container.getBoundingClientRect();
259
+ for (const [name, value] of Object.entries({
260
+ left: left - rect.left,
261
+ right: rect.right - right,
262
+ top: top - rect.top,
263
+ bottom: rect.bottom - bottom
264
+ })) container.style.setProperty(`--k2b-chart-plot-${name}`, `${value}px`);
265
+ return {
266
+ left,
267
+ right,
268
+ top,
269
+ bottom
270
+ };
271
+ };
272
+ const receive = state => {
273
+ if (state?.source === source) return;
274
+ close(false, false);
275
+ if (!state || options.kind() !== "line") return;
276
+ const entry = records().find(item => rawX(item) === state.x);
277
+ if (entry) show(entry, false);
278
+ };
279
+ const linkCursor = cursor => {
280
+ linkedCursor = cursor;
281
+ const unsubscribe = cursor?.subscribe(receive);
282
+ return () => {
283
+ unsubscribe?.();
284
+ cursor?.clear(source);
285
+ linkedCursor = undefined;
286
+ };
287
+ };
288
+ const nearest = (x, y, target) => {
289
+ if (target?.closest("button,a") && !target.closest("[data-chart-datum]")) return;
290
+ const nodes = visibleRecords();
291
+ // A mark wins before the plot bounds: the first and last points of a line sit on the plot's edges, so half of
292
+ // their hit area lies outside it.
293
+ const direct = target?.closest("[data-chart-datum]");
294
+ const hit = direct ? byElement.get(direct) : undefined;
295
+ if (hit && nodes.includes(hit)) return hit;
296
+ if (seriesChart()) {
297
+ const plot = updatePlotBounds();
298
+ if (plot && (x < plot.left || x > plot.right || y < plot.top || y > plot.bottom)) return;
299
+ }
300
+ // Exact shapes win. A small physical hit radius also reaches tiny or
301
+ // zero-sized marks; distant whitespace leaves the chart unselected.
302
+ const points = seriesChart() ? sortedPoints : nodes;
303
+ if (seriesChart() && points.length) {
304
+ const matrix = points[0].element.getScreenCTM?.();
305
+ if (matrix) {
306
+ const determinant = matrix.a * matrix.d - matrix.b * matrix.c;
307
+ if (!determinant) return;
308
+ const targetX = ((x - matrix.e) * matrix.d - (y - matrix.f) * matrix.c) / determinant;
309
+ let low = 0,
310
+ high = points.length - 1;
311
+ while (low < high) {
312
+ const mid = Math.floor((low + high) / 2);
313
+ if (points[mid].datum.anchor[0] < targetX) low = mid + 1;else high = mid;
314
+ }
315
+ const previous = Math.max(0, low - 1);
316
+ return Math.abs(points[previous].datum.anchor[0] - targetX) <= Math.abs(points[low].datum.anchor[0] - targetX) ? points[previous] : points[low];
317
+ }
318
+ }
319
+ const matrices = new Map();
320
+ let best,
321
+ distance = Infinity;
322
+ for (const entry of points) {
323
+ const svg = entry.element.ownerSVGElement;
324
+ if (svg && !matrices.has(svg)) matrices.set(svg, svg.getScreenCTM?.() ?? null);
325
+ const matrix = svg ? matrices.get(svg) : null;
326
+ const [ax, ay] = entry.datum.anchor;
327
+ const point = matrix ? {
328
+ x: matrix.a * ax + matrix.c * ay + matrix.e,
329
+ y: matrix.b * ax + matrix.d * ay + matrix.f
330
+ } : screenPoint(entry);
331
+ const dx = point.x - x,
332
+ dy = point.y - y;
333
+ const next = seriesChart() ? Math.abs(dx) : Math.hypot(dx, dy);
334
+ if (next < distance) {
335
+ distance = next;
336
+ best = entry;
337
+ }
338
+ }
339
+ return seriesChart() || distance <= 24 ? best : undefined;
340
+ };
341
+ const move = event => {
342
+ if (event.target instanceof Node && options.tooltip()?.contains(event.target)) return;
343
+ if (pointerStart && Math.hypot(event.clientX - pointerStart.x, event.clientY - pointerStart.y) > 6) moved = true;
344
+ if (!options.enabled() || pinned || event.pointerType === "touch" || event.buttons) return;
345
+ pending = {
346
+ x: event.clientX,
347
+ y: event.clientY,
348
+ target: event.target instanceof Element ? event.target : null
349
+ };
350
+ if (frame !== undefined) return;
351
+ frame = requestAnimationFrame(() => {
352
+ frame = undefined;
353
+ const sample = pending;
354
+ pending = undefined;
355
+ if (!sample) return;
356
+ const entry = nearest(sample.x, sample.y, sample.target);
357
+ if (entry) {
358
+ show(entry);
359
+ if (seriesChart()) {
360
+ const container = options.container();
361
+ container?.setAttribute("data-cursor-source", "true");
362
+ container?.style.setProperty("--k2b-chart-inspection-y", `${sample.y - container.getBoundingClientRect().top}px`);
363
+ }
364
+ } else close();
365
+ });
366
+ };
367
+ const down = event => {
368
+ pointerStart = {
369
+ x: event.clientX,
370
+ y: event.clientY
371
+ };
372
+ moved = false;
373
+ };
374
+ const click = event => {
375
+ if (!options.enabled() || moved || event.target instanceof Element && event.target.closest("button,a")) return;
376
+ const entry = nearest(event.clientX, event.clientY, event.target instanceof Element ? event.target : null);
377
+ if (!entry) {
378
+ close();
379
+ return;
380
+ }
381
+ pinned = true;
382
+ show(entry);
383
+ options.select({
384
+ kind: options.kind(),
385
+ datum: entry.datum
386
+ });
387
+ };
388
+ const focus = event => {
389
+ if (event.target !== options.container() || !options.enabled()) return;
390
+ const nodes = visibleRecords();
391
+ const first = options.kind() === "line" ? sortedPoints.at(-1) : nodes[0];
392
+ if (first) show(first);
393
+ };
394
+ const key = event => {
395
+ if (!options.enabled() || event.target !== options.container()) return false;
396
+ if (event.key === "Escape") {
397
+ close();
398
+ return true;
399
+ }
400
+ if (event.key === "Enter" && active) {
401
+ if (options.kind() === "map" && !visibleRecords().includes(active)) {
402
+ close();
403
+ return true;
404
+ }
405
+ event.preventDefault();
406
+ pinned = true;
407
+ const link = active.element.closest("a");
408
+ if (link) link.dispatchEvent(new MouseEvent("click", {
409
+ bubbles: true,
410
+ cancelable: true
411
+ }));else options.select({
412
+ kind: options.kind(),
413
+ datum: active.datum
414
+ });
415
+ return true;
416
+ }
417
+ const navigationChart = options.kind() === "map" || options.kind() === "stateTimeline";
418
+ if (navigationChart && !event.altKey) return false;
419
+ if (!["Home", "End", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key)) return false;
420
+ let nodes = visibleRecords();
421
+ if (seriesChart()) nodes = [...nodes].sort((a, b) => a.datum.anchor[0] - b.datum.anchor[0]);
422
+ if (options.kind() === "line") {
423
+ const seen = new Set();
424
+ nodes = nodes.filter(entry => {
425
+ const x = rawX(entry);
426
+ if (seen.has(x)) return false;
427
+ seen.add(x);
428
+ return true;
429
+ });
430
+ }
431
+ if (!nodes.length) return false;
432
+ event.preventDefault();
433
+ pinned = false;
434
+ let index = active ? options.kind() === "line" ? nodes.findIndex(entry => rawX(entry) === rawX(active)) : nodes.indexOf(active) : -1;
435
+ if (event.key === "Home") index = 0;else if (event.key === "End") index = nodes.length - 1;else if (active?.datum.grid && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.key)) {
436
+ const [x, y] = active.datum.grid;
437
+ const dx = event.key === "ArrowLeft" ? -1 : event.key === "ArrowRight" ? 1 : 0;
438
+ const dy = event.key === "ArrowUp" ? -1 : event.key === "ArrowDown" ? 1 : 0;
439
+ const next = nodes.filter(entry => entry.datum.grid && (dx ? entry.datum.grid[1] === y && (entry.datum.grid[0] - x) * dx > 0 : entry.datum.grid[0] === x && (entry.datum.grid[1] - y) * dy > 0)).sort((a, b) => Math.abs(a.datum.grid[0] - x) + Math.abs(a.datum.grid[1] - y) - Math.abs(b.datum.grid[0] - x) - Math.abs(b.datum.grid[1] - y))[0];
440
+ if (next) index = nodes.indexOf(next);
441
+ } else index += event.key === "ArrowLeft" || event.key === "ArrowUp" ? -1 : 1;
442
+ show(nodes[Math.max(0, Math.min(nodes.length - 1, index))]);
443
+ return true;
444
+ };
445
+ return {
446
+ selectDatum: selected => selectData(selected ? [selected] : []),
447
+ selectData,
448
+ linkCursor,
449
+ destroy: () => close(false),
450
+ move,
451
+ down,
452
+ click,
453
+ focus,
454
+ key,
455
+ close: () => close(),
456
+ invalidate,
457
+ leave: () => {
458
+ if (!pinned) close();
459
+ }
460
+ };
461
+ }
462
+
463
+ /** Dismiss inspection on outside interaction, but allow the tooltip itself to scroll. */
464
+ export function closeChartInspectionOnOutside(options) {
465
+ const outside = event => {
466
+ if (event.target instanceof Node && !options.container()?.contains(event.target)) options.close();
467
+ };
468
+ const scroll = event => {
469
+ if (event.target instanceof Node && options.tooltip()?.contains(event.target)) return;
470
+ options.close();
471
+ };
472
+ window.addEventListener("pointerdown", outside);
473
+ window.addEventListener("scroll", scroll, true);
474
+ window.addEventListener("resize", options.close);
475
+ return () => {
476
+ window.removeEventListener("pointerdown", outside);
477
+ window.removeEventListener("scroll", scroll, true);
478
+ window.removeEventListener("resize", options.close);
479
+ };
480
+ }
481
+ let tooltipId = 0;
482
+
483
+ /** Attach inspection to stdlib SVG rendered with inspect: true, without a framework. */
484
+ export function inspectChart(container, options) {
485
+ const document = container.ownerDocument;
486
+ const anchor = document.createElement("span");
487
+ anchor.className = "k2b-chart__anchor";
488
+ anchor.setAttribute("aria-hidden", "true");
489
+ const tooltip = document.createElement("span");
490
+ tooltip.className = "k2b-tooltip k2b-chart__tooltip";
491
+ tooltip.setAttribute("role", "tooltip");
492
+ tooltip.setAttribute("popover", "manual");
493
+ tooltip.setAttribute("data-instant", "true");
494
+ do {
495
+ tooltip.id = `k2b-chart-tooltip-${++tooltipId}`;
496
+ } while (document.getElementById(tooltip.id));
497
+ container.append(anchor, tooltip);
498
+ const addedTabindex = !container.hasAttribute("tabindex");
499
+ if (addedTabindex) container.setAttribute("tabindex", "0");
500
+ const inspector = createChartInspector({
501
+ container: () => container,
502
+ cursor: () => options.cursor,
503
+ tooltip: () => tooltip,
504
+ anchor: () => anchor,
505
+ kind: () => options.kind,
506
+ enabled: () => true,
507
+ format: options.format,
508
+ select: selection => options.select?.(selection)
509
+ });
510
+ container.addEventListener("pointerdown", inspector.down);
511
+ container.addEventListener("pointermove", inspector.move);
512
+ container.addEventListener("click", inspector.click);
513
+ container.addEventListener("pointerleave", inspector.leave);
514
+ container.addEventListener("focusin", inspector.focus);
515
+ container.addEventListener("focusout", inspector.close);
516
+ container.addEventListener("keydown", inspector.key);
517
+ const removeOutside = closeChartInspectionOnOutside({
518
+ container: () => container,
519
+ tooltip: () => tooltip,
520
+ close: inspector.close
521
+ });
522
+ const unlinkCursor = inspector.linkCursor(options.cursor);
523
+ let destroyed = false;
524
+ return {
525
+ invalidate: () => {
526
+ if (!destroyed) inspector.invalidate();
527
+ },
528
+ close: () => {
529
+ if (!destroyed) inspector.close();
530
+ },
531
+ destroy() {
532
+ if (destroyed) return;
533
+ destroyed = true;
534
+ container.removeEventListener("pointerdown", inspector.down);
535
+ container.removeEventListener("pointermove", inspector.move);
536
+ container.removeEventListener("click", inspector.click);
537
+ container.removeEventListener("pointerleave", inspector.leave);
538
+ container.removeEventListener("focusin", inspector.focus);
539
+ container.removeEventListener("focusout", inspector.close);
540
+ container.removeEventListener("keydown", inspector.key);
541
+ removeOutside();
542
+ unlinkCursor();
543
+ inspector.destroy();
544
+ for (const element of container.querySelectorAll("[data-chart-datum]")) {
545
+ element.removeAttribute("data-inspected");
546
+ element.style.removeProperty("--k2b-chart-inspection-color");
547
+ if (!element.getAttribute("style")) element.removeAttribute("style");
548
+ }
549
+ // Inspection geometry the inspector wrote onto the host container.
550
+ for (const name of ["inspection-x", "inspection-y", "plot-left", "plot-right", "plot-top", "plot-bottom"]) container.style.removeProperty(`--k2b-chart-${name}`);
551
+ anchor.remove();
552
+ tooltip.remove();
553
+ if (addedTabindex) container.removeAttribute("tabindex");
554
+ }
555
+ };
556
+ }
557
+ //# sourceMappingURL=chart-inspection-core.js.map