Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/clear-formats/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { RichTextToolbarButton } from '@wordpress/block-editor';
import {
Modal,
Button,
__experimentalText as Text,
__experimentalHStack as HStack,
} from '@wordpress/components';

/**
* Internal dependencies
*/
import { clearFormatsIcon } from './icon';
import { useClearFormats } from './hooks';

/**
* Edit component for clearing all formats.
*
* @param {Object} props - The component properties.
* @param {Object} props.value - The current value of the rich text.
* @param {Function} props.onChange - Function to update the rich text value.
* @return {JSX.Element} - The rendered clear formats button.
*/
export function Edit( { value, onChange } ) {
const [ isModalOpen, setIsModalOpen ] = useState( false );
const { hasSelection, clearFormats } = useClearFormats( {
value,
onChange,
} );

const handleClearFormats = () => {
clearFormats();
setIsModalOpen( false );
};

// Dynamic confirmation message based on selection
const selectionType = hasSelection
? __( 'text', 'blablablocks-formats' )
: __( 'block', 'blablablocks-formats' );

const confirmationMessage = sprintf(
/* translators: %s: selection type (text/block) */
__(
'Are you sure you want to remove all formatting (bold, italic, links, etc.) from the selected %s?',
'blablablocks-formats'
),
selectionType
);

return (
<>
<RichTextToolbarButton
icon={ clearFormatsIcon }
title={ __( 'Clear formatting', 'blablablocks-formats' ) }
onClick={ () => setIsModalOpen( true ) }
/>

{ isModalOpen && (
<Modal
title={ __( 'Clear All Formats', 'blablablocks-formats' ) }
onRequestClose={ () => setIsModalOpen( false ) }
size="small"
>
<Text as="p" highlightWords={ [ 'text', 'block' ] }>
{ confirmationMessage }
</Text>
<HStack
spacing={ 2 }
justify="flex-end"
style={ { marginTop: '24px' } }
>
<Button
variant="tertiary"
onClick={ () => setIsModalOpen( false ) }
>
{ __( 'Cancel', 'blablablocks-formats' ) }
</Button>
<Button
variant="primary"
onClick={ handleClearFormats }
isDestructive
>
{ __( 'Clear', 'blablablocks-formats' ) }
</Button>
</HStack>
</Modal>
) }
</>
);
}
66 changes: 66 additions & 0 deletions src/clear-formats/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* WordPress dependencies
*/
import { useCallback, useMemo } from '@wordpress/element';
import { removeFormat, create, getActiveFormats } from '@wordpress/rich-text';

/**
* Provides helpers for clearing formats from a rich text value.
*
* @param {Object} options - Hook options.
* @param {Object} options.value - Current rich text value.
* @param {Function} options.onChange - Callback to update the value.
* @return {{hasSelection: boolean, clearFormats: Function}} Hook results.
*/
export const useClearFormats = ( { value, onChange } ) => {
const hasSelection = useMemo(
() => value.start !== value.end,
[ value.start, value.end ]
);

const clearFormats = useCallback( () => {
let newValue = value;

if ( hasSelection ) {
const formats = getActiveFormats( value );

formats.forEach( ( format ) => {
newValue = removeFormat(
newValue,
format.type,
value.start,
value.end
);
} );

if ( value.formats ) {
const formatTypes = new Set();

for ( let index = value.start; index < value.end; index++ ) {
if ( value.formats[ index ] ) {
value.formats[ index ].forEach( ( format ) => {
formatTypes.add( format.type );
} );
}
}

formatTypes.forEach( ( formatType ) => {
newValue = removeFormat(
newValue,
formatType,
value.start,
value.end
);
} );
}

onChange( newValue );
return;
}

newValue = create( { text: value.text } );
onChange( newValue );
}, [ hasSelection, onChange, value ] );

return { hasSelection, clearFormats };
};
17 changes: 17 additions & 0 deletions src/clear-formats/icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const clearFormatsIcon = (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M7 6.2V5h12v1.2M7 19h6m.2-14-1.677 6.523M9.6 19l1.029-4M5 5l6.523 6.523M19 19l-7.477-7.477"
/>
</svg>
);
21 changes: 21 additions & 0 deletions src/clear-formats/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { registerFormatType } from '@wordpress/rich-text';

/**
* Internal dependencies
*/
import { Edit } from './edit';

/**
* Registers the Clear All Formats button.
* This is not a traditional format but a toolbar button to clear formats.
*/
registerFormatType( 'blablablocks/clear-formats', {
title: __( 'Clear formatting', 'blablablocks-formats' ),
tagName: 'mark', // Using 'mark' to avoid conflict with core formats that use 'span'
className: null,
edit: Edit,
} );
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
import './marker';
import './infotip';
import './font-size';
import './clear-formats';