├── .DS_Store ├── .dev ├── bin │ └── install-wp-tests.sh └── scripts │ ├── set-phpcs-install-path.sh │ └── update-changelog.sh ├── .github └── workflows │ └── development-zip.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── .wp-env.json ├── README.md ├── app ├── Assets.php ├── Shortcode.php ├── Support.php ├── core │ ├── Builder.php │ ├── Editor.php │ └── Handler.php ├── handlers │ └── ContactForm7.php └── notices │ └── InstallContactFormNotice.php ├── cf7-blocks.php ├── composer.json ├── composer.lock ├── jsconfig.json ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── .DS_Store ├── blocks-library │ ├── .DS_Store │ ├── package.json │ └── src │ │ ├── .DS_Store │ │ ├── acceptance │ │ ├── block.json │ │ ├── edit.js │ │ ├── editor.scss │ │ ├── index.js │ │ ├── inspector.js │ │ └── save.js │ │ ├── components │ │ ├── index.js │ │ └── with-label │ │ │ └── index.js │ │ ├── constants.js │ │ ├── editor.scss │ │ ├── form-template │ │ ├── block.json │ │ ├── edit.js │ │ ├── editor.scss │ │ ├── index.js │ │ ├── placeholder.js │ │ └── templates.js │ │ ├── icons │ │ ├── add-user.js │ │ ├── appointment.js │ │ ├── bell.js │ │ ├── calender.js │ │ ├── cf7blocks.js │ │ ├── checkbox.js │ │ ├── feedback.js │ │ ├── index.js │ │ ├── mail.js │ │ ├── number-input.js │ │ ├── radio.js │ │ ├── range.js │ │ ├── required.js │ │ ├── telephone.js │ │ └── text-input.js │ │ ├── index.js │ │ ├── input-base │ │ ├── block.json │ │ ├── edit.js │ │ ├── editor.scss │ │ ├── index.js │ │ ├── inspector.js │ │ ├── save.js │ │ ├── style.scss │ │ ├── toolbar.js │ │ └── variations.js │ │ ├── selection-base │ │ ├── block.json │ │ ├── components │ │ │ ├── edit-mode.js │ │ │ ├── preview-mode.js │ │ │ └── select-preview-mode.js │ │ ├── edit.js │ │ ├── editor.scss │ │ ├── index.js │ │ ├── inspector.js │ │ ├── save.js │ │ ├── style.scss │ │ ├── toolbar.js │ │ └── variations.js │ │ ├── style.scss │ │ ├── submit │ │ ├── block.json │ │ ├── edit.js │ │ ├── index.js │ │ ├── inspector.js │ │ ├── save.js │ │ └── style.scss │ │ └── utils │ │ ├── convert-to-cf7-shortcode.js │ │ ├── generate-name.js │ │ └── process-input-label.js ├── cf7-integrate │ ├── package.json │ └── src │ │ ├── index.js │ │ └── style.scss └── gutenberg │ ├── package.json │ └── src │ ├── components │ ├── branding │ │ ├── index.js │ │ └── style.scss │ ├── index.js │ ├── interactive-events │ │ └── index.js │ ├── more-menu │ │ └── index.js │ ├── toggle-full-screen │ │ └── index.js │ └── welcome-guide │ │ ├── index.js │ │ ├── pages │ │ ├── intuitive-block-editor.js │ │ ├── modify-field-blocks.js │ │ ├── start-quick-guide.js │ │ └── stay-updated.js │ │ └── style.scss │ ├── editor.js │ ├── icons │ ├── coffee.js │ ├── github.js │ └── index.js │ ├── illustrations │ ├── block-editor-layout.js │ ├── index.js │ ├── modify-fields.js │ ├── start-quick-guide.js │ └── stay-updated.js │ ├── index.js │ ├── portals │ ├── header-toolbar.js │ └── index.js │ └── style.scss ├── readme.txt ├── ruleset.xml └── webpack.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CakeWP/cf7-blocks/88f4fafab6f947146d2c6cc4b23d1a460134955f/.DS_Store -------------------------------------------------------------------------------- /.dev/bin/install-wp-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# -lt 3 ]; then 4 | echo "usage: $0 [db-host] [wp-version] [skip-database-creation]" 5 | exit 1 6 | fi 7 | 8 | DB_NAME=$1 9 | DB_USER=$2 10 | DB_PASS=$3 11 | DB_HOST=${4-localhost} 12 | WP_VERSION=${5-latest} 13 | SKIP_DB_CREATE=${6-false} 14 | 15 | TMPDIR=${TMPDIR-/tmp} 16 | TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//") 17 | WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib} 18 | WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/} 19 | 20 | rm -rf $WP_TESTS_DIR $WP_CORE_DIR 21 | 22 | download() { 23 | if [ $(which curl) ]; then 24 | curl -s "$1" >"$2" 25 | elif [ $(which wget) ]; then 26 | wget -nv -O "$2" "$1" 27 | fi 28 | } 29 | 30 | if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then 31 | WP_BRANCH=${WP_VERSION%\-*} 32 | WP_TESTS_TAG="branches/$WP_BRANCH" 33 | 34 | elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then 35 | WP_TESTS_TAG="branches/$WP_VERSION" 36 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then 37 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 38 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 39 | WP_TESTS_TAG="tags/${WP_VERSION%??}" 40 | else 41 | WP_TESTS_TAG="tags/$WP_VERSION" 42 | fi 43 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 44 | WP_TESTS_TAG="trunk" 45 | else 46 | # http serves a single offer, whereas https serves multiple. we only want one 47 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json 48 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json 49 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//') 50 | if [[ -z "$LATEST_VERSION" ]]; then 51 | echo "Latest WordPress version could not be found" 52 | exit 1 53 | fi 54 | WP_TESTS_TAG="tags/$LATEST_VERSION" 55 | fi 56 | set -ex 57 | 58 | install_wp() { 59 | 60 | if [ -d $WP_CORE_DIR ]; then 61 | return 62 | fi 63 | 64 | mkdir -p $WP_CORE_DIR 65 | 66 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then 67 | mkdir -p $TMPDIR/wordpress-nightly 68 | download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip 69 | unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/ 70 | mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR 71 | else 72 | if [ $WP_VERSION == 'latest' ]; then 73 | local ARCHIVE_NAME='latest' 74 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then 75 | # https serves multiple offers, whereas http serves single. 76 | download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json 77 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then 78 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x 79 | LATEST_VERSION=${WP_VERSION%??} 80 | else 81 | # otherwise, scan the releases and get the most up to date minor version of the major release 82 | local VERSION_ESCAPED=$(echo $WP_VERSION | sed 's/\./\\\\./g') 83 | LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1) 84 | fi 85 | if [[ -z "$LATEST_VERSION" ]]; then 86 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 87 | else 88 | local ARCHIVE_NAME="wordpress-$LATEST_VERSION" 89 | fi 90 | else 91 | local ARCHIVE_NAME="wordpress-$WP_VERSION" 92 | fi 93 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz 94 | tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR 95 | fi 96 | 97 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php 98 | } 99 | 100 | install_test_suite() { 101 | # portable in-place argument for both GNU sed and Mac OSX sed 102 | if [[ $(uname -s) == 'Darwin' ]]; then 103 | local ioption='-i.bak' 104 | else 105 | local ioption='-i' 106 | fi 107 | 108 | # set up testing suite if it doesn't yet exist 109 | if [ ! -d $WP_TESTS_DIR ]; then 110 | # set up testing suite 111 | mkdir -p $WP_TESTS_DIR 112 | svn co --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes 113 | svn co --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data 114 | fi 115 | 116 | if [ ! -f wp-tests-config.php ]; then 117 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php 118 | # remove all forward slashes in the end 119 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::") 120 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php 121 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php 122 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php 123 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php 124 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php 125 | fi 126 | 127 | } 128 | 129 | install_db() { 130 | 131 | if [ ${SKIP_DB_CREATE} = "true" ]; then 132 | return 0 133 | fi 134 | 135 | # parse DB_HOST for port or socket references 136 | local PARTS=(${DB_HOST//\:/ }) 137 | local DB_HOSTNAME=${PARTS[0]} 138 | local DB_SOCK_OR_PORT=${PARTS[1]} 139 | local EXTRA="" 140 | 141 | if ! [ -z $DB_HOSTNAME ]; then 142 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then 143 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp" 144 | elif ! [ -z $DB_SOCK_OR_PORT ]; then 145 | EXTRA=" --socket=$DB_SOCK_OR_PORT" 146 | elif ! [ -z $DB_HOSTNAME ]; then 147 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp" 148 | fi 149 | fi 150 | 151 | # create database 152 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA 153 | 154 | # This is created in run_e2e_tests in config.yml. 155 | if [ "$CIRCLE_JOB" == 'e2e-firefox' ] || [ "$CIRCLE_JOB" == 'e2e-chrome' ]; then 156 | # create the e2e test database 157 | mysqladmin create coblocks --user="$DB_USER" --password="$DB_PASS"$EXTRA 158 | fi 159 | } 160 | 161 | install_wp 162 | install_test_suite 163 | install_db 164 | -------------------------------------------------------------------------------- /.dev/scripts/set-phpcs-install-path.sh: -------------------------------------------------------------------------------- 1 | if [[ -f "lib/bin/phpcs" ]]; then 2 | lib/bin/phpcs --config-set installed_paths lib/wp-coding-standards/wpcs 3 | fi 4 | -------------------------------------------------------------------------------- /.dev/scripts/update-changelog.sh: -------------------------------------------------------------------------------- 1 | PLAN="$1" 2 | RELEASE_VERSION="$2" 3 | CHANGELOG=$(cat changelog-tmp.txt) 4 | 5 | # Replacing premium tag prefix. 6 | RELEASE_VERSION="${RELEASE_VERSION/-premium/}" 7 | 8 | CURRENT_DATE=$(date '+%Y/%m/%d') 9 | 10 | FILENAME="" 11 | 12 | # Obtaining changelog file to update. 13 | if [[ $PLAN == 'free' ]]; then 14 | FILENAME="changelog.md" 15 | fi 16 | 17 | if [[ $PLAN == 'pro' ]]; then 18 | FILENAME="changelog-pro.md" 19 | fi 20 | 21 | if [[ ! -f "$FILENAME" ]]; then 22 | echo 'Changelog file not found.' 23 | exit 1 24 | fi 25 | 26 | # Assuming everything went correctly, due to the checks above. 27 | echo "## **$RELEASE_VERSION ($CURRENT_DATE)** \n$CHANGELOG\n" >>"$FILENAME" 28 | -------------------------------------------------------------------------------- /.github/workflows/development-zip.yml: -------------------------------------------------------------------------------- 1 | name: Build development zip 2 | on: push 3 | jobs: 4 | build: 5 | name: Build development zip 6 | runs-on: ubuntu-latest 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | php: [8.0] 11 | node-version: [16.x] 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v1 15 | 16 | - name: Setup PHP 17 | uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: '${{ matrix.php }}' 20 | tools: composer:v1 21 | 22 | - name: Build autoloader 23 | run: composer install --no-dev 24 | 25 | - name: Using Node version ${{ matrix.node-version }} 26 | uses: actions/setup-node@v1 27 | with: 28 | node-version: ${{ matrix.node-version }} 29 | 30 | - name: Installing lerna 31 | run: npm install lerna -g 32 | 33 | - name: bootstrap, npm install, build, and test 34 | run: | 35 | npm install -f 36 | npm run build 37 | lerna clean -y 38 | touch .devbuild 39 | env: 40 | CI: true 41 | INFURA_KEY: a2542fe40386473b9cce6550ebd9e871 42 | - name: Package 43 | uses: actions/upload-artifact@v2 44 | with: 45 | name: cf7-blocks 46 | retention-days: 1 47 | path: | 48 | ${{ github.workspace }}/ 49 | !${{ github.workspace }}/node_modules/ 50 | !${{ github.workspace }}/.github/ 51 | !${{ github.workspace }}/.git/ 52 | !${{ github.workspace }}/.editorconfig 53 | !${{ github.workspace }}/.eslintrc.js 54 | !${{ github.workspace }}/gruntfile.js 55 | !${{ github.workspace }}/.husky 56 | !${{ github.workspace }}/.eslintignore 57 | !${{ github.workspace }}/.gitignore 58 | !${{ github.workspace }}/.dev 59 | !${{ github.workspace }}/.svgrrc 60 | !${{ github.workspace }}/.prettierrc.js 61 | !${{ github.workspace }}/.prettierignore 62 | !${{ github.workspace }}/.phpcs.xml.dist 63 | !${{ github.workspace }}/webpack.config.js 64 | !${{ github.workspace }}/composer.json 65 | !${{ github.workspace }}/composer.lock 66 | !${{ github.workspace }}/package.json 67 | !${{ github.workspace }}/package-lock.json 68 | !${{ github.workspace }}/readme.md 69 | !${{ github.workspace }}/lerna.json 70 | !${{ github.workspace }}/cypress 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual code directory 2 | /.vscode 3 | 4 | # Build directory 5 | /dist 6 | 7 | # Node modules 8 | node_modules 9 | 10 | # Environment 11 | .env 12 | 13 | # PHP Dependencies 14 | /lib 15 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.scss 2 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require( '@wordpress/prettier-config' ); 2 | -------------------------------------------------------------------------------- /.wp-env.json: -------------------------------------------------------------------------------- 1 | { 2 | "core": "WordPress/WordPress", 3 | "plugins": [ 4 | "." 5 | ], 6 | "config": { 7 | "WP_DEBUG": true 8 | } 9 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## CF7 Blocks 4 | 5 | Effortlessly Create Stunning Contact Forms with CF7 Blocks and Contact Form 7. 6 | 7 | ![banner-1544x500 (3)](https://user-images.githubusercontent.com/48084051/208907433-f45dc0e9-2db8-4971-8290-be605ce1e74c.png) 8 | 9 | ## Description 10 | 11 | CF7 Blocks is the ultimate solution for integrating Contact Form 7 with the WordPress block editor. With CF7 Blocks, you can easily create and customize contact forms within the familiar block editor interface. No more fiddling with shortcodes or HTML - just drag and drop blocks to build your forms exactly how you want them. 12 | 13 | CF7 Blocks comes with a variety of pre-designed form templates to choose from, making it even easier to get started. And with full integration with Contact Form 7, all your form submissions will be saved and managed in the same place as before. 14 | 15 | Don't waste any more time trying to fit your forms into the old editor - upgrade to CF7 Blocks and take advantage of the power and flexibility of the block editor today! 16 | 17 | ## Installation 18 | 19 | Make sure you have Contact Form 7 plugin installed to use CF7 Blocks. 20 | 21 | 1. Install Contact Form 7 if it is not already installed. 22 | 2. Install CF7 Blocks either via the WordPress.org plugin repository or by uploading the files to your server. 23 | 3. Activate CF7 Blocks. 24 | 4. Create a new form. 25 | 26 | ## Disable the CF7 Block Editor 27 | 28 | If you specifically want to disable the block editor on certain contact forms. You use the following additional settings: 29 | 30 | Screenshot 2022-12-29 at 5 07 50 PM 31 | 32 | ``` 33 | cf7_blocks_disable: true 34 | ``` 35 | 36 | ## Enable the Native Shortcode Picker 37 | 38 | As the cf7 blocks does not currently supports all the native cf7 fields. Although you can insert the shortcodes in the block editor, You can also enable the native cf7 shortcode generator with the following additional settings: 39 | 40 | Screenshot 2022-12-29 at 5 08 55 PM 41 | 42 | ``` 43 | cf7_blocks_enable_shortcodes_picker: true 44 | ``` 45 | 46 | ## FAQs 47 | 48 | ### How do I create a form with CF7 Blocks? 49 | 50 | To create a new form with CF7 Blocks, follow these steps: 51 | 52 | 1. From the WordPress dashboard, go to Contact > Add New. 53 | 2. You will notice new block editor interface to create forms. 54 | 3. Choose the form template you want to use, and continue from there. 55 | 6. Click the "Publish" button to save your form. 56 | 57 | ### Is CF7 Blocks compatible with the Classic Editor? 58 | 59 | CF7 Blocks is designed to work exclusively with the WordPress block editor. It is not compatible with the Classic Editor. 60 | 61 | ## Get Connected 62 | 63 | [![facebook](https://user-images.githubusercontent.com/48084051/208910904-d71287c7-a1d8-49d3-9056-d49ccf1aafaa.svg)](https://www.facebook.com/munir.kamal) 64 | [![twitter](https://user-images.githubusercontent.com/48084051/208911049-c8495c74-1fa1-4061-8a9a-e99ebce8f748.svg)](https://twitter.com/m_munirkamal) 65 | 66 | ## Subscribe to newsletter 67 | 68 | To get latest updates regarding cf7blocks, Please subscribe to this newsletter: https://cf7blocks.substack.com/ 69 | 70 | ## Support 71 | 72 | [![206910179-5e94fcf7-449f-45de-9197-bcd8129ca373](https://user-images.githubusercontent.com/48084051/208909435-1286912b-3693-4c01-aaa4-d7bdbbb355cd.svg)](https://www.buymeacoffee.com/munirkamal) 73 | -------------------------------------------------------------------------------- /app/Assets.php: -------------------------------------------------------------------------------- 1 | array(), 34 | ); 35 | $version = isset( $asset['version'] ) ? $asset['version'] : time(); 36 | 37 | wp_register_script( 38 | $name, 39 | CF7BLOCKS_PLUGIN_URL . 'dist/' . $js_file, 40 | $asset['dependencies'], 41 | $version, 42 | true 43 | ); 44 | 45 | wp_enqueue_script( $name ); 46 | 47 | wp_register_style( 48 | $name, 49 | CF7BLOCKS_PLUGIN_URL . 'dist/' . $css_file, 50 | array(), 51 | $version 52 | ); 53 | 54 | wp_enqueue_style( $name ); 55 | 56 | return $version; 57 | } 58 | 59 | /** 60 | * Enqueues a single style file. 61 | * 62 | * @param string $name - Style name. 63 | * @param string $css_file_path - Path to css file. 64 | * 65 | * @return void 66 | */ 67 | public static function enqueue_style( $name, $css_file_path ) { 68 | wp_enqueue_style( 69 | $name, 70 | CF7BLOCKS_PLUGIN_URL . 'dist/' . $css_file_path, 71 | array(), 72 | 'initial' 73 | ); 74 | } 75 | } 76 | 77 | new Assets(); 78 | -------------------------------------------------------------------------------- /app/Shortcode.php: -------------------------------------------------------------------------------- 1 | initial() ? true : false; 40 | return $properties; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/core/Builder.php: -------------------------------------------------------------------------------- 1 | get_current_form_id(); 50 | $is_nonce_verified = wp_verify_nonce( $nonce, 'cf7blocks-edit-' . $form_id ); 51 | 52 | if ( ! $is_nonce_verified ) { 53 | return false; 54 | } 55 | 56 | return true; 57 | } 58 | 59 | /** 60 | * Checks if the current contact form (being edited) is not yet saved to the database. 61 | * 62 | * @return bool - True if initial, otherwise false. 63 | */ 64 | public function is_initial() { 65 | // phpcs:ignore 66 | return isset( $_GET['initial'] ) ? \sanitize_text_field( \wp_unslash( $_GET['initial'] ) ) : false; 67 | } 68 | 69 | /** 70 | * Registers a hidden menu page for the builder. 71 | * 72 | * @return void 73 | */ 74 | public function register_hidden_page() { 75 | add_submenu_page( 76 | null, 77 | __( 'Gutenberg Editor', 'cf7-blocks' ), 78 | __( 'CF7 Block Editor', 'cf7-blocks' ), 79 | 'wpcf7_edit_contact_form', 80 | 'cf7blocks-editor', 81 | function() { 82 | 83 | if ( ! $this->can_edit() ) { 84 | echo esc_html__( 'Sorry, You\'re not allowed to access this page.', 'cf7-blocks' ); 85 | return; 86 | } 87 | 88 | $form_id = $this->get_current_form_id(); 89 | 90 | $handler = new \CakeWP\CF7Blocks\Handlers\ContactForm7(); 91 | 92 | $handler->load_gutenberg_editor( '#cf7-blocks-root-text-area' ); 93 | 94 | $form_content = is_null( $form_id ) ? '' : \WPCF7_ContactForm::get_instance( $form_id )->prop( 'form' ); 95 | 96 | ?> 97 |
98 | 101 |
102 | can_upload = isset( $settings['editor']['hasUploadPermissions'] ) && $settings['editor']['hasUploadPermissions']; 61 | $this->load_extra_blocks(); 62 | 63 | // Restrict tinymce buttons. 64 | add_filter( 'tiny_mce_before_init', array( $this, 'tiny_mce_before_init' ) ); 65 | 66 | // Gutenberg scripts. 67 | wp_enqueue_script( 'wp-block-library' ); 68 | wp_enqueue_script( 'wp-format-library' ); 69 | wp_enqueue_script( 'wp-editor' ); 70 | wp_enqueue_script( 'wp-plugins' ); 71 | 72 | // Gutenberg styles. 73 | wp_enqueue_style( 'wp-edit-post' ); 74 | wp_enqueue_style( 'wp-format-library' ); 75 | 76 | // Keep Jetpack out of things. 77 | 78 | wp_tinymce_inline_scripts(); 79 | wp_enqueue_editor(); 80 | 81 | do_action( 'enqueue_block_editor_assets' ); 82 | 83 | add_action( 'wp_print_footer_scripts', array( '_WP_Editors', 'print_default_editor_scripts' ), 45 ); 84 | 85 | $this->setup_rest_api(); 86 | 87 | set_current_screen( 'front' ); 88 | wp_styles()->done = array( 'wp-reset-editor-styles' ); 89 | 90 | $categories = wp_json_encode( get_block_categories( $post ) ); 91 | 92 | if ( false !== $categories ) { 93 | wp_add_inline_script( 94 | 'wp-blocks', 95 | sprintf( 'wp.blocks.setCategories( %s );', $categories ), 96 | 'after' 97 | ); 98 | } 99 | 100 | wp_add_inline_script( 101 | 'wp-blocks', 102 | 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');' 103 | ); 104 | 105 | $this->setup_media(); 106 | } 107 | 108 | /** 109 | * Load any third-party blocks 110 | * 111 | * @return void 112 | */ 113 | private function load_extra_blocks() { 114 | // phpcs:ignore 115 | $GLOBALS['hook_suffix'] = ''; 116 | 117 | require_once ABSPATH . 'wp-admin/includes/class-wp-screen.php'; 118 | 119 | require_once ABSPATH . 'wp-admin/includes/screen.php'; 120 | 121 | require_once ABSPATH . 'wp-admin/includes/post.php'; 122 | 123 | set_current_screen(); 124 | 125 | $current_screen = get_current_screen(); 126 | if ( $current_screen ) { 127 | $current_screen->is_block_editor( true ); 128 | } 129 | } 130 | 131 | /** 132 | * Override some features that probably don't make sense in an isolated editor 133 | * 134 | * @param array $settings Settings array. 135 | * @return array 136 | */ 137 | public function block_editor_settings_all( array $settings ) { 138 | $settings['availableLegacyWidgets'] = (object) array(); 139 | $settings['hasPermissionsToManageWidgets'] = false; 140 | 141 | return $settings; 142 | } 143 | 144 | /** 145 | * Set up Gutenberg editor settings 146 | * 147 | * @return Array 148 | */ 149 | public function get_editor_settings() { 150 | global $post; 151 | 152 | // phpcs:ignore 153 | $editor_settings = array( 154 | 'availableTemplates' => array(), 155 | 'disablePostFormats' => ! current_theme_supports( 'post-formats' ), 156 | /** This filter is documented in wp-admin/edit-form-advanced.php */ 157 | // phpcs:ignore 158 | 'titlePlaceholder' => __( 'Add title', 'cf7-blocks' ), 159 | 'bodyPlaceholder' => __( 'Create your form', 'cf7-blocks' ), 160 | 'autosaveInterval' => AUTOSAVE_INTERVAL, 161 | 'styles' => function_exists( 'get_block_editor_theme_styles' ) ? get_block_editor_theme_styles() : array(), 162 | 'richEditingEnabled' => user_can_richedit(), 163 | 'postLock' => false, 164 | 'supportsLayout' => false, 165 | '__experimentalBlockPatterns' => array(), 166 | '__experimentalBlockPatternCategories' => array(), 167 | 'supportsTemplateMode' => current_theme_supports( 'block-templates' ), 168 | 'enableCustomFields' => false, 169 | 'generateAnchors' => true, 170 | 'canLockBlocks' => false, 171 | ); 172 | 173 | $block_editor_context = new \WP_Block_Editor_Context( array( 'post' => $post ) ); 174 | return get_block_editor_settings( $editor_settings, $block_editor_context ); 175 | } 176 | 177 | /** 178 | * Set up the Gutenberg REST API and preloaded data 179 | * 180 | * @return void 181 | */ 182 | public function setup_rest_api() { 183 | global $post; 184 | 185 | $post_type = 'post'; 186 | 187 | // Preload common data. 188 | $preload_paths = array( 189 | '/', 190 | '/wp/v2/types?context=edit', 191 | '/wp/v2/taxonomies?per_page=-1&context=edit', 192 | '/wp/v2/themes?status=active', 193 | sprintf( '/wp/v2/types/%s?context=edit', $post_type ), 194 | sprintf( '/wp/v2/users/me?post_type=%s&context=edit', $post_type ), 195 | array( '/wp/v2/media', 'OPTIONS' ), 196 | array( '/wp/v2/blocks', 'OPTIONS' ), 197 | ); 198 | 199 | /** 200 | * @psalm-suppress TooManyArguments 201 | */ 202 | $preload_paths = apply_filters( 'block_editor_preload_paths', $preload_paths, $post ); 203 | $preload_data = array_reduce( $preload_paths, 'rest_preload_api_request', array() ); 204 | 205 | $encoded = wp_json_encode( $preload_data ); 206 | if ( false !== $encoded ) { 207 | wp_add_inline_script( 208 | 'wp-editor', 209 | sprintf( 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', $encoded ), 210 | 'after' 211 | ); 212 | } 213 | } 214 | 215 | /** 216 | * Ensure media works in Gutenberg 217 | * 218 | * @return void 219 | */ 220 | public function setup_media() { 221 | if ( ! $this->can_upload ) { 222 | return; 223 | } 224 | 225 | // If we've already loaded the media stuff then don't do it again. 226 | if ( did_action( 'wp_enqueue_media' ) > 0 ) { 227 | return; 228 | } 229 | 230 | require_once ABSPATH . 'wp-admin/includes/media.php'; 231 | 232 | wp_enqueue_media(); 233 | } 234 | 235 | /** 236 | * Undocumented. 237 | * 238 | * TODO: Add some documentation. 239 | * 240 | * @return void 241 | */ 242 | public function wp_add_iframed_editor_assets_html() { 243 | $script_handles = array(); 244 | $style_handles = array( 245 | 'wp-block-editor', 246 | 'wp-block-library', 247 | 'wp-block-library-theme', 248 | 'wp-edit-blocks', 249 | ); 250 | 251 | $block_registry = \WP_Block_Type_Registry::get_instance(); 252 | 253 | foreach ( $block_registry->get_all_registered() as $block_type ) { 254 | if ( ! empty( $block_type->style ) ) { 255 | $style_handles[] = $block_type->style; 256 | } 257 | 258 | if ( ! empty( $block_type->editor_style ) ) { 259 | $style_handles[] = $block_type->editor_style; 260 | } 261 | 262 | if ( ! empty( $block_type->script ) ) { 263 | $script_handles[] = $block_type->script; 264 | } 265 | } 266 | 267 | $style_handles = apply_filters( 'blocks_everywhere_editor_styles', $style_handles ); 268 | 269 | // Make sure there are only strings in this array. 270 | $style_handles = array_filter( 271 | $style_handles, 272 | function( $handle ) { 273 | return is_string( $handle ); 274 | } 275 | ); 276 | $style_handles = array_unique( $style_handles ); 277 | $done = wp_styles()->done; 278 | 279 | ob_start(); 280 | 281 | // We do not need reset styles for the iframed editor. 282 | wp_styles()->done = array( 'wp-reset-editor-styles' ); 283 | wp_styles()->do_items( $style_handles ); 284 | wp_styles()->done = $done; 285 | 286 | $styles = ob_get_clean(); 287 | 288 | $script_handles = array_unique( apply_filters( 'blocks_everywhere_editor_scripts', $script_handles ) ); 289 | $done = wp_scripts()->done; 290 | 291 | ob_start(); 292 | 293 | wp_scripts()->done = array(); 294 | wp_scripts()->do_items( $script_handles ); 295 | wp_scripts()->done = $done; 296 | 297 | $scripts = ob_get_clean(); 298 | 299 | $editor_assets = wp_json_encode( 300 | array( 301 | 'styles' => $styles, 302 | 'scripts' => $scripts, 303 | ) 304 | ); 305 | 306 | // phpcs:ignore 307 | echo ""; 308 | } 309 | } 310 | 311 | new Editor(); 312 | -------------------------------------------------------------------------------- /app/core/Handler.php: -------------------------------------------------------------------------------- 1 | editor = new \CakeWP\CF7Blocks\Core\Editor(); 42 | 43 | // Settings for the editor. 44 | $default_settings = array( 45 | 'editor' => $this->editor->get_editor_settings(), 46 | 'iso' => array( 47 | 'blocks' => array( 48 | 'allowBlocks' => array( 49 | 'core/paragraph', 50 | 'core/heading', 51 | 'core/separator', 52 | 'core/spacer', 53 | 'core/columns', 54 | 'core/column', 55 | 'core/quote', 56 | 'core/code', 57 | 'core/shortcode', 58 | 'core/group', 59 | 'core/list', 60 | 'core/list-item', 61 | 'core/html', 62 | 'cf7-blocks/template', 63 | 'cf7-blocks/input-base', 64 | 'cf7-blocks/selection-base', 65 | 'cf7-blocks/submit', 66 | 'cf7-blocks/acceptance', 67 | ), 68 | ), 69 | 'moreMenu' => array( 70 | 'topToolbar' => true, 71 | ), 72 | 'sidebar' => array( 73 | 'inserter' => true, 74 | 'inspector' => true, 75 | ), 76 | 'toolbar' => array( 77 | 'navigation' => true, 78 | 'inspector' => true, 79 | ), 80 | 'allowEmbeds' => array(), 81 | ), 82 | 'saveTextarea' => $textarea, 83 | 'container' => $container, 84 | 'editorType' => 'core', 85 | 'allowUrlEmbed' => false, 86 | 'pastePlainText' => false, 87 | 'replaceParagraphCode' => false, 88 | 'pluginsUrl' => plugins_url( '', __DIR__ ), 89 | 'version' => '1.0.0', 90 | ); 91 | 92 | $settings = $default_settings; 93 | 94 | $this->editor->load( $settings ); 95 | $this->settings = $settings; 96 | 97 | \CakeWP\CF7Blocks\Assets::enqueue( 98 | 'contact-form-7-blocks-gutenberg', 99 | 'gutenberg.asset.php', 100 | 'gutenberg.js', 101 | 'gutenberg-styling.css' 102 | ); 103 | 104 | \CakeWP\CF7Blocks\Assets::enqueue( 105 | 'contact-form-7-blocks-blocks-library', 106 | 'blocks-library.asset.php', 107 | 'blocks-library.js', 108 | 'blocks-library-editor.css' 109 | ); 110 | 111 | \CakeWP\CF7Blocks\Assets::enqueue_style( 112 | 'cf7-blocks-editor-style', 113 | 'style-gutenberg.css' 114 | ); 115 | 116 | \wp_localize_script( 117 | 'contact-form-7-blocks-gutenberg', 118 | 'cf7BlockEditors', 119 | array( 120 | $textarea, 121 | ) 122 | ); 123 | 124 | \wp_localize_script( 125 | 'contact-form-7-blocks-gutenberg', 126 | 'cf7BlockEditorSettings', 127 | $this->settings 128 | ); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /app/handlers/ContactForm7.php: -------------------------------------------------------------------------------- 1 | __( 'Form', 'cf7-blocks' ), 39 | 'callback' => function( $contact_form ) use ( $panels ) { 40 | 41 | $default_callback = $panels['form-panel']['callback']; 42 | $_is_using_cf7_block_editor = $contact_form->prop( '_is_using_cf7blocks-block-editor' ); 43 | 44 | $should_render_editor = false !== $_is_using_cf7_block_editor && ! empty( $_is_using_cf7_block_editor ) && 'true' !== $contact_form->pref( 'cf7_blocks_disable' ); 45 | 46 | if ( is_int( $contact_form->id() ) && ! $should_render_editor ) { 47 | $default_callback( $contact_form ); 48 | return; 49 | } 50 | 51 | $editor_page = \add_query_arg( 52 | array( 53 | 'id' => $contact_form->id(), 54 | 'initial' => $contact_form->initial(), 55 | 'nonce' => wp_create_nonce( 'cf7blocks-edit-' . $contact_form->id() ), 56 | ), 57 | admin_url( '/admin.php?page=cf7blocks-editor' ) 58 | ); 59 | 60 | if ( 'true' === $contact_form->pref( 'cf7_blocks_enable_shortcodes_picker' ) ) { 61 | $this->tags_manager( $default_callback, $contact_form ); 62 | } 63 | 64 | ?> 65 |
66 | 70 | 73 |
74 | loadHTML( $content ); 108 | 109 | $element = $doc->getElementById( 'tag-generator-list' ); 110 | 111 | $tag_generator = $doc->saveHTML( $element ); 112 | 113 | // Clear the libxml error buffer. 114 | libxml_clear_errors(); 115 | 116 | // Re-enable the display of libxml errors. 117 | libxml_use_internal_errors( false ); 118 | 119 | echo $tag_generator; 120 | 121 | echo '
'; 122 | echo '
'; 123 | } 124 | 125 | /** 126 | * Loads the gutenberg editor. 127 | * 128 | * @param string $selector - Selector. 129 | * 130 | * @return void 131 | */ 132 | public function load_gutenberg_editor( $selector ) { 133 | $this->load_editor( $selector ); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /app/notices/InstallContactFormNotice.php: -------------------------------------------------------------------------------- 1 | is_cf7_installed() ) { 32 | return; 33 | } 34 | 35 | $installation_link = add_query_arg( 36 | array( 37 | 's' => 'Contact form 7', 38 | 'tab' => 'search', 39 | 'type' => 'term', 40 | ), 41 | admin_url( 'plugin-install.php' ) 42 | ); 43 | 44 | ?> 45 |
46 |

47 | 53 |

54 | Install 55 |
56 | { 33 | // Generating a new field name, if needed. 34 | if ( 35 | isEmpty( autogeneratedName ) || 36 | ! autogeneratedName.startsWith( 'acceptance' ) 37 | ) { 38 | const newAutogeneratedName = generateFieldName( 'acceptance' ); 39 | props.setAttributes( { autogeneratedName: newAutogeneratedName } ); 40 | } 41 | }, [] ); 42 | 43 | return ( 44 | <> 45 |
46 |
47 |
48 | 55 | props.setAttributes( { checked: ! checked } ) 56 | } 57 | /> 58 | 62 | props.setAttributes( { 63 | condition: newCondition, 64 | } ) 65 | } 66 | /> 67 |
68 |
69 |
70 | 71 | 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /packages/blocks-library/src/acceptance/editor.scss: -------------------------------------------------------------------------------- 1 | .cf7-block-acceptance-wrapper { 2 | 3 | .cf7blocks-acceptance { 4 | display: inline-block; 5 | margin: 0 0 0 1em; 6 | 7 | .cf7blocks-acceptance-inner_wrapper { 8 | display: flex; 9 | align-items: center; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/blocks-library/src/acceptance/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from '@wordpress/blocks'; 2 | import metadata from './block.json'; 3 | 4 | import { check } from '@wordpress/icons'; 5 | 6 | import edit from './edit'; 7 | import save from './save'; 8 | 9 | registerBlockType( metadata, { 10 | icon: check, 11 | apiVersion: 2, 12 | edit, 13 | save, 14 | } ); 15 | -------------------------------------------------------------------------------- /packages/blocks-library/src/acceptance/inspector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __, sprintf } from '@wordpress/i18n'; 5 | import { PanelBody, TextControl, ToggleControl } from '@wordpress/components'; 6 | import { 7 | InspectorControls, 8 | InspectorAdvancedControls, 9 | } from '@wordpress/block-editor'; 10 | 11 | /** 12 | * Custom Dependencies 13 | */ 14 | 15 | import { convertAcceptanceBlockToCF7Shortcode } from '../utils/convert-to-cf7-shortcode'; 16 | 17 | function Inspector( props ) { 18 | const { id, name, isOptional, autogeneratedName } = props.attributes; 19 | 20 | return ( 21 | <> 22 | 23 | 24 | 29 | 30 | 31 | { 40 | const normalizedName = newName 41 | .toLowerCase() 42 | .replace( ' ', '-' ); 43 | 44 | props.setAttributes( { name: normalizedName } ); 45 | } } 46 | /> 47 | 48 | 51 | props.setAttributes( { 52 | isOptional: newOptional, 53 | } ) 54 | } 55 | label={ __( 'Optional', 'cf7-blocks' ) } 56 | help={ __( 57 | 'Make this checkbox optional.', 58 | 'cf7-blocks' 59 | ) } 60 | /> 61 | 62 | 63 | 64 | { 69 | const idWithoutSpace = newId.replace( ' ', '' ); 70 | props.setAttributes( { id: idWithoutSpace } ); 71 | } } 72 | /> 73 | 74 | 75 | ); 76 | } 77 | 78 | export default Inspector; 79 | -------------------------------------------------------------------------------- /packages/blocks-library/src/acceptance/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { RawHTML } from '@wordpress/element'; 5 | import { useBlockProps } from '@wordpress/block-editor'; 6 | 7 | /** 8 | * Custom Dependencies 9 | */ 10 | import { convertAcceptanceBlockToCF7Shortcode } from '../utils/convert-to-cf7-shortcode'; 11 | import { WithLabel } from '../components'; 12 | 13 | export default function save( props ) { 14 | const shortcode = convertAcceptanceBlockToCF7Shortcode( props.attributes ); 15 | 16 | return ( 17 |
18 | 22 | { shortcode } 23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /packages/blocks-library/src/components/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * All components should be exported from here. 3 | */ 4 | export { default as WithLabel } from './with-label'; 5 | -------------------------------------------------------------------------------- /packages/blocks-library/src/components/with-label/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom Dependencies 3 | */ 4 | import PropTypes from 'prop-types'; 5 | 6 | import { RawHTML } from '@wordpress/element'; 7 | 8 | function WithLabel( props ) { 9 | if ( ! props.showLabel ) { 10 | return props.children; 11 | } 12 | 13 | return ( 14 | 18 | ); 19 | } 20 | 21 | WithLabel.propTypes = { 22 | /** Should display the provided children with label. */ 23 | showLabel: PropTypes.bool.isRequired, 24 | 25 | /** Label */ 26 | label: PropTypes.string.isRequired, 27 | 28 | /** Child elements */ 29 | children: PropTypes.node.isRequired, 30 | }; 31 | 32 | export default WithLabel; 33 | -------------------------------------------------------------------------------- /packages/blocks-library/src/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Input types that supports the range, along with the type to handle their values. 3 | */ 4 | export const CF7BLOCKS_RANGE_SUPPORTED_INPUT_TYPES = { 5 | number: 'number', 6 | date: 'date', 7 | range: 'number', 8 | }; 9 | 10 | export const CF7BLOCKS_PLACEHOLDER_SUPPORTED_TYPES = [ 11 | 'text', 12 | 'email', 13 | 'url', 14 | 'tel', 15 | 'number', 16 | 'date', 17 | 'textarea', 18 | ]; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/editor.scss: -------------------------------------------------------------------------------- 1 | /** Custom Styles **/ 2 | @import "./selection-base/editor.scss"; 3 | @import "./form-template/editor.scss"; 4 | @import "./acceptance/editor.scss"; 5 | @import "./input-base/editor.scss"; 6 | 7 | .cf7-block-field { 8 | width: 100%; 9 | display: flex; 10 | flex-direction: column; 11 | 12 | & > input, 13 | & > textarea { 14 | width: 100%; 15 | padding: 5px; 16 | } 17 | 18 | textarea { 19 | min-height: 300px; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/blocks-library/src/form-template/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/block.json", 3 | "apiVersion": 2, 4 | "name": "cf7-blocks/template", 5 | "title": "Form", 6 | "category": "text", 7 | "description": "Quickly start creating forms with a pre-made form template", 8 | "keywords": [ 9 | "input" 10 | ], 11 | "version": "1.0.0", 12 | "textdomain": "cf7-blocks", 13 | "attributes": {}, 14 | "supports": { 15 | "inserter": false, 16 | "__experimentalToolbar": false 17 | }, 18 | "styles": [] 19 | } -------------------------------------------------------------------------------- /packages/blocks-library/src/form-template/edit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom Dependencies 3 | */ 4 | import Placeholder from './placeholder'; 5 | 6 | function Edit() { 7 | return ; 8 | } 9 | 10 | export default Edit; 11 | -------------------------------------------------------------------------------- /packages/blocks-library/src/form-template/editor.scss: -------------------------------------------------------------------------------- 1 | .cf7blocks-form-template-block { 2 | 3 | .cf7blocks-form-template__grid { 4 | display: grid; 5 | grid-gap: 10px; 6 | width: 100%; 7 | grid-template-columns: 1fr 1fr; 8 | 9 | & > div { 10 | width: 100%; 11 | 12 | .cf7blocks-form-template { 13 | padding: 30px; 14 | gap: 10px; 15 | width: 100%; 16 | } 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /packages/blocks-library/src/form-template/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from '@wordpress/blocks'; 2 | 3 | import metadata from './block.json'; 4 | import edit from './edit'; 5 | 6 | import { cf7blocks } from '../icons'; 7 | 8 | registerBlockType( metadata, { 9 | apiVersion: 2, 10 | icon: cf7blocks, 11 | edit, 12 | save: () => null, 13 | } ); 14 | -------------------------------------------------------------------------------- /packages/blocks-library/src/form-template/placeholder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __, sprintf } from '@wordpress/i18n'; 5 | import { useDispatch } from '@wordpress/data'; 6 | import { 7 | BlockIcon, 8 | useBlockProps, 9 | useBlockEditContext, 10 | } from '@wordpress/block-editor'; 11 | import { Placeholder, Button, Icon } from '@wordpress/components'; 12 | 13 | /** 14 | * Custom Dependencies 15 | */ 16 | import templates from './templates'; 17 | 18 | function FormPlaceholder() { 19 | const { clientId } = useBlockEditContext(); 20 | 21 | const blockProps = useBlockProps( { 22 | className: 'cf7blocks-form-template-block', 23 | } ); 24 | 25 | const { insertBlocks, removeBlock } = useDispatch( 'core/block-editor' ); 26 | const { createSuccessNotice } = useDispatch( 'core/notices' ); 27 | 28 | const handleInsertion = ( template ) => { 29 | const blocks = wp.blocks.createBlocksFromInnerBlocksTemplate( 30 | template.template 31 | ); 32 | 33 | insertBlocks( blocks ); 34 | removeBlock( clientId ); 35 | 36 | createSuccessNotice( 37 | sprintf( 38 | __( 'Form template "%s" inserted successfully.' ), 39 | template.label 40 | ), 41 | { 42 | type: 'snackbar', 43 | } 44 | ); 45 | }; 46 | 47 | return ( 48 |
49 | 56 |
57 | { templates.map( ( template ) => { 58 | return ( 59 |
60 | 72 |
73 | ); 74 | } ) } 75 |
76 |
77 |
78 | ); 79 | } 80 | 81 | export default FormPlaceholder; 82 | -------------------------------------------------------------------------------- /packages/blocks-library/src/form-template/templates.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { addCard } from '@wordpress/icons'; 6 | 7 | /** 8 | * Custom Dependencies 9 | */ 10 | import { calender, bell, addUser, feedback, appointment } from '../icons'; 11 | 12 | function makeColumns( blocks ) { 13 | const columns = []; 14 | 15 | blocks.forEach( ( block ) => { 16 | columns.push( [ 'core/column', {}, [ block ] ] ); 17 | } ); 18 | 19 | return [ 'core/columns', {}, columns ]; 20 | } 21 | 22 | const templates = [ 23 | { 24 | icon: addCard, 25 | label: __( 'Basic Contact Form', 'cf7-blocks' ), 26 | help: __( 'Create a basic contact form on your page.', 'cf7-blocks' ), 27 | template: [ 28 | makeColumns( [ 29 | [ 30 | 'cf7-blocks/input-base', 31 | { 32 | type: 'text', 33 | id: 'name', 34 | initialValue: 'Enter your name', 35 | isRequired: true, 36 | label: 'Name', 37 | name: 'name', 38 | requiredText: '*', 39 | showLabel: true, 40 | tagName: 'input', 41 | useDefaultValueAsPlaceholder: true, 42 | }, 43 | [], 44 | ], 45 | [ 46 | 'cf7-blocks/input-base', 47 | { 48 | type: 'email', 49 | id: 'email', 50 | initialValue: 'Enter your email', 51 | isRequired: true, 52 | label: 'Email', 53 | name: 'email', 54 | requiredText: '*', 55 | showLabel: true, 56 | tagName: 'input', 57 | useDefaultValueAsPlaceholder: true, 58 | }, 59 | [], 60 | ], 61 | ] ), 62 | [ 63 | 'cf7-blocks/input-base', 64 | { 65 | type: 'textarea', 66 | id: 'message', 67 | initialValue: 'Enter your message (optional)', 68 | isRequired: false, 69 | label: 'Message', 70 | name: 'message', 71 | showLabel: true, 72 | tagName: 'textarea', 73 | useDefaultValueAsPlaceholder: true, 74 | }, 75 | [], 76 | ], 77 | [ 'cf7-blocks/submit', {}, [] ], 78 | ], 79 | }, 80 | { 81 | icon: bell, 82 | label: __( 'Newsletter Form', 'cf7-blocks' ), 83 | help: __( 'Creates a basic newsletter form.', 'cf7-blocks' ), 84 | template: [ 85 | makeColumns( [ 86 | [ 87 | 'cf7-blocks/input-base', 88 | { 89 | type: 'text', 90 | id: 'name', 91 | initialValue: 'Enter your name', 92 | isRequired: true, 93 | label: 'Name', 94 | name: 'your-name', 95 | requiredText: '*', 96 | showLabel: true, 97 | tagName: 'input', 98 | useDefaultValueAsPlaceholder: true, 99 | }, 100 | [], 101 | ], 102 | [ 103 | 'cf7-blocks/input-base', 104 | { 105 | type: 'email', 106 | id: 'email', 107 | initialValue: 'Enter your email', 108 | isRequired: true, 109 | label: 'Email', 110 | name: 'your-email', 111 | requiredText: '*', 112 | showLabel: true, 113 | tagName: 'input', 114 | useDefaultValueAsPlaceholder: true, 115 | }, 116 | [], 117 | ], 118 | ] ), 119 | 120 | [ 121 | 'core/paragraph', 122 | { 123 | content: __( 124 | 'You will recieve email updates by subscribing to this newsletter.', 125 | 'cf7-blocks' 126 | ), 127 | }, 128 | ], 129 | [ 'cf7-blocks/submit', {}, [] ], 130 | ], 131 | }, 132 | { 133 | icon: calender, 134 | label: __( 'RSVP', 'cf7-blocks' ), 135 | help: __( 136 | 'Create a basic RSVP form on your page for invitation purpose', 137 | 'cf7-blocks' 138 | ), 139 | template: [ 140 | makeColumns( [ 141 | [ 142 | 'cf7-blocks/input-base', 143 | { 144 | type: 'text', 145 | initialValue: 'Enter your name', 146 | isRequired: true, 147 | label: 'Name', 148 | name: 'your-name', 149 | requiredText: '*', 150 | showLabel: true, 151 | tagName: 'input', 152 | useDefaultValueAsPlaceholder: true, 153 | }, 154 | [], 155 | ], 156 | [ 157 | 'cf7-blocks/input-base', 158 | { 159 | type: 'email', 160 | initialValue: 'Enter your email', 161 | isRequired: true, 162 | label: 'Email', 163 | name: 'your-email', 164 | requiredText: '*', 165 | showLabel: true, 166 | tagName: 'input', 167 | useDefaultValueAsPlaceholder: true, 168 | }, 169 | [], 170 | ], 171 | ] ), 172 | 173 | [ 174 | 'cf7-blocks/selection-base', 175 | { 176 | type: 'radio', 177 | mainLabel: 'Are you coming?', 178 | isRequired: true, 179 | showLabel: true, 180 | name: 'are-you-coming', 181 | requiredText: '*', 182 | items: [ 183 | { 184 | label: 'Yes', 185 | checked: true, 186 | }, 187 | { 188 | label: 'No', 189 | checked: false, 190 | }, 191 | ], 192 | }, 193 | [], 194 | ], 195 | [ 196 | 'cf7-blocks/input-base', 197 | { 198 | type: 'textarea', 199 | initialValue: 'Add some details (optional)', 200 | isRequired: false, 201 | label: 'Other Details', 202 | name: 'other-details', 203 | showLabel: true, 204 | tagName: 'textarea', 205 | useDefaultValueAsPlaceholder: true, 206 | }, 207 | [], 208 | ], 209 | [ 'cf7-blocks/submit', {}, [] ], 210 | ], 211 | }, 212 | { 213 | icon: appointment, 214 | label: __( 'Appointment Form', 'cf7-blocks' ), 215 | help: __( 216 | 'Create a basic appointment form on your page', 217 | 'cf7-blocks' 218 | ), 219 | template: [ 220 | makeColumns( [ 221 | [ 222 | 'cf7-blocks/input-base', 223 | { 224 | type: 'text', 225 | initialValue: 'Enter your name', 226 | isRequired: true, 227 | label: 'Name', 228 | name: 'appointment-name', 229 | requiredText: '*', 230 | showLabel: true, 231 | tagName: 'input', 232 | useDefaultValueAsPlaceholder: true, 233 | }, 234 | [], 235 | ], 236 | [ 237 | 'cf7-blocks/input-base', 238 | { 239 | type: 'email', 240 | initialValue: 'Enter your email', 241 | isRequired: true, 242 | label: 'Email', 243 | name: 'appointment-email', 244 | requiredText: '*', 245 | showLabel: true, 246 | tagName: 'input', 247 | useDefaultValueAsPlaceholder: true, 248 | }, 249 | [], 250 | ], 251 | ] ), 252 | 253 | [ 254 | 'cf7-blocks/input-base', 255 | { 256 | type: 'tel', 257 | initialValue: 'Enter your number', 258 | isRequired: true, 259 | label: 'Phone', 260 | name: 'appointment-phone', 261 | requiredText: '*', 262 | showLabel: true, 263 | tagName: 'input', 264 | useDefaultValueAsPlaceholder: true, 265 | }, 266 | [], 267 | ], 268 | [ 269 | 'cf7-blocks/input-base', 270 | { 271 | type: 'date', 272 | initialValue: 'Enter appointment date', 273 | isRequired: true, 274 | label: 'Date', 275 | name: 'appointment-date', 276 | requiredText: '*', 277 | showLabel: true, 278 | tagName: 'input', 279 | useDefaultValueAsPlaceholder: true, 280 | }, 281 | [], 282 | ], 283 | [ 284 | 'cf7-blocks/input-base', 285 | { 286 | type: 'textarea', 287 | initialValue: 'Add any additional notes (optional)', 288 | isRequired: false, 289 | label: 'Additional Notes', 290 | name: 'appointment-notes', 291 | showLabel: true, 292 | tagName: 'textarea', 293 | useDefaultValueAsPlaceholder: true, 294 | }, 295 | [], 296 | ], 297 | [ 'cf7-blocks/submit', {}, [] ], 298 | ], 299 | }, 300 | { 301 | icon: feedback, 302 | label: __( 'Feedback Form', 'cf7-blocks' ), 303 | help: __( 304 | 'Create a basic contact form on your page which is helpful to collect user feedback.', 305 | 'cf7-blocks' 306 | ), 307 | template: [ 308 | makeColumns( [ 309 | [ 310 | 'cf7-blocks/input-base', 311 | { 312 | type: 'text', 313 | initialValue: 'Enter your name', 314 | isRequired: true, 315 | label: 'Name', 316 | name: 'user-name', 317 | requiredText: '*', 318 | showLabel: true, 319 | tagName: 'input', 320 | useDefaultValueAsPlaceholder: true, 321 | }, 322 | [], 323 | ], 324 | [ 325 | 'cf7-blocks/input-base', 326 | { 327 | type: 'email', 328 | initialValue: 'Enter your email', 329 | isRequired: true, 330 | label: 'Email', 331 | name: 'user-email', 332 | requiredText: '*', 333 | showLabel: true, 334 | tagName: 'input', 335 | useDefaultValueAsPlaceholder: true, 336 | }, 337 | [], 338 | ], 339 | ] ), 340 | 341 | [ 342 | 'cf7-blocks/selection-base', 343 | { 344 | type: 'radio', 345 | mainLabel: 'Please rate our website?', 346 | isRequired: true, 347 | showLabel: true, 348 | name: 'user-rating', 349 | requiredText: '*', 350 | items: [ 351 | { 352 | label: '1 - Very Bad', 353 | checked: false, 354 | }, 355 | { 356 | label: '2 - Poor', 357 | checked: false, 358 | }, 359 | { 360 | label: '3 - Average', 361 | checked: false, 362 | }, 363 | { 364 | label: '4 - Good', 365 | checked: false, 366 | }, 367 | { 368 | label: '5 - Excellent', 369 | checked: false, 370 | }, 371 | ], 372 | }, 373 | [], 374 | ], 375 | [ 376 | 'cf7-blocks/input-base', 377 | { 378 | type: 'textarea', 379 | initialValue: 'Add your suggestion (optional)', 380 | isRequired: false, 381 | label: 'Any Suggestion?', 382 | name: 'user-suggestion', 383 | showLabel: true, 384 | tagName: 'textarea', 385 | useDefaultValueAsPlaceholder: true, 386 | }, 387 | [], 388 | ], 389 | [ 'cf7-blocks/submit', {}, [] ], 390 | ], 391 | }, 392 | { 393 | icon: addUser, 394 | label: __( 'Registeration Form', 'cf7-blocks' ), 395 | help: __( 396 | 'Create a basic registeration form on your page', 397 | 'cf7-blocks' 398 | ), 399 | template: [ 400 | makeColumns( [ 401 | [ 402 | 'cf7-blocks/input-base', 403 | { 404 | type: 'text', 405 | initialValue: 'Enter your name', 406 | isRequired: true, 407 | label: 'Name', 408 | name: 'user-name', 409 | requiredText: '*', 410 | showLabel: true, 411 | tagName: 'input', 412 | useDefaultValueAsPlaceholder: true, 413 | }, 414 | [], 415 | ], 416 | [ 417 | 'cf7-blocks/input-base', 418 | { 419 | type: 'email', 420 | initialValue: 'Enter your email', 421 | isRequired: true, 422 | label: 'Email', 423 | name: 'user-email', 424 | requiredText: '*', 425 | showLabel: true, 426 | tagName: 'input', 427 | useDefaultValueAsPlaceholder: true, 428 | }, 429 | [], 430 | ], 431 | ] ), 432 | 433 | [ 434 | 'cf7-blocks/selection-base', 435 | { 436 | type: 'select', 437 | mainLabel: 'How did you hear about us?', 438 | isRequired: true, 439 | showLabel: true, 440 | name: 'user-awareness-reason', 441 | requiredText: '*', 442 | items: [ 443 | { 444 | label: 'Search Engine', 445 | checked: false, 446 | }, 447 | { 448 | label: 'Social Media', 449 | checked: false, 450 | }, 451 | { 452 | label: 'Television', 453 | checked: false, 454 | }, 455 | { 456 | label: 'Radio', 457 | checked: false, 458 | }, 459 | { 460 | label: 'Friend or Family', 461 | checked: false, 462 | }, 463 | ], 464 | }, 465 | [], 466 | ], 467 | [ 468 | 'cf7-blocks/input-base', 469 | { 470 | type: 'textarea', 471 | initialValue: 'Add some additional details (optional)', 472 | isRequired: false, 473 | label: 'Additional Details', 474 | name: 'user-additional-details', 475 | showLabel: true, 476 | tagName: 'textarea', 477 | useDefaultValueAsPlaceholder: true, 478 | }, 479 | [], 480 | ], 481 | [ 'cf7-blocks/submit', {}, [] ], 482 | ], 483 | }, 484 | ]; 485 | 486 | export default templates; 487 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/add-user.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const addUser = ( 4 | 11 | 19 | 20 | ); 21 | 22 | export default addUser; 23 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/appointment.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const appointment = ( 4 | 11 | 15 | 16 | ); 17 | 18 | export default appointment; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/bell.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const bell = ( 4 | 11 | 15 | 16 | ); 17 | 18 | export default bell; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/calender.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const calender = ( 4 | 11 | 15 | 16 | ); 17 | 18 | export default calender; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/cf7blocks.js: -------------------------------------------------------------------------------- 1 | const cf7Blocks = ( 2 | 9 | 13 | 17 | 21 | 25 | 29 | 30 | ); 31 | 32 | export default cf7Blocks; 33 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/checkbox.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const checkbox = ( 4 | 11 | 15 | 16 | ); 17 | 18 | export default checkbox; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/feedback.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const feedback = ( 4 | 11 | 15 | 16 | ); 17 | 18 | export default feedback; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Icons 3 | */ 4 | export { default as mail } from './mail'; 5 | export { default as radio } from './radio'; 6 | export { default as textInput } from './text-input'; 7 | export { default as required } from './required'; 8 | export { default as checkbox } from './checkbox'; 9 | export { default as numberInput } from './number-input'; 10 | export { default as calender } from './calender'; 11 | export { default as range } from './range'; 12 | export { default as telephone } from './telephone'; 13 | export { default as bell } from './bell'; 14 | export { default as addUser } from './add-user'; 15 | export { default as feedback } from './feedback'; 16 | export { default as cf7blocks } from './cf7blocks'; 17 | export { default as appointment } from './appointment'; 18 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/mail.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const mail = ( 4 | 11 | 15 | 16 | ); 17 | 18 | export default mail; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/number-input.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const numberInput = ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default numberInput; 16 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/radio.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const radio = ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default radio; 16 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/range.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const range = ( 4 | 11 | 15 | 16 | ); 17 | 18 | export default range; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/required.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const required = ( 4 | 18 | ); 19 | 20 | export default required; 21 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/telephone.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const telephone = ( 4 | 11 | 15 | 16 | ); 17 | 18 | export default telephone; 19 | -------------------------------------------------------------------------------- /packages/blocks-library/src/icons/text-input.js: -------------------------------------------------------------------------------- 1 | import { SVG, Path } from '@wordpress/primitives'; 2 | 3 | const textInput = ( 4 | 11 | 12 | 13 | ); 14 | 15 | export default textInput; 16 | -------------------------------------------------------------------------------- /packages/blocks-library/src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Blocks 3 | */ 4 | import './submit'; 5 | import './form-template'; 6 | import './input-base'; 7 | import './acceptance'; 8 | import './selection-base'; 9 | -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/block.json", 3 | "apiVersion": 2, 4 | "name": "cf7-blocks/input-base", 5 | "title": "Input", 6 | "category": "text", 7 | "description": "Input base", 8 | "keywords": [ 9 | "input" 10 | ], 11 | "version": "1.0.0", 12 | "textdomain": "cf7-blocks", 13 | "attributes": { 14 | "type": { 15 | "type": "string", 16 | "enum": [ 17 | "text", 18 | "email", 19 | "url", 20 | "tel", 21 | "number", 22 | "date", 23 | "range", 24 | "file", 25 | "submit", 26 | "textarea" 27 | ], 28 | "default": "text" 29 | }, 30 | "isRequired": { 31 | "type": "boolean", 32 | "default": false 33 | }, 34 | "requiredText": { 35 | "type": "string", 36 | "default": "*" 37 | }, 38 | "showLabel": { 39 | "type": "boolean", 40 | "default": true 41 | }, 42 | "label": { 43 | "type": "string", 44 | "default": "" 45 | }, 46 | "initialValue": { 47 | "type": "string", 48 | "default": "" 49 | }, 50 | "id": { 51 | "type": "string", 52 | "default": "" 53 | }, 54 | "name": { 55 | "type": "string", 56 | "default": "" 57 | }, 58 | "autogeneratedName": { 59 | "type": "string", 60 | "default": "" 61 | }, 62 | "minimum": { 63 | "type": "string", 64 | "default": "" 65 | }, 66 | "maximum": { 67 | "type": "string", 68 | "default": "" 69 | }, 70 | "useDefaultValueAsPlaceholder": { 71 | "type": "boolean", 72 | "default": false 73 | }, 74 | "tagName": { 75 | "type": "string", 76 | "default": "input", 77 | "enum": [ 78 | "input", 79 | "textarea" 80 | ] 81 | }, 82 | "style": { 83 | "type": "object", 84 | "default": { 85 | "spacing": { 86 | "margin": { 87 | "top": "0", 88 | "right": "0", 89 | "left": "0", 90 | "bottom": "20px" 91 | } 92 | } 93 | } 94 | } 95 | }, 96 | "supports": { 97 | "spacing": { 98 | "margin": true 99 | } 100 | }, 101 | "styles": [] 102 | } -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/edit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { isEmpty } from 'lodash'; 5 | import classnames from 'classnames'; 6 | import { __ } from '@wordpress/i18n'; 7 | import { useEffect } from '@wordpress/element'; 8 | import { useBlockProps, RichText } from '@wordpress/block-editor'; 9 | 10 | /** 11 | * Custom Dependencies 12 | */ 13 | import Toolbar from './toolbar'; 14 | import Inspector from './inspector'; 15 | import generateFieldName from '../utils/generate-name'; 16 | 17 | export default function edit( props ) { 18 | const blockProps = useBlockProps( { 19 | className: classnames( { 20 | [ `cf7-block-${ props.attributes.type }` ]: true, 21 | 'cf7-block-field': true, 22 | } ), 23 | } ); 24 | 25 | const { 26 | id, 27 | type, 28 | name, 29 | label, 30 | showLabel, 31 | isRequired, 32 | requiredText, 33 | initialValue, 34 | autogeneratedName, 35 | useDefaultValueAsPlaceholder, 36 | } = props.attributes; 37 | 38 | useEffect( () => { 39 | // Generating a new field name, if needed. 40 | if ( 41 | isEmpty( autogeneratedName ) || 42 | ! autogeneratedName.startsWith( type ) 43 | ) { 44 | const newAutogeneratedName = generateFieldName( type ); 45 | props.setAttributes( { autogeneratedName: newAutogeneratedName } ); 46 | } 47 | }, [ type ] ); 48 | 49 | const InputTag = props.attributes.tagName; 50 | 51 | return ( 52 | <> 53 |
54 | { showLabel && ( 55 |
56 | 61 | props.setAttributes( { label: newLabel } ) 62 | } 63 | /> 64 | { isRequired && { requiredText } } 65 |
66 | ) } 67 | 77 |
78 | 79 | 80 | 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/editor.scss: -------------------------------------------------------------------------------- 1 | .wp-block-cf7-blocks-input-base { 2 | 3 | input, 4 | textarea { 5 | pointer-events: none; 6 | } 7 | 8 | .cf7blocks-selection-base-main-label-wrapper { 9 | display: flex; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from '@wordpress/blocks'; 2 | import metadata from './block.json'; 3 | 4 | import edit from './edit'; 5 | import save from './save'; 6 | import './variations'; 7 | 8 | registerBlockType( metadata, { 9 | apiVersion: 2, 10 | edit, 11 | save, 12 | } ); 13 | -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/inspector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __, sprintf } from '@wordpress/i18n'; 5 | import { PanelBody, TextControl, ToggleControl } from '@wordpress/components'; 6 | import { 7 | InspectorControls, 8 | InspectorAdvancedControls, 9 | } from '@wordpress/block-editor'; 10 | 11 | /** 12 | * Custom Dependencies 13 | */ 14 | import { 15 | CF7BLOCKS_RANGE_SUPPORTED_INPUT_TYPES, 16 | CF7BLOCKS_PLACEHOLDER_SUPPORTED_TYPES, 17 | } from '../constants'; 18 | import { convertFieldBlockToCF7Shortcode } from '../utils/convert-to-cf7-shortcode'; 19 | 20 | function Inspector( props ) { 21 | const { 22 | id, 23 | type, 24 | name, 25 | minimum, 26 | maximum, 27 | showLabel, 28 | isRequired, 29 | initialValue, 30 | autogeneratedName, 31 | useDefaultValueAsPlaceholder, 32 | } = props.attributes; 33 | 34 | const isRangeSupported = Object.keys( 35 | CF7BLOCKS_RANGE_SUPPORTED_INPUT_TYPES 36 | ).includes( type ); 37 | 38 | const isPlaceholderSupported = CF7BLOCKS_PLACEHOLDER_SUPPORTED_TYPES.includes( 39 | type 40 | ); 41 | 42 | const isFileInput = type === 'file'; 43 | 44 | return ( 45 | <> 46 | 47 | 48 | 54 | 55 | 56 | { 65 | const normalizedName = newName 66 | .toLowerCase() 67 | .replace( ' ', '-' ); 68 | 69 | props.setAttributes( { name: normalizedName } ); 70 | } } 71 | /> 72 | { ! isFileInput && ( 73 | 80 | props.setAttributes( { 81 | initialValue: newInitialValue, 82 | } ) 83 | } 84 | /> 85 | ) } 86 | { isPlaceholderSupported && ( 87 | 99 | props.setAttributes( { 100 | useDefaultValueAsPlaceholder: newStatus, 101 | } ) 102 | } 103 | /> 104 | ) } 105 | 108 | props.setAttributes( { isRequired: newRequired } ) 109 | } 110 | label={ __( 'Required', 'cf7-blocks' ) } 111 | help={ __( 112 | 'Marks the field as required.', 113 | 'cf7-blocks' 114 | ) } 115 | /> 116 | 119 | props.setAttributes( { showLabel: newShowLabel } ) 120 | } 121 | label={ __( 'Label', 'cf7-blocks' ) } 122 | help={ __( 123 | 'Displays an additional label above the field.', 124 | 'cf7-blocks' 125 | ) } 126 | /> 127 | 128 | { isRangeSupported && ( 129 | 130 | 137 | props.setAttributes( { minimum: newMinimum } ) 138 | } 139 | help={ sprintf( 140 | __( 141 | 'Minimum %s a user can input', 142 | 'cf7-blocks' 143 | ), 144 | type 145 | ) } 146 | /> 147 | 153 | props.setAttributes( { maximum: newMaximum } ) 154 | } 155 | label={ __( 'Maximum', 'cf7-blocks' ) } 156 | help={ sprintf( 157 | __( 158 | 'Maximum %s a user can input', 159 | 'cf7-blocks' 160 | ), 161 | type 162 | ) } 163 | /> 164 | 165 | ) } 166 | 167 | 168 | { 173 | const idWithoutSpace = newId.replace( ' ', '' ); 174 | props.setAttributes( { id: idWithoutSpace } ); 175 | } } 176 | /> 177 | 178 | 179 | ); 180 | } 181 | 182 | export default Inspector; 183 | -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { RawHTML } from '@wordpress/element'; 5 | import { useBlockProps } from '@wordpress/block-editor'; 6 | 7 | /** 8 | * Custom Dependencies 9 | */ 10 | import { WithLabel } from '../components'; 11 | import processInputLabel from '../utils/process-input-label'; 12 | import { convertFieldBlockToCF7Shortcode } from '../utils/convert-to-cf7-shortcode'; 13 | 14 | export default function save( props ) { 15 | const shortcode = convertFieldBlockToCF7Shortcode( props.attributes ); 16 | 17 | return ( 18 |
19 | 26 | { shortcode } 27 | 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/style.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | .wp-block-cf7-blocks-input-base { 4 | 5 | input, 6 | textarea { 7 | width: 100%; 8 | padding: 5px; 9 | min-height: 30px; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/toolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { BlockControls } from '@wordpress/block-editor'; 6 | import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; 7 | 8 | /** 9 | * Custom Dependencies 10 | */ 11 | import { required } from '../icons'; 12 | 13 | function Toolbar( props ) { 14 | const { isRequired } = props.attributes; 15 | 16 | return ( 17 | 18 | 19 | 24 | props.setAttributes( { isRequired: ! isRequired } ) 25 | } 26 | /> 27 | 28 | 29 | ); 30 | } 31 | 32 | export default Toolbar; 33 | -------------------------------------------------------------------------------- /packages/blocks-library/src/input-base/variations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { globe, upload, comment } from '@wordpress/icons'; 6 | import { registerBlockVariation } from '@wordpress/blocks'; 7 | 8 | /** 9 | * Custom Dependencies 10 | */ 11 | import { 12 | mail, 13 | textInput, 14 | numberInput, 15 | calender, 16 | range, 17 | telephone, 18 | } from '../icons'; 19 | 20 | const variations = [ 21 | { 22 | name: 'text', 23 | icon: textInput, 24 | title: __( 'Text', 'cf7-blocks' ), 25 | description: __( 'Create basic input text field.' ), 26 | isDefault: true, 27 | scope: [ 'inserter', 'transform' ], 28 | attributes: { 29 | type: 'text', 30 | tagName: 'input', 31 | }, 32 | isActive: ( blockAttributes, variationAttributes ) => { 33 | return ( 34 | blockAttributes.type === variationAttributes.type && 35 | blockAttributes.tagName === variationAttributes.tagName 36 | ); 37 | }, 38 | }, 39 | { 40 | name: 'url', 41 | icon: globe, 42 | title: __( 'Url', 'cf7-blocks' ), 43 | description: __( 'Create basic input url field.' ), 44 | scope: [ 'inserter', 'transform' ], 45 | attributes: { 46 | type: 'url', 47 | tagName: 'input', 48 | }, 49 | isActive: ( blockAttributes, variationAttributes ) => { 50 | return ( 51 | blockAttributes.type === variationAttributes.type && 52 | blockAttributes.tagName === variationAttributes.tagName 53 | ); 54 | }, 55 | }, 56 | { 57 | name: 'date', 58 | icon: calender, 59 | title: __( 'Date', 'cf7-blocks' ), 60 | description: __( 'Create basic input date field.' ), 61 | scope: [ 'inserter', 'transform' ], 62 | attributes: { 63 | type: 'date', 64 | tagName: 'input', 65 | }, 66 | isActive: ( blockAttributes, variationAttributes ) => { 67 | return ( 68 | blockAttributes.type === variationAttributes.type && 69 | blockAttributes.tagName === variationAttributes.tagName 70 | ); 71 | }, 72 | }, 73 | { 74 | name: 'number', 75 | icon: numberInput, 76 | title: __( 'Number', 'cf7-blocks' ), 77 | description: __( 'Create basic input number field.' ), 78 | scope: [ 'inserter', 'transform' ], 79 | attributes: { 80 | type: 'number', 81 | tagName: 'input', 82 | }, 83 | isActive: ( blockAttributes, variationAttributes ) => { 84 | return ( 85 | blockAttributes.type === variationAttributes.type && 86 | blockAttributes.tagName === variationAttributes.tagName 87 | ); 88 | }, 89 | }, 90 | { 91 | name: 'tel', 92 | icon: telephone, 93 | title: __( 'Telephone', 'cf7-blocks' ), 94 | description: __( 'Create basic input telephone field.' ), 95 | scope: [ 'inserter', 'transform' ], 96 | attributes: { 97 | type: 'tel', 98 | tagName: 'input', 99 | }, 100 | isActive: ( blockAttributes, variationAttributes ) => { 101 | return ( 102 | blockAttributes.type === variationAttributes.type && 103 | blockAttributes.tagName === variationAttributes.tagName 104 | ); 105 | }, 106 | }, 107 | { 108 | name: 'textarea', 109 | icon: comment, 110 | title: __( 'Textarea', 'cf7-blocks' ), 111 | description: __( 'Create a basic textarea field.' ), 112 | scope: [ 'inserter', 'transform' ], 113 | attributes: { 114 | type: 'textarea', 115 | tagName: 'textarea', 116 | }, 117 | isActive: ( blockAttributes, variationAttributes ) => { 118 | return blockAttributes.tagName === variationAttributes.tagName; 119 | }, 120 | }, 121 | { 122 | name: 'email', 123 | icon: mail, 124 | title: __( 'Email', 'cf7-blocks' ), 125 | description: __( 'Create basic input email field.' ), 126 | scope: [ 'inserter', 'transform' ], 127 | attributes: { 128 | type: 'email', 129 | tagName: 'input', 130 | }, 131 | isActive: ( blockAttributes, variationAttributes ) => { 132 | return ( 133 | blockAttributes.type === variationAttributes.type && 134 | blockAttributes.tagName === variationAttributes.tagName 135 | ); 136 | }, 137 | }, 138 | { 139 | name: 'range', 140 | icon: range, 141 | title: __( 'Range', 'cf7-blocks' ), 142 | description: __( 'Create basic input range field.' ), 143 | scope: [ 'inserter', 'transform' ], 144 | attributes: { 145 | type: 'range', 146 | tagName: 'input', 147 | }, 148 | isActive: ( blockAttributes, variationAttributes ) => { 149 | return ( 150 | blockAttributes.type === variationAttributes.type && 151 | blockAttributes.tagName === variationAttributes.tagName 152 | ); 153 | }, 154 | }, 155 | { 156 | name: 'file', 157 | icon: upload, 158 | title: __( 'File', 'cf7-blocks' ), 159 | description: __( 'Create basic file input.' ), 160 | scope: [ 'inserter', 'transform' ], 161 | attributes: { 162 | type: 'file', 163 | }, 164 | isActive: ( blockAttributes, variationAttributes ) => { 165 | return ( 166 | blockAttributes.type === variationAttributes.type && 167 | blockAttributes.tagName === variationAttributes.tagName 168 | ); 169 | }, 170 | }, 171 | ]; 172 | 173 | variations.forEach( ( variation ) => { 174 | registerBlockVariation( 'cf7-blocks/input-base', variation ); 175 | } ); 176 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/block.json", 3 | "apiVersion": 2, 4 | "name": "cf7-blocks/selection-base", 5 | "title": "Selection", 6 | "category": "text", 7 | "description": "Selection base", 8 | "keywords": [ 9 | "selection" 10 | ], 11 | "version": "1.0.0", 12 | "textdomain": "cf7-blocks", 13 | "attributes": { 14 | "isMultipleSelect": { 15 | "type": "boolean", 16 | "default": false 17 | }, 18 | "isFirstBlankItem": { 19 | "type": "boolean", 20 | "default": false 21 | }, 22 | "type": { 23 | "type": "string", 24 | "enum": [ 25 | "checkbox", 26 | "radio", 27 | "select" 28 | ], 29 | "default": "checkbox" 30 | }, 31 | "items": { 32 | "type": "array", 33 | "default": [ 34 | { 35 | "label": "", 36 | "checked": false 37 | } 38 | ] 39 | }, 40 | "requiredText": { 41 | "type": "string", 42 | "default": "*" 43 | }, 44 | "mainLabel": { 45 | "type": "string", 46 | "default": "" 47 | }, 48 | "showLabel": { 49 | "type": "boolean", 50 | "default": false 51 | }, 52 | "isRequired": { 53 | "type": "boolean", 54 | "default": false 55 | }, 56 | "isLabelFirst": { 57 | "type": "boolean", 58 | "default": false 59 | }, 60 | "id": { 61 | "type": "string", 62 | "default": "" 63 | }, 64 | "name": { 65 | "type": "string", 66 | "default": "" 67 | }, 68 | "autogeneratedName": { 69 | "type": "string", 70 | "default": "" 71 | }, 72 | "style": { 73 | "type": "object", 74 | "default": { 75 | "spacing": { 76 | "margin": { 77 | "top": "0", 78 | "right": "0", 79 | "left": "0", 80 | "bottom": "20px" 81 | } 82 | } 83 | } 84 | } 85 | }, 86 | "supports": { 87 | "spacing": { 88 | "margin": true 89 | } 90 | }, 91 | "styles": [] 92 | } -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/components/edit-mode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { clone, isEmpty } from 'lodash'; 6 | import { useEffect, useRef } from '@wordpress/element'; 7 | import { trash, plus } from '@wordpress/icons'; 8 | import { ENTER, BACKSPACE } from '@wordpress/keycodes'; 9 | import { Button, TextControl, ToolbarButton } from '@wordpress/components'; 10 | 11 | /** 12 | * Custom Dependencies 13 | */ 14 | import PropTypes from 'prop-types'; 15 | 16 | function EditMode( props ) { 17 | const inputElements = useRef( [] ); 18 | 19 | const { 20 | id, 21 | type, 22 | name, 23 | isRequired, 24 | isLabelFirst, 25 | isFirstBlankItem, 26 | autogeneratedName, 27 | } = props; 28 | 29 | const handleFocus = ( index ) => { 30 | inputElements.current[ index ].focus(); 31 | }; 32 | 33 | const handleChange = ( updatedItem, index ) => { 34 | const newItems = clone( props.items ); 35 | 36 | newItems[ index ] = updatedItem; // Updating. 37 | 38 | props.onUpdate( newItems ); 39 | }; 40 | const handleAdd = ( index = null ) => { 41 | const newItem = { 42 | label: '', 43 | checked: false, 44 | }; 45 | 46 | if ( index === null ) { 47 | props.onUpdate( [ ...props.items, newItem ] ); 48 | } else { 49 | let newItems = clone( props.items ); 50 | newItems.splice( index, 0, newItem ); 51 | 52 | props.onUpdate( newItems ); 53 | 54 | handleFocus( index ); 55 | } 56 | }; 57 | 58 | const handleDelete = ( index ) => { 59 | if ( index === 0 ) return; // preventing the first option from being deleted. 60 | 61 | const newItems = props.items.filter( 62 | ( _, itemIndex ) => index !== itemIndex 63 | ); 64 | 65 | props.onUpdate( newItems ); 66 | handleFocus( index - 1 ); 67 | }; 68 | 69 | const handleKeyDown = ( event, index ) => { 70 | const { keyCode } = event; 71 | if ( keyCode === ENTER ) { 72 | handleAdd( index + 1, event.target ); 73 | } 74 | if ( keyCode === BACKSPACE && event.target.value === '' ) { 75 | handleDelete( index, event.target ); 76 | } 77 | }; 78 | 79 | return ( 80 | <> 81 | { props.items.map( ( item, index ) => { 82 | const label = ( 83 | { 87 | inputElements.current[ index ] = ref; 88 | } } 89 | placeholder={ __( 'Add Label', 'cf7-blocks' ) } 90 | onKeyDown={ ( event ) => 91 | setTimeout( () => handleKeyDown( event, index ), 0 ) 92 | } 93 | onChange={ ( newLabel ) => 94 | handleChange( { ...item, label: newLabel }, index ) 95 | } 96 | /> 97 | ); 98 | 99 | const value = index === 0 && isFirstBlankItem ? '' : item.label; 100 | const fieldName = ( isEmpty( name ) 101 | ? autogeneratedName 102 | : name 103 | ).concat( '[]' ); 104 | 105 | return ( 106 |
107 | { isLabelFirst && label } 108 | { type !== 'select' && ( 109 | null } 115 | checked={ item.checked } 116 | isRequired={ isRequired } 117 | /> 118 | ) } 119 | { ! isLabelFirst && label } 120 |
127 | ); 128 | } ) } 129 | handleAdd() }> 130 | { __( 'Add Option', 'cf7-blocks' ) } 131 | 132 | 133 | ); 134 | } 135 | 136 | EditMode.propTypes = { 137 | /** Selection items */ 138 | items: PropTypes.arrayOf( 139 | PropTypes.shape( { 140 | /** Is the item checked? */ 141 | checked: PropTypes.bool.isRequired, 142 | 143 | /** Item label */ 144 | labe: PropTypes.string.isRequired, 145 | } ) 146 | ), 147 | 148 | /** Callback that will be called with updated items on change. */ 149 | onUpdate: PropTypes.func.isRequired, 150 | }; 151 | 152 | export default EditMode; 153 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/components/preview-mode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { RichText } from '@wordpress/block-editor'; 6 | 7 | /** 8 | * Custom Dependencies 9 | */ 10 | import PropTypes from 'prop-types'; 11 | 12 | function PreviewMode( props ) { 13 | const { id, type, name, isLabelFirst, isRequired } = props; 14 | 15 | return props.items.map( ( item, index ) => { 16 | const label = ; 17 | 18 | return ( 19 |
20 | { isLabelFirst && label } 21 | 29 | { ! isLabelFirst && label } 30 |
31 | ); 32 | } ); 33 | } 34 | 35 | PreviewMode.propTypes = { 36 | /** Selection items */ 37 | items: PropTypes.arrayOf( 38 | PropTypes.shape( { 39 | /** Is the item checked? */ 40 | checked: PropTypes.bool.isRequired, 41 | 42 | /** Item label */ 43 | labe: PropTypes.string.isRequired, 44 | } ) 45 | ), 46 | }; 47 | 48 | export default PreviewMode; 49 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/components/select-preview-mode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | * 4 | */ 5 | import { isEmpty } from 'lodash'; 6 | import { __ } from '@wordpress/i18n'; 7 | 8 | function SelectPreviewMode( props ) { 9 | const { 10 | id, 11 | name, 12 | items, 13 | isRequired, 14 | isMultipleSelect, 15 | autogeneratedName, 16 | } = props; 17 | return ( 18 | 33 | ); 34 | } 35 | 36 | export default SelectPreviewMode; 37 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/edit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { isEmpty } from 'lodash'; 5 | import { __ } from '@wordpress/i18n'; 6 | import { useEffect } from '@wordpress/element'; 7 | import { useBlockProps, RichText } from '@wordpress/block-editor'; 8 | 9 | /** 10 | * Custom Dependencies 11 | */ 12 | import Toolbar from './toolbar'; 13 | import Inspector from './inspector'; 14 | import classnames from 'classnames'; 15 | import PreviewMode from './components/preview-mode'; 16 | import EditMode from './components/edit-mode'; 17 | import generateFieldName from '../utils/generate-name'; 18 | import SelectPreviewMode from './components/select-preview-mode'; 19 | 20 | export default function edit( props ) { 21 | const blockProps = useBlockProps( { 22 | className: classnames( { 23 | [ `cf7-block-${ props.attributes.type }` ]: true, 24 | 'cf7-block-selection-field-editing': props.isSelected, 25 | } ), 26 | } ); 27 | 28 | const { 29 | id, 30 | type, 31 | name, 32 | items, 33 | mainLabel, 34 | showLabel, 35 | isRequired, 36 | isLabelFirst, 37 | requiredText, 38 | isFirstBlankItem, 39 | isMultipleSelect, 40 | autogeneratedName, 41 | } = props.attributes; 42 | 43 | useEffect( () => { 44 | // Generating a new field name, if needed. 45 | if ( 46 | isEmpty( autogeneratedName ) || 47 | ! autogeneratedName.startsWith( type ) 48 | ) { 49 | const newAutogeneratedName = generateFieldName( type ); 50 | props.setAttributes( { autogeneratedName: newAutogeneratedName } ); 51 | } 52 | }, [ type ] ); 53 | 54 | return ( 55 | <> 56 |
57 | { showLabel && ( 58 |
59 | 65 | props.setAttributes( { 66 | mainLabel: newMainLabel, 67 | } ) 68 | } 69 | /> 70 | { isRequired && { requiredText } } 71 |
72 | ) } 73 | { ! props.isSelected ? ( 74 | type !== 'select' ? ( 75 | 86 | ) : ( 87 | 100 | ) 101 | ) : ( 102 | 113 | props.setAttributes( { items: newItems } ) 114 | } 115 | /> 116 | ) } 117 |
118 | 119 | 120 | 121 | ); 122 | } 123 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/editor.scss: -------------------------------------------------------------------------------- 1 | .wp-block-cf7-blocks-selection-base { 2 | 3 | .cf7blocks-selection-base-select-block { 4 | width: 100%; 5 | max-width: 100%; 6 | } 7 | 8 | .cf7blocks-selection-base-main-label-wrapper { 9 | display: flex; 10 | 11 | .cf7blocks-selection-base-main-label { 12 | display: block; 13 | } 14 | } 15 | 16 | &:not(.cf7-block-selection-field-editing) { 17 | 18 | .cf7blocks-selection-field-preview { 19 | display: inline-block; 20 | margin: 0 0 0 1em; 21 | } 22 | } 23 | 24 | &.cf7-block-selection-field-editing { 25 | 26 | .cf7blocks-selection-field { 27 | display: flex; 28 | align-items: center; 29 | width: 100%; 30 | max-width: 100%; 31 | grid-gap: 6px; 32 | margin-bottom: 5px; 33 | 34 | .components-base-control { 35 | width: 100%; 36 | max-width: 100%; 37 | 38 | .components-base-control__field { 39 | margin-bottom: 0 !important; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from '@wordpress/blocks'; 2 | import metadata from './block.json'; 3 | 4 | import edit from './edit'; 5 | import save from './save'; 6 | import './variations'; 7 | 8 | registerBlockType( metadata, { 9 | apiVersion: 2, 10 | edit, 11 | save, 12 | } ); 13 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/inspector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __, sprintf } from '@wordpress/i18n'; 5 | import { PanelBody, TextControl, ToggleControl } from '@wordpress/components'; 6 | import { 7 | InspectorControls, 8 | InspectorAdvancedControls, 9 | } from '@wordpress/block-editor'; 10 | 11 | /** 12 | * Custom Dependencies 13 | */ 14 | 15 | import { convertSelectionBlockToCF7Shortcode } from '../utils/convert-to-cf7-shortcode'; 16 | 17 | function Inspector( props ) { 18 | const { 19 | id, 20 | type, 21 | name, 22 | showLabel, 23 | isRequired, 24 | isLabelFirst, 25 | isMultipleSelect, 26 | isFirstBlankItem, 27 | autogeneratedName, 28 | } = props.attributes; 29 | 30 | return ( 31 | <> 32 | 33 | 34 | 40 | 41 | 42 | { 51 | const normalizedName = newName 52 | .toLowerCase() 53 | .replace( ' ', '-' ); 54 | 55 | props.setAttributes( { name: normalizedName } ); 56 | } } 57 | /> 58 | { type === 'select' && ( 59 | <> 60 | 63 | props.setAttributes( { 64 | isMultipleSelect: newIsMultipleSelect, 65 | } ) 66 | } 67 | label={ __( 68 | 'Multiple Selection', 69 | 'cf7-blocks' 70 | ) } 71 | help={ __( 72 | 'Allow multiple selection.', 73 | 'cf7-blocks' 74 | ) } 75 | /> 76 | 79 | props.setAttributes( { 80 | isFirstBlankItem: newIsFirstBlankItem, 81 | } ) 82 | } 83 | label={ __( 'First blank item', 'cf7-blocks' ) } 84 | help={ __( 85 | 'Insert a blank item as the first option.', 86 | 'cf7-blocks' 87 | ) } 88 | /> 89 | 90 | ) } 91 | { type !== 'radio' && ( 92 | 95 | props.setAttributes( { 96 | isRequired: newRequired, 97 | } ) 98 | } 99 | label={ __( 'Required', 'cf7-blocks' ) } 100 | help={ __( 101 | 'Marks the field as required.', 102 | 'cf7-blocks' 103 | ) } 104 | /> 105 | ) } 106 | 109 | props.setAttributes( { 110 | showLabel: newShowLabel, 111 | } ) 112 | } 113 | label={ __( 'Label', 'cf7-blocks' ) } 114 | help={ __( 115 | 'Displays an additional label above the field.', 116 | 'cf7-blocks' 117 | ) } 118 | /> 119 | 120 | 123 | props.setAttributes( { 124 | isLabelFirst: newLabelFirst, 125 | } ) 126 | } 127 | label={ __( 128 | 'Put a label first, a checkbox last', 129 | 'cf7-blocks' 130 | ) } 131 | help={ __( 132 | 'Put a label first, a checkbox last.', 133 | 'cf7-blocks' 134 | ) } 135 | /> 136 | 137 | 138 | 139 | { 144 | const idWithoutSpace = newId.replace( ' ', '' ); 145 | props.setAttributes( { id: idWithoutSpace } ); 146 | } } 147 | /> 148 | 149 | 150 | ); 151 | } 152 | 153 | export default Inspector; 154 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { RawHTML } from '@wordpress/element'; 5 | import { useBlockProps } from '@wordpress/block-editor'; 6 | 7 | /** 8 | * Custom Dependencies 9 | */ 10 | import { convertSelectionBlockToCF7Shortcode } from '../utils/convert-to-cf7-shortcode'; 11 | import { WithLabel } from '../components'; 12 | 13 | import processInputLabel from '../utils/process-input-label'; 14 | 15 | export default function save( props ) { 16 | const shortcode = convertSelectionBlockToCF7Shortcode( props.attributes ); 17 | 18 | return ( 19 |
20 | 27 | { shortcode } 28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/style.scss: -------------------------------------------------------------------------------- 1 | .wp-block-cf7-blocks-selection-base { 2 | 3 | display: flex; 4 | flex-direction: column; 5 | 6 | select { 7 | width: 100%; 8 | } 9 | 10 | label { 11 | 12 | > span { 13 | display: block; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/toolbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { BlockControls } from '@wordpress/block-editor'; 6 | import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; 7 | 8 | /** 9 | * Custom Dependencies 10 | */ 11 | import { required } from '../icons'; 12 | 13 | function Toolbar( props ) { 14 | const { isRequired, type } = props.attributes; 15 | 16 | return ( 17 | 18 | { type !== 'radio' && ( 19 | 20 | 25 | props.setAttributes( { isRequired: ! isRequired } ) 26 | } 27 | /> 28 | 29 | ) } 30 | 31 | ); 32 | } 33 | 34 | export default Toolbar; 35 | -------------------------------------------------------------------------------- /packages/blocks-library/src/selection-base/variations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { archive, chevronDown } from '@wordpress/icons'; 6 | import { registerBlockVariation } from '@wordpress/blocks'; 7 | 8 | /** 9 | * Custom Dependencies 10 | */ 11 | import { checkbox, radio } from '../icons'; 12 | 13 | const variations = [ 14 | { 15 | name: 'checkbox', 16 | icon: checkbox, 17 | title: __( 'Checkbox', 'cf7-blocks' ), 18 | description: __( 'Create basic checkbox field.' ), 19 | isDefault: true, 20 | scope: [ 'inserter', 'transform' ], 21 | attributes: { 22 | type: 'checkbox', 23 | }, 24 | isActive: ( blockAttributes, variationAttributes ) => { 25 | return blockAttributes.type === variationAttributes.type; 26 | }, 27 | }, 28 | { 29 | name: 'radio', 30 | icon: radio, 31 | title: __( 'Radio', 'cf7-blocks' ), 32 | description: __( 'Create basic radio field.' ), 33 | isDefault: false, 34 | scope: [ 'inserter', 'transform' ], 35 | attributes: { 36 | type: 'radio', 37 | }, 38 | isActive: ( blockAttributes, variationAttributes ) => { 39 | return blockAttributes.type === variationAttributes.type; 40 | }, 41 | }, 42 | { 43 | name: 'select', 44 | icon: chevronDown, 45 | title: __( 'Select', 'cf7-blocks' ), 46 | description: __( 'Create basic select field.' ), 47 | isDefault: false, 48 | scope: [ 'inserter', 'transform' ], 49 | attributes: { 50 | type: 'select', 51 | }, 52 | isActive: ( blockAttributes, variationAttributes ) => { 53 | return blockAttributes.type === variationAttributes.type; 54 | }, 55 | }, 56 | ]; 57 | 58 | variations.forEach( ( variation ) => { 59 | registerBlockVariation( 'cf7-blocks/selection-base', variation ); 60 | } ); 61 | -------------------------------------------------------------------------------- /packages/blocks-library/src/style.scss: -------------------------------------------------------------------------------- 1 | @import "./input-base/style.scss"; 2 | @import "./selection-base//style.scss"; 3 | 4 | .wpcf7-form > p { 5 | display: none; 6 | } 7 | 8 | .wpcf7-form > p br { 9 | display: none; 10 | } 11 | -------------------------------------------------------------------------------- /packages/blocks-library/src/submit/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/block.json", 3 | "apiVersion": 2, 4 | "name": "cf7-blocks/submit", 5 | "title": "Submit", 6 | "category": "text", 7 | "description": "Submit", 8 | "keywords": [ 9 | "submit" 10 | ], 11 | "version": "1.0.0", 12 | "textdomain": "cf7-blocks", 13 | "attributes": { 14 | "label": { 15 | "type": "string", 16 | "default": "" 17 | }, 18 | "id": { 19 | "type": "string", 20 | "default": "" 21 | }, 22 | "style": { 23 | "type": "object", 24 | "default": { 25 | "spacing": { 26 | "margin": { 27 | "top": "0", 28 | "right": "0", 29 | "left": "0", 30 | "bottom": "20px" 31 | } 32 | } 33 | } 34 | } 35 | }, 36 | "supports": { 37 | "multiple": false, 38 | "spacing": { 39 | "margin": true 40 | } 41 | }, 42 | "styles": [] 43 | } -------------------------------------------------------------------------------- /packages/blocks-library/src/submit/edit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { isEmpty } from 'lodash'; 5 | import { __ } from '@wordpress/i18n'; 6 | import { useBlockProps } from '@wordpress/block-editor'; 7 | /** 8 | * Custom Dependencies 9 | */ 10 | import classnames from 'classnames'; 11 | import Inspector from './inspector'; 12 | 13 | export default function edit( props ) { 14 | const blockProps = useBlockProps( { 15 | className: classnames( 'cf7-submit-field' ), 16 | } ); 17 | 18 | const { id, label } = props.attributes; 19 | 20 | return ( 21 | <> 22 |
23 | 28 |
29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /packages/blocks-library/src/submit/index.js: -------------------------------------------------------------------------------- 1 | import { registerBlockType } from '@wordpress/blocks'; 2 | import metadata from './block.json'; 3 | 4 | import edit from './edit'; 5 | import save from './save'; 6 | 7 | import { button } from '@wordpress/icons'; 8 | 9 | registerBlockType( metadata, { 10 | apiVersion: 2, 11 | edit, 12 | save, 13 | icon: button, 14 | } ); 15 | -------------------------------------------------------------------------------- /packages/blocks-library/src/submit/inspector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { 6 | InspectorAdvancedControls, 7 | InspectorControls, 8 | } from '@wordpress/block-editor'; 9 | import { TextControl, PanelBody } from '@wordpress/components'; 10 | import { convertSubmitBlockToCF7Shortcode } from '../utils/convert-to-cf7-shortcode'; 11 | 12 | function Inspector( props ) { 13 | const { label, id } = props.attributes; 14 | 15 | return ( 16 | 17 | 18 | 24 | 25 | 29 | 34 | props.setAttributes( { label: newValue } ) 35 | } 36 | /> 37 | 38 | 39 | { 44 | const idWithoutSpace = newId.replace( ' ', '' ); 45 | props.setAttributes( { id: idWithoutSpace } ); 46 | } } 47 | /> 48 | 49 | 50 | ); 51 | } 52 | 53 | export default Inspector; 54 | -------------------------------------------------------------------------------- /packages/blocks-library/src/submit/save.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { RawHTML } from '@wordpress/element'; 5 | import { useBlockProps } from '@wordpress/block-editor'; 6 | 7 | /** 8 | * Custom Dependencies 9 | */ 10 | import { convertSubmitBlockToCF7Shortcode } from '../utils/convert-to-cf7-shortcode'; 11 | 12 | export default function save( props ) { 13 | const shortcode = convertSubmitBlockToCF7Shortcode( props.attributes ); 14 | 15 | return ( 16 |
17 | { shortcode } 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /packages/blocks-library/src/submit/style.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | .wp-block-cf7-blocks-input-base { 4 | 5 | label { 6 | display: flex; 7 | flex-direction: column; 8 | } 9 | 10 | input, 11 | textarea { 12 | width: 100%; 13 | padding: 5px; 14 | min-height: 30px; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /packages/blocks-library/src/utils/convert-to-cf7-shortcode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { sprintf } from '@wordpress/i18n'; 5 | import { isEmpty, castArray } from 'lodash'; 6 | 7 | /** 8 | * Custom Dependencies 9 | */ 10 | import { CF7BLOCKS_RANGE_SUPPORTED_INPUT_TYPES } from '../constants'; 11 | 12 | /** 13 | * Creates/Converts the given attributes into CF7 shortcode. 14 | * 15 | * @param {object} attributes - Block attributes. 16 | * 17 | * @return {string} - CF7 Shortcode. 18 | */ 19 | export function convertFieldBlockToCF7Shortcode( attributes ) { 20 | const { 21 | id, 22 | type, 23 | name, 24 | minimum, 25 | maximum, 26 | isRequired, 27 | initialValue, 28 | autogeneratedName, 29 | useDefaultValueAsPlaceholder, 30 | } = attributes; 31 | 32 | const fieldName = isEmpty( name ) ? autogeneratedName : name; 33 | 34 | const hasInitialValue = ! isEmpty( initialValue ); 35 | const initialValueCode = useDefaultValueAsPlaceholder 36 | ? ` placeholder "${ initialValue }"` 37 | : ` "${ initialValue }"`; 38 | 39 | let shortcodeAttributes = castArray( type ); // [ type ] 40 | 41 | if ( isRequired ) { 42 | shortcodeAttributes.push( '*' ); 43 | } 44 | 45 | shortcodeAttributes.push( ' ', fieldName ); 46 | 47 | if ( ! isEmpty( id ) ) { 48 | shortcodeAttributes.push( ' ', 'id:'.concat( id ) ); 49 | } 50 | 51 | if ( hasInitialValue ) { 52 | shortcodeAttributes.push( initialValueCode ); 53 | } 54 | 55 | const isRangeSupported = Object.keys( 56 | CF7BLOCKS_RANGE_SUPPORTED_INPUT_TYPES 57 | ).includes( type ); 58 | 59 | if ( isRangeSupported ) { 60 | if ( ! isEmpty( minimum ) ) { 61 | shortcodeAttributes.push( ' ', 'min:' + minimum ); 62 | } 63 | 64 | if ( ! isEmpty( maximum ) ) { 65 | shortcodeAttributes.push( ' ', 'max:' + maximum ); 66 | } 67 | } 68 | 69 | return sprintf( '[%1$s]', shortcodeAttributes.join( '' ) ); 70 | } 71 | 72 | /** 73 | * Converts the submit block to contact form 7 shortcode. 74 | * 75 | * @param {object} attributes - Block attributes. 76 | * @return {string} - CF7 Shortcode. 77 | */ 78 | export function convertSubmitBlockToCF7Shortcode( attributes ) { 79 | const { id, label } = attributes; 80 | const shortcodeAttributes = castArray( 'submit' ); 81 | if ( ! isEmpty( id ) ) { 82 | shortcodeAttributes.push( ' ', 'id:'.concat( id ) ); 83 | } 84 | 85 | shortcodeAttributes.push( 86 | ' ', 87 | isEmpty( label ) ? 'Submit' : `"${ label }"` 88 | ); 89 | 90 | return sprintf( '[%1$s]', shortcodeAttributes.join( '' ) ); 91 | } 92 | /** 93 | * Converts the checkbox/radio block to contact form 7 shortcode. 94 | * 95 | * @param {object} attributes - Block attributes. 96 | * @return {string} - CF7 Shortcode. 97 | */ 98 | export function convertSelectionBlockToCF7Shortcode( attributes ) { 99 | //[select* menu-970 id:asd multiple include_blank "ha" "a" "s"] 100 | const { 101 | type, 102 | id, 103 | items, 104 | name, 105 | isLabelFirst, 106 | isRequired, 107 | isMultipleSelect, 108 | isFirstBlankItem, 109 | autogeneratedName, 110 | } = attributes; 111 | 112 | const fieldName = isEmpty( name ) ? autogeneratedName : name; 113 | 114 | let shortcodeAttributes = castArray( type ); // [ type ] 115 | 116 | if ( ( isRequired && type === 'checkbox' ) || type === 'select' ) { 117 | shortcodeAttributes.push( '*' ); 118 | } 119 | 120 | shortcodeAttributes.push( ' ', fieldName ); 121 | if ( type === 'radio' ) { 122 | shortcodeAttributes.push( ' ', 'default:1' ); 123 | } 124 | if ( ! isEmpty( id ) ) { 125 | shortcodeAttributes.push( ' ', 'id:'.concat( id ) ); 126 | } 127 | 128 | if ( isLabelFirst ) { 129 | shortcodeAttributes.push( ' ', 'label_first' ); 130 | } 131 | if ( type === 'select' ) { 132 | if ( isFirstBlankItem ) { 133 | shortcodeAttributes.push( ' ', 'include_blank' ); 134 | } 135 | if ( isMultipleSelect ) { 136 | shortcodeAttributes.push( ' ', 'multiple' ); 137 | } 138 | } 139 | const options = items 140 | .map( ( item ) => 141 | item.label.trim() !== '' ? `"${ item.label }"` : '' 142 | ) 143 | .join( ' ' ) 144 | .trim(); 145 | shortcodeAttributes.push( ' ', options ); 146 | 147 | return sprintf( '[%1$s]', shortcodeAttributes.join( '' ) ); 148 | } 149 | 150 | /** 151 | * Converts the acceptance block to contact form 7 shortcode. 152 | * 153 | * @param {object} attributes - Block attributes. 154 | * @return {string} - CF7 Shortcode. 155 | */ 156 | export function convertAcceptanceBlockToCF7Shortcode( attributes ) { 157 | const { id, condition, isOptional, autogeneratedName, name } = attributes; 158 | 159 | const shortcodeAttributes = castArray( 'acceptance' ); 160 | if ( ! isEmpty( id ) ) { 161 | shortcodeAttributes.push( ' ', 'id:'.concat( id ) ); 162 | } 163 | 164 | const fieldName = isEmpty( name ) ? autogeneratedName : name; 165 | shortcodeAttributes.push( ' ', fieldName ); 166 | 167 | if ( isOptional ) { 168 | shortcodeAttributes.push( ' ', 'optional' ); 169 | } 170 | 171 | const fieldCondition = isEmpty( condition ) 172 | ? '' 173 | : `${ condition } [/acceptance]`; 174 | 175 | return sprintf( 176 | '[%1$s] %2$s', 177 | shortcodeAttributes.join( '' ), 178 | fieldCondition 179 | ); 180 | } 181 | -------------------------------------------------------------------------------- /packages/blocks-library/src/utils/generate-name.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { sprintf } from '@wordpress/i18n'; 5 | 6 | /** 7 | * Generates a new field name based on the given type. 8 | * 9 | * @param {string} type - Field type. 10 | * @return {string} - Autogenerated field name. 11 | */ 12 | export default function generateFieldName( type ) { 13 | const uniqueId = Math.floor( Math.random() * 1000 ); 14 | 15 | return sprintf( '%s-%d', type, uniqueId ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/blocks-library/src/utils/process-input-label.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generates a new field name based on the given type. 3 | * 4 | * @param {string} label - Label to process. 5 | * @param {boolean} isRequired - Is the label required. 6 | */ 7 | export default function processInputLabel( label, isRequired ) { 8 | return label + ( isRequired ? '*' : '' ); 9 | } 10 | -------------------------------------------------------------------------------- /packages/cf7-integrate/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cakewp/cf7-integrate", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@wordpress/block-editor": "7.0.2", 11 | "@wordpress/icons": "^6.1.1", 12 | "classnames": "^2.3.1" 13 | }, 14 | "devDependencies": { 15 | "@wordpress/prettier-config": "^1.1.1" 16 | } 17 | } -------------------------------------------------------------------------------- /packages/cf7-integrate/src/index.js: -------------------------------------------------------------------------------- 1 | window.addEventListener( 'cf7blocks-content-updated', ( e ) => { 2 | const newContent = e.detail.content; 3 | const targetTextarea = document.querySelector( '#wpcf7-form' ); 4 | 5 | if ( targetTextarea ) { 6 | targetTextarea.value = newContent; 7 | } 8 | } ); 9 | 10 | window.addEventListener( 'cf7blocks-fullscreen-mode', ( e ) => { 11 | let isActive = e.detail.fullscreenStatus; 12 | 13 | let cf7BlockEditor = document.querySelector( '.cf7-block-editor' ); 14 | 15 | if ( isActive ) { 16 | document.body.classList.add( 'is-fullscreen-mode' ); 17 | cf7BlockEditor.classList.add( 'is-fullscreen' ); 18 | } else { 19 | document.body.classList.remove( 'is-fullscreen-mode' ); 20 | cf7BlockEditor.classList.remove( 'is-fullscreen' ); 21 | } 22 | } ); 23 | 24 | window.addEventListener( 'load', () => { 25 | wpcf7.taggen.insert = ( content ) => { 26 | const cf7BlockEditor = document.querySelector( 27 | '.cf7-block-editor iframe' 28 | ); 29 | 30 | cf7BlockEditor.contentWindow.dispatchEvent( 31 | new CustomEvent( 'cf7blocks-insert-block', { 32 | detail: { 33 | shortcode: `${ content }`, 34 | }, 35 | } ) 36 | ); 37 | }; 38 | } ); 39 | 40 | window.addEventListener( 'cf7blocks-editor-loaded', () => { 41 | const cf7BlockEditor = document.querySelector( '.cf7-block-editor' ); 42 | 43 | cf7BlockEditor.classList.remove( 'is-loading' ); 44 | } ); 45 | -------------------------------------------------------------------------------- /packages/cf7-integrate/src/style.scss: -------------------------------------------------------------------------------- 1 | @keyframes rotating { 2 | 3 | from { 4 | -ms-transform: rotate(0deg); 5 | -moz-transform: rotate(0deg); 6 | -webkit-transform: rotate(0deg); 7 | -o-transform: rotate(0deg); 8 | transform: rotate(0deg); 9 | } 10 | 11 | to { 12 | -ms-transform: rotate(360deg); 13 | -moz-transform: rotate(360deg); 14 | -webkit-transform: rotate(360deg); 15 | -o-transform: rotate(360deg); 16 | transform: rotate(360deg); 17 | } 18 | } 19 | 20 | .cf7-block-editor { 21 | 22 | &.is-loading { 23 | height: 600px; 24 | position: relative; 25 | 26 | &::before { 27 | content: ""; 28 | position: absolute; 29 | left: 0; 30 | top: 0; 31 | width: 100%; 32 | height: 100%; 33 | background-color: #fff; 34 | } 35 | 36 | &::after { 37 | top: 50%; 38 | left: 50%; 39 | content: ""; 40 | width: 50px; 41 | height: 50px; 42 | position: absolute; 43 | background-size: 100%; 44 | background-repeat: no-repeat; 45 | transform: translate(-50%, -50%); 46 | animation: rotating 1s linear infinite; 47 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 0 553 553' fill='none' width='24' aria-hidden='true' focusable='false'%3E%3Cpath d='M276.406 20.7292C417.391 20.7292 532.073 135.427 532.073 276.406C532.073 301.792 528.313 326.307 521.38 349.49L283.458 111.573C279.422 107.516 272.859 107.516 268.807 111.573L31.3177 349.052C24.4687 326.01 20.7344 301.646 20.7344 276.406C20.7344 135.427 135.432 20.7292 276.406 20.7292Z' fill='%23F7BB5D'%3E%3C/path%3E%3Cpath d='M276.406 20.7292C271.219 20.7292 266.099 20.9427 260.995 21.25C394.818 29.2396 501.271 140.604 501.271 276.406C501.271 292.901 499.542 308.979 496.51 324.604L521.38 349.49C528.312 326.307 532.073 301.792 532.073 276.406C532.073 135.427 417.391 20.7292 276.406 20.7292Z' fill='%23E5A341'%3E%3C/path%3E%3Cpath d='M276.406 532.078C168.766 532.078 76.5104 465.177 38.875 370.807L184.661 225.021L196.484 292.068C197.219 296.427 200.62 299.839 204.979 300.599C205.552 300.703 206.141 300.734 206.724 300.734C210.458 300.734 213.953 298.724 215.807 295.38L248.661 235.703L267.807 264.203C269.833 267.24 273.286 268.984 276.927 268.781C280.552 268.604 283.833 266.526 285.552 263.323L299.766 236.698L321.635 272.604C323.672 275.964 327.406 277.865 331.365 277.537C335.286 277.198 338.688 274.682 340.141 271.016L360.875 218.286L513.781 371.198C476.047 465.365 383.901 532.078 276.406 532.078Z' fill='%23CABAA0'%3E%3C/path%3E%3Cpath d='M302.646 505.849C195.005 505.849 102.755 438.953 65.1146 344.573L38.875 370.807C76.5104 465.177 168.766 532.078 276.406 532.078C353.542 532.078 422.635 497.625 469.526 443.432C424.714 482.224 366.448 505.849 302.646 505.849Z' fill='%23B9A27F'%3E%3C/path%3E%3Cpath d='M283.458 111.573C279.422 107.516 272.859 107.516 268.807 111.573L31.3177 349.052C24.4688 326.01 20.7344 301.646 20.7344 276.406C20.7344 135.432 135.432 20.7292 276.406 20.7292C417.391 20.7292 532.073 135.432 532.073 276.406C532.073 301.792 528.313 326.307 521.38 349.49L283.458 111.573ZM202.552 207.13L276.141 133.547L344.885 202.292L328.5 244.021L308.031 210.411C306.099 207.24 302.62 205.328 298.885 205.448C295.167 205.552 291.792 207.635 290.031 210.917L275.391 238.339L256.38 210.031C254.38 207.031 250.875 205.266 247.328 205.448C243.714 205.609 240.458 207.635 238.698 210.802L212.073 259.193L202.552 207.13ZM276.406 532.078C168.766 532.078 76.5104 465.177 38.875 370.807L184.661 225.021L196.484 292.068C197.219 296.432 200.62 299.839 204.979 300.599C205.568 300.703 206.141 300.734 206.724 300.734C210.458 300.734 213.953 298.724 215.807 295.38L248.661 235.703L267.807 264.203C269.833 267.24 273.286 268.984 276.927 268.786C280.568 268.604 283.833 266.526 285.552 263.323L299.766 236.698L321.635 272.604C323.672 275.964 327.406 277.865 331.365 277.536C335.286 277.198 338.688 274.682 340.141 271.016L360.875 218.286L513.781 371.198C476.047 465.365 383.901 532.078 276.406 532.078ZM276.406 0C123.995 0 0 123.99 0 276.406C0 428.818 123.995 552.807 276.406 552.807C428.807 552.807 552.807 428.818 552.807 276.406C552.807 123.99 428.807 0 276.406 0Z' fill='%23422A1A'%3E%3C/path%3E%3C/svg%3E"); 48 | } 49 | } 50 | 51 | &.is-fullscreen { 52 | position: fixed; 53 | left: 0; 54 | top: 0; 55 | width: 100%; 56 | height: 100vh !important; 57 | 58 | iframe { 59 | height: 100vh !important; 60 | } 61 | } 62 | } 63 | 64 | body.js.is-fullscreen-mode { 65 | 66 | // Reset the html.wp-topbar padding. 67 | // Because this uses negative margins, we have to compensate for the height. 68 | margin-top: calc(var(--wp-admin--admin-bar--height, 32px) * -1); 69 | height: calc(100% + (calc(var(--wp-admin--admin-bar--height, 32px) * -1))); 70 | 71 | #adminmenumain, 72 | #wpadminbar { 73 | display: none; 74 | } 75 | 76 | #wpcontent, 77 | #wpfooter { 78 | margin-left: 0; 79 | } 80 | 81 | #wpfooter { 82 | display: none; 83 | } 84 | 85 | } 86 | 87 | .cf7-block-editor { 88 | 89 | #wpcf7-form { 90 | display: none; 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /packages/gutenberg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@cakewp/contact-form-7-gutenberg", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@wordpress/block-editor": "7.0.2", 11 | "@wordpress/icons": "^6.1.1", 12 | "classnames": "^2.3.1" 13 | }, 14 | "devDependencies": { 15 | "@wordpress/prettier-config": "^1.1.1" 16 | } 17 | } -------------------------------------------------------------------------------- /packages/gutenberg/src/components/branding/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { Icon, Button } from '@wordpress/components'; 6 | 7 | import { SVG, Path } from '@wordpress/primitives'; 8 | 9 | function Branding() { 10 | return ( 11 |
12 | 20 | 24 | 28 | 32 | 36 | 40 | 41 | } 42 | /> 43 |
44 | ); 45 | } 46 | 47 | export default Branding; 48 | -------------------------------------------------------------------------------- /packages/gutenberg/src/components/branding/style.scss: -------------------------------------------------------------------------------- 1 | 2 | .cf7blocks-branding { 3 | display: flex; 4 | padding: 20px; 5 | border-right: 1px solid #eee; 6 | height: 100%; 7 | margin-right: 10px; 8 | } 9 | -------------------------------------------------------------------------------- /packages/gutenberg/src/components/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * All Components should be included here. 3 | */ 4 | export { default as Branding } from './branding'; 5 | export { default as MoreMenu } from './more-menu'; 6 | export { default as ToggleFullScreen } from './toggle-full-screen'; 7 | export { default as WelcomeGuide } from './welcome-guide'; 8 | export { default as InteractiveEvents } from './interactive-events'; 9 | -------------------------------------------------------------------------------- /packages/gutenberg/src/components/interactive-events/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { parse } from '@wordpress/blocks'; 5 | import { useEffect } from '@wordpress/element'; 6 | import { useDispatch } from '@wordpress/data'; 7 | 8 | function InteractiveEvents() { 9 | const { insertBlocks } = useDispatch( 'core/block-editor' ); 10 | 11 | const listenShortcodeEvent = ( e ) => { 12 | insertBlocks( parse( e.detail.shortcode ) ); 13 | }; 14 | 15 | useEffect( () => { 16 | window.addEventListener( 17 | 'cf7blocks-insert-block', 18 | listenShortcodeEvent 19 | ); 20 | 21 | return () => 22 | window.removeEventListener( 23 | 'cf7blocks-insert-block', 24 | listenShortcodeEvent 25 | ); 26 | } ); 27 | 28 | return null; 29 | } 30 | 31 | export default InteractiveEvents; 32 | -------------------------------------------------------------------------------- /packages/gutenberg/src/components/more-menu/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Components 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { useDispatch } from '@wordpress/data'; 6 | import { MenuGroup, MenuItem } from '@wordpress/components'; 7 | import { help, external, starEmpty, sparkles, info } from '@wordpress/icons'; 8 | 9 | /** 10 | * Custom Dependencies 11 | */ 12 | import { coffee, github } from '../../icons'; 13 | 14 | function MoreMenu( props ) { 15 | const { toggleFeature } = useDispatch( 'core/interface' ); 16 | 17 | return ( 18 | 19 | toggleFeature( 'cf7BlocksWelcomeGuide' ) } 22 | > 23 | { __( 'Show Guide', 'cf7-blocks' ) } 24 | 25 | 26 | 32 | { __( 'Submit a Review', 'cf7-blocks' ) } 33 | 34 | 35 | 41 | { __( 'Report a bug', 'cf7-blocks' ) } 42 | 43 | 49 | { __( 'Contribute', 'cf7-blocks' ) } 50 | 51 | 56 | { __( 'Visit Website', 'cf7-blocks' ) } 57 | 58 | 59 |
60 | 61 | 70 | { __( 'Buy me a Coffee', 'cf7-blocks' ) } 71 | 72 |
73 | ); 74 | } 75 | 76 | export default MoreMenu; 77 | -------------------------------------------------------------------------------- /packages/gutenberg/src/components/toggle-full-screen/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WordPress Dependencies 3 | */ 4 | import { __ } from '@wordpress/i18n'; 5 | import { fullscreen } from '@wordpress/icons'; 6 | import { useState } from '@wordpress/element'; 7 | import { Button } from '@wordpress/components'; 8 | 9 | function ToggleFullScreen() { 10 | const [ isFullScreenEnabled, setFullScreenStatus ] = useState( false ); 11 | 12 | const toggleFullScreen = () => { 13 | setFullScreenStatus( ! isFullScreenEnabled ); 14 | 15 | // Updating it to the parent frame. 16 | parent.window.dispatchEvent( 17 | new CustomEvent( 'cf7blocks-fullscreen-mode', { 18 | detail: { 19 | fullscreenStatus: ! isFullScreenEnabled, 20 | }, 21 | } ) 22 | ); 23 | }; 24 | 25 | return ( 26 |