From 9288b749c7e58333dae4c6b1f77ab989c4d679e1 Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Fri, 17 Feb 2023 15:10:08 +0200 Subject: [PATCH 001/152] feat: add checkbox and radio field --- blocks.json | 5 +- inc/class-registration.php | 20 +- inc/render/class-form-multiple-choice.php | 67 +++++++ src/blocks/blocks/form/index.js | 1 + .../blocks/form/multiple-choice/block.json | 50 +++++ .../blocks/form/multiple-choice/edit.js | 175 ++++++++++++++++++ .../blocks/form/multiple-choice/index.js | 77 ++++++++ .../blocks/form/multiple-choice/inspector.js | 132 +++++++++++++ .../blocks/form/multiple-choice/save.js | 25 +++ .../blocks/form/multiple-choice/types.d.ts | 21 +++ src/blocks/blocks/form/style.scss | 10 +- 11 files changed, 571 insertions(+), 12 deletions(-) create mode 100644 inc/render/class-form-multiple-choice.php create mode 100644 src/blocks/blocks/form/multiple-choice/block.json create mode 100644 src/blocks/blocks/form/multiple-choice/edit.js create mode 100644 src/blocks/blocks/form/multiple-choice/index.js create mode 100644 src/blocks/blocks/form/multiple-choice/inspector.js create mode 100644 src/blocks/blocks/form/multiple-choice/save.js create mode 100644 src/blocks/blocks/form/multiple-choice/types.d.ts diff --git a/blocks.json b/blocks.json index c9fa30204..4eb9f11e6 100644 --- a/blocks.json +++ b/blocks.json @@ -93,6 +93,9 @@ "form-textarea": { "block": "blocks/blocks/form/textarea/block.json" }, + "form-multiple-choice": { + "block": "blocks/blocks/form/multiple-choice/block.json" + }, "google-map": { "block": "blocks/blocks/google-map/block.json", "assets": { @@ -267,4 +270,4 @@ "isPro": true, "block": "pro/woocommerce/upsells/block.json" } -} \ No newline at end of file +} diff --git a/inc/class-registration.php b/inc/class-registration.php index d96e8fd6a..f1cf8552e 100644 --- a/inc/class-registration.php +++ b/inc/class-registration.php @@ -685,15 +685,16 @@ public function enqueue_block_styles( $post ) { */ public function register_blocks() { $dynamic_blocks = array( - 'about-author' => '\ThemeIsle\GutenbergBlocks\Render\About_Author_Block', - 'form-nonce' => '\ThemeIsle\GutenbergBlocks\Render\Form_Nonce_Block', - 'google-map' => '\ThemeIsle\GutenbergBlocks\Render\Google_Map_Block', - 'leaflet-map' => '\ThemeIsle\GutenbergBlocks\Render\Leaflet_Map_Block', - 'plugin-cards' => '\ThemeIsle\GutenbergBlocks\Render\Plugin_Card_Block', - 'posts-grid' => '\ThemeIsle\GutenbergBlocks\Render\Posts_Grid_Block', - 'review' => '\ThemeIsle\GutenbergBlocks\Render\Review_Block', - 'sharing-icons' => '\ThemeIsle\GutenbergBlocks\Render\Sharing_Icons_Block', - 'stripe-checkout' => '\ThemeIsle\GutenbergBlocks\Render\Stripe_Checkout_Block', + 'about-author' => '\ThemeIsle\GutenbergBlocks\Render\About_Author_Block', + 'form-nonce' => '\ThemeIsle\GutenbergBlocks\Render\Form_Nonce_Block', + 'google-map' => '\ThemeIsle\GutenbergBlocks\Render\Google_Map_Block', + 'leaflet-map' => '\ThemeIsle\GutenbergBlocks\Render\Leaflet_Map_Block', + 'plugin-cards' => '\ThemeIsle\GutenbergBlocks\Render\Plugin_Card_Block', + 'posts-grid' => '\ThemeIsle\GutenbergBlocks\Render\Posts_Grid_Block', + 'review' => '\ThemeIsle\GutenbergBlocks\Render\Review_Block', + 'sharing-icons' => '\ThemeIsle\GutenbergBlocks\Render\Sharing_Icons_Block', + 'stripe-checkout' => '\ThemeIsle\GutenbergBlocks\Render\Stripe_Checkout_Block', + 'form-multiple-choice' => '\ThemeIsle\GutenbergBlocks\Render\Form_Multiple_Choice_Block', ); $dynamic_blocks = apply_filters( 'otter_blocks_register_dynamic_blocks', $dynamic_blocks ); @@ -715,6 +716,7 @@ public function register_blocks() { 'form-input', 'form-nonce', 'form-textarea', + 'form-multiple-choice', 'google-map', 'icon-list', 'icon-list-item', diff --git a/inc/render/class-form-multiple-choice.php b/inc/render/class-form-multiple-choice.php new file mode 100644 index 000000000..b1208f15d --- /dev/null +++ b/inc/render/class-form-multiple-choice.php @@ -0,0 +1,67 @@ +'; + $output .= ''; + + $options_array = explode( "\n", $options ); + + foreach ( $options_array as $field_label ) { + $field_value = implode( '_', explode( ' ', sanitize_title( $field_label ) ) ); + $field_id = 'field-' . $field_value; + + $output .= $this->render_field( $field_type, $field_label, $field_value, $id, $field_id ); + } + + $output .= ''; + return $output; + } + + /** + * Render an input field. + * + * @param string $type The type of the field (checkbox, radio). + * @param string $label The label of the field. + * @param string $value The value of the field. + * @param string $name The name of the field. + * @param string $id The id of the field. + * @return string + */ + public function render_field( $type, $label, $value, $name, $id ) { + $output = '
'; + + $output .= ''; + $output .= ''; + + $output .= '
'; + + return $output; + } +} diff --git a/src/blocks/blocks/form/index.js b/src/blocks/blocks/form/index.js index 130d8d9b5..4aacd7d23 100644 --- a/src/blocks/blocks/form/index.js +++ b/src/blocks/blocks/form/index.js @@ -16,6 +16,7 @@ import save from './save.js'; import './input/index.js'; import './nonce/index.js'; import './textarea/index.js'; +import './multiple-choice/index.js'; const { name } = metadata; diff --git a/src/blocks/blocks/form/multiple-choice/block.json b/src/blocks/blocks/form/multiple-choice/block.json new file mode 100644 index 000000000..e8c6b1705 --- /dev/null +++ b/src/blocks/blocks/form/multiple-choice/block.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "themeisle-blocks/form-multiple-choice", + "title": "Form Multiple Choice Input", + "category": "themeisle-blocks", + "description": "Display a checkbox, select, radio.", + "keywords": [ "form", "checkbox", "select", "radio", "multiple" ], + "textdomain": "otter-blocks", + "ancestor": [ "themeisle-blocks/form" ], + "attributes": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "default": "checkbox" + }, + "label": { + "type": "string" + }, + "placeholder": { + "type": "string" + }, + "isRequired": { + "type": "boolean" + }, + "mappedName": { + "type": "string" + }, + "labelColor": { + "type": "string" + }, + "inputWidth": { + "type": "object" + }, + "helpText": { + "type": "string" + }, + "options": { + "type": "string" + }, + "multipleSelection": { + "type": "boolean" + } + }, + "supports": { + "align": [ "wide", "full" ] + } +} diff --git a/src/blocks/blocks/form/multiple-choice/edit.js b/src/blocks/blocks/form/multiple-choice/edit.js new file mode 100644 index 000000000..763c582c2 --- /dev/null +++ b/src/blocks/blocks/form/multiple-choice/edit.js @@ -0,0 +1,175 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +import { + RichText, + useBlockProps +} from '@wordpress/block-editor'; + +import { + Fragment, + useEffect, + useRef +} from '@wordpress/element'; + +import { useDispatch, useSelect } from '@wordpress/data'; + +import { omit } from 'lodash'; + +import { createBlock } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import { blockInit } from '../../../helpers/block-utility.js'; +import Inspector from './inspector.js'; +import { _cssBlock } from '../../../helpers/helper-functions'; + + +const { attributes: defaultAttributes } = metadata; + +const Field = ({ fieldType, label, setAttributes, position, attributes }) => { + + const id = `${attributes.id ?? ''}-field-${position}`; + const value = label?.toLowerCase().replace( / /g, '_' ); + + const onChangeLabel = label => { + const options = attributes.options?.split( '\n' ) ?? []; + if ( options.length < position ) { + return; + } + + options[ position ] = label; + setAttributes({ options: options.join( '\n' ) }); + }; + + return ( +
+ + +
+ ); +}; + +/** + * Form Input component + * @param {import('./types').FormMultipleChoiceInputProps} props + * @returns + */ +const Edit = ({ + attributes, + setAttributes, + clientId +}) => { + useEffect( () => { + const unsubscribe = blockInit( clientId, defaultAttributes ); + return () => unsubscribe( attributes.id ); + }, [ attributes.id ]); + + const blockProps = useBlockProps(); + + const { + parentClientId + } = useSelect( select => { + const { + getBlock, + getBlockRootClientId + } = select( 'core/block-editor' ); + + if ( ! clientId ) { + return { + parentClientId: '' + }; + } + + const parentClientId = getBlockRootClientId( clientId ); + + return { + parentClientId: parentClientId ?? '' + }; + }, [ clientId ]); + + const { selectBlock, replaceBlock } = useDispatch( 'core/block-editor' ); + + const switchToTextarea = () => { + const block = createBlock( 'themeisle-blocks/form-textarea', { ...omit( attributes, [ 'type', 'multipleSelection', 'options' ]) }); + replaceBlock( clientId, block ); + }; + + const switchToInput = ( inputType ) => { + const block = createBlock( 'themeisle-blocks/form-input', { ...omit( attributes, [ 'type', 'multipleSelection', 'options' ]), type: inputType }); + replaceBlock( clientId, block ); + }; + + + return ( + + selectBlock( parentClientId ) } + switchToTextarea={ switchToTextarea } + switchToInput={ switchToInput } + /> + +
+ + + { + ( attributes?.options ?? '' )?.split( '\n' )?.map( ( label, index ) => { + return ; + }) + } + { + attributes.helpText && ( + + { attributes.helpText } + + ) + } +
+
+ ); +}; + +export default Edit; diff --git a/src/blocks/blocks/form/multiple-choice/index.js b/src/blocks/blocks/form/multiple-choice/index.js new file mode 100644 index 000000000..fc93e50f7 --- /dev/null +++ b/src/blocks/blocks/form/multiple-choice/index.js @@ -0,0 +1,77 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +import { registerBlockType } from '@wordpress/blocks'; + +import { createBlock } from '@wordpress/blocks'; + +import { omit } from 'lodash'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; +import { formFieldIcon as icon } from '../../../helpers/icons.js'; +import edit from './edit.js'; +import save from './save.js'; + +const { name } = metadata; + +if ( ! window.themeisleGutenberg.isAncestorTypeAvailable ) { + metadata.parent = [ 'themeisle-blocks/form' ]; +} + +registerBlockType( name, { + ...metadata, + title: __( 'Multiple Choice Field', 'otter-blocks' ), + description: __( 'Display a checkbox or radio list.', 'otter-blocks' ), + icon, + variations: [ + { + name: 'themeisle-blocks/form-input-checkbox', + description: __( 'Insert a checkbox list field', 'otter-blocks' ), + icon: 'email', + title: __( 'Checkbox Field', 'otter-blocks' ), + attributes: { + type: 'checkbox' + } + }, + { + name: 'themeisle-blocks/form-input-radio', + description: __( 'Insert a radio list field', 'otter-blocks' ), + icon: 'calculator', + title: __( 'Radio Field', 'otter-blocks' ), + attributes: { + type: 'radio' + } + } + ], + edit, + save: () => null, + transforms: { + to: [ + { + type: 'block', + blocks: [ 'themeisle-blocks/form-input' ], + transform: ( attributes ) => { + const attrs = omit( attributes, [ 'type', 'multipleChoice', 'options' ]); + return createBlock( 'themeisle-blocks/form-input', { + ...attrs + }); + } + }, + { + type: 'block', + blocks: [ 'themeisle-blocks/form-textarea' ], + transform: ( attributes ) => { + const attrs = omit( attributes, [ 'type', 'multipleChoice', 'options' ]); + return createBlock( 'themeisle-blocks/form-textarea', { + ...attrs + }); + } + } + ] + } +}); diff --git a/src/blocks/blocks/form/multiple-choice/inspector.js b/src/blocks/blocks/form/multiple-choice/inspector.js new file mode 100644 index 000000000..aa3304151 --- /dev/null +++ b/src/blocks/blocks/form/multiple-choice/inspector.js @@ -0,0 +1,132 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +import { + InspectorControls, + PanelColorSettings +} from '@wordpress/block-editor'; + +import { + Button, + PanelBody, + SelectControl, + TextareaControl, + TextControl, + ToggleControl +} from '@wordpress/components'; + +/** + * + * @param {import('./types').FormMultipleChoiceInputInspectorProps} props + * @returns {JSX.Element} + */ +const Inspector = ({ + attributes, + setAttributes, + selectParent, + switchToTextarea, + switchToInput +}) => { + return ( + + + + + { + if ( 'textarea' === type ) { + switchToTextarea?.(); + return; + } else if ( 'radio' === type || 'checkbox' === type ) { + setAttributes({ type }); + } else { + switchToInput?.( type ); + } + }} + /> + + setAttributes({ label }) } + /> + + setAttributes({ options }) } + /> + + setAttributes({ helpText }) } + /> + + setAttributes({ isRequired }) } + /> + + + setAttributes({ labelColor }), + label: __( 'Label Color', 'otter-blocks' ) + } + ] } + /> + + ); +}; + +export default Inspector; diff --git a/src/blocks/blocks/form/multiple-choice/save.js b/src/blocks/blocks/form/multiple-choice/save.js new file mode 100644 index 000000000..e13c26a26 --- /dev/null +++ b/src/blocks/blocks/form/multiple-choice/save.js @@ -0,0 +1,25 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +import { + RichText, + useBlockProps +} from '@wordpress/block-editor'; + +const Save = ({ + attributes +}) => { + const blockProps = useBlockProps.save({ + id: attributes.id + }); + + return ( +
+ +
+ ); +}; + +export default Save; diff --git a/src/blocks/blocks/form/multiple-choice/types.d.ts b/src/blocks/blocks/form/multiple-choice/types.d.ts new file mode 100644 index 000000000..9abd4b5d7 --- /dev/null +++ b/src/blocks/blocks/form/multiple-choice/types.d.ts @@ -0,0 +1,21 @@ +import { + BlockProps, + InspectorProps +} from '../../../helpers/blocks'; + +type Attributes = { + id: string + type: string + label: string + placeholder: string + isRequired: boolean + mappedName: string + labelColor: string + inputWidth: number + helpText: string, + options: string, + multipleSelection: boolean, +} + +export type FormMultipleChoiceInputProps = BlockProps +export interface FormMultipleChoiceInputInspectorProps extends InspectorProps {} diff --git a/src/blocks/blocks/form/style.scss b/src/blocks/blocks/form/style.scss index 0f23ec54b..15d39978e 100644 --- a/src/blocks/blocks/form/style.scss +++ b/src/blocks/blocks/form/style.scss @@ -248,7 +248,7 @@ } - .wp-block-themeisle-blocks-form-input, .wp-block-themeisle-blocks-form-textarea { + .wp-block-themeisle-blocks-form-input, .wp-block-themeisle-blocks-form-textarea, .wp-block-themeisle-blocks-form-multiple-choice { display: flex; flex-direction: column; width: 100%; @@ -265,7 +265,7 @@ } } - .otter-form-input,.otter-form-textarea-input { + .otter-form-input, .otter-form-textarea-input { color: var(--input-color); width: var(--input-width); padding: var(--padding); @@ -352,3 +352,9 @@ } } } + +.o-form-multiple-choice-field { + display: flex; + align-items: center; + flex-direction: row; +} From ce2968f8af58ef77de9c7ec76b1029133a4440eb Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Mon, 20 Feb 2023 10:51:12 +0200 Subject: [PATCH 002/152] chore: styling --- src/blocks/blocks/form/style.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/blocks/blocks/form/style.scss b/src/blocks/blocks/form/style.scss index 15d39978e..dab44f730 100644 --- a/src/blocks/blocks/form/style.scss +++ b/src/blocks/blocks/form/style.scss @@ -357,4 +357,5 @@ display: flex; align-items: center; flex-direction: row; + gap: 5px; } From ba288b5820d508891bd94bcc9210849c9fec2b4e Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Mon, 20 Feb 2023 12:23:47 +0200 Subject: [PATCH 003/152] chore: collect input value from radio & checkbox --- inc/render/class-form-multiple-choice.php | 2 +- src/blocks/frontend/form/index.js | 32 ++++++++++++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/inc/render/class-form-multiple-choice.php b/inc/render/class-form-multiple-choice.php index b1208f15d..3ed2bb326 100644 --- a/inc/render/class-form-multiple-choice.php +++ b/inc/render/class-form-multiple-choice.php @@ -58,7 +58,7 @@ public function render_field( $type, $label, $value, $name, $id ) { $output = '
'; $output .= ''; - $output .= ''; + $output .= ''; $output .= '
'; diff --git a/src/blocks/frontend/form/index.js b/src/blocks/frontend/form/index.js index d46fbb265..de64ffcf3 100644 --- a/src/blocks/frontend/form/index.js +++ b/src/blocks/frontend/form/index.js @@ -17,15 +17,12 @@ const extractFormFields = form => { const elemsWithError = []; const formFieldsData = [{ label: window?.themeisleGutenbergForm?.messages['form-submission'] || 'Form submission from', value: window.location.href }]; - const inputs = form?.querySelectorAll( '.otter-form__container .wp-block-themeisle-blocks-form-input' ); - const textarea = form?.querySelectorAll( '.otter-form__container .wp-block-themeisle-blocks-form-textarea' ); + const inputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-input' ); + const textarea = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-textarea' ); [ ...inputs, ...textarea ]?.forEach( input => { - const label = input.querySelector( '.otter-form-input-label__label, .otter-form-textarea-label__label' )?.innerHTML; - const valueElem = input.querySelector( '.otter-form-input, .otter-form-textarea-input' ); - - // TODO: use checkbox in the future versions - const checked = input.querySelector( '.otter-form-input[type="checkbox"]' )?.checked; + const label = input.querySelector( ':scope > .otter-form-input-label__label, :scope > .otter-form-textarea-label__label' )?.innerHTML; + const valueElem = input.querySelector( ':scope > .otter-form-input, :scope > .otter-form-textarea-input' ); if ( valueElem?.hasAttribute( 'required' ) && ! valueElem?.checkValidity() ) { elemsWithError.push( valueElem ); @@ -35,12 +32,27 @@ const extractFormFields = form => { formFieldsData.push({ label: label, value: valueElem?.value, - type: valueElem?.type, - checked: checked + type: valueElem?.type }); } }); + const multipleChoiceInputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-multiple-choice' ); + + multipleChoiceInputs?.forEach( input => { + + const label = input.querySelector( ':scope > .otter-form-input-label' )?.innerHTML; + + const labels = input.querySelectorAll( '.o-form-multiple-choice-field > label' ); + const valuesElem = input.querySelectorAll( '.o-form-multiple-choice-field > input' ); + + formFieldsData.push({ + label: label, + value: [ ...labels ].filter( ( label, index ) => valuesElem[index]?.checked ).map( label => label.innerHTML ).join( ', ' ), + type: 'multiple-choice' + }); + }); + return { formFieldsData, elemsWithError }; }; @@ -280,7 +292,7 @@ domReady( () => { const sendBtn = form.querySelector( 'button' ); const displayMsg = new DisplayFormMessage( form ); - if ( form.querySelector( 'button[type="submit"]' ) ) { + if ( form.querySelector( ':scope > form > button[type="submit"]' ) ) { form?.addEventListener( 'submit', ( event ) => { event.preventDefault(); if ( ! sendBtn.disabled ) { From 18f47ffbd1fefe35a8b2f136d12cf2c417fe6080 Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Mon, 20 Feb 2023 14:46:28 +0200 Subject: [PATCH 004/152] chore: add select --- inc/render/class-form-multiple-choice.php | 49 ++++++++++++++----- .../blocks/form/multiple-choice/edit.js | 19 ++++++- .../blocks/form/multiple-choice/inspector.js | 6 ++- src/blocks/frontend/form/index.js | 14 ++++-- 4 files changed, 71 insertions(+), 17 deletions(-) diff --git a/inc/render/class-form-multiple-choice.php b/inc/render/class-form-multiple-choice.php index 3ed2bb326..6c1613127 100644 --- a/inc/render/class-form-multiple-choice.php +++ b/inc/render/class-form-multiple-choice.php @@ -23,21 +23,25 @@ class Form_Multiple_Choice_Block { */ public function render( $attributes ) { - $class_names = 'wp-block-themeisle-blocks-form-multiple-choice ' . ( isset( $attributes['className'] ) ? $attributes['className'] : '' ); - $id = isset( $attributes['id'] ) ? $attributes['id'] : ''; - $options = isset( $attributes['options'] ) ? $attributes['options'] : ''; - $field_type = isset( $attributes['type'] ) ? $attributes['type'] : 'checkbox'; - - $output = '
'; - $output .= ''; - + $class_names = 'wp-block-themeisle-blocks-form-multiple-choice ' . ( isset( $attributes['className'] ) ? $attributes['className'] : '' ); + $id = isset( $attributes['id'] ) ? $attributes['id'] : ''; + $options = isset( $attributes['options'] ) ? $attributes['options'] : ''; + $field_type = isset( $attributes['type'] ) ? $attributes['type'] : 'checkbox'; + $label = isset( $attributes['label'] ) ? $attributes['label'] : __( 'Select option', 'otter-blocks' ); $options_array = explode( "\n", $options ); - foreach ( $options_array as $field_label ) { - $field_value = implode( '_', explode( ' ', sanitize_title( $field_label ) ) ); - $field_id = 'field-' . $field_value; + $output = '
'; + + if ( 'select' === $field_type ) { + $output .= $this->render_select_field( $label, $options_array, $id ); + } else { + $output .= ''; + foreach ( $options_array as $field_label ) { + $field_value = implode( '_', explode( ' ', sanitize_title( $field_label ) ) ); + $field_id = 'field-' . $field_value; - $output .= $this->render_field( $field_type, $field_label, $field_value, $id, $field_id ); + $output .= $this->render_field( $field_type, $field_label, $field_value, $id, $field_id ); + } } $output .= '
'; @@ -64,4 +68,25 @@ public function render_field( $type, $label, $value, $name, $id ) { return $output; } + + /** + * Render a select field. + * + * @param string $label The label of the field. + * @param array $options_array The options of the field. + * @param string $id The id of the field. + * @return string + */ + public function render_select_field( $label, $options_array, $id ) { + $output = ''; + $output .= ''; + return $output; + } } diff --git a/src/blocks/blocks/form/multiple-choice/edit.js b/src/blocks/blocks/form/multiple-choice/edit.js index 763c582c2..48d78d3c4 100644 --- a/src/blocks/blocks/form/multiple-choice/edit.js +++ b/src/blocks/blocks/form/multiple-choice/edit.js @@ -62,6 +62,23 @@ const Field = ({ fieldType, label, setAttributes, position, attributes }) => { ); }; +const SelectField = ({ attributes }) => { + return ( + + ); +}; + /** * Form Input component * @param {import('./types').FormMultipleChoiceInputProps} props @@ -147,7 +164,7 @@ const Edit = ({ ) } { - ( attributes?.options ?? '' )?.split( '\n' )?.map( ( label, index ) => { + 'select' === attributes?.type ? : ( attributes?.options ?? '' )?.split( '\n' )?.map( ( label, index ) => { return { if ( 'textarea' === type ) { switchToTextarea?.(); return; - } else if ( 'radio' === type || 'checkbox' === type ) { + } else if ( 'radio' === type || 'checkbox' === type || 'select' === type ) { setAttributes({ type }); } else { switchToInput?.( type ); diff --git a/src/blocks/frontend/form/index.js b/src/blocks/frontend/form/index.js index de64ffcf3..56a245864 100644 --- a/src/blocks/frontend/form/index.js +++ b/src/blocks/frontend/form/index.js @@ -43,12 +43,20 @@ const extractFormFields = form => { const label = input.querySelector( ':scope > .otter-form-input-label' )?.innerHTML; - const labels = input.querySelectorAll( '.o-form-multiple-choice-field > label' ); - const valuesElem = input.querySelectorAll( '.o-form-multiple-choice-field > input' ); + const select = input.querySelector( ':scope > select' ); + let value = undefined; + + if ( select ) { + value = [ ...select.selectedOptions ].map( o => o?.label )?.filter( l => l ).join( ', ' ); + } else { + const labels = input.querySelectorAll( '.o-form-multiple-choice-field > label' ); + const valuesElem = input.querySelectorAll( '.o-form-multiple-choice-field > input' ); + value = [ ...labels ].filter( ( label, index ) => valuesElem[index]?.checked ).map( label => label.innerHTML ).join( ', ' ); + } formFieldsData.push({ label: label, - value: [ ...labels ].filter( ( label, index ) => valuesElem[index]?.checked ).map( label => label.innerHTML ).join( ', ' ), + value: value, type: 'multiple-choice' }); }); From 0843e50daa7d678df3451092f30558c2954b05d6 Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Mon, 20 Feb 2023 16:23:01 +0200 Subject: [PATCH 005/152] chore: extra attributes --- inc/render/class-form-multiple-choice.php | 39 +++++++++++++------ .../blocks/form/multiple-choice/inspector.js | 28 +++++++++++++ src/blocks/blocks/form/style.scss | 14 +++++++ 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/inc/render/class-form-multiple-choice.php b/inc/render/class-form-multiple-choice.php index 6c1613127..9ee075c6f 100644 --- a/inc/render/class-form-multiple-choice.php +++ b/inc/render/class-form-multiple-choice.php @@ -23,19 +23,22 @@ class Form_Multiple_Choice_Block { */ public function render( $attributes ) { - $class_names = 'wp-block-themeisle-blocks-form-multiple-choice ' . ( isset( $attributes['className'] ) ? $attributes['className'] : '' ); - $id = isset( $attributes['id'] ) ? $attributes['id'] : ''; - $options = isset( $attributes['options'] ) ? $attributes['options'] : ''; - $field_type = isset( $attributes['type'] ) ? $attributes['type'] : 'checkbox'; - $label = isset( $attributes['label'] ) ? $attributes['label'] : __( 'Select option', 'otter-blocks' ); - $options_array = explode( "\n", $options ); + $class_names = 'wp-block-themeisle-blocks-form-multiple-choice ' . ( isset( $attributes['className'] ) ? $attributes['className'] : '' ); + $id = isset( $attributes['id'] ) ? $attributes['id'] : ''; + $options = isset( $attributes['options'] ) ? $attributes['options'] : ''; + $field_type = isset( $attributes['type'] ) ? $attributes['type'] : 'checkbox'; + $label = isset( $attributes['label'] ) ? $attributes['label'] : __( 'Select option', 'otter-blocks' ); + $help_text = isset( $attributes['helpText'] ) ? $attributes['helpText'] : ''; + $options_array = explode( "\n", $options ); + $is_required = isset( $attributes['isRequired'] ) ? boolval( $attributes['isRequired'] ) : false; + $has_multiple_selection = isset( $attributes['multipleSelection'] ) ? boolval( $attributes['multipleSelection'] ) : false; $output = '
'; if ( 'select' === $field_type ) { - $output .= $this->render_select_field( $label, $options_array, $id ); + $output .= $this->render_select_field( $label, $options_array, $id, $has_multiple_selection, $is_required ); } else { - $output .= ''; + $output .= ''; foreach ( $options_array as $field_label ) { $field_value = implode( '_', explode( ' ', sanitize_title( $field_label ) ) ); $field_id = 'field-' . $field_value; @@ -44,6 +47,8 @@ public function render( $attributes ) { } } + $output .= '' . $help_text . ''; + $output .= '
'; return $output; } @@ -75,11 +80,13 @@ public function render_field( $type, $label, $value, $name, $id ) { * @param string $label The label of the field. * @param array $options_array The options of the field. * @param string $id The id of the field. + * @param bool $is_multiple The multiple status of the field. + * @param bool $is_required The required status of the field. * @return string */ - public function render_select_field( $label, $options_array, $id ) { - $output = ''; - $output .= ''; foreach ( $options_array as $field_label ) { $field_value = implode( '_', explode( ' ', sanitize_title( $field_label ) ) ); @@ -89,4 +96,14 @@ public function render_select_field( $label, $options_array, $id ) { $output .= ''; return $output; } + + /** + * Render the required sign. + * + * @param bool $is_required The required status of the field. + * @return string + */ + public function render_required_sign( $is_required ) { + return $is_required ? '*' : ''; + } } diff --git a/src/blocks/blocks/form/multiple-choice/inspector.js b/src/blocks/blocks/form/multiple-choice/inspector.js index dd1a0ae02..249380d44 100644 --- a/src/blocks/blocks/form/multiple-choice/inspector.js +++ b/src/blocks/blocks/form/multiple-choice/inspector.js @@ -17,6 +17,15 @@ import { ToggleControl } from '@wordpress/components'; +import { getActiveStyle, changeActiveStyle } from '../../../helpers/helper-functions.js'; + +const styles = [ + { + label: __( 'Inline List', 'otter-blocks' ), + value: 'inline-list' + } +]; + /** * * @param {import('./types').FormMultipleChoiceInputInspectorProps} props @@ -110,6 +119,25 @@ const Inspector = ({ onChange={ helpText => setAttributes({ helpText }) } /> + { + const classes = changeActiveStyle( attributes.className, styles, value ? 'inline-list' : undefined ); + setAttributes({ className: classes }); + } } + /> + + { + 'select' === attributes?.type && ( + setAttributes({ multipleSelection }) } + /> + ) + } + Date: Tue, 21 Feb 2023 11:39:47 +0200 Subject: [PATCH 006/152] chore: input validation for new inputs fields --- inc/render/class-form-multiple-choice.php | 7 ++- src/blocks/frontend/form/index.js | 73 ++++++++++++++++++----- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/inc/render/class-form-multiple-choice.php b/inc/render/class-form-multiple-choice.php index 9ee075c6f..0acd59db2 100644 --- a/inc/render/class-form-multiple-choice.php +++ b/inc/render/class-form-multiple-choice.php @@ -43,7 +43,7 @@ public function render( $attributes ) { $field_value = implode( '_', explode( ' ', sanitize_title( $field_label ) ) ); $field_id = 'field-' . $field_value; - $output .= $this->render_field( $field_type, $field_label, $field_value, $id, $field_id ); + $output .= $this->render_field( $field_type, $field_label, $field_value, $id, $field_id, $is_required ); } } @@ -61,12 +61,13 @@ public function render( $attributes ) { * @param string $value The value of the field. * @param string $name The name of the field. * @param string $id The id of the field. + * @param bool $is_required The required status of the field. * @return string */ - public function render_field( $type, $label, $value, $name, $id ) { + public function render_field( $type, $label, $value, $name, $id, $is_required = false ) { $output = '
'; - $output .= ''; + $output .= ''; $output .= ''; $output .= '
'; diff --git a/src/blocks/frontend/form/index.js b/src/blocks/frontend/form/index.js index 56a245864..06c1d74af 100644 --- a/src/blocks/frontend/form/index.js +++ b/src/blocks/frontend/form/index.js @@ -14,7 +14,6 @@ import { domReady } from '../../helpers/frontend-helper-functions.js'; const extractFormFields = form => { /** @type {Array.} */ - const elemsWithError = []; const formFieldsData = [{ label: window?.themeisleGutenbergForm?.messages['form-submission'] || 'Form submission from', value: window.location.href }]; const inputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-input' ); @@ -24,10 +23,6 @@ const extractFormFields = form => { const label = input.querySelector( ':scope > .otter-form-input-label__label, :scope > .otter-form-textarea-label__label' )?.innerHTML; const valueElem = input.querySelector( ':scope > .otter-form-input, :scope > .otter-form-textarea-input' ); - if ( valueElem?.hasAttribute( 'required' ) && ! valueElem?.checkValidity() ) { - elemsWithError.push( valueElem ); - } - if ( label && valueElem?.value ) { formFieldsData.push({ label: label, @@ -61,7 +56,7 @@ const extractFormFields = form => { }); }); - return { formFieldsData, elemsWithError }; + return { formFieldsData }; }; /** @@ -74,6 +69,61 @@ function extractNonceValue( form ) { return form.querySelector( query )?.value; } +/** + * Validate the inputs from the form. + * + * @param {HTMLDivElement} form The form. + * @returns + */ +function validateInputs( form ) { + let result = true; + + // Validate text inputs. + const inputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-input input' ); + [ ...inputs ]?.forEach( input => { + if ( input?.hasAttribute( 'required' ) && ! input?.checkValidity() ) { + input?.reportValidity(); + result = false; + } + }); + + // Validate textarea inputs. + const textarea = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-textarea textarea' ); + [ ...textarea ]?.forEach( input => { + if ( input?.hasAttribute( 'required' ) && ! input?.checkValidity() ) { + input?.reportValidity(); + result = false; + } + }); + + // Validate multiple choice inputs. + const multipleChoiceInputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-multiple-choice' ); + + multipleChoiceInputs?.forEach( input => { + const select = input.querySelector( ':scope > select' ); + if ( select?.hasAttribute( 'required' ) && ! select?.checkValidity() ) { + select?.reportValidity(); + result = false; + } + + // Check if it is required and at least one is checked. + const radios = input.querySelectorAll( ':scope > .o-form-multiple-choice-field > input[type="radio"]' ); + if ( radios?.length && radios[0]?.hasAttribute( 'required' ) && ! [ ...radios ].some( radio => radio.checked ) ) { + radios[0]?.reportValidity(); + result = false; + } + + const checkboxes = input.querySelectorAll( ':scope > .o-form-multiple-choice-field > input[type="checkbox"]' ); + if ( checkboxes?.length && checkboxes[0]?.hasAttribute( 'required' ) && ! [ ...checkboxes ].some( checkbox => checkbox.checked ) ) { + checkboxes[0]?.reportValidity(); + result = false; + } + }); + + + return result; +} + /** * Send the date from the form to the server * @@ -86,7 +136,7 @@ const collectAndSendInputFormData = ( form, btn, displayMsg ) => { const payload = {}; // Get the data from the form fields. - const { formFieldsData, elemsWithError } = extractFormFields( form ); + const { formFieldsData } = extractFormFields( form ); const formIsEmpty = 2 > formFieldsData?.length; const nonceFieldValue = extractNonceValue( form ); const hasCaptcha = form?.classList?.contains( 'has-captcha' ); @@ -103,12 +153,7 @@ const collectAndSendInputFormData = ( form, btn, displayMsg ) => { return; } - /** - * Validate the form inputs data. - */ - elemsWithError.forEach( input => { - input?.reportValidity(); - }); + const isValidationSuccessful = validateInputs( form ); if ( hasCaptcha && ! hasValidToken ) { const msg = ! window.hasOwnProperty( 'grecaptcha' ) ? @@ -120,7 +165,7 @@ const collectAndSendInputFormData = ( form, btn, displayMsg ) => { ).show(); } - if ( 0 < elemsWithError.length || ( hasCaptcha && ! hasValidToken ) ) { + if ( ! isValidationSuccessful || ( hasCaptcha && ! hasValidToken ) ) { btn.disabled = false; btn.removeChild( spinner ); } else { From 0e1ac76233160b64442ba95c22c903e98f084ea4 Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Tue, 21 Feb 2023 12:50:22 +0200 Subject: [PATCH 007/152] chore: improve input data collection --- src/blocks/frontend/form/index.js | 56 ++++++++++++++----------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/blocks/frontend/form/index.js b/src/blocks/frontend/form/index.js index 06c1d74af..09fd21340 100644 --- a/src/blocks/frontend/form/index.js +++ b/src/blocks/frontend/form/index.js @@ -16,44 +16,38 @@ const extractFormFields = form => { /** @type {Array.} */ const formFieldsData = [{ label: window?.themeisleGutenbergForm?.messages['form-submission'] || 'Form submission from', value: window.location.href }]; - const inputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-input' ); - const textarea = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-textarea' ); + const allInputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-input, :scope > .otter-form__container > .wp-block-themeisle-blocks-form-textarea, :scope > .otter-form__container > .wp-block-themeisle-blocks-form-multiple-choice' ); - [ ...inputs, ...textarea ]?.forEach( input => { - const label = input.querySelector( ':scope > .otter-form-input-label__label, :scope > .otter-form-textarea-label__label' )?.innerHTML; - const valueElem = input.querySelector( ':scope > .otter-form-input, :scope > .otter-form-textarea-input' ); - - if ( label && valueElem?.value ) { - formFieldsData.push({ - label: label, - value: valueElem?.value, - type: valueElem?.type - }); - } - }); - - const multipleChoiceInputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-multiple-choice' ); + allInputs?.forEach( ( input, index ) => { + const label = `(Field ${index + 1}) ${input.querySelector( '.otter-form-input-label, .otter-form-input-label__label, .otter-form-textarea-label__label' )?.innerHTML}`; - multipleChoiceInputs?.forEach( input => { - - const label = input.querySelector( ':scope > .otter-form-input-label' )?.innerHTML; - - const select = input.querySelector( ':scope > select' ); let value = undefined; + let fieldType = undefined; - if ( select ) { - value = [ ...select.selectedOptions ].map( o => o?.label )?.filter( l => l ).join( ', ' ); + const valueElem = input.querySelector( '.otter-form-input:not([type="checkbox"], [type="radio"]), .otter-form-textarea-input' ); + if ( null !== valueElem ) { + value = valueElem?.value; + fieldType = valueElem?.type; } else { - const labels = input.querySelectorAll( '.o-form-multiple-choice-field > label' ); - const valuesElem = input.querySelectorAll( '.o-form-multiple-choice-field > input' ); - value = [ ...labels ].filter( ( label, index ) => valuesElem[index]?.checked ).map( label => label.innerHTML ).join( ', ' ); + const select = input.querySelector( 'select' ); + if ( select ) { + value = [ ...select.selectedOptions ].map( o => o?.label )?.filter( l => l ).join( ', ' ); + fieldType = 'multiple-choice'; + } else { + const labels = input.querySelectorAll( '.o-form-multiple-choice-field > label' ); + const valuesElem = input.querySelectorAll( '.o-form-multiple-choice-field > input' ); + value = [ ...labels ].filter( ( label, index ) => valuesElem[index]?.checked ).map( label => label.innerHTML ).join( ', ' ); + fieldType = 'multiple-choice'; + } } - formFieldsData.push({ - label: label, - value: value, - type: 'multiple-choice' - }); + if ( label && value ) { + formFieldsData.push({ + label: label, + value: value, + type: valueElem?.type + }); + } }); return { formFieldsData }; From 8225ca2bc58386052f2d62c95cd5e0e1e6c35504 Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Tue, 21 Feb 2023 15:06:46 +0200 Subject: [PATCH 008/152] chore: compatibility --- src/blocks/blocks/form/common.ts | 28 +++++++++++++ src/blocks/blocks/form/input/edit.js | 9 +--- src/blocks/blocks/form/input/index.js | 16 ++++++-- src/blocks/blocks/form/input/inspector.js | 32 +++++++++++---- .../blocks/form/multiple-choice/edit.js | 14 +------ .../blocks/form/multiple-choice/inspector.js | 41 +++++++++---------- src/blocks/blocks/form/textarea/edit.js | 7 +--- src/blocks/blocks/form/textarea/index.js | 10 +++++ src/blocks/blocks/form/textarea/inspector.js | 29 +++++++++---- 9 files changed, 117 insertions(+), 69 deletions(-) create mode 100644 src/blocks/blocks/form/common.ts diff --git a/src/blocks/blocks/form/common.ts b/src/blocks/blocks/form/common.ts new file mode 100644 index 000000000..f5142c9bc --- /dev/null +++ b/src/blocks/blocks/form/common.ts @@ -0,0 +1,28 @@ +import { omit } from 'lodash'; +import { createBlock } from '@wordpress/blocks'; +import { dispatch } from '@wordpress/data'; +import { getChoice } from '../../helpers/helper-functions'; + +export const switchFormFieldTo = ( type?: string, clientId ?:string, attributes?: any ) => { + + if ( ! type || ! clientId || ! attributes ) { + return; + } + + const { replaceBlock } = dispatch( 'core/block-editor' ); + + const blockName = getChoice([ + [ 'textarea' === type, 'form-textarea' ], + [ 'select' === type || 'checkbox' === type || 'radio' === type, 'form-multiple-choice' ], + [ 'form-input' ] + ]); + + + const newBlock = createBlock( `themeisle-blocks/${ blockName }`, + omit({ ...attributes, type: type }, 'form-textarea' === blockName ? [ 'multipleSelection', 'options', 'type' ] : [ 'multipleSelection', 'options' ]) + ); + + replaceBlock( clientId, newBlock ); +}; + +export default { switchFormFieldTo }; diff --git a/src/blocks/blocks/form/input/edit.js b/src/blocks/blocks/form/input/edit.js index f31e41947..85ea9e6c5 100644 --- a/src/blocks/blocks/form/input/edit.js +++ b/src/blocks/blocks/form/input/edit.js @@ -74,12 +74,6 @@ const Edit = ({ const { selectBlock, replaceBlock } = useDispatch( 'core/block-editor' ); - const switchToTextarea = () => { - const block = createBlock( 'themeisle-blocks/form-textarea', { ...omit( attributes, [ 'type' ]) }); - replaceBlock( clientId, block ); - }; - - useEffect( () => { const per = x => x ? x + '%' : null; @@ -103,8 +97,7 @@ const Edit = ({ selectBlock( parentClientId ) } - switchToTextarea={ switchToTextarea } + clientId={ clientId } />
diff --git a/src/blocks/blocks/form/input/index.js b/src/blocks/blocks/form/input/index.js index 21a3832d4..1d74b8932 100644 --- a/src/blocks/blocks/form/input/index.js +++ b/src/blocks/blocks/form/input/index.js @@ -39,7 +39,7 @@ registerBlockType( name, { { name: 'themeisle-blocks/form-input-email', description: __( 'Insert an email field', 'otter-blocks' ), - icon: 'email', + icon: icon, title: __( 'Email Field', 'otter-blocks' ), attributes: { type: 'email' @@ -48,7 +48,7 @@ registerBlockType( name, { { name: 'themeisle-blocks/form-input-number', description: __( 'Insert a number field', 'otter-blocks' ), - icon: 'calculator', + icon: icon, title: __( 'Number Field', 'otter-blocks' ), attributes: { type: 'number' @@ -57,7 +57,7 @@ registerBlockType( name, { { name: 'themeisle-blocks/form-input-date', description: __( 'Insert a date field', 'otter-blocks' ), - icon: 'calendar-alt', + icon: icon, title: __( 'Date Field', 'otter-blocks' ), attributes: { type: 'date' @@ -77,6 +77,16 @@ registerBlockType( name, { ...attrs }); } + }, + { + type: 'block', + blocks: [ 'themeisle-blocks/form-multiple-choice' ], + transform: ( attributes ) => { + const attrs = omit( attributes, [ 'type' ]); + return createBlock( 'themeisle-blocks/form-multiple-choice', { + ...attrs + }); + } } ] } diff --git a/src/blocks/blocks/form/input/inspector.js b/src/blocks/blocks/form/input/inspector.js index ab46af53a..776b80d04 100644 --- a/src/blocks/blocks/form/input/inspector.js +++ b/src/blocks/blocks/form/input/inspector.js @@ -15,6 +15,7 @@ import { TextControl, ToggleControl } from '@wordpress/components'; +import { switchFormFieldTo } from '../common'; /** * @@ -25,7 +26,7 @@ const Inspector = ({ attributes, setAttributes, selectParent, - switchToTextarea + clientId }) => { return ( @@ -45,31 +46,44 @@ const Inspector = ({ value={ attributes.type } options={ [ { - label: __( 'Text', 'otter-blocks' ), - value: 'text' - }, - { - label: __( 'Email', 'otter-blocks' ), - value: 'email' + label: __( 'Checkbox', 'otter-blocks' ), + value: 'checkbox' }, { label: __( 'Date', 'otter-blocks' ), value: 'date' }, + { + label: __( 'Email', 'otter-blocks' ), + value: 'email' + }, { label: __( 'Number', 'otter-blocks' ), value: 'number' }, + { + label: __( 'Radio', 'otter-blocks' ), + value: 'radio' + }, + { + label: __( 'Select', 'otter-blocks' ), + value: 'select' + }, + { + label: __( 'Text', 'otter-blocks' ), + value: 'text' + }, { label: __( 'Textarea', 'otter-blocks' ), value: 'textarea' } ] } onChange={ type => { - if ( 'textarea' === type ) { - switchToTextarea?.(); + if ( 'textarea' === type || 'radio' === type || 'checkbox' === type || 'select' === type ) { + switchFormFieldTo( type, clientId, attributes ); return; } + setAttributes({ type }); }} /> diff --git a/src/blocks/blocks/form/multiple-choice/edit.js b/src/blocks/blocks/form/multiple-choice/edit.js index 48d78d3c4..e69b591fd 100644 --- a/src/blocks/blocks/form/multiple-choice/edit.js +++ b/src/blocks/blocks/form/multiple-choice/edit.js @@ -119,25 +119,13 @@ const Edit = ({ const { selectBlock, replaceBlock } = useDispatch( 'core/block-editor' ); - const switchToTextarea = () => { - const block = createBlock( 'themeisle-blocks/form-textarea', { ...omit( attributes, [ 'type', 'multipleSelection', 'options' ]) }); - replaceBlock( clientId, block ); - }; - - const switchToInput = ( inputType ) => { - const block = createBlock( 'themeisle-blocks/form-input', { ...omit( attributes, [ 'type', 'multipleSelection', 'options' ]), type: inputType }); - replaceBlock( clientId, block ); - }; - - return ( selectBlock( parentClientId ) } - switchToTextarea={ switchToTextarea } - switchToInput={ switchToInput } + clientId={ clientId } />
diff --git a/src/blocks/blocks/form/multiple-choice/inspector.js b/src/blocks/blocks/form/multiple-choice/inspector.js index 249380d44..426e40289 100644 --- a/src/blocks/blocks/form/multiple-choice/inspector.js +++ b/src/blocks/blocks/form/multiple-choice/inspector.js @@ -18,6 +18,7 @@ import { } from '@wordpress/components'; import { getActiveStyle, changeActiveStyle } from '../../../helpers/helper-functions.js'; +import { switchFormFieldTo } from '../common'; const styles = [ { @@ -35,8 +36,7 @@ const Inspector = ({ attributes, setAttributes, selectParent, - switchToTextarea, - switchToInput + clientId }) => { return ( @@ -56,47 +56,44 @@ const Inspector = ({ value={ attributes.type } options={ [ { - label: __( 'Text', 'otter-blocks' ), - value: 'text' - }, - { - label: __( 'Email', 'otter-blocks' ), - value: 'email' + label: __( 'Checkbox', 'otter-blocks' ), + value: 'checkbox' }, { label: __( 'Date', 'otter-blocks' ), value: 'date' }, { - label: __( 'Number', 'otter-blocks' ), - value: 'number' + label: __( 'Email', 'otter-blocks' ), + value: 'email' }, { - label: __( 'Textarea', 'otter-blocks' ), - value: 'textarea' + label: __( 'Number', 'otter-blocks' ), + value: 'number' }, { label: __( 'Radio', 'otter-blocks' ), value: 'radio' }, - { - label: __( 'Checkbox', 'otter-blocks' ), - value: 'checkbox' - }, { label: __( 'Select', 'otter-blocks' ), value: 'select' + }, + { + label: __( 'Text', 'otter-blocks' ), + value: 'text' + }, + { + label: __( 'Textarea', 'otter-blocks' ), + value: 'textarea' } ] } onChange={ type => { - if ( 'textarea' === type ) { - switchToTextarea?.(); - return; - } else if ( 'radio' === type || 'checkbox' === type || 'select' === type ) { + if ( 'radio' === type || 'checkbox' === type || 'select' === type ) { setAttributes({ type }); - } else { - switchToInput?.( type ); + return; } + switchFormFieldTo( type, clientId, attributes ); }} /> diff --git a/src/blocks/blocks/form/textarea/edit.js b/src/blocks/blocks/form/textarea/edit.js index 2c370b903..550b4487b 100644 --- a/src/blocks/blocks/form/textarea/edit.js +++ b/src/blocks/blocks/form/textarea/edit.js @@ -70,11 +70,6 @@ const Edit = ({ const { selectBlock, replaceBlock } = useDispatch( 'core/block-editor' ); - const switchToInput = type => { - const block = createBlock( 'themeisle-blocks/form-input', { ...attributes, type: type }); - replaceBlock( clientId, block ); - }; - useEffect( () => { const per = x => x ? x + '%' : x; @@ -99,7 +94,7 @@ const Edit = ({ attributes={ attributes } setAttributes={ setAttributes } selectParent={ () => selectBlock( parentClientId ) } - switchToInput={ switchToInput } + clientId={ clientId } />
diff --git a/src/blocks/blocks/form/textarea/index.js b/src/blocks/blocks/form/textarea/index.js index 94d83795c..204f3b34c 100644 --- a/src/blocks/blocks/form/textarea/index.js +++ b/src/blocks/blocks/form/textarea/index.js @@ -46,6 +46,16 @@ registerBlockType( name, { ...attributes }); } + }, + { + type: 'block', + blocks: [ 'themeisle-blocks/form-multiple-choice' ], + transform: ( attributes ) => { + + return createBlock( 'themeisle-blocks/form-multiple-choice', { + ...attributes + }); + } } ] } diff --git a/src/blocks/blocks/form/textarea/inspector.js b/src/blocks/blocks/form/textarea/inspector.js index ae07ff5a3..0037b5a4a 100644 --- a/src/blocks/blocks/form/textarea/inspector.js +++ b/src/blocks/blocks/form/textarea/inspector.js @@ -12,12 +12,13 @@ import { TextControl, ToggleControl } from '@wordpress/components'; +import { switchFormFieldTo } from '../common'; const Inspector = ({ attributes, setAttributes, selectParent, - switchToInput + clientId }) => { return ( @@ -37,21 +38,33 @@ const Inspector = ({ value={ 'textarea' } options={ [ { - label: __( 'Text', 'otter-blocks' ), - value: 'text' - }, - { - label: __( 'Email', 'otter-blocks' ), - value: 'email' + label: __( 'Checkbox', 'otter-blocks' ), + value: 'checkbox' }, { label: __( 'Date', 'otter-blocks' ), value: 'date' }, + { + label: __( 'Email', 'otter-blocks' ), + value: 'email' + }, { label: __( 'Number', 'otter-blocks' ), value: 'number' }, + { + label: __( 'Radio', 'otter-blocks' ), + value: 'radio' + }, + { + label: __( 'Select', 'otter-blocks' ), + value: 'select' + }, + { + label: __( 'Text', 'otter-blocks' ), + value: 'text' + }, { label: __( 'Textarea', 'otter-blocks' ), value: 'textarea' @@ -61,7 +74,7 @@ const Inspector = ({ if ( 'textarea' === type ) { return; } - switchToInput( type ); + switchFormFieldTo( type, clientId, attributes ); }} /> From 50100331e9af6a6f373d240246f158cc4b6235ed Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Wed, 22 Feb 2023 11:00:17 +0200 Subject: [PATCH 009/152] chore: field switching & hide label --- src/blocks/blocks/form/common.ts | 28 -------- src/blocks/blocks/form/common.tsx | 69 +++++++++++++++++++ src/blocks/blocks/form/input/inspector.js | 8 ++- src/blocks/blocks/form/input/types.d.ts | 10 +-- .../blocks/form/multiple-choice/inspector.js | 8 ++- .../blocks/form/multiple-choice/types.d.ts | 10 +-- src/blocks/blocks/form/style.scss | 16 +++++ src/blocks/blocks/form/textarea/inspector.js | 8 ++- src/blocks/blocks/form/textarea/types.d.ts | 10 +-- src/blocks/frontend/form/index.js | 4 +- src/blocks/helpers/blocks.d.ts | 4 +- src/blocks/helpers/helper-functions.js | 6 +- 12 files changed, 119 insertions(+), 62 deletions(-) delete mode 100644 src/blocks/blocks/form/common.ts create mode 100644 src/blocks/blocks/form/common.tsx diff --git a/src/blocks/blocks/form/common.ts b/src/blocks/blocks/form/common.ts deleted file mode 100644 index f5142c9bc..000000000 --- a/src/blocks/blocks/form/common.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { omit } from 'lodash'; -import { createBlock } from '@wordpress/blocks'; -import { dispatch } from '@wordpress/data'; -import { getChoice } from '../../helpers/helper-functions'; - -export const switchFormFieldTo = ( type?: string, clientId ?:string, attributes?: any ) => { - - if ( ! type || ! clientId || ! attributes ) { - return; - } - - const { replaceBlock } = dispatch( 'core/block-editor' ); - - const blockName = getChoice([ - [ 'textarea' === type, 'form-textarea' ], - [ 'select' === type || 'checkbox' === type || 'radio' === type, 'form-multiple-choice' ], - [ 'form-input' ] - ]); - - - const newBlock = createBlock( `themeisle-blocks/${ blockName }`, - omit({ ...attributes, type: type }, 'form-textarea' === blockName ? [ 'multipleSelection', 'options', 'type' ] : [ 'multipleSelection', 'options' ]) - ); - - replaceBlock( clientId, newBlock ); -}; - -export default { switchFormFieldTo }; diff --git a/src/blocks/blocks/form/common.tsx b/src/blocks/blocks/form/common.tsx new file mode 100644 index 000000000..59bc02c92 --- /dev/null +++ b/src/blocks/blocks/form/common.tsx @@ -0,0 +1,69 @@ +import { omit } from 'lodash'; + +import { ToggleControl } from '@wordpress/components'; +import { createBlock } from '@wordpress/blocks'; +import { __ } from '@wordpress/i18n'; +import { dispatch } from '@wordpress/data'; + +import { changeActiveStyle, getActiveStyle, getChoice } from '../../helpers/helper-functions'; +import { FormInputProps } from './input/types'; +import { BlockProps } from '../../helpers/blocks'; + +export type FormInputCommonProps = { + id: string + label: string + placeholder: string + isRequired: boolean + mappedName: string + labelColor: string + helpText: string +} + +export const switchFormFieldTo = ( type?: string, clientId ?:string, attributes?: any ) => { + + if ( ! type || ! clientId || ! attributes ) { + return; + } + + const { replaceBlock } = dispatch( 'core/block-editor' ); + + const blockName = getChoice([ + [ 'textarea' === type, 'form-textarea' ], + [ 'select' === type || 'checkbox' === type || 'radio' === type, 'form-multiple-choice' ], + [ 'form-input' ] + ]); + + + const newBlock = createBlock( `themeisle-blocks/${ blockName }`, + omit({ ...attributes, type: type }, 'form-textarea' === blockName ? [ 'multipleSelection', 'options', 'type' ] : [ 'multipleSelection', 'options' ]) + ); + + replaceBlock( clientId, newBlock ); +}; + +const stylesHide = [ + { + label: '', + value: 'hidden-label' + } +]; + +export const HideFieldLabelToggle = ( props: Partial> ) => { + + const { attributes, setAttributes } = props; + + return ( + + // @ts-ignore + { + const classes = changeActiveStyle( attributes?.className, stylesHide, value ? 'hidden-label' : undefined ); + setAttributes?.({ className: classes }); + } } + /> + ); +}; + +export default { switchFormFieldTo, HideFieldLabelToggle }; diff --git a/src/blocks/blocks/form/input/inspector.js b/src/blocks/blocks/form/input/inspector.js index 776b80d04..951932be2 100644 --- a/src/blocks/blocks/form/input/inspector.js +++ b/src/blocks/blocks/form/input/inspector.js @@ -15,7 +15,7 @@ import { TextControl, ToggleControl } from '@wordpress/components'; -import { switchFormFieldTo } from '../common'; +import { HideFieldLabelToggle, switchFormFieldTo } from '../common'; /** * @@ -76,6 +76,10 @@ const Inspector = ({ { label: __( 'Textarea', 'otter-blocks' ), value: 'textarea' + }, + { + label: __( 'Url', 'otter-blocks' ), + value: 'url' } ] } onChange={ type => { @@ -94,6 +98,8 @@ const Inspector = ({ onChange={ label => setAttributes({ label }) } /> + + diff --git a/src/blocks/blocks/form/multiple-choice/inspector.js b/src/blocks/blocks/form/multiple-choice/inspector.js index 426e40289..bf6e25d4c 100644 --- a/src/blocks/blocks/form/multiple-choice/inspector.js +++ b/src/blocks/blocks/form/multiple-choice/inspector.js @@ -18,7 +18,7 @@ import { } from '@wordpress/components'; import { getActiveStyle, changeActiveStyle } from '../../../helpers/helper-functions.js'; -import { switchFormFieldTo } from '../common'; +import { HideFieldLabelToggle, switchFormFieldTo } from '../common'; const styles = [ { @@ -86,6 +86,10 @@ const Inspector = ({ { label: __( 'Textarea', 'otter-blocks' ), value: 'textarea' + }, + { + label: __( 'Url', 'otter-blocks' ), + value: 'url' } ] } onChange={ type => { @@ -103,6 +107,8 @@ const Inspector = ({ onChange={ label => setAttributes({ label }) } /> + + { @@ -84,6 +88,8 @@ const Inspector = ({ onChange={ label => setAttributes({ label }) } /> + + diff --git a/src/blocks/frontend/form/index.js b/src/blocks/frontend/form/index.js index 450f26570..b78499863 100644 --- a/src/blocks/frontend/form/index.js +++ b/src/blocks/frontend/form/index.js @@ -77,7 +77,7 @@ function validateInputs( form ) { // Validate text inputs. const inputs = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-input input' ); [ ...inputs ]?.forEach( input => { - if ( input?.hasAttribute( 'required' ) && ! input?.checkValidity() ) { + if ( ! input?.checkValidity() ) { input?.reportValidity(); result = false; } @@ -86,7 +86,7 @@ function validateInputs( form ) { // Validate textarea inputs. const textarea = form?.querySelectorAll( ':scope > .otter-form__container > .wp-block-themeisle-blocks-form-textarea textarea' ); [ ...textarea ]?.forEach( input => { - if ( input?.hasAttribute( 'required' ) && ! input?.checkValidity() ) { + if ( ! input?.checkValidity() ) { input?.reportValidity(); result = false; } diff --git a/src/blocks/helpers/blocks.d.ts b/src/blocks/helpers/blocks.d.ts index 65f9a2653..4e05de646 100644 --- a/src/blocks/helpers/blocks.d.ts +++ b/src/blocks/helpers/blocks.d.ts @@ -1,8 +1,8 @@ import { Dispatch, SetStateAction } from 'react'; export type BlockProps = { - attributes: Partial< T & { className: string }> - setAttributes: ( attributes: Partial ) => void + attributes: Partial< T & { className?: string }> + setAttributes: ( attributes: Partial ) => void isSelected: boolean clientId: string name: string diff --git a/src/blocks/helpers/helper-functions.js b/src/blocks/helpers/helper-functions.js index a1b659195..ae35d9500 100644 --- a/src/blocks/helpers/helper-functions.js +++ b/src/blocks/helpers/helper-functions.js @@ -450,7 +450,7 @@ export const buildResponsiveGetAttributes = ( currentView, defaultView = 'Deskto * Get Active Style Name. * * @param { Object } styles Block styles. - * @param { Array } className Classes of the block. + * @param { string | undefined } className Classes of the block. * * @returns { string } */ @@ -477,9 +477,9 @@ export const getActiveStyle = ( styles, className ) => { /** * Replaces the active style in the block's className. * - * @param { string } className Class name. + * @param { string | undefined } className Class name. * @param { Object } styles Block styles. - * @param { Object } newStyle The replacing style. + * @param { string | undefined } newStyle The replacing style. * * @return { string } The updated className. */ From 4c6ae9e492867e971d55ee5b0dfcc51f7ac636f7 Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Wed, 22 Feb 2023 12:15:27 +0200 Subject: [PATCH 010/152] chore: php css --- inc/class-base-css.php | 1 + .../blocks/class-form-multiple-choice-css.php | 52 +++++++++++++++++++ inc/render/class-form-multiple-choice.php | 2 +- 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 inc/css/blocks/class-form-multiple-choice-css.php diff --git a/inc/class-base-css.php b/inc/class-base-css.php index 8ea116222..120c415cc 100644 --- a/inc/class-base-css.php +++ b/inc/class-base-css.php @@ -88,6 +88,7 @@ public function autoload_block_classes() { '\ThemeIsle\GutenbergBlocks\CSS\Blocks\Form_CSS', '\ThemeIsle\GutenbergBlocks\CSS\Blocks\Form_Input_CSS', '\ThemeIsle\GutenbergBlocks\CSS\Blocks\Form_Textarea_CSS', + '\ThemeIsle\GutenbergBlocks\CSS\Blocks\Form_Multiple_Choice_CSS', '\ThemeIsle\GutenbergBlocks\CSS\Blocks\Flip_CSS', '\ThemeIsle\GutenbergBlocks\CSS\Blocks\Progress_Bar_CSS', '\ThemeIsle\GutenbergBlocks\CSS\Blocks\Popup_CSS', diff --git a/inc/css/blocks/class-form-multiple-choice-css.php b/inc/css/blocks/class-form-multiple-choice-css.php new file mode 100644 index 000000000..1ae268f26 --- /dev/null +++ b/inc/css/blocks/class-form-multiple-choice-css.php @@ -0,0 +1,52 @@ +add_item( + array( + 'properties' => array( + array( + 'property' => '--label-color', + 'value' => 'labelColor', + ), + ), + ) + ); + + $style = $css->generate(); + + return $style; + } + +} diff --git a/inc/render/class-form-multiple-choice.php b/inc/render/class-form-multiple-choice.php index 0acd59db2..a16cd5299 100644 --- a/inc/render/class-form-multiple-choice.php +++ b/inc/render/class-form-multiple-choice.php @@ -68,7 +68,7 @@ public function render_field( $type, $label, $value, $name, $id, $is_required = $output = '
'; $output .= ''; - $output .= ''; + $output .= ''; $output .= '
'; From 4978bc914ec5c7e32c56a1bc1755973d552d0320 Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Wed, 22 Feb 2023 12:15:54 +0200 Subject: [PATCH 011/152] chore: inline only for checkbox & radio --- src/blocks/blocks/form/style.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/blocks/blocks/form/style.scss b/src/blocks/blocks/form/style.scss index 75aa96939..9e44e8880 100644 --- a/src/blocks/blocks/form/style.scss +++ b/src/blocks/blocks/form/style.scss @@ -277,6 +277,10 @@ background: var(--input-bg-color); } + .o-form-choice-label { + font-size: var(--label-font-size); + } + .o-form-help { opacity: 0.8; color: var(--help-label-color); @@ -359,14 +363,10 @@ } } - .wp-block-themeisle-blocks-form-multiple-choice.is-style-inline-list { + .wp-block-themeisle-blocks-form-multiple-choice.is-style-inline-list:not(:has(select)) { flex-direction: row; flex-wrap: wrap; - .otter-form-input-label { - width: max-content; - } - .o-form-help { width: 100%; } From 0d0fad9abd756b7806099b4ff232da711d7b50b9 Mon Sep 17 00:00:00 2001 From: Soare Robert Daniel Date: Wed, 22 Feb 2023 12:42:58 +0200 Subject: [PATCH 012/152] chore: disable checkbox & radio in Editor --- src/blocks/blocks/form/multiple-choice/edit.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/blocks/blocks/form/multiple-choice/edit.js b/src/blocks/blocks/form/multiple-choice/edit.js index e69b591fd..80cdb8828 100644 --- a/src/blocks/blocks/form/multiple-choice/edit.js +++ b/src/blocks/blocks/form/multiple-choice/edit.js @@ -16,10 +16,6 @@ import { import { useDispatch, useSelect } from '@wordpress/data'; -import { omit } from 'lodash'; - -import { createBlock } from '@wordpress/blocks'; - /** * Internal dependencies */ @@ -27,6 +23,7 @@ import metadata from './block.json'; import { blockInit } from '../../../helpers/block-utility.js'; import Inspector from './inspector.js'; import { _cssBlock } from '../../../helpers/helper-functions'; +import { Disabled } from '@wordpress/components'; const { attributes: defaultAttributes } = metadata; @@ -48,7 +45,9 @@ const Field = ({ fieldType, label, setAttributes, position, attributes }) => { return (
- + + +