| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- import { memo, useCallback, useEffect, useRef, useState } from 'react'
- import { createPortal } from 'react-dom'
- import {
- useCodeMirrorStateContext,
- useCodeMirrorViewContext,
- } from './codemirror-editor'
- import { searchPanelOpen } from '@codemirror/search'
- import { useResizeObserver } from '../../../shared/hooks/use-resize-observer'
- import { ToolbarButton } from './toolbar/toolbar-button'
- import { ToolbarItems } from './toolbar/toolbar-items'
- import * as commands from '../extensions/toolbar/commands'
- import { ToolbarOverflow } from './toolbar/overflow'
- import useDropdown from '../../../shared/hooks/use-dropdown'
- import { getPanel } from '@codemirror/view'
- import { createToolbarPanel } from '../extensions/toolbar/toolbar-panel'
- import EditorSwitch from './editor-switch'
- import SwitchToPDFButton from './switch-to-pdf-button'
- import { DetacherSynctexControl } from '../../pdf-preview/components/detach-synctex-control'
- import DetachCompileButtonWrapper from '../../pdf-preview/components/detach-compile-button-wrapper'
- import { isVisual } from '../extensions/visual/visual'
- import { language } from '@codemirror/language'
- import { minimumListDepthForSelection } from '../utils/tree-operations/ancestors'
- export const CodeMirrorToolbar = () => {
- const view = useCodeMirrorViewContext()
- const panel = getPanel(view, createToolbarPanel)
- if (!panel) {
- return null
- }
- return createPortal(<Toolbar />, panel.dom)
- }
- const Toolbar = memo(function Toolbar() {
- const state = useCodeMirrorStateContext()
- const view = useCodeMirrorViewContext()
- const [overflowed, setOverflowed] = useState(false)
- const [collapsed, setCollapsed] = useState(false)
- const overflowedItemsRef = useRef<Set<string>>(new Set())
- const languageName = state.facet(language)?.name
- const visual = isVisual(view)
- const listDepth = minimumListDepthForSelection(state)
- const {
- open: overflowOpen,
- onToggle: setOverflowOpen,
- ref: overflowRef,
- } = useDropdown()
- const buildOverflow = useCallback(
- (element: Element) => {
- setOverflowOpen(false)
- setOverflowed(true)
- overflowedItemsRef.current = new Set()
- const buttonGroups = [
- ...element.querySelectorAll<HTMLDivElement>('[data-overflow]'),
- ].reverse()
- // restore all the overflowed items
- for (const buttonGroup of buttonGroups) {
- buttonGroup.classList.remove('overflow-hidden')
- }
- // find all the available items
- for (const buttonGroup of buttonGroups) {
- if (element.scrollWidth <= element.clientWidth) {
- break
- }
- // add this item to the overflow
- overflowedItemsRef.current.add(buttonGroup.dataset.overflow!)
- buttonGroup.classList.add('overflow-hidden')
- }
- setOverflowed(overflowedItemsRef.current.size > 0)
- },
- [setOverflowOpen]
- )
- // calculate overflow when the container resizes
- const { elementRef, resizeRef } = useResizeObserver(buildOverflow)
- // calculate overflow when `languageName` or `visual` change
- useEffect(() => {
- if (resizeRef.current) {
- buildOverflow(resizeRef.current.element)
- }
- }, [buildOverflow, languageName, resizeRef, visual])
- const insideTable = document.activeElement?.closest(
- '.table-generator-help-modal,.table-generator'
- )
- useEffect(() => {
- if (resizeRef.current) {
- buildOverflow(resizeRef.current.element)
- }
- }, [buildOverflow, insideTable, resizeRef])
- const toggleToolbar = useCallback(() => {
- setCollapsed(value => !value)
- }, [])
- if (collapsed) {
- return null
- }
- const showActions = !state.readOnly && !insideTable
- return (
- <div className="ol-cm-toolbar toolbar-editor" ref={elementRef}>
- <EditorSwitch />
- {showActions && (
- <ToolbarItems
- state={state}
- languageName={languageName}
- visual={visual}
- listDepth={listDepth}
- />
- )}
- <div className="ol-cm-toolbar-button-group ol-cm-toolbar-stretch">
- {showActions && (
- <ToolbarOverflow
- overflowed={overflowed}
- overflowOpen={overflowOpen}
- setOverflowOpen={setOverflowOpen}
- overflowRef={overflowRef}
- >
- <ToolbarItems
- state={state}
- overflowed={overflowedItemsRef.current}
- languageName={languageName}
- visual={visual}
- listDepth={listDepth}
- />
- </ToolbarOverflow>
- )}
- <div className="formatting-buttons-wrapper" />
- </div>
- <div className="ol-cm-toolbar-button-group ol-cm-toolbar-end">
- <ToolbarButton
- id="toolbar-toggle-search"
- label="Toggle Search"
- command={commands.toggleSearch}
- active={searchPanelOpen(state)}
- icon="search"
- />
- <SwitchToPDFButton />
- <DetacherSynctexControl />
- <DetachCompileButtonWrapper />
- </div>
- <div className="ol-cm-toolbar-button-group hidden">
- <ToolbarButton
- id="toolbar-expand-less"
- label="Hide Toolbar"
- command={toggleToolbar}
- icon="caret-up"
- hidden // enable this once there's a way to show the toolbar again
- />
- </div>
- </div>
- )
- })
|