import { DefaultColorStyle, elementShouldCaptureKeys, getColorValue, SharedStyle, StyleProp, TLDefaultColorStyle, useEditor, useValue, } from '@tldraw/editor' import { memo, useMemo, useRef } from 'react' import { StyleValuesForUi } from '../../../styles' import { PORTRAIT_BREAKPOINT } from '../../constants' import { useBreakpoint } from '../../context/breakpoints' import { TLUiTranslationKey } from '../../hooks/useTranslation/TLUiTranslationKey' import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { TldrawUiButtonIcon } from '../primitives/Button/TldrawUiButtonIcon' import { TldrawUiGrid, TldrawUiRow } from '../primitives/layout' import { TldrawUiToolbar, TldrawUiToolbarToggleGroup, TldrawUiToolbarToggleItem, } from '../primitives/TldrawUiToolbar' import { useStylePanelContext } from './StylePanelContext' import { StylePanelSubheading } from './StylePanelSubheading' /** @public */ export interface StylePanelButtonPickerProps { title: string uiType: string style: StyleProp value: SharedStyle items: StyleValuesForUi onValueChange?(style: StyleProp, value: T): void onHistoryMark?(id: string): void } function StylePanelButtonPickerInner(props: StylePanelButtonPickerProps) { const { enhancedA11yMode } = useStylePanelContext() return ( <> {enhancedA11yMode && {props.title}} ) } function StylePanelButtonPickerInlineInner( props: StylePanelButtonPickerProps ) { const ctx = useStylePanelContext() const { uiType, items, title, style, value, onValueChange = ctx.onValueChange, onHistoryMark = ctx.onHistoryMark, } = props const editor = useEditor() const colors = useValue( 'style panel button picker colors', () => editor.getCurrentTheme().colors[editor.getColorMode()], [editor] ) const msg = useTranslation() const breakpoint = useBreakpoint() const rPointing = useRef(false) const rPointingOriginalActiveElement = useRef(null) const { handleButtonClick, handleButtonPointerDown, handleButtonPointerEnter, handleButtonPointerUp, } = useMemo(() => { const handlePointerUp = () => { rPointing.current = false editor.getContainerWindow().removeEventListener('pointerup', handlePointerUp) // This is fun little micro-optimization to make sure that the focus // is retained on a text label. That way, you can continue typing // after selecting a style. const origActiveEl = rPointingOriginalActiveElement.current if (origActiveEl && elementShouldCaptureKeys(origActiveEl, false)) { origActiveEl.focus() } else if (breakpoint >= PORTRAIT_BREAKPOINT.TABLET_SM) { editor.getContainer().focus() } rPointingOriginalActiveElement.current = null } const handleButtonClick = (e: React.PointerEvent) => { const { id } = e.currentTarget.dataset if (value.type === 'shared' && value.value === id) return onHistoryMark?.('point picker item') onValueChange(style, id as T) } const handleButtonPointerDown = (e: React.PointerEvent) => { const { id } = e.currentTarget.dataset onHistoryMark?.('point picker item') onValueChange(style, id as T) rPointing.current = true rPointingOriginalActiveElement.current = editor.getContainerDocument() .activeElement as HTMLElement editor.getContainerWindow().addEventListener('pointerup', handlePointerUp) // see TLD-658 } const handleButtonPointerEnter = (e: React.PointerEvent) => { if (!rPointing.current) return const { id } = e.currentTarget.dataset onValueChange(style, id as T) } const handleButtonPointerUp = (e: React.PointerEvent) => { const { id } = e.currentTarget.dataset if (value.type === 'shared' && value.value === id) return onValueChange(style, id as T) } return { handleButtonClick, handleButtonPointerDown, handleButtonPointerEnter, handleButtonPointerUp, } }, [editor, breakpoint, value, onHistoryMark, onValueChange, style]) const Layout = items.length > 4 ? TldrawUiGrid : TldrawUiRow return ( {items.map((item) => { const isActive = value.type === 'shared' && value.value === item.value const label = title + ' — ' + msg(`${uiType}-style.${item.value}` as TLUiTranslationKey) return (
{label}
{isActive ?
({msg('style-panel.selected')})
: null} } value={item.value} data-state={value.type === 'shared' && value.value === item.value ? 'on' : 'off'} data-isactive={isActive} title={label} style={ style === (DefaultColorStyle as StyleProp) ? { color: getColorValue(colors, item.value as TLDefaultColorStyle, 'solid') } : undefined } onPointerEnter={handleButtonPointerEnter} onPointerDown={handleButtonPointerDown} onPointerUp={handleButtonPointerUp} onClick={handleButtonClick} >
) })}
) } /** @public @react */ export const StylePanelButtonPicker = memo(StylePanelButtonPickerInner) as ( props: StylePanelButtonPickerProps ) => React.JSX.Element /** @public @react*/ export const StylePanelButtonPickerInline = memo(StylePanelButtonPickerInlineInner) as < T extends string, >( props: StylePanelButtonPickerProps ) => React.JSX.Element