import { Editor, TLNullableShapeProps, TLStyleItem, useEditor } from '@tldraw/editor' import React, { useCallback } from 'react' import { minBy } from '@tldraw/utils' import { useValue } from 'signia-react' import { useTranslation } from '../../hooks/useTranslation/useTranslation' import { Button } from '../primitives/Button' import { ButtonPicker } from '../primitives/ButtonPicker' import { Slider } from '../primitives/Slider' import { DoubleDropdownPicker } from './DoubleDropdownPicker' import { DropdownPicker } from './DropdownPicker' interface StylePanelProps { isMobile?: boolean } /** @internal */ export const StylePanel = function StylePanel({ isMobile }: StylePanelProps) { const editor = useEditor() const props = useValue('props', () => editor.props, [editor]) const opacity = useValue('opacity', () => editor.opacity, [editor]) const toolShapeType = useValue('toolShapeType', () => editor.root.current.value?.shapeType, [ editor, ]) const handlePointerOut = useCallback(() => { if (!isMobile) { editor.isChangingStyle = false } }, [editor, isMobile]) if (!props && !toolShapeType) return null const { geo, arrowheadEnd, arrowheadStart, spline, font } = props ?? {} const hideGeo = geo === undefined const hideArrowHeads = arrowheadEnd === undefined && arrowheadStart === undefined const hideSpline = spline === undefined const hideText = font === undefined return (
{!hideText && } {!(hideGeo && hideArrowHeads && hideSpline) && (
)}
) } const { styles } = Editor function useStyleChangeCallback() { const editor = useEditor() return React.useCallback( (item: TLStyleItem, squashing: boolean) => { editor.batch(() => { editor.setProp(item.type, item.id, false, squashing) editor.isChangingStyle = true }) }, [editor] ) } const tldrawSupportedOpacities = [0.1, 0.25, 0.5, 0.75, 1] as const function CommonStylePickerSet({ props, opacity, }: { props: TLNullableShapeProps opacity: number | null }) { const editor = useEditor() const msg = useTranslation() const handleValueChange = useStyleChangeCallback() const handleOpacityValueChange = React.useCallback( (value: number, ephemeral: boolean) => { const item = tldrawSupportedOpacities[value] editor.setOpacity(item, ephemeral) editor.isChangingStyle = true }, [editor] ) const { color, fill, dash, size } = props if ( color === undefined && fill === undefined && dash === undefined && size === undefined && opacity === undefined ) { return null } const showPickers = fill !== undefined || dash !== undefined || size !== undefined const opacityIndex = opacity === null ? -1 : tldrawSupportedOpacities.indexOf( minBy(tldrawSupportedOpacities, (supportedOpacity) => Math.abs(supportedOpacity - opacity) )! ) return ( <>
{color === undefined ? null : ( )} {opacity === undefined ? null : ( = 0 ? opacityIndex : tldrawSupportedOpacities.length - 1} label={opacity ? `opacity-style.${opacity}` : 'style-panel.mixed'} onValueChange={handleOpacityValueChange} steps={tldrawSupportedOpacities.length - 1} title={msg('style-panel.opacity')} /> )}
{showPickers && (
{fill === undefined ? null : ( )} {dash === undefined ? null : ( )} {size === undefined ? null : ( )}
)} ) } function TextStylePickerSet({ props }: { props: TLNullableShapeProps }) { const msg = useTranslation() const handleValueChange = useStyleChangeCallback() const { font, align, verticalAlign } = props if (font === undefined && align === undefined) { return null } return (
{font === undefined ? null : ( )} {align === undefined ? null : (
{verticalAlign === undefined ? (
)}
) } function GeoStylePickerSet({ props }: { props: TLNullableShapeProps }) { const handleValueChange = useStyleChangeCallback() const { geo } = props if (geo === undefined) { return null } return ( ) } function SplineStylePickerSet({ props }: { props: TLNullableShapeProps }) { const handleValueChange = useStyleChangeCallback() const { spline } = props if (spline === undefined) { return null } return ( ) } function ArrowheadStylePickerSet({ props }: { props: TLNullableShapeProps }) { const handleValueChange = useStyleChangeCallback() const { arrowheadEnd, arrowheadStart } = props if (arrowheadEnd === undefined && arrowheadStart === undefined) { return null } return ( ) }