| 1 |
/** |
| 2 |
* Toolbar Delete button on PB form CPTs (replaces Options ⋮; CSS hides the menu). |
| 3 |
* Form-only remove — global row stays. Hidden for mandatoryTypes / !canRemoveBlock. |
| 4 |
*/ |
| 5 |
|
| 6 |
import { addFilter } from '@wordpress/hooks'; |
| 7 |
import { __ } from '@wordpress/i18n'; |
| 8 |
import { BlockControls } from '@wordpress/block-editor'; |
| 9 |
import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; |
| 10 |
import { useSelect } from '@wordpress/data'; |
| 11 |
import { removeBlocksWithUndo } from '../lib/removeBlocksWithUndo'; |
| 12 |
|
| 13 |
// Mandatory default field types whose Delete affordance must never appear — |
| 14 |
// same source and fallback as the Existing Fields panel's MANDATORY_DEFAULT_TYPES |
| 15 |
// (window.wppbFb.mandatoryTypes, populated by the inline-script bridge before |
| 16 |
// this bundle inits). Falls back to the hardcoded triple if the bridge is absent. |
| 17 |
const MANDATORY_FIELD_TYPES = new Set( |
| 18 |
( typeof window !== 'undefined' && window.wppbFb && |
| 19 |
Array.isArray( window.wppbFb.mandatoryTypes ) && window.wppbFb.mandatoryTypes.length ) |
| 20 |
? window.wppbFb.mandatoryTypes |
| 21 |
: [ 'Default - Username', 'Default - E-mail', 'Default - Password' ] |
| 22 |
); |
| 23 |
|
| 24 |
// Resolve a block name to its PB field-type label via the canonical |
| 25 |
// blockToFieldType map (single home under conditionalFields), then test for a |
| 26 |
// mandatory type. Read lazily (not memoized at module load) so it tracks the |
| 27 |
// bridge like BaseFieldEdit's getMetaNameConfig() does. |
| 28 |
function isMandatoryFieldType( blockName ) { |
| 29 |
const fb = ( typeof window !== 'undefined' && window.wppbFb ) || {}; |
| 30 |
const map = ( fb.conditionalFields && fb.conditionalFields.blockToFieldType ) || {}; |
| 31 |
return MANDATORY_FIELD_TYPES.has( map[ blockName ] ); |
| 32 |
} |
| 33 |
|
| 34 |
// Inline trash glyph — matches the FormPreviewButton convention of inlining |
| 35 |
// SVGs (fill via inline style, which also beats Gutenberg's block-icon |
| 36 |
// `svg { fill: currentColor }` override) instead of a @wordpress/icons |
| 37 |
// dependency that would pull the package into the bundle. |
| 38 |
const TrashIcon = () => ( |
| 39 |
<svg |
| 40 |
width="24" |
| 41 |
height="24" |
| 42 |
viewBox="0 0 24 24" |
| 43 |
xmlns="http://www.w3.org/2000/svg" |
| 44 |
aria-hidden="true" |
| 45 |
focusable="false" |
| 46 |
> |
| 47 |
<path |
| 48 |
style={ { fill: 'currentColor' } } |
| 49 |
d="M9 3h6v2h5v2H4V5h5V3zm-3 6h2v9H6V9zm5 0h2v9h-2V9zm5 0h2v9h-2V9zM5 7h14l-1 13a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1L5 7z" |
| 50 |
/> |
| 51 |
</svg> |
| 52 |
); |
| 53 |
|
| 54 |
function DeleteToolbarControl( { clientId } ) { |
| 55 |
const canRemove = useSelect( |
| 56 |
( select ) => select( 'core/block-editor' ).canRemoveBlock( clientId ), |
| 57 |
[ clientId ] |
| 58 |
); |
| 59 |
|
| 60 |
if ( ! canRemove ) return null; |
| 61 |
|
| 62 |
return ( |
| 63 |
<BlockControls> |
| 64 |
<ToolbarGroup> |
| 65 |
<ToolbarButton |
| 66 |
icon={ <TrashIcon /> } |
| 67 |
label={ __( 'Remove field', 'profile-builder' ) } |
| 68 |
// Snackbar-Undo instead of a silent removeBlock: global |
| 69 |
// undo is disabled in this editor, so this is the only |
| 70 |
// in-session recovery path. |
| 71 |
onClick={ () => removeBlocksWithUndo( [ clientId ] ) } |
| 72 |
/> |
| 73 |
</ToolbarGroup> |
| 74 |
</BlockControls> |
| 75 |
); |
| 76 |
} |
| 77 |
|
| 78 |
const withDeleteToolbarButton = ( BlockEdit ) => ( props ) => { |
| 79 |
const { name, isSelected, clientId } = props; |
| 80 |
const isPbBlock = typeof name === 'string' && name.startsWith( 'profile-builder/' ); |
| 81 |
// Field-type gate: never add the Delete button to a mandatory field. Cheap, |
| 82 |
// hook-free, so it's evaluated here in the wrapper rather than inside |
| 83 |
// DeleteToolbarControl (which then never mounts for mandatory fields). |
| 84 |
const showDelete = isPbBlock && isSelected && ! isMandatoryFieldType( name ); |
| 85 |
|
| 86 |
return ( |
| 87 |
<> |
| 88 |
<BlockEdit { ...props } /> |
| 89 |
{ showDelete && <DeleteToolbarControl clientId={ clientId } /> } |
| 90 |
</> |
| 91 |
); |
| 92 |
}; |
| 93 |
|
| 94 |
// editor.BlockEdit filters apply at render time (not registration time), so — |
| 95 |
// unlike the blocks.registerBlockType filters at the top of index.js — this |
| 96 |
// import's position relative to the block imports is not load-bearing. |
| 97 |
addFilter( |
| 98 |
'editor.BlockEdit', |
| 99 |
'profile-builder/block-toolbar-delete', |
| 100 |
withDeleteToolbarButton |
| 101 |
); |
| 102 |
|