import * as React from 'react' import {__, _x} from '@wordpress/i18n' import { RawHTML, useRef, useState, } from '@wordpress/element' import { Button, } from '@ska/components' import { RadioProps, } from '.' import { type ArbitraryValueType, } from '../..' export interface EditableButtonProps { type: ArbitraryValueType label: string value?: string colorValue?: string isActive?: boolean isSmall?: RadioProps['isSmall'] onAdd?: RadioProps['onAdd'] onChange?: (nextValue: string) => void ButtonComponent?: React.FC } const getPrepend = (type: ArbitraryValueType) => { switch(type) { case 'arbitrary': return '[' case 'variable': return '(' case 'name': return '/' } return } const getAppend = (type: ArbitraryValueType) => { switch(type) { case 'arbitrary': return ']' case 'variable': return ')' case 'name': return } return } const EditableButton: React.FC = ({ type = 'bare', label, value = '', colorValue, isActive = false, isSmall = false, onAdd, onChange, ButtonComponent = Button, }) => { const [modified, setModified] = useState(false) const inputRef = useRef(null) return ( {getPrepend(type)} {( { e.preventDefault() e.stopPropagation() if(inputRef.current) { const currentText = inputRef.current.innerText const pastedText = e.clipboardData.getData('text/plain').trim().replace(/(\r\n|\n|\r|\t)/gm, ' ').trim() const {anchorOffset = 0, focusOffset = 0} = inputRef.current?.ownerDocument?.defaultView?.getSelection() || {} const nextText = `${currentText.substring(0, anchorOffset)}${pastedText}${currentText.substring(focusOffset, currentText.length)}` inputRef.current.innerText = nextText const selection = inputRef.current?.ownerDocument?.defaultView?.getSelection() // TODO: this can be perfected by capturing the original selection anchorOffset and adding pastedText.length if(selection) { const range = selection.getRangeAt(0) const index = nextText.indexOf(pastedText) const nextIndex = index > 0 ? Math.min(index + pastedText.length, nextText.length) : pastedText.length if(inputRef.current.firstChild) { range.setStart(inputRef.current.firstChild, nextIndex) range.setEnd(inputRef.current.firstChild, nextIndex) } } } return false }} onKeyDown={e => { if(e.key === 'Enter') { e.preventDefault() e.stopPropagation() if(!inputRef.current) { return } const nextValue = inputRef.current.innerText.trim().replaceAll(' ', '_') if(onChange) { if(!nextValue) { return } setModified(false) if(nextValue === value) { return } onChange(nextValue) return } if(onAdd) { if(nextValue) { onAdd(nextValue) inputRef.current.innerText = '' } } } }} onKeyUp={e => { if(e.key === 'Enter') { return } if(!modified && inputRef.current && inputRef.current.innerText !== value) { setModified(true) } }} children={value} /> )} {getAppend(type)} } onFocus={(e: React.FocusEvent) => { if(e.relatedTarget === inputRef.current) { return } if(isActive) { const defaultVariantLabel = _x('Default', 'Label for "default state" variant', 'ska-blocks') const isDefaultVariantEditableButton = label === defaultVariantLabel && value === defaultVariantLabel if(isDefaultVariantEditableButton) { /** Special case: when popover opens and focuses "Default" variant don't focus the `[contentEditable]`. */ if(e.relatedTarget && e.relatedTarget.getAttribute('aria-haspopup') === 'true') { inputRef?.current?.blur() return } /** Special case: select all text when focusing "Default" variant editable button. */ if(inputRef.current) { const range = document.createRange() range.selectNodeContents(inputRef.current) const selection = window.getSelection() if(selection) { selection.removeAllRanges() selection.addRange(range) } return } } } inputRef?.current?.focus() }} /> ) } export default EditableButton