/** * WordPress dependencies */ import { useState } from '@safe-wordpress/element'; import { Button } from '@safe-wordpress/components'; import { useBlockProps } from '@safe-wordpress/block-editor'; import { _x } from '@safe-wordpress/i18n'; import type { BlockEditProps } from '@safe-wordpress/blocks'; /** * External dependencies */ import clsx from 'clsx'; import { modifiers } from '@nelio/forms/utils'; import { FieldLabel, OptionLabel } from '@nelio/forms/components'; import { useFieldIdsEffect } from '@nelio/forms/hooks'; /** * Internal dependencies */ import Inspector from './inspector'; import BlockToolbar from './toolbar'; import type { Attributes, RadioItem } from './types'; import './editor.scss'; const Edit = ( props: BlockEditProps< Attributes > ): JSX.Element => { const { attributes, className, clientId, isSelected, setAttributes } = props; const { id, label, options, disabled, isLabelHidden, required } = attributes; const [ inFocus, setInFocus ] = useState< number | null >( null ); useFieldIdsEffect( clientId ); const applyModifiers = modifiers( { 'radio-group': true, required, disabled, } ); const blockProps = useBlockProps( { className: clsx( className, applyModifiers( 'nelio-forms-field' ) ), } ); const onOptionLabelChange = ( value: string, index: number ) => setAttributes( { options: options.map( ( o, i ) => i === index ? { ...o, label: value } : o ), } ); const onEnterPressed = ( target: HTMLInputElement, index: number ) => { setAttributes( { options: options.reduce( ( acc, o, i ) => i === index ? [ ...acc, ...splitLabel( o.label, o.checked, target.selectionStart, target.selectionEnd ), ] : [ ...acc, o ], [] as RadioItem[] ), } ); setInFocus( index + 1 ); }; const onRemovePressed = ( key: 'Backspace' | 'Delete', target: HTMLInputElement, index: number ): boolean => { const start = target.selectionStart; const end = target.selectionEnd; if ( start !== end ) { return false; } //end if const isBackspaceJoin = key === 'Backspace' && start === 0; const isDeleteJoin = key === 'Delete' && end === options[ index ]?.label.length; if ( index === 0 ) { if ( ! isDeleteJoin || options.length < 2 ) { return false; } //end if const [ c, n, ...r ] = options; setAttributes( { options: [ joinLabel( c as RadioItem, n as RadioItem ), ...r ], } ); return true; } //end if if ( index === options.length - 1 ) { if ( ! isBackspaceJoin || options.length < 2 ) { return false; } //end if const [ l, p, ...r ] = [ ...options ].reverse(); setAttributes( { options: [ joinLabel( p as RadioItem, l as RadioItem ), ...r, ].reverse(), } ); setInFocus( index - 1 ); return true; } //end if if ( isBackspaceJoin ) { const f = options.slice( 0, index - 1 ); const p = options[ index - 1 ]; const c = options[ index ]; const r = options.slice( index + 1 ); setAttributes( { options: [ ...f, joinLabel( p as RadioItem, c as RadioItem ), ...r, ], } ); setInFocus( index - 1 ); return true; } //end if if ( isDeleteJoin ) { const f = options.slice( 0, index ); const c = options[ index ]; const n = options[ index + 1 ]; const r = options.slice( index + 2 ); setAttributes( { options: [ ...f, joinLabel( c as RadioItem, n as RadioItem ), ...r, ], } ); return true; } //end if return false; }; return ( <> { isSelected && } { isSelected && }
setAttributes( { label: value } ) } />
{ options.map( ( option, index ) => (
setAttributes( { options: options.map( ( o ) => ( { ...o, checked: ev.target.value === o.label, } ) ), } ) } />
setInFocus( index ) } onChange={ ( value ) => onOptionLabelChange( value, index ) } onKeyDown={ ( event ) => { const { key } = event; const target = event.target as HTMLInputElement; if ( 'Enter' === key ) { onEnterPressed( target, index ); } //end if if ( 'Backspace' === key || 'Delete' === key ) { if ( onRemovePressed( key, target, index ) ) { event.preventDefault(); } //end if } //end if if ( 'ArrowDown' === key ) { setInFocus( Math.min( options.length, index + 1 ) ); } //end if if ( 'ArrowUp' === key ) { setInFocus( Math.max( 0, index - 1 ) ); } //end if } } /> { options.length > 2 && (
) ) }
); }; export default Edit; // ======= // HELPERS // ======= const splitLabel = ( label: string, checked: boolean, start: number | null, end: number | null ) => { return [ { checked, label: start !== null ? label.substring( 0, start ) : label, }, { checked: false, label: end !== null ? label.substring( end ) : '', }, ]; }; const joinLabel = ( o1: RadioItem, o2: RadioItem ): RadioItem => { if ( ! o2 ) { return { ...o1 }; } //end if return { label: o1.label + o2.label, checked: o1.checked || o2.checked, }; };