PluginProbe ʕ •ᴥ•ʔ
GenerateBlocks / 2.0.2
GenerateBlocks v2.0.2
trunk 1.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.7.0 1.7.1 1.7.2 1.7.3 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 1.9.1 2.0.0 2.0.1 2.0.2 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.3.0
generateblocks / src / blocks / text / edit.js
generateblocks / src / blocks / text Last commit date
components 1 year ago block.json 1 year ago edit.js 1 year ago editor.scss 1 year ago index.js 1 year ago save.js 1 year ago transforms.js 1 year ago
edit.js
271 lines
1 import { __ } from '@wordpress/i18n';
2 import { RichText, useBlockProps, InspectorControls } from '@wordpress/block-editor';
3 import { Platform, useEffect, useMemo } from '@wordpress/element';
4 import { compose } from '@wordpress/compose';
5
6 import { BlockStyles, withUniqueId } from '@edge22/block-styles';
7
8 import { withDynamicTag } from '../../hoc/withDynamicTag';
9 import { Icon } from './components/Icon.jsx';
10 import RootElement from '../../components/root-element/index.js';
11 import { BlockSettings } from './components/BlockSettings';
12 import { selectorShortcuts } from '@utils/selectorShortcuts';
13 import { withStyles } from '@hoc/withStyles';
14 import { BlockStylesBuilder } from '@components/block-styles-builder/BlockStylesBuilder';
15 import { AlignmentToolbar, LinkBlockToolbar, StylesOnboarder, TagNameToolbar } from '@components/index';
16 import { withHtmlAttributes } from '@hoc/withHtmlAttributes';
17 import { getBlockClasses } from '@utils/getBlockClasses';
18 import { DynamicTagBlockToolbar } from '../../dynamic-tags';
19
20 function EditBlock( props ) {
21 const {
22 attributes,
23 setAttributes,
24 mergeBlocks,
25 onReplace,
26 dynamicTagValue,
27 setContentMode,
28 contentMode,
29 name,
30 clientId,
31 onStyleChange,
32 getStyleValue,
33 editorHtmlAttributes,
34 isSelected,
35 styles,
36 context,
37 } = props;
38
39 const {
40 tagName,
41 content,
42 icon,
43 iconLocation,
44 iconOnly,
45 htmlAttributes,
46 } = attributes;
47
48 useEffect( () => {
49 if ( ! tagName ) {
50 setAttributes( { tagName: 'p' } );
51 }
52 }, [ tagName ] );
53
54 const contentValue = useMemo( () => {
55 if ( ! dynamicTagValue ) {
56 return content;
57 }
58
59 return dynamicTagValue.reduce( ( acc, { original, replacement, fallback } ) => {
60 if ( ! replacement ) {
61 return acc.replaceAll( original, fallback );
62 }
63
64 const replacementWithNoLinks = replacement.replace( /href="[^"]*"/g, 'href="#"' );
65
66 return acc.replaceAll( original, replacementWithNoLinks );
67 }, content );
68 }, [ dynamicTagValue, content ] );
69
70 const classNames = getBlockClasses(
71 'gb-text',
72 {
73 ...attributes,
74 styles,
75 },
76 ! icon
77 );
78
79 const blockProps = useBlockProps(
80 {
81 className: classNames.join( ' ' ).trim(),
82 ...editorHtmlAttributes,
83 }
84 );
85
86 const TagNameWithIcon = tagName || 'p';
87 const richTextProps = {
88 identifier: 'content',
89 value: contentValue,
90 onChange: ( value ) => setAttributes( { content: value } ),
91 onMerge: mergeBlocks,
92 onReplace,
93 onRemove: () => onReplace( [] ),
94 placeholder: __( 'Text', 'generateblocks' ),
95 withoutInteractiveFormatting: 'a' === tagName || 'button' === tagName,
96 ...( Platform.isNative && { deleteEnter: true } ), // setup RichText on native mobile to delete the "Enter" key as it's handled by the JS/RN side
97 };
98 const shortcuts = useMemo( () => {
99 const visibleSelectors = [
100 {
101 label: __( 'Main', 'generateblocks' ),
102 value: '',
103 },
104 ];
105
106 if ( 'a' === tagName || 'button' === tagName ) {
107 visibleSelectors.push(
108 {
109 label: __( 'Hover', 'generateblocks' ),
110 value: '&:is(:hover, :focus)',
111 }
112 );
113
114 delete selectorShortcuts.links;
115 }
116
117 if ( icon ) {
118 visibleSelectors.push(
119 {
120 label: __( 'Icon', 'generateblocks' ),
121 value: '.gb-shape svg',
122 },
123 );
124
125 selectorShortcuts.default.items.push(
126 { label: __( 'Icon', 'generateblocks' ), value: '.gb-shape svg' },
127 { label: __( 'Hovered icon', 'generateblocks' ), value: '&:is(:hover, :focus) .gb-shape svg' },
128 );
129
130 selectorShortcuts.icons = {
131 label: __( 'Icon', 'generateblocks' ),
132 items: [
133 { label: __( 'Icon', 'generateblocks' ), value: '.gb-shape svg' },
134 { label: __( 'Hovered icon', 'generateblocks' ), value: '&:is(:hover, :focus) .gb-shape svg' },
135 ],
136 };
137 }
138
139 return {
140 selectorShortcuts,
141 visibleShortcuts: visibleSelectors,
142 };
143 }, [ tagName, icon ] );
144
145 const renderContent = ( elementTagName, withBlockProps = false ) => {
146 if ( 'preview' === contentMode && dynamicTagValue ) {
147 const ElementTagName = elementTagName;
148
149 // Render a plain HTML tag in preview mode
150 return (
151 <ElementTagName
152 { ...( withBlockProps && blockProps ) }
153 dangerouslySetInnerHTML={ { __html: contentValue } }
154 />
155 );
156 }
157
158 // The RichText component can't handle the `<button>` tag for some reason.
159 // It doesn't allow spaces to be entered, and doesn't allow the user to type if there
160 // isn't already a value entered. To handle this, we'll render a `<button>` tag instead
161 // and use a `span` as the RichText component tag.
162 if ( 'button' === elementTagName ) {
163 return (
164 <button
165 { ...( withBlockProps && blockProps ) }
166 >
167 <RichText
168 { ...richTextProps }
169 tagName={ 'span' }
170 />
171 </button>
172 );
173 }
174
175 return (
176 <RichText
177 { ...richTextProps }
178 { ...( withBlockProps && blockProps ) }
179 tagName={ elementTagName }
180 />
181 );
182 };
183
184 return (
185 <>
186 <TagNameToolbar
187 label={ __( 'Choose tag name', 'generateblocks' ) }
188 tagName={ tagName }
189 onChange={ ( value ) => setAttributes( { tagName: value } ) }
190 />
191
192 <LinkBlockToolbar
193 setAttributes={ setAttributes }
194 htmlAttributes={ htmlAttributes }
195 tagName={ tagName }
196 context={ context }
197 />
198
199 <AlignmentToolbar
200 withTextAlign
201 getStyleValue={ getStyleValue }
202 onStyleChange={ onStyleChange }
203 setAttributes={ setAttributes }
204 clientId={ clientId }
205 />
206
207 { ! iconOnly && (
208 <DynamicTagBlockToolbar
209 value={ content }
210 tagName={ tagName }
211 setContentMode={ setContentMode }
212 contentMode={ contentMode }
213 isSelected={ isSelected }
214 onChange={ ( newValue ) => setAttributes( { content: newValue } ) }
215 context={ context }
216 />
217 ) }
218
219 <InspectorControls>
220 <StylesOnboarder />
221 <BlockStyles
222 settingsTab={ (
223 <BlockSettings
224 { ...props }
225 />
226 ) }
227 stylesTab={ (
228 <BlockStylesBuilder
229 attributes={ attributes }
230 setAttributes={ setAttributes }
231 shortcuts={ shortcuts }
232 onStyleChange={ onStyleChange }
233 name={ name }
234 />
235 ) }
236 />
237 </InspectorControls>
238
239 <RootElement
240 name={ name }
241 clientId={ clientId }
242 >
243 <>
244 { !! icon && (
245 <TagNameWithIcon { ...blockProps }>
246 { 'before' === iconLocation && ( <Icon icon={ icon } /> ) }
247 { ! iconOnly && (
248 renderContent( 'span' )
249 ) }
250 { 'after' === iconLocation && ( <Icon icon={ icon } /> ) }
251 </TagNameWithIcon>
252 ) }
253
254 { ! icon && (
255 renderContent( tagName || 'span', true )
256 ) }
257 </>
258 </RootElement>
259 </>
260 );
261 }
262
263 const Edit = compose(
264 withHtmlAttributes,
265 withStyles,
266 withDynamicTag,
267 withUniqueId
268 )( EditBlock );
269
270 export { Edit };
271