├── .distignore
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .github
└── workflows
│ ├── deploy-plugin.yml
│ └── fetch-extendify.yml
├── .gitignore
├── .nvmrc
├── .package.json.swp
├── LICENSE
├── Utils
└── Bucket.php
├── admin
├── .nvmrc
├── admin.php
├── api
│ └── addons.js
├── components
│ ├── Icons.js
│ ├── integration-block.js
│ ├── logo.js
│ ├── notices.js
│ └── skeleton-row.js
├── contants.js
├── cpt
│ ├── entry.php
│ ├── form.php
│ └── reusable.php
├── extend
│ ├── SlotFillCreator.js
│ ├── addonsProvider.js
│ ├── index.js
│ └── renderAddon.js
├── functions
│ └── index.js
├── hooks
│ ├── usePrevious.js
│ └── useStateCallback.js
├── index.js
├── layout
│ ├── header.js
│ ├── index.js
│ └── navigation.js
├── package-lock.json
├── package.json
├── pages
│ ├── dashboard
│ │ ├── components
│ │ │ └── action.js
│ │ └── dashboard.js
│ ├── integrations
│ │ ├── components
│ │ │ ├── availableIntegration.js
│ │ │ ├── error.js
│ │ │ ├── install_integrations.js
│ │ │ └── loading.js
│ │ └── integrations.js
│ └── settings
│ │ ├── components
│ │ └── sidebar.js
│ │ ├── settings.js
│ │ └── subpages
│ │ ├── general
│ │ ├── components
│ │ │ └── messages.js
│ │ └── general.js
│ │ ├── import
│ │ ├── components
│ │ │ ├── plugin.js
│ │ │ ├── pluginImport.js
│ │ │ └── pluginList.js
│ │ └── index.import.js
│ │ └── integrationSettings
│ │ ├── components
│ │ └── fields.js
│ │ └── integrationSettings.js
├── postcss.config.js
├── redux
│ ├── actions
│ │ ├── entries
│ │ │ ├── getEntries.js
│ │ │ ├── setRefetchingStatus.js
│ │ │ └── updateEntry.js
│ │ ├── generalSettings
│ │ │ ├── saveSettings.js
│ │ │ └── updateSettings.js
│ │ ├── notice
│ │ │ ├── addNotice.js
│ │ │ └── removeNotice.js
│ │ ├── settings
│ │ │ ├── loading.js
│ │ │ ├── setIntegration.js
│ │ │ └── setIntegrationDetails.js
│ │ └── types.js
│ ├── reducers
│ │ ├── entries.js
│ │ ├── generalSettings.js
│ │ ├── index.js
│ │ ├── information.js
│ │ ├── notice.js
│ │ └── settings.js
│ └── store
│ │ └── store.js
├── style.scss
├── styles
│ ├── _mixins.scss
│ ├── _utils.scss
│ ├── breadcrumbs.scss
│ ├── integrations
│ │ └── integrations.scss
│ ├── settings
│ │ ├── settings.scss
│ │ └── sub
│ │ │ ├── generalSettings.scss
│ │ │ ├── importSettings.scss
│ │ │ ├── integrationSettings.scss
│ │ │ └── sidebar.scss
│ └── variables.scss
└── tailwind.config.js
├── assets
├── index.assets.php
└── scripts
│ └── google_recaptcha.js
├── config
├── externals.js
├── paths.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── controllers
├── forms
│ ├── forms.controller.php
│ ├── forms.import.controller.php
│ └── import-managers
│ │ ├── cf7.forms.import.php
│ │ └── test.html
└── index.php
├── dist
├── admin
│ ├── index.asset.php
│ ├── index.js
│ └── style-index.css
├── blocks.build.js
├── blocks.editor.build.css
├── blocks.style.build.css
└── frontend.js
├── gutenberg-forms.php
├── integrations
├── handler.php
└── recaptcha
│ └── guide
│ └── guide.html
├── languages
└── forms-gutenberg.pot
├── lib
└── block
│ ├── api
│ └── index.js
│ └── block.js
├── package-lock.json
├── package.json
├── readme.md
├── readme.txt
├── scripts
├── build.js
├── start.js
└── translate.js
├── src
├── _partials
│ ├── _extend_fields.scss
│ ├── _keyframes.scss
│ ├── _mixins.scss
│ ├── _pre_mixins.scss
│ └── _variables.scss
├── block
│ ├── Icon.js
│ ├── api
│ │ └── index.js
│ ├── block.js
│ ├── components
│ │ ├── condition.js
│ │ ├── datepicker.js
│ │ ├── formulaBuilder.js
│ │ ├── imagePreview.js
│ │ ├── imageUpload.js
│ │ ├── searchTags.js
│ │ ├── tagList.js
│ │ └── tagSelector.js
│ ├── constants
│ │ └── index.js
│ ├── editor.scss
│ ├── fieldStyles
│ │ └── index.js
│ ├── formStyles
│ │ └── index.js
│ ├── functions
│ │ └── index.js
│ ├── lib
│ │ └── datePicker.scss
│ ├── misc
│ │ └── helper.js
│ └── style.scss
├── blocks.js
├── blocks
│ ├── checkbox
│ │ ├── block.json
│ │ ├── deprecated.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── column
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── components
│ │ ├── bulk_add.js
│ │ ├── prefix.js
│ │ └── suffix.js
│ ├── datepicker
│ │ ├── block.json
│ │ ├── deprecated
│ │ │ ├── deprecated.js
│ │ │ ├── edit.js
│ │ │ └── save.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── email
│ │ ├── block.json
│ │ ├── deprecated
│ │ │ ├── deprecation.js
│ │ │ ├── edit.js
│ │ │ └── save.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── file_upload
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── form-button
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── form-calculation
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── form-column
│ │ ├── block.json
│ │ ├── components
│ │ │ └── introduction.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── form-group
│ │ ├── Inspector.js
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── form-steps
│ │ ├── childs
│ │ │ └── form-step
│ │ │ │ ├── block.json
│ │ │ │ ├── edit.js
│ │ │ │ ├── index.js
│ │ │ │ └── save.js
│ │ └── root
│ │ │ ├── block.json
│ │ │ ├── edit.js
│ │ │ ├── index.js
│ │ │ ├── save.js
│ │ │ └── toolbar.js
│ ├── gutenberg-forms
│ │ ├── Inspector.js
│ │ ├── block.json
│ │ ├── components
│ │ │ ├── Integrations.js
│ │ │ ├── fieldPlotter.js
│ │ │ ├── introduction.js
│ │ │ ├── messages.js
│ │ │ └── templateBuilder.js
│ │ ├── deprecated
│ │ │ ├── deprecated.js
│ │ │ ├── edit.js
│ │ │ └── save.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ ├── save.js
│ │ └── settings_modal
│ │ │ ├── components
│ │ │ ├── Empty.js
│ │ │ ├── PostTypeBlock.js
│ │ │ ├── header.js
│ │ │ ├── preview_block.js
│ │ │ ├── sidebar.js
│ │ │ └── templates.js
│ │ │ └── modal.js
│ ├── hidden
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── message
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── name
│ │ ├── block.json
│ │ ├── deprecated
│ │ │ ├── deprecated.js
│ │ │ ├── edit.js
│ │ │ └── save.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ ├── save.js
│ │ └── style.scss
│ ├── number
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── phone
│ │ ├── block.json
│ │ ├── deprecated
│ │ │ ├── deprected.js
│ │ │ ├── edit.js
│ │ │ └── save.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── progress
│ │ ├── block.json
│ │ ├── components
│ │ │ └── progressBar.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── radio
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── reusable_forms
│ │ ├── block.json
│ │ ├── components
│ │ │ └── introduction.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── select
│ │ ├── block.json
│ │ ├── deprecated
│ │ │ ├── deprecated.js
│ │ │ ├── edit.js
│ │ │ └── save.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── text
│ │ ├── block.json
│ │ ├── deprecated
│ │ │ ├── deprecated.js
│ │ │ ├── edit.js
│ │ │ └── save.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ ├── website
│ │ ├── block.json
│ │ ├── deprecated
│ │ │ ├── deprecated.js
│ │ │ ├── edit.js
│ │ │ └── save.js
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
│ └── yes-no
│ │ ├── block.json
│ │ ├── edit.js
│ │ ├── index.js
│ │ └── save.js
├── common.scss
├── constants.js
├── extend
│ └── index.js
└── init.php
├── tagsHandler
└── tagHandler.php
└── triggers
├── email.php
└── validator.php
/.distignore:
--------------------------------------------------------------------------------
1 | # A set of files you probably don't want in your WordPress.org distribution
2 | .distignore
3 | .editorconfig
4 | .git
5 | .gitignore
6 | .gitlab-ci.yml
7 | .travis.yml
8 | .DS_Store
9 | .browserslistrc
10 | .eslintrc.json
11 | .eslintignore
12 | .npmrc
13 | .nvmrc
14 | .stylelintrc
15 | .stylelintignore
16 | babel.config.js
17 | Thumbs.db
18 | behat.yml
19 | bin
20 | circle.yml
21 | composer.json
22 | composer.lock
23 | Gruntfile.js
24 | package.json
25 | package-lock.json
26 | phpunit.xml
27 | phpunit.xml.dist
28 | multisite.xml
29 | multisite.xml.dist
30 | phpcs.ruleset.xml
31 | phpcs.xml
32 | phpcs.xml.dist
33 | README.md
34 | wp-cli.local.yml
35 | tests
36 | node_modules
37 | *.zip
38 | *.tar.gz
39 | tests
40 | config
41 | postcss.config.js
42 | yarn.lock
43 | .wordpress-org
44 | .github
45 | LICENSE.md
46 | .babelrc
47 | DOCKER_ENV
48 | docker_tag
49 | output.log
50 | webpack.config.js
51 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | # WordPress Coding Standards
5 | # https://make.wordpress.org/core/handbook/coding-standards/
6 |
7 | root = true
8 |
9 | [*]
10 | charset = utf-8
11 | end_of_line = lf
12 | insert_final_newline = true
13 | trim_trailing_whitespace = true
14 | indent_style = tab
15 | indent_size = 4
16 |
17 | [*.yml]
18 | indent_style = space
19 | indent_size = 2
20 |
21 | [*.md]
22 | trim_trailing_whitespace = false
23 |
24 | [*.txt]
25 | end_of_line = crlf
26 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.min.js
2 | **/*.build.js
3 | **/node_modules/**
4 | **/vendor/**
5 | build
6 | coverage
7 | cypress
8 | node_modules
9 | vendor
10 |
--------------------------------------------------------------------------------
/.github/workflows/deploy-plugin.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to WordPress.org
2 | on:
3 | push:
4 | tags:
5 | - "*"
6 | jobs:
7 | tag:
8 | name: New tag
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout code
12 | uses: actions/checkout@v1
13 | - name: Using Node version ${{ matrix.node-version }}
14 | uses: actions/setup-node@v1
15 | with:
16 | node-version: ${{ matrix.node-version }}
17 |
18 | - name: WordPress Plugin Deploy
19 | uses: 10up/action-wordpress-plugin-deploy@stable
20 | env:
21 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
22 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
23 | SLUG: forms-gutenberg
24 |
--------------------------------------------------------------------------------
/.github/workflows/fetch-extendify.yml:
--------------------------------------------------------------------------------
1 | name: Fetch Extendify and create PR
2 | on: workflow_dispatch
3 | jobs:
4 | fetch-and-pr:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - name: Checkout code
8 | uses: actions/checkout@v2
9 | - name: Setup PHP
10 | uses: shivammathur/setup-php@v2
11 | - name: Download and replace code
12 | run: |
13 | # 1. Clean up the existing directory
14 | rm -rf ./extendify-sdk && mkdir ./extendify-sdk && mkdir ./extendify-tmp
15 | # 2. Fetch from .org
16 | curl -LO https://downloads.wordpress.org/plugin/extendify.latest-stable.zip
17 | # 3. Unzip to temp directory
18 | unzip ./extendify.latest-stable.zip -d ./extendify-tmp
19 | # 4. Copy to the sdk directory
20 | cp -R ./extendify-tmp/extendify/* ./extendify-sdk
21 | # 5. Cleanup directories and remove zip
22 | rm -rf ./extendify-tmp && rm ./extendify.latest-stable.zip
23 | # 6. Remove main file header content
24 | php -w ./extendify-sdk/extendify.php > ./ext.tmp && mv ./ext.tmp ./extendify-sdk/extendify.php
25 | - name: Create Pull Request
26 | uses: peter-evans/create-pull-request@v3
27 | with:
28 | commit-message: Update Extendify Library
29 | title: Update Extendify Library
30 | branch: update-extendify
31 | branch-suffix: random
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .vscode
3 | dist/dashboard/blocks
4 |
5 | ## Uncomment line below if you prefer to
6 | ## keep compiled files out of version control
7 | # dist/
8 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 12.22.12
2 |
--------------------------------------------------------------------------------
/.package.json.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikolaystrikhar/gutenberg-forms/6106944153b490a8846ccec3c68f6dd6cf8602c5/.package.json.swp
--------------------------------------------------------------------------------
/Utils/Bucket.php:
--------------------------------------------------------------------------------
1 | $file_path,
30 | 'filename' => $file_name,
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/admin/.nvmrc:
--------------------------------------------------------------------------------
1 | 14.21.2
2 |
--------------------------------------------------------------------------------
/admin/api/addons.js:
--------------------------------------------------------------------------------
1 | import { isEqual } from 'lodash';
2 | const { applyFilters } = wp.hooks;
3 |
4 | /**
5 | * Will check if the addon is available in the registered addons list
6 | * @param {string} slug of the addons
7 | * @return {boolean} existence
8 | */
9 |
10 | export const addonExist = (slug) => {
11 | const addonsList = applyFilters('cwp_gf.registerAddon', []);
12 | const requiredAddon = addonsList.filter((addon) =>
13 | isEqual(addon.slug, slug)
14 | );
15 |
16 | return requiredAddon.length !== 0 ? true : false;
17 | };
18 |
--------------------------------------------------------------------------------
/admin/components/notices.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { get, isEmpty } from 'lodash';
4 | import { Notice } from '@wordpress/components';
5 | import { removeNotice } from '../redux/actions/notice/removeNotice';
6 |
7 | function Notices(props) {
8 | return (
9 |
10 | {props.notice.data.map((notice, index) => {
11 | const noticeStatus = get(notice, 'status');
12 | const status = isEmpty(noticeStatus) ? 'warning' : noticeStatus;
13 | const message = get(notice, 'message');
14 | const id = get(notice, 'id');
15 |
16 | return (
17 |
props.removeNotice(id)}
23 | >
24 |
25 |
26 | );
27 | })}
28 |
29 | );
30 | }
31 |
32 | const mapStateToProps = (state) => {
33 | const { notice } = state;
34 |
35 | return {
36 | notice,
37 | };
38 | };
39 |
40 | const mapDispatchToProps = {
41 | removeNotice,
42 | };
43 |
44 | export default connect(mapStateToProps, mapDispatchToProps)(Notices);
45 |
--------------------------------------------------------------------------------
/admin/components/skeleton-row.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function SkeletonRow(props) {
4 | return
;
5 | }
6 |
7 | export default SkeletonRow;
8 |
--------------------------------------------------------------------------------
/admin/contants.js:
--------------------------------------------------------------------------------
1 | export const TEXT_DOMAIN = 'forms-gutenberg'; // text-domain
2 | export const proxy = 'https://api.airtable.com/v0/appywrdcoWllMrj0o/Plugins';
3 | export const airtable_key = 'keyL7TsjgPHy6A6CT';
4 |
--------------------------------------------------------------------------------
/admin/cpt/reusable.php:
--------------------------------------------------------------------------------
1 | post_status === 'publish' ? $post->post_content : '';
8 | }
9 |
10 | function short_code_callback( $atts ) {
11 | extract( shortcode_atts(
12 | array(
13 | 'id' => '',
14 | ),
15 | $atts
16 | ) );
17 |
18 | $content = get_block( $id );
19 |
20 | return $content;
21 | }
22 |
23 | function register_form_shortcode( $post_type ) {
24 | // adding a custom short_code that reflects to the post_id;
25 | add_shortcode(
26 | 'gutenberg_form',
27 | 'short_code_callback'
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/admin/extend/SlotFillCreator.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inserter will just act as an anonymous parent wrapper for inserting child components
3 | * can be used to extend dashboard features
4 | */
5 |
6 | import React from 'react';
7 | import { createSlotFill } from '@wordpress/components';
8 |
9 | export function SlotFillCreator(fill_name) {
10 | const modified_fill_name = 'gf_extend_'.concat(fill_name);
11 | const { Fill, Slot } = createSlotFill(modified_fill_name);
12 |
13 | return {
14 | Fill,
15 | Slot,
16 | };
17 | }
18 |
--------------------------------------------------------------------------------
/admin/extend/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * EXTEND FEATURES
3 | * This directory is responsible for managing all the dashboard features that can be extended
4 | * by using Addons / Integrations
5 | *
6 | * This extend object is formatted by their respected pages and their sub-page then
7 | * their respective components
8 | */
9 |
10 | import { SlotFillCreator } from './SlotFillCreator';
11 | import { set, get, each } from 'lodash';
12 | const availableIntegrations = get(cwp_global, 'settings.integrations');
13 |
14 | export const extend = {
15 | dashboard: {
16 | cards: {},
17 | },
18 | entries: {
19 | entry: {
20 | details: {
21 | footer: {
22 | actions: SlotFillCreator(
23 | 'entries_entry_details_footer_actions'
24 | ),
25 | links: SlotFillCreator(
26 | 'entries_entry_details_footer_links'
27 | ),
28 | },
29 | },
30 | },
31 | },
32 | settings: {
33 | integrations: {
34 | fields: {},
35 | },
36 | },
37 | };
38 |
39 | each(availableIntegrations, (_, name) => {
40 | extend['settings']['integrations']['fields'][name] = SlotFillCreator(
41 | `settings_integrations_${name}_fields`
42 | );
43 | });
44 |
45 | set(window, 'cwp_gf.extend', extend);
46 |
--------------------------------------------------------------------------------
/admin/extend/renderAddon.js:
--------------------------------------------------------------------------------
1 | import { get, set, clone } from 'lodash';
2 | import { addonExist } from '../api/addons';
3 | import { TEXT_DOMAIN } from '../contants';
4 | const { addFilter, doAction } = wp.hooks;
5 | const GlobalComponents = get(window, 'cwp_gf');
6 | const { __ } = wp.i18n;
7 |
8 | /**
9 | *
10 | * Will register a new addon for gutenberg forms
11 | * @param {string} slug - Addon Slug
12 | * @param {object} config - Addon Configurations
13 | *
14 | * @example
15 | *
16 | *
17 | registerAddon("cwp_gf_report_entry_export_btn", {
18 | renderSlot: "entries.entry.details.footer.actions",
19 | render: () => HELLO WORLD ,
20 | });
21 |
22 | *
23 | */
24 |
25 | function registerAddon(slug, config) {
26 | if (typeof slug !== 'string') {
27 | console.error(__(`Addon Slug must be of type string.`, TEXT_DOMAIN));
28 | }
29 |
30 | if (typeof config !== 'object') {
31 | console.error(
32 | __(`Addon Config for ${slug} must be an object`, TEXT_DOMAIN)
33 | );
34 | }
35 |
36 | if (!'parent' in config || typeof config.parent !== 'string') {
37 | console.error(
38 | __(
39 | `Addon ${slug} must have a valid parent integration`,
40 | TEXT_DOMAIN
41 | )
42 | );
43 | }
44 |
45 | if (addonExist(slug)) {
46 | console.error(__(`Addon with slug ${slug} already exist`, TEXT_DOMAIN));
47 | return; // cannot execute code further due to duplication issue
48 | }
49 |
50 | const data = {
51 | slug,
52 | config,
53 | };
54 |
55 | addFilter(
56 | 'cwp_gf.registerAddon',
57 | 'cwp/gutenberg-forms/registerAddon',
58 | (addons) => {
59 | const newAddons = clone(addons);
60 |
61 | newAddons.push(data);
62 | return newAddons;
63 | }
64 | );
65 |
66 | doAction('cwp_gf.update_addons');
67 | }
68 |
69 | set(window, 'cwp_gf.extend.registerAddon', registerAddon);
70 |
--------------------------------------------------------------------------------
/admin/functions/index.js:
--------------------------------------------------------------------------------
1 | import { SETTINGS_SAVING, SETTINGS_SAVED } from '../redux/actions/types';
2 | import { __ } from '@wordpress/i18n';
3 |
4 | export function parseSettingStatus(status) {
5 | switch (status) {
6 | case SETTINGS_SAVING:
7 | return __('Saving', 'forms-gutenberg');
8 |
9 | case SETTINGS_SAVED:
10 | return __('Saved', 'forms-gutenberg');
11 | }
12 | }
13 |
14 | export function parse_guide(html) {
15 | let breaker = '';
16 | const pages = html.split(breaker);
17 |
18 | return pages;
19 | }
20 |
21 | export function makeid(length) {
22 | var result = '';
23 | var characters =
24 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
25 | var charactersLength = characters.length;
26 | for (var i = 0; i < length; i++) {
27 | result += characters.charAt(
28 | Math.floor(Math.random() * charactersLength)
29 | );
30 | }
31 | return result;
32 | }
33 |
34 | /**
35 | * Will convert object into query string
36 | * @param {object} params the query object
37 | * @return {string} query string
38 | */
39 | export function httpQuery(params) {
40 | const qs = Object.keys(params)
41 | .map((key) => `${key}=${params[key]}`)
42 | .join('&');
43 | return qs;
44 | }
45 |
46 | /**
47 | * Will return the array count to a certain range
48 | * @param {number} range
49 | * @return {array}
50 | */
51 | export function createArrayToNum(range) {
52 | let requiredArray = [];
53 |
54 | for (let i = 0; i <= range; ++i) {
55 | requiredArray.push(i);
56 | }
57 |
58 | return requiredArray;
59 | }
60 |
--------------------------------------------------------------------------------
/admin/hooks/usePrevious.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react';
2 |
3 | /**
4 | * Will return the previous instance of the given value in the functional component
5 | * @param {any} value
6 | */
7 |
8 | export function usePrevious(value) {
9 | const ref = useRef();
10 | useEffect(() => {
11 | ref.current = value;
12 | });
13 | return ref.current;
14 | }
15 |
--------------------------------------------------------------------------------
/admin/hooks/useStateCallback.js:
--------------------------------------------------------------------------------
1 | import { useState, useRef, useEffect } from 'react';
2 | /**
3 | * Will allow passing the callback when the state updated
4 | * @param {object} initialState
5 | */
6 |
7 | export function useStateCallback(initialState) {
8 | const [state, setState] = useState(initialState);
9 | const cbRef = useRef(null); // mutable ref to store current callback
10 |
11 | const setStateCallback = (state, cb) => {
12 | cbRef.current = cb; // store passed callback to ref
13 | setState(state);
14 | };
15 |
16 | useEffect(() => {
17 | // cb.current is `null` on initial render, so we only execute cb on state *updates*
18 | if (cbRef.current) {
19 | cbRef.current(state);
20 | cbRef.current = null; // reset callback after execution
21 | }
22 | }, [state]);
23 |
24 | return [state, setStateCallback];
25 | }
26 |
--------------------------------------------------------------------------------
/admin/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable camelcase */
2 | /**
3 | * WordPress dependencies
4 | */
5 | const { Component } = wp.element;
6 | import './extend';
7 | import './extend/renderAddon';
8 |
9 | /**
10 | * Internal dependencies
11 | */
12 | import { render } from 'react-dom';
13 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
14 | import Layout from './layout';
15 | import Dashboard from './pages/dashboard/dashboard';
16 | import Integrations from './pages/integrations/integrations';
17 | import Notices from './components/notices';
18 | import Settings from './pages/settings/settings';
19 | import { Provider } from 'react-redux';
20 | import store from './redux/store/store';
21 | import { SlotFillProvider } from '@wordpress/components';
22 | import AddonsProvider from './extend/addonsProvider';
23 | import './style.scss';
24 |
25 | class App extends Component {
26 | render() {
27 | return (
28 |
29 |
30 |
31 | {
34 | const { hash } = props.location;
35 |
36 | const CurrentPage = () => {
37 | if (hash.startsWith('#/dashboard')) {
38 | return ;
39 | } else if (
40 | hash.startsWith('#/integrations')
41 | ) {
42 | return ;
43 | } else if (hash.startsWith('#/settings')) {
44 | return ;
45 | } else {
46 | return ;
47 | }
48 | };
49 |
50 | return (
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | );
59 | }}
60 | />
61 |
62 |
63 |
64 | );
65 | }
66 | }
67 |
68 | const root = document.querySelector('#cwp-gutenberg-forms-dashboard-root');
69 |
70 | if (root) {
71 | render( , root);
72 | }
73 |
--------------------------------------------------------------------------------
/admin/layout/header.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Logo from '../components/logo';
3 | import Navigation from './Navigation';
4 | import { withRouter } from 'react-router-dom';
5 |
6 | const Header = props => (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
22 |
23 |
24 |
25 |
26 | );
27 |
28 | export default withRouter(Header);
29 |
--------------------------------------------------------------------------------
/admin/layout/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Header from './header';
3 |
4 | function Layout(props) {
5 | return (
6 |
7 |
8 |
9 | {props.children}
10 |
11 |
12 | );
13 | }
14 |
15 | export default Layout;
16 |
--------------------------------------------------------------------------------
/admin/layout/navigation.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { TabPanel, Button } from '@wordpress/components';
3 | import { isEmpty, isEqual, get } from 'lodash';
4 | import { withRouter } from 'react-router-dom';
5 |
6 | const { __ } = wp.i18n;
7 |
8 | function Navigation(props) {
9 | const [currentTab, setCurrentTab] = useState(''); // current tab name here...
10 |
11 | const { hash, pathname, search } = props.location;
12 | const customTabs = get(props, 'tabs');
13 |
14 | const tabs = isEmpty(customTabs)
15 | ? [
16 | {
17 | name: '#/dashboard',
18 | title: __('Dashboard', 'forms-gutenberg'),
19 | },
20 | {
21 | name: '#/integrations',
22 | title: __('Extensions', 'forms-gutenberg'),
23 | },
24 | {
25 | name: '#/settings',
26 | title: __('Settings', 'forms-gutenberg'),
27 | },
28 | ]
29 | : customTabs;
30 |
31 | const getInitialTab = () => {
32 | let initialTab = isEmpty(hash) ? '#/dashboard' : hash;
33 |
34 | tabs.forEach((tab) => {
35 | if (initialTab.startsWith(tab.name)) {
36 | initialTab = tab.name;
37 | }
38 | });
39 |
40 | return initialTab;
41 | };
42 |
43 | useEffect(() => {
44 | setCurrentTab(getInitialTab());
45 | }, []);
46 |
47 | useEffect(() => {
48 | setCurrentTab(getInitialTab()); // subscribing to whenever page hash changes to update the active button
49 | }, [hash]);
50 |
51 | const onSelect = (tab) => {
52 | if (isEqual(tab.name, currentTab)) return; // if the page is already active
53 |
54 | setCurrentTab(tab.name);
55 |
56 | props.history.push({
57 | pathname,
58 | search,
59 | hash: tab.name,
60 | });
61 | };
62 |
63 | return (
64 |
65 | {tabs.map((tab, index) => {
66 | const isActive = isEqual(tab.name, currentTab);
67 | const activeClassName = isActive ? 'gufo-text-white hover:gufo-text-white' : 'gufo-text-cyan-100 hover:gufo-text-cyan-100';
68 | const className = 'gufo-text-sm gufo-rounded-md gufo-bg-white gufo-bg-opacity-0 gufo-px-3 gufo-py-2 hover:gufo-bg-opacity-10 '.concat(activeClassName);
69 |
70 | return (
71 | onSelect(tab)}
75 | >
76 | {tab.title}
77 |
78 | );
79 | })}
80 |
81 | );
82 | }
83 |
84 | export default withRouter(Navigation);
85 |
--------------------------------------------------------------------------------
/admin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-awesome-plugin",
3 | "version": "1.0.0",
4 | "description": "I build, therefore I'm awesome.",
5 | "scripts": {
6 | "build": "wp-scripts build ./index.js --output-path=./../dist/admin",
7 | "start": "wp-scripts start ./index.js --output-path=./../dist/admin",
8 | "packages-update": "wp-scripts packages-update"
9 | },
10 | "devDependencies": {
11 | "@tailwindcss/forms": "^0.5.3",
12 | "@wordpress/scripts": "^19.1.0",
13 | "autoprefixer": "^10.4.13",
14 | "postcss": "^8.4.21",
15 | "tailwindcss": "^3.2.4"
16 | },
17 | "dependencies": {
18 | "@nivo/bar": "^0.79.1",
19 | "@nivo/core": "^0.79.0",
20 | "css-loader": "^6.5.1",
21 | "node-sass": "^7.0.1",
22 | "postcss-loader": "^6.2.1",
23 | "prettier": "^2.5.1",
24 | "react-redux": "^7.2.6",
25 | "react-router": "^5.2.1",
26 | "react-router-dom": "^5.2.1",
27 | "redux-devtools-extension": "^2.13.9",
28 | "redux-thunk": "^2.4.1",
29 | "sass-loader": "^12.4.0",
30 | "unitize": "0.0.3"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/admin/pages/dashboard/components/action.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Action = ({ data }) => (
4 |
5 |
6 |
10 |
11 |
12 |
13 |
19 |
20 |
21 | {data.description}
22 |
23 |
24 |
25 |
28 | {data.external ? (
29 |
30 |
31 |
32 | ) : (
33 |
34 |
35 |
36 | )}
37 |
38 |
39 | );
40 |
41 | export default Action;
42 |
--------------------------------------------------------------------------------
/admin/pages/dashboard/dashboard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {connect} from "react-redux";
3 | import Action from "./components/action";
4 |
5 | const Dashboard = props => (
6 |
7 | {props.information.cards.map((data, key) =>
)}
8 |
9 | );
10 |
11 | const mapStateToProps = (state) => {
12 | return {
13 | information: state.information,
14 | };
15 | };
16 |
17 | export default connect(mapStateToProps, null)(Dashboard);
18 |
--------------------------------------------------------------------------------
/admin/pages/integrations/components/error.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button } from '@wordpress/components';
3 |
4 | function FetchError(props) {
5 | return (
6 |
7 |
8 | Oops! Something went wrong{' '}
9 |
10 | Try Again
11 |
12 |
13 |
14 | );
15 | }
16 |
17 | export default FetchError;
18 |
--------------------------------------------------------------------------------
/admin/pages/integrations/components/install_integrations.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, Fragment } from 'react';
2 | import axios from 'axios';
3 | import { proxy, airtable_key } from '../../../contants';
4 | import { get, isEmpty, map } from 'lodash';
5 | import Loading from './loading';
6 | import FetchError from './error';
7 | import AvailableIntegration from './availableIntegration';
8 | const { __ } = wp.i18n;
9 |
10 | function InstallIntegrations() {
11 | const [availableIntegrations, setAvailableIntegrations] = useState([]);
12 | const [loading, setLoading] = useState(false);
13 | const [error, setError] = useState(false);
14 |
15 | const fetchRecords = () => {
16 | setLoading(true);
17 | setError(false);
18 |
19 | // TODO: Remove addons fetching. They should be static.
20 | axios
21 | .get(proxy, {
22 | headers: {
23 | Authorization: `Bearer ${airtable_key}`,
24 | },
25 | })
26 | .then((response) => {
27 | const records = get(response, 'data.records');
28 | const fetched_records = isEmpty(records) ? [] : records; // null exception
29 | const global_integrations = get(
30 | window,
31 | 'cwp_global.settings.integrations'
32 | );
33 |
34 | const filtered_records = fetched_records.filter((record) => {
35 | const plugin_id = get(record, 'fields.plugin_id');
36 |
37 | if (plugin_id in global_integrations) {
38 | return false;
39 | }
40 |
41 | return true;
42 | }); // filtering the integrations that are already installed
43 |
44 | setAvailableIntegrations(filtered_records);
45 | setLoading(false);
46 | setError(false);
47 | })
48 | .catch((err) => {
49 | console.error(err);
50 | setLoading(false);
51 | setError(true);
52 | });
53 | };
54 |
55 | useEffect(() => {
56 | fetchRecords();
57 | }, []);
58 |
59 | return (
60 |
61 | {!loading && !error && !isEmpty(availableIntegrations) && (
62 |
63 | {__('Available', 'forms-gutenberg')}
64 |
65 | )}
66 | {loading && !error ? (
67 |
68 | ) : (
69 |
70 | {map(availableIntegrations, (integration, index) => {
71 | return (
72 |
76 | );
77 | })}
78 |
79 | )}
80 | {!loading && error &&
}
81 |
82 | );
83 | }
84 |
85 | export default InstallIntegrations;
86 |
--------------------------------------------------------------------------------
/admin/pages/integrations/components/loading.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Spinner } from '@wordpress/components';
3 |
4 | function Loading() {
5 | return (
6 |
7 |
8 | Finding More Available Addons / Integrations
9 |
10 |
11 | );
12 | }
13 |
14 | export default Loading;
15 |
--------------------------------------------------------------------------------
/admin/pages/integrations/integrations.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import IntegrationBlock from '../../components/integration-block';
3 | import { map, isEmpty, get } from 'lodash';
4 | import { connect } from 'react-redux';
5 | import { Snackbar } from '@wordpress/components';
6 | import { parseSettingStatus } from '../../functions';
7 | import InstallIntegrations from './components/install_integrations';
8 | const { __ } = wp.i18n;
9 |
10 | function Integrations(props) {
11 | const { integrations, loading } = props.settings;
12 |
13 | return (
14 |
15 |
16 | {__('Installed', 'forms-gutenberg')}
17 |
18 |
19 |
20 |
21 | {map(integrations, (integration, name) => {
22 | const {
23 | title,
24 | banner,
25 | description,
26 | fields,
27 | enable,
28 | guide,
29 | is_pro,
30 | } = integration;
31 |
32 | const is_disabled = get(integration, 'is_disabled');
33 | const error = get(integration, 'error');
34 | const guide_url = get(integration, 'guide_url');
35 |
36 | return (
37 |
52 | );
53 | })}
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {!isEmpty(loading) && (
63 | {parseSettingStatus(loading)}
64 | )}
65 |
66 |
67 | );
68 | }
69 |
70 | const mapStateToProps = (state) => {
71 | const { settings } = state;
72 |
73 | return {
74 | settings,
75 | };
76 | };
77 |
78 | export default connect(mapStateToProps, null)(Integrations);
79 |
--------------------------------------------------------------------------------
/admin/pages/settings/components/sidebar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { MenuGroup, MenuItem } from '@wordpress/components';
4 | import { map, isEqual, isEmpty, get, each } from 'lodash';
5 |
6 | function Sidebar(props) {
7 | const { integrations } = props.settings;
8 | const { hash, pathname, search } = props.location;
9 |
10 | const getActiveClass = (name) => {
11 | const currentHash = '#/settings/' + name;
12 | const searchHash = hash === '#/settings' ? '#/settings/general' : hash;
13 |
14 | if (isEqual(searchHash, currentHash)) {
15 | return 'gufo-font-medium gufo-text-gray-900 gufo-bg-gray-100';
16 | }
17 |
18 | return '';
19 | };
20 |
21 | const getActiveSettingsPage = () => {
22 | let activePage = '';
23 |
24 | each(integrations, (integrations, name) => {
25 | const buildHash = '#/settings/' + name;
26 |
27 | if (isEqual(buildHash, hash)) {
28 | activePage = name;
29 | }
30 | });
31 |
32 | return activePage;
33 | };
34 |
35 | const onSelect = (tab) => {
36 | props.history.push({
37 | pathname,
38 | search,
39 | hash: '#/settings/' + tab,
40 | });
41 | };
42 |
43 | const basicClass = "gufo-cursor-pointer gufo-text-gray-900 hover:gufo-text-gray-900 hover:gufo-bg-gray-50 hover:gufo-text-gray-900 hover:gufo-bg-gray-50 gufo-group gufo-rounded-md gufo-px-3 gufo-py-2 gufo-flex gufo-items-center gufo-text-sm ";
44 |
45 | return (
46 |
47 |
48 | onSelect('general')}
50 | className={basicClass + getActiveClass('general')}
51 | >
52 | General
53 |
54 |
55 | onSelect('import')}
57 | className={basicClass + getActiveClass('import')}
58 | >
59 | Import
60 |
61 |
62 | {map(integrations, (integration, name) => {
63 | const hasFields = !isEmpty(get(integration, 'fields'));
64 |
65 | return ( integration.enable && hasFields && (
66 | onSelect(name)}
68 | className={basicClass + getActiveClass('recaptcha')}
69 | key={name}
70 | >
71 |
72 | {integration.title}
73 |
74 |
75 | ) );
76 | })}
77 |
78 |
79 | );
80 | }
81 |
82 | const mapStateToProps = (state) => {
83 | const { settings } = state;
84 |
85 | return { settings };
86 | };
87 |
88 | export default connect(mapStateToProps, null)(Sidebar);
89 |
--------------------------------------------------------------------------------
/admin/pages/settings/settings.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Sidebar from './components/sidebar';
3 | import General from './subpages/general/general';
4 | import IntegrationSettings from './subpages/integrationSettings/integrationSettings';
5 | import { connect } from 'react-redux';
6 | import { Snackbar } from '@wordpress/components';
7 | import { isEmpty } from 'lodash';
8 | import { parseSettingStatus } from '../../functions';
9 | import ImportSettingsPage from './subpages/import/index.import';
10 |
11 | function Settings(props) {
12 | const { hash } = props.location;
13 | const { loading } = props.settings;
14 |
15 | const CurrentSettings = () => {
16 | if (hash === '#/settings/general') {
17 | return ;
18 | } else if (hash === '#/settings/import') {
19 | return ;
20 | } else if (hash === '#/settings') {
21 | return ;
22 | } else {
23 | return ;
24 | }
25 | };
26 |
27 | return (
28 | <>
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {!isEmpty(loading) && (
38 |
39 | {parseSettingStatus(loading)}
40 |
41 | )}
42 | >
43 | );
44 | }
45 |
46 | const mapStateToProps = (state) => {
47 | return {
48 | settings: state.settings,
49 | };
50 | };
51 |
52 | export default connect(mapStateToProps, null)(Settings);
53 |
--------------------------------------------------------------------------------
/admin/pages/settings/subpages/general/components/messages.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { connect } from 'react-redux';
3 | import { map, clone, set } from 'lodash';
4 | import { TextControl } from '@wordpress/components';
5 | import { updateSettings } from '../../../../../redux/actions/generalSettings/updateSettings';
6 | const { __ } = wp.i18n;
7 |
8 | function Messages(props) {
9 | const [state, setState] = useState({
10 | messages: props.generalSettings.messages,
11 | });
12 |
13 | const handleChange = (value, field_name) => {
14 | const newMessages = clone(state.messages);
15 |
16 | set(newMessages, field_name, value);
17 |
18 | setState({
19 | messages: newMessages,
20 | });
21 |
22 | props.updateSettings({
23 | messages: newMessages,
24 | });
25 | };
26 |
27 | return (
28 |
29 |
30 | {__('General', 'forms-gutenberg')}
31 |
32 |
33 |
34 |
35 | {map(state.messages, (field, field_name) => {
36 | const { label, value } = field;
37 |
38 | return (
39 | {
41 | const newValue = {
42 | ...field,
43 | value: v,
44 | };
45 |
46 | handleChange(newValue, field_name);
47 | }}
48 | label={label}
49 | value={value}
50 | key={label}
51 | />
52 | );
53 | })}
54 |
55 |
56 | );
57 | }
58 |
59 | const mapStateToProps = (state) => {
60 | const { generalSettings } = state;
61 |
62 | return {
63 | generalSettings,
64 | };
65 | };
66 |
67 | const mapDispatchToProps = {
68 | updateSettings,
69 | };
70 |
71 | export default connect(mapStateToProps, mapDispatchToProps)(Messages); // connecting to the redux-store
72 |
--------------------------------------------------------------------------------
/admin/pages/settings/subpages/general/general.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Messages from './components/messages';
3 | import { Button } from '@wordpress/components';
4 | import { connect } from 'react-redux';
5 | import { saveSettings } from '../../../../redux/actions/generalSettings/saveSettings';
6 |
7 | function General(props) {
8 | const { saveSettings } = props;
9 |
10 | return (
11 |
12 |
13 |
14 |
15 | Save Settings
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | const mapDispatchToProps = {
23 | saveSettings,
24 | };
25 |
26 | const mapStateToProps = (state) => {
27 | return {
28 | settings: state.settings,
29 | };
30 | };
31 |
32 | export default connect(mapStateToProps, mapDispatchToProps)(General);
33 |
--------------------------------------------------------------------------------
/admin/pages/settings/subpages/import/components/plugin.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { get, isEmpty } from 'lodash';
3 |
4 | import {
5 | Card,
6 | CardBody,
7 | CardDivider,
8 | CardFooter,
9 | CardHeader,
10 | CardMedia,
11 | } from '@wordpress/components';
12 | import { TEXT_DOMAIN } from '../../../../../contants';
13 |
14 | const { Icon, Button } = wp.components;
15 | const { __ } = wp.i18n;
16 |
17 | function Plugin(props) {
18 | const data = props.plugin;
19 | const name = get(data, 'Name');
20 | const description = get(data, 'description');
21 | const icon = get(data, 'icon');
22 |
23 | return (
24 |
25 |
26 |
27 |
28 | {!isEmpty(icon) && }
29 | {name}
30 |
31 | props.onSelect(data)}>
32 | {__('Import', TEXT_DOMAIN)}
33 |
34 |
35 | {description}
36 |
37 |
38 | );
39 | }
40 |
41 | export default Plugin;
42 |
--------------------------------------------------------------------------------
/admin/pages/settings/subpages/import/components/pluginList.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import axios from 'axios';
3 | import { get, map, isEmpty } from 'lodash';
4 | import Plugin from './plugin';
5 | import { TEXT_DOMAIN } from '../../../../../contants';
6 |
7 | const { Spinner, Button, Icon } = wp.components;
8 | const { __ } = wp.i18n;
9 |
10 | /**
11 | * Will render a plugin list that forms are available for importing
12 | */
13 |
14 | function PluginList(props) {
15 | const [plugins, setPlugins] = useState([]);
16 | const [loading, setLoading] = useState(false);
17 | const [error, setError] = useState(false);
18 |
19 | const restUrl = get(window, 'cwp_global.rest_url');
20 | const proxy = restUrl + 'gutenberg-forms/forms/v1/imports/plugins';
21 |
22 | const fetchPlugins = () => {
23 | setLoading(true);
24 | setError(false);
25 |
26 | axios
27 | .get(proxy)
28 | .then((response) => {
29 | setLoading(false);
30 | setError(false);
31 | setPlugins(response.data);
32 | })
33 | .catch((error) => {
34 | setLoading(false);
35 | setError(true);
36 | });
37 | };
38 |
39 | useEffect(fetchPlugins, []);
40 |
41 | return (
42 |
43 | {!loading && !error && (
44 |
45 | {__('Import forms from other plugins', 'forms-gutenberg')}
46 |
47 | )}
48 | {!loading &&
49 | !error &&
50 | map(plugins, (plugin, idx) => (
51 |
props.onSelect(pl)}
53 | plugin={plugin}
54 | key={idx}
55 | />
56 | ))}
57 | {loading && !error && (
58 |
59 |
60 |
61 | {__('Fetching Available Imports', TEXT_DOMAIN)}
62 |
63 |
64 | )}
65 | {!loading && error && (
66 |
67 |
68 | {__('Failed to Fetch Available Imports', TEXT_DOMAIN)}{' '}
69 |
74 | {__('Try Again', TEXT_DOMAIN)}
75 |
76 |
77 |
78 | )}
79 | {!loading && !error && isEmpty(plugins) && (
80 |
81 |
82 |
83 | {__(
84 | 'No Supported Form Plugin found to import!',
85 | TEXT_DOMAIN
86 | )}
87 |
88 |
89 | )}
90 |
91 | );
92 | }
93 |
94 | export default PluginList;
95 |
--------------------------------------------------------------------------------
/admin/pages/settings/subpages/import/index.import.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { withRouter } from 'react-router-dom';
3 | import PluginList from './components/pluginList';
4 | import { isEmpty } from 'lodash';
5 | import PluginImport from './components/pluginImport';
6 | import { TEXT_DOMAIN } from '../../../../contants';
7 |
8 | const { __ } = wp.i18n;
9 |
10 | /**
11 | * All available imports from 3rd party plugins like contact form
12 | */
13 |
14 | function Import(props) {
15 | const [state, setState] = useState({
16 | // trying to be elaborative ;)
17 | selectedPluginToImportForms: null,
18 | });
19 |
20 | /**
21 | * When a plugin is selected to import below function will fire.
22 | * This plugin will be selected for further configuration for importing forms
23 | * @param {object} plugin
24 | */
25 |
26 | const onSelect = (plugin) => {
27 | // updating the selected plugin
28 |
29 | setState({ ...state, selectedPluginToImportForms: plugin });
30 | };
31 |
32 | const onExit = () => {
33 | // going back to the plugin lists screen
34 | setState({ ...state, selectedPluginToImportForms: null });
35 | };
36 |
37 | const { selectedPluginToImportForms } = state;
38 |
39 | return (
40 |
41 |
50 | {!isEmpty(selectedPluginToImportForms) && (
51 |
55 | )}
56 |
57 | );
58 | }
59 |
60 | export default withRouter(Import);
61 |
--------------------------------------------------------------------------------
/admin/pages/settings/subpages/integrationSettings/integrationSettings.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import Fields from './components/fields';
4 | import { has, get, isEmpty } from 'lodash';
5 |
6 | function IntegrationSettings(props) {
7 | const { hash } = props.location;
8 | const { integrations } = props.settings;
9 |
10 | const getName = () => {
11 | const queries = hash.split('/');
12 |
13 | const name = queries[queries.length - 1];
14 |
15 | return name;
16 | };
17 |
18 | const hasIntegration = has(integrations, getName());
19 |
20 | const hasFields =
21 | has(integrations, getName()) &&
22 | !isEmpty(get(integrations[getName()], 'fields'));
23 |
24 | return (
25 |
26 | {hasIntegration && hasFields && }
27 |
28 | );
29 | }
30 |
31 | const mapStateToProps = (state) => {
32 | return {
33 | settings: state.settings,
34 | };
35 | };
36 |
37 | export default connect(mapStateToProps, null)(IntegrationSettings);
38 |
--------------------------------------------------------------------------------
/admin/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/admin/redux/actions/entries/getEntries.js:
--------------------------------------------------------------------------------
1 | import {
2 | ENTRIES_FETCHED,
3 | ENTRIES_FETCHING,
4 | ENTRIES_FETCH_FAILED,
5 | } from '../types';
6 | import axios from 'axios';
7 | import { get, omit } from 'lodash';
8 | import { httpQuery } from '../../../functions';
9 | import store from '../../store/store';
10 |
11 | const rest_url = get(window, 'cwp_global.rest_url');
12 |
13 | export const getEntries =
14 | (
15 | currentPage, // current page index to be fetched
16 | queryFilters = {}, // filters that will be assigned when fetching the entries
17 | omitFilters = [], // filters that will be omitted when fetching the entries
18 | callback = () => null, // callback on success,
19 | hash = '' // currentPage Hash
20 | ) =>
21 | (dispatch) => {
22 | const entriesState = store.getState().entries;
23 | const perPage = get(entriesState, 'perPage');
24 | const stateFilters = get(entriesState, 'filters');
25 | const currentPageInState = get(entriesState, 'currentPage');
26 |
27 | let filters = {
28 | ...stateFilters,
29 | ...queryFilters,
30 | page: currentPage === null ? currentPageInState : currentPage,
31 | per_page: perPage,
32 | };
33 |
34 | filters = omit(filters, omitFilters);
35 |
36 | const filters_query = httpQuery(filters);
37 |
38 | // temp fix.
39 | let proxy;
40 | if ( rest_url.includes( 'wp-json' ) ) {
41 | proxy = rest_url.concat(`wp/v2/cwp_gf_entries?${filters_query}`);
42 | } else {
43 | proxy = rest_url.concat(`wp/v2/cwp_gf_entries&${filters_query}`);
44 | }
45 |
46 | dispatch({
47 | type: ENTRIES_FETCHING,
48 | });
49 |
50 | axios
51 | .get(proxy)
52 | .then((response) => {
53 | const totalPages = get(response.headers, 'x-wp-totalpages');
54 | const totalEntries = get(response.headers, 'x-wp-total');
55 | const { data = [] } = response;
56 |
57 | dispatch({
58 | type: ENTRIES_FETCHED,
59 | payload: {
60 | data,
61 | totalPages,
62 | currentPage: currentPage === null ? currentPageInState : currentPage,
63 | totalEntries,
64 | filters,
65 | hash,
66 | },
67 | });
68 |
69 | callback(data);
70 | })
71 | .catch((error) => {
72 | dispatch({
73 | type: ENTRIES_FETCH_FAILED,
74 | });
75 | });
76 | };
77 |
--------------------------------------------------------------------------------
/admin/redux/actions/entries/setRefetchingStatus.js:
--------------------------------------------------------------------------------
1 | import { SET_ENTRIES_REFETCHING_STATUS } from '../types';
2 |
3 | export const setRefetchingStatus = (status) => (dispatch) => {
4 | dispatch({
5 | type: SET_ENTRIES_REFETCHING_STATUS,
6 | payload: {
7 | status,
8 | },
9 | });
10 | };
11 |
--------------------------------------------------------------------------------
/admin/redux/actions/entries/updateEntry.js:
--------------------------------------------------------------------------------
1 | import { UPDATE_ENTRY } from '../types';
2 | import { get, findIndex, clone } from 'lodash';
3 | import store from '../../store/store';
4 |
5 | export const updateEntry = (updatedEntry) => (dispatch) => {
6 | const id = get(updatedEntry, 'id');
7 | const currentEntries = clone(store.getState().entries.data);
8 |
9 | // current entry index
10 | const idx = findIndex(currentEntries, { id });
11 |
12 | currentEntries.splice(idx, 1, updatedEntry);
13 |
14 | // now the entry is updated
15 | dispatch({
16 | type: UPDATE_ENTRY,
17 | payload: {
18 | updatedEntries: currentEntries,
19 | },
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/admin/redux/actions/generalSettings/saveSettings.js:
--------------------------------------------------------------------------------
1 | import {
2 | SAVE_SETTINGS,
3 | SETTINGS_SAVING,
4 | SETTINGS_SAVED,
5 | SETTINGS_DONE,
6 | } from '../types';
7 | import store from '../../store/store';
8 |
9 | export const saveSettings = () => (dispatch) => {
10 | const general_settings_key = 'cwp_gutenberg_forms_general_settings';
11 |
12 | const { generalSettings } = store.getState();
13 |
14 | const model = new wp.api.models.Settings({
15 | [general_settings_key]: JSON.stringify(generalSettings),
16 | });
17 |
18 | dispatch({
19 | type: SAVE_SETTINGS,
20 | });
21 |
22 | dispatch({
23 | type: SETTINGS_SAVING,
24 | });
25 |
26 | model.save().then((response) => {
27 | dispatch({
28 | type: SETTINGS_SAVED,
29 | });
30 |
31 | setTimeout(() => {
32 | dispatch({
33 | type: SETTINGS_DONE,
34 | });
35 | }, 1000);
36 | });
37 | };
38 |
--------------------------------------------------------------------------------
/admin/redux/actions/generalSettings/updateSettings.js:
--------------------------------------------------------------------------------
1 | import { UPDATE_SETTINGS } from '../types';
2 |
3 | export const updateSettings = (newSettings) => (dispatch) => {
4 | dispatch({
5 | type: UPDATE_SETTINGS,
6 | payload: {
7 | newSettings,
8 | },
9 | });
10 | };
11 |
--------------------------------------------------------------------------------
/admin/redux/actions/notice/addNotice.js:
--------------------------------------------------------------------------------
1 | import { ADD_NOTICE, REMOVE_NOTICE, REMOVE_SIMILAR_NOTICES } from '../types';
2 | import { makeid } from '../../../functions';
3 | import { set } from 'lodash';
4 |
5 | export const addNotice = (notice) => (dispatch) => {
6 | set(notice, 'id', 'notice-' + makeid(10));
7 |
8 | dispatch({
9 | type: ADD_NOTICE,
10 | payload: {
11 | notice,
12 | },
13 | });
14 |
15 | dispatch({
16 | type: REMOVE_SIMILAR_NOTICES,
17 | });
18 | };
19 |
--------------------------------------------------------------------------------
/admin/redux/actions/notice/removeNotice.js:
--------------------------------------------------------------------------------
1 | import { REMOVE_NOTICE } from '../types';
2 |
3 | export const removeNotice = (id) => (dispatch) => {
4 | dispatch({
5 | type: REMOVE_NOTICE,
6 | payload: {
7 | id,
8 | },
9 | });
10 | };
11 |
--------------------------------------------------------------------------------
/admin/redux/actions/settings/loading.js:
--------------------------------------------------------------------------------
1 | import { SETTINGS_SAVED, SETTINGS_SAVING } from '../types';
2 |
3 | export const setSettingsStatus = (fetching) => (dispatch) => {
4 | const type = fetching ? SETTINGS_SAVING : SETTINGS_SAVED;
5 |
6 | dispatch({
7 | type,
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/admin/redux/actions/settings/setIntegration.js:
--------------------------------------------------------------------------------
1 | import {
2 | SET_INTEGRATION,
3 | SETTINGS_SAVING,
4 | SETTINGS_SAVED,
5 | SETTINGS_DONE,
6 | } from '../types';
7 |
8 | export const setIntegration = (integrationName, query) => (dispatch) => {
9 | //where query is weather to enable or disable integration
10 |
11 | const integration_key = 'cwp__enable__'.concat(integrationName);
12 | const model = new wp.api.models.Settings({
13 | [integration_key]: query,
14 | });
15 |
16 | dispatch({
17 | type: SETTINGS_SAVING,
18 | });
19 |
20 | model.save().then((response) => {
21 | const payload = {
22 | [integration_key]: response[integration_key],
23 | integrationName,
24 | query,
25 | };
26 |
27 | dispatch({
28 | type: SET_INTEGRATION,
29 | payload,
30 | });
31 |
32 | dispatch({
33 | type: SETTINGS_SAVED,
34 | });
35 |
36 | setTimeout(() => {
37 | dispatch({
38 | type: SETTINGS_DONE,
39 | });
40 | }, 1000);
41 | });
42 | };
43 |
--------------------------------------------------------------------------------
/admin/redux/actions/settings/setIntegrationDetails.js:
--------------------------------------------------------------------------------
1 | import {
2 | SET_INTEGRATION_DETAILS,
3 | SETTINGS_DONE,
4 | SETTINGS_SAVING,
5 | SETTINGS_SAVED,
6 | } from '../types';
7 | import { each } from 'lodash';
8 |
9 | export const setIntegrationDetails =
10 | (fields, integrationName) => (dispatch) => {
11 | let queries = {};
12 |
13 | each(fields, (field, key) => {
14 | const query_key = 'cwp__' + integrationName + '__' + key;
15 |
16 | queries[query_key] = field.value;
17 | });
18 |
19 | dispatch({
20 | type: SETTINGS_SAVING,
21 | });
22 |
23 | const model = new wp.api.models.Settings({
24 | ...queries,
25 | });
26 |
27 | model.save().then((response) => {
28 | const payload = {
29 | fields,
30 | integrationName,
31 | };
32 |
33 | dispatch({
34 | type: SET_INTEGRATION_DETAILS,
35 | payload,
36 | });
37 |
38 | dispatch({
39 | type: SETTINGS_SAVED,
40 | });
41 |
42 | setTimeout(() => {
43 | dispatch({
44 | type: SETTINGS_DONE,
45 | });
46 | }, 1000);
47 | });
48 | };
49 |
--------------------------------------------------------------------------------
/admin/redux/actions/types.js:
--------------------------------------------------------------------------------
1 | //SETTINGS
2 | export const SET_OPTION = 'SET_OPTION';
3 | export const SET_INTEGRATION = 'SET_INTEGRATION';
4 | export const SETTINGS_SAVING = 'SETTINGS_SAVING';
5 | export const SETTINGS_SAVED = 'SETTINGS_SAVED';
6 | export const SETTINGS_DONE = 'SETTINGS_DONE';
7 | export const SET_INTEGRATION_DETAILS = 'SET_INTEGRATION_DETAILS';
8 |
9 | //GENERAL SETTINGS
10 | export const UPDATE_SETTINGS = 'UPDATE_SETTINGS';
11 | export const SAVE_SETTINGS = 'SAVE_SETTINGS';
12 |
13 | //Notices
14 | export const ADD_NOTICE = 'ADD_NOTICE';
15 | export const REMOVE_NOTICE = 'REMOVE_NOTICE';
16 | export const REMOVE_SIMILAR_NOTICES = 'REMOVE_SIMILAR_NOTICES';
17 |
18 | //entries - PLURAL
19 | export const ENTRIES_FETCHING = 'ENTRIES_FETCHING';
20 | export const ENTRIES_FETCHED = 'ENTRIES_FETCHED';
21 | export const ENTRIES_FETCH_FAILED = 'ENTRIES_FETCH_FAILED';
22 | export const SET_ENTRIES_REFETCHING_STATUS = 'SET_ENTRIES_REFETCHING_STATUS';
23 |
24 | // entry - SINGULAR
25 | export const UPDATE_ENTRY = 'UPDATE_ENTRY';
26 |
--------------------------------------------------------------------------------
/admin/redux/reducers/entries.js:
--------------------------------------------------------------------------------
1 | import {
2 | ENTRIES_FETCHED,
3 | ENTRIES_FETCHING,
4 | ENTRIES_FETCH_FAILED,
5 | UPDATE_ENTRY,
6 | SET_ENTRIES_REFETCHING_STATUS,
7 | } from '../actions/types';
8 |
9 | const initialState = {
10 | filters: {},
11 | data: [],
12 | loading: false,
13 | error: false,
14 | totalPages: 0,
15 | currentPage: 0,
16 | perPage: 10,
17 | totalEntries: 0,
18 | refetchingStatus: true,
19 | entriesFetchedHash: '',
20 | };
21 |
22 | export default function (state = initialState, action) {
23 | switch (action.type) {
24 | case ENTRIES_FETCHING: {
25 | return {
26 | ...state,
27 | loading: true,
28 | error: false,
29 | };
30 | }
31 |
32 | case ENTRIES_FETCHED: {
33 | return {
34 | ...state,
35 | loading: false,
36 | error: false,
37 | data: action.payload.data,
38 | totalPages: action.payload.totalPages,
39 | currentPage: action.payload.currentPage,
40 | totalEntries: action.payload.totalEntries,
41 | filters: action.payload.filters,
42 | entriesFetchedHash: action.payload.hash,
43 | };
44 | }
45 |
46 | case ENTRIES_FETCH_FAILED: {
47 | return {
48 | ...state,
49 | loading: false,
50 | error: true,
51 | };
52 | }
53 |
54 | case UPDATE_ENTRY: {
55 | return {
56 | ...state,
57 | data: action.payload.updatedEntries,
58 | };
59 | }
60 |
61 | case SET_ENTRIES_REFETCHING_STATUS: {
62 | return {
63 | ...state,
64 | refetchingStatus: action.payload.status,
65 | };
66 | }
67 |
68 | default:
69 | return state;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/admin/redux/reducers/generalSettings.js:
--------------------------------------------------------------------------------
1 | import { UPDATE_SETTINGS, SAVE_SETTINGS } from '../actions/types';
2 | import { get } from 'lodash';
3 |
4 | const generalSettings = get(cwp_global, 'general');
5 | const initialState = {
6 | ...generalSettings,
7 | };
8 |
9 | export default function (state = initialState, action) {
10 | switch (action.type) {
11 | case UPDATE_SETTINGS:
12 | return {
13 | ...state,
14 | ...action.payload.newSettings,
15 | };
16 |
17 | case SAVE_SETTINGS:
18 | return {
19 | ...state,
20 | };
21 |
22 | default:
23 | return {
24 | ...state,
25 | };
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/admin/redux/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import SettingsReducer from './settings';
3 | import InformationReducer from './information';
4 | import GeneralSettingsReducer from './generalSettings';
5 | import NoticeReducer from './notice';
6 | import EntriesReducer from './entries';
7 |
8 | export default combineReducers({
9 | settings: SettingsReducer,
10 | information: InformationReducer,
11 | generalSettings: GeneralSettingsReducer,
12 | notice: NoticeReducer, // for showing / handling notices like integration error and warnings...
13 | entries: EntriesReducer,
14 | });
15 |
--------------------------------------------------------------------------------
/admin/redux/reducers/information.js:
--------------------------------------------------------------------------------
1 | const information = cwp_global.informations;
2 |
3 | const initialState = {
4 | cards: information.cards,
5 | };
6 |
7 | export default function (state = initialState, action) {
8 | switch (action) {
9 | default:
10 | return state;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/admin/redux/reducers/notice.js:
--------------------------------------------------------------------------------
1 | import {
2 | ADD_NOTICE,
3 | REMOVE_NOTICE,
4 | REMOVE_SIMILAR_NOTICES,
5 | } from '../actions/types';
6 | import { isEqual, clone, uniqBy } from 'lodash';
7 |
8 | const initialState = {
9 | data: [],
10 | };
11 |
12 | export default function (state = initialState, action) {
13 | switch (action.type) {
14 | case ADD_NOTICE: {
15 | let newState = clone(state);
16 | newState.data.push(action.payload.notice);
17 | return newState;
18 | }
19 | case REMOVE_NOTICE: {
20 | let newState = clone(state);
21 | newState.data = newState.data.filter(
22 | (notice) => !isEqual(notice.id, action.payload.id)
23 | );
24 | return newState;
25 | }
26 |
27 | case REMOVE_SIMILAR_NOTICES: {
28 | let newState = clone(state);
29 |
30 | newState.data = uniqBy(newState.data, 'uniqueKey'); // creating a unique notices data
31 |
32 | return newState;
33 | }
34 |
35 | default:
36 | return state;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/admin/redux/reducers/settings.js:
--------------------------------------------------------------------------------
1 | import {
2 | SET_INTEGRATION,
3 | SETTINGS_SAVING,
4 | SETTINGS_SAVED,
5 | SETTINGS_DONE,
6 | SET_INTEGRATION_DETAILS,
7 | } from '../actions/types';
8 |
9 | import { set } from 'lodash';
10 |
11 | const settings = cwp_global.settings;
12 |
13 | const initialState = {
14 | ...settings,
15 | loading: '',
16 | authenticating: false,
17 | };
18 |
19 | export default function (state = initialState, action) {
20 | switch (action.type) {
21 | case SET_INTEGRATION:
22 | const {
23 | payload: { integrationName, query },
24 | } = action;
25 |
26 | state.integrations[integrationName] = {
27 | ...state.integrations[integrationName],
28 | enable: query,
29 | };
30 |
31 | return {
32 | ...state,
33 | };
34 |
35 | case SET_INTEGRATION_DETAILS:
36 | // const { payload: { integrationName, fields } } = action;
37 |
38 | // state.integrations[integrationName].fields = fields;
39 |
40 | return {
41 | ...state,
42 | };
43 |
44 | case SETTINGS_SAVING:
45 | return {
46 | ...state,
47 | loading: SETTINGS_SAVING,
48 | };
49 |
50 | case SETTINGS_SAVED:
51 | return {
52 | ...state,
53 | loading: SETTINGS_SAVED,
54 | };
55 |
56 | case SETTINGS_DONE:
57 | return {
58 | ...state,
59 | loading: '',
60 | };
61 |
62 | default:
63 | return state;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/admin/redux/store/store.js:
--------------------------------------------------------------------------------
1 | import thunk from 'redux-thunk';
2 | import { createStore, applyMiddleware } from 'redux';
3 | import combineReducers from '../reducers/index';
4 | import { composeWithDevTools } from 'redux-devtools-extension';
5 | const initialState = {};
6 |
7 | const middlewares = [thunk];
8 |
9 | const store = createStore(
10 | combineReducers,
11 | initialState,
12 | composeWithDevTools(applyMiddleware(...middlewares))
13 | );
14 |
15 | export default store;
16 |
--------------------------------------------------------------------------------
/admin/style.scss:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @import "./styles/variables.scss";
6 | @import "./styles/mixins";
7 | @import "./styles/utils";
8 | @import "./styles/integrations/integrations.scss";
9 | @import "./styles/settings/settings.scss";
10 | @import "./styles/breadcrumbs.scss";
11 |
12 | .cwp-gf-modal {
13 | .footer {
14 | display: none;
15 | text-align: right;
16 | button:first-child {
17 | margin-right: 10px;
18 | }
19 | }
20 | }
21 |
22 | .cwp-notices-root {
23 | .cwp-notice {
24 | margin: 10px 0px !important;
25 | p {
26 | margin: 0 !important;
27 | }
28 | }
29 | }
30 |
31 | .components-guide__container {
32 | padding: 20px;
33 | .cwp_guide {
34 | img {
35 | width: 100%;
36 | }
37 | }
38 |
39 | .components-guide__footer {
40 | width: auto;
41 | }
42 | }
43 |
44 | .gutenberg-forms_page_cwp_gf_dashboard #wpcontent {
45 | padding-left: 0 !important;
46 | }
47 |
48 | .toplevel_page_gutenberg_forms #wpcontent {
49 | padding-left: 0 !important;
50 | }
51 |
52 | .cwp_load {
53 | position: fixed;
54 | right: 30px;
55 | top: 10px;
56 | }
57 |
58 | .cwp_guide {
59 | & img {
60 | width: 100%;
61 | }
62 |
63 | &.centered {
64 | & img {
65 | width: 70% !important;
66 | }
67 | }
68 | }
69 |
70 | .cwp-gf-skeleton {
71 | height: 30px;
72 | animation: pulse 1s infinite ease-in-out;
73 | margin-top: 10px;
74 | }
75 |
--------------------------------------------------------------------------------
/admin/styles/_utils.scss:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | Used to insert breakpoints while working directly
4 |
5 | **/
6 |
7 | @mixin breakpoint($class) {
8 | @if $class == "xs" {
9 | @media (max-width: 767px) {
10 | @content;
11 | }
12 | } @else if $class == "xxs" {
13 | @media (max-width: 600px) {
14 | @content;
15 | }
16 | } @else if $class == "sm" {
17 | @media (max-width: 768px) {
18 | @content;
19 | }
20 | } @else if $class == "md" {
21 | @media (max-width: 992px) {
22 | @content;
23 | }
24 | } @else if $class == "lg" {
25 | @media (max-width: 1200px) {
26 | @content;
27 | }
28 | } @else {
29 | @warn "Breakpoint mixin supports: xs, sm, md, lg";
30 | }
31 | }
32 |
33 | /**
34 | Will make the text un selectable
35 | **/
36 |
37 | @mixin un-selectable() {
38 | user-select: none;
39 | -moz-user-select: none;
40 | -ms-user-select: none;
41 | }
42 |
43 | /**
44 | Apply given size to width and height
45 | **/
46 | @mixin dimensions($size) {
47 | width: $size;
48 | height: $size;
49 | }
50 |
--------------------------------------------------------------------------------
/admin/styles/breadcrumbs.scss:
--------------------------------------------------------------------------------
1 | .cwp-breadcrumbs {
2 | display: flex;
3 | flex-direction: row;
4 | padding: 20px 0px;
5 |
6 | a {
7 | text-decoration: none !important;
8 | }
9 |
10 | &.end {
11 | justify-content: flex-end;
12 | }
13 |
14 | &.center {
15 | justify-content: flex-center;
16 | }
17 |
18 | &.start {
19 | justify-content: flex-start;
20 | }
21 |
22 | .breadcrumb {
23 | display: flex;
24 | align-items: center;
25 |
26 | .cwp-separator {
27 | margin: 0px 5px;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/admin/styles/settings/settings.scss:
--------------------------------------------------------------------------------
1 | @import "./sub/sidebar.scss";
2 | @import "./sub/integrationSettings.scss";
3 | @import "./sub/generalSettings.scss";
4 | @import "./sub/importSettings.scss";
5 |
6 |
--------------------------------------------------------------------------------
/admin/styles/settings/sub/generalSettings.scss:
--------------------------------------------------------------------------------
1 | .cwp_general_settings_root {
2 | & .cwp_general_settings_messages {
3 | & .fields {
4 | display: grid;
5 | grid-template-columns: 1fr 1fr;
6 | grid-gap: 30px;
7 | & input {
8 | padding: 10px 10px;
9 | }
10 |
11 | @include breakpoint("sm") {
12 | grid-template-columns: 1fr;
13 | }
14 | }
15 | }
16 |
17 | & .cwp_general_settings_root_save {
18 | margin: 20px 0px;
19 | display: flex;
20 | justify-content: flex-end;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/admin/styles/settings/sub/importSettings.scss:
--------------------------------------------------------------------------------
1 | .cwp_plugin_import_root {
2 | .cwp_import_plugin_list {
3 | .cwp-plugin-import {
4 | h3 {
5 | display: flex;
6 | align-items: center;
7 | justify-content: space-between;
8 | span,
9 | svg {
10 | margin-right: 10px;
11 | display: flex;
12 | flex-direction: row;
13 | align-items: center;
14 | }
15 | }
16 | }
17 |
18 | .cwp_import_plugin_list_prompt {
19 | padding: 50px 0px;
20 | text-align: center;
21 | background: #eee;
22 | span {
23 | display: flex;
24 | align-items: center;
25 | justify-content: center;
26 |
27 | .components-spinner {
28 | margin: 0px 10px 0px 0px;
29 | }
30 | }
31 | }
32 |
33 | .cwp_empty {
34 | padding: 50px 0px;
35 | text-align: center;
36 | display: flex;
37 | background: #eee;
38 | flex-direction: column;
39 | justify-content: center;
40 | align-items: center;
41 | svg {
42 | width: 30px;
43 | height: 30px;
44 | margin-bottom: 10px;
45 | }
46 | }
47 | }
48 |
49 | .cwp_plugin_import_options {
50 | .cwp_plugin_import_settings {
51 | .cwp_plugin_import_settings_search {
52 | padding: 30px;
53 | border: 1px solid #eee;
54 | .cwp-sf-label {
55 | display: flex;
56 | flex-direction: row;
57 | padding: 6px 0px;
58 | .components-spinner {
59 | margin: 0px 0px -3px 10px;
60 | }
61 | }
62 |
63 | .cwp_plugin_import_settings_footer {
64 | display: flex;
65 | justify-content: flex-end;
66 | & button:first-child {
67 | margin-right: 10px;
68 | }
69 | }
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/admin/styles/settings/sub/integrationSettings.scss:
--------------------------------------------------------------------------------
1 | .cwp_integ_fields {
2 | & .cwp_setting_options {
3 | display: flex;
4 | justify-content: flex-end;
5 |
6 | & button:nth-child(1) {
7 | margin: 0px 10px 0px 0px;
8 | }
9 |
10 | }
11 |
12 | & h3 {
13 | & span {
14 | vertical-align: text-top;
15 | }
16 | }
17 |
18 | & .cwp_api_field {
19 | margin: 10px 0px;
20 | display: flex;
21 | flex-direction: row;
22 | align-items: flex-end;
23 |
24 | & .api_input {
25 | flex:2;
26 |
27 | & input {
28 | padding: 5px 15px;
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/admin/styles/settings/sub/sidebar.scss:
--------------------------------------------------------------------------------
1 | .cwp_settings_root {
2 | & .cwp_sidebar {
3 | background: #fff;
4 | border-right: 1px solid #ccc;
5 | height: 100%;
6 |
7 | & button.is-primary {
8 | color: #fff;
9 |
10 | &:focus {
11 | color: #fff !important;
12 | }
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/admin/styles/variables.scss:
--------------------------------------------------------------------------------
1 | $secondaryColor: #ccc;
--------------------------------------------------------------------------------
/admin/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 |
3 | module.exports = {
4 | prefix: 'gufo-',
5 | content: [
6 | "./*.php",
7 | "./**/*.php",
8 | "./components/*.js",
9 | "./layout/*.js",
10 | "./pages/*.js",
11 | "./pages/**/*.js",
12 | ],
13 | theme: {
14 | extend: {},
15 | },
16 | plugins: [
17 | require('@tailwindcss/forms'),
18 | ],
19 | }
20 |
--------------------------------------------------------------------------------
/assets/index.assets.php:
--------------------------------------------------------------------------------
1 | blocks = $blocks;
10 | }
11 |
12 | public function enqueue(): void {
13 | if ( has_block( "cwp/block-gutenberg-forms" ) ) {
14 | wp_enqueue_script(
15 | 'gutenberg-forms-google-recaptcha',
16 | plugins_url( '/', __FILE__ ) . 'scripts/google_recaptcha.js',
17 | array( 'jquery' ),
18 | filemtime( plugin_dir_path( __FILE__ ) . 'scripts/google_recaptcha.js' ),
19 | true
20 | );
21 |
22 | $locale = substr( get_bloginfo ( 'language' ), 0, 2 );
23 |
24 | wp_enqueue_script(
25 | 'google-recaptcha',
26 | "https://www.google.com/recaptcha/api.js?hl=" . $locale,
27 | array( 'gutenberg-forms-google-recaptcha' ),
28 | '',
29 | true
30 | );
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/assets/scripts/google_recaptcha.js:
--------------------------------------------------------------------------------
1 | jQuery(function ($) {
2 | function gutenbergFormsRecaptchaLoad() {
3 | // recaptcha required form elements
4 | $(".cwp-form").each(function () {
5 | // checking if the current form has recaptcha enabled
6 | const is_recaptcha_enabled = $(this).data("recaptchaenable");
7 |
8 | if (is_recaptcha_enabled) {
9 | const form = $(this)[0]; // current form
10 | const sitekey = $(this).data("siteKey");
11 |
12 | grecaptcha.render(form, { sitekey });
13 | }
14 | });
15 | }
16 |
17 | window.gutenbergFormsRecaptchaLoad = gutenbergFormsRecaptchaLoad; // making it available globally
18 | });
19 |
--------------------------------------------------------------------------------
/config/externals.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Utility methods for use when generating build configuration objects.
3 | */
4 | const { join } = require( 'path' );
5 |
6 | /**
7 | * Given a string, returns a new string with dash separators converted to
8 | * camel-case equivalent. This is not as aggressive as `_.camelCase`, which
9 | * which would also upper-case letters following numbers.
10 | *
11 | * @param {string} string Input dash-delimited string.
12 | *
13 | * @return {string} Camel-cased string.
14 | */
15 | const camelCaseDash = string => string.replace( /-([a-z])/g, ( match, letter ) => letter.toUpperCase() );
16 |
17 | /**
18 | * Define externals to load components through the wp global.
19 | */
20 | const externals = [
21 | 'components',
22 | 'api-fetch',
23 | 'edit-post',
24 | 'element',
25 | 'plugins',
26 | 'editor',
27 | 'block-editor',
28 | 'blocks',
29 | 'hooks',
30 | 'utils',
31 | 'date',
32 | 'data',
33 | 'i18n',
34 | ].reduce(
35 | ( externals, name ) => ( {
36 | ...externals,
37 | [ `@wordpress/${ name }` ]: `wp.${ camelCaseDash( name ) }`,
38 | } ),
39 | {
40 | wp: 'wp',
41 | ga: 'ga', // Old Google Analytics.
42 | gtag: 'gtag', // New Google Analytics.
43 | react: 'React', // React itself is there in Gutenberg.
44 | jquery: 'jQuery', // import $ from 'jquery'; // Use jQuery from WP after enqueuing it.
45 | 'react-dom': 'ReactDOM',
46 | lodash: 'lodash', // Lodash is there in Gutenberg.
47 | cgbGlobal: 'cgbGlobal', // import globals from 'cgbGlobal'; // Localized data.
48 | }
49 | );
50 |
51 | module.exports = externals;
52 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Paths
3 | *
4 | * Project related paths.
5 | */
6 |
7 | const path = require( 'path' );
8 | const fs = require( 'fs' );
9 |
10 | // Make sure any symlinks in the project folder are resolved:
11 | const pluginDir = fs.realpathSync( process.cwd() );
12 | const resolvePlugin = relativePath => path.resolve( pluginDir, relativePath );
13 |
14 | // Config after eject: we're in ./config/
15 | module.exports = {
16 | dotenv: resolvePlugin( '.env' ),
17 | pluginSrc: resolvePlugin( 'src' ), // Plugin src folder path.
18 | pluginBlocksJs: resolvePlugin( 'src/blocks.js' ),
19 | yarnLockFile: resolvePlugin( 'yarn.lock' ),
20 | pluginDist: resolvePlugin( '.' ), // We are in ./dist folder already so the path '.' resolves to ./dist/.
21 | };
22 |
--------------------------------------------------------------------------------
/controllers/forms/forms.controller.php:
--------------------------------------------------------------------------------
1 | namespace = "/gutenberg-forms/forms/" . self::version;
18 | $this->resource_name = 'posts';
19 | }
20 |
21 | /**
22 | * All rest route required for the current controller will be
23 | * registered in the function below
24 | */
25 |
26 | public function register_routes() {
27 | $sub_controllers = [
28 | new cwp_gf_Forms_Import_controller( $this->namespace ),
29 | ];
30 |
31 | foreach ( $sub_controllers as $sub_controller ) {
32 | // registering routes for each sub controller
33 | $sub_controller->register_routes();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/controllers/index.php:
--------------------------------------------------------------------------------
1 | register_routes();
14 | } );
15 |
16 | add_filter( 'rest_query_vars', function ( $vars ) {
17 | $extra_filters = array( 'post', 'post__in', 'type', 'id' );
18 |
19 | $vars[] = 'meta_query';
20 |
21 | return array_merge( $vars, $extra_filters );
22 | } );
23 |
--------------------------------------------------------------------------------
/dist/admin/index.asset.php:
--------------------------------------------------------------------------------
1 | array('lodash', 'react', 'react-dom', 'wp-components', 'wp-element', 'wp-i18n'), 'version' => 'c7a2479844e25a67ae1625d073946f7f');
--------------------------------------------------------------------------------
/gutenberg-forms.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Activate
8 | Click on the enable toggle to activate Google ReCaptcha.
9 |
10 |
11 |
12 |
13 |
14 |
API Key(s)
15 |
16 | In order to get started and protect your form from spamming. You must provide the following keys
17 |
18 | Site Key
19 | Client Secret
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
How to use?
33 |
34 |
35 | After completing all the steps, You can now navigate to the page where you want to use Gutenberg Forms
36 | . You can now see a ReCaptcha v2 toggle under the Spam Protection Panel.
37 |
38 |
39 |
--------------------------------------------------------------------------------
/lib/block/api/index.js:
--------------------------------------------------------------------------------
1 | import { get } from 'lodash';
2 | import { DATA_PROXY, LIBRARY_PROXY } from "../constants";
3 | export function getCatagories() {
4 | return new Promise((resolve, reject) => {
5 | fetch(DATA_PROXY, {
6 | method: 'GET'
7 | }).then(res => res.json()).then(data => {
8 | let records = data.records,
9 | fields = get(records, '[0].fields'),
10 | Catagories = get(fields, 'Catagories');
11 | resolve(Catagories);
12 | }).catch(err => {
13 | reject(err);
14 | });
15 | });
16 | }
17 | export function getTemplates() {
18 | return new Promise((resolve, reject) => {
19 | fetch(LIBRARY_PROXY, {
20 | method: 'GET'
21 | }).then(res => res.json()).then(data => {
22 | let records = get(data, 'records');
23 | resolve(records);
24 | }).catch(err => {
25 | reject(err);
26 | });
27 | });
28 | }
--------------------------------------------------------------------------------
/lib/block/block.js:
--------------------------------------------------------------------------------
1 | import "./editor.scss";
2 | import "./style.scss";
3 | import { applyFormStyles } from "./formStyles/index";
4 | import { registerFieldStyles } from "./fieldStyles/index";
5 | import { myAttrs } from "../constants";
6 | applyFormStyles("cwp/block-gutenberg-forms"); //registering styles
7 |
8 | registerFieldStyles(myAttrs); //registering field styles
9 |
--------------------------------------------------------------------------------
/src/_partials/_extend_fields.scss:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | * This file contains all the @mixins scss / css of elements
4 | * that will extend fields
5 | * Like Prefix, Suffix etc
6 |
7 | **/
8 |
9 | @mixin InputElement() {
10 | /**
11 |
12 | This mixin will be applied to both input element ( prefix & suffix )
13 | @params {$type} can either be prefix or suffix
14 |
15 | **/
16 | display: table-cell;
17 | padding: 0px 15px;
18 | width: 1%;
19 | }
20 |
21 | @mixin Suffix() {
22 | @include InputElement();
23 |
24 | &.outside {
25 | border-top: 1px solid;
26 | border-bottom: 1px solid;
27 | border-right: 1px solid;
28 | border-left: none !important;
29 | }
30 |
31 | &.inside {
32 | border: none !important;
33 | position: absolute;
34 | top: 0;
35 | right: 0;
36 | height: 100%;
37 | display: flex;
38 | align-items: center;
39 | width: auto !important;
40 | }
41 | }
42 |
43 | @mixin Prefix() {
44 | @include InputElement();
45 |
46 | &.outside {
47 | border-top: 1px solid;
48 | border-bottom: 1px solid;
49 | border-left: 1px solid;
50 | border-right: none !important;
51 | }
52 |
53 | &.inside {
54 | border: none !important;
55 | position: absolute;
56 | top: 0;
57 | left: 0;
58 | height: 100%;
59 | display: flex;
60 | align-items: center;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/_partials/_keyframes.scss:
--------------------------------------------------------------------------------
1 | @keyframes slideInLeft {
2 | 0% {
3 | -webkit-transform: translate3d(-100%, 0, 0);
4 | transform: translate3d(-100%, 0, 0);
5 | visibility: visible;
6 | }
7 | to {
8 | -webkit-transform: translateZ(0);
9 | transform: translateZ(0);
10 | }
11 | }
12 | @keyframes slideInRight {
13 | 0% {
14 | -webkit-transform: translate3d(100%, 0, 0);
15 | transform: translate3d(100%, 0, 0);
16 | visibility: visible;
17 | }
18 | to {
19 | -webkit-transform: translateZ(0);
20 | transform: translateZ(0);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/_partials/_pre_mixins.scss:
--------------------------------------------------------------------------------
1 | @mixin ImagePreview() {
2 | & .cwp-img {
3 | position: relative;
4 | margin: 26px 0px;
5 |
6 | & img {
7 | width: 100%;
8 | height: 100%;
9 | }
10 |
11 | & .cwp-close-image {
12 | position: absolute;
13 | top: 0;
14 | right: 0;
15 |
16 | & button {
17 | &:nth-child(1) {
18 | margin: 0px 3px 0px 0px;
19 | }
20 |
21 | &:nth-child(2) {
22 | margin: 0px 0px 0px 3px;
23 | }
24 | }
25 | }
26 | }
27 | }
28 |
29 |
30 | @mixin bulk_add() {
31 | & .cwp-bulk-add {
32 | & .cwp-save {
33 | display: flex;
34 | justify-content: flex-end;
35 |
36 | & button:nth-child(1) {
37 | margin: 0px 10px 0px 0px;
38 | }
39 |
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/_partials/_variables.scss:
--------------------------------------------------------------------------------
1 | $primaryColor: #007cba;
2 | $secondaryColor: #eee;
3 | $danger: red;
4 |
--------------------------------------------------------------------------------
/src/block/api/index.js:
--------------------------------------------------------------------------------
1 | import { get } from 'lodash'
2 | import { DATA_PROXY, LIBRARY_PROXY } from "../constants"
3 |
4 | export function getCatagories() {
5 |
6 | return new Promise((resolve, reject) => {
7 |
8 |
9 | fetch(DATA_PROXY, {
10 | method: 'GET'
11 | })
12 | .then((res) => res.json())
13 | .then((data) => {
14 |
15 | let records = data.records,
16 | fields = get(records, '[0].fields'),
17 | Catagories = get(fields, 'Catagories');
18 |
19 | resolve(Catagories);
20 |
21 | })
22 | .catch(err => {
23 | reject(err);
24 | });
25 |
26 | })
27 |
28 | }
29 |
30 | export function getTemplates() {
31 |
32 | return new Promise((resolve, reject) => {
33 |
34 | fetch(LIBRARY_PROXY, {
35 | method: 'GET'
36 | })
37 | .then(res => res.json())
38 | .then((data) => {
39 |
40 | let records = get(data, 'records');
41 |
42 | resolve(records);
43 |
44 | })
45 | .catch(err => {
46 | reject(err);
47 | })
48 |
49 | });
50 |
51 | }
--------------------------------------------------------------------------------
/src/block/block.js:
--------------------------------------------------------------------------------
1 | import "./editor.scss";
2 | import "./style.scss";
3 |
4 | import { applyFormStyles } from "./formStyles/index";
5 | import { registerFieldStyles } from "./fieldStyles/index";
6 | import { myAttrs } from "../constants";
7 |
8 | applyFormStyles("cwp/block-gutenberg-forms"); //registering styles
9 | registerFieldStyles(myAttrs); //registering field styles
10 |
--------------------------------------------------------------------------------
/src/block/components/datepicker.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import Pikaday from "pikaday";
3 |
4 | function Datepicker(props) {
5 | const inputField = React.useRef();
6 |
7 | useEffect(() => {
8 | let datePicker = new Pikaday({
9 | field: inputField.current,
10 | onSelect: date => {
11 | props.setAttributes({
12 | placeholder: datePicker.toString()
13 | });
14 | },
15 | format: props.format,
16 | toString(date, format) {
17 | // you should do formatting based on the passed format,
18 | // but we will just return 'D/M/YYYY' for simplicity
19 | const day = date.getDate();
20 | const month = date.getMonth() + 1;
21 | const year = date.getFullYear();
22 |
23 | if (format === "DD/MM/YYYY") {
24 | return `${day}/${month}/${year}`;
25 | } else if (format === "MM/DD/YYYY") {
26 | return `${month}/${day}/${year}`;
27 | } else {
28 | return `${year}/${month}/${day}`;
29 | }
30 | }
31 | });
32 | }, []);
33 |
34 | return (
35 |
44 | );
45 | }
46 |
47 | export default Datepicker;
48 |
--------------------------------------------------------------------------------
/src/block/components/formulaBuilder.js:
--------------------------------------------------------------------------------
1 | import { __ } from '@wordpress/i18n';
2 | import React, { useEffect, useRef, Fragment } from "react";
3 | import {
4 | Button,
5 | DropdownMenu,
6 | MenuGroup,
7 | MenuItem,
8 | } from "@wordpress/components";
9 | import { getSiblings } from "../functions";
10 | import { getFieldIcon } from "../misc/helper";
11 | import { map } from "lodash";
12 | import $ from "jquery";
13 |
14 | function FormulaBuilder(prop) {
15 | const area = useRef();
16 |
17 | const props = prop.data;
18 |
19 | const { clientId } = props,
20 | { formula } = props.attributes;
21 |
22 | const addFieldId = (name) => {
23 | var $txt = $(area.current);
24 | var caretPos = $txt[0].selectionStart;
25 | var textAreaTxt = $txt.val();
26 | var txtToAdd = `{{${name}}}`;
27 |
28 | const val =
29 | textAreaTxt.substring(0, caretPos) +
30 | txtToAdd +
31 | textAreaTxt.substring(caretPos);
32 |
33 | props.setAttributes({ formula: val });
34 | };
35 |
36 | return (
37 |
38 |
39 |
{__('Available Number Fields:', 'forms-gutenberg')}
40 |
41 | {({ onClose }) => (
42 |
43 |
44 | {map(getSiblings(clientId, "cwp/number"), (field) => {
45 | const { field_name, label } = field;
46 |
47 | return (
48 | {
51 | onClose();
52 | addFieldId(field_name);
53 | }}
54 | >
55 |
59 |
60 | );
61 | })}
62 |
63 |
64 | )}
65 |
66 |
67 |
72 |
73 | );
74 | }
75 |
76 | export default FormulaBuilder;
77 |
--------------------------------------------------------------------------------
/src/block/components/imagePreview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Icon, Button } from "@wordpress/components";
3 | import ImageUpload from "../components/imageUpload";
4 |
5 | function ImagePreview(props) {
6 | const { url, height, width } = props.image, //with dimensions;
7 | dimensions = {
8 | height,
9 | width
10 | };
11 |
12 | const { isSelected } = props;
13 |
14 | return (
15 |
16 |
17 | {isSelected && (
18 |
19 | props.onRemove()}>
20 |
21 |
22 | props.onEdit(img)}
26 | />
27 |
28 | )}
29 |
30 |
31 |
32 | );
33 | }
34 |
35 | export default ImagePreview;
36 |
--------------------------------------------------------------------------------
/src/block/components/imageUpload.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button, Icon } from "@wordpress/components";
3 | import { has } from "lodash";
4 | const { MediaUploadCheck, MediaUpload } = wp.blockEditor;
5 |
6 | function ImageUpload(props) {
7 | const { image } = props;
8 |
9 | let value = image ? image : "";
10 |
11 | function getLeastOneSize(s, media) {
12 | let { sizes } = media;
13 |
14 | for (const size of s) {
15 | if (has(sizes, size)) {
16 | return sizes[size];
17 | break;
18 | } else continue;
19 | }
20 | }
21 |
22 | return (
23 |
24 | {
26 | let sizes = ["thumbnail", "medium", "large"],
27 | imageWithDimensions = getLeastOneSize(sizes, media);
28 |
29 | props.onSelect(imageWithDimensions);
30 | }}
31 | allowedTypes={["image"]}
32 | value={value}
33 | render={({ open }) => (
34 |
35 |
36 |
37 | )}
38 | />
39 |
40 | );
41 | }
42 | export default ImageUpload;
43 |
--------------------------------------------------------------------------------
/src/block/components/tagList.js:
--------------------------------------------------------------------------------
1 | import { __ } from '@wordpress/i18n';
2 | import React, { useEffect } from "react";
3 | import { MenuGroup, MenuItem } from "@wordpress/components";
4 | import {
5 | getFieldsTags,
6 | getWordpressTags,
7 | getFormTags,
8 | getOtherTags,
9 | getMetaTags,
10 | } from "../functions";
11 | import { get, isEmpty, has } from "lodash";
12 |
13 | function TagList(props) {
14 | const { onSelect, data } = props;
15 |
16 | const mapList = (list) => {
17 | return !isEmpty(list)
18 | ? list.map((tag, index) => {
19 | const title = get(tag, "title");
20 | const Tag = get(tag, "tag");
21 |
22 | return (
23 | onSelect(Tag)}>
24 | {title}
25 | {Tag}
26 |
27 | );
28 | })
29 | : null;
30 | };
31 |
32 | const getList = () => {
33 | const requiredList = props.list;
34 |
35 | switch (requiredList) {
36 | case "fields":
37 | let fieldsTagList = isEmpty(data)
38 | ? getFieldsTags(props.clientId, false)
39 | : data;
40 |
41 | return fieldsTagList.map((field, index) => {
42 | const id = get(field, "field_id");
43 |
44 | const label = get(field, "fieldName");
45 | const tag = `{{${id}}}`;
46 |
47 | return (
48 | onSelect(tag)}>
49 | {isEmpty(label) ? __("No Label", 'forms-gutenberg') : label}
50 | {tag}
51 |
52 | );
53 | });
54 | case "wordpress":
55 | const wpTags = isEmpty(data) ? getWordpressTags() : data;
56 | return mapList(wpTags);
57 | case "form":
58 | const formTags = isEmpty(data) ? getFormTags() : data;
59 | return mapList(formTags);
60 | case "meta":
61 | const metaTags = isEmpty(data) ? getMetaTags() : data;
62 | return mapList(metaTags);
63 | case "other":
64 | const otherTags = isEmpty(data) ? getOtherTags() : data;
65 | return mapList(otherTags);
66 | }
67 | };
68 |
69 | const noStyling = has(props, "noStyling");
70 | const classes = noStyling ? "no-styling" : "";
71 |
72 | const listToMap = getList();
73 |
74 | return (
75 |
76 | {!isEmpty(listToMap) ? (
77 |
{listToMap}
78 | ) : (
79 |
80 |
{__('No Tags Found !', 'forms-gutenberg')}
81 |
82 | )}
83 |
84 | );
85 | }
86 |
87 | export default TagList;
88 |
--------------------------------------------------------------------------------
/src/block/components/tagSelector.js:
--------------------------------------------------------------------------------
1 | import { __ } from "@wordpress/i18n";
2 | import React, { useState, useEffect } from "react";
3 | import { TextControl, TabPanel } from "@wordpress/components";
4 | import { TEXT_DOMAIN } from "../constants";
5 | import { isEmpty } from "lodash";
6 | import TagList from "./tagList";
7 | import SearchTags from "./searchTags";
8 |
9 | function TagSelector(props) {
10 | const { setAttributes } = props;
11 | const [search, setSearch] = useState("");
12 |
13 | const tabs = [
14 | {
15 | name: "fields",
16 | title: __("Fields", "forms-gutenberg"),
17 | className: "cwp-tag-button",
18 | },
19 | {
20 | name: "wordpress",
21 | title: __("Wordpress", "forms-gutenberg"),
22 | className: "cwp-tag-button",
23 | },
24 | {
25 | name: "form",
26 | title: __("Form", "forms-gutenberg"),
27 | className: "cwp-tag-button",
28 | },
29 | {
30 | name: "meta",
31 | title: __("Meta", "forms-gutenberg"),
32 | className: "cwp-tag-button",
33 | },
34 | {
35 | name: "other",
36 | title: __("Other", "forms-gutenberg"),
37 | className: "cwp-tag-button",
38 | },
39 | ];
40 |
41 | return (
42 |
43 |
44 |
49 |
50 |
51 | {isEmpty(search) ? (
52 |
57 | {(tab) => {
58 | return (
59 |
64 | );
65 | }}
66 |
67 | ) : (
68 |
73 | )}
74 |
75 |
76 | );
77 | }
78 |
79 | export default TagSelector;
80 |
--------------------------------------------------------------------------------
/src/block/constants/index.js:
--------------------------------------------------------------------------------
1 | export const TEXT_DOMAIN = "forms-gutenberg"; // text-domain
2 |
3 |
4 |
5 | //? some endpoints
6 | export const LIBRARY_PROXY = "https://raw.githubusercontent.com/ZafarKamal123/gutenberg-forms-templates/master/library.json";
7 | export const DATA_PROXY = "https://raw.githubusercontent.com/ZafarKamal123/gutenberg-forms-templates/master/data.json";
8 |
--------------------------------------------------------------------------------
/src/block/fieldStyles/index.js:
--------------------------------------------------------------------------------
1 | import { __ } from '@wordpress/i18n';
2 | import { each, isEqual } from "lodash";
3 | const { registerBlockStyle } = wp.blocks;
4 |
5 | let fieldStyles = [
6 | {
7 | name: "inline",
8 | label: __("Inline", "forms-gutenberg"),
9 | }
10 | ];
11 |
12 | const radioFieldStyling = [
13 | {
14 | name: "button",
15 | label: __("Button", "forms-gutenberg"),
16 | }
17 | ];
18 |
19 | export function registerFieldStyles(fields) {
20 | let prefix = "cwp/"; // our block prefix
21 |
22 | each(fields, field => {
23 | let slug = prefix.concat(field); // example => "cwp/name"
24 |
25 | if (isEqual(slug, "cwp/checkbox") || isEqual(slug, "cwp/radio"))
26 | registerBlockStyle(slug, radioFieldStyling);
27 | // styling only for radio and checkbox fields
28 | else registerBlockStyle(slug, fieldStyles); //registering style with the specified field slug
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/src/block/formStyles/index.js:
--------------------------------------------------------------------------------
1 | import { __ } from '@wordpress/i18n';
2 | import { each } from "lodash";
3 | const { registerBlockStyle } = wp.blocks;
4 |
5 | const formStyles = [
6 | {
7 | name: "paper",
8 | label: __("Paper", "forms-gutenberg"),
9 | }
10 | ];
11 |
12 | export let applyFormStyles = slug => {
13 | each(formStyles, style => {
14 | registerBlockStyle(slug, style); //?iterating through each style & registering it
15 | });
16 | };
17 |
--------------------------------------------------------------------------------
/src/block/style.scss:
--------------------------------------------------------------------------------
1 | .cwp-hint {
2 | font-weight: 300;
3 | font-size: 1.5rem;
4 | }
5 |
--------------------------------------------------------------------------------
/src/blocks.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Gutenberg Blocks
3 | *
4 | * All blocks related JavaScript files should be imported here.
5 | * You can create a new block folder in this dir and include code
6 | * for that block here as well.
7 | *
8 | * All blocks should be included here since this is the file that
9 | * Webpack is compiling as the input file.
10 | */
11 |
12 | import "./block/block.js";
13 |
14 | /**
15 | * Main Gutenberg Forms block
16 | */
17 | import "./blocks/gutenberg-forms/index.js";
18 |
19 | /**
20 | * Inner blocks
21 | */
22 |
23 | import "./blocks/reusable_forms/index.js";
24 | import "./blocks/text/index.js";
25 | import "./blocks/number/index.js";
26 | import "./blocks/checkbox/index.js";
27 | import "./blocks/select/index.js";
28 | import "./blocks/datepicker/index.js";
29 | import "./blocks/message/index.js";
30 | import "./blocks/email/index.js";
31 | import "./blocks/form-button/index.js";
32 | import "./blocks/name/index.js";
33 | import "./blocks/phone/index.js";
34 | import "./blocks/radio/index.js";
35 | import "./blocks/website/index.js";
36 | import "./blocks/yes-no/index.js";
37 | import "./blocks/hidden/index.js";
38 | import "./blocks/file_upload/index.js";
39 | import "./blocks/progress/index.js";
40 | import "./blocks/form-calculation/index.js";
41 | import "./blocks/column/index.js";
42 | import "./blocks/form-column/index.js";
43 | import "./blocks/form-group/index.js";
44 |
45 | import "./blocks/form-steps/root/index.js";
46 | import "./blocks/form-steps/childs/form-step/index.js";
47 |
48 | /**
49 | * extended index
50 | */
51 |
52 | import "./extend/index.js";
53 |
--------------------------------------------------------------------------------
/src/blocks/checkbox/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Checkbox",
3 | "attributes": {
4 | "isRequired": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "options": {
9 | "type": "array",
10 | "default": [
11 | {
12 | "label": "Option 1"
13 | }
14 | ]
15 | },
16 | "enableCondition": {
17 | "type": "boolean",
18 | "default": false
19 | },
20 | "label": {
21 | "type": "string",
22 | "default": "Choose One"
23 | },
24 | "id": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "field_name": {
29 | "type": "string",
30 | "default": ""
31 | },
32 | "messages": {
33 | "type": "object",
34 | "default": {
35 | "empty": "Please select atleast one checkbox!"
36 | }
37 | },
38 | "condition": {
39 | "type": "object",
40 | "default": {
41 | "field": null,
42 | "condition": "===",
43 | "value": ""
44 | }
45 | },
46 | "requiredLabel": {
47 | "type": "string",
48 | "default": "*"
49 | },
50 | "fieldStyle": {
51 | "type": "string",
52 | "default": "block"
53 | },
54 | "bulkAdd": {
55 | "type": "boolean",
56 | "default": false
57 | },
58 | "adminId": {
59 | "type": "object",
60 | "default": {
61 | "default": "",
62 | "value": ""
63 | }
64 | },
65 | "showHint" :{
66 | "type": "boolean",
67 | "default": false
68 | },
69 | "hint": {
70 | "type": "string",
71 | "default": ""
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/blocks/checkbox/deprecated.js:
--------------------------------------------------------------------------------
1 | import blockData from "./block.json";
2 |
3 | export const deprecated = [
4 | {
5 | attributes: {
6 | ...blockData.attributes,
7 | },
8 | },
9 | ];
10 |
--------------------------------------------------------------------------------
/src/blocks/checkbox/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 | import { fieldParents, myAttrs } from "../../constants.js";
4 | import { getFieldTransform } from "../../block/functions";
5 | import checkboxEdit from "./edit.js";
6 | import checkboxSave from "./save.js";
7 |
8 | import blockData from "./block.json";
9 |
10 | const { title, attributes } = blockData;
11 |
12 | registerBlockType("cwp/checkbox", {
13 | title: __(title),
14 | icon: "yes",
15 | category: "gutenberg-forms",
16 | keywords: [__("gutenberg-forms"), __("forms"), __("checkbox")],
17 | edit: checkboxEdit,
18 | save: checkboxSave,
19 | attributes,
20 | transforms: {
21 | from: [
22 | {
23 | type: "block",
24 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
25 | transform: (a) => getFieldTransform(a, "checkbox"),
26 | },
27 | ],
28 | },
29 | parent: fieldParents,
30 | });
31 |
--------------------------------------------------------------------------------
/src/blocks/column/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Column",
3 | "attributes": {
4 | "width": {
5 | "type": "number",
6 | "default": 100
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/blocks/column/edit.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { RangeControl, PanelBody } from '@wordpress/components';
3 | import { hasChildBlocks } from '../../block/functions';
4 |
5 | const { InspectorControls, InnerBlocks } = wp.blockEditor;
6 | const { __ } = wp.i18n;
7 | const $ = jQuery;
8 |
9 | function edit( props ) {
10 | const { width } = props.attributes;
11 | const { setAttributes, clientId } = props;
12 |
13 | const updateAttribute = ( key, value ) => {
14 | const currentBlockId = '#block-'.concat( clientId );
15 | const currentBlockElement = $( currentBlockId );
16 |
17 | if ( key === 'width' && currentBlockElement.length ) {
18 | const widthInPercentage = String( value ).concat( '%' );
19 | currentBlockElement.css( 'flex-basis', widthInPercentage ); // updating the dom width
20 | }
21 |
22 | setAttributes( {
23 | [ key ]: value,
24 | } );
25 | };
26 |
27 | return [
28 | // eslint-disable-next-line react/jsx-key
29 |
30 |
37 | }
38 | />
39 |
,
40 | null,
41 | !! props.isSelected && (
42 |
43 |
47 | updateAttribute( 'width', newWidth ) }
51 | />
52 |
53 |
54 | ),
55 | ];
56 | }
57 |
58 | export default edit;
59 |
--------------------------------------------------------------------------------
/src/blocks/column/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import columnEdit from "./edit.js";
5 | import columnSave from "./save.js";
6 |
7 | import blockData from "./block.json";
8 |
9 | // Child block for the form-column block for creating layouts
10 |
11 | const { title, attributes } = blockData;
12 |
13 | registerBlockType("cwp/column", {
14 | title: __(title),
15 | icon: "editor-table",
16 | category: "gutenberg-forms",
17 | keywords: [
18 | __("gutenberg-forms"),
19 | __("forms"),
20 | __("form-column"),
21 | __("column"),
22 | ],
23 | edit: columnEdit,
24 | save: columnSave,
25 | attributes,
26 | parent: ["cwp/form-column"],
27 | });
28 |
--------------------------------------------------------------------------------
/src/blocks/column/save.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { InnerBlocks } from '@wordpress/block-editor';
3 |
4 | function save( props ) {
5 | const width = String( props.attributes.width ).concat( '%' );
6 |
7 | const styling = {
8 | flexBasis: width,
9 | };
10 |
11 | return (
12 |
13 |
14 |
15 | );
16 | }
17 |
18 | export default save;
19 |
--------------------------------------------------------------------------------
/src/blocks/components/bulk_add.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { TextareaControl, Button } from "@wordpress/components";
3 | const { __ } = wp.i18n;
4 |
5 | function bulk_add(prop) {
6 |
7 | const props = prop.data;
8 | const { options } = props.attributes;
9 |
10 | const [bulkText, setBulkText] = useState("");
11 |
12 | const getValue = () => {
13 |
14 | const labels = options.map(v => v.label),
15 | value = labels.join("\n");
16 |
17 | return value;
18 | }
19 |
20 | const handleSave = () => {
21 |
22 | let values = bulkText.split("\n");
23 | let newOption = values.map(o => {
24 | return {
25 | label: o,
26 | checked: false
27 | }
28 | });
29 |
30 | props.setAttributes({ options: newOption, bulkAdd: false });
31 | prop.onChange(newOption);
32 | }
33 |
34 |
35 | const handleCancel = () => {
36 | props.setAttributes({ bulkAdd: false });
37 | }
38 |
39 | useEffect(() => {
40 |
41 | setBulkText(getValue());
42 |
43 | }, [])
44 |
45 | return (
46 |
47 |
52 |
53 | {__("Save", "forms-gutenberg")}
54 | props.setAttributes({ bulkAdd: false })}>{__("Cancel", "forms-gutenberg")}
55 |
56 |
57 | )
58 | }
59 |
60 |
61 | export default bulk_add;
62 |
--------------------------------------------------------------------------------
/src/blocks/components/prefix.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { get, isEmpty } from "lodash";
3 |
4 | /**
5 | * This component will be used before any input field ( can either be inside the input or outside )
6 | * for example Prefix [ Prefix Input_field Suffix ] Suffix
7 | */
8 |
9 | function Prefix(props) {
10 | const position = get(props.prefix, "position");
11 |
12 | const conditionalClass = isEmpty(position) ? "" : position;
13 | const className = "cwp-prefix cwp-field-prefix cwp-field-element ".concat(
14 | conditionalClass
15 | );
16 |
17 | return {props.children}
;
18 | }
19 |
20 | export default Prefix;
21 |
--------------------------------------------------------------------------------
/src/blocks/components/suffix.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { get, isEmpty } from "lodash";
3 |
4 | /**
5 | * This component will be used after any input field ( can either be inside the input or outside )
6 | * for example Prefix [ Prefix Input_field Suffix ] Suffix
7 | */
8 |
9 | function Suffix(props) {
10 | const position = get(props.suffix, "position");
11 | const conditionalClass = isEmpty(position) ? "" : position;
12 | const className = "cwp-suffix cwp-field-suffix cwp-field-element ".concat(
13 | conditionalClass
14 | );
15 |
16 | return {props.children}
;
17 | }
18 |
19 | export default Suffix;
20 |
--------------------------------------------------------------------------------
/src/blocks/datepicker/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Date Picker",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "isRequired": {
9 | "type": "boolean",
10 | "default": false
11 | },
12 | "label": {
13 | "type": "string",
14 | "default": "Pick Date"
15 | },
16 | "placeholder": {
17 | "type": "string",
18 | "default": ""
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "requiredLabel": {
29 | "type": "string",
30 | "default": "*"
31 | },
32 | "format": {
33 | "type": "string",
34 | "default": "DD/MM/YYYY"
35 | },
36 | "messages": {
37 | "type": "object",
38 | "default": {
39 | "empty": "Please select date!"
40 | }
41 | },
42 | "condition": {
43 | "type": "object",
44 | "default": {
45 | "field": null,
46 | "condition": "===",
47 | "value": ""
48 | }
49 | },
50 | "requiredLabel": {
51 | "type": "string",
52 | "default": "*"
53 | },
54 | "adminId": {
55 | "type": "object",
56 | "default": {
57 | "default": "",
58 | "value": ""
59 | }
60 | },
61 | "prefix": {
62 | "type": "object",
63 | "default": {
64 | "enable": false,
65 | "content": "",
66 | "position": "outside"
67 | }
68 | },
69 | "suffix": {
70 | "type": "object",
71 | "default": {
72 | "enable": false,
73 | "content": "",
74 | "position": "outside"
75 | }
76 | },
77 | "showHint" :{
78 | "type": "boolean",
79 | "default": false
80 | },
81 | "hint": {
82 | "type": "string",
83 | "default": ""
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/blocks/datepicker/deprecated/deprecated.js:
--------------------------------------------------------------------------------
1 | import { attributes } from "../block.json";
2 |
3 | // deprecated version
4 |
5 | import edit from "./edit";
6 | import save from "./save";
7 |
8 | export const deprecated = [
9 | {
10 | attributes,
11 | edit,
12 | save,
13 | },
14 | ];
15 |
--------------------------------------------------------------------------------
/src/blocks/datepicker/deprecated/save.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * ! DEPRECATED SAVE VERSION
4 | *
5 | */
6 |
7 | import React from "react";
8 | import { isEmpty } from "lodash";
9 | import { strip_tags } from "../../../block/misc/helper";
10 | import { stringifyCondition } from "../../../block/functions";
11 |
12 | function save(props) {
13 | const {
14 | placeholder,
15 | isRequired,
16 | label,
17 | id,
18 | requiredLabel,
19 | type,
20 | messages: { empty },
21 | format,
22 | condition,
23 | enableCondition,
24 | } = props.attributes;
25 |
26 | const getLabel = () => {
27 | const { label, isRequired } = props.attributes;
28 | let required = !isEmpty(requiredLabel)
29 | ? `${requiredLabel} `
30 | : "";
31 | let required_label = label + " " + required;
32 |
33 | if (isRequired) return required_label;
34 |
35 | return label;
36 | };
37 |
38 | let getFieldType = () => {
39 | switch (type) {
40 | case "both":
41 | return "datetime-local";
42 | case "time":
43 | return "time";
44 | case "date":
45 | return "date";
46 | }
47 | };
48 |
49 | let errors = JSON.stringify({
50 | empty,
51 | });
52 |
53 | const getCondition = () => {
54 | if (!isEmpty(condition.field) && enableCondition) {
55 | //verifying the condition
56 | return {
57 | "data-condition": stringifyCondition(condition),
58 | };
59 | }
60 |
61 | return {};
62 | };
63 |
64 | return (
65 |
66 |
67 | {!isEmpty(label) && (
68 |
72 | )}
73 |
89 |
90 |
91 | );
92 | }
93 |
94 | export default save;
95 |
--------------------------------------------------------------------------------
/src/blocks/datepicker/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import { getFieldTransform } from "../../block/functions";
5 | import { fieldParents, myAttrs } from "../../constants";
6 | import datePickerEdit from "./edit.js";
7 | import datePickerSave from "./save.js";
8 | import { deprecated } from "./deprecated/deprecated";
9 |
10 | import blockData from "./block.json";
11 |
12 | const { attributes, title } = blockData;
13 |
14 | registerBlockType("cwp/datepicker", {
15 | title: __(title),
16 | icon: "calendar-alt",
17 | category: "gutenberg-forms",
18 | keywords: [__("gutenberg-forms"), __("forms"), __("datepicker")],
19 | edit: datePickerEdit,
20 | save: datePickerSave,
21 | attributes,
22 | deprecated,
23 | transforms: {
24 | from: [
25 | {
26 | type: "block",
27 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
28 | transform: (a) => getFieldTransform(a, "datepicker"),
29 | },
30 | ],
31 | },
32 | parent: fieldParents,
33 | });
34 |
--------------------------------------------------------------------------------
/src/blocks/email/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Email",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "email": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Email"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "requiredLabel": {
29 | "type": "string",
30 | "default": "*"
31 | },
32 | "messages": {
33 | "type": "object",
34 | "default": {
35 | "empty": "Please fill out this field!",
36 | "invalidEmail": "The email {{value}} is not valid!"
37 | }
38 | },
39 | "condition": {
40 | "type": "object",
41 | "default": {
42 | "field": null,
43 | "condition": "===",
44 | "value": ""
45 | }
46 | },
47 | "adminId": {
48 | "type": "object",
49 | "default": {
50 | "default": "",
51 | "value": ""
52 | }
53 | },
54 | "prefix": {
55 | "type": "object",
56 | "default": {
57 | "enable": false,
58 | "content": "",
59 | "position": "outside"
60 | }
61 | },
62 | "suffix": {
63 | "type": "object",
64 | "default": {
65 | "enable": false,
66 | "content": "",
67 | "position": "outside"
68 | }
69 | },
70 | "showHint" :{
71 | "type": "boolean",
72 | "default": false
73 | },
74 | "hint": {
75 | "type": "string",
76 | "default": ""
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/blocks/email/deprecated/deprecation.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Handling the deprecation
4 | */
5 |
6 | import blockData from "../block.json";
7 | import edit from "./edit";
8 | import save from "./save";
9 |
10 | const { attributes } = blockData;
11 |
12 | export const deprecation = [
13 | {
14 | attributes,
15 | save,
16 | edit,
17 | },
18 | ];
19 |
--------------------------------------------------------------------------------
/src/blocks/email/deprecated/save.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ! Deprecated Save Version
3 | */
4 |
5 | import React from "react";
6 | import { isEmpty } from "lodash";
7 | import { strip_tags } from "../../../block/misc/helper";
8 | import { stringifyCondition } from "../../../block/functions";
9 |
10 | function save(props) {
11 | const {
12 | email,
13 | isRequired,
14 | label,
15 | id,
16 | requiredLabel,
17 | messages,
18 | messages: { invalidEmail, empty },
19 | condition,
20 | enableCondition,
21 | adminId,
22 | } = props.attributes;
23 |
24 | const getLabel = () => {
25 | const { label, isRequired } = props.attributes;
26 |
27 | const required = !isEmpty(requiredLabel)
28 | ? `${requiredLabel} `
29 | : "";
30 |
31 | const required_label = label + " " + required;
32 |
33 | if (isRequired) {
34 | return required_label;
35 | }
36 |
37 | return label;
38 | };
39 |
40 | const errors = JSON.stringify({
41 | mismatch: invalidEmail,
42 | empty,
43 | });
44 |
45 | const getCondition = () => {
46 | if (!isEmpty(condition.field) && enableCondition) {
47 | //verifying the condition
48 | return {
49 | "data-condition": stringifyCondition(condition),
50 | };
51 | }
52 |
53 | return {};
54 | };
55 |
56 | return (
57 |
58 |
59 | {!isEmpty(label) && (
60 |
64 | )}
65 |
77 |
78 |
79 | );
80 | }
81 |
82 | export default save;
83 |
--------------------------------------------------------------------------------
/src/blocks/email/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import emailEdit from "./edit.js";
5 | import emailSave from "./save.js";
6 | import { getFieldTransform } from "../../block/functions";
7 | import { fieldParents, myAttrs } from "../../constants";
8 |
9 | import blockData from "./block.json";
10 |
11 | const { title, attributes } = blockData;
12 |
13 | import { deprecation } from "./deprecated/deprecation";
14 |
15 | registerBlockType("cwp/email", {
16 | title: __(title),
17 | icon: "email",
18 | category: "gutenberg-forms",
19 | keywords: [__("gutenberg-forms"), __("forms"), __("mail")],
20 | edit: emailEdit,
21 | save: emailSave,
22 | transforms: {
23 | from: [
24 | {
25 | type: "block",
26 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
27 | transform: (a) => getFieldTransform(a, "email"),
28 | },
29 | ],
30 | },
31 | deprecated: deprecation,
32 | attributes,
33 | parent: fieldParents,
34 | });
35 |
--------------------------------------------------------------------------------
/src/blocks/email/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { strip_tags } from "../../block/misc/helper";
4 | import { stringifyCondition } from "../../block/functions";
5 | import Prefix from "../components/prefix";
6 | import Suffix from "../components/suffix";
7 |
8 | function save(props) {
9 | const {
10 | email,
11 | isRequired,
12 | label,
13 | id,
14 | requiredLabel,
15 | messages,
16 | messages: { invalidEmail, empty },
17 | condition,
18 | enableCondition,
19 | adminId,
20 | prefix,
21 | suffix,
22 | hint,
23 | showHint
24 | } = props.attributes;
25 |
26 | const getLabel = () => {
27 | const { label, isRequired } = props.attributes;
28 |
29 | const required = !isEmpty(requiredLabel)
30 | ? `${requiredLabel} `
31 | : "";
32 |
33 | const required_label = label + " " + required;
34 |
35 | if (isRequired) {
36 | return required_label;
37 | }
38 |
39 | return label;
40 | };
41 |
42 | const errors = JSON.stringify({
43 | mismatch: invalidEmail,
44 | empty,
45 | });
46 |
47 | const getCondition = () => {
48 | if (!isEmpty(condition.field) && enableCondition) {
49 | //verifying the condition
50 | return {
51 | "data-condition": stringifyCondition(condition),
52 | };
53 | }
54 |
55 | return {};
56 | };
57 |
58 | return (
59 |
60 |
61 | {!isEmpty(label) && (
62 |
66 | )}
67 |
68 | {prefix.enable && (
69 |
70 |
71 |
72 | )}
73 |
74 |
86 | {suffix.enable && (
87 |
88 |
89 |
90 | )}
91 |
92 |
93 | {showHint && (
94 |
{hint}
95 | )}
96 |
97 | );
98 | }
99 |
100 | export default save;
101 |
--------------------------------------------------------------------------------
/src/blocks/file_upload/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "File",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "file": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Select File"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "messages": {
29 | "type": "object",
30 | "default": {
31 | "empty": "Please select a file",
32 | "invalid": "The file {{value}} is not valid!"
33 | }
34 | },
35 | "condition": {
36 | "type": "object",
37 | "default": {
38 | "field": null,
39 | "condition": "===",
40 | "value": ""
41 | }
42 | },
43 | "requiredLabel": {
44 | "type": "string",
45 | "default": "*"
46 | },
47 | "allowedFormats": {
48 | "type": "string",
49 | "default": [
50 | "jpg",
51 | "jpeg",
52 | "png",
53 | "gif",
54 | "pdf",
55 | "doc",
56 | "docx",
57 | "ppt",
58 | "pptx",
59 | "odt",
60 | "avi",
61 | "ogg",
62 | "m4a",
63 | "mov",
64 | "mp3",
65 | "mp4",
66 | "mpg",
67 | "wav",
68 | "wmv"
69 | ]
70 | },
71 | "adminId": {
72 | "type": "object",
73 | "default": {
74 | "default": "",
75 | "value": ""
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/blocks/file_upload/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import { fieldParents } from "../../constants";
5 |
6 | import fileUploadEdit from "./edit.js";
7 | import fileUploadSave from "./save.js";
8 |
9 | import blockData from "./block.json";
10 |
11 | const { title, attributes } = blockData;
12 |
13 | registerBlockType("cwp/file-upload", {
14 | title: __(title),
15 | icon: "media-document",
16 | category: "gutenberg-forms",
17 | keywords: [__("gutenberg-forms"), __("forms"), __("file"), __("file upload")],
18 | edit: fileUploadEdit,
19 | save: fileUploadSave,
20 | attributes,
21 | supports: {
22 | align: true,
23 | align: ["wide", "full", "center"],
24 | },
25 | parent: fieldParents,
26 | });
27 |
--------------------------------------------------------------------------------
/src/blocks/file_upload/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { strip_tags } from "../../block/misc/helper";
4 | import { stringifyCondition } from "../../block/functions";
5 |
6 | function save(props) {
7 | const {
8 | file,
9 | isRequired,
10 | label,
11 | id,
12 | requiredLabel,
13 | messages: { empty, invalid },
14 | condition,
15 | allowedFormats
16 | } = props.attributes;
17 |
18 | const getLabel = () => {
19 | const { label, isRequired } = props.attributes;
20 |
21 | let required = !isEmpty(requiredLabel)
22 | ? `${requiredLabel} `
23 | : "";
24 | let required_label = label + " " + required;
25 |
26 | if (isRequired) return required_label;
27 |
28 | return label;
29 | };
30 |
31 | let errors = JSON.stringify({
32 | mismatch: invalid,
33 | empty
34 | });
35 |
36 | const getCondition = () => {
37 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
38 | //verifying the condition
39 | return {
40 | "data-condition": stringifyCondition(condition)
41 | };
42 | }
43 |
44 | return {};
45 | };
46 | const suggestions = [
47 | "jpg",
48 | "jpeg",
49 | "png",
50 | "gif",
51 | "pdf",
52 | "doc",
53 | "docx",
54 | "ppt",
55 | "pptx",
56 | "odt",
57 | "avi",
58 | "ogg",
59 | "m4a",
60 | "mov",
61 | "mp3",
62 | "mp4",
63 | "mpg",
64 | "wav",
65 | "wmv"
66 | ]
67 |
68 | const acceptFiles = isEmpty(allowedFormats) ? suggestions.map(s => ".".concat(s)).join(",") : allowedFormats.map(s => ".".concat(s)).join(",");
69 |
70 | return (
71 |
72 |
73 | {!isEmpty(label) && (
74 |
78 | )}
79 |
91 |
92 |
93 | );
94 | }
95 |
96 | export default save;
97 |
--------------------------------------------------------------------------------
/src/blocks/form-button/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Button",
3 | "attributes": {
4 | "label": {
5 | "type": "string",
6 | "default": "Submit"
7 | },
8 | "parentId": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "action": {
13 | "default": "submit",
14 | "type": "string"
15 | },
16 | "styling": {
17 | "type": "object",
18 | "default": {
19 | "backgroundColor": "rgb(238, 238, 238)",
20 | "color": "rgb(49, 49, 49)",
21 | "padding": 25,
22 | "borderRadius": 0
23 | }
24 | }
25 | },
26 | "supports": {
27 | "align": true,
28 | "align": ["wide", "full", "center", "left", "right"]
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/blocks/form-button/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import { fieldParents } from "../../constants";
5 | import formButtonEdit from "./edit.js";
6 | import formButtonSave from "./save.js";
7 |
8 | import blockData from "./block.json";
9 | const { title, attributes } = blockData;
10 |
11 | registerBlockType("cwp/form-button", {
12 | title: __(title),
13 | icon: __(
14 |
23 |
24 |
25 | ),
26 | supports: {
27 | align: true,
28 | align: ["left", "center", "right"],
29 | },
30 | category: "gutenberg-forms",
31 | keywords: [__("gutenberg-forms"), __("forms"), __("button")],
32 | edit: formButtonEdit,
33 | save: formButtonSave,
34 | attributes,
35 | parent: fieldParents,
36 | });
37 |
--------------------------------------------------------------------------------
/src/blocks/form-button/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function save(props) {
4 | const {
5 | styling,
6 | label,
7 | action,
8 | parentId,
9 | styling: { padding },
10 | } = props.attributes;
11 |
12 | const buttonStyling = {
13 | ...styling,
14 | padding: `${Math.floor(padding / 3)}px ${padding}px`,
15 | };
16 |
17 | switch (action) {
18 | case "submit":
19 | return (
20 |
27 | );
28 | case "reset":
29 | return (
30 |
35 | );
36 | default:
37 | return (
38 |
44 | );
45 | }
46 | }
47 |
48 | export default save;
49 |
--------------------------------------------------------------------------------
/src/blocks/form-calculation/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Calculation",
3 | "attributes": {
4 | "formulaBuilder": {
5 | "type": "boolean",
6 | "default": true
7 | },
8 | "calculation": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "label": {
13 | "type": "string",
14 | "default": "Total"
15 | },
16 | "id": {
17 | "type": "string",
18 | "default": ""
19 | },
20 | "field_name": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "formula": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "condition": {
29 | "type": "object",
30 | "default": {
31 | "field": null,
32 | "condition": "===",
33 | "value": ""
34 | }
35 | },
36 | "styling": {
37 | "type": "object",
38 | "default": {
39 | "fontSize": 40
40 | }
41 | },
42 | "enableCondition": {
43 | "type": "boolean",
44 | "default": false
45 | },
46 | "postfix": {
47 | "type": "string",
48 | "default": ""
49 | },
50 | "prefix": {
51 | "type": "string",
52 | "default": ""
53 | },
54 | "adminId": {
55 | "type": "object",
56 | "default": {
57 | "default": "",
58 | "value": ""
59 | }
60 | },
61 | "decimalPlaces": {
62 | "type": "number",
63 | "default": 0
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/blocks/form-calculation/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import calculationEdit from "./edit.js";
5 | import calculationSave from "./save.js";
6 | import { fieldParents } from "../../constants";
7 | import Icon from "../../block/Icon.js";
8 |
9 | import blockData from "./block.json";
10 | const { attributes, title } = blockData;
11 |
12 | registerBlockType("cwp/form-calculation", {
13 | title: __(title),
14 | icon: __( ),
15 | category: "gutenberg-forms",
16 | keywords: [__("gutenberg-forms"), __("forms"), __("calculation")],
17 | edit: calculationEdit,
18 | save: calculationSave,
19 | attributes,
20 | supports: {
21 | align: ["wide", "full", "center"],
22 | },
23 | parent: fieldParents,
24 | });
25 |
--------------------------------------------------------------------------------
/src/blocks/form-calculation/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { strip_tags } from "../../block/misc/helper";
4 | import { stringifyCondition } from "../../block/functions";
5 |
6 | function save(props) {
7 | const {
8 | calculation,
9 | label,
10 | id,
11 | formula,
12 | condition,
13 | enableCondition,
14 | prefix,
15 | postfix,
16 | styling,
17 | decimalPlaces
18 | } = props.attributes;
19 |
20 | const getLabel = () => {
21 | const { label } = props.attributes;
22 |
23 | return label;
24 | };
25 |
26 | const getCondition = () => {
27 | if (props.attributes.enableCondition) {
28 | //verifying the condition
29 | return {
30 | "data-condition": stringifyCondition(condition)
31 | };
32 | }
33 |
34 | return {};
35 | };
36 |
37 | const getCalculation = () => {
38 | if (!isEmpty(formula)) {
39 | return {
40 | "data-cwp-calculation": formula
41 | };
42 | }
43 |
44 | return {};
45 | };
46 |
47 | return (
48 |
54 |
55 | {!isEmpty(label) && (
56 |
60 | )}
61 |
62 | {!isEmpty(prefix) && {prefix} }
63 |
64 | 0
65 |
66 | {!isEmpty(postfix) && {postfix} }
67 |
68 |
78 |
79 |
80 | );
81 | }
82 |
83 | export default save;
84 |
--------------------------------------------------------------------------------
/src/blocks/form-column/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Column",
3 | "attributes": {
4 | "columns": {
5 | "type": "number",
6 | "default": 3
7 | },
8 | "intro": {
9 | "type": "boolean",
10 | "default": false
11 | },
12 | "stack": {
13 | "type": "boolean",
14 | "default": true
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/blocks/form-column/components/introduction.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Icon from "../../../block/Icon";
3 |
4 | function Introduction(props) {
5 | const selections = [
6 | {
7 | label: ,
8 | value: 2
9 | },
10 | {
11 | label: ,
12 | value: 3
13 | },
14 | {
15 | label: ,
16 | value: 4
17 | }
18 | ];
19 |
20 | return (
21 |
22 |
Columns
23 |
Select your column for the form!
24 | {selections.map(selection => (
25 |
props.onSelect(selection.value)}
27 | className="cwp_selector"
28 | >
29 | {selection.label}
30 |
31 | ))}
32 |
33 | );
34 | }
35 |
36 | export default Introduction;
37 |
--------------------------------------------------------------------------------
/src/blocks/form-column/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType, createBlock } = wp.blocks;
3 |
4 | import formColumnEdit from "./edit.js";
5 | import formColumnSave from "./save.js";
6 | import { fieldParents } from "../../constants";
7 |
8 | import blockData from "./block.json";
9 | const { attributes, title } = blockData;
10 |
11 | registerBlockType("cwp/form-column", {
12 | title: __(title),
13 | icon: "editor-table",
14 | category: "gutenberg-forms",
15 | keywords: [
16 | __("gutenberg-forms"),
17 | __("forms"),
18 | __("form-column"),
19 | __("column"),
20 | ],
21 | edit: formColumnEdit,
22 | save: formColumnSave,
23 | attributes,
24 | supports: {
25 | align: true,
26 | align: ["wide", "full", "center"],
27 | },
28 | parent: fieldParents,
29 | });
30 |
--------------------------------------------------------------------------------
/src/blocks/form-column/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { InnerBlocks } from "@wordpress/block-editor";
3 |
4 | function save(props) {
5 | const { columns, stack } = props.attributes,
6 | stackClass = stack ? "cwp_stack_columns" : "";
7 |
8 | return (
9 |
13 |
14 |
15 | );
16 | }
17 |
18 | export default save;
19 |
--------------------------------------------------------------------------------
/src/blocks/form-group/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Group",
3 | "attributes": {
4 | "styling": {
5 | "type": "object",
6 | "default": {
7 | "backgroundColor": "rgb(238, 238, 238)",
8 | "color": "rgb(49, 49, 49)",
9 | "padding": 25,
10 | "borderColor": "rgb(220, 215, 202)",
11 | "borderWidth": 2,
12 | "borderRadius": 0
13 | }
14 | },
15 | "label": {
16 | "type": "string",
17 | "default": "My Group"
18 | },
19 | "content": {
20 | "type": "string",
21 | "default": ""
22 | },
23 | "condition": {
24 | "type": "object",
25 | "default": {
26 | "field": null,
27 | "condition": "===",
28 | "value": ""
29 | }
30 | },
31 | "enableCondition": {
32 | "type": "boolean",
33 | "default": false
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/blocks/form-group/edit.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import Inspector from "./Inspector";
3 | import { Notice } from "@wordpress/components";
4 | import { isChildFieldsRequired } from "../../block/functions";
5 | const { InnerBlocks, RichText } = wp.blockEditor;
6 | const { __ } = wp.i18n;
7 |
8 |
9 | function edit(props) {
10 | const { styling, label, enableCondition } = props.attributes;
11 |
12 | const handleLabel = label => {
13 | props.setAttributes({ label });
14 | };
15 |
16 | const groupStyling = {
17 | border: `${styling.borderWidth}px solid ${styling.borderColor}`,
18 | ...styling
19 | }
20 |
21 | return [
22 | !!props.isSelected && ,
23 | null,
24 |
25 | {isChildFieldsRequired(props.clientId) && enableCondition && (
26 |
27 | {
28 | __("Do not have a required fields inside a conditional group.", "forms-gutenberg")
29 | }
30 |
31 | )}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ];
40 | }
41 |
42 | export default edit;
43 |
--------------------------------------------------------------------------------
/src/blocks/form-group/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import { fieldParents } from "../../constants";
5 | import formGroupEdit from "./edit.js";
6 | import formGroupSave from "./save.js";
7 |
8 | import blockData from "./block.json";
9 | const { attributes, title } = blockData;
10 |
11 | registerBlockType("cwp/form-group", {
12 | title: __(title),
13 | icon: "forms",
14 | category: "gutenberg-forms",
15 | keywords: [
16 | __("gutenberg-forms"),
17 | __("forms"),
18 | __("form group"),
19 | __("column"),
20 | ],
21 | edit: formGroupEdit,
22 | save: formGroupSave,
23 | attributes,
24 | supports: {
25 | align: true,
26 | align: ["wide", "full", "center"],
27 | },
28 | parent: fieldParents,
29 | });
30 |
--------------------------------------------------------------------------------
/src/blocks/form-group/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { stringifyCondition } from "../../block/functions";
4 | const { InnerBlocks } = wp.blockEditor;
5 |
6 | function save(props) {
7 | const { styling, label, condition, enableCondition } = props.attributes;
8 |
9 | const groupStyling = {
10 | border: `${styling.borderWidth}px solid ${styling.borderColor}`,
11 | ...styling
12 | }
13 |
14 |
15 | const getCondition = () => {
16 | if (enableCondition) {
17 | //verifying the condition
18 | return {
19 | "data-condition": stringifyCondition(condition)
20 | };
21 | }
22 |
23 | return {};
24 | };
25 | return (
26 |
27 | {!isEmpty(label) && (
28 |
29 | )}
30 |
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | export default save;
38 |
--------------------------------------------------------------------------------
/src/blocks/form-steps/childs/form-step/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Step",
3 | "attributes": {
4 | "label": {
5 | "type": "string",
6 | "default": "Form Step"
7 | },
8 | "hideStep": {
9 | "type": "boolean",
10 | "default": false
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/blocks/form-steps/childs/form-step/edit.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, Fragment } from "react";
2 | import { InnerBlocks } from "@wordpress/block-editor";
3 | import { getRootFormBlock } from "../../../../block/functions/index";
4 | import { isEmpty, get } from "lodash";
5 | import { Notice, TextControl, PanelBody } from "@wordpress/components";
6 |
7 | const { InspectorControls } = wp.blockEditor;
8 | const { __ } = wp.i18n;
9 |
10 | function edit(props) {
11 | const [disabled, setDisabled] = useState(false);
12 |
13 | const { setAttributes } = props;
14 | const { label, hideStep } = props.attributes;
15 |
16 | useEffect(() => {
17 | const root = getRootFormBlock(props.clientId);
18 |
19 | if (!isEmpty(root)) {
20 | let root_type = get(root, "attributes.formType");
21 |
22 | root_type === "standard" ? setDisabled(true) : null; // checking if the root form is a multistep
23 | }
24 | }, []);
25 |
26 | return [
27 | props.isSelected && !disabled && (
28 |
29 |
30 | setAttributes({ label })}
35 | />
36 |
37 |
38 | ),
39 | null,
40 |
41 | {disabled ? (
42 |
43 | This is to be used only within the Multi-Step Form.
44 |
45 | ) : (
46 |
50 |
51 |
52 | )}
53 |
,
54 | ];
55 | }
56 |
57 | export default edit;
58 |
--------------------------------------------------------------------------------
/src/blocks/form-steps/childs/form-step/index.js:
--------------------------------------------------------------------------------
1 | const { registerBlockType } = wp.blocks;
2 | const { __ } = wp.i18n;
3 |
4 | import stepFormEdit from "./edit";
5 | import stepFormSave from "./save";
6 |
7 | import blockData from "./block.json";
8 | const { attributes, title } = blockData;
9 |
10 | registerBlockType("cwp/form-step", {
11 | title: __(title),
12 | icon: "editor-ol-rtl",
13 | category: "gutenberg-forms",
14 | keywords: [
15 | __("gutenberg-forms"),
16 | __("forms"),
17 | __("form-step"),
18 | __("step"),
19 | __("multistep"),
20 | ],
21 | edit: stepFormEdit,
22 | save: stepFormSave,
23 | attributes,
24 | parent: ["cwp/form-steps"],
25 | });
26 |
--------------------------------------------------------------------------------
/src/blocks/form-steps/childs/form-step/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { InnerBlocks } from "@wordpress/block-editor";
3 | import { isEmpty } from "lodash";
4 |
5 | function save(props) {
6 | const { label } = props.attributes;
7 |
8 | const stepLabel = isEmpty(label) ? "Form Step" : label;
9 |
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 |
17 | export default save;
18 |
--------------------------------------------------------------------------------
/src/blocks/form-steps/root/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Form Steps",
3 | "attributes": {
4 | "currentStep": {
5 | "type": "number",
6 | "default": 0
7 | },
8 | "multiStepEffect": {
9 | "type": "string",
10 | "default": "cwp-noEffect-step"
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/blocks/form-steps/root/index.js:
--------------------------------------------------------------------------------
1 | const { registerBlockType } = wp.blocks;
2 | const { __ } = wp.i18n;
3 |
4 | import stepsEdit from "./edit";
5 | import stepsSave from "./save";
6 |
7 | import blockData from "./block.json";
8 | const { title, attributes } = blockData;
9 |
10 | registerBlockType("cwp/form-steps", {
11 | title: __(title),
12 | icon: "editor-ol-rtl",
13 | category: "gutenberg-forms",
14 | supports: {
15 | inserter: false,
16 | },
17 | keywords: [
18 | __("gutenberg-forms"),
19 | __("forms"),
20 | __("form-step"),
21 | __("step"),
22 | __("multistep"),
23 | ],
24 | edit: stepsEdit,
25 | save: stepsSave,
26 | attributes,
27 | parent: ["cwp/block-gutenberg-forms"],
28 | });
29 |
--------------------------------------------------------------------------------
/src/blocks/form-steps/root/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const { InnerBlocks } = wp.editor;
4 |
5 | function save(props) {
6 | const { multiStepEffect } = props.attributes;
7 |
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | export default save;
16 |
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/components/introduction.js:
--------------------------------------------------------------------------------
1 | import React, { useState, Fragment } from "react";
2 | import { Button, Placeholder, TextControl } from "@wordpress/components";
3 | import SettingsModal from "../settings_modal/modal";
4 | const { __ } = wp.i18n;
5 |
6 | function Introduction(props) {
7 | const [modal, setModal] = useState(false);
8 | const { cpt, formLabel, id } = props.data.attributes; // weather it is a cpt or not
9 | const { setAttributes } = props.data;
10 |
11 | const handleType = (type) => {
12 | props.onSelect(type);
13 | if (formLabel === "") {
14 | const formId = id && "form-".concat(id.split("-")[1]);
15 |
16 | setAttributes({ formLabel: "Gutenberg Form - ".concat(formId) });
17 | }
18 | };
19 |
20 | return (
21 |
22 |
setModal(false)}
27 | />
28 |
36 |
37 |
setAttributes({ formLabel })}
41 | />
42 |
43 |
44 | handleType("standard")}>
45 | {__('Standard', 'forms-gutenberg')}
46 |
47 |
48 | {__('or', 'forms-gutenberg')}
49 |
50 | handleType("multiStep")}>
51 | {__('Multistep', 'forms-gutenberg')}
52 |
53 | {/* setModal(true)}>*/}
54 | {/* Insert Form*/}
55 | {/* */}
56 |
57 |
58 |
59 |
60 | );
61 | }
62 |
63 | export default Introduction;
64 |
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/components/messages.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import { TextControl, Icon } from "@wordpress/components";
3 | import { firstCapital, getFieldIcon } from "../../../block/misc/helper.js";
4 | import { has } from "lodash";
5 | const { __ } = wp.i18n;
6 |
7 | export default function CustomMessages(props) {
8 | const { val } = props;
9 |
10 | const handleChange = (t, v, i, fieldName) => {
11 | props.onChange(t, v, i, fieldName);
12 | };
13 |
14 | return (
15 |
16 | {val.map((v, i) => {
17 | let fieldName = "cwp/".concat(v.fieldName);
18 |
19 | return (
20 |
21 |
22 | {/* handleChange("empty", value, i, fieldName)}
24 | label="Required"
25 | value={v.empty}
26 | /> */}
27 | {has(v, "invalid") && (
28 |
29 |
30 |
31 | {"Invalid ".concat(firstCapital(v.fieldName))}
32 |
33 |
34 |
35 |
37 | handleChange("invalid", value, i, fieldName)
38 | }
39 | value={v.invalid}
40 | />
41 |
42 | )}
43 | {has(v, "invalidEmail") && (
44 |
45 |
46 |
47 | {"Invalid ".concat(firstCapital(v.fieldName))}
48 |
49 |
50 |
51 |
53 | handleChange("invalidEmail", value, i, fieldName)
54 | }
55 | value={v.invalidEmail}
56 | />
57 |
58 | )}
59 | {has(v, "invalidName") && (
60 |
61 |
62 |
63 | {"Invalid ".concat(firstCapital(v.fieldName))}
64 |
65 |
66 |
67 |
69 | handleChange("invalidName", value, i, fieldName)
70 | }
71 | value={v.invalidName}
72 | />
73 |
74 | )}
75 |
76 |
77 | );
78 | })}
79 |
80 | );
81 | }
82 |
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/deprecated/deprecated.js:
--------------------------------------------------------------------------------
1 | import blockData from "../block.json";
2 | import { getGlobalMessages } from "../../../block/functions";
3 | import save from "./save";
4 | import edit from "./edit";
5 |
6 | const blockAttributes = {
7 | ...blockData.attributes,
8 | messages: {
9 | type: "array",
10 | default: getGlobalMessages(),
11 | },
12 | template: {
13 | type: "string",
14 | default: JSON.stringify({
15 | subject: "",
16 | body: "",
17 | }),
18 | },
19 | };
20 |
21 | export const deprecation = [
22 | {
23 | attributes: blockAttributes,
24 | edit,
25 | save,
26 | },
27 | ];
28 |
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import mainEdit from "./edit";
5 | import mainSave from "./save";
6 | import {
7 | getGlobalMessages,
8 | get_submission_message,
9 | } from "../../block/functions";
10 | import { fieldSupport } from "../../constants";
11 | import { deprecation } from "./deprecated/deprecated";
12 | import { get } from "lodash";
13 |
14 | import blockData from "./block.json";
15 |
16 | const { error, spam } = get_submission_message();
17 |
18 | const blockAttributes = {
19 | ...blockData.attributes,
20 | messages: {
21 | type: "array",
22 | default: getGlobalMessages(),
23 | },
24 | template: {
25 | type: "string",
26 | default: JSON.stringify({
27 | subject: "",
28 | body: "",
29 | }),
30 | },
31 | spamMessage: {
32 | type: "string",
33 | default: get(spam, "value"),
34 | },
35 | errorMessage: {
36 | type: "string",
37 | default: get(error, "value"),
38 | },
39 | };
40 |
41 | registerBlockType("cwp/block-gutenberg-forms", {
42 | supports: {
43 | ...fieldSupport,
44 | reusable: false,
45 | },
46 | title: __(blockData.title),
47 | icon: __("feedback"),
48 | category: "gutenberg-forms",
49 | keywords: [__("gutenberg-forms"), __("forms")],
50 | example: blockData.example,
51 | attributes: blockAttributes,
52 | deprecated: deprecation,
53 | edit: mainEdit,
54 | save: mainSave,
55 | });
56 |
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/settings_modal/components/Empty.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Button } from "@wordpress/components"
3 |
4 | const new_form_url = cwpGlobal.new_form_url;
5 |
6 | function Empty({ message }) {
7 |
8 |
9 | return (
10 |
11 |
12 |
{message}
13 |
14 |
15 | Create Form
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Empty;
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/settings_modal/components/PostTypeBlock.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Button, Card, CardBody, CardHeader, CardFooter, CardMedia, Icon } from '@wordpress/components'
3 | const { createBlock, parse } = wp.blocks;
4 | const { replaceBlock } = wp.data.dispatch("core/block-editor");
5 |
6 |
7 | function PostTypeBlock(props) {
8 |
9 | const { form: { post_title, ID } } = props;
10 |
11 | const apply_template = () => {
12 | replaceBlock(
13 | props.clientId,
14 | createBlock(
15 | 'cwp/reusable-form',
16 | {
17 | formId: ID.toString()
18 | }
19 | )
20 | );
21 |
22 | props.onSelect();
23 | }
24 |
25 | return (
26 |
27 |
28 |
29 |
30 |
{post_title}
31 |
32 | Insert
33 |
34 |
35 | )
36 | }
37 |
38 |
39 | export default PostTypeBlock;
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/settings_modal/components/header.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TextControl, Icon } from "@wordpress/components";
3 |
4 | function Header(props) {
5 | return (
6 |
7 |
8 |
{props.currentCatagory}
9 |
10 |
11 |
12 |
13 |
14 | );
15 | }
16 |
17 | export default Header;
18 |
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/settings_modal/components/preview_block.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Button, Card, CardBody, CardHeader, CardFooter, CardMedia, Icon } from '@wordpress/components'
3 | import { get } from 'lodash'
4 | const { createBlock, parse } = wp.blocks;
5 | const { replaceBlock } = wp.data.dispatch("core/block-editor");
6 |
7 | function PreviewBlock(props) {
8 |
9 | const { data } = props;
10 |
11 | const { fields } = data;
12 |
13 | const name = get(fields, 'Name'),
14 | screenshot = get(fields, 'Screenshot[0].thumbnails.large.url'),
15 | code = get(fields, 'Code');
16 |
17 |
18 |
19 | const apply_template = () => {
20 |
21 |
22 | const [template] = parse(code);
23 |
24 |
25 | const { name, attributes, innerBlocks } = template;
26 |
27 | replaceBlock(
28 | props.clientId,
29 | createBlock(
30 | name,
31 | attributes,
32 | innerBlocks
33 | )
34 | );
35 | props.onSelect();
36 | }
37 |
38 | return (
39 |
40 |
41 | Insert Form
42 |
43 |
44 |
45 |
46 |
47 |
48 | )
49 | }
50 |
51 | export default PreviewBlock;
--------------------------------------------------------------------------------
/src/blocks/gutenberg-forms/settings_modal/components/templates.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 |
4 | Panel,
5 | MenuGroup,
6 | MenuItem
7 |
8 | } from "@wordpress/components"
9 | import { get, isEqual } from "lodash"
10 |
11 | const { __ } = wp.i18n;
12 |
13 | function Templates(props) {
14 |
15 |
16 | const { templates, currentTemplate } = props;
17 |
18 | const currentTemplateName = get(currentTemplate, 'fields.Name');
19 |
20 | return (
21 |
22 |
Available Templates, "forms-gutenberg")}>
23 |
24 | {
25 | templates.map((template, index) => {
26 |
27 | const name = get(template, 'fields.Name');
28 |
29 | return props.onSelect(template)} isDefault={isEqual(currentTemplateName, name)} key={index}>{name}
30 |
31 | })
32 | }
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | export default Templates;
40 |
--------------------------------------------------------------------------------
/src/blocks/hidden/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Hidden",
3 | "attributes": {
4 | "value": {
5 | "type": "string",
6 | "default": ""
7 | },
8 | "label": {
9 | "type": "string",
10 | "default": "Hidden"
11 | },
12 | "id": {
13 | "type": "string",
14 | "default": ""
15 | },
16 | "field_name": {
17 | "type": "string",
18 | "default": ""
19 | },
20 | "adminId": {
21 | "type": "object",
22 | "default": {
23 | "default": "",
24 | "value": ""
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/blocks/hidden/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import hiddenEdit from "./edit.js";
5 | import hiddenSave from "./save.js";
6 | import { fieldParents } from "../../constants";
7 |
8 | import blockData from "./block.json";
9 | const { attributes,title } = blockData;
10 |
11 | registerBlockType("cwp/hidden", {
12 | title: __(title),
13 | icon: "hidden",
14 | category: "gutenberg-forms",
15 | keywords: [__("gutenberg-forms"), __("forms"), __("hidden"), __("field")],
16 | edit: hiddenEdit,
17 | save: hiddenSave,
18 | attributes,
19 | parent: fieldParents,
20 | });
21 |
--------------------------------------------------------------------------------
/src/blocks/hidden/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function save(props) {
4 | const { id, value } = props.attributes;
5 |
6 | return (
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | export default save;
14 |
--------------------------------------------------------------------------------
/src/blocks/message/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Textarea",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "message": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Message"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "height": {
25 | "type": "number",
26 | "default": 200
27 | },
28 | "field_name": {
29 | "type": "string",
30 | "default": ""
31 | },
32 | "messages": {
33 | "type": "object",
34 | "default": {
35 | "empty": "Please fill out this field!",
36 | "invalid": "The message {{value}} is not valid!"
37 | }
38 | },
39 | "pattern": {
40 | "type": "string",
41 | "default": ""
42 | },
43 | "condition": {
44 | "type": "object",
45 | "default": {
46 | "field": null,
47 | "condition": "===",
48 | "value": ""
49 | }
50 | },
51 | "requiredLabel": {
52 | "type": "string",
53 | "default": "*"
54 | },
55 | "minimumLength": {
56 | "type": "number",
57 | "default": 0
58 | },
59 | "maximumLength": {
60 | "type": "number",
61 | "default": 1000
62 | },
63 | "adminId": {
64 | "type": "object",
65 | "default": {
66 | "default": "",
67 | "value": ""
68 | }
69 | },
70 | "showHint" :{
71 | "type": "boolean",
72 | "default": false
73 | },
74 | "hint": {
75 | "type": "string",
76 | "default": ""
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/blocks/message/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import { getFieldTransform } from "../../block/functions";
5 | import { fieldParents, myAttrs } from "../../constants";
6 | import messageEdit from "./edit.js";
7 | import messageSave from "./save.js";
8 |
9 | import blockData from "./block.json";
10 | const { attributes, title } = blockData;
11 |
12 | registerBlockType("cwp/message", {
13 | title: __(title),
14 | icon: "testimonial",
15 | category: "gutenberg-forms",
16 | keywords: [__("gutenberg-forms"), __("forms"), __("message")],
17 | edit: messageEdit,
18 | save: messageSave,
19 | attributes,
20 | transforms: {
21 | from: [
22 | {
23 | type: "block",
24 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
25 | transform: (a) => getFieldTransform(a, "message"),
26 | },
27 | ],
28 | },
29 | parent: fieldParents,
30 | });
31 |
--------------------------------------------------------------------------------
/src/blocks/message/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { strip_tags } from "../../block/misc/helper";
4 | import { stringifyCondition } from "../../block/functions";
5 |
6 | function save(props) {
7 | const {
8 | message,
9 | isRequired,
10 | label,
11 | id,
12 | height,
13 | requiredLabel,
14 | messages: { empty, invalid },
15 | pattern,
16 | condition,
17 | enableCondition,
18 | minimumLength,
19 | maximumLength,
20 | hint,
21 | showHint
22 | } = props.attributes;
23 |
24 | const getLabel = () => {
25 | const { label, isRequired } = props.attributes;
26 |
27 | let required = !isEmpty(requiredLabel)
28 | ? `${requiredLabel} `
29 | : "";
30 | let required_label = label + " " + required;
31 |
32 | if (isRequired) return required_label;
33 |
34 | return label;
35 | };
36 | let errors = JSON.stringify({
37 | mismatch: invalid,
38 | empty
39 | });
40 | let getPattern = () => {
41 | return isEmpty(pattern) ? {} : { pattern };
42 | };
43 |
44 | const getCondition = () => {
45 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
46 | //verifying the condition
47 | return {
48 | "data-condition": stringifyCondition(condition)
49 | };
50 | }
51 |
52 | return {};
53 | };
54 |
55 | return (
56 |
57 |
58 | {!isEmpty(label) && (
59 |
63 | )}
64 |
79 |
80 | {showHint && (
81 |
{hint}
82 | )}
83 |
84 | );
85 | }
86 |
87 | export default save;
88 |
--------------------------------------------------------------------------------
/src/blocks/name/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Name",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "name": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Name"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "messages": {
29 | "type": "object",
30 | "default": {
31 | "empty": "Please fill out this field!",
32 | "invalidName": "The name {{value}} is not valid!"
33 | }
34 | },
35 | "pattern": {
36 | "type": "string",
37 | "default": ""
38 | },
39 | "condition": {
40 | "type": "object",
41 | "default": {
42 | "field": null,
43 | "condition": "===",
44 | "value": ""
45 | }
46 | },
47 | "requiredLabel": {
48 | "type": "string",
49 | "default": "*"
50 | },
51 | "adminId": {
52 | "type": "object",
53 | "default": {
54 | "default": "",
55 | "value": ""
56 | }
57 | },
58 | "prefix": {
59 | "type": "object",
60 | "default": {
61 | "enable": false,
62 | "content": "",
63 | "position": "outside"
64 | }
65 | },
66 | "suffix": {
67 | "type": "object",
68 | "default": {
69 | "enable": false,
70 | "content": "",
71 | "position": "outside"
72 | }
73 | },
74 | "showHint" :{
75 | "type": "boolean",
76 | "default": false
77 | },
78 | "hint": {
79 | "type": "string",
80 | "default": ""
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/blocks/name/deprecated/deprecated.js:
--------------------------------------------------------------------------------
1 | import { attributes } from "../block.json";
2 | import edit from "./edit";
3 | import save from "./save";
4 |
5 | export const deprecated = [
6 | {
7 | attributes,
8 | edit,
9 | save,
10 | },
11 | ];
12 |
--------------------------------------------------------------------------------
/src/blocks/name/deprecated/save.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * ! DEPRECATED SAVE VERSION
4 | *
5 | */
6 |
7 | import React from "react";
8 | import { isEmpty } from "lodash";
9 | import { strip_tags } from "../../../block/misc/helper";
10 | import { stringifyCondition } from "../../../block/functions";
11 | import Prefix from "../../components/prefix";
12 | import Suffix from "../../components/suffix";
13 |
14 | function save(props) {
15 | const {
16 | name,
17 | isRequired,
18 | label,
19 | id,
20 | requiredLabel,
21 | messages: { empty, invalidName },
22 | pattern,
23 | condition,
24 | enableCondition,
25 | prefix,
26 | suffix,
27 | } = props.attributes;
28 |
29 | const getLabel = () => {
30 | const { label, isRequired } = props.attributes;
31 |
32 | let required = !isEmpty(requiredLabel)
33 | ? `${requiredLabel} `
34 | : "";
35 | let required_label = label + " " + required;
36 |
37 | if (isRequired) return required_label;
38 |
39 | return label;
40 | };
41 |
42 | let errors = JSON.stringify({
43 | mismatch: invalidName,
44 | empty,
45 | });
46 |
47 | let getPattern = () => {
48 | return isEmpty(pattern) ? {} : { pattern };
49 | };
50 |
51 | const getCondition = () => {
52 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
53 | //verifying the condition
54 | return {
55 | "data-condition": stringifyCondition(condition),
56 | };
57 | }
58 |
59 | return {};
60 | };
61 |
62 | return (
63 |
64 |
65 | {!isEmpty(label) && (
66 |
70 | )}
71 |
72 | {prefix.enable && (
73 |
74 |
75 |
76 | )}
77 |
89 |
90 | {suffix.enable && (
91 |
92 |
93 |
94 | )}
95 |
96 |
97 |
98 | );
99 | }
100 |
101 | export default save;
102 |
--------------------------------------------------------------------------------
/src/blocks/name/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import nameEdit from "./edit.js";
5 | import nameSave from "./save.js";
6 | import { getFieldTransform } from "../../block/functions";
7 | import { fieldParents, myAttrs } from "../../constants";
8 | import { deprecated } from "./deprecated/deprecated";
9 |
10 | import blockData from "./block.json";
11 | const { attributes, title } = blockData;
12 |
13 | registerBlockType("cwp/name", {
14 | title: __(title),
15 | icon: "admin-users",
16 | category: "gutenberg-forms",
17 | keywords: [__("gutenberg-forms"), __("forms"), __("name")],
18 | edit: nameEdit,
19 | save: nameSave,
20 | deprecated,
21 | attributes,
22 | transforms: {
23 | from: [
24 | {
25 | type: "block",
26 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
27 | transform: (a) => getFieldTransform(a, "name"),
28 | },
29 | ],
30 | },
31 | parent: fieldParents,
32 | });
33 |
--------------------------------------------------------------------------------
/src/blocks/name/style.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nikolaystrikhar/gutenberg-forms/6106944153b490a8846ccec3c68f6dd6cf8602c5/src/blocks/name/style.scss
--------------------------------------------------------------------------------
/src/blocks/number/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Number",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "number": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Number:"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "steps": {
29 | "type": "number",
30 | "default": 1
31 | },
32 | "rangeMax": {
33 | "type": "number",
34 | "default": 100
35 | },
36 | "rangeMin": {
37 | "type": "number",
38 | "default": 0
39 | },
40 | "requiredLabel": {
41 | "type": "string",
42 | "default": "*"
43 | },
44 | "errorValidityText": {
45 | "type": "string",
46 | "default": "Please fill out this field!"
47 | },
48 | "condition": {
49 | "type": "object",
50 | "default": {
51 | "field": null,
52 | "condition": "===",
53 | "value": ""
54 | }
55 | },
56 | "messages": {
57 | "type": "object",
58 | "default": {
59 | "empty": "Please fill out this field!",
60 | "invalid": "The number {{value}} is not in range!"
61 | }
62 | },
63 | "adminId": {
64 | "type": "object",
65 | "default": {
66 | "default": "",
67 | "value": ""
68 | }
69 | },
70 | "showHint" :{
71 | "type": "boolean",
72 | "default": false
73 | },
74 | "hint": {
75 | "type": "string",
76 | "default": ""
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/blocks/number/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import numberEdit from "./edit.js";
5 | import numberSave from "./save.js";
6 | import { getFieldTransform } from "../../block/functions";
7 | import { fieldParents, myAttrs } from "../../constants";
8 | import Icon from "../../block/Icon.js";
9 |
10 | import blockData from "./block.json";
11 | const { title, attributes } = blockData;
12 |
13 | registerBlockType("cwp/number", {
14 | title: __(title),
15 | icon: __( ),
16 | category: "gutenberg-forms",
17 | keywords: [__("gutenberg-forms"), __("forms"), __("number")],
18 | edit: numberEdit,
19 | save: numberSave,
20 | attributes,
21 | transforms: {
22 | from: [
23 | {
24 | type: "block",
25 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
26 | transform: (a) => getFieldTransform(a, "number"),
27 | },
28 | ],
29 | },
30 | parent: fieldParents,
31 | });
32 |
--------------------------------------------------------------------------------
/src/blocks/number/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { strip_tags } from "../../block/misc/helper";
4 | import { stringifyCondition } from "../../block/functions";
5 |
6 | function save(props) {
7 | const {
8 | number,
9 | isRequired,
10 | label,
11 | id,
12 | rangeMax,
13 | rangeMin,
14 | requiredLabel,
15 | messages: { invalid, empty },
16 | condition,
17 | steps,
18 | hint,
19 | showHint
20 | } = props.attributes;
21 |
22 | const getLabel = () => {
23 | const { label, isRequired } = props.attributes;
24 |
25 | let required = !isEmpty(requiredLabel)
26 | ? `${requiredLabel} `
27 | : "";
28 | let required_label = label + " " + required;
29 |
30 | if (isRequired) return required_label;
31 |
32 | return label;
33 | };
34 |
35 | let errors = JSON.stringify({
36 | mismatch: invalid,
37 | empty
38 | });
39 |
40 | const getCondition = () => {
41 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
42 | //verifying the condition
43 | return {
44 | "data-condition": stringifyCondition(condition),
45 | };
46 | }
47 |
48 | return {};
49 | };
50 |
51 | return (
52 |
53 |
54 | {!isEmpty(label) && (
55 |
59 | )}
60 |
75 |
76 | {showHint && (
77 |
{hint}
78 | )}
79 |
80 | );
81 | }
82 |
83 | export default save;
84 |
--------------------------------------------------------------------------------
/src/blocks/phone/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Phone",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "phone": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Phone:"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "requiredLabel": {
29 | "type": "string",
30 | "default": "*"
31 | },
32 | "messages": {
33 | "type": "object",
34 | "default": {
35 | "empty": "Please fill out this field!",
36 | "invalid": "The phone {{value}} is not valid!"
37 | }
38 | },
39 | "pattern": {
40 | "type": "string",
41 | "default": "[\\+\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d]+"
42 | },
43 | "condition": {
44 | "type": "object",
45 | "default": {
46 | "field": null,
47 | "condition": "===",
48 | "value": ""
49 | }
50 | },
51 | "requiredLabel": {
52 | "type": "string",
53 | "default": "*"
54 | },
55 | "adminId": {
56 | "type": "object",
57 | "default": {
58 | "default": "",
59 | "value": ""
60 | }
61 | },
62 | "prefix": {
63 | "type": "object",
64 | "default": {
65 | "enable": false,
66 | "content": "",
67 | "position": "outside"
68 | }
69 | },
70 | "suffix": {
71 | "type": "object",
72 | "default": {
73 | "enable": false,
74 | "content": "",
75 | "position": "outside"
76 | }
77 | },
78 | "showHint" :{
79 | "type": "boolean",
80 | "default": false
81 | },
82 | "hint": {
83 | "type": "string",
84 | "default": ""
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/blocks/phone/deprecated/deprected.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ! Handling the deprecation
3 | */
4 |
5 | import { attributes } from "../block.json";
6 | import edit from "./edit";
7 | import save from "./save";
8 |
9 | export const deprecated = [
10 | {
11 | attributes,
12 | edit,
13 | save,
14 | },
15 | ];
16 |
--------------------------------------------------------------------------------
/src/blocks/phone/deprecated/save.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * ! DEPRECATED SAVE VERSION
4 | *
5 | */
6 |
7 | import React from "react";
8 | import { isEmpty } from "lodash";
9 | import { strip_tags } from "../../../block/misc/helper";
10 | import { stringifyCondition } from "../../../block/functions";
11 |
12 | function save(props) {
13 | const {
14 | phone,
15 | isRequired,
16 | label,
17 | id,
18 | requiredLabel,
19 | messages: { empty, invalid },
20 | pattern,
21 | condition,
22 | } = props.attributes;
23 |
24 | const getLabel = () => {
25 | const { label, isRequired } = props.attributes;
26 | let required = !isEmpty(requiredLabel)
27 | ? `${requiredLabel} `
28 | : "";
29 | let required_label = label + " " + required;
30 |
31 | if (isRequired) return required_label;
32 |
33 | return label;
34 | };
35 |
36 | let errors = JSON.stringify({
37 | mismatch: invalid,
38 | empty,
39 | });
40 |
41 | let getPattern = () => {
42 | return isEmpty(pattern) ? {} : { pattern };
43 | };
44 |
45 | const getCondition = () => {
46 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
47 | // verifying the condition
48 | return {
49 | "data-condition": stringifyCondition(condition),
50 | };
51 | }
52 |
53 | return {};
54 | };
55 |
56 | return (
57 |
58 |
59 | {!isEmpty(label) && (
60 |
64 | )}
65 |
78 |
79 |
80 | );
81 | }
82 |
83 | export default save;
84 |
--------------------------------------------------------------------------------
/src/blocks/phone/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import phoneEdit from "./edit.js";
5 | import phoneSave from "./save.js";
6 | import { getFieldTransform } from "../../block/functions";
7 | import { fieldParents, myAttrs } from "../../constants";
8 |
9 | import blockData from "./block.json";
10 | import { deprecated } from "./deprecated/deprected";
11 |
12 | const { attributes, title } = blockData;
13 |
14 | registerBlockType("cwp/phone", {
15 | title: __(title),
16 | icon: "phone",
17 | category: "gutenberg-forms",
18 | keywords: [__("gutenberg-forms"), __("forms"), __("phone")],
19 | edit: phoneEdit,
20 | save: phoneSave,
21 | attributes,
22 | deprecated,
23 | transforms: {
24 | from: [
25 | {
26 | type: "block",
27 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
28 | transform: (a) => getFieldTransform(a, "phone"),
29 | },
30 | ],
31 | },
32 | parent: fieldParents,
33 | });
34 |
--------------------------------------------------------------------------------
/src/blocks/phone/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { strip_tags } from "../../block/misc/helper";
4 | import { stringifyCondition } from "../../block/functions";
5 | import Prefix from "../components/prefix";
6 | import Suffix from "../components/suffix";
7 |
8 | function save(props) {
9 | const {
10 | phone,
11 | isRequired,
12 | label,
13 | id,
14 | requiredLabel,
15 | messages: { empty, invalid },
16 | pattern,
17 | condition,
18 | prefix,
19 | suffix,
20 | hint,
21 | showHint
22 | } = props.attributes;
23 |
24 | const getLabel = () => {
25 | const { label, isRequired } = props.attributes;
26 | let required = !isEmpty(requiredLabel)
27 | ? `${requiredLabel} `
28 | : "";
29 | let required_label = label + " " + required;
30 |
31 | if (isRequired) return required_label;
32 |
33 | return label;
34 | };
35 |
36 | let errors = JSON.stringify({
37 | mismatch: invalid,
38 | empty,
39 | });
40 |
41 | let getPattern = () => {
42 | return isEmpty(pattern) ? {} : { pattern };
43 | };
44 |
45 | const getCondition = () => {
46 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
47 | //verifying the condition
48 | return {
49 | "data-condition": stringifyCondition(condition),
50 | };
51 | }
52 |
53 | return {};
54 | };
55 |
56 | return (
57 |
58 |
59 | {!isEmpty(label) && (
60 |
64 | )}
65 |
66 |
67 | {prefix.enable && (
68 |
69 |
70 |
71 | )}
72 |
85 | {suffix.enable && (
86 |
87 |
88 |
89 | )}
90 |
91 |
92 | {showHint && (
93 |
{hint}
94 | )}
95 |
96 | );
97 | }
98 |
99 | export default save;
100 |
--------------------------------------------------------------------------------
/src/blocks/progress/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Progress Bar",
3 | "attributes": {
4 | "progressColor": {
5 | "type": "string",
6 | "default": "rgb(238, 238, 238)"
7 | },
8 | "progressFillColor": {
9 | "type": "string",
10 | "default": "rgb(6, 147, 227)"
11 | },
12 | "thickness": {
13 | "type": "number",
14 | "default": 20
15 | },
16 | "cornerRadius": {
17 | "type": "number",
18 | "default": 0
19 | },
20 | "showPercentage": {
21 | "type": "boolean",
22 | "default": true
23 | },
24 | "textColor": {
25 | "type": "string",
26 | "default": "rgb(238, 238, 238)"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/blocks/progress/components/progressBar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | function ProgressBar(props) {
4 | const { attributes } = props;
5 | const {
6 | progressColor,
7 | progressFillColor,
8 | thickness,
9 | cornerRadius,
10 | showPercentage,
11 | textColor,
12 | } = attributes;
13 |
14 | const progressStyling = {
15 | backgroundColor: progressColor,
16 | height: thickness,
17 | borderRadius: cornerRadius,
18 | };
19 |
20 | const progressFillStyling = {
21 | backgroundColor: progressFillColor,
22 | borderRadius: cornerRadius,
23 | };
24 |
25 | const textStyling = {
26 | color: textColor,
27 | };
28 |
29 | return (
30 |
34 |
35 | {showPercentage && thickness > 10 && (
36 |
37 | 50%
38 |
39 | )}
40 |
41 |
42 | );
43 | }
44 |
45 | export default ProgressBar;
46 |
--------------------------------------------------------------------------------
/src/blocks/progress/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import progressEdit from "./edit.js";
5 | import progressSave from "./save.js";
6 | import { fieldParents } from "../../constants";
7 | import Icon from "../../block/Icon";
8 |
9 | import blockData from "./block.json";
10 | const { attributes, title } = blockData;
11 |
12 | registerBlockType("cwp/progress", {
13 | title: __(title),
14 | icon: __( ),
15 | category: "gutenberg-forms",
16 | keywords: [__("gutenberg-forms"), __("progress"), __("bar")],
17 | edit: progressEdit,
18 | save: progressSave,
19 | attributes,
20 | parent: fieldParents,
21 | });
22 |
--------------------------------------------------------------------------------
/src/blocks/progress/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ProgressBar from "./components/progressBar";
3 |
4 | function save(props) {
5 | return ;
6 | }
7 |
8 | export default save;
9 |
--------------------------------------------------------------------------------
/src/blocks/radio/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Radio",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "isRequired": {
9 | "type": "boolean",
10 | "default": false
11 | },
12 | "options": {
13 | "type": "array",
14 | "default": [
15 | {
16 | "label": "Option 1"
17 | },
18 | {
19 | "label": "Option 2"
20 | }
21 | ]
22 | },
23 | "label": {
24 | "type": "string",
25 | "default": "Choose One"
26 | },
27 | "id": {
28 | "type": "string",
29 | "default": ""
30 | },
31 | "field_name": {
32 | "type": "string",
33 | "default": ""
34 | },
35 | "requiredLabel": {
36 | "type": "string",
37 | "default": "*"
38 | },
39 | "messages": {
40 | "type": "object",
41 | "default": {
42 | "empty": "Please select radio!"
43 | }
44 | },
45 | "condition": {
46 | "type": "object",
47 | "default": {
48 | "field": null,
49 | "condition": "===",
50 | "value": ""
51 | }
52 | },
53 | "requiredLabel": {
54 | "type": "string",
55 | "default": "*"
56 | },
57 | "fieldStyle": {
58 | "type": "string",
59 | "default": "block"
60 | },
61 | "bulkAdd": {
62 | "type": "boolean",
63 | "default": false
64 | },
65 | "adminId": {
66 | "type": "object",
67 | "default": {
68 | "default": "",
69 | "value": ""
70 | }
71 | },
72 | "showHint" :{
73 | "type": "boolean",
74 | "default": false
75 | },
76 | "hint": {
77 | "type": "string",
78 | "default": ""
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/blocks/radio/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import radioEdit from "./edit.js";
5 | import radioSave from "./save.js";
6 | import { getFieldTransform } from "../../block/functions";
7 | import { fieldParents, myAttrs } from "../../constants";
8 |
9 | import blockData from "./block.json";
10 | const { attributes, title } = blockData;
11 |
12 | registerBlockType("cwp/radio", {
13 | title: __(title),
14 | icon: "marker",
15 | category: "gutenberg-forms",
16 | keywords: [__("gutenberg-forms"), __("forms"), __("radio")],
17 | edit: radioEdit,
18 | save: radioSave,
19 | attributes,
20 | transforms: {
21 | from: [
22 | {
23 | type: "block",
24 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
25 | transform: (a) => getFieldTransform(a, "radio"),
26 | },
27 | ],
28 | },
29 | parent: fieldParents,
30 | });
31 |
--------------------------------------------------------------------------------
/src/blocks/radio/save.js:
--------------------------------------------------------------------------------
1 | import { isEmpty, has } from "lodash";
2 | import { strip_tags } from "../../block/misc/helper";
3 | import { stringifyCondition } from "../../block/functions";
4 |
5 | function save(props) {
6 | const {
7 | isRequired,
8 | options,
9 | label,
10 | id,
11 | requiredLabel,
12 | messages,
13 | messages: { empty },
14 | condition,
15 | fieldStyle,
16 | hint,
17 | showHint
18 | } = props.attributes;
19 |
20 | const getLabel = () => {
21 | const { label, isRequired } = props.attributes;
22 |
23 | let required = !isEmpty(requiredLabel)
24 | ? `${requiredLabel} `
25 | : "";
26 | const required_label = label + " " + required;
27 |
28 | if (isRequired) {
29 | return required_label;
30 | }
31 |
32 | return label;
33 | };
34 |
35 | let errors = JSON.stringify({
36 | empty
37 | });
38 |
39 | const getCondition = () => {
40 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
41 | //verifying the condition
42 | return {
43 | "data-condition": stringifyCondition(condition)
44 | };
45 | }
46 |
47 | return {};
48 | };
49 |
50 | return (
51 |
55 |
59 | {!isEmpty(label) && (
60 |
61 | )}
62 | {options.map((radio, index) => {
63 | return (
64 |
65 |
76 |
77 | {radio.label}
78 | {has(radio, "image") && (
79 |
80 |
87 |
88 | )}
89 |
90 |
91 | );
92 | })}
93 |
94 | {showHint && (
95 |
{hint}
96 | )}
97 |
98 | );
99 | }
100 |
101 | export default save;
102 |
--------------------------------------------------------------------------------
/src/blocks/reusable_forms/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "attributes": {
3 | "formId": {
4 | "type": "string",
5 | "default": ""
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/blocks/reusable_forms/components/introduction.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | import React, { useState } from "react";
3 | import { Placeholder, Button, SelectControl } from "@wordpress/components";
4 | import { get } from "lodash";
5 |
6 | function Introduction(props) {
7 | const savedForms = get(window, "cwpGlobal.cwp-cpt-forms");
8 | const [form, setForm] = useState("");
9 |
10 | const formOptions = [...savedForms].map((form) => {
11 | const form_id = get(form, "ID");
12 | const title = get(form, "post_title");
13 |
14 | return {
15 | label: title,
16 | value: form_id,
17 | };
18 | });
19 |
20 | return (
21 |
26 |
27 |
39 | props.onSelect(form)}>
40 | {__("Choose", "forms-gutenberg")}
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | export default Introduction;
48 |
--------------------------------------------------------------------------------
/src/blocks/reusable_forms/edit.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Introduction from "./components/introduction";
3 | import { isEmpty, get } from "lodash";
4 | import { SelectControl, Button, PanelBody } from "@wordpress/components";
5 | import { getPostUrl } from "../../block/functions";
6 | const { InspectorControls } = wp.blockEditor;
7 | const { __ } = wp.i18n;
8 |
9 | function edit(props) {
10 | const savedForms = get(window, "cwpGlobal.cwp-cpt-forms");
11 |
12 | let formIdTitleHash = [];
13 | [...savedForms].map((form) => {
14 | formIdTitleHash[ get(form, "ID") ] = get(form, "post_title");
15 | });
16 |
17 | const {
18 | setAttributes,
19 | attributes: { formId },
20 | isSelected,
21 | } = props;
22 |
23 | const shouldIntroduce = isEmpty(formId); // render the introduction if there is no form attributes to preview useEffect( () => {
24 | const formTitle = get(formIdTitleHash, formId, '');
25 |
26 | return [
27 | !!isSelected && !shouldIntroduce && (
28 |
29 |
30 | setAttributes({ formId: form })}
35 | />
36 |
37 |
38 | {__("Edit Form", "forms-gutenberg")}
39 |
40 |
41 |
42 |
43 | ),
44 | null,
45 |
46 | {shouldIntroduce
47 | ?
setAttributes({ formId })} />
48 | : (
49 |
50 |
51 | {formTitle.length > 0
52 | ?
{__('Here will be shown a Gutenberg Form called', 'forms-gutenberg')} "{formTitle}".
53 | :
{__('This Gutenberg Form was deleted and will not be shown. You may want to delete this block or restore a form if possible.', 'forms-gutenberg')} (ID {formId})
54 | }
55 |
56 |
57 | )
58 | }
59 | ,
60 | ];
61 | }
62 |
63 | export default edit;
64 |
--------------------------------------------------------------------------------
/src/blocks/reusable_forms/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import reusableEdit from "./edit";
5 | import reusableSave from "./save";
6 | import blockData from "./block.json";
7 |
8 | const { attributes } = blockData;
9 |
10 | /**
11 | *
12 | * This block is used to display the server side render for the saved forms
13 | * instead of replacing the saved form with short code block
14 | * this block will be used
15 | *
16 | */
17 |
18 | registerBlockType("cwp/reusable-form", {
19 | title: __("Existing Form", "forms-gutenberg"),
20 | icon: "index-card",
21 | category: "gutenberg-forms",
22 | keywords: [
23 | __("gutenberg-forms"),
24 | __("forms"),
25 | __("reusable"),
26 | __("reusable forms"),
27 | __("existing form"),
28 | ],
29 | attributes,
30 | edit: reusableEdit,
31 | save: reusableSave,
32 | });
33 |
--------------------------------------------------------------------------------
/src/blocks/reusable_forms/save.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { RawHTML } from '@wordpress/element'
3 | import { isEmpty } from 'lodash'
4 |
5 | function save(props) {
6 | const formId = props.attributes.formId;
7 | const shortCode = `[gutenberg_form id=${formId}]`
8 | const shouldRender = !isEmpty(formId);
9 |
10 | return shouldRender ? {shortCode} : null;
11 | }
12 |
13 | export default save;
14 |
--------------------------------------------------------------------------------
/src/blocks/select/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Select",
3 | "attributes": {
4 | "isRequired": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "options": {
9 | "type": "array",
10 | "default": [
11 | {
12 | "label": "Option 1"
13 | },
14 | {
15 | "label": "Option 2"
16 | }
17 | ]
18 | },
19 | "label": {
20 | "type": "string",
21 | "default": "Choose One"
22 | },
23 | "id": {
24 | "type": "string",
25 | "default": ""
26 | },
27 | "enableCondition": {
28 | "type": "boolean",
29 | "default": false
30 | },
31 | "field_name": {
32 | "type": "string",
33 | "default": ""
34 | },
35 | "requiredLabel": {
36 | "type": "string",
37 | "default": "*"
38 | },
39 | "messages": {
40 | "type": "object",
41 | "default": {
42 | "empty": "Please select option!"
43 | }
44 | },
45 | "condition": {
46 | "type": "object",
47 | "default": {
48 | "field": null,
49 | "condition": "===",
50 | "value": ""
51 | }
52 | },
53 | "bulkAdd": {
54 | "type": "boolean",
55 | "default": false
56 | },
57 | "adminId": {
58 | "type": "object",
59 | "default": {
60 | "default": "",
61 | "value": ""
62 | }
63 | },
64 | "showHint" :{
65 | "type": "boolean",
66 | "default": false
67 | },
68 | "hint": {
69 | "type": "string",
70 | "default": ""
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/blocks/select/deprecated/deprecated.js:
--------------------------------------------------------------------------------
1 | import { attributes } from "../block.json";
2 |
3 | // deprecated version
4 | import edit from "./edit";
5 | import save from "./save";
6 |
7 | export const deprecated = [
8 | {
9 | attributes,
10 | edit,
11 | save,
12 | },
13 | ];
14 |
--------------------------------------------------------------------------------
/src/blocks/select/deprecated/save.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * ! DEPRECATED SAVE VERSION
4 | *
5 | */
6 |
7 | import React from "react";
8 | import { isEmpty } from "lodash";
9 | import { stringifyCondition } from "../../../block/functions";
10 |
11 | function save(props) {
12 | const {
13 | isRequired,
14 | options,
15 | label,
16 | id,
17 | requiredLabel,
18 | messages,
19 | messages: { empty },
20 | condition,
21 | } = props.attributes;
22 |
23 | const getLabel = () => {
24 | const { label, isRequired } = props.attributes;
25 |
26 | let required = !isEmpty(requiredLabel)
27 | ? `${requiredLabel} `
28 | : "";
29 | let required_label = label + " " + required;
30 |
31 | if (isRequired) return required_label;
32 |
33 | return label;
34 | };
35 | const getCondition = () => {
36 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
37 | //verifying the condition
38 | return {
39 | "data-condition": stringifyCondition(condition),
40 | };
41 | }
42 |
43 | return {};
44 | };
45 | const errors = JSON.stringify({
46 | empty,
47 | });
48 |
49 | return (
50 |
51 |
52 | {!isEmpty(label) && (
53 |
54 | )}
55 |
64 |
65 | Select your option
66 |
67 | {options.map((s, index) => {
68 | return {s.label} ;
69 | })}
70 |
71 |
72 |
73 | );
74 | }
75 |
76 | export default save;
77 |
--------------------------------------------------------------------------------
/src/blocks/select/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import { fieldParents, myAttrs } from "../../constants";
5 | import { getFieldTransform } from "../../block/functions";
6 | import selectEdit from "./edit.js";
7 | import selectSave from "./save.js";
8 |
9 | import blockData from "./block.json";
10 | import { deprecated } from "./deprecated/deprecated";
11 | const { attributes, title } = blockData;
12 |
13 | registerBlockType("cwp/select", {
14 | title: __(title),
15 | icon: "menu-alt",
16 | category: "gutenberg-forms",
17 | keywords: [__("gutenberg-forms"), __("forms"), __("select")],
18 | edit: selectEdit,
19 | save: selectSave,
20 | deprecated,
21 | attributes,
22 | transforms: {
23 | from: [
24 | {
25 | type: "block",
26 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
27 | transform: (a) => getFieldTransform(a, "select"),
28 | },
29 | ],
30 | },
31 | parent: fieldParents,
32 | });
33 |
--------------------------------------------------------------------------------
/src/blocks/select/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { stringifyCondition } from "../../block/functions";
4 |
5 | function save(props) {
6 | const {
7 | isRequired,
8 | options,
9 | label,
10 | id,
11 | requiredLabel,
12 | messages,
13 | messages: { empty },
14 | condition,
15 | hint,
16 | showHint
17 | } = props.attributes;
18 |
19 | const getLabel = () => {
20 | const { label, isRequired } = props.attributes;
21 |
22 | let required = !isEmpty(requiredLabel)
23 | ? `${requiredLabel} `
24 | : "";
25 | let required_label = label + " " + required;
26 |
27 | if (isRequired) return required_label;
28 |
29 | return label;
30 | };
31 | const getCondition = () => {
32 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
33 | //verifying the condition
34 | return {
35 | "data-condition": stringifyCondition(condition),
36 | };
37 | }
38 |
39 | return {};
40 | };
41 | const errors = JSON.stringify({
42 | empty,
43 | });
44 |
45 | return (
46 |
47 |
48 | {!isEmpty(label) && (
49 |
50 | )}
51 |
60 | {options.map((s, index) => {
61 | return (
62 |
63 | {s.label}
64 |
65 | );
66 | })}
67 |
68 |
69 | {showHint && (
70 |
{hint}
71 | )}
72 |
73 | );
74 | }
75 |
76 | export default save;
77 |
--------------------------------------------------------------------------------
/src/blocks/text/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Text",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "text": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Text"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "messages": {
29 | "type": "object",
30 | "default": {
31 | "empty": "Please fill out this field!",
32 | "invalid": "The text {{value}} is not valid!"
33 | }
34 | },
35 | "pattern": {
36 | "type": "string",
37 | "default": ""
38 | },
39 | "minimumLength": {
40 | "type": "number",
41 | "default": 0
42 | },
43 | "maximumLength": {
44 | "type": "number",
45 | "default": 100
46 | },
47 | "condition": {
48 | "type": "object",
49 | "default": {
50 | "field": null,
51 | "condition": "===",
52 | "value": ""
53 | }
54 | },
55 | "requiredLabel": {
56 | "type": "string",
57 | "default": "*"
58 | },
59 |
60 | "adminId": {
61 | "type": "object",
62 | "default": {
63 | "default": "",
64 | "value": ""
65 | }
66 | },
67 | "prefix": {
68 | "type": "object",
69 | "default": {
70 | "enable": false,
71 | "content": "",
72 | "position": "outside"
73 | }
74 | },
75 | "suffix": {
76 | "type": "object",
77 | "default": {
78 | "enable": false,
79 | "content": "",
80 | "position": "outside"
81 | }
82 | },
83 | "showHint" :{
84 | "type": "boolean",
85 | "default": false
86 | },
87 | "hint": {
88 | "type": "string",
89 | "default": ""
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/blocks/text/deprecated/deprecated.js:
--------------------------------------------------------------------------------
1 | import { attributes } from "../block.json";
2 |
3 | // deprecated versions
4 |
5 | import edit from "./edit";
6 | import save from "./save";
7 |
8 | export const deprecated = [
9 | {
10 | attributes,
11 | edit,
12 | save,
13 | },
14 | ];
15 |
--------------------------------------------------------------------------------
/src/blocks/text/deprecated/save.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * ! DEPRECATED SAVE VERSION
4 | *
5 | */
6 |
7 | import React from "react";
8 | import { isEmpty } from "lodash";
9 | import { strip_tags } from "../../../block/misc/helper";
10 | import { stringifyCondition } from "../../../block/functions";
11 |
12 | function save(props) {
13 | const {
14 | text,
15 | isRequired,
16 | label,
17 | id,
18 | requiredLabel,
19 | messages: { invalid, empty },
20 | messages,
21 | pattern,
22 | minimumLength,
23 | maximumLength,
24 | } = props.attributes;
25 |
26 | const getLabel = () => {
27 | const { label, isRequired } = props.attributes;
28 |
29 | let required = !isEmpty(requiredLabel)
30 | ? `${requiredLabel} `
31 | : "";
32 | let required_label = label + " " + required;
33 |
34 | if (isRequired) return required_label;
35 |
36 | return label;
37 | };
38 |
39 | let errors = JSON.stringify({
40 | mismatch: invalid,
41 | empty,
42 | });
43 |
44 | let getPattern = () => {
45 | return isEmpty(pattern) ? {} : { pattern };
46 | };
47 |
48 | const getCondition = () => {
49 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
50 | //verifying the condition
51 | return {
52 | "data-condition": stringifyCondition(condition),
53 | };
54 | }
55 |
56 | return {};
57 | };
58 |
59 | return (
60 |
61 |
62 | {!isEmpty(label) && (
63 |
67 | )}
68 |
82 |
83 |
84 | );
85 | }
86 |
87 | export default save;
88 |
--------------------------------------------------------------------------------
/src/blocks/text/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import textEdit from "./edit";
5 | import textSave from "./save";
6 | import { getFieldTransform } from "../../block/functions";
7 | import { fieldParents, myAttrs } from "../../constants";
8 | import blockData from "./block.json";
9 | import { deprecated } from "./deprecated/deprecated";
10 |
11 | const { attributes, title } = blockData;
12 |
13 | registerBlockType("cwp/text", {
14 | title: __(title),
15 | icon: "text",
16 | category: "gutenberg-forms",
17 | keywords: [__("gutenberg-forms"), __("forms"), __("text")],
18 | edit: textEdit,
19 | save: textSave,
20 | attributes,
21 | deprecated,
22 | transforms: {
23 | from: [
24 | {
25 | type: "block",
26 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
27 | transform: (a) => getFieldTransform(a, "text"),
28 | },
29 | ],
30 | },
31 | parent: fieldParents,
32 | });
33 |
--------------------------------------------------------------------------------
/src/blocks/website/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Website",
3 | "attributes": {
4 | "enableCondition": {
5 | "type": "boolean",
6 | "default": false
7 | },
8 | "website": {
9 | "type": "string",
10 | "default": ""
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Website"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "requiredLabel": {
29 | "type": "string",
30 | "default": "*"
31 | },
32 | "messages": {
33 | "type": "object",
34 | "default": {
35 | "empty": "Please fill out this field!",
36 | "invalid": "The website {{value}} is not valid!"
37 | }
38 | },
39 | "condition": {
40 | "type": "object",
41 | "default": {
42 | "field": null,
43 | "condition": "===",
44 | "value": ""
45 | }
46 | },
47 | "requiredLabel": {
48 | "type": "string",
49 | "default": "*"
50 | },
51 | "adminId": {
52 | "type": "object",
53 | "default": {
54 | "default": "",
55 | "value": ""
56 | }
57 | },
58 | "prefix": {
59 | "type": "object",
60 | "default": {
61 | "enable": false,
62 | "content": "",
63 | "position": "outside"
64 | }
65 | },
66 | "suffix": {
67 | "type": "object",
68 | "default": {
69 | "enable": false,
70 | "content": "",
71 | "position": "outside"
72 | }
73 | },
74 | "showHint" :{
75 | "type": "boolean",
76 | "default": false
77 | },
78 | "hint": {
79 | "type": "string",
80 | "default": ""
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/blocks/website/deprecated/deprecated.js:
--------------------------------------------------------------------------------
1 | import { attributes } from "../block.json";
2 |
3 | // deprecated versions
4 |
5 | import edit from "./edit";
6 | import save from "./save";
7 |
8 | export const deprecated = [
9 | {
10 | attributes,
11 | edit,
12 | save,
13 | },
14 | ];
15 |
--------------------------------------------------------------------------------
/src/blocks/website/deprecated/save.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * ! DEPRECTED SAVE VERSION
4 | *
5 | */
6 |
7 | import React from "react";
8 | import { isEmpty } from "lodash";
9 | import { strip_tags } from "../../../block/misc/helper";
10 | import { stringifyCondition } from "../../../block/functions";
11 |
12 | function save(props) {
13 | const {
14 | website,
15 | isRequired,
16 | label,
17 | id,
18 | requiredLabel,
19 | messages: { invalid, empty },
20 | messages,
21 | condition,
22 | } = props.attributes;
23 |
24 | const getLabel = () => {
25 | const { label, isRequired } = props.attributes;
26 | let required = !isEmpty(requiredLabel)
27 | ? `${requiredLabel} `
28 | : "";
29 | let required_label = label + " " + required;
30 |
31 | if (isRequired) return required_label;
32 |
33 | return label;
34 | };
35 | let errors = JSON.stringify({
36 | mismatch: invalid,
37 | empty,
38 | });
39 | const getCondition = () => {
40 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
41 | //verifying the condition
42 | return {
43 | "data-condition": stringifyCondition(condition),
44 | };
45 | }
46 |
47 | return {};
48 | };
49 | return (
50 |
51 |
52 | {!isEmpty(label) && (
53 |
57 | )}
58 |
69 |
70 |
71 | );
72 | }
73 |
74 | export default save;
75 |
--------------------------------------------------------------------------------
/src/blocks/website/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import websiteEdit from "./edit.js";
5 | import websiteSave from "./save.js";
6 | import { getFieldTransform } from "../../block/functions";
7 | import { fieldParents, myAttrs } from "../../constants";
8 |
9 | import blockData from "./block.json";
10 | import { deprecated } from "./deprecated/deprecated";
11 | const { attributes, title } = blockData;
12 |
13 | registerBlockType("cwp/website", {
14 | title: __(title),
15 | icon: "laptop",
16 | category: "gutenberg-forms",
17 | keywords: [__("gutenberg-forms"), __("forms"), __("website")],
18 | edit: websiteEdit,
19 | save: websiteSave,
20 | deprecated,
21 | attributes,
22 | transforms: {
23 | from: [
24 | {
25 | type: "block",
26 | blocks: myAttrs.map((block) => "cwp/".concat(block)),
27 | transform: (a) => getFieldTransform(a, "website"),
28 | },
29 | ],
30 | },
31 | parent: fieldParents,
32 | });
33 |
--------------------------------------------------------------------------------
/src/blocks/website/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { strip_tags } from "../../block/misc/helper";
4 | import { stringifyCondition } from "../../block/functions";
5 | import Prefix from "../components/prefix";
6 | import Suffix from "../components/suffix";
7 |
8 | function save(props) {
9 | const {
10 | website,
11 | isRequired,
12 | label,
13 | id,
14 | requiredLabel,
15 | messages: { invalid, empty },
16 | messages,
17 | condition,
18 | prefix,
19 | suffix,
20 | hint,
21 | showHint
22 | } = props.attributes;
23 |
24 | const getLabel = () => {
25 | const { label, isRequired } = props.attributes;
26 | let required = !isEmpty(requiredLabel)
27 | ? `${requiredLabel} `
28 | : "";
29 | let required_label = label + " " + required;
30 |
31 | if (isRequired) return required_label;
32 |
33 | return label;
34 | };
35 | let errors = JSON.stringify({
36 | mismatch: invalid,
37 | empty,
38 | });
39 | const getCondition = () => {
40 | if (props.attributes.enableCondition && !isEmpty(condition.field)) {
41 | //verifying the condition
42 | return {
43 | "data-condition": stringifyCondition(condition),
44 | };
45 | }
46 |
47 | return {};
48 | };
49 | return (
50 |
51 |
52 | {!isEmpty(label) && (
53 |
57 | )}
58 |
59 | {prefix.enable && (
60 |
61 |
62 |
63 | )}
64 |
65 |
76 |
77 | {suffix.enable && (
78 |
79 |
80 |
81 | )}
82 |
83 |
84 | {showHint && (
85 |
{hint}
86 | )}
87 |
88 | );
89 | }
90 |
91 | export default save;
92 |
--------------------------------------------------------------------------------
/src/blocks/yes-no/block.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Yes / No",
3 | "attributes": {
4 | "yes_no": {
5 | "type": "boolean",
6 | "defaut": false
7 | },
8 | "requiredLabel": {
9 | "type": "string",
10 | "default": "*"
11 | },
12 | "isRequired": {
13 | "type": "boolean",
14 | "default": false
15 | },
16 | "label": {
17 | "type": "string",
18 | "default": "Yes Or No?"
19 | },
20 | "id": {
21 | "type": "string",
22 | "default": ""
23 | },
24 | "field_name": {
25 | "type": "string",
26 | "default": ""
27 | },
28 | "errorValidityText": {
29 | "type": "string",
30 | "default": "Please fill out this field!"
31 | },
32 | "enableCondition": {
33 | "type": "boolean",
34 | "default": false
35 | },
36 | "condition": {
37 | "type": "object",
38 | "default": {
39 | "field": null,
40 | "condition": "===",
41 | "value": ""
42 | }
43 | },
44 | "adminId": {
45 | "type": "object",
46 | "default": {
47 | "default": "",
48 | "value": ""
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/blocks/yes-no/index.js:
--------------------------------------------------------------------------------
1 | const { __ } = wp.i18n;
2 | const { registerBlockType } = wp.blocks;
3 |
4 | import { fieldParents } from "../../constants";
5 | import yesNoEdit from "./edit.js";
6 | import yesNoSave from "./save.js";
7 | import Icon from "../../block/Icon";
8 |
9 | import blockData from "./block.json";
10 | const { attributes, title } = blockData;
11 |
12 | registerBlockType("cwp/yes-no", {
13 | title: __(title),
14 | icon: __( , "forms-gutenberg"),
15 | category: "gutenberg-forms",
16 | keywords: [__("gutenberg-forms"), __("forms"), __("yes-no")],
17 | edit: yesNoEdit,
18 | save: yesNoSave,
19 | attributes,
20 | parent: fieldParents,
21 | });
22 |
--------------------------------------------------------------------------------
/src/blocks/yes-no/save.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { isEmpty } from "lodash";
3 | import { strip_tags } from "../../block/misc/helper";
4 |
5 | function save(props) {
6 | const { yes_no, isRequired, label, id, requiredLabel } = props.attributes;
7 |
8 | const getLabel = () => {
9 | const { label, isRequired } = props.attributes;
10 | let required = !isEmpty(requiredLabel)
11 | ? `${requiredLabel} `
12 | : "";
13 | let required_label = label + " " + required;
14 |
15 | if (isRequired) return required_label;
16 |
17 | return label;
18 | };
19 |
20 | return (
21 |
22 |
23 | {!isEmpty(label) && (
24 |
28 | )}
29 |
30 |
37 |
45 |
46 |
47 |
48 |
49 |
50 | );
51 | }
52 |
53 | export default save;
54 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | export const fieldParents = [
2 | "cwp/block-gutenberg-forms",
3 | "cwp/column",
4 | "cwp/form-group",
5 | "cwp/form-step",
6 | ];
7 |
8 | export const fieldSupport = {
9 | align: true,
10 | align: ["wide", "full", "center"],
11 | };
12 |
13 | export const myAttrs = [
14 | "email",
15 | "name",
16 | "message",
17 | "checkbox",
18 | "datepicker",
19 | "radio",
20 | "phone",
21 | "website",
22 | "text",
23 | "select",
24 | "number",
25 | "yes-no",
26 | "file-upload",
27 | "hidden",
28 | ];
29 |
--------------------------------------------------------------------------------
/src/extend/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ? All extended features of gutenberg forms exposed to be used in the addons
3 | * ? will be imported here.
4 | */
5 |
6 | import TagSelector from "../block/components/tagSelector";
7 |
8 | function cwp_gf_select(scope) {
9 | switch (scope) {
10 | case "cwp/components":
11 | return {
12 | TagSelector,
13 | };
14 | default:
15 | return null;
16 | }
17 | }
18 |
19 | window.cwp_gf_select = cwp_gf_select;
20 |
--------------------------------------------------------------------------------