diff --git a/.babelrc b/.babelrc deleted file mode 100644 index eb1505278..000000000 --- a/.babelrc +++ /dev/null @@ -1,25 +0,0 @@ -{ - "env": { - "production": { - "plugins": [ - ["react-remove-properties", {"properties": ["data-test"]}] - ] - } - }, - "presets": [ - [ - "@babel/preset-env", - { - "targets": { "esmodules": true }, - "useBuiltIns": "usage", - "corejs": "3.23.4" - } - ], - "@babel/preset-react" - ], - "plugins": [ - "@babel/plugin-syntax-dynamic-import", - "@babel/plugin-proposal-object-rest-spread", - "@babel/plugin-proposal-class-properties" - ] -} diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..0f1616700 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +.cache +build +node_modules +vendor \ No newline at end of file diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 66e2ab81b..000000000 --- a/.eslintrc +++ /dev/null @@ -1,27 +0,0 @@ -{ - "parser": "@babel/eslint-parser", - "extends": ["standard", "standard-jsx", "standard-react"], - "globals": { - "GFPDF": false, - "Backbone": false, - "jQuery": false, - "gfpdf_migration_multisite_ids": false, - "gf_vars": false, - "tinyMCE": false, - "gform": false, - "ConditionalLogic": false, - "GetFirstRuleField": false, - "ToggleConditionalLogic": false, - "GetRuleValuesDropDown": false, - "QTags": false, - "switchEditors": false, - "getUserSetting": false, - "wp": false, - "gfMergeTagsObj": false, - "form": false, - "gform_initialize_tooltips": false, - "_": false, - "ClipboardJS": false - }, - "ignorePatterns": ["versionCompare.js"] -} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..57657cb06 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,35 @@ +module.exports = { + root: true, + extends: [ + 'plugin:@wordpress/eslint-plugin/recommended', + 'plugin:jest/recommended', + ], + globals: { + GFPDF: 'readonly', + Backbone: 'readonly', + jQuery: 'readonly', + gfpdf_migration_multisite_ids: 'readonly', + gf_vars: 'readonly', + tinyMCE: 'readonly', + gform: 'readonly', + ConditionalLogic: 'readonly', + GetFirstRuleField: 'readonly', + ToggleConditionalLogic: 'readonly', + GetRuleValuesDropDown: 'readonly', + QTags: 'readonly', + switchEditors: 'readonly', + getUserSetting: 'readonly', + wp: 'readonly', + gfMergeTagsObj: 'readonly', + form: 'readonly', + gform_initialize_tooltips: 'readonly', + _: 'readonly', + ClipboardJS: 'readonly', + }, + rules: { + 'no-alert': 'off', + 'jest/no-done-callback': 'off', + camelcase: 'off', + 'jsdoc/empty-tags': ['off', { tags: ['package'] }], + }, +}; diff --git a/.github/README.md b/.github/README.md index f483493b3..15058c16b 100644 --- a/.github/README.md +++ b/.github/README.md @@ -25,11 +25,11 @@ The Docker setup will create a fully functional development environment preconfi 1. Clone the repository using `git clone https://github.com/GravityPDF/gravity-pdf/` from the terminal 2. Copy and rename `.env.example` to `.env`, then replace `00000000000000000000000000000000` with a valid Gravity Forms license key -3. Run `yarn && yarn build:production` -4. Start Docker and then run `yarn env:install` to setup the local development environment +3. Run `composer install` +3. Run `yarn && yarn start` 5. Access a local development site at `http://localhost:8888` with the login `admin` and `password`. -If you shut down Docker and want to fire up the environment later, use `yarn wp-env start`. You can reset the database back to its original state with `yarn wp-env clean all`. When all else fails, delete everything and start again with `yarn wp-env destroy`. +You can reset the database back to its original state with `yarn wp-env clean all`. When all else fails, delete everything and start again with `yarn wp-env destroy`. [See the WordPress Developer Handbook for more details about managing the docker environment](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/#wp-env-run-container-command). @@ -39,26 +39,21 @@ X-Debug is enabled by default for step debugging and profiling. If you need to [ ### Switch PHP Versions -The default version that will be configured is PHP8.0. If you want to change this you can adjust the `phpVersion` value in the `.wp-env.json` file and then stop and start the environment with `yarn wp-env start`. - -## Setup without Docker - -If you would rather use your own development environment, you can build Gravity PDF using the following commands. - -1. Clone the repository using `git clone https://github.com/GravityPDF/gravity-pdf/` -1. Run `yarn && yarn build:production` -1. Run `composer install` -1. Run `composer run prefix` +The default version that will be configured is PHP8.3. If you want to change this you can adjust the `phpVersion` value in the `.wp-env.json` file and then stop and start the environment with `yarn wp-env start`. ## Building JavaScript -If you are making changes to any of the JavaScript or CSS, run `yarn build:dev` to ensure the files automatically gets built when you make changes on the file system. +If you are making changes to any of the JavaScript or CSS, run `yarn dev` to ensure the files automatically gets built when you make changes on the file system. ## Linting -To lint your JS code use `yarn lint:js`, and to try automatically fix it use `yarn lint:js:fix`. +To lint your: + +1. JS code using `yarn lint:js` +2. CSS code using `yarn lint:css` +3. PHP code using `composer lint` -To lint your PHP code, use `composer lint`, and to try automatically fix it use `composer lint:fix`. +You can auto-fix many issues with `yarn format` and `composer lint:fix`. ## Automated Tests diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 503cec4d9..bf67e2707 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -47,7 +47,7 @@ jobs: - name: Install Composer dependencies run: | - composer install --prefer-dist --no-suggest --no-progress --no-ansi --no-interaction + composer install --prefer-dist --no-suggest --no-progress --no-ansi --no-interaction --no-scripts echo "${PWD}/vendor/bin" >> $GITHUB_PATH env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/end-to-end-tests.yml index d07660a94..996ffb7a7 100644 --- a/.github/workflows/end-to-end-tests.yml +++ b/.github/workflows/end-to-end-tests.yml @@ -81,16 +81,23 @@ jobs: php -i locale -a - - name: Install Dependencies + - name: Install Composer dependencies + run: | + composer install --prefer-dist --no-suggest --no-progress --no-ansi --no-interaction --no-scripts + echo "${PWD}/vendor/bin" >> $GITHUB_PATH + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install JS Dependencies if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn install - name: Build Gravity PDF - run: yarn build:production + run: yarn build - name: Install / Setup Gravity PDF + WordPress run: | - yarn env:install + yarn wp-env start env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/phpunit.tests.yml b/.github/workflows/phpunit.tests.yml index 6c5afcd54..bc9fdfa24 100644 --- a/.github/workflows/phpunit.tests.yml +++ b/.github/workflows/phpunit.tests.yml @@ -89,7 +89,7 @@ jobs: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | - ${{ runner.os }}-yarn- + ${{ runner.os }}-yarn- - name: Log debug information run: | @@ -103,21 +103,28 @@ jobs: php -i locale -a - - name: Install Dependencies + - name: Install Composer dependencies run: | - yarn + composer install --prefer-dist --no-suggest --no-progress --no-ansi --no-interaction --no-scripts + echo "${PWD}/vendor/bin" >> $GITHUB_PATH + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install JS Dependencies + if: steps.cache-nodemodules.outputs.cache-hit != 'true' + run: yarn install - name: Install / Setup Gravity PDF + WordPress if: ${{ ! matrix.report }} run: | - yarn env:install + yarn wp-env start env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Install / Setup Gravity PDF + WordPress if: ${{ matrix.report }} run: | - PHP_ENABLE_XDEBUG=true yarn env:install + PHP_ENABLE_XDEBUG=true yarn wp-env start env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 204666ed5..0a56b4743 100644 --- a/.gitignore +++ b/.gitignore @@ -29,11 +29,7 @@ node_modules/* *.iws ## Plugin-specific files: -*.min.css -*.min.js -chunk-*.js -*.map -dist/* +build/assets/* # Unit tests /tmp @@ -44,6 +40,7 @@ dist/* # Coverage report /coverage /screenshots +/artifacts # Others _notes diff --git a/.nvmrc b/.nvmrc index 19c7bdba7..2edeafb09 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16 \ No newline at end of file +20 \ No newline at end of file diff --git a/.php-scoper/monolog.php b/.php-scoper/monolog.php index 5d9690909..2e5ced6ce 100644 --- a/.php-scoper/monolog.php +++ b/.php-scoper/monolog.php @@ -5,9 +5,6 @@ use Isolated\Symfony\Component\Finder\Finder; $path = './'; -if ( isset( $_SERVER['argv'][0] ) ) { - $path = dirname( $_SERVER['argv'][0] ) . '/'; -} return [ diff --git a/.php-scoper/mpdf.php b/.php-scoper/mpdf.php index e69781a49..a0e039f94 100644 --- a/.php-scoper/mpdf.php +++ b/.php-scoper/mpdf.php @@ -5,9 +5,6 @@ use Isolated\Symfony\Component\Finder\Finder; $path = './'; -if ( isset( $_SERVER['argv'][0] ) ) { - $path = dirname( $_SERVER['argv'][0] ) . '/'; -} return [ @@ -71,7 +68,7 @@ function( string $filePath, string $prefix, string $content ): string { ]; if ( in_array( basename( $filePath ), $files, true ) ) { - $content = str_replace( "\\$prefix\\Psr\\Log\\LoggerInterface", '', $content ); + $content = str_replace( '(LoggerInterface ', '(', $content ); } /* Global polyfills */ diff --git a/.php-scoper/querypath.php b/.php-scoper/querypath.php index 675887f92..20d957f5d 100644 --- a/.php-scoper/querypath.php +++ b/.php-scoper/querypath.php @@ -5,9 +5,6 @@ use Isolated\Symfony\Component\Finder\Finder; $path = './'; -if ( isset( $_SERVER['argv'][0] ) ) { - $path = dirname( $_SERVER['argv'][0] ) . '/'; -} return [ @@ -37,11 +34,6 @@ */ 'patchers' => [ function( string $filePath, string $prefix, string $content ): string { - - if ( basename( $filePath ) === 'DOMTraverser.php' ) { - $content = str_replace( "\\$prefix\SPLObjectStorage", '\SPLObjectStorage', $content ); - } - return str_replace( "'\\\\QueryPath\\\\", "'\\\\$prefix\\\\QueryPath\\\\", diff --git a/.php-scoper/upload.php b/.php-scoper/upload.php index 4c77fb462..fcf4d407d 100644 --- a/.php-scoper/upload.php +++ b/.php-scoper/upload.php @@ -5,9 +5,6 @@ use Isolated\Symfony\Component\Finder\Finder; $path = './'; -if ( isset( $_SERVER['argv'][0] ) ) { - $path = dirname( $_SERVER['argv'][0] ) . '/'; -} return [ diff --git a/.php-scoper/url-signer.php b/.php-scoper/url-signer.php index 63543977c..7007b0b56 100644 --- a/.php-scoper/url-signer.php +++ b/.php-scoper/url-signer.php @@ -5,9 +5,6 @@ use Isolated\Symfony\Component\Finder\Finder; $path = './'; -if ( isset( $_SERVER['argv'][0] ) ) { - $path = dirname( $_SERVER['argv'][0] ) . '/'; -} return [ diff --git a/.testcaferc.js b/.testcaferc.js index 1eb9d8af9..9a2384c1b 100644 --- a/.testcaferc.js +++ b/.testcaferc.js @@ -7,13 +7,13 @@ module.exports = { skipJsErrors: true, screenshots: { takeOnFails: true, - fullPage: true, + fullPage: true }, hooks: { test: { before: async t => { await t.useRole(admin) } - }, - }, -} \ No newline at end of file + } + } +} diff --git a/.wp-env.json b/.wp-env.json index 4291e28bc..a23dd9ef8 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -2,16 +2,22 @@ "core": "https://wordpress.org/latest.zip", "phpVersion": "8.3", "plugins": [ - "gravityforms/gravityformscli" + "gravityforms/gravityformscli", + "." ], + "lifecycleScripts": { + "afterStart": "bash bin/install.sh" + }, "mappings": { - "wp-content/plugins/gravity-pdf": "." + "wp-cli.yml": "./tools/wp-env/wp-cli.yml", + "wp-content/mu-plugins": "./tools/mu-plugins" }, "env": { "tests": { "plugins": [ "gravityforms/gravityformscli", - "GravityPDF/gravity-pdf-test-suite" + "GravityPDF/gravity-pdf-test-suite", + "." ], "config": { "WP_DEBUG": true, diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 000000000..cfccf6082 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,35 @@ +const { hasArgInCLI } = require( '@wordpress/scripts/utils' ); +const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); + +const isProduction = defaultConfig.mode === 'production'; +const hasReactFastRefresh = hasArgInCLI( '--hot' ) && ! isProduction; + +module.exports = ( api ) => { + api.cache.using( () => process.env.NODE_ENV === 'production' ); + + const plugins = [ + 'babel-plugin-inline-json-import', + '@babel/plugin-transform-modules-commonjs', + ]; + + if ( hasReactFastRefresh ) { + plugins.push( 'react-refresh/babel' ); + } + + if ( isProduction ) { + plugins.push( + ...[ + [ + 'react-remove-properties', + { properties: [ 'data-test' ] }, + ], + 'transform-react-remove-prop-types', + ] + ); + } + + return { + presets: [ '@wordpress/babel-preset-default' ], + plugins, + }; +}; diff --git a/bin/build-wp.sh b/bin/build-wp.sh index c603fa5a1..377eda745 100755 --- a/bin/build-wp.sh +++ b/bin/build-wp.sh @@ -22,10 +22,14 @@ tar -zxf ${PACKAGE_DIR}/package.tar.gz --directory ${PACKAGE_DIR} && rm -f ${PAC # Run Composer yarn install --cwd ${PACKAGE_DIR} -yarn --cwd ${PACKAGE_DIR} build:production -composer install --no-dev --prefer-dist --optimize-autoloader --working-dir ${PACKAGE_DIR} +yarn --cwd ${PACKAGE_DIR} build -PLUGIN_DIR="$PACKAGE_DIR/" bash ./bin/vendor-prefix.sh +# Install all dependencies (including dev) +# Prefix will run as post-install command script - Requires php-scoper from dev dependencies +composer install --prefer-dist --working-dir ${PACKAGE_DIR} + +# Run vendor cleanup - Ensures that there's no dev dependencies on production +PLUGIN_DIR="$PACKAGE_DIR" bash ./bin/vendor-cleanup.sh # Cleanup Node JS rm -f -R ${PACKAGE_DIR}/node_modules diff --git a/bin/build.sh b/bin/build.sh index 3afb15a91..10c7b3360 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -22,10 +22,14 @@ tar -zxf ${PACKAGE_DIR}/package.tar.gz --directory ${PACKAGE_DIR} && rm -f ${PAC # Run Composer yarn install --cwd ${PACKAGE_DIR} -yarn --cwd ${PACKAGE_DIR} build:production -composer install --no-dev --prefer-dist --optimize-autoloader --working-dir ${PACKAGE_DIR} +yarn --cwd ${PACKAGE_DIR} build -PLUGIN_DIR="$PACKAGE_DIR/" bash ./bin/vendor-prefix.sh +# Install all dependencies (including dev) +# Prefix will run as post-install command script - Requires php-scoper from dev dependencies +composer install --prefer-dist --working-dir ${PACKAGE_DIR} + +# Run vendor cleanup - Ensures that there's no dev dependencies on production +PLUGIN_DIR="$PACKAGE_DIR" bash ./bin/vendor-cleanup.sh # Cleanup Node JS rm -f -R ${PACKAGE_DIR}/node_modules diff --git a/bin/install-database.sh b/bin/install-database.sh index 99f8456aa..40edc59fe 100755 --- a/bin/install-database.sh +++ b/bin/install-database.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash npm run wp-env run tests-cli wp option add freshinstall yes -npm run wp-env run tests-cli wp user create editor editor@test.com -- --role=editor --user_pass=password --quiet -npm run wp-env run tests-cli wp rewrite structure '/%postname%/' \ No newline at end of file +npm run wp-env run tests-cli wp user create editor editor@test.com -- --role=editor --user_pass=password --quiet \ No newline at end of file diff --git a/bin/install-gravityforms.sh b/bin/install-gravityforms.sh index 3b49536a9..c6081dc14 100755 --- a/bin/install-gravityforms.sh +++ b/bin/install-gravityforms.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -GF_LICENSE="${GF_LICENSE:=$1}" - # Add new variables / override existing if .env file exists if [ -f ".env" ]; then set -a @@ -10,10 +8,6 @@ if [ -f ".env" ]; then fi # Install in both development + test environments -npm run wp-env run cli wp gf install -- -- --key=$GF_LICENSE --activate --force --version=beta -npm run wp-env run tests-cli wp gf install -- -- --key=$GF_LICENSE --activate --force --version=beta - -# Install add-ons in the test environment -npm run wp-env run tests-cli wp gf install gravityformspolls -- --key=$GF_LICENSE --activate --force -npm run wp-env run tests-cli wp gf install gravityformsquiz -- --key=$GF_LICENSE --activate --force -npm run wp-env run tests-cli wp gf install gravityformssurvey -- --key=$GF_LICENSE --activate --force \ No newline at end of file +# --version=beta is not currently supported by add-ons. +npm run wp-env run cli wp gf install -- -- --key=$GF_LICENSE --activate --force --version=latest +npm run wp-env run tests-cli wp gf install -- -- --key=$GF_LICENSE --activate --force --version=latest \ No newline at end of file diff --git a/bin/install-post-actions.sh b/bin/install-post-actions.sh new file mode 100644 index 000000000..912908a19 --- /dev/null +++ b/bin/install-post-actions.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# Install add-ons in the test environment +npm run wp-env run tests-cli wp gf install gravityformspolls -- --key=$GF_LICENSE --activate --force +npm run wp-env run tests-cli wp gf install gravityformsquiz -- --key=$GF_LICENSE --activate --force +npm run wp-env run tests-cli wp gf install gravityformssurvey -- --key=$GF_LICENSE --activate --force diff --git a/bin/install.sh b/bin/install.sh index a7e31801a..421b8fdab 100755 --- a/bin/install.sh +++ b/bin/install.sh @@ -11,14 +11,16 @@ fi # Install Gravity PDF Dependencies composer install -composer run prefix +# this is triggers as post install script +# composer run prefix +# disable for now as it loops the installation # Start local environment -if [[ $PHP_ENABLE_XDEBUG ]]; then - npm run wp-env start -- --upgrade --xdebug=debug,coverage -else - npm run wp-env start -- --upgrade -fi +#if [[ $PHP_ENABLE_XDEBUG ]]; then +# npm run wp-env start -- --upgrade --xdebug=debug,coverage +#else +# npm run wp-env start -- --upgrade +#fi echo "Install Gravity Forms..." bash ./bin/install-gravityforms.sh diff --git a/bin/json-payload.sh b/bin/json-payload.sh index f146438f2..40f96ef80 100644 --- a/bin/json-payload.sh +++ b/bin/json-payload.sh @@ -11,12 +11,12 @@ if [[ -z "$2" ]]; then fi # Ensure the payload folder exists -mkdir -p dist/payload +mkdir -p build/payload # Basic Authentication # $3 is a username/password combo if [[ -z "$3" ]]; then - curl -f -L $1 -o dist/payload/$2 + curl -f -L $1 -o build/payload/$2 else - curl -f -u "$3" -L $1 -o dist/payload/$2 + curl -f -u "$3" -L $1 -o build/payload/$2 fi \ No newline at end of file diff --git a/bin/vendor-cleanup.sh b/bin/vendor-cleanup.sh new file mode 100644 index 000000000..62d9136ef --- /dev/null +++ b/bin/vendor-cleanup.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +if [ -z "$PLUGIN_DIR" ]; then + PLUGIN_DIR="" +fi + +COMPOSER="composer" + +# Temporarily remove post-install script to avoid re-execution +COMPOSER_JSON="${PLUGIN_DIR}/composer.json" +jq 'del(.scripts["post-install-cmd"])' "${COMPOSER_JSON}" > "${COMPOSER_JSON}.tmp" && mv "${COMPOSER_JSON}.tmp" "${COMPOSER_JSON}" + +# Reinstall with only production dependencies without triggering post-install script +rm -r -f ${PLUGIN_DIR}/vendor +composer install --no-dev --prefer-dist --working-dir ${PLUGIN_DIR} + +# Restore post-install script +jq '.scripts["post-install-cmd"] = "bash ./bin/vendor-prefix.sh"' "${COMPOSER_JSON}" > "${COMPOSER_JSON}.tmp" && mv "${COMPOSER_JSON}.tmp" "${COMPOSER_JSON}" + +VENDOR_DIRECTORIES=( + "monolog" + "spatie" + "league" + "masterminds" + "mpdf" + "setasign" + "myclabs" + "gravitypdf" +) + +for dir in "${VENDOR_DIRECTORIES[@]}"; +do + rm -r -f "${PLUGIN_DIR}/vendor/${dir}" +done + +eval "$COMPOSER dump-autoload --optimize --working-dir ${PLUGIN_DIR}" diff --git a/bin/vendor-prefix.sh b/bin/vendor-prefix.sh index b09488a6a..342323f9b 100644 --- a/bin/vendor-prefix.sh +++ b/bin/vendor-prefix.sh @@ -8,12 +8,6 @@ if [ -z "$PLUGIN_DIR" ]; then PLUGIN_DIR="./" fi -if [[ ! -f "${PLUGIN_DIR}php-scoper.phar" ]]; then - curl -L https://github.com/humbug/php-scoper/releases/download/0.14.1/php-scoper.phar -o ${PLUGIN_DIR}php-scoper.phar -fi - -chmod -R 777 "${PLUGIN_DIR}vendor" - PHP="php" COMPOSER="composer" @@ -22,23 +16,23 @@ mkdir "${PLUGIN_DIR}vendor_prefixed" touch "${PLUGIN_DIR}vendor_prefixed/.gitkeep" # Monolog -eval "$PHP ${PLUGIN_DIR}php-scoper.phar add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed/monolog --config=${PLUGIN_DIR}.php-scoper/monolog.php -n -vvv" +eval "$PHP ${PLUGIN_DIR}vendor/bin/php-scoper add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed/monolog --config=${PLUGIN_DIR}.php-scoper/monolog.php -n -vvv" eval "rm -Rf ${PLUGIN_DIR}vendor/monolog" # URL Signer -eval "$PHP ${PLUGIN_DIR}php-scoper.phar add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed --config=${PLUGIN_DIR}.php-scoper/url-signer.php -n -vvv" +eval "$PHP ${PLUGIN_DIR}vendor/bin/php-scoper add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed --config=${PLUGIN_DIR}.php-scoper/url-signer.php -n -vvv" eval "rm -Rf ${PLUGIN_DIR}vendor/spatie" eval "rm -Rf ${PLUGIN_DIR}vendor/league" # Querypath -eval "$PHP ${PLUGIN_DIR}php-scoper.phar add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed --config=${PLUGIN_DIR}.php-scoper/querypath.php -n -vvv" +eval "$PHP ${PLUGIN_DIR}vendor/bin/php-scoper add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed --config=${PLUGIN_DIR}.php-scoper/querypath.php -n -vvv" eval "rm -Rf ${PLUGIN_DIR}vendor/masterminds" # Codeguy -eval "$PHP ${PLUGIN_DIR}php-scoper.phar add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed/gravitypdf/upload --config=${PLUGIN_DIR}.php-scoper/upload.php -n -vvv" +eval "$PHP ${PLUGIN_DIR}vendor/bin/php-scoper add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed/gravitypdf/upload --config=${PLUGIN_DIR}.php-scoper/upload.php -n -vvv" # Mpdf -eval "$PHP ${PLUGIN_DIR}php-scoper.phar add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed --config=${PLUGIN_DIR}.php-scoper/mpdf.php -n -vvv" +eval "$PHP ${PLUGIN_DIR}vendor/bin/php-scoper add-prefix --output-dir=${PLUGIN_DIR}vendor_prefixed --config=${PLUGIN_DIR}.php-scoper/mpdf.php -n -vvv" eval "rm -Rf ${PLUGIN_DIR}vendor/mpdf" eval "rm -Rf ${PLUGIN_DIR}vendor/setasign" eval "rm -Rf ${PLUGIN_DIR}vendor/myclabs" diff --git a/dist/payload/core-fonts.json b/build/payload/core-fonts.json similarity index 100% rename from dist/payload/core-fonts.json rename to build/payload/core-fonts.json diff --git a/composer.json b/composer.json index e6f7dd805..e6b6ad3f1 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "lint": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs", "lint:fix": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf", "lint:compat": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs --standard=phpcompat.xml.dist", - "prefix": "bash ./bin/vendor-prefix.sh" + "prefix": "bash ./bin/vendor-prefix.sh", + "post-install-cmd": "@composer prefix" }, "config": { "preferred-install": "dist", @@ -39,7 +40,8 @@ "phpcompatibility/phpcompatibility-wp": "*", "roave/security-advisories": "dev-master", "yoast/phpunit-polyfills": "^3.0", - "wp-phpunit/wp-phpunit": "^6.4" + "wp-phpunit/wp-phpunit": "^6.4", + "humbug/php-scoper": "^0.15.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 7f7010403..9dc742a86 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5c19d9d7fef8e66c105ac41e8aa93b3d", + "content-hash": "28609387dfffdcfbe9cffa18d352e69c", "packages": [ { "name": "gravitypdf/querypath", @@ -456,16 +456,16 @@ }, { "name": "monolog/monolog", - "version": "2.9.3", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215" + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/a30bfe2e142720dfa990d0a7e573997f5d884215", - "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5cf826f2991858b54d5c3809bee745560a1042a7", + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7", "shasum": "" }, "require": { @@ -542,7 +542,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.9.3" + "source": "https://github.com/Seldaek/monolog/tree/2.10.0" }, "funding": [ { @@ -554,7 +554,7 @@ "type": "tidelift" } ], - "time": "2024-04-12T20:52:51+00:00" + "time": "2024-11-12T12:43:37+00:00" }, { "name": "mpdf/mpdf", @@ -1147,6 +1147,79 @@ } ], "packages-dev": [ + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" + }, { "name": "dealerdirect/phpcodesniffer-composer-installer", "version": "v1.0.0", @@ -1295,6 +1368,135 @@ ], "time": "2022-12-30T00:15:36+00:00" }, + { + "name": "humbug/php-scoper", + "version": "0.15.0", + "source": { + "type": "git", + "url": "https://github.com/humbug/php-scoper.git", + "reference": "98c92f2ec5e12756d59ce04dfad34f9fce6c19c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/humbug/php-scoper/zipball/98c92f2ec5e12756d59ce04dfad34f9fce6c19c3", + "reference": "98c92f2ec5e12756d59ce04dfad34f9fce6c19c3", + "shasum": "" + }, + "require": { + "composer/package-versions-deprecated": "^1.8", + "jetbrains/phpstorm-stubs": "^2020.2", + "nikic/php-parser": "^4.0", + "php": "^7.3 || ^8.0", + "symfony/console": "^3.2 || ^4.0", + "symfony/filesystem": "^3.2 || ^4.0", + "symfony/finder": "^3.2 || ^4.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.1", + "humbug/box": "^3.11", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-scoper" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false + }, + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php", + "src/json.php" + ], + "psr-4": { + "Humbug\\PhpScoper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Théo Fidry", + "email": "theo.fidry@gmail.com" + }, + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com" + } + ], + "description": "Prefixes all PHP namespaces in a file or directory.", + "support": { + "issues": "https://github.com/humbug/php-scoper/issues", + "source": "https://github.com/humbug/php-scoper/tree/0.15.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2021-05-10T20:50:20+00:00" + }, + { + "name": "jetbrains/phpstorm-stubs", + "version": "v2020.3", + "source": { + "type": "git", + "url": "https://github.com/JetBrains/phpstorm-stubs.git", + "reference": "daf8849db40acded37b13231a291c7536922955b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/daf8849db40acded37b13231a291c7536922955b", + "reference": "daf8849db40acded37b13231a291c7536922955b", + "shasum": "" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "nikic/php-parser": "dev-master", + "php": "^8.0", + "phpdocumentor/reflection-docblock": "dev-master", + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "files": [ + "PhpStormStubsMap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "PHP runtime & extensions header files for PhpStorm", + "homepage": "https://www.jetbrains.com/phpstorm", + "keywords": [ + "autocomplete", + "code", + "inference", + "inspection", + "jetbrains", + "phpstorm", + "stubs", + "type" + ], + "support": { + "source": "https://github.com/JetBrains/phpstorm-stubs/tree/v2020.3" + }, + "time": "2020-11-24T13:58:53+00:00" + }, { "name": "nikic/php-parser", "version": "v4.19.4", @@ -2261,18 +2463,66 @@ ], "time": "2024-09-19T10:50:18+00:00" }, + { + "name": "psr/container", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.1" + }, + "time": "2021-03-05T17:36:06+00:00" + }, { "name": "roave/security-advisories", "version": "dev-master", "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "e63317470a1b96346be224a68f9e64567e1001c3" + "reference": "9f1d9b2460cdd0422e8cfd58763bf3156ad7f487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/e63317470a1b96346be224a68f9e64567e1001c3", - "reference": "e63317470a1b96346be224a68f9e64567e1001c3", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/9f1d9b2460cdd0422e8cfd58763bf3156ad7f487", + "reference": "9f1d9b2460cdd0422e8cfd58763bf3156ad7f487", "shasum": "" }, "conflict": { @@ -2318,6 +2568,7 @@ "azuracast/azuracast": "<0.18.3", "backdrop/backdrop": "<1.27.3|>=1.28,<1.28.2", "backpack/crud": "<3.4.9", + "backpack/filemanager": "<3.0.9", "bacula-web/bacula-web": "<8.0.0.0-RC2-dev", "badaso/core": "<2.7", "bagisto/bagisto": "<2.1", @@ -2381,7 +2632,7 @@ "contao/managed-edition": "<=1.5", "corveda/phpsandbox": "<1.3.5", "cosenary/instagram": "<=2.3", - "craftcms/cms": "<4.6.2|>=5,<=5.2.2", + "craftcms/cms": "<=4.12.6.1|>=5,<=5.4.7.1", "croogo/croogo": "<4", "cuyz/valinor": "<0.12", "czim/file-handling": "<1.5|>=2,<2.3", @@ -2591,7 +2842,7 @@ "lara-zeus/artemis": ">=1,<=1.0.6", "lara-zeus/dynamic-dashboard": ">=3,<=3.0.1", "laravel/fortify": "<1.11.1", - "laravel/framework": "<6.20.44|>=7,<7.30.6|>=8,<8.75", + "laravel/framework": "<6.20.45|>=7,<7.30.7|>=8,<8.83.28|>=9,<9.52.17|>=10,<10.48.23|>=11,<11.31", "laravel/laravel": ">=5.4,<5.4.22", "laravel/reverb": "<1.4", "laravel/socialite": ">=1,<2.0.10", @@ -2649,7 +2900,7 @@ "mojo42/jirafeau": "<4.4", "mongodb/mongodb": ">=1,<1.9.2", "monolog/monolog": ">=1.8,<1.12", - "moodle/moodle": "<4.3.6|>=4.4.0.0-beta,<4.4.2", + "moodle/moodle": "<4.3.6|>=4.4,<4.4.4", "mos/cimage": "<0.7.19", "movim/moxl": ">=0.8,<=0.10", "movingbytes/social-network": "<=1.2.1", @@ -2697,7 +2948,7 @@ "openmage/magento-lts": "<20.10.1", "opensolutions/vimbadmin": "<=3.0.15", "opensource-workshop/connect-cms": "<1.7.2|>=2,<2.3.2", - "orchid/platform": ">=9,<9.4.4|>=14.0.0.0-alpha4,<14.5", + "orchid/platform": ">=8,<14.43", "oro/calendar-bundle": ">=4.2,<=4.2.6|>=5,<=5.0.6|>=5.1,<5.1.1", "oro/commerce": ">=4.1,<5.0.11|>=5.1,<5.1.1", "oro/crm": ">=1.7,<1.7.4|>=3.1,<4.1.17|>=4.2,<4.2.7", @@ -2887,7 +3138,7 @@ "symfony/error-handler": ">=4.4,<4.4.4|>=5,<5.0.4", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2|>=2.7,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.1.12|>=4.2,<4.2.7|>=5.3.14,<5.3.15|>=5.4.3,<5.4.4|>=6.0.3,<6.0.4", - "symfony/http-client": ">=4.3,<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/http-client": ">=4.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", "symfony/http-foundation": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", "symfony/http-kernel": ">=2,<4.4.50|>=5,<5.4.20|>=6,<6.0.20|>=6.1,<6.1.12|>=6.2,<6.2.6", "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13", @@ -2905,9 +3156,9 @@ "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<3.4.49|>=4,<4.4.24|>=5,<5.2.9", "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11", "symfony/security-guard": ">=2.8,<3.4.48|>=4,<4.4.23|>=5,<5.2.8", - "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.3.2|>=5.4,<5.4.31|>=6,<6.3.8", + "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.51|>=2.8,<2.8.50|>=3,<3.4.26|>=4,<4.2.12|>=4.3,<4.3.8|>=4.4,<4.4.7|>=5,<5.0.7|>=5.1,<5.2.8|>=5.3,<5.4.47|>=6,<6.4.15|>=7,<7.1.8", "symfony/serializer": ">=2,<2.0.11|>=4.1,<4.4.35|>=5,<5.3.12", - "symfony/symfony": "<5.4.46|>=6,<6.4.14|>=7,<7.1.7", + "symfony/symfony": "<5.4.47|>=6,<6.4.15|>=7,<7.1.8", "symfony/translation": ">=2,<2.0.17", "symfony/twig-bridge": ">=2,<4.4.51|>=5,<5.4.31|>=6,<6.3.8", "symfony/ux-autocomplete": "<2.11.2", @@ -2963,7 +3214,7 @@ "ua-parser/uap-php": "<3.8", "uasoft-indonesia/badaso": "<=2.9.7", "unisharp/laravel-filemanager": "<2.6.4", - "unopim/unopim": "<0.1.4", + "unopim/unopim": "<0.1.5", "userfrosting/userfrosting": ">=0.3.1,<4.6.3", "usmanhalalit/pixie": "<1.0.3|>=2,<2.0.2", "uvdesk/community-skeleton": "<=1.1.1", @@ -3099,7 +3350,7 @@ "type": "tidelift" } ], - "time": "2024-11-07T19:04:57+00:00" + "time": "2024-11-13T19:05:18+00:00" }, { "name": "sebastian/cli-parser", @@ -4066,16 +4317,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.10.3", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "62d32998e820bddc40f99f8251958aed187a5c9c" + "reference": "70c08f8d20c0eb4fe56f26644dd94dae76a7f450" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/62d32998e820bddc40f99f8251958aed187a5c9c", - "reference": "62d32998e820bddc40f99f8251958aed187a5c9c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/70c08f8d20c0eb4fe56f26644dd94dae76a7f450", + "reference": "70c08f8d20c0eb4fe56f26644dd94dae76a7f450", "shasum": "" }, "require": { @@ -4142,7 +4393,687 @@ "type": "open_collective" } ], - "time": "2024-09-18T10:38:58+00:00" + "time": "2024-11-12T09:53:29+00:00" + }, + { + "name": "symfony/console", + "version": "v4.4.49", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", + "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/lock": "<4.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/event-dispatcher": "^4.3", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v4.4.49" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-05T17:10:16+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "80d075412b557d41002320b96a096ca65aa2c98d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/80d075412b557d41002320b96a096ca65aa2c98d", + "reference": "80d075412b557d41002320b96a096ca65aa2c98d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-24T14:02:46+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v4.4.42", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "815412ee8971209bd4c1eecd5f4f481eacd44bf5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/815412ee8971209bd4c1eecd5f4f481eacd44bf5", + "reference": "815412ee8971209bd4c1eecd5f4f481eacd44bf5", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v4.4.42" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-20T08:49:14+00:00" + }, + { + "name": "symfony/finder", + "version": "v4.4.44", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "66bd787edb5e42ff59d3523f623895af05043e4f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/66bd787edb5e42ff59d3523f623895af05043e4f", + "reference": "66bd787edb5e42ff59d3523f623895af05043e4f", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v4.4.44" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-29T07:35:46+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "reference": "a2329596ddc8fd568900e3fc76cba42489ecc7f3", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-21T15:04:16+00:00" }, { "name": "theseer/tokenizer", @@ -4262,16 +5193,16 @@ }, { "name": "wp-phpunit/wp-phpunit", - "version": "6.6.2", + "version": "6.7.0", "source": { "type": "git", "url": "https://github.com/wp-phpunit/wp-phpunit.git", - "reference": "7a1d3a2150033a3d3e19de40aa5b2ef2fee36bc3" + "reference": "2ed55b450c10f6850c44531bed7d86aceba2f842" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/7a1d3a2150033a3d3e19de40aa5b2ef2fee36bc3", - "reference": "7a1d3a2150033a3d3e19de40aa5b2ef2fee36bc3", + "url": "https://api.github.com/repos/wp-phpunit/wp-phpunit/zipball/2ed55b450c10f6850c44531bed7d86aceba2f842", + "reference": "2ed55b450c10f6850c44531bed7d86aceba2f842", "shasum": "" }, "type": "library", @@ -4306,7 +5237,7 @@ "issues": "https://github.com/wp-phpunit/issues", "source": "https://github.com/wp-phpunit/wp-phpunit" }, - "time": "2024-07-17T01:13:44+00:00" + "time": "2024-11-13T01:22:47+00:00" }, { "name": "yoast/phpunit-polyfills", @@ -4382,9 +5313,9 @@ "platform": { "php": ">=7.3" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { "php": "7.3.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/jest.config.js b/jest.config.js index 9512b989b..12173d52e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,27 +1,23 @@ module.exports = { - clearMocks: true, - collectCoverageFrom: [ - 'src/assets/js/react/**/*.{js,jsx}', - '!src/assets/js/react/api/*.{js,jsx}', - '!src/assets/js/react/store/*.{js,jsx}', - '!src/assets/js/react/utilities/versionCompare.{js,jsx}' - ], - roots: [ - './tests/js-unit' - ], - transform: { - '^.+\\.js?$': 'babel-jest' - }, - coverageThreshold: { - global: { - branches: 75, - functions: 75, - lines: 75, - statements: 75 - } - }, - setupFiles: [ - './tests/js-unit/setupTests.js' - ], - testEnvironment: 'jsdom' -} \ No newline at end of file + clearMocks: true, + collectCoverageFrom: [ + 'src/assets/js/react/**/*.{js,jsx}', + '!src/assets/js/react/api/*.{js,jsx}', + '!src/assets/js/react/store/*.{js,jsx}', + '!src/assets/js/react/utilities/versionCompare.{js,jsx}', + ], + roots: [ './tests/js-unit' ], + transform: { + '^.+\\.js?$': 'babel-jest', + }, + coverageThreshold: { + global: { + branches: 75, + functions: 75, + lines: 75, + statements: 75, + }, + }, + setupFilesAfterEnv: [ './tests/js-unit/setupTests.js' ], + testEnvironment: 'jsdom', +}; diff --git a/package.json b/package.json index 561528a9e..b60e4d47c 100644 --- a/package.json +++ b/package.json @@ -2,53 +2,48 @@ "name": "gravity-pdf", "private": true, "dependencies": { - "algoliasearch": "^4.13.1", + "@ffmpeg-installer/ffmpeg": "^1.1.0", + "algoliasearch": "^5.10.2", "classnames": "^2.3.1", "core-js": "^3.23.4", + "history": "^5.3.0", "lodash.debounce": "^4.0.8", "object-to-formdata": "^4.4.2", "prop-types": "^15.8.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", "react-dropzone": "^14.2.2", - "react-instantsearch": "^7.0.0", - "react-redux": "^8.0.2", - "react-router": "^5.2.0", - "react-router-dom": "^5.2.0", - "redux": "^4.2.0", + "react-instantsearch": "^7.13.6", + "react-redux": "^9.1.2", + "react-router": "^6.27.0", + "react-router-dom": "^6.27.0", + "redux": "^5.0.1", "redux-saga": "^1.1.3", "redux-watch": "^1.2.0", - "reselect": "^4.1.6", - "sprintf-js": "^1.1.2", - "superagent": "^9.0.0" + "reselect": "^5.1.1", + "sprintf-js": "^1.1.2" }, "devDependencies": { - "@babel/cli": "^7.18.6", "@babel/core": "^7.18.6", - "@babel/eslint-parser": "^7.18.2", - "@babel/preset-env": "^7.18.6", - "@babel/preset-react": "^7.18.6", - "@wojtekmaj/enzyme-adapter-react-17": "^0.8.0", - "@wordpress/env": "^9.0.0", + "@cfaester/enzyme-adapter-react-18": "^0.8.0", + "@playwright/test": "^1.49.0", + "@redux-devtools/extension": "^3.3.0", + "@wordpress/babel-preset-default": "^8.11.0", + "@wordpress/env": "^10.10.0", + "@wordpress/eslint-plugin": "^21.4.0", + "@wordpress/scripts": "^30.4.0", "babel-jest": "^29.7.0", "babel-loader": "^9.1.3", - "babel-plugin-istanbul": "^6.1.1", + "babel-plugin-inline-json-import": "^0.3.2", + "babel-plugin-istanbul": "^7.0.0", "babel-plugin-react-remove-properties": "^0.3.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "cross-env": "^7.0.3", - "css-loader": "^6.7.1", - "css-minimizer-webpack-plugin": "^5.0.1", + "css-loader": "7.1.2", + "css-minimizer-webpack-plugin": "7.0.0", "dotenv": "^16.0.1", "enzyme": "^3.11.0", - "eslint": "^8.52.0", - "eslint-config-standard": "^17.0", - "eslint-config-standard-jsx": "^11.0.0", - "eslint-config-standard-react": "^13.0.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-n": "^16.2.0", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.0.0", - "eslint-plugin-react": "^7.30.1", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint": "^8", "file-loader": "^6.2.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -56,21 +51,30 @@ "jquery": "^3.6.0", "json-loader": "^0.5.7", "mini-css-extract-plugin": "^2.6.1", - "redux-devtools-extension": "^2.13.9", + "prettier": "^3.3.3", + "react-refresh": "^0.14.2", "redux-mock-store": "^1.5.4", "sass": "^1.53.0", - "sass-loader": "^13.0.2", + "sass-loader": "^16.0.2", "standard": "^17.0.0", "terser-webpack-plugin": "^5.3.3", - "testcafe": "^3.0", + "testcafe": "^3.7.0", + "typescript": "^5.6.3", "url-loader": "^4.1.1", "webpack": "^5.73.0", "webpack-cli": "^5.1.0", - "webpack-merge": "^5.8.0" + "webpack-merge": "^6.0.1" }, "scripts": { - "lint:js": "eslint \"src/assets/js/**/*.js\"", - "lint:js:fix": "eslint \"src/assets/js/**/*.js\" --fix", + "start": "yarn run wp-env start && yarn run dev:hot", + "dev": "WP_DEVTOOL=inline-source-map wp-scripts start --webpack-src-dir=./src/assets/js", + "dev:hot": "yarn run dev --hot", + "dev:build": "yarn run dev --no-watch", + "build": "wp-scripts build --webpack-src-dir=./src/assets/js --webpack-no-externals", + "wp-env": "wp-env", + "format": "wp-scripts format ./src/assets ./tests/js-unit && yarn run lint:js --fix && yarn run lint:css --fix", + "lint:css": "wp-scripts lint-style ./src/assets/**/*.scss ./src/assets/**/*.pcss", + "lint:js": "wp-scripts lint-js ./src/assets/js ./tests/js-unit", "test:e2e": "cross-env NODE_ENV=test testcafe chrome", "test:e2e:headless": "cross-env NODE_ENV=test testcafe chrome:headless", "test:e2e:watch": "cross-env NODE_ENV=test testcafe chrome -L", @@ -79,10 +83,13 @@ "test:js": "jest --verbose", "test:js:coverage": "jest --coverage --verbose", "test:js:watch": "jest --watchAll", - "prebuild:core-fonts": "bash bin/json-payload.sh https://api.github.com/repos/GravityPDF/mpdf-core-fonts/contents/ core-fonts.json", - "build:dev": "cross-env NODE_ENV=development webpack --watch", - "build:production": "cross-env NODE_ENV=production webpack", - "wp-env": "wp-env", - "env:install": "bash bin/install.sh" + "prebuild:core-fonts": "bash bin/json-payload.sh https://api.github.com/repos/GravityPDF/mpdf-core-fonts/contents/ core-fonts.json" + }, + "wp-env": { + "plugin-dir": "gravity-pdf", + "plugin-name": "Gravity PDF" + }, + "resolutions": { + "cheerio": "1.0.0-rc.10" } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 81b7f6129..ce7691a9c 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -99,4 +99,6 @@ /node_modules/* /src/helper/licensing/EDD_SL_Plugin_Updater.php /.php-scoper/* + /build/* + /tools/* \ No newline at end of file diff --git a/src/Controller/Controller_Save_Core_Fonts.php b/src/Controller/Controller_Save_Core_Fonts.php index cb5dac333..951410a5f 100644 --- a/src/Controller/Controller_Save_Core_Fonts.php +++ b/src/Controller/Controller_Save_Core_Fonts.php @@ -133,7 +133,7 @@ protected function download_and_save_font() { /* Verify the font name provided is approved */ /* phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents */ - $core_font_list = json_decode( file_get_contents( __DIR__ . '/../../dist/payload/core-fonts.json' ), true ); + $core_font_list = json_decode( file_get_contents( __DIR__ . '/../../build/payload/core-fonts.json' ), true ); if ( $core_font_list === null ) { $this->log->error( 'Core font list could not be loaded' ); diff --git a/src/Helper/Helper_Data.php b/src/Helper/Helper_Data.php index 5abd69964..a6dd0ff47 100644 --- a/src/Helper/Helper_Data.php +++ b/src/Helper/Helper_Data.php @@ -253,10 +253,11 @@ public function get_localised_script_data( Helper_Abstract_Options $options, Hel 'letsGoCreateOne' => esc_html__( "Let's go create one", 'gravity-pdf' ), 'installedPdfs' => esc_html__( 'Installed PDFs', 'gravity-pdf' ), 'searchTemplatePlaceholder' => esc_html__( 'Search Installed Templates', 'gravity-pdf' ), + 'closeDialog' => esc_html__( 'Close dialog', 'gravity-pdf' ), 'searchPlaceholder' => esc_html__( 'Search the Gravity PDF Knowledgebase...', 'gravity-pdf' ), 'searchResultHeadingText' => esc_html__( 'Gravity PDF Documentation', 'gravity-pdf' ), - 'noResultText' => esc_html__( 'It doesn\'t look like there are any topics related to your issue.', 'gravity-pdf' ), + 'noResultText' => esc_html__( "It doesn't look like there are any topics related to your issue.", 'gravity-pdf' ), 'getSearchResultError' => esc_html__( 'An error occurred. Please try again', 'gravity-pdf' ), 'requiresGravityPdfVersion' => esc_html__( 'Requires Gravity PDF v%s', 'gravity-pdf' ), diff --git a/src/Model/Model_Templates.php b/src/Model/Model_Templates.php index e03510538..007799976 100644 --- a/src/Model/Model_Templates.php +++ b/src/Model/Model_Templates.php @@ -136,15 +136,16 @@ public function ajax_process_uploaded_template() { ] ); + /* Bad Response */ header( 'Content-Type: application/json' ); - echo wp_json_encode( - [ - 'error' => $e->getMessage(), - ] + wp_die( + wp_json_encode( + [ + 'error' => $e->getMessage(), + ] + ), + 400 ); - - /* Bad Response */ - wp_die( '', 400 ); } /* Copy all the files to the active PDF working directory */ @@ -180,14 +181,14 @@ function ( $header ) use ( $unzipped_dir_name, $template_path ) { /* Return newly-installed template headers */ header( 'Content-Type: application/json' ); - echo wp_json_encode( - [ - 'templates' => $headers, - ] + wp_die( + wp_json_encode( + [ + 'templates' => $headers, + ] + ), + 200 ); - - /* Okay Response */ - wp_die( '', 200 ); } /** diff --git a/src/View/html/Settings/help.php b/src/View/html/Settings/help.php index 11180385f..0c9744c4a 100644 --- a/src/View/html/Settings/help.php +++ b/src/View/html/Settings/help.php @@ -31,11 +31,11 @@ diff --git a/src/assets/js/admin/bootstrap.js b/src/assets/js/admin/bootstrap.js index 1d795ad5e..2101ec534 100644 --- a/src/assets/js/admin/bootstrap.js +++ b/src/assets/js/admin/bootstrap.js @@ -1,5 +1,5 @@ -import $ from 'jquery' -import { initialiseSettings } from './settings/initialiseSettings' +import $ from 'jquery'; +import { initialiseSettings } from './settings/initialiseSettings'; /** * Gravity PDF Settings JS Logic @@ -12,20 +12,20 @@ import { initialiseSettings } from './settings/initialiseSettings' * @since 4.0 */ $(function () { - /** - * Our Admin controller - * Applies correct JS to our Gravity PDF pages - * @since 4.0 - */ - function GravityPDF () { - /** - * Process the correct settings area (the global PDF settings or individual form PDF settings) - * Also set up any event listeners needed - * @return void - * @since 4.0 - */ - initialiseSettings.init() - } + /** + * Our Admin controller + * Applies correct JS to our Gravity PDF pages + * @since 4.0 + */ + function GravityPDF() { + /** + * Process the correct settings area (the global PDF settings or individual form PDF settings) + * Also set up any event listeners needed + * @return void + * @since 4.0 + */ + initialiseSettings.init(); + } - GravityPDF() -}) + GravityPDF(); +}); diff --git a/src/assets/js/admin/helper/ajaxCall.js b/src/assets/js/admin/helper/ajaxCall.js index 754004117..47f91e8e1 100644 --- a/src/assets/js/admin/helper/ajaxCall.js +++ b/src/assets/js/admin/helper/ajaxCall.js @@ -1,21 +1,24 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * An AJAX Wrapper function we can use to ajaxify our plugin - * @param post Object an object of data to submit to our ajax endpoint. This MUST include an 'nonce' and an 'action' - * @param responseCallback a callback function - * @return void + * + * @param { Object } post Object an object of data to submit to our ajax endpoint. This MUST include an 'nonce' and an 'action' + * @param { Function } responseCallback a callback function + * + * @return { Object } jqXHR object + * * @since 4.0 */ -export function ajaxCall (post, responseCallback) { - const doAjaxcall = $.ajax({ - type: 'post', - dataType: 'json', - url: GFPDF.ajaxUrl, - data: post, - success: responseCallback, - error: responseCallback - }) +export function ajaxCall(post, responseCallback) { + const doAjaxcall = $.ajax({ + type: 'post', + dataType: 'json', + url: GFPDF.ajaxUrl, + data: post, + success: responseCallback, + error: responseCallback, + }); - return doAjaxcall + return doAjaxcall; } diff --git a/src/assets/js/admin/helper/showMessage.js b/src/assets/js/admin/helper/showMessage.js index e27630b57..b9cd77ed5 100644 --- a/src/assets/js/admin/helper/showMessage.js +++ b/src/assets/js/admin/helper/showMessage.js @@ -1,28 +1,29 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Display a message or error to the user with an appropriate timeout - * @param String msg The message to be displayed - * @param Integer timeout How long to show the message - * @param Boolean error Whether to show an error (true) or a message (false or undefined) - * @return void + * + * @param {string} msg The message to be displayed + * @param { number } timeout How long to show the message + * @param { boolean } error Whether to show an error (true) or a message (false or undefined) + * * @since 4.0 */ -export function showMessage (msg, timeout, error) { - timeout = typeof timeout !== 'undefined' ? timeout : 4500 - error = typeof error !== 'undefined' ? error : false +export function showMessage(msg, timeout, error) { + timeout = typeof timeout !== 'undefined' ? timeout : 4500; + error = typeof error !== 'undefined' ? error : false; - const $elm = $('
').html('

' + msg + '

') + const $elm = $('
').html('

' + msg + '

'); - if (error === true) { - $elm.addClass('error') - } else { - $elm.addClass('updated') - } + if (error === true) { + $elm.addClass('error'); + } else { + $elm.addClass('updated'); + } - $('.wrap > h2').after($elm) + $('.wrap > h2').after($elm); - setTimeout(function () { - $elm.slideUp() - }, timeout) + setTimeout(function () { + $elm.slideUp(); + }, timeout); } diff --git a/src/assets/js/admin/helper/spinner.js b/src/assets/js/admin/helper/spinner.js index c68225cb5..75695cba1 100644 --- a/src/assets/js/admin/helper/spinner.js +++ b/src/assets/js/admin/helper/spinner.js @@ -1,6 +1,14 @@ -import $ from 'jquery' +import $ from 'jquery'; -export function spinner (classname) { - const $spinner = $(' + GFPDF.spinnerAlt + ') - return $spinner +export function spinner(classname) { + const $spinner = $( + ' +
+			GFPDF.spinnerAlt +
+			' + ); + return $spinner; } diff --git a/src/assets/js/admin/settings/common/doColorPicker.js b/src/assets/js/admin/settings/common/doColorPicker.js index 2ba00dea7..1e4dbf190 100644 --- a/src/assets/js/admin/settings/common/doColorPicker.js +++ b/src/assets/js/admin/settings/common/doColorPicker.js @@ -1,15 +1,18 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Check if a Gravity PDF color picker field is present and initialise - * @return void + * * @since 4.0 */ -export function doColorPicker () { - $('.gfpdf-color-picker').each(function () { - $(this).wpColorPicker({ - width: 300 - }) - $(this).parents('.wp-picker-container').find('.wp-color-result').addClass('ed_button') - }) +export function doColorPicker() { + $('.gfpdf-color-picker').each(function () { + $(this).wpColorPicker({ + width: 300, + }); + $(this) + .parents('.wp-picker-container') + .find('.wp-color-result') + .addClass('ed_button'); + }); } diff --git a/src/assets/js/admin/settings/common/doUploadListener.js b/src/assets/js/admin/settings/common/doUploadListener.js index 1589c31ad..77ac0a8f5 100644 --- a/src/assets/js/admin/settings/common/doUploadListener.js +++ b/src/assets/js/admin/settings/common/doUploadListener.js @@ -1,47 +1,50 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Rich Media Uploader * JS Pulled straight from Easy Digital Download's admin-scripts.js - * @return void + * * @since 4.0 */ -export function doUploadListener () { - // WP 3.5+ uploader - let fileFrame - window.formfield = '' - - $('body').off('click', '.gfpdf_settings_upload_button').on('click', '.gfpdf_settings_upload_button', function (e) { - e.preventDefault() - - const $button = $(this) - window.formfield = $(this).prev() - - /* If the media frame already exists, reopen it. */ - if (fileFrame) { - fileFrame.open() - return - } - - /* Create the media frame. */ - fileFrame = wp.media.frames.file_frame = wp.media({ - title: $button.data('uploader-title'), - button: { - text: $button.data('uploader-button-text') - }, - multiple: false - }) - - /* When a file is selected, run a callback. */ - fileFrame.on('select', function () { - const selection = fileFrame.state().get('selection') - selection.each(function (attachment) { - attachment = attachment.toJSON() - window.formfield.val(attachment.url).trigger('change') - }) - }) - - /* Finally, open the modal */ - fileFrame.open() - }) +export function doUploadListener() { + // WP 3.5+ uploader + let fileFrame; + window.formfield = ''; + + $('body') + .off('click', '.gfpdf_settings_upload_button') + .on('click', '.gfpdf_settings_upload_button', function (e) { + e.preventDefault(); + + window.formfield = $(this).prev(); + + /* If the media frame already exists, reopen it. */ + if (fileFrame) { + fileFrame.open(); + return; + } + + const $button = $(this); + + /* Create the media frame. */ + fileFrame = wp.media.frames.file_frame = wp.media({ + title: $button.data('uploader-title'), + button: { + text: $button.data('uploader-button-text'), + }, + multiple: false, + }); + + /* When a file is selected, run a callback. */ + fileFrame.on('select', function () { + const selection = fileFrame.state().get('selection'); + selection.each(function (attachment) { + attachment = attachment.toJSON(); + window.formfield.val(attachment.url).trigger('change'); + }); + }); + + /* Finally, open the modal */ + fileFrame.open(); + }); } diff --git a/src/assets/js/admin/settings/common/dynamicTemplateFields/doMergetags.js b/src/assets/js/admin/settings/common/dynamicTemplateFields/doMergetags.js index df113644c..296d2317e 100644 --- a/src/assets/js/admin/settings/common/dynamicTemplateFields/doMergetags.js +++ b/src/assets/js/admin/settings/common/dynamicTemplateFields/doMergetags.js @@ -1,27 +1,40 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Remove any existing merge tags and reinitialise - * @return void + * * @since 4.0 */ -export function doMergetags () { - if (window.gfMergeTags && typeof form !== 'undefined' && $('#gfpdf-fieldset-gfpdf_form_settings_template .merge-tag-support').length >= 0) { - /* Initialise */ - $('#gfpdf-fieldset-gfpdf_form_settings_template .merge-tag-support').each(function () { - new gfMergeTagsObj(form, $(this)) // eslint-disable-line - }) +export function doMergetags() { + if ( + window.gfMergeTags && + typeof form !== 'undefined' && + $('#gfpdf-fieldset-gfpdf_form_settings_template .merge-tag-support') + .length >= 0 + ) { + /* Initialise */ + $( + '#gfpdf-fieldset-gfpdf_form_settings_template .merge-tag-support' + ).each(function () { + new gfMergeTagsObj( form, $( this ) ); // eslint-disable-line + }); - /* Wrap merge tag selectors with new GF2.5 markup */ - $('#gfpdf-fieldset-gfpdf_form_settings_template .gform-settings-field').each(function () { - $(this) - .find('.merge-tag-support, .merge-tag-support + span') - .wrapAll('
') + /* Wrap merge tag selectors with new GF2.5 markup */ + $( + '#gfpdf-fieldset-gfpdf_form_settings_template .gform-settings-field' + ).each(function () { + $(this) + .find('.merge-tag-support, .merge-tag-support + span') + .wrapAll( + '
' + ); - $(this) - .find('.all-merge-tags.textarea') - .parent() - .wrapAll('
') - }) - } + $(this) + .find('.all-merge-tags.textarea') + .parent() + .wrapAll( + '
' + ); + }); + } } diff --git a/src/assets/js/admin/settings/common/dynamicTemplateFields/loadTinyMCEEditor.js b/src/assets/js/admin/settings/common/dynamicTemplateFields/loadTinyMCEEditor.js index cd3c28388..88c50afdd 100644 --- a/src/assets/js/admin/settings/common/dynamicTemplateFields/loadTinyMCEEditor.js +++ b/src/assets/js/admin/settings/common/dynamicTemplateFields/loadTinyMCEEditor.js @@ -1,54 +1,72 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Initialises AJAX-loaded wp_editor TinyMCE containers for use - * @param Array editors The DOM element IDs to parse - * @param Object settings The TinyMCE settings to use - * @return void + * @param { Array } editors The DOM element IDs to parse + * @param { Object } settings The TinyMCE settings to use + * * @since 4.0 */ -export function loadTinyMCEEditor (editors, settings) { - if (settings != null) { - /* Ensure appropriate settings defaults */ - settings.body_class = 'id post-type-post post-status-publish post-format-standard' - settings.formats = { - alignleft: [ - { selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: { textAlign: 'left' } }, - { selector: 'img,table,dl.wp-caption', classes: 'alignleft' } - ], - aligncenter: [ - { selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: { textAlign: 'center' } }, - { selector: 'img,table,dl.wp-caption', classes: 'aligncenter' } - ], - alignright: [ - { selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: { textAlign: 'right' } }, - { selector: 'img,table,dl.wp-caption', classes: 'alignright' } - ], - strikethrough: { inline: 'del' } - } - settings.content_style = 'body#tinymce { max-width: 100%; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;}' - } +export function loadTinyMCEEditor(editors, settings) { + if (settings) { + /* Ensure appropriate settings defaults */ + settings.body_class = + 'id post-type-post post-status-publish post-format-standard'; + settings.formats = { + alignleft: [ + { + selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', + styles: { textAlign: 'left' }, + }, + { selector: 'img,table,dl.wp-caption', classes: 'alignleft' }, + ], + aligncenter: [ + { + selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', + styles: { textAlign: 'center' }, + }, + { selector: 'img,table,dl.wp-caption', classes: 'aligncenter' }, + ], + alignright: [ + { + selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', + styles: { textAlign: 'right' }, + }, + { selector: 'img,table,dl.wp-caption', classes: 'alignright' }, + ], + strikethrough: { inline: 'del' }, + }; + settings.content_style = + 'body#tinymce { max-width: 100%; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;}'; + } - /* Load our new editors */ - $.each(editors, function (index, fullId) { - /* Setup out selector */ - settings.selector = '#' + fullId + /* Load our new editors */ + $.each(editors, function (index, fullId) { + /* Setup out selector */ + settings.selector = '#' + fullId; - /* Initialise our editor */ - tinyMCE.init(settings) + /* Initialise our editor */ + tinyMCE.init(settings); - /* Add our editor to the DOM */ - tinyMCE.execCommand('mceAddEditor', false, fullId) + /* Add our editor to the DOM */ + tinyMCE.execCommand('mceAddEditor', false, fullId); - /* Enable WP quick tags */ - if (typeof (QTags) === 'function') { - QTags({ id: fullId }) - QTags._buttonsInit() + /* Enable WP quick tags */ + if (typeof QTags === 'function') { + QTags({ id: fullId }); + QTags._buttonsInit(); - /* remember last tab selected */ - if (typeof switchEditors.switchto === 'function') { - switchEditors.switchto(jQuery('#wp-' + fullId + '-wrap').find('.wp-switch-editor.switch-' + (getUserSetting('editor') === 'html' ? 'html' : 'tmce'))[0]) - } - } - }) + /* remember last tab selected */ + if (typeof switchEditors.switchto === 'function') { + switchEditors.switchto( + jQuery('#wp-' + fullId + '-wrap').find( + '.wp-switch-editor.switch-' + + (getUserSetting('editor') === 'html' + ? 'html' + : 'tmce') + )[0] + ); + } + } + }); } diff --git a/src/assets/js/admin/settings/common/initialiseCommonElements.js b/src/assets/js/admin/settings/common/initialiseCommonElements.js index eddf96908..4733f23ac 100644 --- a/src/assets/js/admin/settings/common/initialiseCommonElements.js +++ b/src/assets/js/admin/settings/common/initialiseCommonElements.js @@ -1,10 +1,10 @@ -import { setupGravityForms } from './setupGravityForms' -import { doUploadListener } from './doUploadListener' -import { doColorPicker } from './doColorPicker' -import { setupCustomPaperSize } from './setupCustomPaperSize' -import { setupToggledFields } from './setupToggledFields' -import { setupDynamicTemplateFields } from './setupDynamicTemplateFields' -import { setupLicenseDeactivation } from './setupLicenseDeactivation' +import { setupGravityForms } from './setupGravityForms'; +import { doUploadListener } from './doUploadListener'; +import { doColorPicker } from './doColorPicker'; +import { setupCustomPaperSize } from './setupCustomPaperSize'; +import { setupToggledFields } from './setupToggledFields'; +import { setupDynamicTemplateFields } from './setupDynamicTemplateFields'; +import { setupLicenseDeactivation } from './setupLicenseDeactivation'; /** * Initialise any common elements @@ -12,28 +12,28 @@ import { setupLicenseDeactivation } from './setupLicenseDeactivation' * @since 4.0 */ class InitialiseCommonElements { - runElements () { - /* Change some Gravity Forms parameters */ - setupGravityForms() + runElements() { + /* Change some Gravity Forms parameters */ + setupGravityForms(); - /* If we have a upload field handle the logic */ - doUploadListener() + /* If we have a upload field handle the logic */ + doUploadListener(); - /* If we have a colour picker handle the logic */ - doColorPicker() + /* If we have a colour picker handle the logic */ + doColorPicker(); - /* Setup custom paper size, if needed */ - setupCustomPaperSize() + /* Setup custom paper size, if needed */ + setupCustomPaperSize(); - /* Setup toggled fields, if needed */ - setupToggledFields() + /* Setup toggled fields, if needed */ + setupToggledFields(); - /* Setup our template loader, if needed */ - setupDynamicTemplateFields() + /* Setup our template loader, if needed */ + setupDynamicTemplateFields(); - /* Setup license deactivation, if needed */ - setupLicenseDeactivation() - } + /* Setup license deactivation, if needed */ + setupLicenseDeactivation(); + } } -export const initialiseCommonElements = new InitialiseCommonElements() +export const initialiseCommonElements = new InitialiseCommonElements(); diff --git a/src/assets/js/admin/settings/common/setupCustomPaperSize.js b/src/assets/js/admin/settings/common/setupCustomPaperSize.js index 46a10a399..b6a943e7d 100644 --- a/src/assets/js/admin/settings/common/setupCustomPaperSize.js +++ b/src/assets/js/admin/settings/common/setupCustomPaperSize.js @@ -1,22 +1,27 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Show / Hide our custom paper size as needed - * @return void + * * @since 4.0 */ -export function setupCustomPaperSize () { - $('.gfpdf_paper_size').each(function () { - const $customPaperSize = $(this).nextAll('.gfpdf_paper_size_other').first() - const $paperSize = $(this).find('select') +export function setupCustomPaperSize() { + $('.gfpdf_paper_size').each(function () { + const $customPaperSize = $(this) + .nextAll('.gfpdf_paper_size_other') + .first(); + const $paperSize = $(this).find('select'); - /* Add our change event */ - $paperSize.off('change').on('change', function () { - if ($(this).val() === 'CUSTOM') { - $customPaperSize.fadeIn() - } else { - $customPaperSize.fadeOut() - } - }).trigger('change') - }) + /* Add our change event */ + $paperSize + .off('change') + .on('change', function () { + if ($(this).val() === 'CUSTOM') { + $customPaperSize.fadeIn(); + } else { + $customPaperSize.fadeOut(); + } + }) + .trigger('change'); + }); } diff --git a/src/assets/js/admin/settings/common/setupDynamicTemplateFields.js b/src/assets/js/admin/settings/common/setupDynamicTemplateFields.js index 79e397af1..b26ea9dd9 100644 --- a/src/assets/js/admin/settings/common/setupDynamicTemplateFields.js +++ b/src/assets/js/admin/settings/common/setupDynamicTemplateFields.js @@ -1,96 +1,106 @@ -import $ from 'jquery' -import { ajaxCall } from '../../helper/ajaxCall' -import { spinner } from '../../helper/spinner' -import { loadTinyMCEEditor } from './dynamicTemplateFields/loadTinyMCEEditor' -import { initialiseCommonElements } from './initialiseCommonElements' -import { doMergetags } from './dynamicTemplateFields/doMergetags' -import { toggleFontAppearance } from '../pdf/toggleFontAppearance' -import { insertAfter } from '../../../react/utilities/PdfSettings/addEditButton' +import $ from 'jquery'; +import { ajaxCall } from '../../helper/ajaxCall'; +import { spinner } from '../../helper/spinner'; +import { loadTinyMCEEditor } from './dynamicTemplateFields/loadTinyMCEEditor'; +import { initialiseCommonElements } from './initialiseCommonElements'; +import { doMergetags } from './dynamicTemplateFields/doMergetags'; +import { toggleFontAppearance } from '../pdf/toggleFontAppearance'; +import { insertAfter } from '../../../react/utilities/PdfSettings/addEditButton'; /** * PDF Templates can assign their own custom settings which can enhance a template * This function setups the required listeners and functionality to allow this behaviour - * @return return + * * @since 4.0 */ -export function setupDynamicTemplateFields () { - /* Add change listener to our template */ - $('#gfpdf_settings\\[template\\]').off('change').on('change', function () { - /* Add spinner */ - const $spinner = spinner('gfpdf-spinner-template') +export function setupDynamicTemplateFields() { + /* Add change listener to our template */ + $('#gfpdf_settings\\[template\\]') + .off('change') + .on('change', function () { + /* Add spinner */ + const $spinner = spinner('gfpdf-spinner-template'); - $(this).next().after($spinner) + $(this).next().after($spinner); - const data = { - action: 'gfpdf_get_template_fields', - nonce: GFPDF.ajaxNonce, - template: $(this).val(), - type: $(this).attr('id'), - id: $('#gform_id').val(), - gform_pdf_id: $('#gform_pdf_id').val() - } + const data = { + action: 'gfpdf_get_template_fields', + nonce: GFPDF.ajaxNonce, + template: $(this).val(), + type: $(this).attr('id'), + id: $('#gform_id').val(), + gform_pdf_id: $('#gform_pdf_id').val(), + }; - ajaxCall(data, function (response) { - const addEditButton = $('.submit-container-2')[0] + ajaxCall(data, function (response) { + const addEditButton = $('.submit-container-2')[0]; - /* Remove our UI loader */ - $spinner.remove() + /* Remove our UI loader */ + $spinner.remove(); - /* Reset our legacy Advanced Template option */ - $('input[name="gfpdf_settings[advanced_template]"][value="No"]').prop('checked', true).trigger('change') + /* Reset our legacy Advanced Template option */ + $('input[name="gfpdf_settings[advanced_template]"][value="No"]') + .prop('checked', true) + .trigger('change'); - /* Only process if the response is valid */ - if (response.fields) { - /* Remove any previously loaded editors to prevent conflicts loading an editor with same name */ - $.each(response.editors, function (index, value) { - const editor = tinyMCE.get(value) - if (editor !== null) { - /* Bug Fix for Firefox - http://www.tinymce.com/develop/bugtracker_view.php?id=3152 */ - try { - tinyMCE.remove(editor) - } catch (e) { - // empty - } - } - }) + /* Only process if the response is valid */ + if (response.fields) { + /* Remove any previously loaded editors to prevent conflicts loading an editor with same name */ + $.each(response.editors, function (index, value) { + const editor = tinyMCE.get(value); + if (editor !== null) { + /* Bug Fix for Firefox - http://www.tinymce.com/develop/bugtracker_view.php?id=3152 */ + try { + tinyMCE.remove(editor); + } catch (e) { + // empty + } + } + }); - /* Add floating Add/Edit PDF button */ - if (!addEditButton) { - insertAfter($('#gfpdf-fieldset-gfpdf_form_settings_template')[0], $('#gfpdf_pdf_form')[0], '2') - } + /* Add floating Add/Edit PDF button */ + if (!addEditButton) { + insertAfter( + $( + '#gfpdf-fieldset-gfpdf_form_settings_template' + )[0], + $('#gfpdf_pdf_form')[0], + '2' + ); + } - /* Replace the custom appearance with the AJAX response fields */ - $('#gfpdf-fieldset-gfpdf_form_settings_template') - .show() - .find('.gform-settings-panel__content') - .html(response.fields) + /* Replace the custom appearance with the AJAX response fields */ + $('#gfpdf-fieldset-gfpdf_form_settings_template') + .show() + .find('.gform-settings-panel__content') + .html(response.fields); - /* Load our new editors */ - loadTinyMCEEditor(response.editors, response.editor_init) + /* Load our new editors */ + loadTinyMCEEditor(response.editors, response.editor_init); - /* reinitialise new dom elements */ - initialiseCommonElements.runElements() - doMergetags() - gform_initialize_tooltips() - } else { - /* Remove floating Add/Edit PDF button */ - if (addEditButton) { - addEditButton.remove() - } + /* reinitialise new dom elements */ + initialiseCommonElements.runElements(); + doMergetags(); + gform_initialize_tooltips(); + } else { + /* Remove floating Add/Edit PDF button */ + if (addEditButton) { + addEditButton.remove(); + } - /* Hide our template nav item as there are no fields and clear our the HTML */ - $('#gfpdf-fieldset-gfpdf_form_settings_template') - .hide() - .find('.gform-settings-panel__content') - .html('') - } + /* Hide our template nav item as there are no fields and clear our the HTML */ + $('#gfpdf-fieldset-gfpdf_form_settings_template') + .hide() + .find('.gform-settings-panel__content') + .html(''); + } - /* Check if we should hide or show our font fields */ - if (response.template_type) { - toggleFontAppearance(response.template_type) - } + /* Check if we should hide or show our font fields */ + if (response.template_type) { + toggleFontAppearance(response.template_type); + } - $(document).trigger('gfpdf_template_loaded', [response]) - }) - }) + $(document).trigger('gfpdf_template_loaded', [response]); + }); + }); } diff --git a/src/assets/js/admin/settings/common/setupGravityForms.js b/src/assets/js/admin/settings/common/setupGravityForms.js index 85d8a4838..8702c4daf 100644 --- a/src/assets/js/admin/settings/common/setupGravityForms.js +++ b/src/assets/js/admin/settings/common/setupGravityForms.js @@ -3,15 +3,15 @@ * * @since 4.1 */ -export function setupGravityForms () { - /** - * Check if the global gf_vars has been set and if so replace the .thisFormButton, .show, .hide objects with our - * customised options. - * @since 4.0 - */ - if (typeof gf_vars !== 'undefined') { // eslint-disable-line - gf_vars.thisFormButton = GFPDF.conditionalText - gf_vars.show = GFPDF.enable - gf_vars.hide = GFPDF.disable - } +export function setupGravityForms() { + /** + * Check if the global gf_vars has been set and if so replace the .thisFormButton, .show, .hide objects with our + * customised options. + * @since 4.0 + */ + if (typeof gf_vars !== 'undefined') { + gf_vars.thisFormButton = GFPDF.conditionalText; + gf_vars.show = GFPDF.enable; + gf_vars.hide = GFPDF.disable; + } } diff --git a/src/assets/js/admin/settings/common/setupLicenseDeactivation.js b/src/assets/js/admin/settings/common/setupLicenseDeactivation.js index b32810656..217d94ba3 100644 --- a/src/assets/js/admin/settings/common/setupLicenseDeactivation.js +++ b/src/assets/js/admin/settings/common/setupLicenseDeactivation.js @@ -1,58 +1,58 @@ -import $ from 'jquery' -import { ajaxCall } from '../../helper/ajaxCall' -import { spinner } from '../../helper/spinner' +import $ from 'jquery'; +import { ajaxCall } from '../../helper/ajaxCall'; +import { spinner } from '../../helper/spinner'; /** * Handles individual add-on license key deactivation via AJAX * @since 4.2 */ -export function setupLicenseDeactivation () { - $('.gfpdf-deactivate-license').on('click', function () { - /* Do AJAX call so user can deactivate license */ - const $container = $(this).parent() - - /* Add spinner */ - const $spinner = spinner('gfpdf-spinner') - - /* Add our spinner */ - $(this).append($spinner) - - /* Set up ajax data */ - const slug = $(this).data('addon-name') - - const data = { - action: 'gfpdf_deactivate_license', - addon_name: slug, - nonce: $(this).data('nonce') - } - - /* Do ajax call */ - ajaxCall(data, function (response) { - /* Remove our loading spinner */ - $spinner.remove() - - /* cleanup inputs */ - $('#gfpdf_settings\\[license_' + slug + '\\]').val('') - $('#gfpdf_settings\\[license_' + slug + '_message\\]').val('') - $('#gfpdf_settings\\[license_' + slug + '_status\\]').val('') - $container.find('button').remove() - - if (response.success) { - $container - .find('#message') - .removeClass('error') - .addClass('success') - .html(response.success) - } else { - /* Show error message */ - $container - .find('#message') - .removeClass('success') - .addClass('error') - .html(response.error) - } - }) - - return false - }) +export function setupLicenseDeactivation() { + $('.gfpdf-deactivate-license').on('click', function () { + /* Do AJAX call so user can deactivate license */ + const $container = $(this).parent(); + + /* Add spinner */ + const $spinner = spinner('gfpdf-spinner'); + + /* Add our spinner */ + $(this).append($spinner); + + /* Set up ajax data */ + const slug = $(this).data('addon-name'); + + const data = { + action: 'gfpdf_deactivate_license', + addon_name: slug, + nonce: $(this).data('nonce'), + }; + + /* Do ajax call */ + ajaxCall(data, function (response) { + /* Remove our loading spinner */ + $spinner.remove(); + + /* cleanup inputs */ + $('#gfpdf_settings\\[license_' + slug + '\\]').val(''); + $('#gfpdf_settings\\[license_' + slug + '_message\\]').val(''); + $('#gfpdf_settings\\[license_' + slug + '_status\\]').val(''); + $container.find('button').remove(); + + if (response.success) { + $container + .find('#message') + .removeClass('error') + .addClass('success') + .html(response.success); + } else { + /* Show error message */ + $container + .find('#message') + .removeClass('success') + .addClass('error') + .html(response.error); + } + }); + + return false; + }); } diff --git a/src/assets/js/admin/settings/common/setupToggledFields.js b/src/assets/js/admin/settings/common/setupToggledFields.js index 140cb5d12..81b56d94c 100644 --- a/src/assets/js/admin/settings/common/setupToggledFields.js +++ b/src/assets/js/admin/settings/common/setupToggledFields.js @@ -1,33 +1,35 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Add change event listeners on our toggle params and toggle the container - * @return void + * * @since 4.0 */ -export function setupToggledFields () { - $('form').off('change', '.gfpdf-input-toggle').on('change', '.gfpdf-input-toggle', function () { - const $container = $(this).parent().next() +export function setupToggledFields() { + $('form') + .off('change', '.gfpdf-input-toggle') + .on('change', '.gfpdf-input-toggle', function () { + const $container = $(this).parent().next(); - /* Currently checked so hide out input and if cotains rich_text, textarea or input we will delete values */ - if ($(this).prop('checked')) { - $container.slideDown('slow') - } else { - $container.slideUp('slow') + /* Currently checked so hide out input and if cotains rich_text, textarea or input we will delete values */ + if ($(this).prop('checked')) { + $container.slideDown('slow'); + } else { + $container.slideUp('slow'); - /* Remove TinyMCE Content */ - $container.find('.wp-editor-area').each(function () { - const editor = tinyMCE.get($(this).attr('id')) + /* Remove TinyMCE Content */ + $container.find('.wp-editor-area').each(function () { + const editor = tinyMCE.get($(this).attr('id')); - if (editor !== null) { - editor.setContent('') - } - }) + if (editor !== null) { + editor.setContent(''); + } + }); - /* Remove textarea content */ - $container.find('textarea').each(function () { - $(this).val('') - }) - } - }) + /* Remove textarea content */ + $container.find('textarea').each(function () { + $(this).val(''); + }); + } + }); } diff --git a/src/assets/js/admin/settings/common/updateURLParameter.js b/src/assets/js/admin/settings/common/updateURLParameter.js index 3de29c2a6..ab86c2c51 100644 --- a/src/assets/js/admin/settings/common/updateURLParameter.js +++ b/src/assets/js/admin/settings/common/updateURLParameter.js @@ -1,28 +1,31 @@ /** * Update the URL parameter - * @param String The URL to parse - * @param String The URL parameter to want to update - * @param String The replacement string for the URL parameter - * @return String The processed URL + * @param { string } url - The URL to parse + * @param { string } param - The URL parameter to want to update + * @param { string } paramVal - The replacement string for the URL parameter + * + * @return { string } The processed URL + * * @since 4.0 - * @link http://stackoverflow.com/a/10997390/11236 + * + * {@link http://stackoverflow.com/a/10997390/11236 Link} */ -export function updateURLParameter (url, param, paramVal) { - let newAdditionalURL = '' - let tempArray = url.split('?') - const baseURL = tempArray[0] - const additionalURL = tempArray[1] - let temp = '' - if (additionalURL) { - tempArray = additionalURL.split('&') - for (let i = 0; i < tempArray.length; i++) { - if (tempArray[i].split('=')[0] !== param) { - newAdditionalURL += temp + tempArray[i] - temp = '&' - } - } - } +export function updateURLParameter(url, param, paramVal) { + let newAdditionalURL = ''; + let tempArray = url.split('?'); + const baseURL = tempArray[0]; + const additionalURL = tempArray[1]; + let temp = ''; + if (additionalURL) { + tempArray = additionalURL.split('&'); + for (let i = 0; i < tempArray.length; i++) { + if (tempArray[i].split('=')[0] !== param) { + newAdditionalURL += temp + tempArray[i]; + temp = '&'; + } + } + } - const rowsTxt = temp + '' + param + '=' + paramVal - return baseURL + '?' + newAdditionalURL + rowsTxt + const rowsTxt = temp + '' + param + '=' + paramVal; + return baseURL + '?' + newAdditionalURL + rowsTxt; } diff --git a/src/assets/js/admin/settings/form/doFormSettingsListPage.js b/src/assets/js/admin/settings/form/doFormSettingsListPage.js index d85ae35a4..89be1da5e 100644 --- a/src/assets/js/admin/settings/form/doFormSettingsListPage.js +++ b/src/assets/js/admin/settings/form/doFormSettingsListPage.js @@ -1,6 +1,6 @@ -import { setupAJAXListDeleteListener } from './list/setupAJAXListDeleteListener' -import { setupAJAXListDuplicateListener } from './list/setupAJAXListDuplicateListener' -import { setupAJAXListStateListener } from './list/setupAJAXListStateListener' +import { setupAJAXListDeleteListener } from './list/setupAJAXListDeleteListener'; +import { setupAJAXListDuplicateListener } from './list/setupAJAXListDuplicateListener'; +import { setupAJAXListStateListener } from './list/setupAJAXListStateListener'; /** * Process the functionality for the PDF form settings 'list' page @@ -8,11 +8,11 @@ import { setupAJAXListStateListener } from './list/setupAJAXListStateListener' * @since 4.0 */ class DoFormSettingsListPage { - setupAJAXListListener () { - setupAJAXListDeleteListener() - setupAJAXListDuplicateListener() - setupAJAXListStateListener() - } + setupAJAXListListener() { + setupAJAXListDeleteListener(); + setupAJAXListDuplicateListener(); + setupAJAXListStateListener(); + } } -export const doFormSettingsListPage = new DoFormSettingsListPage() +export const doFormSettingsListPage = new DoFormSettingsListPage(); diff --git a/src/assets/js/admin/settings/form/list/setupAJAXListDeleteListener.js b/src/assets/js/admin/settings/form/list/setupAJAXListDeleteListener.js index f1186fb44..e42ca1a68 100644 --- a/src/assets/js/admin/settings/form/list/setupAJAXListDeleteListener.js +++ b/src/assets/js/admin/settings/form/list/setupAJAXListDeleteListener.js @@ -1,73 +1,74 @@ -import $ from 'jquery' -import { ajaxCall } from '../../../helper/ajaxCall' -import { spinner } from '../../../helper/spinner' -import { showMessage } from '../../../helper/showMessage' +import $ from 'jquery'; +import { ajaxCall } from '../../../helper/ajaxCall'; +import { spinner } from '../../../helper/spinner'; +import { showMessage } from '../../../helper/showMessage'; /** * Handles the deletion of a PDF list item via AJAX - * @return void + * * @since 4.0 */ -export function setupAJAXListDeleteListener () { - /** - * Check if the last item was just deleted - */ - function maybeShowEmptyRow () { - const $container = $('#gfpdf_list_form tbody') +export function setupAJAXListDeleteListener() { + /** + * Check if the last item was just deleted + */ + function maybeShowEmptyRow() { + const $container = $('#gfpdf_list_form tbody'); - if ($container.find('tr').length === 0) { - const $row = $('').addClass('no-items') - const $cell = $('').attr('colspan', '5').addClass('colspanchange') - const $addNew = $('').attr('href', $('#gfpdf_list_form a.button:first').attr('href')).append(GFPDF.letsGoCreateOne + '.') - $cell.append(GFPDF.thisFormHasNoPdfs).append(' ').append($addNew) - $row.append($cell) - $container.append($row) - } - } + if ($container.find('tr').length === 0) { + const $row = $('').addClass('no-items'); + const $cell = $('') + .attr('colspan', '5') + .addClass('colspanchange'); + const $addNew = $('') + .attr('href', $('#gfpdf_list_form a.button:first').attr('href')) + .append(GFPDF.letsGoCreateOne + '.'); + $cell.append(GFPDF.thisFormHasNoPdfs).append(' ').append($addNew); + $row.append($cell); + $container.append($row); + } + } - function deletePdf ($elm) { - $elm - .append(spinner('gfpdf-spinner gfpdf-spinner-small')) - .closest('.row-actions') - .attr('style', 'position:static; visibility: visible;') + function deletePdf($elm) { + $elm.append(spinner('gfpdf-spinner gfpdf-spinner-small')) + .closest('.row-actions') + .attr('style', 'position:static; visibility: visible;'); - const data = { - action: 'gfpdf_list_delete', - nonce: $elm.data('nonce'), - fid: $elm.data('fid'), - pid: $elm.data('id') - } + const data = { + action: 'gfpdf_list_delete', + nonce: $elm.data('nonce'), + fid: $elm.data('fid'), + pid: $elm.data('id'), + }; - ajaxCall(data, function (response) { - if (response.msg) { - /* Remove spinner */ - $elm - .closest('.row-actions') - .attr('style', '') - .find('.gfpdf-spinner') - .remove() + ajaxCall(data, function (response) { + if (response.msg) { + /* Remove spinner */ + $elm.closest('.row-actions') + .attr('style', '') + .find('.gfpdf-spinner') + .remove(); - showMessage(response.msg) + showMessage(response.msg); - $elm - .parents('tr') - .css('background', '#ffb8b8') - .fadeOut(400, function () { - this.remove() - maybeShowEmptyRow() - }) - } - }) - } + $elm.parents('tr') + .css('background', '#ffb8b8') + .fadeOut(400, function () { + this.remove(); + maybeShowEmptyRow(); + }); + } + }); + } - /* Add live delete listener */ - $('#gfpdf_list_form').on('click', 'a.submitdelete', function () { - const id = String($(this).data('id')) - if (id.length > 0 && window.confirm(GFPDF.pdfDeleteWarning)) { - const $elm = $(this) - deletePdf($elm) - } + /* Add live delete listener */ + $('#gfpdf_list_form').on('click', 'a.submitdelete', function () { + const id = String($(this).data('id')); + if (id.length > 0 && window.confirm(GFPDF.pdfDeleteWarning)) { + const $elm = $(this); + deletePdf($elm); + } - return false - }) + return false; + }); } diff --git a/src/assets/js/admin/settings/form/list/setupAJAXListDuplicateListener.js b/src/assets/js/admin/settings/form/list/setupAJAXListDuplicateListener.js index 55f3fca0f..7e8cd8963 100644 --- a/src/assets/js/admin/settings/form/list/setupAJAXListDuplicateListener.js +++ b/src/assets/js/admin/settings/form/list/setupAJAXListDuplicateListener.js @@ -1,95 +1,110 @@ -import $ from 'jquery' -import { updateURLParameter } from '../../common/updateURLParameter' -import { ajaxCall } from '../../../helper/ajaxCall' -import { spinner } from '../../../helper/spinner' -import { showMessage } from '../../../helper/showMessage' +import $ from 'jquery'; +import { updateURLParameter } from '../../common/updateURLParameter'; +import { ajaxCall } from '../../../helper/ajaxCall'; +import { spinner } from '../../../helper/spinner'; +import { showMessage } from '../../../helper/showMessage'; /** * Handles the duplicate of a PDF list item via AJAX and fixes up all the nonce actions - * @return void + * * @since 4.0 */ -export function setupAJAXListDuplicateListener () { - /* Add live duplicate listener */ - $('#gfpdf_list_form').on('click', 'a.submitduplicate', function (e) { - e.preventDefault() - - const id = String($(this).data('id')) - const that = this - - /* Add our spinner */ - $(this).after(spinner('gfpdf-spinner gfpdf-spinner-small')).parent().parent().attr('style', 'position:static; visibility: visible;') - - if (id.length > 0) { - /* Set up ajax data */ - const data = { - action: 'gfpdf_list_duplicate', - nonce: $(this).data('nonce'), - fid: $(this).data('fid'), - pid: $(this).data('id') - } - - /* Do ajax call */ - ajaxCall(data, function (response) { - if (response.msg) { - /* Remove the spinner */ - $(that).parent().parent().attr('style', '').find('.gfpdf-spinner').remove() - - /* Provide feedback to use */ - showMessage(response.msg) - - /* Clone the row to be duplicated */ - const $row = $(that).parents('tr') - const $newRow = $row.clone() - - /* Update the edit links to point to the new location */ - $newRow.find('.column-name > a, .edit a').each(function () { - let href = $(this).attr('href') - href = updateURLParameter(href, 'pid', response.pid) - $(this).attr('href', href) - }) - - /* Update the name field */ - $newRow.find('.column-name > a').html(response.name) - - /* Find duplicate and delete elements */ - const $duplicate = $newRow.find('.duplicate a') - const $delete = $newRow.find('.delete a') - const $state = $newRow.find('.check-column button') - const $shortcode = $newRow.find('.column-shortcode') - - /* Update duplicate ID and nonce pointers so the actions are valid */ - $duplicate.data('id', response.pid) - $duplicate.data('nonce', response.dup_nonce) - - /* Update delete ID and nonce pointers so the actions are valid */ - $delete.data('id', response.pid) - $delete.data('nonce', response.del_nonce) - - /* update state ID and nonce pointers so the actions are valid */ - $state.data('id', response.pid) - $state.data('nonce', response.state_nonce) - - /* Set button data-status to inactive by default */ - $state[0].setAttribute('data-status', 'inactive') - - /* Update our shortcode ID */ - let shortcodeValue = $shortcode.find('button').attr('data-clipboard-text') - shortcodeValue = shortcodeValue.replace(id, response.pid) - $shortcode.find('button').attr('data-clipboard-text', shortcodeValue) - $shortcode.find('input') - .attr('id', response.pid) - .attr('value', shortcodeValue) - - $state.removeClass('gform-status--active') - .addClass('gform-status--inactive') - .find('.gform-status-indicator-status') - .html(response.status) - - /* Add row to node and fade in */ - $newRow.hide().insertAfter($row).fadeIn() - } - }) - } - }) +export function setupAJAXListDuplicateListener() { + /* Add live duplicate listener */ + $('#gfpdf_list_form').on('click', 'a.submitduplicate', function (e) { + e.preventDefault(); + + const id = String($(this).data('id')); + const that = this; + + /* Add our spinner */ + $(this) + .after(spinner('gfpdf-spinner gfpdf-spinner-small')) + .parent() + .parent() + .attr('style', 'position:static; visibility: visible;'); + + if (id.length > 0) { + /* Set up ajax data */ + const data = { + action: 'gfpdf_list_duplicate', + nonce: $(this).data('nonce'), + fid: $(this).data('fid'), + pid: $(this).data('id'), + }; + + /* Do ajax call */ + ajaxCall(data, function (response) { + if (response.msg) { + /* Remove the spinner */ + $(that) + .parent() + .parent() + .attr('style', '') + .find('.gfpdf-spinner') + .remove(); + + /* Provide feedback to use */ + showMessage(response.msg); + + /* Clone the row to be duplicated */ + const $row = $(that).parents('tr'); + const $newRow = $row.clone(); + + /* Update the edit links to point to the new location */ + $newRow.find('.column-name > a, .edit a').each(function () { + let href = $(this).attr('href'); + href = updateURLParameter(href, 'pid', response.pid); + $(this).attr('href', href); + }); + + /* Update the name field */ + $newRow.find('.column-name > a').html(response.name); + + /* Find duplicate and delete elements */ + const $duplicate = $newRow.find('.duplicate a'); + const $delete = $newRow.find('.delete a'); + const $state = $newRow.find('.check-column button'); + const $shortcode = $newRow.find('.column-shortcode'); + + /* Update duplicate ID and nonce pointers so the actions are valid */ + $duplicate.data('id', response.pid); + $duplicate.data('nonce', response.dup_nonce); + + /* Update delete ID and nonce pointers so the actions are valid */ + $delete.data('id', response.pid); + $delete.data('nonce', response.del_nonce); + + /* update state ID and nonce pointers so the actions are valid */ + $state.data('id', response.pid); + $state.data('nonce', response.state_nonce); + + /* Set button data-status to inactive by default */ + $state[0].setAttribute('data-status', 'inactive'); + + /* Update our shortcode ID */ + let shortcodeValue = $shortcode + .find('button') + .attr('data-clipboard-text'); + shortcodeValue = shortcodeValue.replace(id, response.pid); + $shortcode + .find('button') + .attr('data-clipboard-text', shortcodeValue); + $shortcode + .find('input') + .attr('id', response.pid) + .attr('value', shortcodeValue); + + $state + .removeClass('gform-status--active') + .addClass('gform-status--inactive') + .find('.gform-status-indicator-status') + .html(response.status); + + /* Add row to node and fade in */ + $newRow.hide().insertAfter($row).fadeIn(); + } + }); + } + }); } diff --git a/src/assets/js/admin/settings/form/list/setupAJAXListStateListener.js b/src/assets/js/admin/settings/form/list/setupAJAXListStateListener.js index 9665c4d12..bd022b116 100644 --- a/src/assets/js/admin/settings/form/list/setupAJAXListStateListener.js +++ b/src/assets/js/admin/settings/form/list/setupAJAXListStateListener.js @@ -1,53 +1,53 @@ -import $ from 'jquery' -import { ajaxCall } from '../../../helper/ajaxCall' +import $ from 'jquery'; +import { ajaxCall } from '../../../helper/ajaxCall'; /** * Handles the state change of a PDF list item via AJAX - * @return void + * * @since 4.0 */ -export function setupAJAXListStateListener () { - /* Add live state listener to change active / inactive value */ - $('#gfpdf_list_form').on('click', '.check-column button', function () { - const id = String($(this).data('id')) - const button = $(this) - const label = button.find('span.gform-status-indicator-status') +export function setupAJAXListStateListener() { + /* Add live state listener to change active / inactive value */ + $('#gfpdf_list_form').on('click', '.check-column button', function () { + const id = String($(this).data('id')); + const button = $(this); + const label = button.find('span.gform-status-indicator-status'); - if (id.length > 0) { - button - .addClass('gform_status--pending') - .removeClass('gform-status--active gform-status--inactive') + if (id.length > 0) { + button + .addClass('gform_status--pending') + .removeClass('gform-status--active gform-status--inactive'); - /* Set up ajax data */ - const data = { - action: 'gfpdf_change_state', - nonce: $(this).data('nonce'), - fid: $(this).data('fid'), - pid: $(this).data('id') - } + /* Set up ajax data */ + const data = { + action: 'gfpdf_change_state', + nonce: $(this).data('nonce'), + fid: $(this).data('fid'), + pid: $(this).data('id'), + }; - /* Do ajax call */ - ajaxCall(data, function (data) { - label.html(data.state) + /* Do ajax call */ + ajaxCall(data, function (response) { + label.html(response.state); - if (button.data('status') === 'active') { - /* Set button data-status to inactive */ - button[0].setAttribute('data-status', 'inactive') + if (button.data('status') === 'active') { + /* Set button data-status to inactive */ + button[0].setAttribute('data-status', 'inactive'); - button - .data('status', 'inactive') - .removeClass('gform_status--pending') - .addClass('gform-status--inactive') - } else { - /* Set button data-status to active */ - button[0].setAttribute('data-status', 'active') + button + .data('status', 'inactive') + .removeClass('gform_status--pending') + .addClass('gform-status--inactive'); + } else { + /* Set button data-status to active */ + button[0].setAttribute('data-status', 'active'); - button - .data('status', 'active') - .removeClass('gform_status--pending') - .addClass('gform-status--active') - } - }) - } - }) + button + .data('status', 'active') + .removeClass('gform_status--pending') + .addClass('gform-status--active'); + } + }); + } + }); } diff --git a/src/assets/js/admin/settings/global/cleanupGFNavigation.js b/src/assets/js/admin/settings/global/cleanupGFNavigation.js index 7a6795713..bab878eda 100644 --- a/src/assets/js/admin/settings/global/cleanupGFNavigation.js +++ b/src/assets/js/admin/settings/global/cleanupGFNavigation.js @@ -1,17 +1,17 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Our &tab=(.+?) url param causes issues with the default GF navigation - * @return void + * * @since 4.0 */ -export function cleanupGFNavigation () { - const $nav = $('#gform_tabs a') +export function cleanupGFNavigation() { + const $nav = $('#gform_tabs a'); - $nav.each(function () { - const href = $(this).attr('href') - const regex = new RegExp('&tab=[^&;]*', 'g') // eslint-disable-line + $nav.each(function () { + const href = $(this).attr('href'); + const regex = new RegExp( '&tab=[^&;]*', 'g' ); // eslint-disable-line - $(this).attr('href', href.replace(regex, '')) - }) + $(this).attr('href', href.replace(regex, '')); + }); } diff --git a/src/assets/js/admin/settings/global/generalSettings.js b/src/assets/js/admin/settings/global/generalSettings.js index 9f12c292c..b87cbe319 100644 --- a/src/assets/js/admin/settings/global/generalSettings.js +++ b/src/assets/js/admin/settings/global/generalSettings.js @@ -1,30 +1,34 @@ -import $ from 'jquery' -import { setupRequiredFields } from '../pdf/setupRequiredFields' +import $ from 'jquery'; +import { setupRequiredFields } from '../pdf/setupRequiredFields'; /** * The general settings model method * This sets up and processes any of the JS that needs to be applied on the general settings tab - * @return void + * * @since 4.0 */ -export function generalSettings () { - setupRequiredFields($('#pdfextended-settings > form')) +export function generalSettings() { + setupRequiredFields($('#pdfextended-settings > form')); - const $table = $('#pdf-general-security') - const $adminRestrictions = $table.find('input[name="gfpdf_settings[default_restrict_owner]"]') + const $table = $('#pdf-general-security'); + const $adminRestrictions = $table.find( + 'input[name="gfpdf_settings[default_restrict_owner]"]' + ); - /* - * Add change event to admin restrictions to show/hide dependant fields - */ - $adminRestrictions.on('change', function () { - if ($(this).is(':checked')) { - if ($(this).val() === 'Yes') { - /* hide user restrictions and logged out user timeout */ - $table.find('tr:nth-child(3)').hide() - } else { - /* hide user restrictions and logged out user timeout */ - $table.find('tr:nth-child(3)').show() - } - } - }).trigger('change') + /* + * Add change event to admin restrictions to show/hide dependant fields + */ + $adminRestrictions + .on('change', function () { + if ($(this).is(':checked')) { + if ($(this).val() === 'Yes') { + /* hide user restrictions and logged out user timeout */ + $table.find('tr:nth-child(3)').hide(); + } else { + /* hide user restrictions and logged out user timeout */ + $table.find('tr:nth-child(3)').show(); + } + } + }) + .trigger('change'); } diff --git a/src/assets/js/admin/settings/initialiseSettings.js b/src/assets/js/admin/settings/initialiseSettings.js index e408eaceb..450a12038 100644 --- a/src/assets/js/admin/settings/initialiseSettings.js +++ b/src/assets/js/admin/settings/initialiseSettings.js @@ -1,82 +1,87 @@ -import $ from 'jquery' -import { initialiseCommonElements } from './common/initialiseCommonElements' -import { cleanupGFNavigation } from './global/cleanupGFNavigation' -import { generalSettings } from './global/generalSettings' -import { doFormSettingsListPage } from './form/doFormSettingsListPage' -import { doFormSettingsEditPage } from './pdf/doFormSettingsEditPage' -import { pages } from './pages' +import $ from 'jquery'; +import { initialiseCommonElements } from './common/initialiseCommonElements'; +import { cleanupGFNavigation } from './global/cleanupGFNavigation'; +import { generalSettings } from './global/generalSettings'; +import { doFormSettingsListPage } from './form/doFormSettingsListPage'; +import { doFormSettingsEditPage } from './pdf/doFormSettingsEditPage'; +import { pages } from './pages'; /** * Process the correct settings area (the global PDF settings or individual form PDF settings) * Also set up any event listeners needed - * @return void + * * @since 4.0 */ class InitialiseSettings { - init () { - /* Process any common functions */ - initialiseCommonElements.runElements() + init() { + /* Process any common functions */ + initialiseCommonElements.runElements(); - /* Process the global PDF settings */ - if (pages.isSettings()) { - this.processSettings() - } + /* Process the global PDF settings */ + if (pages.isSettings()) { + this.processSettings(); + } - /* Process the individual form PDF settings */ - if (pages.isFormSettings()) { - this.processFormSettings() - } + /* Process the individual form PDF settings */ + if (pages.isFormSettings()) { + this.processFormSettings(); + } - /* Prevent submission when selecting a setting tooltip button */ - $('.gfpdf-tooltip').on('click', () => false) - .on('keypress', () => false) - } + /* Prevent submission when selecting a setting tooltip button */ + $('.gfpdf-tooltip') + .on('click', () => false) + .on('keypress', () => false); + } - /** - * Check the current active PDF settings page - * @return String - * @since 4.0 - */ - getCurrentSettingsPage () { - if (pages.isSettings()) { - return $('.gform-settings-tabs__navigation a.active:first').data('id') - } - return '' - } + /** + * Check the current active PDF settings page + * + * @return {string} current settings page + * + * @since 4.0 + */ + getCurrentSettingsPage() { + if (pages.isSettings()) { + return $('.gform-settings-tabs__navigation a.active:first').data( + 'id' + ); + } + return ''; + } - /** - * Process the global settings page - * @return void - * @since 4.0 - */ - processSettings () { - /* Ensure the Gravity Forms settings navigation (Form Settings / Notifications / Confirmation) has the 'tab' URI stripped from it */ - cleanupGFNavigation() + /** + * Process the global settings page + * + * @since 4.0 + */ + processSettings() { + /* Ensure the Gravity Forms settings navigation (Form Settings / Notifications / Confirmation) has the 'tab' URI stripped from it */ + cleanupGFNavigation(); - /* Run the appropriate settings page */ - switch (this.getCurrentSettingsPage()) { - case 'general': - generalSettings() - break - } - } + /* Run the appropriate settings page */ + switch (this.getCurrentSettingsPage()) { + case 'general': + generalSettings(); + break; + } + } - /** - * Routing functionality for the individual form settings page - * @return void - * @since 4.0 - */ - processFormSettings () { - /* Process PDF list page */ - if (pages.isFormSettingsList()) { - doFormSettingsListPage.setupAJAXListListener() - } + /** + * Routing functionality for the individual form settings page + * + * @since 4.0 + */ + processFormSettings() { + /* Process PDF list page */ + if (pages.isFormSettingsList()) { + doFormSettingsListPage.setupAJAXListListener(); + } - /* Process single edit page */ - if (pages.isFormSettingsEdit()) { - doFormSettingsEditPage() - } - } + /* Process single edit page */ + if (pages.isFormSettingsEdit()) { + doFormSettingsEditPage(); + } + } } -export const initialiseSettings = new InitialiseSettings() +export const initialiseSettings = new InitialiseSettings(); diff --git a/src/assets/js/admin/settings/pages.js b/src/assets/js/admin/settings/pages.js index b998d7783..23b282ab6 100644 --- a/src/assets/js/admin/settings/pages.js +++ b/src/assets/js/admin/settings/pages.js @@ -1,41 +1,49 @@ -import $ from 'jquery' +import $ from 'jquery'; class Pages { - /** - * Get if on the global PDF settings pages - * @return Integer - * @since 4.0 - */ - isSettings () { - return $('#pdfextended-settings').length - } + /** + * Get if on the global PDF settings pages + * + * @return { number } global PDF settings length + * + * @since 4.0 + */ + isSettings() { + return $('#pdfextended-settings').length; + } - /** - * Check if on the individual PDF form settings pages - * @return Integer - * @since 4.0 - */ - isFormSettings () { - return $('.gforms_edit_form').length - } + /** + * Check if on the individual PDF form settings pages + * + * @return { number } individual PDF form settings length + * + * @since 4.0 + */ + isFormSettings() { + return $('.gforms_edit_form').length; + } - /** - * See if we are on the form settings list page - * @return Integer - * @since 4.0 - */ - isFormSettingsList () { - return $('#gfpdf_list_form').length - } + /** + * See if we are on the form settings list page + * + * @return { number } form list settings length + * + * @since 4.0 + */ + isFormSettingsList() { + return $('#gfpdf_list_form').length; + } - /** - * See if we are on the form settings edit page - * @return Integer - * @since 4.0 - */ - isFormSettingsEdit () { - return $('#gfpdf_pdf_form').length - } + /** + * See if we are on the form settings edit page + * + * @return { number } PDF form settings length + * + * @since 4.0 + */ + isFormSettingsEdit() { + return $('#gfpdf_pdf_form').length; + } } -export const pages = new Pages() +export const pages = new Pages(); diff --git a/src/assets/js/admin/settings/pdf/doFormSettingsEditPage.js b/src/assets/js/admin/settings/pdf/doFormSettingsEditPage.js index 20286ed6e..cb0923fee 100644 --- a/src/assets/js/admin/settings/pdf/doFormSettingsEditPage.js +++ b/src/assets/js/admin/settings/pdf/doFormSettingsEditPage.js @@ -1,57 +1,68 @@ -import $ from 'jquery' -import { setupRequiredFields } from './setupRequiredFields' -import { handleSecurityConditionals } from './handleSecurityConditionals' -import { handlePDFConditionalLogic } from './handlePDFConditionalLogic' -import { handleOwnerRestriction } from './handleOwnerRestriction' -import { toggleFontAppearance } from './toggleFontAppearance' -import { toggleAppearanceTab } from './toggleAppearanceTab' - -export function doFormSettingsEditPage () { - setupRequiredFields($('#gfpdf_pdf_form')) - - /* highlight which fields are required and disable in-browser validation */ - handleSecurityConditionals() - handlePDFConditionalLogic() - handleOwnerRestriction() - toggleFontAppearance($('#gfpdf_settings\\[template\\]').data('template_group')) - toggleAppearanceTab() - - /* Add better merge tag support */ - $('.gform-settings-field').each(function () { - $(this) - .find('.merge-tag-support, .merge-tag-support + span') - .wrapAll('
') - - $(this) - .find('.all-merge-tags.textarea') - .parent() - .wrapAll('
') - }) - - /* Hide Template section, if empty */ - const $templateSection = $('#gfpdf-fieldset-gfpdf_form_settings_template') - if ($templateSection.find('.gform-settings-panel__content').children().length === 0) { - $templateSection.hide() - } - - /* Move alert inline */ - $('.gform-settings__wrapper > .alert').detach().prependTo('#tab_PDF') - - /* - * Workaround for Firefix TinyMCE Editor Bug NS_ERROR_UNEXPECTED (http://www.tinymce.com/develop/bugtracker_view.php?id=3152) when loading wp_editor via AJAX - * Manual save TinyMCE editors on form submission - */ - $('#gfpdf_pdf_form').on('submit', function () { - try { - tinyMCE.triggerSave() - } catch (e) { - // empty - } - }) - - /* Add listener on submit functionality */ - $('#gfpdf_pdf_form').on('submit', function () { - /* JSONify the conditional logic so we can pass it through the form and use it in PHP (after running json_decode) */ - $('#gfpdf_settings\\[conditionalLogic\\]').val($.toJSON(window.gfpdf_current_pdf.conditionalLogic)) - }) +import $ from 'jquery'; +import { setupRequiredFields } from './setupRequiredFields'; +import { handleSecurityConditionals } from './handleSecurityConditionals'; +import { handlePDFConditionalLogic } from './handlePDFConditionalLogic'; +import { handleOwnerRestriction } from './handleOwnerRestriction'; +import { toggleFontAppearance } from './toggleFontAppearance'; +import { toggleAppearanceTab } from './toggleAppearanceTab'; + +export function doFormSettingsEditPage() { + setupRequiredFields($('#gfpdf_pdf_form')); + + /* highlight which fields are required and disable in-browser validation */ + handleSecurityConditionals(); + handlePDFConditionalLogic(); + handleOwnerRestriction(); + toggleFontAppearance( + $('#gfpdf_settings\\[template\\]').data('template_group') + ); + toggleAppearanceTab(); + + /* Add better merge tag support */ + $('.gform-settings-field').each(function () { + $(this) + .find('.merge-tag-support, .merge-tag-support + span') + .wrapAll( + '
' + ); + + $(this) + .find('.all-merge-tags.textarea') + .parent() + .wrapAll( + '
' + ); + }); + + /* Hide Template section, if empty */ + const $templateSection = $('#gfpdf-fieldset-gfpdf_form_settings_template'); + if ( + $templateSection.find('.gform-settings-panel__content').children() + .length === 0 + ) { + $templateSection.hide(); + } + + /* Move alert inline */ + $('.gform-settings__wrapper > .alert').detach().prependTo('#tab_PDF'); + + /* + * Workaround for Firefix TinyMCE Editor Bug NS_ERROR_UNEXPECTED (http://www.tinymce.com/develop/bugtracker_view.php?id=3152) when loading wp_editor via AJAX + * Manual save TinyMCE editors on form submission + */ + $('#gfpdf_pdf_form').on('submit', function () { + try { + tinyMCE.triggerSave(); + } catch (e) { + // empty + } + }); + + /* Add listener on submit functionality */ + $('#gfpdf_pdf_form').on('submit', function () { + /* JSONify the conditional logic so we can pass it through the form and use it in PHP (after running json_decode) */ + $('#gfpdf_settings\\[conditionalLogic\\]').val( + $.toJSON(window.gfpdf_current_pdf.conditionalLogic) + ); + }); } diff --git a/src/assets/js/admin/settings/pdf/handleOwnerRestriction.js b/src/assets/js/admin/settings/pdf/handleOwnerRestriction.js index 53cc51c60..02eabf5e4 100644 --- a/src/assets/js/admin/settings/pdf/handleOwnerRestriction.js +++ b/src/assets/js/admin/settings/pdf/handleOwnerRestriction.js @@ -1,22 +1,26 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Show / Hide the Restrict Owner when `Enable Public Access` is set to "Yes" * @since 4.0 */ -export function handleOwnerRestriction () { - const $table = $('#gfpdf-fieldset-gfpdf_form_settings_advanced') - const $publicAccess = $table.find('input[name="gfpdf_settings[public_access]"]') - const $restrictOwner = $('#gfpdf-settings-field-wrapper-restrict_owner') +export function handleOwnerRestriction() { + const $table = $('#gfpdf-fieldset-gfpdf_form_settings_advanced'); + const $publicAccess = $table.find( + 'input[name="gfpdf_settings[public_access]"]' + ); + const $restrictOwner = $('#gfpdf-settings-field-wrapper-restrict_owner'); - /* - * Add change event to admin restrictions to show/hide dependant fields - */ - $publicAccess.on('change', function () { - if ($(this).is(':checked')) { - $restrictOwner.hide() - } else { - $restrictOwner.show() - } - }).trigger('change') + /* + * Add change event to admin restrictions to show/hide dependant fields + */ + $publicAccess + .on('change', function () { + if ($(this).is(':checked')) { + $restrictOwner.hide(); + } else { + $restrictOwner.show(); + } + }) + .trigger('change'); } diff --git a/src/assets/js/admin/settings/pdf/handlePDFConditionalLogic.js b/src/assets/js/admin/settings/pdf/handlePDFConditionalLogic.js index cdda7bc09..3b31b1dd0 100644 --- a/src/assets/js/admin/settings/pdf/handlePDFConditionalLogic.js +++ b/src/assets/js/admin/settings/pdf/handlePDFConditionalLogic.js @@ -1,73 +1,100 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Add GF JS filter to change the conditional logic object type to our PDF - * @return Object + * * @since 4.0 */ -export function handlePDFConditionalLogic () { - gform.addFilter('gform_conditional_object', function (obj, objectType) { - if (objectType === 'gfpdf') { - obj = window.gfpdf_current_pdf +export function handlePDFConditionalLogic() { + gform.addFilter('gform_conditional_object', function (obj, objectType) { + if (objectType === 'gfpdf') { + obj = window.gfpdf_current_pdf; - /* Manually setup new conditional logic object, with fallback to entry metadata if no available fields present */ - if (!obj.conditionalLogic || obj.conditionalLogic.length === 0) { - obj.conditionalLogic = new ConditionalLogic() - obj.conditionalLogic.rules[0].fieldId = GetFirstRuleField() - if (obj.conditionalLogic.rules[0].fieldId === 0) { - obj.conditionalLogic.rules[0].fieldId = 'id' - } - } - } - return obj - }) + /* Manually setup new conditional logic object, with fallback to entry metadata if no available fields present */ + if (!obj.conditionalLogic || obj.conditionalLogic.length === 0) { + obj.conditionalLogic = new ConditionalLogic(); + obj.conditionalLogic.rules[0].fieldId = GetFirstRuleField(); + if (obj.conditionalLogic.rules[0].fieldId === 0) { + obj.conditionalLogic.rules[0].fieldId = 'id'; + } + } + } + return obj; + }); - /* Add support for entry meta */ - const entryOptions = window.gfpdf_extra_conditional_logic_options - gform.addFilter('gform_conditional_logic_fields', function (options, form, selectedFieldId) { - for (const property in entryOptions) { - // Entry meta are already added in Notifications and Confirmations conditional logic but not in feeds. - // Let's just make sure that none of our entry meta options have been previously added. - if (Object.hasOwn(entryOptions, property) && !options.find(opt => opt.value === entryOptions[property].value)) { - options.push({ - label: entryOptions[property].label, - value: entryOptions[property].value - }) - } - } - return options - }) + /* Add support for entry meta */ + const entryOptions = window.gfpdf_extra_conditional_logic_options; + gform.addFilter('gform_conditional_logic_fields', function (options) { + for (const property in entryOptions) { + // Entry meta are already added in Notifications and Confirmations conditional logic but not in feeds. + // Let's just make sure that none of our entry meta options have been previously added. + if ( + Object.hasOwn(entryOptions, property) && + !options.find( + (opt) => opt.value === entryOptions[property].value + ) + ) { + options.push({ + label: entryOptions[property].label, + value: entryOptions[property].value, + }); + } + } + return options; + }); - gform.addFilter('gform_conditional_logic_operators', function (operators, objectType, fieldId) { - if (Object.hasOwn(entryOptions, fieldId)) { - operators = entryOptions[fieldId].operators - } - return operators - }) + gform.addFilter( + 'gform_conditional_logic_operators', + function (operators, objectType, fieldId) { + if (Object.hasOwn(entryOptions, fieldId)) { + operators = entryOptions[fieldId].operators; + } + return operators; + } + ); - gform.addFilter('gform_conditional_logic_values_input', function (str, objectType, ruleIndex, selectedFieldId, selectedValue) { - if (Object.hasOwn(entryOptions, selectedFieldId)) { - if (entryOptions[selectedFieldId].choices) { - const inputName = objectType + '_rule_value_' + ruleIndex - str = GetRuleValuesDropDown(entryOptions[selectedFieldId].choices, objectType, ruleIndex, selectedValue, inputName) - } + gform.addFilter( + 'gform_conditional_logic_values_input', + function (str, objectType, ruleIndex, selectedFieldId, selectedValue) { + if (Object.hasOwn(entryOptions, selectedFieldId)) { + if (entryOptions[selectedFieldId].choices) { + const inputName = objectType + '_rule_value_' + ruleIndex; + str = GetRuleValuesDropDown( + entryOptions[selectedFieldId].choices, + objectType, + ruleIndex, + selectedValue, + inputName + ); + } - if (entryOptions[selectedFieldId].placeholder) { - str = $(str).attr('placeholder', entryOptions[selectedFieldId].placeholder)[0].outerHTML - } - } + if (entryOptions[selectedFieldId].placeholder) { + str = $(str).attr( + 'placeholder', + entryOptions[selectedFieldId].placeholder + )[0].outerHTML; + } + } - return str - }) + return str; + } + ); - /* Add change event to conditional logic field */ - $('#gfpdf_conditional_logic').on('change', function () { - /* Only set up a .conditionalLogic object if it doesn't exist */ - if (typeof window.gfpdf_current_pdf.conditionalLogic === 'undefined' && $(this).prop('checked')) { - window.gfpdf_current_pdf.conditionalLogic = new ConditionalLogic() - } else if (!$(this).prop('checked')) { - window.gfpdf_current_pdf.conditionalLogic = null - } - ToggleConditionalLogic(false, 'gfpdf') - }).trigger('change') + /* Add change event to conditional logic field */ + $('#gfpdf_conditional_logic') + .on('change', function () { + /* Only set up a .conditionalLogic object if it doesn't exist */ + if ( + typeof window.gfpdf_current_pdf.conditionalLogic === + 'undefined' && + $(this).prop('checked') + ) { + window.gfpdf_current_pdf.conditionalLogic = + new ConditionalLogic(); + } else if (!$(this).prop('checked')) { + window.gfpdf_current_pdf.conditionalLogic = null; + } + ToggleConditionalLogic(false, 'gfpdf'); + }) + .trigger('change'); } diff --git a/src/assets/js/admin/settings/pdf/handleSecurityConditionals.js b/src/assets/js/admin/settings/pdf/handleSecurityConditionals.js index 4e76255ba..e04e31185 100644 --- a/src/assets/js/admin/settings/pdf/handleSecurityConditionals.js +++ b/src/assets/js/admin/settings/pdf/handleSecurityConditionals.js @@ -1,46 +1,56 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Handles our DOM security conditional logic based on the user selection - * @return void + * * @since 4.0 */ -export function handleSecurityConditionals () { - /* Get the appropriate elements for use */ - const $secTable = $('#gfpdf-fieldset-gfpdf_form_settings_advanced') - const $pdfSecurity = $secTable.find('input[name="gfpdf_settings[security]"]') - const $format = $secTable.find('input[name="gfpdf_settings[format]"]') - const $securityQuestion = $secTable.find('#gfpdf-settings-field-wrapper-security') - const $securityFields = $secTable.find('#gfpdf-settings-field-wrapper-password,#gfpdf-settings-field-wrapper-privileges,#gfpdf-settings-field-wrapper-master_password:not(.gfpdf-hidden)') +export function handleSecurityConditionals() { + /* Get the appropriate elements for use */ + const $secTable = $('#gfpdf-fieldset-gfpdf_form_settings_advanced'); + const $pdfSecurity = $secTable.find( + 'input[name="gfpdf_settings[security]"]' + ); + const $format = $secTable.find('input[name="gfpdf_settings[format]"]'); + const $securityQuestion = $secTable.find( + '#gfpdf-settings-field-wrapper-security' + ); + const $securityFields = $secTable.find( + '#gfpdf-settings-field-wrapper-password,#gfpdf-settings-field-wrapper-privileges,#gfpdf-settings-field-wrapper-master_password:not(.gfpdf-hidden)' + ); - /* Add change event to admin restrictions to show/hide dependant fields */ - $pdfSecurity.on('change', function () { - /* Get the format dependency */ - const format = $format.filter(':checked').val() + /* Add change event to admin restrictions to show/hide dependant fields */ + $pdfSecurity + .on('change', function () { + /* Get the format dependency */ + const format = $format.filter(':checked').val(); - if ($(this).val() === 'No' || format !== 'Standard') { - /* hide security password / privileges */ - $securityFields.hide() - } else { - /* Show/hide security password / privileges fields under 'Enable PDF Security' */ - if ($(this).is(':checked')) { - $securityFields.show() - } else { - $securityFields.hide() - } - } + if ($(this).val() === 'No' || format !== 'Standard') { + /* hide security password / privileges */ + $securityFields.hide(); + } else { + /* Show/hide security password / privileges fields under 'Enable PDF Security' */ - if (format !== 'Standard') { - $securityQuestion.hide() - } else { - $securityQuestion.show() - } - }).trigger('change') + // eslint-disable-next-line no-unused-expressions + $(this).is(':checked') + ? $securityFields.show() + : $securityFields.hide(); + } - /* The format field effects the security field. When it changes it triggers the security field as changed */ - $format.on('change', function () { - if ($(this).is(':checked')) { - $pdfSecurity.trigger('change') - } - }).trigger('change') + if (format !== 'Standard') { + $securityQuestion.hide(); + } else { + $securityQuestion.show(); + } + }) + .trigger('change'); + + /* The format field effects the security field. When it changes it triggers the security field as changed */ + $format + .on('change', function () { + if ($(this).is(':checked')) { + $pdfSecurity.trigger('change'); + } + }) + .trigger('change'); } diff --git a/src/assets/js/admin/settings/pdf/setupRequiredFields.js b/src/assets/js/admin/settings/pdf/setupRequiredFields.js index 0269077c4..d56614048 100644 --- a/src/assets/js/admin/settings/pdf/setupRequiredFields.js +++ b/src/assets/js/admin/settings/pdf/setupRequiredFields.js @@ -1,27 +1,33 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Enable dynamic required fields on the Gravity Forms PDF Settings page * This function will highlight to the user which fields should be processed, and disable in-browser validation - * @return void + * + * @param { jQuery } $elm + * * @since 4.0 */ -export function setupRequiredFields ($elm) { - /* prevent in browser validation */ - $elm.attr('novalidate', 'novalidate') +export function setupRequiredFields($elm) { + /* prevent in browser validation */ + $elm.attr('novalidate', 'novalidate'); - /* gf compatibility + disable automatic field validation */ - $elm.find('input[type="submit"]').on('click', function () { - $elm.addClass('formSubmitted') - }) + /* gf compatibility + disable automatic field validation */ + $elm.find('input[type="submit"]').on('click', function () { + $elm.addClass('formSubmitted'); + }); - /* add the required star to make it easier for users */ - $elm.find(':input[required=""], :input[required]').each(function () { - const $container = $(this).parent() - if ($container.find('.gform-settings-panel__title a').length) { - $container.find('.gform-settings-panel__title a').before('(required)') - } else { - $container.find('.gform-settings-panel__title').append('(required)') - } - }) + /* add the required star to make it easier for users */ + $elm.find(':input[required=""], :input[required]').each(function () { + const $container = $(this).parent(); + if ($container.find('.gform-settings-panel__title a').length) { + $container + .find('.gform-settings-panel__title a') + .before('(required)'); + } else { + $container + .find('.gform-settings-panel__title') + .append('(required)'); + } + }); } diff --git a/src/assets/js/admin/settings/pdf/toggleAppearanceTab.js b/src/assets/js/admin/settings/pdf/toggleAppearanceTab.js index f2f80d671..9ab17a602 100644 --- a/src/assets/js/admin/settings/pdf/toggleAppearanceTab.js +++ b/src/assets/js/admin/settings/pdf/toggleAppearanceTab.js @@ -1,19 +1,26 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Check if the current PDF template selection uses the legacy Enable Advanced Templating option * and hide the Appearance tab altogether * @since 4.0 */ -export function toggleAppearanceTab () { - const $appearanceSection = $('#gfpdf-fieldset-gfpdf_form_settings_appearance') - $('input[name="gfpdf_settings[advanced_template]"]').on('change', function () { - if ($(this).val() === 'Yes') { - $appearanceSection.hide() - } else { - $appearanceSection.show() - } - }) +export function toggleAppearanceTab() { + const $appearanceSection = $( + '#gfpdf-fieldset-gfpdf_form_settings_appearance' + ); + $('input[name="gfpdf_settings[advanced_template]"]').on( + 'change', + function () { + if ($(this).val() === 'Yes') { + $appearanceSection.hide(); + } else { + $appearanceSection.show(); + } + } + ); - $('input[name="gfpdf_settings[advanced_template]"]:checked').trigger('change') + $('input[name="gfpdf_settings[advanced_template]"]:checked').trigger( + 'change' + ); } diff --git a/src/assets/js/admin/settings/pdf/toggleFontAppearance.js b/src/assets/js/admin/settings/pdf/toggleFontAppearance.js index 3c1ecfacd..fd1df3cf4 100644 --- a/src/assets/js/admin/settings/pdf/toggleFontAppearance.js +++ b/src/assets/js/admin/settings/pdf/toggleFontAppearance.js @@ -1,17 +1,22 @@ -import $ from 'jquery' +import $ from 'jquery'; /** * Check if the template type is 'legacy' and hide the font type, size and colour, otherwise show those fields - * @param type + * + * @param { string } type + * * @since 4.0 */ -export function toggleFontAppearance (type) { - const $rows = $('#gfpdf-settings-field-wrapper-font, #gfpdf-settings-field-wrapper-font_size, #gfpdf-settings-field-wrapper-font_colour') +export function toggleFontAppearance(type) { + const $rows = $( + '#gfpdf-settings-field-wrapper-font, #gfpdf-settings-field-wrapper-font_size, #gfpdf-settings-field-wrapper-font_colour' + ); - /* Hide our font fields if processing a legacy template */ - if (type === 'legacy') { - $rows.hide() - } else { /* Ensure the fields are showing */ - $rows.show() - } + /* Hide our font fields if processing a legacy template */ + if (type === 'legacy') { + $rows.hide(); + } else { + /* Ensure the fields are showing */ + $rows.show(); + } } diff --git a/src/assets/js/legacy/gfpdf-entries.js b/src/assets/js/legacy/gfpdf-entries.js index ed03b2646..d59c185d6 100644 --- a/src/assets/js/legacy/gfpdf-entries.js +++ b/src/assets/js/legacy/gfpdf-entries.js @@ -1,46 +1,49 @@ /** * Gravity PDF Entries * Dependancies: jQuery + * + * @param { jQuery } $ + * * @since 4.0 */ (function ($) { - /** - * Fires on the Document Ready Event - */ - $(function () { - let timer = null - $('.gfpdf_form_action_has_submenu > a') - /* Handle keyboard navigation */ - .on('click', function () { - if ($(this).attr('aria-expanded') === 'false') { - $(this).parent().addClass('open') - $(this).attr('aria-expanded', 'true') - } else { - $(this).parent().removeClass('open') - $(this).attr('aria-expanded', 'false') - } + /** + * Fires on the Document Ready Event + */ + $(function () { + let timer = null; + $('.gfpdf_form_action_has_submenu > a') + /* Handle keyboard navigation */ + .on('click', function () { + if ($(this).attr('aria-expanded') === 'false') { + $(this).parent().addClass('open'); + $(this).attr('aria-expanded', 'true'); + } else { + $(this).parent().removeClass('open'); + $(this).attr('aria-expanded', 'false'); + } - return false - }) - .parent() - /* Hide submenu after a delay */ - .on('mouseover', function () { - clearTimeout(timer) + return false; + }) + .parent() + /* Hide submenu after a delay */ + .on('mouseover', function () { + clearTimeout(timer); - $(this) - .addClass('open') - .find('> a') - .attr('aria-expanded', 'true') - }) - .on('mouseout', function () { - const $submenu = $(this) - timer = setTimeout(function () { - $submenu - .removeClass('open') - .find('> a') - .attr('aria-expanded', 'false') - }, 1000) - }) - }) -})(jQuery) + $(this) + .addClass('open') + .find('> a') + .attr('aria-expanded', 'true'); + }) + .on('mouseout', function () { + const $submenu = $(this); + timer = setTimeout(function () { + $submenu + .removeClass('open') + .find('> a') + .attr('aria-expanded', 'false'); + }, 1000); + }); + }); +})(jQuery); diff --git a/src/assets/js/react/actions/coreFonts.js b/src/assets/js/react/actions/coreFonts.js index 5c283e416..33b8b0e1a 100644 --- a/src/assets/js/react/actions/coreFonts.js +++ b/src/assets/js/react/actions/coreFonts.js @@ -1,19 +1,20 @@ /* Redux Action Type Constants */ -export const ADD_TO_CONSOLE = 'ADD_TO_CONSOLE' -export const ADD_TO_RETRY_LIST = 'ADD_TO_RETRY_LIST' -export const CLEAR_CONSOLE = 'CLEAR_CONSOLE' -export const CLEAR_BUTTON_CLICKED_AND_RETRY_LIST = 'CLEAR_BUTTON_CLICKED_AND_RETRY_LIST' -export const GET_FILES_FROM_GITHUB = 'GET_FILES_FROM_GITHUB' -export const GET_FILES_FROM_GITHUB_SUCCESS = 'GET_FILES_FROM_GITHUB_SUCCESS' -export const GET_FILES_FROM_GITHUB_FAILED = 'GET_FILES_FROM_GITHUB_FAILED' -export const DOWNLOAD_FONTS_API_CALL = 'DOWNLOAD_FONTS_API_CALL' -export const REQUEST_SENT_COUNTER = 'REQUEST_SENT_COUNTER' -export const CLEAR_REQUEST_REMAINING_DATA = 'CLEAR_REQUEST_REMAINING_DATA' +export const ADD_TO_CONSOLE = 'ADD_TO_CONSOLE'; +export const ADD_TO_RETRY_LIST = 'ADD_TO_RETRY_LIST'; +export const CLEAR_CONSOLE = 'CLEAR_CONSOLE'; +export const CLEAR_BUTTON_CLICKED_AND_RETRY_LIST = + 'CLEAR_BUTTON_CLICKED_AND_RETRY_LIST'; +export const GET_FILES_FROM_GITHUB = 'GET_FILES_FROM_GITHUB'; +export const GET_FILES_FROM_GITHUB_SUCCESS = 'GET_FILES_FROM_GITHUB_SUCCESS'; +export const GET_FILES_FROM_GITHUB_FAILED = 'GET_FILES_FROM_GITHUB_FAILED'; +export const DOWNLOAD_FONTS_API_CALL = 'DOWNLOAD_FONTS_API_CALL'; +export const REQUEST_SENT_COUNTER = 'REQUEST_SENT_COUNTER'; +export const CLEAR_REQUEST_REMAINING_DATA = 'CLEAR_REQUEST_REMAINING_DATA'; /** * Redux Actions - payloads of information that send data from your application to your store * - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.0 @@ -22,144 +23,148 @@ export const CLEAR_REQUEST_REMAINING_DATA = 'CLEAR_REQUEST_REMAINING_DATA' /** * Adds a message to our buffer for display to the user * - * @param key - * @param status - * @param message + * @param { string } key + * @param { string } status + * @param { Object } message * - * @returns {{type, key: *, status: *, message: *}} + * @return {{ type: string, key: string, status: string, message: Object}} action object * * @since 5.0 */ export const addToConsole = (key, status, message) => { - return { - type: ADD_TO_CONSOLE, - key, - status, - message - } -} + return { + type: ADD_TO_CONSOLE, + key, + status, + message, + }; +}; /** * Clears the message buffer * - * @returns {{type}} + * @return {{ type: string }} action object * * @since 5.0 */ export const clearConsole = () => { - return { - type: CLEAR_CONSOLE - } -} + return { + type: CLEAR_CONSOLE, + }; +}; /** * Adds a font to our retry list * - * @param name - * @returns {{type, name: *}} + * @param { string } name + * + * @return {{ type: string, name: string }} action object * * @since 5.0 */ export const addToRetryList = (name) => { - return { - type: ADD_TO_RETRY_LIST, - name - } -} + return { + type: ADD_TO_RETRY_LIST, + name, + }; +}; /** * Clears our retry list * - * @returns {{type}} + * @return {{ type }} action object * * @since 5.0 */ export const clearButtonClickedAndRetryList = () => { - return { - type: CLEAR_BUTTON_CLICKED_AND_RETRY_LIST - } -} + return { + type: CLEAR_BUTTON_CLICKED_AND_RETRY_LIST, + }; +}; /** * Request call to API * - * @returns {{type}} + * @return {{ type: string }} action object * * @since 5.2 */ export const getFilesFromGitHub = () => { - return { - type: GET_FILES_FROM_GITHUB - } -} + return { + type: GET_FILES_FROM_GITHUB, + }; +}; /** * Get success data from API call * - * @param files - * @returns {{type, payload: (Array)}} + * @param { Array } files + * + * @return {{ type: string, payload: Array }} action object * * @since 5.2 */ -export const getFilesFromGitHubSuccess = files => { - return { - type: GET_FILES_FROM_GITHUB_SUCCESS, - payload: files - } -} +export const getFilesFromGitHubSuccess = (files) => { + return { + type: GET_FILES_FROM_GITHUB_SUCCESS, + payload: files, + }; +}; /** * Get error data from failed API call * - * @param error - * @returns {{type, payload: (Object)}} + * @param { Object } error + * + * @return {{ type: string, payload: Object }} action object * * @since 5.2 */ -export const getFilesFromGitHubFailed = error => { - return { - type: GET_FILES_FROM_GITHUB_FAILED, - payload: error - } -} +export const getFilesFromGitHubFailed = (error) => { + return { + type: GET_FILES_FROM_GITHUB_FAILED, + payload: error, + }; +}; /** * Get success file name from API call * - * @param file - * @returns {{type, payload: (String)}} + * @param { string } file + * + * @return {{ type: string, payload: string }} action object * * @since 5.2 */ -export const downloadFontsApiCall = file => { - return { - type: DOWNLOAD_FONTS_API_CALL, - payload: file - } -} +export const downloadFontsApiCall = (file) => { + return { + type: DOWNLOAD_FONTS_API_CALL, + payload: file, + }; +}; /** * Request data into our Redux store for getting queue length value * - * @returns {{type}} + * @return {{ type: string }} action object * * @since 5.2 */ export const currentDownload = () => { - return { - type: REQUEST_SENT_COUNTER - } -} + return { + type: REQUEST_SENT_COUNTER, + }; +}; /** * Clear/reset store 'requestDownload' state * - * @returns {{type}} + * @return {{ type: string }} action object * * @since 5.2 */ export const clearRequestRemainingData = () => { - return { - type: CLEAR_REQUEST_REMAINING_DATA - } -} + return { + type: CLEAR_REQUEST_REMAINING_DATA, + }; +}; diff --git a/src/assets/js/react/actions/fontManager.js b/src/assets/js/react/actions/fontManager.js index 547f7417b..dda24c3d3 100644 --- a/src/assets/js/react/actions/fontManager.js +++ b/src/assets/js/react/actions/fontManager.js @@ -1,27 +1,27 @@ /* Redux action types */ -export const GET_CUSTOM_FONT_LIST = 'GET_CUSTOM_FONT_LIST' -export const GET_CUSTOM_FONT_LIST_SUCCESS = 'GET_CUSTOM_FONT_LIST_SUCCESS' -export const GET_CUSTOM_FONT_LIST_ERROR = 'GET_CUSTOM_FONT_LIST_ERROR' -export const ADD_FONT = 'ADD_FONT' -export const ADD_FONT_SUCCESS = 'ADD_FONT_SUCCESS' -export const ADD_FONT_ERROR = 'ADD_FONT_ERROR' -export const EDIT_FONT = 'EDIT_FONT' -export const EDIT_FONT_SUCCESS = 'EDIT_FONT_SUCCESS' -export const EDIT_FONT_ERROR = 'EDIT_FONT_ERROR' -export const VALIDATION_ERROR = 'VALIDATION_ERROR' -export const DELETE_VARIANT_ERROR = 'DELETE_VARIANT_ERROR' -export const DELETE_FONT = 'DELETE_FONT' -export const DELETE_FONT_SUCCESS = 'DELETE_FONT_SUCCESS' -export const DELETE_FONT_ERROR = 'DELETE_FONT_ERROR' -export const CLEAR_ADD_FONT_MSG = 'CLEAR_ADD_FONT_MSG' -export const CLEAR_DROPZONE_ERROR = 'CLEAR_DROPZONE_ERROR' -export const RESET_SEARCH_RESULT = 'RESET_SEARCH_RESULT' -export const SEARCH_FONT_LIST = 'SEARCH_FONT_LIST' -export const SELECT_FONT = 'SELECT_FONT' -export const MOVE_SELECTED_FONT_TO_TOP = 'MOVE_SELECTED_FONT_TO_TOP' +export const GET_CUSTOM_FONT_LIST = 'GET_CUSTOM_FONT_LIST'; +export const GET_CUSTOM_FONT_LIST_SUCCESS = 'GET_CUSTOM_FONT_LIST_SUCCESS'; +export const GET_CUSTOM_FONT_LIST_ERROR = 'GET_CUSTOM_FONT_LIST_ERROR'; +export const ADD_FONT = 'ADD_FONT'; +export const ADD_FONT_SUCCESS = 'ADD_FONT_SUCCESS'; +export const ADD_FONT_ERROR = 'ADD_FONT_ERROR'; +export const EDIT_FONT = 'EDIT_FONT'; +export const EDIT_FONT_SUCCESS = 'EDIT_FONT_SUCCESS'; +export const EDIT_FONT_ERROR = 'EDIT_FONT_ERROR'; +export const VALIDATION_ERROR = 'VALIDATION_ERROR'; +export const DELETE_VARIANT_ERROR = 'DELETE_VARIANT_ERROR'; +export const DELETE_FONT = 'DELETE_FONT'; +export const DELETE_FONT_SUCCESS = 'DELETE_FONT_SUCCESS'; +export const DELETE_FONT_ERROR = 'DELETE_FONT_ERROR'; +export const CLEAR_ADD_FONT_MSG = 'CLEAR_ADD_FONT_MSG'; +export const CLEAR_DROPZONE_ERROR = 'CLEAR_DROPZONE_ERROR'; +export const RESET_SEARCH_RESULT = 'RESET_SEARCH_RESULT'; +export const SEARCH_FONT_LIST = 'SEARCH_FONT_LIST'; +export const SELECT_FONT = 'SELECT_FONT'; +export const MOVE_SELECTED_FONT_TO_TOP = 'MOVE_SELECTED_FONT_TO_TOP'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -30,177 +30,177 @@ export const MOVE_SELECTED_FONT_TO_TOP = 'MOVE_SELECTED_FONT_TO_TOP' /** * Get the custom font list data * - * @returns {{ type: string }} + * @return {{ type: string }} action object * * @since 6.0 */ export const getCustomFontList = () => { - return { - type: GET_CUSTOM_FONT_LIST - } -} + return { + type: GET_CUSTOM_FONT_LIST, + }; +}; /** * Add font request * - * @param font + * @param { Object } font * - * @returns {{ payload: object, type: string }} + * @return {{ payload: Object, type: string }} action object * * @since 6.0 */ -export const addFont = font => { - return { - type: ADD_FONT, - payload: font - } -} +export const addFont = (font) => { + return { + type: ADD_FONT, + payload: font, + }; +}; /** * Edit font request * - * @param fontDetails + * @param { Object } fontDetails * - * @returns {{ payload: object, type: string }} + * @return {{ payload: Object, type: string }} action object * * @since 6.0 */ -export const editFont = fontDetails => { - return { - type: EDIT_FONT, - payload: fontDetails - } -} +export const editFont = (fontDetails) => { + return { + type: EDIT_FONT, + payload: fontDetails, + }; +}; /** * Process validation error * - * @returns {{ type: string }} + * @return {{ type: string }} action object * * @since 6.0 */ export const validationError = () => { - return { - type: VALIDATION_ERROR - } -} + return { + type: VALIDATION_ERROR, + }; +}; /** * Process deletion of font variant if error exist (regular, italics, bold, bold italics) * - * @param fontVariant + * @param { string} fontVariant * - * @returns {{ payload: string, type: string }} + * @return {{ payload: string, type: string }} action object * * @since 6.0 */ -export const deleteVariantError = fontVariant => { - return { - type: DELETE_VARIANT_ERROR, - payload: fontVariant - } -} +export const deleteVariantError = (fontVariant) => { + return { + type: DELETE_VARIANT_ERROR, + payload: fontVariant, + }; +}; /** * Delete font request * - * @param id + * @param { string } id * - * @returns {{ payload: string, type: string }} + * @return {{ payload: string, type: string }} action object * * @since 6.0 */ -export const deleteFont = id => { - return { - type: DELETE_FONT, - payload: id - } -} +export const deleteFont = (id) => { + return { + type: DELETE_FONT, + payload: id, + }; +}; /** * Process cleaning of success message and add font error message * - * @returns {{ type: string }} + * @return {{ type: string }} action object * * @since 6.0 */ export const clearAddFontMsg = () => { - return { - type: CLEAR_ADD_FONT_MSG - } -} + return { + type: CLEAR_ADD_FONT_MSG, + }; +}; /** * Process cleaning of dropzone error message * - * @param key + * @param { string } key * - * @returns {{ payload: string, type: string }} + * @return {{ payload: string, type: string }} action object * * @since 6.0 */ -export const clearDropzoneError = key => { - return { - type: CLEAR_DROPZONE_ERROR, - payload: key - } -} +export const clearDropzoneError = (key) => { + return { + type: CLEAR_DROPZONE_ERROR, + payload: key, + }; +}; /** * Search font list request * - * @param data + * @param { string } data * - * @returns {{ payload: string, type: string }} + * @return {{ payload: string, type: string }} action object * * @since 6.0 */ -export const searchFontList = data => { - return { - type: SEARCH_FONT_LIST, - payload: data - } -} +export const searchFontList = (data) => { + return { + type: SEARCH_FONT_LIST, + payload: data, + }; +}; /** * Process cleaning of search result * - * @returns {{ type: string }} + * @return {{ type: string }} action object * * @since 6.0 */ export const resetSearchResult = () => { - return { - type: RESET_SEARCH_RESULT - } -} + return { + type: RESET_SEARCH_RESULT, + }; +}; /** * Select font * - * @param fontId + * @param { string } fontId * - * @returns {{ payload: string, type: string }} + * @return {{ payload: string, type: string }} action object * * @since 6.0 */ -export const selectFont = fontId => { - return { - type: SELECT_FONT, - payload: fontId - } -} +export const selectFont = (fontId) => { + return { + type: SELECT_FONT, + payload: fontId, + }; +}; /** * Move selected font to top * - * @param fontId + * @param { string } fontId * - * @returns {{ payload: string, type: string }} + * @return {{ payload: string, type: string }} action object */ -export const moveSelectedFontToTop = fontId => { - return { - type: MOVE_SELECTED_FONT_TO_TOP, - payload: fontId - } -} +export const moveSelectedFontToTop = (fontId) => { + return { + type: MOVE_SELECTED_FONT_TO_TOP, + payload: fontId, + }; +}; diff --git a/src/assets/js/react/actions/templates.js b/src/assets/js/react/actions/templates.js index 562952bc3..d30da4699 100644 --- a/src/assets/js/react/actions/templates.js +++ b/src/assets/js/react/actions/templates.js @@ -1,25 +1,29 @@ /* Redux Action Type Constants */ -export const SEARCH_TEMPLATES = 'SEARCH_TEMPLATES' -export const SELECT_TEMPLATE = 'SELECT_TEMPLATE' -export const ADD_TEMPLATE = 'ADD_TEMPLATE' -export const UPDATE_TEMPLATE_PARAM = 'UPDATE_TEMPLATE_PARAM' -export const DELETE_TEMPLATE = 'DELETE_TEMPLATE' -export const UPDATE_SELECT_BOX = 'UPDATE_SELECT_BOX' -export const UPDATE_SELECT_BOX_SUCCESS = 'UPDATE_SELECT_BOX_SUCCESS' -export const UPDATE_SELECT_BOX_FAILED = 'UPDATE_SELECT_BOX_FAILED' -export const TEMPLATE_PROCESSING = 'TEMPLATE_PROCESSING' -export const TEMPLATE_PROCESSING_SUCCESS = 'TEMPLATE_PROCESSING_SUCCESS' -export const TEMPLATE_PROCESSING_FAILED = 'TEMPLATE_PROCESSING_FAILED' -export const CLEAR_TEMPLATE_PROCESSING = 'CLEAR_TEMPLATE_PROCESSING' -export const POST_TEMPLATE_UPLOAD_PROCESSING = 'POST_TEMPLATE_UPLOAD_PROCESSING' -export const TEMPLATE_UPLOAD_PROCESSING_SUCCESS = 'TEMPLATE_UPLOAD_PROCESSING_SUCCESS' -export const TEMPLATE_UPLOAD_PROCESSING_FAILED = 'TEMPLATE_UPLOAD_PROCESSING_FAILED' -export const CLEAR_TEMPLATE_UPLOAD_PROCESSING = 'CLEAR_TEMPLATE_UPLOAD_PROCESSING' +export const SEARCH_TEMPLATES = 'SEARCH_TEMPLATES'; +export const SELECT_TEMPLATE = 'SELECT_TEMPLATE'; +export const ADD_TEMPLATE = 'ADD_TEMPLATE'; +export const UPDATE_TEMPLATE_PARAM = 'UPDATE_TEMPLATE_PARAM'; +export const DELETE_TEMPLATE = 'DELETE_TEMPLATE'; +export const UPDATE_SELECT_BOX = 'UPDATE_SELECT_BOX'; +export const UPDATE_SELECT_BOX_SUCCESS = 'UPDATE_SELECT_BOX_SUCCESS'; +export const UPDATE_SELECT_BOX_FAILED = 'UPDATE_SELECT_BOX_FAILED'; +export const TEMPLATE_PROCESSING = 'TEMPLATE_PROCESSING'; +export const TEMPLATE_PROCESSING_SUCCESS = 'TEMPLATE_PROCESSING_SUCCESS'; +export const TEMPLATE_PROCESSING_FAILED = 'TEMPLATE_PROCESSING_FAILED'; +export const CLEAR_TEMPLATE_PROCESSING = 'CLEAR_TEMPLATE_PROCESSING'; +export const POST_TEMPLATE_UPLOAD_PROCESSING = + 'POST_TEMPLATE_UPLOAD_PROCESSING'; +export const TEMPLATE_UPLOAD_PROCESSING_SUCCESS = + 'TEMPLATE_UPLOAD_PROCESSING_SUCCESS'; +export const TEMPLATE_UPLOAD_PROCESSING_FAILED = + 'TEMPLATE_UPLOAD_PROCESSING_FAILED'; +export const CLEAR_TEMPLATE_UPLOAD_PROCESSING = + 'CLEAR_TEMPLATE_UPLOAD_PROCESSING'; /** * Redux Actions - payloads of information that send data from your application to your store * - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 4.1 @@ -28,250 +32,250 @@ export const CLEAR_TEMPLATE_UPLOAD_PROCESSING = 'CLEAR_TEMPLATE_UPLOAD_PROCESSIN /** * Fires the Advanced Template Search action * - * @param {string} text + * @param { string } text * - * @returns {{type, text: *}} + * @return {{ type: string, text: string }} action object * * @since 4.1 */ -export const searchTemplates = text => { - return { - type: SEARCH_TEMPLATES, - text - } -} +export const searchTemplates = (text) => { + return { + type: SEARCH_TEMPLATES, + text, + }; +}; /** * Fires the Advanced Template select/activate action * - * @param {string} id The template ID + * @param { string } id The template ID * - * @returns {{type, id: *}} + * @return {{ type: string, id: string }} action object * * @since 4.1 */ -export const selectTemplate = id => { - return { - type: SELECT_TEMPLATE, - id - } -} +export const selectTemplate = (id) => { + return { + type: SELECT_TEMPLATE, + id, + }; +}; /** * Fires the Advanced Template add new template action * - * @param {object} template + * @param { Object } template * - * @returns {{type, template: *}} + * @return {{ type, template: Object }} action object * * @since 4.1 */ -export const addTemplate = template => { - return { - type: ADD_TEMPLATE, - template - } -} +export const addTemplate = (template) => { + return { + type: ADD_TEMPLATE, + template, + }; +}; /** * Fires the Advanced Template update action which replaces a template parameter with a new value * - * @param {string} id The template ID - * @param {string} name The parameter key to update - * @param {string} value The new value for the parameter + * @param { string } id The template ID + * @param { string } name The parameter key to update + * @param { string } value The new value for the parameter * - * @returns {{type, id: *, name: *, value: *}} + * @return {{ type: string, id: string, name: string, value: string }} action object * * @since 4.1 */ export const updateTemplateParam = (id, name, value) => { - return { - type: UPDATE_TEMPLATE_PARAM, - id, - name, - value - } -} + return { + type: UPDATE_TEMPLATE_PARAM, + id, + name, + value, + }; +}; /** * Fires the Advanced Template delete action which removes the template from our store * - * @param {string} id The template ID + * @param { string } id The template ID * - * @returns {{type, id: *}} + * @return {{ type: string, id: string }} action object * * @since 4.1 */ -export const deleteTemplate = id => { - return { - type: DELETE_TEMPLATE, - id - } -} +export const deleteTemplate = (id) => { + return { + type: DELETE_TEMPLATE, + id, + }; +}; /** * Fires the Update Select Box action which request the new Select Box DOM data * - * @returns {{type}} + * @return {{ type: string }} action object * * @since 5.2 */ export const updateSelectBox = () => { - return { - type: UPDATE_SELECT_BOX - } -} + return { + type: UPDATE_SELECT_BOX, + }; +}; /** * Fires the Update Select Box Success action with The new Select Box DOM data * - * @param {string} text The new Select Box DOM data + * @param { string } text The new Select Box DOM data * - * @returns {{type, payload: text}} + * @return {{ type: string, payload: string }} action object * * @since 5.2 */ -export const updateSelectBoxSuccess = text => { - return { - type: UPDATE_SELECT_BOX_SUCCESS, - payload: text - } -} +export const updateSelectBoxSuccess = (text) => { + return { + type: UPDATE_SELECT_BOX_SUCCESS, + payload: text, + }; +}; /** * Fires the Update Select Box Failed action * - * @returns {{type}} + * @return {{ type: string }} action object * * @since 5.2 */ export const updateSelectBoxFailed = () => { - return { - type: UPDATE_SELECT_BOX_FAILED - } -} + return { + type: UPDATE_SELECT_BOX_FAILED, + }; +}; /** * Fires to post PDF template to our endpoint for processing * - * @param {string} templateId + * @param { string } templateId * - * @returns {{type, payload: templateId}} + * @return {{ type, payload: string }} action object * * @since 5.2 */ -export const templateProcessing = templateId => { - return { - type: TEMPLATE_PROCESSING, - payload: templateId - } -} +export const templateProcessing = (templateId) => { + return { + type: TEMPLATE_PROCESSING, + payload: templateId, + }; +}; /** * Fires to get PDF processing result to our endpoint * - * @param {string} data + * @param { string } data * - * @returns {{type, payload: data}} + * @return {{ type: string, payload: string }} action object * * @since 5.2 */ -export const templateProcessingSuccess = data => { - return { - type: TEMPLATE_PROCESSING_SUCCESS, - payload: data - } -} +export const templateProcessingSuccess = (data) => { + return { + type: TEMPLATE_PROCESSING_SUCCESS, + payload: data, + }; +}; /** * Fires if an error occured during request of PDF processing to our endpoint * - * @param data - * @returns {{type, payload: data}} + * @param { string } data + * @return {{ type: string, payload: string }} action object * * @since 5.2 */ -export const templateProcessingFailed = data => { - return { - type: TEMPLATE_PROCESSING_FAILED, - payload: data - } -} +export const templateProcessingFailed = (data) => { + return { + type: TEMPLATE_PROCESSING_FAILED, + payload: data, + }; +}; /** * Fires to clear/reset Template Processing data/result * - * @returns {{type}} + * @return {{ type: string }} action object * * @since 5.2 */ export const clearTemplateProcessing = () => { - return { - type: CLEAR_TEMPLATE_PROCESSING - } -} + return { + type: CLEAR_TEMPLATE_PROCESSING, + }; +}; /** * Fires request template to our endpoint for processing * - * @param {Object} file - * @param {String} filename + * @param { Object } file + * @param { string } filename * - * @returns {{type, { payload: { file: file, filename: filename } }}} + * @return {{ type: string, payload: { file: Object, filename: string } }} action object * * @since 5.2 */ export const postTemplateUploadProcessing = (file, filename) => { - return { - type: POST_TEMPLATE_UPLOAD_PROCESSING, - payload: { - file, - filename - } - } -} + return { + type: POST_TEMPLATE_UPLOAD_PROCESSING, + payload: { + file, + filename, + }, + }; +}; /** * Fires request template to our endpoint for processing * - * @param {Object} response + * @param { Object } response * - * @returns {{type, payload: responseText}} + * @return {{ type: string, payload: Object }} action object * * @since 5.2 */ -export const templateUploadProcessingSuccess = response => { - return { - type: TEMPLATE_UPLOAD_PROCESSING_SUCCESS, - payload: response - } -} +export const templateUploadProcessingSuccess = (response) => { + return { + type: TEMPLATE_UPLOAD_PROCESSING_SUCCESS, + payload: response, + }; +}; /** * Fires Update/Show error * - * @param {Object} error + * @param { Object } error * - * @returns {{type, payload: error}} + * @return {{ type: string, payload: Object }} action object * * @since 5.2 */ -export const templateUploadProcessingFailed = error => { - return { - type: TEMPLATE_UPLOAD_PROCESSING_FAILED, - payload: error - } -} +export const templateUploadProcessingFailed = (error) => { + return { + type: TEMPLATE_UPLOAD_PROCESSING_FAILED, + payload: error, + }; +}; /** * Fires to clear/reset data for templateUploadProcessingSuccess and templateUploadProcessingFailed * - * @returns {{type}} + * @return {{ type: string }} action object * * @since 5.2 */ export const clearTemplateUploadProcessing = () => { - return { - type: CLEAR_TEMPLATE_UPLOAD_PROCESSING - } -} + return { + type: CLEAR_TEMPLATE_UPLOAD_PROCESSING, + }; +}; diff --git a/src/assets/js/react/api/api.js b/src/assets/js/react/api/api.js index fd95f25e6..53763b1a4 100644 --- a/src/assets/js/react/api/api.js +++ b/src/assets/js/react/api/api.js @@ -1,5 +1,5 @@ /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -8,15 +8,33 @@ /** * Wrapper for the fetch() API which return a promise response * - * @param url: string - * @param init: object + * @param { string } url url of the request + * @param { Object } init options to be passed on fetch request + * @param { { + * useNativeResponse?: boolean + * useNativeErrorResponse?: boolean + * }= } options used when you need a custom logic for handling success or error responses * - * @returns Promise response + * @return { Promise<*> } response - it might be either a parsed response or a raw response * * @since 6.0 */ -export const api = async (url, init) => { - const response = await window.fetch(url, init) +export const api = async (url, init, options) => { + const response = await fetch(url, init); - return response -} + if (!response.ok) { + if (options?.useNativeErrorResponse) { + return response; + } + + const parsedResponse = await response.json(); + + throw new Error(parsedResponse.error); + } + + if (options?.useNativeResponse) { + return response; + } + + return await response.json(); +}; diff --git a/src/assets/js/react/api/coreFonts.js b/src/assets/js/react/api/coreFonts.js index a50b6348d..9bad4a1b0 100644 --- a/src/assets/js/react/api/coreFonts.js +++ b/src/assets/js/react/api/coreFonts.js @@ -1,40 +1,53 @@ -/* Dependencies */ -import request from 'superagent/dist/superagent.min' - /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.2 */ +import { api } from './api'; + /** * Do AJAX call * - * @returns {{method.get}} + * @return { Object } response * * @since 5.2 */ -export function apiGetFilesFromGitHub () { - return request - .get(GFPDF.pluginUrl + 'dist/payload/core-fonts.json') - .accept('application/json') - .type('json') - .parse(response => JSON.parse(response.text)) +export async function apiGetFilesFromGitHub() { + const res = await api(GFPDF.pluginUrl + 'build/payload/core-fonts.json', { + method: 'GET', + headers: { + Accept: 'application/json', + }, + }); + + return res; } /** * Do AJAX call * - * @param file - * @returns {{method.post}} + * @param { string } file + * + * @return { Promise<*> } response * * @since 5.2 */ -export function apiPostDownloadFonts (file) { - return request - .post(GFPDF.ajaxUrl) - .field('action', 'gfpdf_save_core_font') - .field('nonce', GFPDF.ajaxNonce) - .field('font_name', file) +export function apiPostDownloadFonts(file) { + const formData = new FormData(); + formData.append('action', 'gfpdf_save_core_font'); + formData.append('nonce', GFPDF.ajaxNonce); + formData.append('font_name', file); + + return api( + GFPDF.ajaxUrl, + { + method: 'POST', + body: formData, + }, + { + useNativeErrorResponse: true, + } + ); } diff --git a/src/assets/js/react/api/fontManager.js b/src/assets/js/react/api/fontManager.js index 5e9aa7793..8db3cf9e2 100644 --- a/src/assets/js/react/api/fontManager.js +++ b/src/assets/js/react/api/fontManager.js @@ -1,10 +1,10 @@ /* Dependencies */ -import { serialize } from 'object-to-formdata' +import { serialize } from 'object-to-formdata'; /* APIs */ -import { api } from './api' +import { api } from './api'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -13,83 +13,111 @@ import { api } from './api' /** * Fetch API request to obtain custom font list (GET) * - * @returns Promise response + * @return { Promise<*> } response * * @since 6.0 */ -export const apiGetCustomFontList = () => { - const url = GFPDF.restUrl + 'fonts/' +export const apiGetCustomFontList = async () => { + const url = GFPDF.restUrl + 'fonts/'; - return api(url, { - method: 'GET', - headers: { - 'X-WP-Nonce': GFPDF.restNonce - } - }) -} + return await api( + url, + { + method: 'GET', + headers: { + 'X-WP-Nonce': GFPDF.restNonce, + }, + }, + { + useNativeErrorResponse: true, + } + ); +}; /** * Fetch API request to add new font (POST) * - * @param font: object + * @param { Object } font * - * @returns Promise response + * @return { Promise } response * * @since 6.0 */ -export const apiAddFont = font => { - const url = GFPDF.restUrl + 'fonts/' - const formData = serialize(font) +export const apiAddFont = async (font) => { + const url = GFPDF.restUrl + 'fonts/'; + const formData = serialize(font); - return api(url, { - method: 'POST', - headers: { - 'X-WP-Nonce': GFPDF.restNonce - }, - body: formData - }) -} + return await api( + url, + { + method: 'POST', + headers: { + 'X-WP-Nonce': GFPDF.restNonce, + }, + body: formData, + }, + { + useNativeErrorResponse: true, + } + ); +}; /** * Fetch API request to edit font details (POST) * - * @param id: string - * @param font: object + * @param { Object } args + * @param { string } args.id + * @param { Object } args.font * - * @returns Promise response + * @return { Promise } response * * @since 6.0 */ -export const apiEditFont = ({ id, font }) => { - const url = GFPDF.restUrl + 'fonts/' + id - const data = { ...font } - const formData = serialize(data) +export const apiEditFont = async ({ id, font }) => { + const url = GFPDF.restUrl + 'fonts/' + id; + const data = { ...font }; + const formData = serialize(data); - return api(url, { - method: 'POST', - headers: { - 'X-WP-Nonce': GFPDF.restNonce - }, - body: formData - }) -} + return await api( + url, + { + method: 'POST', + headers: { + 'X-WP-Nonce': GFPDF.restNonce, + }, + body: formData, + }, + { + useNativeErrorResponse: true, + } + ); +}; /** * Fetch API request to delete existing font (DELETE) * - * @param id: string + * @param { string } id * - * @returns Promise response + * @return { Promise} response * * @since 6.0 */ -export const apiDeleteFont = id => { - const url = GFPDF.restUrl + 'fonts/' + id +export const apiDeleteFont = async (id) => { + const url = GFPDF.restUrl + 'fonts/' + id; - return api(url, { - method: 'DELETE', - headers: { - 'X-WP-Nonce': GFPDF.restNonce - } - }) -} + const res = await api( + url, + { + method: 'DELETE', + headers: { + 'X-WP-Nonce': GFPDF.restNonce, + }, + }, + { + useNativeErrorResponse: true, + useNativeResponse: true, + } + ); + + return res; +}; diff --git a/src/assets/js/react/api/help.js b/src/assets/js/react/api/help.js deleted file mode 100644 index 6f8ecce92..000000000 --- a/src/assets/js/react/api/help.js +++ /dev/null @@ -1,22 +0,0 @@ -/* Dependencies */ -import request from 'superagent/dist/superagent.min' - -/** - * @package Gravity PDF - * @copyright Copyright (c) 2024, Blue Liquid Designs - * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License - * @since 5.2 - */ - -/** - * Do AJAX call - * - * @param searchQuery - * - * @returns {{method.get}} - * - * @since 5.2 - */ -export const apiGetSearchResult = searchQuery => { - return request.get(`https://gravitypdf.com/wp-json/wp/v2/v6_docs/?search=${searchQuery}`) -} diff --git a/src/assets/js/react/api/templates.js b/src/assets/js/react/api/templates.js index 7ab78c112..1552008f2 100644 --- a/src/assets/js/react/api/templates.js +++ b/src/assets/js/react/api/templates.js @@ -1,57 +1,85 @@ -/* Dependencies */ -import request from 'superagent/dist/superagent.min' - /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.2 */ +import { api } from './api'; + /** * Do AJAX call * - * @returns {{method.post}} + * @return { string } response * * @since 5.2 */ -export function apiPostUpdateSelectBox () { - return request - .post(GFPDF.ajaxUrl) - .field('action', 'gfpdf_get_template_options') - .field('nonce', GFPDF.ajaxNonce) +export async function apiPostUpdateSelectBox() { + const formData = new FormData(); + formData.append('action', 'gfpdf_get_template_options'); + formData.append('nonce', GFPDF.ajaxNonce); + + const res = await api( + GFPDF.ajaxUrl, + { + method: 'POST', + body: formData, + }, + { + useNativeResponse: true, + } + ); + + return res.text(); } /** * Do AJAX call * - * @param {String} templateId + * @param { string } templateId * - * @returns {{method.post}} + * @return { Object } response * * @since 5.2 */ -export function apiPostTemplateProcessing (templateId) { - return request - .post(GFPDF.ajaxUrl) - .field('action', 'gfpdf_delete_template') - .field('nonce', GFPDF.ajaxNonce) - .field('id', templateId) +export async function apiPostTemplateProcessing(templateId) { + const formData = new FormData(); + formData.append('action', 'gfpdf_delete_template'); + formData.append('nonce', GFPDF.ajaxNonce); + formData.append('id', templateId); + + const res = await fetch( + GFPDF.ajaxUrl, + { + method: 'POST', + body: formData, + }, + { + useNativeResponse: true, + } + ); + + return res.json(); } /** * Do AJAX call * - * @param {{file: Object, filename: String}} + * @param { Object } file + * @param { string } filename * - * @returns {{method.post}} + * @return { Object } response * * @since 5.2 */ -export function apiPostTemplateUploadProcessing (file, filename) { - return request - .post(GFPDF.ajaxUrl) - .field('action', 'gfpdf_upload_template') - .field('nonce', GFPDF.ajaxNonce) - .attach('template', file, filename) +export async function apiPostTemplateUploadProcessing(file, filename) { + const formData = new FormData(); + formData.append('action', 'gfpdf_upload_template'); + formData.append('nonce', GFPDF.ajaxNonce); + formData.append('template', file, filename); + + return await api(GFPDF.ajaxUrl, { + method: 'POST', + body: formData, + }); } diff --git a/src/assets/js/react/bootstrap/coreFontBootstrap.js b/src/assets/js/react/bootstrap/coreFontBootstrap.js index 06add16ab..4541fadef 100644 --- a/src/assets/js/react/bootstrap/coreFontBootstrap.js +++ b/src/assets/js/react/bootstrap/coreFontBootstrap.js @@ -1,16 +1,16 @@ /* Dependencies */ -import React, { lazy, Suspense } from 'react' -import { render } from 'react-dom' -import { Provider } from 'react-redux' +import React, { lazy, Suspense } from 'react'; +import { createRoot } from 'react-dom/client'; +import { Provider } from 'react-redux'; /* Redux store */ -import { getStore } from '../store' +import { getStore } from '../store'; /* Routes */ -const Routes = lazy(() => import('../router/coreFontRouter')) +const Routes = lazy(() => import('../router/coreFontRouter')); /** * Core Font Downloader Bootstrap * - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.0 @@ -21,17 +21,20 @@ const Routes = lazy(() => import('../router/coreFontRouter')) * * @since 5.0 */ -export default function coreFontBootstrap () { - const container = document.getElementById('gfpdf-button-wrapper-install_core_fonts') - const button = container.getElementsByTagName('button')[0] - const store = getStore() +export default function coreFontBootstrap() { + const container = document.getElementById( + 'gfpdf-button-wrapper-install_core_fonts' + ); + const button = container.getElementsByTagName('button')[0]; + const store = getStore(); - render( - {GFPDF.spinnerAlt}}> - - - - , - container - ) + const root = createRoot(container); + + root.render( + {GFPDF.spinnerAlt}}> + + + + + ); } diff --git a/src/assets/js/react/bootstrap/fontManagerBootstrap.js b/src/assets/js/react/bootstrap/fontManagerBootstrap.js index deb6b9d81..a31ef3ea8 100644 --- a/src/assets/js/react/bootstrap/fontManagerBootstrap.js +++ b/src/assets/js/react/bootstrap/fontManagerBootstrap.js @@ -1,16 +1,22 @@ /* Dependencies */ -import React, { lazy, Suspense } from 'react' -import { render } from 'react-dom' -import { HashRouter as Router, Route } from 'react-router-dom' +import React, { lazy, Suspense } from 'react'; +import { createRoot } from 'react-dom/client'; +import { Route, Routes } from 'react-router-dom'; /* Routes */ -import { fontManagerRouter } from '../router/fontManagerRouter' +import { fontManagerRouter } from '../router/fontManagerRouter'; /* Redux store */ -import { getStore } from '../store' +import { getStore } from '../store'; +/* Helpers */ +import withRouterHooks from '../utilities/withRouterHooks.js'; /* Components */ -const AdvancedButton = lazy(() => import('../components/FontManager/AdvancedButton')) +import CustomHashRouter from '../components/CustomHashRouter'; +import Empty from '../components/Empty'; +const AdvancedButton = lazy( + () => import('../components/FontManager/AdvancedButton') +); /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -19,55 +25,77 @@ const AdvancedButton = lazy(() => import('../components/FontManager/AdvancedButt /** * Mount our font manager advanced button on the DOM * - * @param defaultFontField: div element - * @param buttonStyle: string + * @param { HTMLDivElement } defaultFontField + * @param { string } buttonStyle * * @since 6.0 */ -export function fontManagerBootstrap (defaultFontField, buttonStyle) { - const store = getStore() - /* Prevent button reset styling on tools tab */ - const preventButtonReset = !buttonStyle ? '' : buttonStyle +export function fontManagerBootstrap(defaultFontField, buttonStyle) { + const store = getStore(); + /* Prevent button reset styling on tools tab */ + const preventButtonReset = !buttonStyle ? '' : buttonStyle; - createAdvancedButtonWrapper(defaultFontField, preventButtonReset) + createAdvancedButtonWrapper(defaultFontField, preventButtonReset); - render( - {GFPDF.spinnerAlt}}> - - } /> - - , - document.querySelector('#gpdf-advance-font-manager-selector' + preventButtonReset) - ) + const container = document.querySelector( + '#gpdf-advance-font-manager-selector' + preventButtonReset + ); - fontManagerRouter(store) + const root = createRoot(container); + + root.render( + {GFPDF.spinnerAlt}}> + + + } + /> + } /> + + + + ); + + fontManagerRouter(store); } +const AdvancedButtonWithRouter = withRouterHooks(AdvancedButton); + /** * Create html element wrapper for our font manager advanced button * - * @param defaultFontField: div element - * @param preventButtonReset: string + * @param { HTMLDivElement } defaultFontField + * @param { string } preventButtonReset * * @since 6.0 */ -export function createAdvancedButtonWrapper (defaultFontField, preventButtonReset) { - const fontWrapper = document.createElement('span') - fontWrapper.setAttribute('id', 'gpdf-advance-font-manager-selector' + preventButtonReset) +export function createAdvancedButtonWrapper( + defaultFontField, + preventButtonReset +) { + const fontWrapper = document.createElement('span'); + fontWrapper.setAttribute( + 'id', + 'gpdf-advance-font-manager-selector' + preventButtonReset + ); - const popupWrapper = document.createElement('div') - popupWrapper.setAttribute('id', 'font-manager-overlay') - popupWrapper.setAttribute('class', 'theme-overlay') + const popupWrapper = document.createElement('div'); + popupWrapper.setAttribute('id', 'font-manager-overlay'); + popupWrapper.setAttribute('class', 'theme-overlay'); - if (defaultFontField.nodeName === 'SELECT') { - const wrapper = document.createElement('div') - wrapper.setAttribute('id', 'gfpdf-settings-field-wrapper-font-container') - wrapper.innerHTML = defaultFontField.outerHTML - wrapper.appendChild(fontWrapper) - wrapper.appendChild(popupWrapper) - defaultFontField.outerHTML = wrapper.outerHTML - } else { - defaultFontField.appendChild(fontWrapper) - defaultFontField.appendChild(popupWrapper) - } + if (defaultFontField.nodeName === 'SELECT') { + const wrapper = document.createElement('div'); + wrapper.setAttribute( + 'id', + 'gfpdf-settings-field-wrapper-font-container' + ); + wrapper.innerHTML = defaultFontField.outerHTML; + wrapper.appendChild(fontWrapper); + wrapper.appendChild(popupWrapper); + defaultFontField.outerHTML = wrapper.outerHTML; + } else { + defaultFontField.appendChild(fontWrapper); + defaultFontField.appendChild(popupWrapper); + } } diff --git a/src/assets/js/react/bootstrap/helpBootstrap.js b/src/assets/js/react/bootstrap/helpBootstrap.js index 4c9fb3ea4..15df3c567 100644 --- a/src/assets/js/react/bootstrap/helpBootstrap.js +++ b/src/assets/js/react/bootstrap/helpBootstrap.js @@ -1,14 +1,14 @@ /* Dependencies */ -import React, { lazy, Suspense } from 'react' -import { render } from 'react-dom' -import { Provider } from 'react-redux' +import React, { lazy, Suspense } from 'react'; +import { createRoot } from 'react-dom/client'; +import { Provider } from 'react-redux'; /* Redux store */ -import { getStore } from '../store' +import { getStore } from '../store'; /* Components */ -const HelpContainer = lazy(() => import('../components/Help/HelpContainer')) +const HelpContainer = lazy(() => import('../components/Help/HelpContainer')); /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.2 @@ -19,15 +19,18 @@ const HelpContainer = lazy(() => import('../components/Help/HelpContainer')) * * @since 5.2 */ -export default function helpBootstrap () { - const store = getStore() +export default function helpBootstrap() { + const store = getStore(); - render( - {GFPDF.spinnerAlt}}> - - - - , - document.getElementById('gpdf-search') - ) + const container = document.getElementById('gpdf-search'); + + const root = createRoot(container); + + root.render( + {GFPDF.spinnerAlt}}> + + + + + ); } diff --git a/src/assets/js/react/bootstrap/templateBootstrap.js b/src/assets/js/react/bootstrap/templateBootstrap.js index e7b9e93bc..b54f75f09 100644 --- a/src/assets/js/react/bootstrap/templateBootstrap.js +++ b/src/assets/js/react/bootstrap/templateBootstrap.js @@ -1,21 +1,27 @@ /* Dependencies */ -import React, { lazy, Suspense } from 'react' -import { render } from 'react-dom' -import { HashRouter as Router, Route } from 'react-router-dom' -import watch from 'redux-watch' +import React, { lazy, Suspense } from 'react'; +import { createRoot } from 'react-dom/client'; +import { Routes as Switch, Route } from 'react-router-dom'; +import watch from 'redux-watch'; /* Redux store */ -import { getStore } from '../store' +import { getStore } from '../store'; /* Redux actions */ -import { selectTemplate, updateSelectBox } from '../actions/templates' +import { selectTemplate, updateSelectBox } from '../actions/templates'; /* Routes */ -import templateRouter from '../router/templateRouter' +import templateRouter from '../router/templateRouter'; +/* Helpers */ +import withRouterHooks from '../utilities/withRouterHooks.js'; /* Components */ -const TemplateButton = lazy(() => import('../components/Template/TemplateButton')) +import CustomHashRouter from '../components/CustomHashRouter'; +import Empty from '../components/Empty'; +const TemplateButton = lazy( + () => import('../components/Template/TemplateButton') +); /** * Advanced Template Selector Bootstrap * - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 4.1 @@ -28,32 +34,43 @@ const TemplateButton = lazy(() => import('../components/Template/TemplateButton' * * @since 4.1 */ -export function templateBootstrap ($templateField) { - const store = getStore() - - /* Create our button container and render our component in it */ - createTemplateMarkup($templateField) - - /* Render our React Component in the DOM */ - render( - {GFPDF.spinnerAlt}}> - - } /> - - , - document.getElementById('gpdf-advance-template-selector') - ) - - /* Mount our router */ - templateRouter(store) - - /* - * Listen for Redux store updates and do DOM updates - */ - activeTemplateStoreListener(store, $templateField) - templateChangeStoreListener(store, $templateField) +export function templateBootstrap($templateField) { + const store = getStore(); + + /* Create our button container and render our component in it */ + createTemplateMarkup($templateField); + + const container = document.getElementById('gpdf-advance-template-selector'); + + const root = createRoot(container); + + /* Render our React Component in the DOM */ + root.render( + {GFPDF.spinnerAlt}}> + + + } + /> + } /> + + + + ); + + /* Mount our router */ + templateRouter(store); + + /* + * Listen for Redux store updates and do DOM updates + */ + activeTemplateStoreListener(store, $templateField); + templateChangeStoreListener(store, $templateField); } +const TemplateButtonWithRouter = withRouterHooks(TemplateButton); + /** * Dynamically add the required markup to attach our React components to. * @@ -61,12 +78,12 @@ export function templateBootstrap ($templateField) { * * @since 4.1 */ -export function createTemplateMarkup ($templateField) { - $templateField - .wrap('
') - .parent() - .append('') - .append('
') +export function createTemplateMarkup($templateField) { + $templateField + .wrap('
') + .parent() + .append('') + .append('
'); } /** @@ -74,30 +91,30 @@ export function createTemplateMarkup ($templateField) { * and update the select box value based on this change. Also, listen for changes * to our select box and update the store when needed. * - * @param {Object} store The Redux store returned from createStore() + * @param {Object} store The Redux store returned from createStore() * @param {Object} $templateField The jQuery select box we should attach the fancy template selector to * * @since 4.1 */ -export function activeTemplateStoreListener (store, $templateField) { - /* Watch our store for changes */ - const w = watch(store.getState, 'template.activeTemplate') - store.subscribe(w((template) => { - /* Check store and DOM are different to prevent any update recursions */ - if ($templateField.val() !== template) { - $templateField - .val(template) - .trigger('change') - } - })) - - /* Watch our DOM for changes */ - $templateField[0].addEventListener('change', () => { - /* Check store and DOM are different to prevent any update recursions */ - if ($templateField.val() !== store.getState().template.activeTemplate) { - store.dispatch(selectTemplate($templateField.val())) - } - }) +export function activeTemplateStoreListener(store, $templateField) { + /* Watch our store for changes */ + const w = watch(store.getState, 'template.activeTemplate'); + store.subscribe( + w((template) => { + /* Check store and DOM are different to prevent any update recursions */ + if ($templateField.val() !== template) { + $templateField.val(template).trigger('change'); + } + }) + ); + + /* Watch our DOM for changes */ + $templateField[0].addEventListener('change', () => { + /* Check store and DOM are different to prevent any update recursions */ + if ($templateField.val() !== store.getState().template.activeTemplate) { + store.dispatch(selectTemplate($templateField.val())); + } + }); } /** @@ -105,35 +122,42 @@ export function activeTemplateStoreListener (store, $templateField) { * rebuild this. Instead of duplicating the code on both server and client side we do an AJAX call to * get the new select box HTML when the template.list length changes and update the DOM accordingly. * - * @param {Object} store The Redux store returned from createStore() + * @param {Object} store The Redux store returned from createStore() * @param {Object} $templateField The jQuery select box we should attach the fancy template selector to * * @since 4.1 */ -export function templateChangeStoreListener (store, $templateField) { - /* Track the initial list size */ - let listCount = store.getState().template.list.length - - /* Watch our store for changes */ - const w = watch(store.getState, 'template.list') - store.subscribe(w((list) => { - /* Only update if the list size differs from what we expect */ - if (listCount !== list.length) { - /* update the list size so we don't run it twice */ - listCount = list.length - - /* Dispatch Redux Action for an AJAX call to get the new Select Box DOM */ - store.dispatch(updateSelectBox()) - - /* Watch our store for changes */ - const watchSelectBoxText = watch(store.getState, 'template.updateSelectBoxText') - store.subscribe(watchSelectBoxText((updateSelectBoxText) => { - /* Update $templateField */ - $templateField - .html(updateSelectBoxText) - .val(store.getState().template.activeTemplate) - .trigger('chosen:updated') - })) - } - })) +export function templateChangeStoreListener(store, $templateField) { + /* Track the initial list size */ + let listCount = store.getState().template.list.length; + + /* Watch our store for changes */ + const w = watch(store.getState, 'template.list'); + store.subscribe( + w((list) => { + /* Only update if the list size differs from what we expect */ + if (listCount !== list.length) { + /* update the list size so we don't run it twice */ + listCount = list.length; + + /* Dispatch Redux Action for an AJAX call to get the new Select Box DOM */ + store.dispatch(updateSelectBox()); + + /* Watch our store for changes */ + const watchSelectBoxText = watch( + store.getState, + 'template.updateSelectBoxText' + ); + store.subscribe( + watchSelectBoxText((updateSelectBoxText) => { + /* Update $templateField */ + $templateField + .html(updateSelectBoxText) + .val(store.getState().template.activeTemplate) + .trigger('chosen:updated'); + }) + ); + } + }) + ); } diff --git a/src/assets/js/react/components/Alert/Alert.js b/src/assets/js/react/components/Alert/Alert.js index 0a09b5464..a50d4cd5f 100644 --- a/src/assets/js/react/components/Alert/Alert.js +++ b/src/assets/js/react/components/Alert/Alert.js @@ -1,9 +1,9 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' +import React from 'react'; +import PropTypes from 'prop-types'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -12,18 +12,21 @@ import PropTypes from 'prop-types' /** * Display alert box UI * - * @param msg: string + * @param { Object } props + * @param { string } props.msg + * + * @return { JSX.Element } Alert Component * * @since 6.0 */ export const Alert = ({ msg }) => ( -
-
-
-) +
+
+
+); /** * PropTypes @@ -31,7 +34,7 @@ export const Alert = ({ msg }) => ( * @since 6.0 */ Alert.propTypes = { - msg: PropTypes.string.isRequired -} + msg: PropTypes.string.isRequired, +}; -export default Alert +export default Alert; diff --git a/src/assets/js/react/components/CoreFonts/CoreFontContainer.js b/src/assets/js/react/components/CoreFonts/CoreFontContainer.js index ebcf7fdbb..14a4355cf 100644 --- a/src/assets/js/react/components/CoreFonts/CoreFontContainer.js +++ b/src/assets/js/react/components/CoreFonts/CoreFontContainer.js @@ -1,23 +1,23 @@ /* Dependencies */ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; /* Components */ -import { CoreFontListResults } from './CoreFontListResults' -import Counter from './CoreFontCounter' -import Spinner from '../Spinner' +import { CoreFontListResults } from './CoreFontListResults'; +import Counter from './CoreFontCounter'; +import Spinner from '../Spinner'; /* Redux actions */ import { - clearButtonClickedAndRetryList, - addToConsole, - getFilesFromGitHub, - downloadFontsApiCall, - clearRequestRemainingData, - clearConsole -} from '../../actions/coreFonts' + clearButtonClickedAndRetryList, + addToConsole, + getFilesFromGitHub, + downloadFontsApiCall, + clearRequestRemainingData, + clearConsole, +} from '../../actions/coreFonts'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.0 @@ -29,275 +29,287 @@ import { * @since 5.0 */ export class CoreFontContainer extends Component { - /** - * - * @since 5.0 - */ - static propTypes = { - location: PropTypes.object, - requestDownload: PropTypes.string, - clearRequestRemainingData: PropTypes.func, - getFilesFromGitHub: PropTypes.func, - buttonClicked: PropTypes.bool, - fontList: PropTypes.array, - getFilesFromGitHubFailed: PropTypes.string, - retry: PropTypes.array, - clearConsole: PropTypes.func, - history: PropTypes.object, - clearButtonClickedAndRetryList: PropTypes.func, - downloadFontsApiCall: PropTypes.func, - addToConsole: PropTypes.func, - console: PropTypes.object, - buttonClassName: PropTypes.string, - buttonText: PropTypes.string, - counterText: PropTypes.string, - retryText: PropTypes.string, - queue: PropTypes.number - } + /** + * + * @since 5.0 + */ + static propTypes = { + location: PropTypes.object, + requestDownload: PropTypes.string, + clearRequestRemainingData: PropTypes.func, + getFilesFromGitHub: PropTypes.func, + buttonClicked: PropTypes.bool, + fontList: PropTypes.array, + getFilesFromGitHubFailed: PropTypes.string, + retry: PropTypes.array, + clearConsole: PropTypes.func, + navigate: PropTypes.func, + clearButtonClickedAndRetryList: PropTypes.func, + downloadFontsApiCall: PropTypes.func, + addToConsole: PropTypes.func, + console: PropTypes.object, + buttonClassName: PropTypes.string, + buttonText: PropTypes.string, + counterText: PropTypes.string, + retryText: PropTypes.string, + queue: PropTypes.number, + }; - /** - * Switches to show loaders - * - * @type {{ajax: boolean}} - * - * @since 5.0 - */ - state = { - ajax: false - } + /** + * Switches to show loaders + * + * @type {{ajax: boolean}} + * + * @since 5.0 + */ + state = { + ajax: false, + }; - /** - * If component did update and new props are received we'll check if the font list should be loaded - * - * @since 5.0 - */ - componentDidUpdate () { - const { - fontList, - buttonClicked, - location, - retry, - getFilesFromGitHubFailed, - requestDownload - } = this.props + /** + * If component did update and new props are received we'll check if the font list should be loaded + * + * @param { Readonly } prevProps + * + * @since 5.0 + */ + componentDidUpdate(prevProps) { + const { + fontList, + buttonClicked, + location, + retry, + getFilesFromGitHubFailed, + requestDownload, + } = this.props; - /* Load current font list */ - if (fontList.length > 0 && buttonClicked) { - this.startDownloadFonts(fontList) - } + /* Load current font list */ + if (fontList.length > 0 && buttonClicked) { + this.startDownloadFonts(fontList); + } - /* Check for /downloadCoreFonts redirect URL and run the installer */ - if (location.pathname === '/downloadCoreFonts') { - this.handleTriggerFontDownload() - } + /* Check for /downloadCoreFonts redirect URL and run the installer */ + if ( + location.pathname === '/downloadCoreFonts' && + prevProps?.location.pathname !== location.pathname + ) { + this.handleTriggerFontDownload(); + } - /* Load current hash history location & retry font list */ - if (location.pathname === '/retryDownloadCoreFonts') { - this.maybeStartDownload(location.pathname, retry) - } + /* Load current hash history location & retry font list */ + if (location.pathname === '/retryDownloadCoreFonts') { + this.maybeStartDownload(location.pathname, retry); + } - /* Load error if something went wrong */ - if (getFilesFromGitHubFailed !== '' && buttonClicked) { - this.startDownloadFonts(fontList, getFilesFromGitHubFailed) - } + /* Load error if something went wrong */ + if (getFilesFromGitHubFailed !== '' && buttonClicked) { + this.startDownloadFonts(fontList, getFilesFromGitHubFailed); + } - /* If request download is finished, call resetState function */ - if (requestDownload === 'finished') { - this.resetState() - } - } + /* If request download is finished, call resetState function */ + if (requestDownload === 'finished') { + this.resetState(); + } + } - /** - * Check for /downloadCoreFonts redirect URL and run the installer - * - * @since 5.0 - */ - componentDidMount () { - if (this.props.location.pathname === '/downloadCoreFonts') { - this.handleTriggerFontDownload() - } - } + /** + * Check for /downloadCoreFonts redirect URL and run the installer + * + * @since 5.0 + */ + componentDidMount() { + if (this.props.location.pathname === '/downloadCoreFonts') { + this.handleTriggerFontDownload(); + } + } - /** - * If the Hash History matches our keys (and not already loading) start the download - * - * @param location - * @param fontList - * @param error - * - * @since 5.0 - */ - maybeStartDownload = (location, fontList, error = null) => { - if (location === '/downloadCoreFonts') { - this.startDownloadFonts(fontList, error) - } + /** + * If the Hash History matches our keys (and not already loading) start the download + * + * @param { string } location + * @param { Array } fontList + * @param { Object= } error + * + * @since 5.0 + */ + maybeStartDownload = (location, fontList, error = null) => { + if (location === '/downloadCoreFonts') { + this.startDownloadFonts(fontList, error); + } - if (location === '/retryDownloadCoreFonts') { - this.setState({ ajax: true }) - this.startDownloadFonts(fontList, error) - } - } + if (location === '/retryDownloadCoreFonts') { + this.setState({ ajax: true }); + this.startDownloadFonts(fontList, error); + } + }; - /** - * Call our server to download the fonts in batches of 5 - * - * @param array files The font files to download (usually passed in from the 'retry' prop) - * - * @since 5.0 - */ - startDownloadFonts = (files, error) => { - if (files.length === 0) { - this.props.clearButtonClickedAndRetryList() + /** + * Call our server to download the fonts in batches of 5 + * + * @param { Array } files The font files to download (usually passed in from the 'retry' prop) + * @param { Object } error + * + * @since 5.0 + */ + startDownloadFonts = (files, error) => { + if (files.length === 0) { + this.props.clearButtonClickedAndRetryList(); - return this.handleGithubApiError(error) - } + return this.handleGithubApiError(error); + } - this.props.clearConsole() - this.props.clearButtonClickedAndRetryList() + this.props.clearConsole(); + this.props.clearButtonClickedAndRetryList(); - /* Clean Hash History */ - this.props.history.replace('') + /* Clean Hash History */ + this.props.navigate('/'); - setTimeout(() => files.map((file) => this.props.downloadFontsApiCall(file)), 300) - } + setTimeout( + () => files.map((file) => this.props.downloadFontsApiCall(file)), + 300 + ); + }; - /** - * Add a GitHub API overall status to the console - * - * @param error - * - * @since 5.0 - */ - handleGithubApiError = (error) => { - this.setState({ ajax: false }) - this.props.addToConsole('completed', 'error', error) - this.props.history.replace('') - } + /** + * Add a GitHub API overall status to the console + * + * @param { Object } error + * + * @since 5.0 + */ + handleGithubApiError = (error) => { + this.setState({ ajax: false }); + this.props.addToConsole('completed', 'error', error); + this.props.navigate('/'); + }; - /** - * Request GitHub for font names & trigger font download - * - * @since 5.0 - */ - handleTriggerFontDownload = () => { - if (this.state.ajax === false) { - /* Get the font names from GitHub we need to download */ - this.setState({ ajax: true }, () => { - this.props.getFilesFromGitHub() - }) - } - } + /** + * Request GitHub for font names & trigger font download + * + * @since 5.0 + */ + handleTriggerFontDownload = () => { + if (this.state.ajax === false) { + /* Get the font names from GitHub we need to download */ + this.setState({ ajax: true }, () => { + this.props.getFilesFromGitHub(); + }); + } + }; - /** - * Reset ajax/loading state to false - * - * @since 5.0 - */ - resetState = () => { - const { clearRequestRemainingData, history } = this.props + /** + * Reset ajax/loading state to false + * + * @since 5.0 + */ + resetState = () => { + const { clearRequestRemainingData: clear, navigate } = this.props; - this.setState({ ajax: false }) - clearRequestRemainingData() - history.replace('') - } + this.setState({ ajax: false }); + clear(); + navigate('/'); + }; - /** - * Renders our Core Font downloader UI - * - * @returns {XML} & Download Button - * - * @since 5.0 - */ - render () { - const { ajax } = this.state + /** + * Renders our Core Font downloader UI + * + * @return {JSX.Element} CoreFontContainer Component + * + * @since 5.0 + */ + render() { + const { ajax } = this.state; - const { - fontList, - buttonClassName, - buttonText, - counterText, - queue, - history, - console: consoleList, - retry, - retryText - } = this.props + const { + fontList, + buttonClassName, + buttonText, + counterText, + queue, + navigate, + console: consoleList, + retry, + retryText, + } = this.props; - const disabled = (queue < fontList.length && queue !== 0) || ajax + const disabled = (queue < fontList.length && queue !== 0) || ajax; - return ( -
- - {ajax && } - {ajax && queue !== 0 && } - -
- ) - } + return ( +
+ + {ajax && } + {ajax && queue !== 0 && ( + + )} + +
+ ); + } } /** * Map Redux state to props * - * @param state + * @param { Object } state + * @param { Object } state.coreFonts * - * @returns {{ - * buttonClicked: Boolean, - * fontList: Array, - * getFilesFromGitHubFailed: String, + * @return {{ + * buttonClicked: boolean, + * fontList: Array, + * getFilesFromGitHubFailed: string, * console: Object, - * retry: (*|number|Array), - * requestDownload: String, + * retry: Array<*>, + * requestDownload: string, * queue: boolean - * }} + * }} mapped state * * @since 5.0 */ -const mapStateToProps = state => { - return { - buttonClicked: state.coreFonts.buttonClicked, - fontList: state.coreFonts.fontList, - getFilesFromGitHubFailed: state.coreFonts.getFilesFromGitHubFailed, - console: state.coreFonts.console, - retry: state.coreFonts.retry, - requestDownload: state.coreFonts.requestDownload, - queue: state.coreFonts.downloadCounter - } -} +const mapStateToProps = (state) => { + return { + buttonClicked: state.coreFonts.buttonClicked, + fontList: state.coreFonts.fontList, + getFilesFromGitHubFailed: state.coreFonts.getFilesFromGitHubFailed, + console: state.coreFonts.console, + retry: state.coreFonts.retry, + requestDownload: state.coreFonts.requestDownload, + queue: state.coreFonts.downloadCounter, + }; +}; /** * Map Redux actions to props * - * @returns {{ + * @return {{ * addToConsole, * clearButtonClickedAndRetryList, * getFilesFromGitHub, * downloadFontsApiCall, * clearRequestRemainingData, * clearConsole - * }} + * }} mapped dispatch * * @since 5.0 */ export default connect(mapStateToProps, { - addToConsole, - clearButtonClickedAndRetryList, - getFilesFromGitHub, - downloadFontsApiCall, - clearRequestRemainingData, - clearConsole -})(CoreFontContainer) + addToConsole, + clearButtonClickedAndRetryList, + getFilesFromGitHub, + downloadFontsApiCall, + clearRequestRemainingData, + clearConsole, +})(CoreFontContainer); diff --git a/src/assets/js/react/components/CoreFonts/CoreFontCounter.js b/src/assets/js/react/components/CoreFonts/CoreFontCounter.js index 3991b0e8f..7f2e09371 100644 --- a/src/assets/js/react/components/CoreFonts/CoreFontCounter.js +++ b/src/assets/js/react/components/CoreFonts/CoreFontCounter.js @@ -1,9 +1,9 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' +import React from 'react'; +import PropTypes from 'prop-types'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.0 @@ -12,27 +12,30 @@ import PropTypes from 'prop-types' /** * Display an inline counter * - * @param queue - * @param text + * @param { Object } props + * @param { string } props.queue + * @param { string } props.text + * + * @return { JSX.Element } CoreFontCounter Component * * @since 5.0 */ const CoreFontCounter = ({ queue, text }) => ( - - {text} {queue} - -) + + {text} {queue} + +); /** * * @since 5.0 */ CoreFontCounter.propTypes = { - queue: PropTypes.number, - text: PropTypes.string -} + queue: PropTypes.number, + text: PropTypes.string, +}; -export default CoreFontCounter +export default CoreFontCounter; diff --git a/src/assets/js/react/components/CoreFonts/CoreFontListResults.js b/src/assets/js/react/components/CoreFonts/CoreFontListResults.js index fd943a22b..938c32fb4 100644 --- a/src/assets/js/react/components/CoreFonts/CoreFontListResults.js +++ b/src/assets/js/react/components/CoreFonts/CoreFontListResults.js @@ -1,11 +1,11 @@ /* Dependencies */ -import React, { Component } from 'react' -import PropTypes from 'prop-types' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; /* Components */ -import ListSpacer from './CoreFontListSpacer' +import ListSpacer from './CoreFontListSpacer'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.0 @@ -17,95 +17,99 @@ import ListSpacer from './CoreFontListSpacer' * @since 5.0 */ export class CoreFontListResults extends Component { - /** - * - * @since 5.0 - */ - static propTypes = { - console: PropTypes.object, - retry: PropTypes.array, - history: PropTypes.object, - retryText: PropTypes.string - } + /** + * + * @since 5.0 + */ + static propTypes = { + console: PropTypes.object, + retry: PropTypes.array, + navigate: PropTypes.func, + retryText: PropTypes.string, + }; - /** - * @returns {*} - * - * @since 5.0 - */ - render () { - const console = this.props.console - const lines = Object.keys(console).reverse() - const retry = this.props.retry.length > 0 + /** + * @return { JSX.Element } CoreFontListResults Component + * + * @since 5.0 + */ + render() { + const console = this.props.console; + const lines = Object.keys(console).reverse(); + const retry = this.props.retry.length > 0; - return (!lines.length) - ? null - : ( -
    - {lines.map((key) => -
  • - {console[key].message} - {' '} - {key === 'completed' && retry && } - {key === 'completed' && } -
  • - )} -
- ) - } + return !lines.length ? null : ( +
    + {lines.map((key) => ( +
  • + {console[key].message}{' '} + {key === 'completed' && retry && ( + + )} + {key === 'completed' && } +
  • + ))} +
+ ); + } } /** * @since 5.0 */ export class Retry extends Component { - /** - * - * @since 5.0 - */ - static propTypes = { - history: PropTypes.object, - retryText: PropTypes.string - } + /** + * + * @since 5.0 + */ + static propTypes = { + navigate: PropTypes.func, + retryText: PropTypes.string, + }; - /** - * Update the navigation history when the retry link is selected - * - * @param e - * - * @since 5.0 - */ - handleTriggerRetryFontDownload = (e) => { - e.preventDefault() - this.props.history.replace('retryDownloadCoreFonts') - } + /** + * Update the navigation history when the retry link is selected + * + * @param { Event } e + * + * @since 5.0 + */ + handleTriggerRetryFontDownload = (e) => { + e.preventDefault(); + this.props.navigate('retryDownloadCoreFonts'); + }; - /** - * Display a "retry" download link - * - * @returns {*} - * - * @since 5.0 - */ - render () { - return ( - - {this.props.retryText} - - ) - } + /** + * Display a "retry" download link + * + * @return { JSX.Element } Retry Component + * + * @since 5.0 + */ + render() { + return ( + + ); + } } diff --git a/src/assets/js/react/components/CoreFonts/CoreFontListSpacer.js b/src/assets/js/react/components/CoreFonts/CoreFontListSpacer.js index f1693413c..17a2874fe 100644 --- a/src/assets/js/react/components/CoreFonts/CoreFontListSpacer.js +++ b/src/assets/js/react/components/CoreFonts/CoreFontListSpacer.js @@ -1,8 +1,8 @@ /* Dependencies */ -import React from 'react' +import React from 'react'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 5.0 @@ -14,7 +14,12 @@ import React from 'react' * @since 5.0 */ const CoreFontListSpacer = () => ( -
---
-) +
+ --- +
+); -export default CoreFontListSpacer +export default CoreFontListSpacer; diff --git a/src/assets/js/react/components/CustomHashRouter.js b/src/assets/js/react/components/CustomHashRouter.js new file mode 100644 index 000000000..c9e34410a --- /dev/null +++ b/src/assets/js/react/components/CustomHashRouter.js @@ -0,0 +1,51 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { createHashHistory } from 'history'; +import { Router } from 'react-router-dom'; + +// Create a shared hash history instance +export const sharedHashHistory = createHashHistory({ window }); + +/** + * @param {React.ReactNode} children + * + * @return {JSX.Element} CustomHashRouter component + * + * @since 6.12 + */ +function CustomHashRouter({ children }) { + const historyRef = React.useRef(); + if (historyRef.current === null) { + historyRef.current = sharedHashHistory; + } + + const history = historyRef.current ?? sharedHashHistory; + const [state, setStateImpl] = React.useState({ + action: history.action, + location: history.location, + }); + + const setState = React.useCallback( + (newState) => setStateImpl(newState), + [setStateImpl] + ); + + React.useLayoutEffect(() => history.listen(setState), [history, setState]); + + return ( + + {children} + + ); +} + +CustomHashRouter.propTypes = { + children: PropTypes.node.isRequired, +}; + +export default CustomHashRouter; diff --git a/src/assets/js/react/components/Empty.js b/src/assets/js/react/components/Empty.js index 6576599b8..ae8e01942 100644 --- a/src/assets/js/react/components/Empty.js +++ b/src/assets/js/react/components/Empty.js @@ -1,7 +1,7 @@ /** * Render a blank component for use with React Router's default route * - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 4.1 @@ -13,5 +13,5 @@ * @since 4.1 */ export default () => { - return false -} + return false; +}; diff --git a/src/assets/js/react/components/FontManager/AddFont.js b/src/assets/js/react/components/FontManager/AddFont.js index 29c349bd7..a119444a2 100644 --- a/src/assets/js/react/components/FontManager/AddFont.js +++ b/src/assets/js/react/components/FontManager/AddFont.js @@ -1,13 +1,13 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' -import { sprintf } from 'sprintf-js' +import React from 'react'; +import PropTypes from 'prop-types'; +import { sprintf } from 'sprintf-js'; /* Components */ -import FontVariant from './FontVariant' -import AddUpdateFontFooter from './AddUpdateFontFooter' +import FontVariant from './FontVariant'; +import AddUpdateFontFooter from './AddUpdateFontFooter'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -16,96 +16,119 @@ import AddUpdateFontFooter from './AddUpdateFontFooter' /** * Display add font panel UI * - * @param label - * @param onHandleInputChange - * @param onHandleUpload - * @param onHandleDeleteFontStyle - * @param onHandleSubmit - * @param fontStyles - * @param validateLabel - * @param validateRegular - * @param msg - * @param loading - * @param tabIndexFontName - * @param tabIndexFontFiles - * @param tabIndexFooterButtons + * @param { Object } props + * @param { string } props.label + * @param { Function } props.onHandleInputChange + * @param { Function } props.onHandleUpload + * @param { Function } props.onHandleDeleteFontStyle + * @param { Function } props.onHandleSubmit + * @param { Object } props.fontStyles + * @param { boolean } props.validateLabel + * @param { boolean } props.validateRegular + * @param { Object } props.msg + * @param { boolean } props.loading + * @param { string } props.tabIndexFontName + * @param { string } props.tabIndexFontFiles + * @param { string } props.tabIndexFooterButtons + * + * @return {JSX.Element} AddFont component * * @since 6.0 */ -export const AddFont = ( - { - label, - onHandleInputChange, - onHandleUpload, - onHandleDeleteFontStyle, - onHandleSubmit, - fontStyles, - validateLabel, - validateRegular, - msg, - loading, - tabIndexFontName, - tabIndexFontFiles, - tabIndexFooterButtons - } -) => { - const fontNameLabel = sprintf(GFPDF.fontManagerFontNameLabel, "", '') +export const AddFont = ({ + label, + onHandleInputChange, + onHandleUpload, + onHandleDeleteFontStyle, + onHandleSubmit, + fontStyles, + validateLabel, + validateRegular, + msg, + loading, + tabIndexFontName, + tabIndexFontFiles, + tabIndexFooterButtons, +}) => { + // eslint can't detect %s found on fontManagerFontNameLabel + // eslint-disable-next-line @wordpress/valid-sprintf + const fontNameLabel = sprintf( + GFPDF.fontManagerFontNameLabel, + "", + '' + ); - return ( -
-
-

{GFPDF.fontManagerAddTitle}

+ return ( +
+ +

{GFPDF.fontManagerAddTitle}

-

{GFPDF.fontManagerAddDesc}

+

{GFPDF.fontManagerAddDesc}

-
- ) -} + + +
+ ); +}; /** * PropTypes @@ -113,19 +136,19 @@ export const AddFont = ( * @since 6.0 */ AddFont.propTypes = { - label: PropTypes.string.isRequired, - onHandleInputChange: PropTypes.func.isRequired, - onHandleUpload: PropTypes.func.isRequired, - onHandleDeleteFontStyle: PropTypes.func.isRequired, - onHandleSubmit: PropTypes.func.isRequired, - validateLabel: PropTypes.bool.isRequired, - validateRegular: PropTypes.bool.isRequired, - fontStyles: PropTypes.object.isRequired, - msg: PropTypes.object.isRequired, - loading: PropTypes.bool.isRequired, - tabIndexFontName: PropTypes.string.isRequired, - tabIndexFontFiles: PropTypes.string.isRequired, - tabIndexFooterButtons: PropTypes.string.isRequired -} + label: PropTypes.string.isRequired, + onHandleInputChange: PropTypes.func.isRequired, + onHandleUpload: PropTypes.func.isRequired, + onHandleDeleteFontStyle: PropTypes.func.isRequired, + onHandleSubmit: PropTypes.func.isRequired, + validateLabel: PropTypes.bool.isRequired, + validateRegular: PropTypes.bool.isRequired, + fontStyles: PropTypes.object.isRequired, + msg: PropTypes.object.isRequired, + loading: PropTypes.bool.isRequired, + tabIndexFontName: PropTypes.string.isRequired, + tabIndexFontFiles: PropTypes.string.isRequired, + tabIndexFooterButtons: PropTypes.string.isRequired, +}; -export default AddFont +export default AddFont; diff --git a/src/assets/js/react/components/FontManager/AddUpdateFontFooter.js b/src/assets/js/react/components/FontManager/AddUpdateFontFooter.js index 9c98074f0..90ece08a7 100644 --- a/src/assets/js/react/components/FontManager/AddUpdateFontFooter.js +++ b/src/assets/js/react/components/FontManager/AddUpdateFontFooter.js @@ -1,16 +1,19 @@ /* Dependencies */ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' -import { sprintf } from 'sprintf-js' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { sprintf } from 'sprintf-js'; /* Components */ -import Spinner from '../Spinner' +import Spinner from '../Spinner'; /* Redux actions */ -import { selectFont, deleteFont } from '../../actions/fontManager' -import TemplateTooltip from './TemplateTooltip' +import { + selectFont as selectFontAction, + deleteFont as deleteFontAction, +} from '../../actions/fontManager'; +import TemplateTooltip from './TemplateTooltip'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -32,228 +35,270 @@ import TemplateTooltip from './TemplateTooltip' * @since 6.0 */ export class AddUpdateFontFooter extends Component { - /** - * PropTypes - * - * @since 6.0 - */ - static propTypes = { - state: PropTypes.string, - id: PropTypes.string, - disabled: PropTypes.bool, - onHandleCancelEditFont: PropTypes.func, - onHandleCancelEditFontKeypress: PropTypes.func, - selectedFont: PropTypes.string.isRequired, - selectFont: PropTypes.func.isRequired, - deleteFont: PropTypes.func, - msg: PropTypes.object.isRequired, - loading: PropTypes.bool.isRequired, - tabIndex: PropTypes.string.isRequired - } + /** + * PropTypes + * + * @since 6.0 + */ + static propTypes = { + state: PropTypes.string, + id: PropTypes.string, + disabled: PropTypes.bool, + onHandleCancelEditFont: PropTypes.func, + onHandleCancelEditFontKeypress: PropTypes.func, + selectedFont: PropTypes.string.isRequired, + selectFont: PropTypes.func.isRequired, + deleteFont: PropTypes.func, + msg: PropTypes.object.isRequired, + loading: PropTypes.bool.isRequired, + tabIndex: PropTypes.string.isRequired, + }; - /** - * Handle the functionality to select and set default font type to be used in PDFs - * (Under 'update font' panel) - * - * @param fontId: string - * @param selectedFont: string - * - * @since 6.0 - */ - handleSelectFont = (fontId, selectedFont) => { - const { selectFont } = this.props + /** + * Handle the functionality to select and set default font type to be used in PDFs + * (Under 'update font' panel) + * + * @param { string } fontId + * @param { string } selectedFont + * + * @since 6.0 + */ + handleSelectFont = (fontId, selectedFont) => { + const { selectFont } = this.props; - if (fontId === selectedFont) { - return selectFont('') - } + if (fontId === selectedFont) { + return selectFont(''); + } - selectFont(fontId) - } + selectFont(fontId); + }; - /** - * Handle the functionality to select and set default font type to be used in PDFs - * (Under 'update font' panel - Keyboard press) - * - * @param e: object - * @param fontId: string - * @param selectedFont: string - * - * @since 6.0 - */ - handleSelectFontKeypress = (e, fontId, selectedFont) => { - const enter = 13 - const space = 32 - const { selectFont } = this.props + /** + * Handle the functionality to select and set default font type to be used in PDFs + * (Under 'update font' panel - Keyboard press) + * + * @param { KeyboardEvent } e + * @param { string } fontId + * @param { string } selectedFont + * + * @since 6.0 + */ + handleSelectFontKeypress = (e, fontId, selectedFont) => { + const enter = 'Enter'; + const space = ' '; + const { selectFont } = this.props; - if (e.keyCode === enter || e.keyCode === space) { - if (fontId === selectedFont) { - return selectFont('') - } + if (e.key === enter || e.key === space) { + if (fontId === selectedFont) { + return selectFont(''); + } - selectFont(fontId) - } - } + selectFont(fontId); + } + }; - /** - * Handle request of font deletion (Under 'update font' panel) - * - * @param fontId: string - * - * @since 6.0 - */ - handleDeleteFont = fontId => { - /* Fire a native window alert box to confirm deletion request */ - if (window.confirm(GFPDF.fontManagerDeleteFontConfirmation)) { - /* Call redux action deleteFont */ - this.props.deleteFont(fontId) - } - } + /** + * Handle request of font deletion (Under 'update font' panel) + * + * @param { string } fontId + * + * @since 6.0 + */ + handleDeleteFont = (fontId) => { + /* Fire a native window alert box to confirm deletion request */ + if (window.confirm(GFPDF.fontManagerDeleteFontConfirmation)) { + /* Call redux action deleteFont */ + this.props.deleteFont(fontId); + } + }; - /** - * Handle request of font deletion (Under 'update font' panel - Keyboard press) - * - * @param e: object - * @param fontId: string - * - * @since 6.0 - */ - handleDeleteFontKeypress = (e, fontId) => { - const enter = 13 - const space = 32 + /** + * Handle request of font deletion (Under 'update font' panel - Keyboard press) + * + * @param { KeyboardEvent } e + * @param { string } fontId + * + * @since 6.0 + */ + handleDeleteFontKeypress = (e, fontId) => { + const enter = 'Enter'; + const space = ' '; - if (e.keyCode === enter || e.keyCode === space) { - /* Fire a native window alert box to confirm deletion request */ - if (window.confirm(GFPDF.fontManagerDeleteFontConfirmation)) { - /* Call redux action deleteFont */ - this.props.deleteFont(fontId) - } - } - } + if (e.key === enter || e.key === space) { + /* Fire a native window alert box to confirm deletion request */ + if (window.confirm(GFPDF.fontManagerDeleteFontConfirmation)) { + /* Call redux action deleteFont */ + this.props.deleteFont(fontId); + } + } + }; - render () { - const { - state, - id, - disabled, - onHandleCancelEditFont, - onHandleCancelEditFontKeypress, - selectedFont, - msg: { success, error }, - loading, - tabIndex - } = this.props - const cancelButton = document.querySelector('.footer button.cancel') - const errorFontList = error && error.fontList - const successAddFont = success && success.addFont - const showSuccessAddFont = (successAddFont && errorFontList) || (successAddFont && !state) - const errorAddFont = (error && error.addFont) && error.addFont - const errorFontValidation = (errorAddFont && error.fontValidationError) && error.fontValidationError - const fontFileMissing = sprintf(GFPDF.fontFileMissing, '', '') - const selectedBoxStyle = (id !== '') && (id === selectedFont) ? ' checked' : ' uncheck' - /* Display error message for uploading invalid font file */ - const displayInvalidFileErrorMessage = errorAddFont && errorFontValidation - /* Display generic error messages including missing font file */ - const displayGenericErrorMessage = errorAddFont && !errorFontValidation + render() { + const { + state, + id, + disabled, + onHandleCancelEditFont, + onHandleCancelEditFontKeypress, + selectedFont, + msg: { success, error }, + loading, + tabIndex, + } = this.props; + const cancelButton = document.querySelector('.footer button.cancel'); + const errorFontList = error && error.fontList; + const successAddFont = success && success.addFont; + const showSuccessAddFont = + (successAddFont && errorFontList) || (successAddFont && !state); + const errorAddFont = error && error.addFont && error.addFont; + const errorFontValidation = + errorAddFont && + error.fontValidationError && + error.fontValidationError; + // eslint can't detect %s on fontFileMissing + // eslint-disable-next-line @wordpress/valid-sprintf + const fontFileMissing = sprintf( + GFPDF.fontFileMissing, + '', + '' + ); + const selectedBoxStyle = + id !== '' && id === selectedFont ? ' checked' : ' uncheck'; + /* Display error message for uploading invalid font file */ + const displayInvalidFileErrorMessage = + errorAddFont && errorFontValidation; + /* Display generic error messages including missing font file */ + const displayGenericErrorMessage = errorAddFont && !errorFontValidation; - return ( -
-
-
- {id && ( - - )} + return ( +
+
+
+ {id && ( + + )} - + - {loading && } -
+ {loading && } +
-
- {id && ( -
-
+ {id && ( +
+ - {showSuccessAddFont && ( - - )} + {showSuccessAddFont && ( + + )} - {displayInvalidFileErrorMessage && ( - - )} + {displayInvalidFileErrorMessage && ( + + )} - {displayGenericErrorMessage && ( - - )} + {displayGenericErrorMessage && ( + + )} - {id && } -
- ) - } + {id && } + + ); + } } /** * Map redux state to props * - * @param state: object + * @param { Object } state + * @param { Object } state.fontManager * - * @returns {{ selectedFont: string }} + * @return {{ selectedFont: string }} mappedState * * @since 6.0 */ -const mapStateToProps = state => ({ - selectedFont: state.fontManager.selectedFont -}) +const mapStateToProps = (state) => ({ + selectedFont: state.fontManager.selectedFont, +}); /** * Connect and dispatch redux actions as props * * @since 6.0 */ -export default connect(mapStateToProps, { selectFont, deleteFont })(AddUpdateFontFooter) +export default connect(mapStateToProps, { + selectFont: selectFontAction, + deleteFont: deleteFontAction, +})(AddUpdateFontFooter); diff --git a/src/assets/js/react/components/FontManager/AdvancedButton.js b/src/assets/js/react/components/FontManager/AdvancedButton.js index 8b733c271..e6f9f4dd8 100644 --- a/src/assets/js/react/components/FontManager/AdvancedButton.js +++ b/src/assets/js/react/components/FontManager/AdvancedButton.js @@ -1,9 +1,9 @@ /* Dependencies */ -import React, { Component } from 'react' -import PropTypes from 'prop-types' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -15,45 +15,45 @@ import PropTypes from 'prop-types' * @since 6.0 */ export class AdvancedButton extends Component { - /** - * PropTypes - * - * @since 6.0 - */ - static propTypes = { - history: PropTypes.object - } + /** + * PropTypes + * + * @since 6.0 + */ + static propTypes = { + navigate: PropTypes.func, + }; - /** - * Handle advanced button click and open the font manager modal - * - * @param e: object - * - * @since 6.0 - */ - handleClick = e => { - e.preventDefault() + /** + * Handle advanced button click and open the font manager modal + * + * @param { Event } e + * + * @since 6.0 + */ + handleClick = (e) => { + e.preventDefault(); - this.props.history.push('/fontmanager/') - } + this.props.navigate('/fontmanager/'); + }; - /** - * Display advanced button UI - * - * @since 6.0 - */ - render () { - return ( - - ) - } + /** + * Display advanced button UI + * + * @since 6.0 + */ + render() { + return ( + + ); + } } -export default AdvancedButton +export default AdvancedButton; diff --git a/src/assets/js/react/components/FontManager/FontList.js b/src/assets/js/react/components/FontManager/FontList.js index 7251e8436..71f5f6c3d 100644 --- a/src/assets/js/react/components/FontManager/FontList.js +++ b/src/assets/js/react/components/FontManager/FontList.js @@ -1,15 +1,15 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' +import React from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; /* Components */ -import FontListHeader from './FontListHeader' -import FontListItems from './FontListItems' -import FontListSkeleton from './FontListSkeleton' -import FontListAlertMessage from './FontListAlertMessage' +import FontListHeader from './FontListHeader'; +import FontListItems from './FontListItems'; +import FontListSkeleton from './FontListSkeleton'; +import FontListAlertMessage from './FontListAlertMessage'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -18,58 +18,80 @@ import FontListAlertMessage from './FontListAlertMessage' /** * Display font list UI * - * @param id - * @param loading - * @param fontList - * @param searchResult - * @param error - * @param history - * @returns {JSX.Element} + * @param { Object } props + * @param { string } props.id + * @param { boolean } props.loading + * @param { Array } props.fontList + * @param { Object } props.searchResult + * @param { Object } props.msg + * @param { Object } props.msg.error + * + * @return {JSX.Element} FontList component * * @since 6.0 */ -export const FontList = ({ id, loading, fontList, searchResult, msg: { error }, history }) => { - const fontListError = error && error.fontList - const fontListEmpty = fontList.length === 0 && !searchResult - const checkSearchResult = (searchResult && searchResult.length === 0) || !searchResult - const latestData = fontList.length > 0 && !searchResult - const emptySearchResult = (!fontListError && !loading) && (!latestData && checkSearchResult) +export const FontList = ({ + id, + loading, + fontList, + searchResult, + msg: { error }, + navigate, +}) => { + const fontListError = error && error.fontList; + const fontListEmpty = fontList.length === 0 && !searchResult; + const checkSearchResult = + (searchResult && searchResult.length === 0) || !searchResult; + const latestData = fontList.length > 0 && !searchResult; + const emptySearchResult = + !fontListError && !loading && !latestData && checkSearchResult; - return ( -
- + return ( +
+ - {loading ? : } + {loading ? ( + + ) : ( + + )} - {fontListEmpty && emptySearchResult && } + {fontListEmpty && emptySearchResult && ( + + )} - {!fontListEmpty && emptySearchResult && } + {!fontListEmpty && emptySearchResult && } - {fontListError && } -
- ) -} + {fontListError && } +
+ ); +}; /** * Map redux state to props * - * @param state: object + * @param { Object } state + * @param { Object } state.fontManager * - * @returns {{ + * @return {{ * loading: boolean, - * fontList: array of object, - * searchResult: (null || array of object), - * msg: object, - * }} + * fontList: Array, + * searchResult: (null | Array), + * msg: Object, + * }} mappedState * * @since 6.0 */ -const mapStateToProps = state => ({ - loading: state.fontManager.loading, - fontList: state.fontManager.fontList, - searchResult: state.fontManager.searchResult, - msg: state.fontManager.msg -}) +const mapStateToProps = (state) => ({ + loading: state.fontManager.loading, + fontList: state.fontManager.fontList, + searchResult: state.fontManager.searchResult, + msg: state.fontManager.msg, +}); /** * PropTypes @@ -77,15 +99,15 @@ const mapStateToProps = state => ({ * @since 6.0 */ FontList.propTypes = { - id: PropTypes.string, - loading: PropTypes.bool.isRequired, - fontList: PropTypes.arrayOf(PropTypes.object).isRequired, - searchResult: PropTypes.oneOfType([ - PropTypes.oneOf([null]).isRequired, - PropTypes.arrayOf(PropTypes.object).isRequired - ]), - msg: PropTypes.object.isRequired, - history: PropTypes.object.isRequired -} + id: PropTypes.string, + loading: PropTypes.bool.isRequired, + fontList: PropTypes.arrayOf(PropTypes.object).isRequired, + searchResult: PropTypes.oneOfType([ + PropTypes.oneOf([null]).isRequired, + PropTypes.arrayOf(PropTypes.object).isRequired, + ]), + msg: PropTypes.object.isRequired, + navigate: PropTypes.func.isRequired, +}; -export default connect(mapStateToProps, {})(FontList) +export default connect(mapStateToProps, {})(FontList); diff --git a/src/assets/js/react/components/FontManager/FontListAlertMessage.js b/src/assets/js/react/components/FontManager/FontListAlertMessage.js index fda93e78b..7f3f28139 100644 --- a/src/assets/js/react/components/FontManager/FontListAlertMessage.js +++ b/src/assets/js/react/components/FontManager/FontListAlertMessage.js @@ -1,12 +1,15 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' +import React from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; /* Redux actions */ -import { getCustomFontList, resetSearchResult } from '../../actions/fontManager' +import { + getCustomFontList as getCustomFontListAction, + resetSearchResult as resetSearchResultAction, +} from '../../actions/fontManager'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -15,29 +18,47 @@ import { getCustomFontList, resetSearchResult } from '../../actions/fontManager' /** * Display alert message for font list UI * - * @param empty - * @param error - * @param getCustomFontList - * @param resetSearchResult + * @param { Object } props + * @param { boolean } props.empty + * @param { string } props.error + * @param { Function } props.getCustomFontList + * @param { Function } props.resetSearchResult * * @since 6.0 */ -export const FontListAlertMessage = ({ empty, error, getCustomFontList, resetSearchResult }) => { - const fontListEmpty = {GFPDF.fontListEmpty} - const searchResultEmpty = ( - - {GFPDF.searchResultEmpty} Clear Search. - - ) - const apiError =

{error}

- const displayContent = empty ? fontListEmpty : !error ? searchResultEmpty : apiError +export const FontListAlertMessage = ({ + empty, + error, + getCustomFontList, + resetSearchResult, +}) => { + const fontListEmpty = {GFPDF.fontListEmpty}; + const searchResultEmpty = ( + + {GFPDF.searchResultEmpty}{' '} + + + ); + const apiError = ( + + ); + // const displayContent = empty ? fontListEmpty : !error ? searchResultEmpty : apiError + const hasNoError = !error ? searchResultEmpty : apiError; + const displayContent = empty ? fontListEmpty : hasNoError; - return ( -
- {displayContent} -
- ) -} + return ( +
+ {displayContent} +
+ ); +}; /** * PropTypes @@ -45,11 +66,11 @@ export const FontListAlertMessage = ({ empty, error, getCustomFontList, resetSea * @since 6.0 */ FontListAlertMessage.propTypes = { - empty: PropTypes.bool, - error: PropTypes.string, - getCustomFontList: PropTypes.func.isRequired, - resetSearchResult: PropTypes.func.isRequired -} + empty: PropTypes.bool, + error: PropTypes.string, + getCustomFontList: PropTypes.func.isRequired, + resetSearchResult: PropTypes.func.isRequired, +}; /** * Connect and dispatch redux actions as props @@ -57,6 +78,6 @@ FontListAlertMessage.propTypes = { * @since 6.0 */ export default connect(null, { - getCustomFontList, - resetSearchResult -})(FontListAlertMessage) + getCustomFontList: getCustomFontListAction, + resetSearchResult: resetSearchResultAction, +})(FontListAlertMessage); diff --git a/src/assets/js/react/components/FontManager/FontListHeader.js b/src/assets/js/react/components/FontManager/FontListHeader.js index 903954477..059b94860 100644 --- a/src/assets/js/react/components/FontManager/FontListHeader.js +++ b/src/assets/js/react/components/FontManager/FontListHeader.js @@ -1,8 +1,8 @@ /* Dependencies */ -import React from 'react' +import React from 'react'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -14,14 +14,14 @@ import React from 'react' * @since 6.0 */ const FontListHeader = () => ( -
-
{GFPDF.fontListInstalledFonts}
-
{GFPDF.fontListRegular}
-
{GFPDF.fontListItalics}
-
{GFPDF.fontListBold}
-
{GFPDF.fontListBoldItalics}
-
-
-) +
+
{GFPDF.fontListInstalledFonts}
+
{GFPDF.fontListRegular}
+
{GFPDF.fontListItalics}
+
{GFPDF.fontListBold}
+
{GFPDF.fontListBoldItalics}
+
+
+); -export default FontListHeader +export default FontListHeader; diff --git a/src/assets/js/react/components/FontManager/FontListIcon.js b/src/assets/js/react/components/FontManager/FontListIcon.js index 4863e7a00..7ae9b660a 100644 --- a/src/assets/js/react/components/FontManager/FontListIcon.js +++ b/src/assets/js/react/components/FontManager/FontListIcon.js @@ -1,9 +1,9 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' +import React from 'react'; +import PropTypes from 'prop-types'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -12,15 +12,16 @@ import PropTypes from 'prop-types' /** * Display 'x' or 'check' icon in font detail to indicate if font variant is installed or not * - * @param font + * @param { Object } props + * @param { string } props.font * * @since 6.0 */ const FontListIcon = ({ font }) => ( -
- -
-) +
+ +
+); /** * PropTypes @@ -28,7 +29,7 @@ const FontListIcon = ({ font }) => ( * @since 6.0 */ FontListIcon.propTypes = { - font: PropTypes.string.isRequired -} + font: PropTypes.string.isRequired, +}; -export default FontListIcon +export default FontListIcon; diff --git a/src/assets/js/react/components/FontManager/FontListItems.js b/src/assets/js/react/components/FontManager/FontListItems.js index 3fa6d541e..d8b8051f8 100644 --- a/src/assets/js/react/components/FontManager/FontListItems.js +++ b/src/assets/js/react/components/FontManager/FontListItems.js @@ -1,17 +1,22 @@ /* Dependencies */ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; /* Redux actions */ -import { clearAddFontMsg, deleteFont, selectFont, moveSelectedFontToTop } from '../../actions/fontManager' +import { + clearAddFontMsg as clearAddFontMsgAction, + deleteFont as deleteFontAction, + selectFont as selectFontAction, + moveSelectedFontToTop as moveSelectedFontAction, +} from '../../actions/fontManager'; /* Components */ -import FontListIcon from './FontListIcon' -import Spinner from '../Spinner' +import FontListIcon from './FontListIcon'; +import Spinner from '../Spinner'; /* Utilities */ -import { toggleUpdateFont } from '../../utilities/FontManager/toggleUpdateFont' +import { toggleUpdateFont } from '../../utilities/FontManager/toggleUpdateFont'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -23,393 +28,446 @@ import { toggleUpdateFont } from '../../utilities/FontManager/toggleUpdateFont' * @since 6.0 */ export class FontListItems extends Component { - /** - * PropTypes - * - * @since 6.0 - */ - static propTypes = { - id: PropTypes.string, - history: PropTypes.object.isRequired, - clearAddFontMsg: PropTypes.func.isRequired, - msg: PropTypes.object.isRequired, - deleteFont: PropTypes.func.isRequired, - selectFont: PropTypes.func.isRequired, - moveSelectedFontToTop: PropTypes.func.isRequired, - fontList: PropTypes.arrayOf( - PropTypes.shape({ - font_name: PropTypes.string.isRequired, - id: PropTypes.string.isRequired, - regular: PropTypes.string.isRequired, - italics: PropTypes.string.isRequired, - bold: PropTypes.string.isRequired, - bolditalics: PropTypes.string.isRequired - }) - ).isRequired, - searchResult: PropTypes.oneOfType([ - PropTypes.oneOf([null]).isRequired, - PropTypes.arrayOf(PropTypes.object).isRequired - ]), - selectedFont: PropTypes.string.isRequired, - loading: PropTypes.bool.isRequired - } - - /** - * Initialize component state - * - * @type {{ disableSelectFontName: boolean, deleteId: string, moveSelectedFontToTop: boolean }} - * - * @since 6.0 - */ - state = { - disableSelectFontName: false, - deleteId: '', - moveSelectedFontToTop: true - } - - /** - * On mount, Call the method handleDisableSelectFields() - * - * @since 6.0 - */ - componentDidMount () { - const { selectedFont } = this.props - - this.handleDisableSelectFields() - - /* Move selected font at the top of the list */ - if (selectedFont) { - this.handleMoveSelectedFontAtTheTopOfTheList(selectedFont) - } - } - - /** - * If component did update and new props are received we'll check if the methods - * handleResetLoadingState() and toggleUpdateFont() will be called - * - * @param prevProps: object - * - * @since 6.0 - */ - componentDidUpdate (prevProps) { - const { history, loading, fontList, selectedFont, id } = this.props - const updateFontVisible = document.querySelector('.update-font.show') - - /* Reset/Clear deleteId loading state */ - if (prevProps.loading !== loading && !loading) { - this.handleResetLoadingState() - } - - /* Remove update font panel after font is successfully deleted */ - if (prevProps.loading !== loading && prevProps.fontList !== fontList && updateFontVisible) { - toggleUpdateFont(history) - } - - /* Move selected font at the top of the list */ - if (prevProps.selectedFont === '' && selectedFont && !id && this.state.moveSelectedFontToTop) { - this.handleMoveSelectedFontAtTheTopOfTheList(selectedFont) - } - } - - /** - * Check URL to distinguish the current location. If current location is under - * tools tab then disable select font name functionality (radio button) - * - * @since 6.0 - */ - handleDisableSelectFields = () => { - const tabLocation = window.location.search.substr(window.location.search.lastIndexOf('=') + 1) - - if (tabLocation === 'tools') { - return this.setState({ disableSelectFontName: true }) - } - - this.handleSetSelectedFontNameValue(tabLocation) - } - - /** - * Handle the functionality to select and set default font type to be used in PDFs (radio button) - * - * @param location: string - * - * @since 6.0 - */ - handleSetSelectedFontNameValue = (location) => { - let fontManagerSelectBoxValue - const { fontList, selectFont } = this.props - - /* If location not under Global/General settings */ - if (location !== 'PDF' && location !== 'general') { - fontManagerSelectBoxValue = document.querySelector('#gfpdf_settings\\[font\\]').value - } else { - fontManagerSelectBoxValue = document.querySelector('#gfpdf_settings\\[default_font\\]').value - } - - /* Do nothing if selected value doesn't exist on font manager fontList */ - if (!this.handleCheckSelectBoxValue(fontList, fontManagerSelectBoxValue)) { - /* Call redux action selectFont() */ - return selectFont('') - } - - /* Call redux action selectFont() */ - selectFont(fontManagerSelectBoxValue) - } - - /** - * Handle check if font manager default selected font type is listed on custom font list - * - * @param fontList: array - * @param value: string - * - * @returns { boolean } - * - * @since 6.0 - */ - handleCheckSelectBoxValue = (fontList, value) => { - const checkIfValueExist = fontList && fontList.filter(font => font.id === value)[0] - - if (!checkIfValueExist) { - return false - } - - return true - } - - /** - * Reset deleteId state. The state of deleteId is used to match the current delete id request and - * trigger loading spinner - * - * @since 6.0 - */ - handleResetLoadingState = () => { - this.setState({ deleteId: '' }) - } - - /** - * Move selected font at the very top of the font list - * - * @param selectedFont: string - * - * @since 6.0 - */ - handleMoveSelectedFontAtTheTopOfTheList = selectedFont => { - this.setState({ moveSelectedFontToTop: false }) - - this.props.moveSelectedFontToTop(selectedFont) - } - - /** - * Handle font click to display or hide update font panel - * - * @param fontId: string - * - * @since 6.0 - */ - handleFontClick = fontId => { - const { id, history, clearAddFontMsg, msg: { success, error } } = this.props - - /* Remove previous msg */ - if ((success && success.addFont) || (error && error.addFont)) { - /* Call redux action clearAddFontMsg */ - clearAddFontMsg() - } - - if (id === fontId) { - return toggleUpdateFont(history) - } - - toggleUpdateFont(history, fontId) - } - - /** - * Listen to an 'enter' keyboard event on font list item - * - * @param e: object - * @param fontId: string - * - * @since 6.0 - */ - handleFontClickKeypress = (e, fontId) => { - const enter = 13 - const space = 32 - - /* Check if a keyboard keypress is 'enter' (13) and call the method handleFontClick() */ - if (e.keyCode === enter || e.keyCode === space) { - this.handleFontClick(fontId) - } - } - - /** - * Handle request of font deletion - * - * @param e: object - * @param fontId: string - * - * @since 6.0 - */ - handleDeleteFont = (e, fontId) => { - e.stopPropagation() - - this.setState({ deleteId: fontId }) - - /* Fire a native window alert box to confirm deletion request */ - if (window.confirm(GFPDF.fontManagerDeleteFontConfirmation)) { - /* Call redux action deleteFont */ - this.props.deleteFont(fontId) - } - } - - /** - * Listen to an 'enter' keyboard event for font deletion - * - * @param e: object - * @param fontId: string - * - * @since 6.0 - */ - handleDeleteFontKeypress = (e, fontId) => { - const enter = 13 - const space = 32 - - /* Check if a keyboard keypress is 'enter' (13) and call the method handleDeleteFont() */ - if (e.keyCode === enter || e.keyCode === space) { - this.handleDeleteFont(e, fontId) - } - } - - /** - * Handle the process of selecting and deselecting of font type/name (radio button) - * - * @param e: object - * - * @since 6.0 - */ - handleSelectFont = e => { - this.props.selectFont(e.target.value) - - const installedFonts = document.querySelectorAll('.select-font-name') - - /* Handle font list keyboard accessibility thing */ - installedFonts.forEach(item => { - if (item.value === e.target.value) { - item.checked = true - - return - } - - item.checked = false - }) - } - - /** - * Listen to an 'enter' or 'space' keyboard event for selecting a font type/name (radio button) - * - * @param e: object - * - * @since 6.0 - */ - handleSelectFontKeypress = e => { - const enter = 13 - const space = 32 - - /* - * Check if a keyboard keypress is 'enter' (13) or 'space' (12) and call the method handleDeleteFont() - */ - if (e.keyCode === enter || e.keyCode === space) { - e.preventDefault() - e.stopPropagation() - - this.handleSelectFont(e) - } - } - - /** - * Display the font list items UI - * - * @since 6.0 - */ - render () { - const updateFontVisible = document.querySelector('.update-font.show') - const { disableSelectFontName, deleteId } = this.state - const { id, loading, fontList, searchResult, selectedFont } = this.props - const list = !searchResult ? fontList : searchResult - const tabIndex = updateFontVisible ? '-1' : '144' - - return ( -
- {list && list.map(font => { - return ( -
this.handleFontClick(font.id)} - onKeyDown={e => this.handleFontClickKeypress(e, font.id)} - tabIndex={tabIndex} - role='option' - > - - {!disableSelectFontName && ( - this.handleSelectFont(e)} - onClick={e => e.stopPropagation()} - onKeyDown={e => this.handleSelectFontKeypress(e)} - checked={font.id === selectedFont} - aria-label={GFPDF.fontManagerSelectFontAriaLabel + ': ' + font.font_name} - tabIndex={tabIndex} - /> - )} - {font.font_name} - - - - - - - - {loading && (deleteId === font.id) - ? - : ( - this.handleDeleteFont(e, font.id)} - onKeyDown={e => this.handleDeleteFontKeypress(e, font.id)} - tabIndex={tabIndex} - /> - )} -
- ) - })} -
- ) - } + /** + * PropTypes + * + * @since 6.0 + */ + static propTypes = { + id: PropTypes.string, + navigate: PropTypes.func.isRequired, + clearAddFontMsg: PropTypes.func.isRequired, + msg: PropTypes.object.isRequired, + deleteFont: PropTypes.func.isRequired, + selectFont: PropTypes.func.isRequired, + moveSelectedFontToTop: PropTypes.func.isRequired, + fontList: PropTypes.arrayOf( + PropTypes.shape({ + font_name: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, + regular: PropTypes.string.isRequired, + italics: PropTypes.string.isRequired, + bold: PropTypes.string.isRequired, + bolditalics: PropTypes.string.isRequired, + }) + ).isRequired, + searchResult: PropTypes.oneOfType([ + PropTypes.oneOf([null]).isRequired, + PropTypes.arrayOf(PropTypes.object).isRequired, + ]), + selectedFont: PropTypes.string.isRequired, + loading: PropTypes.bool.isRequired, + }; + + /** + * Initialize component state + * + * @type {{ disableSelectFontName: boolean, deleteId: string, moveSelectedFontToTop: boolean }} + * + * @since 6.0 + */ + state = { + disableSelectFontName: false, + deleteId: '', + moveSelectedFontToTop: true, + }; + + /** + * On mount, Call the method handleDisableSelectFields() + * + * @since 6.0 + */ + componentDidMount() { + const { selectedFont } = this.props; + + this.handleDisableSelectFields(); + + /* Move selected font at the top of the list */ + if (selectedFont) { + this.handleMoveSelectedFontAtTheTopOfTheList(selectedFont); + } + } + + /** + * If component did update and new props are received we'll check if the methods + * handleResetLoadingState() and toggleUpdateFont() will be called + * + * @param { Object } prevProps + * + * @since 6.0 + */ + componentDidUpdate(prevProps) { + const { navigate, loading, fontList, selectedFont, id } = this.props; + const updateFontVisible = document.querySelector('.update-font.show'); + + /* Reset/Clear deleteId loading state */ + if (prevProps.loading !== loading && !loading) { + this.handleResetLoadingState(); + } + + /* Remove update font panel after font is successfully deleted */ + if ( + prevProps.loading !== loading && + prevProps.fontList !== fontList && + updateFontVisible + ) { + toggleUpdateFont(navigate); + } + + /* Move selected font at the top of the list */ + if ( + prevProps.selectedFont === '' && + selectedFont && + !id && + this.state.moveSelectedFontToTop + ) { + this.handleMoveSelectedFontAtTheTopOfTheList(selectedFont); + } + } + + /** + * Check URL to distinguish the current location. If current location is under + * tools tab then disable select font name functionality (radio button) + * + * @since 6.0 + */ + handleDisableSelectFields = () => { + const tabLocation = window.location.search.substr( + window.location.search.lastIndexOf('=') + 1 + ); + + if (tabLocation === 'tools') { + return this.setState({ disableSelectFontName: true }); + } + + this.handleSetSelectedFontNameValue(tabLocation); + }; + + /** + * Handle the functionality to select and set default font type to be used in PDFs (radio button) + * + * @param { string } location + * + * @since 6.0 + */ + handleSetSelectedFontNameValue = (location) => { + let fontManagerSelectBoxValue; + const { fontList, selectFont } = this.props; + + /* If location not under Global/General settings */ + if (location !== 'PDF' && location !== 'general') { + fontManagerSelectBoxValue = document.querySelector( + '#gfpdf_settings\\[font\\]' + ).value; + } else { + fontManagerSelectBoxValue = document.querySelector( + '#gfpdf_settings\\[default_font\\]' + ).value; + } + + /* Do nothing if selected value doesn't exist on font manager fontList */ + if ( + !this.handleCheckSelectBoxValue(fontList, fontManagerSelectBoxValue) + ) { + /* Call redux action selectFont() */ + return selectFont(''); + } + + /* Call redux action selectFont() */ + selectFont(fontManagerSelectBoxValue); + }; + + /** + * Handle check if font manager default selected font type is listed on custom font list + * + * @param { Array } fontList + * @param { string } value + * + * @return { boolean } condition value + * + * @since 6.0 + */ + handleCheckSelectBoxValue = (fontList, value) => { + const checkIfValueExist = + fontList && fontList.filter((font) => font.id === value)[0]; + + if (!checkIfValueExist) { + return false; + } + + return true; + }; + + /** + * Reset deleteId state. The state of deleteId is used to match the current delete id request and + * trigger loading spinner + * + * @since 6.0 + */ + handleResetLoadingState = () => { + this.setState({ deleteId: '' }); + }; + + /** + * Move selected font at the very top of the font list + * + * @param { string } selectedFont + * + * @since 6.0 + */ + handleMoveSelectedFontAtTheTopOfTheList = (selectedFont) => { + this.setState({ moveSelectedFontToTop: false }); + + this.props.moveSelectedFontToTop(selectedFont); + }; + + /** + * Handle font click to display or hide update font panel + * + * @param { string } fontId + * + * @since 6.0 + */ + handleFontClick = (fontId) => { + const { + id, + navigate, + clearAddFontMsg, + msg: { success, error }, + } = this.props; + + /* Remove previous msg */ + if ((success && success.addFont) || (error && error.addFont)) { + /* Call redux action clearAddFontMsg */ + clearAddFontMsg(); + } + + if (id === fontId) { + return toggleUpdateFont(navigate); + } + + toggleUpdateFont(navigate, fontId); + }; + + /** + * Listen to an 'enter' keyboard event on font list item + * + * @param { KeyboardEvent } e + * @param { string } fontId + * + * @since 6.0 + */ + handleFontClickKeypress = (e, fontId) => { + const enter = 'Enter'; + const space = ' '; + + /* Check if a keyboard keypress is 'enter' (13) and call the method handleFontClick() */ + if (e.key === enter || e.key === space) { + this.handleFontClick(fontId); + } + }; + + /** + * Handle request of font deletion + * + * @param { Event } e + * @param { string } fontId + * + * @since 6.0 + */ + handleDeleteFont = (e, fontId) => { + e.stopPropagation(); + + this.setState({ deleteId: fontId }); + + /* Fire a native window alert box to confirm deletion request */ + if (window.confirm(GFPDF.fontManagerDeleteFontConfirmation)) { + /* Call redux action deleteFont */ + this.props.deleteFont(fontId); + } + }; + + /** + * Listen to an 'enter' keyboard event for font deletion + * + * @param { KeyboardEvent } e + * @param { string } fontId + * + * @since 6.0 + */ + handleDeleteFontKeypress = (e, fontId) => { + const enter = 'Enter'; + const space = ' '; + + /* Check if a keyboard keypress is 'enter' (13) and call the method handleDeleteFont() */ + if (e.key === enter || e.key === space) { + this.handleDeleteFont(e, fontId); + } + }; + + /** + * Handle the process of selecting and deselecting of font type/name (radio button) + * + * @param { Event & { target: InputEvent } } e + * + * @since 6.0 + */ + handleSelectFont = (e) => { + this.props.selectFont(e.target.value); + + const installedFonts = document.querySelectorAll('.select-font-name'); + + /* Handle font list keyboard accessibility thing */ + installedFonts.forEach((item) => { + if (item.value === e.target.value) { + item.checked = true; + + return; + } + + item.checked = false; + }); + }; + + /** + * Listen to an 'enter' or 'space' keyboard event for selecting a font type/name (radio button) + * + * @param { KeyboardEvent } e + * + * @since 6.0 + */ + handleSelectFontKeypress = (e) => { + const enter = 'Enter'; + const space = ' '; + + /* + * Check if a keyboard keypress is 'enter' (13) or 'space' (12) and call the method handleDeleteFont() + */ + if (e.key === enter || e.key === space) { + e.preventDefault(); + e.stopPropagation(); + + this.handleSelectFont(e); + } + }; + + /** + * Display the font list items UI + * + * @since 6.0 + */ + render() { + const updateFontVisible = document.querySelector('.update-font.show'); + const { disableSelectFontName, deleteId } = this.state; + const { id, loading, fontList, searchResult, selectedFont } = + this.props; + const list = !searchResult ? fontList : searchResult; + const tabIndex = updateFontVisible ? '-1' : '144'; + + return ( +
+ {list && + list.map((font) => { + return ( +
this.handleFontClick(font.id)} + onKeyDown={(e) => + this.handleFontClickKeypress(e, font.id) + } + tabIndex={tabIndex} + role="option" + > + + {!disableSelectFontName && ( + + this.handleSelectFont(e) + } + onClick={(e) => e.stopPropagation()} + onKeyDown={(e) => + this.handleSelectFontKeypress(e) + } + checked={font.id === selectedFont} + aria-label={ + GFPDF.fontManagerSelectFontAriaLabel + + ': ' + + font.font_name + } + tabIndex={tabIndex} + /> + )} + {font.font_name} + + + + + + + + {loading && deleteId === font.id ? ( + + ) : ( + + this.handleDeleteFont(e, font.id) + } + onKeyDown={(e) => + this.handleDeleteFontKeypress( + e, + font.id + ) + } + tabIndex={tabIndex} + /> + )} +
+ ); + })} +
+ ); + } } /** * Map redux state to props * - * @param state: object + * @param { Object } state + * @param { Object } state.fontManager * - * @returns {{ + * @return {{ * loading: boolean, - * fontList: array of object, - * searchResult: null || array of object, + * fontList: Array, + * searchResult: null | Array, * selectedFont: string, * msg: object, - * }} + * }} mappedState * * @since 6.0 */ -const mapStateToProps = state => ({ - loading: state.fontManager.deleteFontLoading, - fontList: state.fontManager.fontList, - searchResult: state.fontManager.searchResult, - selectedFont: state.fontManager.selectedFont, - msg: state.fontManager.msg -}) +const mapStateToProps = (state) => ({ + loading: state.fontManager.deleteFontLoading, + fontList: state.fontManager.fontList, + searchResult: state.fontManager.searchResult, + selectedFont: state.fontManager.selectedFont, + msg: state.fontManager.msg, +}); /** * Connect and dispatch redux actions as props @@ -417,8 +475,8 @@ const mapStateToProps = state => ({ * @since 6.0 */ export default connect(mapStateToProps, { - clearAddFontMsg, - deleteFont, - selectFont, - moveSelectedFontToTop -})(FontListItems) + clearAddFontMsg: clearAddFontMsgAction, + deleteFont: deleteFontAction, + selectFont: selectFontAction, + moveSelectedFontToTop: moveSelectedFontAction, +})(FontListItems); diff --git a/src/assets/js/react/components/FontManager/FontListSkeleton.js b/src/assets/js/react/components/FontManager/FontListSkeleton.js index f9142039b..96f922c0b 100644 --- a/src/assets/js/react/components/FontManager/FontListSkeleton.js +++ b/src/assets/js/react/components/FontManager/FontListSkeleton.js @@ -1,8 +1,8 @@ /* Dependencies */ -import React from 'react' +import React from 'react'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -14,32 +14,35 @@ import React from 'react' * @since 6.0 */ const FontListSkeleton = () => { - const fontList = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] + const fontList = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; - return ( -
- {fontList.map(font => ( -
-
- -
- -
- -
-
- -
-
- -
-
- -
-
- ))} -
- ) -} + return ( +
+ {fontList.map((font) => ( +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ ))} +
+ ); +}; -export default FontListSkeleton +export default FontListSkeleton; diff --git a/src/assets/js/react/components/FontManager/FontManager.js b/src/assets/js/react/components/FontManager/FontManager.js index cccc61220..532d460f8 100644 --- a/src/assets/js/react/components/FontManager/FontManager.js +++ b/src/assets/js/react/components/FontManager/FontManager.js @@ -1,12 +1,12 @@ /* Dependencies */ -import React, { Component } from 'react' -import PropTypes from 'prop-types' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; /* Components */ -import FontManagerHeader from './FontManagerHeader' -import FontManagerBody from './FontManagerBody' +import FontManagerHeader from './FontManagerHeader'; +import FontManagerBody from './FontManagerBody'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -18,88 +18,97 @@ import FontManagerBody from './FontManagerBody' * @since 6.0 */ export class FontManager extends Component { - /** - * PropTypes - * - * @since 6.0 - */ - static propTypes = { - id: PropTypes.string, - history: PropTypes.object.isRequired - } + /** + * PropTypes + * + * @since 6.0 + */ + static propTypes = { + params: PropTypes.object, + navigate: PropTypes.func.isRequired, + }; - /** - * @since 6.0 - */ - constructor (props) { - super(props) - this.handleFocus = this.handleFocus.bind(this) - } + /** + * @param { Object } props + * + * @since 6.0 + */ + constructor(props) { + super(props); + this.handleFocus = this.handleFocus.bind(this); + } - /** - * On mount, add focus event to document option on mount - * Also, if focus isn't currently applied to the search box we'll apply it - * to our container to help with tabbing between elements - * - * @since 6.0 - */ - componentDidMount () { - document.addEventListener('focus', this.handleFocus, true) + /** + * On mount, add focus event to document option on mount + * Also, if focus isn't currently applied to the search box we'll apply it + * to our container to help with tabbing between elements + * + * @since 6.0 + */ + componentDidMount() { + document.addEventListener('focus', this.handleFocus, true); - /* Add focus if not currently applied to search box */ - if (document.activeElement && document.activeElement.className !== 'wp-filter-search') { - this.container.focus() - } - } + /* Add focus if not currently applied to search box */ + if ( + // eslint-disable-next-line @wordpress/no-global-active-element + document.activeElement && + // eslint-disable-next-line @wordpress/no-global-active-element + document.activeElement.className !== 'wp-filter-search' + ) { + this.container.focus(); + } + } - /** - * Cleanup our document event listeners - * - * @since 6.0 - */ - componentWillUnmount () { - document.removeEventListener('focus', this.handleFocus, true) - } + /** + * Cleanup our document event listeners + * + * @since 6.0 + */ + componentWillUnmount() { + document.removeEventListener('focus', this.handleFocus, true); + } - /** - * When a focus event is fired and it's not apart of any DOM elements in our - * container we will focus the container instead. In most cases this keeps the focus from - * jumping outside our font manager container and allows for better keyboard navigation - * - * @param e: object - * - * @since 6.0 - */ - handleFocus (e) { - if (!this.container.contains(e.target)) { - e.stopPropagation() - this.container.focus() - } - } + /** + * When a focus event is fired and it's not apart of any DOM elements in our + * container we will focus the container instead. In most cases this keeps the focus from + * jumping outside our font manager container and allows for better keyboard navigation + * + * @param { Event } e + * + * @since 6.0 + */ + handleFocus(e) { + if (!this.container.contains(e.target)) { + e.stopPropagation(); + this.container.focus(); + } + } - /** - * Display font manager UI - * - * @since 6.0 - */ - render () { - const { id, history } = this.props + /** + * Display font manager UI + * + * @since 6.0 + */ + render() { + const { params, navigate } = this.props; + const { id } = params; - return ( -
(this.container = node)} - tabIndex='140' - > -
-
- + return ( +
(this.container = node)} + // eslint-disable-next-line jsx-a11y/tabindex-no-positive + tabIndex="140" + > +
+
+ - -
-
- ) - } + +
+
+ ); + } } -export default FontManager +export default FontManager; diff --git a/src/assets/js/react/components/FontManager/FontManagerBody.js b/src/assets/js/react/components/FontManager/FontManagerBody.js index b0c59d1d8..118d3838e 100644 --- a/src/assets/js/react/components/FontManager/FontManagerBody.js +++ b/src/assets/js/react/components/FontManager/FontManagerBody.js @@ -1,31 +1,34 @@ /* Dependencies */ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; /* Redux actions */ import { - getCustomFontList, - addFont, - editFont, - validationError, - deleteVariantError, - selectFont, - clearAddFontMsg, - clearDropzoneError -} from '../../actions/fontManager' + getCustomFontList as getCustomFontListAction, + addFont as addFontAction, + editFont as editFontAction, + validationError as validationErrorAction, + deleteVariantError as deleteVariantAction, + selectFont as selectFontAction, + clearAddFontMsg as clearAddFontMsgAction, + clearDropzoneError as clearDropzoneErrorAction, +} from '../../actions/fontManager'; /* Components */ -import Alert from '../Alert/Alert' -import SearchBox from './SearchBox' -import FontList from './FontList' -import AddFont from './AddFont' -import UpdateFont from './UpdateFont' -import initialState from './InitialAddUpdateState' +import Alert from '../Alert/Alert'; +import SearchBox from './SearchBox'; +import FontList from './FontList'; +import AddFont from './AddFont'; +import UpdateFont from './UpdateFont'; +import initialState from './InitialAddUpdateState'; /* Utilities */ -import { adjustFontListHeight } from '../../utilities/FontManager/adjustFontListHeight' -import { toggleUpdateFont, addClass } from '../../utilities/FontManager/toggleUpdateFont' +import { adjustFontListHeight } from '../../utilities/FontManager/adjustFontListHeight'; +import { + toggleUpdateFont, + addClass, +} from '../../utilities/FontManager/toggleUpdateFont'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -37,577 +40,611 @@ import { toggleUpdateFont, addClass } from '../../utilities/FontManager/toggleUp * @since 6.0 */ export class FontManagerBody extends Component { - /** - * PropTypes - * - * @since 6.0 - */ - static propTypes = { - getCustomFontList: PropTypes.func.isRequired, - id: PropTypes.string, - loading: PropTypes.bool.isRequired, - fontList: PropTypes.arrayOf(PropTypes.object).isRequired, - msg: PropTypes.object.isRequired, - clearDropzoneError: PropTypes.func.isRequired, - clearAddFontMsg: PropTypes.func.isRequired, - editFont: PropTypes.func.isRequired, - validationError: PropTypes.func.isRequired, - deleteVariantError: PropTypes.func.isRequired, - selectFont: PropTypes.func.isRequired, - addFont: PropTypes.func.isRequired, - history: PropTypes.object.isRequired - } - - /** - * Initialize component state - * - * @type {{ - * addFont: { - * fontStyles: { italics: string, bold: string, bolditalics: string, regular: string }, - * disableUpdateButton: boolean, - * id: string, - * label: string, - * validateLabel: boolean, - * validateRegular: boolean - * }, - * updateFont: { - * fontStyles: { italics: string, bold: string, bolditalics: string, regular: string }, - * disableUpdateButton: boolean, - * id: string, - * label: string, - * validateLabel: boolean, - * validateRegular: boolean - * }}} - * - * @since 6.0 - */ - state = { - addFont: initialState, - updateFont: initialState - } - - /** - * On mount, Request custom font list by calling redux action getCustomFontList() - * - * @since 6.0 - */ - componentDidMount () { - const { getCustomFontList, id, history } = this.props - - getCustomFontList() - - /* Auto slide 'update font' panel if refreshed */ - if (id) { - addClass(document.querySelector('.update-font'), history, id) - } - } - - /** - * If component did update and new props are received, - * fires appropriate action based on redux store data - * - * @param prevProps: object - * - * @since 6.0 - */ - componentDidUpdate (prevProps) { - const { id, fontList, msg, history } = this.props - - /* If font name is selected call the method handleRequestFontDetails() */ - if (prevProps.id !== id && id) { - /* Perform check if the accessed ID is valid, if not prevent fatal error event */ - if (!this.handleCheckValidId(fontList, id)) { - return history.push('/fontmanager/') - } - - this.handleRequestFontDetails() - } - - /* If font list did update, call the method handleRequestFontDetails() */ - if (prevProps.fontList !== fontList && fontList && id) { - /* Perform check if the accessed ID is valid, if not prevent fatal error event */ - if (!this.handleCheckValidId(fontList, id)) { - return history.push('/fontmanager/') - } - - this.handleRequestFontDetails() - } - - /* If font is successfully installed, auto select the new added font and slide update font panel */ - if (prevProps.msg !== msg && msg.success && !id) { - /* Check if there's a response message error for fontList */ - if (msg.error && msg.error.fontList) { - return this.handleSetDefaultState() - } - - /* Auto select new added font after a successful submission */ - this.handleAutoSelectNewAddedFont(history, fontList) - } - } - - /** - * Handle check if the current accessed ID is valid/active or not - * - * @param fontList: array of object - * @param id: string - * - * @since 6.0 - */ - handleCheckValidId = (fontList, id) => { - const checkValidId = fontList.filter(font => font.id === id)[0] - - if (!checkValidId) { - return false - } - - return true - } - - /** - * Map current font details from our redux store to component state (updateFont) - * - * @since 6.0 - */ - handleRequestFontDetails = () => { - const { fontList, id } = this.props - const font = fontList.filter(font => font.id === id)[0] - - this.setState({ - addFont: initialState, - updateFont: { - id: font.id, - label: font.font_name, - fontStyles: { - regular: font.regular, - italics: font.italics, - bold: font.bold, - bolditalics: font.bolditalics - }, - validateLabel: true, - validateRegular: true, - disableUpdateButton: true - } - }) - - setTimeout(() => adjustFontListHeight(), 100) - } - - /** - * Set component state back to its default state - * - * @since 6.0 - */ - handleSetDefaultState = () => { - this.setState({ - addFont: initialState, - updateFont: initialState - }) - } - - /** - * Auto select new added font and slide update font panel - * - * @param history: object - * @param fontList: array of object - * - * @since 6.0 - */ - handleAutoSelectNewAddedFont = (history, fontList) => { - const newFontIndex = Object.keys(fontList).slice(-1).pop() - const newFont = fontList[newFontIndex] - - this.props.selectFont(newFont.id) - toggleUpdateFont(history, newFont.id) - } - - /** - * Return current active state (addFont or updateFont) - * - * @param column: string - * - * @returns {{ state: object }} - * - * @since 6.0 - */ - handleGetCurrentColumnState = column => { - const state = (column === 'addFont') ? this.state.addFont : this.state.updateFont - - return state - } - - /** - * Handle deletion process of a font variant (font files drop box) - * - * @param e: class - * @param key: string - * @param state: string - * - * @since 6.0 - */ - handleDeleteFontStyle = (e, key, state) => { - e.preventDefault() - - const { msg: { error }, clearDropzoneError } = this.props - - /* Remove addFont error */ - if (error && error.addFont) { - const forValue = `gfpdf-font-variant-${key}` - const dropZone = document.querySelector(`div[for=${forValue}]`) - - dropZone.classList.remove('error') - /* Call redux action clearDropzoneError() */ - clearDropzoneError(key) - } - - this.handleGetCurrentColumnState(state).fontStyles[key] = '' - this.setState({ - [state]: { - ...this.handleGetCurrentColumnState(state), - validateRegular: true - } - }) - this.forceUpdate() - this.handleUpdateFontState() - } - - /** - * Listen to font name input box field change - * - * @param e: object - * @param state: string - * - * @since 6.0 - */ - handleInputChange = (e, state) => { - const { addFont, updateFont } = this.state - const defaultState = (state === 'addFont') ? addFont : updateFont - - this.setState({ - [state]: { - ...defaultState, - label: e.target.value - } - }, () => this.handleUpdateFontState()) - } - - /** - * Handle process for uploading font variant - * - * @param fontVariant: string - * @param file: file - * @param state: string - * - * @since 6.0 - */ - handleUpload = (fontVariant, file, state) => { - const { msg: { error } } = this.props - const fontFileMissing = (error && typeof error.addFont === 'object') && error.addFont - - /* If error exist delete it first to enable dropping */ - fontFileMissing && Object.entries(fontFileMissing).map(([key]) => { - if (fontVariant === key) { - /* Call redux action deleteVariantError() */ - return this.props.deleteVariantError(fontVariant) - } - - return false - }) - - /* Safeguard file */ - const checkFile = !file ? '' : file - - this.setState({ - [state]: { - ...this.handleGetCurrentColumnState(state), - fontStyles: { - ...this.handleGetCurrentColumnState(state).fontStyles, - [fontVariant]: checkFile - } - } - }, () => this.handleUpdateFontState()) - } - - /** - * Check validation for font name input box field and font files drop box field - * - * @param state: string - * @param label: string - * @param regular: string - * - * @returns { boolean } - * - * @since 6.0 - */ - handleValidateInputFields = (state, label, regular) => { - const defaultState = (state === 'addFont') ? this.state.addFont : this.state.updateFont - const validate = { - validateLabel: true, - validateRegular: true - } - let labelField = false - let regularField = false - - /* Regex will allow only a-z, A-Z, and 0-9 */ - const checkSpecialCharRegex = /^[0-9a-zA-Z ]*$/ - - if (!checkSpecialCharRegex.test(label) || label === '') { - labelField = false - validate.validateLabel = false - } - - if (checkSpecialCharRegex.test(label) && label !== '') { - labelField = true - validate.validateLabel = true - } - - if (!regular) { - regularField = false - validate.validateRegular = false - } - - if (regular) { - regularField = true - validate.validateRegular = true - } - - if (labelField && regularField) { - this.setState({ [state]: { ...defaultState, ...validate } }) - return true - } - - this.setState({ [state]: { ...defaultState, ...validate } }) - /* Call redux action validationError() */ - this.props.validationError() - return false - } - - /** - * Check the update font panel state and disable or enable the update button based on - * new field change - * - * @since 6.0 - */ - handleUpdateFontState = () => { - const { fontList, id } = this.props - - if (id) { - const { label, fontStyles } = this.state.updateFont - const activeFont = fontList.filter(font => font.id === id)[0] - - if ( - activeFont.font_name === label && - activeFont.regular === fontStyles.regular && - activeFont.italics === fontStyles.italics && - activeFont.bold === fontStyles.bold && - activeFont.bolditalics === fontStyles.bolditalics - ) { - return this.setState({ - updateFont: { - ...this.state.updateFont, - disableUpdateButton: true - } - }) - } - - this.setState({ - updateFont: { - ...this.state.updateFont, - disableUpdateButton: false - } - }) - } - } - - /** - * Handle our add font process and call our addFont redux action - * - * @since 6.0 - */ - handleAddFont = () => { - const { label, fontStyles } = this.state.addFont - - /* Check if all fields are valid */ - if (!this.handleValidateInputFields('addFont', label, fontStyles.regular)) { - return - } - - /* Call redux action addFont() */ - this.props.addFont({ label, ...fontStyles }) - } - - /** - * Handle our edit font process and call our editFont redux action - * - * @param id: string - * - * @since 6.0 - */ - handleEditFont = id => { - const { label, fontStyles } = this.state.updateFont - const { fontList, editFont, clearAddFontMsg } = this.props - const data = {} - - /* Check if all fields are valid */ - if (!this.handleValidateInputFields('updateFont', label, fontStyles.regular)) { - return - } - - /* Construct the data to be submitted */ - Object.keys(fontStyles).forEach(key => { - if (typeof fontStyles[key] === 'object' || fontStyles[key] === '') { - data[key] = fontStyles[key] - } - }) - - const currentFont = fontList.filter(font => font.id === id)[0] - const currentFontStyles = { - regular: currentFont.regular, - italics: currentFont.italics, - bold: currentFont.bold, - bolditalics: currentFont.bolditalics - } - - /* Check if there's no changes in current font data */ - if ( - label === currentFont.font_name && - JSON.stringify(fontStyles) === JSON.stringify(currentFontStyles) - ) { - /* Call redux action clearAddFontMsg() */ - return clearAddFontMsg() - } - - /* Call redux action editFont() */ - editFont({ - id, - font: { label, ...data } - }) - } - - /** - * Listen to cancel button click event - * - * @since 6.0 - */ - handleCancelEditFont = () => { - const { history, clearAddFontMsg } = this.props - - toggleUpdateFont(history) - /* Call redux action clearAddFontMsg() */ - clearAddFontMsg() - } - - /** - * Listen to cancel button keyboard press event (space and enter) - * - * @param e: class - * - * @since 6.0 - */ - handleCancelEditFontKeypress = e => { - const enter = 13 - const space = 32 - - if (e.keyCode === enter || e.keyCode === space) { - const { history, clearAddFontMsg } = this.props - - toggleUpdateFont(history) - /* Call redux action clearAddFontMsg() */ - clearAddFontMsg() - } - } - - /** - * Listen to form submit event and distinguish if it's add or edit request - * - * @param e: object - * - * @returns { component method } - * - * @since 6.0 - */ - handleSubmit = e => { - e.preventDefault() - const { id } = this.props - - if (id) { - return this.handleEditFont(id) - } - - this.handleAddFont() - } - - /** - * Display the font manager body UI - * - * @since 6.0 - */ - render () { - const updateFontVisible = document.querySelector('.update-font.show') - const { id, fontList, msg, loading, history } = this.props - - return ( -
-
- - - {msg.error && msg.error.deleteFont && } - - -
- -
- - - -
-
- ) - } + /** + * PropTypes + * + * @since 6.0 + */ + static propTypes = { + getCustomFontList: PropTypes.func.isRequired, + id: PropTypes.string, + loading: PropTypes.bool.isRequired, + fontList: PropTypes.arrayOf(PropTypes.object).isRequired, + msg: PropTypes.object.isRequired, + clearDropzoneError: PropTypes.func.isRequired, + clearAddFontMsg: PropTypes.func.isRequired, + editFont: PropTypes.func.isRequired, + validationError: PropTypes.func.isRequired, + deleteVariantError: PropTypes.func.isRequired, + selectFont: PropTypes.func.isRequired, + addFont: PropTypes.func.isRequired, + navigate: PropTypes.func.isRequired, + pathname: PropTypes.string, + }; + + /** + * Initialize component state + * + * @type {{ + * addFont: { + * fontStyles: { italics: string, bold: string, bolditalics: string, regular: string }, + * disableUpdateButton: boolean, + * id: string, + * label: string, + * validateLabel: boolean, + * validateRegular: boolean + * }, + * updateFont: { + * fontStyles: { italics: string, bold: string, bolditalics: string, regular: string }, + * disableUpdateButton: boolean, + * id: string, + * label: string, + * validateLabel: boolean, + * validateRegular: boolean + * }}} + * + * @since 6.0 + */ + state = { + addFont: initialState, + updateFont: initialState, + }; + + /** + * On mount, Request custom font list by calling redux action getCustomFontList() + * + * @since 6.0 + */ + componentDidMount() { + const { getCustomFontList, id, navigate } = this.props; + + getCustomFontList(); + + /* Auto slide 'update font' panel if refreshed */ + if (id) { + addClass(document.querySelector('.update-font'), navigate, id); + } + } + + /** + * If component did update and new props are received, + * fires appropriate action based on redux store data + * + * @param { Object } prevProps + * + * @since 6.0 + */ + componentDidUpdate(prevProps) { + const { id, fontList, msg, navigate } = this.props; + + /* If font name is selected call the method handleRequestFontDetails() */ + if (prevProps.id !== id && id) { + /* Perform check if the accessed ID is valid, if not prevent fatal error event */ + if (!this.handleCheckValidId(fontList, id)) { + return navigate('/fontmanager/'); + } + + this.handleRequestFontDetails(); + } + + /* If font list did update, call the method handleRequestFontDetails() */ + if (prevProps.fontList !== fontList && fontList && id) { + /* Perform check if the accessed ID is valid, if not prevent fatal error event */ + if (!this.handleCheckValidId(fontList, id)) { + return navigate('/fontmanager/'); + } + + this.handleRequestFontDetails(); + } + + /* If font is successfully installed, auto select the new added font and slide update font panel */ + if (prevProps.msg !== msg && msg.success && !id) { + /* Check if there's a response message error for fontList */ + if (msg.error && msg.error.fontList) { + return this.handleSetDefaultState(); + } + + /* Auto select new added font after a successful submission */ + this.handleAutoSelectNewAddedFont(navigate, fontList); + } + } + + /** + * Handle check if the current accessed ID is valid/active or not + * + * @param { Array } fontList + * @param { string } id + * @since 6.0 + */ + handleCheckValidId = (fontList, id) => { + const checkValidId = fontList.filter((font) => font.id === id)[0]; + + if (!checkValidId) { + return false; + } + + return true; + }; + + /** + * Map current font details from our redux store to component state (updateFont) + * + * @since 6.0 + */ + handleRequestFontDetails = () => { + const { fontList, id } = this.props; + const font = fontList.filter((f) => f.id === id)[0]; + + this.setState({ + addFont: initialState, + updateFont: { + id: font.id, + label: font.font_name, + fontStyles: { + regular: font.regular, + italics: font.italics, + bold: font.bold, + bolditalics: font.bolditalics, + }, + validateLabel: true, + validateRegular: true, + disableUpdateButton: true, + }, + }); + + setTimeout(() => adjustFontListHeight(), 100); + }; + + /** + * Set component state back to its default state + * + * @since 6.0 + */ + handleSetDefaultState = () => { + this.setState({ + addFont: initialState, + updateFont: initialState, + }); + }; + + /** + * Auto select new added font and slide update font panel + * + * @param { Function } navigate + * @param { Array } fontList + * @param { string } pathname + * + * @since 6.0 + */ + handleAutoSelectNewAddedFont = (navigate, fontList, pathname) => { + const newFontIndex = Object.keys(fontList).slice(-1).pop(); + const newFont = fontList[newFontIndex]; + + this.props.selectFont(newFont.id); + toggleUpdateFont(navigate, newFont.id, pathname); + }; + + /** + * Return current active state (addFont or updateFont) + * + * @param { string } column + * + * @return { Object } state + * + * @since 6.0 + */ + handleGetCurrentColumnState = (column) => { + return column === 'addFont' + ? this.state.addFont + : this.state.updateFont; + }; + + /** + * Handle deletion process of a font variant (font files drop box) + * + * @param { Event } e + * @param { string } key + * @param { string } state + * @since 6.0 + */ + handleDeleteFontStyle = (e, key, state) => { + e.preventDefault(); + + const { + msg: { error }, + clearDropzoneError, + } = this.props; + + /* Remove addFont error */ + if (error && error.addFont) { + const forValue = `gfpdf-font-variant-${key}`; + const dropZone = document.querySelector(`div[for=${forValue}]`); + + dropZone.classList.remove('error'); + /* Call redux action clearDropzoneError() */ + clearDropzoneError(key); + } + + this.handleGetCurrentColumnState(state).fontStyles[key] = ''; + this.setState({ + [state]: { + ...this.handleGetCurrentColumnState(state), + validateRegular: true, + }, + }); + this.forceUpdate(); + this.handleUpdateFontState(); + }; + + /** + * Listen to font name input box field change + * + * @param { Event & { target: InputEvent } } e + * @param { string } state + * @since 6.0 + */ + handleInputChange = (e, state) => { + const { addFont, updateFont } = this.state; + const defaultState = state === 'addFont' ? addFont : updateFont; + + this.setState( + { + [state]: { + ...defaultState, + label: e.target.value, + }, + }, + () => this.handleUpdateFontState() + ); + }; + + /** + * Handle process for uploading font variant + * + * @param { string } fontVariant + * @param { file } file + * @param { string } state + * + * @since 6.0 + */ + handleUpload = (fontVariant, file, state) => { + const { + msg: { error }, + } = this.props; + const fontFileMissing = + error && typeof error.addFont === 'object' && error.addFont; + + /* If error exist delete it first to enable dropping */ + if (fontFileMissing) { + Object.entries(fontFileMissing).map(([key]) => { + if (fontVariant === key) { + /* Call redux action deleteVariantError() */ + return this.props.deleteVariantError(fontVariant); + } + + return false; + }); + } + + /* Safeguard file */ + const checkFile = !file ? '' : file; + + this.setState( + { + [state]: { + ...this.handleGetCurrentColumnState(state), + fontStyles: { + ...this.handleGetCurrentColumnState(state).fontStyles, + [fontVariant]: checkFile, + }, + }, + }, + () => this.handleUpdateFontState() + ); + }; + + /** + * Check validation for font name input box field and font files drop box field + * + * @param { string } state + * @param { string } label + * @param { string } regular + * + * @return { boolean } a flag used for conditional checks + * + * @since 6.0 + */ + handleValidateInputFields = (state, label, regular) => { + const defaultState = + state === 'addFont' ? this.state.addFont : this.state.updateFont; + const validate = { + validateLabel: true, + validateRegular: true, + }; + let labelField = false; + let regularField = false; + + /* Regex will allow only a-z, A-Z, and 0-9 */ + const checkSpecialCharRegex = /^[0-9a-zA-Z ]*$/; + + if (!checkSpecialCharRegex.test(label) || label === '') { + labelField = false; + validate.validateLabel = false; + } + + if (checkSpecialCharRegex.test(label) && label !== '') { + labelField = true; + validate.validateLabel = true; + } + + if (!regular) { + regularField = false; + validate.validateRegular = false; + } + + if (regular) { + regularField = true; + validate.validateRegular = true; + } + + if (labelField && regularField) { + this.setState({ [state]: { ...defaultState, ...validate } }); + return true; + } + + this.setState({ [state]: { ...defaultState, ...validate } }); + /* Call redux action validationError() */ + this.props.validationError(); + return false; + }; + + /** + * Check the update font panel state and disable or enable the update button based on + * new field change + * + * @since 6.0 + */ + handleUpdateFontState = () => { + const { fontList, id } = this.props; + + if (id) { + const { label, fontStyles } = this.state.updateFont; + const activeFont = fontList.filter((font) => font.id === id)[0]; + + if ( + activeFont.font_name === label && + activeFont.regular === fontStyles.regular && + activeFont.italics === fontStyles.italics && + activeFont.bold === fontStyles.bold && + activeFont.bolditalics === fontStyles.bolditalics + ) { + return this.setState({ + updateFont: { + ...this.state.updateFont, + disableUpdateButton: true, + }, + }); + } + + this.setState({ + updateFont: { + ...this.state.updateFont, + disableUpdateButton: false, + }, + }); + } + }; + + /** + * Handle our add font process and call our addFont redux action + * + * @since 6.0 + */ + handleAddFont = () => { + const { label, fontStyles } = this.state.addFont; + + /* Check if all fields are valid */ + if ( + !this.handleValidateInputFields( + 'addFont', + label, + fontStyles.regular + ) + ) { + return; + } + + /* Call redux action addFont() */ + this.props.addFont({ label, ...fontStyles }); + }; + + /** + * Handle our edit font process and call our editFont redux action + * + * @param { string } id + * + * @since 6.0 + */ + handleEditFont = (id) => { + const { label, fontStyles } = this.state.updateFont; + const { fontList, editFont, clearAddFontMsg } = this.props; + const data = {}; + + /* Check if all fields are valid */ + if ( + !this.handleValidateInputFields( + 'updateFont', + label, + fontStyles.regular + ) + ) { + return; + } + + /* Construct the data to be submitted */ + Object.keys(fontStyles).forEach((key) => { + if (typeof fontStyles[key] === 'object' || fontStyles[key] === '') { + data[key] = fontStyles[key]; + } + }); + + const currentFont = fontList.filter((font) => font.id === id)[0]; + const currentFontStyles = { + regular: currentFont.regular, + italics: currentFont.italics, + bold: currentFont.bold, + bolditalics: currentFont.bolditalics, + }; + + /* Check if there's no changes in current font data */ + if ( + label === currentFont.font_name && + JSON.stringify(fontStyles) === JSON.stringify(currentFontStyles) + ) { + /* Call redux action clearAddFontMsg() */ + return clearAddFontMsg(); + } + + /* Call redux action editFont() */ + editFont({ + id, + font: { label, ...data }, + }); + }; + + /** + * Listen to cancel button click event + * + * @since 6.0 + */ + handleCancelEditFont = () => { + const { navigate, clearAddFontMsg } = this.props; + + toggleUpdateFont(navigate); + /* Call redux action clearAddFontMsg() */ + clearAddFontMsg(); + }; + + /** + * Listen to cancel button keyboard press event (space and enter) + * + * @param { KeyboardEvent } e + * + * @since 6.0 + */ + handleCancelEditFontKeypress = (e) => { + const enter = 'Enter'; + const space = ' '; + + if (e.key === enter || e.key === space) { + const { navigate, clearAddFontMsg } = this.props; + + toggleUpdateFont(navigate); + /* Call redux action clearAddFontMsg() */ + clearAddFontMsg(); + } + }; + + /** + * Listen to form submit event and distinguish if it's add or edit request + * + * @param { Event } e + * + * @since 6.0 + */ + handleSubmit = (e) => { + e.preventDefault(); + const { id } = this.props; + + if (id) { + return this.handleEditFont(id); + } + + this.handleAddFont(); + }; + + /** + * Display the font manager body UI + * + * @since 6.0 + */ + render() { + const updateFontVisible = document.querySelector('.update-font.show'); + const { id, fontList, msg, loading, navigate } = this.props; + + return ( +
+
+ + + {msg.error && msg.error.deleteFont && ( + + )} + + +
+ +
+ + + +
+
+ ); + } } /** * Map redux state to props * - * @param state: object + * @param { Object } state + * @param { Object } state.fontManager + * @param { boolean } state.fontManager.addFontLoading + * @param { Array } state.fontManager.fontList + * @param { Object } state.fontManager.msg * - * @returns {{ loading: boolean, fontList: array of object, msg: object }} + * @return {{ loading: boolean, fontList: Array, msg: Object }} mapped State * * @since 6.0 */ -const mapStateToProps = state => ({ - loading: state.fontManager.addFontLoading, - fontList: state.fontManager.fontList, - msg: state.fontManager.msg -}) +const mapStateToProps = (state) => ({ + loading: state.fontManager.addFontLoading, + fontList: state.fontManager.fontList, + msg: state.fontManager.msg, +}); /** * Connect and dispatch redux actions as props @@ -615,12 +652,12 @@ const mapStateToProps = state => ({ * @since 6.0 */ export default connect(mapStateToProps, { - getCustomFontList, - addFont, - editFont, - validationError, - deleteVariantError, - selectFont, - clearAddFontMsg, - clearDropzoneError -})(FontManagerBody) + getCustomFontList: getCustomFontListAction, + addFont: addFontAction, + editFont: editFontAction, + validationError: validationErrorAction, + deleteVariantError: deleteVariantAction, + selectFont: selectFontAction, + clearAddFontMsg: clearAddFontMsgAction, + clearDropzoneError: clearDropzoneErrorAction, +})(FontManagerBody); diff --git a/src/assets/js/react/components/FontManager/FontManagerHeader.js b/src/assets/js/react/components/FontManager/FontManagerHeader.js index 26de9b722..eca23a1ae 100644 --- a/src/assets/js/react/components/FontManager/FontManagerHeader.js +++ b/src/assets/js/react/components/FontManager/FontManagerHeader.js @@ -1,30 +1,35 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' +import React from 'react'; +import PropTypes from 'prop-types'; /* Components */ -import CloseDialog from '../Modal/CloseDialog' +import CloseDialog from '../Modal/CloseDialog'; +/* Helpers */ +import withRouterHooks from '../../utilities/withRouterHooks'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 */ +const CloseDialogWithRouter = withRouterHooks(CloseDialog); + /** * Display the header of font manager UI * - * @param id + * @param { Object } props + * @param { string } props.id * * @since 6.0 */ const FontManagerHeader = ({ id }) => ( -
-

Font Manager

+
+

Font Manager

- -
-) + +
+); /** * PropTypes @@ -32,7 +37,7 @@ const FontManagerHeader = ({ id }) => ( * @since 6.0 */ FontManagerHeader.propTypes = { - id: PropTypes.string -} + id: PropTypes.string, +}; -export default FontManagerHeader +export default FontManagerHeader; diff --git a/src/assets/js/react/components/FontManager/FontVariant.js b/src/assets/js/react/components/FontManager/FontVariant.js index ab75672ee..ce479b3fe 100644 --- a/src/assets/js/react/components/FontManager/FontVariant.js +++ b/src/assets/js/react/components/FontManager/FontVariant.js @@ -1,12 +1,12 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' -import Dropzone from 'react-dropzone' +import React from 'react'; +import PropTypes from 'prop-types'; +import Dropzone from 'react-dropzone'; /* Components */ -import FontVariantLabel from './FontVariantLabel' +import FontVariantLabel from './FontVariantLabel'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -15,92 +15,124 @@ import FontVariantLabel from './FontVariantLabel' /** * Display font variant drop box UI * - * @param state - * @param fontStyles - * @param validateRegular - * @param onHandleUpload - * @param onHandleDeleteFontStyle - * @param error - * @param tabIndex + * @param { Object } props + * @param { Object } props.state + * @param { Object } props.fontStyles + * @param { boolean } props.validateRegular + * @param { Function } props.onHandleUpload + * @param { Function } props.onHandleDeleteFontStyle + * @param { Object } props.msg + * @param { Object } props.msg.error + * @param { string } props.tabIndex * * @since 6.0 */ export const FontVariant = ({ - state, - fontStyles, - validateRegular, - onHandleUpload, - onHandleDeleteFontStyle, - msg: { error }, - tabIndex + state, + fontStyles, + validateRegular, + onHandleUpload, + onHandleDeleteFontStyle, + msg: { error }, + tabIndex, }) => ( -
- {Object.entries(fontStyles).map(([key, font]) => { - const id = 'gfpdf-font-variant-' + key + ' ' + state - const ariaLabelledby = id + ' gfpdf-font-files-label' - const ariaDescribedby = 'gfpdf-font-files-description' - const currentUploadFontName = font !== '' && typeof font !== 'object' - const fontName = currentUploadFontName ? font.substr(font.lastIndexOf('/') + 1) : font.name - const fontFileMissing = (error && typeof error.addFont === 'object') && error.addFont[key] - const regularFieldValidation = (key === 'regular' && !validateRegular) && fontStyles.regular === '' - const dropZoneActive = font ? ' active' : '' - const dropZoneError = fontFileMissing ? ' error' : '' - const dropZoneRequiredRegular = regularFieldValidation ? ' required' : '' - const dropZoneClassEnhancement = dropZoneActive + dropZoneError + dropZoneRequiredRegular - const dropZoneIcon = font ? 'trash' : 'plus' - const displayRequiredText = font ? 'true' : 'false' +
+ {Object.entries(fontStyles).map(([key, font]) => { + const id = 'gfpdf-font-variant-' + key + ' ' + state; + const ariaLabelledby = id + ' gfpdf-font-files-label'; + const ariaDescribedby = 'gfpdf-font-files-description'; + const currentUploadFontName = + font !== '' && typeof font !== 'object'; + const fontName = currentUploadFontName + ? font.substr(font.lastIndexOf('/') + 1) + : font.name; + const fontFileMissing = + error && + typeof error.addFont === 'object' && + error.addFont[key]; + const regularFieldValidation = + key === 'regular' && + !validateRegular && + fontStyles.regular === ''; + const dropZoneActive = font ? ' active' : ''; + const dropZoneError = fontFileMissing ? ' error' : ''; + const dropZoneRequiredRegular = regularFieldValidation + ? ' required' + : ''; + const dropZoneClassEnhancement = + dropZoneActive + dropZoneError + dropZoneRequiredRegular; + const dropZoneIcon = font ? 'trash' : 'plus'; + const displayRequiredText = font ? 'true' : 'false'; - return ( - onHandleUpload(key, acceptedFiles[0], state)} - multiple={false} - > - {({ getRootProps, getInputProps }) => ( - - {font - ? ( - onHandleDeleteFontStyle(e, key, state) })} - /> - ) - : ( - - )} + return ( + + onHandleUpload(key, acceptedFiles[0], state) + } + multiple={false} + > + {({ getRootProps, getInputProps }) => ( + + {font ? ( + + onHandleDeleteFontStyle( + e, + key, + state + ), + })} + /> + ) : ( + + )} - - {regularFieldValidation && ( - {GFPDF.fontManagerFontFileRequiredRegular} - )} - {!fontFileMissing ? fontName : fontFileMissing} - + + {regularFieldValidation && ( + + { + GFPDF.fontManagerFontFileRequiredRegular + } + + )} + {!fontFileMissing ? fontName : fontFileMissing} + - + - - - )} - - ) - })} -
-) + + + )} + + ); + })} +
+); /** * PropTypes @@ -108,13 +140,13 @@ export const FontVariant = ({ * @since 6.0 */ FontVariant.propTypes = { - state: PropTypes.string.isRequired, - fontStyles: PropTypes.object.isRequired, - validateRegular: PropTypes.bool.isRequired, - onHandleUpload: PropTypes.func.isRequired, - onHandleDeleteFontStyle: PropTypes.func.isRequired, - msg: PropTypes.object.isRequired, - tabIndex: PropTypes.string.isRequired -} + state: PropTypes.string.isRequired, + fontStyles: PropTypes.object.isRequired, + validateRegular: PropTypes.bool.isRequired, + onHandleUpload: PropTypes.func.isRequired, + onHandleDeleteFontStyle: PropTypes.func.isRequired, + msg: PropTypes.object.isRequired, + tabIndex: PropTypes.string.isRequired, +}; -export default FontVariant +export default FontVariant; diff --git a/src/assets/js/react/components/FontManager/FontVariantLabel.js b/src/assets/js/react/components/FontManager/FontVariantLabel.js index 9a390e106..1302ea850 100644 --- a/src/assets/js/react/components/FontManager/FontVariantLabel.js +++ b/src/assets/js/react/components/FontManager/FontVariantLabel.js @@ -1,10 +1,10 @@ /* Dependencies */ -import React from 'react' -import PropTypes from 'prop-types' -import { sprintf } from 'sprintf-js' +import React from 'react'; +import PropTypes from 'prop-types'; +import { sprintf } from 'sprintf-js'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -13,26 +13,36 @@ import { sprintf } from 'sprintf-js' /** * Display information for drop box font variant label * - * @param label - * @param font + * @param { Object } props + * @param { string } props.label + * @param { boolean } props.font * * @since 6.0 */ const FontVariantLabel = ({ label, font }) => ( -
- {(label === 'regular' && font === 'false') && ( - ", '') - }} - /> - )} - {(label === 'regular' && font === 'true') && GFPDF.fontListRegular} - {label === 'italics' && GFPDF.fontListItalics} - {label === 'bold' && GFPDF.fontListBold} - {label === 'bolditalics' && GFPDF.fontListBoldItalics} -
-) +
+ {label === 'regular' && font === 'false' && ( + ", + '' + ), + }} + /> + )} + {label === 'regular' && font === 'true' && GFPDF.fontListRegular} + {label === 'italics' && GFPDF.fontListItalics} + {label === 'bold' && GFPDF.fontListBold} + {label === 'bolditalics' && GFPDF.fontListBoldItalics} +
+); /** * PropTypes @@ -40,8 +50,8 @@ const FontVariantLabel = ({ label, font }) => ( * @since 6.0 */ FontVariantLabel.propTypes = { - label: PropTypes.string.isRequired, - font: PropTypes.string -} + label: PropTypes.string.isRequired, + font: PropTypes.string, +}; -export default FontVariantLabel +export default FontVariantLabel; diff --git a/src/assets/js/react/components/FontManager/InitialAddUpdateState.js b/src/assets/js/react/components/FontManager/InitialAddUpdateState.js index 1a233e85b..830b6b0d4 100644 --- a/src/assets/js/react/components/FontManager/InitialAddUpdateState.js +++ b/src/assets/js/react/components/FontManager/InitialAddUpdateState.js @@ -1,5 +1,5 @@ /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -11,15 +11,15 @@ * @since 6.0 */ export default { - id: '', - label: '', - fontStyles: { - regular: '', - italics: '', - bold: '', - bolditalics: '' - }, - validateLabel: true, - validateRegular: true, - disableUpdateButton: false -} + id: '', + label: '', + fontStyles: { + regular: '', + italics: '', + bold: '', + bolditalics: '', + }, + validateLabel: true, + validateRegular: true, + disableUpdateButton: false, +}; diff --git a/src/assets/js/react/components/FontManager/SearchBox.js b/src/assets/js/react/components/FontManager/SearchBox.js index 74154ea90..647e7257a 100644 --- a/src/assets/js/react/components/FontManager/SearchBox.js +++ b/src/assets/js/react/components/FontManager/SearchBox.js @@ -1,12 +1,12 @@ /* Dependencies */ -import React, { Component } from 'react' -import { connect } from 'react-redux' -import PropTypes from 'prop-types' +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; /* Redux actions */ -import { resetSearchResult, searchFontList } from '../../actions/fontManager' +import { resetSearchResult, searchFontList } from '../../actions/fontManager'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 @@ -18,143 +18,151 @@ import { resetSearchResult, searchFontList } from '../../actions/fontManager' * @since 6.0 */ export class SearchBox extends Component { - /** - * PropTypes - * - * @since 6.0 - */ - static propTypes = { - id: PropTypes.string, - searchResult: PropTypes.oneOfType([ - PropTypes.oneOf([null]).isRequired, - PropTypes.arrayOf(PropTypes.object).isRequired - ]), - msg: PropTypes.object.isRequired, - resetSearchResult: PropTypes.func.isRequired, - searchFontList: PropTypes.func.isRequired - } + /** + * PropTypes + * + * @since 6.0 + */ + static propTypes = { + id: PropTypes.string, + searchResult: PropTypes.oneOfType([ + PropTypes.oneOf([null]).isRequired, + PropTypes.arrayOf(PropTypes.object).isRequired, + ]), + msg: PropTypes.object.isRequired, + resetSearchResult: PropTypes.func.isRequired, + searchFontList: PropTypes.func.isRequired, + }; - /** - * Initialize component state - * - * @type {{ searchInput: string }} - * - * @since 6.0 - */ - state = { - searchInput: '' - } + /** + * Initialize component state + * + * @type {{ searchInput: string }} + * + * @since 6.0 + */ + state = { + searchInput: '', + }; - /** - * On mount, Add focus event to document on mount - * - * @since 6.0 - */ - componentDidMount () { - /* add focus to element */ - this.input.focus() - } + /** + * On mount, Add focus event to document on mount + * + * @since 6.0 + */ + componentDidMount() { + /* add focus to element */ + this.input.focus(); + } - /** - * If component did update and new props are received, - * fires appropriate action based on redux store data - * - * @param prevProps: object - * - * @since 6.0 - */ - componentDidUpdate (prevProps) { - const { id, searchResult, msg } = this.props + /** + * If component did update and new props are received, + * fires appropriate action based on redux store data + * + * @param { Object } prevProps + * + * @since 6.0 + */ + componentDidUpdate(prevProps) { + const { id, searchResult, msg } = this.props; - /* Call the method resetSearchState() */ - if (prevProps.searchResult !== searchResult && !searchResult) { - this.resetSearchState() - } + /* Call the method resetSearchState() */ + if (prevProps.searchResult !== searchResult && !searchResult) { + this.resetSearchState(); + } - /* Clear search box after a successful font has been added */ - if (JSON.stringify(prevProps.msg) !== JSON.stringify(msg) && msg.success && id) { - this.resetSearchState() - } - } + /* Clear search box after a successful font has been added */ + if ( + JSON.stringify(prevProps.msg) !== JSON.stringify(msg) && + msg.success && + id + ) { + this.resetSearchState(); + } + } - /** - * On component unmount, Call our redux action resetSearchResult() - * - * @since 6.0 - */ - componentWillUnmount () { - if (this.state.searchInput !== '') { - /* Call redux action resetSearchResult() */ - this.props.resetSearchResult() - } - } + /** + * On component unmount, Call our redux action resetSearchResult() + * + * @since 6.0 + */ + componentWillUnmount() { + if (this.state.searchInput !== '') { + /* Call redux action resetSearchResult() */ + this.props.resetSearchResult(); + } + } - /** - * Listen to search box input field change - * - * @param e: object - * - * @since 6.0 - */ - handleSearch = e => { - const data = e.target.value + /** + * Listen to search box input field change + * + * @param { Event & { target: InputEvent } } e + * + * @since 6.0 + */ + handleSearch = (e) => { + const data = e.target.value; - this.setState({ searchInput: data }) - /* Call redux action searchFontList() */ - this.props.searchFontList(data) - } + this.setState({ searchInput: data }); + /* Call redux action searchFontList() */ + this.props.searchFontList(data); + }; - /** - * Reset component searchInput state - * - * @since 6.0 - */ - resetSearchState = () => { - this.setState({ searchInput: '' }) - } + /** + * Reset component searchInput state + * + * @since 6.0 + */ + resetSearchState = () => { + this.setState({ searchInput: '' }); + }; - /** - * Display font manager search box UI - * - * @since 6.0 - */ - render () { - return ( -
- e.keyCode === 13 && e.preventDefault()} - ref={node => (this.input = node)} - tabIndex='143' - /> -
- ) - } + /** + * Display font manager search box UI + * + * @since 6.0 + */ + render() { + return ( +
+ e.keyCode === 13 && e.preventDefault()} + ref={(node) => (this.input = node)} + // eslint-disable-next-line jsx-a11y/tabindex-no-positive + tabIndex="143" + /> +
+ ); + } } /** * Map redux state to props * - * @param state: object + * @param { Object } state * - * @returns {{ searchResult: (null || array of object), msg: object }} + * @return {{ searchResult: null | Array, msg: Object }} mapped state * * @since 6.0 */ -const mapStateToProps = state => ({ - searchResult: state.fontManager.searchResult, - msg: state.fontManager.msg -}) +const mapStateToProps = (state) => ({ + searchResult: state.fontManager.searchResult, + msg: state.fontManager.msg, +}); /** * Connect and dispatch redux actions as props * * @since 6.0 */ -export default connect(mapStateToProps, { resetSearchResult, searchFontList })(SearchBox) +export default connect(mapStateToProps, { + resetSearchResult, + searchFontList, +})(SearchBox); diff --git a/src/assets/js/react/components/FontManager/TemplateTooltip.js b/src/assets/js/react/components/FontManager/TemplateTooltip.js index 0c7553e1c..a20663ced 100644 --- a/src/assets/js/react/components/FontManager/TemplateTooltip.js +++ b/src/assets/js/react/components/FontManager/TemplateTooltip.js @@ -1,107 +1,125 @@ /* Dependencies */ -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { sprintf } from 'sprintf-js' +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { sprintf } from 'sprintf-js'; /* Utilities */ -import { adjustFontListHeight } from '../../utilities/FontManager/adjustFontListHeight' +import { adjustFontListHeight } from '../../utilities/FontManager/adjustFontListHeight'; /** - * @package Gravity PDF + * @package Gravity PDF * @copyright Copyright (c) 2024, Blue Liquid Designs * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License * @since 6.0 */ export class TemplateTooltip extends Component { - /** - * PropTypes - * - * @since 6.0 - */ - static propTypes = { - id: PropTypes.string - } + /** + * PropTypes + * + * @since 6.0 + */ + static propTypes = { + id: PropTypes.string, + }; - /** - * Initialize component state - * - * @type {{ tooltip: boolean }} - * - * @since 6.0 - */ - state = { - tooltip: false - } + /** + * Initialize component state + * + * @type {{ tooltip: boolean }} + * + * @since 6.0 + */ + state = { + tooltip: false, + }; - /** - * Toggle state for template usage information box - * - * @since 6.0 - */ - handleDisplayInfo = () => { - this.setState({ tooltip: !this.state.tooltip }) + /** + * Toggle state for template usage information box + * + * @since 6.0 + */ + handleDisplayInfo = () => { + this.setState({ tooltip: !this.state.tooltip }); - setTimeout(() => adjustFontListHeight(), 100) - } + setTimeout(() => adjustFontListHeight(), 100); + }; - /** - * Handle auto highlighting of the information box content once clicked - * - * @param e: object - * - * @since 6.0 - */ - handleContentHighlight = e => { - e.target.focus() - e.target.select() + /** + * Handle auto highlighting of the information box content once clicked + * + * @param { Event & { target: HTMLInputElement } } e + * + * @since 6.0 + */ + handleContentHighlight = (e) => { + e.target.focus(); + e.target.select(); - document.execCommand('copy') - } + document.execCommand('copy'); + }; - /** - * Display template tooltip UI - * - * @since 6.0 - */ - render () { - const { id } = this.props - const { tooltip } = this.state + /** + * Display template tooltip UI + * + * @since 6.0 + */ + render() { + const { id } = this.props; + const { tooltip } = this.state; - /* Construct tooltip value */ - const textareaValue = ` -
Text
` +
Text
`; - return ( -
- {tooltip - ? ( - - ) - : } - - {GFPDF.fontManagerTemplateTooltipLabel} - + return ( +
+ {tooltip ? ( + + ) : ( + + )} + - {tooltip && ( -
', '', '') }} /> - )} + {tooltip && ( +
', + '', + '' + ), + }} + /> + )} - {tooltip && ( -