{"version":3,"file":"KeyboardPlugin.mjs","sources":["../../../../../src/components/uPlot/plugins/KeyboardPlugin.tsx"],"sourcesContent":["import { clamp } from 'lodash';\nimport { useLayoutEffect } from 'react';\nimport uPlot from 'uplot';\n\nimport { UPlotConfigBuilder } from '../config/UPlotConfigBuilder';\n\ninterface KeyboardPluginProps {\n  config: UPlotConfigBuilder; // onkeypress, onkeyup, onkeydown (triggered by vizlayout handlers)\n}\n\nconst PIXELS_PER_MS = 0.1 as const;\nconst SHIFT_MULTIPLIER = 2 as const;\nconst KNOWN_KEYS = new Set(['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown', 'Shift', ' ']);\n\nconst initHook = (u: uPlot) => {\n  let parentWithFocus: HTMLElement | null = u.root;\n  let pressedKeys = new Set<string>();\n  let dragStartX: number | null = null;\n  let keysLastHandledAt: number | null = null;\n\n  if (!parentWithFocus) {\n    return;\n  }\n  // Make Graph area focusable. Setting this in Viz* components will make focus available on panels that do not yet have keyboard support\n  parentWithFocus.tabIndex = 0;\n\n  const moveCursor = (dx: number, dy: number) => {\n    const { cursor } = u;\n    if (cursor.left === undefined || cursor.top === undefined) {\n      return;\n    }\n\n    const { width, height } = u.over.style;\n    const [maxX, maxY] = [Math.floor(parseFloat(width)), Math.floor(parseFloat(height))];\n\n    u.setCursor({\n      left: clamp(cursor.left + dx, 0, maxX),\n      top: clamp(cursor.top + dy, 0, maxY),\n    });\n  };\n\n  const handlePressedKeys = (time: number) => {\n    const nothingPressed = pressedKeys.size === 0;\n    if (nothingPressed || !u) {\n      keysLastHandledAt = null;\n      return;\n    }\n\n    const dt = time - (keysLastHandledAt ?? time);\n    const dx = dt * PIXELS_PER_MS;\n    let horValue = 0;\n    let vertValue = 0;\n\n    if (pressedKeys.has('ArrowUp')) {\n      vertValue -= dx;\n    }\n    if (pressedKeys.has('ArrowDown')) {\n      vertValue += dx;\n    }\n    if (pressedKeys.has('ArrowLeft')) {\n      horValue -= dx;\n    }\n    if (pressedKeys.has('ArrowRight')) {\n      horValue += dx;\n    }\n    if (pressedKeys.has('Shift')) {\n      horValue *= SHIFT_MULTIPLIER;\n      vertValue *= SHIFT_MULTIPLIER;\n    }\n\n    moveCursor(horValue, vertValue);\n\n    const { cursor } = u;\n    if (pressedKeys.has(' ') && cursor) {\n      const drawHeight = Number(u.over.style.height.slice(0, -2));\n\n      u.setSelect(\n        {\n          left: cursor.left! < dragStartX! ? cursor.left! : dragStartX!,\n          top: 0,\n          width: Math.abs(cursor.left! - (dragStartX ?? cursor.left!)),\n          height: drawHeight,\n        },\n        false\n      );\n    }\n\n    keysLastHandledAt = time;\n    window.requestAnimationFrame(handlePressedKeys);\n  };\n\n  const onKeyDown = (e: KeyboardEvent) => {\n    if (e.key === 'Tab') {\n      // Hide the cursor if the user tabs away\n      u.setCursor({ left: -5, top: -5 });\n      return;\n    }\n\n    if (!KNOWN_KEYS.has(e.key)) {\n      return;\n    }\n\n    e.preventDefault();\n    e.stopPropagation();\n\n    const newKey = !pressedKeys.has(e.key);\n    if (newKey) {\n      const initiateAnimationLoop = pressedKeys.size === 0;\n      pressedKeys.add(e.key);\n\n      dragStartX = e.key === ' ' && dragStartX === null ? u.cursor.left! : dragStartX;\n\n      if (initiateAnimationLoop) {\n        window.requestAnimationFrame(handlePressedKeys);\n      }\n    }\n  };\n\n  const onKeyUp = (e: KeyboardEvent) => {\n    if (!KNOWN_KEYS.has(e.key)) {\n      return;\n    }\n\n    pressedKeys.delete(e.key);\n\n    if (e.key === ' ') {\n      e.preventDefault();\n      e.stopPropagation();\n\n      // We do this so setSelect hooks get fired, zooming the plot\n      u.setSelect(u.select);\n      dragStartX = null;\n    }\n  };\n\n  const onFocus = () => {\n    // We only want to initialize the cursor if the user is using keyboard controls\n    if (!parentWithFocus?.matches(':focus-visible')) {\n      return;\n    }\n\n    // Is there a more idiomatic way to do this?\n    const drawWidth = parseFloat(u.over.style.width);\n    const drawHeight = parseFloat(u.over.style.height);\n    u.setCursor({ left: drawWidth / 2, top: drawHeight / 2 });\n  };\n\n  const onBlur = () => {\n    keysLastHandledAt = null;\n    dragStartX = null;\n    pressedKeys.clear();\n    u.setSelect({ left: 0, top: 0, width: 0, height: 0 }, false);\n  };\n\n  parentWithFocus.addEventListener('keydown', onKeyDown);\n  parentWithFocus.addEventListener('keyup', onKeyUp);\n  parentWithFocus.addEventListener('focus', onFocus);\n  parentWithFocus.addEventListener('blur', onBlur);\n\n  const onDestroy = () => {\n    parentWithFocus?.removeEventListener('keydown', onKeyDown);\n    parentWithFocus?.removeEventListener('keyup', onKeyUp);\n    parentWithFocus?.removeEventListener('focus', onFocus);\n    parentWithFocus?.removeEventListener('blur', onBlur);\n    parentWithFocus = null;\n  };\n\n  (u.hooks.destroy ??= []).push(onDestroy);\n};\n\n/**\n * @alpha\n */\nexport const KeyboardPlugin = ({ config }: KeyboardPluginProps) => {\n  useLayoutEffect(() => config.addHook('init', initHook), [config]);\n\n  return null;\n};\n"],"names":[],"mappings":"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"}