Create new richText component with button
[MAILPOET-6354]
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
import { Modal, SearchControl } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { PersonalizationTag, storeName } from '../../store';
|
||||
import { select } from '@wordpress/data';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { external, Icon } from '@wordpress/icons';
|
||||
import './index.scss';
|
||||
import { useState } from '@wordpress/element';
|
||||
@ -12,12 +12,15 @@ const PersonalizationTagsModal = ( { onInsert, isOpened, closeCallback } ) => {
|
||||
const [ activeCategory, setActiveCategory ] = useState( null );
|
||||
const [ searchQuery, setSearchQuery ] = useState( '' );
|
||||
|
||||
const list = useSelect(
|
||||
( select ) => select( storeName ).getPersonalizationTagsList(),
|
||||
[]
|
||||
);
|
||||
|
||||
if ( ! isOpened ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const list = select( storeName ).getPersonalizationTagsList();
|
||||
|
||||
const groupedTags: Record< string, PersonalizationTag[] > = list.reduce(
|
||||
( groups, item ) => {
|
||||
const { category, name, token } = item;
|
||||
|
@ -0,0 +1,130 @@
|
||||
import { BaseControl, Button } from '@wordpress/components';
|
||||
import { PersonalizationTagsModal } from './personalization-tags-modal';
|
||||
import { useRef, useState } from '@wordpress/element';
|
||||
import {
|
||||
createTextToHtmlMap,
|
||||
getCursorPosition,
|
||||
isMatchingComment,
|
||||
} from './rich-text-utils';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { useEntityProp } from '@wordpress/core-data';
|
||||
import { storeName } from '../../store';
|
||||
import { RichText } from '@wordpress/block-editor';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
export function RichTextWithButton( {
|
||||
label,
|
||||
labelSuffix,
|
||||
help,
|
||||
placeholder,
|
||||
attributeName,
|
||||
} ) {
|
||||
const [ mailpoetEmailData ] = useEntityProp(
|
||||
'postType',
|
||||
'mailpoet_email',
|
||||
'mailpoet_data'
|
||||
);
|
||||
|
||||
const { updateEmailMailPoetProperty } = useDispatch( storeName );
|
||||
|
||||
const [ selectionRange, setSelectionRange ] = useState( null );
|
||||
const [ isModalOpened, setIsModalOpened ] = useState( false );
|
||||
|
||||
const richTextRef = useRef( null );
|
||||
|
||||
const handleInsertPersonalizationTag = async ( value ) => {
|
||||
// Retrieve the current value of the active RichText
|
||||
const currentValue = mailpoetEmailData[ attributeName ] ?? '';
|
||||
|
||||
// Generate text-to-HTML mapping
|
||||
const { mapping } = createTextToHtmlMap( currentValue );
|
||||
// Ensure selection range is within bounds
|
||||
const start = selectionRange?.start ?? currentValue.length;
|
||||
const end = selectionRange?.end ?? currentValue.length;
|
||||
|
||||
// Default values for starting and ending indexes.
|
||||
let htmlStart = start;
|
||||
let htmlEnd = end;
|
||||
// If indexes are not matching a comment, update them
|
||||
if ( ! isMatchingComment( currentValue, start, end ) ) {
|
||||
htmlStart = mapping[ start ] ?? currentValue.length;
|
||||
htmlEnd = mapping[ end ] ?? currentValue.length;
|
||||
}
|
||||
|
||||
// Insert the new tag
|
||||
const updatedValue =
|
||||
currentValue.slice( 0, htmlStart ) +
|
||||
`<!--${ value }-->` +
|
||||
currentValue.slice( htmlEnd );
|
||||
|
||||
// Update the corresponding property
|
||||
updateEmailMailPoetProperty( attributeName, updatedValue );
|
||||
|
||||
setSelectionRange( null );
|
||||
};
|
||||
|
||||
const finalLabel = (
|
||||
<>
|
||||
<span>{ label }</span>
|
||||
<Button
|
||||
className="mailpoet-settings-panel__personalization-tags-button"
|
||||
icon="shortcode"
|
||||
title={ __( 'Personalization Tags', 'mailpoet' ) }
|
||||
onClick={ () => setIsModalOpened( true ) }
|
||||
/>
|
||||
{ labelSuffix }
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseControl
|
||||
id={ `mailpoet-settings-panel__${ attributeName }` }
|
||||
label={ finalLabel }
|
||||
className={ `mailpoet-settings-panel__${ attributeName }-text` }
|
||||
help={ help }
|
||||
>
|
||||
<PersonalizationTagsModal
|
||||
isOpened={ isModalOpened }
|
||||
onInsert={ ( value ) => {
|
||||
handleInsertPersonalizationTag( value );
|
||||
setIsModalOpened( false );
|
||||
} }
|
||||
closeCallback={ () => setIsModalOpened( false ) }
|
||||
/>
|
||||
<RichText
|
||||
ref={ richTextRef }
|
||||
className="mailpoet-settings-panel__richtext"
|
||||
placeholder={ placeholder }
|
||||
onFocus={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
richTextRef,
|
||||
mailpoetEmailData[ attributeName ] ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onKeyUp={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
richTextRef,
|
||||
mailpoetEmailData[ attributeName ] ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onClick={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
richTextRef,
|
||||
mailpoetEmailData[ attributeName ] ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onChange={ ( value ) =>
|
||||
updateEmailMailPoetProperty( attributeName, value )
|
||||
}
|
||||
value={ mailpoetEmailData[ attributeName ] ?? '' }
|
||||
data-automation-id="email_preview_text"
|
||||
/>
|
||||
</BaseControl>
|
||||
);
|
||||
}
|
@ -1,37 +1,13 @@
|
||||
import {
|
||||
BaseControl,
|
||||
Button,
|
||||
ExternalLink,
|
||||
PanelBody,
|
||||
} from '@wordpress/components';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { ExternalLink, PanelBody } from '@wordpress/components';
|
||||
import { useEntityProp } from '@wordpress/core-data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { createInterpolateElement, useState, useRef } from '@wordpress/element';
|
||||
import { createInterpolateElement } from '@wordpress/element';
|
||||
import classnames from 'classnames';
|
||||
import { storeName } from '../../store';
|
||||
import { RichText } from '@wordpress/block-editor';
|
||||
import {
|
||||
createTextToHtmlMap,
|
||||
getCursorPosition,
|
||||
isMatchingComment,
|
||||
} from '../personalization-tags/rich-text-utils';
|
||||
import { PersonalizationTagsModal } from '../personalization-tags/personalization-tags-modal';
|
||||
import { RichTextWithButton } from '../personalization-tags/rich-text-with-button';
|
||||
|
||||
const previewTextMaxLength = 150;
|
||||
const previewTextRecommendedLength = 80;
|
||||
|
||||
function PersonalizationTagsButton( { onClick } ) {
|
||||
return (
|
||||
<Button
|
||||
className="mailpoet-settings-panel__personalization-tags-button"
|
||||
icon="shortcode"
|
||||
title={ __( 'Personalization Tags', 'mailpoet' ) }
|
||||
onClick={ () => onClick() }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function DetailsPanel() {
|
||||
const [ mailpoetEmailData ] = useEntityProp(
|
||||
'postType',
|
||||
@ -39,59 +15,6 @@ export function DetailsPanel() {
|
||||
'mailpoet_data'
|
||||
);
|
||||
|
||||
const { updateEmailMailPoetProperty } = useDispatch( storeName );
|
||||
|
||||
const [ selectionRange, setSelectionRange ] = useState( null );
|
||||
const [ subjectModalIsOpened, setSubjectModalIsOpened ] = useState( false );
|
||||
const [ preheaderModalIsOpened, setPreheaderModalIsOpened ] =
|
||||
useState( false );
|
||||
|
||||
const subjectRef = useRef( null );
|
||||
const preheaderRef = useRef( null );
|
||||
|
||||
const handleInsertPersonalizationTag = async ( value, activeRichText ) => {
|
||||
// Retrieve the current value of the active RichText
|
||||
const currentValue =
|
||||
activeRichText === 'subject'
|
||||
? mailpoetEmailData?.subject ?? ''
|
||||
: mailpoetEmailData?.preheader ?? '';
|
||||
|
||||
const ref = activeRichText === 'subject' ? subjectRef : preheaderRef;
|
||||
if ( ! ref ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate text-to-HTML mapping
|
||||
const { mapping } = createTextToHtmlMap( currentValue );
|
||||
// Ensure selection range is within bounds
|
||||
const start = selectionRange?.start ?? currentValue.length;
|
||||
const end = selectionRange?.end ?? currentValue.length;
|
||||
|
||||
// Default values for starting and ending indexes.
|
||||
let htmlStart = start;
|
||||
let htmlEnd = end;
|
||||
// If indexes are not matching a comment, update them
|
||||
if ( ! isMatchingComment( currentValue, start, end ) ) {
|
||||
htmlStart = mapping[ start ] ?? currentValue.length;
|
||||
htmlEnd = mapping[ end ] ?? currentValue.length;
|
||||
}
|
||||
|
||||
// Insert the new tag
|
||||
const updatedValue =
|
||||
currentValue.slice( 0, htmlStart ) +
|
||||
`<!--${ value }-->` +
|
||||
currentValue.slice( htmlEnd );
|
||||
|
||||
// Update the corresponding property
|
||||
if ( activeRichText === 'subject' ) {
|
||||
updateEmailMailPoetProperty( 'subject', updatedValue );
|
||||
} else if ( activeRichText === 'preheader' ) {
|
||||
updateEmailMailPoetProperty( 'preheader', updatedValue );
|
||||
}
|
||||
|
||||
setSelectionRange( null );
|
||||
};
|
||||
|
||||
const subjectHelp = createInterpolateElement(
|
||||
__(
|
||||
'Use shortcodes to personalize your email, or learn more about <bestPracticeLink>best practices</bestPracticeLink> and using <emojiLink>emoji in subject lines</emojiLink>.',
|
||||
@ -117,20 +40,6 @@ export function DetailsPanel() {
|
||||
}
|
||||
);
|
||||
|
||||
const subjectLabel = (
|
||||
<>
|
||||
<span>{ __( 'Subject', 'mailpoet' ) }</span>
|
||||
<PersonalizationTagsButton
|
||||
onClick={ () => {
|
||||
setSubjectModalIsOpened( true );
|
||||
} }
|
||||
/>
|
||||
<ExternalLink href="https://kb.mailpoet.com/article/215-personalize-newsletter-with-shortcodes#list">
|
||||
{ __( 'Shortcode guide', 'mailpoet' ) }
|
||||
</ExternalLink>
|
||||
</>
|
||||
);
|
||||
|
||||
const previewTextLength = mailpoetEmailData?.preheader?.length ?? 0;
|
||||
|
||||
const preheaderHelp = createInterpolateElement(
|
||||
@ -153,140 +62,49 @@ export function DetailsPanel() {
|
||||
),
|
||||
}
|
||||
);
|
||||
const preheaderLabel = (
|
||||
<>
|
||||
<span>{ __( 'Preview text', 'mailpoet' ) }</span>
|
||||
<PersonalizationTagsButton
|
||||
onClick={ () => {
|
||||
setPreheaderModalIsOpened( true );
|
||||
} }
|
||||
/>
|
||||
<span
|
||||
className={ classnames(
|
||||
'mailpoet-settings-panel__preview-text-length',
|
||||
{
|
||||
'mailpoet-settings-panel__preview-text-length-warning':
|
||||
previewTextLength > previewTextRecommendedLength,
|
||||
'mailpoet-settings-panel__preview-text-length-error':
|
||||
previewTextLength > previewTextMaxLength,
|
||||
}
|
||||
) }
|
||||
>
|
||||
{ previewTextLength }/{ previewTextMaxLength }
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<PanelBody
|
||||
title={ __( 'Details', 'mailpoet' ) }
|
||||
className="mailpoet-email-editor__settings-panel"
|
||||
>
|
||||
<BaseControl
|
||||
id="mailpoet-settings-panel__subject"
|
||||
label={ subjectLabel }
|
||||
className="mailpoet-settings-panel__subject"
|
||||
<RichTextWithButton
|
||||
attributeName="subject"
|
||||
label={ __( 'Subject', 'mailpoet' ) }
|
||||
labelSuffix={
|
||||
<ExternalLink href="https://kb.mailpoet.com/article/215-personalize-newsletter-with-shortcodes#list">
|
||||
{ __( 'Shortcode guide', 'mailpoet' ) }
|
||||
</ExternalLink>
|
||||
}
|
||||
help={ subjectHelp }
|
||||
>
|
||||
<PersonalizationTagsModal
|
||||
isOpened={ subjectModalIsOpened }
|
||||
onInsert={ ( value ) => {
|
||||
handleInsertPersonalizationTag( value, 'subject' );
|
||||
setSubjectModalIsOpened( false );
|
||||
} }
|
||||
closeCallback={ () => setSubjectModalIsOpened( false ) }
|
||||
/>
|
||||
<RichText
|
||||
ref={ subjectRef }
|
||||
className="mailpoet-settings-panel__richtext"
|
||||
placeholder={ __(
|
||||
'Eg. The summer sale is here!',
|
||||
'mailpoet'
|
||||
) }
|
||||
onFocus={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
subjectRef,
|
||||
mailpoetEmailData?.subject ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onKeyUp={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
subjectRef,
|
||||
mailpoetEmailData?.subject ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onClick={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
subjectRef,
|
||||
mailpoetEmailData?.subject ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onChange={ ( value ) =>
|
||||
updateEmailMailPoetProperty( 'subject', value )
|
||||
}
|
||||
value={ mailpoetEmailData?.subject ?? '' }
|
||||
data-automation-id="email_subject"
|
||||
/>
|
||||
</BaseControl>
|
||||
placeholder={ __( 'Eg. The summer sale is here!', 'mailpoet' ) }
|
||||
/>
|
||||
|
||||
<BaseControl
|
||||
id="mailpoet-settings-panel__richtext"
|
||||
label={ preheaderLabel }
|
||||
className="mailpoet-settings-panel__preview-text"
|
||||
<RichTextWithButton
|
||||
attributeName="preheader"
|
||||
label={ __( 'Preview text', 'mailpoet' ) }
|
||||
labelSuffix={
|
||||
<span
|
||||
className={ classnames(
|
||||
'mailpoet-settings-panel__preview-text-length',
|
||||
{
|
||||
'mailpoet-settings-panel__preview-text-length-warning':
|
||||
previewTextLength >
|
||||
previewTextRecommendedLength,
|
||||
'mailpoet-settings-panel__preview-text-length-error':
|
||||
previewTextLength > previewTextMaxLength,
|
||||
}
|
||||
) }
|
||||
>
|
||||
{ previewTextLength }/{ previewTextMaxLength }
|
||||
</span>
|
||||
}
|
||||
help={ preheaderHelp }
|
||||
>
|
||||
<PersonalizationTagsModal
|
||||
isOpened={ preheaderModalIsOpened }
|
||||
onInsert={ ( value ) => {
|
||||
handleInsertPersonalizationTag( value, 'preheader' );
|
||||
setPreheaderModalIsOpened( false );
|
||||
} }
|
||||
closeCallback={ () => setPreheaderModalIsOpened( false ) }
|
||||
/>
|
||||
<RichText
|
||||
ref={ preheaderRef }
|
||||
className="mailpoet-settings-panel__richtext"
|
||||
placeholder={ __(
|
||||
"Add a preview text to capture subscribers' attention and increase open rates.",
|
||||
'mailpoet'
|
||||
) }
|
||||
onFocus={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
preheaderRef,
|
||||
mailpoetEmailData?.preheader ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onKeyUp={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
preheaderRef,
|
||||
mailpoetEmailData?.preheader ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onClick={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition(
|
||||
preheaderRef,
|
||||
mailpoetEmailData?.preheader ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onChange={ ( value ) =>
|
||||
updateEmailMailPoetProperty( 'preheader', value )
|
||||
}
|
||||
value={ mailpoetEmailData?.preheader ?? '' }
|
||||
data-automation-id="email_preview_text"
|
||||
/>
|
||||
</BaseControl>
|
||||
placeholder={ __(
|
||||
"Add a preview text to capture subscribers' attention and increase open rates.",
|
||||
'mailpoet'
|
||||
) }
|
||||
/>
|
||||
</PanelBody>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user