simian 0.2.0

A command-line tool for exploring and implementing Machine Learning algorithms in Rust.
import { FC, useCallback, useState } from 'react'
import { createPortal } from 'react-dom'
import { Editor, Range } from 'slate'
import { ReactEditor, useFocused } from 'slate-react'

import { useEditor } from '@/ui/editor/context'

import { Position, TextMenuBar } from './bar'

////////////////////////////////////////////////////////////
// Utilitary Functions
////////////////////////////////////////////////////////////
// function getPosition(editor: Editor, barEl: HTMLElement) {
//   if (!editor.selection) { return; }

//   try {
//     const domRange = ReactEditor.toDOMRange(editor, editor.selection);
//     const rect = domRange.getBoundingClientRect();

//     const editorEl = document.getElementById(`editor-${editor.id}`) as HTMLElement;
//     const editorRect = editorEl.getBoundingClientRect();

//     return {
//       // We use rect.top and subtract the menu height + a small offset (8px)
//       top: rect.top - editorRect.top + editorEl.scrollTop - barEl.offsetHeight - 8,
//       // Center the menu horizontally relative to the selection
//       left: rect.left - editorRect.left + editorEl.scrollLeft + rect.width / 2 - barEl.offsetWidth / 2,
//     };
//   } catch {
//     // Do nothing.
//   }

//   return { top: 0, left: 0 };
// }
function getPosition(editor: Editor, barEl: HTMLElement) {
  if (!editor.selection) return

  try {
    const domRange = ReactEditor.toDOMRange(editor, editor.selection)
    const rect = domRange.getBoundingClientRect()

    return {
      // viewport top + current page scroll - menu height - offset
      top: rect.top + window.scrollY - barEl.offsetHeight - 8,
      // viewport left + current page scroll + half selection width - half menu width
      left: rect.left + window.scrollX + rect.width / 2 - barEl.offsetWidth / 2,
    }
  } catch {
    return { top: 0, left: 0 }
  }
}

////////////////////////////////////////////////////////////
// Floating Menu (For Desktop)
////////////////////////////////////////////////////////////
export const TextFloatingMenu: FC = () => {
  const { editor } = useEditor()
  const [position, setPosition] = useState<Position | undefined>()
  const isFocused = useFocused()

  const { selection } = editor

  const shouldShow =
    isFocused &&
    selection &&
    Range.isExpanded(selection) &&
    Editor.string(editor, selection).trim() !== ''

  const onBarMount = useCallback(
    (barEl: HTMLDivElement) => {
      if (!selection) {
        return
      }

      setPosition(getPosition(editor, barEl))
    },
    [editor, selection, setPosition],
  )

  return shouldShow
    ? createPortal(
        <TextMenuBar position={position} onMount={onBarMount} />,
        document.getElementById(`text-menu-root-${editor.id}`)!,
      )
    : null
}