diff --git a/.eslintignore b/.eslintignore index b6206ec553a90f..9a603c2bceb3d4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,7 +6,6 @@ node_modules packages/block-serialization-spec-parser/parser.js packages/e2e-tests/plugins packages/react-native-editor/bundle -playground/dist vendor wordpress !.*.js diff --git a/.eslintrc.js b/.eslintrc.js index 6d841c60444bed..4f848acaf2bdfb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -121,10 +121,6 @@ module.exports = { 'Avoid truthy checks on length property rendering, as zero length is rendered verbatim.', }, ], - // Temporarily converted to warning until all errors are resolved. - // See https://github.com/WordPress/gutenberg/pull/22771 for the eslint-plugin-jsdoc update. - 'jsdoc/check-param-names': 'warn', - 'jsdoc/require-param': 'warn', }, overrides: [ { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 34236eecef30d5..033ecb9d331c06 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -101,16 +101,16 @@ /packages/plugins @gziolo @adamsilverstein # Rich Text -/packages/format-library @ellatrix @etoledom @cameronvoell @guarani -/packages/rich-text @ellatrix @etoledom @cameronvoell @guarani -/packages/block-editor/src/components/rich-text @ellatrix @etoledom @cameronvoell @guarani +/packages/format-library @ellatrix @cameronvoell @guarani +/packages/rich-text @ellatrix @cameronvoell @guarani +/packages/block-editor/src/components/rich-text @ellatrix @cameronvoell @guarani # Project Management /.github @mapk @karmatosed /packages/project-management-automation # wp-env -/packages/env @epiqueras @noahtallen +/packages/env @noahtallen # PHP /lib @timothybjacobs @spacedmonkey diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 00000000000000..e813100a83a9a5 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,12 @@ + +# Gutenberg Project Support + +Welcome to Gutenberg, a WordPress project. We hope you join us in creating the future platform for publishing; all are welcome here. + +* Please see the [Contributing Guidelines](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md) for additional information on how to contribute. + +* As with all WordPress projects, we want to ensure a welcoming environment for everyone. With that in mind, all contributors are expected to follow our [Code of Conduct](https://github.com/WordPress/gutenberg/blob/master/CODE_OF_CONDUCT.md). + +* Join us on Slack for real-time communication, it is where maintainers coordinate around the project. To get started using Slack, see: https://make.wordpress.org/chat/ + +* For general WordPress support with the core editor, see the [WordPress.org support forums](https://wordpress.org/support/) — it is highly active and well maintained. diff --git a/.github/workflows/build-plugin-zip.yml b/.github/workflows/build-plugin-zip.yml index 6e7f3a3f4439d9..52218898bed0d8 100644 --- a/.github/workflows/build-plugin-zip.yml +++ b/.github/workflows/build-plugin-zip.yml @@ -6,6 +6,8 @@ on: - '**.md' push: branches: [master] + tags: + - 'v*' paths-ignore: - '**.md' diff --git a/.github/workflows/bundle-size.yml b/.github/workflows/bundle-size.yml index caba6ffb0bf03c..e1e6c292fd24df 100644 --- a/.github/workflows/bundle-size.yml +++ b/.github/workflows/bundle-size.yml @@ -12,7 +12,7 @@ jobs: with: fetch-depth: 1 - - uses: preactjs/compressed-size-action@v1 + - uses: preactjs/compressed-size-action@v2 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" pattern: "{build/**/*.js,build/**/*.css}" diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index 7f24b774c69680..a42371f5e13692 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -10,106 +10,16 @@ on: - '**.md' jobs: - admin-1: - name: Admin - 1 + admin: + name: Admin - ${{ matrix.part }} runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Cache node modules - uses: actions/cache@v2 - env: - cache-name: cache-node-modules - with: - # npm cache files are stored in `~/.npm` on Linux/macOS - path: ~/.npm - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - - name: Use Node.js 14.x - uses: actions/setup-node@v1 - with: - node-version: 14.x - - - name: Npm install and build - run: | - npm ci - FORCE_REDUCED_MOTION=true npm run build - - - name: Install WordPress - run: | - chmod -R 767 ./ # TODO: Possibly integrate in wp-env - npm run wp-env start - - - name: Running the tests - run: | - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 0' < ~/.jest-e2e-tests ) - - - name: Archive debug artifacts (screenshots, HTML snapshots) - uses: actions/upload-artifact@v2 - if: always() - with: - name: failures-artifacts - path: artifacts - - admin-2: - name: Admin - 2 - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Cache node modules - uses: actions/cache@v2 - env: - cache-name: cache-node-modules - with: - # npm cache files are stored in `~/.npm` on Linux/macOS - path: ~/.npm - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - - name: Use Node.js 14.x - uses: actions/setup-node@v1 - with: - node-version: 14.x - - - name: Npm install and build - run: | - npm ci - FORCE_REDUCED_MOTION=true npm run build - - - name: Install WordPress - run: | - chmod -R 767 ./ # TODO: Possibly integrate in wp-env - npm run wp-env start - - - name: Running the tests - run: | - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 1' < ~/.jest-e2e-tests ) - - - name: Archive debug artifacts (screenshots, HTML snapshots) - uses: actions/upload-artifact@v2 - if: always() - with: - name: failures-artifacts - path: artifacts + strategy: + fail-fast: false + matrix: + part: [1, 2, 3, 4] - admin-3: - name: Admin - 3 - - runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -145,7 +55,7 @@ jobs: - name: Running the tests run: | $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 2' < ~/.jest-e2e-tests ) + $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == ${{ matrix.part }} - 1' < ~/.jest-e2e-tests ) - name: Archive debug artifacts (screenshots, HTML snapshots) uses: actions/upload-artifact@v2 @@ -153,52 +63,3 @@ jobs: with: name: failures-artifacts path: artifacts - - admin-4: - name: Admin - 4 - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Cache node modules - uses: actions/cache@v2 - env: - cache-name: cache-node-modules - with: - # npm cache files are stored in `~/.npm` on Linux/macOS - path: ~/.npm - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - - name: Use Node.js 14.x - uses: actions/setup-node@v1 - with: - node-version: 14.x - - - name: Npm install and build - run: | - npm ci - FORCE_REDUCED_MOTION=true npm run build - - - name: Install WordPress - run: | - chmod -R 767 ./ # TODO: Possibly integrate in wp-env - npm run wp-env start - - - name: Running the tests - run: | - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --listTests > ~/.jest-e2e-tests - $( npm bin )/wp-scripts test-e2e --config=./packages/e2e-tests/jest.config.js --cacheDirectory="$HOME/.jest-cache" --runTestsByPath $( awk 'NR % 4 == 3' < ~/.jest-e2e-tests ) - - - name: Archive debug artifacts (screenshots, HTML snapshots) - uses: actions/upload-artifact@v2 - if: always() - with: - name: failures-artifacts - path: artifacts - diff --git a/.github/workflows/storybook-pages.yml b/.github/workflows/storybook-pages.yml index 0c66e9b9e92e55..f3a956c75841d6 100644 --- a/.github/workflows/storybook-pages.yml +++ b/.github/workflows/storybook-pages.yml @@ -42,4 +42,4 @@ jobs: uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./playground/dist + publish_dir: ./storybook/build diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index eedace95e743ea..35aa4c4a1a34b8 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -47,6 +47,9 @@ jobs: - name: Running the tests run: npm run test-unit -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache" + - name: Running the date tests + run: npm run test-unit:date -- --ci --maxWorkers=2 --cacheDirectory="$HOME/.jest-cache" + unit-php: name: PHP diff --git a/.gitignore b/.gitignore index f27befbdcebd7c..d79599e194f3cf 100644 --- a/.gitignore +++ b/.gitignore @@ -14,12 +14,13 @@ yarn.lock /wordpress /artifacts -playground/dist .cache *.tsbuildinfo -# Report generated from tests +# Report generated from jest-junit test/native/junit.xml # Local overrides .wp-env.override.json +phpunit.xml +phpunit-watcher.yml diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000000000..3e333f450a0013 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for Xdebug", + "type": "php", + "request": "launch", + "port": 9003, + "pathMappings": { + "/var/www/html/wp-content/plugins/gutenberg": "${workspaceRoot}/" + } + } + ] +} \ No newline at end of file diff --git a/.wp-env.json b/.wp-env.json index 41ecee8042bb17..ccd1231aa28b25 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -3,7 +3,7 @@ "plugins": [ "." ], - "themes": [ "WordPress/theme-experiments/twentytwentyone-blocks" ], + "themes": [ "WordPress/theme-experiments/tt1-blocks" ], "env": { "tests": { "mappings": { diff --git a/README.md b/README.md index 8c7507630c8e21..b5570c6dafdd13 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![lerna](https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg)](https://lerna.js.org) -![Screenshot of the Gutenberg Editor, editing a post in WordPress](https://user-images.githubusercontent.com/1204802/73433964-2540d900-4346-11ea-94f3-5df2e9d876bc.png) +![Screenshot of the Gutenberg Editor, editing a post in WordPress](https://user-images.githubusercontent.com/1204802/100067796-fc3e8700-2e36-11eb-993b-6b80b4310b87.png) Welcome to the development hub for the WordPress Gutenberg project! diff --git a/bin/build-plugin-zip.sh b/bin/build-plugin-zip.sh index a8e91093205a2f..5d9de9b55b79ef 100755 --- a/bin/build-plugin-zip.sh +++ b/bin/build-plugin-zip.sh @@ -111,8 +111,11 @@ mv gutenberg.tmp.php gutenberg.php build_files=$( ls build/*/*.{js,css,asset.php} \ - build/block-library/blocks/*.php build/block-library/blocks/*/block.json \ - build/edit-widgets/blocks/*.php build/edit-widgets/blocks/*/block.json \ + build/block-library/blocks/*.php \ + build/block-library/blocks/*/block.json \ + build/block-library/blocks/*/*.css \ + build/edit-widgets/blocks/*.php \ + build/edit-widgets/blocks/*/block.json \ ) diff --git a/bin/packages/build.js b/bin/packages/build.js index 16a886cac04793..f2a9e381875f2e 100755 --- a/bin/packages/build.js +++ b/bin/packages/build.js @@ -193,7 +193,14 @@ if ( files.length ) { bar.tick( 0 ); stream = glob.stream( - [ `${ PACKAGES_DIR }/*/src/**/*.js`, `${ PACKAGES_DIR }/*/src/*.scss` ], + [ + `${ PACKAGES_DIR }/*/src/**/*.js`, + `${ PACKAGES_DIR }/*/src/*.scss`, + `${ PACKAGES_DIR }/block-library/src/**/*.js`, + `${ PACKAGES_DIR }/block-library/src/*/style.scss`, + `${ PACKAGES_DIR }/block-library/src/*/editor.scss`, + `${ PACKAGES_DIR }/block-library/src/*.scss`, + ], { ignore: [ `**/benchmark/**`, diff --git a/bin/plugin/commands/common.js b/bin/plugin/commands/common.js index d32b687720605b..5a300e50e68c2c 100644 --- a/bin/plugin/commands/common.js +++ b/bin/plugin/commands/common.js @@ -74,8 +74,71 @@ function findReleaseBranchName( packageJsonPath ) { ); } +/** + * Calculates version bump for the packages based on the content + * from the provided CHANGELOG file split into individual lines. + * + * @param {string[]} lines Changelog content split into lines. + * @param {('patch'|'minor'|'major')} [minimumVersionBump] Minimum version bump for the package. + * Defaults to `patch`. + * + * @return {string|null} Version bump when applicable, or null otherwise. + */ +function calculateVersionBumpFromChangelog( + lines, + minimumVersionBump = 'patch' +) { + let changesDetected = false; + let versionBump = null; + for ( const line of lines ) { + const lineNormalized = line.toLowerCase().trimLeft(); + // Detect unpublished changes first. + if ( lineNormalized.startsWith( '## unreleased' ) ) { + changesDetected = true; + continue; + } + + // Skip all lines until unpublished changes found. + if ( ! changesDetected ) { + continue; + } + + // A previous published version detected. Stop processing. + if ( lineNormalized.startsWith( '## ' ) ) { + break; + } + + // A major version bump required. Stop processing. + if ( lineNormalized.startsWith( '### breaking change' ) ) { + versionBump = 'major'; + break; + } + + // A minor version bump required. Proceed to the next line. + if ( + lineNormalized.startsWith( '### deprecation' ) || + lineNormalized.startsWith( '### enhancement' ) || + lineNormalized.startsWith( '### new feature' ) + ) { + versionBump = 'minor'; + continue; + } + + // A version bump required. Found new changelog section. + if ( + versionBump !== 'minor' && + ( lineNormalized.startsWith( '### ' ) || + lineNormalized.includes( '- ' ) ) + ) { + versionBump = minimumVersionBump; + } + } + return versionBump; +} + module.exports = { + calculateVersionBumpFromChangelog, + findReleaseBranchName, runGitRepositoryCloneStep, runCleanLocalFoldersStep, - findReleaseBranchName, }; diff --git a/bin/plugin/commands/packages.js b/bin/plugin/commands/packages.js index 9a16beeb82b06f..6291008ae575af 100644 --- a/bin/plugin/commands/packages.js +++ b/bin/plugin/commands/packages.js @@ -6,6 +6,7 @@ const glob = require( 'fast-glob' ); const fs = require( 'fs' ); const semver = require( 'semver' ); const readline = require( 'readline' ); +const { prompt } = require( 'inquirer' ); /** * Internal dependencies @@ -13,12 +14,19 @@ const readline = require( 'readline' ); const { log, formats } = require( '../lib/logger' ); const { askForConfirmation, runStep, readJSONFile } = require( '../lib/utils' ); const { + calculateVersionBumpFromChangelog, + findReleaseBranchName, runGitRepositoryCloneStep, runCleanLocalFoldersStep, - findReleaseBranchName, } = require( './common' ); const git = require( '../lib/git' ); +/** + * Semantic Versioning labels. + * + * @typedef {('major'|'minor'|'patch')} SemVer + */ + /** * Checks out the WordPress release branch and syncs it with the changes from * the last plugin release. @@ -87,7 +95,7 @@ async function runWordPressReleaseBranchSyncStep( * contain new entries. * * @param {string} gitWorkingDirectoryPath Git working directory path. - * @param {string} minimumVersionBump Minimum version bump for the packages. + * @param {SemVer} minimumVersionBump Minimum version bump for the packages. * @param {boolean} isPrerelease Whether the package version to publish is a prerelease. * @param {string} abortMessage Abort Message. */ @@ -104,55 +112,18 @@ async function updatePackages( changelogFiles.map( async ( changelogPath ) => { const fileStream = fs.createReadStream( changelogPath ); - const lines = readline.createInterface( { + const rl = readline.createInterface( { input: fileStream, } ); - - let changesDetected = false; - let versionBump = null; - for await ( const line of lines ) { - const lineNormalized = line.toLowerCase(); - // Detect unpublished changes first. - if ( lineNormalized.startsWith( '## unreleased' ) ) { - changesDetected = true; - continue; - } - - // Skip all lines until unpublished changes found. - if ( ! changesDetected ) { - continue; - } - - // A previous published version detected. Stop processing. - if ( lineNormalized.startsWith( '## ' ) ) { - break; - } - - // A major version bump required. Stop processing. - if ( lineNormalized.startsWith( '### breaking change' ) ) { - versionBump = 'major'; - break; - } - - // A minor version bump required. Proceed to the next line. - if ( - lineNormalized.startsWith( '### deprecation' ) || - lineNormalized.startsWith( '### enhancement' ) || - lineNormalized.startsWith( '### new feature' ) - ) { - versionBump = 'minor'; - continue; - } - - // A version bump required. Found new changelog section. - if ( - versionBump !== 'minor' && - ( lineNormalized.startsWith( '### ' ) || - lineNormalized.startsWith( '- initial release' ) ) - ) { - versionBump = minimumVersionBump; - } + const lines = []; + for await ( const line of rl ) { + lines.push( line ); } + + const versionBump = calculateVersionBumpFromChangelog( + lines, + minimumVersionBump + ); const packageName = `@wordpress/${ changelogPath.split( '/' ).reverse()[ 1 ] }`; @@ -274,12 +245,11 @@ async function runPushGitChangesStep( /** * Prepare everything to publish WordPress packages to npm. * - * @param {string} minimumVersionBump Minimum version bump for the packages. - * @param {boolean} isPrerelease Whether the package version to publish is a prerelease. + * @param {boolean} [isPrerelease] Whether the package version to publish is a prerelease. * * @return {Promise} Github release object. */ -async function prepareForPackageRelease( minimumVersionBump, isPrerelease ) { +async function prepareForPackageRelease( isPrerelease ) { // This is a variable that contains the abort message shown when the script is aborted. let abortMessage = 'Aborting!'; const temporaryFolders = []; @@ -297,6 +267,16 @@ async function prepareForPackageRelease( minimumVersionBump, isPrerelease ) { abortMessage ); + const { minimumVersionBump } = await prompt( [ + { + type: 'list', + name: 'minimumVersionBump', + message: 'Select the minimum version bump for packages:', + default: 'patch', + choices: [ 'patch', 'minor', 'major' ], + }, + ] ); + await updatePackages( gitWorkingDirectoryPath, minimumVersionBump, @@ -316,6 +296,9 @@ async function prepareForPackageRelease( minimumVersionBump, isPrerelease ) { await runCleanLocalFoldersStep( temporaryFolders, abortMessage ); } +/** + * Prepares everything for publishing a new stable version of WordPress packages. + */ async function prepareLatestDistTag() { log( formats.title( @@ -325,7 +308,7 @@ async function prepareLatestDistTag() { "To perform a release you'll have to be a member of the WordPress Team on npm.\n" ); - await prepareForPackageRelease( 'patch' ); + await prepareForPackageRelease(); log( '\n>> 🎉 WordPress packages are ready to publish!\n', @@ -334,6 +317,9 @@ async function prepareLatestDistTag() { ); } +/** + * Prepares everything for publishing a new RC version of WordPress packages. + */ async function prepareNextDistTag() { log( formats.title( @@ -343,7 +329,7 @@ async function prepareNextDistTag() { "To perform a release you'll have to be a member of the WordPress Team on npm.\n" ); - await prepareForPackageRelease( 'minor', true ); + await prepareForPackageRelease( true ); log( '\n>> 🎉 WordPress packages are ready to publish!\n', diff --git a/bin/plugin/commands/performance.js b/bin/plugin/commands/performance.js index f271e66268a6fb..4a2cfa2df29686 100644 --- a/bin/plugin/commands/performance.js +++ b/bin/plugin/commands/performance.js @@ -30,29 +30,43 @@ const config = require( '../config' ); * @property {number[]} load Load Time. * @property {number[]} type Average type time. * @property {number[]} focus Average block selection time. + * @property {number[]} inserterOpen Average time to open global inserter. + * @property {number[]} inserterHover Average time to move mouse between two block item in the inserter. */ /** * @typedef WPPerformanceResults * - * @property {number} load Load Time. - * @property {number} type Average type time. - * @property {number} minType Minium type time. - * @property {number} maxType Maximum type time. - * @property {number} focus Average block selection time. - * @property {number} minFocus Min block selection time. - * @property {number} maxFocus Max block selection time. + * @property {number} load Load Time. + * @property {number} type Average type time. + * @property {number} minType Minium type time. + * @property {number} maxType Maximum type time. + * @property {number} focus Average block selection time. + * @property {number} minFocus Min block selection time. + * @property {number} maxFocus Max block selection time. + * @property {number} inserterOpen Average time to open global inserter. + * @property {number} minInserterOpen Min time to open global inserter. + * @property {number} maxInserterOpen Max time to open global inserter. + * @property {number} inserterHover Average time to move mouse between two block item in the inserter. + * @property {number} minInserterHover Min time to move mouse between two block item in the inserter. + * @property {number} maxInserterHover Max time to move mouse between two block item in the inserter. */ /** * @typedef WPFormattedPerformanceResults * - * @property {string=} load Load Time. - * @property {string=} type Average type time. - * @property {string=} minType Minium type time. - * @property {string=} maxType Maximum type time. - * @property {string=} focus Average block selection time. - * @property {string=} minFocus Min block selection time. - * @property {string=} maxFocus Max block selection time. + * @property {string=} load Load Time. + * @property {string=} type Average type time. + * @property {string=} minType Minium type time. + * @property {string=} maxType Maximum type time. + * @property {string=} focus Average block selection time. + * @property {string=} minFocus Min block selection time. + * @property {string=} maxFocus Max block selection time. + * @property {string=} inserterOpen Average time to open global inserter. + * @property {string=} minInserterOpen Min time to open global inserter. + * @property {string=} maxInserterOpen Max time to open global inserter. + * @property {string=} inserterHover Average time to move mouse between two block item in the inserter. + * @property {string=} minInserterHover Min time to move mouse between two block item in the inserter. + * @property {string=} maxInserterHover Max time to move mouse between two block item in the inserter. */ /** @@ -109,6 +123,12 @@ function curateResults( results ) { focus: average( results.focus ), minFocus: Math.min( ...results.focus ), maxFocus: Math.max( ...results.focus ), + inserterOpen: average( results.inserterOpen ), + minInserterOpen: Math.min( ...results.inserterOpen ), + maxInserterOpen: Math.max( ...results.inserterOpen ), + inserterHover: average( results.inserterHover ), + minInserterHover: Math.min( ...results.inserterHover ), + maxInserterHover: Math.max( ...results.inserterHover ), }; } @@ -166,6 +186,12 @@ async function runTestSuite( testSuite, performanceTestDirectory ) { focus: results.map( ( r ) => r.focus ), minFocus: results.map( ( r ) => r.minFocus ), maxFocus: results.map( ( r ) => r.maxFocus ), + inserterOpen: results.map( ( r ) => r.inserterOpen ), + minInserterOpen: results.map( ( r ) => r.minInserterOpen ), + maxInserterOpen: results.map( ( r ) => r.maxInserterOpen ), + inserterHover: results.map( ( r ) => r.inserterHover ), + minInserterHover: results.map( ( r ) => r.minInserterHover ), + maxInserterHover: results.map( ( r ) => r.maxInserterHover ), }, median ); @@ -179,8 +205,8 @@ async function runTestSuite( testSuite, performanceTestDirectory ) { /** * Runs the performances tests on an array of branches and output the result. * - * @param {WPPerformanceCommandOptions} options Command options. * @param {string[]} branches Branches to compare + * @param {WPPerformanceCommandOptions} options Command options. */ async function runPerformanceTests( branches, options ) { // The default value doesn't work because commander provides an array. @@ -268,7 +294,21 @@ async function runPerformanceTests( branches, options ) { log( '\n>> 🎉 Results.\n' ); for ( const testSuite of testSuites ) { log( `\n>> ${ testSuite }\n` ); - console.table( results[ testSuite ] ); + + /** @type {Record>} */ + const invertedResult = {}; + Object.entries( results[ testSuite ] ).reduce( + ( acc, [ key, val ] ) => { + for ( const entry of Object.keys( val ) ) { + if ( ! acc[ entry ] ) acc[ entry ] = {}; + // @ts-ignore + acc[ entry ][ key ] = val[ entry ]; + } + return acc; + }, + invertedResult + ); + console.table( invertedResult ); } } diff --git a/bin/plugin/commands/release.js b/bin/plugin/commands/release.js index 974d5fc105f95e..16f8f8796e9033 100644 --- a/bin/plugin/commands/release.js +++ b/bin/plugin/commands/release.js @@ -6,6 +6,7 @@ const fs = require( 'fs' ); const semver = require( 'semver' ); const Octokit = require( '@octokit/rest' ); const { sprintf } = require( 'sprintf-js' ); +const os = require( 'os' ); /** * Internal dependencies @@ -392,52 +393,60 @@ async function runGithubReleaseStep( abortMessage ) { let octokit; + let releaseDraft; let release; - await runStep( 'Creating the GitHub release', abortMessage, async () => { - await askForConfirmation( - 'Proceed with the creation of the GitHub release?', - true, - abortMessage - ); - - const { token } = await inquirer.prompt( [ - { - type: 'input', - name: 'token', - message: - 'Please enter a GitHub personal authentication token.\n' + - 'You can create one by navigating to ' + - formats.success( - 'https://github.com/settings/tokens/new?scopes=repo,admin:org,write:packages' - ) + - '.\nToken:', - }, - ] ); - - octokit = new Octokit( { - auth: token, - } ); - const releaseData = await octokit.repos.createRelease( { - owner: config.githubRepositoryOwner, - repo: config.githubRepositoryName, - tag_name: 'v' + version, - name: versionLabel, - body: changelog, - prerelease: isPrerelease, - } ); - release = releaseData.data; + await runStep( + 'Creating the GitHub release draft', + abortMessage, + async () => { + await askForConfirmation( + 'Proceed with the creation of the GitHub release draft?', + true, + abortMessage + ); - log( '>> The GitHub release has been created.' ); - } ); + const { token } = await inquirer.prompt( [ + { + type: 'input', + name: 'token', + message: + 'Please enter a GitHub personal authentication token.\n' + + 'You can create one by navigating to ' + + formats.success( + 'https://github.com/settings/tokens/new?scopes=repo,admin:org,write:packages' + ) + + '.\nToken:', + }, + ] ); + + octokit = new Octokit( { + auth: token, + } ); + + const releaseDraftData = await octokit.repos.createRelease( { + owner: config.githubRepositoryOwner, + repo: config.githubRepositoryName, + tag_name: 'v' + version, + name: versionLabel, + body: changelog, + prerelease: isPrerelease, + draft: true, + } ); + releaseDraft = releaseDraftData.data; + + log( '>> The GitHub release draft has been created.' ); + } + ); abortMessage = - abortMessage + ' Make sure to remove the the GitHub release as well.'; + abortMessage + + ' Make sure to remove the the GitHub release draft as well.'; // Uploading the Zip to the Github release await runStep( 'Uploading the plugin ZIP', abortMessage, async () => { const filestats = fs.statSync( zipPath ); await octokit.repos.uploadReleaseAsset( { - url: release.upload_url, + url: releaseDraft.upload_url, headers: { 'content-length': filestats.size, 'content-type': 'application/zip', @@ -448,6 +457,19 @@ async function runGithubReleaseStep( log( '>> The plugin ZIP has been successfully uploaded.' ); } ); + // Remove draft status from the Github release + await runStep( 'Publishing the Github release', abortMessage, async () => { + const releaseData = await octokit.repos.updateRelease( { + owner: config.githubRepositoryOwner, + repo: config.githubRepositoryName, + release_id: releaseDraft.id, + draft: false, + } ); + release = releaseData.data; + + log( '>> The GitHub release has been published.' ); + } ); + log( '>> The GitHub release is available here: ' + formats.success( release.html_url ) @@ -497,13 +519,27 @@ async function runUpdateTrunkContentStep( newReadmeFileContent.replace( STABLE_TAG_PLACEHOLDER, stableTag ) ); + let xargsOpts = ''; + if ( os.platform === 'linux' ) { + // When xargs receives no arguments, it behaves differently in macOS and linux: + // - macOS: doesn't run + // - linux: run without arguments + // + // In linux, the -r option teaches xargs not to run if it receives no arguments. + xargsOpts = '-r'; + } + // Commit the content changes runShellScript( - "svn st | grep '^?' | awk '{print $2}' | xargs svn add", + "svn st | grep '^?' | awk '{print $2}' | xargs " + + xargsOpts + + ' svn add', svnWorkingDirectoryPath ); runShellScript( - "svn st | grep '^!' | awk '{print $2}' | xargs svn rm", + "svn st | grep '^!' | awk '{print $2}' | xargs " + + xargsOpts + + ' svn rm', svnWorkingDirectoryPath ); await askForConfirmation( diff --git a/bin/plugin/commands/test/common.js b/bin/plugin/commands/test/common.js new file mode 100644 index 00000000000000..e3a2afafe63c9d --- /dev/null +++ b/bin/plugin/commands/test/common.js @@ -0,0 +1,76 @@ +/** + * Internal dependencies + */ +import { calculateVersionBumpFromChangelog } from '../common'; + +describe( 'calculateVersionBumpFromChangelog', () => { + it( 'should return null when no lines provided', () => { + expect( calculateVersionBumpFromChangelog( [] ) ).toBe( null ); + } ); + + it( 'should return null when no unreleased header found', () => { + expect( + calculateVersionBumpFromChangelog( [ + 'First line', + 'Second line', + 'Third line', + 'Fourth line', + 'Fifth line', + ] ) + ).toBe( null ); + } ); + + it( 'should return null when Unreleased header found but no entries', () => { + expect( + calculateVersionBumpFromChangelog( [ + 'First line', + '## Unreleased', + 'Third line', + 'Fourth line', + 'Fifth line', + ] ) + ).toBe( null ); + } ); + + it( 'should return patch version Unreleased header and new item detected', () => { + expect( + calculateVersionBumpFromChangelog( [ + 'First line', + '## Unreleased', + 'Third line', + ' - new item added', + 'Fifth line', + ] ) + ).toBe( 'patch' ); + } ); + + it( 'should return enforce higher version bump when new item detected with lower level', () => { + expect( + calculateVersionBumpFromChangelog( + [ + 'First line', + '## Unreleased', + 'Third line', + ' - new item added', + 'Fifth line', + ], + 'major' + ) + ).toBe( 'major' ); + } ); + + it( 'should return major version bump when breaking changes detected', () => { + expect( + calculateVersionBumpFromChangelog( + [ + 'First line', + '## Unreleased', + '### Breaking Changes', + ' - new item added', + 'Fifth line', + ], + 'major' + ) + ).toBe( 'major' ); + } ); +} ); diff --git a/bin/unit-test-date.sh b/bin/unit-test-date.sh new file mode 100755 index 00000000000000..d3e5c79bba83df --- /dev/null +++ b/bin/unit-test-date.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -eu + +pids=() +pidsTimezones=() +pidsLocales=() + +timezones=(EST GMT CET) +locales=(en_US ja_JP) + +for timezone in "${timezones[@]}"; do + for locale in "${locales[@]}"; do + TZ=$timezone LANG=$locale npm run test-unit -- packages/date "$@" & + pids+=($!) + pidsTimezones+=($timezone) + pidsLocales+=($locale) + done +done + +for i in "${!pids[@]}"; do + pid=${pids[i]} + timezone=${pidsTimezones[i]} + locale=${pidsLocales[i]} + wait "$pid" || ( + echo "Date tests failed with timezone = $timezone and locale = $locale" + exit 1 + ) +done diff --git a/changelog.txt b/changelog.txt index 9f0b898fa6caac..7b978290d9c44f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,397 @@ == Changelog == += 9.6.2 = + +### Bug Fixes + + - Fix toolbar controls in the widgets screen. + - Fix the slash inserter in the widgets screen. + + += 9.6.1 = + +### Bugfixes + +- Include block's CSS in the release for FSE themes ([27884](https://github.com/WordPress/gutenberg/pull/27884)) + + += 9.6.0 = + + +### Features + +- Allow dragging blocks from the inserter into the canvas. ([27669](https://github.com/WordPress/gutenberg/pull/27669)) +- Buttons: Add variations for vertical layout. ([27297](https://github.com/WordPress/gutenberg/pull/27297)) + +### Enhancements + +- Buttons block: Change position of the link popover. ([27408](https://github.com/WordPress/gutenberg/pull/27408)) + +### New APIs + +- Add a useFocusOnMount hook to the @wordpress/compose package. ([27574](https://github.com/WordPress/gutenberg/pull/27574)) +- Add a useFocusReturn hook. ([27572](https://github.com/WordPress/gutenberg/pull/27572)) +- Add a useConstrainedTabbing hook. ([27544](https://github.com/WordPress/gutenberg/pull/27544)) +- Components: Introduce a isDisabled prop to the Disabled component. ([26730](https://github.com/WordPress/gutenberg/pull/26730)) +- Create block: + - Use Block API version 2. ([26098](https://github.com/WordPress/gutenberg/pull/26098)) + - Fix for supporting external templates. ([27784](https://github.com/WordPress/gutenberg/pull/27784)) ([27776](https://github.com/WordPress/gutenberg/pull/27776)) + +### Bug Fixes + +- Widget screen: Fix block select on focus. ([27755](https://github.com/WordPress/gutenberg/pull/27755)) +- [Embed block]: Add html and reusable support back. ([27733](https://github.com/WordPress/gutenberg/pull/27733)) +- Add useCallbackRef to avoid calling the ref multiple times with the same node. ([27710](https://github.com/WordPress/gutenberg/pull/27710)) +- Correct getRedistributedColumnWidths and related tests. ([27681](https://github.com/WordPress/gutenberg/pull/27681)) +- Remove CSS Custom Property in code block. ([27672](https://github.com/WordPress/gutenberg/pull/27672)) +- Fix regression on code block for font-size property ([27862](https://github.com/WordPress/gutenberg/pull/27862)) +- Block crashes if font family is not found. ([27654](https://github.com/WordPress/gutenberg/pull/27654)) +- popover flickering on small screens. ([27648](https://github.com/WordPress/gutenberg/pull/27648)) +- Adding single use block from main inserter causes focus loss and menu to be stuck open. ([27641](https://github.com/WordPress/gutenberg/pull/27641)) +- Changelog for 9.5.2. ([27638](https://github.com/WordPress/gutenberg/pull/27638)) +- Uncaught error with a custom generic store without a unsubscribe function in useSelect. ([27634](https://github.com/WordPress/gutenberg/pull/27634)) +- Revert date changes from branch 'replace-moment'. ([27550](https://github.com/WordPress/gutenberg/pull/27550)) +- useMediaQuery: Make it safe for SSR environments without window. ([27542](https://github.com/WordPress/gutenberg/pull/27542)) +- Fixes the width on the circle color picker popover. ([27523](https://github.com/WordPress/gutenberg/pull/27523)) +- ComboboxControl/FormTokenField: Fix iOS zooming for input. ([27471](https://github.com/WordPress/gutenberg/pull/27471)) +- Fallback to regular subscribe if the store doesn't exist in useSelect. ([27466](https://github.com/WordPress/gutenberg/pull/27466)) +- Global Styles getPresetVariable uses a wrong variable; Remove GLOBAL_CONTEXT. ([27450](https://github.com/WordPress/gutenberg/pull/27450)) +- Popover: Fix issue with undefined getBoundingClientRect. ([27445](https://github.com/WordPress/gutenberg/pull/27445)) +- Try fixing combobox a11y issues. ([27431](https://github.com/WordPress/gutenberg/pull/27431)) +- Support gradients with omitted stop positions in CustomGradientPicker. ([27413](https://github.com/WordPress/gutenberg/pull/27413)) +- Fix combobox suggestion list closure when clicking scrollbar. ([27367](https://github.com/WordPress/gutenberg/pull/27367)) +- Video Block: Let the video fill the container. ([27328](https://github.com/WordPress/gutenberg/pull/27328)) +- Media & Text “crop image to fill” to work with linked media. ([27211](https://github.com/WordPress/gutenberg/pull/27211)) +- Give editable fields in blocks better aria-labels. ([26582](https://github.com/WordPress/gutenberg/pull/26582)) +- Replace function should handle empty string callback return in the shortcode parser. ([16358](https://github.com/WordPress/gutenberg/pull/16358)) + +### Performance + +- Split core blocks assets loading. ([25220](https://github.com/WordPress/gutenberg/pull/25220)) + +### Experiments + +- Add a useDialog hook and replace the duplicated PopoverWrapper. ([27643](https://github.com/WordPress/gutenberg/pull/27643)) +- Refactor withFocusOutside to hook. ([27369](https://github.com/WordPress/gutenberg/pull/27369)) +- FSE: Block Navigation: update Navigation block placeholder. ([27018](https://github.com/WordPress/gutenberg/pull/27018)) +- FSE: Block Query + - Add new post link to Query. ([27732](https://github.com/WordPress/gutenberg/pull/27732)) + - Allow Query Loop only inside Query block. ([27637](https://github.com/WordPress/gutenberg/pull/27637)) + - Adjust mobile margins for the Query block's grid view. ([27619](https://github.com/WordPress/gutenberg/pull/27619)) + - Query block: Allow inheriting the global query arguments. ([27128](https://github.com/WordPress/gutenberg/pull/27128)) +- FSE: Blocks + - Add comment-form block styles. ([27673](https://github.com/WordPress/gutenberg/pull/27673)) + - Tag Cloud block: Adjust styles for the different block alignments. ([27342](https://github.com/WordPress/gutenberg/pull/27342)) + - Site Logo: + - Remove line height. ([27623](https://github.com/WordPress/gutenberg/pull/27623)) + - Add a rounded block style. ([27621](https://github.com/WordPress/gutenberg/pull/27621)) +- FSE: Infrastructure + - Apply hover class in outline mode. ([27714](https://github.com/WordPress/gutenberg/pull/27714)) + - Update documentation to show how a theme can have FSE automatically enabled. ([27680](https://github.com/WordPress/gutenberg/pull/27680)) + - Make the inserter in the site editor behave as a popover. ([27502](https://github.com/WordPress/gutenberg/pull/27502)) + - Add an outline mode and use it both Site Editor and Template mode. ([27499](https://github.com/WordPress/gutenberg/pull/27499)) + - Load the block patterns in the site editor. ([27497](https://github.com/WordPress/gutenberg/pull/27497)) + - Move the templates prePersist logic to core-data. ([27464](https://github.com/WordPress/gutenberg/pull/27464)) + - Expand the multi-entity saving panel by default. ([27437](https://github.com/WordPress/gutenberg/pull/27437)) + - Reveal block boundaries on hover in the Site Editor. ([27271](https://github.com/WordPress/gutenberg/pull/27271)) + - Site Editor - add query args for current context. ([27124](https://github.com/WordPress/gutenberg/pull/27124)) + - Full Site Editing: Introduce a template editing mode inside the post editor. ([26355](https://github.com/WordPress/gutenberg/pull/26355)) + - Remove optimistic updates to solve the template revert issue ([27797](https://github.com/WordPress/gutenberg/pull/27797)) +- FSE: Style System + - Fix: Font Weight and Style don't work independently on global styles. ([27659](https://github.com/WordPress/gutenberg/pull/27659)) + - Add custom units in BoxControl. ([27626](https://github.com/WordPress/gutenberg/pull/27626)) + - Remove Font style, weight, decoration, and transform presets. ([27555](https://github.com/WordPress/gutenberg/pull/27555)) + - Make client preset metadata match server. ([27453](https://github.com/WordPress/gutenberg/pull/27453)) + - Do not pass selectors and supports information to the client. ([27449](https://github.com/WordPress/gutenberg/pull/27449)) + - Add border radius support. ([25791](https://github.com/WordPress/gutenberg/pull/25791)) + - Update font-weight names. ([27718](https://github.com/WordPress/gutenberg/pull/27718)) + - Update performance of global styles code ([27779](https://github.com/WordPress/gutenberg/pull/27779)) + +### Documentation + +- Add missing dependency to code example. ([27742](https://github.com/WordPress/gutenberg/pull/27742)) +- Precise that element ref returned by the hooks that return a ref can change between function or object. ([27610](https://github.com/WordPress/gutenberg/pull/27610)) +- Add escaping functions to code examples. ([27603](https://github.com/WordPress/gutenberg/pull/27603)) +- Add missing @wordpress/components/CHANGELOG.md entry. ([27576](https://github.com/WordPress/gutenberg/pull/27576)) +- Minor changes to release documentation for clarity. ([27571](https://github.com/WordPress/gutenberg/pull/27571)) +- Capitalize JavaScript in accordance with the word mark. ([27539](https://github.com/WordPress/gutenberg/pull/27539)) +- Fix typo in attributes.md. ([27440](https://github.com/WordPress/gutenberg/pull/27440)) +- Try: Update readme screenshot. ([27223](https://github.com/WordPress/gutenberg/pull/27223)) +- Document the useBlockWrapper hook in the block registration documentation. ([26592](https://github.com/WordPress/gutenberg/pull/26592)) +- Add a document explaining the different block API versions. ([26277](https://github.com/WordPress/gutenberg/pull/26277)) +- Update the registration examples to use apiVersion 2. ([26100](https://github.com/WordPress/gutenberg/pull/26100)) + +### Code Quality + +- Remove: Missed unused weights and style translation code. ([27739](https://github.com/WordPress/gutenberg/pull/27739)) +- useDialog: Remove mousedown propagation stopping. ([27725](https://github.com/WordPress/gutenberg/pull/27725)) +- Try: Simplify focus return. ([27705](https://github.com/WordPress/gutenberg/pull/27705)) +- Popover/Modal: Remove and deprecate IsolatedEventContainer. ([27703](https://github.com/WordPress/gutenberg/pull/27703)) +- Popover: Use focus outside hook. ([27700](https://github.com/WordPress/gutenberg/pull/27700)) +- refactor: Tooltip component from classical to functional with hooks. ([27682](https://github.com/WordPress/gutenberg/pull/27682)) +- Template-part padding: Use variables. ([27679](https://github.com/WordPress/gutenberg/pull/27679)) +- Scope image block style variations to only the image block. ([27649](https://github.com/WordPress/gutenberg/pull/27649)) +- Refactor the EditorProvider component and extract hooks. ([27605](https://github.com/WordPress/gutenberg/pull/27605)) +- Use store definition instead of string for notices packages. ([27548](https://github.com/WordPress/gutenberg/pull/27548)) +- Merge RootContainer with BlockList. ([27531](https://github.com/WordPress/gutenberg/pull/27531)) +- Block wrapper: Isolate functionality into smaller hooks. ([27503](https://github.com/WordPress/gutenberg/pull/27503)) +- Writing flow: Consider events only from DOM descendents. ([27489](https://github.com/WordPress/gutenberg/pull/27489)) +- Writing flow: Isolate multi select focus element. ([27482](https://github.com/WordPress/gutenberg/pull/27482)) +- Multi selection: Move hook to WritingFlow with other multi selection logic. ([27479](https://github.com/WordPress/gutenberg/pull/27479)) +- Insertion indicator: Render after last block if none is specified. ([27472](https://github.com/WordPress/gutenberg/pull/27472)) +- Rewrite selection clearer in Block editor. ([27468](https://github.com/WordPress/gutenberg/pull/27468)) +- Move block focus listener to block props hook. ([27463](https://github.com/WordPress/gutenberg/pull/27463)) +- Block editor: Refactor effect.js to controls. ([27298](https://github.com/WordPress/gutenberg/pull/27298)) +- Animate: Type getAnimateClassName. ([27123](https://github.com/WordPress/gutenberg/pull/27123)) +- Refactor image block's image editing tools into separate components. ([27089](https://github.com/WordPress/gutenberg/pull/27089)) +- Drop zone provider: Option to avoid wrapper element. ([27079](https://github.com/WordPress/gutenberg/pull/27079)) +- Audit variables stylesheet. ([26827](https://github.com/WordPress/gutenberg/pull/26827)) +- group block padding: Use variables. ([27676](https://github.com/WordPress/gutenberg/pull/27676)) + +### Tools + +- Release script: Set draft status, and only remove after uploading asset. ([27713](https://github.com/WordPress/gutenberg/pull/27713)) +- CI: Run date test timezone and locale variations using bash script. ([27600](https://github.com/WordPress/gutenberg/pull/27600)) +- Upgrade Babel packages to 7.12.x. ([27553](https://github.com/WordPress/gutenberg/pull/27553)) +- CI: Run package/date unit tests in different timezones. ([27552](https://github.com/WordPress/gutenberg/pull/27552)) +- Avoid cancelling other end-to-end test jobs when one fails. ([27541](https://github.com/WordPress/gutenberg/pull/27541)) +- Add webpack 5 support to dependency-extraction-webpack-plugin. ([27533](https://github.com/WordPress/gutenberg/pull/27533)) +- Add GitHub support document. ([27524](https://github.com/WordPress/gutenberg/pull/27524)) +- Stabilize adding blocks end to end test. ([27493](https://github.com/WordPress/gutenberg/pull/27493)) +- GitHub Actions: Use a build matrix for the end-to-end tests GH action. ([27487](https://github.com/WordPress/gutenberg/pull/27487)) +- Packages: Make it possible to select minimum version bump for publishing. ([27459](https://github.com/WordPress/gutenberg/pull/27459)) +- Upgrade wp-prettier to 2.2.1. ([27441](https://github.com/WordPress/gutenberg/pull/27441)) +- Testing: Make image size test more stable. ([27439](https://github.com/WordPress/gutenberg/pull/27439)) +- Packages: Improve the script that automates version bumps. ([27436](https://github.com/WordPress/gutenberg/pull/27436)) +- CI: Update bundle size workflow to use the latest version. ([27435](https://github.com/WordPress/gutenberg/pull/27435)) +- wp-env: Xdebug support. ([27346](https://github.com/WordPress/gutenberg/pull/27346)) +- Make zip-based URL parsing more general. ([27019](https://github.com/WordPress/gutenberg/pull/27019)) +- Add inserter performance measures. ([26634](https://github.com/WordPress/gutenberg/pull/26634)) + +### Various + +- Verse Block: + - Add support for custom padding. ([27341](https://github.com/WordPress/gutenberg/pull/27341)) + - Add support for font family. ([27332](https://github.com/WordPress/gutenberg/pull/27332)) + - Add support for font size. ([27735](https://github.com/WordPress/gutenberg/pull/27735)) + - Update CSS for frontend and editor. ([27734](https://github.com/WordPress/gutenberg/pull/27734)) +- Popover: Use a11y hooks instead of HoCs. ([27707](https://github.com/WordPress/gutenberg/pull/27707)) +- Refactor focus on mount. ([27699](https://github.com/WordPress/gutenberg/pull/27699)) +- Search block: Use em values for padding. ([27678](https://github.com/WordPress/gutenberg/pull/27678)) +- Button: Is-busy state candybar animation fixed. ([27592](https://github.com/WordPress/gutenberg/pull/27592)) +- Preformatted block: Add support for font sizes. ([27584](https://github.com/WordPress/gutenberg/pull/27584)) +- Remove autoFocus prop from URLInput and from the inserter search form. ([27578](https://github.com/WordPress/gutenberg/pull/27578)) +- Package lock: Update ws. ([27532](https://github.com/WordPress/gutenberg/pull/27532)) +- Update block-patterns.md. ([27520](https://github.com/WordPress/gutenberg/pull/27520)) +- Update wp-env codeowners. ([27491](https://github.com/WordPress/gutenberg/pull/27491)) +- Update the backup icon to better align with WordPress icon package dna. ([27465](https://github.com/WordPress/gutenberg/pull/27465)) +- Update the rich text control titles to sentence case structure. ([27447](https://github.com/WordPress/gutenberg/pull/27447)) +- Search Block: Remove the button only option from the UI. ([27379](https://github.com/WordPress/gutenberg/pull/27379)) +- Add icons for template parts. ([27378](https://github.com/WordPress/gutenberg/pull/27378)) +- Increase radio dimensions to match checkboxes. ([27377](https://github.com/WordPress/gutenberg/pull/27377)) +- Adjusts settings modal height to 90%. ([27362](https://github.com/WordPress/gutenberg/pull/27362)) +- Change the Labels of the Vertical Align options. ([27356](https://github.com/WordPress/gutenberg/pull/27356)) +- Add element selector to template-part block. ([27101](https://github.com/WordPress/gutenberg/pull/27101)) +- Add explicit dismiss button and on dismiss callback to snackbar. ([26952](https://github.com/WordPress/gutenberg/pull/26952)) +- Make social list block align right able on published page & preview. ([26861](https://github.com/WordPress/gutenberg/pull/26861)) +- Update media-text focalPoint conditional rendering. ([25968](https://github.com/WordPress/gutenberg/pull/25968)) +- Remove default icon from PluginBlockSettingsMenuItem. ([21392](https://github.com/WordPress/gutenberg/pull/21392)) +- Add example preview to video block. ([20703](https://github.com/WordPress/gutenberg/pull/20703)) + + += 9.5.2 = + +### Bug Fixes: + +- Fix uncaught error with a custom generic store without a unsubscribe function in useSelect. + + += 9.5.1 = + +### Bug Fixes: + + - Revert date changes from branch 'replace-moment. + - Popover: Fix issue with undefined getBoundingClientRect. + - Fallback to regular subscribe if the store doesn't exist in useSelect. + + += 9.5.0 = + +### Features +- Full Height Alignment control: Implementation and Cover block integration. ([26615](https://github.com/WordPress/gutenberg/pull/26615)) +- Code block: Add support for font sizes. ([27294](https://github.com/WordPress/gutenberg/pull/27294)) + +### Enhancements + +- Improve block patterns preview in the Inserter. ([27204](https://github.com/WordPress/gutenberg/pull/27204)) +- Enhance social links placeholder to look more like the end result. ([26953](https://github.com/WordPress/gutenberg/pull/26953)) +- Add labels to image zoom control. ([24574](https://github.com/WordPress/gutenberg/pull/24574)) + +### New APIs + +- Data: Use store instance as param for select and dispatch. ([26655](https://github.com/WordPress/gutenberg/pull/26655)) +- Adds instance URL to wp-env start. ([27282](https://github.com/WordPress/gutenberg/pull/27282)) + +### Bug Fixes + +- Fix crash when null date passed to TimePicker. ([27316](https://github.com/WordPress/gutenberg/pull/27316)) +- Add backward compatibility support for lightBlockWrapper in getSaveElement. ([27189](https://github.com/WordPress/gutenberg/pull/27189)) +- Restore the gray background in Post Editor. ([27188](https://github.com/WordPress/gutenberg/pull/27188)) +- Font size picker bug that adds px units to empty string values. ([27111](https://github.com/WordPress/gutenberg/pull/27111)) +- Font size unit back-compatibility does not executes on post edit. ([27106](https://github.com/WordPress/gutenberg/pull/27106)) +- Drop zone: Fix infinite loop in some contexts. ([27090](https://github.com/WordPress/gutenberg/pull/27090)) +- Interface regions: Fix focus style (on click). ([27074](https://github.com/WordPress/gutenberg/pull/27074)) +- Fix Separator editor styles. ([27071](https://github.com/WordPress/gutenberg/pull/27071)) +- Fix custom spacing support. ([27045](https://github.com/WordPress/gutenberg/pull/27045)) +- Fix jest process hanging. ([27008](https://github.com/WordPress/gutenberg/pull/27008)) +- Fix combobox suggestion list closure when clicking scrollbar. ([27367](https://github.com/WordPress/gutenberg/pull/27367)) +- Constrain tabbing to the popover in media replace flow. ([26939](https://github.com/WordPress/gutenberg/pull/26939)) +- Fix RangeControl mark placement and cursor styles. ([26745](https://github.com/WordPress/gutenberg/pull/26745)) +- New authors dropdown breaks author selection for editors. ([26554](https://github.com/WordPress/gutenberg/pull/26554)) +- Hooks: Use own instance's `doAction` for built-in hooks. ([26498](https://github.com/WordPress/gutenberg/pull/26498)) +- Inserter: Show preview in search results. ([27193](https://github.com/WordPress/gutenberg/pull/27193)) +- Gallery block: + - Fix duplicate css class. ([27311](https://github.com/WordPress/gutenberg/pull/27311)) + - Adds back in icon and title for gallery block. ([27293](https://github.com/WordPress/gutenberg/pull/27293)) +- Search block: Fix icon strokeWidth properties. ([27308](https://github.com/WordPress/gutenberg/pull/27308)) +- Image block: + - Fix the zoom slider width. ([27285](https://github.com/WordPress/gutenberg/pull/27285)) + - Fix double paste from clipboard. ([27199](https://github.com/WordPress/gutenberg/pull/27199)) + - Hide some controls on multi selection of Image blocks. ([27105](https://github.com/WordPress/gutenberg/pull/27105)) +- Columns: Align single half width column to left. ([27142](https://github.com/WordPress/gutenberg/pull/27142)) + +### Performance + +- Minimize the calls in useSelect by subscribing to only the stores needed. ([26724](https://github.com/WordPress/gutenberg/pull/26724)) +- Update prefer lang constructs to functions. ([27070](https://github.com/WordPress/gutenberg/pull/27070)) + +### Experiments + +- Full Site Editing Framework: + - Fix template resolution priorities. ([27303](https://github.com/WordPress/gutenberg/pull/27303)) + - Update default templates. ([26941](https://github.com/WordPress/gutenberg/pull/26941)) + - Add Support for Templates Default and Custom Titles and Descriptions (JS side - [27038](https://github.com/WordPress/gutenberg/pull/27038), PHP side [27036](https://github.com/WordPress/gutenberg/pull/27036)) + - Add theme taxonomy to templates and template parts. ([27016](https://github.com/WordPress/gutenberg/pull/27016)) +- Full Site Editing Blocks: + - Make the post title block editable. ([27240](https://github.com/WordPress/gutenberg/pull/27240)) + - Post Comment: Update placeholder. ([27013](https://github.com/WordPress/gutenberg/pull/27013)) + - Enable Post Featured Image to be set and replaced. ([27224](https://github.com/WordPress/gutenberg/pull/27224)) + - Add alignment controls to Post Featured Image block. ([27076](https://github.com/WordPress/gutenberg/pull/27076)) + - Query block: + - Add grid view. ([27067](https://github.com/WordPress/gutenberg/pull/27067)) + - Add Posts List variation. ([26990](https://github.com/WordPress/gutenberg/pull/26990)) + - New settings icon in block toolbar. ([27057](https://github.com/WordPress/gutenberg/pull/27057)) + - Fetch all available post types (#27049). ([27056](https://github.com/WordPress/gutenberg/pull/27056)) + - Update Query block's icon. ([27048](https://github.com/WordPress/gutenberg/pull/27048)) + - Query and QueryLoop use useInnerBlocksProps. ([27014](https://github.com/WordPress/gutenberg/pull/27014)) + +- Site Editor: + - Avoid throwing warnings if there are no terms for a template or template part. ([27210](https://github.com/WordPress/gutenberg/pull/27210)) + - Replace adminbar customize link with site-editor in FSE themes. ([27135](https://github.com/WordPress/gutenberg/pull/27135)) + - Update the new templates dropdown list. ([27235](https://github.com/WordPress/gutenberg/pull/27235)) + - Remove .block-editor selector dependency. ([27063](https://github.com/WordPress/gutenberg/pull/27063)) + - Remove unused FullscreenModeClose component. ([26997](https://github.com/WordPress/gutenberg/pull/26997)) + - Navigation: + - Fix item color and padding. ([27096](https://github.com/WordPress/gutenberg/pull/27096)) + - Hide empty menus. ([27141](https://github.com/WordPress/gutenberg/pull/27141)) + - Add isText prop to NavigationItem. ([27003](https://github.com/WordPress/gutenberg/pull/27003)) + - Handle the no search results state. ([27160](https://github.com/WordPress/gutenberg/pull/27160)) + - Add search to templates and template parts. ([26665](https://github.com/WordPress/gutenberg/pull/26665)) + - Hide navigation item if target menu is empty. ([25746](https://github.com/WordPress/gutenberg/pull/25746)) +- Global Styles: + - Sort Global Styles block panels by panel title. ([27163](https://github.com/WordPress/gutenberg/pull/27163)) + - Font Appearance should be enabled globally. ([27150](https://github.com/WordPress/gutenberg/pull/27150)) + - Restrict edition of theme colors by users. ([27250](https://github.com/WordPress/gutenberg/pull/27250)) + - Update mechanism that resolves Global Styles data. ([27237](https://github.com/WordPress/gutenberg/pull/27237)) + - Include px units on default font sizes defined on theme.json. ([27083](https://github.com/WordPress/gutenberg/pull/27083)) + - Update stylesheet generation at edit site. ([27065](https://github.com/WordPress/gutenberg/pull/27065)) + - Add button to reset color palette. ([26975](https://github.com/WordPress/gutenberg/pull/26975)) + - Abstract preset variable retrieving and setting. ([26970](https://github.com/WordPress/gutenberg/pull/26970)) + - Update metadata and add support for padding. ([27099](https://github.com/WordPress/gutenberg/pull/27099)) +- Templates and Template Parts wp-admin lists: + - Update the template parts admin list with new columns and views. ([27156](https://github.com/WordPress/gutenberg/pull/27156)) + - Add the theme source to the templates wp-admin list. ([27108](https://github.com/WordPress/gutenberg/pull/27108)) + - Extend the wp_template admin list with new views and columns. ([27034](https://github.com/WordPress/gutenberg/pull/27034)) + +- Support registry inheritance with atomic stores. ([27162](https://github.com/WordPress/gutenberg/pull/27162)) +- Code block: Paste plain text. ([27236](https://github.com/WordPress/gutenberg/pull/27236)) +- Generalize the atom family concept as an atom selector concept instead. ([27147](https://github.com/WordPress/gutenberg/pull/27147)) +- Bugs: + - Query block: Fix dirtying post on load. ([27323](https://github.com/WordPress/gutenberg/pull/27323)) + - Preserve 'Your homepage displays' settings when updating the 'general' settings. ([27206](https://github.com/WordPress/gutenberg/pull/27206)) + - Make sure templates and parts queries filter by tax_query. ([27113](https://github.com/WordPress/gutenberg/pull/27113)) + - Fix Template Part Not Found message on Windows server. ([26772](https://github.com/WordPress/gutenberg/pull/26772)) + - Respect filtered settings when they're ported to theme.json format. ([27010](https://github.com/WordPress/gutenberg/pull/27010)) + - Preset controls need the preset CSS variables in scope. ([27119](https://github.com/WordPress/gutenberg/pull/27119)) + - Site Editor: + - Show document title on small screens with nav sidebar open. ([27051](https://github.com/WordPress/gutenberg/pull/27051)) + - Fix block toolbar positioning. ([27266](https://github.com/WordPress/gutenberg/pull/27266)) + - Fix app header on small-medium screens. ([27310](https://github.com/WordPress/gutenberg/pull/27310)) + +### Documentation + +- Expand on the Deprecations documentation. ([27286](https://github.com/WordPress/gutenberg/pull/27286)) +- Publish MainDashboardButton documentation to handbook. ([27317](https://github.com/WordPress/gutenberg/pull/27317)) +- Update: Creating a block-based theme tutorial. ([27257](https://github.com/WordPress/gutenberg/pull/27257)) +- ESLint Plugin: Include a note about the minimum version required. ([27203](https://github.com/WordPress/gutenberg/pull/27203)) +- Docs: Update `@wordpress/data` README with API changes. ([27180](https://github.com/WordPress/gutenberg/pull/27180)) +- Data: Improve documentation for new API added around stores. ([27061](https://github.com/WordPress/gutenberg/pull/27061)) +- wp-env: Improve documentation for "run" command. ([27053](https://github.com/WordPress/gutenberg/pull/27053)) +- Code block: Update the documentation. ([27333](https://github.com/WordPress/gutenberg/pull/27333)) +- Add block toolbar component readme. ([25245](https://github.com/WordPress/gutenberg/pull/25245)) +- Add block patterns list component readme. ([24983](https://github.com/WordPress/gutenberg/pull/24983)) +- [Contributors docs] JS meetings were shifted to 15:00GMT. ([27047](https://github.com/WordPress/gutenberg/pull/27047)) +- Typos and tweaks: ([27120](https://github.com/WordPress/gutenberg/pull/27120), [27081](https://github.com/WordPress/gutenberg/pull/27081), [27062](https://github.com/WordPress/gutenberg/pull/27062), [27060](https://github.com/WordPress/gutenberg/pull/27060), [27039](https://github.com/WordPress/gutenberg/pull/27039), [27153](https://github.com/WordPress/gutenberg/pull/27153)) + +### Code Quality +- Replace store name string with exposed store definition: + - Edit Navigation ([27182](https://github.com/WordPress/gutenberg/pull/27182), [27281](https://github.com/WordPress/gutenberg/pull/27281)) + - Blocks ([27336](https://github.com/WordPress/gutenberg/pull/27336)) + - Reusable Blocks ([27094](https://github.com/WordPress/gutenberg/pull/27094)) + - Block Directory ([27178](https://github.com/WordPress/gutenberg/pull/27178)) + - Keyboard Shortcuts ([27355](https://github.com/WordPress/gutenberg/pull/27355)) +- Search block: Remove invalid prop which was causing a React warning. ([27306](https://github.com/WordPress/gutenberg/pull/27306)) +- Refactor click redirect to avoid trailing div. ([27253](https://github.com/WordPress/gutenberg/pull/27253)) + +- Editor styles: Convert to hook. ([27080](https://github.com/WordPress/gutenberg/pull/27080)) +- Add: PHP util function equivalent to lodash set. ([27077](https://github.com/WordPress/gutenberg/pull/27077)) +- Interface: Remove regions wrapper div. ([27066](https://github.com/WordPress/gutenberg/pull/27066)) +- Refactor typing observer. ([27043](https://github.com/WordPress/gutenberg/pull/27043)) +- Visual editor: Remove 4 wrapper divs. ([27035](https://github.com/WordPress/gutenberg/pull/27035)) +- Insertion point: Avoid wrapper div. ([26994](https://github.com/WordPress/gutenberg/pull/26994)) +- Typewriter: Rewrite with hooks. ([26986](https://github.com/WordPress/gutenberg/pull/26986)) +- Deprecate the withGlobalEvents HoC. ([26749](https://github.com/WordPress/gutenberg/pull/26749)) +- Edit Post: Refactor effects to action generators. ([27069](https://github.com/WordPress/gutenberg/pull/27069)) + +### Build Tooling + +- Add eslint rule to prohibit unsafe APIs. ([27301](https://github.com/WordPress/gutenberg/pull/27301)) +- Update package lock to fix CI failures. ([27098](https://github.com/WordPress/gutenberg/pull/27098), [27102](https://github.com/WordPress/gutenberg/pull/27102)) +- GitHub Actions: Build Plugin zip, store as artifact on every PR. ([26746](https://github.com/WordPress/gutenberg/pull/26746)) +- Scripts: Unpin ignore-emit-webpack-plugin dependency. ([26739](https://github.com/WordPress/gutenberg/pull/26739)) +- Adds Support to wp-env for setting the PHP version. ([25268](https://github.com/WordPress/gutenberg/pull/25268)) +- Configure phpunit-watcher to improve devex. ([27058](https://github.com/WordPress/gutenberg/pull/27058)) +- Run phpunit even when phpcs fails. ([27024](https://github.com/WordPress/gutenberg/pull/27024)) +- Scripts: Auto format TypeScript files with format-js. ([27138](https://github.com/WordPress/gutenberg/pull/27138)) +- End 2 End Tests: + - Fix randomly failing end-to-end test. ([27358](https://github.com/WordPress/gutenberg/pull/27358)) + - Fix multi entity editing test. ([27347](https://github.com/WordPress/gutenberg/pull/27347)) + - Add end-to-end tests for image editing tools. ([27262](https://github.com/WordPress/gutenberg/pull/27262)) + +### Various + +- Simplify CSS for the code block. ([27314](https://github.com/WordPress/gutenberg/pull/27314)) +- Update the block variation widths in the block placeholder. ([27255](https://github.com/WordPress/gutenberg/pull/27255)) +- Don’t split translatable strings in block templates. ([27361](https://github.com/WordPress/gutenberg/pull/27361)) +- Reduce margin on placeholder for media. ([27252](https://github.com/WordPress/gutenberg/pull/27252)) +- Interface package: Move MainDashboardButton slot. ([27213](https://github.com/WordPress/gutenberg/pull/27213)) +- Decrease scrim when in a modal overlay. ([27054](https://github.com/WordPress/gutenberg/pull/27054)) +- Replace 'Remove from Reusable blocks' with 'Manage Reusable blocks'. ([27026](https://github.com/WordPress/gutenberg/pull/27026)) +- Mark AWAIT_PROMISE as unstable API. ([26852](https://github.com/WordPress/gutenberg/pull/26852)) +- Block Support: Separate opt in for font style and weight options. ([26844](https://github.com/WordPress/gutenberg/pull/26844)) +- Latest Posts: Don't use `target="_blank"`. ([25730](https://github.com/WordPress/gutenberg/pull/25730)) + + = 9.4.1 = @@ -96,7 +488,7 @@ - Add preset classes generation on the client side. ([26224](https://github.com/WordPress/gutenberg/pull/26224)) - Navigation block: Fix color support declaration. ([26928](https://github.com/WordPress/gutenberg/pull/26928)) - Popover: Add sticky boundary element prop. ([26728](https://github.com/WordPress/gutenberg/pull/26728)) -- Block Support: +- Block Support: - Add font style and weight options with combined UI. ([26444](https://github.com/WordPress/gutenberg/pull/26444)) ([26868](https://github.com/WordPress/gutenberg/pull/26868)) - Add text transform block support flag. ([26060](https://github.com/WordPress/gutenberg/pull/26060)) ([26059](https://github.com/WordPress/gutenberg/pull/26059)) - Add: Font Family picking mechanism. ([24868](https://github.com/WordPress/gutenberg/pull/24868)), ([26750](https://github.com/WordPress/gutenberg/pull/26750)), ([26759](https://github.com/WordPress/gutenberg/pull/26759)). @@ -238,14 +630,14 @@ - Fix Invisible Template Previews in the Sidebar. ([26424](https://github.com/WordPress/gutenberg/pull/26424)) - Add convert to template part flow. ([20445](https://github.com/WordPress/gutenberg/pull/20445)) - Fix custom template part theme meta. ([26587](https://github.com/WordPress/gutenberg/pull/26587)) -- Query block: +- Query block: - Add initial variations. ([26378](https://github.com/WordPress/gutenberg/pull/26378)) - Add sticky support. ([26279](https://github.com/WordPress/gutenberg/pull/26279)) -- Global Styles: +- Global Styles: - Use block settings on the block panels. ([26218](https://github.com/WordPress/gutenberg/pull/26218)) - Fix: Font size picker regression on edit site global styles. ([26603](https://github.com/WordPress/gutenberg/pull/26603)) - Process settings only once. ([26330](https://github.com/WordPress/gutenberg/pull/26330)) -- Navigation Component: +- Navigation Component: - Add Support for RTL Languages. ([26334](https://github.com/WordPress/gutenberg/pull/26334)) - Styling revisions. ([26338](https://github.com/WordPress/gutenberg/pull/26338)) - Fix focus behavior when opening the panel. ([26296](https://github.com/WordPress/gutenberg/pull/26296)) @@ -270,7 +662,7 @@ - Improve @wordpress/I18n types. ([26171](https://github.com/WordPress/gutenberg/pull/26171)) - Migrate to builtin data controls. ([25993](https://github.com/WordPress/gutenberg/pull/25993)) ([25949](https://github.com/WordPress/gutenberg/pull/25949)) ([25773](https://github.com/WordPress/gutenberg/pull/25773)) ([25990](https://github.com/WordPress/gutenberg/pull/25990)) ([26509](https://github.com/WordPress/gutenberg/pull/26509)) ([25772](https://github.com/WordPress/gutenberg/pull/25772)) - Chore: Ensure WordPress packages share the same hoisted dependencies. ([26453](https://github.com/WordPress/gutenberg/pull/26453)) -- Use CSS-in-JS in @wordpress/components: +- Use CSS-in-JS in @wordpress/components: - Spinner. ([26433](https://github.com/WordPress/gutenberg/pull/26433)) - Disabled. ([25843](https://github.com/WordPress/gutenberg/pull/25843)) @@ -602,7 +994,7 @@ - Improve the Audio block shortcode transform to account for all sources. ([25114](https://github.com/WordPress/gutenberg/pull/25114)) - Code block: Allow HTML editing & rich text content. ([24689](https://github.com/WordPress/gutenberg/pull/24689)) - Remove appender from unselected Buttons and Social Icons block. ([25518](https://github.com/WordPress/gutenberg/pull/25518)) -- Widgets Screen: +- Widgets Screen: - Register legacy widgets as block variations. ([24905](https://github.com/WordPress/gutenberg/pull/24905)) - Use the default block list appender for the widget areas. ([25635](https://github.com/WordPress/gutenberg/pull/25635)) - Add titles to Legacy Widgets. ([25638](https://github.com/WordPress/gutenberg/pull/25638)) @@ -629,14 +1021,14 @@ ### Bug Fixes -- Widgets Screen: +- Widgets Screen: - Auto expand the last selected widget area when opening the inserter. ([25669](https://github.com/WordPress/gutenberg/pull/25669)) - Ensure all widgets are properly initialized when they're added, do not unmount widgets once they're mounted. ([25645](https://github.com/WordPress/gutenberg/pull/25645)) - Fix Legacy widget block previews and use iFrames. ([25443](https://github.com/WordPress/gutenberg/pull/25443)) ([14643](https://github.com/WordPress/gutenberg/pull/14643)) - Report save errors. ([25408](https://github.com/WordPress/gutenberg/pull/25408)) - Fix global inserter. ([24908](https://github.com/WordPress/gutenberg/pull/24908)) - Fix RangeControl direct entry in input field. ([25609](https://github.com/WordPress/gutenberg/pull/25609)) -- A11y: +- A11y: - Fix the color contrast in the code editor. ([25593](https://github.com/WordPress/gutenberg/pull/25593)) - Fix Publish sidebar Cancel button not usable through screen readers. ([25441](https://github.com/WordPress/gutenberg/pull/25441)) - Fix keyboard navigation on the Image block toolbar. ([25127](https://github.com/WordPress/gutenberg/pull/25127)) @@ -647,7 +1039,7 @@ - Remove Embed block aspect ratio classes on url change. ([25295](https://github.com/WordPress/gutenberg/pull/25295)) - Remove duplicate help item. ([25283](https://github.com/WordPress/gutenberg/pull/25283)) - Fix Block Directory author average rating formating. ([24732](https://github.com/WordPress/gutenberg/pull/24732)) -- @wordpress/api-fetch: +- @wordpress/api-fetch: - Fix preloading middleware referencing stale data. ([25550](https://github.com/WordPress/gutenberg/pull/25550)) - Check nonce header value before skipping adding it. ([25458](https://github.com/WordPress/gutenberg/pull/25458)) - Use esc_html instead of esc_attr in the Archives block. ([25476](https://github.com/WordPress/gutenberg/pull/25476)) @@ -698,7 +1090,7 @@ - Add new block supports page to the handbook. ([25647](https://github.com/WordPress/gutenberg/pull/25647)) - Block Directory: Add developer documentation. ([25591](https://github.com/WordPress/gutenberg/pull/25591)) - Move custom-fields note to the 'Register Meta Field' documentation. ([25584](https://github.com/WordPress/gutenberg/pull/25584)) -- Add Block Editor Components documentation: +- Add Block Editor Components documentation: - Warning ([25574](https://github.com/WordPress/gutenberg/pull/25574)) - FontSizePicker ([25568](https://github.com/WordPress/gutenberg/pull/25568)) - UnitControl ([25565](https://github.com/WordPress/gutenberg/pull/25565)) diff --git a/composer.json b/composer.json index 49d6605108cfef..2a16d1accff3e9 100644 --- a/composer.json +++ b/composer.json @@ -10,19 +10,26 @@ "support": { "issues": "https://github.com/WordPress/gutenberg/issues" }, + "config": { + "process-timeout": 0 + }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^0.7", "squizlabs/php_codesniffer": "^3.5", "phpcompatibility/php-compatibility": "^9.3", "wp-coding-standards/wpcs": "^2.2", "sirbrillig/phpcs-variable-analysis": "^2.8", - "wp-phpunit/wp-phpunit": "^5.4" + "wp-phpunit/wp-phpunit": "^5.4", + "phpunit/phpunit": "^7.5.20", + "spatie/phpunit-watcher": "^1.23" }, "require": { "composer/installers": "~1.0" }, "scripts": { "format": "phpcbf --standard=phpcs.xml.dist --report-summary --report-source", - "lint": "phpcs --standard=phpcs.xml.dist --runtime-set ignore_warnings_on_exit 1" + "lint": "phpcs --standard=phpcs.xml.dist --runtime-set ignore_warnings_on_exit 1", + "test": "phpunit", + "test:watch": "phpunit-watcher watch < /dev/tty" } } diff --git a/composer.lock b/composer.lock index ca3dde5106f29d..bff5a02fc188c0 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": "693be7bb8e616544f4b253f4a2168e04", + "content-hash": "a0cee819dff559c9839fd0e98cb4186a", "packages": [ { "name": "composer/installers", @@ -145,37 +145,2824 @@ } ], "packages-dev": [ + { + "name": "clue/stdio-react", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-stdio.git", + "reference": "5f42a3a5a29f52432f0f68b57890353e8ca65069" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-stdio/zipball/5f42a3a5a29f52432f0f68b57890353e8ca65069", + "reference": "5f42a3a5a29f52432f0f68b57890353e8ca65069", + "shasum": "" + }, + "require": { + "clue/term-react": "^1.0 || ^0.1.1", + "clue/utf8-react": "^1.0 || ^0.1", + "php": ">=5.3", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3", + "react/stream": "^1.0 || ^0.7 || ^0.6" + }, + "require-dev": { + "clue/arguments": "^2.0", + "clue/commander": "^1.2", + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + }, + "suggest": { + "ext-mbstring": "Using ext-mbstring should provide slightly better performance for handling I/O" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\Stdio\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@lueck.tv" + } + ], + "description": "Async, event-driven console input & output (STDIN, STDOUT) for truly interactive CLI applications, built on top of ReactPHP", + "homepage": "https://github.com/clue/reactphp-stdio", + "keywords": [ + "async", + "autocomplete", + "autocompletion", + "cli", + "history", + "interactive", + "reactphp", + "readline", + "stdin", + "stdio", + "stdout" + ], + "time": "2019-08-28T12:01:30+00:00" + }, + { + "name": "clue/term-react", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-term.git", + "reference": "eb6eb063eda04a714ef89f066586a2c49588f7ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-term/zipball/eb6eb063eda04a714ef89f066586a2c49588f7ca", + "reference": "eb6eb063eda04a714ef89f066586a2c49588f7ca", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/stream": "^1.0 || ^0.7" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\Term\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming terminal emulator, built on top of ReactPHP.", + "homepage": "https://github.com/clue/reactphp-term", + "keywords": [ + "C0", + "CSI", + "ansi", + "apc", + "ascii", + "c1", + "control codes", + "dps", + "osc", + "pm", + "reactphp", + "streaming", + "terminal", + "vt100", + "xterm" + ], + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2020-11-06T11:50:12+00:00" + }, + { + "name": "clue/utf8-react", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/clue/reactphp-utf8.git", + "reference": "8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/clue/reactphp-utf8/zipball/8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96", + "reference": "8bc3f8c874cdf642c8f10f9ae93aadb8cd63da96", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "react/stream": "^1.0 || ^0.7 || ^0.6 || ^0.5 || ^0.4 || ^0.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 ||^5.7 || ^4.8", + "react/stream": "^1.0 || ^0.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Clue\\React\\Utf8\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming UTF-8 parser, built on top of ReactPHP.", + "homepage": "https://github.com/clue/reactphp-utf8", + "keywords": [ + "reactphp", + "streaming", + "unicode", + "utf-8", + "utf8" + ], + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2020-11-06T11:48:09+00:00" + }, { "name": "dealerdirect/phpcodesniffer-composer-installer", "version": "v0.7.0", "source": { "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "e8d808670b8f882188368faaf1144448c169c0b7" + "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", + "reference": "e8d808670b8f882188368faaf1144448c169c0b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e8d808670b8f882188368faaf1144448c169c0b7", + "reference": "e8d808670b8f882188368faaf1144448c169c0b7", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2 || ^3 || 4.0.x-dev" + }, + "require-dev": { + "composer/composer": "*", + "phpcompatibility/php-compatibility": "^9.0", + "sensiolabs/security-checker": "^4.1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "franck.nijhof@dealerdirect.com", + "homepage": "http://www.frenck.nl", + "role": "Developer / IT Manager" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "homepage": "http://www.dealerdirect.com", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "time": "2020-06-25T14:57:39+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "time": "2017-07-23T21:35:13+00:00" + }, + { + "name": "jolicode/jolinotif", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/jolicode/JoliNotif.git", + "reference": "52f5b98f964f6009b8ec4c0e951edcd0862e2ac7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jolicode/JoliNotif/zipball/52f5b98f964f6009b8ec4c0e951edcd0862e2ac7", + "reference": "52f5b98f964f6009b8ec4c0e951edcd0862e2ac7", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "symfony/process": "^3.3|^4.0|^5.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "symfony/finder": "^3.3|^4.0|^5.0", + "symfony/phpunit-bridge": "^3.4.26|^4.0|^5.0" + }, + "bin": [ + "jolinotif" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Joli\\JoliNotif\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Loïck Piera", + "email": "pyrech@gmail.com" + } + ], + "description": "Send desktop notifications on Windows, Linux, MacOS.", + "keywords": [ + "MAC", + "growl", + "linux", + "notification", + "windows" + ], + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/jolicode/jolinotif", + "type": "tidelift" + } + ], + "time": "2020-06-17T08:25:38+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.10.2", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.2.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", + "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2020-09-03T19:13:55+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2020-09-17T18:55:26+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/8ce87516be71aae9b956f81906aaf0338e0d8a2d", + "reference": "8ce87516be71aae9b956f81906aaf0338e0d8a2d", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.1", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0", + "phpunit/phpunit": "^8.0 || ^9.0 <9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2020-09-29T09:10:42+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "6.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.1", + "phpunit/php-file-iterator": "^2.0", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.1 || ^4.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "suggest": { + "ext-xdebug": "^2.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2018-10-31T16:06:48+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "050bedf145a257b1ff02746c31894800e5122946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2018-09-13T20:33:42+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2019-06-07T04:22:29+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "abandoned": true, + "time": "2019-09-17T06:23:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "7.5.20", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.1", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.0", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpunit/phpunit-mock-objects": "*" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2020-01-08T08:45:45+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://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" + ], + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "6d24de090cd59cfc830263cfba965be77b563c13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/6d24de090cd59cfc830263cfba965be77b563c13", + "reference": "6d24de090cd59cfc830263cfba965be77b563c13", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + }, + "suggest": { + "ext-event": "~1.0 for ExtEventLoop", + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "time": "2020-01-01T18:39:52+00:00" + }, + { + "name": "react/stream", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/7c02b510ee3f582c810aeccd3a197b9c2f52ff1a", + "reference": "7c02b510ee3f582c810aeccd3a197b9c2f52ff1a", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "time": "2020-05-04T10:17:57+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "shasum": "" + }, + "require": { + "php": "^7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2018-07-12T15:12:46+00:00" + }, + { + "name": "sebastian/diff", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "time": "2019-02-04T06:01:07+00:00" + }, + { + "name": "sebastian/environment", + "version": "4.2.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2019-11-20T08:46:58+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2019-09-14T09:02:43+00:00" + }, + { + "name": "sebastian/global-state", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2017-04-27T15:39:26+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2018-10-04T04:07:39+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "sirbrillig/phpcs-variable-analysis", + "version": "v2.9.0", + "source": { + "type": "git", + "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", + "reference": "ff54d4ec7f2bd152d526fdabfeff639aa9b8be01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/ff54d4ec7f2bd152d526fdabfeff639aa9b8be01", + "reference": "ff54d4ec7f2bd152d526fdabfeff639aa9b8be01", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "squizlabs/php_codesniffer": "^3.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4 || ^0.5 || ^0.6", + "limedeck/phpunit-detailed-printer": "^3.1 || ^4.0 || ^5.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^5.0 || ^6.5 || ^7.0 || ^8.0", + "sirbrillig/phpcs-import-detection": "^1.1" + }, + "type": "phpcodesniffer-standard", + "autoload": { + "psr-4": { + "VariableAnalysis\\": "VariableAnalysis/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Sam Graham", + "email": "php-codesniffer-variableanalysis@illusori.co.uk" + }, + { + "name": "Payton Swick", + "email": "payton@foolord.com" + } + ], + "description": "A PHPCS sniff to detect problems with variables.", + "time": "2020-10-07T23:32:29+00:00" + }, + { + "name": "spatie/phpunit-watcher", + "version": "1.23.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/phpunit-watcher.git", + "reference": "8a8e0c3c8f3f03dfdb6bf62abf89c1b7273fc0b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/phpunit-watcher/zipball/8a8e0c3c8f3f03dfdb6bf62abf89c1b7273fc0b3", + "reference": "8a8e0c3c8f3f03dfdb6bf62abf89c1b7273fc0b3", + "shasum": "" + }, + "require": { + "clue/stdio-react": "^2.0", + "jolicode/jolinotif": "^2.0", + "php": "^7.2", + "symfony/console": "^4.0|^5.0", + "symfony/process": "^4.0|^5.0", + "symfony/yaml": "^4.0|^5.0", + "yosymfony/resource-watcher": "^2.0" + }, + "conflict": { + "yosymfony/resource-watcher": "<2.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "bin": [ + "phpunit-watcher" + ], + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\PhpUnitWatcher\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Automatically rerun PHPUnit tests when source code changes", + "homepage": "https://github.com/spatie/phpunit-watcher", + "keywords": [ + "phpunit-watcher", + "spatie" + ], + "time": "2020-10-27T07:36:25+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.5.8", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4", + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "time": "2020-10-23T02:01:07+00:00" + }, + { + "name": "symfony/console", + "version": "v5.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", + "reference": "e0b2c29c0fa6a69089209bbe8fcff4df2a313d0e", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1|^2", + "symfony/string": "^5.1" + }, + "conflict": { + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^4.4|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/event-dispatcher": "^4.4|^5.0", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^4.4|^5.0", + "symfony/var-dumper": "^4.4|^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": "Symfony Console Component", + "homepage": "https://symfony.com", + "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": "2020-10-24T12:01:57+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-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", + "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": "2020-09-07T11:33:47+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "e70eb5a69c2ff61ea135a13d2266e8914a67b3a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/e70eb5a69c2ff61ea135a13d2266e8914a67b3a0", + "reference": "e70eb5a69c2ff61ea135a13d2266e8914a67b3a0", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "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": "Symfony Finder Component", + "homepage": "https://symfony.com", + "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": "2020-10-24T12:01:57+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "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" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", + "reference": "c7cf3f858ec7d70b89559d6e6eb1f7c2517d479c", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + }, + "files": [ + "bootstrap.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": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "727d1096295d807c309fb01a851577302394c897" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897", + "reference": "727d1096295d807c309fb01a851577302394c897", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "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 for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "39d483bdf39be819deabf04ec872eb0b2410b531" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/39d483bdf39be819deabf04ec872eb0b2410b531", + "reference": "39d483bdf39be819deabf04ec872eb0b2410b531", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.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": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/8ff431c517be11c78c48a39a66d37431e26a6bed", + "reference": "8ff431c517be11c78c48a39a66d37431e26a6bed", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "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" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.20.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de", + "reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.20-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "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" + ], + "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": "2020-10-23T14:02:19+00:00" + }, + { + "name": "symfony/process", + "version": "v5.1.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "f00872c3f6804150d6a0f73b4151daab96248101" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/f00872c3f6804150d6a0f73b4151daab96248101", + "reference": "f00872c3f6804150d6a0f73b4151daab96248101", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.15" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "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": "Symfony Process Component", + "homepage": "https://symfony.com", + "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": "2020-10-24T12:01:57+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/e8d808670b8f882188368faaf1144448c169c0b7", - "reference": "e8d808670b8f882188368faaf1144448c169c0b7", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2 || ^3 || 4.0.x-dev" + "php": ">=7.2.5", + "psr/container": "^1.0" }, - "require-dev": { - "composer/composer": "*", - "phpcompatibility/php-compatibility": "^9.0", - "sensiolabs/security-checker": "^4.1.0" + "suggest": { + "symfony/service-implementation": "" }, - "type": "composer-plugin", + "type": "library", "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } }, "autoload": { "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + "Symfony\\Contracts\\Service\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -184,189 +2971,286 @@ ], "authors": [ { - "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", "keywords": [ - "PHPCodeSniffer", - "PHP_CodeSniffer", - "code quality", - "codesniffer", - "composer", - "installer", - "phpcs", - "plugin", - "qa", - "quality", - "standard", - "standards", - "style guide", - "stylecheck", - "tests" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], - "time": "2020-06-25T14:57:39+00:00" + "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": "2020-09-07T11:33:47+00:00" }, { - "name": "phpcompatibility/php-compatibility", - "version": "9.3.5", + "name": "symfony/string", + "version": "v5.1.8", "source": { "type": "git", - "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", - "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + "url": "https://github.com/symfony/string.git", + "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", - "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "url": "https://api.github.com/repos/symfony/string/zipball/a97573e960303db71be0dd8fda9be3bca5e0feea", + "reference": "a97573e960303db71be0dd8fda9be3bca5e0feea", "shasum": "" }, "require": { - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" - }, - "conflict": { - "squizlabs/php_codesniffer": "2.6.2" + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" }, "require-dev": { - "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + "symfony/error-handler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0" }, - "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", - "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] }, - "type": "phpcodesniffer-standard", "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0-or-later" + "MIT" ], "authors": [ { - "name": "Wim Godden", - "homepage": "https://github.com/wimg", - "role": "lead" - }, - { - "name": "Juliette Reinders Folmer", - "homepage": "https://github.com/jrfnl", - "role": "lead" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Contributors", - "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", - "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "description": "Symfony String component", + "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "phpcs", - "standards" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" ], - "time": "2019-12-27T09:44:58+00:00" + "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": "2020-10-24T12:01:57+00:00" }, { - "name": "sirbrillig/phpcs-variable-analysis", - "version": "v2.9.0", + "name": "symfony/yaml", + "version": "v5.1.8", "source": { "type": "git", - "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", - "reference": "ff54d4ec7f2bd152d526fdabfeff639aa9b8be01" + "url": "https://github.com/symfony/yaml.git", + "reference": "f284e032c3cefefb9943792132251b79a6127ca6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/ff54d4ec7f2bd152d526fdabfeff639aa9b8be01", - "reference": "ff54d4ec7f2bd152d526fdabfeff639aa9b8be01", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f284e032c3cefefb9943792132251b79a6127ca6", + "reference": "f284e032c3cefefb9943792132251b79a6127ca6", "shasum": "" }, "require": { - "php": ">=5.4.0", - "squizlabs/php_codesniffer": "^3.1" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<4.4" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.4 || ^0.5 || ^0.6", - "limedeck/phpunit-detailed-printer": "^3.1 || ^4.0 || ^5.0", - "phpstan/phpstan": "^0.11.8", - "phpunit/phpunit": "^5.0 || ^6.5 || ^7.0 || ^8.0", - "sirbrillig/phpcs-import-detection": "^1.1" + "symfony/console": "^4.4|^5.0" }, - "type": "phpcodesniffer-standard", + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", "autoload": { "psr-4": { - "VariableAnalysis\\": "VariableAnalysis/" - } + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-2-Clause" + "MIT" ], "authors": [ { - "name": "Sam Graham", - "email": "php-codesniffer-variableanalysis@illusori.co.uk" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "Payton Swick", - "email": "payton@foolord.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A PHPCS sniff to detect problems with variables.", - "time": "2020-10-07T23:32:29+00:00" + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "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": "2020-10-24T12:03:25+00:00" }, { - "name": "squizlabs/php_codesniffer", - "version": "3.5.8", + "name": "theseer/tokenizer", + "version": "1.2.0", "source": { "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "9d583721a7157ee997f235f327de038e7ea6dac4" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "75a63c33a8577608444246075ea0af0d052e452a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4", - "reference": "9d583721a7157ee997f235f327de038e7ea6dac4", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", + "reference": "75a63c33a8577608444246075ea0af0d052e452a", "shasum": "" }, "require": { - "ext-simplexml": "*", + "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": ">=5.4.0" + "php": "^7.2 || ^8.0" }, - "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] }, - "bin": [ - "bin/phpcs", - "bin/phpcbf" + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } ], + "time": "2020-07-12T23:59:07+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0 || ^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<3.9.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36 || ^7.5.13" + }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Greg Sherwood", - "role": "lead" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "description": "Assertions to validate method input/output with nice error messages.", "keywords": [ - "phpcs", - "standards" + "assert", + "check", + "validate" ], - "time": "2020-10-23T02:01:07+00:00" + "time": "2020-07-08T17:02:28+00:00" }, { "name": "wp-coding-standards/wpcs", @@ -416,7 +3300,7 @@ }, { "name": "wp-phpunit/wp-phpunit", - "version": "5.5.1", + "version": "5.5.3", "source": { "type": "git", "url": "https://github.com/wp-phpunit/wp-phpunit.git", @@ -456,6 +3340,59 @@ "wordpress" ], "time": "2020-09-02T15:53:50+00:00" + }, + { + "name": "yosymfony/resource-watcher", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/yosymfony/resource-watcher.git", + "reference": "a8c34f704e6bd4f786c97f3c0ba65bd86cb2bd73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yosymfony/resource-watcher/zipball/a8c34f704e6bd4f786c97f3c0ba65bd86cb2bd73", + "reference": "a8c34f704e6bd4f786c97f3c0ba65bd86cb2bd73", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "symfony/finder": "^2.7|^3.0|^4.0|^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7", + "symfony/filesystem": "^2.7|^3.0|^4.0|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Yosymfony\\ResourceWatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Victor Puertas", + "email": "vpgugr@gmail.com" + } + ], + "description": "A simple resource watcher using Symfony Finder", + "homepage": "http://yosymfony.com", + "keywords": [ + "finder", + "resources", + "symfony", + "watcher" + ], + "time": "2020-01-04T15:36:55+00:00" } ], "aliases": [], diff --git a/docs/contributors/release.md b/docs/contributors/release.md index 750e6ec17c05d0..a1bc58edce80a6 100644 --- a/docs/contributors/release.md +++ b/docs/contributors/release.md @@ -199,9 +199,9 @@ You'll need to use Subversion to publish the plugin to WordPress.org. 6. Add new files/remove deleted files from the repository: ```bash # Add new files: -svn st | grep '^\?' | awk '{print $2}' | xargs svn add +svn st | grep '^\?' | awk '{print $2}' | xargs svn add # add the -r option to xargs if you use a linux-based OS # Delete old files: -svn st | grep '^!' | awk '{print $2}' | xargs svn rm +svn st | grep '^!' | awk '{print $2}' | xargs svn rm # add the -r option to xargs if you use a linux-based OS ``` 7. Commit the new version: ```bash @@ -233,7 +233,7 @@ If you don't have access to [make.wordpress.org/core](https://make.wordpress.org The Gutenberg repository mirrors the [WordPress SVN repository](https://make.wordpress.org/core/handbook/about/release-cycle/) in terms of branching for each SVN branch, a corresponding Gutenberg `wp/*` branch is created: - The `wp/trunk` branch contains all the packages that are published and used in the `trunk` branch of WordPress. -- A Gutenberg branch targeting a specific WordPress major release (including its further minor increments) is created (example `wp/5.2`) based on the `wp/trunk` Gutenberg branch when the WordPress `trunk` branch is marked as "feature-freezed". (This usually happens when the first `beta` of the next WordPress major version is released). +- A Gutenberg branch targeting a specific WordPress major release (including its further minor increments) is created (example `wp/5.2`) based on the `wp/trunk` Gutenberg branch when the corresponding WordPress release branch is created. (This usually happens when the first `RC` of the next WordPress major version is released). ### Synchronizing WordPress Trunk @@ -249,7 +249,7 @@ The first step is automated via `./bin/plugin/cli.js npm-stable` command. You on 4. Remove all files from the current branch: `git rm -r .`. 5. Check out all the files from the release branch: `git checkout release/x.x -- .`. 6. Commit all changes to the `wp/trunk` branch with `git commit -m "Merge changes published in the Gutenberg plugin vX.X release"` and push to the repository. -7. Update the `CHANGELOG.md` files of the packages with the new publish version calculated and commit to the `wp/trunk` branch. Assuming the package versions are written using this format `major.minor.patch`, make sure to bump at least the `minor` version number. For example, if the CHANGELOG of the package to be released indicates that the next unreleased version is `5.6.1`, choose `5.7.0` as a version in case of `minor` version. +7. Update the `CHANGELOG.md` files of the packages with the new publish version calculated and commit to the `wp/trunk` branch. Assuming the package versions are written using this format `major.minor.patch`, make sure to bump at least the `minor` version number. For example, if the CHANGELOG of the package to be released indicates that the next unreleased version is `5.6.1`, choose `5.7.0` as a version in case of `minor` version. This is important as the patch version numbers should be reserved in case bug fixes are needed for a minor WordPress release (see below). Once the command is finished, you can start the second part: publishing the npm packages. @@ -270,15 +270,16 @@ The following workflow is needed when bug fixes or security releases need to be - During the `beta` and the `RC` period of the WordPress release cycle. - For WordPress minor releases and WordPress security releases (example `5.1.1`). -1. Cherry-pick -2. Check out the last published Gutenberg release branch `git checkout release/x.x` -3. Create a Pull Request from this branch targeting the WordPress related major branch (Example `wp/5.2`). +1. Check out the relevant WordPress major branch (If the minor release is 5.2.1, check out `wp/5.2`). +2. Create a feature branch from that branch, and cherry-pick the merge commits for the needed bug fixes onto it. +3. Create a Pull Request from this branch targeting the WordPress major branch used above. 4. Merge the Pull Request using the "Rebase and Merge" button to keep the history of the commits. Now, the branch is ready to be used to publish the npm packages. 1. Check out the WordPress branch used before (Example `wp/5.2`). -2. Run the [package release process] but when asked for the version numbers to choose for each package, (assuming the package versions are written using this format `major.minor.patch`) make sure to bump only the `patch` version number. For example, if the last published package version for this WordPress branch was `5.6.0`, choose `5.6.1` as a version. +2. `git pull`. +3. Run the [package release process] but when asked for the version numbers to choose for each package, (assuming the package versions are written using this format `major.minor.patch`) make sure to bump only the `patch` version number. For example, if the last published package version for this WordPress branch was `5.6.0`, choose `5.6.1` as a version. **Note:** For WordPress `5.0` and WordPress `5.1`, a different release process was used. This means that when choosing npm package versions targeting these two releases, you won't be able to use the next `patch` version number as it may have been already used. You should use the "metadata" modifier for these. For example, if the last published package version for this WordPress branch was `5.6.1`, choose `5.6.1+patch.1` as a version. @@ -311,7 +312,7 @@ Now _cherry-pick_ the commits from `master` to `wp/trunk`, use `-m 1 commithash` 1. `git cherry-pick -m 1 cb150a2` 2. `git push` -Whilst waiting for the Travis CI build for `wp/trunk` [branch to pass](https://travis-ci.com/WordPress/gutenberg/branches) identify and begin updating the `CHANGELOG.md` files: +Whilst waiting for the GitHub actions build for `wp/trunk`[branch to pass](https://github.com/WordPress/gutenberg/actions?query=branch%3Awp%2Ftrunk), identify and begin updating the `CHANGELOG.md` files: 1. `git checkout wp/trunk` 2. `npm run publish:check` > Example diff --git a/docs/contributors/testing-overview.md b/docs/contributors/testing-overview.md index c48360190946f3..7616af58afacf0 100644 --- a/docs/contributors/testing-overview.md +++ b/docs/contributors/testing-overview.md @@ -355,9 +355,8 @@ It's tempting to snapshot deep renders, but that makes for huge snapshots. Addit Sometimes we need to mock refs for some stories which use them. Check the following documents to learn more: - Why we need to use [Mocking Refs for Snapshot Testing](https://reactjs.org/blog/2016/11/16/react-v15.4.0.html#mocking-refs-for-snapshot-testing) with React. -- [Using createNodeMock to mock refs](https://github.com/storybookjs/storybook/tree/master/addons/storyshots/storyshots-core#using-createnodemock-to-mock-refs) with StoryShots. -In that case, you might see test failures and `TypeError` reported by Jest in the lines which try to access a property from `ref.current`. If this happens, search for `initStoryshots` method call, which contains all necessary configurations to adjust. +In that case, you might see test failures and `TypeError` reported by Jest in the lines which try to access a property from `ref.current`. ### Debugging Jest unit tests @@ -461,8 +460,16 @@ Tests for PHP use [PHPUnit](https://phpunit.de/) as the testing framework. If yo npm run test-php ``` +To re-run tests automatically when files change (similar to Jest), run: + +``` +npm run test-php:watch +``` + _Note: The phpunit commands require `wp-env` to be running and composer dependencies to be installed. The package script will start wp-env for you if it is not already running._ +In other environments, run `composer run test` and `composer run test:watch`. + Code style in PHP is enforced using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer). It is recommended that you install PHP_CodeSniffer and the [WordPress Coding Standards for PHP_CodeSniffer](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards#installation) ruleset using [Composer](https://getcomposer.org/). With Composer installed, run `composer install` from the project directory to install dependencies. The above `npm run test-php` will execute both unit tests and code linting. Code linting can be verified independently by running `npm run lint-php`. To run unit tests only, without the linter, use `npm run test-unit-php` instead. diff --git a/docs/designers-developers/developers/block-api/block-deprecation.md b/docs/designers-developers/developers/block-api/block-deprecation.md index ba1dd2f25b1f97..d598a157d4ab71 100644 --- a/docs/designers-developers/developers/block-api/block-deprecation.md +++ b/docs/designers-developers/developers/block-api/block-deprecation.md @@ -1,19 +1,47 @@ # Deprecated Blocks -When updating static blocks markup and attributes, block authors need to consider existing posts using the old versions of their block. In order to provide a good upgrade path, you can choose one of the following strategies: +When updating static blocks markup and attributes, block authors need to consider existing posts using the old versions of their block. To provide a good upgrade path, you can choose one of the following strategies: - Do not deprecate the block and create a new one (a different name) - Provide a "deprecated" version of the block allowing users opening these in the block editor to edit them using the updated block. -A block can have several deprecated versions. A deprecation will be tried if a parsed block appears to be invalid, or if there is a deprecation defined for which its `isEligible` property function returns true. +A block can have several deprecated versions. A deprecation will be tried if the current state of a parsed block is invalid, or if the deprecation defines an `isEligible` function that returns true. + +It is important to note that if a deprecation's `save` method does not produce a valid block then it is skipped, including its `migrate` method, even if `isEligible` would return true for the given attributes. This means that if you have several deprecations for a block and want to perform a new migration, like moving content to `InnerBlocks`, you may need to include the `migrate` method in multiple deprecations for it to be applied to all previous versions of the block. + +Deprecations do not operate as a chain of updates in the way other software data updates, like database migrations, do. At first glance, it is easy to think that each deprecation is going to make the required changes to the data and then hand this new form of the block onto the next deprecation to make its changes. What happens instead, is that each deprecation is passed the original saved content, and if its `save` method produces valid content the deprecation is used to parse the block attributes. If it has a `migrate` method it will also be run using the attributes parsed by the deprecation. The current block is updated with the migrated attributes and inner blocks before the current block's `save` function is run to generate new valid content for the block. At this point the current block should now be in a valid state. + +For blocks with multiple deprecations, it may be easier to save each deprecation to a constant with the version of the block it applies to, and then add each of these to the block's `deprecated` array. The deprecations in the array should be in reverse chronological order. This allows the block editor to attempt to apply the most recent and likely deprecations first, avoiding unnecessary and expensive processing. + +### Example: + +{% codetabs %} +{% ESNext %} +```js +const v1 = {}; +const v2 = {}; +const v3 = {}; +const deprecated = [ v3, v2, v1 ]; + +``` +{% ES5 %} +```js +var v1 = {}; +var v2 = {}; +var v3 = {}; +var deprecated = [ v3, v2, v1 ]; +``` +{% end %} + +It is also recommended to keep [fixtures](https://github.com/WordPress/gutenberg/blob/master/packages/e2e-tests/fixtures/blocks/README.md) which contain the different versions of the block content to allow you to easily test that new deprecations and migrations are working across all previous versions of the block. Deprecations are defined on a block type as its `deprecated` property, an array of deprecation objects where each object takes the form: - `attributes` (Object): The [attributes definition](/docs/designers-developers/developers/block-api/block-attributes.md) of the deprecated form of the block. - `supports` (Object): The [supports definition](/docs/designers-developers/developers/block-api/block-registration.md) of the deprecated form of the block. - `save` (Function): The [save implementation](/docs/designers-developers/developers/block-api/block-edit-save.md) of the deprecated form of the block. -- `migrate` (Function, Optional): A function which, given the old attributes and inner blocks is expected to return either the new attributes or a tuple array of `[ attributes, innerBlocks ]` compatible with the block. -- `isEligible` (Function, Optional): A function which, given the attributes and inner blocks of the parsed block, returns true if the deprecation can handle the block migration even if the block is valid. This function is not called when the block is invalid. This is particularly useful in cases where a block is technically valid even once deprecated, and requires updates to its attributes or inner blocks. +- `migrate` (Function, Optional): A function which, given the old attributes and inner blocks is expected to return either the new attributes or a tuple array of `[ attributes, innerBlocks ]` compatible with the block. As mentioned above, a deprecation's `migrate` will not be run if its `save` function does not return a valid block so you will need to make sure your migrations are available in all the deprecations where they are relevant. +- `isEligible` (Function, Optional): A function which, given the attributes and inner blocks of the parsed block, returns true if the deprecation can handle the block migration even if the block is valid. This is particularly useful in cases where a block is technically valid even once deprecated, and requires updates to its attributes or inner blocks. This function is not called when the results of all previous deprecations' `save` functions were invalid. It's important to note that `attributes`, `supports`, and `save` are not automatically inherited from the current version, since they can impact parsing and serialization of a block, so they must be defined on the deprecated object in order to be processed during a migration. diff --git a/docs/designers-developers/developers/block-api/block-edit-save.md b/docs/designers-developers/developers/block-api/block-edit-save.md index 5086bf7fdd3863..d51ae9f7820581 100644 --- a/docs/designers-developers/developers/block-api/block-edit-save.md +++ b/docs/designers-developers/developers/block-api/block-edit-save.md @@ -9,75 +9,118 @@ The `edit` function describes the structure of your block in the context of the {% codetabs %} {% ESNext %} ```jsx -edit: () => { - return
Your block.
; -} +import { useBlockProps } from '@wordpress/block-editor'; + +// ... +const blockSettings = { + apiVersion: 2, + + // ... + + edit: () => { + const blockProps = useBlockProps(); + + return
Your block.
; + } +}; ``` {% ES5 %} ```js -// A static div -edit: function() { - return wp.element.createElement( - 'div', - null, - 'Your block.' - ); -} +var blockSettings = { + apiVersion: 2, + + // ... + + edit: function() { + var blockProps = wp.blockEditor.useBlockProps(); + + return wp.element.createElement( + 'div', + blockProps, + 'Your block.' + ); + } +}; ``` {% end %} -The function receives the following properties through an object argument: +### block wrapper props -### attributes +The first thing to notice here is the use of the `useBlockProps` React hook on the block wrapper element. In the example above, the block wrapper renders a "div" in the editor, but in order for the Gutenberg editor to know how to manipulate the block, add any extra classNames that are needed for the block... the block wrapper element should apply props retrieved from the `useBlockProps` react hook call. -This property surfaces all the available attributes and their corresponding values, as described by the `attributes` property when the block type was registered. See [attributes documentation](/docs/designers-developers/developers/block-api/block-attributes.md) for how to specify attribute sources. - -In this case, assuming we had defined an attribute of `content` during block registration, we would receive and use that value in our edit function: +If the element wrapper needs any extra custom HTML attributes, these need to be passed as an argument to the `useBlockProps` hook. For example to add a `my-random-classname` className to the wrapper, you can use the following code: {% codetabs %} {% ESNext %} -```js -edit: ( { attributes } ) => { - return
{ attributes.content }
; -} +```jsx +import { useBlockProps } from '@wordpress/block-editor'; + +// ... +const blockSettings = { + apiVersion: 2, + + // ... + + edit: () => { + const blockProps = useBlockProps( { className: 'my-random-classname' } ); + + return
Your block.
; + } +}; ``` {% ES5 %} ```js -edit: function( props ) { - return wp.element.createElement( - 'div', - null, - props.attributes.content - ); -} +var blockSettings = { + apiVersion: 2, + + // ... + + edit: function() { + var blockProps = wp.blockEditor.useBlockProps( { className: 'my-random-classname' } ); + + return wp.element.createElement( + 'div', + blockProps, + 'Your block.' + ); + } +}; ``` {% end %} -The value of `attributes.content` will be displayed inside the `div` when inserting the block in the editor. +### attributes + +The `edit` function also receives a number of properties through an object argument. You can use these properties to adapt the behavior of your block. -### className +The `attributes` property surfaces all the available attributes and their corresponding values, as described by the `attributes` property when the block type was registered. See [attributes documentation](/docs/designers-developers/developers/block-api/block-attributes.md) for how to specify attribute sources. -This property returns the class name for the wrapper element. This is automatically added in the `save` method, but not on `edit`, as the root element may not correspond to what is _visually_ the main element of the block. You can request it to add it to the correct element in your function. +In this case, assuming we had defined an attribute of `content` during block registration, we would receive and use that value in our edit function: {% codetabs %} {% ESNext %} ```js -edit: ( { attributes, className } ) => { - return
{ attributes.content }
; +edit: ( { attributes } ) => { + const blockProps = useBlockProps(); + + return
{ attributes.content }
; } ``` {% ES5 %} ```js edit: function( props ) { + var blockProps = wp.blockEditor.useBlockProps(); + return wp.element.createElement( 'div', - { className: props.className }, + blockProps, props.attributes.content ); } ``` {% end %} +The value of `attributes.content` will be displayed inside the `div` when inserting the block in the editor. + ### isSelected The isSelected property is an object that communicates whether the block is currently selected. @@ -85,9 +128,11 @@ The isSelected property is an object that communicates whether the block is curr {% codetabs %} {% ESNext %} ```jsx -edit: ( { attributes, className, isSelected } ) => { +edit: ( { attributes, isSelected } ) => { + const blockProps = useBlockProps(); + return ( -
+
Your block. { isSelected && Shows only when the block is selected. @@ -99,9 +144,11 @@ edit: ( { attributes, className, isSelected } ) => { {% ES5 %} ```js edit: function( props ) { + var blockProps = wp.blockEditor.useBlockProps(); + return wp.element.createElement( 'div', - { className: props.className }, + blockProps, [ 'Your block.', props.isSelected ? wp.element.createElement( @@ -122,14 +169,16 @@ This function allows the block to update individual attributes based on user int {% codetabs %} {% ESNext %} ```jsx -edit: ( { attributes, setAttributes, className, isSelected } ) => { +edit: ( { attributes, setAttributes, isSelected } ) => { + const blockProps = useBlockProps(); + // Simplify access to attributes const { content, mySetting } = attributes; // Toggle a setting when the user clicks the button const toggleSetting = () => setAttributes( { mySetting: ! mySetting } ); return ( -
+
{ content } { isSelected && @@ -141,6 +190,8 @@ edit: ( { attributes, setAttributes, className, isSelected } ) => { {% ES5 %} ```js edit: function( props ) { + var blockProps = wp.blockEditor.useBlockProps(); + // Simplify access to attributes let content = props.attributes.content; let mySetting = props.attributes.mySetting; @@ -149,7 +200,7 @@ edit: function( props ) { let toggleSetting = () => props.setAttributes( { mySetting: ! mySetting } ); return wp.element.createElement( 'div', - { className: props.className }, + blockProps, [ content, props.isSelected ? wp.element.createElement( @@ -207,15 +258,19 @@ The `save` function defines the way in which the different attributes should be {% ESNext %} ```jsx save: () => { - return
Your block.
; + const blockProps = useBlockProps.save(); + + return
Your block.
; } ``` {% ES5 %} ```js save: function() { + var blockProps = wp.blockEditor.useBlockProps.save(); + return wp.element.createElement( 'div', - null, + blockProps, 'Your block.' ); } @@ -237,6 +292,10 @@ For [dynamic blocks](/docs/designers-developers/developers/tutorials/block-tutor If left unspecified, the default implementation will save no markup in post content for the dynamic block, instead deferring this to always be calculated when the block is shown on the front of the site. +### block wrapper props + +Like the `edit` function, when rendering static blocks, it's important to add the block props returned by `useBlockProps.save()` to the wrapper element of your block. This ensures that the block class name is rendered properly in addition to any HTML attribute injected by the block supports API. + ### attributes As with `edit`, the `save` function also receives an object argument including attributes which can be inserted into the markup. @@ -245,15 +304,19 @@ As with `edit`, the `save` function also receives an object argument including a {% ESNext %} ```jsx save: ( { attributes } ) => { - return
{ attributes.content }
; + const blockProps = useBlockProps.save(); + + return
{ attributes.content }
; } ``` {% ES5 %} ```js save: function( props ) { + var blockProps = wp.blockEditor.useBlockProps.save(); + return wp.element.createElement( 'div', - null, + blockProps, props.attributes.content ); } @@ -276,23 +339,30 @@ attributes: { content: { type: 'string', source: 'html', - selector: 'p' + selector: 'div' } }, edit: ( { attributes, setAttributes } ) => { + const blockProps = useBlockProps(); const updateFieldValue = ( val ) => { setAttributes( { content: val } ); } - return ; + return ( +
+ +

+ ); }, save: ( { attributes } ) => { - return

{ attributes.content }

; + const blockProps = useBlockProps.save(); + + return
{ attributes.content }
; }, ``` {% ES5 %} @@ -306,22 +376,30 @@ attributes: { }, edit: function( props ) { + var blockProps = wp.blockEditor.useBlockProps(); var updateFieldValue = function( val ) { props.setAttributes( { content: val } ); } + return wp.element.createElement( - wp.components.TextControl, - { - label: 'My Text Field', - value: props.attributes.content, - onChange: updateFieldValue, + 'div', + blockProps, + wp.element.createElement( + wp.components.TextControl, + { + label: 'My Text Field', + value: props.attributes.content, + onChange: updateFieldValue, - } + } + ) ); }, save: function( props ) { - return el( 'p', {}, props.attributes.content ); + var blockProps = wp.blockEditor.useBlockProps.save(); + + return wp.element.createElement( 'div', blockProps, props.attributes.content ); }, ``` {% end %} @@ -342,13 +420,18 @@ attributes: { }, edit: ( { attributes, setAttributes } ) => { - return { - setAttributes( { postsToShow: parseInt( val ) } ); - }}, - } + const blockProps = useBlockProps(); + + return ( +
+ { + setAttributes( { postsToShow: parseInt( val ) } ); + }} + /> +

); }, @@ -365,15 +448,21 @@ attributes: { }, edit: function( props ) { - return wp.element.createElement( - wp.components.TextControl, - { - label: 'Number Posts to Show', - value: props.attributes.postsToShow, - onChange: function( val ) { - props.setAttributes( { postsToShow: parseInt( val ) } ); - }, - } + var blockProps = wp.blockEditor.useBlockProps(); + + return wp.element.createEleement( + 'div', + blockProps, + wp.element.createElement( + wp.components.TextControl, + { + label: 'Number Posts to Show', + value: props.attributes.postsToShow, + onChange: function( val ) { + props.setAttributes( { postsToShow: parseInt( val ) } ); + }, + } + ) ); }, diff --git a/docs/designers-developers/developers/block-api/block-patterns.md b/docs/designers-developers/developers/block-api/block-patterns.md index dbcf18f973fa87..2987118a0479e4 100644 --- a/docs/designers-developers/developers/block-api/block-patterns.md +++ b/docs/designers-developers/developers/block-api/block-patterns.md @@ -14,9 +14,9 @@ The properties of the block pattern include: - `title` (required): A human-readable title for the pattern. - `content` (required): Raw HTML content for the pattern. - `description`: A visually hidden text used to describe the pattern in the inserter. A description is optional but it is strongly encouraged when the title does not fully describe what the pattern does. - - `categories`: A list of pattern categories used to group block patterns. Block patterns can be shown on multiple categories. - - `keywords`: Aliases or keywords that help users discover it while searching. - - `viewportWidth`: Specify the width of the pattern in the inserter. + - `categories`: An array of pattern categories used to group block patterns. Block patterns can be shown on multiple categories. + - `keywords`: An array of aliases or keywords that help users discover the pattern while searching. + - `viewportWidth`: An integer specifying the width of the pattern in the inserter. ```php register_block_pattern( diff --git a/docs/designers-developers/developers/block-api/block-registration.md b/docs/designers-developers/developers/block-api/block-registration.md index 46560971890650..ffe0981510eb16 100644 --- a/docs/designers-developers/developers/block-api/block-registration.md +++ b/docs/designers-developers/developers/block-api/block-registration.md @@ -222,7 +222,7 @@ example: { #### variations (optional) -- **Type:** `Object[]` +- **Type:** `Object[]` Similarly to how the block's style variations can be declared, a block type can define block variations that the user can pick from. The difference is that, rather than changing only the visual appearance, this field provides a way to apply initial custom attributes and inner blocks at the time when a block is inserted. @@ -256,19 +256,20 @@ variations: [ An object describing a variation defined for the block type can contain the following fields: -- `name` (type `string`) – The unique and machine-readable name. -- `title` (type `string`) – A human-readable variation title. -- `description` (optional, type `string`) – A detailed variation description. -- `icon` (optional, type `string` | `Object`) – An icon helping to visualize the variation. It can have the same shape as the block type. -- `isDefault` (optional, type `boolean`) – Indicates whether the current variation is the default one. Defaults to `false`. -- `attributes` (optional, type `Object`) – Values that override block attributes. -- `innerBlocks` (optional, type `Array[]`) – Initial configuration of nested blocks. -- `example` (optional, type `Object`) – Example provides structured data for the block preview. You can set to `undefined` to disable the preview shown for the block type. -- `scope` (optional, type `WPBlockVariationScope[]`) - the list of scopes where the variation is applicable. When not provided, it defaults to `block` and `inserter`. Available options: - - `inserter` - Block Variation is shown on the inserter. - - `block` - Used by blocks to filter specific block variations. Mostly used in Placeholder patterns like `Columns` block. - - `transform` - Block Variation will be shown in the component for Block Variations transformations. -- `keywords` (optional, type `string[]`) - An array of terms (which can be translated) that help users discover the variation while searching. +- `name` (type `string`) – The unique and machine-readable name. +- `title` (type `string`) – A human-readable variation title. +- `description` (optional, type `string`) – A detailed variation description. +- `icon` (optional, type `string` | `Object`) – An icon helping to visualize the variation. It can have the same shape as the block type. +- `isDefault` (optional, type `boolean`) – Indicates whether the current variation is the default one. Defaults to `false`. +- `attributes` (optional, type `Object`) – Values that override block attributes. +- `innerBlocks` (optional, type `Array[]`) – Initial configuration of nested blocks. +- `example` (optional, type `Object`) – Example provides structured data for the block preview. You can set to `undefined` to disable the preview shown for the block type. +- `scope` (optional, type `WPBlockVariationScope[]`) - the list of scopes where the variation is applicable. When not provided, it defaults to `block` and `inserter`. Available options: + - `inserter` - Block Variation is shown on the inserter. + - `block` - Used by blocks to filter specific block variations. Mostly used in Placeholder patterns like `Columns` block. + - `transform` - Block Variation will be shown in the component for Block Variations transformations. +- `keywords` (optional, type `string[]`) - An array of terms (which can be translated) that help users discover the variation while searching. +- `isActive` (optional, type `Function`) - A function that accepts a block's attributes and the variation's attributes and determines if a variation is active. This function doesn't try to find a match dynamically based on all block's attributes, as in many cases some attributes are irrelevant. An example would be for `embed` block where we only care about `providerNameSlug` attribute's value. It's also possible to override the default block style variation using the `className` attribute when defining block variations. @@ -278,15 +279,17 @@ variations: [ name: 'blue', title: __( 'Blue Quote' ), isDefault: true, - attributes: { className: 'is-style-blue-quote' }, + attributes: { color: 'blue', className: 'is-style-blue-quote' }, icon: 'format-quote', + isActive: ( blockAttributes, variationAttributes ) => + blockAttributes.color === variationAttributes.color }, ], ``` #### supports (optional) -- ***Type:*** `Object` +- **_Type:_** `Object` Supports contains as set of options to control features used in the editor. See the [the supports documentation](/docs/designers-developers/developers/block-api/block-supports.md) for more details. diff --git a/docs/designers-developers/developers/block-api/block-supports.md b/docs/designers-developers/developers/block-api/block-supports.md index cd93af21096276..b4d8634ed33816 100644 --- a/docs/designers-developers/developers/block-api/block-supports.md +++ b/docs/designers-developers/developers/block-api/block-supports.md @@ -338,24 +338,6 @@ supports: { } ``` -When the block declares support for a specific spacing property, the attributes definition is extended to include some attributes. +When the block declares support for a specific spacing property, the attributes definition is extended to include the `style` attribute. -- `style`: attribute of `object` type with no default assigned. This is added when `padding` support is declared. It stores the custom values set by the user. The block can apply a default style by specifying its own `style` attribute with a default e.g.: - -```js -attributes: { - style: { - type: 'object', - default: { - spacing: { - padding: { - top: 'value', - right: 'value', - bottom: 'value', - left: 'value' - } - } - } - } -} -``` +- `style`: attribute of `object` type with no default assigned. This is added when `padding` support is declared. It stores the custom values set by the user. diff --git a/docs/designers-developers/developers/block-api/block-templates.md b/docs/designers-developers/developers/block-api/block-templates.md index ad961e8f8066d1..42e7a660545833 100644 --- a/docs/designers-developers/developers/block-api/block-templates.md +++ b/docs/designers-developers/developers/block-api/block-templates.md @@ -1,6 +1,6 @@ # Templates -A block template is defined as a list of block items. Such blocks can have predefined attributes, placeholder content, and be static or dynamic. Block templates allow to specify a default initial state for an editor session. +A block template is defined as a list of block items. Such blocks can have predefined attributes, placeholder content, and be static or dynamic. Block templates allow specifying a default initial state for an editor session. The scope of templates include: @@ -110,6 +110,8 @@ add_action( 'init', 'myplugin_register_template' ); - `all` — prevents all operations. It is not possible to insert new blocks, move existing blocks, or delete blocks. - `insert` — prevents inserting or removing blocks, but allows moving existing blocks. +Lock settings can be inherited by InnerBlocks. If `templateLock` is not set in an InnerBlocks area, the locking of the parent InnerBlocks area is used. If the block is a top level block, the locking configuration of the current post type is used. + ## Nested Templates Container blocks like the columns blocks also support templates. This is achieved by assigning a nested template to the block. diff --git a/docs/designers-developers/developers/block-api/versions.md b/docs/designers-developers/developers/block-api/versions.md new file mode 100644 index 00000000000000..cd78b5873dba66 --- /dev/null +++ b/docs/designers-developers/developers/block-api/versions.md @@ -0,0 +1,12 @@ +# Block API Versions + +This document lists the changes made between the different API versions. + +## Version 2 (>= WordPress 5.6) + +- To render the block element wrapper for the block's `edit` implementation, the block author must use the `useBlockProps()` hook. +- The generated class names and styles are no longer added automatically to the saved markup for static blocks when `save` is processed. To include them, the block author must explicitly use `useBlockProps.save()` and add to their block wrapper. + +## Version 1 + +Initial version. \ No newline at end of file diff --git a/docs/designers-developers/developers/data/data-core-block-editor.md b/docs/designers-developers/developers/data/data-core-block-editor.md index b6da0e47ef9b36..ded41c1a63dfc6 100644 --- a/docs/designers-developers/developers/data/data-core-block-editor.md +++ b/docs/designers-developers/developers/data/data-core-block-editor.md @@ -344,6 +344,40 @@ _Returns_ - `?string`: Client ID of block selection start. +# **getBlockTransformItems** + +Determines the items that appear in the available block transforms list. + +Each item object contains what's necessary to display a menu item in the +transform list and handle its selection. + +The 'frecency' property is a heuristic () +that combines block usage frequenty and recency. + +Items are returned ordered descendingly by their 'frecency'. + +_Parameters_ + +- _state_ `Object`: Editor state. +- _rootClientId_ `?string`: Optional root client ID of block list. + +_Returns_ + +- `Array`: Items that appear in inserter. + +_Type Definition_ + +- _WPEditorTransformItem_ `Object` + +_Properties_ + +- _id_ `string`: Unique identifier for the item. +- _name_ `string`: The type of block to create. +- _title_ `string`: Title of the item, as it appears in the inserter. +- _icon_ `string`: Dashicon for the item, as it appears in the inserter. +- _isDisabled_ `boolean`: Whether or not the user should be prevented from inserting this item. +- _frecency_ `number`: Heuristic that combines frequency and recency. + # **getClientIdsOfDescendants** Returns an array containing the clientIds of all descendants @@ -1134,10 +1168,6 @@ _Parameters_ - _firstBlockClientId_ `string`: Client ID of the first block to merge. - _secondBlockClientId_ `string`: Client ID of the second block to merge. -_Returns_ - -- `Object`: Action object. - # **moveBlocksDown** Undocumented declaration. @@ -1179,10 +1209,6 @@ _Parameters_ - _start_ `string`: First block of the multi selection. - _end_ `string`: Last block of the multiselection. -_Returns_ - -- `Object`: Action object. - # **receiveBlocks** Returns an action object used in signalling that blocks have been received. @@ -1273,10 +1299,6 @@ _Parameters_ - _blocks_ `Array`: Array of blocks. -_Returns_ - -- `Object`: Action object. - # **resetSelection** Returns an action object used in signalling that selection state should be @@ -1540,5 +1562,16 @@ _Returns_ - `Object`: Action object +# **validateBlocksToTemplate** + +Block validity is a function of blocks state (at the point of a +reset) and the template setting. As a compromise to its placement +across distinct parts of state, it is implemented here as a side- +effect of the block reset action. + +_Parameters_ + +- _blocks_ `Array`: Array of blocks. + diff --git a/docs/designers-developers/developers/data/data-core-edit-post.md b/docs/designers-developers/developers/data/data-core-edit-post.md index 85e5d162c0bf97..ddaac2ac0ca707 100644 --- a/docs/designers-developers/developers/data/data-core-edit-post.md +++ b/docs/designers-developers/developers/data/data-core-edit-post.md @@ -110,6 +110,18 @@ _Returns_ - `boolean`: Whether there are metaboxes or not. +# **isEditingTemplate** + +Returns true if the template editing mode is enabled. + +_Parameters_ + +- _state_ `Object`: Global application state. + +_Returns_ + +- `boolean`: Whether we're editing the template. + # **isEditorPanelEnabled** Returns true if the given panel is enabled, or false otherwise. Panels are @@ -372,10 +384,6 @@ _Returns_ Returns an action object used to request meta box update. -_Returns_ - -- `Object`: Action object. - # **setAvailableMetaBoxesPerLocation** Returns an action object used in signaling @@ -385,6 +393,14 @@ _Parameters_ - _metaBoxesPerLocation_ `Object`: Meta boxes per location. +# **setIsEditingTemplate** + +Returns an action object used to switch to template editing. + +_Parameters_ + +- _value_ `boolean`: Is editing template. + _Returns_ - `Object`: Action object. diff --git a/docs/designers-developers/developers/data/data-core-editor.md b/docs/designers-developers/developers/data/data-core-editor.md index 1cf2a7cf6e297b..621461421e085c 100644 --- a/docs/designers-developers/developers/data/data-core-editor.md +++ b/docs/designers-developers/developers/data/data-core-editor.md @@ -568,18 +568,11 @@ _Related_ # **getStateBeforeOptimisticTransaction** +> **Deprecated** since Gutenberg 9.7.0. + Returns state object prior to a specified optimist transaction ID, or `null` if the transaction corresponding to the given ID cannot be found. -_Parameters_ - -- _state_ `Object`: Current global application state. -- _transactionId_ `Object`: Optimist transaction ID. - -_Returns_ - -- `Object`: Global application state prior to transaction. - # **getSuggestedPostFormat** Returns a suggested post format for the current post, inferred only if there @@ -696,18 +689,11 @@ _Related_ # **inSomeHistory** +> **Deprecated** since Gutenberg 9.7.0. + Returns true if an optimistic transaction is pending commit, for which the before state satisfies the given predicate function. -_Parameters_ - -- _state_ `Object`: Editor state. -- _predicate_ `Function`: Function given state, returning true if match. - -_Returns_ - -- `boolean`: Whether predicate matches for some history. - # **isAncestorMultiSelected** _Related_ @@ -1490,13 +1476,11 @@ Undocumented declaration. # **updatePost** +> **Deprecated** since Gutenberg 9.7.0. + Returns an action object used in signalling that a patch of updates for the latest version of the post have been received. -_Parameters_ - -- _edits_ `Object`: Updated post fields. - _Returns_ - `Object`: Action object. diff --git a/docs/designers-developers/developers/data/data-core-keyboard-shortcuts.md b/docs/designers-developers/developers/data/data-core-keyboard-shortcuts.md index 1cb83fc4c26d24..fefa8e189e2150 100644 --- a/docs/designers-developers/developers/data/data-core-keyboard-shortcuts.md +++ b/docs/designers-developers/developers/data/data-core-keyboard-shortcuts.md @@ -12,6 +12,7 @@ Returns the raw representation of all the keyboard combinations of a given short _Parameters_ +- _state_ `Object`: Global state. - _name_ `string`: Shortcut name. _Returns_ @@ -24,7 +25,8 @@ Returns the shortcut names list for a given category name. _Parameters_ -- _categoryName_ `string`: Category name. +- _state_ `Object`: Global state. +- _name_ `string`: Category name. _Returns_ @@ -36,6 +38,7 @@ Returns the aliases for a given shortcut name. _Parameters_ +- _state_ `Object`: Global state. - _name_ `string`: Shortcut name. _Returns_ @@ -48,6 +51,7 @@ Returns the shortcut description given its name. _Parameters_ +- _state_ `Object`: Global state. - _name_ `string`: Shortcut name. _Returns_ @@ -60,6 +64,7 @@ Returns the main key combination for a given shortcut name. _Parameters_ +- _state_ `Object`: Global state. - _name_ `string`: Shortcut name. _Returns_ @@ -72,6 +77,7 @@ Returns a string representing the main key combination for a given shortcut name _Parameters_ +- _state_ `Object`: Global state. - _name_ `string`: Shortcut name. - _representation_ (unknown type): Type of representation (display, raw, ariaLabel). @@ -91,19 +97,23 @@ Returns an action object used to register a new keyboard shortcut. _Parameters_ -- _get_ `Function`: Atom resover. -- _set_ `Function`: Atom updater. - _config_ `WPShortcutConfig`: Shortcut config. +_Returns_ + +- `Object`: action. + # **unregisterShortcut** Returns an action object used to unregister a keyboard shortcut. _Parameters_ -- _get_ `Function`: get atom value. -- _set_ `Function`: set atom value. - _name_ `string`: Shortcut name. +_Returns_ + +- `Object`: action. + diff --git a/docs/designers-developers/developers/data/data-core-notices.md b/docs/designers-developers/developers/data/data-core-notices.md index cce07f6e3fdc0c..0ecaa5cc3a7a19 100644 --- a/docs/designers-developers/developers/data/data-core-notices.md +++ b/docs/designers-developers/developers/data/data-core-notices.md @@ -78,6 +78,8 @@ _Parameters_ - _options.speak_ `[boolean]`: Whether the notice content should be announced to screen readers. - _options.actions_ `[Array]`: User actions to be presented with notice. - _options.icon_ `[Object]`: An icon displayed with the notice. +- _options.explicitDismiss_ `[boolean]`: Whether the notice includes an explict dismiss button and can't be dismissed by clicking the body of the notice. +- _options.onDismiss_ `[Function]`: Called when the notice is dismissed. _Returns_ diff --git a/docs/designers-developers/developers/internationalization.md b/docs/designers-developers/developers/internationalization.md index bc97e97f48348b..50bc9c12268d8a 100644 --- a/docs/designers-developers/developers/internationalization.md +++ b/docs/designers-developers/developers/internationalization.md @@ -24,10 +24,11 @@ function myguten_block_init() { wp_register_script( 'myguten-script', plugins_url( 'block.js', __FILE__ ), - array( 'wp-blocks', 'wp-element', 'wp-i18n' ) + array( 'wp-blocks', 'wp-element', 'wp-i18n', 'wp-block-editor' ) ); register_block_type( 'myguten/simple', array( + 'apiVersion' => 2, 'editor_script' => 'myguten-script', ) ); } @@ -41,22 +42,28 @@ In your code, you can include the i18n functions. The most common function is ** ```js import { __ } from '@wordpress/i18n'; import { registerBlockType } from '@wordpress/blocks'; +import { useBlockProps } from '@wordpress/block-editor'; registerBlockType( 'myguten/simple', { + apiVersion: 2, title: __( 'Simple Block', 'myguten' ), category: 'widgets', edit: () => { + const blockProps = useBlockProps( { style: { color: 'red' } } ); + return ( -

+

{ __( 'Hello World', 'myguten' ) }

); }, save: () => { + const blockProps = useBlockProps.save( { style: { color: 'red' } } ); + return ( -

+

{ __( 'Hello World', 'myguten' ) }

); @@ -68,23 +75,27 @@ registerBlockType( 'myguten/simple', { const { __ } = wp.i18n; const el = wp.element.createElement; const { registerBlockType } = wp.blocks; +const { useBlockProps } = wp.blockEditor; registerBlockType( 'myguten/simple', { title: __( 'Simple Block', 'myguten' ), category: 'widgets', edit: function() { + const blockProps = useBlockProps( { style: { color: 'red' } } ); + return el( 'p', - { style: { color: 'red' } }, + blockProps, __( 'Hello World', 'myguten' ) ); }, save: function() { + const blockProps = useBlockProps.save( { style: { color: 'red' } } ); return el( 'p', - { style: { color: 'red' } }, + blockProps, __( 'Hello World', 'myguten' ) ); }, diff --git a/docs/designers-developers/developers/richtext.md b/docs/designers-developers/developers/richtext.md index 3909306a92d6d2..e565780a48bb24 100644 --- a/docs/designers-developers/developers/richtext.md +++ b/docs/designers-developers/developers/richtext.md @@ -29,7 +29,7 @@ There are a number of core blocks using the RichText component. The JavaScript e {% ESNext %} ```js import { registerBlockType } from '@wordpress/blocks'; -import { RichText } from '@wordpress/block-editor'; +import { useBlockProps, RichText } from '@wordpress/block-editor'; registerBlockType( /* ... */, { // ... @@ -42,11 +42,13 @@ registerBlockType( /* ... */, { }, }, - edit( { className, attributes, setAttributes } ) { + edit( { attributes, setAttributes } ) { + const blockProps = useBlockProps(); + return ( setAttributes( { content } ) } // Store updated content as a block attribute @@ -56,7 +58,9 @@ registerBlockType( /* ... */, { }, save( { attributes } ) { - return ; // Saves

Content added in the editor...

to the database for frontend display + const blockProps = useBlockProps.save(); + + return ; // Saves

Content added in the editor...

to the database for frontend display } } ); ``` @@ -74,22 +78,25 @@ wp.blocks.registerBlockType( /* ... */, { }, edit: function( props ) { - return wp.element.createElement( wp.editor.RichText, { + var blockProps = wp.blockEditor.useBlockProps(); + + return wp.element.createElement( wp.blockEditor.RichText, Object.assign( blockProps, { tagName: 'h2', // The tag here is the element output and editable in the admin - className: props.className, value: props.attributes.content, // Any existing content, either from the database or an attribute default formattingControls: [ 'bold', 'italic' ], // Allow the content to be made bold or italic, but do not allow other formatting options onChange: function( content ) { props.setAttributes( { content: content } ); // Store updated content as a block attribute }, placeholder: __( 'Heading...' ), // Display this text before any content has been added by the user - } ); + } ) ); }, save: function( props ) { - return wp.element.createElement( wp.editor.RichText.Content, { + var blockProps = wp.blockEditor.useBlockProps.save(); + + return wp.element.createElement( wp.blockEditor.RichText.Content, Object.assign( blockProps, { tagName: 'h2', value: props.attributes.content // Saves

Content added in the editor...

to the database for frontend display - } ); + } ) ); } } ); ``` diff --git a/docs/designers-developers/developers/slotfills/main-dashboard-button.md b/docs/designers-developers/developers/slotfills/main-dashboard-button.md index 43b3730a35bcf4..3392eed131218f 100644 --- a/docs/designers-developers/developers/slotfills/main-dashboard-button.md +++ b/docs/designers-developers/developers/slotfills/main-dashboard-button.md @@ -1,18 +1,19 @@ # MainDashboardButton -This slot allows replacing the default main dashboard button in the post editor -that's used for closing the editor in fullscreen mode. In the site editor this slot -refers to the "back to dashboard" button in the navigation sidebar. +This slot allows replacing the default main dashboard button in the post editor and site editor. +It's used for returning back to main wp-admin dashboard when editor is in fullscreen mode. ## Examples -Basic usage: +### Post editor example + +This will override the W icon button in the header. ```js import { registerPlugin } from '@wordpress/plugins'; import { __experimentalMainDashboardButton as MainDashboardButton, -} from '@wordpress/interface'; +} from '@wordpress/edit-post'; const MainDashboardButtonTest = () => ( @@ -32,16 +33,14 @@ the post editor, that can be achieved in the following way: import { registerPlugin } from '@wordpress/plugins'; import { __experimentalFullscreenModeClose as FullscreenModeClose, -} from '@wordpress/edit-post'; -import { __experimentalMainDashboardButton as MainDashboardButton, -} from '@wordpress/interface'; +} from '@wordpress/edit-post'; import { close } from '@wordpress/icons'; const MainDashboardButtonIconTest = () => ( - + ); @@ -50,13 +49,15 @@ registerPlugin( 'main-dashboard-button-icon-test', { } ); ``` -Site editor example: +### Site editor example + +In the site editor this slot refers to the "back to dashboard" button in the navigation sidebar. ```js import { registerPlugin } from '@wordpress/plugins'; import { __experimentalMainDashboardButton as MainDashboardButton, -} from '@wordpress/interface'; +} from '@wordpress/edit-site'; import { __experimentalNavigationBackButton as NavigationBackButton, } from '@wordpress/components'; diff --git a/docs/designers-developers/developers/themes/block-based-themes.md b/docs/designers-developers/developers/themes/block-based-themes.md index 6f91f7ab7092f8..34d37f32c403d8 100644 --- a/docs/designers-developers/developers/themes/block-based-themes.md +++ b/docs/designers-developers/developers/themes/block-based-themes.md @@ -4,7 +4,7 @@ > Documentation has been shared early to surface what’s being worked on and invite feedback from those experimenting with the APIs. You can provide feedback in the weekly #core-editor chats where the latest progress of this effort will be shared and discussed, or async via Github issues. -**Note:** In order to use these features, make sure to use a block-based theme. +**Note:** To use these features, activate a theme that includes a `block-templates/index.html` file. This will signal to the block editor that the theme should use full-site editing features. ## What is a block-based theme? diff --git a/docs/designers-developers/developers/themes/theme-json.md b/docs/designers-developers/developers/themes/theme-json.md index 86fbfc115716a9..b66cd3d621439c 100644 --- a/docs/designers-developers/developers/themes/theme-json.md +++ b/docs/designers-developers/developers/themes/theme-json.md @@ -93,10 +93,13 @@ The settings section has the following structure and default values: }, "typography": { "customFontSize": true, /* false to opt-out, as in add_theme_support( 'disable-custom-font-sizes' ) */ + "customFontWeight": true, /* false to opt-out */ + "customFontStyle": true, /* false to opt-out */ "customLineHeight": false, /* true to opt-in, as in add_theme_support( 'custom-line-height' ) */ "dropCap": true, /* false to opt-out */ "fontFamilies": [ ... ], /* font family presets */ "fontSizes": [ ... ], /* font size presets, as in add_theme_support('editor-font-sizes', ... ) */ + "fontStyles": [ ... ], /* font style presets */ "fontWeights": [ ... ], /* font weight presets */ "textDecorations": [ ... ], /* text decoration presets */ "textTransforms": [ ... ] /* text transform presets */ @@ -349,6 +352,7 @@ These are the current typography properties supported by blocks: | Context | Font Family | Font Size | Font Style | Font Weight | Line Height | Text Decoration | Text Transform | | --- | --- | --- | --- | --- | --- | --- | --- | | Global | Yes | Yes | Yes | Yes | Yes | Yes | Yes | +| Code | - | Yes | - | - | - | - | - | | Heading [1] | - | Yes | - | - | Yes | - | - | | List | - | Yes | - | - | - | - | - | | Navigation | Yes | Yes | Yes | Yes | - | Yes | Yes | @@ -362,7 +366,9 @@ These are the current typography properties supported by blocks: | Post Hierarchical Terms | - | Yes | - | - | Yes | - | - | | Post Tags | - | Yes | - | - | Yes | - | - | | Post Title | Yes | Yes | - | - | Yes | - | - | +| Preformatted | - | Yes | - | - | - | - | - | | Site Tagline | Yes | Yes | - | - | Yes | - | - | | Site Title | Yes | Yes | - | - | Yes | - | - | +| Verse | Yes | Yes | - | - | - | - | - | [1] The heading block represents 6 distinct HTML elements: H1-H6. It comes with selectors to target each individual element (ex: core/heading/h1 for H1, etc). diff --git a/docs/designers-developers/developers/themes/theme-support.md b/docs/designers-developers/developers/themes/theme-support.md index a080aa297c5a02..5f022bca7094db 100644 --- a/docs/designers-developers/developers/themes/theme-support.md +++ b/docs/designers-developers/developers/themes/theme-support.md @@ -22,22 +22,22 @@ To opt-in for one of these features, call `add_theme_support` in the `functions. function mytheme_setup_theme_supported_features() { add_theme_support( 'editor-color-palette', array( array( - 'name' => __( 'strong magenta', 'themeLangDomain' ), + 'name' => esc_attr__( 'strong magenta', 'themeLangDomain' ), 'slug' => 'strong-magenta', 'color' => '#a156b4', ), array( - 'name' => __( 'light grayish magenta', 'themeLangDomain' ), + 'name' => esc_attr__( 'light grayish magenta', 'themeLangDomain' ), 'slug' => 'light-grayish-magenta', 'color' => '#d0a5db', ), array( - 'name' => __( 'very light gray', 'themeLangDomain' ), + 'name' => esc_attr__( 'very light gray', 'themeLangDomain' ), 'slug' => 'very-light-gray', 'color' => '#eee', ), array( - 'name' => __( 'very dark gray', 'themeLangDomain' ), + 'name' => esc_attr__( 'very dark gray', 'themeLangDomain' ), 'slug' => 'very-dark-gray', 'color' => '#444', ), @@ -104,22 +104,22 @@ Different blocks have the possibility of customizing colors. The block editor pr ```php add_theme_support( 'editor-color-palette', array( array( - 'name' => __( 'strong magenta', 'themeLangDomain' ), + 'name' => esc_attr__( 'strong magenta', 'themeLangDomain' ), 'slug' => 'strong-magenta', 'color' => '#a156b4', ), array( - 'name' => __( 'light grayish magenta', 'themeLangDomain' ), + 'name' => esc_attr__( 'light grayish magenta', 'themeLangDomain' ), 'slug' => 'light-grayish-magenta', 'color' => '#d0a5db', ), array( - 'name' => __( 'very light gray', 'themeLangDomain' ), + 'name' => esc_attr__( 'very light gray', 'themeLangDomain' ), 'slug' => 'very-light-gray', 'color' => '#eee', ), array( - 'name' => __( 'very dark gray', 'themeLangDomain' ), + 'name' => esc_attr__( 'very dark gray', 'themeLangDomain' ), 'slug' => 'very-dark-gray', 'color' => '#444', ), @@ -155,27 +155,27 @@ add_theme_support( 'editor-gradient-presets', array( array( - 'name' => __( 'Vivid cyan blue to vivid purple', 'themeLangDomain' ), + 'name' => esc_attr__( 'Vivid cyan blue to vivid purple', 'themeLangDomain' ), 'gradient' => 'linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)', 'slug' => 'vivid-cyan-blue-to-vivid-purple' ), array( - 'name' => __( 'Vivid green cyan to vivid cyan blue', 'themeLangDomain' ), + 'name' => esc_attr__( 'Vivid green cyan to vivid cyan blue', 'themeLangDomain' ), 'gradient' => 'linear-gradient(135deg,rgba(0,208,132,1) 0%,rgba(6,147,227,1) 100%)', 'slug' => 'vivid-green-cyan-to-vivid-cyan-blue', ), array( - 'name' => __( 'Light green cyan to vivid green cyan', 'themeLangDomain' ), + 'name' => esc_attr__( 'Light green cyan to vivid green cyan', 'themeLangDomain' ), 'gradient' => 'linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%)', 'slug' => 'light-green-cyan-to-vivid-green-cyan', ), array( - 'name' => __( 'Luminous vivid amber to luminous vivid orange', 'themeLangDomain' ), + 'name' => esc_attr__( 'Luminous vivid amber to luminous vivid orange', 'themeLangDomain' ), 'gradient' => 'linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%)', 'slug' => 'luminous-vivid-amber-to-luminous-vivid-orange', ), array( - 'name' => __( 'Luminous vivid orange to vivid red', 'themeLangDomain' ), + 'name' => esc_attr__( 'Luminous vivid orange to vivid red', 'themeLangDomain' ), 'gradient' => 'linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%)', 'slug' => 'luminous-vivid-orange-to-vivid-red', ), @@ -201,22 +201,22 @@ Blocks may allow the user to configure the font sizes they use, e.g., the paragr ```php add_theme_support( 'editor-font-sizes', array( array( - 'name' => __( 'Small', 'themeLangDomain' ), + 'name' => esc_attr__( 'Small', 'themeLangDomain' ), 'size' => 12, 'slug' => 'small' ), array( - 'name' => __( 'Regular', 'themeLangDomain' ), + 'name' => esc_attr__( 'Regular', 'themeLangDomain' ), 'size' => 16, 'slug' => 'regular' ), array( - 'name' => __( 'Large', 'themeLangDomain' ), + 'name' => esc_attr__( 'Large', 'themeLangDomain' ), 'size' => 36, 'slug' => 'large' ), array( - 'name' => __( 'Huge', 'themeLangDomain' ), + 'name' => esc_attr__( 'Huge', 'themeLangDomain' ), 'size' => 50, 'slug' => 'huge' ) diff --git a/docs/designers-developers/developers/tutorials/block-based-themes/README.md b/docs/designers-developers/developers/tutorials/block-based-themes/README.md index 1b13d6214f2331..a701cd46228814 100644 --- a/docs/designers-developers/developers/tutorials/block-based-themes/README.md +++ b/docs/designers-developers/developers/tutorials/block-based-themes/README.md @@ -15,7 +15,7 @@ This tutorial is up to date as of Gutenberg version 9.1. 1. [What is needed to create a block-based theme?](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#what-is-needed-to-create-a-block-based-theme) 2. [Creating the theme](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#creating-the-theme) 3. [Creating the templates and template parts](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#creating-the-templates-and-template-parts) - 4. [Experimental-theme.json -Global styles](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#experimental-theme-json-global-styles) + 4. [Experimental-theme.json - Global styles](/docs/designers-developers/developers/tutorials/block-based-themes/README.md#experimental-theme-json-global-styles) 5. [Adding blocks](/docs/designers-developers/developers/tutorials/block-based-themes/block-based-themes-2-adding-blocks.md) ## What is needed to create a block-based theme? @@ -34,7 +34,7 @@ A block based theme requires an `index.php` file, an index template file, a `sty The theme may optionally include an [experimental-theme.json file](/docs/designers-developers/developers/themes/theme-json.md) to manage global styles. You decide what additional templates and template parts to include in your theme. -Templates are placed inside the block-templates folder, and template parts are placed inside the block-template-parts folder: +Templates are placed inside the `block-templates` folder, and template parts are placed inside the `block-template-parts` folder: ``` theme @@ -57,7 +57,7 @@ theme ## Creating the theme Create a new folder for your theme in `/wp-content/themes/`. -Inside this folder, create the block-templates and block-template-parts folders. +Inside this folder, create the `block-templates` and `block-template-parts` folders. Create a `style.css` file. The file header in the `style.css` file has [the same items that you would use in a traditional theme](https://developer.wordpress.org/themes/basics/main-stylesheet-style-css/#explanations). @@ -166,7 +166,7 @@ theme ### Creating the templates and template parts -Create two template parts called `footer.html` and `header.html` and place them inside the block-template-parts folder. You can leave the files empty for now. +Create two template parts called `footer.html` and `header.html` and place them inside the `block-template-parts` folder. You can leave the files empty for now. Inside the block-templates folder, create an `index.html` file. @@ -318,7 +318,7 @@ Below are the presets and styles combined: ``` { "global": { - "setttings": { + "settings": { "color": { "palette": [ { diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md b/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md index b8a4a94996b014..5df98a568fbcdd 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/applying-styles-with-stylesheets.md @@ -8,8 +8,11 @@ The editor will automatically generate a class name for each block type to simpl {% ESNext %} ```jsx import { registerBlockType } from '@wordpress/blocks'; +import { useBlockProps } from '@wordpress/block-editor'; registerBlockType( 'gutenberg-examples/example-02-stylesheets', { + apiVersion: 2, + title: 'Example: Stylesheets', icon: 'universal-access-alt', @@ -18,43 +21,51 @@ registerBlockType( 'gutenberg-examples/example-02-stylesheets', { example: {}, - edit( { className } ) { - return

Hello World, step 2 (from the editor, in green).

; + edit() { + const blockProps = useBlockProps(); + + return

Hello World, step 2 (from the editor, in green).

; }, save() { - return

Hello World, step 2 (from the frontend, in red).

; + const blockProps = useBlockProps.save(); + + return

Hello World, step 2 (from the frontend, in red).

; }, } ); ``` {% ES5 %} ```js -( function( blocks, element ) { +( function( blocks, element, blockEditor ) { var el = element.createElement; blocks.registerBlockType( 'gutenberg-examples/example-02-stylesheets', { + apiVersion: 2, title: 'Example: Stylesheets', icon: 'universal-access-alt', category: 'design', example: {}, edit: function( props ) { + var blockProps = blockEditor.useBlockProps(); return el( 'p', - { className: props.className }, + blockProps, 'Hello World, step 2 (from the editor, in green).' ); }, save: function() { + var blockProps = blockEditor.useBlockProps.save(); return el( 'p', - {}, + blockProps, 'Hello World, step 2 (from the frontend, in red).' ); }, } ); }( window.wp.blocks, - window.wp.element + window.wp.element, + window.wp.blockEditor, ) ); ``` {% end %} @@ -120,6 +131,7 @@ function gutenberg_examples_02_register_block() { ); register_block_type( 'gutenberg-examples/example-02-stylesheets', array( + 'apiVersion' => 2, 'style' => 'gutenberg-examples-02', 'editor_style' => 'gutenberg-examples-02-editor', 'editor_script' => 'gutenberg-examples-02', diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md b/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md index b1798ccde55ca9..fe4dfe67d0f365 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md @@ -16,12 +16,14 @@ You can also customize the toolbar to include controls specific to your block ty import { registerBlockType } from '@wordpress/blocks'; import { + useBlockProps, RichText, AlignmentToolbar, BlockControls, } from '@wordpress/block-editor'; registerBlockType( 'gutenberg-examples/example-04-controls-esnext', { + apiVersion: 2, title: 'Example: Controls (esnext)', icon: 'universal-access-alt', category: 'design', @@ -48,9 +50,10 @@ registerBlockType( 'gutenberg-examples/example-04-controls-esnext', { content, alignment, }, - className, } = props; + const blockProps = useBlockProps(); + const onChangeContent = ( newContent ) => { props.setAttributes( { content: newContent } ); }; @@ -60,7 +63,7 @@ registerBlockType( 'gutenberg-examples/example-04-controls-esnext', { }; return ( -
+
{ { + const blockProps = useBlockProps.save(); + return ( - +
+ +
); }, } ); ``` {% ES5 %} ```js -( function( blocks, editor, element ) { +( function( blocks, blockEditor, element ) { var el = element.createElement; - var RichText = editor.RichText; - var AlignmentToolbar = editor.AlignmentToolbar; - var BlockControls = editor.BlockControls; + var RichText = blockEditor.RichText; + var AlignmentToolbar = blockEditor.AlignmentToolbar; + var BlockControls = blockEditor.BlockControls; + var useBlockProps = blockEditor.useBlockProps; blocks.registerBlockType( 'gutenberg-examples/example-04-controls', { title: 'Example: Controls', @@ -123,6 +131,7 @@ registerBlockType( 'gutenberg-examples/example-04-controls-esnext', { edit: function( props ) { var content = props.attributes.content; var alignment = props.attributes.alignment; + var blockProps = useBlockProps(); function onChangeContent( newContent ) { props.setAttributes( { content: newContent } ); @@ -132,7 +141,9 @@ registerBlockType( 'gutenberg-examples/example-04-controls-esnext', { props.setAttributes( { alignment: newAlignment === undefined ? 'none' : newAlignment } ); } - return [ + return el( + 'div', + blockProps, el( BlockControls, { key: 'controls' }, @@ -150,25 +161,30 @@ registerBlockType( 'gutenberg-examples/example-04-controls-esnext', { key: 'richtext', tagName: 'p', style: { textAlign: alignment }, - className: props.className, onChange: onChangeContent, value: content, } ), - ]; + ); }, save: function( props ) { - return el( RichText.Content, { - tagName: 'p', - className: 'gutenberg-examples-align-' + props.attributes.alignment, - value: props.attributes.content, - } ); + var blockProps = useBlockProps.save(); + + return el( + 'div', + blockProps, + el( RichText.Content, { + tagName: 'p', + className: 'gutenberg-examples-align-' + props.attributes.alignment, + value: props.attributes.content, + } ) + ); }, } ); }( window.wp.blocks, - window.wp.editor, + window.wp.blockEditor, window.wp.element ) ); ``` diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md b/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md index da9175642d5456..4f6d2c2557d5f7 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/creating-dynamic-blocks.md @@ -22,8 +22,10 @@ The following code example shows how to create a dynamic block that shows only t ```jsx import { registerBlockType } from '@wordpress/blocks'; import { withSelect } from '@wordpress/data'; +import { useBlockProps } from '@wordpress/block-editor'; registerBlockType( 'gutenberg-examples/example-dynamic', { + apiVersion: 2, title: 'Example: last post', icon: 'megaphone', category: 'widgets', @@ -32,31 +34,34 @@ registerBlockType( 'gutenberg-examples/example-dynamic', { return { posts: select( 'core' ).getEntityRecords( 'postType', 'post' ), }; - } )( ( { posts, className } ) => { - if ( ! posts ) { - return 'Loading...'; - } + } )( ( { posts } ) => { + const blockProps = useBlockProps(); - if ( posts && posts.length === 0 ) { - return 'No posts'; - } - - const post = posts[ 0 ]; - - return - { post.title.rendered } - ; + return ( +
+ { ! posts && 'Loading' } + { posts && posts.length === 0 && 'No Posts' } + { posts && posts.length > 0 && ( + + { posts[ 0 ].title.rendered } + + ) } +
+ ) + } ), } ); ``` {% ES5 %} ```js -( function( blocks, element, data ) { +( function( blocks, element, data, blockEditor ) { var el = element.createElement, registerBlockType = blocks.registerBlockType, - withSelect = data.withSelect; + withSelect = data.withSelect, + useBlockProps = blockEditor.useBlockProps; registerBlockType( 'gutenberg-examples/example-dynamic', { + apiVersion: 2, title: 'Example: last post', icon: 'megaphone', category: 'widgets', @@ -65,20 +70,25 @@ registerBlockType( 'gutenberg-examples/example-dynamic', { posts: select( 'core' ).getEntityRecords( 'postType', 'post' ), }; } )( function( props ) { + var blockProps = useBlockProps(); + var content; if ( ! props.posts ) { - return 'Loading...'; - } - - if ( props.posts.length === 0 ) { - return 'No posts'; + content = 'Loading...'; + } else if ( props.posts.length === 0 ) { + content = 'No posts'; + } else { + var post = props.posts[ 0 ]; + content = el( + 'a', + { href: post.link }, + post.title.rendered + ); } - var className = props.className; - var post = props.posts[ 0 ]; - return el( - 'a', - { className: className, href: post.link }, - post.title.rendered + return el( + 'div', + blockProps, + content ); } ), } ); @@ -86,6 +96,7 @@ registerBlockType( 'gutenberg-examples/example-dynamic', { window.wp.blocks, window.wp.element, window.wp.data, + window.wp.blockEditor, ) ); ``` {% end %} @@ -128,6 +139,7 @@ function gutenberg_examples_dynamic() { ); register_block_type( 'gutenberg-examples/example-dynamic', array( + 'apiVersion' => 2, 'editor_script' => 'gutenberg-examples-dynamic', 'render_callback' => 'gutenberg_examples_dynamic_render_callback' ) ); @@ -154,40 +166,52 @@ Gutenberg 2.8 added the [``](/packages/server-side-render/READ ```jsx import { registerBlockType } from '@wordpress/blocks'; import ServerSideRender from '@wordpress/server-side-render'; +import { useBlockProps } from '@wordpress/block-editor'; registerBlockType( 'gutenberg-examples/example-dynamic', { + apiVersion: 2, title: 'Example: last post', icon: 'megaphone', category: 'widgets', edit: function( props ) { + const blockProps = useBlockProps(); return ( - +
+ +
); }, } ); ``` {% ES5 %} ```js -( function( blocks, element, serverSideRender ) { +( function( blocks, element, serverSideRender, blockEditor ) { var el = element.createElement, registerBlockType = blocks.registerBlockType, - ServerSideRender = serverSideRender; + ServerSideRender = serverSideRender, + useBlockProps = blockEditor.useBlockProps; registerBlockType( 'gutenberg-examples/example-dynamic', { + apiVersion: 2, title: 'Example: last post', icon: 'megaphone', category: 'widgets', edit: function( props ) { + var blockProps = useBlockProps(); return ( - el( ServerSideRender, { - block: 'gutenberg-examples/example-dynamic', - attributes: props.attributes, - } ) + el( + 'div', + blockProps, + el( ServerSideRender, { + block: 'gutenberg-examples/example-dynamic', + attributes: props.attributes, + } ) + ) ); }, } ); @@ -195,6 +219,7 @@ registerBlockType( 'gutenberg-examples/example-dynamic', { window.wp.blocks, window.wp.element, window.wp.serverSideRender, + window.wp.blockEditor, ) ); ``` {% end %} diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md b/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md index 0956c6bdd9fa2e..e3696634992eb4 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/introducing-attributes-and-editable-fields.md @@ -56,9 +56,10 @@ Here is the complete block definition for Example 03. {% ESNext %} ```jsx import { registerBlockType } from '@wordpress/blocks'; -import { RichText } from '@wordpress/block-editor'; +import { useBlockProps, RichText } from '@wordpress/block-editor'; registerBlockType( 'gutenberg-examples/example-03-editable-esnext', { + apiVersion: 2, title: 'Example: Editable (esnext)', icon: 'universal-access-alt', category: 'design', @@ -76,30 +77,34 @@ registerBlockType( 'gutenberg-examples/example-03-editable-esnext', { }, edit: ( props ) => { const { attributes: { content }, setAttributes, className } = props; + const blockProps = useBlockProps(); const onChangeContent = ( newContent ) => { setAttributes( { content: newContent } ); }; return ( ); }, save: ( props ) => { - return ; + const blockProps = useBlockProps.save(); + return ; }, } ); ``` {% ES5 %} ```js -( function( blocks, editor, element ) { +( function( blocks, blockEditor, element ) { var el = element.createElement; - var RichText = editor.RichText; + var RichText = blockEditor.RichText; + var useBlockProps = blockEditor.useBlockProps; blocks.registerBlockType( 'gutenberg-examples/example-03-editable', { + apiVersion: 2, title: 'Example: Editable', icon: 'universal-access-alt', category: 'design', @@ -117,6 +122,7 @@ registerBlockType( 'gutenberg-examples/example-03-editable-esnext', { }, }, edit: function( props ) { + var blockProps = useBlockProps(); var content = props.attributes.content; function onChangeContent( newContent ) { props.setAttributes( { content: newContent } ); @@ -124,24 +130,25 @@ registerBlockType( 'gutenberg-examples/example-03-editable-esnext', { return el( RichText, - { + Object.assign( blockProps, { tagName: 'p', - className: props.className, onChange: onChangeContent, value: content, - } + } ) ); }, save: function( props ) { - return el( RichText.Content, { - tagName: 'p', value: props.attributes.content, - } ); + var blockProps = useBlockProps.save(); + return el( RichText.Content, Object.assign( blockProps, { + tagName: 'p', + value: props.attributes.content, + } ) ); }, } ); }( window.wp.blocks, - window.wp.editor, + window.wp.blockEditor, window.wp.element ) ); ``` diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/nested-blocks-inner-blocks.md b/docs/designers-developers/developers/tutorials/block-tutorial/nested-blocks-inner-blocks.md index ed156a0eb72777..f7f46d6c71bec8 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/nested-blocks-inner-blocks.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/nested-blocks-inner-blocks.md @@ -10,22 +10,26 @@ Here is the basic InnerBlocks usage. {% ESNext %} ```js import { registerBlockType } from '@wordpress/blocks'; -import { InnerBlocks } from '@wordpress/block-editor'; +import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; registerBlockType( 'gutenberg-examples/example-06', { // ... - edit: ( { className } ) => { + edit: () => { + const blockProps = useBlockProps(); + return ( -
+
); }, - save: ( { className } ) => { + save: () => { + const blockProps = useBlockProps.save(); + return ( -
+
); @@ -37,23 +41,28 @@ registerBlockType( 'gutenberg-examples/example-06', { ( function( blocks, element, blockEditor ) { var el = element.createElement; var InnerBlocks = blockEditor.InnerBlocks; + var useBlockProps = blockEditor.useBlockProps; blocks.registerBlockType( 'gutenberg-examples/example-06', { title: 'Example: Inner Blocks', category: 'design', - edit: function( props ) { + edit: function() { + var blockProps = useBlockProps(); + return el( 'div', - { className: props.className }, + blockProps, el( InnerBlocks ) ); }, - save: function( props ) { + save: function() { + var blockProps = useBlockProps.save(); + return el( 'div', - { className: props.className }, + blockProps, el( InnerBlocks.Content ) ); }, diff --git a/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md b/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md index 6ba06f0981009a..53a73d7c5d7e80 100644 --- a/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md +++ b/docs/designers-developers/developers/tutorials/block-tutorial/writing-your-first-block-type.md @@ -28,6 +28,7 @@ function gutenberg_examples_01_register_block() { ); register_block_type( 'gutenberg-examples/example-01-basic-esnext', array( + 'apiVersion' => 2, 'editor_script' => 'gutenberg-examples-01-esnext', ) ); @@ -51,6 +52,7 @@ With the script enqueued, let's look at the implementation of the block itself: {% ESNext %} ```jsx import { registerBlockType } from '@wordpress/blocks'; +import { useBlockProps } from '@wordpress/block-editor'; const blockStyle = { backgroundColor: '#900', @@ -59,22 +61,28 @@ const blockStyle = { }; registerBlockType( 'gutenberg-examples/example-01-basic-esnext', { + apiVersion: 2, title: 'Example: Basic (esnext)', icon: 'universal-access-alt', category: 'design', example: {}, edit() { - return
Hello World, step 1 (from the editor).
; + const blockProps = useBlockProps( { style: blockStyle } ); + + return
Hello World, step 1 (from the editor).
; }, save() { - return
Hello World, step 1 (from the frontend).
; + const blockProps = useBlockProps.save( { style: blockStyle } ); + + return
Hello World, step 1 (from the frontend).
; }, } ); ``` {% ES5 %} ```js -( function( blocks, element ) { +( function( blocks, element, blockEditor ) { var el = element.createElement; + var useBlockProps = blockEditor.useBlockProps; var blockStyle = { backgroundColor: '#900', @@ -83,28 +91,32 @@ registerBlockType( 'gutenberg-examples/example-01-basic-esnext', { }; blocks.registerBlockType( 'gutenberg-examples/example-01-basic', { + apiVersion: 2, title: 'Example: Basic', icon: 'universal-access-alt', category: 'design', example: {}, edit: function() { + var blockProps = useBlockProps( { style: blockStyle } ); return el( 'p', - { style: blockStyle }, + blockProps, 'Hello World, step 1 (from the editor).' ); }, save: function() { + var blockProps = useBlockProps.save( { style: blockStyle } ); return el( 'p', - { style: blockStyle }, + blockProps, 'Hello World, step 1 (from the frontend).' ); }, } ); }( window.wp.blocks, - window.wp.element + window.wp.element, + window.wp.blockEditor ) ); ``` {% end %} diff --git a/docs/designers-developers/developers/tutorials/create-block/attributes.md b/docs/designers-developers/developers/tutorials/create-block/attributes.md index 50ab92359eaa18..5614987e33fa03 100644 --- a/docs/designers-developers/developers/tutorials/create-block/attributes.md +++ b/docs/designers-developers/developers/tutorials/create-block/attributes.md @@ -16,7 +16,7 @@ attributes: { Add this to the `index.js` file within the `registerBlockType` function. The `attributes` are at the same level as the title and description fields. -When the block loads it will: look at the saved content for the block, look for the div tag, take the text portion, and store the content in an `attributes.message` variable. +When the block loads it will look at the saved content for the block, look for the div tag, take the text portion, and store the content in an `attributes.message` variable. Note: The text portion is equivalent to `innerText` attribute of a DOM element. For more details and other examples see the [Block Attributes documentation](/docs/designers-developers/developers/block-api/block-attributes.md). diff --git a/docs/designers-developers/developers/tutorials/create-block/block-anatomy.md b/docs/designers-developers/developers/tutorials/create-block/block-anatomy.md index e078c5e7a2807a..2eea119b98ecd2 100644 --- a/docs/designers-developers/developers/tutorials/create-block/block-anatomy.md +++ b/docs/designers-developers/developers/tutorials/create-block/block-anatomy.md @@ -8,8 +8,10 @@ Here is the complete code for registering a block: ```js import { registerBlockType } from '@wordpress/blocks'; +import { useBlockProps } from '@wordpress/block-editor'; registerBlockType( 'create-block/gutenpride', { + apiVersion: 2, title: 'Gutenpride', description: 'Example block.', category: 'widgets', @@ -20,11 +22,13 @@ registerBlockType( 'create-block/gutenpride', { }, edit: () => { - return
Hello in Editor.
; + const blockProps = useBlockProps(); + return
Hello in Editor.
; }, save: () => { - return
Hello in Save.
; + const blockProps = useBlockProps.save(); + return
Hello in Save.
; }, } ); ``` diff --git a/docs/designers-developers/developers/tutorials/create-block/block-code.md b/docs/designers-developers/developers/tutorials/create-block/block-code.md index d97ccb0863d77b..61a4b920aab95f 100644 --- a/docs/designers-developers/developers/tutorials/create-block/block-code.md +++ b/docs/designers-developers/developers/tutorials/create-block/block-code.md @@ -12,6 +12,7 @@ In the `gutenpride.php` file, the enqueue process is already setup from the gene ```php register_block_type( 'create-block/gutenpride', array( + 'apiVersion' => 2, 'editor_script' => 'create-block-gutenpride-block-editor', 'editor_style' => 'create-block-gutenpride-block-editor', 'style' => 'create-block-gutenpride-block', diff --git a/docs/designers-developers/developers/tutorials/create-block/wp-plugin.md b/docs/designers-developers/developers/tutorials/create-block/wp-plugin.md index c8a0626ca0af0c..abfe91bc62ea2e 100644 --- a/docs/designers-developers/developers/tutorials/create-block/wp-plugin.md +++ b/docs/designers-developers/developers/tutorials/create-block/wp-plugin.md @@ -127,6 +127,7 @@ function create_block_gutenpride_block_init() { ); register_block_type( 'create-block/gutenpride', array( + 'apiVersion' => 2, 'editor_script' => 'create-block-gutenpride-block-editor', 'editor_style' => 'create-block-gutenpride-block-editor', 'style' => 'create-block-gutenpride-block', diff --git a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md index 887f6ffa193b5a..bca448658d2c41 100644 --- a/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md +++ b/docs/designers-developers/developers/tutorials/metabox/meta-block-3-add.md @@ -15,13 +15,15 @@ import { registerBlockType } from '@wordpress/blocks'; import { TextControl } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { useEntityProp } from '@wordpress/core-data'; +import { useBlockProps } from '@wordpress/block-editor'; registerBlockType( 'myguten/meta-block', { title: 'Meta Block', icon: 'smiley', category: 'text', - edit( { className, setAttributes, attributes } ) { + edit( { setAttributes, attributes } ) { + const blockProps = useBlockProps(); const postType = useSelect( ( select ) => select( 'core/editor' ).getCurrentPostType(), [] @@ -37,7 +39,7 @@ registerBlockType( 'myguten/meta-block', { } return ( -
+
2, 'render_callback' => 'myguten_render_paragraph', ) ); ``` diff --git a/docs/manifest.json b/docs/manifest.json index 61e72bc0a20393..8c0881895f1428 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -155,6 +155,12 @@ "markdown_source": "../docs/designers-developers/developers/block-api/block-annotations.md", "parent": "block-api" }, + { + "title": "Block API Versions", + "slug": "versions", + "markdown_source": "../docs/designers-developers/developers/block-api/versions.md", + "parent": "block-api" + }, { "title": "Filter Reference", "slug": "filters", @@ -191,6 +197,12 @@ "markdown_source": "../docs/designers-developers/developers/slotfills/README.md", "parent": "developers" }, + { + "title": "MainDashboardButton", + "slug": "main-dashboard-button", + "markdown_source": "../docs/designers-developers/developers/slotfills/main-dashboard-button.md", + "parent": "slotfills" + }, { "title": "PluginBlockSettingsMenuItem", "slug": "plugin-block-settings-menu-item", @@ -1794,9 +1806,9 @@ "parent": "packages" }, { - "title": "@wordpress/stan", - "slug": "packages-stan", - "markdown_source": "../packages/stan/README.md", + "title": "@wordpress/stylelint-config", + "slug": "packages-stylelint-config", + "markdown_source": "../packages/stylelint-config/README.md", "parent": "packages" }, { diff --git a/docs/toc.json b/docs/toc.json index ff54916e323e32..3d685561927b4e 100644 --- a/docs/toc.json +++ b/docs/toc.json @@ -26,7 +26,8 @@ { "docs/designers-developers/developers/block-api/block-transforms.md": [] }, { "docs/designers-developers/developers/block-api/block-templates.md": [] }, { "docs/designers-developers/developers/block-api/block-patterns.md": [] }, - { "docs/designers-developers/developers/block-api/block-annotations.md": [] } + { "docs/designers-developers/developers/block-api/block-annotations.md": [] }, + { "docs/designers-developers/developers/block-api/versions.md": [] } ] }, { "docs/designers-developers/developers/filters/README.md": [ { "docs/designers-developers/developers/filters/block-filters.md": [] }, @@ -35,6 +36,7 @@ { "docs/designers-developers/developers/filters/autocomplete-filters.md": [] } ] }, {"docs/designers-developers/developers/slotfills/README.md": [ + { "docs/designers-developers/developers/slotfills/main-dashboard-button.md": [] }, { "docs/designers-developers/developers/slotfills/plugin-block-settings-menu-item.md": [] }, { "docs/designers-developers/developers/slotfills/plugin-document-setting-panel.md": [] }, { "docs/designers-developers/developers/slotfills/plugin-more-menu-item.md": [] }, diff --git a/gutenberg.php b/gutenberg.php index 328e88a96644b1..1b920ae4e71f0f 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -5,7 +5,7 @@ * Description: Printing since 1440. This is the development plugin for the new block editor in core. * Requires at least: 5.3 * Requires PHP: 5.6 - * Version: 9.4.1 + * Version: 9.6.2 * Author: Gutenberg Team * Text Domain: gutenberg * diff --git a/lib/block-directory.php b/lib/block-directory.php deleted file mode 100644 index cb1ec4bd9d1a2c..00000000000000 --- a/lib/block-directory.php +++ /dev/null @@ -1,54 +0,0 @@ -` tag for the enqueued script. - * @param string $handle The script's registered handle. - * @param string $esc_src The script's pre-escaped registered src. - * - * @return string Filtered script tag. - */ - function gutenberg_change_script_tag( $tag, $handle, $esc_src ) { - if ( ! is_admin() ) { - return $tag; - } - - $tag = str_replace( - sprintf( "", $esc_src ), - sprintf( "", esc_attr( $handle ), $esc_src ), - $tag - ); - - return $tag; - } - add_filter( 'script_loader_tag', 'gutenberg_change_script_tag', 1, 3 ); -} diff --git a/lib/block-patterns.php b/lib/block-patterns.php deleted file mode 100644 index e8c0d0811dae91..00000000000000 --- a/lib/block-patterns.php +++ /dev/null @@ -1,66 +0,0 @@ -get_all_registered(); - $settings['__experimentalBlockPatternCategories'] = WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(); - - return $settings; -} -add_filter( 'block_editor_settings', 'gutenberg_extend_settings_block_patterns', 0 ); - - -/** - * Load a block pattern by name. - * - * @param string $name Block Pattern File name. - * - * @return array Block Pattern Array. - */ -function gutenberg_load_block_pattern( $name ) { - return require( __DIR__ . '/patterns/' . $name . '.php' ); -} - -/** - * Register default patterns and categories, potentially overriding ones that were already registered in Core. - * - * This can be removed when plugin support requires WordPress 5.5.0+, and patterns have been synced back to Core. - * - * @see https://core.trac.wordpress.org/ticket/50550 - */ -function gutenberg_register_block_patterns() { - $should_register_core_patterns = get_theme_support( 'core-block-patterns' ); - - if ( $should_register_core_patterns ) { - register_block_pattern( 'core/text-two-columns', gutenberg_load_block_pattern( 'text-two-columns' ) ); - register_block_pattern( 'core/two-buttons', gutenberg_load_block_pattern( 'two-buttons' ) ); - register_block_pattern( 'core/two-images', gutenberg_load_block_pattern( 'two-images' ) ); - register_block_pattern( 'core/text-two-columns-with-images', gutenberg_load_block_pattern( 'text-two-columns-with-images' ) ); - register_block_pattern( 'core/text-three-columns-buttons', gutenberg_load_block_pattern( 'text-three-columns-buttons' ) ); - register_block_pattern( 'core/large-header', gutenberg_load_block_pattern( 'large-header' ) ); - register_block_pattern( 'core/large-header-button', gutenberg_load_block_pattern( 'large-header-button' ) ); - register_block_pattern( 'core/three-buttons', gutenberg_load_block_pattern( 'three-buttons' ) ); - register_block_pattern( 'core/heading-paragraph', gutenberg_load_block_pattern( 'heading-paragraph' ) ); - register_block_pattern( 'core/quote', gutenberg_load_block_pattern( 'quote' ) ); - } - - register_block_pattern_category( 'buttons', array( 'label' => _x( 'Buttons', 'Block pattern category', 'gutenberg' ) ) ); - register_block_pattern_category( 'columns', array( 'label' => _x( 'Columns', 'Block pattern category', 'gutenberg' ) ) ); - register_block_pattern_category( 'gallery', array( 'label' => _x( 'Gallery', 'Block pattern category', 'gutenberg' ) ) ); - register_block_pattern_category( 'header', array( 'label' => _x( 'Headers', 'Block pattern category', 'gutenberg' ) ) ); - register_block_pattern_category( 'text', array( 'label' => _x( 'Text', 'Block pattern category', 'gutenberg' ) ) ); -} -add_action( 'init', 'gutenberg_register_block_patterns' ); diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php new file mode 100644 index 00000000000000..085cdda187da0c --- /dev/null +++ b/lib/block-supports/border.php @@ -0,0 +1,89 @@ +attributes ) { + $block_type->attributes = array(); + } + + if ( $has_border_radius_support && ! array_key_exists( 'style', $block_type->attributes ) ) { + $block_type->attributes['style'] = array( + 'type' => 'object', + ); + } +} + +/** + * Adds CSS classes and inline styles for border styles to the incoming + * attributes array. This will be applied to the block markup in the front-end. + * + * @param WP_Block_type $block_type Block type. + * @param array $block_attributes Block attributes. + * + * @return array Border CSS classes and inline styles. + */ +function gutenberg_apply_border_support( $block_type, $block_attributes ) { + // Arrays used to ease addition of further border related features in future. + $styles = array(); + + // Border Radius. + if ( gutenberg_has_border_support( $block_type, 'radius' ) ) { + if ( isset( $block_attributes['style']['border']['radius'] ) ) { + $border_radius = intval( $block_attributes['style']['border']['radius'] ); + $styles[] = sprintf( 'border-radius: %dpx;', $border_radius ); + } + } + + // Border width, style etc can be added here. + + // Collect classes and styles. + $attributes = array(); + + if ( ! empty( $styles ) ) { + $attributes['style'] = implode( ' ', $styles ); + } + + return $attributes; +} + +/** + * Checks whether the current block type supports the feature requested. + * + * @param WP_Block_Type $block_type Block type to check for support. + * @param string $feature Name of the feature to check support for. + * @param mixed $default Fallback value for feature support, defaults to false. + * + * @return boolean Whether or not the feature is supported. + */ +function gutenberg_has_border_support( $block_type, $feature, $default = false ) { + $block_support = false; + if ( property_exists( $block_type, 'supports' ) ) { + $block_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalBorder' ), $default ); + } + + return true === $block_support || ( is_array( $block_support ) && gutenberg_experimental_get( $block_support, array( $feature ), false ) ); +} + +// Register the block support. +WP_Block_Supports::get_instance()->register( + 'border', + array( + 'register_attribute' => 'gutenberg_register_border_support', + 'apply' => 'gutenberg_apply_border_support', + ) +); diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index d797bdc21bac41..29454c43296fd1 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -15,12 +15,19 @@ function gutenberg_register_typography_support( $block_type ) { return; } - $has_font_appearance_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontAppearance' ), false ); $has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false ); + $has_font_style_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontStyle' ), false ); + $has_font_weight_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontWeight' ), false ); $has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false ); $has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false ); $has_text_transform_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextTransform' ), false ); - $has_typography_support = $has_font_appearance_support || $has_font_size_support || $has_line_height_support || $has_text_transform_support || $has_text_decoration_support; + + $has_typography_support = $has_font_size_support + || $has_font_weight_support + || $has_font_style_support + || $has_line_height_support + || $has_text_transform_support + || $has_text_decoration_support; if ( ! $block_type->attributes ) { $block_type->attributes = array(); @@ -57,8 +64,9 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $classes = array(); $styles = array(); - $has_font_appearance_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontAppearance' ), false ); $has_font_family_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontFamily' ), false ); + $has_font_style_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontStyle' ), false ); + $has_font_weight_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontWeight' ), false ); $has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false ); $has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false ); $has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false ); @@ -94,14 +102,17 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { } } - // Font appearance - style and weight. - if ( $has_font_appearance_support ) { + // Font style. + if ( $has_font_style_support ) { // Apply font style. $font_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontStyle', 'font-style' ); if ( $font_style ) { $styles[] = $font_style; } + } + // Font weight. + if ( $has_font_weight_support ) { // Apply font weight. $font_weight = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontWeight', 'font-weight' ); if ( $font_weight ) { diff --git a/lib/blocks.php b/lib/blocks.php index 751b178fc8a277..f4f5a6536cee2a 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -17,7 +17,7 @@ function gutenberg_reregister_core_block_types() { 'audio', 'button', 'buttons', - 'classic', + 'freeform', 'code', 'column', 'columns', @@ -120,6 +120,7 @@ function gutenberg_reregister_core_block_types() { $registry->unregister( $metadata['name'] ); } + gutenberg_register_core_block_styles( $folder_name ); register_block_type_from_metadata( $block_json_file ); } @@ -132,11 +133,13 @@ function gutenberg_reregister_core_block_types() { if ( $registry->is_registered( $block_names ) ) { $registry->unregister( $block_names ); } + gutenberg_register_core_block_styles( $block_names ); } elseif ( is_array( $block_names ) ) { foreach ( $block_names as $block_name ) { if ( $registry->is_registered( $block_name ) ) { $registry->unregister( $block_name ); } + gutenberg_register_core_block_styles( $block_name ); } } @@ -147,6 +150,46 @@ function gutenberg_reregister_core_block_types() { add_action( 'init', 'gutenberg_reregister_core_block_types' ); +/** + * Registers block styles for a core block. + * + * @param string $block_name The block-name. + * + * @return void + */ +function gutenberg_register_core_block_styles( $block_name ) { + if ( ! gutenberg_should_load_separate_block_styles() ) { + return; + } + + $block_name = str_replace( 'core/', '', $block_name ); + + $style_path = is_rtl() + ? "build/block-library/blocks/$block_name/style-rtl.css" + : "build/block-library/blocks/$block_name/style.css"; + $editor_style_path = is_rtl() + ? "build/block-library/blocks/$block_name/style-editor-rtl.css" + : "build/block-library/blocks/$block_name/style-editor.css"; + + if ( file_exists( gutenberg_dir_path() . $style_path ) ) { + wp_register_style( + 'wp-block-' . $block_name, + gutenberg_url( $style_path ), + array(), + filemtime( gutenberg_dir_path() . $style_path ) + ); + } + + if ( file_exists( gutenberg_dir_path() . $editor_style_path ) ) { + wp_register_style( + 'wp-block-' . $block_name . '-editor', + gutenberg_url( $editor_style_path ), + array(), + filemtime( gutenberg_dir_path() . $editor_style_path ) + ); + } +} + /** * Complements the implementation of block type `core/social-icon`, whether it * be provided by core or the plugin, with derived block types for each diff --git a/lib/class-wp-block-list.php b/lib/class-wp-block-list.php deleted file mode 100644 index 3bf1f6252088b8..00000000000000 --- a/lib/class-wp-block-list.php +++ /dev/null @@ -1,194 +0,0 @@ -blocks = $blocks; - $this->available_context = $available_context; - $this->registry = $registry; - } - - /* - * ArrayAccess interface methods. - */ - - /** - * Returns true if a block exists by the specified block index, or false - * otherwise. - * - * @link https://www.php.net/manual/en/arrayaccess.offsetexists.php - * - * @param string $index Index of block to check. - * - * @return bool Whether block exists. - */ - public function offsetExists( $index ) { - return isset( $this->blocks[ $index ] ); - } - - /** - * Returns the value by the specified block index. - * - * @link https://www.php.net/manual/en/arrayaccess.offsetget.php - * - * @param string $index Index of block value to retrieve. - * - * @return mixed|null Block value if exists, or null. - */ - public function offsetGet( $index ) { - $block = $this->blocks[ $index ]; - - if ( isset( $block ) && is_array( $block ) ) { - $block = new WP_Block( $block, $this->available_context, $this->registry ); - $this->blocks[ $index ] = $block; - } - - return $block; - } - - /** - * Assign a block value by the specified block index. - * - * @link https://www.php.net/manual/en/arrayaccess.offsetset.php - * - * @param string $index Index of block value to set. - * @param mixed $value Block value. - */ - public function offsetSet( $index, $value ) { - if ( is_null( $index ) ) { - $this->blocks[] = $value; - } else { - $this->blocks[ $index ] = $value; - } - } - - /** - * Unset a block. - * - * @link https://www.php.net/manual/en/arrayaccess.offsetunset.php - * - * @param string $index Index of block value to unset. - */ - public function offsetUnset( $index ) { - unset( $this->blocks[ $index ] ); - } - - /* - * Iterator interface methods. - */ - - /** - * Rewinds back to the first element of the Iterator. - * - * @link https://www.php.net/manual/en/iterator.rewind.php - */ - public function rewind() { - reset( $this->blocks ); - } - - /** - * Returns the current element of the block list. - * - * @link https://www.php.net/manual/en/iterator.current.php - * - * @return mixed Current element. - */ - public function current() { - return $this->offsetGet( $this->key() ); - } - - /** - * Returns the key of the current element of the block list. - * - * @link https://www.php.net/manual/en/iterator.key.php - * - * @return mixed Key of the current element. - */ - public function key() { - return key( $this->blocks ); - } - - /** - * Moves the current position of the block list to the next element. - * - * @link https://www.php.net/manual/en/iterator.next.php - */ - public function next() { - next( $this->blocks ); - } - - /** - * Checks if current position is valid. - * - * @link https://www.php.net/manual/en/iterator.valid.php - */ - public function valid() { - return null !== key( $this->blocks ); - } - - /* - * Countable interface methods. - */ - - /** - * Returns the count of blocks in the list. - * - * @link https://www.php.net/manual/en/countable.count.php - * - * @return int Block count. - */ - public function count() { - return count( $this->blocks ); - } - -} diff --git a/lib/class-wp-block-pattern-categories-registry.php b/lib/class-wp-block-pattern-categories-registry.php deleted file mode 100644 index 2404f6a88dd92b..00000000000000 --- a/lib/class-wp-block-pattern-categories-registry.php +++ /dev/null @@ -1,144 +0,0 @@ -registered_categories[ $category_name ] = array_merge( - array( 'name' => $category_name ), - $category_properties - ); - - return true; - } - - /** - * Unregisters a pattern category. - * - * @param string $category_name Pattern name including namespace. - * @return boolean True if the pattern was unregistered with success and false otherwise. - */ - public function unregister( $category_name ) { - if ( ! $this->is_registered( $category_name ) ) { - /* translators: 1: Block pattern name. */ - $message = sprintf( __( 'Block pattern category "%1$s" not found.', 'gutenberg' ), $category_name ); - _doing_it_wrong( __METHOD__, $message, '8.1.0' ); - return false; - } - - unset( $this->registered_categories[ $category_name ] ); - - return true; - } - - /** - * Retrieves an array containing the properties of a registered pattern category. - * - * @param string $category_name Pattern category name. - * @return array Registered pattern properties. - */ - public function get_registered( $category_name ) { - if ( ! $this->is_registered( $category_name ) ) { - return null; - } - - return $this->registered_categories[ $category_name ]; - } - - /** - * Retrieves all registered pattern categories. - * - * @return array Array of arrays containing the registered pattern categories properties. - */ - public function get_all_registered() { - return array_values( $this->registered_categories ); - } - - /** - * Checks if a pattern category is registered. - * - * @param string $category_name Pattern category name. - * @return bool True if the pattern category is registered, false otherwise. - */ - public function is_registered( $category_name ) { - return isset( $this->registered_categories[ $category_name ] ); - } - - /** - * Utility method to retrieve the main instance of the class. - * - * The instance will be created if it does not exist yet. - * - * @since 5.3.0 - * - * @return WP_Block_Pattern_Categories_Registry The main instance. - */ - public static function get_instance() { - if ( null === self::$instance ) { - self::$instance = new self(); - } - - return self::$instance; - } -} - -/** - * Registers a new pattern category. - * - * @param string $category_name Pattern category name. - * @param array $category_properties Array containing the properties of the category. - * - * @return boolean True if the pattern category was registered with success and false otherwise. - */ -function register_block_pattern_category( $category_name, $category_properties ) { - return WP_Block_Pattern_Categories_Registry::get_instance()->register( $category_name, $category_properties ); -} - -/** - * Unregisters a pattern category. - * - * @param string $category_name Pattern category name including namespace. - * - * @return boolean True if the pattern category was unregistered with success and false otherwise. - */ -function unregister_block_pattern_category( $category_name ) { - return WP_Block_Pattern_Categories_Registry::get_instance()->unregister( $category_name ); -} diff --git a/lib/class-wp-block-patterns-registry.php b/lib/class-wp-block-patterns-registry.php deleted file mode 100644 index 56a36bce386f67..00000000000000 --- a/lib/class-wp-block-patterns-registry.php +++ /dev/null @@ -1,157 +0,0 @@ -registered_patterns[ $pattern_name ] = array_merge( - $pattern_properties, - array( 'name' => $pattern_name ) - ); - - return true; - } - - /** - * Unregisters a pattern. - * - * @param string $pattern_name Pattern name including namespace. - * @return boolean True if the pattern was unregistered with success and false otherwise. - */ - public function unregister( $pattern_name ) { - if ( ! $this->is_registered( $pattern_name ) ) { - /* translators: 1: Pattern name. */ - $message = sprintf( __( 'Block pattern "%1$s" not found.', 'gutenberg' ), $pattern_name ); - _doing_it_wrong( __METHOD__, $message, '7.8.0' ); - return false; - } - - unset( $this->registered_patterns[ $pattern_name ] ); - - return true; - } - - /** - * Retrieves an array containing the properties of a registered pattern. - * - * @param string $pattern_name Pattern name including namespace. - * @return array Registered pattern properties. - */ - public function get_registered( $pattern_name ) { - if ( ! $this->is_registered( $pattern_name ) ) { - return null; - } - - return $this->registered_patterns[ $pattern_name ]; - } - - /** - * Retrieves all registered patterns. - * - * @return array Array of arrays containing the registered patterns properties, - * and per style. - */ - public function get_all_registered() { - return array_values( $this->registered_patterns ); - } - - /** - * Checks if a pattern is registered. - * - * @param string $pattern_name Pattern name including namespace. - * @return bool True if the pattern is registered, false otherwise. - */ - public function is_registered( $pattern_name ) { - return isset( $this->registered_patterns[ $pattern_name ] ); - } - - /** - * Utility method to retrieve the main instance of the class. - * - * The instance will be created if it does not exist yet. - * - * @since 5.3.0 - * - * @return WP_Block_Patterns_Registry The main instance. - */ - public static function get_instance() { - if ( null === self::$instance ) { - self::$instance = new self(); - } - - return self::$instance; - } -} - -/** - * Registers a new pattern. - * - * @param string $pattern_name Pattern name including namespace. - * @param array $pattern_properties Array containing the properties of the pattern. - * - * @return boolean True if the pattern was registered with success and false otherwise. - */ -function register_block_pattern( $pattern_name, $pattern_properties ) { - return WP_Block_Patterns_Registry::get_instance()->register( $pattern_name, $pattern_properties ); -} - -/** - * Unregisters a pattern. - * - * @param string $pattern_name Pattern name including namespace. - * - * @return boolean True if the pattern was unregistered with success and false otherwise. - */ -function unregister_block_pattern( $pattern_name ) { - return WP_Block_Patterns_Registry::get_instance()->unregister( $pattern_name ); -} diff --git a/lib/class-wp-block.php b/lib/class-wp-block.php deleted file mode 100644 index cd029050745660..00000000000000 --- a/lib/class-wp-block.php +++ /dev/null @@ -1,243 +0,0 @@ - testing..." -> "Just testing..." - * - * @var string - */ - public $inner_html = ''; - - /** - * List of string fragments and null markers where inner blocks were found - * - * @example array( - * 'inner_html' => 'BeforeInnerAfter', - * 'inner_blocks' => array( block, block ), - * 'inner_content' => array( 'Before', null, 'Inner', null, 'After' ), - * ) - * - * @var array - */ - public $inner_content = array(); - - /** - * Constructor. - * - * Populates object properties from the provided block instance argument. - * - * The given array of context values will not necessarily be available on - * the instance itself, but is treated as the full set of values provided by - * the block's ancestry. This is assigned to the private `available_context` - * property. Only values which are configured to consumed by the block via - * its registered type will be assigned to the block's `context` property. - * - * @param array $block Array of parsed block properties. - * @param array $available_context Optional array of ancestry context values. - * @param WP_Block_Type_Registry $registry Optional block type registry. - */ - public function __construct( $block, $available_context = array(), $registry = null ) { - $this->parsed_block = $block; - $this->name = $block['blockName']; - - if ( is_null( $registry ) ) { - $registry = WP_Block_Type_Registry::get_instance(); - } - - $this->block_type = $registry->get_registered( $this->name ); - - if ( ! empty( $this->block_type->context ) ) { - $message = sprintf( - /* translators: 1: Block name. */ - __( 'The "context" parameter provided in block type "%s" is deprecated. Please use "uses_context" instead.', 'gutenberg' ), - $this->name - ); - _doing_it_wrong( __CLASS__, $message, '8.6.0' ); - $this->block_type->uses_context = $this->block_type->context; - } - if ( ! empty( $this->block_type->providesContext ) ) { - $message = sprintf( - /* translators: 1: Block name. */ - __( 'The "providesContext" parameter provided in block type "%s" is deprecated. Please use "provides_context".', 'gutenberg' ), - $this->name - ); - _doing_it_wrong( __CLASS__, $message, '8.6.0' ); - $this->block_type->provides_context = $this->block_type->providesContext; - } - - $this->available_context = $available_context; - - if ( ! empty( $this->block_type->uses_context ) ) { - foreach ( $this->block_type->uses_context as $context_name ) { - if ( array_key_exists( $context_name, $this->available_context ) ) { - $this->context[ $context_name ] = $this->available_context[ $context_name ]; - } - } - } - - if ( ! empty( $block['innerBlocks'] ) ) { - $child_context = $this->available_context; - - if ( ! empty( $this->block_type->provides_context ) ) { - foreach ( $this->block_type->provides_context as $context_name => $attribute_name ) { - if ( array_key_exists( $attribute_name, $this->attributes ) ) { - $child_context[ $context_name ] = $this->attributes[ $attribute_name ]; - } - } - } - - $this->inner_blocks = new WP_Block_List( $block['innerBlocks'], $child_context, $registry ); - } - - if ( ! empty( $block['innerHTML'] ) ) { - $this->inner_html = $block['innerHTML']; - } - - if ( ! empty( $block['innerContent'] ) ) { - $this->inner_content = $block['innerContent']; - } - } - - /** - * Returns a value from an inaccessible property. - * - * This is used to lazily initialize the `attributes` property of a block, - * such that it is only prepared with default attributes at the time that - * the property is accessed. For all other inaccessible properties, a `null` - * value is returned. - * - * @param string $name Property name. - * - * @return array|null Prepared attributes, or null. - */ - public function __get( $name ) { - if ( 'attributes' === $name ) { - $this->attributes = isset( $this->parsed_block['attrs'] ) ? - $this->parsed_block['attrs'] : - array(); - - if ( ! is_null( $this->block_type ) ) { - $this->attributes = $this->block_type->prepare_attributes_for_render( $this->attributes ); - } - - return $this->attributes; - } - - return null; - } - - /** - * Generates the render output for the block. - * - * @param array $options { - * Optional options object. - * - * @type bool $dynamic Defaults to 'true'. Optionally set to false to avoid using the block's render_callback. - * } - * - * @return string Rendered block output. - */ - public function render( $options = array() ) { - global $post; - $options = array_replace( - array( - 'dynamic' => true, - ), - $options - ); - - $is_dynamic = $options['dynamic'] && $this->name && null !== $this->block_type && $this->block_type->is_dynamic(); - $block_content = ''; - - if ( ! $options['dynamic'] || empty( $this->block_type->skip_inner_blocks ) ) { - $index = 0; - foreach ( $this->inner_content as $chunk ) { - $block_content .= is_string( $chunk ) ? - $chunk : - $this->inner_blocks[ $index++ ]->render(); - } - } - - if ( $is_dynamic ) { - $global_post = $post; - $block_content = (string) call_user_func( $this->block_type->render_callback, $this->attributes, $block_content, $this ); - $post = $global_post; - } - - if ( ! empty( $this->block_type->script ) ) { - wp_enqueue_script( $this->block_type->script ); - } - - if ( ! empty( $this->block_type->style ) ) { - wp_enqueue_style( $this->block_type->style ); - } - - /** This filter is documented in src/wp-includes/blocks.php */ - return apply_filters( 'render_block', $block_content, $this->parsed_block ); - } - -} diff --git a/lib/class-wp-rest-block-directory-controller.php b/lib/class-wp-rest-block-directory-controller.php deleted file mode 100644 index df1f2e408506e0..00000000000000 --- a/lib/class-wp-rest-block-directory-controller.php +++ /dev/null @@ -1,343 +0,0 @@ -namespace = 'wp/v2'; - $this->rest_base = 'block-directory'; - } - - /** - * Registers the necessary REST API routes. - */ - public function register_routes() { - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/search', - array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Checks whether a given request has permission to install and activate plugins. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * - * @return WP_Error|bool True if the request has permission, WP_Error object otherwise. - */ - public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - if ( ! current_user_can( 'install_plugins' ) || ! current_user_can( 'activate_plugins' ) ) { - return new WP_Error( - 'rest_block_directory_cannot_view', - __( 'Sorry, you are not allowed to browse the block directory.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - return true; - } - - /** - * Search and retrieve blocks metadata - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * - * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. - */ - public function get_items( $request ) { - require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - - $response = plugins_api( - 'query_plugins', - array( - 'block' => $request['term'], - 'per_page' => $request['per_page'], - 'page' => $request['page'], - ) - ); - - if ( is_wp_error( $response ) ) { - $response->add_data( array( 'status' => 500 ) ); - - return $response; - } - - $result = array(); - - foreach ( $response->plugins as $plugin ) { - $data = $this->prepare_item_for_response( $plugin, $request ); - $result[] = $this->prepare_response_for_collection( $data ); - } - - return rest_ensure_response( $result ); - } - - /** - * Parse block metadata for a block, and prepare it for an API repsonse. - * - * @since 5.5.0 - * - * @param array $plugin The plugin metadata. - * @param WP_REST_Request $request Request object. - * - * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. - */ - public function prepare_item_for_response( $plugin, $request ) { - // There might be multiple blocks in a plugin. Only the first block is mapped. - $block_data = reset( $plugin['blocks'] ); - - // A data array containing the properties we'll return. - $block = array( - 'name' => $block_data['name'], - 'title' => ( $block_data['title'] ? $block_data['title'] : $plugin['name'] ), - 'description' => wp_trim_words( $plugin['description'], 30, '...' ), - 'id' => $plugin['slug'], - 'rating' => $plugin['rating'] / 20, - 'rating_count' => intval( $plugin['num_ratings'] ), - 'active_installs' => intval( $plugin['active_installs'] ), - 'author_block_rating' => $plugin['author_block_rating'] / 20, - 'author_block_count' => intval( $plugin['author_block_count'] ), - 'author' => wp_strip_all_tags( $plugin['author'] ), - 'icon' => ( isset( $plugin['icons']['1x'] ) ? $plugin['icons']['1x'] : 'block-default' ), - 'assets' => array(), - 'last_updated' => $plugin['last_updated'], - 'humanized_updated' => sprintf( - /* translators: %s: Human-readable time difference. */ - __( '%s ago', 'gutenberg' ), - human_time_diff( strtotime( $plugin['last_updated'] ) ) - ), - ); - - foreach ( $plugin['block_assets'] as $asset ) { - // Allow for fully qualified URLs in future. - if ( 'https' === wp_parse_url( $asset, PHP_URL_SCHEME ) && ! empty( wp_parse_url( $asset, PHP_URL_HOST ) ) ) { - $block['assets'][] = esc_url_raw( - $asset, - array( 'https' ) - ); - } else { - $block['assets'][] = esc_url_raw( - add_query_arg( 'v', strtotime( $block['last_updated'] ), 'https://ps.w.org/' . $plugin['slug'] . $asset ), - array( 'https' ) - ); - } - } - - $this->add_additional_fields_to_object( $block, $request ); - - $response = new WP_REST_Response( $block ); - $response->add_links( $this->prepare_links( $plugin ) ); - - return $response; - } - - /** - * Generates a list of links to include in the response for the plugin. - * - * @since 5.5.0 - * - * @param array $plugin The plugin data from WordPress.org. - * - * @return array - */ - protected function prepare_links( $plugin ) { - $links = array( - 'https://api.w.org/install-plugin' => array( - 'href' => add_query_arg( 'slug', urlencode( $plugin['slug'] ), rest_url( 'wp/v2/plugins' ) ), - ), - ); - - $plugin_file = $this->find_plugin_for_slug( $plugin['slug'] ); - - if ( $plugin_file ) { - $links['https://api.w.org/plugin'] = array( - 'href' => rest_url( 'wp/v2/plugins/' . substr( $plugin_file, 0, - 4 ) ), - 'embeddable' => true, - ); - } - - return $links; - } - - /** - * Finds an installed plugin for the given slug. - * - * @since 5.5.0 - * - * @param string $slug The WordPress.org directory slug for a plugin. - * - * @return string The plugin file found matching it. - */ - protected function find_plugin_for_slug( $slug ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - - $plugin_files = get_plugins( '/' . $slug ); - - if ( ! $plugin_files ) { - return ''; - } - - $plugin_files = array_keys( $plugin_files ); - - return $slug . '/' . reset( $plugin_files ); - } - - /** - * Retrieves the theme's schema, conforming to JSON Schema. - * - * @since 5.5.0 - * - * @return array Item schema data. - */ - public function get_item_schema() { - if ( $this->schema ) { - return $this->add_additional_fields_schema( $this->schema ); - } - - $this->schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'block-directory-item', - 'type' => 'object', - 'properties' => array( - 'name' => array( - 'description' => __( 'The block name, in namespace/block-name format.', 'gutenberg' ), - 'type' => 'string', - 'context' => array( 'view' ), - ), - 'title' => array( - 'description' => __( 'The block title, in human readable format.', 'gutenberg' ), - 'type' => 'string', - 'context' => array( 'view' ), - ), - 'description' => array( - 'description' => __( 'A short description of the block, in human readable format.', 'gutenberg' ), - 'type' => 'string', - 'context' => array( 'view' ), - ), - 'id' => array( - 'description' => __( 'The block slug.', 'gutenberg' ), - 'type' => 'string', - 'context' => array( 'view' ), - ), - 'rating' => array( - 'description' => __( 'The star rating of the block.', 'gutenberg' ), - 'type' => 'integer', - 'context' => array( 'view' ), - ), - 'rating_count' => array( - 'description' => __( 'The number of ratings.', 'gutenberg' ), - 'type' => 'integer', - 'context' => array( 'view' ), - ), - 'active_installs' => array( - 'description' => __( 'The number sites that have activated this block.', 'gutenberg' ), - 'type' => 'string', - 'context' => array( 'view' ), - ), - 'author_block_rating' => array( - 'description' => __( 'The average rating of blocks published by the same author.', 'gutenberg' ), - 'type' => 'integer', - 'context' => array( 'view' ), - ), - 'author_block_count' => array( - 'description' => __( 'The number of blocks published by the same author.', 'gutenberg' ), - 'type' => 'integer', - 'context' => array( 'view' ), - ), - 'author' => array( - 'description' => __( 'The WordPress.org username of the block author.', 'gutenberg' ), - 'type' => 'string', - 'context' => array( 'view' ), - ), - 'icon' => array( - 'description' => __( 'The block icon.', 'gutenberg' ), - 'type' => 'string', - 'format' => 'uri', - 'context' => array( 'view' ), - ), - 'humanized_updated' => array( - 'description' => __( 'The date when the block was last updated, in fuzzy human readable format.', 'gutenberg' ), - 'type' => 'string', - 'context' => array( 'view' ), - ), - 'assets' => array( - 'description' => __( 'An object representing the block CSS and JavaScript assets.', 'gutenberg' ), - 'type' => 'array', - 'context' => array( 'view' ), - 'readonly' => true, - 'items' => array( - 'type' => 'string', - 'format' => 'uri', - ), - - ), - - ), - ); - - return $this->add_additional_fields_schema( $this->schema ); - } - - /** - * Retrieves the search params for the blocks collection. - * - * @since 5.5.0 - * - * @return array Collection parameters. - */ - public function get_collection_params() { - $query_params = parent::get_collection_params(); - - $query_params['context']['default'] = 'view'; - - $query_params['term'] = array( - 'description' => __( 'Limit result set to blocks matching the search term.', 'gutenberg' ), - 'type' => 'string', - 'required' => true, - 'minLength' => 1, - ); - - unset( $query_params['search'] ); - - /** - * Filter collection parameters for the block directory controller. - * - * @since 5.5.0 - * - * @param array $query_params JSON Schema-formatted collection parameters. - */ - return apply_filters( 'rest_block_directory_collection_params', $query_params ); - } -} diff --git a/lib/class-wp-rest-block-types-controller.php b/lib/class-wp-rest-block-types-controller.php deleted file mode 100644 index 9d7d819cf70b0a..00000000000000 --- a/lib/class-wp-rest-block-types-controller.php +++ /dev/null @@ -1,528 +0,0 @@ -namespace = '__experimental'; - $this->rest_base = 'block-types'; - $this->block_registry = WP_Block_Type_Registry::get_instance(); - $this->style_registry = WP_Block_Styles_Registry::get_instance(); - } - - /** - * Registers the routes for the objects of the controller. - * - * @see register_rest_route() - */ - public function register_routes() { - - register_rest_route( - $this->namespace, - '/' . $this->rest_base, - array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[a-zA-Z0-9_-]+)', - array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[a-zA-Z0-9_-]+)/(?P[a-zA-Z0-9_-]+)', - array( - 'args' => array( - 'name' => array( - 'description' => __( 'Block name.', 'gutenberg' ), - 'type' => 'string', - ), - 'namespace' => array( - 'description' => __( 'Block namespace.', 'gutenberg' ), - 'type' => 'string', - ), - ), - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), - 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Checks whether a given request has permission to read post block types. - * - * @param WP_REST_Request $request Full details about the request. - * - * @return WP_Error|bool True if the request has read access, WP_Error object otherwise. - */ - public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - return $this->check_read_permission(); - } - - /** - * Retrieves all post block types, depending on user context. - * - * @param WP_REST_Request $request Full details about the request. - * - * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. - */ - public function get_items( $request ) { - $data = array(); - $block_types = $this->block_registry->get_all_registered(); - - // Retrieve the list of registered collection query parameters. - $registered = $this->get_collection_params(); - $namespace = ''; - if ( isset( $registered['namespace'] ) && ! empty( $request['namespace'] ) ) { - $namespace = $request['namespace']; - } - - foreach ( $block_types as $obj ) { - if ( $namespace ) { - $pieces = explode( '/', $obj->name ); - $block_namespace = $pieces[0]; - if ( $namespace !== $block_namespace ) { - continue; - } - } - $block_type = $this->prepare_item_for_response( $obj, $request ); - $data[] = $this->prepare_response_for_collection( $block_type ); - } - - return rest_ensure_response( $data ); - } - - /** - * Checks if a given request has access to read a block type. - * - * @param WP_REST_Request $request Full details about the request. - * - * @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise. - */ - public function get_item_permissions_check( $request ) { - $check = $this->check_read_permission(); - if ( is_wp_error( $check ) ) { - return $check; - } - $block_name = sprintf( '%s/%s', $request['namespace'], $request['name'] ); - $block_type = $this->get_block( $block_name ); - if ( is_wp_error( $block_type ) ) { - return $block_type; - } - - return true; - } - - /** - * Checks whether a given block type should be visible. - * - * @return WP_Error|bool True if the block type is visible, otherwise false. - */ - protected function check_read_permission() { - if ( current_user_can( 'edit_posts' ) ) { - return true; - } - foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) { - if ( current_user_can( $post_type->cap->edit_posts ) ) { - return true; - } - } - - return new WP_Error( 'rest_block_type_cannot_view', __( 'Sorry, you are not allowed to manage block types.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) ); - } - - /** - * Get the block, if the name is valid. - * - * @param string $name Block name. - * @return WP_Block_Type|WP_Error Block type object if name is valid, WP_Error otherwise. - */ - protected function get_block( $name ) { - $block_type = $this->block_registry->get_registered( $name ); - if ( empty( $block_type ) ) { - return new WP_Error( 'rest_block_type_invalid', __( 'Invalid block type.', 'gutenberg' ), array( 'status' => 404 ) ); - } - - return $block_type; - } - - /** - * Retrieves a specific block type. - * - * @param WP_REST_Request $request Full details about the request. - * - * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. - */ - public function get_item( $request ) { - $block_name = sprintf( '%s/%s', $request['namespace'], $request['name'] ); - $block_type = $this->get_block( $block_name ); - if ( is_wp_error( $block_type ) ) { - return $block_type; - } - $data = $this->prepare_item_for_response( $block_type, $request ); - - return rest_ensure_response( $data ); - } - - /** - * Prepares a block type object for serialization. - * - * @param WP_Block_Type $block_type block type data. - * @param WP_REST_Request $request Full details about the request. - * - * @return WP_REST_Response block type data. - */ - public function prepare_item_for_response( $block_type, $request ) { - - $fields = $this->get_fields_for_response( $request ); - $data = array(); - - if ( rest_is_field_included( 'attributes', $fields ) ) { - $data['attributes'] = $block_type->get_attributes(); - } - - if ( rest_is_field_included( 'is_dynamic', $fields ) ) { - $data['is_dynamic'] = $block_type->is_dynamic(); - } - - $schema = $this->get_item_schema(); - $extra_fields = array( - 'name' => 'name', - 'title' => 'title', - 'description' => 'description', - 'icon' => 'icon', - 'category' => 'category', - 'keywords' => 'keywords', - 'parent' => 'parent', - 'provides_context' => 'provides_context', - 'uses_context' => 'uses_context', - 'supports' => 'supports', - 'styles' => 'styles', - 'textdomain' => 'textdomain', - 'example' => 'example', - 'editor_script' => 'editor_script', - 'script' => 'script', - 'editor_style' => 'editor_style', - 'style' => 'style', - ); - foreach ( $extra_fields as $key => $extra_field ) { - if ( rest_is_field_included( $key, $fields ) ) { - if ( isset( $block_type->$extra_field ) ) { - $field = $block_type->$extra_field; - } elseif ( array_key_exists( 'default', $schema['properties'][ $key ] ) ) { - $field = $schema['properties'][ $key ]['default']; - } else { - $field = ''; - } - $data[ $key ] = rest_sanitize_value_from_schema( $field, $schema['properties'][ $key ] ); - } - } - - if ( rest_is_field_included( 'styles', $fields ) ) { - $styles = $this->style_registry->get_registered_styles_for_block( $block_type->name ); - $styles = array_values( $styles ); - $data['styles'] = wp_parse_args( $styles, $data['styles'] ); - } - - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; - $data = $this->add_additional_fields_to_object( $data, $request ); - $data = $this->filter_response_by_context( $data, $context ); - - $response = rest_ensure_response( $data ); - - $response->add_links( $this->prepare_links( $block_type ) ); - - /** - * Filters a block type returned from the REST API. - * - * Allows modification of the block type data right before it is returned. - * - * @param WP_REST_Response $response The response object. - * @param object $block_type The original block type object. - * @param WP_REST_Request $request Request used to generate the response. - */ - return apply_filters( 'rest_prepare_block_type', $response, $block_type, $request ); - } - - /** - * Prepares links for the request. - * - * @param WP_Block_Type $block_type block type data. - * @return array Links for the given block type. - */ - protected function prepare_links( $block_type ) { - $pieces = explode( '/', $block_type->name ); - $namespace = $pieces[0]; - $links = array( - 'collection' => array( - 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), - ), - 'self' => array( - 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $block_type->name ) ), - ), - 'up' => array( - 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $namespace ) ), - ), - ); - - if ( $block_type->is_dynamic() ) { - $links['https://api.w.org/render-block']['href'] = add_query_arg( 'context', 'edit', rest_url( sprintf( '%s/%s/%s', 'wp/v2', 'block-renderer', $block_type->name ) ) ); - } - - return $links; - } - - /** - * Retrieves the block type' schema, conforming to JSON Schema. - * - * @return array Item schema data. - */ - public function get_item_schema() { - if ( $this->schema ) { - return $this->add_additional_fields_schema( $this->schema ); - } - - $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'block-type', - 'type' => 'object', - 'properties' => array( - 'title' => array( - 'description' => __( 'Title of block type.', 'gutenberg' ), - 'type' => 'string', - 'default' => '', - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'name' => array( - 'description' => __( 'Unique name identifying the block type.', 'gutenberg' ), - 'type' => 'string', - 'default' => '', - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'description' => array( - 'description' => __( 'Description of block type.', 'gutenberg' ), - 'type' => 'string', - 'default' => '', - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'icon' => array( - 'description' => __( 'Icon of block type.', 'gutenberg' ), - 'type' => array( 'string', 'null' ), - 'default' => null, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'attributes' => array( - 'description' => __( 'Block attributes.', 'gutenberg' ), - 'type' => array( 'object', 'null' ), - 'properties' => array(), - 'default' => null, - 'additionalProperties' => array( - 'type' => 'object', - ), - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'provides_context' => array( - 'description' => __( 'Context provided by blocks of this type.', 'gutenberg' ), - 'type' => 'object', - 'properties' => array(), - 'additionalProperties' => array( - 'type' => 'string', - ), - 'default' => array(), - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'uses_context' => array( - 'description' => __( 'Context values inherited by blocks of this type.', 'gutenberg' ), - 'type' => 'array', - 'default' => array(), - 'items' => array( - 'type' => 'string', - ), - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'supports' => array( - 'description' => __( 'Block supports.', 'gutenberg' ), - 'type' => 'object', - 'default' => array(), - 'properties' => array(), - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'category' => array( - 'description' => __( 'Block category.', 'gutenberg' ), - 'type' => array( 'string', null ), - 'default' => null, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'is_dynamic' => array( - 'description' => __( 'Is the block dynamically rendered.', 'gutenberg' ), - 'type' => 'boolean', - 'default' => false, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'editor_script' => array( - 'description' => __( 'Editor script handle.', 'gutenberg' ), - 'type' => array( 'string', null ), - 'default' => null, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'script' => array( - 'description' => __( 'Public facing script handle.', 'gutenberg' ), - 'type' => array( 'string', null ), - 'default' => null, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'editor_style' => array( - 'description' => __( 'Editor style handle.', 'gutenberg' ), - 'type' => array( 'string', null ), - 'default' => null, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'style' => array( - 'description' => __( 'Public facing style handle.', 'gutenberg' ), - 'type' => array( 'string', null ), - 'default' => null, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'styles' => array( - 'description' => __( 'Block style variations.', 'gutenberg' ), - 'type' => 'array', - 'properties' => array(), - 'additionalProperties' => array( - 'type' => 'object', - ), - 'default' => array(), - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'textdomain' => array( - 'description' => __( 'Public text domain.', 'gutenberg' ), - 'type' => array( 'string', 'null' ), - 'default' => null, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'parent' => array( - 'description' => __( 'Parent blocks.', 'gutenberg' ), - 'type' => array( 'array', 'null' ), - 'items' => array( - 'type' => 'string', - ), - 'default' => null, - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'keywords' => array( - 'description' => __( 'Block keywords.', 'gutenberg' ), - 'type' => 'array', - 'items' => array( - 'type' => 'string', - ), - 'default' => array(), - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - 'example' => array( - 'description' => __( 'Block example.', 'gutenberg' ), - 'type' => array( 'object', 'null' ), - 'default' => null, - 'properties' => array(), - 'additionalProperties' => array( - 'type' => 'object', - ), - 'context' => array( 'embed', 'view', 'edit' ), - 'readonly' => true, - ), - ), - ); - - $this->schema = $schema; - - return $this->add_additional_fields_schema( $this->schema ); - } - - /** - * Retrieves the query params for collections. - * - * @return array Collection parameters. - */ - public function get_collection_params() { - $new_params = array(); - $new_params['context'] = $this->get_context_param( array( 'default' => 'view' ) ); - $new_params['namespace'] = array( - 'description' => __( 'Block namespace.', 'gutenberg' ), - 'type' => 'string', - ); - return $new_params; - } - -} diff --git a/lib/class-wp-rest-image-editor-controller.php b/lib/class-wp-rest-image-editor-controller.php deleted file mode 100644 index 66c8531e02d7ed..00000000000000 --- a/lib/class-wp-rest-image-editor-controller.php +++ /dev/null @@ -1,362 +0,0 @@ -namespace = 'wp/v2'; - $this->rest_base = 'media'; - } - - /** - * Registers the necessary REST API routes. - * - * @since 7.x ? - * @access public - */ - public function register_routes() { - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[\d]+)/edit', - array( - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'apply_edits' ), - 'permission_callback' => array( $this, 'permission_callback' ), - 'args' => array( - 'rotation' => array( - 'type' => 'integer', - ), - - // Src is required to check for correct $image_meta. - 'src' => array( - 'type' => 'string', - 'required' => true, - ), - - // Crop values are in percents. - 'x' => array( - 'type' => 'number', - 'minimum' => 0, - 'maximum' => 100, - ), - 'y' => array( - 'type' => 'number', - 'minimum' => 0, - 'maximum' => 100, - ), - 'width' => array( - 'type' => 'number', - 'minimum' => 0, - 'maximum' => 100, - ), - 'height' => array( - 'type' => 'number', - 'minimum' => 0, - 'maximum' => 100, - ), - ), - ), - ) - ); - } - - /** - * Checks if the user has permissions to make the request. - * - * @since 7.x ? - * @access public - * - * @param WP_REST_Request $request Full details about the request. - * @return true|WP_Error True if the request has read access, WP_Error object otherwise. - */ - public function permission_callback( $request ) { - if ( ! current_user_can( 'edit_post', $request['id'] ) ) { - $error = __( 'Sorry, you are not allowed to edit images.', 'gutenberg' ); - return new WP_Error( 'rest_cannot_edit_image', $error, array( 'status' => rest_authorization_required_code() ) ); - } - - if ( ! current_user_can( 'upload_files' ) ) { - return new WP_Error( 'rest_cannot_edit_image', __( 'Sorry, you are not allowed to upload media on this site.', 'gutenberg' ), array( 'status' => rest_authorization_required_code() ) ); - } - - return true; - } - - /** - * Applies all edits in one go. - * - * @since 7.x ? - * @access public - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error If successful image JSON for the modified image, otherwise a WP_Error. - */ - public function apply_edits( $request ) { - require_once ABSPATH . 'wp-admin/includes/image.php'; - - $attachment_id = $request['id']; - - // This also confirms the attachment is an image. - $image_file = wp_get_original_image_path( $attachment_id ); - $image_meta = wp_get_attachment_metadata( $attachment_id ); - - if ( function_exists( 'wp_image_file_matches_image_meta' ) ) { - if ( - ! $image_meta || - ! $image_file || - ! wp_image_file_matches_image_meta( $request['src'], $image_meta ) - ) { - return new WP_Error( - 'rest_unknown_attachment', - __( 'Unable to get meta information for file.', 'gutenberg' ), - array( 'status' => 404 ) - ); - } - } else { - // Back-compat for WP versions < 5.5. - if ( ! $image_meta || ! $image_file ) { - return new WP_Error( - 'rest_unknown_attachment', - __( 'Unable to get meta information for file.', 'gutenberg' ), - array( 'status' => 404 ) - ); - } else { - $match = false; - $image_src = $request['src']; - - if ( isset( $image_meta['file'] ) && strlen( $image_meta['file'] ) > 4 ) { - // Remove quiery args. - list( $image_src ) = explode( '?', $image_src ); - - // Check if the relative image path from the image meta is at the end of $image_src. - if ( strrpos( $image_src, $image_meta['file'] ) === strlen( $image_src ) - strlen( $image_meta['file'] ) ) { - $match = true; - } - - if ( ! empty( $image_meta['sizes'] ) ) { - // Retrieve the uploads sub-directory from the full size image. - $dirname = _wp_get_attachment_relative_path( $image_meta['file'] ); - - if ( $dirname ) { - $dirname = trailingslashit( $dirname ); - } - - foreach ( $image_meta['sizes'] as $image_size_data ) { - $relative_path = $dirname . $image_size_data['file']; - - if ( strrpos( $image_src, $relative_path ) === strlen( $image_src ) - strlen( $relative_path ) ) { - $match = true; - break; - } - } - } - } - - if ( ! $match ) { - return new WP_Error( - 'rest_unknown_attachment', - __( 'Unable to get meta information for file.', 'gutenberg' ), - array( 'status' => 404 ) - ); - } - } - } - - $supported_types = array( 'image/jpeg', 'image/png', 'image/gif' ); - $mime_type = get_post_mime_type( $attachment_id ); - if ( ! in_array( $mime_type, $supported_types, true ) ) { - return new WP_Error( - 'rest_cannot_edit_file_type', - __( 'This type of file cannot be edited.', 'gutenberg' ), - array( 'status' => 400 ) - ); - } - - // Check if we need to do anything. - $rotate = 0; - $crop = false; - - if ( ! empty( $request['rotation'] ) ) { - // Rotation direction: clockwise vs. counter clockwise. - $rotate = 0 - (int) $request['rotation']; - } - - if ( isset( $request['x'], $request['y'], $request['width'], $request['height'] ) ) { - $crop = true; - } - - if ( ! $rotate && ! $crop ) { - $error = __( 'The image was not edited. Edit the image before applying the changes.', 'gutenberg' ); - return new WP_Error( 'rest_image_not_edited', $error, array( 'status' => 400 ) ); - } - - // If the file doesn't exist, attempt a URL fopen on the src link. - // This can occur with certain file replication plugins. - // Keep the original file path to get a modified name later. - $image_file_to_edit = $image_file; - if ( ! file_exists( $image_file_to_edit ) ) { - $image_file_to_edit = _load_image_to_edit_path( $attachment_id ); - } - - $image_editor = wp_get_image_editor( $image_file_to_edit ); - - if ( is_wp_error( $image_editor ) ) { - // This image cannot be edited. - $error = __( 'Unable to edit this image.', 'gutenberg' ); - return new WP_Error( 'rest_unknown_image_file_type', $error, array( 'status' => 500 ) ); - } - - if ( 0 !== $rotate ) { - $result = $image_editor->rotate( $rotate ); - - if ( is_wp_error( $result ) ) { - $error = __( 'Unable to rotate this image.', 'gutenberg' ); - return new WP_Error( 'rest_image_rotation_failed', $error, array( 'status' => 500 ) ); - } - } - - if ( $crop ) { - $size = $image_editor->get_size(); - - $crop_x = round( ( $size['width'] * floatval( $request['x'] ) ) / 100.0 ); - $crop_y = round( ( $size['height'] * floatval( $request['y'] ) ) / 100.0 ); - $width = round( ( $size['width'] * floatval( $request['width'] ) ) / 100.0 ); - $height = round( ( $size['height'] * floatval( $request['height'] ) ) / 100.0 ); - - $result = $image_editor->crop( $crop_x, $crop_y, $width, $height ); - - if ( is_wp_error( $result ) ) { - $error = __( 'Unable to crop this image.', 'gutenberg' ); - return new WP_Error( 'rest_image_crop_failed', $error, array( 'status' => 500 ) ); - } - } - - // Calculate the file name. - $image_ext = pathinfo( $image_file, PATHINFO_EXTENSION ); - $image_name = wp_basename( $image_file, ".{$image_ext}" ); - - // Do not append multiple `-edited` to the file name. - // The user may be editing a previously edited image. - if ( preg_match( '/-edited(-\d+)?$/', $image_name ) ) { - // Remove any `-1`, `-2`, etc. `wp_unique_filename()` will add the proper number. - $image_name = preg_replace( '/-edited(-\d+)?$/', '-edited', $image_name ); - } else { - // Append `-edited` before the extension. - $image_name .= '-edited'; - } - - $filename = "{$image_name}.{$image_ext}"; - - // Create the uploads sub-directory if needed. - $uploads = wp_upload_dir(); - - // Make the file name unique in the (new) upload directory. - $filename = wp_unique_filename( $uploads['path'], $filename ); - - // Save to disk. - $saved = $image_editor->save( $uploads['path'] . "/$filename" ); - - if ( is_wp_error( $saved ) ) { - return $saved; - } - - // Create new attachment post. - $attachment_post = array( - 'post_mime_type' => $saved['mime-type'], - 'guid' => $uploads['url'] . "/$filename", - 'post_title' => $filename, - 'post_content' => '', - ); - - $new_attachment_id = wp_insert_attachment( wp_slash( $attachment_post ), $saved['path'], 0, true ); - - if ( is_wp_error( $new_attachment_id ) ) { - if ( 'db_update_error' === $new_attachment_id->get_error_code() ) { - $new_attachment_id->add_data( array( 'status' => 500 ) ); - } else { - $new_attachment_id->add_data( array( 'status' => 400 ) ); - } - - return $new_attachment_id; - } - - if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { - // Set a custom header with the attachment_id. - // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. - header( 'X-WP-Upload-Attachment-ID: ' . $new_attachment_id ); - } - - // Generate image sub-sizes and meta. - $new_image_meta = wp_generate_attachment_metadata( $new_attachment_id, $saved['path'] ); - - // Copy the EXIF metadata from the original attachment if not generated for the edited image. - if ( ! empty( $image_meta['image_meta'] ) ) { - $empty_image_meta = true; - - if ( isset( $new_image_meta['image_meta'] ) && is_array( $new_image_meta['image_meta'] ) ) { - $empty_image_meta = empty( array_filter( array_values( $new_image_meta['image_meta'] ) ) ); - } - - if ( $empty_image_meta ) { - $new_image_meta['image_meta'] = $image_meta['image_meta']; - } - } - - // Reset orientation. At this point the image is edited and orientation is correct. - if ( ! empty( $new_image_meta['image_meta']['orientation'] ) ) { - $new_image_meta['image_meta']['orientation'] = 1; - } - - // The attachment_id may change if the site is exported and imported. - $new_image_meta['parent_image'] = array( - 'attachment_id' => $attachment_id, - // Path to the originally uploaded image file relative to the uploads directory. - 'file' => _wp_relative_upload_path( $image_file ), - ); - - /** - * Filters the updated attachment meta data. - * - * @since 5.5.0 - * - * @param array $data Array of updated attachment meta data. - * @param int $new_attachment_id Attachment post ID. - * @param int $attachment_id Original Attachment post ID. - */ - $new_image_meta = apply_filters( 'wp_edited_attachment_metadata', $new_image_meta, $new_attachment_id, $attachment_id ); - - wp_update_attachment_metadata( $new_attachment_id, $new_image_meta ); - - $path = '/wp/v2/media/' . $new_attachment_id; - $new_request = new WP_REST_Request( 'GET', $path ); - $new_request->set_query_params( array( 'context' => 'edit' ) ); - $response = rest_do_request( $new_request ); - - if ( ! $response->is_error() ) { - $response->set_status( 201 ); - $response->header( 'Location', rest_url( $path ) ); - } - - return $response; - } -} diff --git a/lib/class-wp-rest-plugins-controller.php b/lib/class-wp-rest-plugins-controller.php deleted file mode 100644 index 49a55192dad204..00000000000000 --- a/lib/class-wp-rest-plugins-controller.php +++ /dev/null @@ -1,950 +0,0 @@ -namespace = 'wp/v2'; - $this->rest_base = 'plugins'; - } - - /** - * Registers the routes for the plugins controller. - * - * @since 5.5.0 - */ - public function register_routes() { - register_rest_route( - $this->namespace, - '/' . $this->rest_base, - array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'get_items_permissions_check' ), - 'args' => $this->get_collection_params(), - ), - array( - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'create_item' ), - 'permission_callback' => array( $this, 'create_item_permissions_check' ), - 'args' => array( - 'slug' => array( - 'type' => 'string', - 'required' => true, - 'description' => __( 'WordPress.org plugin directory slug.', 'gutenberg' ), - 'pattern' => '[\w\-]+', - ), - 'status' => array( - 'description' => __( 'The plugin activation status.', 'gutenberg' ), - 'type' => 'string', - 'enum' => is_multisite() ? array( 'inactive', 'active', 'network-active' ) : array( 'inactive', 'active' ), - 'default' => 'inactive', - ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P' . self::PATTERN . ')', - array( - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'get_item_permissions_check' ), - ), - array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), - 'permission_callback' => array( $this, 'update_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), - ), - array( - 'methods' => WP_REST_Server::DELETABLE, - 'callback' => array( $this, 'delete_item' ), - 'permission_callback' => array( $this, 'delete_item_permissions_check' ), - ), - 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), - 'plugin' => array( - 'type' => 'string', - 'pattern' => self::PATTERN, - 'validate_callback' => array( $this, 'validate_plugin_param' ), - 'sanitize_callback' => array( $this, 'sanitize_plugin_param' ), - ), - ), - 'schema' => array( $this, 'get_public_item_schema' ), - ) - ); - } - - /** - * Checks if a given request has access to get plugins. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return true|WP_Error True if the request has read access, WP_Error object otherwise. - */ - public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable - if ( ! current_user_can( 'activate_plugins' ) ) { - return new WP_Error( - 'rest_cannot_view_plugins', - __( 'Sorry, you are not allowed to manage plugins for this site.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - return true; - } - - /** - * Retrieves a collection of plugins. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function get_items( $request ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - - $plugins = array(); - - foreach ( get_plugins() as $file => $data ) { - if ( is_wp_error( $this->check_read_permission( $file ) ) ) { - continue; - } - - $data['_file'] = $file; - - if ( ! $this->does_plugin_match_request( $request, $data ) ) { - continue; - } - - $plugins[] = $this->prepare_response_for_collection( $this->prepare_item_for_response( $data, $request ) ); - } - - return new WP_REST_Response( $plugins ); - } - - /** - * Checks if a given request has access to get a specific plugin. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. - */ - public function get_item_permissions_check( $request ) { - if ( ! current_user_can( 'activate_plugins' ) ) { - return new WP_Error( - 'rest_cannot_view_plugin', - __( 'Sorry, you are not allowed to manage plugins for this site.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - $can_read = $this->check_read_permission( $request['plugin'] ); - - if ( is_wp_error( $can_read ) ) { - return $can_read; - } - - return true; - } - - /** - * Retrieves one plugin from the site. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function get_item( $request ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - - $data = $this->get_plugin_data( $request['plugin'] ); - - if ( is_wp_error( $data ) ) { - return $data; - } - - return $this->prepare_item_for_response( $data, $request ); - } - - /** - * Checks if the given plugin can be viewed by the current user. - * - * On multisite, this hides non-active network only plugins if the user does not have permission - * to manage network plugins. - * - * @since 5.5.0 - * - * @param string $plugin The plugin file to check. - * @return true|WP_Error True if can read, a WP_Error instance otherwise. - */ - protected function check_read_permission( $plugin ) { - if ( ! $this->is_plugin_installed( $plugin ) ) { - return new WP_Error( 'rest_plugin_not_found', __( 'Plugin not found.', 'gutenberg' ), array( 'status' => 404 ) ); - } - - if ( ! is_multisite() ) { - return true; - } - - if ( ! is_network_only_plugin( $plugin ) || is_plugin_active( $plugin ) || current_user_can( 'manage_network_plugins' ) ) { - return true; - } - - return new WP_Error( - 'rest_cannot_view_plugin', - __( 'Sorry, you are not allowed to manage this plugin.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - /** - * Checks if a given request has access to upload plugins. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise. - */ - public function create_item_permissions_check( $request ) { - if ( ! current_user_can( 'install_plugins' ) ) { - return new WP_Error( - 'rest_cannot_install_plugin', - __( 'Sorry, you are not allowed to install plugins on this site.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - if ( 'inactive' !== $request['status'] && ! current_user_can( 'activate_plugins' ) ) { - return new WP_Error( - 'rest_cannot_activate_plugin', - __( 'Sorry, you are not allowed to activate plugins.', 'gutenberg' ), - array( - 'status' => rest_authorization_required_code(), - ) - ); - } - - return true; - } - - /** - * Uploads a plugin and optionally activates it. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function create_item( $request ) { - require_once ABSPATH . 'wp-admin/includes/file.php'; - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; - - $slug = $request['slug']; - - // Verify filesystem is accessible first. - $filesystem_available = $this->is_filesystem_available(); - if ( is_wp_error( $filesystem_available ) ) { - return $filesystem_available; - } - - $api = plugins_api( - 'plugin_information', - array( - 'slug' => $slug, - 'fields' => array( - 'sections' => false, - ), - ) - ); - - if ( is_wp_error( $api ) ) { - if ( false !== strpos( $api->get_error_message(), 'Plugin not found.' ) ) { - $api->add_data( array( 'status' => 404 ) ); - } else { - $api->add_data( array( 'status' => 500 ) ); - } - - return $api; - } - - $skin = new WP_Ajax_Upgrader_Skin(); - $upgrader = new Plugin_Upgrader( $skin ); - - $result = $upgrader->install( $api->download_link ); - - if ( is_wp_error( $result ) ) { - $result->add_data( array( 'status' => 500 ) ); - - return $result; - } - - // This should be the same as $result above. - if ( is_wp_error( $skin->result ) ) { - $skin->result->add_data( array( 'status' => 500 ) ); - - return $skin->result; - } - - if ( $skin->get_errors()->has_errors() ) { - $error = $skin->get_errors(); - $error->add_data( array( 'status' => 500 ) ); - - return $error; - } - - if ( is_null( $result ) ) { - global $wp_filesystem; - // Pass through the error from WP_Filesystem if one was raised. - if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { - return new WP_Error( 'unable_to_connect_to_filesystem', $wp_filesystem->errors->get_error_message(), array( 'status' => 500 ) ); - } - - return new WP_Error( 'unable_to_connect_to_filesystem', __( 'Unable to connect to the filesystem. Please confirm your credentials.', 'gutenberg' ), array( 'status' => 500 ) ); - } - - $file = $upgrader->plugin_info(); - - if ( ! $file ) { - return new WP_Error( 'unable_to_determine_installed_plugin', __( 'Unable to determine what plugin was installed.', 'gutenberg' ), array( 'status' => 500 ) ); - } - - if ( 'inactive' !== $request['status'] ) { - $can_change_status = $this->plugin_status_permission_check( $file, $request['status'], 'inactive' ); - - if ( is_wp_error( $can_change_status ) ) { - return $can_change_status; - } - - $changed_status = $this->handle_plugin_status( $file, $request['status'], 'inactive' ); - - if ( is_wp_error( $changed_status ) ) { - return $changed_status; - } - } - - $path = WP_PLUGIN_DIR . '/' . $file; - $data = get_plugin_data( $path, false, false ); - $data['_file'] = $file; - - $response = $this->prepare_item_for_response( $data, $request ); - $response->set_status( 201 ); - $response->header( 'Location', rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, substr( $file, 0, - 4 ) ) ) ); - - return $response; - } - - /** - * Checks if a given request has access to update a specific plugin. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise. - */ - public function update_item_permissions_check( $request ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - - if ( ! current_user_can( 'activate_plugins' ) ) { - return new WP_Error( - 'rest_cannot_manage_plugins', - __( 'Sorry, you are not allowed to manage plugins for this site.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - $can_read = $this->check_read_permission( $request['plugin'] ); - - if ( is_wp_error( $can_read ) ) { - return $can_read; - } - - $status = $this->get_plugin_status( $request['plugin'] ); - - if ( $request['status'] && $status !== $request['status'] ) { - $can_change_status = $this->plugin_status_permission_check( $request['plugin'], $request['status'], $status ); - - if ( is_wp_error( $can_change_status ) ) { - return $can_change_status; - } - } - - return true; - } - - /** - * Updates one plugin. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function update_item( $request ) { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - - $data = $this->get_plugin_data( $request['plugin'] ); - - if ( is_wp_error( $data ) ) { - return $data; - } - - $status = $this->get_plugin_status( $request['plugin'] ); - - if ( $request['status'] && $status !== $request['status'] ) { - $handled = $this->handle_plugin_status( $request['plugin'], $request['status'], $status ); - - if ( is_wp_error( $handled ) ) { - return $handled; - } - } - - $this->update_additional_fields_for_object( $data, $request ); - - $request['context'] = 'edit'; - - return $this->prepare_item_for_response( $data, $request ); - } - - /** - * Checks if a given request has access to delete a specific plugin. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise. - */ - public function delete_item_permissions_check( $request ) { - if ( ! current_user_can( 'activate_plugins' ) ) { - return new WP_Error( - 'rest_cannot_manage_plugins', - __( 'Sorry, you are not allowed to manage plugins for this site.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - if ( ! current_user_can( 'delete_plugins' ) ) { - return new WP_Error( - 'rest_cannot_manage_plugins', - __( 'Sorry, you are not allowed to delete plugins for this site.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - $can_read = $this->check_read_permission( $request['plugin'] ); - - if ( is_wp_error( $can_read ) ) { - return $can_read; - } - - return true; - } - - /** - * Deletes one plugin from the site. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function delete_item( $request ) { - require_once ABSPATH . 'wp-admin/includes/file.php'; - require_once ABSPATH . 'wp-admin/includes/plugin.php'; - - $data = $this->get_plugin_data( $request['plugin'] ); - - if ( is_wp_error( $data ) ) { - return $data; - } - - if ( is_plugin_active( $request['plugin'] ) ) { - return new WP_Error( - 'rest_cannot_delete_active_plugin', - __( 'Cannot delete an active plugin. Please deactivate it first.', 'gutenberg' ), - array( 'status' => 400 ) - ); - } - - $filesystem_available = $this->is_filesystem_available(); - if ( is_wp_error( $filesystem_available ) ) { - return $filesystem_available; - } - - $prepared = $this->prepare_item_for_response( $data, $request ); - $deleted = delete_plugins( array( $request['plugin'] ) ); - - if ( is_wp_error( $deleted ) ) { - $deleted->add_data( array( 'status' => 500 ) ); - - return $deleted; - } - - return new WP_REST_Response( - array( - 'deleted' => true, - 'previous' => $prepared->get_data(), - ) - ); - } - - /** - * Prepares the plugin for the REST response. - * - * @since 5.5.0 - * - * @param mixed $item Unmarked up and untranslated plugin data from {@see get_plugin_data()}. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function prepare_item_for_response( $item, $request ) { - $item = _get_plugin_data_markup_translate( $item['_file'], $item, false ); - $marked = _get_plugin_data_markup_translate( $item['_file'], $item, true ); - - $data = array( - 'plugin' => substr( $item['_file'], 0, - 4 ), - 'status' => $this->get_plugin_status( $item['_file'] ), - 'name' => $item['Name'], - 'plugin_uri' => $item['PluginURI'], - 'author' => $item['Author'], - 'author_uri' => $item['AuthorURI'], - 'description' => array( - 'raw' => $item['Description'], - 'rendered' => $marked['Description'], - ), - 'version' => $item['Version'], - 'network_only' => $item['Network'], - 'requires_wp' => $item['RequiresWP'], - 'requires_php' => $item['RequiresPHP'], - 'text_domain' => $item['TextDomain'], - ); - - $data = $this->add_additional_fields_to_object( $data, $request ); - - $response = new WP_REST_Response( $data ); - $response->add_links( $this->prepare_links( $item ) ); - - /** - * Filters the plugin data for a response. - * - * @since 5.5.0 - * - * @param WP_REST_Response $response The response object. - * @param array $item The plugin item from {@see get_plugin_data()}. - * @param WP_REST_Request $request The request object. - */ - return apply_filters( 'rest_prepare_plugin', $response, $item, $request ); - } - - /** - * Prepares links for the request. - * - * @since 5.5.0 - * - * @param array $item The plugin item. - * @return array[] - */ - protected function prepare_links( $item ) { - return array( - 'self' => array( - 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, substr( $item['_file'], 0, - 4 ) ) ), - ), - ); - } - - /** - * Gets the plugin header data for a plugin. - * - * @since 5.5.0 - * - * @param string $plugin The plugin file to get data for. - * @return array|WP_Error The plugin data, or a WP_Error if the plugin is not installed. - */ - protected function get_plugin_data( $plugin ) { - $plugins = get_plugins(); - - if ( ! isset( $plugins[ $plugin ] ) ) { - return new WP_Error( 'rest_plugin_not_found', __( 'Plugin not found.', 'gutenberg' ), array( 'status' => 404 ) ); - } - - $data = $plugins[ $plugin ]; - $data['_file'] = $plugin; - - return $data; - } - - /** - * Get's the activation status for a plugin. - * - * @since 5.5.0 - * - * @param string $plugin The plugin file to check. - * @return string Either 'network-active', 'active' or 'inactive'. - */ - protected function get_plugin_status( $plugin ) { - if ( is_plugin_active_for_network( $plugin ) ) { - return 'network-active'; - } - - if ( is_plugin_active( $plugin ) ) { - return 'active'; - } - - return 'inactive'; - } - - /** - * Handle updating a plugin's status. - * - * @since 5.5.0 - * - * @param string $plugin The plugin file to update. - * @param string $new_status The plugin's new status. - * @param string $current_status The plugin's current status. - * - * @return true|WP_Error - */ - protected function plugin_status_permission_check( $plugin, $new_status, $current_status ) { - if ( is_multisite() && ( 'network-active' === $current_status || 'network-active' === $new_status ) && ! current_user_can( 'manage_network_plugins' ) ) { - return new WP_Error( - 'rest_cannot_manage_network_plugins', - __( 'Sorry, you do not have permission to manage network plugins.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - if ( ( 'active' === $new_status || 'network-active' === $new_status ) && ! current_user_can( 'activate_plugin', $plugin ) ) { - return new WP_Error( - 'rest_cannot_activate_plugin', - __( 'Sorry, you are not allowed to activate this plugin.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - if ( 'inactive' === $new_status && ! current_user_can( 'deactivate_plugin', $plugin ) ) { - return new WP_Error( - 'rest_cannot_deactivate_plugin', - __( 'Sorry, you are not allowed to deactivate this plugin.', 'gutenberg' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - return true; - } - - /** - * Handle updating a plugin's status. - * - * @since 5.5.0 - * - * @param string $plugin The plugin file to update. - * @param string $new_status The plugin's new status. - * @param string $current_status The plugin's current status. - * @return true|WP_Error - */ - protected function handle_plugin_status( $plugin, $new_status, $current_status ) { - if ( 'inactive' === $new_status ) { - deactivate_plugins( $plugin, false, 'network-active' === $current_status ); - - return true; - } - - if ( 'active' === $new_status && 'network-active' === $current_status ) { - return true; - } - - $network_activate = 'network-active' === $new_status; - - if ( is_multisite() && ! $network_activate && is_network_only_plugin( $plugin ) ) { - return new WP_Error( - 'rest_network_only_plugin', - __( 'Network only plugin must be network activated.', 'gutenberg' ), - array( 'status' => 400 ) - ); - } - - $activated = activate_plugin( $plugin, '', $network_activate ); - - if ( is_wp_error( $activated ) ) { - $activated->add_data( array( 'status' => 500 ) ); - - return $activated; - } - - return true; - } - - /** - * Checks that the "plugin" parameter is a valid path. - * - * @since 5.5.0 - * - * @param string $file The plugin file parameter. - * @return bool - */ - public function validate_plugin_param( $file ) { - if ( ! is_string( $file ) || ! preg_match( '/' . self::PATTERN . '/u', $file ) ) { - return false; - } - - $validated = validate_file( plugin_basename( $file ) ); - - return 0 === $validated; - } - - /** - * Sanitizes the "plugin" parameter to be a proper plugin file with ".php" appended. - * - * @since 5.5.0 - * - * @param string $file The plugin file parameter. - * @return string - */ - public function sanitize_plugin_param( $file ) { - return plugin_basename( sanitize_text_field( $file . '.php' ) ); - } - - /** - * Checks if the plugin matches the requested parameters. - * - * @since 5.5.0 - * - * @param WP_REST_Request $request The request to require the plugin matches against. - * @param array $item The plugin item. - * - * @return bool - */ - protected function does_plugin_match_request( $request, $item ) { - $search = $request['search']; - - if ( $search ) { - $matched_search = false; - - foreach ( $item as $field ) { - if ( is_string( $field ) && false !== strpos( strip_tags( $field ), $search ) ) { - $matched_search = true; - break; - } - } - - if ( ! $matched_search ) { - return false; - } - } - - $status = $request['status']; - - if ( $status && ! in_array( $this->get_plugin_status( $item['_file'] ), $status, true ) ) { - return false; - } - - return true; - } - - /** - * Checks if the plugin is installed. - * - * @since 5.5.0 - * - * @param string $plugin The plugin file. - * @return bool - */ - protected function is_plugin_installed( $plugin ) { - return file_exists( WP_PLUGIN_DIR . '/' . $plugin ); - } - - /** - * Determine if the endpoints are available. - * - * Only the 'Direct' filesystem transport, and SSH/FTP when credentials are stored are supported at present. - * - * @since 5.5.0 - * - * @return true|WP_Error True if filesystem is available, WP_Error otherwise. - */ - protected function is_filesystem_available() { - $filesystem_method = get_filesystem_method(); - - if ( 'direct' === $filesystem_method ) { - return true; - } - - ob_start(); - $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() ); - ob_end_clean(); - - if ( $filesystem_credentials_are_stored ) { - return true; - } - - return new WP_Error( 'fs_unavailable', __( 'The filesystem is currently unavailable for managing plugins.', 'gutenberg' ), array( 'status' => 500 ) ); - } - - /** - * Retrieves the plugin's schema, conforming to JSON Schema. - * - * @since 4.7.0 - * - * @return array Item schema data. - */ - public function get_item_schema() { - if ( $this->schema ) { - return $this->add_additional_fields_schema( $this->schema ); - } - - $this->schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'plugin', - 'type' => 'object', - 'properties' => array( - 'plugin' => array( - 'description' => __( 'The plugin file.', 'gutenberg' ), - 'type' => 'string', - 'pattern' => self::PATTERN, - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'status' => array( - 'description' => __( 'The plugin activation status.', 'gutenberg' ), - 'type' => 'string', - 'enum' => is_multisite() ? array( 'inactive', 'active', 'network-active' ) : array( 'inactive', 'active' ), - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'name' => array( - 'description' => __( 'The plugin name.', 'gutenberg' ), - 'type' => 'string', - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'plugin_uri' => array( - 'description' => __( 'The plugin\'s website address.', 'gutenberg' ), - 'type' => 'string', - 'format' => 'uri', - 'readonly' => true, - 'context' => array( 'view', 'edit' ), - ), - 'author' => array( - 'description' => __( 'The plugin author.', 'gutenberg' ), - 'type' => 'object', - 'readonly' => true, - 'context' => array( 'view', 'edit' ), - ), - 'author_uri' => array( - 'description' => __( 'Plugin author\'s website address.', 'gutenberg' ), - 'type' => 'string', - 'format' => 'uri', - 'readonly' => true, - 'context' => array( 'view', 'edit' ), - ), - 'description' => array( - 'description' => __( 'The plugin description.', 'gutenberg' ), - 'type' => 'object', - 'readonly' => true, - 'context' => array( 'view', 'edit' ), - 'properties' => array( - 'raw' => array( - 'description' => __( 'The raw plugin description.', 'gutenberg' ), - 'type' => 'string', - ), - 'rendered' => array( - 'description' => __( 'The plugin description formatted for display.', 'gutenberg' ), - 'type' => 'string', - ), - ), - ), - 'version' => array( - 'description' => __( 'The plugin version number.', 'gutenberg' ), - 'type' => 'string', - 'readonly' => true, - 'context' => array( 'view', 'edit' ), - ), - 'network_only' => array( - 'description' => __( 'Whether the plugin can only be activated network-wide.', 'gutenberg' ), - 'type' => 'boolean', - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'requires_wp' => array( - 'description' => __( 'Minimum required version of WordPress.', 'gutenberg' ), - 'type' => 'string', - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'requires_php' => array( - 'description' => __( 'Minimum required version of PHP.', 'gutenberg' ), - 'type' => 'string', - 'readonly' => true, - 'context' => array( 'view', 'edit', 'embed' ), - ), - 'text_domain' => array( - 'description' => __( 'The plugin\'s text domain.', 'gutenberg' ), - 'type' => 'string', - 'readonly' => true, - 'context' => array( 'view', 'edit' ), - ), - ), - ); - - return $this->add_additional_fields_schema( $this->schema ); - } - - /** - * Retrieves the query params for the collections. - * - * @since 5.5.0 - * - * @return array Query parameters for the collection. - */ - public function get_collection_params() { - $query_params = parent::get_collection_params(); - - $query_params['context']['default'] = 'view'; - - $query_params['status'] = array( - 'description' => __( 'Limits results to plugins with the given status.', 'gutenberg' ), - 'type' => 'array', - 'items' => array( - 'type' => 'string', - 'enum' => is_multisite() ? array( 'inactive', 'active', 'network-active' ) : array( 'inactive', 'active' ), - ), - ); - - unset( $query_params['page'], $query_params['per_page'] ); - - return $query_params; - } -} diff --git a/lib/class-wp-theme-json-resolver.php b/lib/class-wp-theme-json-resolver.php new file mode 100644 index 00000000000000..a94dbb1fde86d6 --- /dev/null +++ b/lib/class-wp-theme-json-resolver.php @@ -0,0 +1,347 @@ + __( 'Black', 'gutenberg' ), + 'cyan-bluish-gray' => __( 'Cyan bluish gray', 'gutenberg' ), + 'white' => __( 'White', 'gutenberg' ), + 'pale-pink' => __( 'Pale pink', 'gutenberg' ), + 'vivid-red' => __( 'Vivid red', 'gutenberg' ), + 'luminous-vivid-orange' => __( 'Luminous vivid orange', 'gutenberg' ), + 'luminous-vivid-amber' => __( 'Luminous vivid amber', 'gutenberg' ), + 'light-green-cyan' => __( 'Light green cyan', 'gutenberg' ), + 'vivid-green-cyan' => __( 'Vivid green cyan', 'gutenberg' ), + 'pale-cyan-blue' => __( 'Pale cyan blue', 'gutenberg' ), + 'vivid-cyan-blue' => __( 'Vivid cyan blue', 'gutenberg' ), + 'vivid-purple' => __( 'Vivid purple', 'gutenberg' ), + ); + if ( ! empty( $config['global']['settings']['color']['palette'] ) ) { + foreach ( $config['global']['settings']['color']['palette'] as &$color ) { + $color['name'] = $default_colors_i18n[ $color['slug'] ]; + } + } + + $default_gradients_i18n = array( + 'vivid-cyan-blue-to-vivid-purple' => __( 'Vivid cyan blue to vivid purple', 'gutenberg' ), + 'light-green-cyan-to-vivid-green-cyan' => __( 'Light green cyan to vivid green cyan', 'gutenberg' ), + 'luminous-vivid-amber-to-luminous-vivid-orange' => __( 'Luminous vivid amber to luminous vivid orange', 'gutenberg' ), + 'luminous-vivid-orange-to-vivid-red' => __( 'Luminous vivid orange to vivid red', 'gutenberg' ), + 'very-light-gray-to-cyan-bluish-gray' => __( 'Very light gray to cyan bluish gray', 'gutenberg' ), + 'cool-to-warm-spectrum' => __( 'Cool to warm spectrum', 'gutenberg' ), + 'blush-light-purple' => __( 'Blush light purple', 'gutenberg' ), + 'blush-bordeaux' => __( 'Blush bordeaux', 'gutenberg' ), + 'luminous-dusk' => __( 'Luminous dusk', 'gutenberg' ), + 'pale-ocean' => __( 'Pale ocean', 'gutenberg' ), + 'electric-grass' => __( 'Electric grass', 'gutenberg' ), + 'midnight' => __( 'Midnight', 'gutenberg' ), + ); + if ( ! empty( $config['global']['settings']['color']['gradients'] ) ) { + foreach ( $config['global']['settings']['color']['gradients'] as &$gradient ) { + $gradient['name'] = $default_gradients_i18n[ $gradient['slug'] ]; + } + } + + $default_font_sizes_i18n = array( + 'small' => __( 'Small', 'gutenberg' ), + 'normal' => __( 'Normal', 'gutenberg' ), + 'medium' => __( 'Medium', 'gutenberg' ), + 'large' => __( 'Large', 'gutenberg' ), + 'huge' => __( 'Huge', 'gutenberg' ), + ); + if ( ! empty( $config['global']['settings']['typography']['fontSizes'] ) ) { + foreach ( $config['global']['settings']['typography']['fontSizes'] as &$font_size ) { + $font_size['name'] = $default_font_sizes_i18n[ $font_size['slug'] ]; + } + } + // End i18n logic to remove when JSON i18 strings are extracted. + + self::$core = new WP_Theme_JSON( $config ); + + return self::$core; + } + + /** + * Returns the theme's origin config. + * + * It uses the theme support data if + * the theme hasn't declared any via theme.json. + * + * @param array $theme_support_data Theme support data in theme.json format. + * + * @return WP_Theme_JSON Entity that holds theme data. + */ + private function get_theme_origin( $theme_support_data = array() ) { + $theme_json_data = self::get_from_file( locate_template( 'experimental-theme.json' ) ); + + /* + * We want the presets and settings declared in theme.json + * to override the ones declared via add_theme_support. + */ + $this->theme = new WP_Theme_JSON( $theme_support_data ); + $this->theme->merge( new WP_Theme_JSON( $theme_json_data ) ); + + return $this->theme; + } + + /** + * Returns the CPT that contains the user's origin config + * for the current theme or a void array if none found. + * + * It can also create and return a new draft CPT. + * + * @param bool $should_create_cpt Whether a new CPT should be created if no one was found. + * False by default. + * @param array $post_status_filter Filter CPT by post status. + * ['publish'] by default, so it only fetches published posts. + * + * @return array Custom Post Type for the user's origin config. + */ + private static function get_user_data_from_custom_post_type( $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) { + $user_cpt = array(); + $post_type_filter = 'wp_global_styles'; + $post_name_filter = 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() ); + $recent_posts = wp_get_recent_posts( + array( + 'numberposts' => 1, + 'orderby' => 'date', + 'order' => 'desc', + 'post_type' => $post_type_filter, + 'post_status' => $post_status_filter, + 'name' => $post_name_filter, + ) + ); + + if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) { + $user_cpt = $recent_posts[0]; + } elseif ( $should_create_cpt ) { + $cpt_post_id = wp_insert_post( + array( + 'post_content' => '{}', + 'post_status' => 'publish', + 'post_type' => $post_type_filter, + 'post_name' => $post_name_filter, + ), + true + ); + $user_cpt = get_post( $cpt_post_id, ARRAY_A ); + } + + return $user_cpt; + } + + /** + * Returns the user's origin config. + * + * @return WP_Theme_JSON Entity that holds user data. + */ + private static function get_user_origin() { + if ( null !== self::$user ) { + return self::$user; + } + + $config = array(); + $user_cpt = self::get_user_data_from_custom_post_type(); + if ( array_key_exists( 'post_content', $user_cpt ) ) { + $decoded_data = json_decode( $user_cpt['post_content'], true ); + + $json_decoding_error = json_last_error(); + if ( JSON_ERROR_NONE !== $json_decoding_error ) { + error_log( 'Error when decoding user schema: ' . json_last_error_msg() ); + return $config; + } + + if ( is_array( $decoded_data ) ) { + $config = $decoded_data; + } + } + self::$user = new WP_Theme_JSON( $config, true ); + + return self::$user; + } + + /** + * There are three sources of data for a site: + * core, theme, and user. + * + * The main function of the resolver is to + * merge all this data following this algorithm: + * theme overrides core, and user overrides + * data coming from either theme or core. + * + * user data > theme data > core data + * + * The main use case for the resolver is to return + * the merged data up to the user level.However, + * there are situations in which we need the + * data merged up to a different level (theme) + * or no merged at all. + * + * @param array $theme_support_data Existing block editor settings. + * Empty array by default. + * @param string $origin The source of data the consumer wants. + * Valid values are 'core', 'theme', 'user'. + * Default is 'user'. + * @param boolean $merged Whether the data should be merged + * with the previous origins (the default). + * + * @return WP_Theme_JSON + */ + public function get_origin( $theme_support_data = array(), $origin = 'user', $merged = true ) { + if ( ( 'user' === $origin ) && $merged ) { + $result = new WP_Theme_JSON(); + $result->merge( self::get_core_origin() ); + $result->merge( $this->get_theme_origin( $theme_support_data ) ); + $result->merge( self::get_user_origin() ); + return $result; + } + + if ( ( 'theme' === $origin ) && $merged ) { + $result = new WP_Theme_JSON(); + $result->merge( self::get_core_origin() ); + $result->merge( $this->get_theme_origin( $theme_support_data ) ); + return $result; + } + + if ( 'user' === $origin ) { + return self::get_user_origin(); + } + + if ( 'theme' === $origin ) { + return $this->get_theme_origin( $theme_support_data ); + } + + return self::get_core_origin(); + } + + /** + * Registers a Custom Post Type to store the user's origin config. + */ + public static function register_user_custom_post_type() { + $args = array( + 'label' => __( 'Global Styles', 'gutenberg' ), + 'description' => 'CPT to store user design tokens', + 'public' => false, + 'show_ui' => false, + 'show_in_rest' => true, + 'rest_base' => '__experimental/global-styles', + 'capabilities' => array( + 'read' => 'edit_theme_options', + 'create_posts' => 'edit_theme_options', + 'edit_posts' => 'edit_theme_options', + 'edit_published_posts' => 'edit_theme_options', + 'delete_published_posts' => 'edit_theme_options', + 'edit_others_posts' => 'edit_theme_options', + 'delete_others_posts' => 'edit_theme_options', + ), + 'map_meta_cap' => true, + 'supports' => array( + 'editor', + 'revisions', + ), + ); + register_post_type( 'wp_global_styles', $args ); + } + + /** + * Returns the ID of the custom post type + * that stores user data. + * + * @return integer + */ + public static function get_user_custom_post_type_id() { + if ( null !== self::$user_custom_post_type_id ) { + return self::$user_custom_post_type_id; + } + + $user_cpt = self::get_user_data_from_custom_post_type( true ); + if ( array_key_exists( 'ID', $user_cpt ) ) { + self::$user_custom_post_type_id = $user_cpt['ID']; + } + + return self::$user_custom_post_type_id; + } + +} diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index 0452510d306c6c..95344313853c8f 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -50,6 +50,7 @@ class WP_Theme_JSON { '--wp--style--color--link', 'background', 'backgroundColor', + 'border', 'color', 'fontFamily', 'fontSize', @@ -88,8 +89,6 @@ class WP_Theme_JSON { * } */ const SCHEMA = array( - 'selector' => null, - 'supports' => null, 'styles' => array( 'color' => array( 'background' => null, @@ -116,6 +115,9 @@ class WP_Theme_JSON { ), ), 'settings' => array( + 'border' => array( + 'customRadius' => null, + ), 'color' => array( 'custom' => null, 'customGradient' => null, @@ -128,15 +130,15 @@ class WP_Theme_JSON { 'units' => null, ), 'typography' => array( - 'customFontSize' => null, - 'customLineHeight' => null, - 'dropCap' => null, - 'fontFamilies' => null, - 'fontSizes' => null, - 'fontStyles' => null, - 'fontWeights' => null, - 'textDecorations' => null, - 'textTransforms' => null, + 'customFontSize' => null, + 'customLineHeight' => null, + 'dropCap' => null, + 'fontFamilies' => null, + 'fontSizes' => null, + 'customFontStyle' => null, + 'customFontWeight' => null, + 'customTextDecorations' => null, + 'customTextTransforms' => null, ), 'custom' => null, ), @@ -219,50 +221,6 @@ class WP_Theme_JSON { 'css_var_infix' => 'font-family', 'classes' => array(), ), - array( - 'path' => array( 'settings', 'typography', 'fontStyles' ), - 'value_key' => 'slug', - 'css_var_infix' => 'font-style', - 'classes' => array( - array( - 'class_suffix' => 'font-style', - 'property_name' => 'font-style', - ), - ), - ), - array( - 'path' => array( 'settings', 'typography', 'fontWeights' ), - 'value_key' => 'slug', - 'css_var_infix' => 'font-weight', - 'classes' => array( - array( - 'class_suffix' => 'font-weight', - 'property_name' => 'font-weight', - ), - ), - ), - array( - 'path' => array( 'settings', 'typography', 'textDecorations' ), - 'value_key' => 'value', - 'css_var_infix' => 'text-decoration', - 'classes' => array( - array( - 'class_suffix' => 'text-decoration', - 'property_name' => 'text-decoration', - ), - ), - ), - array( - 'path' => array( 'settings', 'typography', 'textTransforms' ), - 'value_key' => 'slug', - 'css_var_infix' => 'text-transform', - 'classes' => array( - array( - 'class_suffix' => 'text-transform', - 'property_name' => 'text-transform', - ), - ), - ), ); /** @@ -286,6 +244,10 @@ class WP_Theme_JSON { 'value' => array( 'color', 'background' ), 'support' => array( 'color' ), ), + 'borderRadius' => array( + 'value' => array( 'border', 'radius' ), + 'support' => array( '__experimentalBorder' ), + ), 'color' => array( 'value' => array( 'color', 'text' ), 'support' => array( 'color' ), @@ -300,11 +262,11 @@ class WP_Theme_JSON { ), 'fontStyle' => array( 'value' => array( 'typography', 'fontStyle' ), - 'support' => array( '__experimentalFontAppearance' ), + 'support' => array( '__experimentalFontStyle' ), ), 'fontWeight' => array( 'value' => array( 'typography', 'fontWeight' ), - 'support' => array( '__experimentalFontAppearance' ), + 'support' => array( '__experimentalFontWeight' ), ), 'lineHeight' => array( 'value' => array( 'typography', 'lineHeight' ), @@ -339,9 +301,10 @@ class WP_Theme_JSON { /** * Constructor. * - * @param array $contexts A structure that follows the theme.json schema. + * @param array $contexts A structure that follows the theme.json schema. + * @param boolean $should_escape_styles Whether the incoming styles should be escaped. */ - public function __construct( $contexts = array() ) { + public function __construct( $contexts = array(), $should_escape_styles = false ) { $this->contexts = array(); if ( ! is_array( $contexts ) ) { @@ -359,15 +322,12 @@ public function __construct( $contexts = array() ) { // Filter out top-level keys that aren't valid according to the schema. $context = array_intersect_key( $context, self::SCHEMA ); - // Selector & Supports are always taken from metadata. - $this->contexts[ $key ]['selector'] = $metadata[ $key ]['selector']; - $this->contexts[ $key ]['supports'] = $metadata[ $key ]['supports']; - // Process styles subtree. $this->process_key( 'styles', $context, self::SCHEMA ); if ( isset( $context['styles'] ) ) { - $this->process_key( 'color', $context['styles'], self::SCHEMA['styles'] ); - $this->process_key( 'typography', $context['styles'], self::SCHEMA['styles'] ); + $this->process_key( 'color', $context['styles'], self::SCHEMA['styles'], $should_escape_styles ); + $this->process_key( 'spacing', $context['styles'], self::SCHEMA['styles'], $should_escape_styles ); + $this->process_key( 'typography', $context['styles'], self::SCHEMA['styles'], $should_escape_styles ); if ( empty( $context['styles'] ) ) { unset( $context['styles'] ); @@ -379,6 +339,7 @@ public function __construct( $contexts = array() ) { // Process settings subtree. $this->process_key( 'settings', $context, self::SCHEMA ); if ( isset( $context['settings'] ) ) { + $this->process_key( 'border', $context['settings'], self::SCHEMA['settings'] ); $this->process_key( 'color', $context['settings'], self::SCHEMA['settings'] ); $this->process_key( 'spacing', $context['settings'], self::SCHEMA['settings'] ); $this->process_key( 'typography', $context['settings'], self::SCHEMA['settings'] ); @@ -511,11 +472,12 @@ private static function get_blocks_metadata() { * This function modifies the given input by removing * the nodes that aren't valid per the schema. * - * @param string $key Key of the subtree to normalize. - * @param array $input Whole tree to normalize. - * @param array $schema Schema to use for normalization. + * @param string $key Key of the subtree to normalize. + * @param array $input Whole tree to normalize. + * @param array $schema Schema to use for normalization. + * @param boolean $should_escape Whether the subproperties should be escaped. */ - private static function process_key( $key, &$input, $schema ) { + private static function process_key( $key, &$input, $schema, $should_escape = false ) { if ( ! isset( $input[ $key ] ) ) { return; } @@ -535,6 +497,21 @@ private static function process_key( $key, &$input, $schema ) { $schema[ $key ] ); + if ( $should_escape ) { + $subtree = $input[ $key ]; + foreach ( $subtree as $property => $value ) { + $name = 'background-color'; + if ( 'gradient' === $property ) { + $name = 'background'; + } + $result = safecss_filter_attr( "$name: $value" ); + + if ( '' === $result ) { + unset( $input[ $key ][ $property ] ); + } + } + } + if ( 0 === count( $input[ $key ] ) ) { unset( $input[ $key ] ); } @@ -661,16 +638,17 @@ private static function get_property_value( $styles, $path ) { * * Note that this modifies the $declarations in place. * - * @param array $declarations Holds the existing declarations. - * @param array $context Input context to process. + * @param array $declarations Holds the existing declarations. + * @param array $context Input context to process. + * @param array $context_supports Supports information for this context. */ - private static function compute_style_properties( &$declarations, $context ) { - if ( empty( $context['supports'] ) || empty( $context['styles'] ) ) { + private static function compute_style_properties( &$declarations, $context, $context_supports ) { + if ( empty( $context['styles'] ) ) { return; } foreach ( self::PROPERTIES_METADATA as $name => $metadata ) { - if ( ! in_array( $name, $context['supports'], true ) ) { + if ( ! in_array( $name, $context_supports, true ) ) { continue; } @@ -693,9 +671,9 @@ private static function compute_style_properties( &$declarations, $context ) { * * @param string $stylesheet Input stylesheet to add the presets to. * @param array $context Context to process. + * @param string $selector Selector wrapping the classes. */ - private static function compute_preset_classes( &$stylesheet, $context ) { - $selector = $context['selector']; + private static function compute_preset_classes( &$stylesheet, $context, $selector ) { if ( self::GLOBAL_SELECTOR === $selector ) { // Classes at the global level do not need any CSS prefixed, // and we don't want to increase its specificity. @@ -790,6 +768,9 @@ private static function compute_theme_vars( &$declarations, $context ) { * @return string CSS ruleset. */ private static function to_ruleset( $selector, $declarations ) { + if ( empty( $declarations ) ) { + return ''; + } $ruleset = ''; if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { @@ -816,17 +797,50 @@ function ( $carry, $element ) { /** * Converts each context into a list of rulesets * to be appended to the stylesheet. + * These rulesets contain all the css variables (custom variables and preset variables). * * See glossary at https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax * * For each context this creates a new ruleset such as: * * context-selector { - * style-property-one: value; * --wp--preset--category--slug: value; * --wp--custom--variable: value; * } * + * @return string The new stylesheet. + */ + private function get_css_variables() { + $stylesheet = ''; + $metadata = $this->get_blocks_metadata(); + foreach ( $this->contexts as $context_name => $context ) { + if ( empty( $metadata[ $context_name ]['selector'] ) ) { + continue; + } + $selector = $metadata[ $context_name ]['selector']; + + $declarations = array(); + self::compute_preset_vars( $declarations, $context ); + self::compute_theme_vars( $declarations, $context ); + + // Attach the ruleset for style and custom properties. + $stylesheet .= self::to_ruleset( $selector, $declarations ); + } + return $stylesheet; + } + + /** + * Converts each context into a list of rulesets + * containing the block styles to be appended to the stylesheet. + * + * See glossary at https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax + * + * For each context this creates a new ruleset such as: + * + * context-selector { + * style-property-one: value; + * } + * * Additionally, it'll also create new rulesets * as classes for each preset value such as: * @@ -846,33 +860,30 @@ function ( $carry, $element ) { * background: value; * } * - * @param string $stylesheet Stylesheet to append new rules to. - * @param array $context Context to be processed. + * p.has-value-gradient-background { + * background: value; + * } * * @return string The new stylesheet. */ - private static function to_stylesheet( $stylesheet, $context ) { - if ( empty( $context['selector'] ) ) { - return ''; - } + private function get_block_styles() { + $stylesheet = ''; + $metadata = $this->get_blocks_metadata(); + foreach ( $this->contexts as $context_name => $context ) { + if ( empty( $metadata[ $context_name ]['selector'] ) || empty( $metadata[ $context_name ]['supports'] ) ) { + continue; + } + $selector = $metadata[ $context_name ]['selector']; + $supports = $metadata[ $context_name ]['supports']; - $declarations = array(); - self::compute_style_properties( $declarations, $context ); - self::compute_preset_vars( $declarations, $context ); - self::compute_theme_vars( $declarations, $context ); + $declarations = array(); + self::compute_style_properties( $declarations, $context, $supports ); - // If there are no declarations at this point, - // it won't have any preset classes either, - // so bail out earlier. - if ( empty( $declarations ) ) { - return ''; - } + $stylesheet .= self::to_ruleset( $selector, $declarations ); - // Attach the ruleset for style and custom properties. - $stylesheet .= self::to_ruleset( $context['selector'], $declarations ); - - // Attach the rulesets for the classes. - self::compute_preset_classes( $stylesheet, $context ); + // Attach the rulesets for the classes. + self::compute_preset_classes( $stylesheet, $context, $selector ); + } return $stylesheet; } @@ -910,10 +921,18 @@ function ( $element ) { * Returns the stylesheet that results of processing * the theme.json structure this object represents. * + * @param string $type Type of stylesheet we want accepts 'all', 'block_styles', and 'css_variables'. * @return string Stylesheet. */ - public function get_stylesheet() { - return array_reduce( $this->contexts, array( $this, 'to_stylesheet' ), '' ); + public function get_stylesheet( $type = 'all' ) { + switch ( $type ) { + case 'block_styles': + return $this->get_block_styles(); + case 'css_variables': + return $this->get_css_variables(); + default: + return $this->get_css_variables() . $this->get_block_styles(); + } } /** @@ -923,13 +942,8 @@ public function get_stylesheet() { */ public function merge( $theme_json ) { $incoming_data = $theme_json->get_raw_data(); - $metadata = $this->get_blocks_metadata(); foreach ( array_keys( $incoming_data ) as $context ) { - // Selector & Supports are always taken from metadata. - $this->contexts[ $context ]['selector'] = $metadata[ $context ]['selector']; - $this->contexts[ $context ]['supports'] = $metadata[ $context ]['supports']; - foreach ( array( 'settings', 'styles' ) as $subtree ) { if ( ! isset( $incoming_data[ $context ][ $subtree ] ) ) { continue; diff --git a/lib/client-assets.php b/lib/client-assets.php index d17b40b801bebf..47d178667b2ba6 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -335,12 +335,13 @@ function gutenberg_register_packages_styles( $styles ) { ); $styles->add_data( 'wp-components', 'rtl', 'replace' ); + $block_library_filename = gutenberg_should_load_separate_block_styles() ? 'common' : 'style'; gutenberg_override_style( $styles, 'wp-block-library', - gutenberg_url( 'build/block-library/style.css' ), + gutenberg_url( 'build/block-library/' . $block_library_filename . '.css' ), array(), - filemtime( gutenberg_dir_path() . 'build/block-library/style.css' ) + filemtime( gutenberg_dir_path() . 'build/block-library/' . $block_library_filename . '.css' ) ); $styles->add_data( 'wp-block-library', 'rtl', 'replace' ); @@ -649,3 +650,16 @@ function gutenberg_extend_block_editor_settings_with_default_editor_styles( $set return $settings; } add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_settings_with_default_editor_styles' ); + +/** + * Adds a flag to the editor settings to know whether we're in FSE theme or not. + * + * @param array $settings Default editor settings. + * + * @return array Filtered editor settings. + */ +function gutenberg_extend_block_editor_settings_with_fse_theme_flag( $settings ) { + $settings['isFSETheme'] = gutenberg_is_fse_theme(); + return $settings; +} +add_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_settings_with_fse_theme_flag' ); diff --git a/lib/compat.php b/lib/compat.php index df4e144a1131ac..008298df270967 100644 --- a/lib/compat.php +++ b/lib/compat.php @@ -8,231 +8,6 @@ * @package gutenberg */ -/** - * These functions can be removed when plugin support requires WordPress 5.5.0+. - * - * @see https://core.trac.wordpress.org/ticket/50263 - * @see https://core.trac.wordpress.org/changeset/48141 - */ -if ( ! function_exists( 'register_block_type_from_metadata' ) ) { - /** - * Removes the block asset's path prefix if provided. - * - * @since 5.5.0 - * - * @param string $asset_handle_or_path Asset handle or prefixed path. - * - * @return string Path without the prefix or the original value. - */ - function remove_block_asset_path_prefix( $asset_handle_or_path ) { - $path_prefix = 'file:'; - if ( strpos( $asset_handle_or_path, $path_prefix ) !== 0 ) { - return $asset_handle_or_path; - } - return substr( - $asset_handle_or_path, - strlen( $path_prefix ) - ); - } - - /** - * Generates the name for an asset based on the name of the block - * and the field name provided. - * - * @since 5.5.0 - * - * @param string $block_name Name of the block. - * @param string $field_name Name of the metadata field. - * - * @return string Generated asset name for the block's field. - */ - function generate_block_asset_handle( $block_name, $field_name ) { - $field_mappings = array( - 'editorScript' => 'editor-script', - 'script' => 'script', - 'editorStyle' => 'editor-style', - 'style' => 'style', - ); - return str_replace( '/', '-', $block_name ) . - '-' . $field_mappings[ $field_name ]; - } - - /** - * Finds a script handle for the selected block metadata field. It detects - * when a path to file was provided and finds a corresponding - * asset file with details necessary to register the script under - * automatically generated handle name. It returns unprocessed script handle - * otherwise. - * - * @since 5.5.0 - * - * @param array $metadata Block metadata. - * @param string $field_name Field name to pick from metadata. - * - * @return string|boolean Script handle provided directly or created through - * script's registration, or false on failure. - */ - function register_block_script_handle( $metadata, $field_name ) { - if ( empty( $metadata[ $field_name ] ) ) { - return false; - } - $script_handle = $metadata[ $field_name ]; - $script_path = remove_block_asset_path_prefix( $metadata[ $field_name ] ); - if ( $script_handle === $script_path ) { - return $script_handle; - } - - $script_handle = generate_block_asset_handle( $metadata['name'], $field_name ); - $script_asset_path = realpath( - dirname( $metadata['file'] ) . '/' . - substr_replace( $script_path, '.asset.php', - strlen( '.js' ) ) - ); - if ( ! file_exists( $script_asset_path ) ) { - $message = sprintf( - /* translators: %1: field name. %2: block name */ - __( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.', 'default' ), - $field_name, - $metadata['name'] - ); - _doing_it_wrong( __FUNCTION__, $message, '5.5.0' ); - return false; - } - $script_asset = require( $script_asset_path ); - $result = wp_register_script( - $script_handle, - plugins_url( $script_path, $metadata['file'] ), - $script_asset['dependencies'], - $script_asset['version'] - ); - return $result ? $script_handle : false; - } - - /** - * Finds a style handle for the block metadata field. It detects when a path - * to file was provided and registers the style under automatically - * generated handle name. It returns unprocessed style handle otherwise. - * - * @since 5.5.0 - * - * @param array $metadata Block metadata. - * @param string $field_name Field name to pick from metadata. - * - * @return string|boolean Style handle provided directly or created through - * style's registration, or false on failure. - */ - function register_block_style_handle( $metadata, $field_name ) { - if ( empty( $metadata[ $field_name ] ) ) { - return false; - } - $style_handle = $metadata[ $field_name ]; - $style_path = remove_block_asset_path_prefix( $metadata[ $field_name ] ); - if ( $style_handle === $style_path ) { - return $style_handle; - } - - $style_handle = generate_block_asset_handle( $metadata['name'], $field_name ); - $block_dir = dirname( $metadata['file'] ); - $result = wp_register_style( - $style_handle, - plugins_url( $style_path, $metadata['file'] ), - array(), - filemtime( realpath( "$block_dir/$style_path" ) ) - ); - return $result ? $style_handle : false; - } - - /** - * Registers a block type from metadata stored in the `block.json` file. - * - * @since 7.9.0 - * - * @param string $file_or_folder Path to the JSON file with metadata definition for - * the block or path to the folder where the `block.json` file is located. - * @param array $args { - * Optional. Array of block type arguments. Any arguments may be defined, however the - * ones described below are supported by default. Default empty array. - * - * @type callable $render_callback Callback used to render blocks of this block type. - * } - * @return WP_Block_Type|false The registered block type on success, or false on failure. - */ - function register_block_type_from_metadata( $file_or_folder, $args = array() ) { - $filename = 'block.json'; - $metadata_file = ( substr( $file_or_folder, -strlen( $filename ) ) !== $filename ) ? - trailingslashit( $file_or_folder ) . $filename : - $file_or_folder; - if ( ! file_exists( $metadata_file ) ) { - return false; - } - - $metadata = json_decode( file_get_contents( $metadata_file ), true ); - if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) { - return false; - } - $metadata['file'] = $metadata_file; - - $settings = array(); - $property_mappings = array( - 'title' => 'title', - 'category' => 'category', - 'parent' => 'parent', - 'icon' => 'icon', - 'description' => 'description', - 'keywords' => 'keywords', - 'attributes' => 'attributes', - 'providesContext' => 'provides_context', - 'usesContext' => 'uses_context', - // Deprecated: remove with Gutenberg 8.6 release. - 'context' => 'context', - 'supports' => 'supports', - 'styles' => 'styles', - 'example' => 'example', - ); - - foreach ( $property_mappings as $key => $mapped_key ) { - if ( isset( $metadata[ $key ] ) ) { - $settings[ $mapped_key ] = $metadata[ $key ]; - } - } - - if ( ! empty( $metadata['editorScript'] ) ) { - $settings['editor_script'] = register_block_script_handle( - $metadata, - 'editorScript' - ); - } - - if ( ! empty( $metadata['script'] ) ) { - $settings['script'] = register_block_script_handle( - $metadata, - 'script' - ); - } - - if ( ! empty( $metadata['editorStyle'] ) ) { - $settings['editor_style'] = register_block_style_handle( - $metadata, - 'editorStyle' - ); - } - - if ( ! empty( $metadata['style'] ) ) { - $settings['style'] = register_block_style_handle( - $metadata, - 'style' - ); - } - - return register_block_type( - $metadata['name'], - array_merge( - $settings, - $args - ) - ); - } -} - /** * Adds a wp.date.setSettings with timezone abbr parameter * @@ -305,112 +80,46 @@ function gutenberg_add_date_settings_timezone( $scripts ) { add_action( 'wp_default_scripts', 'gutenberg_add_date_settings_timezone', 20 ); /** - * Filters default block categories to substitute legacy category names with new - * block categories. - * - * This can be removed when plugin support requires WordPress 5.5.0+. + * Determine if the current theme needs to load separate block styles or not. * - * @see https://core.trac.wordpress.org/ticket/50278 - * @see https://core.trac.wordpress.org/changeset/48177 - * - * @param array[] $default_categories Array of block categories. - * - * @return array[] Filtered block categories. + * @return bool */ -function gutenberg_replace_default_block_categories( $default_categories ) { - $substitution = array( - 'common' => array( - 'slug' => 'text', - 'title' => __( 'Text', 'gutenberg' ), - 'icon' => null, - ), - 'formatting' => array( - 'slug' => 'media', - 'title' => __( 'Media', 'gutenberg' ), - 'icon' => null, - ), - 'layout' => array( - 'slug' => 'design', - 'title' => __( 'Design', 'gutenberg' ), - 'icon' => null, - ), - ); - - // Loop default categories to perform in-place substitution by legacy slug. - foreach ( $default_categories as $i => $default_category ) { - $slug = $default_category['slug']; - if ( isset( $substitution[ $slug ] ) ) { - $default_categories[ $i ] = $substitution[ $slug ]; - unset( $substitution[ $slug ] ); - } - } - - /* - * At this point, `$substitution` should contain only the categories which - * could not be in-place substituted with a default category, likely in the - * case that core has since been updated to use the default categories. - * Check to verify they exist. - */ - $default_category_slugs = wp_list_pluck( $default_categories, 'slug' ); - foreach ( $substitution as $i => $substitute_category ) { - if ( in_array( $substitute_category['slug'], $default_category_slugs, true ) ) { - unset( $substitution[ $i ] ); - } - } - - /* - * Any substitutes remaining should be appended, as they are not yet - * assigned in the default categories array. +function gutenberg_should_load_separate_block_styles() { + $load_separate_styles = gutenberg_is_fse_theme(); + /** + * Determine if separate styles will be loaded for blocks on-render or not. + * + * @param bool $load_separate_styles Whether separate styles will be loaded or not. + * + * @return bool */ - return array_merge( $default_categories, array_values( $substitution ) ); + return apply_filters( 'load_separate_block_styles', $load_separate_styles ); } -add_filter( 'block_categories', 'gutenberg_replace_default_block_categories' ); /** - * Shim that hooks into `pre_render_block` so as to override `render_block` with - * a function that assigns block context. + * Remove the `wp_enqueue_registered_block_scripts_and_styles` hook if needed. * - * The context handling can be removed when plugin support requires WordPress 5.5.0+. - * - * @see https://core.trac.wordpress.org/ticket/49927 - * @see https://core.trac.wordpress.org/changeset/48243 - * - * @param string|null $pre_render The pre-rendered content. Defaults to null. - * @param array $parsed_block The parsed block being rendered. - * - * @return string String of rendered HTML. + * @return void */ -function gutenberg_render_block_with_assigned_block_context( $pre_render, $parsed_block ) { - $already_supports_context = version_compare( get_bloginfo( 'version' ), '5.5', '>=' ); - - /* - * If a non-null value is provided, a filter has run at an earlier priority - * and has already handled custom rendering and should take precedence. - */ - if ( null !== $pre_render || $already_supports_context ) { - return $pre_render; +function gutenberg_remove_hook_wp_enqueue_registered_block_scripts_and_styles() { + if ( gutenberg_should_load_separate_block_styles() ) { + /** + * Avoid enqueueing block assets of all registered blocks for all posts, instead + * deferring to block render mechanics to enqueue scripts, thereby ensuring only + * blocks of the content have their assets enqueued. + * + * This can be removed once minimum support for the plugin is outside the range + * of the version associated with closure of the following ticket. + * + * @see https://core.trac.wordpress.org/ticket/50328 + * + * @see WP_Block::render + */ + remove_action( 'enqueue_block_assets', 'wp_enqueue_registered_block_scripts_and_styles' ); } - - $source_block = $parsed_block; - - /** This filter is documented in src/wp-includes/blocks.php */ - $parsed_block = apply_filters( 'render_block_data', $parsed_block, $source_block ); - - $context = array(); - - /** - * Filters the default context provided to a rendered block. - * - * @param array $context Default context. - * @param array $parsed_block Block being rendered, filtered by `render_block_data`. - */ - $context = apply_filters( 'render_block_context', $context, $parsed_block ); - - $block = new WP_Block( $parsed_block, $context ); - - return $block->render(); } -add_filter( 'pre_render_block', 'gutenberg_render_block_with_assigned_block_context', 9, 2 ); + +add_action( 'init', 'gutenberg_remove_hook_wp_enqueue_registered_block_scripts_and_styles' ); /** * Callback hooked to the register_block_type_args filter. diff --git a/lib/edit-site-page.php b/lib/edit-site-page.php index 891a39bf47178b..d73ad831a5233a 100644 --- a/lib/edit-site-page.php +++ b/lib/edit-site-page.php @@ -104,11 +104,13 @@ function gutenberg_edit_site_init( $hook ) { $settings = array_merge( gutenberg_get_common_block_editor_settings(), array( - 'alignWide' => get_theme_support( 'align-wide' ), - 'siteUrl' => site_url(), - 'postsPerPage' => get_option( 'posts_per_page' ), - 'styles' => gutenberg_get_editor_styles(), - 'defaultTemplateTypes' => gutenberg_get_indexed_default_template_types(), + 'alignWide' => get_theme_support( 'align-wide' ), + 'siteUrl' => site_url(), + 'postsPerPage' => get_option( 'posts_per_page' ), + 'styles' => gutenberg_get_editor_styles(), + 'defaultTemplateTypes' => gutenberg_get_indexed_default_template_types(), + '__experimentalBlockPatterns' => WP_Block_Patterns_Registry::get_instance()->get_all_registered(), + '__experimentalBlockPatternCategories' => WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(), ) ); $settings = gutenberg_experimental_global_styles_settings( $settings ); @@ -182,7 +184,7 @@ function gutenberg_edit_site_init( $hook ) { */ function register_site_editor_homepage_settings() { register_setting( - 'general', + 'reading', 'show_on_front', array( 'show_in_rest' => true, @@ -192,7 +194,7 @@ function register_site_editor_homepage_settings() { ); register_setting( - 'general', + 'reading', 'page_on_front', array( 'show_in_rest' => true, diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json index 7b35275d9af036..15904819cfd7f5 100644 --- a/lib/experimental-default-theme.json +++ b/lib/experimental-default-theme.json @@ -134,6 +134,10 @@ "dropCap": true, "customFontSize": true, "customLineHeight": false, + "customFontStyle": true, + "customFontWeight": true, + "customTextTransforms": true, + "customTextDecorations": true, "fontSizes": [ { "name": "Small", @@ -160,85 +164,14 @@ "slug": "huge", "size": "42px" } - ], - "fontStyles": [ - { - "name": "Regular", - "slug": "normal" - }, - { - "name": "Italic", - "slug": "italic" - } - ], - "fontWeights": [ - { - "name": "Ultralight", - "slug": "100" - }, - { - "name": "Thin", - "slug": "200" - }, - { - "name": "Light", - "slug": "300" - }, - { - "name": "Regular", - "slug": "400" - }, - { - "name": "Medium", - "slug": "500" - }, - { - "name": "Semibold", - "slug": "600" - }, - { - "name": "Bold", - "slug": "700" - }, - { - "name": "Heavy", - "slug": "800" - }, - { - "name": "Black", - "slug": "900" - } - ], - "textTransforms": [ - { - "name": "AB", - "slug": "uppercase" - }, - { - "name": "ab", - "slug": "lowercase" - }, - { - "name": "Ab", - "slug": "capitalize" - } - ], - "textDecorations": [ - { - "name": "Underline", - "slug": "underline", - "value": "underline" - }, - { - "name": "Strikethrough", - "slug": "strikethrough", - "value": "line-through" - } ] }, "spacing": { "customPadding": false, "units": [ "px", "em", "rem", "vh", "vw" ] + }, + "border": { + "customRadius": true } } } diff --git a/lib/full-site-editing/default-template-types.php b/lib/full-site-editing/default-template-types.php index df70c1bd15865d..54cc722be96f37 100644 --- a/lib/full-site-editing/default-template-types.php +++ b/lib/full-site-editing/default-template-types.php @@ -14,68 +14,72 @@ function gutenberg_get_default_template_types() { $default_template_types = array( 'index' => array( - 'title' => _x( 'Default (Index)', 'Template name', 'gutenberg' ), - 'description' => __( 'Main template, applied when no other template is found', 'gutenberg' ), + 'title' => _x( 'Index', 'Template name', 'gutenberg' ), + 'description' => __( 'The default template which is used when no other template can be found', 'gutenberg' ), ), 'home' => array( 'title' => _x( 'Home', 'Template name', 'gutenberg' ), - 'description' => __( 'Template for the latest blog posts', 'gutenberg' ), + 'description' => __( 'The home page template, which is the front page by default. If you use a static front page this is the template for the page with the latest posts', 'gutenberg' ), ), 'front-page' => array( 'title' => _x( 'Front Page', 'Template name', 'gutenberg' ), - 'description' => __( 'Front page template, whether it displays the blog posts index or a static page', 'gutenberg' ), + 'description' => __( 'Used when the site home page is queried', 'gutenberg' ), ), 'singular' => array( - 'title' => _x( 'Default Singular', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays any content on a single page', 'gutenberg' ), + 'title' => _x( 'Singular', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a single entry is queried. This template will be overridden the Single, Post, and Page templates where appropriate', 'gutenberg' ), ), 'single' => array( - 'title' => _x( 'Default Single', 'Template name', 'gutenberg' ), - 'description' => __( 'Applied to individual content like a blog post', 'gutenberg' ), + 'title' => _x( 'Single', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a single entry that is not a Page is queried', 'gutenberg' ), + ), + 'single-post' => array( + 'title' => _x( 'Post', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a single Post is queried', 'gutenberg' ), ), 'page' => array( - 'title' => _x( 'Default Page', 'Template name', 'gutenberg' ), - 'description' => __( 'Applied to individual pages', 'gutenberg' ), + 'title' => _x( 'Page', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when an individual Page is queried', 'gutenberg' ), ), 'archive' => array( - 'title' => _x( 'Default Archive', 'Template name', 'gutenberg' ), - 'description' => __( 'Applied to archives like your posts page, categories, or tags', 'gutenberg' ), + 'title' => _x( 'Archive', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when multiple entries are queried. This template will be overridden the Category, Author, and Date templates where appropriate', 'gutenberg' ), ), 'author' => array( - 'title' => _x( 'Default Author Archive', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays a list of posts by a single author', 'gutenberg' ), + 'title' => _x( 'Author Archive', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a list of Posts from a single author is queried', 'gutenberg' ), ), 'category' => array( - 'title' => _x( 'Default Post Category Archive', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays a list of posts in a category', 'gutenberg' ), + 'title' => _x( 'Post Category Archive', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a list of Posts from a category is queried', 'gutenberg' ), ), 'taxonomy' => array( - 'title' => _x( 'Default Taxonomy Archive', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays a list of posts in a taxonomy', 'gutenberg' ), + 'title' => _x( 'Taxonomy Archive', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a list of posts from a taxonomy is queried', 'gutenberg' ), ), 'date' => array( - 'title' => _x( 'Default Date Archive', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays a list of posts in a date range', 'gutenberg' ), + 'title' => _x( 'Date Archive', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a list of Posts from a certain date are queried', 'gutenberg' ), ), 'tag' => array( - 'title' => _x( 'Default Tag Archive', 'Template name', 'gutenberg' ), - 'description' => __( 'Displays a list of posts with a tag', 'gutenberg' ), + 'title' => _x( 'Tag Archive', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a list of Posts with a certain tag is queried', 'gutenberg' ), ), 'attachment' => array( 'title' => __( 'Media', 'gutenberg' ), - 'description' => __( 'Displays media content', 'gutenberg' ), + 'description' => __( 'Used when a Media entry is queried', 'gutenberg' ), ), 'search' => array( - 'title' => __( 'Search Results', 'gutenberg' ), - 'description' => __( 'Applied to search results', 'gutenberg' ), + 'title' => _x( 'Search Results', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when a visitor searches the site', 'gutenberg' ), ), 'privacy-policy' => array( 'title' => __( 'Privacy Policy', 'gutenberg' ), 'description' => '', ), '404' => array( - 'title' => _x( '404 (Not Found)', 'Template name', 'gutenberg' ), - 'description' => __( 'Applied when content cannot be found', 'gutenberg' ), + 'title' => _x( '404', 'Template name', 'gutenberg' ), + 'description' => __( 'Used when the queried content cannot be found', 'gutenberg' ), ), ); diff --git a/lib/full-site-editing/page-templates.php b/lib/full-site-editing/page-templates.php new file mode 100644 index 00000000000000..89ce31dba143f0 --- /dev/null +++ b/lib/full-site-editing/page-templates.php @@ -0,0 +1,42 @@ + $page_template ) { + if ( ( ! isset( $page_template['postTypes'] ) && 'page' === $post->post_type ) || + ( isset( $page_template['postTypes'] ) && in_array( $post->post_type, $page_template['postTypes'], true ) ) + ) { + $page_templates[ $key ] = $page_template['title']; + } + } + } + + return $page_templates; +} +add_filter( 'theme_post_templates', 'gutenberg_load_block_page_templates', 10, 3 ); +add_filter( 'theme_page_templates', 'gutenberg_load_block_page_templates', 10, 3 ); diff --git a/lib/full-site-editing/templates-utils.php b/lib/full-site-editing/templates-utils.php new file mode 100644 index 00000000000000..5430470cfa640a --- /dev/null +++ b/lib/full-site-editing/templates-utils.php @@ -0,0 +1,121 @@ +post_name ); + return; + } + + if ( 'description' === $column_name && has_excerpt( $post_id ) ) { + the_excerpt( $post_id ); + return; + } + + if ( 'status' === $column_name ) { + $post_status = get_post_status( $post_id ); + // The auto-draft status doesn't have localized labels. + if ( 'auto-draft' === $post_status ) { + echo esc_html_x( 'Auto-Draft', 'Post status', 'gutenberg' ); + return; + } + $post_status_object = get_post_status_object( $post_status ); + echo esc_html( $post_status_object->label ); + return; + } + + if ( 'theme' === $column_name ) { + $terms = get_the_terms( $post_id, 'wp_theme' ); + if ( empty( $terms ) || is_wp_error( $terms ) ) { + return; + } + $themes = array(); + $is_file_based = false; + foreach ( $terms as $term ) { + if ( '_wp_file_based' === $term->slug ) { + $is_file_based = true; + } else { + $themes[] = esc_html( wp_get_theme( $term->slug ) ); + } + } + echo implode( '
', $themes ); + if ( $is_file_based ) { + echo '
' . __( '(Created from a template file)', 'gutenberg' ); + } + return; + } +} + +/** + * Adds the auto-draft view to the templates and template parts admin lists. + * + * @param array $views The edit views to filter. + */ +function gutenberg_filter_templates_edit_views( $views ) { + $post_type = get_current_screen()->post_type; + $url = add_query_arg( + array( + 'post_type' => $post_type, + 'post_status' => 'auto-draft', + ), + 'edit.php' + ); + $is_auto_draft_view = isset( $_REQUEST['post_status'] ) && 'auto-draft' === $_REQUEST['post_status']; + $class_html = $is_auto_draft_view ? ' class="current"' : ''; + $aria_current = $is_auto_draft_view ? ' aria-current="page"' : ''; + $post_count = wp_count_posts( $post_type, 'readable' ); + $label = sprintf( + // The auto-draft status doesn't have localized labels. + translate_nooped_plural( + /* translators: %s: Number of auto-draft posts. */ + _nx_noop( + 'Auto-Draft (%s)', + 'Auto-Drafts (%s)', + 'Post status', + 'gutenberg' + ), + $post_count->{'auto-draft'} + ), + number_format_i18n( $post_count->{'auto-draft'} ) + ); + + $auto_draft_view = sprintf( + '%s', + esc_url( $url ), + $class_html, + $aria_current, + $label + ); + + array_splice( $views, 1, 0, array( 'auto-draft' => $auto_draft_view ) ); + + return $views; +} diff --git a/lib/global-styles.php b/lib/global-styles.php index 9ec1d698da1ff9..800048bdcb219f 100644 --- a/lib/global-styles.php +++ b/lib/global-styles.php @@ -14,218 +14,6 @@ function gutenberg_experimental_global_styles_has_theme_json_support() { return is_readable( locate_template( 'experimental-theme.json' ) ); } -/** - * Processes a file that adheres to the theme.json - * schema and returns an array with its contents, - * or a void array if none found. - * - * @param string $file_path Path to file. - * @return array Contents that adhere to the theme.json schema. - */ -function gutenberg_experimental_global_styles_get_from_file( $file_path ) { - $config = array(); - if ( file_exists( $file_path ) ) { - $decoded_file = json_decode( - file_get_contents( $file_path ), - true - ); - - $json_decoding_error = json_last_error(); - if ( JSON_ERROR_NONE !== $json_decoding_error ) { - error_log( 'Error when decoding file schema: ' . json_last_error_msg() ); - return $config; - } - - if ( is_array( $decoded_file ) ) { - $config = $decoded_file; - } - } - return $config; -} - -/** - * Returns the user's origin config. - * - * @return WP_Theme_JSON Entity that holds user data. - */ -function gutenberg_experimental_global_styles_get_user() { - $config = array(); - $user_cpt = gutenberg_experimental_global_styles_get_user_cpt(); - if ( array_key_exists( 'post_content', $user_cpt ) ) { - $decoded_data = json_decode( $user_cpt['post_content'], true ); - - $json_decoding_error = json_last_error(); - if ( JSON_ERROR_NONE !== $json_decoding_error ) { - error_log( 'Error when decoding user schema: ' . json_last_error_msg() ); - return $config; - } - - if ( is_array( $decoded_data ) ) { - $config = $decoded_data; - } - } - - return new WP_Theme_JSON( $config ); -} - -/** - * Returns the CPT that contains the user's origin config - * for the current theme or a void array if none found. - * - * It can also create and return a new draft CPT. - * - * @param bool $should_create_cpt Whether a new CPT should be created if no one was found. - * False by default. - * @param array $post_status_filter Filter CPT by post status. - * ['publish'] by default, so it only fetches published posts. - * @return array Custom Post Type for the user's origin config. - */ -function gutenberg_experimental_global_styles_get_user_cpt( $should_create_cpt = false, $post_status_filter = array( 'publish' ) ) { - $user_cpt = array(); - $post_type_filter = 'wp_global_styles'; - $post_name_filter = 'wp-global-styles-' . urlencode( wp_get_theme()->get_stylesheet() ); - $recent_posts = wp_get_recent_posts( - array( - 'numberposts' => 1, - 'orderby' => 'date', - 'order' => 'desc', - 'post_type' => $post_type_filter, - 'post_status' => $post_status_filter, - 'name' => $post_name_filter, - ) - ); - - if ( is_array( $recent_posts ) && ( count( $recent_posts ) === 1 ) ) { - $user_cpt = $recent_posts[0]; - } elseif ( $should_create_cpt ) { - $cpt_post_id = wp_insert_post( - array( - 'post_content' => '{}', - 'post_status' => 'publish', - 'post_type' => $post_type_filter, - 'post_name' => $post_name_filter, - ), - true - ); - $user_cpt = get_post( $cpt_post_id, ARRAY_A ); - } - - return $user_cpt; -} - -/** - * Returns the post ID of the CPT containing the user's origin config. - * - * @return integer - */ -function gutenberg_experimental_global_styles_get_user_cpt_id() { - $user_cpt_id = null; - $user_cpt = gutenberg_experimental_global_styles_get_user_cpt( true ); - if ( array_key_exists( 'ID', $user_cpt ) ) { - $user_cpt_id = $user_cpt['ID']; - } - return $user_cpt_id; -} - -/** - * Return core's origin config. - * - * @return WP_Theme_JSON Entity that holds core data. - */ -function gutenberg_experimental_global_styles_get_core() { - $config = gutenberg_experimental_global_styles_get_from_file( - __DIR__ . '/experimental-default-theme.json' - ); - - // Start i18n logic to remove when JSON i18 strings are extracted. - $default_colors_i18n = array( - 'black' => __( 'Black', 'gutenberg' ), - 'cyan-bluish-gray' => __( 'Cyan bluish gray', 'gutenberg' ), - 'white' => __( 'White', 'gutenberg' ), - 'pale-pink' => __( 'Pale pink', 'gutenberg' ), - 'vivid-red' => __( 'Vivid red', 'gutenberg' ), - 'luminous-vivid-orange' => __( 'Luminous vivid orange', 'gutenberg' ), - 'luminous-vivid-amber' => __( 'Luminous vivid amber', 'gutenberg' ), - 'light-green-cyan' => __( 'Light green cyan', 'gutenberg' ), - 'vivid-green-cyan' => __( 'Vivid green cyan', 'gutenberg' ), - 'pale-cyan-blue' => __( 'Pale cyan blue', 'gutenberg' ), - 'vivid-cyan-blue' => __( 'Vivid cyan blue', 'gutenberg' ), - 'vivid-purple' => __( 'Vivid purple', 'gutenberg' ), - ); - if ( ! empty( $config['global']['settings']['color']['palette'] ) ) { - foreach ( $config['global']['settings']['color']['palette'] as &$color ) { - $color['name'] = $default_colors_i18n[ $color['slug'] ]; - } - } - - $default_gradients_i18n = array( - 'vivid-cyan-blue-to-vivid-purple' => __( 'Vivid cyan blue to vivid purple', 'gutenberg' ), - 'light-green-cyan-to-vivid-green-cyan' => __( 'Light green cyan to vivid green cyan', 'gutenberg' ), - 'luminous-vivid-amber-to-luminous-vivid-orange' => __( 'Luminous vivid amber to luminous vivid orange', 'gutenberg' ), - 'luminous-vivid-orange-to-vivid-red' => __( 'Luminous vivid orange to vivid red', 'gutenberg' ), - 'very-light-gray-to-cyan-bluish-gray' => __( 'Very light gray to cyan bluish gray', 'gutenberg' ), - 'cool-to-warm-spectrum' => __( 'Cool to warm spectrum', 'gutenberg' ), - 'blush-light-purple' => __( 'Blush light purple', 'gutenberg' ), - 'blush-bordeaux' => __( 'Blush bordeaux', 'gutenberg' ), - 'luminous-dusk' => __( 'Luminous dusk', 'gutenberg' ), - 'pale-ocean' => __( 'Pale ocean', 'gutenberg' ), - 'electric-grass' => __( 'Electric grass', 'gutenberg' ), - 'midnight' => __( 'Midnight', 'gutenberg' ), - ); - if ( ! empty( $config['global']['settings']['color']['gradients'] ) ) { - foreach ( $config['global']['settings']['color']['gradients'] as &$gradient ) { - $gradient['name'] = $default_gradients_i18n[ $gradient['slug'] ]; - } - } - - $default_font_sizes_i18n = array( - 'small' => __( 'Small', 'gutenberg' ), - 'normal' => __( 'Normal', 'gutenberg' ), - 'medium' => __( 'Medium', 'gutenberg' ), - 'large' => __( 'Large', 'gutenberg' ), - 'huge' => __( 'Huge', 'gutenberg' ), - ); - if ( ! empty( $config['global']['settings']['typography']['fontSizes'] ) ) { - foreach ( $config['global']['settings']['typography']['fontSizes'] as &$font_size ) { - $font_size['name'] = $default_font_sizes_i18n[ $font_size['slug'] ]; - } - } - - $default_font_styles_i18n = array( - 'normal' => __( 'Regular', 'gutenberg' ), - 'italic' => __( 'Italic', 'gutenberg' ), - 'initial' => __( 'Initial', 'gutenberg' ), - 'inherit' => __( 'Inherit', 'gutenberg' ), - ); - if ( ! empty( $config['global']['settings']['typography']['fontStyles'] ) ) { - foreach ( $config['global']['settings']['typography']['fontStyles'] as &$font_style ) { - $font_style['name'] = $default_font_styles_i18n[ $font_style['slug'] ]; - } - } - - $default_font_weights_i18n = array( - '100' => __( 'Ultralight', 'gutenberg' ), - '200' => __( 'Thin', 'gutenberg' ), - '300' => __( 'Light', 'gutenberg' ), - '400' => __( 'Regular', 'gutenberg' ), - '500' => __( 'Medium', 'gutenberg' ), - '600' => __( 'Semibold', 'gutenberg' ), - '700' => __( 'Bold', 'gutenberg' ), - '800' => __( 'Heavy', 'gutenberg' ), - '900' => __( 'Black', 'gutenberg' ), - 'initial' => __( 'Initial', 'gutenberg' ), - 'inherit' => __( 'Inherit', 'gutenberg' ), - ); - if ( ! empty( $config['global']['settings']['typography']['fontWeights'] ) ) { - foreach ( $config['global']['settings']['typography']['fontWeights'] as &$font_weight ) { - $font_weight['name'] = $default_font_weights_i18n[ $font_weight['slug'] ]; - } - } - // End i18n logic to remove when JSON i18 strings are extracted. - - return new WP_Theme_JSON( $config ); -} - /** * Returns the theme presets registered via add_theme_support, if any. * @@ -322,43 +110,19 @@ function gutenberg_experimental_global_styles_get_theme_support_settings( $setti return $theme_settings; } -/** - * Returns the theme's origin config. - * - * It also fetches the existing presets the theme declared via add_theme_support - * and uses them if the theme hasn't declared any via theme.json. - * - * @param array $settings Existing editor settings. - * - * @return WP_Theme_JSON Entity that holds theme data. - */ -function gutenberg_experimental_global_styles_get_theme( $settings ) { - $theme_support_data = gutenberg_experimental_global_styles_get_theme_support_settings( $settings ); - $theme_json_data = gutenberg_experimental_global_styles_get_from_file( - locate_template( 'experimental-theme.json' ) - ); - - /* - * We want the presets and settings declared in theme.json - * to override the ones declared via add_theme_support. - */ - $result = new WP_Theme_JSON( $theme_support_data ); - $result->merge( new WP_Theme_JSON( $theme_json_data ) ); - - return $result; -} - /** * Takes a tree adhering to the theme.json schema and generates * the corresponding stylesheet. * * @param WP_Theme_JSON $tree Input tree. + * @param string $type Type of stylesheet we want accepts 'all', 'block_styles', and 'css_variables'. * * @return string Stylesheet. */ -function gutenberg_experimental_global_styles_get_stylesheet( $tree ) { +function gutenberg_experimental_global_styles_get_stylesheet( $tree, $type = 'all' ) { // Check if we can use cached. $can_use_cached = ( + ( 'all' === $type ) && ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) && ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) && ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && @@ -373,9 +137,9 @@ function gutenberg_experimental_global_styles_get_stylesheet( $tree ) { } } - $stylesheet = $tree->get_stylesheet(); + $stylesheet = $tree->get_stylesheet( $type ); - if ( gutenberg_experimental_global_styles_has_theme_json_support() ) { + if ( ( 'all' === $type || 'block_styles' === $type ) && gutenberg_experimental_global_styles_has_theme_json_support() ) { // To support all themes, we added in the block-library stylesheet // a style rule such as .has-link-color a { color: var(--wp--style--color--link, #00e); } // so that existing link colors themes used didn't break. @@ -386,7 +150,7 @@ function gutenberg_experimental_global_styles_get_stylesheet( $tree ) { if ( $can_use_cached ) { // Cache for a minute. - // This cache doesn't need to be any longer, we only want to avoid spikes on high-trafic sites. + // This cache doesn't need to be any longer, we only want to avoid spikes on high-traffic sites. set_transient( 'global_styles', $stylesheet, MINUTE_IN_SECONDS ); } @@ -398,10 +162,15 @@ function gutenberg_experimental_global_styles_get_stylesheet( $tree ) { * and enqueues the resulting stylesheet. */ function gutenberg_experimental_global_styles_enqueue_assets() { - $settings = gutenberg_get_common_block_editor_settings(); - $all = gutenberg_experimental_global_styles_get_core(); - $all->merge( gutenberg_experimental_global_styles_get_theme( $settings ) ); - $all->merge( gutenberg_experimental_global_styles_get_user() ); + if ( ! gutenberg_experimental_global_styles_has_theme_json_support() ) { + return; + } + + $settings = gutenberg_get_common_block_editor_settings(); + $theme_support_data = gutenberg_experimental_global_styles_get_theme_support_settings( $settings ); + + $resolver = new WP_Theme_JSON_Resolver(); + $all = $resolver->get_origin( $theme_support_data ); $stylesheet = gutenberg_experimental_global_styles_get_stylesheet( $all ); if ( empty( $stylesheet ) ) { @@ -420,20 +189,7 @@ function gutenberg_experimental_global_styles_enqueue_assets() { * @return array New block editor settings */ function gutenberg_experimental_global_styles_settings( $settings ) { - $base = gutenberg_experimental_global_styles_get_core(); - $all = gutenberg_experimental_global_styles_get_core(); - $theme = gutenberg_experimental_global_styles_get_theme( $settings ); - $user = gutenberg_experimental_global_styles_get_user(); - - $base->merge( $theme ); - - $all->merge( $theme ); - $all->merge( $user ); - - // STEP 1: ADD FEATURES - // These need to be added to settings always. - // We also need to unset the deprecated settings defined by core. - $settings['__experimentalFeatures'] = $all->get_settings(); + $theme_support_data = gutenberg_experimental_global_styles_get_theme_support_settings( $settings ); unset( $settings['colors'] ); unset( $settings['disableCustomColors'] ); unset( $settings['disableCustomFontSizes'] ); @@ -443,65 +199,75 @@ function gutenberg_experimental_global_styles_settings( $settings ) { unset( $settings['fontSizes'] ); unset( $settings['gradients'] ); + $resolver = new WP_Theme_JSON_Resolver(); + $origin = 'theme'; + if ( + gutenberg_experimental_global_styles_has_theme_json_support() && + gutenberg_is_fse_theme() + ) { + // Only lookup for the user data if we need it. + $origin = 'user'; + } + $tree = $resolver->get_origin( $theme_support_data, $origin ); + + // STEP 1: ADD FEATURES + // + // These need to be always added to the editor settings, + // even for themes that don't support theme.json. + // An example of this is that the presets are configured + // from the theme support data. + $settings['__experimentalFeatures'] = $tree->get_settings(); + // STEP 2 - IF EDIT-SITE, ADD DATA REQUIRED FOR GLOBAL STYLES SIDEBAR - // The client needs some information to be able to access/update the user styles. - // We only do this if the theme has support for theme.json, though, - // as an indicator that the theme will know how to combine this with its stylesheet. + // + // In the site editor, the user can change styles, so the client + // needs the ability to create them. Hence, we pass it some data + // for this: base styles (core+theme) and the ID of the user CPT. $screen = get_current_screen(); if ( ! empty( $screen ) && function_exists( 'gutenberg_is_edit_site_page' ) && gutenberg_is_edit_site_page( $screen->id ) && - gutenberg_experimental_global_styles_has_theme_json_support() + gutenberg_experimental_global_styles_has_theme_json_support() && + gutenberg_is_fse_theme() ) { - $settings['__experimentalGlobalStylesUserEntityId'] = gutenberg_experimental_global_styles_get_user_cpt_id(); - $settings['__experimentalGlobalStylesBaseStyles'] = $base->get_raw_data(); - } else { - // STEP 3 - OTHERWISE, ADD STYLES + $user_cpt_id = WP_Theme_JSON_Resolver::get_user_custom_post_type_id(); + $base_styles = $resolver->get_origin( $theme_support_data, 'theme' )->get_raw_data(); + + $settings['__experimentalGlobalStylesUserEntityId'] = $user_cpt_id; + $settings['__experimentalGlobalStylesBaseStyles'] = $base_styles; + } elseif ( gutenberg_experimental_global_styles_has_theme_json_support() ) { + // STEP 3 - ADD STYLES IF THEME HAS SUPPORT // // If we are in a block editor context, but not in edit-site, - // we need to add the styles via the settings. This is because - // we want them processed as if they were added via add_editor_styles, - // which adds the editor wrapper class. - $settings['styles'][] = array( 'css' => gutenberg_experimental_global_styles_get_stylesheet( $all ) ); + // we add the styles via the settings, so the editor knows that + // some of these should be added the wrapper class, + // as if they were added via add_editor_styles. + $settings['styles'][] = array( + 'css' => gutenberg_experimental_global_styles_get_stylesheet( $tree, 'css_variables' ), + '__experimentalNoWrapper' => true, + ); + $settings['styles'][] = array( + 'css' => gutenberg_experimental_global_styles_get_stylesheet( $tree, 'block_styles' ), + ); } return $settings; } /** - * Registers a Custom Post Type to store the user's origin config. + * Register CPT to store/access user data. + * + * @return array|undefined */ -function gutenberg_experimental_global_styles_register_cpt() { +function gutenberg_experimental_global_styles_register_user_cpt() { if ( ! gutenberg_experimental_global_styles_has_theme_json_support() ) { return; } - $args = array( - 'label' => __( 'Global Styles', 'gutenberg' ), - 'description' => 'CPT to store user design tokens', - 'public' => false, - 'show_ui' => false, - 'show_in_rest' => true, - 'rest_base' => '__experimental/global-styles', - 'capabilities' => array( - 'read' => 'edit_theme_options', - 'create_posts' => 'edit_theme_options', - 'edit_posts' => 'edit_theme_options', - 'edit_published_posts' => 'edit_theme_options', - 'delete_published_posts' => 'edit_theme_options', - 'edit_others_posts' => 'edit_theme_options', - 'delete_others_posts' => 'edit_theme_options', - ), - 'map_meta_cap' => true, - 'supports' => array( - 'editor', - 'revisions', - ), - ); - register_post_type( 'wp_global_styles', $args ); + WP_Theme_JSON_Resolver::register_user_custom_post_type(); } -add_action( 'init', 'gutenberg_experimental_global_styles_register_cpt' ); +add_action( 'init', 'gutenberg_experimental_global_styles_register_user_cpt' ); add_filter( 'block_editor_settings', 'gutenberg_experimental_global_styles_settings', PHP_INT_MAX ); add_action( 'wp_enqueue_scripts', 'gutenberg_experimental_global_styles_enqueue_assets' ); diff --git a/lib/load.php b/lib/load.php index d8b89f7ba0200c..02b54459609dd6 100644 --- a/lib/load.php +++ b/lib/load.php @@ -38,12 +38,6 @@ function gutenberg_is_experiment_enabled( $name ) { if ( ! class_exists( 'WP_REST_Widgets_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-widgets-controller.php'; } - if ( ! class_exists( 'WP_REST_Block_Directory_Controller' ) ) { - require __DIR__ . '/class-wp-rest-block-directory-controller.php'; - } - if ( ! class_exists( 'WP_REST_Block_Types_Controller' ) ) { - require __DIR__ . '/class-wp-rest-block-types-controller.php'; - } if ( ! class_exists( 'WP_REST_Menus_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-menus-controller.php'; } @@ -56,12 +50,6 @@ function gutenberg_is_experiment_enabled( $name ) { if ( ! class_exists( 'WP_Rest_Customizer_Nonces' ) ) { require_once __DIR__ . '/class-wp-rest-customizer-nonces.php'; } - if ( ! class_exists( 'WP_REST_Image_Editor_Controller' ) ) { - require __DIR__ . '/class-wp-rest-image-editor-controller.php'; - } - if ( ! class_exists( 'WP_REST_Plugins_Controller' ) ) { - require_once __DIR__ . '/class-wp-rest-plugins-controller.php'; - } if ( ! class_exists( 'WP_REST_Post_Format_Search_Handler' ) ) { require_once __DIR__ . '/class-wp-rest-post-format-search-handler.php'; } @@ -78,22 +66,6 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/rest-api.php'; } -if ( ! class_exists( 'WP_Block_Patterns_Registry' ) ) { - require __DIR__ . '/class-wp-block-patterns-registry.php'; -} - -if ( ! class_exists( 'WP_Block_Pattern_Categories_Registry' ) ) { - require __DIR__ . '/class-wp-block-pattern-categories-registry.php'; -} - -if ( ! class_exists( 'WP_Block' ) ) { - require __DIR__ . '/class-wp-block.php'; -} - -if ( ! class_exists( 'WP_Block_List' ) ) { - require __DIR__ . '/class-wp-block-list.php'; -} - if ( ! class_exists( 'WP_Widget_Block' ) ) { require_once __DIR__ . '/class-wp-widget-block.php'; } @@ -106,6 +78,8 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/full-site-editing.php'; require __DIR__ . '/full-site-editing/default-template-types.php'; +require __DIR__ . '/full-site-editing/templates-utils.php'; +require __DIR__ . '/full-site-editing/page-templates.php'; require __DIR__ . '/templates-sync.php'; require __DIR__ . '/templates.php'; require __DIR__ . '/template-parts.php'; @@ -113,16 +87,15 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/edit-site-page.php'; require __DIR__ . '/edit-site-export.php'; -require __DIR__ . '/block-patterns.php'; require __DIR__ . '/blocks.php'; require __DIR__ . '/client-assets.php'; -require __DIR__ . '/block-directory.php'; require __DIR__ . '/demo.php'; require __DIR__ . '/widgets.php'; require __DIR__ . '/navigation.php'; require __DIR__ . '/navigation-page.php'; require __DIR__ . '/experiments-page.php'; require __DIR__ . '/class-wp-theme-json.php'; +require __DIR__ . '/class-wp-theme-json-resolver.php'; require __DIR__ . '/global-styles.php'; if ( ! class_exists( 'WP_Block_Supports' ) ) { @@ -133,3 +106,4 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/block-supports/align.php'; require __DIR__ . '/block-supports/typography.php'; require __DIR__ . '/block-supports/custom-classname.php'; +require __DIR__ . '/block-supports/border.php'; diff --git a/lib/patterns/heading-paragraph.php b/lib/patterns/heading-paragraph.php deleted file mode 100644 index 9a8b160a787727..00000000000000 --- a/lib/patterns/heading-paragraph.php +++ /dev/null @@ -1,14 +0,0 @@ - __( 'Heading and paragraph', 'gutenberg' ), - 'content' => "\n
\n

2.
" . __( 'Which treats of the first sally the ingenious Don Quixote made from home', 'gutenberg' ) . "

\n\n\n\n

" . __( 'These preliminaries settled, he did not care to put off any longer the execution of his design, urged on to it by the thought of all the world was losing by his delay, seeing what wrongs he intended to right, grievances to redress, injustices to repair, abuses to remove, and duties to discharge.', 'gutenberg' ) . "

\n
\n", - 'viewportWidth' => 1000, - 'categories' => array( 'text' ), - 'description' => _x( 'A heading preceded by a chapter number, and followed by a paragraph.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/large-header-button.php b/lib/patterns/large-header-button.php deleted file mode 100644 index 1d71e5703f5b5f..00000000000000 --- a/lib/patterns/large-header-button.php +++ /dev/null @@ -1,14 +0,0 @@ - __( 'Large header with a heading and a button ', 'gutenberg' ), - 'content' => "\n
\n
\n
\n
\n
\n\n\n\n
\n
\n\n\n\n

" . __( 'Thou hast seen', 'gutenberg' ) . '
' . __( 'nothing yet', 'gutenberg' ) . "

\n\n\n\n\n\n\n\n
\n
\n\n\n\n
\n
\n
\n
\n
\n", - 'viewportWidth' => 1000, - 'categories' => array( 'header' ), - 'description' => _x( 'A large hero section with a bright gradient background, a big heading and a filled button.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/large-header.php b/lib/patterns/large-header.php deleted file mode 100644 index 0213bf8bfeb724..00000000000000 --- a/lib/patterns/large-header.php +++ /dev/null @@ -1,14 +0,0 @@ - __( 'Large header with a heading', 'gutenberg' ), - 'content' => "\n
\n

" . __( 'Don Quixote', 'gutenberg' ) . "

\n
\n", - 'viewportWidth' => 1000, - 'categories' => array( 'header' ), - 'description' => _x( 'A large hero section with an example background image and a heading in the center.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/quote.php b/lib/patterns/quote.php deleted file mode 100644 index 0e1e0f3b248410..00000000000000 --- a/lib/patterns/quote.php +++ /dev/null @@ -1,14 +0,0 @@ - __( 'Quote', 'gutenberg' ), - 'content' => "\n
\n
\""
\n\n\n\n

" . __( '"Do you see over yonder, friend Sancho, thirty or forty hulking giants? I intend to do battle with them and slay them."', 'gutenberg' ) . '

' . __( '— Don Quixote', 'gutenberg' ) . "
\n\n\n\n
\n
\n", - 'viewportWidth' => 800, - 'categories' => array( 'text' ), - 'description' => _x( 'A quote and citation with an image above, and a separator at the bottom.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/text-three-columns-buttons.php b/lib/patterns/text-three-columns-buttons.php deleted file mode 100644 index 629c839b89bb88..00000000000000 --- a/lib/patterns/text-three-columns-buttons.php +++ /dev/null @@ -1,14 +0,0 @@ - __( 'Three columns of text with buttons', 'gutenberg' ), - 'categories' => array( 'columns' ), - 'content' => "\n
\n
\n
\n

" . __( 'Which treats of the character and pursuits of the famous Don Quixote of La Mancha.', 'gutenberg' ) . "

\n\n\n\n\n
\n\n\n\n
\n

" . __( 'Which treats of the first sally the ingenious Don Quixote made from home.', 'gutenberg' ) . "

\n\n\n\n\n
\n\n\n\n
\n

" . __( 'Wherein is related the droll way in which Don Quixote had himself dubbed a knight.', 'gutenberg' ) . "

\n\n\n\n\n
\n
\n
\n", - 'viewportWidth' => 1000, - 'description' => _x( 'Three small columns of text, each with an outlined button with rounded corners at the bottom.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/text-two-columns-with-images.php b/lib/patterns/text-two-columns-with-images.php deleted file mode 100644 index c4978acbc371e7..00000000000000 --- a/lib/patterns/text-two-columns-with-images.php +++ /dev/null @@ -1,13 +0,0 @@ - __( 'Two columns of text with images', 'gutenberg' ), - 'categories' => array( 'columns' ), - 'content' => "\n
\n
\n
\n
\"\"/
\n\n\n\n

" . __( 'They must know, then, that the above-named gentleman whenever he was at leisure (which was mostly all the year round) gave himself up to reading books of chivalry with such ardour and avidity that he almost entirely neglected the pursuit of his field-sports, and even the management of his property; and to such a pitch did his eagerness and infatuation go that he sold many an acre of tillageland to buy books of chivalry to read, and brought home as many of them as he could get.', 'gutenberg' ) . "

\n
\n\n\n\n
\n
\"\"/
\n\n\n\n

" . __( 'But of all there were none he liked so well as those of the famous Feliciano de Silva\'s composition, for their lucidity of style and complicated conceits were as pearls in his sight, particularly when in his reading he came upon courtships and cartels, where he often found passages like "the reason of the unreason with which my reason is afflicted so weakens my reason that with reason I murmur at your beauty;" or again, "the high heavens render you deserving of the desert your greatness deserves."', 'gutenberg' ) . "

\n
\n
\n
\n", - 'description' => _x( 'Two columns of text, each with an image on top.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/text-two-columns.php b/lib/patterns/text-two-columns.php deleted file mode 100644 index ca40c74289a0a6..00000000000000 --- a/lib/patterns/text-two-columns.php +++ /dev/null @@ -1,13 +0,0 @@ - __( 'Two columns of text', 'gutenberg' ), - 'categories' => array( 'columns' ), - 'content' => "\n
\n

" . __( 'Which treats of the character and pursuits of the famous gentleman Don Quixote of La Mancha', 'gutenberg' ) . "

\n\n\n\n
\n
\n

" . __( 'In a village of La Mancha, the name of which I have no desire to call to mind, there lived not long since one of those gentlemen that keep a lance in the lance-rack, an old buckler, a lean hack, and a greyhound for coursing. An olla of rather more beef than mutton, a salad on most nights, scraps on Saturdays, lentils on Fridays, and a pigeon or so extra on Sundays, made away with three-quarters of his income.', 'gutenberg' ) . "

\n
\n\n\n\n
\n

" . __( 'The rest of it went in a doublet of fine cloth and velvet breeches and shoes to match for holidays, while on week-days he made a brave figure in his best homespun. He had in his house a housekeeper past forty, a niece under twenty, and a lad for the field and market-place, who used to saddle the hack as well as handle the bill-hook. The age of this gentleman of ours was bordering on fifty; he was of a hardy habit, spare, gaunt-featured, a very early riser and a great sportsman.', 'gutenberg' ) . "

\n
\n
\n
\n", - 'description' => _x( 'Two columns of text preceded by a long heading.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/three-buttons.php b/lib/patterns/three-buttons.php deleted file mode 100644 index 2f60d3cf009bfd..00000000000000 --- a/lib/patterns/three-buttons.php +++ /dev/null @@ -1,14 +0,0 @@ - __( 'Three buttons', 'gutenberg' ), - 'content' => "\n\n", - 'viewportWidth' => 600, - 'categories' => array( 'buttons' ), - 'description' => _x( 'Three filled buttons with rounded corners, side by side.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/two-buttons.php b/lib/patterns/two-buttons.php deleted file mode 100644 index 8cd24997ce20ed..00000000000000 --- a/lib/patterns/two-buttons.php +++ /dev/null @@ -1,14 +0,0 @@ - __( 'Two buttons', 'gutenberg' ), - 'content' => "\n\n", - 'viewportWidth' => 500, - 'categories' => array( 'buttons' ), - 'description' => _x( 'Two buttons, one filled and one outlined, side by side.', 'Block pattern description', 'gutenberg' ), -); diff --git a/lib/patterns/two-images.php b/lib/patterns/two-images.php deleted file mode 100644 index 6b71853f3f9707..00000000000000 --- a/lib/patterns/two-images.php +++ /dev/null @@ -1,13 +0,0 @@ - __( 'Two images side by side', 'gutenberg' ), - 'categories' => array( 'gallery' ), - 'description' => _x( 'An image gallery with two example images.', 'Block pattern description', 'gutenberg' ), - 'content' => "\n\n", -); diff --git a/lib/rest-api.php b/lib/rest-api.php index 30c44eb39d2452..add3d35f249d23 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -10,141 +10,6 @@ die( 'Silence is golden.' ); } -/** - * Handle a failing oEmbed proxy request to try embedding as a shortcode. - * - * @see https://core.trac.wordpress.org/ticket/45447 - * - * @since 2.3.0 - * - * @param WP_HTTP_Response|WP_Error $response The REST Request response. - * @param WP_REST_Server $handler ResponseHandler instance (usually WP_REST_Server). - * @param WP_REST_Request $request Request used to generate the response. - * @return WP_HTTP_Response|object|WP_Error The REST Request response. - */ -function gutenberg_filter_oembed_result( $response, $handler, $request ) { - if ( ! is_wp_error( $response ) || 'oembed_invalid_url' !== $response->get_error_code() || - '/oembed/1.0/proxy' !== $request->get_route() ) { - return $response; - } - - // Try using a classic embed instead. - global $wp_embed; - $html = $wp_embed->shortcode( array(), $_GET['url'] ); - if ( ! $html ) { - return $response; - } - - global $wp_scripts; - - // Check if any scripts were enqueued by the shortcode, and include them in - // the response. - $enqueued_scripts = array(); - foreach ( $wp_scripts->queue as $script ) { - $enqueued_scripts[] = $wp_scripts->registered[ $script ]->src; - } - - return array( - 'provider_name' => __( 'Embed Handler', 'gutenberg' ), - 'html' => $html, - 'scripts' => $enqueued_scripts, - ); -} -add_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result', 10, 3 ); - -/** - * Add fields required for site editing to the /themes endpoint. - * - * @todo Remove once Gutenberg's minimum required WordPress version is v5.5. - * @see https://core.trac.wordpress.org/ticket/49906 - * @see https://core.trac.wordpress.org/changeset/47921 - * - * @param WP_REST_Response $response The response object. - * @param WP_Theme $theme Theme object used to create response. - */ -function gutenberg_filter_rest_prepare_theme( $response, $theme ) { - $data = $response->get_data(); - $fields = array_keys( $data ); - - /** - * The following is basically copied from Core's WP_REST_Themes_Controller::prepare_item_for_response() - * (as of WP v5.5), with `rest_is_field_included()` replaced by `! in_array()`. - * This makes sure that we add all the fields that are missing from Core. - * - * @see https://github.com/WordPress/WordPress/blob/019bc2d244c4d536338d2c634419583e928143df/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php#L118-L167 - */ - if ( ! in_array( 'stylesheet', $fields, true ) ) { - $data['stylesheet'] = $theme->get_stylesheet(); - } - - if ( ! in_array( 'template', $fields, true ) ) { - /** - * Use the get_template() method, not the 'Template' header, for finding the template. - * The 'Template' header is only good for what was written in the style.css, while - * get_template() takes into account where WordPress actually located the theme and - * whether it is actually valid. - */ - $data['template'] = $theme->get_template(); - } - - $plain_field_mappings = array( - 'requires_php' => 'RequiresPHP', - 'requires_wp' => 'RequiresWP', - 'textdomain' => 'TextDomain', - 'version' => 'Version', - ); - - foreach ( $plain_field_mappings as $field => $header ) { - if ( ! in_array( $field, $fields, true ) ) { - $data[ $field ] = $theme->get( $header ); - } - } - - if ( ! in_array( 'screenshot', $fields, true ) ) { - // Using $theme->get_screenshot() with no args to get absolute URL. - $data['screenshot'] = $theme->get_screenshot() ? $theme->get_screenshot() : ''; - } - - $rich_field_mappings = array( - 'author' => 'Author', - 'author_uri' => 'AuthorURI', - 'description' => 'Description', - 'name' => 'Name', - 'tags' => 'Tags', - 'theme_uri' => 'ThemeURI', - ); - - foreach ( $rich_field_mappings as $field => $header ) { - if ( ! in_array( $field, $fields, true ) ) { - $data[ $field ]['raw'] = $theme->display( $header, false, true ); - $data[ $field ]['rendered'] = $theme->display( $header ); - } - } - - $response->set_data( $data ); - return $response; -} -add_filter( 'rest_prepare_theme', 'gutenberg_filter_rest_prepare_theme', 10, 2 ); - -/** - * Registers the block directory. - * - * @since 6.5.0 - */ -function gutenberg_register_rest_block_directory() { - $block_directory_controller = new WP_REST_Block_Directory_Controller(); - $block_directory_controller->register_routes(); -} -add_filter( 'rest_api_init', 'gutenberg_register_rest_block_directory' ); - -/** - * Registers the Block types REST API routes. - */ -function gutenberg_register_block_type() { - $block_types = new WP_REST_Block_Types_Controller(); - $block_types->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_block_type' ); /** * Registers the menu locations area REST API routes. */ @@ -163,16 +28,6 @@ function gutenberg_register_rest_customizer_nonces() { } add_action( 'rest_api_init', 'gutenberg_register_rest_customizer_nonces' ); - -/** - * Registers the Plugins REST API routes. - */ -function gutenberg_register_plugins_endpoint() { - $plugins = new WP_REST_Plugins_Controller(); - $plugins->register_routes(); -} -add_action( 'rest_api_init', 'gutenberg_register_plugins_endpoint' ); - /** * Registers the Sidebars & Widgets REST API routes. */ @@ -307,25 +162,6 @@ function gutenberg_auto_draft_get_sample_permalink( $permalink, $id, $title, $na } add_filter( 'get_sample_permalink', 'gutenberg_auto_draft_get_sample_permalink', 10, 5 ); -/** - * Registers the image editor. - * - * @since 7.x.0 - */ -function gutenberg_register_image_editor() { - global $wp_version; - - // Strip '-src' from the version string. Messes up version_compare(). - $version = str_replace( '-src', '', $wp_version ); - - // Only register routes for versions older than WP 5.5. - if ( version_compare( $version, '5.5-beta', '<' ) ) { - $image_editor = new WP_REST_Image_Editor_Controller(); - $image_editor->register_routes(); - } -} -add_filter( 'rest_api_init', 'gutenberg_register_image_editor' ); - /** * Registers the post format search handler. * diff --git a/lib/template-loader.php b/lib/template-loader.php index 601b64de7a629b..5dc55ec88cb8f1 100644 --- a/lib/template-loader.php +++ b/lib/template-loader.php @@ -63,7 +63,6 @@ function get_template_hierarchy( $template_type ) { */ function gutenberg_override_query_template( $template, $type, array $templates = array() ) { global $_wp_current_template_content; - $current_template = gutenberg_resolve_template( $type, $templates ); if ( $current_template ) { @@ -151,9 +150,7 @@ function gutenberg_resolve_template( $template_type, $template_hierarchy = array usort( $templates, function ( $template_a, $template_b ) use ( $slug_priorities ) { - $priority_a = $slug_priorities[ $template_a->post_name ] * 2 + ( 'publish' === $template_a->post_status ? 1 : 0 ); - $priority_b = $slug_priorities[ $template_b->post_name ] * 2 + ( 'publish' === $template_b->post_status ? 1 : 0 ); - return $priority_b - $priority_a; + return $slug_priorities[ $template_a->post_name ] - $slug_priorities[ $template_b->post_name ]; } ); @@ -217,7 +214,7 @@ function gutenberg_viewport_meta_tag() { * @return string Template file name without extension. */ function gutenberg_strip_php_suffix( $template_file ) { - return preg_replace( '/\.php$/', '', $template_file ); + return preg_replace( '/\.(php|html)$/', '', $template_file ); } /** diff --git a/lib/template-parts.php b/lib/template-parts.php index 11532570786a5b..464200f66d000a 100644 --- a/lib/template-parts.php +++ b/lib/template-parts.php @@ -49,9 +49,9 @@ function gutenberg_register_template_part_post_type() { 'supports' => array( 'title', 'slug', + 'excerpt', 'editor', 'revisions', - 'custom-fields', ), ); @@ -104,36 +104,10 @@ function gutenberg_fix_template_part_admin_menu_entry() { } add_action( 'admin_menu', 'gutenberg_fix_template_part_admin_menu_entry' ); -/** - * Filters the 'wp_template_part' post type columns in the admin list table. - * - * @param array $columns Columns to display. - * @return array Filtered $columns. - */ -function gutenberg_filter_template_part_list_table_columns( array $columns ) { - $columns['slug'] = __( 'Slug', 'gutenberg' ); - if ( isset( $columns['date'] ) ) { - unset( $columns['date'] ); - } - return $columns; -} -add_filter( 'manage_wp_template_part_posts_columns', 'gutenberg_filter_template_part_list_table_columns' ); - -/** - * Renders column content for the 'wp_template_part' post type list table. - * - * @param string $column_name Column name to render. - * @param int $post_id Post ID. - */ -function gutenberg_render_template_part_list_table_column( $column_name, $post_id ) { - if ( 'slug' !== $column_name ) { - return; - } - $post = get_post( $post_id ); - echo esc_html( $post->post_name ); -} -add_action( 'manage_wp_template_part_posts_custom_column', 'gutenberg_render_template_part_list_table_column', 10, 2 ); - +// Customize the `wp_template` admin list. +add_filter( 'manage_wp_template_part_posts_columns', 'gutenberg_templates_lists_custom_columns' ); +add_action( 'manage_wp_template_part_posts_custom_column', 'gutenberg_render_templates_lists_custom_column', 10, 2 ); +add_filter( 'views_edit-wp_template_part', 'gutenberg_filter_templates_edit_views' ); /** * Filter for adding and a `theme` parameter to `wp_template_part` queries. diff --git a/lib/templates.php b/lib/templates.php index d99d146e9eff42..aa085c0d54d125 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -192,118 +192,9 @@ function gutenberg_fix_template_admin_menu_entry() { } add_action( 'admin_menu', 'gutenberg_fix_template_admin_menu_entry' ); -/** - * Filters the 'wp_template' post type columns in the admin list table. - * - * @param array $columns Columns to display. - * @return array Filtered $columns. - */ -function gutenberg_filter_template_list_table_columns( array $columns ) { - $columns['slug'] = __( 'Slug', 'gutenberg' ); - $columns['description'] = __( 'Description', 'gutenberg' ); - $columns['status'] = __( 'Status', 'gutenberg' ); - $columns['theme'] = __( 'Theme', 'gutenberg' ); - if ( isset( $columns['date'] ) ) { - unset( $columns['date'] ); - } - return $columns; -} -add_filter( 'manage_wp_template_posts_columns', 'gutenberg_filter_template_list_table_columns' ); - -/** - * Renders column content for the 'wp_template' post type list table. - * - * @param string $column_name Column name to render. - * @param int $post_id Post ID. - */ -function gutenberg_render_template_list_table_column( $column_name, $post_id ) { - if ( 'slug' === $column_name ) { - $post = get_post( $post_id ); - echo esc_html( $post->post_name ); - return; - } - - if ( 'description' === $column_name ) { - the_excerpt( $post_id ); - return; - } - - if ( 'status' === $column_name ) { - $post_status = get_post_status( $post_id ); - // The auto-draft status doesn't have localized labels. - if ( 'auto-draft' === $post_status ) { - echo esc_html_x( 'Auto-Draft', 'Post status', 'gutenberg' ); - return; - } - $post_status_object = get_post_status_object( $post_status ); - echo esc_html( $post_status_object->label ); - return; - } - - if ( 'theme' === $column_name ) { - $terms = get_the_terms( $post_id, 'wp_theme' ); - $themes = array(); - $is_file_based = false; - foreach ( $terms as $term ) { - if ( '_wp_file_based' === $term->slug ) { - $is_file_based = true; - } else { - $themes[] = esc_html( wp_get_theme( $term->slug ) ); - } - } - echo implode( '
', $themes ); - if ( $is_file_based ) { - echo '
' . __( '(Created from a template file)', 'gutenberg' ); - } - return; - } -} -add_action( 'manage_wp_template_posts_custom_column', 'gutenberg_render_template_list_table_column', 10, 2 ); - -/** - * Adds the auto-draft view to the 'wp_template' post type list. - * - * @param array $views The edit views to filter. - */ -function gutenberg_filter_templates_edit_views( $views ) { - $url = add_query_arg( - array( - 'post_type' => 'wp_template', - 'post_status' => 'auto-draft', - ), - 'edit.php' - ); - $is_auto_draft_view = isset( $_REQUEST['post_status'] ) && 'auto-draft' === $_REQUEST['post_status']; - $class_html = $is_auto_draft_view ? ' class="current"' : ''; - $aria_current = $is_auto_draft_view ? ' aria-current="page"' : ''; - $post_count = wp_count_posts( 'wp_template', 'readable' ); - $label = sprintf( - // The auto-draft status doesn't have localized labels. - translate_nooped_plural( - /* translators: %s: Number of auto-draft posts. */ - _nx_noop( - 'Auto-Draft (%s)', - 'Auto-Drafts (%s)', - 'Post status', - 'gutenberg' - ), - $post_count->{'auto-draft'} - ), - number_format_i18n( $post_count->{'auto-draft'} ) - ); - - $auto_draft_view = sprintf( - '%s', - esc_url( $url ), - $class_html, - $aria_current, - $label - ); - - array_splice( $views, 1, 0, array( 'auto-draft' => $auto_draft_view ) ); - - return $views; -} +// Customize the `wp_template` admin list. +add_filter( 'manage_wp_template_posts_columns', 'gutenberg_templates_lists_custom_columns' ); +add_action( 'manage_wp_template_posts_custom_column', 'gutenberg_render_templates_lists_custom_column', 10, 2 ); add_filter( 'views_edit-wp_template', 'gutenberg_filter_templates_edit_views' ); /** diff --git a/package-lock.json b/package-lock.json index 78edc4dff6a2ee..e68a92b065cef5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "9.4.1", + "version": "9.6.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -38,32 +38,32 @@ } }, "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.10.4" } }, "@babel/compat-data": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.1.tgz", - "integrity": "sha512-725AQupWJZ8ba0jbKceeFblZTY90McUBWMwHhkFQ9q1zKPJ95GUktljFcgcsIVwRnTnRKlcYzfiNImg5G9m6ZQ==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", + "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", "dev": true }, "@babel/core": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz", - "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==", + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.6", - "@babel/helper-module-transforms": "^7.11.0", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.5", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.5", - "@babel/types": "^7.11.5", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -74,150 +74,6 @@ "source-map": "^0.5.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", - "requires": { - "@babel/types": "^7.11.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -227,11 +83,11 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "json5": { @@ -242,11 +98,6 @@ "minimist": "^1.2.5" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -260,31 +111,30 @@ } }, "@babel/generator": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.4.tgz", - "integrity": "sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.5.tgz", + "integrity": "sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==", "requires": { - "@babel/types": "^7.9.0", + "@babel/types": "^7.12.5", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-annotate-as-pure": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", - "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", - "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", "requires": { - "@babel/helper-explode-assignable-expression": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-builder-react-jsx": { @@ -294,53 +144,27 @@ "requires": { "@babel/helper-annotate-as-pure": "^7.10.4", "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-builder-react-jsx-experimental": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.0.tgz", - "integrity": "sha512-3xJEiyuYU4Q/Ar9BsHisgdxZsRlsShMe90URZ0e6przL26CCs8NJbDoxH94kKT17PcxlMhsCAwZd90evCo26VQ==", - "dev": true, + "version": "7.12.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.4.tgz", + "integrity": "sha512-AjEa0jrQqNk7eDQOo0pTfUOwQBMF+xVqrausQwT9/rTKy0g04ggFNaJpaE09IQMn9yExluigWMJcj0WC7bq+Og==", "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-module-imports": "^7.8.3", - "@babel/types": "^7.9.0" + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-module-imports": "^7.12.1", + "@babel/types": "^7.12.1" } }, "@babel/helper-compilation-targets": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.1.tgz", - "integrity": "sha512-jtBEif7jsPwP27GPHs06v4WBV0KrE8a/P7n0N0sSvHn2hwUCYnolP/CLmz51IzAW4NlN+HuoBtb9QcwnRo9F/g==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", + "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", "dev": true, "requires": { - "@babel/compat-data": "^7.12.1", + "@babel/compat-data": "^7.12.5", "@babel/helper-validator-option": "^7.12.1", - "browserslist": "^4.12.0", + "browserslist": "^4.14.5", "semver": "^5.5.0" }, "dependencies": { @@ -353,63 +177,75 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.6.tgz", - "integrity": "sha512-klTBDdsr+VFFqaDHm5rR69OpEQtO2Qv8ECxHS1mNhJJvaHArR6a1xTf5K/eZW7eZpJbhCx3NW1Yt/sKsLXLblg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", + "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-split-export-declaration": "^7.8.3" + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz", - "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", + "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-regex": "^7.8.3", - "regexpu-core": "^4.7.0" + "@babel/helper-annotate-as-pure": "^7.10.4", + "regexpu-core": "^4.7.1" + }, + "dependencies": { + "regexpu-core": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + } } }, "@babel/helper-define-map": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", - "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/types": "^7.8.3", - "lodash": "^4.17.13" + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" } }, "@babel/helper-explode-assignable-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", - "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", + "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", "requires": { - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.1" } }, "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.10.4" } }, "@babel/helper-hoist-variables": { @@ -419,150 +255,102 @@ "dev": true, "requires": { "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } } }, "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", + "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.7" } }, "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", + "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.5" } }, "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", + "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-simple-access": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/helper-validator-identifier": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.1", + "@babel/types": "^7.12.1", + "lodash": "^4.17.19" } }, "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.7.tgz", + "integrity": "sha512-I5xc9oSJ2h59OwyUqjv95HRyzxj53DAubUERgQMrpcCEYQyToeHA+NEcUEsVWB4j53RDeskeBJ0SgRAYHDBckw==", "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.7" } }, "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" - }, - "@babel/helper-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", - "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", - "requires": { - "lodash": "^4.17.13" - } + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" }, "@babel/helper-remap-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", - "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", + "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-wrap-function": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/types": "^7.12.1" } }, "@babel/helper-replace-supers": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", - "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz", + "integrity": "sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA==", "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.6", - "@babel/types": "^7.8.6" + "@babel/helper-member-expression-to-functions": "^7.12.1", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", + "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/types": "^7.12.1" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", - "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", - "dev": true, + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", + "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", "requires": { - "@babel/types": "^7.11.0" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.12.1" } }, "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.11.0" } }, "@babel/helper-validator-identifier": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz", - "integrity": "sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==" + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" }, "@babel/helper-validator-option": { "version": "7.12.1", @@ -571,33 +359,32 @@ "dev": true }, "@babel/helper-wrap-function": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", - "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", + "version": "7.12.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", + "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helpers": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.2.tgz", - "integrity": "sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==", - "dev": true, + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", + "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.0", - "@babel/types": "^7.9.0" + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.12.5", + "@babel/types": "^7.12.5" } }, "@babel/highlight": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", - "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "requires": { - "@babel/helper-validator-identifier": "^7.9.0", + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -615,16 +402,16 @@ } }, "@babel/parser": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", - "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==" + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.7.tgz", + "integrity": "sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg==" }, "@babel/plugin-external-helpers": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.8.3.tgz", - "integrity": "sha512-mx0WXDDiIl5DwzMtzWGRSPugXi9BxROS05GQrhLNbEamhBiicgn994ibwkyiBH+6png7bm/yA7AUsvHyCXi4Vw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-external-helpers/-/plugin-external-helpers-7.12.1.tgz", + "integrity": "sha512-5VBqan0daXhDSRjrq2miABuELRwWJWFdM42Jvs/CDuhp+Es+fW+ISA5l+co8d+9oN3WLz/N3VvzyeseL3AvjxA==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-proposal-async-generator-functions": { @@ -636,346 +423,26 @@ "@babel/helper-plugin-utils": "^7.10.4", "@babel/helper-remap-async-to-generator": "^7.12.1", "@babel/plugin-syntax-async-generators": "^7.8.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", - "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", - "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } } }, "@babel/plugin-proposal-class-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", - "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", + "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-proposal-decorators": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz", - "integrity": "sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.12.1.tgz", + "integrity": "sha512-knNIuusychgYN8fGJHONL0RbFxLGawhXOJNLBk75TniTsZZeA+wdkDuv6wp4lGwzQEKjZi6/WYtnb3udNPmQmQ==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.5", + "@babel/helper-create-class-features-plugin": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-decorators": "^7.10.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", - "dev": true, - "requires": { - "@babel/types": "^7.11.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "@babel/plugin-syntax-decorators": "^7.12.1" } }, "@babel/plugin-proposal-dynamic-import": { @@ -986,41 +453,25 @@ "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-dynamic-import": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-proposal-export-default-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.8.3.tgz", - "integrity": "sha512-PYtv2S2OdCdp7GSPDg5ndGZFm9DmWFvuLoS5nBxZCgOBggluLnhTScspJxng96alHQzPyrrHxvC9/w4bFuspeA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.12.1.tgz", + "integrity": "sha512-z5Q4Ke7j0AexQRfgUvnD+BdCSgpTEKnqQ3kskk2jWtOBulxICzd1X9BGt7kmWftxZ2W3++OZdt5gtmC8KLxdRQ==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-export-default-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-export-default-from": "^7.12.1" } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz", - "integrity": "sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", + "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-proposal-json-strings": { @@ -1031,428 +482,178 @@ "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz", - "integrity": "sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", + "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - } } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", + "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.1.tgz", - "integrity": "sha512-MR7Ok+Af3OhNTCxYVjJZHS0t97ydnJZt/DbR4WISO39iDnhiD8XHrY12xuSJ90FFEGjir0Fzyyn7g/zY6hxbxA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", + "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - } } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz", - "integrity": "sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", + "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.4", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz", - "integrity": "sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", + "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", "@babel/plugin-syntax-optional-chaining": "^7.8.0" } }, "@babel/plugin-proposal-private-methods": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", + "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", + "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", + "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.12.1.tgz", + "integrity": "sha512-ir9YW5daRrTYiy9UJ2TzdNIJEZu8KclVzDcfSt4iEmOtwQ4llPtWInNKJyKnVXp1vE4bbVd5S31M/im3mYMO1w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-default-from": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.12.1.tgz", + "integrity": "sha512-dP5eGg6tHEkhnRD2/vRG/KJKRSg8gtxu2i+P/8/yFPJn/CfPU5G0/7Gks2i3M6IOVAPQekmsLN9LPsmXFFL4Uw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.1.tgz", + "integrity": "sha512-1lBLLmtxrwpm4VKmtVFselI/P3pX+G63fAtUUt6b2Nzgao77KNDwyuRt90Mj2/9pKobtt68FdvjfqohZjg/FCA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", - "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", - "dev": true, - "requires": { - "@babel/types": "^7.11.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz", - "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.8", - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz", - "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz", - "integrity": "sha512-2NaoC6fAk2VMdhY1eerkfHV+lVYC1u8b+jmRJISqANCJlTxYy19HGdIkkQtix2UtkcPuPu+IlDgrVseZnU03bw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-default-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.8.3.tgz", - "integrity": "sha512-a1qnnsr73KLNIQcQlcQ4ZHxqqfBKM6iNQZW2OMTyxNbA2WC7SHWHtGVpFzWtQAuS2pspkWVzdEBXXx8Ik0Za4w==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-flow": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.8.3.tgz", - "integrity": "sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.4.tgz", - "integrity": "sha512-KCg9mio9jwiARCB7WAcQ7Y1q+qicILjoK8LP/VkPkEKaf5dkaZZK1EcTe91a3JJlZ3qy6L5s9X52boEYi8DM9g==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - } - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz", - "integrity": "sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-syntax-nullish-coalescing-operator": { @@ -1464,12 +665,12 @@ } }, "@babel/plugin-syntax-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", - "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-syntax-object-rest-spread": { @@ -1503,71 +704,62 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-syntax-typescript": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz", - "integrity": "sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz", + "integrity": "sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", - "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", + "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", - "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", + "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-remap-async-to-generator": "^7.8.3" + "@babel/helper-module-imports": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.12.1" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", - "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", + "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", - "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", + "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "lodash": "^4.17.13" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-classes": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz", - "integrity": "sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-define-map": "^7.8.3", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-split-export-declaration": "^7.8.3", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", + "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1", + "@babel/helper-split-export-declaration": "^7.10.4", "globals": "^11.1.0" }, "dependencies": { @@ -1579,29 +771,29 @@ } }, "@babel/plugin-transform-computed-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", - "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", + "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-destructuring": { - "version": "7.8.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz", - "integrity": "sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", + "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", - "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", + "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-duplicate-keys": { @@ -1611,65 +803,57 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", - "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", + "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-flow-strip-types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz", - "integrity": "sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.12.1.tgz", + "integrity": "sha512-8hAtkmsQb36yMmEtk2JZ9JnVyDSnDOdlB+0nEGzIDLuK4yR3JcEjfuFPYkdEPSh8Id+rAMeBEn+X0iVEyho6Hg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-flow": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-flow": "^7.12.1" } }, "@babel/plugin-transform-for-of": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz", - "integrity": "sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", + "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", - "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", + "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", - "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", + "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", - "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", + "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-modules-amd": { @@ -1683,188 +867,6 @@ "babel-plugin-dynamic-import-node": "^2.3.3" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", - "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", - "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz", - "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz", - "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.1", - "@babel/types": "^7.12.1", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -1873,50 +875,28 @@ "requires": { "object.assign": "^4.1.0" } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true } } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz", - "integrity": "sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", + "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", "requires": { - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-simple-access": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.12.1", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "dependencies": { + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + } } }, "@babel/plugin-transform-modules-systemjs": { @@ -1932,188 +912,6 @@ "babel-plugin-dynamic-import-node": "^2.3.3" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", - "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", - "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz", - "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz", - "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.1", - "@babel/types": "^7.12.1", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, "babel-plugin-dynamic-import-node": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", @@ -2122,38 +920,6 @@ "requires": { "object.assign": "^4.1.0" } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true } } }, @@ -2165,222 +931,6 @@ "requires": { "@babel/helper-module-transforms": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", - "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", - "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz", - "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz", - "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.1", - "@babel/types": "^7.12.1", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } } }, "@babel/plugin-transform-named-capturing-groups-regex": { @@ -2390,68 +940,6 @@ "dev": true, "requires": { "@babel/helper-create-regexp-features-plugin": "^7.12.1" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz", - "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.1" - } - }, - "@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - } } }, "@babel/plugin-transform-new-target": { @@ -2461,160 +949,111 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-transform-object-assign": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.8.3.tgz", - "integrity": "sha512-i3LuN8tPDqUCRFu3dkzF2r1Nx0jp4scxtm7JxtIqI9he9Vk20YD+/zshdzR9JLsoBMlJlNR82a62vQExNEVx/Q==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.12.1.tgz", + "integrity": "sha512-geUHn4XwHznRAFiuROTy0Hr7bKbpijJCmr1Svt/VNGhpxmp0OrdxURNpWbOAf94nUbL+xj6gbxRVPHWIbRpRoA==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-object-super": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", - "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", + "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.12.1" } }, "@babel/plugin-transform-parameters": { - "version": "7.9.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz", - "integrity": "sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", + "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-property-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", - "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", + "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-react-constant-elements": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.9.0.tgz", - "integrity": "sha512-wXMXsToAUOxJuBBEHajqKLFWcCkOSLshTI2ChCFFj1zDd7od4IOxiwLCOObNUvOpkxLpjIuaIdBMmNt6ocCPAw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.12.1.tgz", + "integrity": "sha512-KOHd0tIRLoER+J+8f9DblZDa1fLGPwaaN1DI1TVHuQFOpjHV22C3CUB3obeC4fexHY9nx+fH0hQNvLFFfA1mxA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-react-display-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.8.3.tgz", - "integrity": "sha512-3Jy/PCw8Fe6uBKtEgz3M82ljt+lTg+xJaM4og+eyu83qLT87ZUSckn0wy7r31jflURWLO83TW6Ylf7lyXj3m5A==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz", + "integrity": "sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-react-jsx": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.4.tgz", - "integrity": "sha512-L+MfRhWjX0eI7Js093MM6MacKU4M6dnCRa/QPDwYMxjljzSCzzlzKzj9Pk4P3OtrPcxr2N3znR419nr3Xw+65A==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.7.tgz", + "integrity": "sha512-YFlTi6MEsclFAPIDNZYiCRbneg1MFGao9pPG9uD5htwE0vDbPaMUMeYd6itWjw7K4kro4UbdQf3ljmFl9y48dQ==", "requires": { "@babel/helper-builder-react-jsx": "^7.10.4", - "@babel/helper-builder-react-jsx-experimental": "^7.10.4", + "@babel/helper-builder-react-jsx-experimental": "^7.12.4", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-jsx": "^7.10.4" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-builder-react-jsx-experimental": { - "version": "7.12.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.4.tgz", - "integrity": "sha512-AjEa0jrQqNk7eDQOo0pTfUOwQBMF+xVqrausQwT9/rTKy0g04ggFNaJpaE09IQMn9yExluigWMJcj0WC7bq+Og==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-module-imports": "^7.12.1", - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", - "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "@babel/plugin-syntax-jsx": "^7.12.1" } }, "@babel/plugin-transform-react-jsx-development": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.9.0.tgz", - "integrity": "sha512-tK8hWKrQncVvrhvtOiPpKrQjfNX3DtkNLSX4ObuGcpS9p0QrGetKmlySIGR07y48Zft8WVgPakqd/bk46JrMSw==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.7.tgz", + "integrity": "sha512-Rs3ETtMtR3VLXFeYRChle5SsP/P9Jp/6dsewBQfokDSzKJThlsuFcnzLTDRALiUmTC48ej19YD9uN1mupEeEDg==", "dev": true, "requires": { - "@babel/helper-builder-react-jsx-experimental": "^7.9.0", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-jsx": "^7.8.3" + "@babel/helper-builder-react-jsx-experimental": "^7.12.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.12.1" } }, "@babel/plugin-transform-react-jsx-self": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.9.0.tgz", - "integrity": "sha512-K2ObbWPKT7KUTAoyjCsFilOkEgMvFG+y0FqOl6Lezd0/13kMkkjHskVsZvblRPj1PHA44PrToaZANrryppzTvQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.12.1.tgz", + "integrity": "sha512-FbpL0ieNWiiBB5tCldX17EtXgmzeEZjFrix72rQYeq9X6nUK38HCaxexzVQrZWXanxKJPKVVIU37gFjEQYkPkA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-jsx": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-react-jsx-source": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.9.0.tgz", - "integrity": "sha512-K6m3LlSnTSfRkM6FcRk8saNEeaeyG5k7AVkBU2bZK3+1zdkSED3qNdsWrUgQBeTVD2Tp3VMmerxVO2yM5iITmw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.12.1.tgz", + "integrity": "sha512-keQ5kBfjJNRc6zZN1/nVHCd6LLIHq4aUKcVnvE/2l+ZZROSbqoiGFRtT5t3Is89XJxBQaP7NLZX2jgGHdZvvFQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz", + "integrity": "sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==", + "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-jsx": "^7.8.3" + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-regenerator": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", - "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", + "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", "requires": { "regenerator-transform": "^0.14.2" } @@ -2626,55 +1065,19 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-transform-runtime": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.5.tgz", - "integrity": "sha512-9aIoee+EhjySZ6vY5hnLjigHzunBlscx9ANKutkeWTJTx6m5Rbq6Ic01tLvO54lSusR+BxV7u4UDdCmXv5aagg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz", + "integrity": "sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg==", "requires": { - "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-module-imports": "^7.12.1", "@babel/helper-plugin-utils": "^7.10.4", "resolve": "^1.8.1", "semver": "^5.5.1" }, "dependencies": { - "@babel/helper-module-imports": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", - "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -2683,37 +1086,36 @@ } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", - "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", + "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", - "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", + "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", - "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", + "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-regex": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-template-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", - "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", + "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/plugin-transform-typeof-symbol": { @@ -2723,77 +1125,62 @@ "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-transform-typescript": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.9.4.tgz", - "integrity": "sha512-yeWeUkKx2auDbSxRe8MusAG+n4m9BFY/v+lPjmQDgOFX5qnySkUY5oXzkp6FwPdsYqnKay6lorXYdC0n3bZO7w==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz", + "integrity": "sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-typescript": "^7.8.3" + "@babel/helper-create-class-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-typescript": "^7.12.1" } }, "@babel/plugin-transform-unicode-escapes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", - "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", + "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - } } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", - "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", + "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.12.1", + "@babel/helper-plugin-utils": "^7.10.4" } }, "@babel/preset-env": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz", - "integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==", + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.7.tgz", + "integrity": "sha512-OnNdfAr1FUQg7ksb7bmbKoby4qFOHw6DKWWUNB9KqnnCldxhxJlP+21dpyaWFmf2h0rTbOkXJtAGevY3XW1eew==", "dev": true, "requires": { - "@babel/compat-data": "^7.11.0", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", + "@babel/compat-data": "^7.12.7", + "@babel/helper-compilation-targets": "^7.12.5", + "@babel/helper-module-imports": "^7.12.5", "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.11.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-proposal-async-generator-functions": "^7.12.1", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-dynamic-import": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.1", + "@babel/plugin-proposal-json-strings": "^7.12.1", + "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-numeric-separator": "^7.12.7", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", + "@babel/plugin-syntax-class-properties": "^7.12.1", "@babel/plugin-syntax-dynamic-import": "^7.8.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.0", @@ -2803,1960 +1190,2164 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.11.0", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", + "@babel/plugin-syntax-top-level-await": "^7.12.1", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-async-to-generator": "^7.12.1", + "@babel/plugin-transform-block-scoped-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-computed-properties": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-dotall-regex": "^7.12.1", + "@babel/plugin-transform-duplicate-keys": "^7.12.1", + "@babel/plugin-transform-exponentiation-operator": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-function-name": "^7.12.1", + "@babel/plugin-transform-literals": "^7.12.1", + "@babel/plugin-transform-member-expression-literals": "^7.12.1", + "@babel/plugin-transform-modules-amd": "^7.12.1", + "@babel/plugin-transform-modules-commonjs": "^7.12.1", + "@babel/plugin-transform-modules-systemjs": "^7.12.1", + "@babel/plugin-transform-modules-umd": "^7.12.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", + "@babel/plugin-transform-new-target": "^7.12.1", + "@babel/plugin-transform-object-super": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-property-literals": "^7.12.1", + "@babel/plugin-transform-regenerator": "^7.12.1", + "@babel/plugin-transform-reserved-words": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-sticky-regex": "^7.12.7", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/plugin-transform-typeof-symbol": "^7.12.1", + "@babel/plugin-transform-unicode-escapes": "^7.12.1", + "@babel/plugin-transform-unicode-regex": "^7.12.1", "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.11.5", - "browserslist": "^4.12.0", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", + "@babel/types": "^7.12.7", + "core-js-compat": "^3.7.0", "semver": "^5.5.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "core-js-compat": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.1.tgz", + "integrity": "sha512-a16TLmy9NVD1rkjUGbwuyWkiDoN0FDpAwrfLONvHFQx0D9k7J9y0srwMT8QP/Z6HE3MIFaVynEeYwZwPX1o5RQ==", "dev": true, "requires": { - "@babel/types": "^7.10.4" + "browserslist": "^4.15.0", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } } }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/preset-flow": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.12.1.tgz", + "integrity": "sha512-UAoyMdioAhM6H99qPoKvpHMzxmNVXno8GYU/7vZmGaHk6/KqfDYL1W0NxszVbJ2EP271b7e6Ox+Vk2A9QsB3Sw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-transform-flow-strip-types": "^7.12.1" + } + }, + "@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.12.7.tgz", + "integrity": "sha512-wKeTdnGUP5AEYCYQIMeXMMwU7j+2opxrG0WzuZfxuuW9nhKvvALBjl67653CWamZJVefuJGI219G591RSldrqQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-transform-react-display-name": "^7.12.1", + "@babel/plugin-transform-react-jsx": "^7.12.7", + "@babel/plugin-transform-react-jsx-development": "^7.12.7", + "@babel/plugin-transform-react-jsx-self": "^7.12.1", + "@babel/plugin-transform-react-jsx-source": "^7.12.1", + "@babel/plugin-transform-react-pure-annotations": "^7.12.1" + } + }, + "@babel/preset-typescript": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.12.7.tgz", + "integrity": "sha512-nOoIqIqBmHBSEgBXWR4Dv/XBehtIFcw9PqZw6rFYuKrzsZmOQm3PR5siLBnKZFEsDb03IegG8nSjU/iXXXYRmw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-validator-option": "^7.12.1", + "@babel/plugin-transform-typescript": "^7.12.1" + } + }, + "@babel/register": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.12.1.tgz", + "integrity": "sha512-XWcmseMIncOjoydKZnWvWi0/5CUCD+ZYKhRwgYlWOrA8fGZ/FjuLRpqtIhLOVD/fvR1b9DQHtZPn68VvhpYf+Q==", + "requires": { + "find-cache-dir": "^2.0.0", + "lodash": "^4.17.19", + "make-dir": "^2.1.0", + "pirates": "^4.0.0", + "source-map-support": "^0.5.16" + }, + "dependencies": { + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" } }, - "@babel/helper-create-class-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", - "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", - "dev": true, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4" + "locate-path": "^3.0.0" } }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz", - "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==", - "dev": true, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.1" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" + "pify": "^4.0.1", + "semver": "^5.6.0" } }, - "@babel/helper-explode-assignable-expression": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", - "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", - "dev": true, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "requires": { - "@babel/types": "^7.12.1" + "p-try": "^2.0.0" } }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "p-limit": "^2.0.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz", - "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" }, - "@babel/helper-module-imports": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz", - "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==", - "dev": true, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "requires": { - "@babel/types": "^7.12.1" + "find-up": "^3.0.0" } }, - "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@babel/runtime-corejs3": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", + "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", + "dev": true, + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" + } + }, + "@babel/traverse": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz", + "integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" + "ms": "2.1.2" } }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@babel/types": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.7.tgz", + "integrity": "sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@base2/pretty-print-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz", + "integrity": "sha512-4Th98KlMHr5+JkxfcoDT//6vY8vM+iSPrLNpHhRyLx2CFYi8e2RfqPLdpbnpo0Q5lQC5hNB79yes07zb02fvCw==", + "dev": true + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@egjs/hammerjs": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", + "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", + "requires": { + "@types/hammerjs": "^2.0.36" + } + }, + "@emotion/cache": { + "version": "10.0.29", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", + "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==", + "dev": true, + "requires": { + "@emotion/sheet": "0.9.4", + "@emotion/stylis": "0.8.5", + "@emotion/utils": "0.11.3", + "@emotion/weak-memoize": "0.2.5" + }, + "dependencies": { + "@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", "dev": true - }, - "@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "dev": true, + } + } + }, + "@emotion/core": { + "version": "10.0.28", + "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.28.tgz", + "integrity": "sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA==", + "requires": { + "@babel/runtime": "^7.5.5", + "@emotion/cache": "^10.0.27", + "@emotion/css": "^10.0.27", + "@emotion/serialize": "^0.11.15", + "@emotion/sheet": "0.9.4", + "@emotion/utils": "0.11.3" + }, + "dependencies": { + "@emotion/cache": { + "version": "10.0.29", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", + "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==", "requires": { - "lodash": "^4.17.19" + "@emotion/sheet": "0.9.4", + "@emotion/stylis": "0.8.5", + "@emotion/utils": "0.11.3", + "@emotion/weak-memoize": "0.2.5" } }, - "@babel/helper-remap-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", - "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/types": "^7.12.1" - } + "@emotion/sheet": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz", + "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==" }, - "@babel/helper-replace-supers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz", - "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1" - } + "@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" }, - "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } + "@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } + "@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" + } + } + }, + "@emotion/css": { + "version": "10.0.27", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz", + "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==", + "requires": { + "@emotion/serialize": "^0.11.15", + "@emotion/utils": "0.11.3", + "babel-plugin-emotion": "^10.0.27" + }, + "dependencies": { + "@emotion/hash": { + "version": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.4.tgz", + "integrity": "sha512-fxfMSBMX3tlIbKUdtGKxqB1fyrH6gVrX39Gsv3y8lRYKUqlgDt3UMqQyGnR1bQMa2B8aGnhLZokZgg8vT0Le+A==" }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } + "@emotion/memoize": { + "version": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true + "@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" + } + } + }, + "@emotion/is-prop-valid": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.5.tgz", + "integrity": "sha512-6ZODuZSFofbxSbcxwsFz+6ioPjb0ISJRRPLZ+WIbjcU2IMU0Io+RGQjjaTgOvNQl007KICBm7zXQaYQEC1r6Bg==", + "requires": { + "@emotion/memoize": "0.7.3" + } + }, + "@emotion/memoize": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.3.tgz", + "integrity": "sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==" + }, + "@emotion/native": { + "version": "10.0.27", + "resolved": "https://registry.npmjs.org/@emotion/native/-/native-10.0.27.tgz", + "integrity": "sha512-3qxR2XFizGfABKKbX9kAYc0PHhKuCEuyxshoq3TaMEbi9asWHdQVChg32ULpblm4XAf9oxaitAU7J9SfdwFxtw==", + "requires": { + "@emotion/primitives-core": "10.0.27" + } + }, + "@emotion/primitives-core": { + "version": "10.0.27", + "resolved": "https://registry.npmjs.org/@emotion/primitives-core/-/primitives-core-10.0.27.tgz", + "integrity": "sha512-fRBEDNPSFFOrBJ0OcheuElayrNTNdLF9DzMxtL0sFgsCFvvadlzwJHhJMSwEJuxwARm9GhVLr1p8G8JGkK98lQ==", + "requires": { + "css-to-react-native": "^2.2.1" + } + }, + "@emotion/serialize": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.15.tgz", + "integrity": "sha512-YE+qnrmGwyR+XB5j7Bi+0GT1JWsdcjM/d4POu+TXkcnrRs4RFCCsi3d/Ebf+wSStHqAlTT2+dfd+b9N9EO2KBg==", + "requires": { + "@emotion/hash": "0.7.4", + "@emotion/memoize": "0.7.4", + "@emotion/unitless": "0.7.5", + "@emotion/utils": "0.11.3", + "csstype": "^2.5.7" + }, + "dependencies": { + "@emotion/hash": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.4.tgz", + "integrity": "sha512-fxfMSBMX3tlIbKUdtGKxqB1fyrH6gVrX39Gsv3y8lRYKUqlgDt3UMqQyGnR1bQMa2B8aGnhLZokZgg8vT0Le+A==" }, - "@babel/helper-wrap-function": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", - "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } + "@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } + "@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" }, - "@babel/parser": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz", - "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==", + "@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" + } + } + }, + "@emotion/sheet": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz", + "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==", + "dev": true + }, + "@emotion/styled": { + "version": "10.0.23", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-10.0.23.tgz", + "integrity": "sha512-gNr04eqBQ2iYUx8wFLZDfm3N8/QUOODu/ReDXa693uyQGy2OqA+IhPJk+kA7id8aOfwAsMuvZ0pJImEXXKtaVQ==", + "requires": { + "@emotion/styled-base": "^10.0.23", + "babel-plugin-emotion": "^10.0.23" + } + }, + "@emotion/styled-base": { + "version": "10.0.24", + "resolved": "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.0.24.tgz", + "integrity": "sha512-AnBImerf0h4dGAJVo0p0VE8KoAns71F28ErGFK474zbNAHX6yqSWQUasb+1jvg/VPwZjCp19+tAr6oOB0pwmLQ==", + "requires": { + "@babel/runtime": "^7.5.5", + "@emotion/is-prop-valid": "0.8.5", + "@emotion/serialize": "^0.11.14", + "@emotion/utils": "0.11.2" + } + }, + "@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "dev": true + }, + "@emotion/utils": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.2.tgz", + "integrity": "sha512-UHX2XklLl3sIaP6oiMmlVzT0J+2ATTVpf0dHQVyPJHTkOITvXfaSqnRk6mdDhV9pR8T/tHc3cex78IKXssmzrA==" + }, + "@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==", + "dev": true + }, + "@evocateur/libnpmaccess": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz", + "integrity": "sha512-KSCAHwNWro0CF2ukxufCitT9K5LjL/KuMmNzSu8wuwN2rjyKHD8+cmOsiybK+W5hdnwc5M1SmRlVCaMHQo+3rg==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "npm-package-arg": "^6.1.0" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", "dev": true }, - "@babel/plugin-proposal-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", - "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", - "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + "pump": "^3.0.0" } }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" } }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", - "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz", - "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@evocateur/libnpmpublish": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@evocateur/libnpmpublish/-/libnpmpublish-1.2.2.tgz", + "integrity": "sha512-MJrrk9ct1FeY9zRlyeoyMieBjGDG9ihyyD9/Ft6MMrTxql9NyoEx2hw9casTIP4CdqEVu+3nQ2nXxoJ8RCXyFg==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "aproba": "^2.0.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.0.0", + "lodash.clonedeep": "^4.5.0", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "semver": "^5.5.1", + "ssri": "^6.0.1" + }, + "dependencies": { + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", - "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "pump": "^3.0.0" } }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" } }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", - "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "figgy-pudding": "^3.5.1" } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", - "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", + } + } + }, + "@evocateur/npm-registry-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@evocateur/npm-registry-fetch/-/npm-registry-fetch-4.0.0.tgz", + "integrity": "sha512-k1WGfKRQyhJpIr+P17O5vLIo2ko1PFLKwoetatdduUSt/aQ4J2sJrJwwatdI5Z3SiYk/mRH9S3JpdmMFd/IK4g==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "npm-package-arg": "^6.1.0", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1" + "yallist": "^3.0.2" } }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", - "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" } }, - "@babel/plugin-transform-block-scoping": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz", - "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true }, - "@babel/plugin-transform-classes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", - "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" - } + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "@evocateur/pacote": { + "version": "9.6.5", + "resolved": "https://registry.npmjs.org/@evocateur/pacote/-/pacote-9.6.5.tgz", + "integrity": "sha512-EI552lf0aG2nOV8NnZpTxNo2PcXKPmDbF9K8eCBFQdIZwHNGN/mi815fxtmUMa2wTa1yndotICIDt/V0vpEx2w==", + "dev": true, + "requires": { + "@evocateur/npm-registry-fetch": "^4.0.0", + "bluebird": "^3.5.3", + "cacache": "^12.0.3", + "chownr": "^1.1.2", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^5.1.1", + "make-fetch-happen": "^5.0.0", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.5.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.4.4", + "npm-pick-manifest": "^3.0.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.3", + "safe-buffer": "^5.2.0", + "semver": "^5.7.0", + "ssri": "^6.0.1", + "tar": "^4.4.10", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "dev": true }, - "@babel/plugin-transform-computed-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", - "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", + "cacache": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" } }, - "@babel/plugin-transform-destructuring": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", - "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", + "dev": true }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", - "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "pump": "^3.0.0" } }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", - "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", + "glob": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", + "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "@babel/plugin-transform-for-of": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", - "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true }, - "@babel/plugin-transform-function-name": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", - "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "yallist": "^3.0.2" } }, - "@babel/plugin-transform-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", - "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" } }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", - "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", - "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.12.1", - "babel-plugin-dynamic-import-node": "^2.3.3" + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" } }, - "@babel/plugin-transform-object-super": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", - "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1" - } + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, - "@babel/plugin-transform-parameters": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", - "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "@babel/plugin-transform-property-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", - "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "path-parse": "^1.0.6" } }, - "@babel/plugin-transform-regenerator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", - "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { - "regenerator-transform": "^0.14.2" + "glob": "^7.1.3" } }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", - "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true }, - "@babel/plugin-transform-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", - "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" - } + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz", - "integrity": "sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ==", + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" + "figgy-pudding": "^3.5.1" } }, - "@babel/plugin-transform-template-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", - "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + } } }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", - "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" + "unique-slug": "^2.0.0" } }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz", - "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.1", - "@babel/types": "^7.12.1", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz", - "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, - "regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } }, - "@babel/preset-flow": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.9.0.tgz", - "integrity": "sha512-88uSmlshIrlmPkNkEcx3UpSZ6b8n0UGBq0/0ZMZCF/uxAW0XIAUuDHBhIOAh0pvweafH4RxOwi/H3rWhtqOYPA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-transform-flow-strip-types": "^7.9.0" - } + "@hapi/address": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.0.0.tgz", + "integrity": "sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw==" }, - "@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } + "@hapi/hoek": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-6.2.4.tgz", + "integrity": "sha512-HOJ20Kc93DkDVvjwHyHawPwPkX44sIrbXazAUDiUXaY2R9JwQGo2PhFfnQtdrsIe4igjG2fPgMra7NYw7qhy0A==" }, - "@babel/preset-react": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.9.4.tgz", - "integrity": "sha512-AxylVB3FXeOTQXNXyiuAQJSvss62FEotbX2Pzx3K/7c+MKJMdSg6Ose6QYllkdCFA8EInCJVw7M/o5QbLuA4ZQ==", - "dev": true, + "@hapi/joi": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.0.tgz", + "integrity": "sha512-n6kaRQO8S+kepUTbXL9O/UOL788Odqs38/VOfoCrATDtTvyfiO3fgjlSRaNkHabpTLgM7qru9ifqXlXbXk8SeQ==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-transform-react-display-name": "^7.8.3", - "@babel/plugin-transform-react-jsx": "^7.9.4", - "@babel/plugin-transform-react-jsx-development": "^7.9.0", - "@babel/plugin-transform-react-jsx-self": "^7.9.0", - "@babel/plugin-transform-react-jsx-source": "^7.9.0" + "@hapi/address": "2.x.x", + "@hapi/hoek": "6.x.x", + "@hapi/marker": "1.x.x", + "@hapi/topo": "3.x.x" } }, - "@babel/preset-typescript": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.9.0.tgz", - "integrity": "sha512-S4cueFnGrIbvYJgwsVFKdvOmpiL0XGw9MFW9D0vgRys5g36PBhZRL8NX8Gr2akz8XRtzq6HuDXPD/1nniagNUg==", - "dev": true, + "@hapi/marker": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@hapi/marker/-/marker-1.0.0.tgz", + "integrity": "sha512-JOfdekTXnJexfE8PyhZFyHvHjt81rBFSAbTIRAhF2vv/2Y1JzoKsGqxH/GpZJoF7aEfYok8JVcAHmSz1gkBieA==" + }, + "@hapi/topo": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.2.tgz", + "integrity": "sha512-r+aumOqJ5QbD6aLPJWqVjMAPsx5pZKz+F5yPqXZ/WWG9JTtHbQqlzrJoknJ0iJxLj9vlXtmpSdjlkszseeG8OA==", "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-transform-typescript": "^7.9.0" + "@hapi/hoek": "8.x.x" + }, + "dependencies": { + "@hapi/hoek": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.0.2.tgz", + "integrity": "sha512-O6o6mrV4P65vVccxymuruucb+GhP2zl9NLCG8OdoFRS8BEGw3vwpPp20wpAtpbQQxz1CEUtmxJGgWhjq1XA3qw==" + } } }, - "@babel/register": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.9.0.tgz", - "integrity": "sha512-Tv8Zyi2J2VRR8g7pC5gTeIN8Ihultbmk0ocyNz8H2nEZbmhp1N6q0A1UGsQbDvGP/sNinQKUHf3SqXwqjtFv4Q==", + "@icons/material": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", + "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", + "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "dev": true, "requires": { - "find-cache-dir": "^2.0.0", - "lodash": "^4.17.13", - "make-dir": "^2.1.0", - "pirates": "^4.0.0", - "source-map-support": "^0.5.16" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "dependencies": { - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "p-locate": "^4.1.0" } }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "requires": { "p-try": "^2.0.0" } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^2.2.0" } }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "requires": { - "find-up": "^3.0.0" - } + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true }, - "source-map": { - "version": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true } } }, - "@babel/runtime": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", - "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" - } - } + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "dev": true }, - "@babel/runtime-corejs3": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz", - "integrity": "sha512-qh5IR+8VgFz83VBa6OkaET6uN/mJOhHONuy3m1sgF0CV6mXdPSEBdA7e1eUbVvyNtANjMbg22JUv71BaDXLY6A==", - "dev": true, + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", "requires": { - "core-js-pure": "^3.0.0", - "regenerator-runtime": "^0.13.4" + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" }, "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" } } }, - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", + "@jest/core": { + "version": "25.5.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.5.4.tgz", + "integrity": "sha512-3uSo7laYxF00Dg/DMgbn4xMJKmDdWvZnf89n8Xj/5/AeQ2dOQmn6b6Hkj/MleyzZWXpwv+WSdYWl4cLsy2JsoA==", + "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "@jest/console": "^25.5.0", + "@jest/reporters": "^25.5.1", + "@jest/test-result": "^25.5.0", + "@jest/transform": "^25.5.1", + "@jest/types": "^25.5.0", + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^25.5.0", + "jest-config": "^25.5.4", + "jest-haste-map": "^25.5.1", + "jest-message-util": "^25.5.0", + "jest-regex-util": "^25.2.6", + "jest-resolve": "^25.5.1", + "jest-resolve-dependencies": "^25.5.4", + "jest-runner": "^25.5.4", + "jest-runtime": "^25.5.4", + "jest-snapshot": "^25.5.1", + "jest-util": "^25.5.0", + "jest-validate": "^25.5.0", + "jest-watcher": "^25.5.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "realpath-native": "^2.0.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "@jest/console": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", + "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", + "dev": true, "requires": { - "@babel/highlight": "^7.10.4" + "@jest/types": "^25.5.0", + "chalk": "^3.0.0", + "jest-message-util": "^25.5.0", + "jest-util": "^25.5.0", + "slash": "^3.0.0" } }, - "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", + "@jest/reporters": { + "version": "25.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.5.1.tgz", + "integrity": "sha512-3jbd8pPDTuhYJ7vqiHXbSwTJQNavczPs+f1kRprRDxETeE3u6srJ+f0NPuwvOmk+lmunZzPkYWIFZDLHQPkviw==", + "dev": true, "requires": { - "@babel/types": "^7.11.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^25.5.0", + "@jest/test-result": "^25.5.0", + "@jest/transform": "^25.5.1", + "@jest/types": "^25.5.0", + "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^25.5.1", + "jest-resolve": "^25.5.1", + "jest-util": "^25.5.0", + "jest-worker": "^25.5.0", + "node-notifier": "^6.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^3.1.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^4.1.3" } }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "@jest/test-result": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", + "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", + "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" + "@jest/console": "^25.5.0", + "@jest/types": "^25.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" } }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "@jest/transform": { + "version": "25.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.5.1.tgz", + "integrity": "sha512-Y8CEoVwXb4QwA6Y/9uDkn0Xfz0finGkieuV0xkdF9UtZGJeLukD5nLkaVrVsODB1ojRWlaoD0AJZpVHCSnJEvg==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^25.5.0", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^3.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^25.5.1", + "jest-regex-util": "^25.2.6", + "jest-util": "^25.5.0", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "realpath-native": "^2.0.0", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, "requires": { - "@babel/types": "^7.10.4" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, "requires": { - "@babel/types": "^7.11.0" + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } } }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "color-convert": "^2.0.1" } }, - "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==" + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "babel-plugin-istanbul": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" } }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" + "fill-range": "^7.0.1" } }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { - "ms": "^2.1.1" + "color-name": "~1.1.4" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "@babel/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.0.tgz", - "integrity": "sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==", - "requires": { - "@babel/helper-validator-identifier": "^7.9.0", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - }, - "@base2/pretty-print-object": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz", - "integrity": "sha512-4Th98KlMHr5+JkxfcoDT//6vY8vM+iSPrLNpHhRyLx2CFYi8e2RfqPLdpbnpo0Q5lQC5hNB79yes07zb02fvCw==", - "dev": true - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "@egjs/hammerjs": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", - "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", - "requires": { - "@types/hammerjs": "^2.0.36" - } - }, - "@egoist/vue-to-react": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@egoist/vue-to-react/-/vue-to-react-1.1.0.tgz", - "integrity": "sha512-MwfwXHDh6ptZGLEtNLPXp2Wghteav7mzpT2Mcwl3NZWKF814i5hhHnNkVrcQQEuxUroSWQqzxLkMKSb+nhPang==", - "dev": true - }, - "@emotion/cache": { - "version": "10.0.19", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.19.tgz", - "integrity": "sha512-BoiLlk4vEsGBg2dAqGSJu0vJl/PgVtCYLBFJaEO8RmQzPugXewQCXZJNXTDFaRlfCs0W+quesayav4fvaif5WQ==", - "dev": true, - "requires": { - "@emotion/sheet": "0.9.3", - "@emotion/stylis": "0.8.4", - "@emotion/utils": "0.11.2", - "@emotion/weak-memoize": "0.2.4" - } - }, - "@emotion/core": { - "version": "10.0.28", - "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.28.tgz", - "integrity": "sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA==", - "requires": { - "@babel/runtime": "^7.5.5", - "@emotion/cache": "^10.0.27", - "@emotion/css": "^10.0.27", - "@emotion/serialize": "^0.11.15", - "@emotion/sheet": "0.9.4", - "@emotion/utils": "0.11.3" - }, - "dependencies": { - "@emotion/cache": { - "version": "10.0.29", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", - "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "requires": { - "@emotion/sheet": "0.9.4", - "@emotion/stylis": "0.8.5", - "@emotion/utils": "0.11.3", - "@emotion/weak-memoize": "0.2.5" + "to-regex-range": "^5.0.1" } }, - "@emotion/sheet": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz", - "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==" + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } }, - "@emotion/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + "fsevents": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", + "dev": true, + "optional": true }, - "@emotion/utils": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", - "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true }, - "@emotion/weak-memoize": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", - "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" - } - } - }, - "@emotion/css": { - "version": "10.0.27", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz", - "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==", - "requires": { - "@emotion/serialize": "^0.11.15", - "@emotion/utils": "0.11.3", - "babel-plugin-emotion": "^10.0.27" - }, - "dependencies": { - "@emotion/hash": { - "version": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.4.tgz", - "integrity": "sha512-fxfMSBMX3tlIbKUdtGKxqB1fyrH6gVrX39Gsv3y8lRYKUqlgDt3UMqQyGnR1bQMa2B8aGnhLZokZgg8vT0Le+A==" + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, - "@emotion/memoize": { - "version": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, - "@emotion/utils": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", - "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" - } - } - }, - "@emotion/is-prop-valid": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.5.tgz", - "integrity": "sha512-6ZODuZSFofbxSbcxwsFz+6ioPjb0ISJRRPLZ+WIbjcU2IMU0Io+RGQjjaTgOvNQl007KICBm7zXQaYQEC1r6Bg==", - "requires": { - "@emotion/memoize": "0.7.3" - } - }, - "@emotion/memoize": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.3.tgz", - "integrity": "sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==" - }, - "@emotion/native": { - "version": "10.0.27", - "resolved": "https://registry.npmjs.org/@emotion/native/-/native-10.0.27.tgz", - "integrity": "sha512-3qxR2XFizGfABKKbX9kAYc0PHhKuCEuyxshoq3TaMEbi9asWHdQVChg32ULpblm4XAf9oxaitAU7J9SfdwFxtw==", - "requires": { - "@emotion/primitives-core": "10.0.27" - } - }, - "@emotion/primitives-core": { - "version": "10.0.27", - "resolved": "https://registry.npmjs.org/@emotion/primitives-core/-/primitives-core-10.0.27.tgz", - "integrity": "sha512-fRBEDNPSFFOrBJ0OcheuElayrNTNdLF9DzMxtL0sFgsCFvvadlzwJHhJMSwEJuxwARm9GhVLr1p8G8JGkK98lQ==", - "requires": { - "css-to-react-native": "^2.2.1" - } - }, - "@emotion/serialize": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.15.tgz", - "integrity": "sha512-YE+qnrmGwyR+XB5j7Bi+0GT1JWsdcjM/d4POu+TXkcnrRs4RFCCsi3d/Ebf+wSStHqAlTT2+dfd+b9N9EO2KBg==", - "requires": { - "@emotion/hash": "0.7.4", - "@emotion/memoize": "0.7.4", - "@emotion/unitless": "0.7.5", - "@emotion/utils": "0.11.3", - "csstype": "^2.5.7" - }, - "dependencies": { - "@emotion/hash": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.4.tgz", - "integrity": "sha512-fxfMSBMX3tlIbKUdtGKxqB1fyrH6gVrX39Gsv3y8lRYKUqlgDt3UMqQyGnR1bQMa2B8aGnhLZokZgg8vT0Le+A==" + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "requires": { + "is-docker": "^2.0.0" + } }, - "@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "dev": true }, - "@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + "jest-haste-map": { + "version": "25.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.5.1.tgz", + "integrity": "sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "@types/graceful-fs": "^4.1.2", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-serializer": "^25.5.0", + "jest-util": "^25.5.0", + "jest-worker": "^25.5.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7", + "which": "^2.0.2" + } }, - "@emotion/utils": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", - "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==" - } - } - }, - "@emotion/sheet": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.3.tgz", - "integrity": "sha512-c3Q6V7Df7jfwSq5AzQWbXHa5soeE4F5cbqi40xn0CzXxWW9/6Mxq48WJEtqfWzbZtW9odZdnRAkwCQwN12ob4A==", - "dev": true - }, - "@emotion/styled": { - "version": "10.0.23", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-10.0.23.tgz", - "integrity": "sha512-gNr04eqBQ2iYUx8wFLZDfm3N8/QUOODu/ReDXa693uyQGy2OqA+IhPJk+kA7id8aOfwAsMuvZ0pJImEXXKtaVQ==", - "requires": { - "@emotion/styled-base": "^10.0.23", - "babel-plugin-emotion": "^10.0.23" - } - }, - "@emotion/styled-base": { - "version": "10.0.24", - "resolved": "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.0.24.tgz", - "integrity": "sha512-AnBImerf0h4dGAJVo0p0VE8KoAns71F28ErGFK474zbNAHX6yqSWQUasb+1jvg/VPwZjCp19+tAr6oOB0pwmLQ==", - "requires": { - "@babel/runtime": "^7.5.5", - "@emotion/is-prop-valid": "0.8.5", - "@emotion/serialize": "^0.11.14", - "@emotion/utils": "0.11.2" - } - }, - "@emotion/stylis": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.4.tgz", - "integrity": "sha512-TLmkCVm8f8gH0oLv+HWKiu7e8xmBIaokhxcEKPh1m8pXiV/akCiq50FvYgOwY42rjejck8nsdQxZlXZ7pmyBUQ==", - "dev": true - }, - "@emotion/utils": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.2.tgz", - "integrity": "sha512-UHX2XklLl3sIaP6oiMmlVzT0J+2ATTVpf0dHQVyPJHTkOITvXfaSqnRk6mdDhV9pR8T/tHc3cex78IKXssmzrA==" - }, - "@emotion/weak-memoize": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.4.tgz", - "integrity": "sha512-6PYY5DVdAY1ifaQW6XYTnOMihmBVT27elqSjEoodchsGjzYlEsTQMcEhSud99kVawatyTZRTiVkJ/c6lwbQ7nA==", - "dev": true - }, - "@evocateur/libnpmaccess": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz", - "integrity": "sha512-KSCAHwNWro0CF2ukxufCitT9K5LjL/KuMmNzSu8wuwN2rjyKHD8+cmOsiybK+W5hdnwc5M1SmRlVCaMHQo+3rg==", - "dev": true, - "requires": { - "@evocateur/npm-registry-fetch": "^4.0.0", - "aproba": "^2.0.0", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "npm-package-arg": "^6.1.0" - }, - "dependencies": { - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "jest-message-util": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", + "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^25.5.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^3.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "slash": "^3.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-regex-util": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", + "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "jest-resolve": { + "version": "25.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", + "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", "dev": true, "requires": { - "pump": "^3.0.0" + "@jest/types": "^25.5.0", + "browser-resolve": "^1.11.3", + "chalk": "^3.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.1", + "read-pkg-up": "^7.0.1", + "realpath-native": "^2.0.0", + "resolve": "^1.17.0", + "slash": "^3.0.0" } }, - "npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "jest-serializer": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.5.0.tgz", + "integrity": "sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==", "dev": true, "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" + "graceful-fs": "^4.2.4" } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "jest-util": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", + "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "@jest/types": "^25.5.0", + "chalk": "^3.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "make-dir": "^3.0.0" } }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@evocateur/libnpmpublish": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@evocateur/libnpmpublish/-/libnpmpublish-1.2.2.tgz", - "integrity": "sha512-MJrrk9ct1FeY9zRlyeoyMieBjGDG9ihyyD9/Ft6MMrTxql9NyoEx2hw9casTIP4CdqEVu+3nQ2nXxoJ8RCXyFg==", - "dev": true, - "requires": { - "@evocateur/npm-registry-fetch": "^4.0.0", - "aproba": "^2.0.0", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.0.0", - "lodash.clonedeep": "^4.5.0", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "semver": "^5.5.1", - "ssri": "^6.0.1" - }, - "dependencies": { - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", "dev": true, "requires": { - "pump": "^3.0.0" + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" } }, - "npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "jest-worker": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", "dev": true, "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" } }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "p-locate": "^4.1.0" } }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "figgy-pudding": "^3.5.1" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } - } - } - }, - "@evocateur/npm-registry-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@evocateur/npm-registry-fetch/-/npm-registry-fetch-4.0.0.tgz", - "integrity": "sha512-k1WGfKRQyhJpIr+P17O5vLIo2ko1PFLKwoetatdduUSt/aQ4J2sJrJwwatdI5Z3SiYk/mRH9S3JpdmMFd/IK4g==", - "dev": true, - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0", - "safe-buffer": "^5.1.2" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + }, + "node-notifier": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz", + "integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==", "dev": true, + "optional": true, "requires": { - "yallist": "^3.0.2" + "growly": "^1.3.0", + "is-wsl": "^2.1.1", + "semver": "^6.3.0", + "shellwords": "^0.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "optional": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "optional": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, - "npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "@evocateur/pacote": { - "version": "9.6.5", - "resolved": "https://registry.npmjs.org/@evocateur/pacote/-/pacote-9.6.5.tgz", - "integrity": "sha512-EI552lf0aG2nOV8NnZpTxNo2PcXKPmDbF9K8eCBFQdIZwHNGN/mi815fxtmUMa2wTa1yndotICIDt/V0vpEx2w==", - "dev": true, - "requires": { - "@evocateur/npm-registry-fetch": "^4.0.0", - "bluebird": "^3.5.3", - "cacache": "^12.0.3", - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.5.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.4.4", - "npm-pick-manifest": "^3.0.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.3", - "safe-buffer": "^5.2.0", - "semver": "^5.7.0", - "ssri": "^6.0.1", - "tar": "^4.4.10", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "bluebird": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", - "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "p-try": "^2.0.0" } }, - "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", - "dev": true - }, - "get-stream": { + "p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "pump": "^3.0.0" + "p-limit": "^2.2.0" } }, - "glob": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", - "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" } }, - "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", "dev": true, "requires": { - "yallist": "^3.0.2" + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" } }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } } }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" } }, - "npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", + "realpath-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", + "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", + "dev": true + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" } }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, - "pump": { + "slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "ansi-regex": "^5.0.0" } }, - "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "path-parse": "^1.0.6" + "has-flag": "^4.0.0" } }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "glob": "^7.1.3" + "is-number": "^7.0.0" } }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "v8-to-istanbul": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", + "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", "dev": true, "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" }, "dependencies": { - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "dev": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "safe-buffer": "~5.1.1" } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { - "unique-slug": "^2.0.0" + "isexe": "^2.0.0" } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "@hapi/address": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.0.0.tgz", - "integrity": "sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw==" - }, - "@hapi/hoek": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-6.2.4.tgz", - "integrity": "sha512-HOJ20Kc93DkDVvjwHyHawPwPkX44sIrbXazAUDiUXaY2R9JwQGo2PhFfnQtdrsIe4igjG2fPgMra7NYw7qhy0A==" - }, - "@hapi/joi": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.0.tgz", - "integrity": "sha512-n6kaRQO8S+kepUTbXL9O/UOL788Odqs38/VOfoCrATDtTvyfiO3fgjlSRaNkHabpTLgM7qru9ifqXlXbXk8SeQ==", - "requires": { - "@hapi/address": "2.x.x", - "@hapi/hoek": "6.x.x", - "@hapi/marker": "1.x.x", - "@hapi/topo": "3.x.x" - } - }, - "@hapi/marker": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@hapi/marker/-/marker-1.0.0.tgz", - "integrity": "sha512-JOfdekTXnJexfE8PyhZFyHvHjt81rBFSAbTIRAhF2vv/2Y1JzoKsGqxH/GpZJoF7aEfYok8JVcAHmSz1gkBieA==" - }, - "@hapi/topo": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.2.tgz", - "integrity": "sha512-r+aumOqJ5QbD6aLPJWqVjMAPsx5pZKz+F5yPqXZ/WWG9JTtHbQqlzrJoknJ0iJxLj9vlXtmpSdjlkszseeG8OA==", - "requires": { - "@hapi/hoek": "8.x.x" - }, - "dependencies": { - "@hapi/hoek": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.0.2.tgz", - "integrity": "sha512-O6o6mrV4P65vVccxymuruucb+GhP2zl9NLCG8OdoFRS8BEGw3vwpPp20wpAtpbQQxz1CEUtmxJGgWhjq1XA3qw==" + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } } } }, - "@icons/material": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz", - "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", - "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "@jest/environment": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.5.0.tgz", + "integrity": "sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA==", "dev": true, "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@jest/fake-timers": "^25.5.0", + "@jest/types": "^25.5.0", + "jest-mock": "^25.5.0" }, "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "@jest/fake-timers": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.5.0.tgz", + "integrity": "sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "jest-message-util": "^25.5.0", + "jest-mock": "^25.5.0", + "jest-util": "^25.5.0", + "lolex": "^5.0.0" + } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", "dev": true, "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" } }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "color-convert": "^2.0.1" } }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "p-try": "^2.0.0" + "fill-range": "^7.0.1" } }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "jest-message-util": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", + "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^25.5.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^3.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "slash": "^3.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.5.0.tgz", + "integrity": "sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0" + } + }, + "jest-util": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", + "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "chalk": "^3.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "make-dir": "^3.0.0" + } + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } }, - "@jest/console": { + "@jest/fake-timers": { "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" }, "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" } }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==" + "@types/yargs": { + "version": "13.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", + "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", + "requires": { + "@types/yargs-parser": "*" + } } } }, - "@jest/core": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-25.5.4.tgz", - "integrity": "sha512-3uSo7laYxF00Dg/DMgbn4xMJKmDdWvZnf89n8Xj/5/AeQ2dOQmn6b6Hkj/MleyzZWXpwv+WSdYWl4cLsy2JsoA==", + "@jest/globals": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-25.5.2.tgz", + "integrity": "sha512-AgAS/Ny7Q2RCIj5kZ+0MuKM1wbF0WMLxbCVl/GOMoCNbODRdJ541IxJ98xnZdVSZXivKpJlNPIWa3QmY0l4CXA==", "dev": true, "requires": { - "@jest/console": "^25.5.0", - "@jest/reporters": "^25.5.1", - "@jest/test-result": "^25.5.0", - "@jest/transform": "^25.5.1", + "@jest/environment": "^25.5.0", "@jest/types": "^25.5.0", - "ansi-escapes": "^4.2.1", + "expect": "^25.5.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/reporters": { + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.3.0.tgz", + "integrity": "sha512-1u0ZBygs0C9DhdYgLCrRfZfNKQa+9+J7Uo+Z9z0RWLHzgsxhoG32lrmMOtUw48yR6bLNELdvzormwUqSk4H4Vg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^25.3.0", + "@jest/test-result": "^25.3.0", + "@jest/transform": "^25.3.0", + "@jest/types": "^25.3.0", "chalk": "^3.0.0", + "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^25.5.0", - "jest-config": "^25.5.4", - "jest-haste-map": "^25.5.1", - "jest-message-util": "^25.5.0", - "jest-regex-util": "^25.2.6", - "jest-resolve": "^25.5.1", - "jest-resolve-dependencies": "^25.5.4", - "jest-runner": "^25.5.4", - "jest-runtime": "^25.5.4", - "jest-snapshot": "^25.5.1", - "jest-util": "^25.5.0", - "jest-validate": "^25.5.0", - "jest-watcher": "^25.5.0", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "realpath-native": "^2.0.0", - "rimraf": "^3.0.0", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^25.3.0", + "jest-resolve": "^25.3.0", + "jest-util": "^25.3.0", + "jest-worker": "^25.2.6", + "node-notifier": "^6.0.0", "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "source-map": "^0.6.0", + "string-length": "^3.1.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^4.0.1" }, "dependencies": { "@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.3.0.tgz", + "integrity": "sha512-LvSDNqpmZIZyweFaEQ6wKY7CbexPitlsLHGJtcooNECo0An/w49rFhjCJzu6efeb6+a3ee946xss1Jcd9r03UQ==", "dev": true, "requires": { - "@jest/types": "^25.5.0", + "@jest/source-map": "^25.2.6", "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", + "jest-util": "^25.3.0", "slash": "^3.0.0" } }, - "@jest/reporters": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.5.1.tgz", - "integrity": "sha512-3jbd8pPDTuhYJ7vqiHXbSwTJQNavczPs+f1kRprRDxETeE3u6srJ+f0NPuwvOmk+lmunZzPkYWIFZDLHQPkviw==", + "@jest/source-map": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.6.tgz", + "integrity": "sha512-VuIRZF8M2zxYFGTEhkNSvQkUKafQro4y+mwUxy5ewRqs5N/ynSFUODYp3fy1zCnbCMy1pz3k+u57uCqx8QRSQQ==", "dev": true, "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^25.5.0", - "@jest/test-result": "^25.5.0", - "@jest/transform": "^25.5.1", - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^25.5.1", - "jest-resolve": "^25.5.1", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", - "node-notifier": "^6.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^3.1.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.1.3" + "callsites": "^3.0.0", + "graceful-fs": "^4.2.3", + "source-map": "^0.6.0" } }, "@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.3.0.tgz", + "integrity": "sha512-mqrGuiiPXl1ap09Mydg4O782F3ouDQfsKqtQzIjitpwv3t1cHDwCto21jThw6WRRE+dKcWQvLG70GpyLJICfGw==", "dev": true, "requires": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", + "@jest/console": "^25.3.0", + "@jest/types": "^25.3.0", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/transform": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.5.1.tgz", - "integrity": "sha512-Y8CEoVwXb4QwA6Y/9uDkn0Xfz0finGkieuV0xkdF9UtZGJeLukD5nLkaVrVsODB1ojRWlaoD0AJZpVHCSnJEvg==", + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.3.0.tgz", + "integrity": "sha512-W01p8kTDvvEX6kd0tJc7Y5VdYyFaKwNWy1HQz6Jqlhu48z/8Gxp+yFCDVj+H8Rc7ezl3Mg0hDaGuFVkmHOqirg==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^25.5.0", + "@jest/types": "^25.3.0", "babel-plugin-istanbul": "^6.0.0", "chalk": "^3.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^25.5.1", + "graceful-fs": "^4.2.3", + "jest-haste-map": "^25.3.0", "jest-regex-util": "^25.2.6", - "jest-util": "^25.5.0", + "jest-util": "^25.3.0", "micromatch": "^4.0.2", "pirates": "^4.0.1", "realpath-native": "^2.0.0", @@ -4765,47 +3356,13 @@ "write-file-atomic": "^3.0.0" } }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, - "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { + "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -4841,12 +3398,6 @@ "fill-range": "^7.0.1" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -4881,27 +3432,17 @@ "to-regex-range": "^5.0.1" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, "fsevents": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", - "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.0.tgz", + "integrity": "sha512-pKnaUh2TNvk+/egJdBw1h46LwyLx8BzEq+MGCf/RMCVfEHHsGOCWG00dqk91kUPPArIIwMBg9T/virxwzP03cA==", "dev": true, "optional": true }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, "has-flag": { @@ -4917,136 +3458,66 @@ "dev": true }, "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", "dev": true, - "optional": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", - "dev": true + "optional": true }, "jest-haste-map": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.5.1.tgz", - "integrity": "sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==", + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.3.0.tgz", + "integrity": "sha512-LjXaRa+F8wwtSxo9G+hHD/Cp63PPQzvaBL9XCVoJD2rrcJO0Zr2+YYzAFWWYJ5GlPUkoaJFJtOuk0sL6MJY80A==", "dev": true, "requires": { - "@jest/types": "^25.5.0", - "@types/graceful-fs": "^4.1.2", + "@jest/types": "^25.3.0", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-serializer": "^25.5.0", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", + "graceful-fs": "^4.2.3", + "jest-serializer": "^25.2.6", + "jest-util": "^25.3.0", + "jest-worker": "^25.2.6", "micromatch": "^4.0.2", "sane": "^4.0.3", "walker": "^1.0.7", "which": "^2.0.2" } }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, "jest-regex-util": { "version": "25.2.6", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", "dev": true }, - "jest-resolve": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", - "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "read-pkg-up": "^7.0.1", - "realpath-native": "^2.0.0", - "resolve": "^1.17.0", - "slash": "^3.0.0" - } - }, "jest-serializer": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.5.0.tgz", - "integrity": "sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4" - } + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.6.tgz", + "integrity": "sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ==", + "dev": true }, "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.3.0.tgz", + "integrity": "sha512-dc625P/KS/CpWTJJJxKc4bA3A6c+PJGBAqS8JTJqx4HqPoKNqXg/Ec8biL2Z1TabwK7E7Ilf0/ukSEXM1VwzNA==", "dev": true, "requires": { - "@jest/types": "^25.5.0", + "@jest/types": "^25.3.0", "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", "is-ci": "^2.0.0", "make-dir": "^3.0.0" } }, - "jest-validate": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", - "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "jest-get-type": "^25.2.6", - "leven": "^3.1.0", - "pretty-format": "^25.5.0" - } - }, "jest-worker": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", - "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.6.tgz", + "integrity": "sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA==", "dev": true, "requires": { "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5077,13 +3548,6 @@ "which": "^1.3.1" }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "optional": true - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -5096,120 +3560,16 @@ } } }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" }, "realpath-native": { "version": "2.0.0", @@ -5218,20 +3578,18 @@ "dev": true }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, + "version": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", "requires": { - "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "optional": true }, "slash": { "version": "3.0.0", @@ -5245,19 +3603,10 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -5272,40 +3621,6 @@ "is-number": "^7.0.0" } }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "v8-to-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", - "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5329,28 +3644,94 @@ } } }, - "@jest/environment": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.5.0.tgz", - "integrity": "sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA==", + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/yargs": { + "version": "13.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", + "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", + "requires": { + "@types/yargs-parser": "*" + } + } + } + }, + "@jest/test-sequencer": { + "version": "25.5.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.5.4.tgz", + "integrity": "sha512-pTJGEkSeg1EkCO2YWq6hbFvKNXk8ejqlxiOg1jBNLnWrgXOkdY6UmqZpwGFXNnRt9B8nO1uWMzLLZ4eCmhkPNA==", "dev": true, "requires": { - "@jest/fake-timers": "^25.5.0", - "@jest/types": "^25.5.0", - "jest-mock": "^25.5.0" + "@jest/test-result": "^25.5.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^25.5.1", + "jest-runner": "^25.5.4", + "jest-runtime": "^25.5.4" }, "dependencies": { - "@jest/fake-timers": { + "@jest/console": { "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.5.0.tgz", - "integrity": "sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", + "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", "dev": true, "requires": { "@jest/types": "^25.5.0", + "chalk": "^3.0.0", "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", "jest-util": "^25.5.0", - "lolex": "^5.0.0" + "slash": "^3.0.0" + } + }, + "@jest/test-result": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", + "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", + "dev": true, + "requires": { + "@jest/console": "^25.5.0", + "@jest/types": "^25.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" } }, "@jest/types": { @@ -5374,6 +3755,16 @@ "color-convert": "^2.0.1" } }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -5417,6 +3808,13 @@ "to-regex-range": "^5.0.1" } }, + "fsevents": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", + "dev": true, + "optional": true + }, "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", @@ -5435,6 +3833,27 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "jest-haste-map": { + "version": "25.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.5.1.tgz", + "integrity": "sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "@types/graceful-fs": "^4.1.2", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-serializer": "^25.5.0", + "jest-util": "^25.5.0", + "jest-worker": "^25.5.0", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7", + "which": "^2.0.2" + } + }, "jest-message-util": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", @@ -5451,13 +3870,13 @@ "stack-utils": "^1.0.1" } }, - "jest-mock": { + "jest-serializer": { "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.5.0.tgz", - "integrity": "sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA==", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.5.0.tgz", + "integrity": "sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==", "dev": true, "requires": { - "@jest/types": "^25.5.0" + "graceful-fs": "^4.2.4" } }, "jest-util": { @@ -5473,6 +3892,22 @@ "make-dir": "^3.0.0" } }, + "jest-worker": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", + "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "micromatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", @@ -5483,6 +3918,12 @@ "picomatch": "^2.0.5" } }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5506,94 +3947,102 @@ "requires": { "is-number": "^7.0.0" } - } - } - }, - "@jest/fake-timers": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", - "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", - "requires": { - "@jest/types": "^24.9.0", - "jest-message-util": "^24.9.0", - "jest-mock": "^24.9.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } }, - "@types/yargs": { - "version": "13.0.8", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", - "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "requires": { - "@types/yargs-parser": "*" + "isexe": "^2.0.0" } } } }, - "@jest/globals": { - "version": "25.5.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-25.5.2.tgz", - "integrity": "sha512-AgAS/Ny7Q2RCIj5kZ+0MuKM1wbF0WMLxbCVl/GOMoCNbODRdJ541IxJ98xnZdVSZXivKpJlNPIWa3QmY0l4CXA==", + "@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", "dev": true, "requires": { - "@jest/environment": "^25.5.0", - "@jest/types": "^25.5.0", - "expect": "^25.5.0" + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" }, "dependencies": { "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" + "chalk": "^4.0.0" } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", "dev": true, "requires": { - "color-convert": "^2.0.1" + "@types/istanbul-lib-report": "*" } }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "color-name": "~1.1.4" + "fill-range": "^7.0.1" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fsevents": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", + "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", + "dev": true, + "optional": true + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, "has-flag": { @@ -5602,266 +4051,65 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.1.2", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" } - } - } - }, - "@jest/reporters": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-25.3.0.tgz", - "integrity": "sha512-1u0ZBygs0C9DhdYgLCrRfZfNKQa+9+J7Uo+Z9z0RWLHzgsxhoG32lrmMOtUw48yR6bLNELdvzormwUqSk4H4Vg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^25.3.0", - "@jest/test-result": "^25.3.0", - "@jest/transform": "^25.3.0", - "@jest/types": "^25.3.0", - "chalk": "^3.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^25.3.0", - "jest-resolve": "^25.3.0", - "jest-util": "^25.3.0", - "jest-worker": "^25.2.6", - "node-notifier": "^6.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^3.1.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.0.1" - }, - "dependencies": { - "@jest/console": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.3.0.tgz", - "integrity": "sha512-LvSDNqpmZIZyweFaEQ6wKY7CbexPitlsLHGJtcooNECo0An/w49rFhjCJzu6efeb6+a3ee946xss1Jcd9r03UQ==", - "dev": true, - "requires": { - "@jest/source-map": "^25.2.6", - "chalk": "^3.0.0", - "jest-util": "^25.3.0", - "slash": "^3.0.0" - } - }, - "@jest/source-map": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.2.6.tgz", - "integrity": "sha512-VuIRZF8M2zxYFGTEhkNSvQkUKafQro4y+mwUxy5ewRqs5N/ynSFUODYp3fy1zCnbCMy1pz3k+u57uCqx8QRSQQ==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.3", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.3.0.tgz", - "integrity": "sha512-mqrGuiiPXl1ap09Mydg4O782F3ouDQfsKqtQzIjitpwv3t1cHDwCto21jThw6WRRE+dKcWQvLG70GpyLJICfGw==", - "dev": true, - "requires": { - "@jest/console": "^25.3.0", - "@jest/types": "^25.3.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/transform": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.3.0.tgz", - "integrity": "sha512-W01p8kTDvvEX6kd0tJc7Y5VdYyFaKwNWy1HQz6Jqlhu48z/8Gxp+yFCDVj+H8Rc7ezl3Mg0hDaGuFVkmHOqirg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^25.3.0", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^3.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.3", - "jest-haste-map": "^25.3.0", - "jest-regex-util": "^25.2.6", - "jest-util": "^25.3.0", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "realpath-native": "^2.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "babel-plugin-istanbul": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", - "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^4.0.0", - "test-exclude": "^6.0.0" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.0.tgz", - "integrity": "sha512-pKnaUh2TNvk+/egJdBw1h46LwyLx8BzEq+MGCf/RMCVfEHHsGOCWG00dqk91kUPPArIIwMBg9T/virxwzP03cA==", - "dev": true, - "optional": true - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-wsl": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", - "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", - "dev": true, - "optional": true }, - "jest-haste-map": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.3.0.tgz", - "integrity": "sha512-LjXaRa+F8wwtSxo9G+hHD/Cp63PPQzvaBL9XCVoJD2rrcJO0Zr2+YYzAFWWYJ5GlPUkoaJFJtOuk0sL6MJY80A==", + "jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", "dev": true, "requires": { - "@jest/types": "^25.3.0", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.3", - "jest-serializer": "^25.2.6", - "jest-util": "^25.3.0", - "jest-worker": "^25.2.6", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" + "@types/node": "*", + "graceful-fs": "^4.2.4" } }, - "jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true - }, - "jest-serializer": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.2.6.tgz", - "integrity": "sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ==", - "dev": true - }, "jest-util": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.3.0.tgz", - "integrity": "sha512-dc625P/KS/CpWTJJJxKc4bA3A6c+PJGBAqS8JTJqx4HqPoKNqXg/Ec8biL2Z1TabwK7E7Ilf0/ukSEXM1VwzNA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", "dev": true, "requires": { - "@jest/types": "^25.3.0", - "chalk": "^3.0.0", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", "is-ci": "^2.0.0", - "make-dir": "^3.0.0" + "micromatch": "^4.0.2" } }, "jest-worker": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.2.6.tgz", - "integrity": "sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", "dev": true, "requires": { + "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } @@ -5882,63 +4130,12 @@ "picomatch": "^2.0.5" } }, - "node-notifier": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-6.0.0.tgz", - "integrity": "sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw==", - "dev": true, - "optional": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^2.1.1", - "semver": "^6.3.0", - "shellwords": "^0.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "optional": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "realpath-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", - "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", - "dev": true - }, - "resolve": { - "version": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", - "requires": { - "path-parse": "^1.0.6" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "optional": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5952,9 +4149,9 @@ "dev": true }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -5969,15 +4166,6 @@ "is-number": "^7.0.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -5992,136 +4180,28 @@ } } }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", - "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.8", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", - "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@jest/test-sequencer": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.5.4.tgz", - "integrity": "sha512-pTJGEkSeg1EkCO2YWq6hbFvKNXk8ejqlxiOg1jBNLnWrgXOkdY6UmqZpwGFXNnRt9B8nO1uWMzLLZ4eCmhkPNA==", + "@jest/types": { + "version": "25.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.3.0.tgz", + "integrity": "sha512-UkaDNewdqXAmCDbN2GlUM6amDKS78eCqiw/UmF5nE0mmLTd6moJkiZJML/X52Ke3LH7Swhw883IRXq8o9nWjVw==", "dev": true, "requires": { - "@jest/test-result": "^25.5.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^25.5.1", - "jest-runner": "^25.5.4", - "jest-runtime": "^25.5.4" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" }, "dependencies": { - "@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", - "slash": "^3.0.0" - } - }, - "@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { + "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -6147,352 +4227,59 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz", - "integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==", - "dev": true, - "optional": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "jest-haste-map": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.5.1.tgz", - "integrity": "sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==", + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { - "@jest/types": "^25.5.0", - "@types/graceful-fs": "^4.1.2", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-serializer": "^25.5.0", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" + "has-flag": "^4.0.0" } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", + } + } + }, + "@lerna/add": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.18.0.tgz", + "integrity": "sha512-Z5EaQbBnJn1LEPb0zb0Q2o9T8F8zOnlCsj6JYpY6aSke17UUT7xx0QMN98iBK+ueUHKjN/vdFdYlNCYRSIdujA==", + "dev": true, + "requires": { + "@evocateur/pacote": "^9.6.3", + "@lerna/bootstrap": "3.18.0", + "@lerna/command": "3.18.0", + "@lerna/filter-options": "3.18.0", + "@lerna/npm-conf": "3.16.0", + "@lerna/validation-error": "3.13.0", + "dedent": "^0.7.0", + "npm-package-arg": "^6.1.0", + "p-map": "^2.1.0", + "semver": "^6.2.0" + }, + "dependencies": { + "npm-package-arg": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", + "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-serializer": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.5.0.tgz", - "integrity": "sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "jest-worker": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", - "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "@jest/transform": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", - "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.9.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.9.0", - "jest-regex-util": "^24.9.0", - "jest-util": "^24.9.0", - "micromatch": "^3.1.10", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" - }, - "dependencies": { - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.8", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.8.tgz", - "integrity": "sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "write-file-atomic": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", - "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - } - } - }, - "@jest/types": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.3.0.tgz", - "integrity": "sha512-UkaDNewdqXAmCDbN2GlUM6amDKS78eCqiw/UmF5nE0mmLTd6moJkiZJML/X52Ke3LH7Swhw883IRXq8o9nWjVw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@lerna/add": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/@lerna/add/-/add-3.18.0.tgz", - "integrity": "sha512-Z5EaQbBnJn1LEPb0zb0Q2o9T8F8zOnlCsj6JYpY6aSke17UUT7xx0QMN98iBK+ueUHKjN/vdFdYlNCYRSIdujA==", - "dev": true, - "requires": { - "@evocateur/pacote": "^9.6.3", - "@lerna/bootstrap": "3.18.0", - "@lerna/command": "3.18.0", - "@lerna/filter-options": "3.18.0", - "@lerna/npm-conf": "3.16.0", - "@lerna/validation-error": "3.13.0", - "dedent": "^0.7.0", - "npm-package-arg": "^6.1.0", - "p-map": "^2.1.0", - "semver": "^6.2.0" - }, - "dependencies": { - "npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", - "dev": true, - "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "hosted-git-info": "^2.7.1", + "osenv": "^0.1.5", + "semver": "^5.6.0", + "validate-npm-package-name": "^3.0.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "p-map": { @@ -8683,3228 +6470,492 @@ } }, "@mdx-js/loader": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@mdx-js/loader/-/loader-1.5.5.tgz", - "integrity": "sha512-2/2WX73qj79Kv2cYk14kQsN/aypAH3RPzuNMx1gxwZjj77G0N6tzhM9WFkEDM/SXjasWep03ZmSRb9d//b2D8w==", - "dev": true, - "requires": { - "@mdx-js/mdx": "^1.5.5", - "@mdx-js/react": "^1.5.5", - "loader-utils": "1.2.3" - }, - "dependencies": { - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - } - } - }, - "@mdx-js/mdx": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.5.5.tgz", - "integrity": "sha512-Xv1lJ+VWt8giWQrqf4GdIBxl08SfepfIWAnuuIzuR+wA59SaXDvkW6XFIvl8u495OQEB1eugMvq8l2XR8ZGr1A==", - "dev": true, - "requires": { - "@babel/core": "7.8.0", - "@babel/plugin-syntax-jsx": "7.8.0", - "@babel/plugin-syntax-object-rest-spread": "7.8.0", - "@mdx-js/util": "^1.5.5", - "babel-plugin-apply-mdx-type-prop": "^1.5.5", - "babel-plugin-extract-import-names": "^1.5.5", - "camelcase-css": "2.0.1", - "detab": "2.0.2", - "hast-util-raw": "5.0.1", - "lodash.uniq": "4.5.0", - "mdast-util-to-hast": "6.0.2", - "remark-mdx": "^1.5.5", - "remark-parse": "7.0.2", - "remark-squeeze-paragraphs": "3.0.4", - "style-to-object": "0.3.0", - "unified": "8.4.2", - "unist-builder": "1.0.4", - "unist-util-visit": "2.0.1" - }, - "dependencies": { - "@babel/core": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.0.tgz", - "integrity": "sha512-3rqPi/bv/Xfu2YzHvBz4XqMI1fKVwnhntPA1/fjoECrSjrhbOCxlTrbVu5gUtr8zkxW+RpkDOa/HCW93gzS2Dw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.0", - "@babel/generator": "^7.8.0", - "@babel/helpers": "^7.8.0", - "@babel/parser": "^7.8.0", - "@babel/template": "^7.8.0", - "@babel/traverse": "^7.8.0", - "@babel/types": "^7.8.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.0.tgz", - "integrity": "sha512-zLDUckAuKeOtxJhfNE0TlR7iEApb2u7EYRlh5cxKzq6A5VzUbYEdyJGJlug41jDbjRbHTtsLKZUnUcy/8V3xZw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.0.tgz", - "integrity": "sha512-dt89fDlkfkTrQcy5KavMQPyF2A6tR0kYp8HAnIoQv5hO34iAUffHghP/hMGd7Gf/+uYTmLQO0ar7peX1SUWyIA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "globals": { - "version": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - }, - "is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true - }, - "json5": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "remark-parse": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-7.0.2.tgz", - "integrity": "sha512-9+my0lQS80IQkYXsMA8Sg6m9QfXYJBnXjWYN5U+kFc5/n69t+XZVXU/ZBYr3cYH8FheEGf1v87rkFDhJ8bVgMA==", - "dev": true, - "requires": { - "collapse-white-space": "^1.0.2", - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-whitespace-character": "^1.0.0", - "is-word-character": "^1.0.0", - "markdown-escapes": "^1.0.0", - "parse-entities": "^1.1.0", - "repeat-string": "^1.5.4", - "state-toggle": "^1.0.0", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0", - "xtend": "^4.0.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "unified": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-8.4.2.tgz", - "integrity": "sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA==", - "dev": true, - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^2.0.0", - "trough": "^1.0.0", - "vfile": "^4.0.0" - } - }, - "unist-util-is": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.1.tgz", - "integrity": "sha512-7NYjErP4LJtkEptPR22wO5RsCPnHZZrop7t2SoQzjvpFedCFer4WW8ujj9GI5DkUX7yVcffXLjoURf6h2QUv6Q==", - "dev": true - }, - "unist-util-stringify-position": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.2.tgz", - "integrity": "sha512-nK5n8OGhZ7ZgUwoUbL8uiVRwAbZyzBsB/Ddrlbu6jwwubFza4oe15KlyEaLNMXQW1svOQq4xesUeqA85YrIUQA==", - "dev": true, - "requires": { - "@types/unist": "^2.0.2" - } - }, - "unist-util-visit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.1.tgz", - "integrity": "sha512-bEDa5S/O8WRDeI1mLaMoKuFFi89AjF+UAoMNxO+bbVdo06q+53Vhq4iiv1PenL6Rx1ZxIpXIzqZoc5HD2I1oMA==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - } - }, - "unist-util-visit-parents": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.0.1.tgz", - "integrity": "sha512-umEOTkm6/y1gIqPrqet55mYqlvGXCia/v1FSc5AveLAI7jFmOAIbqiwcHcviLcusAkEQt1bq2hixCKO9ltMb2Q==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - } - }, - "vfile": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.0.2.tgz", - "integrity": "sha512-yhoTU5cDMSsaeaMfJ5g0bUKYkYmZhAh9fn9TZicxqn+Cw4Z439il2v3oT9S0yjlpqlI74aFOQCt3nOV+pxzlkw==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "replace-ext": "1.0.0", - "unist-util-stringify-position": "^2.0.0", - "vfile-message": "^2.0.0" - } - }, - "vfile-message": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.2.tgz", - "integrity": "sha512-gNV2Y2fDvDOOqq8bEe7cF3DXU6QgV4uA9zMR2P8tix11l1r7zju3zry3wZ8sx+BEfuO6WQ7z2QzfWTvqHQiwsA==", - "dev": true, - "requires": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^2.0.0" - } - } - } - }, - "@mdx-js/react": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.5.5.tgz", - "integrity": "sha512-Qwvri4zyU9ZbhhXsH0wfSZ/J9b8mARRTB6GSCTnyKRffO2CaQXl9oLsvRAeQSLRei/onEARc+RexH+jMeNS1rw==", - "dev": true - }, - "@mdx-js/util": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.5.5.tgz", - "integrity": "sha512-IudQkyZuM8T1CrSX9r0ShPXCABjtEtyrV4lxQqhKAwFqw1aYpy/5LOZhitMLoJTybZPVdPotuh+zjqYy9ZOSbA==", - "dev": true - }, - "@mrmlnc/readdir-enhanced": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", - "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", - "dev": true, - "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.2.tgz", - "integrity": "sha512-wrIBsjA5pl13f0RN4Zx4FNWmU71lv03meGKnqRUoCyan17s4V3WL92f3w3AIuWbNnpcrQyFBU5qMavJoB8d27w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.2", - "run-parallel": "^1.1.9" - }, - "dependencies": { - "@nodelib/fs.stat": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz", - "integrity": "sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw==", - "dev": true - } - } - }, - "@nodelib/fs.stat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", - "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.3.tgz", - "integrity": "sha512-l6t8xEhfK9Sa4YO5mIRdau7XSOADfmh3jCr0evNHdY+HNkW6xuQhgMH7D73VV6WpZOagrW0UludvMTiifiwTfA==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.2", - "fastq": "^1.6.0" - } - }, - "@npmcli/move-file": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", - "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", - "dev": true, - "requires": { - "mkdirp": "^1.0.4" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "@octokit/auth-token": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", - "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.0" - } - }, - "@octokit/endpoint": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.1.2.tgz", - "integrity": "sha512-bBGGmcRFq1x0jrB29G/9KjYmO3cdHfk3476B2JOHRvLsNw1Pn3l+ZvbiqtcO9qAS4Ti+zFedLB84ziHZRZclQA==", - "dev": true, - "requires": { - "deepmerge": "3.2.0", - "is-plain-object": "^3.0.0", - "universal-user-agent": "^2.1.0", - "url-template": "^2.0.8" - }, - "dependencies": { - "deepmerge": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz", - "integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==", - "dev": true - }, - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - } - } - }, - "@octokit/graphql": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-2.1.3.tgz", - "integrity": "sha512-XoXJqL2ondwdnMIW3wtqJWEwcBfKk37jO/rYkoxNPEVeLBDGsGO1TCWggrAlq3keGt/O+C/7VepXnukUxwt5vA==", - "dev": true, - "requires": { - "@octokit/request": "^5.0.0", - "universal-user-agent": "^2.0.3" - }, - "dependencies": { - "@octokit/request": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.0.2.tgz", - "integrity": "sha512-z1BQr43g4kOL4ZrIVBMHwi68Yg9VbkRUyuAgqCp1rU3vbYa69+2gIld/+gHclw15bJWQnhqqyEb7h5a5EqgZ0A==", - "dev": true, - "requires": { - "@octokit/endpoint": "^5.1.0", - "@octokit/request-error": "^1.0.1", - "deprecation": "^2.0.0", - "is-plain-object": "^3.0.0", - "node-fetch": "^2.3.0", - "once": "^1.4.0", - "universal-user-agent": "^3.0.0" - }, - "dependencies": { - "universal-user-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz", - "integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==", - "dev": true, - "requires": { - "os-name": "^3.0.0" - } - } - } - }, - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", - "dev": true - } - } - }, - "@octokit/plugin-enterprise-rest": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-3.6.2.tgz", - "integrity": "sha512-3wF5eueS5OHQYuAEudkpN+xVeUsg8vYEMMenEzLphUZ7PRZ8OJtDcsreL3ad9zxXmBbaFWzLmFcdob5CLyZftA==", - "dev": true - }, - "@octokit/request": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-4.1.0.tgz", - "integrity": "sha512-RvpQAba4i+BNH0z8i0gPRc1ShlHidj4puQjI/Tno6s+Q3/Mzb0XRSHJiOhpeFrZ22V7Mwjq1E7QS27P5CgpWYA==", - "dev": true, - "requires": { - "@octokit/endpoint": "^5.1.0", - "@octokit/request-error": "^1.0.1", - "deprecation": "^2.0.0", - "is-plain-object": "^3.0.0", - "node-fetch": "^2.3.0", - "once": "^1.4.0", - "universal-user-agent": "^2.1.0" - }, - "dependencies": { - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", - "dev": true - } - } - }, - "@octokit/request-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.2.tgz", - "integrity": "sha512-T9swMS/Vc4QlfWrvyeSyp/GjhXtYaBzCcibjGywV4k4D2qVrQKfEMPy8OxMDEj7zkIIdpHwqdpVbKCvnUPqkXw==", - "dev": true, - "requires": { - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "16.26.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.26.0.tgz", - "integrity": "sha512-NBpzre44ZAQWZhlH+zUYTgqI0pHN+c9rNj4d+pCydGEiKTGc1HKmoTghEUyr9GxazDyoAvmpx9nL0I7QS1Olvg==", - "dev": true, - "requires": { - "@octokit/request": "^4.0.1", - "@octokit/request-error": "^1.0.2", - "atob-lite": "^2.0.0", - "before-after-hook": "^1.4.0", - "btoa-lite": "^1.0.0", - "deprecation": "^2.0.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lodash.uniq": "^4.5.0", - "octokit-pagination-methods": "^1.1.0", - "once": "^1.4.0", - "universal-user-agent": "^2.0.0", - "url-template": "^2.0.8" - } - }, - "@octokit/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.5.0.tgz", - "integrity": "sha512-KEnLwOfdXzxPNL34fj508bhi9Z9cStyN7qY1kOfVahmqtAfrWw6Oq3P4R+dtsg0lYtZdWBpUrS/Ixmd5YILSww==", - "dev": true, - "requires": { - "@types/node": ">= 8" - } - }, - "@octokit/webhooks": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-7.1.0.tgz", - "integrity": "sha512-kHyYkJkqY/wiP/hp0IT9FhkY5PhnV01co16V2YMRP/Zgnk3Vsy3U5iLAaP6U/0eRIlz5T4LSvkrcfNlfSb3cVQ==", - "dev": true, - "requires": { - "debug": "^4.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@popperjs/core": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.2.tgz", - "integrity": "sha512-JlGTGRYHC2QK+DDbePyXdBdooxFq2+noLfWpRqJtkxcb/oYWzOF0kcbfvvbWrwevCC1l6hLUg1wHYT+ona5BWQ==" - }, - "@reach/router": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.2.1.tgz", - "integrity": "sha512-kTaX08X4g27tzIFQGRukaHmNbtMYDS3LEWIS8+l6OayGIw6Oyo1HIF/JzeuR2FoF9z6oV+x/wJSVSq4v8tcUGQ==", - "dev": true, - "requires": { - "create-react-context": "^0.2.1", - "invariant": "^2.2.3", - "prop-types": "^15.6.1", - "react-lifecycles-compat": "^3.0.4", - "warning": "^3.0.0" - } - }, - "@react-native-community/blur": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-native-community/blur/-/blur-3.6.0.tgz", - "integrity": "sha512-GtDBhpX2pQcjl4VopOC8FktrVufrEfYRwVeMQ2WWckqKIv2BdwvlvWvj88L1WmEdBr9UNcm3rtgz+d+YXkmirA==", - "requires": { - "prop-types": "^15.5.10" - } - }, - "@react-native-community/cli-debugger-ui": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-3.0.0.tgz", - "integrity": "sha512-m3X+iWLsK/H7/b7PpbNO33eQayR/+M26la4ZbYe1KRke5Umg4PIWsvg21O8Tw4uJcY8LA5hsP+rBi/syBkBf0g==", - "requires": { - "serve-static": "^1.13.1" - } - }, - "@react-native-community/cli-platform-android": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-3.0.3.tgz", - "integrity": "sha512-rNO9DmRiVhB6aP2DVUjEJv7ecriTARDZND88ny3xNVUkrD1Y+zwF6aZu3eoT52VXOxLCSLiJzz19OiyGmfqxYg==", - "requires": { - "@react-native-community/cli-tools": "^3.0.0", - "chalk": "^2.4.2", - "execa": "^1.0.0", - "jetifier": "^1.6.2", - "logkitty": "^0.6.0", - "slash": "^3.0.0", - "xmldoc": "^1.1.2" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "requires": { - "pump": "^3.0.0" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - } - } - }, - "@react-native-community/cli-platform-ios": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-3.0.0.tgz", - "integrity": "sha512-QoNVlDj8eMXRZk9uktPFsctHurQpv9jKmiu6mQii4NEtT2npE7g1hbWpRNojutBsfgmCdQGDHd9uB54eeCnYgg==", - "requires": { - "@react-native-community/cli-tools": "^3.0.0", - "chalk": "^2.4.2", - "js-yaml": "^3.13.1", - "xcode": "^2.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@react-native-community/cli-tools": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-3.0.0.tgz", - "integrity": "sha512-8IhQKZdf3E4CR8T7HhkPGgorot/cLkRDgneJFDSWk/wCYZAuUh4NEAdumQV7N0jLSMWX7xxiWUPi94lOBxVY9g==", - "requires": { - "chalk": "^2.4.2", - "lodash": "^4.17.5", - "mime": "^2.4.1", - "node-fetch": "^2.5.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - } - } - }, - "@react-native-community/cli-types": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-3.0.0.tgz", - "integrity": "sha512-ng6Tm537E/M42GjE4TRUxQyL8sRfClcL7bQWblOCoxPZzJ2J3bdALsjeG3vDnVCIfI/R0AeFalN9KjMt0+Z/Zg==" - }, - "@react-native-community/masked-view": { - "version": "git+https://github.com/wordpress-mobile/react-native-masked-view.git#f65a51a3320e58404d7f38d967bfd1f42b439ca9", - "from": "git+https://github.com/wordpress-mobile/react-native-masked-view.git#f65a51a3320e58404d7f38d967bfd1f42b439ca9" - }, - "@react-native-community/slider": { - "version": "git+https://github.com/wordpress-mobile/react-native-slider.git#d263ff16cdd9fb7352b354342522ff030f220f42", - "from": "git+https://github.com/wordpress-mobile/react-native-slider.git#d263ff16cdd9fb7352b354342522ff030f220f42" - }, - "@react-navigation/core": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.12.0.tgz", - "integrity": "sha512-CTmYrFXCZwInN40CpEzkPxhrpzujj20qvsUgpH05+oO1flwsnaJsyBfYawIcTS62/1/Z6SAM7iW5PbKk+qw9iQ==", - "requires": { - "@react-navigation/routers": "^5.4.9", - "escape-string-regexp": "^4.0.0", - "nanoid": "^3.1.9", - "query-string": "^6.13.1", - "react-is": "^16.13.0", - "use-subscription": "^1.4.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "query-string": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz", - "integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==", - "requires": { - "decode-uri-component": "^0.2.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" - } - } - }, - "@react-navigation/native": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.7.0.tgz", - "integrity": "sha512-a2JBOdRB3q20Jdc5hF8shR4Dk+ZmjF2Rr9RviErtARztu08lU+jcb1gK6c31OKL37JDuaS3xexE9Cb7dYeMy3Q==", - "requires": { - "@react-navigation/core": "^5.12.0", - "nanoid": "^3.1.9" - } - }, - "@react-navigation/routers": { - "version": "5.4.9", - "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.4.9.tgz", - "integrity": "sha512-dYD5qrIKUmuBEp+O98hB0tDYpEsGQgCQFQgMEoFKBmVVhx2JnJJ1zxRjU7xWcCU4VdBA8IOowgHQHJsVNKYyrg==", - "requires": { - "nanoid": "^3.1.9" - } - }, - "@react-navigation/stack": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.6.2.tgz", - "integrity": "sha512-51Aasxg8j2eKxz4mhA0ajJXrhAyJQkk2iiNE511zcqJ3tlfxv/h70Eej3PetnbbHFMOwNsEwc2GjB3OnfQcxjQ==", - "requires": { - "color": "^3.1.2", - "react-native-iphone-x-helper": "^1.2.1" - } - }, - "@samverschueren/stream-to-observable": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", - "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", - "dev": true, - "requires": { - "any-observable": "^0.3.0" - } - }, - "@sindresorhus/is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.0.tgz", - "integrity": "sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", - "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@storybook/addon-a11y": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-5.3.2.tgz", - "integrity": "sha512-JOx4GZl8ZDxPlYnMTiBNnzE6GQzojlJobcbJiXWXJP1P7YnOL7IwVS5zNxtGAtN0npL2I0ZhhSSsaG2Si5IoLQ==", - "dev": true, - "requires": { - "@storybook/addons": "5.3.2", - "@storybook/api": "5.3.2", - "@storybook/client-logger": "5.3.2", - "@storybook/components": "5.3.2", - "@storybook/core-events": "5.3.2", - "@storybook/theming": "5.3.2", - "axe-core": "^3.3.2", - "core-js": "^3.0.1", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "react": "^16.8.3", - "react-redux": "^7.0.2", - "react-sizeme": "^2.5.2", - "redux": "^4.0.1", - "ts-dedent": "^1.1.0", - "util-deprecate": "^1.0.2" - }, - "dependencies": { - "redux": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", - "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "symbol-observable": "^1.2.0" - } - } - } - }, - "@storybook/addon-docs": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-5.3.2.tgz", - "integrity": "sha512-gGxDYP2VMl2Js3eSR0FM/QCf8PULLoZq57LIA8mxX38oxOydC3mIG1970sUM/g6MDtP4TV4F6rjd++eY4Gc07g==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.2", - "@babel/plugin-transform-react-jsx": "^7.3.0", - "@egoist/vue-to-react": "^1.1.0", - "@jest/transform": "^24.9.0", - "@mdx-js/loader": "^1.5.1", - "@mdx-js/mdx": "^1.5.1", - "@mdx-js/react": "^1.5.1", - "@storybook/addons": "5.3.2", - "@storybook/api": "5.3.2", - "@storybook/components": "5.3.2", - "@storybook/core-events": "5.3.2", - "@storybook/csf": "0.0.1", - "@storybook/postinstall": "5.3.2", - "@storybook/source-loader": "5.3.2", - "@storybook/theming": "5.3.2", - "acorn": "^7.1.0", - "acorn-jsx": "^5.1.0", - "acorn-walk": "^7.0.0", - "core-js": "^3.0.1", - "doctrine": "^3.0.0", - "escodegen": "^1.12.0", - "global": "^4.3.2", - "html-tags": "^3.1.0", - "js-string-escape": "^1.0.1", - "lodash": "^4.17.15", - "prop-types": "^15.7.2", - "react-element-to-jsx-string": "^14.1.0", - "remark-external-links": "^5.0.0", - "remark-slug": "^5.1.2", - "ts-dedent": "^1.1.0", - "util-deprecate": "^1.0.2", - "vue-docgen-api": "^4.1.0", - "vue-docgen-loader": "^1.3.0-beta.0" - }, - "dependencies": { - "acorn": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz", - "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==", - "dev": true - }, - "acorn-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", - "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", - "dev": true - }, - "source-map": { - "version": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "@storybook/addon-knobs": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-knobs/-/addon-knobs-5.3.2.tgz", - "integrity": "sha512-5enTXIoVzepQDDe55wTQOfqT1cOwngjlakKar7lYWqww2ixVJJ3xMZ9dLqnYZkROMsN67sDwmuawvze+W5lbEA==", - "dev": true, - "requires": { - "@storybook/addons": "5.3.2", - "@storybook/api": "5.3.2", - "@storybook/client-api": "5.3.2", - "@storybook/components": "5.3.2", - "@storybook/core-events": "5.3.2", - "@storybook/theming": "5.3.2", - "@types/react-color": "^3.0.1", - "copy-to-clipboard": "^3.0.8", - "core-js": "^3.0.1", - "escape-html": "^1.0.3", - "fast-deep-equal": "^2.0.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "prop-types": "^15.7.2", - "qs": "^6.6.0", - "react-color": "^2.17.0", - "react-lifecycles-compat": "^3.0.4", - "react-select": "^3.0.8" - }, - "dependencies": { - "qs": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz", - "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==", - "dev": true - } - } - }, - "@storybook/addon-storysource": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-5.3.2.tgz", - "integrity": "sha512-wLbYfwM/XX0HbXvJNWpTrA+Z61EIZQafEDClFeM0OUSoG7PkXzLEHQb5UC0me1an9veZfgejjEqn2NmMkAAV0Q==", - "dev": true, - "requires": { - "@storybook/addons": "5.3.2", - "@storybook/components": "5.3.2", - "@storybook/router": "5.3.2", - "@storybook/source-loader": "5.3.2", - "@storybook/theming": "5.3.2", - "core-js": "^3.0.1", - "estraverse": "^4.2.0", - "loader-utils": "^1.2.3", - "prettier": "^1.16.4", - "prop-types": "^15.7.2", - "react-syntax-highlighter": "^11.0.2", - "regenerator-runtime": "^0.13.3", - "util-deprecate": "^1.0.2" - }, - "dependencies": { - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", - "dev": true - } - } - }, - "@storybook/addon-viewport": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-5.3.2.tgz", - "integrity": "sha512-4W1pxVymFIcFO535Q2h7gXXdGeVluvVhHJGU58fFtrKVAN1UOgMprMe/6PnisvDtyWwqUbEHwxxghoaVF28xXA==", - "dev": true, - "requires": { - "@storybook/addons": "5.3.2", - "@storybook/api": "5.3.2", - "@storybook/client-logger": "5.3.2", - "@storybook/components": "5.3.2", - "@storybook/core-events": "5.3.2", - "@storybook/theming": "5.3.2", - "core-js": "^3.0.1", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "prop-types": "^15.7.2", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/addons": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-5.3.2.tgz", - "integrity": "sha512-vSByVK0yQJ8kjYmw8Ayj7cmCIOEdQKNjV7OLTXmLMpde+hUBqmDbCzwT0m5kZjzSFRTvB8bz0WBPavE02ZBN8A==", - "dev": true, - "requires": { - "@storybook/api": "5.3.2", - "@storybook/channels": "5.3.2", - "@storybook/client-logger": "5.3.2", - "@storybook/core-events": "5.3.2", - "core-js": "^3.0.1", - "global": "^4.3.2", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/api": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/api/-/api-5.3.2.tgz", - "integrity": "sha512-4XK1+RXCb0HnQsmDzQn6H9SljibV/Mi/vIixlcprcTZ5sBPhaMumt/T3d2rUEyP7Lpm4/7HMsuuhkkkXcjPJJw==", - "dev": true, - "requires": { - "@reach/router": "^1.2.1", - "@storybook/channels": "5.3.2", - "@storybook/client-logger": "5.3.2", - "@storybook/core-events": "5.3.2", - "@storybook/csf": "0.0.1", - "@storybook/router": "5.3.2", - "@storybook/theming": "5.3.2", - "@types/reach__router": "^1.2.3", - "core-js": "^3.0.1", - "fast-deep-equal": "^2.0.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "memoizerific": "^1.11.3", - "prop-types": "^15.6.2", - "react": "^16.8.3", - "semver": "^6.0.0", - "shallow-equal": "^1.1.0", - "store2": "^2.7.1", - "telejson": "^3.2.0", - "util-deprecate": "^1.0.2" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "shallow-equal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", - "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", - "dev": true - } - } - }, - "@storybook/channel-postmessage": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-5.3.2.tgz", - "integrity": "sha512-2SnYDtzBhtBEu8tKn+2QzewdW1/R7xpBY3FmMqhY+vdBCtFFSqdiFuaKbGUQRKKLH+mQEKc7DBVf/U+HJlKPyg==", - "dev": true, - "requires": { - "@storybook/channels": "5.3.2", - "@storybook/client-logger": "5.3.2", - "core-js": "^3.0.1", - "global": "^4.3.2", - "telejson": "^3.2.0" - } - }, - "@storybook/channels": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-5.3.2.tgz", - "integrity": "sha512-eVHGrFCsQ6sO/H1cdEvPoBCsEndIbkbvOEMdI3Fah6jnWLb9fXJwM7f+UKkMPUOflMvzyO17dfXtigSU8uJMoA==", - "dev": true, - "requires": { - "core-js": "^3.0.1" - } - }, - "@storybook/client-api": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-5.3.2.tgz", - "integrity": "sha512-zfnfy46YUBukPpztAB9nZYJ3RazZx1a+0ZCCMEK6KuZkFSOVcYeqJe2PRAbiRVdCG+fG05OaSWw/AXraHZCE8Q==", - "dev": true, - "requires": { - "@storybook/addons": "5.3.2", - "@storybook/channel-postmessage": "5.3.2", - "@storybook/channels": "5.3.2", - "@storybook/client-logger": "5.3.2", - "@storybook/core-events": "5.3.2", - "@storybook/csf": "0.0.1", - "core-js": "^3.0.1", - "eventemitter3": "^4.0.0", - "global": "^4.3.2", - "is-plain-object": "^3.0.0", - "lodash": "^4.17.15", - "memoizerific": "^1.11.3", - "qs": "^6.6.0", - "stable": "^0.1.8", - "ts-dedent": "^1.1.0", - "util-deprecate": "^1.0.2" - }, - "dependencies": { - "eventemitter3": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", - "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", - "dev": true - }, - "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - }, - "qs": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz", - "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==", - "dev": true - } - } - }, - "@storybook/client-logger": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-5.3.2.tgz", - "integrity": "sha512-sLP3MZ1LPQg/YR9yDoJq2S9/iM+yuD925RR0tD1ItQksKZA2gsI1CSf6zRPBluf7UwS7ACsotLlIPJg+czvr4A==", - "dev": true, - "requires": { - "core-js": "^3.0.1" - } - }, - "@storybook/components": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-5.3.2.tgz", - "integrity": "sha512-JtVPz03vVDI9/QqUbYitTQhLGxXHN0+49y97G527Xn3y6S71DkdQAVgYqUNnsUpPSyNLESgvAV39HRvzy/LSyw==", - "dev": true, - "requires": { - "@storybook/client-logger": "5.3.2", - "@storybook/theming": "5.3.2", - "@types/react-syntax-highlighter": "11.0.2", - "@types/react-textarea-autosize": "^4.3.3", - "core-js": "^3.0.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "markdown-to-jsx": "^6.9.1", - "memoizerific": "^1.11.3", - "polished": "^3.3.1", - "popper.js": "^1.14.7", - "prop-types": "^15.7.2", - "react": "^16.8.3", - "react-dom": "^16.8.3", - "react-focus-lock": "^2.1.0", - "react-helmet-async": "^1.0.2", - "react-popper-tooltip": "^2.8.3", - "react-syntax-highlighter": "^11.0.2", - "react-textarea-autosize": "^7.1.0", - "simplebar-react": "^1.0.0-alpha.6", - "ts-dedent": "^1.1.0" - } - }, - "@storybook/core": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.0.21.tgz", - "integrity": "sha512-/Et5NLabB12dnuPdhHDA/Q1pj0Mm2DGdL3KiLO4IC2VZeICCLGmU3/EGJBgjLK+anQ59pkclOiQ8i9eMXFiJ6A==", - "dev": true, - "requires": { - "@babel/plugin-proposal-class-properties": "^7.8.3", - "@babel/plugin-proposal-decorators": "^7.8.3", - "@babel/plugin-proposal-export-default-from": "^7.8.3", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.1", - "@babel/plugin-proposal-object-rest-spread": "^7.9.6", - "@babel/plugin-proposal-optional-chaining": "^7.10.1", - "@babel/plugin-proposal-private-methods": "^7.8.3", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.8.3", - "@babel/plugin-transform-block-scoping": "^7.8.3", - "@babel/plugin-transform-classes": "^7.9.5", - "@babel/plugin-transform-destructuring": "^7.9.5", - "@babel/plugin-transform-for-of": "^7.9.0", - "@babel/plugin-transform-parameters": "^7.9.5", - "@babel/plugin-transform-shorthand-properties": "^7.8.3", - "@babel/plugin-transform-spread": "^7.8.3", - "@babel/plugin-transform-template-literals": "^7.8.3", - "@babel/preset-env": "^7.9.6", - "@babel/preset-react": "^7.8.3", - "@babel/preset-typescript": "^7.9.0", - "@babel/register": "^7.10.5", - "@storybook/addons": "6.0.21", - "@storybook/api": "6.0.21", - "@storybook/channel-postmessage": "6.0.21", - "@storybook/channels": "6.0.21", - "@storybook/client-api": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/components": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/csf": "0.0.1", - "@storybook/node-logger": "6.0.21", - "@storybook/router": "6.0.21", - "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.0.21", - "@storybook/ui": "6.0.21", - "@types/glob-base": "^0.3.0", - "@types/micromatch": "^4.0.1", - "@types/node-fetch": "^2.5.4", - "airbnb-js-shims": "^2.2.1", - "ansi-to-html": "^0.6.11", - "autoprefixer": "^9.7.2", - "babel-loader": "^8.0.6", - "babel-plugin-emotion": "^10.0.20", - "babel-plugin-macros": "^2.8.0", - "babel-preset-minify": "^0.5.0 || 0.6.0-alpha.5", - "better-opn": "^2.0.0", - "boxen": "^4.1.0", - "case-sensitive-paths-webpack-plugin": "^2.2.0", - "chalk": "^4.0.0", - "cli-table3": "0.6.0", - "commander": "^5.0.0", - "core-js": "^3.0.1", - "css-loader": "^3.5.3", - "detect-port": "^1.3.0", - "dotenv-webpack": "^1.7.0", - "ejs": "^3.1.2", - "express": "^4.17.0", - "file-loader": "^6.0.0", - "file-system-cache": "^1.0.5", - "find-up": "^4.1.0", - "fork-ts-checker-webpack-plugin": "^4.1.4", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "glob-base": "^0.3.0", - "glob-promise": "^3.4.0", - "global": "^4.3.2", - "html-webpack-plugin": "^4.2.1", - "inquirer": "^7.0.0", - "interpret": "^2.0.0", - "ip": "^1.1.5", - "json5": "^2.1.1", - "lazy-universal-dotenv": "^3.0.1", - "micromatch": "^4.0.2", - "node-fetch": "^2.6.0", - "pkg-dir": "^4.2.0", - "pnp-webpack-plugin": "1.6.4", - "postcss-flexbugs-fixes": "^4.1.0", - "postcss-loader": "^3.0.0", - "pretty-hrtime": "^1.0.3", - "qs": "^6.6.0", - "raw-loader": "^4.0.1", - "react-dev-utils": "^10.0.0", - "regenerator-runtime": "^0.13.3", - "resolve-from": "^5.0.0", - "serve-favicon": "^2.5.0", - "shelljs": "^0.8.3", - "stable": "^0.1.8", - "style-loader": "^1.2.1", - "terser-webpack-plugin": "^3.0.0", - "ts-dedent": "^1.1.1", - "unfetch": "^4.1.0", - "url-loader": "^4.0.0", - "util-deprecate": "^1.0.2", - "webpack": "^4.43.0", - "webpack-dev-middleware": "^3.7.0", - "webpack-hot-middleware": "^2.25.0", - "webpack-virtual-modules": "^0.2.2" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/compat-data": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz", - "integrity": "sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", - "dev": true, - "requires": { - "@babel/types": "^7.11.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" - } - }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz", - "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz", - "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } - } - }, - "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", - "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", - "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz", - "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.4" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz", - "integrity": "sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", - "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", - "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", - "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/preset-env": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz", - "integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.11.0", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.11.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.11.0", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.11.5", - "browserslist": "^4.12.0", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" - }, - "dependencies": { - "@babel/plugin-proposal-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", - "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", - "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", - "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", - "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/register": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.11.5.tgz", - "integrity": "sha512-CAml0ioKX+kOAvBQDHa/+t1fgOt3qkTIz0TrRtRAT6XY0m5qYZXR85k6/sLCNPMGhYDlCFHCYuU0ybTJbvlC6w==", - "dev": true, - "requires": { - "find-cache-dir": "^2.0.0", - "lodash": "^4.17.19", - "make-dir": "^2.1.0", - "pirates": "^4.0.0", - "source-map-support": "^0.5.16" - } - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", - "dev": true, - "requires": { - "@emotion/memoize": "0.7.4" - } - }, - "@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", - "dev": true - }, - "@reach/router": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.3.4.tgz", - "integrity": "sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA==", - "dev": true, - "requires": { - "create-react-context": "0.3.0", - "invariant": "^2.2.3", - "prop-types": "^15.6.1", - "react-lifecycles-compat": "^3.0.4" - } - }, - "@storybook/addons": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.0.21.tgz", - "integrity": "sha512-yDttNLc3vXqBxwK795ykgzTC6MpvuXDQuF4LHSlHZQe6wsMu1m3fljnbYdafJWdx6cNZwUblU3KYcR11PqhkPg==", - "dev": true, - "requires": { - "@storybook/api": "6.0.21", - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/router": "6.0.21", - "@storybook/theming": "6.0.21", - "core-js": "^3.0.1", - "global": "^4.3.2", - "regenerator-runtime": "^0.13.3" - } - }, - "@storybook/api": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.0.21.tgz", - "integrity": "sha512-cRRGf/KGFwYiDouTouEcDdp45N1AbYnAfvLqYZ3KuUTGZ+CiU/PN/vavkp07DQeM4FIQO8TLhzHdsLFpLT7Lkw==", - "dev": true, - "requires": { - "@reach/router": "^1.3.3", - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/csf": "0.0.1", - "@storybook/router": "6.0.21", - "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.0.21", - "@types/reach__router": "^1.3.5", - "core-js": "^3.0.1", - "fast-deep-equal": "^3.1.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "memoizerific": "^1.11.3", - "react": "^16.8.3", - "regenerator-runtime": "^0.13.3", - "store2": "^2.7.1", - "telejson": "^5.0.2", - "ts-dedent": "^1.1.1", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/channel-postmessage": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.0.21.tgz", - "integrity": "sha512-ArRnoaS+b7qpAku/SO27z/yjRDCXb37mCPYGX0ntPbiQajootUbGO7otfnjFkaP44hCEC9uDYlOfMU1hYU1N6A==", - "dev": true, - "requires": { - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/core-events": "6.0.21", - "core-js": "^3.0.1", - "global": "^4.3.2", - "qs": "^6.6.0", - "telejson": "^5.0.2" - } - }, - "@storybook/channels": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.0.21.tgz", - "integrity": "sha512-G6gjcEotSwDmOlxSmOMgsO3VhQ42RLJK7kFp6D5eg0Q6S8vsypltdT8orxdu+6+AbcBrL+5Sla8lThzaCvXsVQ==", - "dev": true, - "requires": { - "core-js": "^3.0.1", - "ts-dedent": "^1.1.1", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/client-api": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.0.21.tgz", - "integrity": "sha512-emBXd/ml6pc3G8gP3MsR9zQsAq1zZbqof9MxB51tG/jpTXdqWQ8ce1pt1tJS8Xj0QDM072jR6wsY+mmro0GZnA==", - "dev": true, - "requires": { - "@storybook/addons": "6.0.21", - "@storybook/channel-postmessage": "6.0.21", - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/csf": "0.0.1", - "@types/qs": "^6.9.0", - "@types/webpack-env": "^1.15.2", - "core-js": "^3.0.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "memoizerific": "^1.11.3", - "qs": "^6.6.0", - "stable": "^0.1.8", - "store2": "^2.7.1", - "ts-dedent": "^1.1.1", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/client-logger": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.0.21.tgz", - "integrity": "sha512-8aUEbhjXV+UMYQWukVYnp+kZafF+LD4Dm7eMo37IUZvt3VIjV1VvhxIDVJtqjk2vv0KZTepESFBkZQLmBzI9Zg==", - "dev": true, - "requires": { - "core-js": "^3.0.1", - "global": "^4.3.2" - } - }, - "@storybook/components": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.0.21.tgz", - "integrity": "sha512-r6btqFW/rcXIU5v231EifZfdh9O0fy7bJDXwwDf8zVUgLx8JRc0VnSs3nvK3Is9HF1wZ9vjx/7Lh4rTIDZAjgg==", - "dev": true, - "requires": { - "@storybook/client-logger": "6.0.21", - "@storybook/csf": "0.0.1", - "@storybook/theming": "6.0.21", - "@types/overlayscrollbars": "^1.9.0", - "@types/react-color": "^3.0.1", - "@types/react-syntax-highlighter": "11.0.4", - "core-js": "^3.0.1", - "fast-deep-equal": "^3.1.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "markdown-to-jsx": "^6.11.4", - "memoizerific": "^1.11.3", - "overlayscrollbars": "^1.10.2", - "polished": "^3.4.4", - "popper.js": "^1.14.7", - "react": "^16.8.3", - "react-color": "^2.17.0", - "react-dom": "^16.8.3", - "react-popper-tooltip": "^2.11.0", - "react-syntax-highlighter": "^12.2.1", - "react-textarea-autosize": "^8.1.1", - "ts-dedent": "^1.1.1" - } - }, - "@storybook/core-events": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.0.21.tgz", - "integrity": "sha512-p84fbPcsAhnqDhp+HJ4P8+vI2BqJus4IRoVAemLAwuPjyPElrV9UvOa/RHy1BN8Z6jXwFA+FFzfGl2kPJ3WYcA==", - "dev": true, - "requires": { - "core-js": "^3.0.1" - } - }, - "@storybook/router": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.0.21.tgz", - "integrity": "sha512-46SsKJfcd12lRrISnfrWhicJx8EylkgGDGohfH0n5p7inkkGOkKV8QFZoYPRKZueMXmUKpzJ0Z3HmVsLTCrCDw==", - "dev": true, - "requires": { - "@reach/router": "^1.3.3", - "@types/reach__router": "^1.3.5", - "core-js": "^3.0.1", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "qs": "^6.6.0" - } - }, - "@storybook/semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==", - "dev": true, - "requires": { - "core-js": "^3.6.5", - "find-up": "^4.1.0" - }, - "dependencies": { - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", - "dev": true - } - } - }, - "@storybook/theming": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.0.21.tgz", - "integrity": "sha512-n97DfB9kG6WrV1xBGDyeQibTrh8pBBCp3dSL3UTGH+KX3C2+4sm6QHlTgyekbi5FrbFEbnuZOKAS3YbLVONsRQ==", - "dev": true, - "requires": { - "@emotion/core": "^10.0.20", - "@emotion/is-prop-valid": "^0.8.6", - "@emotion/styled": "^10.0.17", - "@storybook/client-logger": "6.0.21", - "core-js": "^3.0.1", - "deep-object-diff": "^1.1.0", - "emotion-theming": "^10.0.19", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "polished": "^3.4.4", - "resolve-from": "^5.0.0", - "ts-dedent": "^1.1.1" - } - }, - "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", - "dev": true - }, - "@types/reach__router": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.5.tgz", - "integrity": "sha512-h0NbqXN/tJuBY/xggZSej1SKQEstbHO7J/omt1tYoFGmj3YXOodZKbbqD4mNDh7zvEGYd7YFrac1LTtAr3xsYQ==", - "dev": true, - "requires": { - "@types/history": "*", - "@types/react": "*" - } - }, - "@types/react-syntax-highlighter": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz", - "integrity": "sha512-9GfTo3a0PHwQeTVoqs0g5bS28KkSY48pp5659wA+Dp4MqceDEa8EHBqrllJvvtyusszyJhViUEap0FDvlk/9Zg==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", - "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", - "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", - "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", - "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", - "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/floating-point-hex-parser": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-code-frame": "1.9.0", - "@webassemblyjs/helper-fsm": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "optional": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.32", - "postcss-value-parser": "^4.1.0" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true, - "optional": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001124", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001124.tgz", - "integrity": "sha512-zQW8V3CdND7GHRH6rxm6s59Ww4g/qGWTheoboW9nfeMg7sUoopIfKCcNZUjwYRCOrvereh3kwDpZj4VLQ7zGtA==", - "dev": true - }, - "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "colorette": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", - "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", - "dev": true - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true - }, - "create-react-context": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", - "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", - "dev": true, - "requires": { - "gud": "^1.0.0", - "warning": "^4.0.3" - } - }, - "css-loader": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", - "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "cssesc": "^3.0.0", - "icss-utils": "^4.1.1", - "loader-utils": "^1.2.3", - "normalize-path": "^3.0.0", - "postcss": "^7.0.32", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.2", - "postcss-modules-scope": "^2.2.0", - "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^2.7.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ejs": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.5.tgz", - "integrity": "sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w==", - "dev": true, - "requires": { - "jake": "^10.6.1" - } - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "enhanced-resolve": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", - "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "dependencies": { - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } - } - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/loader/-/loader-1.6.22.tgz", + "integrity": "sha512-9CjGwy595NaxAYp0hF9B/A0lH6C8Rms97e2JS9d3jVUtILn6pT5i5IV965ra3lIWc7Rs1GG1tBdVF7dCowYe6Q==", + "dev": true, + "requires": { + "@mdx-js/mdx": "1.6.22", + "@mdx-js/react": "1.6.22", + "loader-utils": "2.0.0" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, - "highlight.js": { - "version": "9.15.10", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz", - "integrity": "sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw==", - "dev": true + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "dev": true, - "optional": true, "requires": { - "binary-extensions": "^2.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } + } + } + }, + "@mdx-js/mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", + "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", + "dev": true, + "requires": { + "@babel/core": "7.12.9", + "@babel/plugin-syntax-jsx": "7.12.1", + "@babel/plugin-syntax-object-rest-spread": "7.8.3", + "@mdx-js/util": "1.6.22", + "babel-plugin-apply-mdx-type-prop": "1.6.22", + "babel-plugin-extract-import-names": "1.6.22", + "camelcase-css": "2.0.1", + "detab": "2.0.4", + "hast-util-raw": "6.0.1", + "lodash.uniq": "4.5.0", + "mdast-util-to-hast": "10.0.1", + "remark-footnotes": "2.0.0", + "remark-mdx": "1.6.22", + "remark-parse": "8.0.3", + "remark-squeeze-paragraphs": "4.0.0", + "style-to-object": "0.3.0", + "unified": "9.2.0", + "unist-builder": "2.0.3", + "unist-util-visit": "2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true }, - "is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "dev": true, - "optional": true, "requires": { - "is-extglob": "^2.1.1" + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" } }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" } }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "unist-util-is": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", "dev": true }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", "dev": true, "requires": { - "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } + "unist-util-visit": "^2.0.0" } }, - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", "dev": true, "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" + "@types/unist": "^2.0.2" } }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", "dev": true, "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" } }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" } }, - "lowlight": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.12.1.tgz", - "integrity": "sha512-OqaVxMGIESnawn+TU/QMV5BJLbUghUfjDWPAtFqDYDmDtr4FnB+op8xM+pR7nKlauHNUHXGt0VgWatFB8voS5w==", + "vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", "dev": true, "requires": { - "fault": "^1.0.2", - "highlight.js": "~9.15.0" + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" } }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "vfile-location": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", + "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "dev": true + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", "dev": true, "requires": { - "yallist": "^3.0.2" + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" } + } + } + }, + "@mdx-js/react": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", + "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==", + "dev": true + }, + "@mdx-js/util": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", + "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==", + "dev": true + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.2.tgz", + "integrity": "sha512-wrIBsjA5pl13f0RN4Zx4FNWmU71lv03meGKnqRUoCyan17s4V3WL92f3w3AIuWbNnpcrQyFBU5qMavJoB8d27w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.2", + "run-parallel": "^1.1.9" + }, + "dependencies": { + "@nodelib/fs.stat": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz", + "integrity": "sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw==", + "dev": true + } + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.3.tgz", + "integrity": "sha512-l6t8xEhfK9Sa4YO5mIRdau7XSOADfmh3jCr0evNHdY+HNkW6xuQhgMH7D73VV6WpZOagrW0UludvMTiifiwTfA==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.2", + "fastq": "^1.6.0" + } + }, + "@npmcli/move-file": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", + "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "@octokit/auth-token": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.0.tgz", + "integrity": "sha512-eoOVMjILna7FVQf96iWc3+ZtE/ZT6y8ob8ZzcqKY1ibSQCnu4O/B7pJvzMx5cyZ/RjAff6DAdEb0O0Cjcxidkg==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.0" + } + }, + "@octokit/endpoint": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.1.2.tgz", + "integrity": "sha512-bBGGmcRFq1x0jrB29G/9KjYmO3cdHfk3476B2JOHRvLsNw1Pn3l+ZvbiqtcO9qAS4Ti+zFedLB84ziHZRZclQA==", + "dev": true, + "requires": { + "deepmerge": "3.2.0", + "is-plain-object": "^3.0.0", + "universal-user-agent": "^2.1.0", + "url-template": "^2.0.8" + }, + "dependencies": { + "deepmerge": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz", + "integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==", + "dev": true }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", "dev": true, "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "isobject": "^4.0.0" } }, - "markdown-to-jsx": { - "version": "6.11.4", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz", - "integrity": "sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==", + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + } + } + }, + "@octokit/graphql": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-2.1.3.tgz", + "integrity": "sha512-XoXJqL2ondwdnMIW3wtqJWEwcBfKk37jO/rYkoxNPEVeLBDGsGO1TCWggrAlq3keGt/O+C/7VepXnukUxwt5vA==", + "dev": true, + "requires": { + "@octokit/request": "^5.0.0", + "universal-user-agent": "^2.0.3" + }, + "dependencies": { + "@octokit/request": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.0.2.tgz", + "integrity": "sha512-z1BQr43g4kOL4ZrIVBMHwi68Yg9VbkRUyuAgqCp1rU3vbYa69+2gIld/+gHclw15bJWQnhqqyEb7h5a5EqgZ0A==", "dev": true, "requires": { - "prop-types": "^15.6.2", - "unquote": "^1.1.0" + "@octokit/endpoint": "^5.1.0", + "@octokit/request-error": "^1.0.1", + "deprecation": "^2.0.0", + "is-plain-object": "^3.0.0", + "node-fetch": "^2.3.0", + "once": "^1.4.0", + "universal-user-agent": "^3.0.0" + }, + "dependencies": { + "universal-user-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz", + "integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==", + "dev": true, + "requires": { + "os-name": "^3.0.0" + } + } } }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "isobject": "^4.0.0" } }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", "dev": true }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "dev": true, - "requires": { - "mime-db": "1.44.0" - } - }, - "mississippi": { + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "dev": true + } + } + }, + "@octokit/plugin-enterprise-rest": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-3.6.2.tgz", + "integrity": "sha512-3wF5eueS5OHQYuAEudkpN+xVeUsg8vYEMMenEzLphUZ7PRZ8OJtDcsreL3ad9zxXmBbaFWzLmFcdob5CLyZftA==", + "dev": true + }, + "@octokit/request": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-4.1.0.tgz", + "integrity": "sha512-RvpQAba4i+BNH0z8i0gPRc1ShlHidj4puQjI/Tno6s+Q3/Mzb0XRSHJiOhpeFrZ22V7Mwjq1E7QS27P5CgpWYA==", + "dev": true, + "requires": { + "@octokit/endpoint": "^5.1.0", + "@octokit/request-error": "^1.0.1", + "deprecation": "^2.0.0", + "is-plain-object": "^3.0.0", + "node-fetch": "^2.3.0", + "once": "^1.4.0", + "universal-user-agent": "^2.1.0" + }, + "dependencies": { + "is-plain-object": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "isobject": "^4.0.0" } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "dev": true + } + } + }, + "@octokit/request-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.2.tgz", + "integrity": "sha512-T9swMS/Vc4QlfWrvyeSyp/GjhXtYaBzCcibjGywV4k4D2qVrQKfEMPy8OxMDEj7zkIIdpHwqdpVbKCvnUPqkXw==", + "dev": true, + "requires": { + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "16.26.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.26.0.tgz", + "integrity": "sha512-NBpzre44ZAQWZhlH+zUYTgqI0pHN+c9rNj4d+pCydGEiKTGc1HKmoTghEUyr9GxazDyoAvmpx9nL0I7QS1Olvg==", + "dev": true, + "requires": { + "@octokit/request": "^4.0.1", + "@octokit/request-error": "^1.0.2", + "atob-lite": "^2.0.0", + "before-after-hook": "^1.4.0", + "btoa-lite": "^1.0.0", + "deprecation": "^2.0.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.uniq": "^4.5.0", + "octokit-pagination-methods": "^1.1.0", + "once": "^1.4.0", + "universal-user-agent": "^2.0.0", + "url-template": "^2.0.8" + } + }, + "@octokit/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.5.0.tgz", + "integrity": "sha512-KEnLwOfdXzxPNL34fj508bhi9Z9cStyN7qY1kOfVahmqtAfrWw6Oq3P4R+dtsg0lYtZdWBpUrS/Ixmd5YILSww==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + }, + "@octokit/webhooks": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-7.1.0.tgz", + "integrity": "sha512-kHyYkJkqY/wiP/hp0IT9FhkY5PhnV01co16V2YMRP/Zgnk3Vsy3U5iLAaP6U/0eRIlz5T4LSvkrcfNlfSb3cVQ==", + "dev": true, + "requires": { + "debug": "^4.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } + "ms": "^2.1.1" } }, "ms": { @@ -11912,1406 +6963,1575 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + } + } + }, + "@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.4.3.tgz", + "integrity": "sha512-br5Qwvh8D2OQqSXpd1g/xqXKnK0r+Jz6qVKBbWmpUcrbGOxUrf39V5oZ1876084CGn18uMdR5uvPqBv9UqtBjQ==", + "dev": true, + "requires": { + "ansi-html": "^0.0.7", + "error-stack-parser": "^2.0.6", + "html-entities": "^1.2.1", + "native-url": "^0.2.6", + "schema-utils": "^2.6.5", + "source-map": "^0.7.3" + }, + "dependencies": { + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "p-try": "^2.0.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true + } + } + }, + "@popperjs/core": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.2.tgz", + "integrity": "sha512-JlGTGRYHC2QK+DDbePyXdBdooxFq2+noLfWpRqJtkxcb/oYWzOF0kcbfvvbWrwevCC1l6hLUg1wHYT+ona5BWQ==" + }, + "@reach/router": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.3.4.tgz", + "integrity": "sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA==", + "dev": true, + "requires": { + "create-react-context": "0.3.0", + "invariant": "^2.2.3", + "prop-types": "^15.6.1", + "react-lifecycles-compat": "^3.0.4" + } + }, + "@react-native-community/blur": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@react-native-community/blur/-/blur-3.6.0.tgz", + "integrity": "sha512-GtDBhpX2pQcjl4VopOC8FktrVufrEfYRwVeMQ2WWckqKIv2BdwvlvWvj88L1WmEdBr9UNcm3rtgz+d+YXkmirA==", + "requires": { + "prop-types": "^15.5.10" + } + }, + "@react-native-community/cli-debugger-ui": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-3.0.0.tgz", + "integrity": "sha512-m3X+iWLsK/H7/b7PpbNO33eQayR/+M26la4ZbYe1KRke5Umg4PIWsvg21O8Tw4uJcY8LA5hsP+rBi/syBkBf0g==", + "requires": { + "serve-static": "^1.13.1" + } + }, + "@react-native-community/cli-platform-android": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-3.0.3.tgz", + "integrity": "sha512-rNO9DmRiVhB6aP2DVUjEJv7ecriTARDZND88ny3xNVUkrD1Y+zwF6aZu3eoT52VXOxLCSLiJzz19OiyGmfqxYg==", + "requires": { + "@react-native-community/cli-tools": "^3.0.0", + "chalk": "^2.4.2", + "execa": "^1.0.0", + "jetifier": "^1.6.2", + "logkitty": "^0.6.0", + "slash": "^3.0.0", + "xmldoc": "^1.1.2" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "requires": { - "find-up": "^4.0.0" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, - "polished": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/polished/-/polished-3.6.6.tgz", - "integrity": "sha512-yiB2ims2DZPem0kCD6V0wnhcVGFEhNh0Iw0axNpKU+oSAgFt6yx6HxIT23Qg0WWvgS379cS35zT4AOyZZRzpQQ==", - "dev": true, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "requires": { - "@babel/runtime": "^7.9.2" + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" } }, - "postcss-value-parser": { + "get-stream": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", - "dev": true + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", - "dev": true + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, - "react-popper-tooltip": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-2.11.1.tgz", - "integrity": "sha512-04A2f24GhyyMicKvg/koIOQ5BzlrRbKiAgP6L+Pdj1MVX3yJ1NeZ8+EidndQsbejFT55oW1b++wg2Z8KlAyhfQ==", - "dev": true, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "@react-native-community/cli-platform-ios": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-3.0.0.tgz", + "integrity": "sha512-QoNVlDj8eMXRZk9uktPFsctHurQpv9jKmiu6mQii4NEtT2npE7g1hbWpRNojutBsfgmCdQGDHd9uB54eeCnYgg==", + "requires": { + "@react-native-community/cli-tools": "^3.0.0", + "chalk": "^2.4.2", + "js-yaml": "^3.13.1", + "xcode": "^2.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "requires": { - "@babel/runtime": "^7.9.2", - "react-popper": "^1.3.7" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } - }, - "react-syntax-highlighter": { - "version": "12.2.1", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz", - "integrity": "sha512-CTsp0ZWijwKRYFg9xhkWD4DSpQqE4vb2NKVMdPAkomnILSmsNBHE0n5GuI5zB+PU3ySVvXvdt9jo+ViD9XibCA==", - "dev": true, + } + } + }, + "@react-native-community/cli-tools": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-3.0.0.tgz", + "integrity": "sha512-8IhQKZdf3E4CR8T7HhkPGgorot/cLkRDgneJFDSWk/wCYZAuUh4NEAdumQV7N0jLSMWX7xxiWUPi94lOBxVY9g==", + "requires": { + "chalk": "^2.4.2", + "lodash": "^4.17.5", + "mime": "^2.4.1", + "node-fetch": "^2.5.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "requires": { - "@babel/runtime": "^7.3.1", - "highlight.js": "~9.15.1", - "lowlight": "1.12.1", - "prismjs": "^1.8.4", - "refractor": "^2.4.1" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, - "react-textarea-autosize": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.2.0.tgz", - "integrity": "sha512-grajUlVbkx6VdtSxCgzloUIphIZF5bKr21OYMceWPKkniy7H0mRAT/AXPrRtObAe+zUePnNlBwUc4ivVjUGIjw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.10.2", - "use-composed-ref": "^1.0.0", - "use-latest": "^1.0.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } - } - } + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + } + } + }, + "@react-native-community/cli-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-3.0.0.tgz", + "integrity": "sha512-ng6Tm537E/M42GjE4TRUxQyL8sRfClcL7bQWblOCoxPZzJ2J3bdALsjeG3vDnVCIfI/R0AeFalN9KjMt0+Z/Zg==" + }, + "@react-native-community/masked-view": { + "version": "git+https://github.com/wordpress-mobile/react-native-masked-view.git#f65a51a3320e58404d7f38d967bfd1f42b439ca9", + "from": "git+https://github.com/wordpress-mobile/react-native-masked-view.git#f65a51a3320e58404d7f38d967bfd1f42b439ca9" + }, + "@react-native-community/slider": { + "version": "git+https://github.com/wordpress-mobile/react-native-slider.git#d263ff16cdd9fb7352b354342522ff030f220f42", + "from": "git+https://github.com/wordpress-mobile/react-native-slider.git#d263ff16cdd9fb7352b354342522ff030f220f42" + }, + "@react-navigation/core": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-5.12.0.tgz", + "integrity": "sha512-CTmYrFXCZwInN40CpEzkPxhrpzujj20qvsUgpH05+oO1flwsnaJsyBfYawIcTS62/1/Z6SAM7iW5PbKk+qw9iQ==", + "requires": { + "@react-navigation/routers": "^5.4.9", + "escape-string-regexp": "^4.0.0", + "nanoid": "^3.1.9", + "query-string": "^6.13.1", + "react-is": "^16.13.0", + "use-subscription": "^1.4.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, - "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "dev": true, - "optional": true, + "query-string": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz", + "integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==", "requires": { - "picomatch": "^2.2.1" - }, - "dependencies": { - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true, - "optional": true - } + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" } }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" + } + } + }, + "@react-navigation/native": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.7.0.tgz", + "integrity": "sha512-a2JBOdRB3q20Jdc5hF8shR4Dk+ZmjF2Rr9RviErtARztu08lU+jcb1gK6c31OKL37JDuaS3xexE9Cb7dYeMy3Q==", + "requires": { + "@react-navigation/core": "^5.12.0", + "nanoid": "^3.1.9" + } + }, + "@react-navigation/routers": { + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-5.4.9.tgz", + "integrity": "sha512-dYD5qrIKUmuBEp+O98hB0tDYpEsGQgCQFQgMEoFKBmVVhx2JnJJ1zxRjU7xWcCU4VdBA8IOowgHQHJsVNKYyrg==", + "requires": { + "nanoid": "^3.1.9" + } + }, + "@react-navigation/stack": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-5.6.2.tgz", + "integrity": "sha512-51Aasxg8j2eKxz4mhA0ajJXrhAyJQkk2iiNE511zcqJ3tlfxv/h70Eej3PetnbbHFMOwNsEwc2GjB3OnfQcxjQ==", + "requires": { + "color": "^3.1.2", + "react-native-iphone-x-helper": "^1.2.1" + } + }, + "@samverschueren/stream-to-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", + "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", + "dev": true, + "requires": { + "any-observable": "^0.3.0" + } + }, + "@sindresorhus/is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.0.tgz", + "integrity": "sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz", + "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@storybook/addon-a11y": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-6.1.11.tgz", + "integrity": "sha512-DJJeBoYlO/WjSKgQEE44fwaKg9KZE6yifPAXT6oJlEPU9sRNnIIY5cAJNeaRbJ61SZ8rDr51iF/4yJb0uvyNxw==", + "dev": true, + "requires": { + "@storybook/addons": "6.1.11", + "@storybook/api": "6.1.11", + "@storybook/channels": "6.1.11", + "@storybook/client-api": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/components": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/theming": "6.1.11", + "axe-core": "^4.0.1", + "core-js": "^3.0.1", + "global": "^4.3.2", + "lodash": "^4.17.15", + "react-sizeme": "^2.5.2", + "regenerator-runtime": "^0.13.7", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "dependencies": { "regenerator-runtime": { "version": "0.13.7", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", "dev": true - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + } + } + }, + "@storybook/addon-docs": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-6.1.11.tgz", + "integrity": "sha512-dC6RpNWFvbEs68WDk30jrzmPncR0u7nn0SQl0Ao1wdLqCU81ptti/t6Yc7zanlo9hokMzGiom87ZMef0ad9nTQ==", + "dev": true, + "requires": { + "@babel/core": "^7.12.1", + "@babel/generator": "^7.12.1", + "@babel/parser": "^7.12.3", + "@babel/plugin-transform-react-jsx": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@jest/transform": "^26.0.0", + "@mdx-js/loader": "^1.6.19", + "@mdx-js/mdx": "^1.6.19", + "@mdx-js/react": "^1.6.19", + "@storybook/addons": "6.1.11", + "@storybook/api": "6.1.11", + "@storybook/client-api": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/components": "6.1.11", + "@storybook/core": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/csf": "0.0.1", + "@storybook/node-logger": "6.1.11", + "@storybook/postinstall": "6.1.11", + "@storybook/source-loader": "6.1.11", + "@storybook/theming": "6.1.11", + "acorn": "^7.1.0", + "acorn-jsx": "^5.1.0", + "acorn-walk": "^7.0.0", + "core-js": "^3.0.1", + "doctrine": "^3.0.0", + "escodegen": "^1.12.0", + "fast-deep-equal": "^3.1.1", + "global": "^4.3.2", + "html-tags": "^3.1.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "prettier": "~2.0.5", + "prop-types": "^15.7.2", + "react-element-to-jsx-string": "^14.3.1", + "regenerator-runtime": "^0.13.7", + "remark-external-links": "^6.0.0", + "remark-slug": "^6.0.0", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { - "glob": "^7.1.3" + "esutils": "^2.0.2" } }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - } + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "dev": true }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@storybook/addon-knobs": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/addon-knobs/-/addon-knobs-6.1.11.tgz", + "integrity": "sha512-5bVB7JbClyL/v8hJcifRL1Jetj1rDbVjmIOw2RputZHPbOJ2Dd18X3uOAfzNa7e2KJ0BmtA1ojZ3PuDfy9MAPw==", + "dev": true, + "requires": { + "@storybook/addons": "6.1.11", + "@storybook/api": "6.1.11", + "@storybook/channels": "6.1.11", + "@storybook/client-api": "6.1.11", + "@storybook/components": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/theming": "6.1.11", + "copy-to-clipboard": "^3.0.8", + "core-js": "^3.0.1", + "escape-html": "^1.0.3", + "fast-deep-equal": "^3.1.1", + "global": "^4.3.2", + "lodash": "^4.17.15", + "prop-types": "^15.7.2", + "qs": "^6.6.0", + "react-color": "^2.17.0", + "react-lifecycles-compat": "^3.0.4", + "react-select": "^3.0.8", + "regenerator-runtime": "^0.13.7" + }, + "dependencies": { + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, - "style-loader": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.2.1.tgz", - "integrity": "sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^2.6.6" - }, - "dependencies": { - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - } - } + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@storybook/addon-storysource": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-6.1.11.tgz", + "integrity": "sha512-uscKgALJa/inMFPb/Fpe9LXB6c6WlrGOfaUiAdrahn9gZbDAG9qunaSkAZ9EPWckSosq3RuV59HMCQ2Bolh/lQ==", + "dev": true, + "requires": { + "@storybook/addons": "6.1.11", + "@storybook/api": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/components": "6.1.11", + "@storybook/router": "6.1.11", + "@storybook/source-loader": "6.1.11", + "@storybook/theming": "6.1.11", + "core-js": "^3.0.1", + "estraverse": "^4.2.0", + "loader-utils": "^2.0.0", + "prettier": "~2.0.5", + "prop-types": "^15.7.2", + "react-syntax-highlighter": "^13.5.0", + "regenerator-runtime": "^0.13.7" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, - "telejson": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/telejson/-/telejson-5.0.2.tgz", - "integrity": "sha512-XCrDHGbinczsscs8LXFr9jDhvy37yBk9piB7FJrCfxE8oP66WDkolNMpaBkWYgQqB9dQGBGtTDzGQPedc9KJmw==", - "dev": true, - "requires": { - "@types/is-function": "^1.0.0", - "global": "^4.4.0", - "is-function": "^1.0.2", - "is-regex": "^1.1.1", - "is-symbol": "^1.0.3", - "isobject": "^4.0.0", - "lodash": "^4.17.19", - "memoizerific": "^1.11.3" - } + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", "dev": true, "requires": { - "is-number": "^7.0.0" + "minimist": "^1.2.5" } }, - "ts-dedent": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-1.1.1.tgz", - "integrity": "sha512-UGTRZu1evMw4uTPyYF66/KFd22XiU+jMaIuHrkIHQ2GivAXVlLV0v/vHrpOuTRf9BmpNHi/SO7Vd0rLu0y57jg==", - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "dev": true, "requires": { - "unique-slug": "^2.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", "dev": true }, - "url-loader": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.0.tgz", - "integrity": "sha512-IzgAAIC8wRrg6NYkFIJY09vtktQcsvU8V6HhtQj9PTefbYImzLB1hufqo4m+RyM5N3mLx5BqJKccgxJS+W3kqw==", + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@storybook/addon-viewport": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-6.1.11.tgz", + "integrity": "sha512-VgCRLpTu56WzSJ0piBu4nL99GtXeMcDxaLvEZKyVCoYjyQZxRcg4N9OFwxpnu6bYYF7O4pMbs2wITc4mW6YSEg==", + "dev": true, + "requires": { + "@storybook/addons": "6.1.11", + "@storybook/api": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/components": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/theming": "6.1.11", + "core-js": "^3.0.1", + "global": "^4.3.2", + "memoizerific": "^1.11.3", + "prop-types": "^15.7.2", + "regenerator-runtime": "^0.13.7" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@storybook/addons": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.1.11.tgz", + "integrity": "sha512-OZXsdmn60dVe482l9zWxzOqqJApD2jggk/8QJKn3/Ub9posmqdqg712bW6v71BBe0UXXG/QfkZA7gcyiyEENbw==", + "dev": true, + "requires": { + "@storybook/api": "6.1.11", + "@storybook/channels": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/router": "6.1.11", + "@storybook/theming": "6.1.11", + "core-js": "^3.0.1", + "global": "^4.3.2", + "regenerator-runtime": "^0.13.7" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } + } + }, + "@storybook/api": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.1.11.tgz", + "integrity": "sha512-/p4QW/p3uWO0AKVveNezX3I/CotyBKaJ5ui8PuvSPsl7yvqcsK41qI4evKOw7GMQn6oP+2enRbzHpGuCUgQSjA==", + "dev": true, + "requires": { + "@reach/router": "^1.3.3", + "@storybook/channels": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/csf": "0.0.1", + "@storybook/router": "6.1.11", + "@storybook/semver": "^7.3.2", + "@storybook/theming": "6.1.11", + "@types/reach__router": "^1.3.5", + "core-js": "^3.0.1", + "fast-deep-equal": "^3.1.1", + "global": "^4.3.2", + "lodash": "^4.17.15", + "memoizerific": "^1.11.3", + "regenerator-runtime": "^0.13.7", + "store2": "^2.7.1", + "telejson": "^5.0.2", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "dependencies": { + "@storybook/semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==", "dev": true, "requires": { - "loader-utils": "^2.0.0", - "mime-types": "^2.1.26", - "schema-utils": "^2.6.5" + "core-js": "^3.6.5", + "find-up": "^4.1.0" }, "dependencies": { - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } + "core-js": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz", + "integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==", + "dev": true } } }, - "warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "loose-envify": "^1.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, - "watchpack": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", - "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "chokidar": "^3.4.1", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" + "p-locate": "^4.1.0" } }, - "webpack": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.1.tgz", - "integrity": "sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ==", + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.3.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.3", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", - "dev": true, - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } + "p-try": "^2.0.0" } }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "p-limit": "^2.2.0" } }, - "y18n": { + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", "dev": true } } }, - "@storybook/core-events": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.3.2.tgz", - "integrity": "sha512-a1zVQqN8SMMAbdq0OV6Pc130VNasFYP85HO72VJf8t1aZGq40lYKFiALuF2S3Ax4ZIvJFbSrLM9OCpNNYg/ung==", + "@storybook/channel-postmessage": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.1.11.tgz", + "integrity": "sha512-voW4Z2SUacDOxwN2q1NEBL//8OpgvL2C5CeoG1VQyEllKM8Vg9t1Nxo2FFTJBzv5LeEX7VIJKeBoB25DYvKyng==", "dev": true, "requires": { - "core-js": "^3.0.1" + "@storybook/channels": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/core-events": "6.1.11", + "core-js": "^3.0.1", + "global": "^4.3.2", + "qs": "^6.6.0", + "telejson": "^5.0.2" + }, + "dependencies": { + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true + } } }, - "@storybook/csf": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.1.tgz", - "integrity": "sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==", + "@storybook/channels": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.1.11.tgz", + "integrity": "sha512-NvjWzQ95TSV1e18jaQBCOGoe+vptKH2NOKZ7QRQ7I0O5OoHKr47IXoh+MQ5C8CRD9FTdLE/xWdn1sVVEPRyHEw==", "dev": true, "requires": { - "lodash": "^4.17.15" + "core-js": "^3.0.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" } }, - "@storybook/node-logger": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.0.21.tgz", - "integrity": "sha512-KRBf+Fz7fgtwHdnYt70JTZbcYMZ1pQPtDyqbrFYCjwkbx5GPX5vMOozlxCIj9elseqPIsF8CKgHOW7cFHVyWYw==", + "@storybook/client-api": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.1.11.tgz", + "integrity": "sha512-DodJQzGCR+PYs26klvbquTjfBgkw5nvCZd3jpgWQtOrYaY/cMY1LLkVkKqrm2ENW8f7vf7tiw78RtxaXy7xeIQ==", "dev": true, "requires": { - "@types/npmlog": "^4.1.2", - "chalk": "^4.0.0", + "@storybook/addons": "6.1.11", + "@storybook/channel-postmessage": "6.1.11", + "@storybook/channels": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/csf": "0.0.1", + "@types/qs": "^6.9.0", + "@types/webpack-env": "^1.15.3", "core-js": "^3.0.1", - "npmlog": "^4.1.2", - "pretty-hrtime": "^1.0.3" + "global": "^4.3.2", + "lodash": "^4.17.15", + "memoizerific": "^1.11.3", + "qs": "^6.6.0", + "regenerator-runtime": "^0.13.7", + "stable": "^0.1.8", + "store2": "^2.7.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "dependencies": { + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + } } }, - "@storybook/postinstall": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-5.3.2.tgz", - "integrity": "sha512-SHDXiubFKSoIpV5E2R9K104Ye4qhRQuoN9SKCB400Ya2JyBSGz5FMuNVmRpO8JqJrUe9uI1d/k18/Mma1/73YA==", + "@storybook/client-logger": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.1.11.tgz", + "integrity": "sha512-dSc+VKLW1UaiMPMhlZYRqhynrrHdHFiBEgU28+8LcmoZ1yhZBwLkcKdSD4YTT0CbMJAG1/+NUW5kRI8Geeg+rA==", "dev": true, "requires": { - "core-js": "^3.0.1" + "core-js": "^3.0.1", + "global": "^4.3.2" } }, - "@storybook/react": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-6.0.21.tgz", - "integrity": "sha512-L3PcoBJq5aK1aTaJNfwsSJ8Kxgcyk0WknN4TDqhP7a+oXmuMY1YEi96hEvQVIm0TBCkQxs61K70/T7vlilEtHg==", + "@storybook/components": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.1.11.tgz", + "integrity": "sha512-DGDl76uONTkg0rpsa36TpVuXv4K7rFYe8GnQ/Q8n4By5tvldC4s9YXwcDRYHVrfnYybKzuZ/+jv2ZAp4/8ZaeA==", "dev": true, "requires": { - "@babel/preset-flow": "^7.0.0", - "@babel/preset-react": "^7.0.0", - "@storybook/addons": "6.0.21", - "@storybook/core": "6.0.21", - "@storybook/node-logger": "6.0.21", - "@storybook/semver": "^7.3.2", - "@svgr/webpack": "^5.4.0", - "@types/webpack-env": "^1.15.2", - "babel-plugin-add-react-displayname": "^0.0.5", - "babel-plugin-named-asset-import": "^0.3.1", - "babel-plugin-react-docgen": "^4.1.0", + "@popperjs/core": "^2.5.4", + "@storybook/client-logger": "6.1.11", + "@storybook/csf": "0.0.1", + "@storybook/theming": "6.1.11", + "@types/overlayscrollbars": "^1.9.0", + "@types/react-color": "^3.0.1", + "@types/react-syntax-highlighter": "11.0.4", "core-js": "^3.0.1", + "fast-deep-equal": "^3.1.1", "global": "^4.3.2", "lodash": "^4.17.15", - "prop-types": "^15.7.2", - "react-dev-utils": "^10.0.0", - "react-docgen-typescript-plugin": "^0.5.2", - "regenerator-runtime": "^0.13.3", - "ts-dedent": "^1.1.1", - "webpack": "^4.43.0" + "markdown-to-jsx": "^6.11.4", + "memoizerific": "^1.11.3", + "overlayscrollbars": "^1.10.2", + "polished": "^3.4.4", + "react-color": "^2.17.0", + "react-popper-tooltip": "^3.1.1", + "react-syntax-highlighter": "^13.5.0", + "react-textarea-autosize": "^8.1.1", + "ts-dedent": "^2.0.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/compat-data": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz", - "integrity": "sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.11.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", - "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", - "dev": true, - "requires": { - "@babel/types": "^7.11.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" - } + "@popperjs/core": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.6.0.tgz", + "integrity": "sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw==", + "dev": true }, - "@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + } + } + }, + "@storybook/core": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.1.11.tgz", + "integrity": "sha512-pYOOQwiNJ5myLRn6p6nnLUjjjISHK/N55vS4HFnETYSaRLA++h1coN1jk7Zwt89dOQTdF0EsTJn+6snYOC+lxQ==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-decorators": "^7.12.1", + "@babel/plugin-proposal-export-default-from": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.1", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.1", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.1", + "@babel/preset-typescript": "^7.12.1", + "@babel/register": "^7.12.1", + "@storybook/addons": "6.1.11", + "@storybook/api": "6.1.11", + "@storybook/channel-postmessage": "6.1.11", + "@storybook/channels": "6.1.11", + "@storybook/client-api": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/components": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/csf": "0.0.1", + "@storybook/node-logger": "6.1.11", + "@storybook/router": "6.1.11", + "@storybook/semver": "^7.3.2", + "@storybook/theming": "6.1.11", + "@storybook/ui": "6.1.11", + "@types/glob-base": "^0.3.0", + "@types/micromatch": "^4.0.1", + "@types/node-fetch": "^2.5.4", + "airbnb-js-shims": "^2.2.1", + "ansi-to-html": "^0.6.11", + "autoprefixer": "^9.7.2", + "babel-loader": "^8.0.6", + "babel-plugin-emotion": "^10.0.20", + "babel-plugin-macros": "^2.8.0", + "babel-preset-minify": "^0.5.0 || 0.6.0-alpha.5", + "better-opn": "^2.0.0", + "boxen": "^4.1.0", + "case-sensitive-paths-webpack-plugin": "^2.2.0", + "chalk": "^4.0.0", + "cli-table3": "0.6.0", + "commander": "^5.0.0", + "core-js": "^3.0.1", + "cpy": "^8.1.1", + "css-loader": "^3.5.3", + "detect-port": "^1.3.0", + "dotenv-webpack": "^1.7.0", + "ejs": "^3.1.2", + "express": "^4.17.0", + "file-loader": "^6.0.0", + "file-system-cache": "^1.0.5", + "find-up": "^4.1.0", + "fork-ts-checker-webpack-plugin": "^4.1.4", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "glob-base": "^0.3.0", + "glob-promise": "^3.4.0", + "global": "^4.3.2", + "html-webpack-plugin": "^4.2.1", + "inquirer": "^7.0.0", + "interpret": "^2.0.0", + "ip": "^1.1.5", + "json5": "^2.1.1", + "lazy-universal-dotenv": "^3.0.1", + "micromatch": "^4.0.2", + "node-fetch": "^2.6.0", + "pkg-dir": "^4.2.0", + "pnp-webpack-plugin": "1.6.4", + "postcss-flexbugs-fixes": "^4.1.0", + "postcss-loader": "^3.0.0", + "pretty-hrtime": "^1.0.3", + "qs": "^6.6.0", + "raw-loader": "^4.0.1", + "react-dev-utils": "^10.0.0", + "regenerator-runtime": "^0.13.7", + "resolve-from": "^5.0.0", + "serve-favicon": "^2.5.0", + "shelljs": "^0.8.4", + "stable": "^0.1.8", + "style-loader": "^1.2.1", + "telejson": "^5.0.2", + "terser-webpack-plugin": "^3.0.0", + "ts-dedent": "^2.0.0", + "unfetch": "^4.1.0", + "url-loader": "^4.0.0", + "util-deprecate": "^1.0.2", + "webpack": "^4.44.2", + "webpack-dev-middleware": "^3.7.0", + "webpack-filter-warnings-plugin": "^1.2.1", + "webpack-hot-middleware": "^2.25.0", + "webpack-virtual-modules": "^0.2.2" + }, + "dependencies": { + "@storybook/semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==", "dev": true, "requires": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" + "core-js": "^3.6.5", + "find-up": "^4.1.0" }, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "core-js": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz", + "integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==", "dev": true } } }, - "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" - } - }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz", - "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "@types/json-schema": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", + "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, - "@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", "dev": true, "requires": { - "lodash": "^4.17.19" + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" } }, - "@babel/helper-remap-async-to-generator": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz", - "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", "dev": true, "requires": { - "@babel/types": "^7.11.0" + "@webassemblyjs/wast-printer": "1.9.0" } }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", "dev": true }, - "@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@webassemblyjs/ast": "1.9.0" } }, - "@babel/parser": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", - "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", "dev": true }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", - "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", - "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" } }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" + "@xtuc/ieee754": "^1.2.0" } }, - "@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" + "@xtuc/long": "4.2.2" } }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", - "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" } }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz", - "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==", + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.4" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" } }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" } }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz", - "integrity": "sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==", + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" } }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" } }, - "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" } }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, + "optional": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" - } + "optional": true }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true }, - "@babel/plugin-transform-block-scoping": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz", - "integrity": "sha512-00dYeDE0EVEHuuM+26+0w/SCL0BH2Qy7LwHuI4Hi4MH5gkC8/AqMN5uWFJIsoXZrAphiMm1iXzBw6L2T+eA0ew==", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "fill-range": "^7.0.1" } }, - "@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" } }, - "@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, - "@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", + "chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, + "optional": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" } }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, - "@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "jake": "^10.6.1" } }, - "@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true }, - "@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "enhanced-resolve": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", + "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } } }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, - "@babel/plugin-transform-modules-amd": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", - "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" + "to-regex-range": "^5.0.1" } }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } } }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", - "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, - "@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" } }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" - } + "optional": true }, - "@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, - "@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "dev": true, + "optional": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" + "is-glob": "^4.0.1" } }, - "@babel/plugin-transform-parameters": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", - "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true }, - "@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "optional": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "binary-extensions": "^2.0.0" } }, - "@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, + "optional": true, "requires": { - "regenerator-transform": "^0.14.2" + "is-extglob": "^2.1.1" } }, - "@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "minimist": "^1.2.5" } }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } } }, - "@babel/plugin-transform-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", - "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + } } }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" + "p-locate": "^4.1.0" } }, - "@babel/plugin-transform-template-literals": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", - "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "yallist": "^3.0.2" } }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/preset-env": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz", - "integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.11.0", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.11.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.11.0", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.11.5", - "browserslist": "^4.12.0", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" + "pify": "^4.0.1", + "semver": "^5.6.0" }, "dependencies": { "semver": { @@ -13322,318 +8542,585 @@ } } }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" + "braces": "^3.0.1", + "picomatch": "^2.0.5" } }, - "@babel/traverse": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", - "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "dev": true, "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.5", - "@babel/types": "^7.11.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" + "mime-db": "1.44.0" } }, - "@babel/types": { - "version": "7.11.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", - "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" } }, - "@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "@emotion/memoize": "0.7.4" + "minimist": "^1.2.5" } }, - "@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "@reach/router": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.3.4.tgz", - "integrity": "sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA==", - "dev": true, - "requires": { - "create-react-context": "0.3.0", - "invariant": "^2.2.3", - "prop-types": "^15.6.1", - "react-lifecycles-compat": "^3.0.4" - } + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, - "@storybook/addons": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.0.21.tgz", - "integrity": "sha512-yDttNLc3vXqBxwK795ykgzTC6MpvuXDQuF4LHSlHZQe6wsMu1m3fljnbYdafJWdx6cNZwUblU3KYcR11PqhkPg==", + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "@storybook/api": "6.0.21", - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/router": "6.0.21", - "@storybook/theming": "6.0.21", - "core-js": "^3.0.1", - "global": "^4.3.2", - "regenerator-runtime": "^0.13.3" + "p-try": "^2.0.0" } }, - "@storybook/api": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.0.21.tgz", - "integrity": "sha512-cRRGf/KGFwYiDouTouEcDdp45N1AbYnAfvLqYZ3KuUTGZ+CiU/PN/vavkp07DQeM4FIQO8TLhzHdsLFpLT7Lkw==", + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "@reach/router": "^1.3.3", - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/csf": "0.0.1", - "@storybook/router": "6.0.21", - "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.0.21", - "@types/reach__router": "^1.3.5", - "core-js": "^3.0.1", - "fast-deep-equal": "^3.1.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "memoizerific": "^1.11.3", - "react": "^16.8.3", - "regenerator-runtime": "^0.13.3", - "store2": "^2.7.1", - "telejson": "^5.0.2", - "ts-dedent": "^1.1.1", - "util-deprecate": "^1.0.2" + "p-limit": "^2.2.0" } }, - "@storybook/channels": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.0.21.tgz", - "integrity": "sha512-G6gjcEotSwDmOlxSmOMgsO3VhQ42RLJK7kFp6D5eg0Q6S8vsypltdT8orxdu+6+AbcBrL+5Sla8lThzaCvXsVQ==", + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "requires": { - "core-js": "^3.0.1", - "ts-dedent": "^1.1.1", - "util-deprecate": "^1.0.2" + "find-up": "^4.0.0" } }, - "@storybook/client-logger": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.0.21.tgz", - "integrity": "sha512-8aUEbhjXV+UMYQWukVYnp+kZafF+LD4Dm7eMo37IUZvt3VIjV1VvhxIDVJtqjk2vv0KZTepESFBkZQLmBzI9Zg==", + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "core-js": "^3.0.1", - "global": "^4.3.2" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "@storybook/core-events": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.0.21.tgz", - "integrity": "sha512-p84fbPcsAhnqDhp+HJ4P8+vI2BqJus4IRoVAemLAwuPjyPElrV9UvOa/RHy1BN8Z6jXwFA+FFzfGl2kPJ3WYcA==", + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, + "optional": true, "requires": { - "core-js": "^3.0.1" + "picomatch": "^2.2.1" + }, + "dependencies": { + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true, + "optional": true + } } }, - "@storybook/router": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.0.21.tgz", - "integrity": "sha512-46SsKJfcd12lRrISnfrWhicJx8EylkgGDGohfH0n5p7inkkGOkKV8QFZoYPRKZueMXmUKpzJ0Z3HmVsLTCrCDw==", + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { - "@reach/router": "^1.3.3", - "@types/reach__router": "^1.3.5", - "core-js": "^3.0.1", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "qs": "^6.6.0" + "glob": "^7.1.3" } }, - "@storybook/semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==", + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, "requires": { - "core-js": "^3.6.5", - "find-up": "^4.1.0" - }, - "dependencies": { - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", - "dev": true - } + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" } }, - "@storybook/theming": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.0.21.tgz", - "integrity": "sha512-n97DfB9kG6WrV1xBGDyeQibTrh8pBBCp3dSL3UTGH+KX3C2+4sm6QHlTgyekbi5FrbFEbnuZOKAS3YbLVONsRQ==", + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "dev": true, "requires": { - "@emotion/core": "^10.0.20", - "@emotion/is-prop-valid": "^0.8.6", - "@emotion/styled": "^10.0.17", - "@storybook/client-logger": "6.0.21", - "core-js": "^3.0.1", - "deep-object-diff": "^1.1.0", - "emotion-theming": "^10.0.19", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "polished": "^3.4.4", - "resolve-from": "^5.0.0", - "ts-dedent": "^1.1.1" + "randombytes": "^2.1.0" } }, - "@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", - "dev": true - }, - "@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", - "dev": true - }, - "@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, - "@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", - "dev": true + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } }, - "@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", - "dev": true + "style-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", + "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^2.7.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } }, - "@svgr/babel-plugin-transform-svg-component": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.4.0.tgz", - "integrity": "sha512-zLl4Fl3NvKxxjWNkqEcpdSOpQ3LGVH2BNFQ6vjaK6sFo2IrSznrhURIPI0HAphKiiIwNYjAfE0TNoQDSZv0U9A==", + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, - "@svgr/babel-preset": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.4.0.tgz", - "integrity": "sha512-Gyx7cCxua04DBtyILTYdQxeO/pwfTBev6+eXTbVbxe4HTGhOUW6yo7PSbG2p6eJMl44j6XSequ0ZDP7bl0nu9A==", + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.4.0" + "is-number": "^7.0.0" } }, - "@svgr/core": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.4.0.tgz", - "integrity": "sha512-hWGm1DCCvd4IEn7VgDUHYiC597lUYhFau2lwJBYpQWDirYLkX4OsXu9IslPgJ9UpP7wsw3n2Ffv9sW7SXJVfqQ==", + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { - "@svgr/plugin-jsx": "^5.4.0", - "camelcase": "^6.0.0", - "cosmiconfig": "^6.0.0" + "unique-slug": "^2.0.0" } }, - "@svgr/hast-util-to-babel-ast": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.4.0.tgz", - "integrity": "sha512-+U0TZZpPsP2V1WvVhqAOSTk+N+CjYHdZx+x9UBa1eeeZDXwH8pt0CrQf2+SvRl/h2CAPRFkm+Ey96+jKP8Bsgg==", + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "dev": true + }, + "url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", "dev": true, "requires": { - "@babel/types": "^7.9.5" + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } } }, - "@svgr/plugin-jsx": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.4.0.tgz", - "integrity": "sha512-SGzO4JZQ2HvGRKDzRga9YFSqOqaNrgLlQVaGvpZ2Iht2gwRp/tq+18Pvv9kS9ZqOMYgyix2LLxZMY1LOe9NPqw==", + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "dev": true, "requires": { - "@babel/core": "^7.7.5", - "@svgr/babel-preset": "^5.4.0", - "@svgr/hast-util-to-babel-ast": "^5.4.0", - "svg-parser": "^2.0.2" + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" } }, - "@svgr/plugin-svgo": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.4.0.tgz", - "integrity": "sha512-3Cgv3aYi1l6SHyzArV9C36yo4kgwVdF3zPQUC6/aCDUeXAofDYwE5kk3e3oT5ZO2a0N3lB+lLGvipBG6lnG8EA==", + "webpack": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", + "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", "dev": true, "requires": { - "cosmiconfig": "^6.0.0", - "merge-deep": "^3.0.2", - "svgo": "^1.2.2" + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.3.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } } }, - "@svgr/webpack": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.4.0.tgz", - "integrity": "sha512-LjepnS/BSAvelnOnnzr6Gg0GcpLmnZ9ThGFK5WJtm1xOqdBE/1IACZU7MMdVzjyUkfFqGz87eRE4hFaSLiUwYg==", + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "dev": true, "requires": { - "@babel/core": "^7.9.0", - "@babel/plugin-transform-react-constant-elements": "^7.9.0", - "@babel/preset-env": "^7.9.5", - "@babel/preset-react": "^7.9.4", - "@svgr/core": "^5.4.0", - "@svgr/plugin-jsx": "^5.4.0", - "@svgr/plugin-svgo": "^5.4.0", - "loader-utils": "^2.0.0" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" } }, - "@types/reach__router": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.5.tgz", - "integrity": "sha512-h0NbqXN/tJuBY/xggZSej1SKQEstbHO7J/omt1tYoFGmj3YXOodZKbbqD4mNDh7zvEGYd7YFrac1LTtAr3xsYQ==", + "y18n": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "@storybook/core-events": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.1.11.tgz", + "integrity": "sha512-hTib81W8PxepM7iXVvl3pBXSaGpChl5LTzaLCoIRO9sSB8dy0/x2DLAHzbQvShk/l1wqUc3TtOLIxq+eC9l3wg==", + "dev": true, + "requires": { + "core-js": "^3.0.1" + } + }, + "@storybook/csf": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.1.tgz", + "integrity": "sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "@storybook/node-logger": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.1.11.tgz", + "integrity": "sha512-MASonXDWpSMU9HF9mqbGOR1Ps/DTJ8AVmYD50+OnB9kXl4M42Dliobeq7JwKFMnZ42RelUCCSXdWW80hGrUKKA==", + "dev": true, + "requires": { + "@types/npmlog": "^4.1.2", + "chalk": "^4.0.0", + "core-js": "^3.0.1", + "npmlog": "^4.1.2", + "pretty-hrtime": "^1.0.3" + } + }, + "@storybook/postinstall": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-6.1.11.tgz", + "integrity": "sha512-ICW70RuOJOHD7PcKq62yr7hCmo04F7yRMuy5/MD/G+Neaw3YpfYTc6pQ228h5UrmXiKSKG1unPkjzuIAoIeN7w==", + "dev": true, + "requires": { + "core-js": "^3.0.1" + } + }, + "@storybook/react": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-6.1.11.tgz", + "integrity": "sha512-EmR7yvVW6z6AYhfzAgJMGR/5+igeBGa1EePaEIibn51r5uboSB72N12NaADyF2OaycIdV+0sW6vP9Zvlvexa/w==", + "dev": true, + "requires": { + "@babel/preset-flow": "^7.12.1", + "@babel/preset-react": "^7.12.1", + "@pmmmwh/react-refresh-webpack-plugin": "^0.4.2", + "@storybook/addons": "6.1.11", + "@storybook/core": "6.1.11", + "@storybook/node-logger": "6.1.11", + "@storybook/semver": "^7.3.2", + "@types/webpack-env": "^1.15.3", + "babel-plugin-add-react-displayname": "^0.0.5", + "babel-plugin-named-asset-import": "^0.3.1", + "babel-plugin-react-docgen": "^4.2.1", + "core-js": "^3.0.1", + "global": "^4.3.2", + "lodash": "^4.17.15", + "prop-types": "^15.7.2", + "react-dev-utils": "^10.0.0", + "react-docgen-typescript-plugin": "^0.6.2", + "react-refresh": "^0.8.3", + "regenerator-runtime": "^0.13.7", + "ts-dedent": "^2.0.0", + "webpack": "^4.44.2" + }, + "dependencies": { + "@storybook/semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==", "dev": true, "requires": { - "@types/history": "*", - "@types/react": "*" + "core-js": "^3.6.5", + "find-up": "^4.1.0" + }, + "dependencies": { + "core-js": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz", + "integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==", + "dev": true + } } }, "@webassemblyjs/ast": { @@ -13822,15 +9309,6 @@ "picomatch": "^2.0.4" } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -13891,27 +9369,10 @@ } } }, - "camelcase": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", - "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, "optional": true, "requires": { @@ -13922,7 +9383,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "chownr": { @@ -13931,126 +9392,6 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - } - }, - "create-react-context": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", - "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", - "dev": true, - "requires": { - "gud": "^1.0.0", - "warning": "^4.0.3" - } - }, - "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "dev": true, - "requires": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "css-what": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz", - "integrity": "sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==", - "dev": true - }, - "csso": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz", - "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==", - "dev": true, - "requires": { - "css-tree": "1.0.0-alpha.39" - }, - "dependencies": { - "css-tree": { - "version": "1.0.0-alpha.39", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz", - "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==", - "dev": true, - "requires": { - "mdn-data": "2.0.6", - "source-map": "^0.6.1" - } - }, - "mdn-data": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", - "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, "emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", @@ -14080,50 +9421,6 @@ } } }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - } - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -14186,18 +9483,6 @@ "is-glob": "^4.0.1" } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -14208,18 +9493,6 @@ "binary-extensions": "^2.0.0" } }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true - }, - "is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", - "dev": true - }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -14237,48 +9510,24 @@ "dev": true, "optional": true }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - }, "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.0" } }, "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "dev": true, "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", - "json5": "^2.1.2" + "json5": "^1.0.1" } }, "locate-path": { @@ -14317,18 +9566,6 @@ } } }, - "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "dev": true - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "mississippi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", @@ -14347,11 +9584,14 @@ "through2": "^2.0.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } }, "neo-async": { "version": "2.6.2", @@ -14366,33 +9606,6 @@ "dev": true, "optional": true }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, - "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -14417,30 +9630,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -14492,15 +9687,6 @@ } } }, - "polished": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/polished/-/polished-3.6.6.tgz", - "integrity": "sha512-yiB2ims2DZPem0kCD6V0wnhcVGFEhNh0Iw0axNpKU+oSAgFt6yx6HxIT23Qg0WWvgS379cS35zT4AOyZZRzpQQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.9.2" - } - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -14511,16 +9697,16 @@ "once": "^1.3.1" } }, - "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "react-refresh": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", + "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==", "dev": true }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "optional": true, "requires": { @@ -14542,12 +9728,6 @@ "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", "dev": true }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -14566,6 +9746,12 @@ "randombytes": "^2.1.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, "ssri": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", @@ -14575,69 +9761,12 @@ "figgy-pudding": "^3.5.1" } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - } - }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true }, - "telejson": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/telejson/-/telejson-5.0.2.tgz", - "integrity": "sha512-XCrDHGbinczsscs8LXFr9jDhvy37yBk9piB7FJrCfxE8oP66WDkolNMpaBkWYgQqB9dQGBGtTDzGQPedc9KJmw==", - "dev": true, - "requires": { - "@types/is-function": "^1.0.0", - "global": "^4.4.0", - "is-function": "^1.0.2", - "is-regex": "^1.1.1", - "is-symbol": "^1.0.3", - "isobject": "^4.0.0", - "lodash": "^4.17.19", - "memoizerific": "^1.11.3" - } - }, "terser-webpack-plugin": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", @@ -14653,14 +9782,6 @@ "terser": "^4.1.2", "webpack-sources": "^1.4.0", "worker-farm": "^1.7.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "to-regex-range": { @@ -14673,12 +9794,6 @@ "is-number": "^7.0.0" } }, - "ts-dedent": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-1.1.1.tgz", - "integrity": "sha512-UGTRZu1evMw4uTPyYF66/KFd22XiU+jMaIuHrkIHQ2GivAXVlLV0v/vHrpOuTRf9BmpNHi/SO7Vd0rLu0y57jg==", - "dev": true - }, "unique-filename": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", @@ -14688,31 +9803,22 @@ "unique-slug": "^2.0.0" } }, - "warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, "watchpack": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", - "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "dev": true, "requires": { "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" + "watchpack-chokidar2": "^2.0.1" } }, "webpack": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.1.tgz", - "integrity": "sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", + "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", "dev": true, "requires": { "@webassemblyjs/ast": "1.9.0", @@ -14738,37 +9844,6 @@ "terser-webpack-plugin": "^1.4.3", "watchpack": "^1.7.4", "webpack-sources": "^1.4.1" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - } } }, "webpack-sources": { @@ -14779,20 +9854,12 @@ "requires": { "source-list-map": "^2.0.0", "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "dev": true }, "yallist": { @@ -14804,46 +9871,44 @@ } }, "@storybook/router": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-5.3.2.tgz", - "integrity": "sha512-EeM27i+89WS2mdT4j7RyVMSk7e7QiDb8bmyJAX3qyxg9ZOI4Wrvawgj6OAGuetItC1nayCPXFlXtIfHsP1h3lg==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.1.11.tgz", + "integrity": "sha512-YEYOoKMo/WI13MZCkdqI9X3H1G0Oj5OUxi7So4qd3khX3zcCjSr3LjiMDBcmIVZpFo5VAvzjhIY4KqpgvzTG0A==", "dev": true, "requires": { - "@reach/router": "^1.2.1", - "@storybook/csf": "0.0.1", - "@types/reach__router": "^1.2.3", + "@reach/router": "^1.3.3", + "@types/reach__router": "^1.3.5", "core-js": "^3.0.1", "global": "^4.3.2", - "lodash": "^4.17.15", "memoizerific": "^1.11.3", - "qs": "^6.6.0", - "util-deprecate": "^1.0.2" + "qs": "^6.6.0" }, "dependencies": { "qs": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz", - "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "dev": true } } }, "@storybook/source-loader": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-5.3.2.tgz", - "integrity": "sha512-bHEWLXTSn89jip/fy/rjf2Psrmu4ZdquOD/SmB+WVuKRYE8JxcN1ybNUOhM7+XqekzIV/iw9rivhjGO1nZ7HAQ==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-6.1.11.tgz", + "integrity": "sha512-pEMWGn3XwZYAXwIrtmd7ziH5d1zN8NCpJM8vNJssntZFW45rDo69xgGM/PrTLPDca6f/Mhv+vqzR99tdfarJSw==", "dev": true, "requires": { - "@storybook/addons": "5.3.2", - "@storybook/client-logger": "5.3.2", + "@storybook/addons": "6.1.11", + "@storybook/client-logger": "6.1.11", "@storybook/csf": "0.0.1", "core-js": "^3.0.1", "estraverse": "^4.2.0", "global": "^4.3.2", - "loader-utils": "^1.2.3", - "prettier": "^1.16.4", - "prop-types": "^15.7.2", - "regenerator-runtime": "^0.13.3" + "loader-utils": "^2.0.0", + "lodash": "^4.17.15", + "prettier": "~2.0.5", + "regenerator-runtime": "^0.13.7", + "source-map": "^0.7.3" }, "dependencies": { "big.js": { @@ -14852,60 +9917,107 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" } }, "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", "dev": true, "requires": { "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" + "emojis-list": "^3.0.0", + "json5": "^2.1.2" } }, "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", "dev": true }, "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true } } }, "@storybook/theming": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-5.3.2.tgz", - "integrity": "sha512-WzFVgE2v/0mlK/5CqM9kSDdCMag7uqFsjI5oe3HCWEpwrUH70ndhuVZTVpyx0fscdL74vT5XZieoy2WwpVBl5Q==", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.1.11.tgz", + "integrity": "sha512-zRChacVgKoU2BmpvwK1ntiF3KIpc8QblJT7IGiKfP/BNpy9gNeXbLPLk3g/tkHszOvVYtkaZhEXni4Od8tqy1A==", "dev": true, "requires": { - "@emotion/core": "^10.0.20", - "@emotion/styled": "^10.0.17", - "@storybook/client-logger": "5.3.2", + "@emotion/core": "^10.1.1", + "@emotion/is-prop-valid": "^0.8.6", + "@emotion/styled": "^10.0.23", + "@storybook/client-logger": "6.1.11", "core-js": "^3.0.1", "deep-object-diff": "^1.1.0", "emotion-theming": "^10.0.19", "global": "^4.3.2", "memoizerific": "^1.11.3", - "polished": "^3.3.1", - "prop-types": "^15.7.2", + "polished": "^3.4.4", "resolve-from": "^5.0.0", - "ts-dedent": "^1.1.0" + "ts-dedent": "^2.0.0" }, "dependencies": { + "@emotion/core": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.1.1.tgz", + "integrity": "sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.5.5", + "@emotion/cache": "^10.0.27", + "@emotion/css": "^10.0.27", + "@emotion/serialize": "^0.11.15", + "@emotion/sheet": "0.9.4", + "@emotion/utils": "0.11.3" + } + }, + "@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "dev": true, + "requires": { + "@emotion/memoize": "0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "dev": true + }, + "@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "dev": true + }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -14915,25 +10027,26 @@ } }, "@storybook/ui": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.0.21.tgz", - "integrity": "sha512-50QYF8tHUgpVq7B7PWp7kmyf79NySWJO0piQFjHv027vV8GfbXMWVswAXwo3IfCihPlnLKe01WbsigM/9T1HCQ==", - "dev": true, - "requires": { - "@emotion/core": "^10.0.20", - "@storybook/addons": "6.0.21", - "@storybook/api": "6.0.21", - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/components": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/router": "6.0.21", + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.1.11.tgz", + "integrity": "sha512-Qth2dxS5+VbKHcqgkiKpeD+xr/hRUuUIDUA/2Ierh/BaA8Up/krlso/mCLaQOa5E8Og9WJAdDFO0cUbt939c2Q==", + "dev": true, + "requires": { + "@emotion/core": "^10.1.1", + "@storybook/addons": "6.1.11", + "@storybook/api": "6.1.11", + "@storybook/channels": "6.1.11", + "@storybook/client-logger": "6.1.11", + "@storybook/components": "6.1.11", + "@storybook/core-events": "6.1.11", + "@storybook/router": "6.1.11", "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.0.21", + "@storybook/theming": "6.1.11", "@types/markdown-to-jsx": "^6.11.0", "copy-to-clipboard": "^3.0.8", "core-js": "^3.0.1", "core-js-pure": "^3.0.1", + "downshift": "^6.0.6", "emotion-theming": "^10.0.19", "fuse.js": "^3.6.1", "global": "^4.3.2", @@ -14942,163 +10055,35 @@ "memoizerific": "^1.11.3", "polished": "^3.4.4", "qs": "^6.6.0", - "react": "^16.8.3", - "react-dom": "^16.8.3", "react-draggable": "^4.0.3", "react-helmet-async": "^1.0.2", "react-hotkeys": "2.0.0", "react-sizeme": "^2.6.7", - "regenerator-runtime": "^0.13.3", + "regenerator-runtime": "^0.13.7", "resolve-from": "^5.0.0", "store2": "^2.7.1" }, "dependencies": { - "@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "@emotion/core": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.1.1.tgz", + "integrity": "sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA==", "dev": true, "requires": { - "@emotion/memoize": "0.7.4" + "@babel/runtime": "^7.5.5", + "@emotion/cache": "^10.0.27", + "@emotion/css": "^10.0.27", + "@emotion/serialize": "^0.11.15", + "@emotion/sheet": "0.9.4", + "@emotion/utils": "0.11.3" } }, - "@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", "dev": true }, - "@reach/router": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@reach/router/-/router-1.3.4.tgz", - "integrity": "sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA==", - "dev": true, - "requires": { - "create-react-context": "0.3.0", - "invariant": "^2.2.3", - "prop-types": "^15.6.1", - "react-lifecycles-compat": "^3.0.4" - } - }, - "@storybook/addons": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.0.21.tgz", - "integrity": "sha512-yDttNLc3vXqBxwK795ykgzTC6MpvuXDQuF4LHSlHZQe6wsMu1m3fljnbYdafJWdx6cNZwUblU3KYcR11PqhkPg==", - "dev": true, - "requires": { - "@storybook/api": "6.0.21", - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/router": "6.0.21", - "@storybook/theming": "6.0.21", - "core-js": "^3.0.1", - "global": "^4.3.2", - "regenerator-runtime": "^0.13.3" - } - }, - "@storybook/api": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.0.21.tgz", - "integrity": "sha512-cRRGf/KGFwYiDouTouEcDdp45N1AbYnAfvLqYZ3KuUTGZ+CiU/PN/vavkp07DQeM4FIQO8TLhzHdsLFpLT7Lkw==", - "dev": true, - "requires": { - "@reach/router": "^1.3.3", - "@storybook/channels": "6.0.21", - "@storybook/client-logger": "6.0.21", - "@storybook/core-events": "6.0.21", - "@storybook/csf": "0.0.1", - "@storybook/router": "6.0.21", - "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.0.21", - "@types/reach__router": "^1.3.5", - "core-js": "^3.0.1", - "fast-deep-equal": "^3.1.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "memoizerific": "^1.11.3", - "react": "^16.8.3", - "regenerator-runtime": "^0.13.3", - "store2": "^2.7.1", - "telejson": "^5.0.2", - "ts-dedent": "^1.1.1", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/channels": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.0.21.tgz", - "integrity": "sha512-G6gjcEotSwDmOlxSmOMgsO3VhQ42RLJK7kFp6D5eg0Q6S8vsypltdT8orxdu+6+AbcBrL+5Sla8lThzaCvXsVQ==", - "dev": true, - "requires": { - "core-js": "^3.0.1", - "ts-dedent": "^1.1.1", - "util-deprecate": "^1.0.2" - } - }, - "@storybook/client-logger": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.0.21.tgz", - "integrity": "sha512-8aUEbhjXV+UMYQWukVYnp+kZafF+LD4Dm7eMo37IUZvt3VIjV1VvhxIDVJtqjk2vv0KZTepESFBkZQLmBzI9Zg==", - "dev": true, - "requires": { - "core-js": "^3.0.1", - "global": "^4.3.2" - } - }, - "@storybook/components": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.0.21.tgz", - "integrity": "sha512-r6btqFW/rcXIU5v231EifZfdh9O0fy7bJDXwwDf8zVUgLx8JRc0VnSs3nvK3Is9HF1wZ9vjx/7Lh4rTIDZAjgg==", - "dev": true, - "requires": { - "@storybook/client-logger": "6.0.21", - "@storybook/csf": "0.0.1", - "@storybook/theming": "6.0.21", - "@types/overlayscrollbars": "^1.9.0", - "@types/react-color": "^3.0.1", - "@types/react-syntax-highlighter": "11.0.4", - "core-js": "^3.0.1", - "fast-deep-equal": "^3.1.1", - "global": "^4.3.2", - "lodash": "^4.17.15", - "markdown-to-jsx": "^6.11.4", - "memoizerific": "^1.11.3", - "overlayscrollbars": "^1.10.2", - "polished": "^3.4.4", - "popper.js": "^1.14.7", - "react": "^16.8.3", - "react-color": "^2.17.0", - "react-dom": "^16.8.3", - "react-popper-tooltip": "^2.11.0", - "react-syntax-highlighter": "^12.2.1", - "react-textarea-autosize": "^8.1.1", - "ts-dedent": "^1.1.1" - } - }, - "@storybook/core-events": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.0.21.tgz", - "integrity": "sha512-p84fbPcsAhnqDhp+HJ4P8+vI2BqJus4IRoVAemLAwuPjyPElrV9UvOa/RHy1BN8Z6jXwFA+FFzfGl2kPJ3WYcA==", - "dev": true, - "requires": { - "core-js": "^3.0.1" - } - }, - "@storybook/router": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.0.21.tgz", - "integrity": "sha512-46SsKJfcd12lRrISnfrWhicJx8EylkgGDGohfH0n5p7inkkGOkKV8QFZoYPRKZueMXmUKpzJ0Z3HmVsLTCrCDw==", - "dev": true, - "requires": { - "@reach/router": "^1.3.3", - "@types/reach__router": "^1.3.5", - "core-js": "^3.0.1", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "qs": "^6.6.0" - } - }, "@storybook/semver": { "version": "7.3.2", "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz", @@ -15110,68 +10095,31 @@ }, "dependencies": { "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.1.tgz", + "integrity": "sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==", "dev": true } } }, - "@storybook/theming": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.0.21.tgz", - "integrity": "sha512-n97DfB9kG6WrV1xBGDyeQibTrh8pBBCp3dSL3UTGH+KX3C2+4sm6QHlTgyekbi5FrbFEbnuZOKAS3YbLVONsRQ==", - "dev": true, - "requires": { - "@emotion/core": "^10.0.20", - "@emotion/is-prop-valid": "^0.8.6", - "@emotion/styled": "^10.0.17", - "@storybook/client-logger": "6.0.21", - "core-js": "^3.0.1", - "deep-object-diff": "^1.1.0", - "emotion-theming": "^10.0.19", - "global": "^4.3.2", - "memoizerific": "^1.11.3", - "polished": "^3.4.4", - "resolve-from": "^5.0.0", - "ts-dedent": "^1.1.1" - } - }, - "@types/reach__router": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.5.tgz", - "integrity": "sha512-h0NbqXN/tJuBY/xggZSej1SKQEstbHO7J/omt1tYoFGmj3YXOodZKbbqD4mNDh7zvEGYd7YFrac1LTtAr3xsYQ==", - "dev": true, - "requires": { - "@types/history": "*", - "@types/react": "*" - } - }, - "@types/react-syntax-highlighter": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz", - "integrity": "sha512-9GfTo3a0PHwQeTVoqs0g5bS28KkSY48pp5659wA+Dp4MqceDEa8EHBqrllJvvtyusszyJhViUEap0FDvlk/9Zg==", - "dev": true, - "requires": { - "@types/react": "*" - } + "compute-scroll-into-view": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.16.tgz", + "integrity": "sha512-a85LHKY81oQnikatZYA90pufpZ6sQx++BoCxOEMsjpZx+ZnaKGQnCyCehTRr/1p9GBIAHTjcU9k71kSYWloLiQ==", + "dev": true }, - "create-react-context": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", - "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", + "downshift": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-6.0.7.tgz", + "integrity": "sha512-+rqgx3JTSs8b4V9q++hsLvaP8mhMOdX7u+jy5S2etg7+Y7uFLVzZaI0S7Xo2yEnNDmTcNnYZZ/vFjdWuJ2kZdg==", "dev": true, "requires": { - "gud": "^1.0.0", - "warning": "^4.0.3" + "@babel/runtime": "^7.12.5", + "compute-scroll-into-view": "^1.0.16", + "prop-types": "^15.7.2", + "react-is": "^17.0.1" } }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -15182,48 +10130,6 @@ "path-exists": "^4.0.0" } }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "highlight.js": { - "version": "9.15.10", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz", - "integrity": "sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw==", - "dev": true - }, - "is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", - "dev": true - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -15233,26 +10139,6 @@ "p-locate": "^4.1.0" } }, - "lowlight": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.12.1.tgz", - "integrity": "sha512-OqaVxMGIESnawn+TU/QMV5BJLbUghUfjDWPAtFqDYDmDtr4FnB+op8xM+pR7nKlauHNUHXGt0VgWatFB8voS5w==", - "dev": true, - "requires": { - "fault": "^1.0.2", - "highlight.js": "~9.15.0" - } - }, - "markdown-to-jsx": { - "version": "6.11.4", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz", - "integrity": "sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==", - "dev": true, - "requires": { - "prop-types": "^15.6.2", - "unquote": "^1.1.0" - } - }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -15283,65 +10169,17 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "polished": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/polished/-/polished-3.6.6.tgz", - "integrity": "sha512-yiB2ims2DZPem0kCD6V0wnhcVGFEhNh0Iw0axNpKU+oSAgFt6yx6HxIT23Qg0WWvgS379cS35zT4AOyZZRzpQQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.9.2" - } - }, "qs": { "version": "6.9.4", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "dev": true }, - "react-popper-tooltip": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-2.11.1.tgz", - "integrity": "sha512-04A2f24GhyyMicKvg/koIOQ5BzlrRbKiAgP6L+Pdj1MVX3yJ1NeZ8+EidndQsbejFT55oW1b++wg2Z8KlAyhfQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.9.2", - "react-popper": "^1.3.7" - } - }, - "react-syntax-highlighter": { - "version": "12.2.1", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-12.2.1.tgz", - "integrity": "sha512-CTsp0ZWijwKRYFg9xhkWD4DSpQqE4vb2NKVMdPAkomnILSmsNBHE0n5GuI5zB+PU3ySVvXvdt9jo+ViD9XibCA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.3.1", - "highlight.js": "~9.15.1", - "lowlight": "1.12.1", - "prismjs": "^1.8.4", - "refractor": "^2.4.1" - } - }, - "react-textarea-autosize": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.2.0.tgz", - "integrity": "sha512-grajUlVbkx6VdtSxCgzloUIphIZF5bKr21OYMceWPKkniy7H0mRAT/AXPrRtObAe+zUePnNlBwUc4ivVjUGIjw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.10.2", - "use-composed-ref": "^1.0.0", - "use-latest": "^1.0.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } - } - } + "react-is": { + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", + "dev": true }, "regenerator-runtime": { "version": "0.13.7", @@ -15354,37 +10192,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true - }, - "telejson": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/telejson/-/telejson-5.0.2.tgz", - "integrity": "sha512-XCrDHGbinczsscs8LXFr9jDhvy37yBk9piB7FJrCfxE8oP66WDkolNMpaBkWYgQqB9dQGBGtTDzGQPedc9KJmw==", - "dev": true, - "requires": { - "@types/is-function": "^1.0.0", - "global": "^4.4.0", - "is-function": "^1.0.2", - "is-regex": "^1.1.1", - "is-symbol": "^1.0.3", - "isobject": "^4.0.0", - "lodash": "^4.17.19", - "memoizerific": "^1.11.3" - } - }, - "ts-dedent": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-1.1.1.tgz", - "integrity": "sha512-UGTRZu1evMw4uTPyYF66/KFd22XiU+jMaIuHrkIHQ2GivAXVlLV0v/vHrpOuTRf9BmpNHi/SO7Vd0rLu0y57jg==", - "dev": true - }, - "warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } } } }, @@ -16194,12 +11001,6 @@ "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", "dev": true }, - "@types/babel-types": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz", - "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ==", - "dev": true - }, "@types/babel__core": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", @@ -16241,15 +11042,6 @@ "@babel/types": "^7.3.0" } }, - "@types/babylon": { - "version": "6.16.5", - "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz", - "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==", - "dev": true, - "requires": { - "@types/babel-types": "*" - } - }, "@types/braces": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/braces/-/braces-3.0.0.tgz", @@ -16331,16 +11123,25 @@ "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", "integrity": "sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==" }, + "@types/hast": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.1.tgz", + "integrity": "sha512-viwwrB+6xGzw+G1eWpF9geV3fnsDgXqHG+cqgiHrvQfDUW5hzhCyV7Sy3UJxhfRFBsgky2SSW33qi/YrIkjX5Q==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, "@types/history": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.4.tgz", - "integrity": "sha512-+o2igcuZA3xtOoFH56s+MCZVidwlJNcJID57DSCyawS2i910yG9vkwehCjJNZ6ImhCR5S9DbvIJKyYHcMyOfMw==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==", "dev": true }, "@types/html-minifier-terser": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz", - "integrity": "sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==", "dev": true }, "@types/http-cache-semantics": { @@ -16405,14 +11206,23 @@ "dev": true }, "@types/markdown-to-jsx": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.2.tgz", - "integrity": "sha512-ESuCu8Bk7jpTZ3YPdMW1+6wUj13F5N15vXfc7BuUAN0eCp0lrvVL9nzOTzoqvbRzXMciuqXr1KrHt3xQAhfwPA==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.3.tgz", + "integrity": "sha512-30nFYpceM/ZEvhGiqWjm5quLUxNeld0HCzJEXMZZDpq53FPkS85mTwkWtCXzCqq8s5JYLgM5W392a02xn8Bdaw==", "dev": true, "requires": { "@types/react": "*" } }, + "@types/mdast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.3.tgz", + "integrity": "sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, "@types/micromatch": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/micromatch/-/micromatch-4.0.1.tgz", @@ -16507,6 +11317,12 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, + "@types/parse5": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", + "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==", + "dev": true + }, "@types/prettier": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.0.tgz", @@ -16531,9 +11347,9 @@ "dev": true }, "@types/reach__router": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.2.6.tgz", - "integrity": "sha512-Oh5DAVr/L2svBvubw6QEFpXGu295Y406BPs4i9t1n2pp7M+q3pmCmhzb9oZV5wncR41KCD3NHl1Yhi7uKnTPsA==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.6.tgz", + "integrity": "sha512-RHYataCUPQnt+GHoASyRLq6wmZ0n8jWlBW8Lxcwd30NN6vQfbmTeoSDfkgxO0S1lEzArp8OFDsq5KIs7FygjtA==", "dev": true, "requires": { "@types/history": "*", @@ -16557,12 +11373,13 @@ } }, "@types/react-color": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.1.tgz", - "integrity": "sha512-J6mYm43Sid9y+OjZ7NDfJ2VVkeeuTPNVImNFITgQNXodHteKfl/t/5pAR5Z9buodZ2tCctsZjgiMlQOpfntakw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.4.tgz", + "integrity": "sha512-EswbYJDF1kkrx93/YU+BbBtb46CCtDMvTiGmcOa/c5PETnwTiSWoseJ1oSWeRl/4rUXkhME9bVURvvPg0W5YQw==", "dev": true, "requires": { - "@types/react": "*" + "@types/react": "*", + "@types/reactcss": "*" } }, "@types/react-dom": { @@ -16583,18 +11400,18 @@ } }, "@types/react-syntax-highlighter": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz", - "integrity": "sha512-iMNcixH8330f2dq0RY+VOXCP8JFehgmOhLOtnO85Ty+qu0fHXJNEqWx5VuFv8v0aEq0U/N9d/k1yvA+c6PEmPw==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz", + "integrity": "sha512-9GfTo3a0PHwQeTVoqs0g5bS28KkSY48pp5659wA+Dp4MqceDEa8EHBqrllJvvtyusszyJhViUEap0FDvlk/9Zg==", "dev": true, "requires": { "@types/react": "*" } }, - "@types/react-textarea-autosize": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/react-textarea-autosize/-/react-textarea-autosize-4.3.5.tgz", - "integrity": "sha512-PiDL83kPMTolyZAWW3lyzO6ktooTb9tFTntVy7CA83/qFLWKLJ5bLeRboy6J6j3b1e8h2Eec6gBTEOOJRjV14A==", + "@types/reactcss": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.3.tgz", + "integrity": "sha512-d2gQQ0IL6hXLnoRfVYZukQNWHuVsE75DzFTLPUuyyEhJS8G2VvlE+qfQQ91SJjaMqlURRCNIsX7Jcsw6cEuJlA==", "dev": true, "requires": { "@types/react": "*" @@ -16848,9 +11665,9 @@ } }, "@types/webpack-env": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.15.2.tgz", - "integrity": "sha512-67ZgZpAlhIICIdfQrB5fnDvaKFcDxpKibxznfYRVAT4mQE41Dido/3Ty+E3xGBmTogc5+0Qb8tWhna+5B8z1iQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.0.tgz", + "integrity": "sha512-Fx+NpfOO0CpeYX2g9bkvX8O5qh9wrU1sOF4g8sft4Mu7z+qfe387YlyY8w8daDyDsKY5vUxM0yxkAYnbkRbZEw==", "dev": true }, "@types/webpack-sources": { @@ -17162,55 +11979,10 @@ "@xtuc/long": "4.2.2" } }, - "@webpack-contrib/schema-utils": { - "version": "1.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz", - "integrity": "sha512-LonryJP+FxQQHsjGBi6W786TQB1Oym+agTpY0c+Kj8alnIw+DLUJb6SI8Y1GHGhLCH1yPRrucjObUmxNICQ1pg==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chalk": "^2.3.2", - "strip-ansi": "^4.0.0", - "text-table": "^0.2.0", - "webpack-log": "^1.1.2" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "webpack-log": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", - "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", - "dev": true, - "requires": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "loglevelnext": "^1.0.1", - "uuid": "^3.1.0" - } - } - } - }, "@wordpress/a11y": { "version": "file:packages/a11y", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/dom-ready": "file:packages/dom-ready", "@wordpress/i18n": "file:packages/i18n" } @@ -17218,7 +11990,7 @@ "@wordpress/annotations": { "version": "file:packages/annotations", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/data": "file:packages/data", "@wordpress/hooks": "file:packages/hooks", "@wordpress/i18n": "file:packages/i18n", @@ -17231,7 +12003,7 @@ "@wordpress/api-fetch": { "version": "file:packages/api-fetch", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/i18n": "file:packages/i18n", "@wordpress/url": "file:packages/url" } @@ -17239,7 +12011,7 @@ "@wordpress/autop": { "version": "file:packages/autop", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/babel-plugin-import-jsx-pragma": { @@ -17250,7 +12022,7 @@ "version": "file:packages/babel-plugin-makepot", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "gettext-parser": "^1.3.1", "lodash": "^4.17.19" } @@ -17259,11 +12031,11 @@ "version": "file:packages/babel-preset-default", "dev": true, "requires": { - "@babel/core": "^7.11.6", - "@babel/plugin-transform-react-jsx": "^7.10.4", - "@babel/plugin-transform-runtime": "^7.11.5", - "@babel/preset-env": "^7.11.5", - "@babel/runtime": "^7.11.2", + "@babel/core": "^7.12.9", + "@babel/plugin-transform-react-jsx": "^7.12.7", + "@babel/plugin-transform-runtime": "^7.12.1", + "@babel/preset-env": "^7.12.7", + "@babel/runtime": "^7.12.5", "@wordpress/babel-plugin-import-jsx-pragma": "file:packages/babel-plugin-import-jsx-pragma", "@wordpress/browserslist-config": "file:packages/browserslist-config", "@wordpress/element": "file:packages/element", @@ -17278,13 +12050,13 @@ "@wordpress/blob": { "version": "file:packages/blob", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/block-directory": { "version": "file:packages/block-directory", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:packages/a11y", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/block-editor": "file:packages/block-editor", @@ -17308,13 +12080,14 @@ "@wordpress/block-editor": { "version": "file:packages/block-editor", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:packages/a11y", "@wordpress/blob": "file:packages/blob", "@wordpress/blocks": "file:packages/blocks", "@wordpress/components": "file:packages/components", "@wordpress/compose": "file:packages/compose", "@wordpress/data": "file:packages/data", + "@wordpress/data-controls": "file:packages/data-controls", "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/dom": "file:packages/dom", "@wordpress/element": "file:packages/element", @@ -17330,7 +12103,6 @@ "@wordpress/shortcode": "file:packages/shortcode", "@wordpress/token-list": "file:packages/token-list", "@wordpress/url": "file:packages/url", - "@wordpress/warning": "file:packages/warning", "@wordpress/wordcount": "file:packages/wordcount", "classnames": "^2.2.5", "css-mediaquery": "^0.1.2", @@ -17341,10 +12113,8 @@ "memize": "^1.1.0", "react-autosize-textarea": "^7.1.0", "react-spring": "^8.0.19", - "react-transition-group": "^2.9.0", "reakit": "1.1.0", "redux-multi": "^0.1.12", - "refx": "^3.0.0", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "traverse": "^0.6.6" @@ -17353,7 +12123,7 @@ "@wordpress/block-library": { "version": "file:packages/block-library", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:packages/a11y", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/autop": "file:packages/autop", @@ -17395,7 +12165,7 @@ "@wordpress/block-serialization-default-parser": { "version": "file:packages/block-serialization-default-parser", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/block-serialization-spec-parser": { @@ -17408,7 +12178,7 @@ "@wordpress/blocks": { "version": "file:packages/blocks", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/autop": "file:packages/autop", "@wordpress/blob": "file:packages/blob", "@wordpress/block-serialization-default-parser": "file:packages/block-serialization-default-parser", @@ -17439,7 +12209,7 @@ "@wordpress/components": { "version": "file:packages/components", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@emotion/core": "^10.0.22", "@emotion/css": "^10.0.22", "@emotion/native": "^10.0.22", @@ -17480,14 +12250,18 @@ "@wordpress/compose": { "version": "file:packages/compose", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/deprecated": "file:packages/deprecated", + "@wordpress/dom": "file:packages/dom", "@wordpress/element": "file:packages/element", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", + "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/priority-queue": "file:packages/priority-queue", "clipboard": "^2.0.1", "lodash": "^4.17.19", + "memize": "^1.1.0", "mousetrap": "^1.6.5", + "react-merge-refs": "^1.0.0", "react-resize-aware": "^3.0.1", "use-memo-one": "^1.1.1" } @@ -17495,7 +12269,7 @@ "@wordpress/core-data": { "version": "file:packages/core-data", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/blocks": "file:packages/blocks", "@wordpress/data": "file:packages/data", @@ -17525,6 +12299,8 @@ "lodash": "^4.17.19", "make-dir": "^3.0.0", "mustache": "^4.0.0", + "npm-package-arg": "^8.0.1", + "rimraf": "^3.0.2", "write-pkg": "^4.0.0" } }, @@ -17538,14 +12314,13 @@ "@wordpress/data": { "version": "file:packages/data", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:packages/compose", "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/element": "file:packages/element", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/priority-queue": "file:packages/priority-queue", "@wordpress/redux-routine": "file:packages/redux-routine", - "@wordpress/stan": "file:packages/stan", "equivalent-key-map": "^0.2.2", "is-promise": "^4.0.0", "lodash": "^4.17.19", @@ -17558,7 +12333,7 @@ "@wordpress/data-controls": { "version": "file:packages/data-controls", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/data": "file:packages/data", "@wordpress/deprecated": "file:packages/deprecated" @@ -17567,23 +12342,9 @@ "@wordpress/date": { "version": "file:packages/date", "requires": { - "@babel/runtime": "^7.11.2", - "date-fns": "^2.16.1", - "date-fns-tz": "^1.0.12", + "@babel/runtime": "^7.12.5", "moment": "^2.22.1", "moment-timezone": "^0.5.31" - }, - "dependencies": { - "date-fns": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", - "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==" - }, - "date-fns-tz": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.0.12.tgz", - "integrity": "sha512-Ca+9pjGkU90XDHnclfSjz9o7g/ZqyYyYI0aCYmbf65P75oy8gktuaRslO3UPXl3ADgAnF9/KCykQkpU3/xvtWQ==" - } } }, "@wordpress/dependency-extraction-webpack-plugin": { @@ -17591,14 +12352,13 @@ "dev": true, "requires": { "json2php": "^0.0.4", - "webpack": "^4.8.3", "webpack-sources": "^1.3.0" } }, "@wordpress/deprecated": { "version": "file:packages/deprecated", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/hooks": "file:packages/hooks" } }, @@ -17606,7 +12366,7 @@ "version": "file:packages/docgen", "dev": true, "requires": { - "@babel/core": "^7.11.0", + "@babel/core": "^7.12.9", "doctrine": "^2.1.0", "lodash": "^4.17.19", "mdast-util-inject": "1.1.0", @@ -17619,21 +12379,21 @@ "@wordpress/dom": { "version": "file:packages/dom", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19" } }, "@wordpress/dom-ready": { "version": "file:packages/dom-ready", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/e2e-test-utils": { "version": "file:packages/e2e-test-utils", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/url": "file:packages/url", "lodash": "^4.17.19", @@ -17658,7 +12418,7 @@ "@wordpress/edit-navigation": { "version": "file:packages/edit-navigation", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/block-editor": "file:packages/block-editor", "@wordpress/block-library": "file:packages/block-library", @@ -17687,7 +12447,7 @@ "@wordpress/edit-post": { "version": "file:packages/edit-post", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:packages/a11y", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/block-editor": "file:packages/block-editor", @@ -17697,6 +12457,7 @@ "@wordpress/compose": "file:packages/compose", "@wordpress/core-data": "file:packages/core-data", "@wordpress/data": "file:packages/data", + "@wordpress/data-controls": "file:packages/data-controls", "@wordpress/editor": "file:packages/editor", "@wordpress/element": "file:packages/element", "@wordpress/hooks": "file:packages/hooks", @@ -17709,21 +12470,19 @@ "@wordpress/notices": "file:packages/notices", "@wordpress/plugins": "file:packages/plugins", "@wordpress/primitives": "file:packages/primitives", - "@wordpress/reusable-blocks": "file:packages/reusable-blocks", "@wordpress/url": "file:packages/url", "@wordpress/viewport": "file:packages/viewport", "@wordpress/warning": "file:packages/warning", "classnames": "^2.2.5", "lodash": "^4.17.19", "memize": "^1.1.0", - "refx": "^3.0.0", "rememo": "^3.0.0" } }, "@wordpress/edit-site": { "version": "file:packages/edit-site", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:packages/a11y", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/block-editor": "file:packages/block-editor", @@ -17758,7 +12517,7 @@ "@wordpress/edit-widgets": { "version": "file:packages/edit-widgets", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/block-editor": "file:packages/block-editor", "@wordpress/block-library": "file:packages/block-library", @@ -17791,7 +12550,7 @@ "@wordpress/editor": { "version": "file:packages/editor", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/autop": "file:packages/autop", "@wordpress/blob": "file:packages/blob", @@ -17823,7 +12582,6 @@ "lodash": "^4.17.19", "memize": "^1.1.0", "react-autosize-textarea": "^7.1.0", - "redux-optimist": "^1.0.0", "refx": "^3.0.0", "rememo": "^3.0.0" } @@ -17831,7 +12589,7 @@ "@wordpress/element": { "version": "file:packages/element", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@types/react": "^16.9.0", "@types/react-dom": "^16.9.0", "@wordpress/escape-html": "file:packages/escape-html", @@ -17861,7 +12619,7 @@ "@wordpress/escape-html": { "version": "file:packages/escape-html", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/eslint-plugin": { @@ -17879,14 +12637,14 @@ "eslint-plugin-react": "^7.20.0", "eslint-plugin-react-hooks": "^4.0.4", "globals": "^12.0.0", - "prettier": "npm:wp-prettier@2.0.5", + "prettier": "npm:wp-prettier@2.2.1-beta-1", "requireindex": "^1.2.0" } }, "@wordpress/format-library": { "version": "file:packages/format-library", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:packages/a11y", "@wordpress/block-editor": "file:packages/block-editor", "@wordpress/components": "file:packages/components", @@ -17906,19 +12664,19 @@ "@wordpress/hooks": { "version": "file:packages/hooks", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/html-entities": { "version": "file:packages/html-entities", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/i18n": { "version": "file:packages/i18n", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "gettext-parser": "^1.3.1", "lodash": "^4.17.19", "memize": "^1.1.0", @@ -17929,7 +12687,7 @@ "@wordpress/icons": { "version": "file:packages/icons", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/element": "file:packages/element", "@wordpress/primitives": "file:packages/primitives" } @@ -17937,7 +12695,7 @@ "@wordpress/interface": { "version": "file:packages/interface", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/components": "file:packages/components", "@wordpress/data": "file:packages/data", "@wordpress/deprecated": "file:packages/deprecated", @@ -17953,14 +12711,14 @@ "@wordpress/is-shallow-equal": { "version": "file:packages/is-shallow-equal", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/jest-console": { "version": "file:packages/jest-console", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "jest-matcher-utils": "^25.3.0", "lodash": "^4.17.19" } @@ -17982,18 +12740,17 @@ "dev": true, "requires": { "@axe-core/puppeteer": "^4.0.0", - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/keyboard-shortcuts": { "version": "file:packages/keyboard-shortcuts", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:packages/compose", "@wordpress/data": "file:packages/data", "@wordpress/element": "file:packages/element", "@wordpress/keycodes": "file:packages/keycodes", - "@wordpress/stan": "file:packages/stan", "lodash": "^4.17.19", "rememo": "^3.0.0" } @@ -18001,7 +12758,7 @@ "@wordpress/keycodes": { "version": "file:packages/keycodes", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/i18n": "file:packages/i18n", "lodash": "^4.17.19" } @@ -18026,7 +12783,7 @@ "@wordpress/list-reusable-blocks": { "version": "file:packages/list-reusable-blocks", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/components": "file:packages/components", "@wordpress/compose": "file:packages/compose", @@ -18038,7 +12795,7 @@ "@wordpress/media-utils": { "version": "file:packages/media-utils", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/blob": "file:packages/blob", "@wordpress/element": "file:packages/element", @@ -18049,7 +12806,7 @@ "@wordpress/notices": { "version": "file:packages/notices", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:packages/a11y", "@wordpress/data": "file:packages/data", "lodash": "^4.17.19" @@ -18062,7 +12819,7 @@ "@wordpress/nux": { "version": "file:packages/nux", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/components": "file:packages/components", "@wordpress/compose": "file:packages/compose", "@wordpress/data": "file:packages/data", @@ -18077,7 +12834,7 @@ "@wordpress/plugins": { "version": "file:packages/plugins", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:packages/compose", "@wordpress/element": "file:packages/element", "@wordpress/hooks": "file:packages/hooks", @@ -18109,7 +12866,7 @@ "@wordpress/primitives": { "version": "file:packages/primitives", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/element": "file:packages/element", "classnames": "^2.2.5" } @@ -18117,7 +12874,7 @@ "@wordpress/priority-queue": { "version": "file:packages/priority-queue", "requires": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/project-management-automation": { @@ -18126,7 +12883,7 @@ "requires": { "@actions/core": "^1.0.0", "@actions/github": "^1.0.0", - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" } }, "@wordpress/react-native-aztec": { @@ -18144,7 +12901,7 @@ "@wordpress/react-native-editor": { "version": "file:packages/react-native-editor", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@react-native-community/blur": "3.6.0", "@react-native-community/masked-view": "git+https://github.com/wordpress-mobile/react-native-masked-view.git#f65a51a3320e58404d7f38d967bfd1f42b439ca9", "@react-native-community/slider": "git+https://github.com/wordpress-mobile/react-native-slider.git#d263ff16cdd9fb7352b354342522ff030f220f42", @@ -18190,14 +12947,6 @@ "react-native-video": "git+https://github.com/wordpress-mobile/react-native-video.git#1b964b107863351ed744fc104d7952bbec3e2d4f" }, "dependencies": { - "@babel/runtime": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", - "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, "jsc-android": { "version": "241213.1.0", "resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-241213.1.0.tgz", @@ -18213,7 +12962,7 @@ "@wordpress/redux-routine": { "version": "file:packages/redux-routine", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "is-promise": "^4.0.0", "lodash": "^4.17.19", "rungen": "^0.3.2" @@ -18232,13 +12981,14 @@ "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", "@wordpress/notices": "file:packages/notices", + "@wordpress/url": "file:packages/url", "lodash": "^4.17.19" } }, "@wordpress/rich-text": { "version": "file:packages/rich-text", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:packages/compose", "@wordpress/data": "file:packages/data", "@wordpress/deprecated": "file:packages/deprecated", @@ -18275,7 +13025,7 @@ "dir-glob": "^3.0.1", "eslint": "^7.1.0", "eslint-plugin-markdown": "^1.0.2", - "ignore-emit-webpack-plugin": "2.0.3", + "ignore-emit-webpack-plugin": "^2.0.6", "jest": "^25.3.0", "jest-puppeteer": "^4.4.0", "markdownlint": "^0.18.0", @@ -18284,7 +13034,7 @@ "minimist": "^1.2.0", "npm-package-json-lint": "^5.0.0", "postcss-loader": "^3.0.0", - "prettier": "npm:wp-prettier@2.0.5", + "prettier": "npm:wp-prettier@2.2.1-beta-1", "puppeteer": "npm:puppeteer-core@3.0.0", "read-pkg-up": "^1.0.1", "resolve-bin": "^0.4.0", @@ -18305,7 +13055,7 @@ "@wordpress/server-side-render": { "version": "file:packages/server-side-render", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:packages/api-fetch", "@wordpress/components": "file:packages/components", "@wordpress/data": "file:packages/data", @@ -18319,40 +13069,30 @@ "@wordpress/shortcode": { "version": "file:packages/shortcode", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19", "memize": "^1.1.0" } }, - "@wordpress/stan": { - "version": "file:packages/stan", - "requires": { - "@babel/runtime": "^7.11.2", - "@wordpress/priority-queue": "file:packages/priority-queue", - "equivalent-key-map": "^0.2.2", - "lodash": "^4.17.19" - } - }, "@wordpress/token-list": { "version": "file:packages/token-list", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19" } }, "@wordpress/url": { "version": "file:packages/url", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19", - "qs": "^6.5.2", "react-native-url-polyfill": "^1.1.2" } }, "@wordpress/viewport": { "version": "file:packages/viewport", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:packages/compose", "@wordpress/data": "file:packages/data", "lodash": "^4.17.19" @@ -18364,7 +13104,7 @@ "@wordpress/wordcount": { "version": "file:packages/wordcount", "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19" } }, @@ -18546,20 +13286,21 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -18590,9 +13331,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -18614,52 +13355,73 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, "object.entries": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", - "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", + "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", + "es-abstract": "^1.18.0-next.1", "has": "^1.0.3" } }, "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", + "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", + "es-abstract": "^1.18.0-next.1", "has": "^1.0.3" } }, "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } @@ -18704,28 +13466,6 @@ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", "dev": true }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "alphanum-sort": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", @@ -26806,13 +21546,14 @@ } }, "array.prototype.flatmap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.3.tgz", - "integrity": "sha512-OOEk+lkePcg+ODXIpvuU9PAryCikCJyo7GlDG1upleEpQRx6mzL9puEBkozQ5iAx20KV0l3DbyQwqciJtqe5Pg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", + "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", + "es-abstract": "^1.18.0-next.1", "function-bind": "^1.1.1" }, "dependencies": { @@ -26826,20 +21567,21 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -26870,9 +21612,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -26894,43 +21636,64 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } }, "array.prototype.map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", - "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.3.tgz", + "integrity": "sha512-nNcb30v0wfDyIe26Yif3PcV1JXQp4zEeEfupG7L4SRjnD6HLbO5b2a7eVSba53bOx4YCHYMBHt+Fp4vYstneRA==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", + "es-abstract": "^1.18.0-next.1", "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.4" + "is-string": "^1.0.5" }, "dependencies": { "define-properties": { @@ -26943,20 +21706,21 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -26987,9 +21751,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -27011,29 +21775,49 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } @@ -27110,10 +21894,21 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "ast-types": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.12.4.tgz", - "integrity": "sha512-ky/YVYCbtVAS8TdMIaTiPFHwEpRB5z1hctepJplTr3UW5q8TDrpIMCILyk8pmLxGtn2KCtC/lSn7zOsaI7nzDw==", - "dev": true + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", + "dev": true, + "requires": { + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", + "dev": true + } + } }, "ast-types-flow": { "version": "0.0.7", @@ -27221,9 +22016,9 @@ "dev": true }, "axe-core": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.3.tgz", - "integrity": "sha512-HZpLE7xu05+8AbpqXITGdxp1Xwk8ysAXrg7MiKRY27py3DAyEJpoJQo1727pWF3F+O79V3r+cTWhOzfB49P89w==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz", + "integrity": "sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==", "dev": true }, "axobject-query": { @@ -27294,12 +22089,6 @@ } } }, - "babel-core": { - "version": "7.0.0-bridge.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", - "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", - "dev": true - }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -27879,29 +22668,13 @@ "dev": true }, "babel-plugin-apply-mdx-type-prop": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.5.8.tgz", - "integrity": "sha512-xYp5F9mAnZdDRFSd1vF3XQ0GQUbIulCpnuht2jCmK30GAHL8szVL7TgzwhEGamQ6yJmP/gEyYNM9OR5D2n26eA==", + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", + "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "7.8.3", - "@mdx-js/util": "^1.5.8" - }, - "dependencies": { - "@mdx-js/util": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.5.8.tgz", - "integrity": "sha512-a7Gjjw8bfBSertA/pTWBA/9WKEhgaSxvQE2NTSUzaknrzGFOhs4alZSHh3RHmSFdSWv5pUuzAgsWseMLhWEVkQ==", - "dev": true - } - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", - "requires": { - "object.assign": "^4.1.0" + "@babel/helper-plugin-utils": "7.10.4", + "@mdx-js/util": "1.6.22" } }, "babel-plugin-emotion": { @@ -27956,12 +22729,12 @@ } }, "babel-plugin-extract-import-names": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.5.8.tgz", - "integrity": "sha512-LcLfP8ZRBZMdMAXHLugyvvd5PY0gMmLMWFogWAUsG32X6TYW2Eavx+il2bw73KDbW+UdCC1bAJ3NuU25T1MI3g==", + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", + "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "7.8.3" + "@babel/helper-plugin-utils": "7.10.4" } }, "babel-plugin-inline-json-import": { @@ -27974,176 +22747,16 @@ } }, "babel-plugin-istanbul": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", - "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", + "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.0.0", - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.3.0", - "test-exclude": "^5.2.3" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", - "dev": true, - "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", - "dev": true, - "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" - } - } + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^4.0.0", + "test-exclude": "^6.0.0" } }, "babel-plugin-jest-hoist": { @@ -28305,46 +22918,20 @@ } }, "babel-plugin-named-asset-import": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.6.tgz", - "integrity": "sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.7.tgz", + "integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==", "dev": true }, "babel-plugin-react-docgen": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.1.0.tgz", - "integrity": "sha512-vzpnBlfGv8XOhJM2zbPyyqw2OLEbelgZZsaaRRTpVwNKuYuc+pUg4+dy7i9gCRms0uOQn4osX571HRcCJMJCmA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz", + "integrity": "sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ==", "dev": true, "requires": { + "ast-types": "^0.14.2", "lodash": "^4.17.15", - "react-docgen": "^5.0.0", - "recast": "^0.14.7" - }, - "dependencies": { - "ast-types": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.3.tgz", - "integrity": "sha512-XA5o5dsNw8MhyW0Q7MWXJWc4oOzZKbdsEJq45h7c8q/d9DwWZ5F2ugUc1PuMLPGsUnphCt/cNDHu8JeBbxf1qA==", - "dev": true - }, - "recast": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.14.7.tgz", - "integrity": "sha512-/nwm9pkrcWagN40JeJhkPaRxiHXBRkXyRh/hgU088Z/v+qCy+zIHHY6bC6o7NaKAxPqtE6nD8zBH1LfU0/Wx6A==", - "dev": true, - "requires": { - "ast-types": "0.11.3", - "esprima": "~4.0.0", - "private": "~0.1.5", - "source-map": "~0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } + "react-docgen": "^5.0.0" } }, "babel-plugin-react-native-classname-to-style": { @@ -28362,12 +22949,6 @@ "@babel/template": "^7.0.0-beta.49" } }, - "babel-plugin-require-context-hook": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-require-context-hook/-/babel-plugin-require-context-hook-1.0.0.tgz", - "integrity": "sha512-EMZD1563QUqLhzrqcThk759RhuNVX/ZJdrtGK6drwzgvnR+ARjWyXIHPbu+tUNaMGtPz/gQeAM2M6VUw2UiUeA==", - "dev": true - }, "babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", @@ -28543,56 +23124,6 @@ "lodash": "^4.17.11" } }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - }, - "dependencies": { - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - } - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, - "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - } - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, "bail": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz", @@ -28700,9 +23231,9 @@ } }, "better-opn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-2.0.0.tgz", - "integrity": "sha512-PPbGRgO/K0LowMHbH/JNvaV3qY3Vt+A2nH28fzJxy16h/DfR5OsVti6ldGl6S9SMsyUqT13sltikiAVtI6tKLA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-2.1.1.tgz", + "integrity": "sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA==", "dev": true, "requires": { "open": "^7.0.3" @@ -28718,9 +23249,9 @@ } }, "open": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/open/-/open-7.2.1.tgz", - "integrity": "sha512-xbYCJib4spUdmcs0g/2mK1nKo/jO2T7INClWd/beL7PFkXRWgr8B23ssDHX/USPn2M2IjDR5UdpYs6I67SnTSA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", + "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", "dev": true, "requires": { "is-docker": "^2.0.0", @@ -28935,12 +23466,11 @@ "dev": true }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -29188,33 +23718,46 @@ } }, "browserslist": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", - "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.15.0.tgz", + "integrity": "sha512-IJ1iysdMkGmjjYeRlDU8PQejVwxvVO5QOfXH7ylW31GO6LwNRSmm/SgRXtNsEXqMLl2e+2H5eEJ7sfynF8TCaQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001111", - "electron-to-chromium": "^1.3.523", - "escalade": "^3.0.2", - "node-releases": "^1.1.60" + "caniuse-lite": "^1.0.30001164", + "colorette": "^1.2.1", + "electron-to-chromium": "^1.3.612", + "escalade": "^3.1.1", + "node-releases": "^1.1.67" }, "dependencies": { "caniuse-lite": { - "version": "1.0.30001117", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001117.tgz", - "integrity": "sha512-4tY0Fatzdx59kYjQs+bNxUwZB03ZEBgVmJ1UkFPz/Q8OLiUUbjct2EdpnXj0fvFTPej2EkbPIG0w8BWsjAyk1Q==", + "version": "1.0.30001165", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz", + "integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==", + "dev": true + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", "dev": true }, "electron-to-chromium": { - "version": "1.3.544", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.544.tgz", - "integrity": "sha512-jx6H7M1db76Q/dI3MadZC4qwNTvpiq8tdYEJswxexrIm5bH+LKRdg+VAteMF1tJJbBLrcuogE9N3nxT3Dp1gag==", + "version": "1.3.619", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.619.tgz", + "integrity": "sha512-WFGatwtk7Fw0QcKCZzfGD72hvbcXV8kLY8aFuj0Ip0QRnOtyLYMsc+wXbSjb2w4lk1gcAeNU1/lQ20A+tvuypQ==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, "node-releases": { - "version": "1.1.60", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", - "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", + "version": "1.1.67", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz", + "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==", "dev": true } } @@ -29445,6 +23988,16 @@ } } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "call-me-maybe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", @@ -29486,19 +24039,19 @@ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==" }, "camel-case": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz", - "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", "dev": true, "requires": { - "pascal-case": "^3.1.1", - "tslib": "^1.10.0" + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", "dev": true } } @@ -29530,12 +24083,6 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, - "can-use-dom": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz", - "integrity": "sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo=", - "dev": true - }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -29580,16 +24127,6 @@ "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==", "dev": true }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, "chalk": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", @@ -29666,15 +24203,6 @@ "integrity": "sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA==", "dev": true }, - "character-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", - "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", - "dev": true, - "requires": { - "is-regex": "^1.0.3" - } - }, "character-reference-invalid": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz", @@ -29849,9 +24377,9 @@ "integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0=" }, "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", "dev": true, "requires": { "source-map": "~0.6.0" @@ -30295,9 +24823,9 @@ } }, "comma-separated-tokens": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.7.tgz", - "integrity": "sha512-Jrx3xsP4pPv4AwJUDWY9wOXGtwPXARej6Xd99h4TUGotmf8APuquKMpK+dnD3UgyxK7OEWaisjZz+3b5jtL6xQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", "dev": true }, "command-exists": { @@ -30493,12 +25021,6 @@ "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=", "dev": true }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, "has-ansi": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", @@ -30591,18 +25113,6 @@ "resolved": "https://registry.npmjs.org/consolidated-events/-/consolidated-events-2.0.2.tgz", "integrity": "sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ==" }, - "constantinople": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz", - "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==", - "dev": true, - "requires": { - "@types/babel-types": "^7.0.0", - "@types/babylon": "^6.16.2", - "babel-types": "^6.26.0", - "babylon": "^6.18.0" - } - }, "constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -31140,9 +25650,9 @@ "dev": true }, "copy-to-clipboard": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz", - "integrity": "sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", "dev": true, "requires": { "toggle-selection": "^1.0.6" @@ -31203,24 +25713,6 @@ "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", "dev": true }, - "core-js-compat": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", - "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==", - "dev": true, - "requires": { - "browserslist": "^4.8.3", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, "core-js-pure": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.2.1.tgz", @@ -31287,6 +25779,43 @@ } } }, + "cp-file": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-7.0.0.tgz", + "integrity": "sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "nested-error-stacks": "^2.0.0", + "p-event": "^4.1.0" + } + }, + "cpy": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/cpy/-/cpy-8.1.1.tgz", + "integrity": "sha512-vqHT+9o67sMwJ5hUd/BAOYeemkU+MuFRsK2c36Xc3eefQpAsp1kAsyDxEDcc5JS1+y9l/XHPrIsVTcyGGmkUUQ==", + "dev": true, + "requires": { + "arrify": "^2.0.1", + "cp-file": "^7.0.0", + "globby": "^9.2.0", + "has-glob": "^1.0.0", + "junk": "^3.1.0", + "nested-error-stacks": "^2.1.0", + "p-all": "^2.1.0", + "p-filter": "^2.1.0", + "p-map": "^3.0.0" + }, + "dependencies": { + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + } + } + }, "crc": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", @@ -31379,13 +25908,13 @@ } }, "create-react-context": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz", - "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", "dev": true, "requires": { - "fbjs": "^0.8.0", - "gud": "^1.0.0" + "gud": "^1.0.0", + "warning": "^4.0.3" } }, "cross-env": { @@ -31881,16 +26410,6 @@ "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", "dev": true }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, "damerau-levenshtein": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz", @@ -31926,6 +26445,12 @@ "whatwg-url": "^7.0.0" } }, + "date-fns": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", + "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==", + "dev": true + }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -31937,12 +26462,6 @@ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.18.tgz", "integrity": "sha512-JBMJZghNK8TtuoPnKNIzW9xavVVigld/zmZNpZSyQbkb2Opp55YIfZUpE4OEqPF/iyUVQTKcn1bC2HtC8B7s3g==" }, - "de-indent": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", - "dev": true - }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -32010,28 +26529,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - } - } - }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -32327,9 +26824,9 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, "detab": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.2.tgz", - "integrity": "sha512-Q57yPrxScy816TTE1P/uLRXLDKjXhvYTbfxS/e6lPD+YrqghbsMlGB9nQzj/zVtSPaF0DFPSdO916EWO4sQUyQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz", + "integrity": "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==", "dev": true, "requires": { "repeat-string": "^1.5.4" @@ -32358,12 +26855,6 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", - "dev": true - }, "detect-port": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz", @@ -32465,12 +26956,6 @@ "esutils": "^2.0.2" } }, - "doctypes": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", - "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=", - "dev": true - }, "dom-accessibility-api": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.4.3.tgz", @@ -32487,11 +26972,21 @@ } }, "dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "dev": true, "requires": { - "@babel/runtime": "^7.1.2" + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "dev": true + } } }, "dom-scroll-into-view": { @@ -32516,9 +27011,9 @@ } }, "dom-walk": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", "dev": true }, "domain-browser": { @@ -32559,19 +27054,19 @@ } }, "dot-case": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz", - "integrity": "sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "dev": true, "requires": { - "no-case": "^3.0.3", - "tslib": "^1.10.0" + "no-case": "^3.0.4", + "tslib": "^2.0.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", "dev": true } } @@ -32684,9 +27179,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.564", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.564.tgz", - "integrity": "sha512-fNaYN3EtKQWLQsrKXui8mzcryJXuA0LbCLoizeX6oayG2emBaS5MauKjCPAvc29NEY4FpLHIUWiP+Y0Bfrs5dg==", + "version": "1.3.629", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.629.tgz", + "integrity": "sha512-iSPPJtPvHrMAvYOt+9cdbDmTasPqwnwz4lkP8Dn200gDNUBQOLQ96xUsWXBwXslAo5XxdoXAoQQ3RAy4uao9IQ==", "dev": true }, "elegant-spinner": { @@ -32741,16 +27236,10 @@ "hoist-non-react-statics": "^3.3.0" }, "dependencies": { - "@emotion/weak-memoize": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", - "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==", - "dev": true - }, "hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "dev": true, "requires": { "react-is": "^16.7.0" @@ -33580,6 +28069,15 @@ "is-arrayish": "^0.2.1" } }, + "error-stack-parser": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", + "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "dev": true, + "requires": { + "stackframe": "^1.1.1" + } + }, "errorhandler": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", @@ -33637,12 +28135,13 @@ "dev": true }, "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.1.tgz", + "integrity": "sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw==", "dev": true, "requires": { - "es-abstract": "^1.17.4", + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.1", "has-symbols": "^1.0.1", "is-arguments": "^1.0.4", "is-map": "^2.0.1", @@ -33651,112 +28150,17 @@ "isarray": "^2.0.5" }, "dependencies": { - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, "isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true - }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } } } }, @@ -33770,34 +28174,12 @@ "is-symbol": "^1.0.1" } }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dev": true, - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, "es5-shim": { "version": "4.5.14", "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.14.tgz", "integrity": "sha512-7SwlpL+2JpymWTt8sNLuC2zdhhc+wrfe5cMPI2j0o6WsPdfAiPwmFy2f0AocPB4RQVBOZ9kNTgi5YF7TdhkvEg==", "dev": true }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -33814,25 +28196,9 @@ } }, "es6-shim": { - "version": "0.35.5", - "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.5.tgz", - "integrity": "sha512-E9kK/bjtCQRpN1K28Xh4BlmP8egvZBGJJ+9GtnzOwt7mdqtrjHFuVGr7QJfdjBIKqrlU5duPf3pCBoDrkjVYFg==", - "dev": true - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "escalade": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", - "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", + "version": "0.35.6", + "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.6.tgz", + "integrity": "sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==", "dev": true }, "escape-html": { @@ -35524,23 +29890,6 @@ } } }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dev": true, - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", - "dev": true - } - } - }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -35756,12 +30105,12 @@ } }, "fault": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.3.tgz", - "integrity": "sha512-sfFuP4X0hzrbGKjAUNXYvNqsZ5F6ohx/dZ9I0KQud/aiZNwg263r5L9yGB0clvXHCkzXh5W3t7RSHchggYIFmA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", "dev": true, "requires": { - "format": "^0.2.2" + "format": "^0.2.0" } }, "faye-websocket": { @@ -35869,13 +30218,13 @@ } }, "file-loader": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.0.tgz", - "integrity": "sha512-26qPdHyTsArQ6gU4P1HJbAbnFTyT2r0pG7czh1GFAd9TZbj0n94wWbupgixZH/ET/meqi2/5+F7DhW4OAXD+Lg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", "dev": true, "requires": { "loader-utils": "^2.0.0", - "schema-utils": "^2.7.1" + "schema-utils": "^3.0.0" }, "dependencies": { "@types/json-schema": { @@ -35885,9 +30234,9 @@ "dev": true }, "ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -35940,20 +30289,14 @@ "json5": "^2.1.2" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } } @@ -36355,12 +30698,6 @@ "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", "dev": true }, - "flow-parser": { - "version": "0.116.1", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.116.1.tgz", - "integrity": "sha512-uMbaTjiMhBKa/il1esHyWyVVWfrWdG/eLmG62MQulZ59Yghpa30H1tmukFZLptsBafZ8ddiPyf7I+SiA+euZ6A==", - "dev": true - }, "flush-write-stream": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", @@ -36371,12 +30708,6 @@ "readable-stream": "^2.0.4" } }, - "focus-lock": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.6.tgz", - "integrity": "sha512-Dx69IXGCq1qsUExWuG+5wkiMqVM/zGx/reXSJSLogECwp3x6KeNQZ+NAetgxEFpnC41rD8U3+jRCW68+LNzdtw==", - "dev": true - }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -36651,6 +30982,25 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "get-intrinsic": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", + "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + } + } + }, "get-own-enumerable-property-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", @@ -37023,9 +31373,9 @@ } }, "github-slugger": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.2.1.tgz", - "integrity": "sha512-SsZUjg/P03KPzQBt7OxJPasGw6NRO5uOgiZ5RGXVud5iSIZ0eNZeNp5rTwCxtavrRUa/A77j8mePVc5lEvk0KQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.3.0.tgz", + "integrity": "sha512-gwJScWVNhFYSRDvURk/8yhcFBee6aFjye2a7Lhb2bUyRulpIoek9p0I9Kt7PT67d/nUlZbFu8L9RLiA0woQN8Q==", "dev": true, "requires": { "emoji-regex": ">=6.0.0 <=6.1.1" @@ -37463,6 +31813,26 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "has-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-glob/-/has-glob-1.0.0.tgz", + "integrity": "sha1-mqqe7b/7G6OZCnsAEPtnjuAIEgc=", + "dev": true, + "requires": { + "is-glob": "^3.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", @@ -37513,12 +31883,6 @@ "safe-buffer": "^5.0.1" } }, - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", - "dev": true - }, "hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -37530,98 +31894,176 @@ } }, "hast-to-hyperscript": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-7.0.4.tgz", - "integrity": "sha512-vmwriQ2H0RPS9ho4Kkbf3n3lY436QKLq6VaGA1pzBh36hBi3tm1DO9bR+kaJIbpT10UqaANDkMjxvjVfr+cnOA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", + "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==", "dev": true, "requires": { + "@types/unist": "^2.0.3", "comma-separated-tokens": "^1.0.0", "property-information": "^5.3.0", "space-separated-tokens": "^1.0.0", - "style-to-object": "^0.2.1", - "unist-util-is": "^3.0.0", - "web-namespaces": "^1.1.2" + "style-to-object": "^0.3.0", + "unist-util-is": "^4.0.0", + "web-namespaces": "^1.0.0" }, "dependencies": { - "style-to-object": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.2.3.tgz", - "integrity": "sha512-1d/k4EY2N7jVLOqf2j04dTc37TPOv/hHxZmvpg8Pdh8UYydxeu/C1W1U4vD8alzf5V2Gt7rLsmkr4dxAlDm9ng==", - "dev": true, - "requires": { - "inline-style-parser": "0.1.1" - } - }, "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", "dev": true } } }, "hast-util-from-parse5": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-5.0.2.tgz", - "integrity": "sha512-YXFjoRS7ES7PEoLx6uihtSfKTO1s3z/tzGiV5cVpsUiihduogFXubNRCzTIW3yOOGO1nws9CxPq4MbwD39Uo+w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz", + "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==", "dev": true, "requires": { - "ccount": "^1.0.3", - "hastscript": "^5.0.0", + "@types/parse5": "^5.0.0", + "hastscript": "^6.0.0", "property-information": "^5.0.0", - "web-namespaces": "^1.1.2", - "xtend": "^4.0.1" + "vfile": "^4.0.0", + "vfile-location": "^3.2.0", + "web-namespaces": "^1.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.2" + } + }, + "vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + } + }, + "vfile-location": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", + "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "dev": true + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + } } }, "hast-util-parse-selector": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.3.tgz", - "integrity": "sha512-nxbeqjQNxsvo/uYYAw9kij6td05YVUlf1qti09rVfbWSLT5H6wo3c+USIwX6nzXWk5kFZzXnEqO82856r0aM2Q==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", "dev": true }, "hast-util-raw": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-5.0.1.tgz", - "integrity": "sha512-iHo7G6BjRc/GU1Yun5CIEXjil0wVnIbz11C6k0JdDichSDMtYi2+NNtk6YN7EOP0JfPstX30d3pRLfaJv5CkdA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz", + "integrity": "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==", "dev": true, "requires": { - "hast-util-from-parse5": "^5.0.0", - "hast-util-to-parse5": "^5.0.0", - "html-void-elements": "^1.0.1", - "parse5": "^5.0.0", + "@types/hast": "^2.0.0", + "hast-util-from-parse5": "^6.0.0", + "hast-util-to-parse5": "^6.0.0", + "html-void-elements": "^1.0.0", + "parse5": "^6.0.0", "unist-util-position": "^3.0.0", + "vfile": "^4.0.0", "web-namespaces": "^1.0.0", - "xtend": "^4.0.1", + "xtend": "^4.0.0", "zwitch": "^1.0.0" }, "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + }, "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "dev": true + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.2" + } + }, + "vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + } + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } } } }, "hast-util-to-parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-5.1.1.tgz", - "integrity": "sha512-ivCeAd5FCXr7bapJIVsWMnx/EmbjkkW2TU2hd1prq+jGwiaUoK+FcpjyPNwsC5ogzCwWO669tOqIovGeLc/ntg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", + "integrity": "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==", "dev": true, "requires": { - "hast-to-hyperscript": "^7.0.0", + "hast-to-hyperscript": "^9.0.0", "property-information": "^5.0.0", "web-namespaces": "^1.0.0", - "xtend": "^4.0.1", + "xtend": "^4.0.0", "zwitch": "^1.0.0" } }, "hastscript": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-5.1.1.tgz", - "integrity": "sha512-xHo1Hkcqd0LlWNuDL3/BxwhgAGp3d7uEvCMgCTrBY+zsOooPPH+8KAvW8PCgl+GB8H3H44nfSaF0A4BQ+4xlYg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", "dev": true, "requires": { + "@types/hast": "^2.0.0", "comma-separated-tokens": "^1.0.0", "hast-util-parse-selector": "^2.0.0", "property-information": "^5.0.0", @@ -37646,9 +32088,9 @@ "dev": true }, "highlight.js": { - "version": "9.13.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.13.1.tgz", - "integrity": "sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz", + "integrity": "sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg==", "dev": true }, "hmac-drbg": { @@ -37729,9 +32171,9 @@ } }, "html-entities": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", - "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.3.tgz", + "integrity": "sha512-/VulV3SYni1taM7a4RMdceqzJWR39gpZHjBwUnsCFKWV/GJkD14CJ5F7eWcZozmHJK0/f/H5U3b3SiPkuvxMgg==", "dev": true }, "html-escaper": { @@ -37755,26 +32197,11 @@ "terser": "^4.6.3" }, "dependencies": { - "clean-css": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", - "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", - "dev": true, - "requires": { - "source-map": "~0.6.0" - } - }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true } } }, @@ -37785,15 +32212,15 @@ "dev": true }, "html-void-elements": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.4.tgz", - "integrity": "sha512-yMk3naGPLrfvUV9TdDbuYXngh/TpHbA6TrOw3HL9kS8yhwx7i309BReNg7CbAJXGE+UMJ6je5OqJ7lC63o6YuQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", + "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", "dev": true }, "html-webpack-plugin": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.4.1.tgz", - "integrity": "sha512-nEtdEIsIGXdXGG7MjTTZlmhqhpHU9pJFc1OYxcP36c5/ZKP6b0BJMww2QTvJGQYA9aMxUnjDujpZdYcVOXiBCQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz", + "integrity": "sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw==", "dev": true, "requires": { "@types/html-minifier-terser": "^5.0.0", @@ -38281,9 +32708,9 @@ "dev": true }, "ignore-emit-webpack-plugin": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/ignore-emit-webpack-plugin/-/ignore-emit-webpack-plugin-2.0.3.tgz", - "integrity": "sha512-ahTYD5KZ3DiZG9goS8NCxBaPEfXsPLH5JeWKmFTThD8lsPen6R4tLnWcN/mrksK5cDqyxOzmRL12feJQZjffuA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/ignore-emit-webpack-plugin/-/ignore-emit-webpack-plugin-2.0.6.tgz", + "integrity": "sha512-/zC18RWCC2wz4ZwnS4UoujGWzvSKy28DLjtE+jrGBOXej6YdmityhBDzE8E0NlktEqi4tgdNbydX8B6G4haHSQ==", "dev": true }, "ignore-walk": { @@ -38907,10 +33334,13 @@ } }, "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } }, "is-arrayish": { "version": "0.2.1", @@ -39038,24 +33468,6 @@ "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", "dev": true }, - "is-expression": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz", - "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=", - "dev": true, - "requires": { - "acorn": "~4.0.2", - "object-assign": "^4.0.1" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -39082,9 +33494,9 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-function": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", - "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", "dev": true }, "is-generator-fn": { @@ -39115,9 +33527,15 @@ "dev": true }, "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true + }, + "is-negative-zero": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", - "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "dev": true }, "is-number": { @@ -39236,9 +33654,9 @@ "dev": true }, "is-set": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", - "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "dev": true }, "is-ssh": { @@ -42173,9 +36591,9 @@ } }, "jest-regex-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", - "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", "dev": true }, "jest-resolve": { @@ -44461,12 +38879,6 @@ "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=", "dev": true }, - "js-stringify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", - "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -44493,64 +38905,6 @@ "resolved": "https://registry.npmjs.org/jsc-android/-/jsc-android-245459.0.0.tgz", "integrity": "sha512-wkjURqwaB1daNkDi2OYYbsLnIdC/lUM2nPXQKRs5pqEU9chDg435bjvo+LSaHotDENygHQDHe+ntUkkw2gwMtg==" }, - "jscodeshift": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.7.0.tgz", - "integrity": "sha512-Kt6rpTa1HVhAWagD6J0y6qxxqRmDgkFvczerLgOsDNSGoUZSmq2CO1vFRcda9OV1BaZKSHCIh+VREPts5tB/Ig==", - "dev": true, - "requires": { - "@babel/core": "^7.1.6", - "@babel/parser": "^7.1.6", - "@babel/plugin-proposal-class-properties": "^7.1.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/preset-env": "^7.1.6", - "@babel/preset-flow": "^7.0.0", - "@babel/preset-typescript": "^7.1.0", - "@babel/register": "^7.0.0", - "babel-core": "^7.0.0-bridge.0", - "colors": "^1.1.2", - "flow-parser": "0.*", - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "neo-async": "^2.5.0", - "node-dir": "^0.1.17", - "recast": "^0.18.1", - "temp": "^0.8.1", - "write-file-atomic": "^2.3.0" - }, - "dependencies": { - "ast-types": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz", - "integrity": "sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "recast": { - "version": "0.18.5", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.18.5.tgz", - "integrity": "sha512-sD1WJrpLQAkXGyQZyGzTM75WJvyAd98II5CHdK3IYbt/cZlU0UzCRVU11nUFNXX9fBVEt4E9ajkMjBlUlG+Oog==", - "dev": true, - "requires": { - "ast-types": "0.13.2", - "esprima": "~4.0.0", - "private": "^0.1.8", - "source-map": "~0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "jsdoctypeparser": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz", @@ -44797,24 +39151,6 @@ "verror": "1.10.0" } }, - "jstransformer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", - "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", - "dev": true, - "requires": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" - }, - "dependencies": { - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - } - } - }, "jsx-ast-utils": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz", @@ -44836,6 +39172,12 @@ "set-immediate-shim": "~1.0.1" } }, + "junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true + }, "keyv": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.0.tgz", @@ -44954,23 +39296,6 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" - }, - "dependencies": { - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - } - } - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -45412,12 +39737,6 @@ "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } - }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true } } }, @@ -45471,6 +39790,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" }, + "lodash-es": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", + "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==", + "dev": true + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -45483,12 +39808,6 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "dev": true - }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -45839,16 +40158,6 @@ } } }, - "loglevelnext": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", - "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", - "dev": true, - "requires": { - "es6-symbol": "^3.1.1", - "object.assign": "^4.1.0" - } - }, "lolex": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", @@ -45858,12 +40167,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, "longest-streak": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.2.tgz", @@ -45889,18 +40192,18 @@ } }, "lower-case": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz", - "integrity": "sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", "dev": true, "requires": { - "tslib": "^1.10.0" + "tslib": "^2.0.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", "dev": true } } @@ -45912,13 +40215,13 @@ "dev": true }, "lowlight": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.11.0.tgz", - "integrity": "sha512-xrGGN6XLL7MbTMdPD6NfWPwY43SNkjf/d0mecSx/CW36fUZTjRHEq0/Cdug3TWKtRXLWi7iMl1eP0olYxj/a4A==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.17.0.tgz", + "integrity": "sha512-vmtBgYKD+QVNy7tIa7ulz5d//Il9R4MooOVh4nkOf9R9Cb/Dk5TXMSTieg/vDulkBkIWj59/BIlyFQxT9X1oAQ==", "dev": true, "requires": { - "fault": "^1.0.2", - "highlight.js": "~9.13.0" + "fault": "^1.0.0", + "highlight.js": "~10.4.0" } }, "lru-cache": { @@ -46192,9 +40495,9 @@ "dev": true }, "markdown-to-jsx": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.10.3.tgz", - "integrity": "sha512-PSoUyLnW/xoW6RsxZrquSSz5eGEOTwa15H5eqp3enmrp8esmgDJmhzd6zmQ9tgAA9TxJzx1Hmf3incYU/IamoQ==", + "version": "6.11.4", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz", + "integrity": "sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==", "dev": true, "requires": { "prop-types": "^15.6.2", @@ -46290,12 +40593,12 @@ } }, "mdast-squeeze-paragraphs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-3.0.5.tgz", - "integrity": "sha512-xX6Vbe348Y/rukQlG4W3xH+7v4ZlzUbSY4HUIQCuYrF2DrkcHx584mCaFxkWoDZKNUfyLZItHC9VAqX3kIP7XA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==", "dev": true, "requires": { - "unist-util-remove": "^1.0.0" + "unist-util-remove": "^2.0.0" } }, "mdast-util-compact": { @@ -46309,12 +40612,41 @@ } }, "mdast-util-definitions": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-1.2.5.tgz", - "integrity": "sha512-CJXEdoLfiISCDc2JB6QLb79pYfI6+GcIH+W2ox9nMc7od0Pz+bovcHsiq29xAQY6ayqe/9CsK2VzkSJdg1pFYA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", "dev": true, "requires": { - "unist-util-visit": "^1.0.0" + "unist-util-visit": "^2.0.0" + }, + "dependencies": { + "unist-util-is": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", + "dev": true + }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + } } }, "mdast-util-inject": { @@ -46327,22 +40659,48 @@ } }, "mdast-util-to-hast": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-6.0.2.tgz", - "integrity": "sha512-GjcOimC9qHI0yNFAQdBesrZXzUkRdFleQlcoU8+TVNfDW6oLUazUx8MgUoTaUyCJzBOnE5AOgqhpURrSlf0QwQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", + "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", "dev": true, "requires": { - "collapse-white-space": "^1.0.0", - "detab": "^2.0.0", - "mdast-util-definitions": "^1.2.0", - "mdurl": "^1.0.1", - "trim": "0.0.1", - "trim-lines": "^1.0.0", - "unist-builder": "^1.0.1", - "unist-util-generated": "^1.1.0", + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", "unist-util-position": "^3.0.0", - "unist-util-visit": "^1.1.0", - "xtend": "^4.0.1" + "unist-util-visit": "^2.0.0" + }, + "dependencies": { + "unist-util-is": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", + "dev": true + }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + } } }, "mdast-util-to-string": { @@ -48063,6 +42421,15 @@ "to-regex": "^3.0.1" } }, + "native-url": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/native-url/-/native-url-0.2.6.tgz", + "integrity": "sha512-k4bDC87WtgrdD362gZz6zoiXQrl40kYlBmpfmSjwRO1VU0V5ccwJTlxuE72F6m3V0vc1xOf6n3UCP9QyerRqmA==", + "dev": true, + "requires": { + "querystring": "^0.2.0" + } + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -48129,10 +42496,10 @@ "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==", "dev": true }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "nested-error-stacks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", + "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", "dev": true }, "nice-try": { @@ -48141,19 +42508,19 @@ "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==" }, "no-case": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz", - "integrity": "sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, "requires": { - "lower-case": "^2.0.1", - "tslib": "^1.10.0" + "lower-case": "^2.0.2", + "tslib": "^2.0.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", "dev": true } } @@ -48428,9 +42795,9 @@ } }, "node-releases": { - "version": "1.1.60", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", - "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", + "version": "1.1.67", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.67.tgz", + "integrity": "sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg==", "dev": true }, "node-watch": { @@ -49378,14 +43745,14 @@ } }, "object.fromentries": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", - "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.3.tgz", + "integrity": "sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", + "es-abstract": "^1.18.0-next.1", "has": "^1.0.3" }, "dependencies": { @@ -49399,20 +43766,21 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -49443,9 +43811,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -49467,29 +43835,49 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } @@ -49782,11 +44170,28 @@ } }, "overlayscrollbars": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.0.tgz", - "integrity": "sha512-p8oHrMeRAKxXDMPI/EBNITj/zTVHKNnAnM59Im+xnoZUlV07FyTg46wom2286jJlXGGfcPFG/ba5NUiCwWNd4w==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.1.tgz", + "integrity": "sha512-gIQfzgGgu1wy80EB4/6DaJGHMEGmizq27xHIESrzXq0Y/J0Ay1P3DWk6tuVmEPIZH15zaBlxeEJOqdJKmowHCQ==", "dev": true }, + "p-all": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-all/-/p-all-2.1.0.tgz", + "integrity": "sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA==", + "dev": true, + "requires": { + "p-map": "^2.0.0" + }, + "dependencies": { + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, "p-cancelable": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", @@ -49813,6 +44218,23 @@ "p-timeout": "^2.0.1" } }, + "p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "requires": { + "p-map": "^2.0.0" + }, + "dependencies": { + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -49918,19 +44340,19 @@ } }, "param-case": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz", - "integrity": "sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, "requires": { - "dot-case": "^3.0.3", - "tslib": "^1.10.0" + "dot-case": "^3.0.4", + "tslib": "^2.0.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", "dev": true } } @@ -50033,19 +44455,19 @@ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, "pascal-case": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz", - "integrity": "sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", "dev": true, "requires": { - "no-case": "^3.0.3", - "tslib": "^1.10.0" + "no-case": "^3.0.4", + "tslib": "^2.0.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", "dev": true } } @@ -50287,12 +44709,57 @@ } }, "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", "dev": true, "requires": { - "find-up": "^2.1.0" + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + } } }, "platform": { @@ -50386,20 +44853,14 @@ } }, "polished": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/polished/-/polished-3.4.2.tgz", - "integrity": "sha512-9Rch6iMZckABr6EFCLPZsxodeBpXMo9H4fRlfR/9VjMEyy5xpo1/WgXlJGgSjPyVhEZNycbW7UmYMNyWS5MI0g==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/polished/-/polished-3.6.7.tgz", + "integrity": "sha512-b4OViUOihwV0icb9PHmWbR+vPqaSzSAEbgLskvb7ANPATVXGiYv/TQFHQo65S53WU9i5EQ1I03YDOJW7K0bmYg==", "dev": true, "requires": { - "@babel/runtime": "^7.6.3" + "@babel/runtime": "^7.9.2" } }, - "popper.js": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz", - "integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw==", - "dev": true - }, "portfinder": { "version": "1.0.25", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", @@ -51367,9 +45828,9 @@ "dev": true }, "prettier": { - "version": "npm:wp-prettier@2.0.5", - "resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-2.0.5.tgz", - "integrity": "sha512-5GCgdeevIXwR3cW4Qj5XWC5MO1iSCz8+IPn0mMw6awAt/PBiey8yyO7MhePRsaMqghJAhg6Q3QLYWSnUHWkG6A==", + "version": "npm:wp-prettier@2.2.1-beta-1", + "resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-2.2.1-beta-1.tgz", + "integrity": "sha512-+JHkqs9LC/JPp51yy1hzs3lQ7qeuWCwOcSzpQNeeY/G7oSpnF61vxt7hRh87zNRTr6ob2ndy0W8rVzhgrcA+Gw==", "dev": true }, "prettier-linter-helpers": { @@ -51382,13 +45843,21 @@ } }, "pretty-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", - "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", "dev": true, "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + }, + "dependencies": { + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + } } }, "pretty-format": { @@ -51434,9 +45903,9 @@ "dev": true }, "prismjs": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.19.0.tgz", - "integrity": "sha512-IVFtbW9mCWm9eOIaEkNyo2Vl4NnEifis2GQ7/MLRG5TQe6t+4Sj9J5QWI9i3v+SS43uZBlCAOn+zYTVYQcPXJw==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.22.0.tgz", + "integrity": "sha512-lLJ/Wt9yy0AiSYBf212kK3mM5L8ycwlyTlSxHBAneXLR0nzFMlZ5y7riFPF3E33zXOF2IH95xdY5jIyZbM9z/w==", "dev": true, "requires": { "clipboard": "^2.0.0" @@ -51511,20 +45980,20 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -51555,9 +46024,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -51579,29 +46048,49 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } @@ -51627,20 +46116,20 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -51671,9 +46160,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -51695,29 +46184,49 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } @@ -51768,12 +46277,12 @@ "dev": true }, "property-information": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.3.0.tgz", - "integrity": "sha512-IslotQn1hBCZDY7SaJ3zmCjVea219VTwmOk6Pu3z9haU9m4+T8GwaDubur+6NMHEU+Fjs/6/p66z6QULPkcL1w==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", "dev": true, "requires": { - "xtend": "^4.0.1" + "xtend": "^4.0.0" } }, "proto-list": { @@ -51844,180 +46353,6 @@ "safe-buffer": "^5.1.2" } }, - "pug": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz", - "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==", - "dev": true, - "requires": { - "pug-code-gen": "^2.0.2", - "pug-filters": "^3.1.1", - "pug-lexer": "^4.1.0", - "pug-linker": "^3.0.6", - "pug-load": "^2.0.12", - "pug-parser": "^5.0.1", - "pug-runtime": "^2.0.5", - "pug-strip-comments": "^1.0.4" - } - }, - "pug-attrs": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz", - "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==", - "dev": true, - "requires": { - "constantinople": "^3.0.1", - "js-stringify": "^1.0.1", - "pug-runtime": "^2.0.5" - } - }, - "pug-code-gen": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz", - "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==", - "dev": true, - "requires": { - "constantinople": "^3.1.2", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.1", - "pug-attrs": "^2.0.4", - "pug-error": "^1.3.3", - "pug-runtime": "^2.0.5", - "void-elements": "^2.0.1", - "with": "^5.0.0" - } - }, - "pug-error": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz", - "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ==", - "dev": true - }, - "pug-filters": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz", - "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==", - "dev": true, - "requires": { - "clean-css": "^4.1.11", - "constantinople": "^3.0.1", - "jstransformer": "1.0.0", - "pug-error": "^1.3.3", - "pug-walk": "^1.1.8", - "resolve": "^1.1.6", - "uglify-js": "^2.6.1" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - } - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } - } - }, - "pug-lexer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz", - "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==", - "dev": true, - "requires": { - "character-parser": "^2.1.1", - "is-expression": "^3.0.0", - "pug-error": "^1.3.3" - } - }, - "pug-linker": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz", - "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==", - "dev": true, - "requires": { - "pug-error": "^1.3.3", - "pug-walk": "^1.1.8" - } - }, - "pug-load": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz", - "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==", - "dev": true, - "requires": { - "object-assign": "^4.1.0", - "pug-walk": "^1.1.8" - } - }, - "pug-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz", - "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==", - "dev": true, - "requires": { - "pug-error": "^1.3.3", - "token-stream": "0.0.1" - } - }, - "pug-runtime": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz", - "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw==", - "dev": true - }, - "pug-strip-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz", - "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==", - "dev": true, - "requires": { - "pug-error": "^1.3.3" - } - }, - "pug-walk": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz", - "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA==", - "dev": true - }, "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", @@ -52226,9 +46561,9 @@ } }, "ws": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.0.tgz", - "integrity": "sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", + "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==", "dev": true }, "yauzl": { @@ -52252,7 +46587,8 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true }, "query-string": { "version": "4.3.4", @@ -52385,13 +46721,13 @@ } }, "raw-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.1.tgz", - "integrity": "sha512-baolhQBSi3iNh1cglJjA0mYzga+wePk7vdEX//1dTFd+v4TsQlQE0jitJSNF1OIP82rdYulH7otaVmdlDaJ64A==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", "dev": true, "requires": { "loader-utils": "^2.0.0", - "schema-utils": "^2.6.5" + "schema-utils": "^3.0.0" }, "dependencies": { "@types/json-schema": { @@ -52401,9 +46737,9 @@ "dev": true }, "ajv": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", - "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -52456,20 +46792,14 @@ "json5": "^2.1.2" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", "dev": true, "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } } @@ -52524,23 +46854,15 @@ "prop-types": "^15.5.6" } }, - "react-clientside-effect": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.2.tgz", - "integrity": "sha512-nRmoyxeok5PBO6ytPvSjKp9xwXg9xagoTK1mMjwnQxqM9Hd7MNPl+LS1bOSOe+CV2+4fnEquc7H/S8QD3q697A==", - "dev": true, - "requires": { - "@babel/runtime": "^7.0.0" - } - }, "react-color": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.18.0.tgz", - "integrity": "sha512-FyVeU1kQiSokWc8NPz22azl1ezLpJdUyTbWL0LPUpcuuYDrZ/Y1veOk9rRK5B3pMlyDGvTk4f4KJhlkIQNRjEA==", + "version": "2.19.3", + "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz", + "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==", "dev": true, "requires": { "@icons/material": "^0.2.4", - "lodash": "^4.17.11", + "lodash": "^4.17.15", + "lodash-es": "^4.17.15", "material-colors": "^1.2.1", "prop-types": "^15.5.10", "reactcss": "^1.2.0", @@ -52599,6 +46921,15 @@ "text-table": "0.2.0" }, "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -52658,9 +46989,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001124", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001124.tgz", - "integrity": "sha512-zQW8V3CdND7GHRH6rxm6s59Ww4g/qGWTheoboW9nfeMg7sUoopIfKCcNZUjwYRCOrvereh3kwDpZj4VLQ7zGtA==", + "version": "1.0.30001168", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz", + "integrity": "sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ==", "dev": true }, "chalk": { @@ -52683,9 +47014,9 @@ } }, "chokidar": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", - "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "dev": true, "requires": { "anymatch": "~3.1.1", @@ -52695,7 +47026,7 @@ "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" + "readdirp": "~3.5.0" } }, "cli-cursor": { @@ -52799,32 +47130,6 @@ "requires": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" - }, - "dependencies": { - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } } }, "fork-ts-checker-webpack-plugin": { @@ -52972,13 +47277,12 @@ } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, "mimic-fn": { @@ -53009,9 +47313,9 @@ } }, "open": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/open/-/open-7.2.1.tgz", - "integrity": "sha512-xbYCJib4spUdmcs0g/2mK1nKo/jO2T7INClWd/beL7PFkXRWgr8B23ssDHX/USPn2M2IjDR5UdpYs6I67SnTSA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", + "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", "dev": true, "requires": { "is-docker": "^2.0.0", @@ -53028,12 +47332,12 @@ } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^2.2.0" } }, "p-try": { @@ -53042,6 +47346,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -53054,30 +47364,10 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - } - } - }, "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -53210,14 +47500,14 @@ } }, "react-docgen": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-5.3.0.tgz", - "integrity": "sha512-hUrv69k6nxazOuOmdGeOpC/ldiKy7Qj/UFpxaQi0eDMrUFUTIPGtY5HJu7BggSmiyAMfREaESbtBL9UzdQ+hyg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-5.3.1.tgz", + "integrity": "sha512-YG7YujVTwlLslr2Ny8nQiUfbBuEwKsLHJdQTSdEga1eY/nRFh/7LjCWUn6ogYhu2WDKg4z+6W/BJtUi+DPUIlA==", "dev": true, "requires": { "@babel/core": "^7.7.5", "@babel/runtime": "^7.7.6", - "ast-types": "^0.13.2", + "ast-types": "^0.14.2", "commander": "^2.19.0", "doctrine": "^3.0.0", "neo-async": "^2.6.1", @@ -53225,15 +47515,6 @@ "strip-indent": "^3.0.0" }, "dependencies": { - "ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "requires": { - "tslib": "^2.0.1" - } - }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -53263,77 +47544,25 @@ "requires": { "min-indent": "^1.0.0" } - }, - "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", - "dev": true } } }, "react-docgen-typescript": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-1.20.4.tgz", - "integrity": "sha512-gE2SeseJd6+o981qr9VQJRbvFJ5LjLSKQiwhHsuLN4flt+lheKtG1jp2BPzrv2MKR5gmbLwpmNtK4wbLCPSZAw==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-1.20.5.tgz", + "integrity": "sha512-AbLGMtn76bn7SYBJSSaKJrZ0lgNRRR3qL60PucM5M4v/AXyC8221cKBXW5Pyt9TfDRfe+LDnPNlg7TibxX0ovA==", "dev": true }, - "react-docgen-typescript-loader": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/react-docgen-typescript-loader/-/react-docgen-typescript-loader-3.7.2.tgz", - "integrity": "sha512-fNzUayyUGzSyoOl7E89VaPKJk9dpvdSgyXg81cUkwy0u+NBvkzQG3FC5WBIlXda0k/iaxS+PWi+OC+tUiGxzPA==", - "dev": true, - "requires": { - "@webpack-contrib/schema-utils": "^1.0.0-beta.0", - "loader-utils": "^1.2.3", - "react-docgen-typescript": "^1.15.0" - }, - "dependencies": { - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, "react-docgen-typescript-plugin": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-0.5.2.tgz", - "integrity": "sha512-NQfWyWLmzUnedkiN2nPDb6Nkm68ik6fqbC3UvgjqYSeZsbKijXUA4bmV6aU7qICOXdop9PevPdjEgJuAN0nNVQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-0.6.3.tgz", + "integrity": "sha512-av1S/fmWBNFGgNa4qtkidFjjOz23eEi6EdCtwSWo9WNhGzUMyMygbD/DosMWoeFlZpk9R3MXPkRE7PDH6j5GMQ==", "dev": true, "requires": { "debug": "^4.1.1", "endent": "^2.0.1", "micromatch": "^4.0.2", - "react-docgen-typescript": "^1.20.1", - "react-docgen-typescript-loader": "^3.7.2", + "react-docgen-typescript": "^1.20.5", "tslib": "^2.0.0" }, "dependencies": { @@ -53347,12 +47576,12 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "fill-range": { @@ -53396,9 +47625,9 @@ } }, "tslib": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", - "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", "dev": true } } @@ -53451,68 +47680,45 @@ } }, "react-element-to-jsx-string": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.1.tgz", - "integrity": "sha512-LRdQWRB+xcVPOL4PU4RYuTg6dUJ/FNmaQ8ls6w38YbzkbV6Yr5tFNESroub9GiSghtnMq8dQg2LcNN5aMIDzVg==", + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.2.tgz", + "integrity": "sha512-WZbvG72cjLXAxV7VOuSzuHEaI3RHj10DZu8EcKQpkKcAj7+qAkG5XUeSdX5FXrA0vPrlx0QsnAzZEBJwzV0e+w==", "dev": true, "requires": { "@base2/pretty-print-object": "1.0.0", - "is-plain-object": "3.0.0" + "is-plain-object": "3.0.1" }, "dependencies": { "is-plain-object": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", - "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", - "dev": true, - "requires": { - "isobject": "^4.0.0" - } - }, - "isobject": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", - "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", "dev": true } } }, "react-error-overlay": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz", - "integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==", + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.8.tgz", + "integrity": "sha512-HvPuUQnLp5H7TouGq3kzBeioJmXms1wHy9EGjz2OURWBp4qZO6AfGEcnxts1D/CbwPLRAgTMPCEgYhA3sEM4vw==", "dev": true }, "react-fast-compare": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", - "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==", "dev": true }, - "react-focus-lock": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.2.1.tgz", - "integrity": "sha512-47g0xYcCTZccdzKRGufepY8oZ3W1Qg+2hn6u9SHZ0zUB6uz/4K4xJe7yYFNZ1qT6m+2JDm82F6QgKeBTbjW4PQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.0.0", - "focus-lock": "^0.6.6", - "prop-types": "^15.6.2", - "react-clientside-effect": "^1.2.2", - "use-callback-ref": "^1.2.1", - "use-sidecar": "^1.0.1" - } - }, "react-helmet-async": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.0.4.tgz", - "integrity": "sha512-KTGHE9sz8N7+fCkZ2a3vzXH9eIkiTNhL2NhKR7XzzQl3WsGlCHh76arauJUIiGdfhjeMp7DY7PkASAmYFXeJYg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.0.7.tgz", + "integrity": "sha512-By90p5uxAriGukbyejq2poK41DwTxpNWOpOjN8mIyX/BKrCd3+sXZ5pHUZXjHyjR5OYS7PGsOD9dbM61YxfFmA==", "dev": true, "requires": { - "@babel/runtime": "^7.3.4", + "@babel/runtime": "^7.11.2", "invariant": "^2.2.4", "prop-types": "^15.7.2", - "react-fast-compare": "^2.0.4", + "react-fast-compare": "^3.2.0", "shallowequal": "^1.1.0" } }, @@ -53542,7 +47748,8 @@ "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "dev": true }, "react-merge-refs": { "version": "1.0.0", @@ -54633,49 +48840,32 @@ } }, "react-popper": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz", - "integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.4.tgz", + "integrity": "sha512-NacOu4zWupdQjVXq02XpTD3yFPSfg5a7fex0wa3uGKVkFK7UN6LvVxgcb+xYr56UCuWiNPMH20tntdVdJRwYew==", "dev": true, "requires": { - "@babel/runtime": "^7.1.2", - "create-react-context": "^0.3.0", - "deep-equal": "^1.1.1", - "popper.js": "^1.14.4", - "prop-types": "^15.6.1", - "typed-styles": "^0.0.7", + "react-fast-compare": "^3.0.1", "warning": "^4.0.2" - }, - "dependencies": { - "create-react-context": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", - "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", - "dev": true, - "requires": { - "gud": "^1.0.0", - "warning": "^4.0.3" - } - }, - "warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - } } }, "react-popper-tooltip": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-2.10.1.tgz", - "integrity": "sha512-cib8bKiyYcrIlHo9zXx81G0XvARfL8Jt+xum709MFCgQa3HTqTi4au3iJ9tm7vi7WU7ngnqbpWkMinBOtwo+IQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz", + "integrity": "sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==", "dev": true, "requires": { - "@babel/runtime": "^7.7.4", - "react-popper": "^1.3.6" + "@babel/runtime": "^7.12.5", + "@popperjs/core": "^2.5.4", + "react-popper": "^2.2.4" + }, + "dependencies": { + "@popperjs/core": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.6.0.tgz", + "integrity": "sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw==", + "dev": true + } } }, "react-portal": { @@ -54686,37 +48876,6 @@ "prop-types": "^15.5.8" } }, - "react-redux": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.1.3.tgz", - "integrity": "sha512-uI1wca+ECG9RoVkWQFF4jDMqmaw0/qnvaSvOoL/GA4dNxf6LoV8sUAcNDvE5NWKs4hFpn0t6wswNQnY3f7HT3w==", - "dev": true, - "requires": { - "@babel/runtime": "^7.5.5", - "hoist-non-react-statics": "^3.3.0", - "invariant": "^2.2.4", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^16.9.0" - }, - "dependencies": { - "hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", - "dev": true, - "requires": { - "react-is": "^16.7.0" - } - }, - "react-is": { - "version": "16.12.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", - "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", - "dev": true - } - } - }, "react-refresh": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.2.tgz", @@ -54728,9 +48887,9 @@ "integrity": "sha512-HdPzwdcAv+BMFQEgyacFB40G4IxNMO7tSqaMjbnAouot8LXi5/Rx3/Fv+LU2cQekqiivE1LF4sGnwQ7SnoHrpg==" }, "react-select": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.0.8.tgz", - "integrity": "sha512-v9LpOhckLlRmXN5A6/mGGEft4FMrfaBFTGAnuPHcUgVId7Je42kTq9y0Z+Ye5z8/j0XDT3zUqza8gaRaI1PZIg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.1.1.tgz", + "integrity": "sha512-HjC6jT2BhUxbIbxMZWqVcDibrEpdUJCfGicN0MMV+BQyKtCaPTgFekKWiOizSCy4jdsLMGjLqcFGJMhVGWB0Dg==", "dev": true, "requires": { "@babel/runtime": "^7.4.4", @@ -54740,7 +48899,7 @@ "memoize-one": "^5.0.0", "prop-types": "^15.6.0", "react-input-autosize": "^2.2.2", - "react-transition-group": "^2.2.1" + "react-transition-group": "^4.3.0" } }, "react-sizeme": { @@ -54765,16 +48924,16 @@ } }, "react-syntax-highlighter": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz", - "integrity": "sha512-kqmpM2OH5OodInbEADKARwccwSQWBfZi0970l5Jhp4h39q9Q65C4frNcnd6uHE5pR00W8pOWj9HDRntj2G4Rww==", + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.3.tgz", + "integrity": "sha512-crPaF+QGPeHNIblxxCdf2Lg936NAHKhNhuMzRL3F9ct6aYXL3NcZtCL0Rms9+qVo6Y1EQLdXGypBNSbPL/r+qg==", "dev": true, "requires": { "@babel/runtime": "^7.3.1", - "highlight.js": "~9.13.0", - "lowlight": "~1.11.0", - "prismjs": "^1.8.4", - "refractor": "^2.4.1" + "highlight.js": "^10.1.1", + "lowlight": "^1.14.0", + "prismjs": "^1.21.0", + "refractor": "^3.1.0" } }, "react-test-renderer": { @@ -54808,24 +48967,26 @@ } }, "react-textarea-autosize": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-7.1.2.tgz", - "integrity": "sha512-uH3ORCsCa3C6LHxExExhF4jHoXYCQwE5oECmrRsunlspaDAbS4mGKNlWZqjLfInWtFQcf0o1n1jC/NGXFdUBCg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.0.tgz", + "integrity": "sha512-3GLWFAan2pbwBeoeNDoqGmSbrShORtgWfaWX0RJDivsUrpShh01saRM5RU/i4Zmf+whpBVEY5cA90Eq8Ub1N3w==", "dev": true, "requires": { - "@babel/runtime": "^7.1.2", - "prop-types": "^15.6.0" + "@babel/runtime": "^7.10.2", + "use-composed-ref": "^1.0.0", + "use-latest": "^1.0.0" } }, "react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "dev": true, "requires": { - "dom-helpers": "^3.4.0", + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" + "prop-types": "^15.6.2" } }, "react-use-gesture": { @@ -55044,35 +49205,6 @@ "reakit-utils": "^0.13.0" } }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", - "dev": true, - "requires": { - "util.promisify": "^1.0.0" - } - }, - "recast": { - "version": "0.17.6", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.17.6.tgz", - "integrity": "sha512-yoQRMRrK1lszNtbkGyM4kN45AwylV5hMiuEveUBlxytUViWevjvX6w+tzJt1LH4cfUhWt4NZvy3ThIhu6+m5wQ==", - "dev": true, - "requires": { - "ast-types": "0.12.4", - "esprima": "~4.0.0", - "private": "^0.1.8", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -55115,34 +49247,34 @@ "resolved": "https://registry.npmjs.org/redux-multi/-/redux-multi-0.1.12.tgz", "integrity": "sha1-KOH+XklnLLxb2KB/Cyrq8O+DVcI=" }, - "redux-optimist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redux-optimist/-/redux-optimist-1.0.0.tgz", - "integrity": "sha512-AG1v8o6UZcGXTEH2jVcWG6KD+gEix+Cj9JXAAzln9MPkauSVd98H7N7EOOyT/v4c9N1mJB4sm1zfspGlLDkUEw==" - }, "reflect.ownkeys": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=" }, "refractor": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-2.10.0.tgz", - "integrity": "sha512-maW2ClIkm9IYruuFYGTqKzj+m31heq92wlheW4h7bOstP+gf8bocmMec+j7ljLcaB1CAID85LMB3moye31jH1g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.2.0.tgz", + "integrity": "sha512-hSo+EyMIZTLBvNNgIU5lW4yjCzNYMZ4dcEhBq/3nReGfqzd2JfVhdlPDfU9rEsgcAyWx+OimIIUoL4ZU7NtYHQ==", "dev": true, "requires": { - "hastscript": "^5.0.0", - "parse-entities": "^1.1.2", - "prismjs": "~1.17.0" + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.22.0" }, "dependencies": { - "prismjs": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", - "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "dev": true, "requires": { - "clipboard": "^2.0.0" + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" } } } @@ -55309,19 +49441,6 @@ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, - "regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - }, "regextras": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/regextras/-/regextras-0.7.1.tgz", @@ -55366,16 +49485,16 @@ } }, "remark-external-links": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/remark-external-links/-/remark-external-links-5.0.0.tgz", - "integrity": "sha512-lYnZGNN10N3YGMvO95Zup4hZ+VHI82JgcrGJfuxOLQExoho/iNhlrPVSkmdapYQl928pkOn4YOgJf/0pdZkteA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/remark-external-links/-/remark-external-links-6.1.0.tgz", + "integrity": "sha512-dJr+vhe3wuh1+E9jltQ+efRMqtMDOOnfFkhtoArOmhnBcPQX6THttXMkc/H0kdnAvkXTk7f2QdOYm5qo/sGqdw==", "dev": true, "requires": { "extend": "^3.0.0", "is-absolute-url": "^3.0.0", - "mdast-util-definitions": "^1.2.3", - "space-separated-tokens": "^1.1.2", - "unist-util-visit": "^1.4.0" + "mdast-util-definitions": "^2.0.0", + "space-separated-tokens": "^1.0.0", + "unist-util-visit": "^2.0.0" }, "dependencies": { "is-absolute-url": { @@ -55383,105 +49502,77 @@ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", "dev": true - } - } - }, - "remark-mdx": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.5.5.tgz", - "integrity": "sha512-w1XW9UzsQ6XAecV59dP8LJWn4tMftaXGwH5LEvUU5uIEJEJvHDE1jkKiPr3ow2IuhjuRfWs3b079Jtnk5qlUgQ==", - "dev": true, - "requires": { - "@babel/core": "7.8.0", - "@babel/helper-plugin-utils": "7.8.0", - "@babel/plugin-proposal-object-rest-spread": "7.8.0", - "@babel/plugin-syntax-jsx": "7.8.0", - "@mdx-js/util": "^1.5.5", - "is-alphabetical": "1.0.3", - "remark-parse": "7.0.2", - "unified": "8.4.2" - }, - "dependencies": { - "@babel/core": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.0.tgz", - "integrity": "sha512-3rqPi/bv/Xfu2YzHvBz4XqMI1fKVwnhntPA1/fjoECrSjrhbOCxlTrbVu5gUtr8zkxW+RpkDOa/HCW93gzS2Dw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.0", - "@babel/generator": "^7.8.0", - "@babel/helpers": "^7.8.0", - "@babel/parser": "^7.8.0", - "@babel/template": "^7.8.0", - "@babel/traverse": "^7.8.0", - "@babel/types": "^7.8.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==", - "dev": true }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.0.tgz", - "integrity": "sha512-SjJ2ZXCylpWC+5DTES0/pbpNmw/FnjU/3dF068xF0DU9aN+oOKah+3MCSFcb4pnZ9IwmxfOy4KnbGJSQR+hAZA==", + "mdast-util-definitions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-2.0.1.tgz", + "integrity": "sha512-Co+DQ6oZlUzvUR7JCpP249PcexxygiaKk9axJh+eRzHDZJk2julbIdKB4PXHVxdBuLzvJ1Izb+YDpj2deGMOuA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0" + "unist-util-visit": "^2.0.0" } }, - "@babel/plugin-syntax-jsx": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.0.tgz", - "integrity": "sha512-zLDUckAuKeOtxJhfNE0TlR7iEApb2u7EYRlh5cxKzq6A5VzUbYEdyJGJlug41jDbjRbHTtsLKZUnUcy/8V3xZw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } + "unist-util-is": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", + "dev": true }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", "dev": true, "requires": { - "safe-buffer": "~5.1.1" + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" } }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", "dev": true, "requires": { - "ms": "^2.1.1" + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" } - }, - "globals": { - "version": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, + } + } + }, + "remark-footnotes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", + "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==", + "dev": true + }, + "remark-mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", + "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", + "dev": true, + "requires": { + "@babel/core": "7.12.9", + "@babel/helper-plugin-utils": "7.10.4", + "@babel/plugin-proposal-object-rest-spread": "7.12.1", + "@babel/plugin-syntax-jsx": "7.12.1", + "@mdx-js/util": "1.6.22", + "is-alphabetical": "1.0.4", + "remark-parse": "8.0.3", + "unified": "9.2.0" + }, + "dependencies": { "is-alphabetical": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.3.tgz", - "integrity": "sha512-eEMa6MKpHFzw38eKm56iNNi6GJ7lf6aLLio7Kr23sJPAECscgRtZvOBYybejWDQ2bM949Y++61PY+udzj5QMLA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", "dev": true }, "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "dev": true }, "is-plain-obj": { @@ -55490,89 +49581,125 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, - "json5": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", "dev": true, "requires": { - "minimist": "^1.2.0" + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "remark-parse": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-7.0.2.tgz", - "integrity": "sha512-9+my0lQS80IQkYXsMA8Sg6m9QfXYJBnXjWYN5U+kFc5/n69t+XZVXU/ZBYr3cYH8FheEGf1v87rkFDhJ8bVgMA==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", "dev": true, "requires": { + "ccount": "^1.0.0", "collapse-white-space": "^1.0.2", "is-alphabetical": "^1.0.0", "is-decimal": "^1.0.0", "is-whitespace-character": "^1.0.0", "is-word-character": "^1.0.0", "markdown-escapes": "^1.0.0", - "parse-entities": "^1.1.0", + "parse-entities": "^2.0.0", "repeat-string": "^1.5.4", "state-toggle": "^1.0.0", "trim": "0.0.1", "trim-trailing-lines": "^1.0.0", "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", "xtend": "^4.0.1" } }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, "unified": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-8.4.2.tgz", - "integrity": "sha512-JCrmN13jI4+h9UAyKEoGcDZV+i1E7BLFuG7OsaDvTXI5P0qhHX+vZO/kOhz9jn8HGENDKbwSeB0nVOg4gVStGA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", "dev": true, "requires": { "bail": "^1.0.0", "extend": "^3.0.0", + "is-buffer": "^2.0.0", "is-plain-obj": "^2.0.0", "trough": "^1.0.0", "vfile": "^4.0.0" } }, + "unist-util-is": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", + "dev": true + }, + "unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "dev": true, + "requires": { + "unist-util-visit": "^2.0.0" + } + }, "unist-util-stringify-position": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.2.tgz", - "integrity": "sha512-nK5n8OGhZ7ZgUwoUbL8uiVRwAbZyzBsB/Ddrlbu6jwwubFza4oe15KlyEaLNMXQW1svOQq4xesUeqA85YrIUQA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", "dev": true, "requires": { "@types/unist": "^2.0.2" } }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + }, "vfile": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.0.2.tgz", - "integrity": "sha512-yhoTU5cDMSsaeaMfJ5g0bUKYkYmZhAh9fn9TZicxqn+Cw4Z439il2v3oT9S0yjlpqlI74aFOQCt3nOV+pxzlkw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", "dev": true, "requires": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", - "replace-ext": "1.0.0", "unist-util-stringify-position": "^2.0.0", "vfile-message": "^2.0.0" } }, + "vfile-location": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", + "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "dev": true + }, "vfile-message": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.2.tgz", - "integrity": "sha512-gNV2Y2fDvDOOqq8bEe7cF3DXU6QgV4uA9zMR2P8tix11l1r7zju3zry3wZ8sx+BEfuO6WQ7z2QzfWTvqHQiwsA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", "dev": true, "requires": { "@types/unist": "^2.0.0", @@ -55605,23 +49732,52 @@ } }, "remark-slug": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-5.1.2.tgz", - "integrity": "sha512-DWX+Kd9iKycqyD+/B+gEFO3jjnt7Yg1O05lygYSNTe5i5PIxxxPjp5qPBDxPIzp5wreF7+1ROCwRgjEcqmzr3A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-6.0.0.tgz", + "integrity": "sha512-ln67v5BrGKHpETnm6z6adlJPhESFJwfuZZ3jrmi+lKTzeZxh2tzFzUfDD4Pm2hRGOarHLuGToO86MNMZ/hA67Q==", "dev": true, "requires": { "github-slugger": "^1.0.0", "mdast-util-to-string": "^1.0.0", - "unist-util-visit": "^1.0.0" + "unist-util-visit": "^2.0.0" + }, + "dependencies": { + "unist-util-is": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", + "dev": true + }, + "unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + } } }, "remark-squeeze-paragraphs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-3.0.4.tgz", - "integrity": "sha512-Wmz5Yj9q+W1oryo8BV17JrOXZgUKVcpJ2ApE2pwnoHwhFKSk4Wp2PmFNbmJMgYSqAdFwfkoe+TSYop5Fy8wMgA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", "dev": true, "requires": { - "mdast-squeeze-paragraphs": "^3.0.0" + "mdast-squeeze-paragraphs": "^4.0.0" } }, "remark-stringify": { @@ -55657,16 +49813,16 @@ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, "renderkid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", - "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.4.tgz", + "integrity": "sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g==", "dev": true, "requires": { "css-select": "^1.1.0", "dom-converter": "^0.2", "htmlparser2": "^3.3.0", - "strip-ansi": "^3.0.0", - "utila": "^0.4.0" + "lodash": "^4.17.20", + "strip-ansi": "^3.0.0" }, "dependencies": { "ansi-regex": { @@ -55675,6 +49831,12 @@ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -55778,12 +49940,6 @@ "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", "dev": true }, - "resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "dev": true - }, "resolve": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", @@ -55907,15 +50063,6 @@ "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "dev": true }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "^0.1.1" - } - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -56836,30 +50983,6 @@ } } }, - "simplebar": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/simplebar/-/simplebar-4.2.3.tgz", - "integrity": "sha512-9no0pK7/1y+8/oTF3sy/+kx0PjQ3uk4cYwld5F1CJGk2gx+prRyUq8GRfvcVLq5niYWSozZdX73a2wIr1o9l/g==", - "dev": true, - "requires": { - "can-use-dom": "^0.1.0", - "core-js": "^3.0.1", - "lodash.debounce": "^4.0.8", - "lodash.memoize": "^4.1.2", - "lodash.throttle": "^4.1.1", - "resize-observer-polyfill": "^1.5.1" - } - }, - "simplebar-react": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/simplebar-react/-/simplebar-react-1.2.3.tgz", - "integrity": "sha512-1EOWJzFC7eqHUp1igD1/tb8GBv5aPQA5ZMvpeDnVkpNJ3jAuvmrL2kir3HuijlxhG7njvw9ssxjjBa89E5DrJg==", - "dev": true, - "requires": { - "prop-types": "^15.6.1", - "simplebar": "^4.2.3" - } - }, "sisteransi": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.4.tgz", @@ -57088,9 +51211,9 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, "space-separated-tokens": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.4.tgz", - "integrity": "sha512-UyhMSmeIqZrQn2UdjYpxEkwY9JUrn8pP+7L4f91zRzOQuI8MF1FGLfYU9DKCYeLdo7LXMxwrX5zKFy7eeeVHuA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", "dev": true }, "spawn-command": { @@ -57226,6 +51349,12 @@ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz", "integrity": "sha1-1PM6tU6OOHeLDKXP07OvsS22hiA=" }, + "stackframe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", + "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", + "dev": true + }, "stacktrace-parser": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.8.tgz", @@ -57278,9 +51407,9 @@ "dev": true }, "store2": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/store2/-/store2-2.10.0.tgz", - "integrity": "sha512-tWEpK0snS2RPUq1i3R6OahfJNjWCQYNxq0+by1amCSuw0mXtymJpzmZIeYpA1UAa+7B0grCpNYIbDcd7AgTbFg==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/store2/-/store2-2.12.0.tgz", + "integrity": "sha512-7t+/wpKLanLzSnQPX8WAcuLCCeuSHoWdQuh9SB3xD0kNOM38DNf+0Oa+wmvxmYueRzkmh6IcdKFtvTa+ecgPDw==", "dev": true }, "stream-browserify": { @@ -57501,13 +51630,14 @@ } }, "string.prototype.padend": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz", - "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.1.tgz", + "integrity": "sha512-eCzTASPnoCr5Ht+Vn1YXgm8SB015hHKgEIMu9Nr9bQmLhRBxKRfmzSj/IQsxDFc8JInJDDFA0qXwK+xxI7wDkg==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "es-abstract": "^1.18.0-next.1" }, "dependencies": { "define-properties": { @@ -57520,20 +51650,21 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -57564,9 +51695,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -57588,41 +51719,62 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } }, "string.prototype.padstart": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.1.0.tgz", - "integrity": "sha512-envqZvUp2JItI+OeQ5UAh1ihbAV5G/2bixTojvlIa090GGqF+NQRxbWb2nv9fTGrZABv6+pE6jXoAZhhS2k4Hw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.1.1.tgz", + "integrity": "sha512-kcFjKhQYg40AK9MITCWYr/vIebruAD01sc/fxi8szHJaEG7Rke4XHw6LU9c1VWXh/+J/PxvWLLf/aIAGKhXkAQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "es-abstract": "^1.18.0-next.1" }, "dependencies": { "define-properties": { @@ -57635,20 +51787,21 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" }, @@ -57679,9 +51832,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -57703,29 +51856,49 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } @@ -58400,18 +52573,6 @@ "fill-range": "^7.0.1" } }, - "browserslist": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", - "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001043", - "electron-to-chromium": "^1.3.413", - "node-releases": "^1.1.53", - "pkg-up": "^2.0.0" - } - }, "caniuse-lite": { "version": "1.0.30001081", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001081.tgz", @@ -58443,8 +52604,7 @@ "electron-to-chromium": { "version": "1.3.468", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.468.tgz", - "integrity": "sha512-+KAppdklzPd5v8nLOvtDiD/S67mCT9gFRAvngYe8zuFy9azHhT9vWWH6WEPPCcyjQ1JMYVgqbN29yZ0paqxsEw==", - "dev": true + "integrity": "sha512-+KAppdklzPd5v8nLOvtDiD/S67mCT9gFRAvngYe8zuFy9azHhT9vWWH6WEPPCcyjQ1JMYVgqbN29yZ0paqxsEw==" }, "emoji-regex": { "version": "8.0.0", @@ -58565,8 +52725,7 @@ "node-releases": { "version": "1.1.58", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", - "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==", - "dev": true + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" }, "parse-json": { "version": "5.0.0", @@ -59016,13 +53175,15 @@ "dev": true }, "symbol.prototype.description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/symbol.prototype.description/-/symbol.prototype.description-1.0.2.tgz", - "integrity": "sha512-2CW5SU4/Ki1cYOOHcL2cXK4rxSg5hCU1TwZ7X4euKhV9VnfqKslh7T6/UyKkubA8cq2tOmsOv7m3ZUmQslBRuw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/symbol.prototype.description/-/symbol.prototype.description-1.0.3.tgz", + "integrity": "sha512-NvwWb5AdyTtmFNa1x0ksJakFUV/WJ+z7iRrYGU1xZew77Qd+kMrZKsk3uatCckk6yPNpbHhRcOO+JBU+ohcMBw==", "dev": true, "requires": { - "es-abstract": "^1.17.0-next.1", - "has-symbols": "^1.0.1" + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" }, "dependencies": { "define-properties": { @@ -59035,20 +53196,21 @@ } }, "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.1", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } @@ -59071,9 +53233,9 @@ "dev": true }, "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", "dev": true }, "is-regex": { @@ -59095,9 +53257,9 @@ } }, "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", "dev": true }, "object-keys": { @@ -59106,24 +53268,47 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" } }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "object.getownpropertydescriptors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", + "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", "dev": true, "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" + "es-abstract": "^1.18.0-next.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } } } @@ -59272,18 +53457,18 @@ } }, "telejson": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/telejson/-/telejson-3.3.0.tgz", - "integrity": "sha512-er08AylQ+LEbDLp1GRezORZu5wKOHaBczF6oYJtgC3Idv10qZ8A3p6ffT+J5BzDKkV9MqBvu8HAKiIIOp6KJ2w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/telejson/-/telejson-5.1.0.tgz", + "integrity": "sha512-Yy0N2OV0mosmr1SCZEm3Ezhu/oi5Dbao5RqauZu4+VI5I/XtVBHXajRk0txuqbFYtKdzzWGDZFGSif9ovVLjEA==", "dev": true, "requires": { "@types/is-function": "^1.0.0", "global": "^4.4.0", - "is-function": "^1.0.1", - "is-regex": "^1.0.4", + "is-function": "^1.0.2", + "is-regex": "^1.1.1", "is-symbol": "^1.0.3", "isobject": "^4.0.0", - "lodash": "^4.17.15", + "lodash": "^4.17.20", "memoizerific": "^1.11.3" }, "dependencies": { @@ -59293,6 +53478,15 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -59307,6 +53501,12 @@ "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", "dev": true + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true } } }, @@ -59370,9 +53570,9 @@ } }, "term-size": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", - "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", "dev": true }, "terminal-link": { @@ -59857,9 +54057,9 @@ "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=" }, "throttle-debounce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.1.0.tgz", - "integrity": "sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", "dev": true }, "through": { @@ -60006,12 +54206,6 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, - "token-stream": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", - "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=", - "dev": true - }, "toolkit.ts": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/toolkit.ts/-/toolkit.ts-0.0.2.tgz", @@ -60061,12 +54255,6 @@ "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", "dev": true }, - "trim-lines": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-1.1.2.tgz", - "integrity": "sha512-3GOuyNeTqk3FAqc3jOJtw7FTjYl94XBR5aD9QnDbK/T4CA9sW/J0l9RoaRPE9wyPP7NF331qnHnvJFBJ+IDkmQ==", - "dev": true - }, "trim-newlines": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", @@ -60098,9 +54286,9 @@ "dev": true }, "ts-dedent": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-1.1.0.tgz", - "integrity": "sha512-CVCvDwMBWZKjDxpN3mU/Dx1v3k+sJgE8nrhXcC9vRopRfoa7vVzilNvHEAUi5jQnmFHpnxDx5jZdI1TpG8ny2g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.0.0.tgz", + "integrity": "sha512-DfxKjSFQfw9+uf7N9Cy8Ebx9fv5fquK4hZ6SD3Rzr+1jKP6AVA6H8+B5457ZpUs0JKsGpGqIevbpZ9DMQJDp1A==", "dev": true }, "ts-essentials": { @@ -60109,12 +54297,6 @@ "integrity": "sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==", "dev": true }, - "ts-map": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-map/-/ts-map-1.0.3.tgz", - "integrity": "sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==", - "dev": true - }, "ts-pnp": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", @@ -60163,12 +54345,6 @@ "dev": true, "optional": true }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -60216,12 +54392,6 @@ } } }, - "typed-styles": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", - "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==", - "dev": true - }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -60301,13 +54471,6 @@ } } }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, "uid-number": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", @@ -60366,9 +54529,9 @@ "dev": true }, "unfetch": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz", - "integrity": "sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", "dev": true }, "unherit": { @@ -60463,13 +54626,10 @@ } }, "unist-builder": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-1.0.4.tgz", - "integrity": "sha512-v6xbUPP7ILrT15fHGrNyHc1Xda8H3xVhP7/HAIotHOhVPjH5dCXA097C3Rry1Q2O+HbOLCao4hfPB+EYEjHgVg==", - "dev": true, - "requires": { - "object-assign": "^4.1.0" - } + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "dev": true }, "unist-util-find-all-after": { "version": "3.0.1", @@ -60489,9 +54649,9 @@ } }, "unist-util-generated": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.5.tgz", - "integrity": "sha512-1TC+NxQa4N9pNdayCYA1EGUOCAO0Le3fVp7Jzns6lnua/mYgwHo0tz5WUAfrdpNch1RZLHc61VZ1SDgrtNXLSw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", "dev": true }, "unist-util-is": { @@ -60510,24 +54670,24 @@ } }, "unist-util-position": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.0.4.tgz", - "integrity": "sha512-tWvIbV8goayTjobxDIr4zVTyG+Q7ragMSMeKC3xnPl9xzIc0+she8mxXLM3JVNDDsfARPbCd3XdzkyLdo7fF3g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", "dev": true }, "unist-util-remove": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-1.0.3.tgz", - "integrity": "sha512-mB6nCHCQK0pQffUAcCVmKgIWzG/AXs/V8qpS8K72tMPtOSCMSjDeMc5yN+Ye8rB0FhcE+JvW++o1xRNc0R+++g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.0.1.tgz", + "integrity": "sha512-YtuetK6o16CMfG+0u4nndsWpujgsHDHHLyE0yGpJLLn5xSjKeyGyzEBOI2XbmoUHCYabmNgX52uxlWoQhcvR7Q==", "dev": true, "requires": { - "unist-util-is": "^3.0.0" + "unist-util-is": "^4.0.0" }, "dependencies": { "unist-util-is": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", - "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz", + "integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==", "dev": true } } @@ -60724,31 +54884,25 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, - "use-callback-ref": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.1.tgz", - "integrity": "sha512-C3nvxh0ZpaOxs9RCnWwAJ+7bJPwQI8LHF71LzbQ3BvzH5XkdtlkMadqElGevg5bYBDFip4sAnD4m06zAKebg1w==", - "dev": true - }, "use-composed-ref": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.0.0.tgz", - "integrity": "sha512-RVqY3NFNjZa0xrmK3bIMWNmQ01QjKPDc7DeWR3xa/N8aliVppuutOE5bZzPkQfvL+5NRWMMp0DJ99Trd974FIw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.1.0.tgz", + "integrity": "sha512-my1lNHGWsSDAhhVAT4MKs6IjBUtG6ZG11uUqexPH9PptiIZDQOzaF4f5tEbJ2+7qvNbtXNBbU3SfmN+fXlWDhg==", "dev": true, "requires": { "ts-essentials": "^2.0.3" } }, "use-isomorphic-layout-effect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.0.0.tgz", - "integrity": "sha512-JMwJ7Vd86NwAt1jH7q+OIozZSIxA4ND0fx6AsOe2q1H8ooBUp5aN6DvVCqZiIaYU6JaMRJGyR0FO7EBCIsb/Rg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.0.tgz", + "integrity": "sha512-kady5Z1O1qx5RitodCCKbpJSVEtECXYcnBnb5Q48Bz5V6gBmTu85ZcGdVwVFs8+DaOurNb/L5VdGHoQRMknghw==", "dev": true }, "use-latest": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.1.0.tgz", - "integrity": "sha512-gF04d0ZMV3AMB8Q7HtfkAWe+oq1tFXP6dZKwBHQF5nVXtGsh2oAYeeqma5ZzxtlpOcW8Ro/tLcfmEodjDeqtuw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz", + "integrity": "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==", "dev": true, "requires": { "use-isomorphic-layout-effect": "^1.0.0" @@ -60759,16 +54913,6 @@ "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.1.tgz", "integrity": "sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==" }, - "use-sidecar": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.0.2.tgz", - "integrity": "sha512-287RZny6m5KNMTb/Kq9gmjafi7lQL0YHO1lYolU6+tY1h9+Z3uCtkJJ3OSOq3INwYf2hBryCcDh4520AhJibMA==", - "dev": true, - "requires": { - "detect-node": "^2.0.4", - "tslib": "^1.9.3" - } - }, "use-subscription": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.0.tgz", @@ -60952,97 +55096,6 @@ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", "dev": true }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "vue-docgen-api": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/vue-docgen-api/-/vue-docgen-api-4.7.0.tgz", - "integrity": "sha512-yAzaM5NNuJKouzAaUeLdopcRW4J2BmD+aNXJGTyFVhr83yyTjDQz4QuwugcTJXGpStGGAXR1mOcAOitx59DC0g==", - "dev": true, - "requires": { - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0", - "ast-types": "^0.12.2", - "hash-sum": "^1.0.2", - "lru-cache": "^4.1.5", - "pug": "^2.0.3", - "recast": "^0.17.3", - "ts-map": "^1.0.3", - "vue-template-compiler": "^2.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - } - } - }, - "vue-docgen-loader": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/vue-docgen-loader/-/vue-docgen-loader-1.3.0.tgz", - "integrity": "sha512-K/r3IulRQlZpRIvR0Ed8vdPQCCd1WbcajOgm/4fdwtO4pWorLLX9o0YGM1rlkX3DXybqOolQ5LEh7E3kTer1qg==", - "dev": true, - "requires": { - "clone": "^2.1.2", - "jscodeshift": "^0.7.0", - "loader-utils": "^1.2.3", - "querystring": "^0.2.0" - }, - "dependencies": { - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - } - } - }, - "vue-template-compiler": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", - "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", - "dev": true, - "requires": { - "de-indent": "^1.0.2", - "he": "^1.1.0" - } - }, "w3c-hr-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", @@ -61144,9 +55197,9 @@ } }, "warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", - "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "dev": true, "requires": { "loose-envify": "^1.0.0" @@ -61164,9 +55217,9 @@ } }, "watchpack-chokidar2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", - "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", "dev": true, "optional": true, "requires": { @@ -61197,9 +55250,9 @@ } }, "web-namespaces": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.3.tgz", - "integrity": "sha512-r8sAtNmgR0WKOKOxzuSgk09JsHlpKlB+uHi937qypOu3PZ17UxPrierFKDye/uNHjNTTEshu5PId8rojIPj/tA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", + "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", "dev": true }, "webidl-conversions": { @@ -61921,9 +55974,9 @@ } }, "webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", "dev": true, "requires": { "memory-fs": "^0.4.1", @@ -61941,6 +55994,12 @@ } } }, + "webpack-filter-warnings-plugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/webpack-filter-warnings-plugin/-/webpack-filter-warnings-plugin-1.2.1.tgz", + "integrity": "sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==", + "dev": true + }, "webpack-hot-middleware": { "version": "2.25.0", "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.0.tgz", @@ -62191,12 +56250,6 @@ } } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, "windows-release": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz", @@ -62261,41 +56314,6 @@ } } }, - "with": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz", - "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=", - "dev": true, - "requires": { - "acorn": "^3.1.0", - "acorn-globals": "^3.0.0" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - }, - "acorn-globals": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz", - "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=", - "dev": true, - "requires": { - "acorn": "^4.0.4" - }, - "dependencies": { - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - } - } - } - } - }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -62740,9 +56758,9 @@ } }, "zwitch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.4.tgz", - "integrity": "sha512-YO803/X+13GNaZB7fVopjvHH0uWQKgJkgKnU1YCjxShjKGVuN9PPHHW8g+uFDpkHpSTNi3rCMKMewIcbC1BAYg==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", "dev": true } } diff --git a/package.json b/package.json index b175c863484627..bb427cfc8ffd2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gutenberg", - "version": "9.4.1", + "version": "9.6.2", "private": true, "description": "A new WordPress editor experience.", "author": "The WordPress Contributors", @@ -73,7 +73,6 @@ "@wordpress/rich-text": "file:packages/rich-text", "@wordpress/server-side-render": "file:packages/server-side-render", "@wordpress/shortcode": "file:packages/shortcode", - "@wordpress/stan": "file:packages/stan", "@wordpress/token-list": "file:packages/token-list", "@wordpress/url": "file:packages/url", "@wordpress/viewport": "file:packages/viewport", @@ -83,19 +82,19 @@ "devDependencies": { "@actions/core": "1.2.6", "@actions/github": "1.0.0", - "@babel/core": "7.11.6", - "@babel/plugin-syntax-jsx": "7.10.4", - "@babel/runtime-corejs3": "7.11.2", - "@babel/traverse": "7.11.5", + "@babel/core": "7.12.9", + "@babel/plugin-syntax-jsx": "7.12.1", + "@babel/runtime-corejs3": "7.12.5", + "@babel/traverse": "7.12.9", "@jest/types": "25.3.0", "@octokit/rest": "16.26.0", "@octokit/webhooks": "7.1.0", - "@storybook/addon-a11y": "5.3.2", - "@storybook/addon-docs": "5.3.2", - "@storybook/addon-knobs": "5.3.2", - "@storybook/addon-storysource": "5.3.2", - "@storybook/addon-viewport": "5.3.2", - "@storybook/react": "6.0.21", + "@storybook/addon-a11y": "6.1.11", + "@storybook/addon-docs": "6.1.11", + "@storybook/addon-knobs": "6.1.11", + "@storybook/addon-storysource": "6.1.11", + "@storybook/addon-viewport": "6.1.11", + "@storybook/react": "6.1.11", "@testing-library/react": "10.0.2", "@types/classnames": "2.2.10", "@types/eslint": "6.8.0", @@ -142,9 +141,8 @@ "babel-plugin-inline-json-import": "0.3.2", "babel-plugin-react-native-classname-to-style": "1.2.2", "babel-plugin-react-native-platform-specific-extensions": "1.1.1", - "babel-plugin-require-context-hook": "1.0.0", "benchmark": "2.1.4", - "browserslist": "4.14.0", + "browserslist": "4.15.0", "chalk": "4.0.0", "commander": "4.1.0", "concurrently": "3.5.0", @@ -180,7 +178,7 @@ "patch-package": "6.2.2", "postcss": "7.0.32", "postcss-loader": "3.0.0", - "prettier": "npm:wp-prettier@2.0.5", + "prettier": "npm:wp-prettier@2.2.1-beta-1", "progress": "2.0.3", "puppeteer": "npm:puppeteer-core@3.0.0", "react": "16.13.1", @@ -250,7 +248,9 @@ "test-e2e:watch": "npm run test-e2e -- --watch", "test-performance": "wp-scripts test-e2e --config packages/e2e-tests/jest.performance.config.js", "test-php": "npm run lint-php && npm run test-unit-php", + "test-php:watch": "wp-env run composer run-script test:watch", "test-unit": "wp-scripts test-unit-js --config test/unit/jest.config.js", + "test-unit:date": "./bin/unit-test-date.sh", "test-unit:debug": "wp-scripts --inspect-brk test-unit-js --runInBand --no-cache --verbose --config test/unit/jest.config.js ", "test-unit:update": "npm run test-unit -- --updateSnapshot", "test-unit:watch": "npm run test-unit -- --watch", @@ -261,13 +261,9 @@ "test-unit:native": "cd test/native/ && cross-env NODE_ENV=test jest --config ./jest.config.js", "test-unit:native:debug": "cd test/native/ && node --inspect ../../node_modules/.bin/jest --runInBand --config ./jest.config.js", "prestorybook:build": "npm run build:packages", - "storybook:build": "build-storybook -c ./storybook -o ./playground/dist", + "storybook:build": "build-storybook -c ./storybook -o ./storybook/build", "prestorybook:dev": "npm run build:packages", "storybook:dev": "concurrently \"npm run dev:packages\" \"start-storybook -c ./storybook -p 50240\"", - "design-system:build": "echo \"Please use storybook:build instead.\"", - "design-system:dev": "echo \"Please use storybook:dev instead.\"", - "playground:build": "npm run storybook:build", - "playground:dev": "echo \"Please use storybook:dev instead.\"", "env": "wp-scripts env", "wp-env": "wp-env" }, diff --git a/packages/README.md b/packages/README.md index 9d8c549ab96fd6..eaf6f9fee0d5a1 100644 --- a/packages/README.md +++ b/packages/README.md @@ -27,7 +27,7 @@ When creating a new package, you need to provide at least the following: "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/a11y/package.json b/packages/a11y/package.json index 4d4be5cdbfa660..3f1724746d842b 100644 --- a/packages/a11y/package.json +++ b/packages/a11y/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/a11y", - "version": "2.13.0", + "version": "2.14.0", "description": "Accessibility (a11y) utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -25,7 +25,7 @@ "react-native": "src/index", "types": "build-types", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/dom-ready": "file:../dom-ready", "@wordpress/i18n": "file:../i18n" }, diff --git a/packages/annotations/CHANGELOG.md b/packages/annotations/CHANGELOG.md index ac05be83598b2e..a3437bba3777fa 100644 --- a/packages/annotations/CHANGELOG.md +++ b/packages/annotations/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.24.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the annotations namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/annotations/package.json b/packages/annotations/package.json index 956f3e3a4b5992..6ea4ed2283a885 100644 --- a/packages/annotations/package.json +++ b/packages/annotations/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/annotations", - "version": "1.23.0", + "version": "1.24.0", "description": "Annotate content in the Gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/data": "file:../data", "@wordpress/hooks": "file:../hooks", "@wordpress/i18n": "file:../i18n", diff --git a/packages/api-fetch/package.json b/packages/api-fetch/package.json index 8f5225db3178bb..66e8f708e65e5f 100644 --- a/packages/api-fetch/package.json +++ b/packages/api-fetch/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/api-fetch", - "version": "3.20.0", + "version": "3.21.0", "description": "Utility to make WordPress REST API requests.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/i18n": "file:../i18n", "@wordpress/url": "file:../url" }, diff --git a/packages/autop/package.json b/packages/autop/package.json index 46d41447a9d5f3..8794b561c51a46 100644 --- a/packages/autop/package.json +++ b/packages/autop/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/autop", - "version": "2.10.0", + "version": "2.11.0", "description": "WordPress's automatic paragraph functions `autop` and `removep`.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/babel-plugin-makepot/package.json b/packages/babel-plugin-makepot/package.json index 6b44ddfbe85352..6aa2d9342da1c1 100644 --- a/packages/babel-plugin-makepot/package.json +++ b/packages/babel-plugin-makepot/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-plugin-makepot", - "version": "3.9.0", + "version": "3.10.0", "description": "WordPress Babel internationalization (i18n) plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -30,7 +30,7 @@ "main": "build/index.js", "module": "build-module/index.js", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "gettext-parser": "^1.3.1", "lodash": "^4.17.19" }, diff --git a/packages/babel-preset-default/CHANGELOG.md b/packages/babel-preset-default/CHANGELOG.md index a45e8107796925..d13d1e82985b1a 100644 --- a/packages/babel-preset-default/CHANGELOG.md +++ b/packages/babel-preset-default/CHANGELOG.md @@ -2,7 +2,15 @@ ## Unreleased -## Breaking Changes +## 4.20.0 (2020-12-17) + +### New Features + +- The bundled `@babel/core` dependency has been updated from requiring `^7.11.6` to requiring `^7.12.9`. All other Babel plugins were updated to the latest version (see [Highlights](https://babeljs.io/blog/2020/10/15/7.12.0)). + +## 4.14.0 (2020-05-27) + +### Breaking Changes - Revert enabling the `shippedProposals` flag. That flag enables the use of stage-3 proposals, but the goal of this preset is to only support stage-4 features. [#22083](https://github.com/WordPress/gutenberg/pull/22083) diff --git a/packages/babel-preset-default/package.json b/packages/babel-preset-default/package.json index 38fac877c8f836..68244782ff6b33 100644 --- a/packages/babel-preset-default/package.json +++ b/packages/babel-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/babel-preset-default", - "version": "4.19.0", + "version": "4.20.0", "description": "Default Babel preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -28,11 +28,11 @@ ], "main": "index.js", "dependencies": { - "@babel/core": "^7.11.6", - "@babel/plugin-transform-react-jsx": "^7.10.4", - "@babel/plugin-transform-runtime": "^7.11.5", - "@babel/preset-env": "^7.11.5", - "@babel/runtime": "^7.11.2", + "@babel/core": "^7.12.9", + "@babel/plugin-transform-react-jsx": "^7.12.7", + "@babel/plugin-transform-runtime": "^7.12.1", + "@babel/preset-env": "^7.12.7", + "@babel/runtime": "^7.12.5", "@wordpress/babel-plugin-import-jsx-pragma": "file:../babel-plugin-import-jsx-pragma", "@wordpress/browserslist-config": "file:../browserslist-config", "@wordpress/element": "file:../element", diff --git a/packages/base-styles/_mixins.scss b/packages/base-styles/_mixins.scss index ee617699e2354f..dc8968dab8daa7 100644 --- a/packages/base-styles/_mixins.scss +++ b/packages/base-styles/_mixins.scss @@ -358,20 +358,26 @@ margin-right: $grid-unit-15; transition: none; border-radius: $radius-round; + width: $radio-input-size-sm; + height: $radio-input-size-sm; + + @include break-small() { + height: $radio-input-size; + width: $radio-input-size; + } &:checked::before { - width: 7px; - height: 7px; - margin: 8px 0 0 8px; + width: 8px; + height: 8px; + transform: translate(7px, 7px); + margin: 0; background-color: $white; // This border serves as a background color in Windows High Contrast mode. - border: 3px solid $white; + border: 4px solid $white; - @include break-medium() { - width: 6px; - height: 6px; - margin: 4px 0 0 4px; + @include break-small() { + transform: translate(5px, 5px); } } diff --git a/packages/base-styles/_variables.scss b/packages/base-styles/_variables.scss index b24992ba3a084b..a8f22314dfc430 100644 --- a/packages/base-styles/_variables.scss +++ b/packages/base-styles/_variables.scss @@ -1,3 +1,10 @@ +/** + * SCSS Variables. + * + * Please use variables from this sheet to ensure consistency across the UI. + * Don't add to this sheet unless you're pretty sure the value will be reused in many places. + */ + @import "./colors"; /** @@ -10,15 +17,11 @@ $default-line-height: 1.4; $editor-font: "Noto Serif", serif; $editor-html-font: Menlo, Consolas, monaco, monospace; $editor-font-size: 16px; -$default-block-margin: 28px; // This value provides a consistent, contiguous spacing between blocks (it's 2x $block-padding). +$default-block-margin: 28px; // This value provides a consistent, contiguous spacing between blocks. $text-editor-font-size: 15px; $editor-line-height: 1.8; -$big-font-size: 18px; -$mobile-text-min-font-size: 16px; // Any font size below 16px will cause Mobile Safari to "zoom in" -$border-width: 1px; -$border-width-focus: 1.5px; -$border-width-tab: 4px; -$helptext-font-size: 12px; +$mobile-text-min-font-size: 16px; // Any font size below 16px will cause Mobile Safari to "zoom in". + /** * Grid System. @@ -35,6 +38,7 @@ $grid-unit-40: 4 * $grid-unit; // 32px $grid-unit-50: 5 * $grid-unit; // 40px $grid-unit-60: 6 * $grid-unit; // 48px + /** * Dimensions. */ @@ -53,11 +57,7 @@ $admin-sidebar-width-big: 190px; $admin-sidebar-width-collapsed: 36px; $modal-min-width: 360px; $spinner-size: 18px; -$mobile-header-toolbar-height: 44px; -$mobile-floating-toolbar-height: 44px; -$mobile-floating-toolbar-margin: 8px; -$mobile-color-swatch: 48px; -$header-toolbar-min-width: 335px; + /** * Shadows. @@ -76,18 +76,48 @@ $content-width: 840px; $wide-content-width: 1100px; $widget-area-width: 700px; + /** - * Block UI. + * Block & Editor UI. */ $block-toolbar-height: $grid-unit-60; +$border-width: 1px; +$border-width-focus: 1.5px; +$border-width-tab: 4px; +$helptext-font-size: 12px; +$radius-round: 50%; +$radius-block-ui: 2px; +$radio-input-size: 20px; +$radio-input-size-sm: 24px; // Width & height for small viewports. + +// Deprecated, please avoid using these. +$block-padding: 14px; // Used to define space between block footprint and surrouding borders. + + +/** + * Block paddings. + */ + +// Padding for blocks with a background color (e.g. paragraph or group). +$block-bg-padding--v: 1.25em; +$block-bg-padding--h: 2.375em; + + +/** + * React Native specific. + * These variables do not appear to be used anywhere else. + */ + +// Dimensions. +$mobile-header-toolbar-height: 44px; +$mobile-floating-toolbar-height: 44px; +$mobile-floating-toolbar-margin: 8px; +$mobile-color-swatch: 48px; + +// Block UI. $mobile-block-toolbar-height: 44px; -$block-padding: 14px; // Space between block footprint and focus boundaries. These are drawn outside the block footprint, and do not affect the size. -$block-spacing: 4px; // Vertical space between blocks. -$block-side-ui-width: $button-size; // Width of the movers/drag handle UI. -$block-side-ui-clearance: 2px; // Space between movers/drag handle UI, and block. $dimmed-opacity: 1; - $block-edge-to-content: 16px; $solid-border-space: 12px; $dashed-border-space: 6px; @@ -96,17 +126,3 @@ $block-selected-border-width: 1px; $block-selected-padding: 0; $block-selected-child-margin: 5px; $block-selected-to-content: $block-edge-to-content - $block-selected-margin - $block-selected-border-width; - -/** - * Border radii. - */ - -$radius-round: 50%; -$radius-block-ui: 2px; - -/** - * Block paddings. - */ -// Padding for blocks with a background color (e.g. paragraph or group). -$block-bg-padding--v: 1.25em; -$block-bg-padding--h: 2.375em; diff --git a/packages/base-styles/package.json b/packages/base-styles/package.json index 112d2c075bb04a..404228338dc1e8 100644 --- a/packages/base-styles/package.json +++ b/packages/base-styles/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/base-styles", - "version": "3.2.0", + "version": "3.3.0", "description": "Base SCSS utilities and variables for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/blob/package.json b/packages/blob/package.json index 65fc026c6fb758..4aefb144d6593d 100644 --- a/packages/blob/package.json +++ b/packages/blob/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blob", - "version": "2.11.0", + "version": "2.12.0", "description": "Blob utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/block-directory/CHANGELOG.md b/packages/block-directory/CHANGELOG.md index cf32748eb92eee..5c2897739a14d2 100644 --- a/packages/block-directory/CHANGELOG.md +++ b/packages/block-directory/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.18.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the block directory namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/block-directory/package.json b/packages/block-directory/package.json index d379ed500114e9..fab8b043c52d84 100644 --- a/packages/block-directory/package.json +++ b/packages/block-directory/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-directory", - "version": "1.17.4", + "version": "1.18.0", "description": "Extend editor with block directory features to search, download and install blocks.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:../a11y", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/block-editor": "file:../block-editor", diff --git a/packages/block-directory/src/components/auto-block-uninstaller/index.js b/packages/block-directory/src/components/auto-block-uninstaller/index.js index 12016417bc8851..e35141c5e08581 100644 --- a/packages/block-directory/src/components/auto-block-uninstaller/index.js +++ b/packages/block-directory/src/components/auto-block-uninstaller/index.js @@ -5,8 +5,13 @@ import { unregisterBlockType } from '@wordpress/blocks'; import { useDispatch, useSelect } from '@wordpress/data'; import { useEffect } from '@wordpress/element'; +/** + * Internal dependencies + */ +import { store as blockDirectoryStore } from '../../store'; + export default function AutoBlockUninstaller() { - const { uninstallBlockType } = useDispatch( 'core/block-directory' ); + const { uninstallBlockType } = useDispatch( blockDirectoryStore ); const shouldRemoveBlockTypes = useSelect( ( select ) => { const { isAutosavingPost, isSavingPost } = select( 'core/editor' ); @@ -14,7 +19,7 @@ export default function AutoBlockUninstaller() { }, [] ); const unusedBlockTypes = useSelect( - ( select ) => select( 'core/block-directory' ).getUnusedBlockTypes(), + ( select ) => select( blockDirectoryStore ).getUnusedBlockTypes(), [] ); diff --git a/packages/block-directory/src/components/downloadable-block-list-item/index.js b/packages/block-directory/src/components/downloadable-block-list-item/index.js index 5d35741af51384..8a88e8593f0adc 100644 --- a/packages/block-directory/src/components/downloadable-block-list-item/index.js +++ b/packages/block-directory/src/components/downloadable-block-list-item/index.js @@ -10,12 +10,13 @@ import DownloadableBlockAuthorInfo from '../downloadable-block-author-info'; import DownloadableBlockHeader from '../downloadable-block-header'; import DownloadableBlockInfo from '../downloadable-block-info'; import DownloadableBlockNotice from '../downloadable-block-notice'; +import { store as blockDirectoryStore } from '../../store'; export default function DownloadableBlockListItem( { item, onClick } ) { const { isLoading, isInstallable } = useSelect( ( select ) => { const { isInstalling, getErrorNoticeForBlock } = select( - 'core/block-directory' + blockDirectoryStore ); const notice = getErrorNoticeForBlock( item.id ); const hasFatal = notice && notice.isFatal; diff --git a/packages/block-directory/src/components/downloadable-block-notice/index.js b/packages/block-directory/src/components/downloadable-block-notice/index.js index 75e7d5a513bca9..f24545954e63ae 100644 --- a/packages/block-directory/src/components/downloadable-block-notice/index.js +++ b/packages/block-directory/src/components/downloadable-block-notice/index.js @@ -5,10 +5,15 @@ import { __ } from '@wordpress/i18n'; import { Button, Notice } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as blockDirectoryStore } from '../../store'; + export const DownloadableBlockNotice = ( { block, onClick } ) => { const errorNotice = useSelect( ( select ) => - select( 'core/block-directory' ).getErrorNoticeForBlock( block.id ), + select( blockDirectoryStore ).getErrorNoticeForBlock( block.id ), [ block ] ); diff --git a/packages/block-directory/src/components/downloadable-blocks-list/index.js b/packages/block-directory/src/components/downloadable-blocks-list/index.js index 5aff92c1914a15..377bac14f208de 100644 --- a/packages/block-directory/src/components/downloadable-blocks-list/index.js +++ b/packages/block-directory/src/components/downloadable-blocks-list/index.js @@ -7,15 +7,17 @@ import { noop } from 'lodash'; * WordPress dependencies */ import { useDispatch } from '@wordpress/data'; +import { store as editPostStore } from '@wordpress/edit-post'; /** * Internal dependencies */ import DownloadableBlockListItem from '../downloadable-block-list-item'; +import { store as blockDirectoryStore } from '../../store'; function DownloadableBlocksList( { items, onHover = noop, onSelect } ) { - const { installBlockType } = useDispatch( 'core/block-directory' ); - const { setIsInserterOpened } = useDispatch( 'core/edit-post' ); + const { installBlockType } = useDispatch( blockDirectoryStore ); + const { setIsInserterOpened } = useDispatch( editPostStore ); if ( ! items.length ) { return null; diff --git a/packages/block-directory/src/components/downloadable-blocks-panel/index.js b/packages/block-directory/src/components/downloadable-blocks-panel/index.js index 6cdde9efc28c9a..fa6af8b174dbda 100644 --- a/packages/block-directory/src/components/downloadable-blocks-panel/index.js +++ b/packages/block-directory/src/components/downloadable-blocks-panel/index.js @@ -12,6 +12,7 @@ import { speak } from '@wordpress/a11y'; * Internal dependencies */ import DownloadableBlocksList from '../downloadable-blocks-list'; +import { store as blockDirectoryStore } from '../../store'; function DownloadableBlocksPanel( { downloadableItems, @@ -80,7 +81,7 @@ export default compose( [ const { getDownloadableBlocks, isRequestingDownloadableBlocks, - } = select( 'core/block-directory' ); + } = select( blockDirectoryStore ); const hasPermission = select( 'core' ).canUser( 'read', diff --git a/packages/block-directory/src/index.js b/packages/block-directory/src/index.js index 673db466448953..2623b6b2b7284a 100644 --- a/packages/block-directory/src/index.js +++ b/packages/block-directory/src/index.js @@ -1,8 +1,3 @@ -/** - * WordPress dependencies - */ -import '@wordpress/notices'; - /** * Internal dependencies */ diff --git a/packages/block-directory/src/plugins/get-install-missing/index.js b/packages/block-directory/src/plugins/get-install-missing/index.js index da4337e69249c0..563fd6e845b582 100644 --- a/packages/block-directory/src/plugins/get-install-missing/index.js +++ b/packages/block-directory/src/plugins/get-install-missing/index.js @@ -12,6 +12,7 @@ import { Warning } from '@wordpress/block-editor'; * Internal dependencies */ import InstallButton from './install-button'; +import { store as blockDirectoryStore } from '../../store'; const getInstallMissing = ( OriginalComponent ) => ( props ) => { const { originalName, originalUndelimitedContent } = props.attributes; @@ -19,7 +20,7 @@ const getInstallMissing = ( OriginalComponent ) => ( props ) => { // eslint-disable-next-line react-hooks/rules-of-hooks const { block, hasPermission } = useSelect( ( select ) => { - const { getDownloadableBlocks } = select( 'core/block-directory' ); + const { getDownloadableBlocks } = select( blockDirectoryStore ); const blocks = getDownloadableBlocks( 'block:' + originalName ).filter( ( { name } ) => originalName === name ); diff --git a/packages/block-directory/src/plugins/get-install-missing/install-button.js b/packages/block-directory/src/plugins/get-install-missing/install-button.js index f4985de41846e2..7805589da40490 100644 --- a/packages/block-directory/src/plugins/get-install-missing/install-button.js +++ b/packages/block-directory/src/plugins/get-install-missing/install-button.js @@ -6,11 +6,16 @@ import { Button } from '@wordpress/components'; import { createBlock, getBlockType, parse } from '@wordpress/blocks'; import { useSelect, useDispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as blockDirectoryStore } from '../../store'; + export default function InstallButton( { attributes, block, clientId } ) { const isInstallingBlock = useSelect( ( select ) => - select( 'core/block-directory' ).isInstalling( block.id ) + select( blockDirectoryStore ).isInstalling( block.id ) ); - const { installBlockType } = useDispatch( 'core/block-directory' ); + const { installBlockType } = useDispatch( blockDirectoryStore ); const { replaceBlock } = useDispatch( 'core/block-editor' ); return ( diff --git a/packages/block-directory/src/plugins/installed-blocks-pre-publish-panel/index.js b/packages/block-directory/src/plugins/installed-blocks-pre-publish-panel/index.js index f00528793dc634..a51b68fcaffef2 100644 --- a/packages/block-directory/src/plugins/installed-blocks-pre-publish-panel/index.js +++ b/packages/block-directory/src/plugins/installed-blocks-pre-publish-panel/index.js @@ -10,10 +10,11 @@ import { blockDefault } from '@wordpress/icons'; * Internal dependencies */ import CompactList from '../../components/compact-list'; +import { store as blockDirectoryStore } from '../../store'; export default function InstalledBlocksPrePublishPanel() { const newBlockTypes = useSelect( - ( select ) => select( 'core/block-directory' ).getNewBlockTypes(), + ( select ) => select( blockDirectoryStore ).getNewBlockTypes(), [] ); diff --git a/packages/block-directory/src/store/actions.js b/packages/block-directory/src/store/actions.js index 5f134e67820556..ee143492ab7b56 100644 --- a/packages/block-directory/src/store/actions.js +++ b/packages/block-directory/src/store/actions.js @@ -1,9 +1,11 @@ /** * WordPress dependencies */ +import { store as blocksStore } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; import { controls } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -85,7 +87,7 @@ export function* installBlockType( block ) { yield loadAssets( assets ); const registeredBlocks = yield controls.select( - 'core/blocks', + blocksStore.name, 'getBlockTypes' ); if ( ! registeredBlocks.some( ( i ) => i.name === block.name ) ) { @@ -143,7 +145,7 @@ export function* uninstallBlockType( block ) { yield removeInstalledBlockType( block ); } catch ( error ) { yield controls.dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', error.message || __( 'An error occurred.' ) ); diff --git a/packages/block-directory/src/store/controls.js b/packages/block-directory/src/store/controls.js index a5ee96d8e6c9d4..e5b983642dbf59 100644 --- a/packages/block-directory/src/store/controls.js +++ b/packages/block-directory/src/store/controls.js @@ -7,7 +7,7 @@ import apiFetch from '@wordpress/api-fetch'; * Load an asset for a block. * * This function returns a Promise that will resolve once the asset is loaded, - * or in the case of Stylesheets and Inline Javascript, will resolve immediately. + * or in the case of Stylesheets and Inline JavaScript, will resolve immediately. * * @param {HTMLElement} el A HTML Element asset to inject. * @@ -65,10 +65,10 @@ const controls = { LOAD_ASSETS() { /* * Fetch the current URL (post-new.php, or post.php?post=1&action=edit) and compare the - * Javascript and CSS assets loaded between the pages. This imports the required assets + * JavaScript and CSS assets loaded between the pages. This imports the required assets * for the block into the current page while not requiring that we know them up-front. * In the future this can be improved by reliance upon block.json and/or a script-loader - * dependancy API. + * dependency API. */ return apiFetch( { url: document.location.href, diff --git a/packages/block-directory/src/store/test/actions.js b/packages/block-directory/src/store/test/actions.js index 037a585f4facb0..0b40494b44d838 100644 --- a/packages/block-directory/src/store/test/actions.js +++ b/packages/block-directory/src/store/test/actions.js @@ -1,3 +1,9 @@ +/** + * WordPress dependencies + */ +import { store as blocksStore } from '@wordpress/blocks'; +import { store as noticesStore } from '@wordpress/notices'; + /** * Internal dependencies */ @@ -80,7 +86,7 @@ describe( 'actions', () => { expect( generator.next().value ).toEqual( { args: [], selectorName: 'getBlockTypes', - storeKey: 'core/blocks', + storeKey: blocksStore.name, type: '@@data/SELECT', } ); @@ -142,7 +148,7 @@ describe( 'actions', () => { expect( generator.next().value ).toEqual( { args: [], selectorName: 'getBlockTypes', - storeKey: 'core/blocks', + storeKey: blocksStore.name, type: '@@data/SELECT', } ); @@ -276,7 +282,7 @@ describe( 'actions', () => { expect( generator.throw( apiError ).value ).toMatchObject( { type: '@@data/DISPATCH', actionName: 'createErrorNotice', - storeKey: 'core/notices', + storeKey: noticesStore, } ); expect( generator.next() ).toEqual( { diff --git a/packages/block-directory/src/store/test/selectors.js b/packages/block-directory/src/store/test/selectors.js index a2cff5a9510b65..f4efdd3b507924 100644 --- a/packages/block-directory/src/store/test/selectors.js +++ b/packages/block-directory/src/store/test/selectors.js @@ -89,9 +89,9 @@ describe( 'selectors', () => { describe( 'getNewBlockTypes', () => { it( 'should retrieve the block types that are installed and in the post content', () => { - getNewBlockTypes.__unstableGetSelect = jest.fn( () => ( { - getBlocks: () => blockList, - } ) ); + getNewBlockTypes.registry = { + select: jest.fn( () => ( { getBlocks: () => blockList } ) ), + }; const state = { blockManagement: { installedBlockTypes: [ @@ -106,9 +106,9 @@ describe( 'selectors', () => { } ); it( 'should return an empty array if no blocks are used', () => { - getNewBlockTypes.__unstableGetSelect = jest.fn( () => ( { - getBlocks: () => [], - } ) ); + getNewBlockTypes.registry = { + select: jest.fn( () => ( { getBlocks: () => [] } ) ), + }; const state = { blockManagement: { installedBlockTypes: [ @@ -124,10 +124,9 @@ describe( 'selectors', () => { describe( 'getUnusedBlockTypes', () => { it( 'should retrieve the block types that are installed but not used', () => { - getUnusedBlockTypes.__unstableGetSelect = jest.fn( () => ( { - getBlocks: () => blockList, - } ) ); - + getUnusedBlockTypes.registry = { + select: jest.fn( () => ( { getBlocks: () => blockList } ) ), + }; const state = { blockManagement: { installedBlockTypes: [ @@ -142,10 +141,9 @@ describe( 'selectors', () => { } ); it( 'should return all block types if no blocks are used', () => { - getUnusedBlockTypes.__unstableGetSelect = jest.fn( () => ( { - getBlocks: () => [], - } ) ); - + getUnusedBlockTypes.registry = { + select: jest.fn( () => ( { getBlocks: () => [] } ) ), + }; const state = { blockManagement: { installedBlockTypes: [ diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 76cd8209c15b0c..4afb64b2f65e49 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.2.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the block editor namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index cfadf407b3b655..95611432d3e1bb 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -19,12 +19,12 @@ import { BlockEditorProvider, BlockList, WritingFlow, - ObserveTyping + ObserveTyping, } from '@wordpress/block-editor'; import { SlotFillProvider, Popover } from '@wordpress/components'; import { useState } from '@wordpress/element'; -function MyEditorComponent () { +function MyEditorComponent() { const [ blocks, updateBlocks ] = useState( [] ); return ( @@ -490,7 +490,6 @@ _Properties_ - _hasFixedToolbar_ `boolean`: Whether or not the editor toolbar is fixed - _focusMode_ `boolean`: Whether the focus mode is enabled or not - _styles_ `Array`: Editor Styles -- _isRTL_ `boolean`: Whether the editor is in RTL mode - _keepCaretInsideBlock_ `boolean`: Whether caret should move between blocks in edit mode - _bodyPlaceholder_ `string`: Empty post placeholder - _titlePlaceholder_ `string`: Empty title placeholder @@ -569,6 +568,26 @@ _Related_ - +# **useBlockDisplayInformation** + +Hook used to try to find a matching block variation and return +the appropriate information for display reasons. In order to +to try to find a match we need to things: +1\. Block's client id to extract it's current attributes. +2\. A block variation should have set `isActive` prop to a proper function. + +If for any reason a block variaton match cannot be found, +the returned information come from the Block Type. +If no blockType is found with the provided clientId, returns null. + +_Parameters_ + +- _clientId_ `string`: Block's client id. + +_Returns_ + +- `?WPBlockDisplayInformation`: Block's display information, or `null` when the block or its type not found. + # **useBlockEditContext** Undocumented declaration. diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index b8264cfd2e8ee4..8b1e16ce5cc976 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-editor", - "version": "5.1.3", + "version": "5.2.0", "description": "Generic block editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -28,13 +28,14 @@ "{src,build,build-module}/{index.js,store/index.js,hooks/**}" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:../a11y", "@wordpress/blob": "file:../blob", "@wordpress/blocks": "file:../blocks", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", + "@wordpress/data-controls": "file:../data-controls", "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", @@ -50,7 +51,6 @@ "@wordpress/shortcode": "file:../shortcode", "@wordpress/token-list": "file:../token-list", "@wordpress/url": "file:../url", - "@wordpress/warning": "file:../warning", "@wordpress/wordcount": "file:../wordcount", "classnames": "^2.2.5", "css-mediaquery": "^0.1.2", @@ -61,10 +61,8 @@ "memize": "^1.1.0", "react-autosize-textarea": "^7.1.0", "react-spring": "^8.0.19", - "react-transition-group": "^2.9.0", "reakit": "1.1.0", "redux-multi": "^0.1.12", - "refx": "^3.0.0", "rememo": "^3.0.0", "tinycolor2": "^1.4.1", "traverse": "^0.6.6" diff --git a/packages/block-editor/src/autocompleters/block.js b/packages/block-editor/src/autocompleters/block.js index aa7dfa160ed738..6495956da0dadf 100644 --- a/packages/block-editor/src/autocompleters/block.js +++ b/packages/block-editor/src/autocompleters/block.js @@ -22,18 +22,11 @@ import BlockIcon from '../components/block-icon'; const SHOWN_BLOCK_TYPES = 9; -/** @typedef {import('@wordpress/block-editor').WPEditorInserterItem} WPEditorInserterItem */ - /** @typedef {import('@wordpress/components').WPCompleter} WPCompleter */ /** * Creates a blocks repeater for replacing the current block with a selected block type. * - * @param {Object} props Component props. - * @param {string} [props.getBlockInsertionParentClientId] Client ID of the parent. - * @param {string} [props.getInserterItems] Inserter items for parent. - * @param {string} [props.getSelectedBlockName] Name of selected block or null. - * * @return {WPCompleter} A blocks completer. */ function createBlockCompleter() { diff --git a/packages/block-editor/src/components/alignment-toolbar/index.js b/packages/block-editor/src/components/alignment-toolbar/index.js index 4136f5f7a4c9e3..8eed48b364e480 100644 --- a/packages/block-editor/src/components/alignment-toolbar/index.js +++ b/packages/block-editor/src/components/alignment-toolbar/index.js @@ -6,7 +6,7 @@ import { find } from 'lodash'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, isRTL } from '@wordpress/i18n'; import { ToolbarGroup } from '@wordpress/components'; import { alignLeft, alignRight, alignCenter } from '@wordpress/icons'; @@ -40,7 +40,6 @@ export function AlignmentToolbar( props ) { alignmentControls = DEFAULT_ALIGNMENT_CONTROLS, label = __( 'Change text alignment' ), isCollapsed = true, - isRTL, } = props; function applyOrUnset( align ) { @@ -54,7 +53,7 @@ export function AlignmentToolbar( props ) { function setIcon() { if ( activeAlignment ) return activeAlignment.icon; - return isRTL ? alignRight : alignLeft; + return isRTL() ? alignRight : alignLeft; } return ( diff --git a/packages/block-editor/src/components/block-actions/index.js b/packages/block-editor/src/components/block-actions/index.js index e7de0fa5c40f32..da858d2b9a7e39 100644 --- a/packages/block-editor/src/components/block-actions/index.js +++ b/packages/block-editor/src/components/block-actions/index.js @@ -7,7 +7,11 @@ import { castArray, first, last, every } from 'lodash'; * WordPress dependencies */ import { useDispatch, useSelect } from '@wordpress/data'; -import { hasBlockSupport, switchToBlockType } from '@wordpress/blocks'; +import { + hasBlockSupport, + switchToBlockType, + store as blocksStore, +} from '@wordpress/blocks'; /** * Internal dependencies @@ -26,7 +30,7 @@ export default function BlockActions( { getTemplateLock, } = useSelect( ( select ) => select( 'core/block-editor' ), [] ); const { getDefaultBlockName, getGroupingBlockName } = useSelect( - ( select ) => select( 'core/blocks' ), + ( select ) => select( blocksStore ), [] ); diff --git a/packages/block-editor/src/components/block-card/index.js b/packages/block-editor/src/components/block-card/index.js index c209ea39b5c73e..60f8616b5f49dd 100644 --- a/packages/block-editor/src/components/block-card/index.js +++ b/packages/block-editor/src/components/block-card/index.js @@ -1,9 +1,20 @@ +/** + * WordPress dependencies + */ +import deprecated from '@wordpress/deprecated'; + /** * Internal dependencies */ import BlockIcon from '../block-icon'; -function BlockCard( { blockType: { icon, title, description } } ) { +function BlockCard( { title, icon, description, blockType } ) { + if ( blockType ) { + deprecated( '`blockType` property in `BlockCard component`', { + alternative: '`title, icon and description` properties', + } ); + ( { title, icon, description } = blockType ); + } return (
diff --git a/packages/block-editor/src/components/block-compare/style.scss b/packages/block-editor/src/components/block-compare/style.scss index 90301874000653..5474ad6a264162 100644 --- a/packages/block-editor/src/components/block-compare/style.scss +++ b/packages/block-editor/src/components/block-compare/style.scss @@ -60,7 +60,7 @@ .block-editor-block-compare__preview { padding: 0; - padding-top: $block-padding; + padding-top: $grid-unit-20; p { font-size: 12px; @@ -69,7 +69,7 @@ } .block-editor-block-compare__action { - margin-top: $block-padding; + margin-top: $grid-unit-20; } .block-editor-block-compare__heading { diff --git a/packages/block-editor/src/components/block-controls/index.js b/packages/block-editor/src/components/block-controls/index.js index f2b35e4b5301f8..297d3839df4aa9 100644 --- a/packages/block-editor/src/components/block-controls/index.js +++ b/packages/block-editor/src/components/block-controls/index.js @@ -20,24 +20,18 @@ import useDisplayBlockControls from '../use-display-block-controls'; const { Fill, Slot } = createSlotFill( 'BlockControls' ); -function BlockControlsSlot( { __experimentalIsExpanded = false, ...props } ) { +function BlockControlsSlot( props ) { const accessibleToolbarState = useContext( ToolbarContext ); - return ( - - ); + return ; } -function BlockControlsFill( { controls, __experimentalIsExpanded, children } ) { +function BlockControlsFill( { controls, children } ) { if ( ! useDisplayBlockControls() ) { return null; } return ( - + { ( fillProps ) => { // Children passed to BlockControlsFill will not have access to any // React Context whose Provider is part of the BlockControlsSlot tree. @@ -54,9 +48,6 @@ function BlockControlsFill( { controls, __experimentalIsExpanded, children } ) { ); } -const buildSlotName = ( isExpanded ) => - `BlockControls${ isExpanded ? '-expanded' : '' }`; - const BlockControls = BlockControlsFill; BlockControls.Slot = BlockControlsSlot; diff --git a/packages/block-editor/src/components/block-draggable/draggable-chip.js b/packages/block-editor/src/components/block-draggable/draggable-chip.js index 2849645ab76de7..5ba2b4b46da3a0 100644 --- a/packages/block-editor/src/components/block-draggable/draggable-chip.js +++ b/packages/block-editor/src/components/block-draggable/draggable-chip.js @@ -2,8 +2,6 @@ * WordPress dependencies */ import { _n, sprintf } from '@wordpress/i18n'; -import { getBlockType } from '@wordpress/blocks'; -import { useSelect } from '@wordpress/data'; import { Flex, FlexItem } from '@wordpress/components'; import { dragHandle } from '@wordpress/icons'; @@ -12,22 +10,7 @@ import { dragHandle } from '@wordpress/icons'; */ import BlockIcon from '../block-icon'; -export default function BlockDraggableChip( { clientIds } ) { - const icon = useSelect( - ( select ) => { - if ( clientIds.length !== 1 ) { - return; - } - - const { getBlockName } = select( 'core/block-editor' ); - const [ firstId ] = clientIds; - const blockName = getBlockName( firstId ); - - return getBlockType( blockName )?.icon; - }, - [ clientIds ] - ); - +export default function BlockDraggableChip( { count, icon } ) { return (
@@ -41,8 +24,8 @@ export default function BlockDraggableChip( { clientIds } ) { ) : ( sprintf( /* translators: %d: Number of blocks. */ - _n( '%d block', '%d blocks', clientIds.length ), - clientIds.length + _n( '%d block', '%d blocks', count ), + count ) ) } diff --git a/packages/block-editor/src/components/block-draggable/index.js b/packages/block-editor/src/components/block-draggable/index.js index ba7c90e523833f..bb7692ec2abf9f 100644 --- a/packages/block-editor/src/components/block-draggable/index.js +++ b/packages/block-editor/src/components/block-draggable/index.js @@ -1,6 +1,7 @@ /** * WordPress dependencies */ +import { getBlockType } from '@wordpress/blocks'; import { Draggable } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect, useRef } from '@wordpress/element'; @@ -19,19 +20,23 @@ const BlockDraggable = ( { onDragEnd, elementId, } ) => { - const { srcRootClientId, isDraggable } = useSelect( + const { srcRootClientId, isDraggable, icon } = useSelect( ( select ) => { - const { getBlockRootClientId, getTemplateLock } = select( - 'core/block-editor' - ); + const { + getBlockRootClientId, + getTemplateLock, + getBlockName, + } = select( 'core/block-editor' ); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); const templateLock = rootClientId ? getTemplateLock( rootClientId ) : null; + const blockName = getBlockName( clientIds[ 0 ] ); return { srcRootClientId: rootClientId, isDraggable: 'all' !== templateLock, + icon: getBlockType( blockName )?.icon, }; }, [ clientIds ] @@ -93,14 +98,14 @@ const BlockDraggable = ( { } } } __experimentalDragComponent={ - + } > { ( { onDraggableStart, onDraggableEnd } ) => { return children( { - isDraggable: true, - onDraggableStart, - onDraggableEnd, + draggable: true, + onDragStart: onDraggableStart, + onDragEnd: onDraggableEnd, } ); } } diff --git a/packages/block-editor/src/components/block-full-height-alignment-toolbar/README.md b/packages/block-editor/src/components/block-full-height-alignment-toolbar/README.md new file mode 100644 index 00000000000000..6febd1fc272f3a --- /dev/null +++ b/packages/block-editor/src/components/block-full-height-alignment-toolbar/README.md @@ -0,0 +1,3 @@ +# Full Height Toolbar Toolbar + +Unlike the block alignment options, `Full Height Alignment` can be applied to a block in an independent way. But also, it works very well together with these block alignment options, where the combination empowers the design-layout capability. \ No newline at end of file diff --git a/packages/block-editor/src/components/block-full-height-alignment-toolbar/index.js b/packages/block-editor/src/components/block-full-height-alignment-toolbar/index.js new file mode 100644 index 00000000000000..0912a5b8fb26b6 --- /dev/null +++ b/packages/block-editor/src/components/block-full-height-alignment-toolbar/index.js @@ -0,0 +1,25 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { ToolbarGroup, ToolbarButton } from '@wordpress/components'; +import { fullscreen } from '@wordpress/icons'; + +function BlockFullHeightAlignmentToolbar( { + isActive, + label = __( 'Toggle full height' ), + onToggle, +} ) { + return ( + + onToggle( ! isActive ) } + /> + + ); +} + +export default BlockFullHeightAlignmentToolbar; diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 3d68af7c08391a..dd6edc46f5be04 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -6,6 +6,7 @@ import { getBlockType, getUnregisteredTypeHandlerName, hasBlockSupport, + store as blocksStore, } from '@wordpress/blocks'; import { PanelBody, @@ -24,6 +25,8 @@ import BlockStyles from '../block-styles'; import MultiSelectionInspector from '../multi-selection-inspector'; import DefaultStylePicker from '../default-style-picker'; import BlockVariationTransforms from '../block-variation-transforms'; +import useBlockDisplayInformation from '../use-block-display-information'; + const BlockInspector = ( { blockType, count, @@ -63,22 +66,36 @@ const BlockInspector = ( { } return null; } + return ( + + ); +}; +const BlockInspectorSingleBlock = ( { + clientId, + blockName, + hasBlockStyles, + bubblesVirtually, +} ) => { + const blockInformation = useBlockDisplayInformation( clientId ); return (
- - + + { hasBlockStyles && (
- + { hasBlockSupport( - blockType.name, + blockName, 'defaultStylePicker', true - ) && ( - - ) } + ) && }
) } @@ -121,7 +138,7 @@ export default withSelect( ( select ) => { getSelectedBlockCount, getBlockName, } = select( 'core/block-editor' ); - const { getBlockStyles } = select( 'core/blocks' ); + const { getBlockStyles } = select( blocksStore ); const selectedBlockClientId = getSelectedBlockClientId(); const selectedBlockName = selectedBlockClientId && getBlockName( selectedBlockClientId ); diff --git a/packages/block-editor/src/components/block-list-appender/index.js b/packages/block-editor/src/components/block-list-appender/index.js index fae056e50dcb50..0fc9b408ee8466 100644 --- a/packages/block-editor/src/components/block-list-appender/index.js +++ b/packages/block-editor/src/components/block-list-appender/index.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { createContext, useContext } from '@wordpress/element'; +import { createContext } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { getDefaultBlockName } from '@wordpress/blocks'; @@ -34,8 +34,6 @@ function BlockListAppender( { selectedBlockClientId, tagName: TagName = 'div', } ) { - const appenderNodesMap = useContext( AppenderNodesContext ); - if ( isLocked || CustomAppender === false ) { return null; } @@ -100,15 +98,6 @@ function BlockListAppender( { 'wp-block', className ) } - ref={ ( ref ) => { - if ( ref ) { - // Set the reference of the "Appender" with `rootClientId` as key. - appenderNodesMap.set( rootClientId || '', ref ); - } else { - // If it un-mounts, cleanup the map. - appenderNodesMap.delete( rootClientId || '' ); - } - } } > { appender } diff --git a/packages/block-editor/src/components/block-list-appender/style.scss b/packages/block-editor/src/components/block-list-appender/style.scss index f5eb38c57f8f47..1b893c26875d7e 100644 --- a/packages/block-editor/src/components/block-list-appender/style.scss +++ b/packages/block-editor/src/components/block-list-appender/style.scss @@ -3,12 +3,6 @@ .block-editor-block-list__block .block-list-appender { margin: $grid-unit-10 0; - // Add additional margin to the appender when inside a group with a background color. - // If changing this, be sure to sync up with group/editor.scss line 13. - .has-background & { - margin: ($grid-unit-20 + $block-spacing) $grid-unit-10; - } - // Animate appearance. .block-list-appender__toggle { padding: 0; diff --git a/packages/block-editor/src/components/block-list/block-contextual-toolbar.js b/packages/block-editor/src/components/block-list/block-contextual-toolbar.js index 154a8f2e1f303c..3d02a274795862 100644 --- a/packages/block-editor/src/components/block-list/block-contextual-toolbar.js +++ b/packages/block-editor/src/components/block-list/block-contextual-toolbar.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { hasBlockSupport } from '@wordpress/blocks'; +import { hasBlockSupport, store as blocksStore } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; /** @@ -16,7 +16,7 @@ function BlockContextualToolbar( { focusOnMount, ...props } ) { const { getBlockName, getSelectedBlockClientIds } = select( 'core/block-editor' ); - const { getBlockType } = select( 'core/blocks' ); + const { getBlockType } = select( blocksStore ); const selectedBlockClientIds = getSelectedBlockClientIds(); const selectedBlockClientId = selectedBlockClientIds[ 0 ]; return { diff --git a/packages/block-editor/src/components/block-list/block-list-item.native.js b/packages/block-editor/src/components/block-list/block-list-item.native.js index 8623514b1b44c2..230b8d720e6b71 100644 --- a/packages/block-editor/src/components/block-list/block-list-item.native.js +++ b/packages/block-editor/src/components/block-list/block-list-item.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { View } from 'react-native'; +import { View, Dimensions } from 'react-native'; /** * WordPress dependencies @@ -9,11 +9,7 @@ import { View } from 'react-native'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { - ReadableContentView, - WIDE_ALIGNMENTS, - ALIGNMENT_BREAKPOINTS, -} from '@wordpress/components'; +import { ReadableContentView, alignmentHelpers } from '@wordpress/components'; /** * Internal dependencies @@ -26,6 +22,13 @@ const stretchStyle = { flex: 1, }; +const { + isFullWidth, + isWideWidth, + isWider, + isContainerRelated, +} = alignmentHelpers; + export class BlockListItem extends Component { constructor() { super( ...arguments ); @@ -51,39 +54,69 @@ export class BlockListItem extends Component { blockAlignment, marginHorizontal, parentBlockAlignment, + hasParents, + blockName, + parentBlockName, + parentWidth, } = this.props; const { blockWidth } = this.state; - if ( blockAlignment === WIDE_ALIGNMENTS.alignments.full ) { - return 0; + if ( isFullWidth( blockAlignment ) ) { + if ( ! hasParents ) { + return 0; + } + return marginHorizontal; } - - if ( blockAlignment === WIDE_ALIGNMENTS.alignments.wide ) { + if ( isWideWidth( blockAlignment ) ) { return marginHorizontal; } + const screenWidth = Math.floor( Dimensions.get( 'window' ).width ); + if ( - parentBlockAlignment === WIDE_ALIGNMENTS.alignments.full && - blockWidth <= ALIGNMENT_BREAKPOINTS.medium + isFullWidth( parentBlockAlignment ) && + ! isWider( blockWidth, 'medium' ) ) { + if ( + isContainerRelated( blockName ) || + isWider( screenWidth, 'mobile' ) + ) { + return marginHorizontal; + } return marginHorizontal * 2; } + if ( + isContainerRelated( parentBlockName ) && + ! isContainerRelated( blockName ) + ) { + const isScreenWidthEqual = parentWidth === screenWidth; + if ( isScreenWidthEqual || isWider( screenWidth, 'mobile' ) ) { + return marginHorizontal * 2; + } + } + return marginHorizontal; } getContentStyles( readableContentViewStyle ) { - const { blockAlignment, hasParents } = this.props; - const isFullWidth = blockAlignment === WIDE_ALIGNMENTS.alignments.full; + const { + blockAlignment, + blockName, + hasParents, + parentBlockName, + } = this.props; return [ readableContentViewStyle, - isFullWidth && + isFullWidth( blockAlignment ) && ! hasParents && { width: styles.fullAlignment.width, }, - isFullWidth && - hasParents && { + ! blockAlignment && + hasParents && + ! isContainerRelated( parentBlockName ) && + isContainerRelated( blockName ) && { paddingHorizontal: styles.fullAlignmentPadding.paddingLeft, }, ]; @@ -98,15 +131,23 @@ export class BlockListItem extends Component { shouldShowInsertionPointAfter, contentResizeMode, shouldShowInnerBlockAppender, + parentWidth, + marginHorizontal, + blockName, ...restProps } = this.props; const readableContentViewStyle = contentResizeMode === 'stretch' && stretchStyle; - return ( @@ -169,7 +211,7 @@ export default compose( [ const isReadOnly = getSettings().readOnly; const block = __unstableGetBlockWithoutInnerBlocks( clientId ); - const { attributes } = block || {}; + const { attributes, name } = block || {}; const { align } = attributes || {}; const parents = getBlockParents( clientId, true ); const hasParents = !! parents.length; @@ -178,6 +220,7 @@ export default compose( [ : {}; const { align: parentBlockAlignment } = parentBlock?.attributes || {}; + const { name: parentBlockName } = parentBlock || {}; return { shouldShowInsertionPointBefore, @@ -186,6 +229,8 @@ export default compose( [ hasParents, blockAlignment: align, parentBlockAlignment, + blockName: name, + parentBlockName, }; } ), diff --git a/packages/block-editor/src/components/block-list/block-popover.js b/packages/block-editor/src/components/block-list/block-popover.js index 3fb6332d076573..5bf1e5a12164df 100644 --- a/packages/block-editor/src/components/block-list/block-popover.js +++ b/packages/block-editor/src/components/block-list/block-popover.js @@ -27,7 +27,7 @@ import { getScrollContainer } from '@wordpress/dom'; import BlockSelectionButton from './block-selection-button'; import BlockContextualToolbar from './block-contextual-toolbar'; import Inserter from '../inserter'; -import { BlockNodes } from './root-container'; +import { BlockNodes } from './'; import { getBlockDOMNode } from '../../utils/dom'; function selector( select ) { @@ -229,6 +229,7 @@ function BlockPopover( { ) } { showEmptyBlockSideInserter && ( diff --git a/packages/block-editor/src/components/block-list/block-selection-button.js b/packages/block-editor/src/components/block-list/block-selection-button.js index 0462b3dbf28f86..8c7deae94570e7 100644 --- a/packages/block-editor/src/components/block-list/block-selection-button.js +++ b/packages/block-editor/src/components/block-list/block-selection-button.js @@ -9,12 +9,24 @@ import classnames from 'classnames'; import { Button } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect, useRef } from '@wordpress/element'; -import { BACKSPACE, DELETE } from '@wordpress/keycodes'; +import { + BACKSPACE, + DELETE, + UP, + DOWN, + LEFT, + RIGHT, + TAB, + ESCAPE, + ENTER, + SPACE, +} from '@wordpress/keycodes'; import { getBlockType, __experimentalGetAccessibleBlockLabel as getAccessibleBlockLabel, } from '@wordpress/blocks'; import { speak } from '@wordpress/a11y'; +import { focus } from '@wordpress/dom'; /** * Internal dependencies @@ -30,6 +42,40 @@ function isWindows() { return window.navigator.platform.indexOf( 'Win' ) > -1; } +function selector( select ) { + const { + getSelectedBlockClientId, + getMultiSelectedBlocksEndClientId, + getPreviousBlockClientId, + getNextBlockClientId, + hasBlockMovingClientId, + getBlockIndex, + getBlockRootClientId, + getClientIdsOfDescendants, + canInsertBlockType, + getBlockName, + } = select( 'core/block-editor' ); + + const selectedBlockClientId = getSelectedBlockClientId(); + const selectionEndClientId = getMultiSelectedBlocksEndClientId(); + + return { + selectedBlockClientId, + selectionBeforeEndClientId: getPreviousBlockClientId( + selectionEndClientId || selectedBlockClientId + ), + selectionAfterEndClientId: getNextBlockClientId( + selectionEndClientId || selectedBlockClientId + ), + hasBlockMovingClientId, + getBlockIndex, + getBlockRootClientId, + getClientIdsOfDescendants, + canInsertBlockType, + getBlockName, + }; +} + /** * Block selection button component, displaying the label of the block. If the block * descends from a root block, a button is displayed enabling the user to select @@ -40,7 +86,7 @@ function isWindows() { * * @return {WPComponent} The component to be rendered. */ -function BlockSelectionButton( { clientId, rootClientId, ...props } ) { +function BlockSelectionButton( { clientId, rootClientId, blockElement } ) { const selected = useSelect( ( select ) => { const { @@ -82,12 +128,111 @@ function BlockSelectionButton( { clientId, rootClientId, ...props } ) { } }, [] ); + const { + selectedBlockClientId, + selectionBeforeEndClientId, + selectionAfterEndClientId, + hasBlockMovingClientId, + getBlockIndex, + getBlockRootClientId, + getClientIdsOfDescendants, + } = useSelect( selector, [] ); + const { + selectBlock, + clearSelectedBlock, + setBlockMovingClientId, + moveBlockToPosition, + } = useDispatch( 'core/block-editor' ); + function onKeyDown( event ) { const { keyCode } = event; + const isUp = keyCode === UP; + const isDown = keyCode === DOWN; + const isLeft = keyCode === LEFT; + const isRight = keyCode === RIGHT; + const isTab = keyCode === TAB; + const isEscape = keyCode === ESCAPE; + const isEnter = keyCode === ENTER; + const isSpace = keyCode === SPACE; + const isShift = event.shiftKey; if ( keyCode === BACKSPACE || keyCode === DELETE ) { removeBlock( clientId ); event.preventDefault(); + return; + } + + const navigateUp = ( isTab && isShift ) || isUp; + const navigateDown = ( isTab && ! isShift ) || isDown; + // Move out of current nesting level (no effect if at root level). + const navigateOut = isLeft; + // Move into next nesting level (no effect if the current block has no innerBlocks). + const navigateIn = isRight; + + let focusedBlockUid; + if ( navigateUp ) { + focusedBlockUid = selectionBeforeEndClientId; + } else if ( navigateDown ) { + focusedBlockUid = selectionAfterEndClientId; + } else if ( navigateOut ) { + focusedBlockUid = + getBlockRootClientId( selectedBlockClientId ) ?? + selectedBlockClientId; + } else if ( navigateIn ) { + focusedBlockUid = + getClientIdsOfDescendants( [ selectedBlockClientId ] )[ 0 ] ?? + selectedBlockClientId; + } + const startingBlockClientId = hasBlockMovingClientId(); + + if ( isEscape && startingBlockClientId ) { + setBlockMovingClientId( null ); + } + if ( ( isEnter || isSpace ) && startingBlockClientId ) { + const sourceRoot = getBlockRootClientId( startingBlockClientId ); + const destRoot = getBlockRootClientId( selectedBlockClientId ); + const sourceBlockIndex = getBlockIndex( + startingBlockClientId, + sourceRoot + ); + let destinationBlockIndex = getBlockIndex( + selectedBlockClientId, + destRoot + ); + if ( + sourceBlockIndex < destinationBlockIndex && + sourceRoot === destRoot + ) { + destinationBlockIndex -= 1; + } + moveBlockToPosition( + startingBlockClientId, + sourceRoot, + destRoot, + destinationBlockIndex + ); + selectBlock( startingBlockClientId ); + setBlockMovingClientId( null ); + } + if ( navigateDown || navigateUp || navigateOut || navigateIn ) { + if ( focusedBlockUid ) { + event.preventDefault(); + selectBlock( focusedBlockUid ); + } else if ( isTab && selectedBlockClientId ) { + let nextTabbable; + + if ( navigateDown ) { + nextTabbable = focus.tabbable.findNext( blockElement ); + } else { + nextTabbable = focus.tabbable.findPrevious( blockElement ); + } + + if ( nextTabbable ) { + event.preventDefault(); + nextTabbable.focus(); + clearSelectedBlock(); + } + } } } @@ -107,7 +252,7 @@ function BlockSelectionButton( { clientId, rootClientId, ...props } ) { ); return ( -
+
+ aria-label={ textDecoration.name } + /> ); } ) }
diff --git a/packages/block-editor/src/components/text-transform-control/index.js b/packages/block-editor/src/components/text-transform-control/index.js index 545fed8407f398..6aeec3101256db 100644 --- a/packages/block-editor/src/components/text-transform-control/index.js +++ b/packages/block-editor/src/components/text-transform-control/index.js @@ -9,71 +9,53 @@ import { formatUppercase, } from '@wordpress/icons'; +const TEXT_TRANSFORMS = [ + { + name: __( 'Uppercase' ), + value: 'uppercase', + icon: formatUppercase, + }, + { + name: __( 'Lowercase' ), + value: 'lowercase', + icon: formatLowercase, + }, + { + name: __( 'Capitalize' ), + value: 'capitalize', + icon: formatCapitalize, + }, +]; + /** * Control to facilitate text transform selections. * * @param {Object} props Component props. * @param {string} props.value Currently selected text transform. - * @param {Array} props.textTransforms Text transforms available for selection. * @param {Function} props.onChange Handles change in text transform selection. * @return {WPElement} Text transform control. */ -export default function TextTransformControl( { - value: textTransform, - textTransforms, - onChange, -} ) { - /** - * Determines what the new text transform is as a result of a user - * interaction with the control. Then passes this on to the supplied - * onChange handler. - * - * @param {string} newTransform Slug for selected text transform. - */ - const handleOnChange = ( newTransform ) => { - // Check if we are toggling a transform off. - const transform = - textTransform === newTransform ? undefined : newTransform; - - // Ensure only defined text transforms are allowed. - const presetTransform = textTransforms.find( - ( { slug } ) => slug === transform - ); - - // Create string that will be turned into CSS custom property - const newTextTransform = presetTransform - ? `var:preset|text-transform|${ presetTransform.slug }` - : undefined; - - onChange( newTextTransform ); - }; - - // Text transform icons to use. - // Icons still to be created/designed. - const icons = { - capitalize: formatCapitalize, - lowercase: formatLowercase, - uppercase: formatUppercase, - }; - +export default function TextTransformControl( { value, onChange } ) { return (
{ __( 'Letter case' ) }
- { textTransforms.map( ( presetTransform ) => { + { TEXT_TRANSFORMS.map( ( textTransform ) => { return ( + /> ); } ) }
diff --git a/packages/block-editor/src/components/ungroup-button/index.native.js b/packages/block-editor/src/components/ungroup-button/index.native.js index e3056a5104e9e6..652a848b8cba42 100644 --- a/packages/block-editor/src/components/ungroup-button/index.native.js +++ b/packages/block-editor/src/components/ungroup-button/index.native.js @@ -6,6 +6,7 @@ import { noop } from 'lodash'; /** * WordPress dependencies */ +import { store as blocksStore } from '@wordpress/blocks'; import { Toolbar, ToolbarButton } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { withSelect, withDispatch } from '@wordpress/data'; @@ -37,7 +38,7 @@ export default compose( [ 'core/block-editor' ); - const { getGroupingBlockName } = select( 'core/blocks' ); + const { getGroupingBlockName } = select( blocksStore ); const selectedId = getSelectedBlockClientId(); const selectedBlock = getBlock( selectedId ); diff --git a/packages/block-editor/src/components/url-input/README.md b/packages/block-editor/src/components/url-input/README.md index d78a1cb09d56a8..b2837bb6dca430 100644 --- a/packages/block-editor/src/components/url-input/README.md +++ b/packages/block-editor/src/components/url-input/README.md @@ -129,12 +129,6 @@ Renders the URL input field used by the `URLInputButton` component. It can be us *Optional.* If this property is added, a label will be generated using label property as the content. -### `autoFocus: Boolean` - -*Optional.* By default, the input will gain focus when it is rendered, as typically it is displayed conditionally. For example when clicking on `URLInputButton` or editing a block. - -If you are not conditionally rendering this component set this property to `false`. - ### `className: String` *Optional.* Adds and optional class to the parent `div` that wraps the URLInput field and popover diff --git a/packages/block-editor/src/components/url-input/index.js b/packages/block-editor/src/components/url-input/index.js index 26b0f98c81daf2..944c9694fda8fe 100644 --- a/packages/block-editor/src/components/url-input/index.js +++ b/packages/block-editor/src/components/url-input/index.js @@ -22,12 +22,6 @@ import { withInstanceId, withSafeTimeout, compose } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; import { isURL } from '@wordpress/url'; -// Since URLInput is rendered in the context of other inputs, but should be -// considered a separate modal node, prevent keyboard events from propagating -// as being considered from the input. -const stopEventPropagation = ( event ) => event.stopPropagation(); - -/* eslint-disable jsx-a11y/no-autofocus */ class URLInput extends Component { constructor( props ) { super( props ); @@ -398,7 +392,6 @@ class URLInput extends Component { placeholder = __( 'Paste URL or type to search' ), __experimentalRenderControl: renderControl, value = '', - autoFocus = true, } = this.props; const { @@ -420,12 +413,10 @@ class URLInput extends Component { const inputProps = { value, required: true, - autoFocus, className: 'block-editor-url-input__input', type: 'text', onChange: this.onChange, onFocus: this.onFocus, - onInput: stopEventPropagation, placeholder, onKeyDown: this.onKeyDown, role: 'combobox', @@ -545,7 +536,6 @@ class URLInput extends Component { return null; } } -/* eslint-enable jsx-a11y/no-autofocus */ /** * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/url-input/README.md diff --git a/packages/block-editor/src/components/url-popover/image-url-input-ui.js b/packages/block-editor/src/components/url-popover/image-url-input-ui.js index ec2fa36bb68b1e..660e0fbb558d40 100644 --- a/packages/block-editor/src/components/url-popover/image-url-input-ui.js +++ b/packages/block-editor/src/components/url-popover/image-url-input-ui.js @@ -18,7 +18,6 @@ import { SVG, Path, } from '@wordpress/components'; -import { LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes'; import { link as linkIcon, close } from '@wordpress/icons'; /** @@ -61,21 +60,6 @@ const ImageURLInputUI = ( { const autocompleteRef = useRef( null ); - const stopPropagation = ( event ) => { - event.stopPropagation(); - }; - - const stopPropagationRelevantKeys = ( event ) => { - if ( - [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( - event.keyCode - ) > -1 - ) { - // Stop the key event from propagating up to ObserveTyping.startTypingInTextField. - event.stopPropagation(); - } - }; - const startEditLink = useCallback( () => { if ( linkDestination === LINK_DESTINATION_MEDIA || @@ -240,14 +224,10 @@ const ImageURLInputUI = ( { label={ __( 'Link Rel' ) } value={ removeNewTabRel( rel ) || '' } onChange={ onSetLinkRel } - onKeyPress={ stopPropagation } - onKeyDown={ stopPropagationRelevantKeys } /> @@ -299,8 +279,6 @@ const ImageURLInputUI = ( { className="block-editor-format-toolbar__link-container-content" value={ linkEditorValue } onChangeInputValue={ setUrlInput } - onKeyDown={ stopPropagationRelevantKeys } - onKeyPress={ stopPropagation } onSubmit={ onSubmitLinkChange() } autocompleteRef={ autocompleteRef } /> @@ -309,7 +287,6 @@ const ImageURLInputUI = ( { <> + +

{ title }

+

{ description }

+
+ ); +} +``` + +## Props + +The hook accepts the following props. + +### clientId + +A block's clientId + +- Type: `String` +- Required: Yes diff --git a/packages/block-editor/src/components/use-block-display-information/index.js b/packages/block-editor/src/components/use-block-display-information/index.js new file mode 100644 index 00000000000000..7ee0d350ffef8a --- /dev/null +++ b/packages/block-editor/src/components/use-block-display-information/index.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { store as blocksStore } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../../store'; + +/** @typedef {import('@wordpress/blocks').WPIcon} WPIcon */ + +/** + * Contains basic block's information for display reasons. + * + * @typedef {Object} WPBlockDisplayInformation + * + * @property {string} title Human-readable block type label. + * @property {WPIcon} icon Block type icon. + * @property {string} description A detailed block type description. + */ + +/** + * Hook used to try to find a matching block variation and return + * the appropriate information for display reasons. In order to + * to try to find a match we need to things: + * 1. Block's client id to extract it's current attributes. + * 2. A block variation should have set `isActive` prop to a proper function. + * + * If for any reason a block variaton match cannot be found, + * the returned information come from the Block Type. + * If no blockType is found with the provided clientId, returns null. + * + * @param {string} clientId Block's client id. + * @return {?WPBlockDisplayInformation} Block's display information, or `null` when the block or its type not found. + */ + +export default function useBlockDisplayInformation( clientId ) { + return useSelect( + ( select ) => { + if ( ! clientId ) return null; + const { getBlockName, getBlockAttributes } = select( + blockEditorStore + ); + const { getBlockType, getBlockVariations } = select( blocksStore ); + const blockName = getBlockName( clientId ); + const blockType = getBlockType( blockName ); + if ( ! blockType ) return null; + const variations = getBlockVariations( blockName ); + const blockTypeInfo = { + title: blockType.title, + icon: blockType.icon, + description: blockType.description, + }; + if ( ! variations?.length ) return blockTypeInfo; + const attributes = getBlockAttributes( clientId ); + const match = variations.find( ( variation ) => + variation.isActive?.( attributes, variation.attributes ) + ); + if ( ! match ) return blockTypeInfo; + return { + title: match.title || blockType.title, + icon: match.icon || blockType.icon, + description: match.description || blockType.description, + }; + }, + [ clientId ] + ); +} diff --git a/packages/block-editor/src/components/use-canvas-click-redirect/index.js b/packages/block-editor/src/components/use-canvas-click-redirect/index.js new file mode 100644 index 00000000000000..6b8280e95ab093 --- /dev/null +++ b/packages/block-editor/src/components/use-canvas-click-redirect/index.js @@ -0,0 +1,50 @@ +/** + * External dependencies + */ +import { overEvery, findLast } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useEffect } from '@wordpress/element'; +import { focus, isTextField, placeCaretAtHorizontalEdge } from '@wordpress/dom'; + +/** + * Given an element, returns true if the element is a tabbable text field, or + * false otherwise. + * + * @param {Element} element Element to test. + * + * @return {boolean} Whether element is a tabbable text field. + */ +const isTabbableTextField = overEvery( [ + isTextField, + focus.tabbable.isTabbableIndex, +] ); + +export function useCanvasClickRedirect( ref ) { + useEffect( () => { + function onMouseDown( event ) { + // Only handle clicks on the canvas, not the content. + if ( event.target !== ref.current ) { + return; + } + + const focusableNodes = focus.focusable.find( ref.current ); + const target = findLast( focusableNodes, isTabbableTextField ); + + if ( ! target ) { + return; + } + + placeCaretAtHorizontalEdge( target, true ); + event.preventDefault(); + } + + ref.current.addEventListener( 'mousedown', onMouseDown ); + + return () => { + ref.current.addEventListener( 'mousedown', onMouseDown ); + }; + }, [] ); +} diff --git a/packages/block-editor/src/components/use-editor-feature/index.js b/packages/block-editor/src/components/use-editor-feature/index.js index 692e4846e59e15..098ed231d8c186 100644 --- a/packages/block-editor/src/components/use-editor-feature/index.js +++ b/packages/block-editor/src/components/use-editor-feature/index.js @@ -6,6 +6,7 @@ import { get, isObject } from 'lodash'; /** * WordPress dependencies */ +import { store as blocksStore } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; /** @@ -78,7 +79,7 @@ export default function useEditorFeature( featurePath ) { 'core/block-editor' ); const settings = getSettings(); - const blockType = select( 'core/blocks' ).getBlockType( blockName ); + const blockType = select( blocksStore ).getBlockType( blockName ); let context = blockName; const selectors = get( blockType, [ @@ -86,7 +87,7 @@ export default function useEditorFeature( featurePath ) { '__experimentalSelector', ] ); if ( isObject( selectors ) ) { - const blockAttributes = getBlockAttributes( clientId ); + const blockAttributes = getBlockAttributes( clientId ) || {}; for ( const contextSelector in selectors ) { const { attributes } = selectors[ contextSelector ]; if ( blockAttributesMatch( blockAttributes, attributes ) ) { diff --git a/packages/block-editor/src/components/use-on-block-drop/index.js b/packages/block-editor/src/components/use-on-block-drop/index.js index eda98177563ef9..6f9cc801599f74 100644 --- a/packages/block-editor/src/components/use-on-block-drop/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/index.js @@ -23,6 +23,7 @@ export function parseDropEvent( event ) { srcClientIds: null, srcIndex: null, type: null, + blocks: null, }; if ( ! event.dataTransfer ) { @@ -44,12 +45,13 @@ export function parseDropEvent( event ) { /** * A function that returns an event handler function for block drop events. * - * @param {string} targetRootClientId The root client id where the block(s) will be inserted. - * @param {number} targetBlockIndex The index where the block(s) will be inserted. + * @param {string} targetRootClientId The root client id where the block(s) will be inserted. + * @param {number} targetBlockIndex The index where the block(s) will be inserted. * @param {Function} getBlockIndex A function that gets the index of a block. * @param {Function} getClientIdsOfDescendants A function that gets the client ids of descendant blocks. * @param {Function} moveBlocksToPosition A function that moves blocks. - * + * @param {Function} insertBlocks A function that inserts blocks. + * @param {Function} clearSelectedBlock A function that clears block selection. * @return {Function} The event handler for a block drop event. */ export function onBlockDrop( @@ -57,62 +59,69 @@ export function onBlockDrop( targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, - moveBlocksToPosition + moveBlocksToPosition, + insertBlocks, + clearSelectedBlock ) { return ( event ) => { const { srcRootClientId: sourceRootClientId, srcClientIds: sourceClientIds, type: dropType, + blocks, } = parseDropEvent( event ); - // If the user isn't dropping a block, return early. - if ( dropType !== 'block' ) { - return; + // If the user is inserting a block + if ( dropType === 'inserter' ) { + clearSelectedBlock(); + insertBlocks( blocks, targetBlockIndex, targetRootClientId, false ); } - const sourceBlockIndex = getBlockIndex( - sourceClientIds[ 0 ], - sourceRootClientId - ); - - // If the user is dropping to the same position, return early. - if ( - sourceRootClientId === targetRootClientId && - sourceBlockIndex === targetBlockIndex - ) { - return; - } + // If the user is moving a block + if ( dropType === 'block' ) { + const sourceBlockIndex = getBlockIndex( + sourceClientIds[ 0 ], + sourceRootClientId + ); - // If the user is attempting to drop a block within its own - // nested blocks, return early as this would create infinite - // recursion. - if ( - sourceClientIds.includes( targetRootClientId ) || - getClientIdsOfDescendants( sourceClientIds ).some( - ( id ) => id === targetRootClientId - ) - ) { - return; + // If the user is dropping to the same position, return early. + if ( + sourceRootClientId === targetRootClientId && + sourceBlockIndex === targetBlockIndex + ) { + return; + } + + // If the user is attempting to drop a block within its own + // nested blocks, return early as this would create infinite + // recursion. + if ( + sourceClientIds.includes( targetRootClientId ) || + getClientIdsOfDescendants( sourceClientIds ).some( + ( id ) => id === targetRootClientId + ) + ) { + return; + } + + const isAtSameLevel = sourceRootClientId === targetRootClientId; + const draggedBlockCount = sourceClientIds.length; + + // If the block is kept at the same level and moved downwards, + // subtract to take into account that the blocks being dragged + // were removed from the block list above the insertion point. + const insertIndex = + isAtSameLevel && sourceBlockIndex < targetBlockIndex + ? targetBlockIndex - draggedBlockCount + : targetBlockIndex; + + moveBlocksToPosition( + sourceClientIds, + sourceRootClientId, + targetRootClientId, + insertIndex + ); } - - const isAtSameLevel = sourceRootClientId === targetRootClientId; - const draggedBlockCount = sourceClientIds.length; - - // If the block is kept at the same level and moved downwards, - // subtract to take into account that the blocks being dragged - // were removed from the block list above the insertion point. - const insertIndex = - isAtSameLevel && sourceBlockIndex < targetBlockIndex - ? targetBlockIndex - draggedBlockCount - : targetBlockIndex; - - moveBlocksToPosition( - sourceClientIds, - sourceRootClientId, - targetRootClientId, - insertIndex - ); }; } @@ -209,6 +218,7 @@ export default function useOnBlockDrop( targetRootClientId, targetBlockIndex ) { insertBlocks, moveBlocksToPosition, updateBlockAttributes, + clearSelectedBlock, } = useDispatch( 'core/block-editor' ); return { @@ -217,7 +227,9 @@ export default function useOnBlockDrop( targetRootClientId, targetBlockIndex ) { targetBlockIndex, getBlockIndex, getClientIdsOfDescendants, - moveBlocksToPosition + moveBlocksToPosition, + insertBlocks, + clearSelectedBlock ), onFilesDrop: onFilesDrop( targetRootClientId, diff --git a/packages/block-editor/src/components/use-on-block-drop/test/index.js b/packages/block-editor/src/components/use-on-block-drop/test/index.js index beae8362fbfcb3..964a7cab74b06d 100644 --- a/packages/block-editor/src/components/use-on-block-drop/test/index.js +++ b/packages/block-editor/src/components/use-on-block-drop/test/index.js @@ -21,6 +21,7 @@ jest.mock( '@wordpress/blocks/src/api/raw-handling', () => ( { describe( 'parseDropEvent', () => { it( 'converts an event dataTransfer property from JSON to an object', () => { const rawDataTransfer = { + blocks: null, srcRootClientId: '123', srcClientIds: [ 'abc' ], srcIndex: 1, @@ -53,12 +54,14 @@ describe( 'parseDropEvent', () => { expect( parseDropEvent( event ) ).toEqual( { srcRootClientId: null, srcIndex: null, + blocks: null, ...rawDataTransfer, } ); } ); it( 'returns an object with null values if the event dataTransfer can not be parsed', () => { const expected = { + blocks: null, srcRootClientId: null, srcClientIds: null, srcIndex: null, @@ -77,6 +80,7 @@ describe( 'parseDropEvent', () => { it( 'returns an object with null values if the event has no dataTransfer property', () => { const expected = { + blocks: null, srcRootClientId: null, srcClientIds: null, srcIndex: null, diff --git a/packages/block-editor/src/components/writing-flow/index.js b/packages/block-editor/src/components/writing-flow/index.js index d48428a80cf9c9..e9cc4132ce79f6 100644 --- a/packages/block-editor/src/components/writing-flow/index.js +++ b/packages/block-editor/src/components/writing-flow/index.js @@ -1,13 +1,13 @@ /** * External dependencies */ -import { overEvery, find, findLast, reverse, first, last } from 'lodash'; +import { find, reverse, first, last } from 'lodash'; import classnames from 'classnames'; /** * WordPress dependencies */ -import { useRef, useEffect, useState } from '@wordpress/element'; +import { useRef, useEffect, createContext } from '@wordpress/element'; import { computeCaretRect, focus, @@ -26,8 +26,6 @@ import { TAB, isKeyboardEvent, ESCAPE, - ENTER, - SPACE, } from '@wordpress/keycodes'; import { useSelect, useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; @@ -44,24 +42,14 @@ import { getBlockClientId, } from '../../utils/dom'; import FocusCapture from './focus-capture'; +import useMultiSelection from './use-multi-selection'; + +export const SelectionStart = createContext(); function getComputedStyle( node ) { return node.ownerDocument.defaultView.getComputedStyle( node ); } -/** - * Given an element, returns true if the element is a tabbable text field, or - * false otherwise. - * - * @param {Element} element Element to test. - * - * @return {boolean} Whether element is a tabbable text field. - */ -const isTabbableTextField = overEvery( [ - isTextField, - focus.tabbable.isTabbableIndex, -] ); - /** * Returns true if the element should consider edge navigation upon a keyboard * event of the given directional key code, or false otherwise. @@ -203,12 +191,6 @@ function selector( select ) { hasMultiSelection, getBlockOrder, isNavigationMode, - hasBlockMovingClientId, - getBlockIndex, - getBlockRootClientId, - getClientIdsOfDescendants, - canInsertBlockType, - getBlockName, isSelectionEnabled, getBlockSelectionStart, isMultiSelecting, @@ -218,6 +200,7 @@ function selector( select ) { const selectedBlockClientId = getSelectedBlockClientId(); const selectionStartClientId = getMultiSelectedBlocksStartClientId(); const selectionEndClientId = getMultiSelectedBlocksEndClientId(); + const blocks = getBlockOrder(); return { selectedBlockClientId, @@ -231,14 +214,9 @@ function selector( select ) { selectedFirstClientId: getFirstMultiSelectedBlockClientId(), selectedLastClientId: getLastMultiSelectedBlockClientId(), hasMultiSelection: hasMultiSelection(), - blocks: getBlockOrder(), + firstBlock: first( blocks ), + lastBlock: last( blocks ), isNavigationMode: isNavigationMode(), - hasBlockMovingClientId, - getBlockIndex, - getBlockRootClientId, - getClientIdsOfDescendants, - canInsertBlockType, - getBlockName, isSelectionEnabled: isSelectionEnabled(), blockSelectionStart: getBlockSelectionStart(), isMultiSelecting: isMultiSelecting(), @@ -270,6 +248,8 @@ export default function WritingFlow( { children } ) { // browser behaviour across blocks. const verticalRect = useRef(); + const onSelectionStart = useMultiSelection( container ); + const { selectedBlockClientId, selectionStartClientId, @@ -278,29 +258,17 @@ export default function WritingFlow( { children } ) { selectedFirstClientId, selectedLastClientId, hasMultiSelection, - blocks, + firstBlock, + lastBlock, isNavigationMode, - hasBlockMovingClientId, isSelectionEnabled, blockSelectionStart, isMultiSelecting, - getBlockIndex, - getBlockRootClientId, - getClientIdsOfDescendants, - canInsertBlockType, - getBlockName, keepCaretInsideBlock, } = useSelect( selector, [] ); - const { - multiSelect, - selectBlock, - clearSelectedBlock, - setNavigationMode, - setBlockMovingClientId, - moveBlockToPosition, - } = useDispatch( 'core/block-editor' ); - - const [ canInsertMovingBlock, setCanInsertMovingBlock ] = useState( false ); + const { multiSelect, selectBlock, setNavigationMode } = useDispatch( + 'core/block-editor' + ); function onMouseDown( event ) { verticalRect.current = null; @@ -317,18 +285,6 @@ export default function WritingFlow( { children } ) { ) ) { setNavigationMode( false ); - setBlockMovingClientId( null ); - } else if ( - isNavigationMode && - hasBlockMovingClientId() && - getBlockClientId( event.target ) - ) { - setCanInsertMovingBlock( - canInsertBlockType( - getBlockName( hasBlockMovingClientId() ), - getBlockRootClientId( getBlockClientId( event.target ) ) - ) - ); } // Multi-select blocks when Shift+clicking. @@ -403,14 +359,25 @@ export default function WritingFlow( { children } ) { function onKeyDown( event ) { const { keyCode, target } = event; + + // Handle only if the event occurred within the same DOM hierarchy as + // the rendered container. This is used to distinguish between events + // which bubble through React's virtual event system from those which + // strictly occur in the DOM created by the component. + // + // The implication here is: If it's not desirable for a bubbled event to + // be considered by WritingFlow, it can be avoided by rendering to a + // distinct place in the DOM (e.g. using Slot/Fill). + if ( ! container.current.contains( target ) ) { + return; + } + const isUp = keyCode === UP; const isDown = keyCode === DOWN; const isLeft = keyCode === LEFT; const isRight = keyCode === RIGHT; const isTab = keyCode === TAB; const isEscape = keyCode === ESCAPE; - const isEnter = keyCode === ENTER; - const isSpace = keyCode === SPACE; const isReverse = isUp || isLeft; const isHorizontal = isLeft || isRight; const isVertical = isUp || isDown; @@ -422,100 +389,6 @@ export default function WritingFlow( { children } ) { const { ownerDocument } = container.current; const { defaultView } = ownerDocument; - // In navigation mode, tab and arrows navigate from block to block. - if ( isNavigationMode ) { - const navigateUp = ( isTab && isShift ) || isUp; - const navigateDown = ( isTab && ! isShift ) || isDown; - // Move out of current nesting level (no effect if at root level). - const navigateOut = isLeft; - // Move into next nesting level (no effect if the current block has no innerBlocks). - const navigateIn = isRight; - - let focusedBlockUid; - if ( navigateUp ) { - focusedBlockUid = selectionBeforeEndClientId; - } else if ( navigateDown ) { - focusedBlockUid = selectionAfterEndClientId; - } else if ( navigateOut ) { - focusedBlockUid = - getBlockRootClientId( selectedBlockClientId ) ?? - selectedBlockClientId; - } else if ( navigateIn ) { - focusedBlockUid = - getClientIdsOfDescendants( [ - selectedBlockClientId, - ] )[ 0 ] ?? selectedBlockClientId; - } - const startingBlockClientId = hasBlockMovingClientId(); - - if ( startingBlockClientId && focusedBlockUid ) { - setCanInsertMovingBlock( - canInsertBlockType( - getBlockName( startingBlockClientId ), - getBlockRootClientId( focusedBlockUid ) - ) - ); - } - if ( isEscape && startingBlockClientId ) { - setBlockMovingClientId( null ); - setCanInsertMovingBlock( false ); - } - if ( ( isEnter || isSpace ) && startingBlockClientId ) { - const sourceRoot = getBlockRootClientId( - startingBlockClientId - ); - const destRoot = getBlockRootClientId( selectedBlockClientId ); - const sourceBlockIndex = getBlockIndex( - startingBlockClientId, - sourceRoot - ); - let destinationBlockIndex = getBlockIndex( - selectedBlockClientId, - destRoot - ); - if ( - sourceBlockIndex < destinationBlockIndex && - sourceRoot === destRoot - ) { - destinationBlockIndex -= 1; - } - moveBlockToPosition( - startingBlockClientId, - sourceRoot, - destRoot, - destinationBlockIndex - ); - selectBlock( startingBlockClientId ); - setBlockMovingClientId( null ); - } - if ( navigateDown || navigateUp || navigateOut || navigateIn ) { - if ( focusedBlockUid ) { - event.preventDefault(); - selectBlock( focusedBlockUid ); - } else if ( isTab && selectedBlockClientId ) { - const wrapper = getBlockDOMNode( - selectedBlockClientId, - ownerDocument - ); - let nextTabbable; - - if ( navigateDown ) { - nextTabbable = focus.tabbable.findNext( wrapper ); - } else { - nextTabbable = focus.tabbable.findPrevious( wrapper ); - } - - if ( nextTabbable ) { - event.preventDefault(); - nextTabbable.focus(); - clearSelectedBlock(); - } - } - } - - return; - } - // In Edit mode, Tab should focus the first tabbable element after the // content, which is normally the sidebar (with block controls) and // Shift+Tab should focus the first tabbable element before the content, @@ -552,21 +425,6 @@ export default function WritingFlow( { children } ) { } else if ( isEscape ) { setNavigationMode( true ); } - } else if ( - hasMultiSelection && - isTab && - target === multiSelectionContainer.current - ) { - // See comment above. - noCapture.current = true; - - if ( isShift ) { - focusCaptureBeforeRef.current.focus(); - } else { - focusCaptureAfterRef.current.focus(); - } - - return; } // When presing any key other than up or down, the initial vertical @@ -601,7 +459,7 @@ export default function WritingFlow( { children } ) { ? entirelySelected.current : isEntirelySelected( target ) ) { - multiSelect( first( blocks ), last( blocks ) ); + multiSelect( firstBlock, lastBlock ); event.preventDefault(); } @@ -635,19 +493,14 @@ export default function WritingFlow( { children } ) { // Ensure that there is a target block. ( ( isReverse && selectionBeforeEndClientId ) || ( ! isReverse && selectionAfterEndClientId ) ) && - ( hasMultiSelection || - ( isTabbableEdge( target, isReverse ) && - isNavEdge( target, isReverse ) ) ) + isTabbableEdge( target, isReverse ) && + isNavEdge( target, isReverse ) ) { // Shift key is down, and there is multi selection or we're at // the end of the current block. expandSelection( isReverse ); event.preventDefault(); } - } else if ( hasMultiSelection ) { - // Moving from block multi-selection to single block selection - moveSelection( isReverse ); - event.preventDefault(); } else if ( isVertical && isVerticalEdge( target, isReverse ) && @@ -684,11 +537,32 @@ export default function WritingFlow( { children } ) { } } - function focusLastTextField() { - const focusableNodes = focus.focusable.find( container.current ); - const target = findLast( focusableNodes, isTabbableTextField ); - if ( target ) { - placeCaretAtHorizontalEdge( target, true ); + function onMultiSelectKeyDown( event ) { + const { keyCode, shiftKey } = event; + const isUp = keyCode === UP; + const isDown = keyCode === DOWN; + const isLeft = keyCode === LEFT; + const isRight = keyCode === RIGHT; + const isReverse = isUp || isLeft; + const isHorizontal = isLeft || isRight; + const isVertical = isUp || isDown; + const isNav = isHorizontal || isVertical; + + if ( keyCode === TAB ) { + // Disable focus capturing on the focus capture element, so it + // doesn't refocus this element and so it allows default behaviour + // (moving focus to the next tabbable element). + noCapture.current = true; + + if ( shiftKey ) { + focusCaptureBeforeRef.current.focus(); + } else { + focusCaptureAfterRef.current.focus(); + } + } else if ( isNav ) { + const action = shiftKey ? expandSelection : moveSelection; + action( isReverse ); + event.preventDefault(); } } @@ -700,15 +574,13 @@ export default function WritingFlow( { children } ) { const className = classnames( 'block-editor-writing-flow', { 'is-navigate-mode': isNavigationMode, - 'is-block-moving-mode': !! hasBlockMovingClientId(), - 'can-insert-moving-block': canInsertMovingBlock, } ); // Disable reason: Wrapper itself is non-interactive, but must capture // bubbling events from children to determine focus transition intents. /* eslint-disable jsx-a11y/no-static-element-interactions */ return ( - <> + +
-
{ children }
-
- + ); /* eslint-enable jsx-a11y/no-static-element-interactions */ } diff --git a/packages/block-editor/src/components/writing-flow/style.scss b/packages/block-editor/src/components/writing-flow/style.scss deleted file mode 100644 index 422b378f2e8e2e..00000000000000 --- a/packages/block-editor/src/components/writing-flow/style.scss +++ /dev/null @@ -1,8 +0,0 @@ -.block-editor-writing-flow { - display: flex; - flex-direction: column; -} - -.block-editor-writing-flow__click-redirect { - cursor: text; -} diff --git a/packages/block-editor/src/components/block-list/use-multi-selection.js b/packages/block-editor/src/components/writing-flow/use-multi-selection.js similarity index 100% rename from packages/block-editor/src/components/block-list/use-multi-selection.js rename to packages/block-editor/src/components/writing-flow/use-multi-selection.js diff --git a/packages/block-editor/src/hooks/border-radius.js b/packages/block-editor/src/hooks/border-radius.js new file mode 100644 index 00000000000000..f9d90b444b640c --- /dev/null +++ b/packages/block-editor/src/hooks/border-radius.js @@ -0,0 +1,79 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; +import { RangeControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import useEditorFeature from '../components/use-editor-feature'; +import { BORDER_SUPPORT_KEY } from './border'; +import { cleanEmptyObject } from './utils'; + +const MIN_BORDER_RADIUS_VALUE = 0; +const MAX_BORDER_RADIUS_VALUE = 50; + +/** + * Inspector control panel containing the border radius related configuration. + * + * @param {Object} props Block properties. + * @return {WPElement} Border radius edit element. + */ +export function BorderRadiusEdit( props ) { + const { + attributes: { style }, + setAttributes, + } = props; + + if ( useIsBorderRadiusDisabled( props ) ) { + return null; + } + + const onChange = ( newRadius ) => { + const newStyle = { + ...style, + border: { + ...style?.border, + radius: newRadius, + }, + }; + + setAttributes( { style: cleanEmptyObject( newStyle ) } ); + }; + + return ( + + ); +} + +/** + * Determines if there is border radius support. + * + * @param {string|Object} blockType Block name or Block Type object. + * @return {boolean} Whether there is support. + */ +export function hasBorderRadiusSupport( blockType ) { + const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); + return true === support || ( support && !! support.radius ); +} + +/** + * Custom hook that checks if border radius settings have been disabled. + * + * @param {string} name The name of the block. + * @return {boolean} Whether border radius setting is disabled. + */ +export function useIsBorderRadiusDisabled( { name: blockName } = {} ) { + const isDisabled = ! useEditorFeature( 'border.customRadius' ); + return ! hasBorderRadiusSupport( blockName ) || isDisabled; +} diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js new file mode 100644 index 00000000000000..228abd4083bfa5 --- /dev/null +++ b/packages/block-editor/src/hooks/border.js @@ -0,0 +1,67 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; +import { PanelBody } from '@wordpress/components'; +import { Platform } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import InspectorControls from '../components/inspector-controls'; +import { BorderRadiusEdit, useIsBorderRadiusDisabled } from './border-radius'; + +export const BORDER_SUPPORT_KEY = '__experimentalBorder'; + +export function BorderPanel( props ) { + const isDisabled = useIsBorderDisabled( props ); + const isSupported = hasBorderSupport( props.name ); + + if ( isDisabled || ! isSupported ) { + return null; + } + + return ( + + + + + + ); +} + +/** + * Determine whether there is block support for borders. + * + * @param {string} blockName Block name. + * @return {boolean} Whether there is support. + */ +export function hasBorderSupport( blockName ) { + if ( Platform.OS !== 'web' ) { + return false; + } + + const support = getBlockSupport( blockName, BORDER_SUPPORT_KEY ); + + // Further border properties to be added in future iterations. + // e.g. support && ( support.radius || support.width || support.style ) + return true === support || ( support && support.radius ); +} + +/** + * Determines whether there is any block support for borders e.g. border radius, + * style, width etc. + * + * @param {Object} props Block properties. + * @return {boolean} If border support is completely disabled. + */ +const useIsBorderDisabled = ( props = {} ) => { + // Further border properties to be added in future iterations. + // e.g. const configs = [ + // useIsBorderRadiusDisabled( props ), + // useIsBorderWidthDisabled( props ), + // ]; + const configs = [ useIsBorderRadiusDisabled( props ) ]; + return configs.filter( Boolean ).length === configs.length; +}; diff --git a/packages/block-editor/src/hooks/font-appearance.js b/packages/block-editor/src/hooks/font-appearance.js index bbad86a20f133b..52661ce0364118 100644 --- a/packages/block-editor/src/hooks/font-appearance.js +++ b/packages/block-editor/src/hooks/font-appearance.js @@ -11,10 +11,14 @@ import useEditorFeature from '../components/use-editor-feature'; import { cleanEmptyObject } from './utils'; /** - * Key within block settings' support array indicating support for font - * appearance options e.g. font weight and style. + * Key within block settings' support array indicating support for font style. */ -export const FONT_APPEARANCE_SUPPORT_KEY = '__experimentalFontAppearance'; +export const FONT_STYLE_SUPPORT_KEY = '__experimentalFontStyle'; + +/** + * Key within block settings' support array indicating support for font weight. + */ +export const FONT_WEIGHT_SUPPORT_KEY = '__experimentalFontWeight'; /** * Inspector control panel containing the font appearance options. @@ -28,11 +32,10 @@ export function FontAppearanceEdit( props ) { setAttributes, } = props; - const fontStyles = useEditorFeature( 'typography.fontStyles' ); - const fontWeights = useEditorFeature( 'typography.fontWeights' ); - const isDisabled = useIsFontAppearanceDisabled( props ); + const hasFontStyles = ! useIsFontStyleDisabled( props ); + const hasFontWeights = ! useIsFontWeightDisabled( props ); - if ( isDisabled ) { + if ( ! hasFontStyles && ! hasFontWeights ) { return null; } @@ -42,41 +45,66 @@ export function FontAppearanceEdit( props ) { ...style, typography: { ...style?.typography, - ...newStyles, + fontStyle: newStyles.fontStyle, + fontWeight: newStyles.fontWeight, }, } ), } ); }; - const currentSelection = { - fontStyle: style?.typography?.fontStyle, - fontWeight: style?.typography?.fontWeight, - }; + const fontStyle = style?.typography?.fontStyle; + + const fontWeight = style?.typography?.fontWeight; return ( ); } /** - * Checks if font appearance support has been disabled. + * Checks if font style support has been disabled either by not opting in for + * support or by failing to provide preset styles. * * @param {Object} props Block properties. * @param {string} props.name Name for the block type. + * @return {boolean} Whether font style support has been disabled. + */ +export function useIsFontStyleDisabled( { name: blockName } = {} ) { + const styleSupport = hasBlockSupport( blockName, FONT_STYLE_SUPPORT_KEY ); + const hasFontStyles = useEditorFeature( 'typography.customFontStyle' ); + + return ! styleSupport || ! hasFontStyles; +} + +/** + * Checks if font weight support has been disabled either by not opting in for + * support or by failing to provide preset weights. + * + * @param {Object} props Block properties. + * @param {string} props.name Name for the block type. + * @return {boolean} Whether font weight support has been disabled. + */ +export function useIsFontWeightDisabled( { name: blockName } = {} ) { + const weightSupport = hasBlockSupport( blockName, FONT_WEIGHT_SUPPORT_KEY ); + const hasFontWeights = useEditorFeature( 'typography.customFontWeight' ); + + return ! weightSupport || ! hasFontWeights; +} + +/** + * Checks if font appearance support has been disabled. + * + * @param {Object} props Block properties. * @return {boolean} Whether font appearance support has been disabled. */ -export function useIsFontAppearanceDisabled( { name: blockName } = {} ) { - const notSupported = ! hasBlockSupport( - blockName, - FONT_APPEARANCE_SUPPORT_KEY - ); - const fontStyles = useEditorFeature( 'typography.fontStyles' ); - const fontWeights = useEditorFeature( 'typography.fontWeights' ); - const hasFontAppearance = !! fontStyles?.length && !! fontWeights?.length; +export function useIsFontAppearanceDisabled( props ) { + const stylesDisabled = useIsFontStyleDisabled( props ); + const weightsDisabled = useIsFontWeightDisabled( props ); - return notSupported || ! hasFontAppearance; + return stylesDisabled && weightsDisabled; } diff --git a/packages/block-editor/src/hooks/font-family.js b/packages/block-editor/src/hooks/font-family.js index 89fa784abf6e10..859606e9cffb1c 100644 --- a/packages/block-editor/src/hooks/font-family.js +++ b/packages/block-editor/src/hooks/font-family.js @@ -20,9 +20,12 @@ export const FONT_FAMILY_SUPPORT_KEY = '__experimentalFontFamily'; const getFontFamilyFromAttributeValue = ( fontFamilies, value ) => { const attributeParsed = /var:preset\|font-family\|(.+)/.exec( value ); if ( attributeParsed && attributeParsed[ 1 ] ) { - return find( fontFamilies, ( { slug } ) => { + const fontFamilyObject = find( fontFamilies, ( { slug } ) => { return slug === attributeParsed[ 1 ]; - } ).fontFamily; + } ); + if ( fontFamilyObject ) { + return fontFamilyObject.fontFamily; + } } return value; }; diff --git a/packages/block-editor/src/hooks/padding.js b/packages/block-editor/src/hooks/padding.js index a83d6aff730f00..4f2d5ea51754c2 100644 --- a/packages/block-editor/src/hooks/padding.js +++ b/packages/block-editor/src/hooks/padding.js @@ -10,7 +10,7 @@ import { __experimentalBoxControl as BoxControl } from '@wordpress/components'; * Internal dependencies */ import { cleanEmptyObject } from './utils'; -import { useCustomUnits } from '../components/unit-control'; +import useEditorFeature from '../components/use-editor-feature'; export const SPACING_SUPPORT_KEY = 'spacing'; @@ -33,7 +33,11 @@ export function PaddingEdit( props ) { setAttributes, } = props; - const units = useCustomUnits(); + const customUnits = useEditorFeature( 'spacing.units' ); + const units = customUnits?.map( ( unit ) => ( { + value: unit, + label: unit, + } ) ); if ( ! hasPaddingSupport( blockName ) ) { return null; diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index d533390250cb1b..125a850836f103 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -16,6 +16,7 @@ import { createHigherOrderComponent } from '@wordpress/compose'; /** * Internal dependencies */ +import { BORDER_SUPPORT_KEY, BorderPanel } from './border'; import { COLOR_SUPPORT_KEY, ColorEdit } from './color'; import { TypographyPanel, TYPOGRAPHY_SUPPORT_KEYS } from './typography'; import { SPACING_SUPPORT_KEY, PaddingEdit } from './padding'; @@ -23,6 +24,7 @@ import SpacingPanelControl from '../components/spacing-panel-control'; const styleSupportKeys = [ ...TYPOGRAPHY_SUPPORT_KEYS, + BORDER_SUPPORT_KEY, COLOR_SUPPORT_KEY, SPACING_SUPPORT_KEY, ]; @@ -156,6 +158,7 @@ export const withBlockControls = createHigherOrderComponent( return [ , + , , , hasSpacingSupport && ( diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index 533694be7282ed..62c5a97b6e211e 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -17,9 +17,11 @@ describe( 'getInlineStyles', () => { getInlineStyles( { color: { text: 'red', background: 'black' }, typography: { lineHeight: 1.5, fontSize: 10 }, + border: { radius: 10 }, } ) ).toEqual( { backgroundColor: 'black', + borderRadius: 10, color: 'red', lineHeight: 1.5, fontSize: 10, diff --git a/packages/block-editor/src/hooks/text-decoration.js b/packages/block-editor/src/hooks/text-decoration.js index 04aaa57ae76656..d2433277e8d4b8 100644 --- a/packages/block-editor/src/hooks/text-decoration.js +++ b/packages/block-editor/src/hooks/text-decoration.js @@ -27,18 +27,12 @@ export function TextDecorationEdit( props ) { attributes: { style }, setAttributes, } = props; - const textDecorations = useEditorFeature( 'typography.textDecorations' ); const isDisabled = useIsTextDecorationDisabled( props ); if ( isDisabled ) { return null; } - const textDecoration = getTextDecorationFromAttributeValue( - textDecorations, - style?.typography?.textDecoration - ); - function onChange( newDecoration ) { setAttributes( { style: cleanEmptyObject( { @@ -53,8 +47,7 @@ export function TextDecorationEdit( props ) { return ( ); @@ -71,28 +64,9 @@ export function useIsTextDecorationDisabled( { name: blockName } = {} ) { blockName, TEXT_DECORATION_SUPPORT_KEY ); - const textDecorations = useEditorFeature( 'typography.textDecorations' ); - const hasTextDecorations = !! textDecorations?.length; + const hasTextDecoration = useEditorFeature( + 'typography.customTextDecorations' + ); - return notSupported || ! hasTextDecorations; + return notSupported || ! hasTextDecoration; } - -/** - * Extracts the current text decoration selection, if available, from the CSS - * variable set as the `styles.typography.textDecoration` attribute. - * - * @param {Array} textDecorations Available text decorations as defined in theme.json. - * @param {string} value Attribute value in `styles.typography.textDecoration` - * @return {string} Actual text decoration value - */ -const getTextDecorationFromAttributeValue = ( textDecorations, value ) => { - const attributeParsed = /var:preset\|text-decoration\|(.+)/.exec( value ); - - if ( attributeParsed && attributeParsed[ 1 ] ) { - return textDecorations.find( - ( { slug } ) => slug === attributeParsed[ 1 ] - )?.slug; - } - - return value; -}; diff --git a/packages/block-editor/src/hooks/text-transform.js b/packages/block-editor/src/hooks/text-transform.js index 8d7e807584cb09..fd317861f0e717 100644 --- a/packages/block-editor/src/hooks/text-transform.js +++ b/packages/block-editor/src/hooks/text-transform.js @@ -27,18 +27,12 @@ export function TextTransformEdit( props ) { attributes: { style }, setAttributes, } = props; - const textTransforms = useEditorFeature( 'typography.textTransforms' ); const isDisabled = useIsTextTransformDisabled( props ); if ( isDisabled ) { return null; } - const textTransform = getTextTransformFromAttributeValue( - textTransforms, - style?.typography?.textTransform - ); - function onChange( newTransform ) { setAttributes( { style: cleanEmptyObject( { @@ -53,8 +47,7 @@ export function TextTransformEdit( props ) { return ( ); @@ -71,28 +64,8 @@ export function useIsTextTransformDisabled( { name: blockName } = {} ) { blockName, TEXT_TRANSFORM_SUPPORT_KEY ); - const textTransforms = useEditorFeature( 'typography.textTransforms' ); - const hasTextTransforms = !! textTransforms?.length; - + const hasTextTransforms = useEditorFeature( + 'typography.customTextTransforms' + ); return notSupported || ! hasTextTransforms; } - -/** - * Extracts the current text transform selection, if available, from the CSS - * variable set as the `styles.typography.textTransform` attribute. - * - * @param {Array} textTransforms Available text transforms as defined in theme.json. - * @param {string} value Attribute value in `styles.typography.textTransform` - * @return {string} Actual text transform value - */ -const getTextTransformFromAttributeValue = ( textTransforms, value ) => { - const attributeParsed = /var:preset\|text-transform\|(.+)/.exec( value ); - - if ( attributeParsed && attributeParsed[ 1 ] ) { - return textTransforms.find( - ( { slug } ) => slug === attributeParsed[ 1 ] - )?.slug; - } - - return value; -}; diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index db17bd98aaca9f..dfc27748acb00f 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -18,7 +18,8 @@ import { useIsLineHeightDisabled, } from './line-height'; import { - FONT_APPEARANCE_SUPPORT_KEY, + FONT_STYLE_SUPPORT_KEY, + FONT_WEIGHT_SUPPORT_KEY, FontAppearanceEdit, useIsFontAppearanceDisabled, } from './font-appearance'; @@ -43,8 +44,9 @@ import { export const TYPOGRAPHY_SUPPORT_KEYS = [ LINE_HEIGHT_SUPPORT_KEY, - FONT_APPEARANCE_SUPPORT_KEY, FONT_SIZE_SUPPORT_KEY, + FONT_STYLE_SUPPORT_KEY, + FONT_WEIGHT_SUPPORT_KEY, FONT_FAMILY_SUPPORT_KEY, TEXT_DECORATION_SUPPORT_KEY, TEXT_TRANSFORM_SUPPORT_KEY, diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js index 1e12ca9f4acaed..e8a1ebcd480c2b 100644 --- a/packages/block-editor/src/index.js +++ b/packages/block-editor/src/index.js @@ -1,10 +1,7 @@ /** * WordPress dependencies */ -import '@wordpress/blocks'; import '@wordpress/rich-text'; -import '@wordpress/keyboard-shortcuts'; -import '@wordpress/notices'; /** * Internal dependencies diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index 48eef28b2f25d1..cf69468a598a29 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { castArray, first, last, some } from 'lodash'; +import { castArray, findKey, first, last, some } from 'lodash'; /** * WordPress dependencies @@ -9,12 +9,22 @@ import { castArray, first, last, some } from 'lodash'; import { cloneBlock, createBlock, + doBlocksMatchTemplate, + getBlockType, getDefaultBlockName, hasBlockSupport, + switchToBlockType, + synchronizeBlocksWithTemplate, } from '@wordpress/blocks'; import { speak } from '@wordpress/a11y'; -import { __ } from '@wordpress/i18n'; +import { __, _n, sprintf } from '@wordpress/i18n'; import { controls } from '@wordpress/data'; +import { create, insert, remove, toHTMLString } from '@wordpress/rich-text'; + +/** + * Internal dependencies + */ +import { __unstableMarkAutomaticChangeFinalControl } from '../store/controls'; /** * Generator which will yield a default block insert action if there @@ -38,14 +48,50 @@ function* ensureDefaultBlock() { * content reflected as an edit in state. * * @param {Array} blocks Array of blocks. - * - * @return {Object} Action object. */ -export function resetBlocks( blocks ) { - return { +export function* resetBlocks( blocks ) { + yield { type: 'RESET_BLOCKS', blocks, }; + return yield* validateBlocksToTemplate( blocks ); +} + +/** + * Block validity is a function of blocks state (at the point of a + * reset) and the template setting. As a compromise to its placement + * across distinct parts of state, it is implemented here as a side- + * effect of the block reset action. + * + * @param {Array} blocks Array of blocks. + */ +export function* validateBlocksToTemplate( blocks ) { + const template = yield controls.select( + 'core/block-editor', + 'getTemplate' + ); + const templateLock = yield controls.select( + 'core/block-editor', + 'getTemplateLock' + ); + + // Unlocked templates are considered always valid because they act + // as default values only. + const isBlocksValidToTemplate = + ! template || + templateLock !== 'all' || + doBlocksMatchTemplate( blocks, template ); + + // Update if validity has changed. + const isValidTemplate = yield controls.select( + 'core/block-editor', + 'isValidTemplate' + ); + + if ( isBlocksValidToTemplate !== isValidTemplate ) { + yield setTemplateValidity( isBlocksValidToTemplate ); + return isBlocksValidToTemplate; + } } /** @@ -211,15 +257,27 @@ export function stopMultiSelect() { * * @param {string} start First block of the multi selection. * @param {string} end Last block of the multiselection. - * - * @return {Object} Action object. */ -export function multiSelect( start, end ) { - return { +export function* multiSelect( start, end ) { + yield { type: 'MULTI_SELECT', start, end, }; + + const blockCount = yield controls.select( + 'core/block-editor', + 'getSelectedBlockCount' + ); + + speak( + sprintf( + /* translators: %s: number of selected blocks */ + _n( '%s block selected.', '%s blocks selected.', blockCount ), + blockCount + ), + 'assertive' + ); } /** @@ -592,10 +650,18 @@ export function setTemplateValidity( isValid ) { * * @return {Object} Action object. */ -export function synchronizeTemplate() { - return { +export function* synchronizeTemplate() { + yield { type: 'SYNCHRONIZE_TEMPLATE', }; + const blocks = yield controls.select( 'core/block-editor', 'getBlocks' ); + const template = yield controls.select( + 'core/block-editor', + 'getTemplate' + ); + const updatedBlockList = synchronizeBlocksWithTemplate( blocks, template ); + + return yield resetBlocks( updatedBlockList ); } /** @@ -603,14 +669,165 @@ export function synchronizeTemplate() { * * @param {string} firstBlockClientId Client ID of the first block to merge. * @param {string} secondBlockClientId Client ID of the second block to merge. - * - * @return {Object} Action object. */ -export function mergeBlocks( firstBlockClientId, secondBlockClientId ) { - return { +export function* mergeBlocks( firstBlockClientId, secondBlockClientId ) { + const blocks = [ firstBlockClientId, secondBlockClientId ]; + yield { type: 'MERGE_BLOCKS', - blocks: [ firstBlockClientId, secondBlockClientId ], + blocks, }; + + const [ clientIdA, clientIdB ] = blocks; + const blockA = yield controls.select( + 'core/block-editor', + 'getBlock', + clientIdA + ); + const blockAType = getBlockType( blockA.name ); + + // Only focus the previous block if it's not mergeable + if ( ! blockAType.merge ) { + yield selectBlock( blockA.clientId ); + return; + } + + const blockB = yield controls.select( + 'core/block-editor', + 'getBlock', + clientIdB + ); + const blockBType = getBlockType( blockB.name ); + const { clientId, attributeKey, offset } = yield controls.select( + 'core/block-editor', + 'getSelectionStart' + ); + const selectedBlockType = clientId === clientIdA ? blockAType : blockBType; + const attributeDefinition = selectedBlockType.attributes[ attributeKey ]; + const canRestoreTextSelection = + ( clientId === clientIdA || clientId === clientIdB ) && + attributeKey !== undefined && + offset !== undefined && + // We cannot restore text selection if the RichText identifier + // is not a defined block attribute key. This can be the case if the + // fallback intance ID is used to store selection (and no RichText + // identifier is set), or when the identifier is wrong. + !! attributeDefinition; + + if ( ! attributeDefinition ) { + if ( typeof attributeKey === 'number' ) { + window.console.error( + `RichText needs an identifier prop that is the block attribute key of the attribute it controls. Its type is expected to be a string, but was ${ typeof attributeKey }` + ); + } else { + window.console.error( + 'The RichText identifier prop does not match any attributes defined by the block.' + ); + } + } + + // A robust way to retain selection position through various transforms + // is to insert a special character at the position and then recover it. + const START_OF_SELECTED_AREA = '\u0086'; + + // Clone the blocks so we don't insert the character in a "live" block. + const cloneA = cloneBlock( blockA ); + const cloneB = cloneBlock( blockB ); + + if ( canRestoreTextSelection ) { + const selectedBlock = clientId === clientIdA ? cloneA : cloneB; + const html = selectedBlock.attributes[ attributeKey ]; + const { + multiline: multilineTag, + __unstableMultilineWrapperTags: multilineWrapperTags, + __unstablePreserveWhiteSpace: preserveWhiteSpace, + } = attributeDefinition; + const value = insert( + create( { + html, + multilineTag, + multilineWrapperTags, + preserveWhiteSpace, + } ), + START_OF_SELECTED_AREA, + offset, + offset + ); + + selectedBlock.attributes[ attributeKey ] = toHTMLString( { + value, + multilineTag, + preserveWhiteSpace, + } ); + } + + // We can only merge blocks with similar types + // thus, we transform the block to merge first + const blocksWithTheSameType = + blockA.name === blockB.name + ? [ cloneB ] + : switchToBlockType( cloneB, blockA.name ); + + // If the block types can not match, do nothing + if ( ! blocksWithTheSameType || ! blocksWithTheSameType.length ) { + return; + } + + // Calling the merge to update the attributes and remove the block to be merged + const updatedAttributes = blockAType.merge( + cloneA.attributes, + blocksWithTheSameType[ 0 ].attributes + ); + + if ( canRestoreTextSelection ) { + const newAttributeKey = findKey( + updatedAttributes, + ( v ) => + typeof v === 'string' && + v.indexOf( START_OF_SELECTED_AREA ) !== -1 + ); + const convertedHtml = updatedAttributes[ newAttributeKey ]; + const { + multiline: multilineTag, + __unstableMultilineWrapperTags: multilineWrapperTags, + __unstablePreserveWhiteSpace: preserveWhiteSpace, + } = blockAType.attributes[ newAttributeKey ]; + const convertedValue = create( { + html: convertedHtml, + multilineTag, + multilineWrapperTags, + preserveWhiteSpace, + } ); + const newOffset = convertedValue.text.indexOf( START_OF_SELECTED_AREA ); + const newValue = remove( convertedValue, newOffset, newOffset + 1 ); + const newHtml = toHTMLString( { + value: newValue, + multilineTag, + preserveWhiteSpace, + } ); + + updatedAttributes[ newAttributeKey ] = newHtml; + + yield selectionChange( + blockA.clientId, + newAttributeKey, + newOffset, + newOffset + ); + } + + yield* replaceBlocks( + [ blockA.clientId, blockB.clientId ], + [ + { + ...blockA, + attributes: { + ...blockA.attributes, + ...updatedAttributes, + }, + }, + ...blocksWithTheSameType.slice( 1 ), + ] + ); } /** @@ -907,11 +1124,16 @@ export function __unstableMarkNextChangeAsNotPersistent() { * after the change was made, and any actions that are a consequence of it, so * it is recommended to be called at the next idle period to ensure all * selection changes have been recorded. - * - * @return {Object} Action object. */ -export function __unstableMarkAutomaticChange() { - return { type: 'MARK_AUTOMATIC_CHANGE' }; +export function* __unstableMarkAutomaticChange() { + yield { type: 'MARK_AUTOMATIC_CHANGE' }; + yield __unstableMarkAutomaticChangeFinalControl(); +} + +export function __unstableMarkAutomaticChangeFinal() { + return { + type: 'MARK_AUTOMATIC_CHANGE_FINAL', + }; } /** diff --git a/packages/block-editor/src/store/controls.js b/packages/block-editor/src/store/controls.js index 83b4f453425462..84d97f38654bb0 100644 --- a/packages/block-editor/src/store/controls.js +++ b/packages/block-editor/src/store/controls.js @@ -1,9 +1,34 @@ +/** + * WordPress dependencies + */ +import { createRegistryControl } from '@wordpress/data'; + +export const __unstableMarkAutomaticChangeFinalControl = function () { + return { + type: 'MARK_AUTOMATIC_CHANGE_FINAL_CONTROL', + }; +}; + const controls = { SLEEP( { duration } ) { return new Promise( ( resolve ) => { setTimeout( resolve, duration ); } ); }, + + MARK_AUTOMATIC_CHANGE_FINAL_CONTROL: createRegistryControl( + ( registry ) => () => { + const { + requestIdleCallback = ( callback ) => + setTimeout( callback, 100 ), + } = window; + requestIdleCallback( () => + registry + .dispatch( 'core/block-editor' ) + .__unstableMarkAutomaticChangeFinal() + ); + } + ), }; export default controls; diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js index 8bbcbff537dbb8..ba608d34a35f87 100644 --- a/packages/block-editor/src/store/defaults.js +++ b/packages/block-editor/src/store/defaults.js @@ -20,7 +20,6 @@ export const PREFERENCES_DEFAULTS = { * @property {boolean} hasFixedToolbar Whether or not the editor toolbar is fixed * @property {boolean} focusMode Whether the focus mode is enabled or not * @property {Array} styles Editor Styles - * @property {boolean} isRTL Whether the editor is in RTL mode * @property {boolean} keepCaretInsideBlock Whether caret should move between blocks in edit mode * @property {string} bodyPlaceholder Empty post placeholder * @property {string} titlePlaceholder Empty title placeholder diff --git a/packages/block-editor/src/store/effects.js b/packages/block-editor/src/store/effects.js deleted file mode 100644 index 5142020ffffa1e..00000000000000 --- a/packages/block-editor/src/store/effects.js +++ /dev/null @@ -1,256 +0,0 @@ -/** - * External dependencies - */ -import { findKey } from 'lodash'; - -/** - * WordPress dependencies - */ -import { speak } from '@wordpress/a11y'; -import { - getBlockType, - doBlocksMatchTemplate, - switchToBlockType, - synchronizeBlocksWithTemplate, - cloneBlock, -} from '@wordpress/blocks'; -import { _n, sprintf } from '@wordpress/i18n'; -import { create, toHTMLString, insert, remove } from '@wordpress/rich-text'; - -/** - * Internal dependencies - */ -import { - replaceBlocks, - selectBlock, - setTemplateValidity, - resetBlocks, - selectionChange, -} from './actions'; -import { - getBlock, - getBlocks, - getSelectedBlockCount, - getTemplateLock, - getTemplate, - isValidTemplate, - getSelectionStart, -} from './selectors'; - -/** - * Block validity is a function of blocks state (at the point of a - * reset) and the template setting. As a compromise to its placement - * across distinct parts of state, it is implemented here as a side- - * effect of the block reset action. - * - * @param {Object} action RESET_BLOCKS action. - * @param {Object} store Store instance. - * - * @return {?Object} New validity set action if validity has changed. - */ -export function validateBlocksToTemplate( action, store ) { - const state = store.getState(); - const template = getTemplate( state ); - const templateLock = getTemplateLock( state ); - - // Unlocked templates are considered always valid because they act - // as default values only. - const isBlocksValidToTemplate = - ! template || - templateLock !== 'all' || - doBlocksMatchTemplate( action.blocks, template ); - - // Update if validity has changed. - if ( isBlocksValidToTemplate !== isValidTemplate( state ) ) { - return setTemplateValidity( isBlocksValidToTemplate ); - } -} - -export default { - MERGE_BLOCKS( action, store ) { - const { dispatch } = store; - const state = store.getState(); - const [ clientIdA, clientIdB ] = action.blocks; - const blockA = getBlock( state, clientIdA ); - const blockAType = getBlockType( blockA.name ); - - // Only focus the previous block if it's not mergeable - if ( ! blockAType.merge ) { - dispatch( selectBlock( blockA.clientId ) ); - return; - } - - const blockB = getBlock( state, clientIdB ); - const blockBType = getBlockType( blockB.name ); - const { clientId, attributeKey, offset } = getSelectionStart( state ); - const selectedBlockType = - clientId === clientIdA ? blockAType : blockBType; - const attributeDefinition = - selectedBlockType.attributes[ attributeKey ]; - const canRestoreTextSelection = - ( clientId === clientIdA || clientId === clientIdB ) && - attributeKey !== undefined && - offset !== undefined && - // We cannot restore text selection if the RichText identifier - // is not a defined block attribute key. This can be the case if the - // fallback intance ID is used to store selection (and no RichText - // identifier is set), or when the identifier is wrong. - !! attributeDefinition; - - if ( ! attributeDefinition ) { - if ( typeof attributeKey === 'number' ) { - window.console.error( - `RichText needs an identifier prop that is the block attribute key of the attribute it controls. Its type is expected to be a string, but was ${ typeof attributeKey }` - ); - } else { - window.console.error( - 'The RichText identifier prop does not match any attributes defined by the block.' - ); - } - } - - // A robust way to retain selection position through various transforms - // is to insert a special character at the position and then recover it. - const START_OF_SELECTED_AREA = '\u0086'; - - // Clone the blocks so we don't insert the character in a "live" block. - const cloneA = cloneBlock( blockA ); - const cloneB = cloneBlock( blockB ); - - if ( canRestoreTextSelection ) { - const selectedBlock = clientId === clientIdA ? cloneA : cloneB; - const html = selectedBlock.attributes[ attributeKey ]; - const { - multiline: multilineTag, - __unstableMultilineWrapperTags: multilineWrapperTags, - __unstablePreserveWhiteSpace: preserveWhiteSpace, - } = attributeDefinition; - const value = insert( - create( { - html, - multilineTag, - multilineWrapperTags, - preserveWhiteSpace, - } ), - START_OF_SELECTED_AREA, - offset, - offset - ); - - selectedBlock.attributes[ attributeKey ] = toHTMLString( { - value, - multilineTag, - preserveWhiteSpace, - } ); - } - - // We can only merge blocks with similar types - // thus, we transform the block to merge first - const blocksWithTheSameType = - blockA.name === blockB.name - ? [ cloneB ] - : switchToBlockType( cloneB, blockA.name ); - - // If the block types can not match, do nothing - if ( ! blocksWithTheSameType || ! blocksWithTheSameType.length ) { - return; - } - - // Calling the merge to update the attributes and remove the block to be merged - const updatedAttributes = blockAType.merge( - cloneA.attributes, - blocksWithTheSameType[ 0 ].attributes - ); - - if ( canRestoreTextSelection ) { - const newAttributeKey = findKey( - updatedAttributes, - ( v ) => - typeof v === 'string' && - v.indexOf( START_OF_SELECTED_AREA ) !== -1 - ); - const convertedHtml = updatedAttributes[ newAttributeKey ]; - const { - multiline: multilineTag, - __unstableMultilineWrapperTags: multilineWrapperTags, - __unstablePreserveWhiteSpace: preserveWhiteSpace, - } = blockAType.attributes[ newAttributeKey ]; - const convertedValue = create( { - html: convertedHtml, - multilineTag, - multilineWrapperTags, - preserveWhiteSpace, - } ); - const newOffset = convertedValue.text.indexOf( - START_OF_SELECTED_AREA - ); - const newValue = remove( convertedValue, newOffset, newOffset + 1 ); - const newHtml = toHTMLString( { - value: newValue, - multilineTag, - preserveWhiteSpace, - } ); - - updatedAttributes[ newAttributeKey ] = newHtml; - - dispatch( - selectionChange( - blockA.clientId, - newAttributeKey, - newOffset, - newOffset - ) - ); - } - - dispatch( - replaceBlocks( - [ blockA.clientId, blockB.clientId ], - [ - { - ...blockA, - attributes: { - ...blockA.attributes, - ...updatedAttributes, - }, - }, - ...blocksWithTheSameType.slice( 1 ), - ] - ) - ); - }, - RESET_BLOCKS: [ validateBlocksToTemplate ], - MULTI_SELECT: ( action, { getState } ) => { - const blockCount = getSelectedBlockCount( getState() ); - - speak( - sprintf( - /* translators: %s: number of selected blocks */ - _n( '%s block selected.', '%s blocks selected.', blockCount ), - blockCount - ), - 'assertive' - ); - }, - SYNCHRONIZE_TEMPLATE( action, { getState } ) { - const state = getState(); - const blocks = getBlocks( state ); - const template = getTemplate( state ); - const updatedBlockList = synchronizeBlocksWithTemplate( - blocks, - template - ); - - return resetBlocks( updatedBlockList ); - }, - MARK_AUTOMATIC_CHANGE( action, store ) { - const { - setTimeout, - requestIdleCallback = ( callback ) => setTimeout( callback, 100 ), - } = window; - - requestIdleCallback( () => { - store.dispatch( { type: 'MARK_AUTOMATIC_CHANGE_FINAL' } ); - } ); - }, -}; diff --git a/packages/block-editor/src/store/index.js b/packages/block-editor/src/store/index.js index 8fd1f3532c632c..4ba66689166777 100644 --- a/packages/block-editor/src/store/index.js +++ b/packages/block-editor/src/store/index.js @@ -7,7 +7,6 @@ import { createReduxStore, registerStore } from '@wordpress/data'; * Internal dependencies */ import reducer from './reducer'; -import applyMiddlewares from './middlewares'; import * as selectors from './selectors'; import * as actions from './actions'; import controls from './controls'; @@ -44,10 +43,7 @@ export const store = createReduxStore( STORE_NAME, { } ); // Ideally we'd use register instead of register stores. -// We should be able to make the switch once we remove the "effects" middleware. -// We also need a more generic way of defining persistence and not rely on a plugin. -const instantiatedStore = registerStore( STORE_NAME, { +registerStore( STORE_NAME, { ...storeConfig, persist: [ 'preferences' ], } ); -applyMiddlewares( instantiatedStore ); diff --git a/packages/block-editor/src/store/middlewares.js b/packages/block-editor/src/store/middlewares.js deleted file mode 100644 index 0f4c5aef8df703..00000000000000 --- a/packages/block-editor/src/store/middlewares.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * External dependencies - */ -import refx from 'refx'; -import multi from 'redux-multi'; -import { flowRight } from 'lodash'; - -/** - * Internal dependencies - */ -import effects from './effects'; - -/** - * Applies the custom middlewares used specifically in the editor module. - * - * @param {Object} store Store Object. - * - * @return {Object} Update Store Object. - */ -function applyMiddlewares( store ) { - const middlewares = [ refx( effects ), multi ]; - - let enhancedDispatch = () => { - throw new Error( - 'Dispatching while constructing your middleware is not allowed. ' + - 'Other middleware would not be applied to this dispatch.' - ); - }; - let chain = []; - - const middlewareAPI = { - getState: store.getState, - dispatch: ( ...args ) => enhancedDispatch( ...args ), - }; - chain = middlewares.map( ( middleware ) => middleware( middlewareAPI ) ); - enhancedDispatch = flowRight( ...chain )( store.dispatch ); - - store.dispatch = enhancedDispatch; - return store; -} - -export default applyMiddlewares; diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 9bbe764ef6e95f..366d47b7d77f2e 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1585,6 +1585,10 @@ export function hasBlockMovingClientId( state = null, action ) { return action.hasBlockMovingClientId; } + if ( action.type === 'SET_NAVIGATION_MODE' ) { + return null; + } + return state; } diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index c2153ee33cb4b1..764f8350b986d9 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -13,6 +13,8 @@ import { some, find, filter, + mapKeys, + orderBy, } from 'lodash'; import createSelector from 'rememo'; @@ -23,6 +25,7 @@ import { getBlockType, getBlockTypes, hasBlockSupport, + getPossibleBlockTransformations, parse, } from '@wordpress/blocks'; import { SVG, Rect, G, Path } from '@wordpress/components'; @@ -1400,6 +1403,85 @@ const getItemFromVariation = ( item ) => ( variation ) => ( { keywords: variation.keywords || item.keywords, } ); +/** + * Returns the calculated frecency. + * + * 'frecency' is a heuristic (https://en.wikipedia.org/wiki/Frecency) + * that combines block usage frequenty and recency. + * + * @param {number} time When the last insert occurred as a UNIX epoch + * @param {number} count The number of inserts that have occurred. + * + * @return {number} The calculated frecency. + */ +const calculateFrecency = ( time, count ) => { + if ( ! time ) { + return count; + } + // The selector is cached, which means Date.now() is the last time that the + // relevant state changed. This suits our needs. + const duration = Date.now() - time; + switch ( true ) { + case duration < MILLISECONDS_PER_HOUR: + return count * 4; + case duration < MILLISECONDS_PER_DAY: + return count * 2; + case duration < MILLISECONDS_PER_WEEK: + return count / 2; + default: + return count / 4; + } +}; + +/** + * Returns a function that accepts a block type and builds an item to be shown + * in a specific context. It's used for building items for Inserter and available + * block Transfroms list. + * + * @param {Object} state Editor state. + * @param {Object} options Options object for handling the building of a block type. + * @param {string} options.buildScope The scope for which the item is going to be used. + * @return {Function} Function returns an item to be shown in a specific context (Inserter|Transforms list). + */ +const buildBlockTypeItem = ( state, { buildScope = 'inserter' } ) => ( + blockType +) => { + const id = blockType.name; + + let isDisabled = false; + if ( ! hasBlockSupport( blockType.name, 'multiple', true ) ) { + isDisabled = some( + getBlocksByClientId( state, getClientIdsWithDescendants( state ) ), + { name: blockType.name } + ); + } + + const { time, count = 0 } = getInsertUsage( state, id ) || {}; + const blockItemBase = { + id, + name: blockType.name, + title: blockType.title, + icon: blockType.icon, + isDisabled, + frecency: calculateFrecency( time, count ), + }; + if ( buildScope === 'transform' ) return blockItemBase; + + const inserterVariations = blockType.variations.filter( + ( { scope } ) => ! scope || scope.includes( 'inserter' ) + ); + return { + ...blockItemBase, + initialAttributes: {}, + description: blockType.description, + category: blockType.category, + keywords: blockType.keywords, + variations: inserterVariations, + example: blockType.example, + utility: 1, // deprecated + }; +}; + /** * Determines the items that appear in the inserter. Includes both static * items (e.g. a regular block type) and dynamic items (e.g. a reusable block). @@ -1431,64 +1513,9 @@ const getItemFromVariation = ( item ) => ( variation ) => ( { */ export const getInserterItems = createSelector( ( state, rootClientId = null ) => { - const calculateFrecency = ( time, count ) => { - if ( ! time ) { - return count; - } - - // The selector is cached, which means Date.now() is the last time that the - // relevant state changed. This suits our needs. - const duration = Date.now() - time; - - switch ( true ) { - case duration < MILLISECONDS_PER_HOUR: - return count * 4; - case duration < MILLISECONDS_PER_DAY: - return count * 2; - case duration < MILLISECONDS_PER_WEEK: - return count / 2; - default: - return count / 4; - } - }; - - const buildBlockTypeInserterItem = ( blockType ) => { - const id = blockType.name; - - let isDisabled = false; - if ( ! hasBlockSupport( blockType.name, 'multiple', true ) ) { - isDisabled = some( - getBlocksByClientId( - state, - getClientIdsWithDescendants( state ) - ), - { - name: blockType.name, - } - ); - } - - const { time, count = 0 } = getInsertUsage( state, id ) || {}; - const inserterVariations = blockType.variations.filter( - ( { scope } ) => ! scope || scope.includes( 'inserter' ) - ); - - return { - id, - name: blockType.name, - initialAttributes: {}, - title: blockType.title, - description: blockType.description, - icon: blockType.icon, - category: blockType.category, - keywords: blockType.keywords, - variations: inserterVariations, - example: blockType.example, - isDisabled, - utility: 1, // deprecated - frecency: calculateFrecency( time, count ), - }; - }; + const buildBlockTypeInserterItem = buildBlockTypeItem( state, { + buildScope: 'inserter', + } ); const buildReusableBlockInserterItem = ( reusableBlock ) => { const id = `core/block/${ reusableBlock.id }`; @@ -1572,6 +1599,71 @@ export const getInserterItems = createSelector( ] ); +/** + * Determines the items that appear in the available block transforms list. + * + * Each item object contains what's necessary to display a menu item in the + * transform list and handle its selection. + * + * The 'frecency' property is a heuristic (https://en.wikipedia.org/wiki/Frecency) + * that combines block usage frequenty and recency. + * + * Items are returned ordered descendingly by their 'frecency'. + * + * @param {Object} state Editor state. + * @param {?string} rootClientId Optional root client ID of block list. + * + * @return {WPEditorTransformItem[]} Items that appear in inserter. + * + * @typedef {Object} WPEditorTransformItem + * @property {string} id Unique identifier for the item. + * @property {string} name The type of block to create. + * @property {string} title Title of the item, as it appears in the inserter. + * @property {string} icon Dashicon for the item, as it appears in the inserter. + * @property {boolean} isDisabled Whether or not the user should be prevented from inserting + * this item. + * @property {number} frecency Heuristic that combines frequency and recency. + */ +export const getBlockTransformItems = createSelector( + ( state, blocks, rootClientId = null ) => { + const buildBlockTypeTransformItem = buildBlockTypeItem( state, { + buildScope: 'transform', + } ); + const blockTypeTransformItems = getBlockTypes() + .filter( ( blockType ) => + canIncludeBlockTypeInInserter( state, blockType, rootClientId ) + ) + .map( buildBlockTypeTransformItem ); + + const itemsByName = mapKeys( + blockTypeTransformItems, + ( { name } ) => name + ); + const possibleTransforms = getPossibleBlockTransformations( + blocks + ).reduce( ( accumulator, block ) => { + if ( itemsByName[ block?.name ] ) { + accumulator.push( itemsByName[ block.name ] ); + } + return accumulator; + }, [] ); + const possibleBlockTransformations = orderBy( + possibleTransforms, + ( block ) => itemsByName[ block.name ].frecency, + 'desc' + ); + return possibleBlockTransformations; + }, + ( state, rootClientId ) => [ + state.blockListSettings[ rootClientId ], + state.blocks.byClientId, + state.preferences.insertUsage, + state.settings.allowedBlockTypes, + state.settings.templateLock, + getBlockTypes(), + ] +); + /** * Determines whether there are items to show in the inserter. * diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index aa5f622598700f..d9598b9a7e6b86 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -41,10 +41,10 @@ import { describe( 'actions', () => { describe( 'resetBlocks', () => { - it( 'should return the RESET_BLOCKS actions', () => { + it( 'should yield the RESET_BLOCKS actions', () => { const blocks = []; - const result = resetBlocks( blocks ); - expect( result ).toEqual( { + const fulfillment = resetBlocks( blocks ); + expect( fulfillment.next().value ).toEqual( { type: 'RESET_BLOCKS', blocks, } ); @@ -119,7 +119,8 @@ describe( 'actions', () => { it( 'should return MULTI_SELECT action', () => { const start = 'start'; const end = 'end'; - expect( multiSelect( start, end ) ).toEqual( { + const fulfillment = multiSelect( start, end ); + expect( fulfillment.next().value ).toEqual( { type: 'MULTI_SELECT', start, end, @@ -740,9 +741,11 @@ describe( 'actions', () => { it( 'should return MERGE_BLOCKS action', () => { const firstBlockClientId = 'blockA'; const secondBlockClientId = 'blockB'; - expect( - mergeBlocks( firstBlockClientId, secondBlockClientId ) - ).toEqual( { + const fulfillment = mergeBlocks( + firstBlockClientId, + secondBlockClientId + ); + expect( fulfillment.next().value ).toEqual( { type: 'MERGE_BLOCKS', blocks: [ firstBlockClientId, secondBlockClientId ], } ); diff --git a/packages/block-editor/src/store/test/effects.js b/packages/block-editor/src/store/test/effects.js index 1832e7fe2a8292..9a8ecc49801875 100644 --- a/packages/block-editor/src/store/test/effects.js +++ b/packages/block-editor/src/store/test/effects.js @@ -21,16 +21,13 @@ import { createRegistry } from '@wordpress/data'; import actions, { updateSettings, mergeBlocks, - replaceBlocks, resetBlocks, selectBlock, selectionChange, - setTemplateValidity, + validateBlocksToTemplate, } from '../actions'; -import effects, { validateBlocksToTemplate } from '../effects'; import * as selectors from '../selectors'; import reducer from '../reducer'; -import applyMiddlewares from '../middlewares'; import '../../'; describe( 'effects', () => { @@ -44,14 +41,10 @@ describe( 'effects', () => { }; describe( '.MERGE_BLOCKS', () => { - const handler = effects.MERGE_BLOCKS; - const defaultGetBlock = selectors.getBlock; - afterEach( () => { getBlockTypes().forEach( ( block ) => { unregisterBlockType( block.name ); } ); - selectors.getBlock = defaultGetBlock; } ); it( 'should only focus the blockA if the blockA has no merge function', () => { @@ -64,19 +57,21 @@ describe( 'effects', () => { clientId: 'ribs', name: 'core/test-block', } ); - selectors.getBlock = ( state, clientId ) => { - return blockA.clientId === clientId ? blockA : blockB; - }; - const dispatch = jest.fn(); - const getState = () => ( {} ); - handler( mergeBlocks( blockA.clientId, blockB.clientId ), { - dispatch, - getState, + const fulfillment = mergeBlocks( blockA.clientId, blockB.clientId ); + expect( fulfillment.next() ).toEqual( { + done: false, + value: { + type: 'MERGE_BLOCKS', + blocks: [ blockA.clientId, blockB.clientId ], + }, } ); - - expect( dispatch ).toHaveBeenCalledTimes( 1 ); - expect( dispatch ).toHaveBeenCalledWith( selectBlock( 'chicken' ) ); + fulfillment.next(); + expect( fulfillment.next( blockA ) ).toEqual( { + done: false, + value: selectBlock( 'chicken' ), + } ); + expect( fulfillment.next( blockA ).done ).toEqual( true ); } ); it( 'should merge the blocks if blocks of the same type', () => { @@ -108,24 +103,23 @@ describe( 'effects', () => { attributes: { content: 'ribs' }, innerBlocks: [], } ); - selectors.getBlock = ( state, clientId ) => { - return blockA.clientId === clientId ? blockA : blockB; - }; - const dispatch = jest.fn(); - const getState = () => ( { - selectionStart: { - clientId: blockB.clientId, - attributeKey: 'content', - offset: 0, - }, - } ); - handler( mergeBlocks( blockA.clientId, blockB.clientId ), { - dispatch, - getState, - } ); - expect( dispatch ).toHaveBeenCalledTimes( 2 ); - expect( dispatch ).toHaveBeenCalledWith( + const fulfillment = mergeBlocks( blockA.clientId, blockB.clientId ); + // MERGE_BLOCKS + fulfillment.next(); + // getBlock A + fulfillment.next(); + fulfillment.next( blockA ); + // getBlock B + fulfillment.next( blockB ); + // getSelectionStart + fulfillment.next( { + clientId: blockB.clientId, + attributeKey: 'content', + offset: 0, + } ); + // selectionChange + fulfillment.next( selectionChange( blockA.clientId, 'content', @@ -133,22 +127,19 @@ describe( 'effects', () => { 'chicken'.length + 1 ) ); - const lastCall = dispatch.mock.calls[ 1 ]; - expect( lastCall ).toHaveLength( 1 ); - const [ lastCallArgument ] = lastCall; - const expectedGenerator = replaceBlocks( - [ 'chicken', 'ribs' ], - [ + fulfillment.next(); + fulfillment.next(); + expect( fulfillment.next( blockA ).value ).toMatchObject( { + type: 'REPLACE_BLOCKS', + clientIds: [ 'chicken', 'ribs' ], + blocks: [ { clientId: 'chicken', name: 'core/test-block', attributes: { content: 'chicken ribs' }, }, - ] - ); - expect( Array.from( lastCallArgument ) ).toEqual( - Array.from( expectedGenerator ) - ); + ], + } ); } ); it( 'should not merge the blocks have different types without transformation', () => { @@ -181,23 +172,28 @@ describe( 'effects', () => { attributes: { content: 'ribs' }, innerBlocks: [], } ); - selectors.getBlock = ( state, clientId ) => { - return blockA.clientId === clientId ? blockA : blockB; - }; - const dispatch = jest.fn(); - const getState = () => ( { - selectionStart: { - clientId: blockB.clientId, - attributeKey: 'content', - offset: 0, - }, + + const fulfillment = mergeBlocks( blockA.clientId, blockB.clientId ); + // MERGE_BLOCKS + fulfillment.next(); + // getBlock A + fulfillment.next(); + fulfillment.next( blockA ); + // getBlock B + expect( fulfillment.next( blockB ).value ).toEqual( { + args: [], + selectorName: 'getSelectionStart', + storeKey: 'core/block-editor', + type: '@@data/SELECT', } ); - handler( mergeBlocks( blockA.clientId, blockB.clientId ), { - dispatch, - getState, + // getSelectionStart + const next = fulfillment.next( { + clientId: blockB.clientId, + attributeKey: 'content', + offset: 0, } ); - - expect( dispatch ).not.toHaveBeenCalled(); + expect( next.value ).toEqual( undefined ); + expect( next.done ).toBe( true ); } ); it( 'should transform and merge the blocks', () => { @@ -254,24 +250,27 @@ describe( 'effects', () => { attributes: { content2: 'ribs' }, innerBlocks: [], } ); - selectors.getBlock = ( state, clientId ) => { - return blockA.clientId === clientId ? blockA : blockB; - }; - const dispatch = jest.fn(); - const getState = () => ( { - selectionStart: { + + const fulfillment = mergeBlocks( blockA.clientId, blockB.clientId ); + // MERGE_BLOCKS + fulfillment.next(); + // getBlock A + fulfillment.next(); + fulfillment.next( blockA ); + // getBlock B + expect( fulfillment.next( blockB ).value ).toEqual( { + args: [], + selectorName: 'getSelectionStart', + storeKey: 'core/block-editor', + type: '@@data/SELECT', + } ); + expect( + fulfillment.next( { clientId: blockB.clientId, attributeKey: 'content2', offset: 0, - }, - } ); - handler( mergeBlocks( blockA.clientId, blockB.clientId ), { - dispatch, - getState, - } ); - - expect( dispatch ).toHaveBeenCalledTimes( 2 ); - expect( dispatch ).toHaveBeenCalledWith( + } ).value + ).toEqual( selectionChange( blockA.clientId, 'content', @@ -279,34 +278,32 @@ describe( 'effects', () => { 'chicken'.length + 1 ) ); - const expectedGenerator = replaceBlocks( - [ 'chicken', 'ribs' ], - [ + + fulfillment.next(); + fulfillment.next(); + fulfillment.next(); + expect( fulfillment.next( blockA ).value ).toMatchObject( { + type: 'REPLACE_BLOCKS', + clientIds: [ 'chicken', 'ribs' ], + blocks: [ { clientId: 'chicken', name: 'core/test-block', attributes: { content: 'chicken ribs' }, }, - ] - ); - const lastCall = dispatch.mock.calls[ 1 ]; - expect( lastCall ).toHaveLength( 1 ); - const [ lastCallArgument ] = lastCall; - expect( Array.from( lastCallArgument ) ).toEqual( - Array.from( expectedGenerator ) - ); + ], + } ); } ); } ); describe( 'validateBlocksToTemplate', () => { let store; beforeEach( () => { - store = createRegistry().registerStore( 'test', { + store = createRegistry().registerStore( 'core/block-editor', { actions, selectors, reducer, } ); - applyMiddlewares( store ); registerBlockType( 'core/test-block', defaultBlockSettings ); } ); @@ -317,31 +314,32 @@ describe( 'effects', () => { } ); } ); - it( 'should return undefined if no template assigned', () => { - const result = validateBlocksToTemplate( - resetBlocks( [ createBlock( 'core/test-block' ) ] ), - store + it( 'should return undefined if no template assigned', async () => { + const result = await store.dispatch( + validateBlocksToTemplate( + resetBlocks( [ createBlock( 'core/test-block' ) ] ), + store + ) ); - expect( result ).toBe( undefined ); + expect( result ).toEqual( undefined ); } ); - it( 'should return undefined if invalid but unlocked', () => { + it( 'should return undefined if invalid but unlocked', async () => { store.dispatch( updateSettings( { template: [ [ 'core/foo', {} ] ], } ) ); - const result = validateBlocksToTemplate( - resetBlocks( [ createBlock( 'core/test-block' ) ] ), - store + const result = await store.dispatch( + validateBlocksToTemplate( [ createBlock( 'core/test-block' ) ] ) ); - expect( result ).toBe( undefined ); + expect( result ).toEqual( undefined ); } ); - it( 'should return undefined if locked and valid', () => { + it( 'should return undefined if locked and valid', async () => { store.dispatch( updateSettings( { template: [ [ 'core/test-block' ] ], @@ -349,15 +347,14 @@ describe( 'effects', () => { } ) ); - const result = validateBlocksToTemplate( - resetBlocks( [ createBlock( 'core/test-block' ) ] ), - store + const result = await store.dispatch( + validateBlocksToTemplate( [ createBlock( 'core/test-block' ) ] ) ); - expect( result ).toBe( undefined ); + expect( result ).toEqual( undefined ); } ); - it( 'should return validity set action if invalid on default state', () => { + it( 'should return validity set action if invalid on default state', async () => { store.dispatch( updateSettings( { template: [ [ 'core/foo' ] ], @@ -365,12 +362,11 @@ describe( 'effects', () => { } ) ); - const result = validateBlocksToTemplate( - resetBlocks( [ createBlock( 'core/test-block' ) ] ), - store + const result = await store.dispatch( + validateBlocksToTemplate( [ createBlock( 'core/test-block' ) ] ) ); - expect( result ).toEqual( setTemplateValidity( false ) ); + expect( result ).toEqual( false ); } ); } ); } ); diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index d11e53a9e74853..a0803a0e5b2c20 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -61,6 +61,7 @@ const { canInsertBlockType, canInsertBlocks, getInserterItems, + getBlockTransformItems, isValidTemplate, getTemplate, getTemplateLock, @@ -2675,6 +2676,222 @@ describe( 'selectors', () => { } ); } ); + describe( 'getBlockTransformItems', () => { + beforeAll( () => { + registerBlockType( 'core/with-tranforms-a', { + category: 'text', + title: 'Tranforms a', + edit: () => {}, + save: () => {}, + transforms: { + to: [ + { + type: 'block', + blocks: [ 'core/with-tranforms-b' ], + transform: () => {}, + }, + { + type: 'block', + blocks: [ 'core/with-tranforms-c' ], + transform: () => {}, + }, + { + type: 'block', + blocks: [ + 'core/with-tranforms-b', + 'core/with-tranforms-c', + ], + transform: () => {}, + isMultiBlock: true, + }, + ], + }, + } ); + registerBlockType( 'core/with-tranforms-b', { + category: 'text', + title: 'Tranforms b', + edit: () => {}, + save: () => {}, + transforms: { + to: [ + { + type: 'block', + blocks: [ 'core/with-tranforms-a' ], + transform: () => {}, + }, + ], + }, + } ); + registerBlockType( 'core/with-tranforms-c', { + category: 'text', + title: 'Tranforms c', + edit: () => {}, + save: () => {}, + transforms: { + to: [ + { + type: 'block', + blocks: [ 'core/with-tranforms-a' ], + transform: () => {}, + }, + ], + }, + supports: { multiple: false }, + } ); + } ); + afterAll( () => { + [ + 'core/with-tranforms-a', + 'core/with-tranforms-b', + 'core/with-tranforms-c', + ].forEach( unregisterBlockType ); + } ); + it( 'should properly return block type items', () => { + const state = { + blocks: { + byClientId: {}, + attributes: {}, + order: {}, + parents: {}, + cache: {}, + }, + settings: {}, + preferences: {}, + blockListSettings: {}, + }; + const blocks = [ { name: 'core/with-tranforms-a' } ]; + const items = getBlockTransformItems( state, blocks ); + expect( items ).toHaveLength( 2 ); + const returnedProps = Object.keys( items[ 0 ] ); + // Verify we have only the wanted props. + expect( returnedProps ).toHaveLength( 6 ); + expect( returnedProps ).toEqual( + expect.arrayContaining( [ + 'id', + 'name', + 'title', + 'icon', + 'frecency', + 'isDisabled', + ] ) + ); + expect( items ).toEqual( + expect.arrayContaining( [ + expect.objectContaining( { + name: 'core/with-tranforms-b', + } ), + expect.objectContaining( { + name: 'core/with-tranforms-c', + } ), + ] ) + ); + } ); + it( 'should return only eligible blocks for transformation - `allowedBlocks`', () => { + const state = { + blocks: { + byClientId: { + block1: { name: 'core/with-tranforms-b' }, + block2: { name: 'core/with-tranforms-a' }, + }, + attributes: { + block1: {}, + block2: {}, + }, + order: {}, + parents: { + block1: '', + block2: 'block1', + }, + cache: {}, + controlledInnerBlocks: {}, + }, + settings: {}, + preferences: {}, + blockListSettings: { + block1: { + allowedBlocks: [ 'core/with-tranforms-c' ], + }, + block2: {}, + }, + }; + const blocks = [ + { clientId: 'block2', name: 'core/with-tranforms-a' }, + ]; + const items = getBlockTransformItems( state, blocks, 'block1' ); + expect( items ).toHaveLength( 1 ); + expect( items[ 0 ].name ).toEqual( 'core/with-tranforms-c' ); + } ); + it( 'should take into account the usage of blocks settings `multiple` - if multiple blocks of the same type are allowed', () => { + const state = { + blocks: { + byClientId: { + block1: { + clientId: 'block1', + name: 'core/with-tranforms-c', + }, + }, + attributes: { + block1: { attribute: {} }, + }, + order: { + '': [ 'block1' ], + }, + cache: { + block1: {}, + }, + controlledInnerBlocks: {}, + }, + preferences: { + insertUsage: {}, + }, + blockListSettings: {}, + settings: {}, + }; + const blocks = [ { name: 'core/with-tranforms-a' } ]; + const items = getBlockTransformItems( state, blocks ); + expect( items ).toHaveLength( 2 ); + expect( items ).toEqual( + expect.arrayContaining( [ + expect.objectContaining( { + name: 'core/with-tranforms-b', + isDisabled: false, + } ), + expect.objectContaining( { + name: 'core/with-tranforms-c', + isDisabled: true, + } ), + ] ) + ); + } ); + it( 'should set frecency', () => { + const state = { + blocks: { + byClientId: {}, + attributes: {}, + order: {}, + parents: {}, + cache: {}, + }, + preferences: { + insertUsage: { + 'core/with-tranforms-a': { count: 10, time: 1000 }, + }, + }, + blockListSettings: {}, + settings: {}, + }; + const blocks = [ { name: 'core/with-tranforms-c' } ]; + const items = getBlockTransformItems( state, blocks ); + expect( items ).toHaveLength( 1 ); + expect( items[ 0 ] ).toEqual( + expect.objectContaining( { + name: 'core/with-tranforms-a', + frecency: 2.5, + } ) + ); + } ); + } ); + describe( 'isValidTemplate', () => { it( 'should return true if template is valid', () => { const state = { diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 5421186cbdff53..1ea21ed4574523 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -52,7 +52,6 @@ @import "./components/url-input/style.scss"; @import "./components/url-popover/style.scss"; @import "./components/warning/style.scss"; -@import "./components/writing-flow/style.scss"; @import "./hooks/anchor.scss"; // This tag marks the end of the styles that apply to editing canvas contents and need to be manipulated when we resize the editor. diff --git a/packages/block-editor/src/utils/get-paste-event-data.js b/packages/block-editor/src/utils/get-paste-event-data.js index b840de2964250c..0db158c5f6e8e6 100644 --- a/packages/block-editor/src/utils/get-paste-event-data.js +++ b/packages/block-editor/src/utils/get-paste-event-data.js @@ -25,9 +25,9 @@ export function getPasteEventData( { clipboardData } ) { } } - const files = [ - ...getFilesFromDataTransfer( clipboardData ), - ].filter( ( { type } ) => /^image\/(?:jpe?g|png|gif)$/.test( type ) ); + const files = getFilesFromDataTransfer( + clipboardData + ).filter( ( { type } ) => /^image\/(?:jpe?g|png|gif)$/.test( type ) ); // Only process files if no HTML is present. // A pasted file may have the URL as plain text. diff --git a/packages/block-editor/src/utils/transform-styles/index.js b/packages/block-editor/src/utils/transform-styles/index.js index b0f1d66fe55111..763554707bd2e7 100644 --- a/packages/block-editor/src/utils/transform-styles/index.js +++ b/packages/block-editor/src/utils/transform-styles/index.js @@ -23,20 +23,23 @@ import wrap from './transforms/wrap'; * @return {Array} converted rules. */ const transformStyles = ( styles, wrapperClassName = '' ) => { - return map( styles, ( { css, baseURL } ) => { - const transforms = []; - if ( wrapperClassName ) { - transforms.push( wrap( wrapperClassName ) ); - } - if ( baseURL ) { - transforms.push( urlRewrite( baseURL ) ); - } - if ( transforms.length ) { - return traverse( css, compose( transforms ) ); - } + return map( + styles, + ( { css, baseURL, __experimentalNoWrapper = false } ) => { + const transforms = []; + if ( wrapperClassName && ! __experimentalNoWrapper ) { + transforms.push( wrap( wrapperClassName ) ); + } + if ( baseURL ) { + transforms.push( urlRewrite( baseURL ) ); + } + if ( transforms.length ) { + return traverse( css, compose( transforms ) ); + } - return css; - } ); + return css; + } + ); }; export default transformStyles; diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index 1261a02bf8de7b..b93a100c74ec29 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.27.0 (2020-12-17) + ### Enhancement - File Block: Copy url button is moved to Block toolbar. diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 37f576c96c09c5..c261049d5ca401 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-library", - "version": "2.26.4", + "version": "2.27.0", "description": "Block library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -26,7 +26,7 @@ "src/**/*.scss" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:../a11y", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/autop": "file:../autop", diff --git a/packages/block-library/src/archives/block.json b/packages/block-library/src/archives/block.json index 7be2a90e41e8d1..d35f8b05001c4b 100644 --- a/packages/block-library/src/archives/block.json +++ b/packages/block-library/src/archives/block.json @@ -15,5 +15,6 @@ "supports": { "align": true, "html": false - } + }, + "editorStyle": "wp-block-archives-editor" } diff --git a/packages/block-library/src/audio/block.json b/packages/block-library/src/audio/block.json index a077767932e73c..c8e6e8e19d693e 100644 --- a/packages/block-library/src/audio/block.json +++ b/packages/block-library/src/audio/block.json @@ -39,5 +39,7 @@ "supports": { "anchor": true, "align": true - } + }, + "editorStyle": "wp-block-audio-editor", + "style": "wp-block-audio" } diff --git a/packages/block-library/src/audio/edit.js b/packages/block-library/src/audio/edit.js index 96a52d3f9d4a28..5f6e18dc09861d 100644 --- a/packages/block-library/src/audio/edit.js +++ b/packages/block-library/src/audio/edit.js @@ -186,6 +186,7 @@ function AudioEdit( { { ( ! RichText.isEmpty( caption ) || isSelected ) && ( diff --git a/packages/block-library/src/block/block.json b/packages/block-library/src/block/block.json index 97afcca2e594b0..aece916ab601ba 100644 --- a/packages/block-library/src/block/block.json +++ b/packages/block-library/src/block/block.json @@ -11,5 +11,6 @@ "customClassName": false, "html": false, "inserter": false - } + }, + "editorStyle": "wp-block-editor" } diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js index af64ac7b80645f..1c7b9b039252ca 100644 --- a/packages/block-library/src/block/edit.js +++ b/packages/block-library/src/block/edit.js @@ -19,6 +19,8 @@ import { BlockControls, useBlockProps, } from '@wordpress/block-editor'; +import { store as noticesStore } from '@wordpress/notices'; +import { store as reusableBlocksStore } from '@wordpress/reusable-blocks'; /** * Internal dependencies @@ -51,16 +53,17 @@ export default function ReusableBlockEdit( { isSaving: select( 'core' ).isSavingEntityRecord( ...recordArgs ), canUserUpdate: select( 'core' ).canUser( 'update', 'blocks', ref ), isEditing: select( - 'core/reusable-blocks' + reusableBlocksStore ).__experimentalIsEditingReusableBlock( clientId ), settings: select( 'core/block-editor' ).getSettings(), } ), [ ref, clientId ] ); + const { clearSelectedBlock } = useDispatch( 'core/block-editor' ); const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' ); const { __experimentalSetEditingReusableBlock } = useDispatch( - 'core/reusable-blocks' + reusableBlocksStore ); const setIsEditing = useCallback( ( value ) => { @@ -71,10 +74,10 @@ export default function ReusableBlockEdit( { const { __experimentalConvertBlockToStatic: convertBlockToStatic, - } = useDispatch( 'core/reusable-blocks' ); + } = useDispatch( reusableBlocksStore ); const { createSuccessNotice, createErrorNotice } = useDispatch( - 'core/notices' + noticesStore ); const save = useCallback( async function () { try { @@ -117,6 +120,15 @@ export default function ReusableBlockEdit( { ); } + /** + * Clear the selected block when focus moves to the reusable block list. + * These blocks are in different stores and only one block should be + * selected at a time. + */ + function onFocus() { + clearSelectedBlock(); + } + let element = ( - - - +
+ + + +
); diff --git a/packages/block-library/src/block/editor.scss b/packages/block-library/src/block/editor.scss index b5c828e0680d5a..b3eb1a67e99459 100644 --- a/packages/block-library/src/block/editor.scss +++ b/packages/block-library/src/block/editor.scss @@ -1,8 +1,4 @@ .edit-post-visual-editor .block-library-block__reusable-block-container { - .block-editor-writing-flow__click-redirect { - min-height: auto; - } - // Unset the padding that root containers get when they're actually root containers. .is-root-container { padding-left: 0; diff --git a/packages/block-library/src/button/block.json b/packages/block-library/src/button/block.json index 622c43ef124296..a5b4435271483b 100644 --- a/packages/block-library/src/button/block.json +++ b/packages/block-library/src/button/block.json @@ -63,5 +63,7 @@ "alignWide": false, "reusable": false, "__experimentalSelector": ".wp-block-button > a" - } + }, + "editorStyle": "wp-block-button-editor", + "style": "wp-block-button" } diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index 2136f5ddc6b24f..c7832ec7564202 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -107,6 +107,7 @@ function URLPicker( { setAttributes, opensInNewTab, onToggleOpenInNewTab, + anchorRef, } ) { const [ isURLPickerOpen, setIsURLPickerOpen ] = useState( false ); const urlIsSet = !! url; @@ -127,6 +128,7 @@ function URLPicker( { setIsURLPickerOpen( false ) } + anchorRef={ anchorRef?.current } > setAttributes( { text: value } ) } @@ -276,6 +279,7 @@ function ButtonEdit( props ) { isSelected={ isSelected } opensInNewTab={ linkTarget === '_blank' } onToggleOpenInNewTab={ onToggleOpenInNewTab } + anchorRef={ blockProps.ref } /> .wp-block { - // Override editor auto block margins. - margin-left: 0; -} - .wp-block > .wp-block-buttons { display: flex; flex-wrap: wrap; } +.wp-block-buttons { + > .wp-block { + // Override editor auto block margins. + margin-left: 0; + margin-top: $button-margin; + } + > .block-list-appender { + display: inline-flex; + align-items: center; + } + &.is-vertical { + > .block-list-appender .block-list-appender__toggle { + justify-content: flex-start; + } + } + > .wp-block-button { + &:focus { + box-shadow: none; + } + } +} + .wp-block[data-align="center"] > .wp-block-buttons { align-items: center; justify-content: center; @@ -16,8 +33,3 @@ .wp-block[data-align="right"] > .wp-block-buttons { justify-content: flex-end; } - -.wp-block-buttons > .block-list-appender { - display: inline-flex; - align-items: center; -} diff --git a/packages/block-library/src/buttons/index.js b/packages/block-library/src/buttons/index.js index 81653d1e12d686..eac726ba4de305 100644 --- a/packages/block-library/src/buttons/index.js +++ b/packages/block-library/src/buttons/index.js @@ -12,6 +12,7 @@ import transforms from './transforms'; import edit from './edit'; import metadata from './block.json'; import save from './save'; +import variations from './variations'; const { name } = metadata; @@ -40,4 +41,5 @@ export const settings = { transforms, edit, save, + variations, }; diff --git a/packages/block-library/src/buttons/save.js b/packages/block-library/src/buttons/save.js index 5c64fc110e0d57..784833b63ca321 100644 --- a/packages/block-library/src/buttons/save.js +++ b/packages/block-library/src/buttons/save.js @@ -8,12 +8,15 @@ import classnames from 'classnames'; */ import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; -export default function save( { attributes: { contentJustification } } ) { +export default function save( { + attributes: { contentJustification, orientation }, +} ) { return (
diff --git a/packages/block-library/src/buttons/style.scss b/packages/block-library/src/buttons/style.scss index 438c7b1e42be31..64bac05b155b8f 100644 --- a/packages/block-library/src/buttons/style.scss +++ b/packages/block-library/src/buttons/style.scss @@ -3,6 +3,17 @@ flex-direction: row; flex-wrap: wrap; + &.is-vertical { + flex-direction: column; + > .wp-block-button { + /*rtl:ignore*/ + margin-right: 0; + &:last-child { + margin-bottom: 0; + } + } + } + // Increased specificity to override blocks default margin. > .wp-block-button { display: inline-block; @@ -18,10 +29,16 @@ &.is-content-justification-left { justify-content: flex-start; + &.is-vertical { + align-items: flex-start; + } } &.is-content-justification-center { justify-content: center; + &.is-vertical { + align-items: center; + } } &.is-content-justification-right { @@ -38,6 +55,10 @@ margin-left: 0; } } + + &.is-vertical { + align-items: flex-end; + } } // Kept for backward compatibiity. diff --git a/packages/block-library/src/buttons/variations.js b/packages/block-library/src/buttons/variations.js new file mode 100644 index 00000000000000..438a5fc97b5ebe --- /dev/null +++ b/packages/block-library/src/buttons/variations.js @@ -0,0 +1,24 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +const variations = [ + { + name: 'buttons-horizontal', + isDefault: true, + title: __( 'Horizontal' ), + description: __( 'Buttons shown in a row.' ), + attributes: { orientation: 'horizontal' }, + scope: [ 'transform' ], + }, + { + name: 'buttons-vertical', + title: __( 'Vertical' ), + description: __( 'Buttons shown in a column.' ), + attributes: { orientation: 'vertical' }, + scope: [ 'transform' ], + }, +]; + +export default variations; diff --git a/packages/block-library/src/calendar/block.json b/packages/block-library/src/calendar/block.json index eb585dec70156c..edb73671e692a2 100644 --- a/packages/block-library/src/calendar/block.json +++ b/packages/block-library/src/calendar/block.json @@ -12,5 +12,6 @@ }, "supports": { "align": true - } + }, + "style": "wp-block-calendar" } diff --git a/packages/block-library/src/categories/block.json b/packages/block-library/src/categories/block.json index be8e9d1c213e74..5fe562622c83e3 100644 --- a/packages/block-library/src/categories/block.json +++ b/packages/block-library/src/categories/block.json @@ -19,5 +19,7 @@ "supports": { "align": true, "html": false - } + }, + "editorStyle": "wp-block-categories-editor", + "style": "wp-block-categories" } diff --git a/packages/block-library/src/code/block.json b/packages/block-library/src/code/block.json index d9f37ed061f808..0f29cf33cc0307 100644 --- a/packages/block-library/src/code/block.json +++ b/packages/block-library/src/code/block.json @@ -10,6 +10,8 @@ } }, "supports": { - "anchor": true - } + "anchor": true, + "fontSize": true + }, + "style": "wp-block-code" } diff --git a/packages/block-library/src/code/edit.js b/packages/block-library/src/code/edit.js index b8960d242ee886..4153b14d753cdc 100644 --- a/packages/block-library/src/code/edit.js +++ b/packages/block-library/src/code/edit.js @@ -16,6 +16,7 @@ export default function CodeEdit( { attributes, setAttributes, onRemove } ) { placeholder={ __( 'Write code…' ) } aria-label={ __( 'Code' ) } preserveWhiteSpace + __unstablePastePlainText /> ); diff --git a/packages/block-library/src/code/editor.scss b/packages/block-library/src/code/editor.scss deleted file mode 100644 index bc47a4f35ee9da..00000000000000 --- a/packages/block-library/src/code/editor.scss +++ /dev/null @@ -1,4 +0,0 @@ -.wp-block-code > code { - // PlainText cannot be an inline element yet. - display: block; -} diff --git a/packages/block-library/src/code/style.scss b/packages/block-library/src/code/style.scss index 3961cfbe8e7d96..35cab12be3c1eb 100644 --- a/packages/block-library/src/code/style.scss +++ b/packages/block-library/src/code/style.scss @@ -1,5 +1,6 @@ // Provide a minimum of overflow handling. .wp-block-code code { + display: block; white-space: pre-wrap; overflow-wrap: break-word; } diff --git a/packages/block-library/src/code/theme.scss b/packages/block-library/src/code/theme.scss index be1469328db3dc..4dfc5a41a988e3 100644 --- a/packages/block-library/src/code/theme.scss +++ b/packages/block-library/src/code/theme.scss @@ -1,6 +1,5 @@ .wp-block-code { font-family: $editor-html-font; - font-size: 0.9em; color: $gray-900; padding: 0.8em 1em; border: 1px solid $gray-300; diff --git a/packages/block-library/src/column/edit.native.js b/packages/block-library/src/column/edit.native.js index 16e03fadc5fdd3..4fec805c235ea4 100644 --- a/packages/block-library/src/column/edit.native.js +++ b/packages/block-library/src/column/edit.native.js @@ -1,14 +1,14 @@ /** * External dependencies */ -import { View } from 'react-native'; +import { View, Dimensions } from 'react-native'; /** * WordPress dependencies */ import { withSelect } from '@wordpress/data'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; -import { useEffect, useState } from '@wordpress/element'; +import { useEffect, useState, useCallback } from '@wordpress/element'; import { InnerBlocks, BlockControls, @@ -20,6 +20,7 @@ import { FooterMessageControl, UnitControl, getValueAndUnit, + alignmentHelpers, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; /** @@ -27,7 +28,14 @@ import { __ } from '@wordpress/i18n'; */ import styles from './editor.scss'; import ColumnsPreview from './column-preview'; -import { getWidths, getWidthWithUnit, CSS_UNITS } from '../columns/utils'; +import { + getWidths, + getWidthWithUnit, + isPercentageUnit, + CSS_UNITS, +} from '../columns/utils'; + +const { isWider } = alignmentHelpers; function ColumnEdit( { attributes, @@ -40,11 +48,14 @@ function ColumnEdit( { columns, selectedColumnIndex, parentAlignment, + clientId, } ) { const { verticalAlignment, width } = attributes; const { valueUnit = '%' } = getValueAndUnit( width ) || {}; - const [ widthUnit, setWidthUnit ] = useState( valueUnit ); + const screenWidth = Math.floor( Dimensions.get( 'window' ).width ); + + const [ widthUnit, setWidthUnit ] = useState( valueUnit || '%' ); const updateAlignment = ( alignment ) => { setAttributes( { verticalAlignment: alignment } ); @@ -70,15 +81,44 @@ function ColumnEdit( { const onChangeUnit = ( nextUnit ) => { setWidthUnit( nextUnit ); - const tempWidth = parseFloat( + const widthWithoutUnit = parseFloat( width || getWidths( columns )[ selectedColumnIndex ] ); setAttributes( { - width: getWidthWithUnit( tempWidth, nextUnit ), + width: getWidthWithUnit( widthWithoutUnit, nextUnit ), } ); }; + const onChange = ( nextWidth ) => { + if ( isPercentageUnit( widthUnit ) || ! widthUnit ) { + return; + } + onChangeWidth( nextWidth ); + }; + + const renderAppender = useCallback( () => { + const { width: blockWidth } = contentStyle[ clientId ]; + const isScreenWidthEqual = blockWidth === screenWidth; + + if ( isSelected ) { + return ( + + + + ); + } + return null; + }, [ contentStyle[ clientId ], screenWidth, isSelected, hasChildren ] ); + if ( ! isSelected && ! hasChildren ) { return ( ); @@ -108,11 +148,12 @@ function ColumnEdit( { diff --git a/packages/block-library/src/column/editor.native.scss b/packages/block-library/src/column/editor.native.scss index e5e2e853f59b73..6c52b88af035e4 100644 --- a/packages/block-library/src/column/editor.native.scss +++ b/packages/block-library/src/column/editor.native.scss @@ -16,7 +16,7 @@ } .innerBlocksBottomSpace { - margin-bottom: $block-selected-to-content; + margin-bottom: $block-edge-to-content; } .is-vertically-aligned-top { @@ -57,3 +57,13 @@ .columnIndicatorDark { background-color: $gray-20; } + +.columnAppender { + margin-left: $grid-unit-20; + margin-right: $grid-unit-20; +} + +.wideColumnAppender { + margin-left: $grid-unit-10; + margin-right: $grid-unit-10; +} diff --git a/packages/block-library/src/columns/block.json b/packages/block-library/src/columns/block.json index 0cc93b718a89b1..7aa5ff3e1bc8c4 100644 --- a/packages/block-library/src/columns/block.json +++ b/packages/block-library/src/columns/block.json @@ -18,5 +18,7 @@ "gradients": true, "link": true } - } + }, + "editorStyle": "wp-block-columns-editor", + "style": "wp-block-columns" } diff --git a/packages/block-library/src/columns/columnCalculations.native.js b/packages/block-library/src/columns/columnCalculations.native.js new file mode 100644 index 00000000000000..46d334b9d0e8ea --- /dev/null +++ b/packages/block-library/src/columns/columnCalculations.native.js @@ -0,0 +1,178 @@ +/** + * WordPress dependencies + */ +import { + ALIGNMENT_BREAKPOINTS, + convertUnitToMobile, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { getColumnWidths, getWidths } from './utils'; +import styles from './editor.scss'; + +/** + * Maximum number of columns in a row + * + * @type {number} + */ +const MAX_COLUMNS_NUM_IN_ROW = 3; + +/** + * Minimum width of column + * + * @type {number} + */ +const MIN_WIDTH = styles.columnsContainer?.minWidth; + +/** + * Container margin value + * + * @type {number} + */ +const MARGIN = styles.columnsContainer?.marginLeft; + +export const getColumnsInRow = ( width, columnCount ) => { + if ( width ) { + if ( width < ALIGNMENT_BREAKPOINTS.mobile ) { + // show only 1 Column in row for mobile breakpoint container width + return 1; + } else if ( width <= ALIGNMENT_BREAKPOINTS.medium ) { + // show the maximum number of columns in a row for large breakpoint container width + return Math.min( + Math.max( 1, columnCount ), + MAX_COLUMNS_NUM_IN_ROW + ); + } + // show all Column in one row + return columnCount; + } +}; + +export const calculateContainerWidth = ( containerWidth, columnsInRow ) => + 2 * MARGIN + containerWidth - columnsInRow * 2 * MARGIN; + +export const getContentWidths = ( + columnsInRow, + width, + columnCount, + innerColumns, + globalStyles +) => { + const widths = {}; + const columnWidthsWithUnits = getWidths( innerColumns, false ); + const columnWidths = getColumnWidths( innerColumns, columnCount ); + + // Array of column width attribute values + const columnWidthsValues = columnWidthsWithUnits.map( ( v ) => + convertUnitToMobile( { width }, globalStyles, v ) + ); + + // The sum of column width attribute values + const columnWidthsSum = columnWidthsValues.reduce( + ( acc, curr ) => acc + curr, + 0 + ); + + // Array of ratios of each column width attribute value to their sum + const columnRatios = columnWidthsValues.map( + ( colWidth ) => colWidth / columnWidthsSum + ); + + // Array of calculated column width for its ratio + const columnWidthsPerRatio = columnRatios.map( + ( columnRatio ) => + columnRatio * calculateContainerWidth( width, columnsInRow ) + ); + + // Array of columns whose calculated width is lower than minimum width value + const filteredColumnWidthsPerRatio = columnWidthsPerRatio.filter( + ( columnWidthPerRatio ) => columnWidthPerRatio <= MIN_WIDTH + ); + + // Container width to be divided. If there are some results within `filteredColumnWidthsPerRatio` + // there is a need to reduce the main width by multiplying number + // of results in `filteredColumnWidthsPerRatio` and minimum width value + const baseContainerWidth = + width - filteredColumnWidthsPerRatio.length * MIN_WIDTH; + + // The minimum percentage ratio for which column width is equal minimum width value + const minPercentageRatio = + MIN_WIDTH / calculateContainerWidth( width, columnsInRow ); + + // The sum of column widths which ratio is higher than `minPercentageRatio` + const largeColumnsWidthsSum = columnRatios + .map( ( ratio, index ) => { + if ( ratio > minPercentageRatio ) { + return columnWidthsValues[ index ]; + } + return 0; + } ) + .reduce( ( acc, curr ) => acc + curr, 0 ); + + const containerWidth = calculateContainerWidth( + baseContainerWidth, + columnsInRow + ); + + let columnWidth = + calculateContainerWidth( width, columnsInRow ) / columnsInRow; + let maxColumnWidth = columnWidth; + + innerColumns.forEach( + ( { attributes: innerColumnAttributes, clientId } ) => { + const attributeWidth = convertUnitToMobile( + { width }, + globalStyles, + innerColumnAttributes.width || columnWidths[ clientId ] + ); + const proportionalRatio = attributeWidth / columnWidthsSum; + const percentageRatio = attributeWidth / width; + const initialColumnWidth = proportionalRatio * containerWidth; + + if ( columnCount === 1 && width > ALIGNMENT_BREAKPOINTS.medium ) { + // Exactly one column inside columns on the breakpoint higher than medium + // has to take a percentage of the full width + columnWidth = percentageRatio * containerWidth; + } else if ( columnsInRow > 1 ) { + if ( width > ALIGNMENT_BREAKPOINTS.medium ) { + if ( initialColumnWidth <= MIN_WIDTH ) { + // Column width cannot be lower than minimum 32px + columnWidth = MIN_WIDTH; + } else if ( initialColumnWidth > MIN_WIDTH ) { + // Column width has to be the result of multiplying the container width and + // the ratio of attribute and the sum of widths of columns wider than 32px + columnWidth = + ( attributeWidth / largeColumnsWidthsSum ) * + containerWidth; + } + + maxColumnWidth = columnWidth; + + if ( Math.round( columnWidthsSum ) < width ) { + // In case that column width attribute values does not exceed 100, each column + // should have attribute percentage of container width + const newColumnWidth = percentageRatio * containerWidth; + if ( newColumnWidth <= MIN_WIDTH ) { + columnWidth = MIN_WIDTH; + } else { + columnWidth = newColumnWidth; + } + } + } else if ( width < ALIGNMENT_BREAKPOINTS.medium ) { + // On the breakpoint lower than medium each column inside columns + // has to take equal part of container width + columnWidth = + calculateContainerWidth( width, columnsInRow ) / + columnsInRow; + } + } + widths[ clientId ] = { + width: Math.floor( columnWidth ), + maxWidth: Math.floor( maxColumnWidth ), + }; + } + ); + return widths; +}; diff --git a/packages/block-library/src/columns/edit.js b/packages/block-library/src/columns/edit.js index 6e2eccf038f3c4..9ad79099af20aa 100644 --- a/packages/block-library/src/columns/edit.js +++ b/packages/block-library/src/columns/edit.js @@ -22,6 +22,7 @@ import { withDispatch, useDispatch, useSelect } from '@wordpress/data'; import { createBlock, createBlocksFromInnerBlocksTemplate, + store as blocksStore, } from '@wordpress/blocks'; /** @@ -209,7 +210,7 @@ function Placeholder( { clientId, name, setAttributes } ) { getBlockVariations, getBlockType, getDefaultBlockVariation, - } = select( 'core/blocks' ); + } = select( blocksStore ); return { blockType: getBlockType( name ), diff --git a/packages/block-library/src/columns/edit.native.js b/packages/block-library/src/columns/edit.native.js index 453cf8f9eed3f0..68de0e05ca1b81 100644 --- a/packages/block-library/src/columns/edit.native.js +++ b/packages/block-library/src/columns/edit.native.js @@ -1,9 +1,8 @@ /** * External dependencies */ -import { View } from 'react-native'; +import { View, Dimensions } from 'react-native'; import { dropRight, times, map, compact, delay } from 'lodash'; - /** * WordPress dependencies */ @@ -14,6 +13,8 @@ import { FooterMessageControl, UnitControl, getValueAndUnit, + GlobalStylesContext, + alignmentHelpers, } from '@wordpress/components'; import { InspectorControls, @@ -23,7 +24,13 @@ import { BlockVariationPicker, } from '@wordpress/block-editor'; import { withDispatch, useSelect } from '@wordpress/data'; -import { useEffect, useState, useMemo } from '@wordpress/element'; +import { + useEffect, + useState, + useContext, + useMemo, + useCallback, +} from '@wordpress/element'; import { useResizeObserver } from '@wordpress/compose'; import { createBlock } from '@wordpress/blocks'; import { columns } from '@wordpress/icons'; @@ -39,8 +46,14 @@ import { toWidthPrecision, getWidths, getWidthWithUnit, + isPercentageUnit, CSS_UNITS, } from './utils'; +import { + getColumnsInRow, + calculateContainerWidth, + getContentWidths, +} from './columnCalculations.native'; import ColumnsPreview from '../column/column-preview'; /** @@ -69,17 +82,7 @@ const DEFAULT_COLUMNS_NUM = 2; */ const MIN_COLUMNS_NUM = 1; -/** - * Maximum number of columns in a row - * - * @type {number} - */ -const MAX_COLUMNS_NUM_IN_ROW = 3; - -const BREAKPOINTS = { - mobile: 480, - large: 768, -}; +const { isWider, isFullWidth } = alignmentHelpers; function ColumnsEditContainer( { attributes, @@ -87,71 +90,48 @@ function ColumnsEditContainer( { updateColumns, columnCount, isSelected, - onAddNextColumn, onDeleteBlock, innerColumns, updateInnerColumnWidth, + editorSidebarOpened, } ) { const [ resizeListener, sizes ] = useResizeObserver(); const [ columnsInRow, setColumnsInRow ] = useState( MIN_COLUMNS_NUM ); + const screenWidth = Math.floor( Dimensions.get( 'window' ).width ); + const globalStyles = useContext( GlobalStylesContext ); - const { verticalAlignment } = attributes; + const { verticalAlignment, align } = attributes; const { width } = sizes || {}; - const newColumnCount = columnCount || DEFAULT_COLUMNS_NUM; - useEffect( () => { - updateColumns( columnCount, newColumnCount ); - }, [] ); + if ( columnCount === 0 ) { + const newColumnCount = columnCount || DEFAULT_COLUMNS_NUM; - useEffect( () => { - if ( width ) { - setColumnsInRow( getColumnsInRow( width, newColumnCount ) ); + updateColumns( columnCount, newColumnCount ); } - }, [ columnCount ] ); + }, [] ); useEffect( () => { if ( width ) { - setColumnsInRow( getColumnsInRow( width, columnCount ) ); - } - }, [ width ] ); - - const contentStyle = useMemo( () => { - const minWidth = Math.min( width, styles.columnsContainer.maxWidth ); - const columnBaseWidth = minWidth / columnsInRow; - - let columnWidth = columnBaseWidth; - if ( columnsInRow > 1 ) { - const margins = - columnsInRow * - Math.min( columnsInRow, MAX_COLUMNS_NUM_IN_ROW ) * - styles.columnMargin.marginLeft; - columnWidth = ( minWidth - margins ) / columnsInRow; - } - return { width: columnWidth }; - }, [ width, columnsInRow ] ); - - const getColumnsInRow = ( containerWidth, columnsNumber ) => { - if ( containerWidth < BREAKPOINTS.mobile ) { - // show only 1 Column in row for mobile breakpoint container width - return 1; - } else if ( containerWidth < BREAKPOINTS.large ) { - // show the maximum number of columns in a row for large breakpoint container width - return Math.min( - Math.max( 1, columnCount ), - MAX_COLUMNS_NUM_IN_ROW - ); + if ( getColumnsInRow( width, columnCount ) !== columnsInRow ) { + setColumnsInRow( getColumnsInRow( width, columnCount ) ); + } } - // show all Column in one row - return Math.max( 1, columnsNumber ); - }; + }, [ width, columnCount ] ); const renderAppender = () => { + const isEqualWidth = width === screenWidth; + if ( isSelected ) { return ( - + ); @@ -159,6 +139,22 @@ function ColumnsEditContainer( { return null; }; + const contentWidths = useMemo( + () => + getContentWidths( + columnsInRow, + width, + columnCount, + innerColumns, + globalStyles + ), + [ width, columnsInRow, columnCount, innerColumns, globalStyles ] + ); + + const onAddBlock = useCallback( () => { + updateColumns( columnCount, columnCount + 1 ); + }, [ columnCount ] ); + const onChangeWidth = ( nextWidth, valueUnit, columnId ) => { const widthWithUnit = getWidthWithUnit( nextWidth, valueUnit ); @@ -166,13 +162,26 @@ function ColumnsEditContainer( { }; const onChangeUnit = ( nextUnit, index, columnId ) => { - const tempWidth = parseFloat( getWidths( innerColumns )[ index ] ); - const widthWithUnit = getWidthWithUnit( tempWidth, nextUnit ); + const widthWithoutUnit = parseFloat( + getWidths( innerColumns )[ index ] + ); + const widthWithUnit = getWidthWithUnit( widthWithoutUnit, nextUnit ); updateInnerColumnWidth( widthWithUnit, columnId ); }; + const onChange = ( nextWidth, valueUnit, columnId ) => { + if ( isPercentageUnit( valueUnit ) || ! valueUnit ) { + return; + } + onChangeWidth( nextWidth, valueUnit, columnId ); + }; + const getColumnsSliders = () => { + if ( ! editorSidebarOpened || ! isSelected ) { + return null; + } + return innerColumns.map( ( column, index ) => { const { valueUnit = '%' } = getValueAndUnit( column.attributes.width ) || {}; @@ -183,15 +192,22 @@ function ColumnsEditContainer( { getWidths( innerColumns ).length }` } min={ 1 } - max={ valueUnit === '%' || ! valueUnit ? 100 : undefined } + max={ + isPercentageUnit( valueUnit ) || ! valueUnit + ? 100 + : undefined + } decimalNum={ 1 } value={ getWidths( innerColumns )[ index ] } onChange={ ( nextWidth ) => { - onChangeWidth( nextWidth, valueUnit, column.clientId ); + onChange( nextWidth, valueUnit, column.clientId ); } } onUnitChange={ ( nextUnit ) => onChangeUnit( nextUnit, index, column.clientId ) } + onComplete={ ( nextWidth ) => { + onChangeWidth( nextWidth, valueUnit, column.clientId ); + } } unit={ valueUnit } units={ CSS_UNITS } preview={ @@ -247,12 +263,17 @@ function ColumnsEditContainer( { horizontal={ true } allowedBlocks={ ALLOWED_BLOCKS } contentResizeMode="stretch" - onAddBlock={ onAddNextColumn } + onAddBlock={ onAddBlock } onDeleteBlock={ columnCount === 1 ? onDeleteBlock : undefined } - contentStyle={ contentStyle } - parentWidth={ width } + blockWidth={ width } + contentStyle={ contentWidths } + parentWidth={ + isFullWidth( align ) && columnCount === 0 + ? screenWidth + : calculateContainerWidth( width, columnsInRow ) + } /> ) } diff --git a/packages/block-library/src/columns/editor.native.scss b/packages/block-library/src/columns/editor.native.scss index bae76a50d6d2e1..d212677477927a 100644 --- a/packages/block-library/src/columns/editor.native.scss +++ b/packages/block-library/src/columns/editor.native.scss @@ -1,5 +1,6 @@ .columnsContainer { - max-width: $content-width; + min-width: $block-edge-to-content * 2; + margin-left: $block-edge-to-content; } .innerBlocksSelected { @@ -10,3 +11,7 @@ margin: $block-edge-to-content / 2; } +.columnAppender { + margin-left: $grid-unit-20; + margin-right: $grid-unit-20; +} diff --git a/packages/block-library/src/columns/test/utils.js b/packages/block-library/src/columns/test/utils.js index 6caf9b9d1e6b85..5d266a3b001f40 100644 --- a/packages/block-library/src/columns/test/utils.js +++ b/packages/block-library/src/columns/test/utils.js @@ -114,7 +114,7 @@ describe( 'getColumnWidths', () => { describe( 'getRedistributedColumnWidths', () => { describe( 'explicit width', () => { - const blocks = [ + let blocks = [ { clientId: 'a', attributes: { width: 30 } }, { clientId: 'b', attributes: { width: 40 } }, ]; @@ -123,8 +123,8 @@ describe( 'getRedistributedColumnWidths', () => { const widths = getRedistributedColumnWidths( blocks, 60 ); expect( widths ).toEqual( { - a: 25, - b: 35, + a: 25.71, + b: 34.29, } ); } ); @@ -132,8 +132,36 @@ describe( 'getRedistributedColumnWidths', () => { const widths = getRedistributedColumnWidths( blocks, 80 ); expect( widths ).toEqual( { - a: 35, - b: 45, + a: 34.29, + b: 45.71, + } ); + } ); + + it( 'should decrease proportionally for third column', () => { + blocks = [ + { clientId: 'a', attributes: { width: 99 } }, + { clientId: 'b', attributes: { width: 1 } }, + ]; + const widths = getRedistributedColumnWidths( blocks, 66.67 ); + + expect( widths ).toEqual( { + a: 66, + b: 0.67, + } ); + } ); + + it( 'should decrease proportionally for fourth column', () => { + blocks = [ + { clientId: 'a', attributes: { width: 98 } }, + { clientId: 'b', attributes: { width: 1 } }, + { clientId: 'c', attributes: { width: 1 } }, + ]; + const widths = getRedistributedColumnWidths( blocks, 75 ); + + expect( widths ).toEqual( { + a: 73.5, + b: 0.75, + c: 0.75, } ); } ); } ); diff --git a/packages/block-library/src/columns/utils.js b/packages/block-library/src/columns/utils.js index 7bf40d3eacdf63..0d9c5d9db59623 100644 --- a/packages/block-library/src/columns/utils.js +++ b/packages/block-library/src/columns/utils.js @@ -89,12 +89,11 @@ export function getRedistributedColumnWidths( totalBlockCount = blocks.length ) { const totalWidth = getTotalColumnsWidth( blocks, totalBlockCount ); - const difference = availableWidth - totalWidth; - const adjustment = difference / blocks.length; - return mapValues( getColumnWidths( blocks, totalBlockCount ), ( width ) => - toWidthPrecision( width + adjustment ) - ); + return mapValues( getColumnWidths( blocks, totalBlockCount ), ( width ) => { + const newWidth = ( availableWidth * width ) / totalWidth; + return toWidthPrecision( newWidth ); + } ); } /** @@ -163,7 +162,7 @@ export function getWidths( blocks, withParsing = true ) { export function getWidthWithUnit( width, unit ) { width = 0 > parseFloat( width ) ? '0' : width; - if ( unit === '%' ) { + if ( isPercentageUnit( unit ) ) { width = Math.min( width, 100 ); } @@ -199,3 +198,14 @@ export const CSS_UNITS = [ default: '', }, ]; + +/** + * Returns a boolean whether passed unit is percentage + * + * @param {string} unit Column width unit. + * + * @return {boolean} Whether unit is '%'. + */ +export function isPercentageUnit( unit ) { + return unit === '%'; +} diff --git a/packages/block-library/src/common.scss b/packages/block-library/src/common.scss new file mode 100644 index 00000000000000..bf437ce82cbb2f --- /dev/null +++ b/packages/block-library/src/common.scss @@ -0,0 +1,66 @@ +// The following selectors have increased specificity (using the :root prefix) +// to assure colors take effect over another base class color, mainly to let +// the colors override the added specificity by link states such as :hover. + +:root { + // Background colors. + @include background-colors(); + + // Foreground colors. + @include foreground-colors(); + + // Gradients + @include gradient-colors(); + + .has-link-color a { + color: var(--wp--style--color--link, #00e); + } +} + +// Font sizes. +.has-small-font-size { + font-size: 0.8125em; +} + +.has-regular-font-size, // Not used now, kept because of backward compatibility. +.has-normal-font-size { + font-size: 1em; +} + +.has-medium-font-size { + font-size: 1.25em; +} + +.has-large-font-size { + font-size: 2.25em; +} + +.has-larger-font-size, // Not used now, kept because of backward compatibility. +.has-huge-font-size { + font-size: 2.625em; +} + +// Text alignments. +.has-text-align-center { + text-align: center; +} + +.has-text-align-left { + /*rtl:ignore*/ + text-align: left; +} + +.has-text-align-right { + /*rtl:ignore*/ + text-align: right; +} + +// This tag marks the end of the styles that apply to editing canvas contents and need to be manipulated when we resize the editor. +#end-resizable-editor-section { + display: none; +} + +// Block alignments. +.aligncenter { + clear: both; +} diff --git a/packages/block-library/src/cover/block.json b/packages/block-library/src/cover/block.json index 29f1b17d7bb235..b0b2d58bde2aa8 100644 --- a/packages/block-library/src/cover/block.json +++ b/packages/block-library/src/cover/block.json @@ -57,5 +57,7 @@ "spacing": { "padding": true } - } + }, + "editorStyle": "wp-block-cover-editor", + "style": "wp-block-cover" } diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index ff9d9f8cd6eb47..00ea5c46633201 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -37,6 +37,7 @@ import { __experimentalPanelColorGradientSettings as PanelColorGradientSettings, __experimentalUnitControl as UnitControl, __experimentalBlockAlignmentMatrixToolbar as BlockAlignmentMatrixToolbar, + __experimentalBlockFullHeightAligmentToolbar as FullHeightAlignment, } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; import { withDispatch } from '@wordpress/data'; @@ -89,6 +90,7 @@ function CoverHeightInput( { value = '', } ) { const [ temporaryInput, setTemporaryInput ] = useState( null ); + const instanceId = useInstanceId( UnitControl ); const inputId = `block-cover-height-input-${ instanceId }`; const isPx = unit === 'px'; @@ -264,6 +266,39 @@ function CoverEdit( { const onSelectMedia = attributesFromMedia( setAttributes ); const isBlogUrl = isBlobURL( url ); + const [ prevMinHeightValue, setPrevMinHeightValue ] = useState( minHeight ); + const [ prevMinHeightUnit, setPrevMinHeightUnit ] = useState( + minHeightUnit + ); + const isMinFullHeight = minHeightUnit === 'vh' && minHeight === 100; + + const toggleMinFullHeight = () => { + if ( isMinFullHeight ) { + // If there aren't previous values, take the default ones. + if ( prevMinHeightUnit === 'vh' && prevMinHeightValue === 100 ) { + return setAttributes( { + minHeight: undefined, + minHeightUnit: undefined, + } ); + } + + // Set the previous values of height. + return setAttributes( { + minHeight: prevMinHeightValue, + minHeightUnit: prevMinHeightUnit, + } ); + } + + setPrevMinHeightValue( minHeight ); + setPrevMinHeightUnit( minHeightUnit ); + + // Set full height. + return setAttributes( { + minHeight: 100, + minHeightUnit: 'vh', + } ); + }; + const toggleParallax = () => { setAttributes( { hasParallax: ! hasParallax, @@ -322,6 +357,10 @@ function CoverEdit( { const controls = ( <> + { hasBackground && ( <> setAttributes( { minHeight: newMinHeight } ) } - onUnitChange={ ( nextUnit ) => { + onUnitChange={ ( nextUnit ) => setAttributes( { minHeightUnit: nextUnit, - } ); - } } + } ) + } /> { + if ( variation.isActive ) return; + variation.isActive = ( blockAttributes, variationAttributes ) => + blockAttributes.providerNameSlug === + variationAttributes.providerNameSlug; +} ); + export default variations; diff --git a/packages/block-library/src/file/block.json b/packages/block-library/src/file/block.json index 230942f76a6c36..ec42e3e31bf875 100644 --- a/packages/block-library/src/file/block.json +++ b/packages/block-library/src/file/block.json @@ -39,5 +39,7 @@ "supports": { "anchor": true, "align": true - } + }, + "editorStyle": "wp-block-file-editor", + "style": "wp-block-file" } diff --git a/packages/block-library/src/file/edit.js b/packages/block-library/src/file/edit.js index 488f9581ef3e03..c2364b581133e7 100644 --- a/packages/block-library/src/file/edit.js +++ b/packages/block-library/src/file/edit.js @@ -8,7 +8,7 @@ import classnames from 'classnames'; */ import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob'; import { - __unstableUseAnimate as useAnimate, + __unstableGetAnimateClassName as getAnimateClassName, withNotices, ToolbarGroup, ToolbarButton, @@ -129,7 +129,7 @@ function FileEdit( { attributes, setAttributes, noticeUI, noticeOperations } ) { const blockProps = useBlockProps( { className: classnames( - useAnimate( { type: isBlobURL( href ) ? 'loading' : null } ), + isBlobURL( href ) && getAnimateClassName( { type: 'loading' } ), { 'is-transient': isBlobURL( href ), } @@ -205,6 +205,7 @@ function FileEdit( { attributes, setAttributes, noticeUI, noticeOperations } ) { { /* Using RichText here instead of PlainText so that it can be styled like a button */ } - - { retryMessage } - - - ) + onShowLinkSettings() { + this.setState( + { + isSidebarLinkSettings: true, + }, + this.props.openSidebar ); } @@ -186,6 +231,11 @@ export class FileEdit extends Component { icon={ replace } onClick={ open } /> + ); @@ -199,6 +249,7 @@ export class FileEdit extends Component { ) { let linkDestinationOptions = [ { value: href, label: __( 'URL' ) } ]; const attachmentPage = media && media.link; + const { isSidebarLinkSettings } = this.state; if ( attachmentPage ) { linkDestinationOptions = [ @@ -222,7 +273,9 @@ export class FileEdit extends Component { return ( - + { isSidebarLinkSettings || ( + + ) } - + { ! isSidebarLinkSettings && ( + + ) } { + const textWidth = + nativeEvent.lines[ 0 ] && nativeEvent.lines[ 0 ].width; + if ( textWidth && textWidth !== placeholderTextWidth ) { + this.setState( { + placeholderTextWidth: Math.min( + textWidth, + maxWidth + ), + } ); + } + } } + > + { placeholderText } + + ); + } + getFileComponent( openMediaOptions, getMediaOptions ) { - const { attributes, media } = this.props; + const { attributes, media, isSelected } = this.props; + const { isButtonFocused, placeholderTextWidth } = this.state; const { fileName, @@ -296,13 +400,21 @@ export class FileEdit extends Component { align, } = attributes; - const dimmedStyle = - this.state.isUploadInProgress && styles.disabledButton; - const finalButtonStyle = Object.assign( - {}, - styles.defaultButton, - dimmedStyle - ); + const minWidth = + isButtonFocused || + ( ! isButtonFocused && + downloadButtonText && + downloadButtonText !== '' ) + ? MIN_WIDTH + : placeholderTextWidth; + + const placeholderText = + isButtonFocused || + ( ! isButtonFocused && + downloadButtonText && + downloadButtonText !== '' ) + ? '' + : __( 'Add text…' ); return ( {} } + onFinishMediaUploadWithFailure={ + this.finishMediaUploadWithFailure + } onMediaUploadStateReset={ this.mediaUploadStateReset } - renderContent={ ( { - isUploadInProgress, - isUploadFailed, - retryMessage, - } ) => { - if ( isUploadFailed ) { - return this.getErrorComponent( retryMessage ); - } + renderContent={ ( { isUploadInProgress, isUploadFailed } ) => { + const dimmedStyle = + ( this.state.isUploadInProgress || isUploadFailed ) && + styles.disabledButton; + const finalButtonStyle = [ + styles.defaultButton, + dimmedStyle, + ]; + + const errorIconStyle = Object.assign( + {}, + styles.errorIcon, + styles.uploadFailed + ); return ( - - { isUploadInProgress || - this.getToolbarEditButton( openMediaOptions ) } - { getMediaOptions() } - { this.getInspectorControls( - attributes, - media, - isUploadInProgress, - isUploadFailed - ) } - + + { this.getPlaceholderWidth( placeholderText ) } + { isUploadInProgress || + this.getToolbarEditButton( + openMediaOptions + ) } + { getMediaOptions() } + { this.getInspectorControls( + attributes, + media, + isUploadInProgress, + isUploadFailed ) } - /> - { showDownloadButton && ( - - + <RichText + withoutInteractiveFormatting + __unstableMobileNoFocusOnMount + onChange={ this.onChangeFileName } + placeholder={ __( 'File name' ) } + rootTagsToEliminate={ [ 'p' ] } + tagName="p" + underlineColorAndroid="transparent" + value={ fileName } + deleteEnter={ true } + textAlign={ this.getTextAlignmentForAlignment( + align + ) } /> + { isUploadFailed && ( + <View style={ styles.errorContainer }> + <Icon + icon={ warning } + style={ errorIconStyle } + /> + <PlainText + editable={ false } + value={ __( 'Error' ) } + style={ styles.uploadFailed } + /> + </View> + ) } </View> - ) } - </View> + { showDownloadButton && + this.state.maxWidth > 0 && ( + <View + style={ [ + finalButtonStyle, + this.getStyleForAlignment( + align + ), + ] } + > + <RichText + withoutInteractiveFormatting + __unstableMobileNoFocusOnMount + rootTagsToEliminate={ [ 'p' ] } + tagName="p" + textAlign="center" + minWidth={ minWidth } + maxWidth={ this.state.maxWidth } + deleteEnter={ true } + style={ styles.buttonText } + value={ downloadButtonText } + placeholder={ placeholderText } + unstableOnFocus={ () => + this.setState( { + isButtonFocused: true, + } ) + } + onBlur={ () => + this.setState( { + isButtonFocused: false, + } ) + } + selectionColor={ + styles.buttonText.color + } + placeholderTextColor={ + styles.placeholderTextColor + .color + } + underlineColorAndroid="transparent" + onChange={ + this + .onChangeDownloadButtonText + } + /> + </View> + ) } + </View> + </TouchableWithoutFeedback> ); } } /> @@ -383,14 +560,14 @@ export class FileEdit extends Component { } } onSelect={ this.onSelectFile } onFocus={ this.props.onFocus } - allowedTypes={ [ 'other' ] } + allowedTypes={ [ MEDIA_TYPE_ANY ] } /> ); } return ( <MediaUpload - allowedTypes={ [ 'other' ] } + allowedTypes={ [ MEDIA_TYPE_ANY ] } isReplacingMedia={ true } onSelect={ this.onSelectFile } render={ ( { open, getMediaOptions } ) => { @@ -404,10 +581,18 @@ export class FileEdit extends Component { export default compose( [ withSelect( ( select, props ) => { const { attributes } = props; - const { id } = attributes; + const { id, href } = attributes; + const { isEditorSidebarOpened } = select( 'core/edit-post' ); + const isNotFileHref = id && getProtocol( href ) !== 'file:'; + return { + media: isNotFileHref ? select( 'core' ).getMedia( id ) : undefined, + isSidebarOpened: isEditorSidebarOpened(), + }; + } ), + withDispatch( ( dispatch ) => { + const { openGeneralSidebar } = dispatch( 'core/edit-post' ); return { - media: - id === undefined ? undefined : select( 'core' ).getMedia( id ), + openSidebar: () => openGeneralSidebar( 'edit-post/block' ), }; } ), withPreferredColorScheme, diff --git a/packages/block-library/src/file/style.native.scss b/packages/block-library/src/file/style.native.scss index f3a83da6c8404c..957956ba645d80 100644 --- a/packages/block-library/src/file/style.native.scss +++ b/packages/block-library/src/file/style.native.scss @@ -1,7 +1,10 @@ +.container { + margin-top: 2px; +} + .defaultButton { border-radius: $border-width * 4; - padding: $block-spacing * 2; - border-width: $border-width; + padding: $grid-unit-10; margin-top: $grid-unit-20; background-color: $button-fallback-bg; } @@ -9,11 +12,14 @@ .buttonText { background-color: transparent; color: $white; - font-size: 16; + padding: 0; + font-size: 16px; + padding-left: $grid-unit-20; + padding-right: $grid-unit-20; } .disabledButton { - opacity: 0.25; + opacity: 0.45; } .uploadFailedText { @@ -34,3 +40,29 @@ .actionButtonDark { color: $blue-30; } + +.errorContainer { + flex-direction: row; + align-items: center; + padding-top: 4px; +} + +.errorIcon { + margin-left: -4px; +} + +.uploadFailed { + padding: 0; + color: $alert-red; +} + +.placeholderTextColor { + color: rgba($color: $white, $alpha: 0.43); +} + +.placeholder { + font-family: $default-regular-font; + min-height: 22px; + font-size: 16px; + display: none; +} diff --git a/packages/block-library/src/file/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/file/test/__snapshots__/edit.native.js.snap new file mode 100644 index 00000000000000..a4f88395a19e90 --- /dev/null +++ b/packages/block-library/src/file/test/__snapshots__/edit.native.js.snap @@ -0,0 +1,423 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`File block renders file error state without crashing 1`] = ` +<View + pointerEvents="box-none" +> + <View + style={ + Array [ + undefined, + undefined, + ] + } + /> + <View + accessible={true} + focusable={true} + onClick={[Function]} + onLayout={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + > + <Text + onTextLayout={[Function]} + style={ + Object { + "color": "gray", + } + } + > + + </Text> + Modal + <View + style={ + Object { + "height": 44, + } + } + > + <View> + <RCTAztecView + accessible={true} + activeFormats={Array []} + blockType={ + Object { + "tag": "p", + } + } + deleteEnter={true} + disableEditingMenu={false} + focusable={true} + fontFamily="serif" + isMultiline={false} + maxImagesWidth={200} + onBackspace={[Function]} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onContentSizeChange={[Function]} + onEnter={[Function]} + onFocus={[Function]} + onHTMLContentWithCursor={[Function]} + onKeyDown={[Function]} + onPaste={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onSelectionChange={[Function]} + onStartShouldSetResponder={[Function]} + placeholder="File name" + placeholderTextColor="gray" + style={ + Object { + "backgroundColor": undefined, + "maxWidth": undefined, + "minHeight": 0, + } + } + text={ + Object { + "eventCount": undefined, + "linkTextColor": undefined, + "selection": null, + "text": "<p >File name</p>", + } + } + textAlign="left" + triggerKeyCodes={Array []} + /> + </View> + <View> + Svg + <TextInput + allowFontScaling={true} + editable={false} + fontFamily="serif" + onChange={[Function]} + rejectResponderTermination={true} + scrollEnabled={false} + style={ + Object { + "fontFamily": "serif", + } + } + underlineColorAndroid="transparent" + value="Error" + /> + </View> + </View> + <View + style={ + Array [ + Array [ + Object { + "paddingLeft": 10, + "paddingRight": 10, + }, + undefined, + ], + Object { + "alignSelf": "flex-start", + }, + ] + } + > + <View> + <RCTAztecView + accessible={true} + activeFormats={Array []} + blockType={ + Object { + "tag": "p", + } + } + color="white" + deleteEnter={true} + disableEditingMenu={false} + focusable={true} + fontFamily="serif" + isMultiline={false} + maxImagesWidth={200} + minWidth={40} + onBackspace={[Function]} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onContentSizeChange={[Function]} + onEnter={[Function]} + onFocus={[Function]} + onHTMLContentWithCursor={[Function]} + onKeyDown={[Function]} + onPaste={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onSelectionChange={[Function]} + onStartShouldSetResponder={[Function]} + placeholder="" + placeholderTextColor="white" + selectionColor="white" + style={ + Object { + "backgroundColor": undefined, + "color": "white", + "maxWidth": 80, + "minHeight": 0, + } + } + text={ + Object { + "eventCount": undefined, + "linkTextColor": undefined, + "selection": null, + "text": "<p >Download</p>", + } + } + textAlign="center" + triggerKeyCodes={Array []} + /> + </View> + </View> + </View> +</View> +`; + +exports[`File block renders file without crashing 1`] = ` +<View + pointerEvents="box-none" +> + <View + style={ + Array [ + undefined, + undefined, + ] + } + /> + <View + accessible={true} + focusable={true} + onClick={[Function]} + onLayout={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + > + <Text + onTextLayout={[Function]} + style={ + Object { + "color": "gray", + } + } + > + + </Text> + Modal + <View + style={ + Object { + "height": 44, + } + } + > + <View> + <RCTAztecView + accessible={true} + activeFormats={Array []} + blockType={ + Object { + "tag": "p", + } + } + deleteEnter={true} + disableEditingMenu={false} + focusable={true} + fontFamily="serif" + isMultiline={false} + maxImagesWidth={200} + onBackspace={[Function]} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onContentSizeChange={[Function]} + onEnter={[Function]} + onFocus={[Function]} + onHTMLContentWithCursor={[Function]} + onKeyDown={[Function]} + onPaste={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onSelectionChange={[Function]} + onStartShouldSetResponder={[Function]} + placeholder="File name" + placeholderTextColor="gray" + style={ + Object { + "backgroundColor": undefined, + "maxWidth": undefined, + "minHeight": 0, + } + } + text={ + Object { + "eventCount": undefined, + "linkTextColor": undefined, + "selection": null, + "text": "<p >File name</p>", + } + } + textAlign="left" + triggerKeyCodes={Array []} + /> + </View> + </View> + <View + style={ + Array [ + Array [ + Object { + "paddingLeft": 10, + "paddingRight": 10, + }, + false, + ], + Object { + "alignSelf": "flex-start", + }, + ] + } + > + <View> + <RCTAztecView + accessible={true} + activeFormats={Array []} + blockType={ + Object { + "tag": "p", + } + } + color="white" + deleteEnter={true} + disableEditingMenu={false} + focusable={true} + fontFamily="serif" + isMultiline={false} + maxImagesWidth={200} + minWidth={40} + onBackspace={[Function]} + onBlur={[Function]} + onChange={[Function]} + onClick={[Function]} + onContentSizeChange={[Function]} + onEnter={[Function]} + onFocus={[Function]} + onHTMLContentWithCursor={[Function]} + onKeyDown={[Function]} + onPaste={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onSelectionChange={[Function]} + onStartShouldSetResponder={[Function]} + placeholder="" + placeholderTextColor="white" + selectionColor="white" + style={ + Object { + "backgroundColor": undefined, + "color": "white", + "maxWidth": 80, + "minHeight": 0, + } + } + text={ + Object { + "eventCount": undefined, + "linkTextColor": undefined, + "selection": null, + "text": "<p >Download</p>", + } + } + textAlign="center" + triggerKeyCodes={Array []} + /> + </View> + </View> + </View> +</View> +`; + +exports[`File block renders placeholder without crashing 1`] = ` +<View + style={ + Object { + "flex": 1, + } + } +> + <View + accessibilityHint="Double tap to select" + accessibilityLabel="File block. Empty" + accessibilityRole="button" + accessible={true} + focusable={true} + onClick={[Function]} + onResponderGrant={[Function]} + onResponderMove={[Function]} + onResponderRelease={[Function]} + onResponderTerminate={[Function]} + onResponderTerminationRequest={[Function]} + onStartShouldSetResponder={[Function]} + style={ + Array [ + Array [ + undefined, + undefined, + undefined, + ], + undefined, + ] + } + > + Modal + <View + style={ + Object { + "fill": "gray", + } + } + > + <View + style={Object {}} + > + Svg + </View> + </View> + <Text> + File + </Text> + <Text> + CHOOSE A FILE + </Text> + </View> +</View> +`; diff --git a/packages/block-library/src/file/test/edit.native.js b/packages/block-library/src/file/test/edit.native.js new file mode 100644 index 00000000000000..c9d5453ce8f5b8 --- /dev/null +++ b/packages/block-library/src/file/test/edit.native.js @@ -0,0 +1,71 @@ +/** + * External dependencies + */ +import renderer from 'react-test-renderer'; + +/** + * WordPress dependencies + */ +import { MediaUploadProgress } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { FileEdit } from '../edit.native.js'; + +const getTestComponentWithContent = ( attributes = {} ) => { + return renderer.create( + <FileEdit + attributes={ attributes } + setAttributes={ jest.fn() } + getMedia={ jest.fn() } + getStylesFromColorScheme={ jest.fn() } + /> + ); +}; + +describe( 'File block', () => { + it( 'renders placeholder without crashing', () => { + const component = getTestComponentWithContent(); + const rendered = component.toJSON(); + expect( rendered ).toMatchSnapshot(); + } ); + + it( 'renders file without crashing', () => { + const component = getTestComponentWithContent( { + showDownloadButton: true, + downloadButtonText: 'Download', + href: 'https://wordpress.org/latest.zip', + fileName: 'File name', + textLinkHref: 'https://wordpress.org/latest.zip', + id: '1', + } ); + + component + .getInstance() + .onLayout( { nativeEvent: { layout: { width: 100 } } } ); + + const rendered = component.toJSON(); + expect( rendered ).toMatchSnapshot(); + } ); + + it( 'renders file error state without crashing', () => { + const component = getTestComponentWithContent( { + showDownloadButton: true, + downloadButtonText: 'Download', + href: 'https://wordpress.org/latest.zip', + fileName: 'File name', + textLinkHref: 'https://wordpress.org/latest.zip', + id: '1', + } ); + component + .getInstance() + .onLayout( { nativeEvent: { layout: { width: 100 } } } ); + + const mediaUpload = component.root.findByType( MediaUploadProgress ); + mediaUpload.instance.finishMediaUploadWithFailure( { mediaId: -1 } ); + + const rendered = component.toJSON(); + expect( rendered ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/block-library/src/classic/block.json b/packages/block-library/src/freeform/block.json similarity index 83% rename from packages/block-library/src/classic/block.json rename to packages/block-library/src/freeform/block.json index 0ee6ba171b3f81..a18cd84635004a 100644 --- a/packages/block-library/src/classic/block.json +++ b/packages/block-library/src/freeform/block.json @@ -12,5 +12,6 @@ "className": false, "customClassName": false, "reusable": false - } + }, + "editorStyle": "wp-block-freeform-editor" } diff --git a/packages/block-library/src/classic/convert-to-blocks-button.js b/packages/block-library/src/freeform/convert-to-blocks-button.js similarity index 100% rename from packages/block-library/src/classic/convert-to-blocks-button.js rename to packages/block-library/src/freeform/convert-to-blocks-button.js diff --git a/packages/block-library/src/classic/edit.js b/packages/block-library/src/freeform/edit.js similarity index 100% rename from packages/block-library/src/classic/edit.js rename to packages/block-library/src/freeform/edit.js diff --git a/packages/block-library/src/classic/edit.native.js b/packages/block-library/src/freeform/edit.native.js similarity index 100% rename from packages/block-library/src/classic/edit.native.js rename to packages/block-library/src/freeform/edit.native.js diff --git a/packages/block-library/src/classic/editor.scss b/packages/block-library/src/freeform/editor.scss similarity index 100% rename from packages/block-library/src/classic/editor.scss rename to packages/block-library/src/freeform/editor.scss diff --git a/packages/block-library/src/classic/index.js b/packages/block-library/src/freeform/index.js similarity index 100% rename from packages/block-library/src/classic/index.js rename to packages/block-library/src/freeform/index.js diff --git a/packages/block-library/src/classic/save.js b/packages/block-library/src/freeform/save.js similarity index 100% rename from packages/block-library/src/classic/save.js rename to packages/block-library/src/freeform/save.js diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json index 07c4afcc1ff7e8..8beac02827f612 100644 --- a/packages/block-library/src/gallery/block.json +++ b/packages/block-library/src/gallery/block.json @@ -79,5 +79,7 @@ "supports": { "anchor": true, "align": true - } + }, + "editorStyle": "wp-block-gallery-editor", + "style": "wp-block-gallery" } diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 0e266524a2e706..8482e29812516c 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -30,7 +30,7 @@ import { InspectorControls, useBlockProps, } from '@wordpress/block-editor'; -import { Platform, useEffect, useState } from '@wordpress/element'; +import { Platform, useEffect, useState, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob'; import { useDispatch, withSelect } from '@wordpress/data'; @@ -281,7 +281,7 @@ function GalleryEdit( props ) { }; } ); - setAttributes( { images: updatedImages, newSizeSlug } ); + setAttributes( { images: updatedImages, sizeSlug: newSizeSlug } ); } useEffect( () => { @@ -414,45 +414,47 @@ export default compose( [ const { getSettings } = select( 'core/block-editor' ); const { imageSizes, mediaUpload } = getSettings(); - let resizedImages = {}; - - if ( isSelected ) { - resizedImages = reduce( - ids, - ( currentResizedImages, id ) => { - if ( ! id ) { - return currentResizedImages; - } - const image = getMedia( id ); - const sizes = reduce( - imageSizes, - ( currentSizes, size ) => { - const defaultUrl = get( image, [ - 'sizes', - size.slug, - 'url', - ] ); - const mediaDetailsUrl = get( image, [ - 'media_details', - 'sizes', - size.slug, - 'source_url', - ] ); - return { - ...currentSizes, - [ size.slug ]: defaultUrl || mediaDetailsUrl, - }; - }, - {} - ); - return { - ...currentResizedImages, - [ parseInt( id, 10 ) ]: sizes, - }; - }, - {} - ); - } + const resizedImages = useMemo( () => { + if ( isSelected ) { + return reduce( + ids, + ( currentResizedImages, id ) => { + if ( ! id ) { + return currentResizedImages; + } + const image = getMedia( id ); + const sizes = reduce( + imageSizes, + ( currentSizes, size ) => { + const defaultUrl = get( image, [ + 'sizes', + size.slug, + 'url', + ] ); + const mediaDetailsUrl = get( image, [ + 'media_details', + 'sizes', + size.slug, + 'source_url', + ] ); + return { + ...currentSizes, + [ size.slug ]: + defaultUrl || mediaDetailsUrl, + }; + }, + {} + ); + return { + ...currentResizedImages, + [ parseInt( id, 10 ) ]: sizes, + }; + }, + {} + ); + } + return {}; + }, [ isSelected, ids, imageSizes ] ); return { imageSizes, diff --git a/packages/block-library/src/gallery/editor.scss b/packages/block-library/src/gallery/editor.scss index 2205dfdb1e52f1..c67ce5bf3778d2 100644 --- a/packages/block-library/src/gallery/editor.scss +++ b/packages/block-library/src/gallery/editor.scss @@ -7,16 +7,11 @@ } // @todo: this deserves a refactor, by being moved to the toolbar. - .block-editor-media-placeholder { - margin-bottom: $grid-unit-15; - padding: $grid-unit-15; - - // This element is empty here anyway. + .block-editor-media-placeholder.is-appender { .components-placeholder__label { display: none; } - - .components-button { + .block-editor-media-placeholder__button { margin-bottom: 0; } } diff --git a/packages/block-library/src/gallery/gallery-image.js b/packages/block-library/src/gallery/gallery-image.js index 447d4febbb9e01..4dac396d289e0e 100644 --- a/packages/block-library/src/gallery/gallery-image.js +++ b/packages/block-library/src/gallery/gallery-image.js @@ -275,6 +275,7 @@ class GalleryImage extends Component { { ! isEditing && ( isSelected || caption ) && ( <RichText tagName="figcaption" + aria-label={ __( 'Image caption text' ) } placeholder={ isSelected ? __( 'Write caption…' ) : null } diff --git a/packages/block-library/src/gallery/gallery.js b/packages/block-library/src/gallery/gallery.js index 6638df41b74c6c..dc261bbf1e4699 100644 --- a/packages/block-library/src/gallery/gallery.js +++ b/packages/block-library/src/gallery/gallery.js @@ -96,6 +96,7 @@ export const Gallery = ( props ) => { isHidden={ ! isSelected && RichText.isEmpty( caption ) } tagName="figcaption" className="blocks-gallery-caption" + aria-label={ __( 'Gallery caption text' ) } placeholder={ __( 'Write gallery caption…' ) } value={ caption } unstableOnFocus={ onFocusGalleryCaption } diff --git a/packages/block-library/src/gallery/gallery.native.js b/packages/block-library/src/gallery/gallery.native.js index d465ca7daa4d9a..e83e89417177f0 100644 --- a/packages/block-library/src/gallery/gallery.native.js +++ b/packages/block-library/src/gallery/gallery.native.js @@ -20,7 +20,7 @@ import { BlockCaption } from '@wordpress/block-editor'; import { useState, useEffect } from '@wordpress/element'; import { mediaUploadSync } from '@wordpress/react-native-bridge'; import { useSelect } from '@wordpress/data'; -import { WIDE_ALIGNMENTS } from '@wordpress/components'; +import { alignmentHelpers } from '@wordpress/components'; const TILE_SPACING = 15; @@ -28,6 +28,8 @@ const TILE_SPACING = 15; const MAX_DISPLAYED_COLUMNS = 4; const MAX_DISPLAYED_COLUMNS_NARROW = 2; +const { isFullWidth } = alignmentHelpers; + export const Gallery = ( props ) => { const [ isCaptionSelected, setIsCaptionSelected ] = useState( false ); useEffect( mediaUploadSync, [] ); @@ -84,8 +86,6 @@ export const Gallery = ( props ) => { onFocusGalleryCaption(); }; - const isFullWidth = align === WIDE_ALIGNMENTS.alignments.full; - return ( <View style={ { flex: 1 } }> <Tiles @@ -131,7 +131,7 @@ export const Gallery = ( props ) => { ); } ) } </Tiles> - <View style={ [ isFullWidth && styles.fullWidth ] }> + <View style={ isFullWidth( align ) && styles.fullWidth }> { mediaPlaceholder } </View> <BlockCaption diff --git a/packages/block-library/src/group/block.json b/packages/block-library/src/group/block.json index 6014985192acfe..e0be130edc00a6 100644 --- a/packages/block-library/src/group/block.json +++ b/packages/block-library/src/group/block.json @@ -25,5 +25,7 @@ "spacing": { "padding": true } - } + }, + "editorStyle": "wp-block-group-editor", + "style": "wp-block-group" } diff --git a/packages/block-library/src/group/edit.native.js b/packages/block-library/src/group/edit.native.js index 2ec026b420a632..fbb2b6e1640653 100644 --- a/packages/block-library/src/group/edit.native.js +++ b/packages/block-library/src/group/edit.native.js @@ -1,22 +1,28 @@ /** * External dependencies */ -import { View } from 'react-native'; +import { View, Dimensions } from 'react-native'; /** * WordPress dependencies */ import { withSelect } from '@wordpress/data'; -import { compose, withPreferredColorScheme } from '@wordpress/compose'; +import { + compose, + withPreferredColorScheme, + useResizeObserver, +} from '@wordpress/compose'; import { InnerBlocks } from '@wordpress/block-editor'; import { useCallback } from '@wordpress/element'; -import { WIDE_ALIGNMENTS } from '@wordpress/components'; +import { alignmentHelpers } from '@wordpress/components'; /** * Internal dependencies */ import styles from './editor.scss'; +const { isWider, isFullWidth } = alignmentHelpers; + function GroupEdit( { attributes, hasInnerBlocks, @@ -26,19 +32,27 @@ function GroupEdit( { mergedStyle, } ) { const { align } = attributes; - const isFullWidth = align === WIDE_ALIGNMENTS.alignments.full; + const [ resizeObserver, sizes ] = useResizeObserver(); + const { width } = sizes || { width: 0 }; + const screenWidth = Math.floor( Dimensions.get( 'window' ).width ); + const isEqualWidth = width === screenWidth; const renderAppender = useCallback( () => ( <View - style={ [ - isFullWidth && hasInnerBlocks && styles.fullWidthAppender, - ] } + style={ + ( isWider( screenWidth, 'mobile' ) || + isEqualWidth || + isFullWidth( align ) ) && + ( hasInnerBlocks + ? styles.groupAppender + : styles.wideGroupAppender ) + } > <InnerBlocks.ButtonBlockAppender /> </View> ), - [ align, hasInnerBlocks ] + [ align, hasInnerBlocks, width ] ); if ( ! isSelected && ! hasInnerBlocks ) { @@ -62,10 +76,6 @@ function GroupEdit( { <View style={ [ isSelected && hasInnerBlocks && styles.innerBlocks, - isSelected && - ! hasInnerBlocks && - isFullWidth && - styles.fullWidth, mergedStyle, isSelected && hasInnerBlocks && @@ -76,7 +86,11 @@ function GroupEdit( { styles.isLastInnerBlockSelected, ] } > - <InnerBlocks renderAppender={ isSelected && renderAppender } /> + { resizeObserver } + <InnerBlocks + renderAppender={ isSelected && renderAppender } + parentWidth={ width } + /> </View> ); } @@ -87,7 +101,9 @@ export default compose( [ getBlock, getBlockIndex, hasSelectedInnerBlock, + getBlockRootClientId, getSelectedBlockClientId, + getBlockAttributes, } = select( 'core/block-editor' ); const block = getBlock( clientId ); @@ -104,9 +120,13 @@ export default compose( [ isLastInnerBlockSelected = totalInnerBlocks === blockIndex; } + const parentId = getBlockRootClientId( clientId ); + const parentBlockAlignment = getBlockAttributes( parentId )?.align; + return { hasInnerBlocks, isLastInnerBlockSelected, + parentBlockAlignment, }; } ), withPreferredColorScheme, diff --git a/packages/block-library/src/group/editor.native.scss b/packages/block-library/src/group/editor.native.scss index d33982b2b36bf9..af4616c8e74a5a 100644 --- a/packages/block-library/src/group/editor.native.scss +++ b/packages/block-library/src/group/editor.native.scss @@ -32,9 +32,14 @@ margin-right: $block-edge-to-content; } -.fullWidthAppender { - margin-left: $block-edge-to-content; - margin-right: $block-edge-to-content; +.groupAppender { + margin-left: $grid-unit-20; + margin-right: $grid-unit-20; +} + +.wideGroupAppender { + margin-left: $grid-unit-10; + margin-right: $grid-unit-10; } .hasBackgroundAppender { diff --git a/packages/block-library/src/group/theme.scss b/packages/block-library/src/group/theme.scss index 61e711c3aaa2f8..2aa1b4f5ade7e1 100644 --- a/packages/block-library/src/group/theme.scss +++ b/packages/block-library/src/group/theme.scss @@ -1,8 +1,7 @@ .wp-block-group { &.has-background { // Matches paragraph Block padding - // Todo: normalise with variables - padding: 20px 30px; + padding: $block-bg-padding--v $block-bg-padding--h; margin-top: 0; margin-bottom: 0; } diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json index fd8a683cfd404a..6cd496431f799d 100644 --- a/packages/block-library/src/heading/block.json +++ b/packages/block-library/src/heading/block.json @@ -74,5 +74,7 @@ } }, "__unstablePasteTextInline": true - } + }, + "editorStyle": "wp-block-heading-editor", + "style": "wp-block-heading" } diff --git a/packages/block-library/src/heading/edit.js b/packages/block-library/src/heading/edit.js index ed15802b0bb493..f798d8501b3e13 100644 --- a/packages/block-library/src/heading/edit.js +++ b/packages/block-library/src/heading/edit.js @@ -73,6 +73,7 @@ function HeadingEdit( { } } onReplace={ onReplace } onRemove={ () => onReplace( [] ) } + aria-label={ __( 'Heading text' ) } placeholder={ placeholder || __( 'Write heading…' ) } textAlign={ textAlign } { ...blockProps } diff --git a/packages/block-library/src/html/block.json b/packages/block-library/src/html/block.json index 13aa611346e07e..266b4511e0fe5b 100644 --- a/packages/block-library/src/html/block.json +++ b/packages/block-library/src/html/block.json @@ -12,5 +12,6 @@ "customClassName": false, "className": false, "html": false - } + }, + "editorStyle": "wp-block-html-editor" } diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index ec80dc4c9ed250..02cfd21dfd9cd5 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -72,5 +72,7 @@ }, "supports": { "anchor": true - } + }, + "editorStyle": "wp-block-image-editor", + "style": "wp-block-image" } diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index e48e0bb4015448..263bd1d2d263f1 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -20,11 +20,11 @@ import { Icon, PanelBody, TextControl, - ToggleControl, ToolbarButton, ToolbarGroup, Image, WIDE_ALIGNMENTS, + LinkSettingsNavigation, } from '@wordpress/components'; import { BlockCaption, @@ -43,8 +43,6 @@ import { doAction, hasAction } from '@wordpress/hooks'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; import { - external, - link, image as placeholderIcon, textColor, replace, @@ -338,6 +336,49 @@ export class ImageEdit extends React.Component { : width; } + getLinkSettings() { + const { isLinkSheetVisible } = this.state; + const { + attributes: { href: url, ...unMappedAttributes }, + setAttributes, + } = this.props; + + const mappedAttributes = { ...unMappedAttributes, url }; + const setMappedAttributes = ( { url: href, ...restAttributes } ) => + href === undefined + ? setAttributes( restAttributes ) + : setAttributes( { ...restAttributes, href } ); + + const options = { + url: { + label: __( 'Image Link URL' ), + placeholder: __( 'Add URL' ), + autoFocus: false, + autoFill: true, + }, + openInNewTab: { + label: __( 'Open in new tab' ), + }, + linkRel: { + label: __( 'Link Rel' ), + placeholder: __( 'None' ), + }, + }; + + return ( + <LinkSettingsNavigation + isVisible={ isLinkSheetVisible } + attributes={ mappedAttributes } + onClose={ this.dismissSheet } + setAttributes={ setMappedAttributes } + withBottomSheet={ false } + hasPicker + options={ options } + showIcon={ false } + /> + ); + } + render() { const { isCaptionSelected } = this.state; const { @@ -347,16 +388,7 @@ export class ImageEdit extends React.Component { imageSizes, clientId, } = this.props; - const { - align, - url, - alt, - href, - id, - linkTarget, - sizeSlug, - className, - } = attributes; + const { align, url, alt, id, sizeSlug, className } = attributes; const sizeOptions = map( imageSizes, ( { name, slug } ) => ( { value: slug, @@ -392,22 +424,6 @@ export class ImageEdit extends React.Component { ) } </PanelBody> <PanelBody> - <TextControl - icon={ link } - label={ __( 'Link To' ) } - value={ href || '' } - valuePlaceholder={ __( 'Add URL' ) } - onChange={ this.onSetLinkDestination } - autoCapitalize="none" - autoCorrect={ false } - keyboardType="url" - /> - <ToggleControl - icon={ external } - label={ __( 'Open in new tab' ) } - checked={ linkTarget === '_blank' } - onChange={ this.onSetNewTab } - /> { image && sizeOptionsValid && ( <CycleSelectControl icon={ expand } @@ -427,6 +443,9 @@ export class ImageEdit extends React.Component { onChangeValue={ this.updateAlt } /> </PanelBody> + <PanelBody title={ __( 'Link Settings' ) }> + { this.getLinkSettings( true ) } + </PanelBody> </InspectorControls> ); diff --git a/packages/block-library/src/image/editor.scss b/packages/block-library/src/image/editor.scss index a8ead3186e057d..563f68b9541c70 100644 --- a/packages/block-library/src/image/editor.scss +++ b/packages/block-library/src/image/editor.scss @@ -106,6 +106,7 @@ figure.wp-block-image:not(.wp-block) { .wp-block-image__zoom { .components-popover__content { overflow: visible; + min-width: 260px; } .components-range-control { @@ -115,6 +116,8 @@ figure.wp-block-image:not(.wp-block) { .components-base-control__field { display: flex; margin-bottom: 0; + flex-direction: column; + align-items: flex-start; } } diff --git a/packages/block-library/src/image/image-editing/aspect-ratio-dropdown.js b/packages/block-library/src/image/image-editing/aspect-ratio-dropdown.js new file mode 100644 index 00000000000000..978df291303099 --- /dev/null +++ b/packages/block-library/src/image/image-editing/aspect-ratio-dropdown.js @@ -0,0 +1,129 @@ +/** + * WordPress dependencies + */ +import { check, aspectRatio as aspectRatioIcon } from '@wordpress/icons'; +import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { POPOVER_PROPS } from './constants'; +import { useImageEditingContext } from './context'; + +function AspectGroup( { aspectRatios, isDisabled, label, onClick, value } ) { + return ( + <MenuGroup label={ label }> + { aspectRatios.map( ( { title, aspect } ) => ( + <MenuItem + key={ aspect } + disabled={ isDisabled } + onClick={ () => { + onClick( aspect ); + } } + role="menuitemradio" + isSelected={ aspect === value } + icon={ aspect === value ? check : undefined } + > + { title } + </MenuItem> + ) ) } + </MenuGroup> + ); +} + +export default function AspectRatioDropdown( { toggleProps } ) { + const { + isInProgress, + aspect, + setAspect, + defaultAspect, + } = useImageEditingContext(); + + return ( + <DropdownMenu + icon={ aspectRatioIcon } + label={ __( 'Aspect Ratio' ) } + popoverProps={ POPOVER_PROPS } + toggleProps={ toggleProps } + className="wp-block-image__aspect-ratio" + > + { ( { onClose } ) => ( + <> + <AspectGroup + isDisabled={ isInProgress } + onClick={ ( newAspect ) => { + setAspect( newAspect ); + onClose(); + } } + value={ aspect } + aspectRatios={ [ + { + title: __( 'Original' ), + aspect: defaultAspect, + }, + { + title: __( 'Square' ), + aspect: 1, + }, + ] } + /> + <AspectGroup + label={ __( 'Landscape' ) } + isDisabled={ isInProgress } + onClick={ ( newAspect ) => { + setAspect( newAspect ); + onClose(); + } } + value={ aspect } + aspectRatios={ [ + { + title: __( '16:10' ), + aspect: 16 / 10, + }, + { + title: __( '16:9' ), + aspect: 16 / 9, + }, + { + title: __( '4:3' ), + aspect: 4 / 3, + }, + { + title: __( '3:2' ), + aspect: 3 / 2, + }, + ] } + /> + <AspectGroup + label={ __( 'Portrait' ) } + isDisabled={ isInProgress } + onClick={ ( newAspect ) => { + setAspect( newAspect ); + onClose(); + } } + value={ aspect } + aspectRatios={ [ + { + title: __( '10:16' ), + aspect: 10 / 16, + }, + { + title: __( '9:16' ), + aspect: 9 / 16, + }, + { + title: __( '3:4' ), + aspect: 3 / 4, + }, + { + title: __( '2:3' ), + aspect: 2 / 3, + }, + ] } + /> + </> + ) } + </DropdownMenu> + ); +} diff --git a/packages/block-library/src/image/image-editing/constants.js b/packages/block-library/src/image/image-editing/constants.js new file mode 100644 index 00000000000000..0692a76895ca1f --- /dev/null +++ b/packages/block-library/src/image/image-editing/constants.js @@ -0,0 +1,6 @@ +export const MIN_ZOOM = 100; +export const MAX_ZOOM = 300; +export const POPOVER_PROPS = { + position: 'bottom right', + isAlternate: true, +}; diff --git a/packages/block-library/src/image/image-editing/context.js b/packages/block-library/src/image/image-editing/context.js new file mode 100644 index 00000000000000..ed11045c819da9 --- /dev/null +++ b/packages/block-library/src/image/image-editing/context.js @@ -0,0 +1,56 @@ +/** + * WordPress dependencies + */ +import { createContext, useContext, useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import useSaveImage from './use-save-image'; +import useTransformImage from './use-transform-image'; + +const ImageEditingContext = createContext( {} ); + +export const useImageEditingContext = () => useContext( ImageEditingContext ); + +export default function ImageEditingProvider( { + id, + url, + naturalWidth, + naturalHeight, + isEditing, + onFinishEditing, + onSaveImage, + children, +} ) { + const transformImage = useTransformImage( + { + url, + naturalWidth, + naturalHeight, + }, + isEditing + ); + + const saveImage = useSaveImage( { + id, + url, + onSaveImage, + onFinishEditing, + ...transformImage, + } ); + + const providerValue = useMemo( + () => ( { + ...transformImage, + ...saveImage, + } ), + [ transformImage, saveImage ] + ); + + return ( + <ImageEditingContext.Provider value={ providerValue }> + { children } + </ImageEditingContext.Provider> + ); +} diff --git a/packages/block-library/src/image/image-editing/cropper.js b/packages/block-library/src/image/image-editing/cropper.js new file mode 100644 index 00000000000000..24e56573455f9f --- /dev/null +++ b/packages/block-library/src/image/image-editing/cropper.js @@ -0,0 +1,74 @@ +/** + * External dependencies + */ +import Cropper from 'react-easy-crop'; +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { Spinner } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { MIN_ZOOM, MAX_ZOOM } from './constants'; + +import { useImageEditingContext } from './context'; + +export default function ImageCropper( { + url, + width, + height, + clientWidth, + naturalHeight, + naturalWidth, +} ) { + const { + isInProgress, + editedUrl, + position, + zoom, + aspect, + setPosition, + setCrop, + setZoom, + rotation, + } = useImageEditingContext(); + + let editedHeight = height || ( clientWidth * naturalHeight ) / naturalWidth; + + if ( rotation % 180 === 90 ) { + editedHeight = ( clientWidth * naturalWidth ) / naturalHeight; + } + + return ( + <div + className={ classnames( 'wp-block-image__crop-area', { + 'is-applying': isInProgress, + } ) } + style={ { + width, + height: editedHeight, + } } + > + <Cropper + image={ editedUrl || url } + disabled={ isInProgress } + minZoom={ MIN_ZOOM / 100 } + maxZoom={ MAX_ZOOM / 100 } + crop={ position } + zoom={ zoom / 100 } + aspect={ aspect } + onCropChange={ setPosition } + onCropComplete={ ( newCropPercent ) => { + setCrop( newCropPercent ); + } } + onZoomChange={ ( newZoom ) => { + setZoom( newZoom * 100 ); + } } + /> + { isInProgress && <Spinner /> } + </div> + ); +} diff --git a/packages/block-library/src/image/image-editing/form-controls.js b/packages/block-library/src/image/image-editing/form-controls.js new file mode 100644 index 00000000000000..50bb37e08b5eb1 --- /dev/null +++ b/packages/block-library/src/image/image-editing/form-controls.js @@ -0,0 +1,22 @@ +/** + * WordPress dependencies + */ +import { ToolbarButton } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { useImageEditingContext } from './context'; + +export default function FormControls() { + const { isInProgress, apply, cancel } = useImageEditingContext(); + return ( + <> + <ToolbarButton onClick={ apply } disabled={ isInProgress }> + { __( 'Apply' ) } + </ToolbarButton> + <ToolbarButton onClick={ cancel }>{ __( 'Cancel' ) }</ToolbarButton> + </> + ); +} diff --git a/packages/block-library/src/image/image-editing/index.js b/packages/block-library/src/image/image-editing/index.js new file mode 100644 index 00000000000000..223c291f191aae --- /dev/null +++ b/packages/block-library/src/image/image-editing/index.js @@ -0,0 +1,54 @@ +/** + * WordPress dependencies + */ +import { BlockControls } from '@wordpress/block-editor'; +import { ToolbarGroup, ToolbarItem } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import Cropper from './cropper'; +import ZoomDropdown from './zoom-dropdown'; +import AspectRatioDropdown from './aspect-ratio-dropdown'; +import RotationButton from './rotation-button'; +import FormControls from './form-controls'; + +export default function ImageEditor( { + url, + width, + height, + clientWidth, + naturalHeight, + naturalWidth, +} ) { + return ( + <> + <Cropper + url={ url } + width={ width } + height={ height } + clientWidth={ clientWidth } + naturalHeight={ naturalHeight } + naturalWidth={ naturalWidth } + /> + <BlockControls> + <ToolbarGroup> + <ZoomDropdown /> + <ToolbarItem> + { ( toggleProps ) => ( + <AspectRatioDropdown toggleProps={ toggleProps } /> + ) } + </ToolbarItem> + </ToolbarGroup> + <ToolbarGroup> + <RotationButton /> + </ToolbarGroup> + <ToolbarGroup> + <FormControls /> + </ToolbarGroup> + </BlockControls> + </> + ); +} + +export { default as ImageEditingProvider } from './context'; diff --git a/packages/block-library/src/image/image-editing/rotation-button.js b/packages/block-library/src/image/image-editing/rotation-button.js new file mode 100644 index 00000000000000..b29108f72e7e9c --- /dev/null +++ b/packages/block-library/src/image/image-editing/rotation-button.js @@ -0,0 +1,24 @@ +/** + * WordPress dependencies + */ + +import { ToolbarButton } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { rotateRight as rotateRightIcon } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { useImageEditingContext } from './context'; + +export default function RotationButton() { + const { isInProgress, rotateClockwise } = useImageEditingContext(); + return ( + <ToolbarButton + icon={ rotateRightIcon } + label={ __( 'Rotate' ) } + onClick={ rotateClockwise } + disabled={ isInProgress } + /> + ); +} diff --git a/packages/block-library/src/image/image-editing/use-save-image.js b/packages/block-library/src/image/image-editing/use-save-image.js new file mode 100644 index 00000000000000..3f95f45a2abfca --- /dev/null +++ b/packages/block-library/src/image/image-editing/use-save-image.js @@ -0,0 +1,97 @@ +/** + * WordPress dependencies + */ +import apiFetch from '@wordpress/api-fetch'; +import { useDispatch } from '@wordpress/data'; +import { useCallback, useMemo, useState } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; + +export default function useSaveImage( { + crop, + rotation, + height, + width, + aspect, + url, + id, + onSaveImage, + onFinishEditing, +} ) { + const { createErrorNotice } = useDispatch( noticesStore ); + const [ isInProgress, setIsInProgress ] = useState( false ); + + const cancel = useCallback( () => { + setIsInProgress( false ); + onFinishEditing(); + }, [ setIsInProgress, onFinishEditing ] ); + + const apply = useCallback( () => { + setIsInProgress( true ); + + let attrs = {}; + + // The crop script may return some very small, sub-pixel values when the image was not cropped. + // Crop only when the new size has changed by more than 0.1%. + if ( crop.width < 99.9 || crop.height < 99.9 ) { + attrs = crop; + } + + if ( rotation > 0 ) { + attrs.rotation = rotation; + } + + attrs.src = url; + + apiFetch( { + path: `/wp/v2/media/${ id }/edit`, + method: 'POST', + data: attrs, + } ) + .then( ( response ) => { + onSaveImage( { + id: response.id, + url: response.source_url, + height: height && width ? width / aspect : undefined, + } ); + } ) + .catch( ( error ) => { + createErrorNotice( + sprintf( + /* translators: 1. Error message */ + __( 'Could not edit image. %s' ), + error.message + ), + { + id: 'image-editing-error', + type: 'snackbar', + } + ); + } ) + .finally( () => { + setIsInProgress( false ); + onFinishEditing(); + } ); + }, [ + setIsInProgress, + crop, + rotation, + height, + width, + aspect, + url, + onSaveImage, + createErrorNotice, + setIsInProgress, + onFinishEditing, + ] ); + + return useMemo( + () => ( { + isInProgress, + apply, + cancel, + } ), + [ isInProgress, apply, cancel ] + ); +} diff --git a/packages/block-library/src/image/image-editing/use-transform-image.js b/packages/block-library/src/image/image-editing/use-transform-image.js new file mode 100644 index 00000000000000..4ce55a921f9ced --- /dev/null +++ b/packages/block-library/src/image/image-editing/use-transform-image.js @@ -0,0 +1,152 @@ +/** + * WordPress dependencies + */ +import { useCallback, useEffect, useMemo, useState } from '@wordpress/element'; + +function useTransformState( { url, naturalWidth, naturalHeight } ) { + const [ editedUrl, setEditedUrl ] = useState(); + const [ crop, setCrop ] = useState(); + const [ position, setPosition ] = useState( { x: 0, y: 0 } ); + const [ zoom, setZoom ] = useState(); + const [ rotation, setRotation ] = useState(); + const [ aspect, setAspect ] = useState(); + const [ defaultAspect, setDefaultAspect ] = useState(); + + const initializeTransformValues = useCallback( () => { + setPosition( { x: 0, y: 0 } ); + setZoom( 100 ); + setRotation( 0 ); + setAspect( naturalWidth / naturalHeight ); + setDefaultAspect( naturalWidth / naturalHeight ); + }, [ + naturalWidth, + naturalHeight, + setPosition, + setZoom, + setRotation, + setAspect, + setDefaultAspect, + ] ); + + const rotateClockwise = useCallback( () => { + const angle = ( rotation + 90 ) % 360; + + let naturalAspectRatio = naturalWidth / naturalHeight; + + if ( rotation % 180 === 90 ) { + naturalAspectRatio = naturalHeight / naturalWidth; + } + + if ( angle === 0 ) { + setEditedUrl(); + setRotation( angle ); + setAspect( 1 / aspect ); + setPosition( { + x: -( position.y * naturalAspectRatio ), + y: position.x * naturalAspectRatio, + } ); + return; + } + + function editImage( event ) { + const canvas = document.createElement( 'canvas' ); + + let translateX = 0; + let translateY = 0; + + if ( angle % 180 ) { + canvas.width = event.target.height; + canvas.height = event.target.width; + } else { + canvas.width = event.target.width; + canvas.height = event.target.height; + } + + if ( angle === 90 || angle === 180 ) { + translateX = canvas.width; + } + + if ( angle === 270 || angle === 180 ) { + translateY = canvas.height; + } + + const context = canvas.getContext( '2d' ); + + context.translate( translateX, translateY ); + context.rotate( ( angle * Math.PI ) / 180 ); + context.drawImage( event.target, 0, 0 ); + + canvas.toBlob( ( blob ) => { + setEditedUrl( URL.createObjectURL( blob ) ); + setRotation( angle ); + setAspect( 1 / aspect ); + setPosition( { + x: -( position.y * naturalAspectRatio ), + y: position.x * naturalAspectRatio, + } ); + } ); + } + + const el = new window.Image(); + el.src = url; + el.onload = editImage; + }, [ + rotation, + naturalWidth, + naturalHeight, + setEditedUrl, + setRotation, + setAspect, + setPosition, + ] ); + + return useMemo( + () => ( { + editedUrl, + setEditedUrl, + crop, + setCrop, + position, + setPosition, + zoom, + setZoom, + rotation, + setRotation, + rotateClockwise, + aspect, + setAspect, + defaultAspect, + initializeTransformValues, + } ), + [ + editedUrl, + setEditedUrl, + crop, + setCrop, + position, + setPosition, + zoom, + setZoom, + rotation, + setRotation, + rotateClockwise, + aspect, + setAspect, + defaultAspect, + initializeTransformValues, + ] + ); +} + +export default function useTransformImage( imageProperties, isEditing ) { + const transformState = useTransformState( imageProperties ); + const { initializeTransformValues } = transformState; + + useEffect( () => { + if ( isEditing ) { + initializeTransformValues(); + } + }, [ isEditing, initializeTransformValues ] ); + + return transformState; +} diff --git a/packages/block-library/src/image/image-editing/zoom-dropdown.js b/packages/block-library/src/image/image-editing/zoom-dropdown.js new file mode 100644 index 00000000000000..2694382e3860c2 --- /dev/null +++ b/packages/block-library/src/image/image-editing/zoom-dropdown.js @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { ToolbarButton, RangeControl, Dropdown } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { search } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { MIN_ZOOM, MAX_ZOOM, POPOVER_PROPS } from './constants'; +import { useImageEditingContext } from './context'; + +export default function ZoomDropdown() { + const { isInProgress, zoom, setZoom } = useImageEditingContext(); + return ( + <Dropdown + contentClassName="wp-block-image__zoom" + popoverProps={ POPOVER_PROPS } + renderToggle={ ( { isOpen, onToggle } ) => ( + <ToolbarButton + icon={ search } + label={ __( 'Zoom' ) } + onClick={ onToggle } + aria-expanded={ isOpen } + disabled={ isInProgress } + /> + ) } + renderContent={ () => ( + <RangeControl + label={ __( 'Zoom' ) } + min={ MIN_ZOOM } + max={ MAX_ZOOM } + value={ Math.round( zoom ) } + onChange={ setZoom } + /> + ) } + /> + ); +} diff --git a/packages/block-library/src/image/image-editor.js b/packages/block-library/src/image/image-editor.js deleted file mode 100644 index a3401132390fb3..00000000000000 --- a/packages/block-library/src/image/image-editor.js +++ /dev/null @@ -1,376 +0,0 @@ -/** - * External dependencies - */ - -import Cropper from 'react-easy-crop'; -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ - -import { BlockControls } from '@wordpress/block-editor'; -import { useState } from '@wordpress/element'; -import { - search, - check, - rotateRight as rotateRightIcon, - aspectRatio as aspectRatioIcon, -} from '@wordpress/icons'; -import { - ToolbarGroup, - ToolbarButton, - ToolbarItem, - Spinner, - RangeControl, - DropdownMenu, - MenuGroup, - MenuItem, - Dropdown, -} from '@wordpress/components'; -import { __, sprintf } from '@wordpress/i18n'; -import { useDispatch } from '@wordpress/data'; -import apiFetch from '@wordpress/api-fetch'; - -const MIN_ZOOM = 100; -const MAX_ZOOM = 300; -const POPOVER_PROPS = { - position: 'bottom right', - isAlternate: true, -}; - -function AspectGroup( { aspectRatios, isDisabled, label, onClick, value } ) { - return ( - <MenuGroup label={ label }> - { aspectRatios.map( ( { title, aspect } ) => ( - <MenuItem - key={ aspect } - disabled={ isDisabled } - onClick={ () => { - onClick( aspect ); - } } - role="menuitemradio" - isSelected={ aspect === value } - icon={ aspect === value ? check : undefined } - > - { title } - </MenuItem> - ) ) } - </MenuGroup> - ); -} - -function AspectMenu( { - toggleProps, - isDisabled, - onClick, - value, - defaultValue, -} ) { - return ( - <DropdownMenu - icon={ aspectRatioIcon } - label={ __( 'Aspect Ratio' ) } - popoverProps={ POPOVER_PROPS } - toggleProps={ toggleProps } - className="wp-block-image__aspect-ratio" - > - { ( { onClose } ) => ( - <> - <AspectGroup - isDisabled={ isDisabled } - onClick={ ( aspect ) => { - onClick( aspect ); - onClose(); - } } - value={ value } - aspectRatios={ [ - { - title: __( 'Original' ), - aspect: defaultValue, - }, - { - title: __( 'Square' ), - aspect: 1, - }, - ] } - /> - <AspectGroup - label={ __( 'Landscape' ) } - isDisabled={ isDisabled } - onClick={ ( aspect ) => { - onClick( aspect ); - onClose(); - } } - value={ value } - aspectRatios={ [ - { - title: __( '16:10' ), - aspect: 16 / 10, - }, - { - title: __( '16:9' ), - aspect: 16 / 9, - }, - { - title: __( '4:3' ), - aspect: 4 / 3, - }, - { - title: __( '3:2' ), - aspect: 3 / 2, - }, - ] } - /> - <AspectGroup - label={ __( 'Portrait' ) } - isDisabled={ isDisabled } - onClick={ ( aspect ) => { - onClick( aspect ); - onClose(); - } } - value={ value } - aspectRatios={ [ - { - title: __( '10:16' ), - aspect: 10 / 16, - }, - { - title: __( '9:16' ), - aspect: 9 / 16, - }, - { - title: __( '3:4' ), - aspect: 3 / 4, - }, - { - title: __( '2:3' ), - aspect: 2 / 3, - }, - ] } - /> - </> - ) } - </DropdownMenu> - ); -} - -export default function ImageEditor( { - id, - url, - setAttributes, - naturalWidth, - naturalHeight, - width, - height, - clientWidth, - setIsEditingImage, -} ) { - const { createErrorNotice } = useDispatch( 'core/notices' ); - const [ inProgress, setIsProgress ] = useState( false ); - const [ crop, setCrop ] = useState( null ); - const [ position, setPosition ] = useState( { x: 0, y: 0 } ); - const [ zoom, setZoom ] = useState( 100 ); - const [ aspect, setAspect ] = useState( naturalWidth / naturalHeight ); - const [ rotation, setRotation ] = useState( 0 ); - const [ editedUrl, setEditedUrl ] = useState(); - - const editedWidth = width; - let editedHeight = height || ( clientWidth * naturalHeight ) / naturalWidth; - let naturalAspectRatio = naturalWidth / naturalHeight; - - if ( rotation % 180 === 90 ) { - editedHeight = ( clientWidth * naturalWidth ) / naturalHeight; - naturalAspectRatio = naturalHeight / naturalWidth; - } - - function apply() { - setIsProgress( true ); - - let attrs = {}; - - // The crop script may return some very small, sub-pixel values when the image was not cropped. - // Crop only when the new size has changed by more than 0.1%. - if ( crop.width < 99.9 || crop.height < 99.9 ) { - attrs = crop; - } - - if ( rotation > 0 ) { - attrs.rotation = rotation; - } - - attrs.src = url; - - apiFetch( { - path: `/wp/v2/media/${ id }/edit`, - method: 'POST', - data: attrs, - } ) - .then( ( response ) => { - setAttributes( { - id: response.id, - url: response.source_url, - height: height && width ? width / aspect : undefined, - } ); - } ) - .catch( ( error ) => { - createErrorNotice( - sprintf( - /* translators: 1. Error message */ - __( 'Could not edit image. %s' ), - error.message - ), - { - id: 'image-editing-error', - type: 'snackbar', - } - ); - } ) - .finally( () => { - setIsProgress( false ); - setIsEditingImage( false ); - } ); - } - - function rotate() { - const angle = ( rotation + 90 ) % 360; - - if ( angle === 0 ) { - setEditedUrl(); - setRotation( angle ); - setAspect( 1 / aspect ); - setPosition( { - x: -( position.y * naturalAspectRatio ), - y: position.x * naturalAspectRatio, - } ); - return; - } - - function editImage( event ) { - const canvas = document.createElement( 'canvas' ); - - let translateX = 0; - let translateY = 0; - - if ( angle % 180 ) { - canvas.width = event.target.height; - canvas.height = event.target.width; - } else { - canvas.width = event.target.width; - canvas.height = event.target.height; - } - - if ( angle === 90 || angle === 180 ) { - translateX = canvas.width; - } - - if ( angle === 270 || angle === 180 ) { - translateY = canvas.height; - } - - const context = canvas.getContext( '2d' ); - - context.translate( translateX, translateY ); - context.rotate( ( angle * Math.PI ) / 180 ); - context.drawImage( event.target, 0, 0 ); - - canvas.toBlob( ( blob ) => { - setEditedUrl( URL.createObjectURL( blob ) ); - setRotation( angle ); - setAspect( 1 / aspect ); - setPosition( { - x: -( position.y * naturalAspectRatio ), - y: position.x * naturalAspectRatio, - } ); - } ); - } - - const el = new window.Image(); - el.src = url; - el.onload = editImage; - } - - return ( - <> - <div - className={ classnames( 'wp-block-image__crop-area', { - 'is-applying': inProgress, - } ) } - style={ { - width: editedWidth, - height: editedHeight, - } } - > - <Cropper - image={ editedUrl || url } - disabled={ inProgress } - minZoom={ MIN_ZOOM / 100 } - maxZoom={ MAX_ZOOM / 100 } - crop={ position } - zoom={ zoom / 100 } - aspect={ aspect } - onCropChange={ setPosition } - onCropComplete={ ( newCropPercent ) => { - setCrop( newCropPercent ); - } } - onZoomChange={ ( newZoom ) => { - setZoom( newZoom * 100 ); - } } - /> - { inProgress && <Spinner /> } - </div> - <BlockControls> - <ToolbarGroup> - <Dropdown - contentClassName="wp-block-image__zoom" - popoverProps={ POPOVER_PROPS } - renderToggle={ ( { isOpen, onToggle } ) => ( - <ToolbarButton - icon={ search } - label={ __( 'Zoom' ) } - onClick={ onToggle } - aria-expanded={ isOpen } - disabled={ inProgress } - /> - ) } - renderContent={ () => ( - <RangeControl - min={ MIN_ZOOM } - max={ MAX_ZOOM } - value={ Math.round( zoom ) } - onChange={ setZoom } - /> - ) } - /> - <ToolbarItem> - { ( toggleProps ) => ( - <AspectMenu - toggleProps={ toggleProps } - isDisabled={ inProgress } - onClick={ setAspect } - value={ aspect } - defaultValue={ naturalWidth / naturalHeight } - /> - ) } - </ToolbarItem> - </ToolbarGroup> - <ToolbarGroup> - <ToolbarButton - icon={ rotateRightIcon } - label={ __( 'Rotate' ) } - onClick={ rotate } - disabled={ inProgress } - /> - </ToolbarGroup> - <ToolbarGroup> - <ToolbarButton onClick={ apply } disabled={ inProgress }> - { __( 'Apply' ) } - </ToolbarButton> - <ToolbarButton onClick={ () => setIsEditingImage( false ) }> - { __( 'Cancel' ) } - </ToolbarButton> - </ToolbarGroup> - </BlockControls> - </> - ); -} diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 53d7da7a1c0547..808c02b30afdbe 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -29,17 +29,18 @@ import { MediaReplaceFlow, } from '@wordpress/block-editor'; import { useEffect, useState, useRef } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; +import { __, sprintf, isRTL } from '@wordpress/i18n'; import { getPath } from '@wordpress/url'; import { createBlock } from '@wordpress/blocks'; import { crop, upload } from '@wordpress/icons'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies */ import { createUpgradedEmbedBlock } from '../embed/util'; import useClientWidth from './use-client-width'; -import ImageEditor from './image-editor'; +import ImageEditor, { ImageEditingProvider } from './image-editing'; import { isExternalImage } from './edit'; /** @@ -101,25 +102,20 @@ export default function Image( { }, [ id, isSelected ] ); - const { - imageEditing, - imageSizes, - isRTL, - maxWidth, - mediaUpload, - } = useSelect( ( select ) => { - const { getSettings } = select( 'core/block-editor' ); - return pick( getSettings(), [ - 'imageEditing', - 'imageSizes', - 'isRTL', - 'maxWidth', - 'mediaUpload', - ] ); - } ); + const { imageEditing, imageSizes, maxWidth, mediaUpload } = useSelect( + ( select ) => { + const { getSettings } = select( 'core/block-editor' ); + return pick( getSettings(), [ + 'imageEditing', + 'imageSizes', + 'maxWidth', + 'mediaUpload', + ] ); + } + ); const { toggleSelection } = useDispatch( 'core/block-editor' ); const { createErrorNotice, createSuccessNotice } = useDispatch( - 'core/notices' + noticesStore ); const isLargeViewport = useViewportMatch( 'medium' ); const [ captionFocused, setCaptionFocused ] = useState( false ); @@ -415,15 +411,12 @@ export default function Image( { if ( canEditImage && isEditingImage ) { img = ( <ImageEditor - id={ id } url={ url } - setAttributes={ setAttributes } - naturalWidth={ naturalWidth } - naturalHeight={ naturalHeight } width={ width } height={ height } clientWidth={ clientWidth } - setIsEditingImage={ setIsEditingImage } + naturalHeight={ naturalHeight } + naturalWidth={ naturalWidth } /> ); } else if ( ! isResizable || ! imageWidthWithinContainer ) { @@ -458,7 +451,7 @@ export default function Image( { // When the image is centered, show both handles. showRightHandle = true; showLeftHandle = true; - } else if ( isRTL ) { + } else if ( isRTL() ) { // In RTL mode the image is on the right by default. // Show the right handle and hide the left handle only when it is // aligned left. Otherwise always show the left handle. @@ -508,13 +501,25 @@ export default function Image( { } return ( - <> + <ImageEditingProvider + id={ id } + url={ url } + naturalWidth={ naturalWidth } + naturalHeight={ naturalHeight } + clientWidth={ clientWidth } + onSaveImage={ ( imageAttributes ) => + setAttributes( imageAttributes ) + } + isEditing={ isEditingImage } + onFinishEditing={ () => setIsEditingImage( false ) } + > { controls } { img } { ( ! RichText.isEmpty( caption ) || isSelected ) && ( <RichText ref={ captionRef } tagName="figcaption" + aria-label={ __( 'Image caption text' ) } placeholder={ __( 'Write caption…' ) } value={ caption } unstableOnFocus={ onFocusCaption } @@ -528,6 +533,6 @@ export default function Image( { } /> ) } - </> + </ImageEditingProvider> ); } diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss index a20c12dc9046b1..3771670675ed8f 100644 --- a/packages/block-library/src/image/style.scss +++ b/packages/block-library/src/image/style.scss @@ -58,32 +58,32 @@ figcaption { @include caption-style(); } -} -// Variations -.is-style-rounded img { - // We use an absolute pixel to prevent the oval shape that a value of 50% would give - // to rectangular images. A pill-shape is better than otherwise. - border-radius: 9999px; -} + // Variations + &.is-style-rounded img { + // We use an absolute pixel to prevent the oval shape that a value of 50% would give + // to rectangular images. A pill-shape is better than otherwise. + border-radius: 9999px; + } -// The following variation is deprecated. -// The CSS is kept here for the time being, to support blocks using the old variation. -.is-style-circle-mask img { - // We use an absolute pixel to prevent the oval shape that a value of 50% would give - // to rectangular images. A pill-shape is better than otherwise. - border-radius: 9999px; + // The following variation is deprecated. + // The CSS is kept here for the time being, to support blocks using the old variation. + &.is-style-circle-mask img { + // We use an absolute pixel to prevent the oval shape that a value of 50% would give + // to rectangular images. A pill-shape is better than otherwise. + border-radius: 9999px; - // If a browser supports it, we will switch to using a circular SVG mask. - // The stylelint override is necessary to use the SVG inline here. - @supports (mask-image: none) or (-webkit-mask-image: none) { - /* stylelint-disable */ - mask-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="50"/></svg>'); - /* stylelint-enable */ - mask-mode: alpha; - mask-repeat: no-repeat; - mask-size: contain; - mask-position: center; - border-radius: 0; + // If a browser supports it, we will switch to using a circular SVG mask. + // The stylelint override is necessary to use the SVG inline here. + @supports (mask-image: none) or (-webkit-mask-image: none) { + /* stylelint-disable */ + mask-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="50"/></svg>'); + /* stylelint-enable */ + mask-mode: alpha; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + border-radius: 0; + } } } diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index b5b7493bef6f65..7cfcb0e0c74a9f 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -2,9 +2,7 @@ * WordPress dependencies */ import '@wordpress/core-data'; -import '@wordpress/notices'; import '@wordpress/block-editor'; -import '@wordpress/reusable-blocks'; import { registerBlockType, setDefaultBlockName, @@ -59,7 +57,7 @@ import * as textColumns from './text-columns'; import * as verse from './verse'; import * as video from './video'; import * as tagCloud from './tag-cloud'; -import * as classic from './classic'; +import * as classic from './freeform'; import * as socialLinks from './social-links'; import * as socialLink from './social-link'; diff --git a/packages/block-library/src/index.native.js b/packages/block-library/src/index.native.js index 76fd69f1e26536..65785c7f3fb378 100644 --- a/packages/block-library/src/index.native.js +++ b/packages/block-library/src/index.native.js @@ -58,7 +58,7 @@ import * as textColumns from './text-columns'; import * as verse from './verse'; import * as video from './video'; import * as tagCloud from './tag-cloud'; -import * as classic from './classic'; +import * as classic from './freeform'; import * as group from './group'; import * as buttons from './buttons'; import * as socialLink from './social-link'; @@ -223,7 +223,7 @@ export const registerCoreBlocks = () => { socialLink, socialLinks, pullquote, - devOnly( file ), + file, ].forEach( registerBlock ); registerBlockVariations( socialLink ); diff --git a/packages/block-library/src/latest-comments/block.json b/packages/block-library/src/latest-comments/block.json index 8ad73394cd93bb..37600026f8f710 100644 --- a/packages/block-library/src/latest-comments/block.json +++ b/packages/block-library/src/latest-comments/block.json @@ -25,5 +25,7 @@ "supports": { "align": true, "html": false - } + }, + "editorStyle": "wp-block-latest-comments-editor", + "style": "wp-block-latest-comments" } diff --git a/packages/block-library/src/latest-posts/block.json b/packages/block-library/src/latest-posts/block.json index fbe711ee99788c..2f603bc57274e4 100644 --- a/packages/block-library/src/latest-posts/block.json +++ b/packages/block-library/src/latest-posts/block.json @@ -84,5 +84,7 @@ "supports": { "align": true, "html": false - } + }, + "editorStyle": "wp-block-latest-posts-editor", + "style": "wp-block-latest-posts" } diff --git a/packages/block-library/src/list/block.json b/packages/block-library/src/list/block.json index 23bca15ac9459f..002f0bc82bc7bb 100644 --- a/packages/block-library/src/list/block.json +++ b/packages/block-library/src/list/block.json @@ -33,5 +33,7 @@ "gradients": true }, "__unstablePasteTextInline": true - } + }, + "editorStyle": "wp-block-list-editor", + "style": "wp-block-list" } diff --git a/packages/block-library/src/list/edit.js b/packages/block-library/src/list/edit.js index 5ca9052dbf6ecb..b653e39e3dfb50 100644 --- a/packages/block-library/src/list/edit.js +++ b/packages/block-library/src/list/edit.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { __, _x } from '@wordpress/i18n'; +import { __, _x, isRTL } from '@wordpress/i18n'; import { createBlock } from '@wordpress/blocks'; import { RichText, @@ -29,7 +29,6 @@ import { formatOutdent, formatOutdentRTL, } from '@wordpress/icons'; -import { useSelect } from '@wordpress/data'; /** * Internal dependencies @@ -47,10 +46,6 @@ export default function ListEdit( { const { ordered, values, type, reversed, start } = attributes; const tagName = ordered ? 'ol' : 'ul'; - const isRTL = useSelect( ( select ) => { - return !! select( 'core/block-editor' ).getSettings().isRTL; - }, [] ); - const controls = ( { value, onChange, onFocus } ) => ( <> { isSelected && ( @@ -93,7 +88,7 @@ export default function ListEdit( { <ToolbarGroup controls={ [ { - icon: isRTL + icon: isRTL() ? formatListBulletsRTL : formatListBullets, title: __( 'Convert to unordered list' ), @@ -110,7 +105,7 @@ export default function ListEdit( { }, }, { - icon: isRTL + icon: isRTL() ? formatListNumberedRTL : formatListNumbered, title: __( 'Convert to ordered list' ), @@ -127,7 +122,7 @@ export default function ListEdit( { }, }, { - icon: isRTL ? formatOutdentRTL : formatOutdent, + icon: isRTL() ? formatOutdentRTL : formatOutdent, title: __( 'Outdent list item' ), shortcut: _x( 'Backspace', 'keyboard key' ), isDisabled: ! canOutdentListItems( value ), @@ -137,7 +132,7 @@ export default function ListEdit( { }, }, { - icon: isRTL ? formatIndentRTL : formatIndent, + icon: isRTL() ? formatIndentRTL : formatIndent, title: __( 'Indent list item' ), shortcut: _x( 'Space', 'keyboard key' ), isDisabled: ! canIndentListItems( value ), @@ -167,6 +162,7 @@ export default function ListEdit( { setAttributes( { values: nextValues } ) } value={ values } + aria-label={ __( 'List text' ) } placeholder={ __( 'Write list…' ) } onMerge={ mergeBlocks } onSplit={ ( value ) => diff --git a/packages/block-library/src/media-text/block.json b/packages/block-library/src/media-text/block.json index e88b1b1d1b8182..0e175cf04bd538 100644 --- a/packages/block-library/src/media-text/block.json +++ b/packages/block-library/src/media-text/block.json @@ -89,5 +89,7 @@ "gradients": true, "link": true } - } + }, + "editorStyle": "wp-block-media-text-editor", + "style": "wp-block-media-text" } diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js index fb9381ddb6a623..853b1585c4ee1a 100644 --- a/packages/block-library/src/media-text/edit.js +++ b/packages/block-library/src/media-text/edit.js @@ -242,7 +242,7 @@ function MediaTextEdit( { attributes, isSelected, setAttributes } ) { } /> ) } - { imageFill && ( + { imageFill && mediaUrl && mediaType === 'image' && ( <FocalPointPicker label={ __( 'Focal point picker' ) } url={ mediaUrl } diff --git a/packages/block-library/src/media-text/style.scss b/packages/block-library/src/media-text/style.scss index 93b6d2a0c803b8..98c7e0629130f1 100644 --- a/packages/block-library/src/media-text/style.scss +++ b/packages/block-library/src/media-text/style.scss @@ -72,13 +72,18 @@ vertical-align: middle; } -.wp-block-media-text.is-image-fill figure.wp-block-media-text__media { +.wp-block-media-text.is-image-fill .wp-block-media-text__media { height: 100%; min-height: 250px; background-size: cover; } -.wp-block-media-text.is-image-fill figure.wp-block-media-text__media > img { +.wp-block-media-text.is-image-fill .wp-block-media-text__media > a { + display: block; + height: 100%; +} + +.wp-block-media-text.is-image-fill .wp-block-media-text__media img { // The image is visually hidden but accessible to assistive technologies. position: absolute; width: 1px; diff --git a/packages/block-library/src/more/block.json b/packages/block-library/src/more/block.json index 4279443517b000..f49bbd58583a34 100644 --- a/packages/block-library/src/more/block.json +++ b/packages/block-library/src/more/block.json @@ -16,5 +16,6 @@ "className": false, "html": false, "multiple": false - } + }, + "editorStyle": "wp-block-more-editor" } diff --git a/packages/block-library/src/navigation-link/block.json b/packages/block-library/src/navigation-link/block.json index 689707fc5ba77e..2104f50a13870e 100644 --- a/packages/block-library/src/navigation-link/block.json +++ b/packages/block-library/src/navigation-link/block.json @@ -42,5 +42,7 @@ "supports": { "reusable": false, "html": false - } + }, + "editorStyle": "wp-block-navigation-link-editor", + "style": "wp-block-navigation-link" } diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index b1dc3338c801e9..4f438723393990 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -170,7 +170,7 @@ function NavigationLinkEdit( { // Show the LinkControl on mount if the URL is empty // ( When adding a new menu item) - // This can't be done in the useState call because it cconflicts + // This can't be done in the useState call because it conflicts // with the autofocus behavior of the BlockListBlock component. useEffect( () => { if ( ! url ) { @@ -362,6 +362,7 @@ function NavigationLinkEdit( { createBlock( 'core/navigation-link' ) ) } + aria-label={ __( 'Navigation link text' ) } placeholder={ itemLabelPlaceholder } keepPlaceholderOnFocus withoutInteractiveFormatting diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json index 66fa697b3eacd7..5b70d51eaafa7e 100644 --- a/packages/block-library/src/navigation/block.json +++ b/packages/block-library/src/navigation/block.json @@ -50,10 +50,13 @@ "html": false, "inserter": true, "fontSize": true, - "__experimentalFontAppearance": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, "__experimentalTextTransform": true, "color": true, "__experimentalFontFamily": true, "__experimentalTextDecoration": true - } + }, + "editorStyle": "wp-block-navigation-editor", + "style": "wp-block-navigation" } diff --git a/packages/block-library/src/navigation/deprecated.js b/packages/block-library/src/navigation/deprecated.js index 9f8b8bce707096..0ee64c0a44da89 100644 --- a/packages/block-library/src/navigation/deprecated.js +++ b/packages/block-library/src/navigation/deprecated.js @@ -1,14 +1,113 @@ /** * External dependencies */ -import { omit } from 'lodash'; +import { mapValues, omit } from 'lodash'; /** * WordPress dependencies */ import { InnerBlocks } from '@wordpress/block-editor'; +const TYPOGRAPHY_PRESET_DEPRECATION_MAP = { + fontStyle: 'var:preset|font-style|', + fontWeight: 'var:preset|font-weight|', + textDecoration: 'var:preset|text-decoration|', + textTransform: 'var:preset|text-transform|', +}; + export default [ + { + attributes: { + orientation: { + type: 'string', + }, + textColor: { + type: 'string', + }, + customTextColor: { + type: 'string', + }, + rgbTextColor: { + type: 'string', + }, + backgroundColor: { + type: 'string', + }, + customBackgroundColor: { + type: 'string', + }, + rgbBackgroundColor: { + type: 'string', + }, + itemsJustification: { + type: 'string', + }, + showSubmenuIcon: { + type: 'boolean', + default: true, + }, + }, + supports: { + align: [ 'wide', 'full' ], + anchor: true, + html: false, + inserter: true, + fontSize: true, + __experimentalFontStyle: true, + __experimentalFontWeight: true, + __experimentalTextTransform: true, + color: true, + __experimentalFontFamily: true, + __experimentalTextDecoration: true, + }, + save() { + return <InnerBlocks.Content />; + }, + isEligible( attributes ) { + if ( ! attributes.style || ! attributes.style.typography ) { + return false; + } + for ( const styleAttribute in TYPOGRAPHY_PRESET_DEPRECATION_MAP ) { + const attributeValue = + attributes.style.typography[ styleAttribute ]; + if ( + attributeValue && + attributeValue.startsWith( + TYPOGRAPHY_PRESET_DEPRECATION_MAP[ styleAttribute ] + ) + ) { + return true; + } + } + return false; + }, + migrate( attributes ) { + return { + ...attributes, + style: { + ...attributes.style, + typography: mapValues( + attributes.style.typography, + ( value, key ) => { + const prefix = + TYPOGRAPHY_PRESET_DEPRECATION_MAP[ key ]; + if ( prefix && value.startsWith( prefix ) ) { + const newValue = value.slice( prefix.length ); + if ( + 'textDecoration' === key && + 'strikethrough' === newValue + ) { + return 'line-through'; + } + return newValue; + } + return value; + } + ), + }, + }; + }, + }, { attributes: { className: { diff --git a/packages/block-library/src/navigation/edit.js b/packages/block-library/src/navigation/edit.js index 0cfb54aa8b46fe..cd47d5b89a0b9a 100644 --- a/packages/block-library/src/navigation/edit.js +++ b/packages/block-library/src/navigation/edit.js @@ -47,7 +47,13 @@ function Navigation( { const { selectBlock } = useDispatch( 'core/block-editor' ); - const blockProps = useBlockProps(); + const blockProps = useBlockProps( { + className: classnames( className, { + [ `items-justified-${ attributes.itemsJustification }` ]: attributes.itemsJustification, + 'is-vertical': attributes.orientation === 'vertical', + } ), + } ); + const { navigatorToolbarButton, navigatorModal } = useBlockNavigator( clientId ); @@ -104,11 +110,6 @@ function Navigation( { }; } - const blockClassNames = classnames( className, { - [ `items-justified-${ attributes.itemsJustification }` ]: attributes.itemsJustification, - 'is-vertical': attributes.orientation === 'vertical', - } ); - return ( <> <BlockControls> @@ -170,13 +171,7 @@ function Navigation( { </PanelBody> ) } </InspectorControls> - <nav - { ...blockProps } - className={ classnames( - blockProps.className, - blockClassNames - ) } - > + <nav { ...blockProps }> <ul { ...innerBlocksProps } /> </nav> </> diff --git a/packages/block-library/src/navigation/editor.scss b/packages/block-library/src/navigation/editor.scss index 9c605bbb796071..cc439d16f0325b 100644 --- a/packages/block-library/src/navigation/editor.scss +++ b/packages/block-library/src/navigation/editor.scss @@ -212,3 +212,91 @@ $color-control-label-height: 20px; .wp-block-navigation .block-editor-button-block-appender { justify-content: flex-start; } + + +/** + * Setup state + */ + +// Unselected state. +.wp-block-navigation-placeholder__preview { + min-height: $grid-unit-10 + $grid-unit-10 + $button-size; + display: flex; + flex-direction: row; + align-items: center; + + // Hide when selected. + .is-selected & { + display: none; + } + + // Style skeleton elements. + // Needs specificity. + .wp-block-navigation-link.wp-block-navigation-link { + border-radius: $radius-block-ui; + background: currentColor; + min-width: 72px; + height: $grid-unit-20; + margin: $grid-unit-15 $grid-unit-30 $grid-unit-15 0; + } + + .wp-block-navigation-link.wp-block-navigation-link, + svg { + opacity: 0.3; + } +} + +// Selected state. +.wp-block-navigation-placeholder__controls { + padding: $grid-unit-10; + border-radius: $radius-block-ui; + background-color: $white; + box-shadow: inset 0 0 0 $border-width $gray-900; + flex-direction: row; + align-items: center; + display: none; + + // Show when selected. + .is-selected & { + display: flex; + } + + // Vertical navigation. + .is-vertical & { + .wp-block-navigation-placeholder__actions { + flex-direction: column; + } + } + + // Both selected and vertical. + .is-selected.is-vertical & { + display: inline-flex; // This makes the white box not take up all available space. + } + + .wp-block-navigation-placeholder__icon { + margin-right: $grid-unit-15; + height: $button-size; // Prevents jumpiness. + } +} + +// Both, when block is vertical. +.wp-block-navigation-placeholder__preview, +.wp-block-navigation-placeholder__controls { + .is-vertical & { + flex-direction: column; + align-items: flex-start; + min-height: $icon-size + ($grid-unit-20 + $grid-unit-15 + $grid-unit-15) * 3; + } +} + + +.wp-block-navigation-placeholder__actions { + display: flex; + font-size: $default-font-size; + + .components-button.components-dropdown-menu__toggle.has-icon { + padding: ($grid-unit-15 / 2) $grid-unit-15; + display: flex; + flex-direction: row-reverse; // This puts the chevron, which is hidden from screen readers, on the right. + } +} diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index b9046b529d9dcd..57e312c11f3ed5 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -171,3 +171,34 @@ function register_block_core_navigation() { } add_action( 'init', 'register_block_core_navigation' ); + +/** + * Filter that changes the parsed attribute values of navigation blocks contain typographic presets to contain the values directly. + * + * @param array $parsed_block The block being rendered. + * @return array The block being rendered without typographic presets. + */ +function block_core_navigation_typographic_presets_backcompatibility( $parsed_block ) { + if ( 'core/navigation' === $parsed_block['blockName'] ) { + $attribute_to_prefix_map = array( + 'fontStyle' => 'var:preset|font-style|', + 'fontWeight' => 'var:preset|font-weight|', + 'textDecoration' => 'var:preset|text-decoration|', + 'textTransform' => 'var:preset|text-transform|', + ); + foreach ( $attribute_to_prefix_map as $style_attribute => $prefix ) { + if ( ! empty( $parsed_block['attrs']['style']['typography'][ $style_attribute ] ) ) { + $prefix_len = strlen( $prefix ); + $attribute_value = &$parsed_block['attrs']['style']['typography'][ $style_attribute ]; + if ( 0 === strncmp( $attribute_value, $prefix, $prefix_len ) ) { + $attribute_value = substr( $attribute_value, $prefix_len ); + } + if ( 'textDecoration' === $style_attribute && 'strikethrough' === $attribute_value ) { + $attribute_value = 'line-through'; + } + } + } + } + return $parsed_block; +} +add_filter( 'render_block_data', 'block_core_navigation_typographic_presets_backcompatibility' ); diff --git a/packages/block-library/src/navigation/placeholder.js b/packages/block-library/src/navigation/placeholder.js index 79739656ba9e1d..5a3bb3bff98c6b 100644 --- a/packages/block-library/src/navigation/placeholder.js +++ b/packages/block-library/src/navigation/placeholder.js @@ -2,7 +2,6 @@ * External dependencies */ import { some } from 'lodash'; -import classnames from 'classnames'; /** * WordPress dependencies @@ -10,70 +9,26 @@ import classnames from 'classnames'; import { createBlock, parse } from '@wordpress/blocks'; import { Button, - CustomSelectControl, + DropdownMenu, + MenuGroup, + MenuItem, Spinner, - Placeholder, } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { forwardRef, useCallback, - useMemo, useState, useEffect, } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { navigation as icon } from '@wordpress/icons'; +import { Icon, chevronDown, search } from '@wordpress/icons'; /** * Internal dependencies */ import createDataTree from './create-data-tree'; -const CREATE_EMPTY_OPTION_VALUE = '__CREATE_EMPTY__'; -const CREATE_FROM_PAGES_OPTION_VALUE = '__CREATE_FROM_PAGES__'; - -/** - * Get instruction text for the Placeholder component. - * - * @param {boolean} hasMenus Flag that indicates if there are menus. - * @param {boolean} hasPages Flag that indicates if there are pages. - * - * @return {string} Text to display as the placeholder instructions. - */ -function getPlaceholderInstructions( hasMenus, hasPages ) { - if ( hasMenus && hasPages ) { - return __( - 'Use an existing menu here, include all top-level pages, or add an empty Navigation block.' - ); - } else if ( hasMenus && ! hasPages ) { - return __( - 'Use an existing menu here, or add an empty Navigation block.' - ); - } else if ( ! hasMenus && hasPages ) { - return __( - 'Include all existing pages here, or add an empty Navigation block.' - ); - } - - return __( 'Create an empty navigation.' ); -} - -/** - * Return the menu id if the user has one selected. - * - * @param {Object} selectedCreateOption An object containing details of - * the selected create option. - * - * @return {number|undefined} The menu id. - */ -function getSelectedMenu( selectedCreateOption ) { - const optionId = selectedCreateOption?.id; - return optionId !== undefined && Number.isInteger( optionId ) - ? optionId - : undefined; -} - /** * A recursive function that maps menu item nodes to blocks. * @@ -165,7 +120,7 @@ function convertPagesToBlocks( pages ) { } function NavigationPlaceholder( { onCreate }, ref ) { - const [ selectedCreateOption, setSelectedCreateOption ] = useState(); + const [ selectedMenu, setSelectedMenu ] = useState(); const [ isCreatingFromMenu, setIsCreatingFromMenu ] = useState( false ); @@ -198,7 +153,6 @@ function NavigationPlaceholder( { onCreate }, ref ) { }, ]; const menusParameters = [ { per_page: -1 } ]; - const selectedMenu = getSelectedMenu( selectedCreateOption ); const hasSelectedMenu = selectedMenu !== undefined; const menuItemsParameters = hasSelectedMenu ? [ @@ -236,76 +190,39 @@ function NavigationPlaceholder( { onCreate }, ref ) { : false, }; }, - [ selectedCreateOption ] + [ selectedMenu ] ); const hasPages = !! ( hasResolvedPages && pages?.length ); const hasMenus = !! ( hasResolvedMenus && menus?.length ); const isLoading = isResolvingPages || isResolvingMenus; - const createOptions = useMemo( - () => [ - ...( hasMenus ? menus : [] ), - { - id: CREATE_EMPTY_OPTION_VALUE, - name: __( 'Create empty Navigation' ), - className: 'is-create-empty-option', - }, - ...( hasPages - ? [ - { - id: CREATE_FROM_PAGES_OPTION_VALUE, - name: __( 'Create from all top-level pages' ), - }, - ] - : [] ), - ], - [ menus, hasMenus, hasPages ] - ); - const createFromMenu = useCallback( () => { - // If an empty menu was selected, create an empty block. - if ( ! menuItems.length ) { - onCreate( [] ); - return; - } - const blocks = convertMenuItemsToBlocks( menuItems ); const selectNavigationBlock = true; onCreate( blocks, selectNavigationBlock ); } ); - const onCreateButtonClick = useCallback( () => { - if ( ! selectedCreateOption ) { + const onCreateFromMenu = () => { + // If we have menu items, create the block right away. + if ( hasResolvedMenuItems ) { + createFromMenu(); return; } - const { key } = selectedCreateOption; - switch ( key ) { - case CREATE_EMPTY_OPTION_VALUE: { - onCreate( [] ); - return; - } + // Otherwise, create the block when resolution finishes. + setIsCreatingFromMenu( true ); + }; - case CREATE_FROM_PAGES_OPTION_VALUE: { - const blocks = convertPagesToBlocks( pages ); - const selectNavigationBlock = true; - onCreate( blocks, selectNavigationBlock ); - return; - } - - // The default case indicates that a menu was selected. - default: - // If we have menu items, create the block right away. - if ( hasResolvedMenuItems ) { - createFromMenu(); - return; - } + const onCreateEmptyMenu = () => { + onCreate( [] ); + }; - // Otherwise, create the block when resolution finishes. - setIsCreatingFromMenu( true ); - } - } ); + const onCreateAllPages = () => { + const blocks = convertPagesToBlocks( pages ); + const selectNavigationBlock = true; + onCreate( blocks, selectNavigationBlock ); + }; useEffect( () => { // If the user selected a menu but we had to wait for menu items to @@ -316,72 +233,66 @@ function NavigationPlaceholder( { onCreate }, ref ) { } }, [ isCreatingFromMenu, hasResolvedMenuItems ] ); - if ( hasMenus && ! selectedCreateOption ) { - setSelectedCreateOption( createOptions[ 0 ] ); - } - return ( - <Placeholder - className="wp-block-navigation-placeholder" - icon={ icon } - label={ __( 'Navigation' ) } - > - { isLoading && ( - <div ref={ ref }> - <Spinner /> { __( 'Loading…' ) } - </div> - ) } - { ! isLoading && ( - <div - ref={ ref } - className="wp-block-navigation-placeholder__actions" - > - <> - <CustomSelectControl - className={ classnames( - 'wp-block-navigation-placeholder__select-control', - { - 'has-menus': hasMenus, - } - ) } - label={ - ! isLoading - ? getPlaceholderInstructions( - hasMenus, - hasPages - ) - : undefined - } - value={ selectedCreateOption || createOptions[ 0 ] } - onChange={ ( { selectedItem } ) => { - if ( - selectedItem?.key === selectedCreateOption - ) { - return; - } - setSelectedCreateOption( selectedItem ); - setIsCreatingFromMenu( false ); - } } - options={ createOptions.map( ( option ) => { - return { - ...option, - key: option.id, - }; - } ) } - /> - <Button - isSecondary - className="wp-block-navigation-placeholder__button" - disabled={ ! selectedCreateOption } - isBusy={ isCreatingFromMenu } - onClick={ onCreateButtonClick } - > - { __( 'Create' ) } + <div className="wp-block-navigation-placeholder"> + <div className="wp-block-navigation-placeholder__preview"> + <span className="wp-block-navigation-link"></span> + <span className="wp-block-navigation-link"></span> + <span className="wp-block-navigation-link"></span> + <Icon icon={ search } /> + </div> + + <div className="wp-block-navigation-placeholder__controls"> + { isLoading && ( + <div ref={ ref }> + <Spinner /> + </div> + ) } + { ! isLoading && ( + <div + ref={ ref } + className="wp-block-navigation-placeholder__actions" + > + { hasMenus ? ( + <DropdownMenu + text={ __( 'Existing menu' ) } + icon={ chevronDown } + className="wp-block-navigation-placeholder__actions__dropdown" + > + { ( { onClose } ) => ( + <MenuGroup> + { menus.map( ( menu ) => { + return ( + <MenuItem + onClick={ () => { + setSelectedMenu( + menu.id + ); + onCreateFromMenu(); + } } + onClose={ onClose } + key={ menu.id } + > + { menu.name } + </MenuItem> + ); + } ) } + </MenuGroup> + ) } + </DropdownMenu> + ) : undefined } + { hasPages ? ( + <Button onClick={ onCreateAllPages }> + { __( 'Add all pages' ) } + </Button> + ) : undefined } + <Button onClick={ onCreateEmptyMenu }> + { __( 'Start empty' ) } </Button> - </> - </div> - ) } - </Placeholder> + </div> + ) } + </div> + </div> ); } diff --git a/packages/block-library/src/navigation/style.scss b/packages/block-library/src/navigation/style.scss index ce6ab12eab566f..842d84108e1930 100644 --- a/packages/block-library/src/navigation/style.scss +++ b/packages/block-library/src/navigation/style.scss @@ -1,9 +1,7 @@ // Default background and font color .wp-block-navigation:not(.has-background) .wp-block-navigation__container { - .wp-block-navigation-link:not(.has-text-color) { - color: $gray-900; - } .wp-block-navigation__container { + color: $gray-900; background-color: $white; } } diff --git a/packages/block-library/src/nextpage/block.json b/packages/block-library/src/nextpage/block.json index 2236627dbb7e01..f1a8a2745cb572 100644 --- a/packages/block-library/src/nextpage/block.json +++ b/packages/block-library/src/nextpage/block.json @@ -7,5 +7,6 @@ "customClassName": false, "className": false, "html": false - } + }, + "editorStyle": "wp-block-nextpage-editor" } diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 44118c165c2770..a29e22c01d5af3 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -37,5 +37,7 @@ "lineHeight": true, "__experimentalSelector": "p", "__unstablePasteTextInline": true - } + }, + "editorStyle": "wp-block-paragraph-editor", + "style": "wp-block-paragraph" } diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js index f84fc833e8e473..54650eeab792e3 100644 --- a/packages/block-library/src/paragraph/edit.js +++ b/packages/block-library/src/paragraph/edit.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { __, _x } from '@wordpress/i18n'; +import { __, _x, isRTL } from '@wordpress/i18n'; import { PanelBody, ToggleControl, ToolbarGroup } from '@wordpress/components'; import { AlignmentToolbar, @@ -29,12 +29,8 @@ function getComputedStyle( node, pseudo ) { const name = 'core/paragraph'; function ParagraphRTLToolbar( { direction, setDirection } ) { - const isRTL = useSelect( ( select ) => { - return !! select( 'core/block-editor' ).getSettings().isRTL; - }, [] ); - return ( - isRTL && ( + isRTL() && ( <ToolbarGroup controls={ [ { diff --git a/packages/block-library/src/post-author/block.json b/packages/block-library/src/post-author/block.json index 99f1cd2c8a8163..8a4d8d66dbb670 100644 --- a/packages/block-library/src/post-author/block.json +++ b/packages/block-library/src/post-author/block.json @@ -33,5 +33,7 @@ "link": true }, "lineHeight": true - } + }, + "editorStyle": "wp-block-post-author-editor", + "style": "wp-block-post-author" } diff --git a/packages/block-library/src/post-author/edit.js b/packages/block-library/src/post-author/edit.js index 96d517dc773c31..b26c4269d7f33a 100644 --- a/packages/block-library/src/post-author/edit.js +++ b/packages/block-library/src/post-author/edit.js @@ -144,7 +144,8 @@ function PostAuthorEdit( { isSelected, context, attributes, setAttributes } ) { <RichText className="wp-block-post-author__byline" multiline={ false } - placeholder={ __( 'Write byline …' ) } + aria-label={ __( 'Post author byline text' ) } + placeholder={ __( 'Write byline…' ) } value={ byline } onChange={ ( value ) => setAttributes( { byline: value } ) diff --git a/packages/block-library/src/post-comments-form/block.json b/packages/block-library/src/post-comments-form/block.json index 64cdcbba8f95fd..61c339f5012879 100644 --- a/packages/block-library/src/post-comments-form/block.json +++ b/packages/block-library/src/post-comments-form/block.json @@ -19,5 +19,6 @@ }, "fontSize": true, "lineHeight": true - } + }, + "style": "wp-block-post-comments-form" } diff --git a/packages/block-library/src/post-comments-form/style.scss b/packages/block-library/src/post-comments-form/style.scss new file mode 100644 index 00000000000000..bd2e9c10ec5cdb --- /dev/null +++ b/packages/block-library/src/post-comments-form/style.scss @@ -0,0 +1,24 @@ +$blocks-button__height: 3.1em; + +// Styles copied from button block styles. +.wp-block-post-comments-form input[type="submit"] { + color: $white; + background-color: #32373c; + border: none; + border-radius: $blocks-button__height / 2; + box-shadow: none; + cursor: pointer; + display: inline-block; + font-size: 1.125em; + padding: 0.667em 1.333em; + text-align: center; + text-decoration: none; + overflow-wrap: break-word; + + &:hover, + &:focus, + &:active, + &:visited { + color: $white; + } +} diff --git a/packages/block-library/src/post-content/block.json b/packages/block-library/src/post-content/block.json index 9eb24596ec59fe..1869af668fd839 100644 --- a/packages/block-library/src/post-content/block.json +++ b/packages/block-library/src/post-content/block.json @@ -9,5 +9,6 @@ "supports": { "align": [ "wide", "full" ], "html": false - } + }, + "editorStyle": "wp-block-post-content-editor" } diff --git a/packages/block-library/src/post-content/edit.js b/packages/block-library/src/post-content/edit.js index 855383fe19c4cd..d4c86853533d89 100644 --- a/packages/block-library/src/post-content/edit.js +++ b/packages/block-library/src/post-content/edit.js @@ -2,7 +2,6 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; import { useBlockProps } from '@wordpress/block-editor'; /** @@ -13,19 +12,8 @@ import PostContentInnerBlocks from './inner-blocks'; export default function PostContentEdit( { context: { postId: contextPostId, postType: contextPostType }, } ) { - const { id: currentPostId, type: currentPostType } = useSelect( - ( select ) => select( 'core/editor' ).getCurrentPost() ?? {} - ); const blockProps = useBlockProps(); - - // Only render InnerBlocks if the context is different from the active post - // to avoid infinite recursion of post content. - if ( - contextPostId && - contextPostType && - contextPostId !== currentPostId && - contextPostType !== currentPostType - ) { + if ( contextPostId && contextPostType ) { return ( <div { ...blockProps }> <PostContentInnerBlocks diff --git a/packages/block-library/src/post-excerpt/block.json b/packages/block-library/src/post-excerpt/block.json index f45971f7746367..2e2690d398625f 100644 --- a/packages/block-library/src/post-excerpt/block.json +++ b/packages/block-library/src/post-excerpt/block.json @@ -30,5 +30,6 @@ "link": true }, "lineHeight": true - } + }, + "editorStyle": "wp-block-post-excerpt-editor" } diff --git a/packages/block-library/src/post-excerpt/edit.js b/packages/block-library/src/post-excerpt/edit.js index 16b9e8aa37d0a4..126c38418d49ac 100644 --- a/packages/block-library/src/post-excerpt/edit.js +++ b/packages/block-library/src/post-excerpt/edit.js @@ -103,6 +103,7 @@ function PostExcerptEditor( { ! showMoreOnNewLine && 'wp-block-post-excerpt__excerpt is-inline' } + aria-label={ __( 'Post excerpt text' ) } value={ excerpt || postContentExcerpt || @@ -115,6 +116,7 @@ function PostExcerptEditor( { <p className="wp-block-post-excerpt__more-text"> <RichText tagName="a" + aria-label={ __( 'Read more link text' ) } placeholder={ __( 'Read more…' ) } value={ moreText } onChange={ ( newMoreText ) => @@ -125,6 +127,7 @@ function PostExcerptEditor( { ) : ( <RichText tagName="a" + aria-label={ __( 'Read more link text' ) } placeholder={ __( 'Read more…' ) } value={ moreText } onChange={ ( newMoreText ) => diff --git a/packages/block-library/src/post-featured-image/block.json b/packages/block-library/src/post-featured-image/block.json index 0c183bf7d0e235..f3a2deb587122e 100644 --- a/packages/block-library/src/post-featured-image/block.json +++ b/packages/block-library/src/post-featured-image/block.json @@ -13,6 +13,9 @@ "postType" ], "supports": { + "align": [ "left", "right", "center", "wide", "full" ], "html": false - } + }, + "editorStyle": "wp-block-post-featured-image-editor", + "style": "wp-block-post-featured-image" } diff --git a/packages/block-library/src/post-featured-image/edit.js b/packages/block-library/src/post-featured-image/edit.js index 35a6650585f8cd..c85faf7a9dddf7 100644 --- a/packages/block-library/src/post-featured-image/edit.js +++ b/packages/block-library/src/post-featured-image/edit.js @@ -3,14 +3,27 @@ */ import { useEntityProp } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; -import { Icon, ToggleControl, PanelBody } from '@wordpress/components'; +import { + Icon, + ToggleControl, + PanelBody, + withNotices, +} from '@wordpress/components'; +import { + InspectorControls, + BlockControls, + MediaPlaceholder, + MediaReplaceFlow, + BlockIcon, + useBlockProps, +} from '@wordpress/block-editor'; import { __, sprintf } from '@wordpress/i18n'; -import { postFeaturedImage as icon } from '@wordpress/icons'; -import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; +import { postFeaturedImage } from '@wordpress/icons'; +const ALLOWED_MEDIA_TYPES = [ 'image' ]; const placeholderChip = ( <div className="post-featured-image_placeholder"> - <Icon icon={ icon } /> + <Icon icon={ postFeaturedImage } /> <p> { __( 'Featured Image' ) }</p> </div> ); @@ -19,8 +32,10 @@ function PostFeaturedImageDisplay( { attributes: { isLink }, setAttributes, context: { postId, postType }, + noticeUI, + noticeOperations, } ) { - const [ featuredImage ] = useEntityProp( + const [ featuredImage, setFeaturedImage ] = useEntityProp( 'postType', postType, 'featured_media', @@ -31,14 +46,44 @@ function PostFeaturedImageDisplay( { featuredImage && select( 'core' ).getMedia( featuredImage ), [ featuredImage ] ); - const image = ! media ? ( - placeholderChip - ) : ( - <img - src={ media.source_url } - alt={ media.alt_text || __( 'No alternative text set' ) } - /> - ); + const onSelectImage = ( value ) => { + if ( value?.id ) { + setFeaturedImage( value.id ); + } + }; + function onUploadError( message ) { + noticeOperations.removeAllNotices(); + noticeOperations.createErrorNotice( message ); + } + let image; + if ( ! featuredImage ) { + image = ( + <MediaPlaceholder + icon={ <BlockIcon icon={ postFeaturedImage } /> } + onSelect={ onSelectImage } + notices={ noticeUI } + onError={ onUploadError } + accept="image/*" + allowedTypes={ ALLOWED_MEDIA_TYPES } + labels={ { + title: __( 'Featured image' ), + instructions: __( + 'Upload a media file or pick one from your media library.' + ), + } } + /> + ); + } else { + // We have a Featured image so show a Placeholder if is loading. + image = ! media ? ( + placeholderChip + ) : ( + <img + src={ media.source_url } + alt={ media.alt_text || __( 'Featured image' ) } + /> + ); + } return ( <> @@ -55,14 +100,28 @@ function PostFeaturedImageDisplay( { /> </PanelBody> </InspectorControls> + <BlockControls> + { !! media && ( + <MediaReplaceFlow + mediaId={ featuredImage } + mediaURL={ media.source_url } + allowedTypes={ ALLOWED_MEDIA_TYPES } + accept="image/*" + onSelect={ onSelectImage } + onError={ onUploadError } + /> + ) } + </BlockControls> <div { ...useBlockProps() }>{ image }</div> </> ); } +const PostFeaturedImageWithNotices = withNotices( PostFeaturedImageDisplay ); + export default function PostFeaturedImageEdit( props ) { if ( ! props.context?.postId ) { return placeholderChip; } - return <PostFeaturedImageDisplay { ...props } />; + return <PostFeaturedImageWithNotices { ...props } />; } diff --git a/packages/block-library/src/post-featured-image/editor.scss b/packages/block-library/src/post-featured-image/editor.scss index 35faa55448736c..1f9cbb8e718a6e 100644 --- a/packages/block-library/src/post-featured-image/editor.scss +++ b/packages/block-library/src/post-featured-image/editor.scss @@ -1,4 +1,12 @@ div[data-type="core/post-featured-image"] { + img { + max-width: 100%; + height: auto; + display: block; + } +} + +.editor-styles-wrapper { .post-featured-image_placeholder { display: flex; flex-direction: row; @@ -16,9 +24,4 @@ div[data-type="core/post-featured-image"] { margin: 0; } } - img { - max-width: 100%; - height: auto; - display: block; - } } diff --git a/packages/block-library/src/post-hierarchical-terms/edit.js b/packages/block-library/src/post-hierarchical-terms/edit.js index fda083bf96e4b2..a3fb6459a34e3e 100644 --- a/packages/block-library/src/post-hierarchical-terms/edit.js +++ b/packages/block-library/src/post-hierarchical-terms/edit.js @@ -14,6 +14,7 @@ import { useBlockProps, __experimentalBlockVariationPicker as BlockVariationPicker, } from '@wordpress/block-editor'; +import { store as blocksStore } from '@wordpress/blocks'; import { Spinner } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; @@ -39,7 +40,7 @@ export default function PostHierarchicalTermsEdit( { getBlockVariations, getBlockType, getDefaultBlockVariation, - } = select( 'core/blocks' ); + } = select( blocksStore ); return { blockType: getBlockType( name ), diff --git a/packages/block-library/src/post-title/edit.js b/packages/block-library/src/post-title/edit.js index 46030a4ed4cec3..e7c5b39f8cfaeb 100644 --- a/packages/block-library/src/post-title/edit.js +++ b/packages/block-library/src/post-title/edit.js @@ -6,12 +6,13 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { useSelect } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; import { AlignmentToolbar, BlockControls, InspectorControls, useBlockProps, + PlainText, } from '@wordpress/block-editor'; import { ToolbarGroup, @@ -42,6 +43,7 @@ export default function PostTitleEdit( { ), [ postType, postId ] ); + const { editEntityRecord } = useDispatch( 'core' ); const blockProps = useBlockProps( { className: classnames( { @@ -53,11 +55,33 @@ export default function PostTitleEdit( { return null; } - let title = post.title || __( 'Post Title' ); + const { title, link } = post; + + let titleElement = ( + <PlainText + tagName={ TagName } + placeholder={ __( 'No Title' ) } + value={ title } + onChange={ ( value ) => + editEntityRecord( 'postType', postType, postId, { + title: value, + } ) + } + __experimentalVersion={ 2 } + { ...( isLink ? {} : blockProps ) } + /> + ); + if ( isLink ) { - title = ( - <a href={ post.link } target={ linkTarget } rel={ rel }> - { title } + titleElement = ( + <a + href={ link } + target={ linkTarget } + rel={ rel } + onClick={ ( event ) => event.preventDefault() } + { ...blockProps } + > + { titleElement } </a> ); } @@ -109,7 +133,7 @@ export default function PostTitleEdit( { ) } </PanelBody> </InspectorControls> - <TagName { ...blockProps }>{ title }</TagName> + { titleElement } </> ); } diff --git a/packages/block-library/src/preformatted/block.json b/packages/block-library/src/preformatted/block.json index d235d33ccfdc59..6ed73534883ba0 100644 --- a/packages/block-library/src/preformatted/block.json +++ b/packages/block-library/src/preformatted/block.json @@ -12,6 +12,8 @@ } }, "supports": { - "anchor": true - } + "anchor": true, + "fontSize": true + }, + "style": "wp-block-preformatted" } diff --git a/packages/block-library/src/preformatted/edit.js b/packages/block-library/src/preformatted/edit.js index d3bc0f626807f5..69ce4745de5269 100644 --- a/packages/block-library/src/preformatted/edit.js +++ b/packages/block-library/src/preformatted/edit.js @@ -25,6 +25,7 @@ export default function PreformattedEdit( { } ); } } onRemove={ onRemove } + aria-label={ __( 'Preformatted text' ) } placeholder={ __( 'Write preformatted text…' ) } onMerge={ mergeBlocks } { ...blockProps } diff --git a/packages/block-library/src/preformatted/edit.native.js b/packages/block-library/src/preformatted/edit.native.js index 88a7c56a79ff1c..e22574498c7cf7 100644 --- a/packages/block-library/src/preformatted/edit.native.js +++ b/packages/block-library/src/preformatted/edit.native.js @@ -12,7 +12,7 @@ import { withPreferredColorScheme } from '@wordpress/compose'; import WebPreformattedEdit from './edit.js'; import styles from './styles.scss'; -function PreformattedEdit( props ) { +export function PreformattedEdit( props ) { const { getStylesFromColorScheme } = props; const richTextStyle = getStylesFromColorScheme( styles.wpRichTextLight, diff --git a/packages/block-library/src/preformatted/style.scss b/packages/block-library/src/preformatted/style.scss new file mode 100644 index 00000000000000..d9309db9cf4a4e --- /dev/null +++ b/packages/block-library/src/preformatted/style.scss @@ -0,0 +1,3 @@ +.wp-block-preformatted { + white-space: pre-wrap; +} diff --git a/packages/block-library/src/preformatted/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/preformatted/test/__snapshots__/edit.native.js.snap new file mode 100644 index 00000000000000..9d729e151661dc --- /dev/null +++ b/packages/block-library/src/preformatted/test/__snapshots__/edit.native.js.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`core/more/edit/native should match snapshot when content is empty 1`] = ` +<View> + <PreformattedEdit + getStylesFromColorScheme={[Function]} + setAttributes={[MockFunction]} + /> +</View> +`; + +exports[`core/more/edit/native should match snapshot when content is not empty 1`] = ` +<View> + <PreformattedEdit + attributes={ + Object { + "content": "Hello World!", + } + } + getStylesFromColorScheme={[Function]} + setAttributes={[MockFunction]} + /> +</View> +`; diff --git a/packages/block-library/src/preformatted/test/edit.native.js b/packages/block-library/src/preformatted/test/edit.native.js new file mode 100644 index 00000000000000..5b72ad1e420324 --- /dev/null +++ b/packages/block-library/src/preformatted/test/edit.native.js @@ -0,0 +1,44 @@ +/** + * External dependencies + */ +import ShallowRenderer from 'react-test-renderer/shallow'; +const shallowRenderer = new ShallowRenderer(); + +/** + * Internal dependencies + */ +import { PreformattedEdit } from '../edit'; + +describe( 'core/more/edit/native', () => { + it( 'renders without crashing', () => { + shallowRenderer.render( + <PreformattedEdit + setAttributes={ jest.fn() } + getStylesFromColorScheme={ jest.fn() } + /> + ); + const element = shallowRenderer.getRenderOutput(); + expect( element.type ).toBeDefined(); + } ); + + it( 'should match snapshot when content is empty', () => { + shallowRenderer.render( + <PreformattedEdit + setAttributes={ jest.fn() } + getStylesFromColorScheme={ ( styles1 ) => styles1 } + /> + ); + expect( shallowRenderer.getRenderOutput() ).toMatchSnapshot(); + } ); + + it( 'should match snapshot when content is not empty', () => { + shallowRenderer.render( + <PreformattedEdit + attributes={ { content: 'Hello World!' } } + setAttributes={ jest.fn() } + getStylesFromColorScheme={ ( styles1 ) => styles1 } + /> + ); + expect( shallowRenderer.getRenderOutput() ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/block-library/src/pullquote/block.json b/packages/block-library/src/pullquote/block.json index fa49196bec1d4f..e8a0fd00d52b97 100644 --- a/packages/block-library/src/pullquote/block.json +++ b/packages/block-library/src/pullquote/block.json @@ -36,5 +36,7 @@ "wide", "full" ] - } + }, + "editorStyle": "wp-block-pullquote-editor", + "style": "wp-block-pullquote" } diff --git a/packages/block-library/src/pullquote/edit.js b/packages/block-library/src/pullquote/edit.js index 8ba6075d753222..709f3745108741 100644 --- a/packages/block-library/src/pullquote/edit.js +++ b/packages/block-library/src/pullquote/edit.js @@ -124,6 +124,7 @@ function PullQuoteEdit( { value: nextValue, } ) } + aria-label={ __( 'Pullquote text' ) } placeholder={ // translators: placeholder text used for the quote __( 'Write quote…' ) @@ -134,6 +135,7 @@ function PullQuoteEdit( { <RichText identifier="citation" value={ citation } + aria-label={ __( 'Pullquote citation text' ) } placeholder={ // translators: placeholder text used for the citation __( 'Write citation…' ) diff --git a/packages/block-library/src/query-loop/block.json b/packages/block-library/src/query-loop/block.json index 5373403ea79a53..6059febae1dd60 100644 --- a/packages/block-library/src/query-loop/block.json +++ b/packages/block-library/src/query-loop/block.json @@ -11,5 +11,7 @@ "supports": { "reusable": false, "html": false - } + }, + "style": "wp-block-query-loop", + "editorStyle": "wp-block-query-loop-editor" } diff --git a/packages/block-library/src/query-loop/edit.js b/packages/block-library/src/query-loop/edit.js index a32ffd67410507..370eb45e08a7d5 100644 --- a/packages/block-library/src/query-loop/edit.js +++ b/packages/block-library/src/query-loop/edit.js @@ -41,6 +41,7 @@ export default function QueryLoopEdit( { search, exclude, sticky, + inherit, } = {}, queryContext, layout: { type: layoutType = 'flex', columns = 1 } = {}, @@ -78,6 +79,32 @@ export default function QueryLoopEdit( { if ( sticky ) { query.sticky = sticky === 'only'; } + + // When you insert this block outside of the edit site then store + // does not exist therefore we check for its existence. + // TODO: remove this code, edit-site shouldn't be called in block-library. + // This creates a cycle dependency. + if ( inherit && select( 'core/edit-site' ) ) { + // This should be passed from the context exposed by edit site. + const { getEditedPostType, getEditedPostId } = select( + 'core/edit-site' + ); + + if ( 'wp_template' === getEditedPostType() ) { + const { slug } = select( 'core' ).getEntityRecord( + 'postType', + 'wp_template', + getEditedPostId() + ); + + // Change the post-type if needed. + if ( slug?.startsWith( 'archive-' ) ) { + query.postType = slug.replace( 'archive-', '' ); + postType = query.postType; + } + } + } + return { posts: getEntityRecords( 'postType', postType, query ), blocks: getBlocks( clientId ), @@ -97,6 +124,7 @@ export default function QueryLoopEdit( { postType, exclude, sticky, + inherit, ] ); diff --git a/packages/block-library/src/query-loop/index.js b/packages/block-library/src/query-loop/index.js index 3aa9deb41c8139..2995100a1b874f 100644 --- a/packages/block-library/src/query-loop/index.js +++ b/packages/block-library/src/query-loop/index.js @@ -19,4 +19,5 @@ export const settings = { icon: loop, edit, save, + parent: [ 'core/query' ], }; diff --git a/packages/block-library/src/query-loop/index.php b/packages/block-library/src/query-loop/index.php index d1aef57b1cb66f..f611e4fbd81d70 100644 --- a/packages/block-library/src/query-loop/index.php +++ b/packages/block-library/src/query-loop/index.php @@ -42,7 +42,8 @@ function render_block_core_query_loop( $attributes, $content, $block ) { $query['post__not_in'] = array_merge( $query['post__not_in'], $block->context['query']['exclude'] ); } if ( isset( $block->context['query']['perPage'] ) ) { - $query['offset'] = ( $block->context['query']['perPage'] * ( $page - 1 ) ) + $block->context['query']['offset']; + $query['offset'] = ( $block->context['query']['perPage'] * ( $page - 1 ) ) + $block->context['query']['offset']; + $query['posts_per_page'] = $block->context['query']['perPage']; } if ( isset( $block->context['query']['categoryIds'] ) ) { $query['category__in'] = $block->context['query']['categoryIds']; @@ -56,9 +57,6 @@ function render_block_core_query_loop( $attributes, $content, $block ) { if ( isset( $block->context['query']['orderBy'] ) ) { $query['orderby'] = $block->context['query']['orderBy']; } - if ( isset( $block->context['query']['perPage'] ) ) { - $query['posts_per_page'] = $block->context['query']['perPage']; - } if ( isset( $block->context['query']['author'] ) ) { $query['author'] = $block->context['query']['author']; } @@ -67,6 +65,15 @@ function render_block_core_query_loop( $attributes, $content, $block ) { } } + // Override the custom query with the global query if needed. + $use_global_query = ( isset( $block->context['query']['inherit'] ) && $block->context['query']['inherit'] ); + if ( $use_global_query ) { + global $wp_query; + if ( $wp_query && isset( $wp_query->query_vars ) && is_array( $wp_query->query_vars ) ) { + $query = wp_parse_args( $wp_query->query_vars, $query ); + } + } + $posts = get_posts( $query ); $classnames = ''; @@ -89,7 +96,7 @@ function render_block_core_query_loop( $attributes, $content, $block ) { ) ) )->render( array( 'dynamic' => false ) ); - $content .= "<li>{$block_content}</li>"; + $content .= "<li>{$block_content}</li>"; } return sprintf( '<ul %1$s>%2$s</ul>', diff --git a/packages/block-library/src/query-loop/style.scss b/packages/block-library/src/query-loop/style.scss index 40413f4041a27d..676040b147ebbd 100644 --- a/packages/block-library/src/query-loop/style.scss +++ b/packages/block-library/src/query-loop/style.scss @@ -13,11 +13,15 @@ flex-wrap: wrap; li { - margin: 0 1.25em 1.25em 0; + margin: 0 0 1.25em 0; width: 100%; } @include break-small { + li { + margin-right: 1.25em; + } + @for $i from 2 through 6 { &.is-flex-container.columns-#{ $i } > li { width: calc((100% / #{ $i }) - 1.25em + (1.25em / #{ $i })); diff --git a/packages/block-library/src/query/block.json b/packages/block-library/src/query/block.json index 7477e03d6f8485..302ea1a8e80856 100644 --- a/packages/block-library/src/query/block.json +++ b/packages/block-library/src/query/block.json @@ -20,7 +20,8 @@ "author": "", "search": "", "exclude": [], - "sticky": "" + "sticky": "", + "inherit": true } }, "layout": { @@ -40,5 +41,6 @@ ], "supports": { "html": false - } + }, + "editorStyle": "wp-block-query-editor" } diff --git a/packages/block-library/src/query/edit/query-inspector-controls.js b/packages/block-library/src/query/edit/query-inspector-controls.js index 0be60543a65122..93323ae63a38cd 100644 --- a/packages/block-library/src/query/edit/query-inspector-controls.js +++ b/packages/block-library/src/query/edit/query-inspector-controls.js @@ -14,12 +14,20 @@ import { FormTokenField, SelectControl, RangeControl, + ToggleControl, Notice, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { InspectorControls } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; -import { useEffect, useState, useCallback, useMemo } from '@wordpress/element'; +import { addQueryArgs } from '@wordpress/url'; +import { + useEffect, + useState, + useCallback, + useMemo, + createInterpolateElement, +} from '@wordpress/element'; /** * Internal dependencies @@ -33,6 +41,21 @@ const stickyOptions = [ { label: __( 'Only' ), value: 'only' }, ]; +const CreateNewPostLink = ( { type } ) => { + const newPostUrl = addQueryArgs( 'post-new.php', { + post_type: type, + } ); + return ( + <div className="wp-block-query__create-new-link"> + { createInterpolateElement( + __( '<a>Create a new post</a> for this feed.' ), + // eslint-disable-next-line jsx-a11y/anchor-has-content + { a: <a href={ newPostUrl } /> } + ) } + </div> + ); +}; + export default function QueryInspectorControls( { attributes: { query, layout }, setQuery, @@ -44,6 +67,7 @@ export default function QueryInspectorControls( { author: selectedAuthorId, postType, sticky, + inherit, } = query; const [ showCategories, setShowCategories ] = useState( true ); const [ showTags, setShowTags ] = useState( true ); @@ -129,22 +153,39 @@ export default function QueryInspectorControls( { const [ querySearch, setQuerySearch ] = useState( query.search ); const onChangeDebounced = useCallback( - debounce( () => setQuery( { search: querySearch } ), 250 ), - [ querySearch ] + debounce( () => { + if ( query.search !== querySearch ) { + setQuery( { search: querySearch } ); + } + }, 250 ), + [ querySearch, query.search ] ); + useEffect( () => { onChangeDebounced(); return onChangeDebounced.cancel; }, [ querySearch, onChangeDebounced ] ); + return ( <InspectorControls> + <CreateNewPostLink type={ postType } /> <PanelBody title={ __( 'Settings' ) }> - <SelectControl - options={ postTypesSelectOptions } - value={ postType } - label={ __( 'Post Type' ) } - onChange={ onPostTypeChange } + <ToggleControl + label={ __( 'Inherit query from URL' ) } + help={ __( + 'Disable the option to customize the query arguments. Leave enabled to inherit the global query depending on the URL.' + ) } + checked={ !! inherit } + onChange={ ( value ) => setQuery( { inherit: !! value } ) } /> + { ! inherit && ( + <SelectControl + options={ postTypesSelectOptions } + value={ postType } + label={ __( 'Post Type' ) } + onChange={ onPostTypeChange } + /> + ) } { layout?.type === 'flex' && ( <> <RangeControl @@ -165,13 +206,17 @@ export default function QueryInspectorControls( { ) } </> ) } - <QueryControls - { ...{ order, orderBy } } - onOrderChange={ ( value ) => setQuery( { order: value } ) } - onOrderByChange={ ( value ) => - setQuery( { orderBy: value } ) - } - /> + { ! inherit && ( + <QueryControls + { ...{ order, orderBy } } + onOrderChange={ ( value ) => + setQuery( { order: value } ) + } + onOrderByChange={ ( value ) => + setQuery( { orderBy: value } ) + } + /> + ) } { showSticky && ( <SelectControl label={ __( 'Sticky posts' ) } @@ -181,45 +226,48 @@ export default function QueryInspectorControls( { /> ) } </PanelBody> - <PanelBody title={ __( 'Filters' ) }> - { showCategories && categories?.terms?.length > 0 && ( - <FormTokenField - label={ __( 'Categories' ) } - value={ ( query.categoryIds || [] ).map( - ( categoryId ) => ( { - id: categoryId, - value: categories.mapById[ categoryId ].name, + { ! inherit && ( + <PanelBody title={ __( 'Filters' ) }> + { showCategories && categories?.terms?.length > 0 && ( + <FormTokenField + label={ __( 'Categories' ) } + value={ ( query.categoryIds || [] ).map( + ( categoryId ) => ( { + id: categoryId, + value: + categories.mapById[ categoryId ].name, + } ) + ) } + suggestions={ categories.names } + onChange={ onCategoriesChange } + /> + ) } + { showTags && tags?.terms?.length > 0 && ( + <FormTokenField + label={ __( 'Tags' ) } + value={ ( query.tagIds || [] ).map( ( tagId ) => ( { + id: tagId, + value: tags.mapById[ tagId ].name, + } ) ) } + suggestions={ tags.names } + onChange={ onTagsChange } + /> + ) } + <QueryControls + { ...{ selectedAuthorId, authorList } } + onAuthorChange={ ( value ) => + setQuery( { + author: value !== '' ? +value : undefined, } ) - ) } - suggestions={ categories.names } - onChange={ onCategoriesChange } + } /> - ) } - { showTags && tags?.terms?.length > 0 && ( - <FormTokenField - label={ __( 'Tags' ) } - value={ ( query.tagIds || [] ).map( ( tagId ) => ( { - id: tagId, - value: tags.mapById[ tagId ].name, - } ) ) } - suggestions={ tags.names } - onChange={ onTagsChange } + <TextControl + label={ __( 'Keyword' ) } + value={ querySearch } + onChange={ setQuerySearch } /> - ) } - <QueryControls - { ...{ selectedAuthorId, authorList } } - onAuthorChange={ ( value ) => - setQuery( { - author: value !== '' ? +value : undefined, - } ) - } - /> - <TextControl - label={ __( 'Keyword' ) } - value={ querySearch } - onChange={ setQuerySearch } - /> - </PanelBody> + </PanelBody> + ) } </InspectorControls> ); } diff --git a/packages/block-library/src/query/edit/query-placeholder.js b/packages/block-library/src/query/edit/query-placeholder.js index 1d638f8f5a1be0..6d9172141a5005 100644 --- a/packages/block-library/src/query/edit/query-placeholder.js +++ b/packages/block-library/src/query/edit/query-placeholder.js @@ -7,7 +7,10 @@ import { __experimentalBlockVariationPicker, __experimentalGetMatchingVariation as getMatchingVariation, } from '@wordpress/block-editor'; -import { createBlocksFromInnerBlocksTemplate } from '@wordpress/blocks'; +import { + createBlocksFromInnerBlocksTemplate, + store as blocksStore, +} from '@wordpress/blocks'; const QueryPlaceholder = ( { clientId, name, attributes, setAttributes } ) => { const { @@ -21,7 +24,7 @@ const QueryPlaceholder = ( { clientId, name, attributes, setAttributes } ) => { getBlockVariations, getBlockType, getDefaultBlockVariation, - } = select( 'core/blocks' ); + } = select( blocksStore ); return { blockType: getBlockType( name ), diff --git a/packages/block-library/src/query/editor.scss b/packages/block-library/src/query/editor.scss index c4bffda04e7858..e28e6de5643246 100644 --- a/packages/block-library/src/query/editor.scss +++ b/packages/block-library/src/query/editor.scss @@ -5,3 +5,7 @@ .block-library-query-toolbar__popover .components-popover__content { min-width: 230px; } + +.wp-block-query__create-new-link { + padding: 0 $grid-unit-20 $grid-unit-20 56px; +} diff --git a/packages/block-library/src/query/test/utils.js b/packages/block-library/src/query/test/utils.js index c7acb4b6110bae..9c92ae7c581714 100644 --- a/packages/block-library/src/query/test/utils.js +++ b/packages/block-library/src/query/test/utils.js @@ -13,8 +13,8 @@ describe( 'Query block utils', () => { expect( getTermsInfo( terms ) ).toEqual( expect.objectContaining( { mapById: expect.objectContaining( { - '4': expect.objectContaining( { name: 'nba' } ), - '11': expect.objectContaining( { + 4: expect.objectContaining( { name: 'nba' } ), + 11: expect.objectContaining( { name: 'featured', } ), } ), diff --git a/packages/block-library/src/query/variations.js b/packages/block-library/src/query/variations.js index 4d6d4f1913530a..dbf9290cc58fc1 100644 --- a/packages/block-library/src/query/variations.js +++ b/packages/block-library/src/query/variations.js @@ -41,7 +41,7 @@ const variations = [ }, { name: 'title-date', - title: __( 'Title and Date' ), + title: __( 'Title & Date' ), icon: titleDate, innerBlocks: [ [ @@ -54,7 +54,7 @@ const variations = [ }, { name: 'title-excerpt', - title: __( 'Title and Excerpt' ), + title: __( 'Title & Excerpt' ), icon: titleExcerpt, innerBlocks: [ [ @@ -67,7 +67,7 @@ const variations = [ }, { name: 'title-date-excerpt', - title: __( 'Title, Date and Excerpt' ), + title: __( 'Title, Date, & Excerpt' ), icon: titleDateExcerpt, innerBlocks: [ [ @@ -84,7 +84,7 @@ const variations = [ }, { name: 'image-date-title', - title: __( 'Image, Date and Title ' ), + title: __( 'Image, Date, & Title' ), icon: imageDateTitle, innerBlocks: [ [ diff --git a/packages/block-library/src/quote/block.json b/packages/block-library/src/quote/block.json index 9de3a338c9f1ed..bba83461367fa1 100644 --- a/packages/block-library/src/quote/block.json +++ b/packages/block-library/src/quote/block.json @@ -22,5 +22,7 @@ }, "supports": { "anchor": true - } + }, + "editorStyle": "wp-block-quote-editor", + "style": "wp-block-quote" } diff --git a/packages/block-library/src/quote/edit.js b/packages/block-library/src/quote/edit.js index 8ad2d5204f2b8c..8064aca0eef303 100644 --- a/packages/block-library/src/quote/edit.js +++ b/packages/block-library/src/quote/edit.js @@ -62,6 +62,7 @@ export default function QuoteEdit( { onReplace( [] ); } } } + aria-label={ __( 'Quote text' ) } placeholder={ // translators: placeholder text used for the quote __( 'Write quote…' ) @@ -88,6 +89,7 @@ export default function QuoteEdit( { } ) } __unstableMobileNoFocusOnMount + aria-label={ __( 'Quote citation text' ) } placeholder={ // translators: placeholder text used for the citation __( 'Write citation…' ) diff --git a/packages/block-library/src/rss/block.json b/packages/block-library/src/rss/block.json index 49555c41cb0ea6..15a0feaa6f3552 100644 --- a/packages/block-library/src/rss/block.json +++ b/packages/block-library/src/rss/block.json @@ -39,5 +39,7 @@ "supports": { "align": true, "html": false - } + }, + "editorStyle": "wp-block-rss-editor", + "style": "wp-block-rss" } diff --git a/packages/block-library/src/rss/editor.scss b/packages/block-library/src/rss/editor.scss index 48d14bc25631c5..aa6288897aea4b 100644 --- a/packages/block-library/src/rss/editor.scss +++ b/packages/block-library/src/rss/editor.scss @@ -1,9 +1,3 @@ -.wp-block-rss { - padding-left: 2.5em; - &.is-grid { - padding-left: 0; - } -} .wp-block-rss li a > div { display: inline; } diff --git a/packages/block-library/src/rss/style.scss b/packages/block-library/src/rss/style.scss index a2727bab04f560..3a3995cf6e384e 100644 --- a/packages/block-library/src/rss/style.scss +++ b/packages/block-library/src/rss/style.scss @@ -1,4 +1,9 @@ .wp-block-rss { + // This needs extra specificity due to the reset mixin on the parent: https://github.com/WordPress/gutenberg/blob/a250e9e5fe00dd5195624f96a3d924e7078951c3/packages/edit-post/src/style.scss#L54 + &.wp-block-rss { + box-sizing: border-box; + } + &.alignleft { /*rtl:ignore*/ margin-right: 2em; diff --git a/packages/block-library/src/search/block.json b/packages/block-library/src/search/block.json index d57ba7fd9c3900..534be0e97cb5fa 100644 --- a/packages/block-library/src/search/block.json +++ b/packages/block-library/src/search/block.json @@ -35,5 +35,7 @@ "supports": { "align": [ "left", "center", "right" ], "html": false - } + }, + "editorStyle": "wp-block-search-editor", + "style": "wp-block-search" } diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index 7e7b1934b3fc31..c82086f2e5ff33 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -221,17 +221,6 @@ export default function SearchEdit( { > { __( 'Button Inside' ) } </MenuItem> - <MenuItem - icon={ buttonOnly } - onClick={ () => { - setAttributes( { - buttonPosition: 'button-only', - } ); - onClose(); - } } - > - { __( 'Button Only' ) } - </MenuItem> </MenuGroup> ) } </DropdownMenu> @@ -343,7 +332,6 @@ export default function SearchEdit( { width: `${ width }${ widthUnit }`, } } className="wp-block-search__inside-wrapper" - isResetValueOnUnitChange minWidth={ MIN_WIDTH } enable={ getResizableSides() } onResizeStart={ ( event, direction, elt ) => { diff --git a/packages/block-library/src/search/icons.js b/packages/block-library/src/search/icons.js index d795398382859b..3d50d161462965 100644 --- a/packages/block-library/src/search/icons.js +++ b/packages/block-library/src/search/icons.js @@ -18,7 +18,7 @@ export const buttonOutside = ( height="9.5" transform="rotate(-90 4.75 15.25)" stroke="currentColor" - stroke-width="1.5" + strokeWidth="1.5" fill="none" /> <Rect x="16" y="10" width="4" height="4" rx="1" fill="currentColor" /> @@ -34,7 +34,7 @@ export const buttonInside = ( height="14.5" transform="rotate(-90 4.75 15.25)" stroke="currentColor" - stroke-width="1.5" + strokeWidth="1.5" fill="none" /> <Rect x="14" y="10" width="4" height="4" rx="1" fill="currentColor" /> @@ -51,7 +51,7 @@ export const noButton = ( transform="rotate(-90 4.75 15.25)" stroke="currentColor" fill="none" - stroke-width="1.5" + strokeWidth="1.5" /> </SVG> ); @@ -66,7 +66,7 @@ export const buttonWithIcon = ( rx="1.25" stroke="currentColor" fill="none" - stroke-width="1.5" + strokeWidth="1.5" /> <Rect x="8" y="11" width="8" height="2" fill="currentColor" /> </SVG> @@ -82,7 +82,7 @@ export const toggleLabel = ( transform="rotate(-90 4.75 17.25)" stroke="currentColor" fill="none" - stroke-width="1.5" + strokeWidth="1.5" /> <Rect x="4" y="7" width="10" height="2" fill="currentColor" /> </SVG> diff --git a/packages/block-library/src/search/style.scss b/packages/block-library/src/search/style.scss index f5972a44658425..6f47aa3761dbcb 100644 --- a/packages/block-library/src/search/style.scss +++ b/packages/block-library/src/search/style.scss @@ -3,7 +3,7 @@ .wp-block-search__button { background: #f7f7f7; border: 1px solid #ccc; - padding: 6px 10px; + padding: 0.375em 0.625em; color: #32373c; margin-left: 0.625em; word-break: normal; diff --git a/packages/block-library/src/separator/block.json b/packages/block-library/src/separator/block.json index 3cf6ae1b505ee1..2983a93a86190a 100644 --- a/packages/block-library/src/separator/block.json +++ b/packages/block-library/src/separator/block.json @@ -13,5 +13,7 @@ "supports": { "anchor": true, "align": ["center","wide","full"] - } + }, + "editorStyle": "wp-block-separator-editor", + "style": "wp-block-separator" } diff --git a/packages/block-library/src/shortcode/block.json b/packages/block-library/src/shortcode/block.json index 8c419763739044..4f92abd419f6e9 100644 --- a/packages/block-library/src/shortcode/block.json +++ b/packages/block-library/src/shortcode/block.json @@ -12,5 +12,6 @@ "className": false, "customClassName": false, "html": false - } + }, + "editorStyle": "wp-block-shortcode-editor" } diff --git a/packages/block-library/src/shortcode/edit.js b/packages/block-library/src/shortcode/edit.js index 02de1744787642..aecebd115a983f 100644 --- a/packages/block-library/src/shortcode/edit.js +++ b/packages/block-library/src/shortcode/edit.js @@ -23,6 +23,7 @@ export default function ShortcodeEdit( { attributes, setAttributes } ) { className="blocks-shortcode__textarea" id={ inputId } value={ attributes.text } + aria-label={ __( 'Shortcode text' ) } placeholder={ __( 'Write shortcode here…' ) } onChange={ ( text ) => setAttributes( { text } ) } /> diff --git a/packages/block-library/src/site-logo/block.json b/packages/block-library/src/site-logo/block.json index b2818f5e751ac6..35313f0212b39d 100644 --- a/packages/block-library/src/site-logo/block.json +++ b/packages/block-library/src/site-logo/block.json @@ -12,5 +12,7 @@ }, "supports": { "html": false - } + }, + "editorStyle": "wp-block-site-logo-editor", + "style": "wp-block-site-logo" } diff --git a/packages/block-library/src/site-logo/edit.js b/packages/block-library/src/site-logo/edit.js index f19f2076eeaac8..39681475fad943 100644 --- a/packages/block-library/src/site-logo/edit.js +++ b/packages/block-library/src/site-logo/edit.js @@ -9,7 +9,7 @@ import { includes, pick } from 'lodash'; */ import { isBlobURL } from '@wordpress/blob'; import { useState, useRef } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; +import { __, isRTL } from '@wordpress/i18n'; import { Notice, PanelBody, @@ -63,7 +63,7 @@ const SiteLogo = ( { const classes = classnames( { 'is-transient': isBlobURL( logoUrl ), } ); - const { maxWidth, isRTL, title } = useSelect( ( select ) => { + const { maxWidth, title } = useSelect( ( select ) => { const { getSettings } = select( 'core/block-editor' ); const siteEntities = select( 'core' ).getEditedEntityRecord( 'root', @@ -71,7 +71,7 @@ const SiteLogo = ( { ); return { title: siteEntities.title, - ...pick( getSettings(), [ 'imageSizes', 'isRTL', 'maxWidth' ] ), + ...pick( getSettings(), [ 'imageSizes', 'maxWidth' ] ), }; } ); @@ -151,7 +151,7 @@ const SiteLogo = ( { // When the image is centered, show both handles. showRightHandle = true; showLeftHandle = true; - } else if ( isRTL ) { + } else if ( isRTL() ) { // In RTL mode the image is on the right by default. // Show the right handle and hide the left handle only when it is // aligned left. Otherwise always show the left handle. diff --git a/packages/block-library/src/site-logo/index.js b/packages/block-library/src/site-logo/index.js index b021f2b08bbecc..07e7856860fc3f 100644 --- a/packages/block-library/src/site-logo/index.js +++ b/packages/block-library/src/site-logo/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, _x } from '@wordpress/i18n'; /** * Internal dependencies @@ -17,6 +17,14 @@ export const settings = { title: __( 'Site Logo' ), description: __( 'Show a site logo' ), icon, + styles: [ + { + name: 'default', + label: _x( 'Default', 'block style' ), + isDefault: true, + }, + { name: 'rounded', label: _x( 'Rounded', 'block style' ) }, + ], supports: { align: true, alignWide: false, diff --git a/packages/block-library/src/site-logo/style.scss b/packages/block-library/src/site-logo/style.scss index 6e186d32296df0..c3ad4dc2a57f22 100644 --- a/packages/block-library/src/site-logo/style.scss +++ b/packages/block-library/src/site-logo/style.scss @@ -1,5 +1,14 @@ .wp-block-custom-logo { + line-height: 0; + .aligncenter { display: table; } + + // Variations + &.is-style-rounded img { + // We use an absolute pixel to prevent the oval shape that a value of 50% would give + // to rectangular images. A pill-shape is better than otherwise. + border-radius: 9999px; + } } diff --git a/packages/block-library/src/site-tagline/edit.js b/packages/block-library/src/site-tagline/edit.js index 937291438f1bb5..faf839c0bfb657 100644 --- a/packages/block-library/src/site-tagline/edit.js +++ b/packages/block-library/src/site-tagline/edit.js @@ -41,7 +41,8 @@ export default function SiteTaglineEdit( { attributes, setAttributes } ) { <RichText allowedFormats={ [] } onChange={ setSiteTagline } - placeholder={ __( 'Site Tagline' ) } + aria-label={ __( 'Site tagline text' ) } + placeholder={ __( 'Write site tagline…' ) } tagName="p" value={ siteTagline } { ...blockProps } diff --git a/packages/block-library/src/site-title/edit/index.js b/packages/block-library/src/site-title/edit/index.js index df4de13dc33aba..8feb6d4a4611d9 100644 --- a/packages/block-library/src/site-title/edit/index.js +++ b/packages/block-library/src/site-title/edit/index.js @@ -50,7 +50,8 @@ export default function SiteTitleEdit( { attributes, setAttributes } ) { <RichText tagName={ tagName } - placeholder={ __( 'Site Title' ) } + aria-label={ __( 'Site title text' ) } + placeholder={ __( 'Write site title…' ) } value={ title } onChange={ setTitle } allowedFormats={ [] } diff --git a/packages/block-library/src/social-link/block.json b/packages/block-library/src/social-link/block.json index 0b9072548b6f80..0ec91e23d905d7 100644 --- a/packages/block-library/src/social-link/block.json +++ b/packages/block-library/src/social-link/block.json @@ -22,5 +22,6 @@ "supports": { "reusable": false, "html": false - } + }, + "editorStyle": "wp-block-social-link-editor" } diff --git a/packages/block-library/src/social-link/variations.js b/packages/block-library/src/social-link/variations.js index 4297ced3c41718..a2f259feaaabbd 100644 --- a/packages/block-library/src/social-link/variations.js +++ b/packages/block-library/src/social-link/variations.js @@ -304,4 +304,15 @@ const variations = [ }, ]; +/** + * Add `isActive` function to all `social link` variations, if not defined. + * `isActive` function is used to find a variation match from a created + * Block by providing its attributes. + */ +variations.forEach( ( variation ) => { + if ( variation.isActive ) return; + variation.isActive = ( blockAttributes, variationAttributes ) => + blockAttributes.service === variationAttributes.service; +} ); + export default variations; diff --git a/packages/block-library/src/social-links/block.json b/packages/block-library/src/social-links/block.json index 47147dee69c89b..2bc44dd79f36e7 100644 --- a/packages/block-library/src/social-links/block.json +++ b/packages/block-library/src/social-links/block.json @@ -21,5 +21,7 @@ "right" ], "anchor": true - } + }, + "editorStyle": "wp-block-social-links-editor", + "style": "wp-block-social-links" } diff --git a/packages/block-library/src/social-links/style.scss b/packages/block-library/src/social-links/style.scss index cc93e45a113619..b95d01c771bd79 100644 --- a/packages/block-library/src/social-links/style.scss +++ b/packages/block-library/src/social-links/style.scss @@ -57,6 +57,17 @@ &.has-huge-icon-size { font-size: 48px; } + + // Center flex items. This has an equivalent in editor.scss. + // It also needs to override some of the default classes usually applied to the centering class. + // align left must not be set, because this is the default (flex-start). + &.aligncenter { + justify-content: center; + display: flex; + } + &.alignright { + justify-content: flex-end; + } } .wp-social-link { @@ -88,13 +99,6 @@ } } -// Center flex items. This has an equivalent in editor.scss. -// It also needs to override some of the default classes usually applied to the centering class. -.wp-block-social-links.aligncenter { - justify-content: center; - display: flex; -} - // Provide colors for a range of icons. .wp-block-social-links:not(.is-style-logos-only) { diff --git a/packages/block-library/src/spacer/block.json b/packages/block-library/src/spacer/block.json index 9882f53dfa5af2..fd4172079fa50c 100644 --- a/packages/block-library/src/spacer/block.json +++ b/packages/block-library/src/spacer/block.json @@ -10,5 +10,7 @@ }, "supports": { "anchor": true - } + }, + "editorStyle": "wp-block-spacer-editor", + "style": "wp-block-spacer" } diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index eeafa5502873c3..3a44e690c5e82e 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -25,6 +25,8 @@ @import "./navigation-link/style.scss"; @import "./paragraph/style.scss"; @import "./post-author/style.scss"; +@import "./post-comments-form/style.scss"; +@import "./preformatted/style.scss"; @import "./pullquote/style.scss"; @import "./query-loop/style.scss"; @import "./quote/style.scss"; @@ -35,74 +37,11 @@ @import "./social-links/style.scss"; @import "./spacer/style.scss"; @import "./subhead/style.scss"; +@import "./tag-cloud/style.scss"; @import "./table/style.scss"; @import "./text-columns/style.scss"; +@import "./verse/style.scss"; @import "./video/style.scss"; @import "./post-featured-image/style.scss"; -// The following selectors have increased specificity (using the :root prefix) -// to assure colors take effect over another base class color, mainly to let -// the colors override the added specificity by link states such as :hover. - -:root { - // Background colors. - @include background-colors(); - - // Foreground colors. - @include foreground-colors(); - - // Gradients - @include gradient-colors(); - - .has-link-color a { - color: var(--wp--style--color--link, #00e); - } -} - -// Font sizes. -.has-small-font-size { - font-size: 0.8125em; -} - -.has-regular-font-size, // Not used now, kept because of backward compatibility. -.has-normal-font-size { - font-size: 1em; -} - -.has-medium-font-size { - font-size: 1.25em; -} - -.has-large-font-size { - font-size: 2.25em; -} - -.has-larger-font-size, // Not used now, kept because of backward compatibility. -.has-huge-font-size { - font-size: 2.625em; -} - -// Text alignments. -.has-text-align-center { - text-align: center; -} - -.has-text-align-left { - /*rtl:ignore*/ - text-align: left; -} - -.has-text-align-right { - /*rtl:ignore*/ - text-align: right; -} - -// This tag marks the end of the styles that apply to editing canvas contents and need to be manipulated when we resize the editor. -#end-resizable-editor-section { - display: none; -} - -// Block alignments. -.aligncenter { - clear: both; -} +@import "common.scss"; diff --git a/packages/block-library/src/subhead/block.json b/packages/block-library/src/subhead/block.json index 6fe2bf287f7eac..2dd213d5391d05 100644 --- a/packages/block-library/src/subhead/block.json +++ b/packages/block-library/src/subhead/block.json @@ -15,5 +15,7 @@ "supports": { "inserter": false, "multiple": false - } + }, + "editorStyle": "wp-block-subhead-editor", + "style": "wp-block-subhead" } diff --git a/packages/block-library/src/subhead/edit.js b/packages/block-library/src/subhead/edit.js index f9551eaad3a77e..c0317ba6ef9801 100644 --- a/packages/block-library/src/subhead/edit.js +++ b/packages/block-library/src/subhead/edit.js @@ -43,6 +43,7 @@ export default function SubheadEdit( { } } style={ { textAlign: align } } className={ className } + aria-label={ __( 'Subheading text' ) } placeholder={ placeholder || __( 'Write subheading…' ) } /> </div> diff --git a/packages/block-library/src/table/block.json b/packages/block-library/src/table/block.json index 1c3529f93eca9f..65e907620afb51 100644 --- a/packages/block-library/src/table/block.json +++ b/packages/block-library/src/table/block.json @@ -126,5 +126,7 @@ "anchor": true, "align": true, "__experimentalSelector": ".wp-block-table > table" - } + }, + "editorStyle": "wp-block-table-editor", + "style": "wp-block-table" } diff --git a/packages/block-library/src/table/edit.js b/packages/block-library/src/table/edit.js index 941516ed295c12..5876f7f956c250 100644 --- a/packages/block-library/src/table/edit.js +++ b/packages/block-library/src/table/edit.js @@ -101,6 +101,12 @@ const ALIGNMENT_CONTROLS = [ const withCustomBackgroundColors = createCustomColorsHOC( BACKGROUND_COLORS ); +const cellAriaLabel = { + head: __( 'Header cell text' ), + body: __( 'Body cell text' ), + foot: __( 'Footer cell text' ), +}; + const placeholder = { head: __( 'Header label' ), foot: __( 'Footer label' ), @@ -432,6 +438,7 @@ function TableEdit( { type: 'cell', } ); } } + aria-label={ cellAriaLabel[ name ] } placeholder={ placeholder[ name ] } /> ) @@ -520,6 +527,7 @@ function TableEdit( { { ! isEmpty && ( <RichText tagName="figcaption" + aria-label={ __( 'Table caption text' ) } placeholder={ __( 'Write caption…' ) } value={ caption } onChange={ ( value ) => diff --git a/packages/block-library/src/tag-cloud/block.json b/packages/block-library/src/tag-cloud/block.json index 3ec21ed2765ccb..de4b9b9714f664 100644 --- a/packages/block-library/src/tag-cloud/block.json +++ b/packages/block-library/src/tag-cloud/block.json @@ -15,5 +15,6 @@ "supports": { "html": false, "align": true - } + }, + "editorStyle": "wp-block-tag-cloud-editor" } diff --git a/packages/block-library/src/tag-cloud/style.scss b/packages/block-library/src/tag-cloud/style.scss new file mode 100644 index 00000000000000..3592e5d2473142 --- /dev/null +++ b/packages/block-library/src/tag-cloud/style.scss @@ -0,0 +1,10 @@ +.wp-block-tag-cloud { + &.aligncenter { + text-align: center; + } + + &.alignfull { + padding-left: 1em; + padding-right: 1em; + } +} diff --git a/packages/block-library/src/template-part/block.json b/packages/block-library/src/template-part/block.json index 2cf99160a027b2..3d9e5dfa0476e9 100644 --- a/packages/block-library/src/template-part/block.json +++ b/packages/block-library/src/template-part/block.json @@ -24,5 +24,6 @@ "gradients": true, "link": true } - } + }, + "editorStyle": "wp-block-template-part-editor" } diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index 4dc0b89640c8ef..e96f85d7ecadbc 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -3,8 +3,13 @@ */ import { useRef, useEffect } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; -import { BlockControls, useBlockProps } from '@wordpress/block-editor'; import { + BlockControls, + InspectorAdvancedControls, + useBlockProps, +} from '@wordpress/block-editor'; +import { + SelectControl, Dropdown, ToolbarGroup, ToolbarButton, @@ -96,53 +101,77 @@ export default function TemplatePartEdit( { // Part of a template file, post ID not resolved yet. const isUnresolvedTemplateFile = ! isPlaceholder && ! postId; + const inspectorAdvancedControls = ( + <InspectorAdvancedControls> + <SelectControl + label={ __( 'HTML element' ) } + options={ [ + { label: __( 'Default (<div>)' ), value: 'div' }, + { label: '<header>', value: 'header' }, + { label: '<main>', value: 'main' }, + { label: '<section>', value: 'section' }, + { label: '<article>', value: 'article' }, + { label: '<aside>', value: 'aside' }, + { label: '<footer>', value: 'footer' }, + ] } + value={ TagName } + onChange={ ( value ) => setAttributes( { tagName: value } ) } + /> + </InspectorAdvancedControls> + ); + return ( - <TagName { ...blockProps }> - { isPlaceholder && ( - <TemplatePartPlaceholder - setAttributes={ setAttributes } - innerBlocks={ innerBlocks } - /> - ) } - { isTemplateFile && ( - <BlockControls> - <ToolbarGroup className="wp-block-template-part__block-control-group"> - <TemplatePartNamePanel - postId={ postId } - setAttributes={ setAttributes } - /> - <Dropdown - className="wp-block-template-part__preview-dropdown-button" - contentClassName="wp-block-template-part__preview-dropdown-content" - position="bottom right left" - renderToggle={ ( { isOpen, onToggle } ) => ( - <ToolbarButton - aria-expanded={ isOpen } - icon={ isOpen ? chevronUp : chevronDown } - label={ __( 'Choose another' ) } - onClick={ onToggle } - // Disable when open to prevent odd FireFox bug causing reopening. - // As noted in https://github.com/WordPress/gutenberg/pull/24990#issuecomment-689094119 . - disabled={ isOpen } - /> - ) } - renderContent={ ( { onClose } ) => ( - <TemplatePartSelection - setAttributes={ setAttributes } - onClose={ onClose } - /> - ) } - /> - </ToolbarGroup> - </BlockControls> - ) } - { isTemplateFile && ( - <TemplatePartInnerBlocks - postId={ postId } - hasInnerBlocks={ innerBlocks.length > 0 } - /> - ) } - { isUnresolvedTemplateFile && <Spinner /> } - </TagName> + <> + { inspectorAdvancedControls } + <TagName { ...blockProps }> + { isPlaceholder && ( + <TemplatePartPlaceholder + setAttributes={ setAttributes } + innerBlocks={ innerBlocks } + /> + ) } + { isTemplateFile && ( + <BlockControls> + <ToolbarGroup className="wp-block-template-part__block-control-group"> + <TemplatePartNamePanel + postId={ postId } + setAttributes={ setAttributes } + /> + <Dropdown + className="wp-block-template-part__preview-dropdown-button" + contentClassName="wp-block-template-part__preview-dropdown-content" + position="bottom right left" + renderToggle={ ( { isOpen, onToggle } ) => ( + <ToolbarButton + aria-expanded={ isOpen } + icon={ + isOpen ? chevronUp : chevronDown + } + label={ __( 'Choose another' ) } + onClick={ onToggle } + // Disable when open to prevent odd FireFox bug causing reopening. + // As noted in https://github.com/WordPress/gutenberg/pull/24990#issuecomment-689094119 . + disabled={ isOpen } + /> + ) } + renderContent={ ( { onClose } ) => ( + <TemplatePartSelection + setAttributes={ setAttributes } + onClose={ onClose } + /> + ) } + /> + </ToolbarGroup> + </BlockControls> + ) } + { isTemplateFile && ( + <TemplatePartInnerBlocks + postId={ postId } + hasInnerBlocks={ innerBlocks.length > 0 } + /> + ) } + { isUnresolvedTemplateFile && <Spinner /> } + </TagName> + </> ); } diff --git a/packages/block-library/src/template-part/edit/selection/template-part-previews.js b/packages/block-library/src/template-part/edit/selection/template-part-previews.js index e98cfb14210820..b47dfaa92970e5 100644 --- a/packages/block-library/src/template-part/edit/selection/template-part-previews.js +++ b/packages/block-library/src/template-part/edit/selection/template-part-previews.js @@ -9,6 +9,7 @@ import { __, sprintf } from '@wordpress/i18n'; import { BlockPreview } from '@wordpress/block-editor'; import { Icon } from '@wordpress/components'; import { useAsyncList } from '@wordpress/compose'; +import { store as noticesStore } from '@wordpress/notices'; /** * External dependencies @@ -36,7 +37,7 @@ function TemplatePartItem( { // The fallback prevents an error in the parse function while saving. const content = templatePart.content.raw || ''; const blocks = useMemo( () => parse( content ), [ content ] ); - const { createSuccessNotice } = useDispatch( 'core/notices' ); + const { createSuccessNotice } = useDispatch( noticesStore ); const onClick = useCallback( () => { setAttributes( { postId: id, slug, theme } ); diff --git a/packages/block-library/src/template-part/editor.scss b/packages/block-library/src/template-part/editor.scss index 9c1b5275bf4db1..aed32baffd118f 100644 --- a/packages/block-library/src/template-part/editor.scss +++ b/packages/block-library/src/template-part/editor.scss @@ -89,37 +89,3 @@ box-shadow: 0 0 0 $border-width var(--wp-admin-theme-color); } } - -.block-editor-block-list__block[data-type="core/template-part"] { - &.is-selected, - &.has-child-selected { - &::after { - top: $border-width; - bottom: $border-width; - left: $border-width; - right: $border-width; - border-radius: $radius-block-ui - $border-width; // Border is outset, so so subtract the width to achieve correct radius. - } - } - - &.is-selected { - &::after { - // 2px outside. - box-shadow: 0 0 0 $border-width-focus var(--wp-admin-theme-color); - // Show a light color for dark themes. - .is-dark-theme & { - box-shadow: 0 0 0 $border-width-focus $dark-theme-focus; - } - } - } - - &.has-child-selected { - &::after { - box-shadow: 0 0 0 $border-width $gray-300; - - .is-dark-theme & { - box-shadow: 0 0 0 $border-width-focus $gray-700; - } - } - } -} diff --git a/packages/block-library/src/template-part/index.php b/packages/block-library/src/template-part/index.php index c19f23c7bd546b..a960aa969bc906 100644 --- a/packages/block-library/src/template-part/index.php +++ b/packages/block-library/src/template-part/index.php @@ -45,7 +45,7 @@ function render_block_core_template_part( $attributes ) { // Else, if the template part was provided by the active theme, // render the corresponding file content. $template_part_file_path = get_stylesheet_directory() . '/block-template-parts/' . $attributes['slug'] . '.html'; - if ( 0 === validate_file( $template_part_file_path ) && file_exists( $template_part_file_path ) ) { + if ( 0 === validate_file( $attributes['slug'] ) && file_exists( $template_part_file_path ) ) { $content = file_get_contents( $template_part_file_path ); } } diff --git a/packages/block-library/src/template-part/theme.scss b/packages/block-library/src/template-part/theme.scss index e2480154a14c59..58e7b1fdf2d9ff 100644 --- a/packages/block-library/src/template-part/theme.scss +++ b/packages/block-library/src/template-part/theme.scss @@ -2,8 +2,7 @@ .wp-block-template-part { &.has-background { // Matches paragraph Block padding - // Todo: normalise with variables - padding: 20px 30px; + padding: $block-bg-padding--v $block-bg-padding--h; margin-top: 0; margin-bottom: 0; } diff --git a/packages/block-library/src/text-columns/block.json b/packages/block-library/src/text-columns/block.json index 5cfe5e4712b076..bb52b71b0183da 100644 --- a/packages/block-library/src/text-columns/block.json +++ b/packages/block-library/src/text-columns/block.json @@ -29,5 +29,7 @@ }, "supports": { "inserter": false - } + }, + "editorStyle": "wp-block-text-columns-editor", + "style": "wp-block-text-columns" } diff --git a/packages/block-library/src/text-columns/edit.js b/packages/block-library/src/text-columns/edit.js index dacace06acf605..ec69c75e1a4a9e 100644 --- a/packages/block-library/src/text-columns/edit.js +++ b/packages/block-library/src/text-columns/edit.js @@ -6,7 +6,7 @@ import { get, times } from 'lodash'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { PanelBody, RangeControl } from '@wordpress/components'; import { BlockControls, @@ -73,6 +73,11 @@ export default function TextColumnsEdit( { attributes, setAttributes } ) { ], } ); } } + aria-label={ sprintf( + // translators: %d: column index (starting with 1) + __( 'Column %d text' ), + index + 1 + ) } placeholder={ __( 'New Column' ) } /> </div> diff --git a/packages/block-library/src/verse/block.json b/packages/block-library/src/verse/block.json index eec9404d7ab7c9..be3cb69adf0bfd 100644 --- a/packages/block-library/src/verse/block.json +++ b/packages/block-library/src/verse/block.json @@ -15,6 +15,10 @@ } }, "supports": { - "anchor": true - } + "anchor": true, + "__experimentalFontFamily": true, + "fontSize": true + }, + "style": "wp-block-verse", + "editorStyle": "wp-block-verse-editor" } diff --git a/packages/block-library/src/verse/edit.js b/packages/block-library/src/verse/edit.js index 583d228c510a91..a8031e0b0756e0 100644 --- a/packages/block-library/src/verse/edit.js +++ b/packages/block-library/src/verse/edit.js @@ -46,7 +46,8 @@ export default function VerseEdit( { content: nextContent, } ); } } - placeholder={ __( 'Write…' ) } + aria-label={ __( 'Verse text' ) } + placeholder={ __( 'Write verse…' ) } onMerge={ mergeBlocks } textAlign={ textAlign } { ...blockProps } diff --git a/packages/block-library/src/verse/editor.scss b/packages/block-library/src/verse/editor.scss index 6217e8f66ac5ec..f863f5e2556f71 100644 --- a/packages/block-library/src/verse/editor.scss +++ b/packages/block-library/src/verse/editor.scss @@ -1,8 +1,4 @@ pre.wp-block-verse { color: $gray-900; - white-space: nowrap; - font-family: inherit; - font-size: inherit; padding: 1em; - overflow: auto; } diff --git a/packages/block-library/src/verse/style.scss b/packages/block-library/src/verse/style.scss new file mode 100644 index 00000000000000..b13d80b6ddf564 --- /dev/null +++ b/packages/block-library/src/verse/style.scss @@ -0,0 +1,5 @@ +pre.wp-block-verse { + font-family: inherit; + overflow: auto; + white-space: nowrap; +} diff --git a/packages/block-library/src/video/block.json b/packages/block-library/src/video/block.json index e2b7eab17560eb..9f8b569d239b24 100644 --- a/packages/block-library/src/video/block.json +++ b/packages/block-library/src/video/block.json @@ -72,5 +72,7 @@ "supports": { "anchor": true, "align": true - } + }, + "editorStyle": "wp-block-video-editor", + "style": "wp-block-video" } diff --git a/packages/block-library/src/video/edit.js b/packages/block-library/src/video/edit.js index cd3cdfde25ba36..9d540aaf6e8f66 100644 --- a/packages/block-library/src/video/edit.js +++ b/packages/block-library/src/video/edit.js @@ -235,6 +235,7 @@ function VideoEdit( { { ( ! RichText.isEmpty( caption ) || isSelected ) && ( <RichText tagName="figcaption" + aria-label={ __( 'Video caption text' ) } placeholder={ __( 'Write caption…' ) } value={ caption } onChange={ ( value ) => diff --git a/packages/block-library/src/video/index.js b/packages/block-library/src/video/index.js index 341d95eb289060..ce7d6fd2cdd62f 100644 --- a/packages/block-library/src/video/index.js +++ b/packages/block-library/src/video/index.js @@ -23,6 +23,14 @@ export const settings = { ), icon, keywords: [ __( 'movie' ) ], + example: { + attributes: { + src: + 'https://upload.wikimedia.org/wikipedia/commons/c/ca/Wood_thrush_in_Central_Park_switch_sides_%2816510%29.webm', + // translators: Caption accompanying a video of the wood thrush singing, which serves as an example for the Video block. + caption: __( 'Wood thrush singing in Central Park, NYC.' ), + }, + }, transforms, edit, save, diff --git a/packages/block-library/src/video/style.scss b/packages/block-library/src/video/style.scss index de4e9499dd67c5..0735f6bfe8035c 100644 --- a/packages/block-library/src/video/style.scss +++ b/packages/block-library/src/video/style.scss @@ -4,7 +4,7 @@ margin-right: 0; video { - max-width: 100%; + width: 100%; } @supports (position: sticky) { diff --git a/packages/block-serialization-default-parser/package.json b/packages/block-serialization-default-parser/package.json index b610500f6e4573..967b03e7ac56a7 100644 --- a/packages/block-serialization-default-parser/package.json +++ b/packages/block-serialization-default-parser/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/block-serialization-default-parser", - "version": "3.8.0", + "version": "3.9.0", "description": "Block serialization specification parser for WordPress posts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "react-native": "src/index", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/block-serialization-default-parser/src/index.js b/packages/block-serialization-default-parser/src/index.js index c6a844d0cc6dd7..54e76d877dcb02 100644 --- a/packages/block-serialization-default-parser/src/index.js +++ b/packages/block-serialization-default-parser/src/index.js @@ -309,7 +309,7 @@ function nextToken() { // we're also using a trick here because the only difference between a // block opener and a block closer is the leading `/` before `wp:` (and // a closer has no attributes). we can trap them both and process the - // match back in Javascript to see which one it was. + // match back in JavaScript to see which one it was. const matches = tokenizer.exec( document ); // we have no more tokens diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index bcdf8b5ee6d734..9848848d0c6e65 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 6.25.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the blocks namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/blocks/README.md b/packages/blocks/README.md index 233883c81b7ee4..c1f424fffd85a8 100644 --- a/packages/blocks/README.md +++ b/packages/blocks/README.md @@ -73,7 +73,7 @@ function random_image_enqueue_block_editor_assets() { wp_enqueue_script( 'random-image-block', plugins_url( 'block.js', __FILE__ ), - array( 'wp-blocks', 'wp-element' ) + array( 'wp-blocks', 'wp-element', 'wp-block-editor', ) ); } add_action( 'enqueue_block_editor_assets', 'random_image_enqueue_block_editor_assets' ); @@ -81,9 +81,10 @@ add_action( 'enqueue_block_editor_assets', 'random_image_enqueue_block_editor_as ```js // block.js -( function( blocks, element ) { +( function( blocks, element, blockEditor ) { var el = element.createElement, - source = blocks.source; + source = blocks.source, + useBlockProps = blockEditor.useBlockProps; function RandomImage( props ) { var src = 'http://lorempixel.com/400/200/' + props.category; @@ -95,6 +96,8 @@ add_action( 'enqueue_block_editor_assets', 'random_image_enqueue_block_editor_as } blocks.registerBlockType( 'myplugin/random-image', { + apiVersion: 2, + title: 'Random Image', icon: 'format-image', @@ -111,6 +114,7 @@ add_action( 'enqueue_block_editor_assets', 'random_image_enqueue_block_editor_as }, edit: function( props ) { + var blockProps = useBlockProps(); var category = props.attributes.category, children; @@ -134,7 +138,7 @@ add_action( 'enqueue_block_editor_assets', 'random_image_enqueue_block_editor_as ) ); - return el( 'form', { onSubmit: setCategory }, children ); + return el( 'form', Object.assing( blockProps, { onSubmit: setCategory } ), children ); }, save: function( props ) { @@ -143,7 +147,8 @@ add_action( 'enqueue_block_editor_assets', 'random_image_enqueue_block_editor_as } ); } )( window.wp.blocks, - window.wp.element + window.wp.element, + window.wp.blockEditor ); ``` diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 22a7807f46ec92..3514048e8a6148 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/blocks", - "version": "6.24.1", + "version": "6.25.0", "description": "Block API for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -25,7 +25,7 @@ "{src,build,build-module}/{index.js,store/index.js}" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/autop": "file:../autop", "@wordpress/blob": "file:../blob", "@wordpress/block-serialization-default-parser": "file:../block-serialization-default-parser", diff --git a/packages/blocks/src/api/categories.js b/packages/blocks/src/api/categories.js index 6e89b489960b0e..1a0f5a319b277b 100644 --- a/packages/blocks/src/api/categories.js +++ b/packages/blocks/src/api/categories.js @@ -3,6 +3,11 @@ */ import { dispatch, select } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as blocksStore } from '../store'; + /** @typedef {import('../store/reducer').WPBlockCategory} WPBlockCategory */ /** @@ -11,7 +16,7 @@ import { dispatch, select } from '@wordpress/data'; * @return {WPBlockCategory[]} Block categories. */ export function getCategories() { - return select( 'core/blocks' ).getCategories(); + return select( blocksStore ).getCategories(); } /** @@ -20,7 +25,7 @@ export function getCategories() { * @param {WPBlockCategory[]} categories Block categories. */ export function setCategories( categories ) { - dispatch( 'core/blocks' ).setCategories( categories ); + dispatch( blocksStore ).setCategories( categories ); } /** @@ -31,5 +36,5 @@ export function setCategories( categories ) { * that should be updated. */ export function updateCategory( slug, category ) { - dispatch( 'core/blocks' ).updateCategory( slug, category ); + dispatch( blocksStore ).updateCategory( slug, category ); } diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index 1dee92f5b47b08..8612244ff09840 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -25,6 +25,10 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { value: [ 'color', 'background' ], support: [ 'color' ], }, + borderRadius: { + value: [ 'border', 'radius' ], + support: [ '__experimentalBorder', 'radius' ], + }, color: { value: [ 'color', 'text' ], support: [ 'color' ], @@ -39,11 +43,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { }, fontStyle: { value: [ 'typography', 'fontStyle' ], - support: [ '__experimentalFontAppearance' ], + support: [ '__experimentalFontStyle' ], }, fontWeight: { value: [ 'typography', 'fontWeight' ], - support: [ '__experimentalFontAppearance' ], + support: [ '__experimentalFontWeight' ], }, lineHeight: { value: [ 'typography', 'lineHeight' ], diff --git a/packages/blocks/src/api/raw-handling/get-raw-transforms.js b/packages/blocks/src/api/raw-handling/get-raw-transforms.js new file mode 100644 index 00000000000000..cc7d77053a9101 --- /dev/null +++ b/packages/blocks/src/api/raw-handling/get-raw-transforms.js @@ -0,0 +1,24 @@ +/** + * External dependencies + */ +import { filter } from 'lodash'; + +/** + * Internal dependencies + */ +import { getBlockTransforms } from '../factory'; + +export function getRawTransforms() { + return filter( getBlockTransforms( 'from' ), { type: 'raw' } ).map( + ( transform ) => { + return transform.isMatch + ? transform + : { + ...transform, + isMatch: ( node ) => + transform.selector && + node.matches( transform.selector ), + }; + } + ); +} diff --git a/packages/blocks/src/api/raw-handling/html-to-blocks.js b/packages/blocks/src/api/raw-handling/html-to-blocks.js new file mode 100644 index 00000000000000..a3cdfae5168f52 --- /dev/null +++ b/packages/blocks/src/api/raw-handling/html-to-blocks.js @@ -0,0 +1,47 @@ +/** + * Internal dependencies + */ +import { createBlock, findTransform } from '../factory'; +import { getBlockAttributes } from '../parser'; +import { getRawTransforms } from './get-raw-transforms'; + +/** + * Converts HTML directly to blocks. Looks for a matching transform for each + * top-level tag. The HTML should be filtered to not have any text between + * top-level tags and formatted in a way that blocks can handle the HTML. + * + * @param {string} html HTML to convert. + * + * @return {Array} An array of blocks. + */ +export function htmlToBlocks( html ) { + const doc = document.implementation.createHTMLDocument( '' ); + + doc.body.innerHTML = html; + + return Array.from( doc.body.children ).map( ( node ) => { + const rawTransform = findTransform( + getRawTransforms(), + ( { isMatch } ) => isMatch( node ) + ); + + if ( ! rawTransform ) { + return createBlock( + // Should not be hardcoded. + 'core/html', + getBlockAttributes( 'core/html', node.outerHTML ) + ); + } + + const { transform, blockName } = rawTransform; + + if ( transform ) { + return transform( node ); + } + + return createBlock( + blockName, + getBlockAttributes( blockName, node.outerHTML ) + ); + } ); +} diff --git a/packages/blocks/src/api/raw-handling/index.js b/packages/blocks/src/api/raw-handling/index.js index f0e51d23dbb80c..3d062892e7fa7f 100644 --- a/packages/blocks/src/api/raw-handling/index.js +++ b/packages/blocks/src/api/raw-handling/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { flatMap, filter, compact } from 'lodash'; +import { flatMap, compact } from 'lodash'; /** * WordPress dependencies @@ -12,8 +12,8 @@ import { getPhrasingContentSchema } from '@wordpress/dom'; /** * Internal dependencies */ -import { createBlock, getBlockTransforms, findTransform } from '../factory'; -import { getBlockAttributes, parseWithGrammar } from '../parser'; +import { htmlToBlocks } from './html-to-blocks'; +import { parseWithGrammar } from '../parser'; import normaliseBlocks from './normalise-blocks'; import specialCommentConverter from './special-comment-converter'; import listReducer from './list-reducer'; @@ -31,63 +31,6 @@ export function deprecatedGetPhrasingContentSchema( context ) { return getPhrasingContentSchema( context ); } -function getRawTransformations() { - return filter( getBlockTransforms( 'from' ), { type: 'raw' } ).map( - ( transform ) => { - return transform.isMatch - ? transform - : { - ...transform, - isMatch: ( node ) => - transform.selector && - node.matches( transform.selector ), - }; - } - ); -} - -/** - * Converts HTML directly to blocks. Looks for a matching transform for each - * top-level tag. The HTML should be filtered to not have any text between - * top-level tags and formatted in a way that blocks can handle the HTML. - * - * @param {Object} $1 Named parameters. - * @param {string} $1.html HTML to convert. - * @param {Array} $1.rawTransforms Transforms that can be used. - * - * @return {Array} An array of blocks. - */ -function htmlToBlocks( { html, rawTransforms } ) { - const doc = document.implementation.createHTMLDocument( '' ); - - doc.body.innerHTML = html; - - return Array.from( doc.body.children ).map( ( node ) => { - const rawTransform = findTransform( rawTransforms, ( { isMatch } ) => - isMatch( node ) - ); - - if ( ! rawTransform ) { - return createBlock( - // Should not be hardcoded. - 'core/html', - getBlockAttributes( 'core/html', node.outerHTML ) - ); - } - - const { transform, blockName } = rawTransform; - - if ( transform ) { - return transform( node ); - } - - return createBlock( - blockName, - getBlockAttributes( blockName, node.outerHTML ) - ); - } ); -} - /** * Converts an HTML string to known blocks. * @@ -105,12 +48,7 @@ export function rawHandler( { HTML = '' } ) { // An array of HTML strings and block objects. The blocks replace matched // shortcodes. const pieces = shortcodeConverter( HTML ); - const rawTransforms = getRawTransformations(); - const phrasingContentSchema = getPhrasingContentSchema(); - const blockContentSchema = getBlockContentSchema( - rawTransforms, - phrasingContentSchema - ); + const blockContentSchema = getBlockContentSchema(); return compact( flatMap( pieces, ( piece ) => { @@ -137,7 +75,7 @@ export function rawHandler( { HTML = '' } ) { piece = deepFilterHTML( piece, filters, blockContentSchema ); piece = normaliseBlocks( piece ); - return htmlToBlocks( { html: piece, rawTransforms } ); + return htmlToBlocks( piece ); } ) ); } diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js index 41129299cc354f..665cdc46136e9b 100644 --- a/packages/blocks/src/api/raw-handling/paste-handler.js +++ b/packages/blocks/src/api/raw-handling/paste-handler.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { flatMap, filter, compact } from 'lodash'; +import { flatMap, compact } from 'lodash'; /** * WordPress dependencies @@ -11,10 +11,10 @@ import { getPhrasingContentSchema, removeInvalidHTML } from '@wordpress/dom'; /** * Internal dependencies */ -import { createBlock, getBlockTransforms, findTransform } from '../factory'; +import { htmlToBlocks } from './html-to-blocks'; import { hasBlockSupport } from '../registration'; import { getBlockContent } from '../serializer'; -import { getBlockAttributes, parseWithGrammar } from '../parser'; +import { parseWithGrammar } from '../parser'; import normaliseBlocks from './normalise-blocks'; import specialCommentConverter from './special-comment-converter'; import commentRemover from './comment-remover'; @@ -68,63 +68,6 @@ function filterInlineHTML( HTML, preserveWhiteSpace ) { return HTML; } -function getRawTransformations() { - return filter( getBlockTransforms( 'from' ), { type: 'raw' } ).map( - ( transform ) => { - return transform.isMatch - ? transform - : { - ...transform, - isMatch: ( node ) => - transform.selector && - node.matches( transform.selector ), - }; - } - ); -} - -/** - * Converts HTML directly to blocks. Looks for a matching transform for each - * top-level tag. The HTML should be filtered to not have any text between - * top-level tags and formatted in a way that blocks can handle the HTML. - * - * @param {Object} $1 Named parameters. - * @param {string} $1.html HTML to convert. - * @param {Array} $1.rawTransforms Transforms that can be used. - * - * @return {Array} An array of blocks. - */ -function htmlToBlocks( { html, rawTransforms } ) { - const doc = document.implementation.createHTMLDocument( '' ); - - doc.body.innerHTML = html; - - return Array.from( doc.body.children ).map( ( node ) => { - const rawTransform = findTransform( rawTransforms, ( { isMatch } ) => - isMatch( node ) - ); - - if ( ! rawTransform ) { - return createBlock( - // Should not be hardcoded. - 'core/html', - getBlockAttributes( 'core/html', node.outerHTML ) - ); - } - - const { transform, blockName } = rawTransform; - - if ( transform ) { - return transform( node ); - } - - return createBlock( - blockName, - getBlockAttributes( blockName, node.outerHTML ) - ); - } ); -} - /** * Converts an HTML string to known blocks. Strips everything else. * @@ -222,13 +165,8 @@ export function pasteHandler( { return filterInlineHTML( HTML, preserveWhiteSpace ); } - const rawTransforms = getRawTransformations(); const phrasingContentSchema = getPhrasingContentSchema( 'paste' ); - const blockContentSchema = getBlockContentSchema( - rawTransforms, - phrasingContentSchema, - true - ); + const blockContentSchema = getBlockContentSchema( 'paste' ); const blocks = compact( flatMap( pieces, ( piece ) => { @@ -269,7 +207,7 @@ export function pasteHandler( { // Allows us to ask for this information when we get a report. console.log( 'Processed HTML piece:\n\n', piece ); - return htmlToBlocks( { html: piece, rawTransforms } ); + return htmlToBlocks( piece ); } ) ); diff --git a/packages/blocks/src/api/raw-handling/test/utils.js b/packages/blocks/src/api/raw-handling/test/utils.js index 45e4d84c0c3352..c414f3ae24eeeb 100644 --- a/packages/blocks/src/api/raw-handling/test/utils.js +++ b/packages/blocks/src/api/raw-handling/test/utils.js @@ -6,13 +6,16 @@ import deepFreeze from 'deep-freeze'; /** * Internal dependencies */ -import { getBlockContentSchema, isPlain } from '../utils'; +import { getBlockContentSchemaFromTransforms, isPlain } from '../utils'; +import { store as mockStore } from '../../../store'; +import { STORE_NAME as mockStoreName } from '../../../store/constants'; jest.mock( '@wordpress/data', () => { return { select: jest.fn( ( store ) => { switch ( store ) { - case 'core/blocks': { + case [ mockStoreName ]: + case mockStore: { return { hasBlockSupport: ( blockName, supports ) => { return ( @@ -24,6 +27,18 @@ jest.mock( '@wordpress/data', () => { } } } ), + combineReducers: () => { + const mock = jest.fn(); + return mock; + }, + createReduxStore: () => { + const mock = jest.fn(); + return mock; + }, + register: () => { + const mock = jest.fn(); + return mock; + }, }; } ); @@ -72,7 +87,9 @@ describe( 'getBlockContentSchema', () => { isMatch: undefined, }, }; - expect( getBlockContentSchema( transforms ) ).toEqual( output ); + expect( getBlockContentSchemaFromTransforms( transforms ) ).toEqual( + output + ); } ); it( 'should handle multiple raw transforms', () => { @@ -112,7 +129,9 @@ describe( 'getBlockContentSchema', () => { isMatch: preformattedIsMatch, }, }; - expect( getBlockContentSchema( transforms ) ).toEqual( output ); + expect( getBlockContentSchemaFromTransforms( transforms ) ).toEqual( + output + ); } ); it( 'should correctly merge the children', () => { @@ -150,7 +169,9 @@ describe( 'getBlockContentSchema', () => { }, }, }; - expect( getBlockContentSchema( transforms ) ).toEqual( output ); + expect( getBlockContentSchemaFromTransforms( transforms ) ).toEqual( + output + ); } ); it( 'should correctly merge the attributes', () => { @@ -182,6 +203,8 @@ describe( 'getBlockContentSchema', () => { attributes: [ 'data-chicken', 'data-ribs' ], }, }; - expect( getBlockContentSchema( transforms ) ).toEqual( output ); + expect( getBlockContentSchemaFromTransforms( transforms ) ).toEqual( + output + ); } ); } ); diff --git a/packages/blocks/src/api/raw-handling/utils.js b/packages/blocks/src/api/raw-handling/utils.js index 706bb566399b0b..7517e5ed1696b3 100644 --- a/packages/blocks/src/api/raw-handling/utils.js +++ b/packages/blocks/src/api/raw-handling/utils.js @@ -6,33 +6,21 @@ import { mapValues, mergeWith, isFunction } from 'lodash'; /** * WordPress dependencies */ -import { isPhrasingContent } from '@wordpress/dom'; +import { isPhrasingContent, getPhrasingContentSchema } from '@wordpress/dom'; /** * Internal dependencies */ import { hasBlockSupport } from '..'; +import { getRawTransforms } from './get-raw-transforms'; -/** - * Given raw transforms from blocks, merges all schemas into one. - * - * @param {Array} transforms Block transforms, of the `raw` type. - * @param {Object} phrasingContentSchema The phrasing content schema. - * @param {Object} isPaste Whether the context is pasting or not. - * - * @return {Object} A complete block content schema. - */ -export function getBlockContentSchema( - transforms, - phrasingContentSchema, - isPaste -) { +export function getBlockContentSchemaFromTransforms( transforms, context ) { + const phrasingContentSchema = getPhrasingContentSchema( context ); + const schemaArgs = { phrasingContentSchema, isPaste: context === 'paste' }; const schemas = transforms.map( ( { isMatch, blockName, schema } ) => { const hasAnchorSupport = hasBlockSupport( blockName, 'anchor' ); - schema = isFunction( schema ) - ? schema( { phrasingContentSchema, isPaste } ) - : schema; + schema = isFunction( schema ) ? schema( schemaArgs ) : schema; // If the block does not has anchor support and the transform does not // provides an isMatch we can return the schema right away. @@ -83,6 +71,19 @@ export function getBlockContentSchema( } ); } +/** + * Gets the block content schema, which is extracted and merged from all + * registered blocks with raw transfroms. + * + * @param {string} context Set to "paste" when in paste context, where the + * schema is more strict. + * + * @return {Object} A complete block content schema. + */ +export function getBlockContentSchema( context ) { + return getBlockContentSchemaFromTransforms( getRawTransforms(), context ); +} + /** * Checks wether HTML can be considered plain text. That is, it does not contain * any elements that are not line breaks. diff --git a/packages/blocks/src/api/registration.js b/packages/blocks/src/api/registration.js index db9f1a3b7fd61e..7c1ce2c1643384 100644 --- a/packages/blocks/src/api/registration.js +++ b/packages/blocks/src/api/registration.js @@ -26,6 +26,7 @@ import { blockDefault } from '@wordpress/icons'; */ import { isValidIcon, normalizeIconObject } from './utils'; import { DEPRECATED_ENTRY_KEYS } from './constants'; +import { store as blocksStore } from '../store'; /** * An icon type definition. One of a Dashicon slug, an element, @@ -96,6 +97,14 @@ import { DEPRECATED_ENTRY_KEYS } from './constants'; * @property {string[]} [keywords] An array of terms (which can be translated) * that help users discover the variation * while searching. + * @property {Function} [isActive] A function that accepts a block's attributes + * and the variation's attributes and determines + * if a variation is active. This function doesn't + * try to find a match dynamically based on all + * block's attributes, as in many cases some + * attributes are irrelevant. An example would + * be for `embed` block where we only care about + * `providerNameSlug` attribute's value. */ /** @@ -191,7 +200,7 @@ export function registerBlockType( name, settings ) { ); return; } - if ( select( 'core/blocks' ).getBlockType( name ) ) { + if ( select( blocksStore ).getBlockType( name ) ) { console.error( 'Block "' + name + '" is already registered.' ); return; } @@ -242,7 +251,7 @@ export function registerBlockType( name, settings ) { if ( 'category' in settings && - ! some( select( 'core/blocks' ).getCategories(), { + ! some( select( blocksStore ).getCategories(), { slug: settings.category, } ) ) { @@ -274,7 +283,7 @@ export function registerBlockType( name, settings ) { return; } - dispatch( 'core/blocks' ).addBlockTypes( settings ); + dispatch( blocksStore ).addBlockTypes( settings ); return settings; } @@ -288,7 +297,7 @@ export function registerBlockType( name, settings ) { * @param {Object} [settings.icon] The icon to display in the block inserter. */ export function registerBlockCollection( namespace, { title, icon } ) { - dispatch( 'core/blocks' ).addBlockCollection( namespace, title, icon ); + dispatch( blocksStore ).addBlockCollection( namespace, title, icon ); } /** @@ -298,7 +307,7 @@ export function registerBlockCollection( namespace, { title, icon } ) { * */ export function unregisterBlockCollection( namespace ) { - dispatch( 'core/blocks' ).removeBlockCollection( namespace ); + dispatch( blocksStore ).removeBlockCollection( namespace ); } /** @@ -310,12 +319,12 @@ export function unregisterBlockCollection( namespace ) { * unregistered; otherwise `undefined`. */ export function unregisterBlockType( name ) { - const oldBlock = select( 'core/blocks' ).getBlockType( name ); + const oldBlock = select( blocksStore ).getBlockType( name ); if ( ! oldBlock ) { console.error( 'Block "' + name + '" is not registered.' ); return; } - dispatch( 'core/blocks' ).removeBlockTypes( name ); + dispatch( blocksStore ).removeBlockTypes( name ); return oldBlock; } @@ -325,7 +334,7 @@ export function unregisterBlockType( name ) { * @param {string} blockName Block name. */ export function setFreeformContentHandlerName( blockName ) { - dispatch( 'core/blocks' ).setFreeformFallbackBlockName( blockName ); + dispatch( blocksStore ).setFreeformFallbackBlockName( blockName ); } /** @@ -335,7 +344,7 @@ export function setFreeformContentHandlerName( blockName ) { * @return {?string} Block name. */ export function getFreeformContentHandlerName() { - return select( 'core/blocks' ).getFreeformFallbackBlockName(); + return select( blocksStore ).getFreeformFallbackBlockName(); } /** @@ -344,7 +353,7 @@ export function getFreeformContentHandlerName() { * @return {?string} Block name. */ export function getGroupingBlockName() { - return select( 'core/blocks' ).getGroupingBlockName(); + return select( blocksStore ).getGroupingBlockName(); } /** @@ -353,7 +362,7 @@ export function getGroupingBlockName() { * @param {string} blockName Block name. */ export function setUnregisteredTypeHandlerName( blockName ) { - dispatch( 'core/blocks' ).setUnregisteredFallbackBlockName( blockName ); + dispatch( blocksStore ).setUnregisteredFallbackBlockName( blockName ); } /** @@ -363,7 +372,7 @@ export function setUnregisteredTypeHandlerName( blockName ) { * @return {?string} Block name. */ export function getUnregisteredTypeHandlerName() { - return select( 'core/blocks' ).getUnregisteredFallbackBlockName(); + return select( blocksStore ).getUnregisteredFallbackBlockName(); } /** @@ -372,7 +381,7 @@ export function getUnregisteredTypeHandlerName() { * @param {string} name Block name. */ export function setDefaultBlockName( name ) { - dispatch( 'core/blocks' ).setDefaultBlockName( name ); + dispatch( blocksStore ).setDefaultBlockName( name ); } /** @@ -381,7 +390,7 @@ export function setDefaultBlockName( name ) { * @param {string} name Block name. */ export function setGroupingBlockName( name ) { - dispatch( 'core/blocks' ).setGroupingBlockName( name ); + dispatch( blocksStore ).setGroupingBlockName( name ); } /** @@ -390,7 +399,7 @@ export function setGroupingBlockName( name ) { * @return {?string} Block name. */ export function getDefaultBlockName() { - return select( 'core/blocks' ).getDefaultBlockName(); + return select( blocksStore ).getDefaultBlockName(); } /** @@ -401,7 +410,7 @@ export function getDefaultBlockName() { * @return {?Object} Block type. */ export function getBlockType( name ) { - return select( 'core/blocks' ).getBlockType( name ); + return select( blocksStore ).getBlockType( name ); } /** @@ -410,7 +419,7 @@ export function getBlockType( name ) { * @return {Array} Block settings. */ export function getBlockTypes() { - return select( 'core/blocks' ).getBlockTypes(); + return select( blocksStore ).getBlockTypes(); } /** @@ -424,7 +433,7 @@ export function getBlockTypes() { * @return {?*} Block support value */ export function getBlockSupport( nameOrType, feature, defaultSupports ) { - return select( 'core/blocks' ).getBlockSupport( + return select( blocksStore ).getBlockSupport( nameOrType, feature, defaultSupports @@ -442,7 +451,7 @@ export function getBlockSupport( nameOrType, feature, defaultSupports ) { * @return {boolean} Whether block supports feature. */ export function hasBlockSupport( nameOrType, feature, defaultSupports ) { - return select( 'core/blocks' ).hasBlockSupport( + return select( blocksStore ).hasBlockSupport( nameOrType, feature, defaultSupports @@ -470,7 +479,7 @@ export function isReusableBlock( blockOrType ) { * @return {Array} Array of child block names. */ export const getChildBlockNames = ( blockName ) => { - return select( 'core/blocks' ).getChildBlockNames( blockName ); + return select( blocksStore ).getChildBlockNames( blockName ); }; /** @@ -481,7 +490,7 @@ export const getChildBlockNames = ( blockName ) => { * @return {boolean} True if a block contains child blocks and false otherwise. */ export const hasChildBlocks = ( blockName ) => { - return select( 'core/blocks' ).hasChildBlocks( blockName ); + return select( blocksStore ).hasChildBlocks( blockName ); }; /** @@ -493,9 +502,7 @@ export const hasChildBlocks = ( blockName ) => { * and false otherwise. */ export const hasChildBlocksWithInserterSupport = ( blockName ) => { - return select( 'core/blocks' ).hasChildBlocksWithInserterSupport( - blockName - ); + return select( blocksStore ).hasChildBlocksWithInserterSupport( blockName ); }; /** @@ -505,7 +512,7 @@ export const hasChildBlocksWithInserterSupport = ( blockName ) => { * @param {Object} styleVariation Object containing `name` which is the class name applied to the block and `label` which identifies the variation to the user. */ export const registerBlockStyle = ( blockName, styleVariation ) => { - dispatch( 'core/blocks' ).addBlockStyles( blockName, styleVariation ); + dispatch( blocksStore ).addBlockStyles( blockName, styleVariation ); }; /** @@ -515,10 +522,7 @@ export const registerBlockStyle = ( blockName, styleVariation ) => { * @param {string} styleVariationName Name of class applied to the block. */ export const unregisterBlockStyle = ( blockName, styleVariationName ) => { - dispatch( 'core/blocks' ).removeBlockStyles( - blockName, - styleVariationName - ); + dispatch( blocksStore ).removeBlockStyles( blockName, styleVariationName ); }; /** @@ -530,7 +534,7 @@ export const unregisterBlockStyle = ( blockName, styleVariationName ) => { * @return {(WPBlockVariation[]|void)} Block variations. */ export const getBlockVariations = ( blockName, scope ) => { - return select( 'core/blocks' ).getBlockVariations( blockName, scope ); + return select( blocksStore ).getBlockVariations( blockName, scope ); }; /** @@ -540,7 +544,7 @@ export const getBlockVariations = ( blockName, scope ) => { * @param {WPBlockVariation} variation Object describing a block variation. */ export const registerBlockVariation = ( blockName, variation ) => { - dispatch( 'core/blocks' ).addBlockVariations( blockName, variation ); + dispatch( blocksStore ).addBlockVariations( blockName, variation ); }; /** @@ -550,5 +554,5 @@ export const registerBlockVariation = ( blockName, variation ) => { * @param {string} variationName Name of the variation defined for the block. */ export const unregisterBlockVariation = ( blockName, variationName ) => { - dispatch( 'core/blocks' ).removeBlockVariations( blockName, variationName ); + dispatch( blocksStore ).removeBlockVariations( blockName, variationName ); }; diff --git a/packages/blocks/src/api/serializer.js b/packages/blocks/src/api/serializer.js index 2b081bf471fbb4..86b0147beea13a 100644 --- a/packages/blocks/src/api/serializer.js +++ b/packages/blocks/src/api/serializer.js @@ -17,6 +17,7 @@ import { getBlockType, getFreeformContentHandlerName, getUnregisteredTypeHandlerName, + hasBlockSupport, } from './registration'; import { normalizeBlockType } from './utils'; import BlockContentProvider from '../block-content-provider'; @@ -116,10 +117,14 @@ export function getSaveElement( let element = save( { attributes, innerBlocks } ); + const hasLightBlockWrapper = + blockType.apiVersion > 1 || + hasBlockSupport( blockType, 'lightBlockWrapper', false ); + if ( isObject( element ) && hasFilter( 'blocks.getSaveContent.extraProps' ) && - ! blockType.apiVersion + ! hasLightBlockWrapper ) { /** * Filters the props applied to the block save result element. diff --git a/packages/blocks/src/api/test/registration.js b/packages/blocks/src/api/test/registration.js index 9a7b5f41cad72d..13e43b4a52b10d 100644 --- a/packages/blocks/src/api/test/registration.js +++ b/packages/blocks/src/api/test/registration.js @@ -37,6 +37,7 @@ import { unstable__bootstrapServerSideBlockDefinitions, // eslint-disable-line camelcase } from '../registration'; import { DEPRECATED_ENTRY_KEYS } from '../constants'; +import { store as blocksStore } from '../../store'; describe( 'blocks', () => { const defaultBlockSettings = { @@ -732,7 +733,7 @@ describe( 'blocks', () => { it( 'creates a new block collection', () => { registerBlockCollection( 'core', { title: 'Core' } ); - expect( select( 'core/blocks' ).getCollections() ).toEqual( { + expect( select( blocksStore ).getCollections() ).toEqual( { core: { title: 'Core', icon: undefined }, } ); } ); @@ -744,7 +745,7 @@ describe( 'blocks', () => { registerBlockCollection( 'core2', { title: 'Core2' } ); unregisterBlockCollection( 'core' ); - expect( select( 'core/blocks' ).getCollections() ).toEqual( { + expect( select( blocksStore ).getCollections() ).toEqual( { core2: { title: 'Core2', icon: undefined }, } ); } ); diff --git a/packages/blocks/src/store/constants.js b/packages/blocks/src/store/constants.js new file mode 100644 index 00000000000000..7dda6b11d0254e --- /dev/null +++ b/packages/blocks/src/store/constants.js @@ -0,0 +1 @@ +export const STORE_NAME = 'core/blocks'; diff --git a/packages/blocks/src/store/index.js b/packages/blocks/src/store/index.js index 7672b6e5e7fb85..f88fe4c423fa4a 100644 --- a/packages/blocks/src/store/index.js +++ b/packages/blocks/src/store/index.js @@ -9,8 +9,7 @@ import { createReduxStore, register } from '@wordpress/data'; import reducer from './reducer'; import * as selectors from './selectors'; import * as actions from './actions'; - -const STORE_NAME = 'core/blocks'; +import { STORE_NAME } from './constants'; /** * Store definition for the blocks namespace. diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 67379981172f2c..1748317bc6bbf6 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,12 +2,26 @@ ## Unreleased -- Introduce `Navigation` component as `__experimentalNavigation` for displaying a hierarchy of items. +## 12.0.0 (2020-12-17) + +### Enhancements +- ComboboxControl: Deburr option labels before filter ### Breaking Change - Introduce support for other units and advanced CSS properties on `FontSizePicker`. Provided the value passed to the `FontSizePicker` is a string or one of the size options passed is a string, onChange will start to be called with a string value instead of a number. On WordPress usage, font size options are now automatically converted to strings with the default "px" unit added. +## 10.1.0 (2020-09-03) + +### New Feature + +- Add `ToolbarItem` component. +- Support `label` prop on the `Toolbar` component. + +### Deprecations + +- Deprecate the `Toolbar` component when used without the `label` prop. `ToolbarGroup` should be used instead. + ## 10.0.0 (2020-07-07) ### Breaking Change diff --git a/packages/components/package.json b/packages/components/package.json index 02feffdfebe8b0..fb6c585551576c 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/components", - "version": "11.1.1", + "version": "12.0.0", "description": "UI components for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -26,7 +26,7 @@ "src/**/*.scss" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@emotion/core": "^10.0.22", "@emotion/css": "^10.0.22", "@emotion/native": "^10.0.22", diff --git a/packages/components/src/alignment-matrix-control/index.js b/packages/components/src/alignment-matrix-control/index.js index cce4cd59a096a4..28e41f9c65d4f1 100644 --- a/packages/components/src/alignment-matrix-control/index.js +++ b/packages/components/src/alignment-matrix-control/index.js @@ -8,7 +8,7 @@ import { useCompositeState, Composite, CompositeGroup } from 'reakit'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, isRTL } from '@wordpress/i18n'; import { useInstanceId } from '@wordpress/compose'; import { useState, useEffect } from '@wordpress/element'; @@ -17,7 +17,6 @@ import { useState, useEffect } from '@wordpress/element'; */ import Cell from './cell'; import { Root, Row } from './styles/alignment-matrix-control-styles'; -import { useRTL } from '../utils/rtl'; import AlignmentMatrixControlIcon from './icon'; import { GRID, getItemId } from './utils'; @@ -41,14 +40,13 @@ export default function AlignmentMatrixControl( { ...props } ) { const [ immutableDefaultValue ] = useState( value ?? defaultValue ); - const isRTL = useRTL(); const baseId = useBaseId( id ); const initialCurrentId = getItemId( baseId, immutableDefaultValue ); const composite = useCompositeState( { baseId, currentId: initialCurrentId, - rtl: isRTL, + rtl: isRTL(), } ); const handleOnChange = ( nextValue ) => { diff --git a/packages/components/src/animate/index.js b/packages/components/src/animate/index.js index 2eeed8e7be1306..95fba645fd1782 100644 --- a/packages/components/src/animate/index.js +++ b/packages/components/src/animate/index.js @@ -3,11 +3,37 @@ */ import classnames from 'classnames'; +/** + * @typedef {'top' | 'top left' | 'top right' | 'middle' | 'middle left' | 'middle right' | 'bottom' | 'bottom left' | 'bottom right'} AppearOrigin + * @typedef {'left' | 'right'} SlideInOrigin + * @typedef {{ type: 'appear'; origin?: AppearOrigin }} AppearOptions + * @typedef {{ type: 'slide-in'; origin?: SlideInOrigin }} SlideInOptions + * @typedef {{ type: 'loading'; }} LoadingOptions + * @typedef {AppearOptions | SlideInOptions | LoadingOptions} GetAnimateOptions + */ + +/* eslint-disable jsdoc/valid-types */ +/** + * @param {GetAnimateOptions['type']} type The animation type + * @return {'top' | 'left'} Default origin + */ function getDefaultOrigin( type ) { return type === 'appear' ? 'top' : 'left'; } +/* eslint-enable jsdoc/valid-types */ + +/** + * @param {GetAnimateOptions} options + * + * @return {string | void} ClassName that applies the animations + */ +export function getAnimateClassName( options ) { + if ( options.type === 'loading' ) { + return classnames( 'components-animate__loading' ); + } + + const { type, origin = getDefaultOrigin( type ) } = options; -export function useAnimate( { type, origin = getDefaultOrigin( type ) } ) { if ( type === 'appear' ) { const [ yAxis, xAxis = 'center' ] = origin.split( ' ' ); return classnames( 'components-animate__appear', { @@ -22,14 +48,11 @@ export function useAnimate( { type, origin = getDefaultOrigin( type ) } ) { 'is-from-' + origin ); } - - if ( type === 'loading' ) { - return classnames( 'components-animate__loading' ); - } } +// @ts-ignore Reason: Planned for deprecation export default function Animate( { type, options = {}, children } ) { return children( { - className: useAnimate( { type, ...options } ), + className: getAnimateClassName( { type, ...options } ), } ); } diff --git a/packages/components/src/base-control/README.md b/packages/components/src/base-control/README.md index 7b20ced34734d9..854a2420c9497d 100644 --- a/packages/components/src/base-control/README.md +++ b/packages/components/src/base-control/README.md @@ -82,7 +82,7 @@ import { BaseControl } from '@wordpress/components'; const MyBaseControl = () => ( <BaseControl - help="Pressing the Select an author buttom will open a modal that allows an advanced mechanism for author selection" + help="Pressing the Select an author button will open a modal that allows an advanced mechanism for author selection" > <BaseControl.VisualLabel> Author diff --git a/packages/components/src/button/style.scss b/packages/components/src/button/style.scss index 208c34b8f795d6..f7cc62fc5efc1a 100644 --- a/packages/components/src/button/style.scss +++ b/packages/components/src/button/style.scss @@ -91,10 +91,10 @@ /* stylelint-disable */ background-image: linear-gradient( -45deg, - var(--wp-admin-theme-color) 28%, - var(--wp-admin-theme-color-darker-20) 28%, - var(--wp-admin-theme-color-darker-20) 72%, - var(--wp-admin-theme-color) 72% + var(--wp-admin-theme-color) 33%, + var(--wp-admin-theme-color-darker-20) 33%, + var(--wp-admin-theme-color-darker-20) 70%, + var(--wp-admin-theme-color) 70% ); /* stylelint-enable */ border-color: var(--wp-admin-theme-color); @@ -255,10 +255,10 @@ /* stylelint-disable */ background-image: linear-gradient( -45deg, - darken($white, 2%) 28%, - darken($white, 12%) 28%, - darken($white, 12%) 72%, - darken($white, 2%) 72% + darken($white, 2%) 33%, + darken($white, 12%) 33%, + darken($white, 12%) 70%, + darken($white, 2%) 70% ); /* stylelint-enable */ } diff --git a/packages/components/src/circular-option-picker/style.scss b/packages/components/src/circular-option-picker/style.scss index 0dc42542dc80ba..353a432dda6639 100644 --- a/packages/components/src/circular-option-picker/style.scss +++ b/packages/components/src/circular-option-picker/style.scss @@ -4,6 +4,7 @@ $color-palette-circle-spacing: 12px; .components-circular-option-picker { display: inline-block; width: 100%; + min-width: 188px; .components-circular-option-picker__custom-clear-wrapper { display: flex; diff --git a/packages/components/src/color-edit/index.js b/packages/components/src/color-edit/index.js index a22167efc94858..31b49ad1454b33 100644 --- a/packages/components/src/color-edit/index.js +++ b/packages/components/src/color-edit/index.js @@ -53,7 +53,8 @@ function ColorOption( { ); const isShowingControls = - isHover || isFocused || isEditingName || isShowingAdvancedPanel; + ( isHover || isFocused || isEditingName || isShowingAdvancedPanel ) && + ! immutableColorSlugs.includes( slug ); return ( <div @@ -119,9 +120,7 @@ function ColorOption( { onChange={ ( newColorName ) => onChange( { color, - slug: immutableColorSlugs.includes( slug ) - ? slug - : kebabCase( newColorName ), + slug: kebabCase( newColorName ), name: newColorName, } ) } diff --git a/packages/components/src/color-palette/index.native.js b/packages/components/src/color-palette/index.native.js index cc75a22c67d3c8..8df4889acd83b0 100644 --- a/packages/components/src/color-palette/index.native.js +++ b/packages/components/src/color-palette/index.native.js @@ -46,9 +46,9 @@ function ColorPalette( { customIndicatorWrapperStyles, } ) { const customSwatchGradients = [ - 'linear-gradient(120deg, rgba(255,0,0,.8), 0%, rgba(255,255,255,1) 70.71%)', - 'linear-gradient(240deg, rgba(0,255,0,.8), 0%, rgba(0,255,0,0) 70.71%)', - 'linear-gradient(360deg, rgba(0,0,255,.8), 0%, rgba(0,0,255,0) 70.71%)', + 'linear-gradient(120deg, rgba(255,0,0,.8) 0%, rgba(255,255,255,1) 70.71%)', + 'linear-gradient(240deg, rgba(0,255,0,.8) 0%, rgba(0,255,0,0) 70.71%)', + 'linear-gradient(360deg, rgba(0,0,255,.8) 0%, rgba(0,0,255,0) 70.71%)', ]; const scrollViewRef = useRef(); diff --git a/packages/components/src/combobox-control/index.js b/packages/components/src/combobox-control/index.js index 9cf667f6a09387..fdd406530ee297 100644 --- a/packages/components/src/combobox-control/index.js +++ b/packages/components/src/combobox-control/index.js @@ -2,12 +2,18 @@ * External dependencies */ import classnames from 'classnames'; - +import { deburr } from 'lodash'; /** * WordPress dependencies */ import { __, _n, sprintf } from '@wordpress/i18n'; -import { useState, useMemo, useRef, useEffect } from '@wordpress/element'; +import { + Component, + useState, + useMemo, + useRef, + useEffect, +} from '@wordpress/element'; import { useInstanceId } from '@wordpress/compose'; import { ENTER, UP, DOWN, ESCAPE } from '@wordpress/keycodes'; import { speak } from '@wordpress/a11y'; @@ -21,6 +27,19 @@ import SuggestionsList from '../form-token-field/suggestions-list'; import BaseControl from '../base-control'; import Button from '../button'; import { Flex, FlexBlock, FlexItem } from '../flex'; +import withFocusOutside from '../higher-order/with-focus-outside'; + +const DetectOutside = withFocusOutside( + class extends Component { + handleFocusOutside( event ) { + this.props.onFocusOutside( event ); + } + + render() { + return this.props.children; + } + } +); function ComboboxControl( { value, @@ -47,9 +66,11 @@ function ComboboxControl( { const matchingSuggestions = useMemo( () => { const startsWithMatch = []; const containsMatch = []; - const match = inputValue.toLocaleLowerCase(); + const match = deburr( inputValue.toLocaleLowerCase() ); options.forEach( ( option ) => { - const index = option.label.toLocaleLowerCase().indexOf( match ); + const index = deburr( option.label ) + .toLocaleLowerCase() + .indexOf( match ); if ( index === 0 ) { startsWithMatch.push( option ); } else if ( index > 0 ) { @@ -119,7 +140,7 @@ function ComboboxControl( { setInputValue( '' ); }; - const onBlur = () => { + const onFocusOutside = () => { setIsExpanded( false ); }; @@ -151,7 +172,7 @@ function ComboboxControl( { ) : __( 'No results.' ); - speak( message, 'assertive' ); + speak( message, 'polite' ); } }, [ matchingSuggestions, isExpanded ] ); @@ -160,63 +181,74 @@ function ComboboxControl( { // TODO: Refactor click detection to use blur to stop propagation. /* eslint-disable jsx-a11y/no-static-element-interactions */ return ( - <BaseControl - className={ classnames( className, 'components-combobox-control' ) } - tabIndex="-1" - label={ label } - id={ `components-form-token-input-${ instanceId }` } - hideLabelFromVision={ hideLabelFromVision } - help={ help } - > - <div - className="components-combobox-control__suggestions-container" + <DetectOutside onFocusOutside={ onFocusOutside }> + <BaseControl + className={ classnames( + className, + 'components-combobox-control' + ) } tabIndex="-1" - onKeyDown={ onKeyDown } + label={ label } + id={ `components-form-token-input-${ instanceId }` } + hideLabelFromVision={ hideLabelFromVision } + help={ help } > - <Flex> - <FlexBlock> - <TokenInput - className="components-combobox-control__input" + <div + className="components-combobox-control__suggestions-container" + tabIndex="-1" + onKeyDown={ onKeyDown } + > + <Flex> + <FlexBlock> + <TokenInput + className="components-combobox-control__input" + instanceId={ instanceId } + ref={ inputContainer } + value={ isExpanded ? inputValue : currentLabel } + aria-label={ + currentLabel + ? `${ currentLabel }, ${ label }` + : null + } + onFocus={ onFocus } + isExpanded={ isExpanded } + selectedSuggestionIndex={ matchingSuggestions.indexOf( + selectedSuggestion + ) } + onChange={ onInputChange } + /> + </FlexBlock> + { allowReset && ( + <FlexItem> + <Button + className="components-combobox-control__reset" + icon={ closeSmall } + disabled={ ! value } + onClick={ handleOnReset } + label={ __( 'Reset' ) } + /> + </FlexItem> + ) } + </Flex> + { isExpanded && ( + <SuggestionsList instanceId={ instanceId } - ref={ inputContainer } - value={ isExpanded ? inputValue : currentLabel } - onBlur={ onBlur } - onFocus={ onFocus } - isExpanded={ isExpanded } - selectedSuggestionIndex={ matchingSuggestions.indexOf( + match={ { label: inputValue } } + displayTransform={ ( suggestion ) => + suggestion.label + } + suggestions={ matchingSuggestions } + selectedIndex={ matchingSuggestions.indexOf( selectedSuggestion ) } - onChange={ onInputChange } + onHover={ setSelectedSuggestion } + onSelect={ onSuggestionSelected } + scrollIntoView /> - </FlexBlock> - { allowReset && ( - <FlexItem> - <Button - className="components-combobox-control__reset" - icon={ closeSmall } - disabled={ ! value } - onClick={ handleOnReset } - label={ __( 'Reset' ) } - /> - </FlexItem> ) } - </Flex> - { isExpanded && ( - <SuggestionsList - instanceId={ instanceId } - match={ { label: inputValue } } - displayTransform={ ( suggestion ) => suggestion.label } - suggestions={ matchingSuggestions } - selectedIndex={ matchingSuggestions.indexOf( - selectedSuggestion - ) } - onHover={ setSelectedSuggestion } - onSelect={ onSuggestionSelected } - scrollIntoView - /> - ) } - </div> - </BaseControl> + </div> + </BaseControl> + </DetectOutside> ); /* eslint-enable jsx-a11y/no-static-element-interactions */ } diff --git a/packages/components/src/combobox-control/stories/index.js b/packages/components/src/combobox-control/stories/index.js index a7f250e38573e1..8bca43c948fd34 100644 --- a/packages/components/src/combobox-control/stories/index.js +++ b/packages/components/src/combobox-control/stories/index.js @@ -276,17 +276,8 @@ function ComboboxControlWithState() { onChange={ setValue } label="Select a country" options={ filteredOptions } - onFilterValueChange={ ( filter ) => - setFilteredOptions( - countries - .filter( ( country ) => - country.name - .toLowerCase() - .startsWith( filter.toLowerCase() ) - ) - .slice( 0, 20 ) - .map( mapCountryOption ) - ) + onFilterValueChange={ () => + setFilteredOptions( countries.map( mapCountryOption ) ) } /> <p>Value: { value }</p> diff --git a/packages/components/src/combobox-control/style.scss b/packages/components/src/combobox-control/style.scss index 1b5faf68b91cef..3cb0a8272e65c8 100644 --- a/packages/components/src/combobox-control/style.scss +++ b/packages/components/src/combobox-control/style.scss @@ -6,11 +6,18 @@ input.components-combobox-control__input[type="text"] { width: 100%; border: none; box-shadow: none; + font-size: 16px; padding: 2px; margin: 0; line-height: inherit; min-height: auto; + // Resolves Zooming on iOS devices + // https://github.com/WordPress/gutenberg/issues/27405 + @include break-small() { + font-size: 13px; + } + &:focus { outline: none; box-shadow: none; diff --git a/packages/components/src/custom-gradient-picker/serializer.js b/packages/components/src/custom-gradient-picker/serializer.js index 90166311ce0b1d..c270ac50544cd7 100644 --- a/packages/components/src/custom-gradient-picker/serializer.js +++ b/packages/components/src/custom-gradient-picker/serializer.js @@ -13,7 +13,11 @@ export function serializeGradientColor( { type, value } ) { return `${ type }(${ value.join( ',' ) })`; } -export function serializeGradientPosition( { type, value } ) { +export function serializeGradientPosition( position ) { + if ( ! position ) { + return ''; + } + const { value, type } = position; return `${ value }${ type }`; } diff --git a/packages/components/src/custom-gradient-picker/utils.js b/packages/components/src/custom-gradient-picker/utils.js index 24fd56cec3bad2..a4212d2c3f1da4 100644 --- a/packages/components/src/custom-gradient-picker/utils.js +++ b/packages/components/src/custom-gradient-picker/utils.js @@ -256,6 +256,21 @@ const DIRECTIONAL_ORIENTATION_ANGLE_MAP = { 'left top': 315, }; +function hasUnsupportedLength( item ) { + return item.length === undefined || item.length.type !== '%'; +} + +function assignColorStopLengths( gradientAST ) { + const { colorStops } = gradientAST; + const step = 100 / ( colorStops.length - 1 ); + colorStops.forEach( ( stop, index ) => { + stop.length = { + value: step * index, + type: '%', + }; + } ); +} + export function getGradientParsed( value ) { let hasGradient = !! value; // gradientAST will contain the gradient AST as parsed by gradient-parser npm module. @@ -280,6 +295,12 @@ export function getGradientParsed( value ) { gradientAST.orientation.value ].toString(); } + + if ( gradientAST.colorStops.some( hasUnsupportedLength ) ) { + assignColorStopLengths( gradientAST ); + gradientValue = serializeGradient( gradientAST ); + } + return { hasGradient, gradientAST, diff --git a/packages/components/src/date-time/date.js b/packages/components/src/date-time/date.js index 0b1ba7c19aa5fc..6461cc00c9b902 100644 --- a/packages/components/src/date-time/date.js +++ b/packages/components/src/date-time/date.js @@ -10,12 +10,12 @@ import DayPickerSingleDateController from 'react-dates/lib/components/DayPickerS * WordPress dependencies */ import { Component, createRef } from '@wordpress/element'; +import { isRTL } from '@wordpress/i18n'; /** * Module Constants */ const TIMEZONELESS_FORMAT = 'YYYY-MM-DDTHH:mm:ss'; -const isRTL = () => document.documentElement.dir === 'rtl'; class DatePicker extends Component { constructor() { diff --git a/packages/components/src/date-time/stories/index.js b/packages/components/src/date-time/stories/index.js index 1fa91a6470859c..e929e9ab999555 100644 --- a/packages/components/src/date-time/stories/index.js +++ b/packages/components/src/date-time/stories/index.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { boolean } from '@storybook/addon-knobs'; +import { boolean, button } from '@storybook/addon-knobs'; /** * WordPress dependencies @@ -11,7 +11,7 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import { DateTimePicker } from '../'; +import DateTimePicker from '../'; export default { title: 'Components/DateTimePicker', @@ -34,3 +34,36 @@ export const _default = () => { const is12Hour = boolean( 'Is 12 hour (shows AM/PM)', false ); return <DateTimePickerWithState is12Hour={ is12Hour } />; }; + +// Date utils, for demo purposes. +const DAY_IN_MS = 24 * 60 * 60 * 1000; +const aFewDaysAfter = ( date ) => { + // eslint-disable-next-line no-restricted-syntax + return new Date( date.getTime() + ( 1 + Math.random() * 5 ) * DAY_IN_MS ); +}; + +const now = new Date(); + +export const WithDaysHighlighted = () => { + const [ date, setDate ] = useState( now ); + + const [ highlights, setHighlights ] = useState( [ + { date: aFewDaysAfter( now ) }, + ] ); + + button( 'Add random highlight', () => { + const lastHighlight = highlights[ highlights.length - 1 ]; + setHighlights( [ + ...highlights, + { date: aFewDaysAfter( lastHighlight.date ) }, + ] ); + } ); + + return ( + <DateTimePicker + currentDate={ date } + onChange={ setDate } + events={ highlights } + /> + ); +}; diff --git a/packages/components/src/date-time/stories/with-days-highlighted.js b/packages/components/src/date-time/stories/with-days-highlighted.js deleted file mode 100644 index 81838bea518381..00000000000000 --- a/packages/components/src/date-time/stories/with-days-highlighted.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * External dependencies - */ -import { button } from '@storybook/addon-knobs'; - -/** - * WordPress dependencies - */ -import { useState } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { DateTimePicker } from '..'; - -export default { - title: 'Components/DateTimePicker', - component: DateTimePicker, -}; - -// Date utils, for demo purposes. -const DAY_IN_MS = 24 * 60 * 60 * 1000; -const aFewDaysAfter = ( date ) => { - // eslint-disable-next-line no-restricted-syntax - return new Date( date.getTime() + ( 1 + Math.random() * 5 ) * DAY_IN_MS ); -}; - -const now = new Date(); - -export const WithDaysHighlighted = () => { - const [ date, setDate ] = useState( now ); - - const [ highlights, setHighlights ] = useState( [ - { date: aFewDaysAfter( now ) }, - ] ); - - button( 'Add random highlight', () => { - const lastHighlight = highlights[ highlights.length - 1 ]; - setHighlights( [ - ...highlights, - { date: aFewDaysAfter( lastHighlight.date ) }, - ] ); - } ); - - return ( - <DateTimePicker - currentDate={ date } - onChange={ setDate } - events={ highlights } - /> - ); -}; diff --git a/packages/components/src/date-time/test/time.js b/packages/components/src/date-time/test/time.js index 32a47ca9ed2dc6..7124e80e6c1b1a 100644 --- a/packages/components/src/date-time/test/time.js +++ b/packages/components/src/date-time/test/time.js @@ -271,4 +271,28 @@ describe( 'TimePicker', () => { expect( monthInputIndex > dayInputIndex ).toBe( true ); } ); + + it( 'Should set a time when passed a null currentTime', () => { + const onChangeSpy = jest.fn(); + + render( + <TimePicker + currentTime={ null } + onChange={ onChangeSpy } + is12Hour + /> + ); + + const monthInput = screen.getByLabelText( 'Month' ).value; + const dayInput = screen.getByLabelText( 'Day' ).value; + const yearInput = screen.getByLabelText( 'Year' ).value; + const hoursInput = screen.getByLabelText( 'Hours' ).value; + const minutesInput = screen.getByLabelText( 'Minutes' ).value; + + expect( Number.isNaN( parseInt( monthInput, 10 ) ) ).toBe( false ); + expect( Number.isNaN( parseInt( dayInput, 10 ) ) ).toBe( false ); + expect( Number.isNaN( parseInt( yearInput, 10 ) ) ).toBe( false ); + expect( Number.isNaN( parseInt( hoursInput, 10 ) ) ).toBe( false ); + expect( Number.isNaN( parseInt( minutesInput, 10 ) ) ).toBe( false ); + } ); } ); diff --git a/packages/components/src/date-time/time.js b/packages/components/src/date-time/time.js index 5df865e95e27d4..de10573f992524 100644 --- a/packages/components/src/date-time/time.js +++ b/packages/components/src/date-time/time.js @@ -88,7 +88,9 @@ export function TimePicker( { is12Hour, currentTime, onChange } ) { // Reset the state when currentTime changed. useEffect( () => { - setDate( moment( currentTime ).startOf( 'minutes' ) ); + setDate( + currentTime ? moment( currentTime ).startOf( 'minutes' ) : moment() + ); }, [ currentTime ] ); const { day, month, year, minutes, hours, am } = useMemo( diff --git a/packages/components/src/disabled/README.md b/packages/components/src/disabled/README.md index 3b823b120be1a1..cb871d5d837cf6 100644 --- a/packages/components/src/disabled/README.md +++ b/packages/components/src/disabled/README.md @@ -49,3 +49,15 @@ function CustomButton() { ); } ``` + +### Props + +The component accepts the following props: + +#### isDisabled + +Whether to disable all the descendant fields. Defaults to `true`. + +- Type: `Boolean` +- Required: No +- Default: `true` diff --git a/packages/components/src/disabled/index.js b/packages/components/src/disabled/index.js index 22aaa7b7664e63..2e7cab577a2bd1 100644 --- a/packages/components/src/disabled/index.js +++ b/packages/components/src/disabled/index.js @@ -41,7 +41,7 @@ const DISABLED_ELIGIBLE_NODE_NAMES = [ 'TEXTAREA', ]; -function Disabled( { className, children, ...props } ) { +function Disabled( { className, children, isDisabled = true, ...props } ) { const node = useRef(); const disable = () => { @@ -75,6 +75,10 @@ function Disabled( { className, children, ...props } ) { ); useLayoutEffect( () => { + if ( ! isDisabled ) { + return; + } + disable(); const observer = new window.MutationObserver( debouncedDisable ); @@ -90,6 +94,10 @@ function Disabled( { className, children, ...props } ) { }; }, [] ); + if ( ! isDisabled ) { + return <Provider value={ false }>{ children }</Provider>; + } + return ( <Provider value={ true }> <StyledWrapper diff --git a/packages/components/src/disabled/stories/index.js b/packages/components/src/disabled/stories/index.js index 527c76899c6527..fb994153c1037c 100644 --- a/packages/components/src/disabled/stories/index.js +++ b/packages/components/src/disabled/stories/index.js @@ -1,7 +1,13 @@ +/** + * WordPress dependencies + */ +import { useState } from '@wordpress/element'; + /** * Internal dependencies */ import Disabled from '../'; +import Button from '../../button/'; import SelectControl from '../../select-control/'; import TextControl from '../../text-control/'; import TextareaControl from '../../textarea-control/'; @@ -11,21 +17,45 @@ export default { component: Disabled, }; +const Form = () => ( + <div> + <TextControl label="Text Control" /> + <TextareaControl label="TextArea Control" /> + <SelectControl + label="Select Control" + onChange={ () => {} } + options={ [ + { value: null, label: 'Select an option', disabled: true }, + { value: 'a', label: 'Option A' }, + { value: 'b', label: 'Option B' }, + { value: 'c', label: 'Option C' }, + ] } + /> + </div> +); + export const _default = () => { return ( <Disabled> - <TextControl label="Text Control" /> - <TextareaControl label="TextArea Control" /> - <SelectControl - label="Select Control" - onChange={ () => {} } - options={ [ - { value: null, label: 'Select an option', disabled: true }, - { value: 'a', label: 'Option A' }, - { value: 'b', label: 'Option B' }, - { value: 'c', label: 'Option C' }, - ] } - /> + <Form /> </Disabled> ); }; + +export const DisabledWithProp = () => { + const [ isDisabled, setState ] = useState( true ); + const toggleDisabled = () => { + setState( () => ! isDisabled ); + }; + + return ( + <div> + <Disabled isDisabled={ isDisabled }> + <Form /> + </Disabled> + <Button isPrimary onClick={ toggleDisabled }> + Set isDisabled to { isDisabled ? 'false' : 'true' } + </Button> + </div> + ); +}; diff --git a/packages/components/src/disabled/test/index.js b/packages/components/src/disabled/test/index.js index bef7a9e9e1e6cd..57697d51ba13fa 100644 --- a/packages/components/src/disabled/test/index.js +++ b/packages/components/src/disabled/test/index.js @@ -68,9 +68,9 @@ describe( 'Disabled', () => { // this is needed because TestUtils does not accept a stateless component. class DisabledComponent extends Component { render() { - const { children } = this.props; + const { children, isDisabled } = this.props; - return <Disabled>{ children }</Disabled>; + return <Disabled isDisabled={ isDisabled }>{ children }</Disabled>; } } @@ -133,6 +133,51 @@ describe( 'Disabled', () => { expect( div.hasAttribute( 'tabindex' ) ).toBe( true ); } ); + it( 'will disable or enable descendant fields based on the isDisabled prop value', () => { + class MaybeDisable extends Component { + constructor() { + super( ...arguments ); + this.state = { isDisabled: true }; + } + + render() { + return ( + <DisabledComponent isDisabled={ this.state.isDisabled }> + <Form /> + </DisabledComponent> + ); + } + } + + const wrapper = TestUtils.renderIntoDocument( <MaybeDisable /> ); + + const input = TestUtils.findRenderedDOMComponentWithTag( + wrapper, + 'input' + ); + const div = TestUtils.scryRenderedDOMComponentsWithTag( + wrapper, + 'div' + )[ 1 ]; + + expect( input.hasAttribute( 'disabled' ) ).toBe( true ); + expect( div.getAttribute( 'contenteditable' ) ).toBe( 'false' ); + + wrapper.setState( { isDisabled: false } ); + + const input2 = TestUtils.findRenderedDOMComponentWithTag( + wrapper, + 'input' + ); + const div2 = TestUtils.scryRenderedDOMComponentsWithTag( + wrapper, + 'div' + )[ 0 ]; + + expect( input2.hasAttribute( 'disabled' ) ).toBe( false ); + expect( div2.getAttribute( 'contenteditable' ) ).not.toBe( 'false' ); + } ); + // Ideally, we'd have two more test cases here: // // - it( 'will disable all fields on component render change' ) @@ -170,6 +215,19 @@ describe( 'Disabled', () => { expect( wrapperElement.textContent ).toBe( 'Disabled' ); } ); + test( "lets components know that they're not disabled via context when isDisabled is false", () => { + const wrapper = TestUtils.renderIntoDocument( + <DisabledComponent isDisabled={ false }> + <DisabledStatus /> + </DisabledComponent> + ); + const wrapperElement = TestUtils.findRenderedDOMComponentWithTag( + wrapper, + 'p' + ); + expect( wrapperElement.textContent ).toBe( 'Not disabled' ); + } ); + test( "lets components know that they're not disabled via context", () => { const wrapper = TestUtils.renderIntoDocument( <DisabledStatus /> ); const wrapperElement = TestUtils.findRenderedDOMComponentWithTag( diff --git a/packages/components/src/drop-zone/provider.js b/packages/components/src/drop-zone/provider.js index 45c6954a6a9be5..38cab06bd92d4c 100644 --- a/packages/components/src/drop-zone/provider.js +++ b/packages/components/src/drop-zone/provider.js @@ -1,18 +1,17 @@ /** * External dependencies */ -import { find, some, filter, includes } from 'lodash'; +import { find, some, filter, includes, throttle } from 'lodash'; /** * WordPress dependencies */ import { createContext, - useCallback, useEffect, useRef, + useContext, } from '@wordpress/element'; -import { useThrottle } from '@wordpress/compose'; import { getFilesFromDataTransfer } from '@wordpress/dom'; import isShallowEqual from '@wordpress/is-shallow-equal'; @@ -22,13 +21,16 @@ const { Provider } = Context; function getDragEventType( { dataTransfer } ) { if ( dataTransfer ) { - if ( getFilesFromDataTransfer( dataTransfer ).size > 0 ) { - return 'file'; - } - // Use lodash `includes` here as in the Edge browser `types` is implemented // as a DomStringList, whereas in other browsers it's an array. `includes` // happily works with both types. + if ( + includes( dataTransfer.types, 'Files' ) || + getFilesFromDataTransfer( dataTransfer ).length > 0 + ) { + return 'file'; + } + if ( includes( dataTransfer.types, 'text/html' ) ) { return 'html'; } @@ -74,7 +76,7 @@ function getPosition( event ) { function getHoveredDropZone( dropZones, position, dragEventType ) { const hoveredDropZones = filter( - Array.from( dropZones.current ), + Array.from( dropZones ), ( dropZone ) => isTypeSupportedByDropZone( dragEventType, dropZone ) && isWithinElementBounds( @@ -107,126 +109,121 @@ export const INITIAL_DROP_ZONE_STATE = { type: null, }; -export default function DropZoneProvider( { children } ) { - const ref = useRef(); - const dropZones = useRef( new Set( [] ) ); - const lastRelative = useRef(); +export function useDrop( ref ) { + const dropZones = useContext( Context ); - const updateDragZones = useCallback( ( event ) => { - if ( - lastRelative.current && - lastRelative.current.contains( event.target ) - ) { - return; - } + useEffect( () => { + const { ownerDocument } = ref.current; + const { defaultView } = ownerDocument; - const dragEventType = getDragEventType( event ); - const position = getPosition( event ); - const hoveredDropZone = getHoveredDropZone( - dropZones, - position, - dragEventType - ); + let lastRelative; - if ( hoveredDropZone && hoveredDropZone.isRelative ) { - lastRelative.current = hoveredDropZone.element.current.offsetParent; - } else { - lastRelative.current = null; - } + function updateDragZones( event ) { + if ( lastRelative && lastRelative.contains( event.target ) ) { + return; + } - // Notifying the dropzones - dropZones.current.forEach( ( dropZone ) => { - const isDraggingOverDropZone = dropZone === hoveredDropZone; - const newState = { - isDraggingOverDocument: isTypeSupportedByDropZone( - dragEventType, - dropZone - ), - isDraggingOverElement: isDraggingOverDropZone, - x: - isDraggingOverDropZone && dropZone.withPosition - ? position.x - : null, - y: - isDraggingOverDropZone && dropZone.withPosition - ? position.y - : null, - type: isDraggingOverDropZone ? dragEventType : null, - }; - - dropZone.setState( ( state ) => { - if ( isShallowEqual( state, newState ) ) { - return state; - } + const dragEventType = getDragEventType( event ); + const position = getPosition( event ); + const hoveredDropZone = getHoveredDropZone( + dropZones, + position, + dragEventType + ); + + if ( hoveredDropZone && hoveredDropZone.isRelative ) { + lastRelative = hoveredDropZone.element.current.offsetParent; + } else { + lastRelative = null; + } - return newState; + // Notifying the dropzones + dropZones.forEach( ( dropZone ) => { + const isDraggingOverDropZone = dropZone === hoveredDropZone; + const newState = { + isDraggingOverDocument: isTypeSupportedByDropZone( + dragEventType, + dropZone + ), + isDraggingOverElement: isDraggingOverDropZone, + x: + isDraggingOverDropZone && dropZone.withPosition + ? position.x + : null, + y: + isDraggingOverDropZone && dropZone.withPosition + ? position.y + : null, + type: isDraggingOverDropZone ? dragEventType : null, + }; + + dropZone.setState( ( state ) => { + if ( isShallowEqual( state, newState ) ) { + return state; + } + + return newState; + } ); } ); - } ); - event.preventDefault(); - }, [] ); + event.preventDefault(); + } - const throttledUpdateDragZones = useThrottle( updateDragZones, 200 ); + const throttledUpdateDragZones = throttle( updateDragZones, 200 ); - const onDragOver = useCallback( - ( event ) => { + function onDragOver( event ) { throttledUpdateDragZones( event ); event.preventDefault(); - }, - [ throttledUpdateDragZones ] - ); - - const resetDragState = useCallback( () => { - // Avoid throttled drag over handler calls - throttledUpdateDragZones.cancel(); + } - dropZones.current.forEach( ( dropZone ) => - dropZone.setState( INITIAL_DROP_ZONE_STATE ) - ); - }, [] ); - - function onDrop( event ) { - // This seemingly useless line has been shown to resolve a Safari issue - // where files dragged directly from the dock are not recognized - event.dataTransfer && event.dataTransfer.files.length; // eslint-disable-line no-unused-expressions - - const dragEventType = getDragEventType( event ); - const position = getPosition( event ); - const hoveredDropZone = getHoveredDropZone( - dropZones, - position, - dragEventType - ); + function resetDragState() { + // Avoid throttled drag over handler calls + throttledUpdateDragZones.cancel(); - resetDragState(); - - if ( hoveredDropZone ) { - switch ( dragEventType ) { - case 'file': - hoveredDropZone.onFilesDrop( - [ ...getFilesFromDataTransfer( event.dataTransfer ) ], - position - ); - break; - case 'html': - hoveredDropZone.onHTMLDrop( - event.dataTransfer.getData( 'text/html' ), - position - ); - break; - case 'default': - hoveredDropZone.onDrop( event, position ); - } + dropZones.forEach( ( dropZone ) => + dropZone.setState( INITIAL_DROP_ZONE_STATE ) + ); } - event.stopPropagation(); - event.preventDefault(); - } + function onDrop( event ) { + // This seemingly useless line has been shown to resolve a Safari issue + // where files dragged directly from the dock are not recognized + event.dataTransfer && event.dataTransfer.files.length; // eslint-disable-line no-unused-expressions + + const dragEventType = getDragEventType( event ); + const position = getPosition( event ); + const hoveredDropZone = getHoveredDropZone( + dropZones, + position, + dragEventType + ); + + resetDragState(); + + if ( hoveredDropZone ) { + switch ( dragEventType ) { + case 'file': + hoveredDropZone.onFilesDrop( + getFilesFromDataTransfer( event.dataTransfer ), + position + ); + break; + case 'html': + hoveredDropZone.onHTMLDrop( + event.dataTransfer.getData( 'text/html' ), + position + ); + break; + case 'default': + hoveredDropZone.onDrop( event, position ); + } + } - useEffect( () => { - const { ownerDocument } = ref.current; - const { defaultView } = ownerDocument; + event.stopPropagation(); + event.preventDefault(); + } + defaultView.addEventListener( 'drop', onDrop ); defaultView.addEventListener( 'dragover', onDragOver ); defaultView.addEventListener( 'mouseup', resetDragState ); // Note that `dragend` doesn't fire consistently for file and HTML drag @@ -235,19 +232,33 @@ export default function DropZoneProvider( { children } ) { defaultView.addEventListener( 'dragend', resetDragState ); return () => { + defaultView.removeEventListener( 'drop', onDrop ); defaultView.removeEventListener( 'dragover', onDragOver ); defaultView.removeEventListener( 'mouseup', resetDragState ); defaultView.removeEventListener( 'dragend', resetDragState ); }; - }, [ onDragOver, resetDragState ] ); + }, [ ref, dropZones ] ); +} +export function DropZoneContextProvider( props ) { + const ref = useRef( new Set( [] ) ); + return <Provider { ...props } value={ ref.current } />; +} + +function DropContainer( { children } ) { + const ref = useRef(); + useDrop( ref ); return ( - <div - ref={ ref } - onDrop={ onDrop } - className="components-drop-zone__provider" - > - <Provider value={ dropZones.current }>{ children }</Provider> + <div ref={ ref } className="components-drop-zone__provider"> + { children } </div> ); } + +export default function DropZoneProvider( { children } ) { + return ( + <DropZoneContextProvider> + <DropContainer>{ children }</DropContainer> + </DropZoneContextProvider> + ); +} diff --git a/packages/components/src/focal-point-picker/README.md b/packages/components/src/focal-point-picker/README.md index 9100e4734f2119..7989f7e8ec90c9 100644 --- a/packages/components/src/focal-point-picker/README.md +++ b/packages/components/src/focal-point-picker/README.md @@ -65,7 +65,7 @@ Autoplays HTML5 video. This only applies to video sources (`url`). - Type: `Object` - Required: Yes -An object describing the height and width of the image. Requires two paramaters: `height`, `width`. +An object describing the height and width of the image. Requires two parameters: `height`, `width`. ### `value` diff --git a/packages/components/src/form-token-field/style.scss b/packages/components/src/form-token-field/style.scss index af1437ad2290b7..1fcb039924219b 100644 --- a/packages/components/src/form-token-field/style.scss +++ b/packages/components/src/form-token-field/style.scss @@ -22,6 +22,7 @@ input[type="text"].components-form-token-field__input { display: inline-block; flex: 1; + font-size: 16px; width: 100%; max-width: 100%; margin-left: 4px; @@ -33,6 +34,12 @@ color: $gray-900; box-shadow: none; + // Resolves Zooming on iOS devices + // https://github.com/WordPress/gutenberg/issues/27405 + @include break-small() { + font-size: 13px; + } + &:focus, .components-form-token-field.is-active & { outline: none; diff --git a/packages/components/src/higher-order/with-constrained-tabbing/index.js b/packages/components/src/higher-order/with-constrained-tabbing/index.js index d3e372d8655de1..db1dbe27f6908c 100644 --- a/packages/components/src/higher-order/with-constrained-tabbing/index.js +++ b/packages/components/src/higher-order/with-constrained-tabbing/index.js @@ -1,69 +1,20 @@ /** * WordPress dependencies */ -import { Component, createRef } from '@wordpress/element'; -import { createHigherOrderComponent } from '@wordpress/compose'; -import { TAB } from '@wordpress/keycodes'; -import { focus } from '@wordpress/dom'; +import { + createHigherOrderComponent, + useConstrainedTabbing, +} from '@wordpress/compose'; const withConstrainedTabbing = createHigherOrderComponent( ( WrappedComponent ) => - class extends Component { - constructor() { - super( ...arguments ); - - this.focusContainRef = createRef(); - this.handleTabBehaviour = this.handleTabBehaviour.bind( this ); - } - - handleTabBehaviour( event ) { - if ( event.keyCode !== TAB ) { - return; - } - - const tabbables = focus.tabbable.find( - this.focusContainRef.current - ); - if ( ! tabbables.length ) { - return; - } - const firstTabbable = tabbables[ 0 ]; - const lastTabbable = tabbables[ tabbables.length - 1 ]; - - if ( event.shiftKey && event.target === firstTabbable ) { - event.preventDefault(); - lastTabbable.focus(); - } else if ( - ! event.shiftKey && - event.target === lastTabbable - ) { - event.preventDefault(); - firstTabbable.focus(); - /* - * When pressing Tab and none of the tabbables has focus, the keydown - * event happens on the wrapper div: move focus on the first tabbable. - */ - } else if ( ! tabbables.includes( event.target ) ) { - event.preventDefault(); - firstTabbable.focus(); - } - } - - render() { - // Disable reason: this component is non-interactive, but must capture - // events from the wrapped component to determine when the Tab key is used. - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( - <div - onKeyDown={ this.handleTabBehaviour } - ref={ this.focusContainRef } - tabIndex="-1" - > - <WrappedComponent { ...this.props } /> - </div> - ); - /* eslint-enable jsx-a11y/no-static-element-interactions */ - } + function ComponentWithConstrainedTabbing( props ) { + const ref = useConstrainedTabbing(); + return ( + <div ref={ ref } tabIndex="-1"> + <WrappedComponent { ...props } /> + </div> + ); }, 'withConstrainedTabbing' ); diff --git a/packages/components/src/higher-order/with-focus-outside/index.js b/packages/components/src/higher-order/with-focus-outside/index.js index ca966eb048af37..c83de77f009060 100644 --- a/packages/components/src/higher-order/with-focus-outside/index.js +++ b/packages/components/src/higher-order/with-focus-outside/index.js @@ -1,142 +1,33 @@ -/** - * External dependencies - */ -import { includes } from 'lodash'; - /** * WordPress dependencies */ -import { Component } from '@wordpress/element'; -import { createHigherOrderComponent } from '@wordpress/compose'; - -/** - * Input types which are classified as button types, for use in considering - * whether element is a (focus-normalized) button. - * - * @type {string[]} - */ -const INPUT_BUTTON_TYPES = [ 'button', 'submit' ]; - -/** - * Returns true if the given element is a button element subject to focus - * normalization, or false otherwise. - * - * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus - * - * @param {Element} element Element to test. - * - * @return {boolean} Whether element is a button. - */ -function isFocusNormalizedButton( element ) { - switch ( element.nodeName ) { - case 'A': - case 'BUTTON': - return true; - - case 'INPUT': - return includes( INPUT_BUTTON_TYPES, element.type ); - } - - return false; -} - -export default createHigherOrderComponent( ( WrappedComponent ) => { - return class extends Component { - constructor() { - super( ...arguments ); - - this.bindNode = this.bindNode.bind( this ); - this.cancelBlurCheck = this.cancelBlurCheck.bind( this ); - this.queueBlurCheck = this.queueBlurCheck.bind( this ); - this.normalizeButtonFocus = this.normalizeButtonFocus.bind( this ); - } - - componentWillUnmount() { - this.cancelBlurCheck(); - } - - bindNode( node ) { - if ( node ) { - this.node = node; - } else { - delete this.node; - this.cancelBlurCheck(); - } - } - - queueBlurCheck( event ) { - // React does not allow using an event reference asynchronously - // due to recycling behavior, except when explicitly persisted. - event.persist(); - - // Skip blur check if clicking button. See `normalizeButtonFocus`. - if ( this.preventBlurCheck ) { - return; - } - - this.blurCheckTimeout = setTimeout( () => { - // If document is not focused then focus should remain - // inside the wrapped component and therefore we cancel - // this blur event thereby leaving focus in place. - // https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus. - if ( ! document.hasFocus() ) { - event.preventDefault(); - return; - } - if ( 'function' === typeof this.node.handleFocusOutside ) { - this.node.handleFocusOutside( event ); - } - }, 0 ); - } - - cancelBlurCheck() { - clearTimeout( this.blurCheckTimeout ); - } - - /** - * Handles a mousedown or mouseup event to respectively assign and - * unassign a flag for preventing blur check on button elements. Some - * browsers, namely Firefox and Safari, do not emit a focus event on - * button elements when clicked, while others do. The logic here - * intends to normalize this as treating click on buttons as focus. - * - * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus - * - * @param {MouseEvent} event Event for mousedown or mouseup. - */ - normalizeButtonFocus( event ) { - const { type, target } = event; - - const isInteractionEnd = includes( - [ 'mouseup', 'touchend' ], - type - ); - - if ( isInteractionEnd ) { - this.preventBlurCheck = false; - } else if ( isFocusNormalizedButton( target ) ) { - this.preventBlurCheck = true; - } - } - - render() { - // Disable reason: See `normalizeButtonFocus` for browser-specific - // focus event normalization. - - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( - <div - onFocus={ this.cancelBlurCheck } - onMouseDown={ this.normalizeButtonFocus } - onMouseUp={ this.normalizeButtonFocus } - onTouchStart={ this.normalizeButtonFocus } - onTouchEnd={ this.normalizeButtonFocus } - onBlur={ this.queueBlurCheck } - > - <WrappedComponent ref={ this.bindNode } { ...this.props } /> - </div> - ); - /* eslint-enable jsx-a11y/no-static-element-interactions */ - } - }; -}, 'withFocusOutside' ); +import { useCallback, useState } from '@wordpress/element'; +import { + createHigherOrderComponent, + __experimentalUseFocusOutside as useFocusOutside, +} from '@wordpress/compose'; + +export default createHigherOrderComponent( + ( WrappedComponent ) => ( props ) => { + const [ handleFocusOutside, setHandleFocusOutside ] = useState(); + const bindFocusOutsideHandler = useCallback( + ( node ) => + setHandleFocusOutside( () => + node?.handleFocusOutside + ? node.handleFocusOutside.bind( node ) + : undefined + ), + [] + ); + + return ( + <div { ...useFocusOutside( handleFocusOutside ) }> + <WrappedComponent + ref={ bindFocusOutsideHandler } + { ...props } + /> + </div> + ); + }, + 'withFocusOutside' +); diff --git a/packages/components/src/higher-order/with-focus-outside/index.native.js b/packages/components/src/higher-order/with-focus-outside/index.native.js index 98249c09fbdd22..e23ae14b0b52de 100644 --- a/packages/components/src/higher-order/with-focus-outside/index.native.js +++ b/packages/components/src/higher-order/with-focus-outside/index.native.js @@ -1,133 +1,37 @@ /** * External dependencies */ -import { includes } from 'lodash'; import { View } from 'react-native'; - /** * WordPress dependencies */ -import { Component } from '@wordpress/element'; -import { createHigherOrderComponent } from '@wordpress/compose'; - -/** - * Input types which are classified as button types, for use in considering - * whether element is a (focus-normalized) button. - * - * @type {string[]} - */ -const INPUT_BUTTON_TYPES = [ 'button', 'submit' ]; - -/** - * Returns true if the given element is a button element subject to focus - * normalization, or false otherwise. - * - * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus - * - * @param {Element} element Element to test. - * - * @return {boolean} Whether element is a button. - */ -function isFocusNormalizedButton( element ) { - switch ( element.nodeName ) { - case 'A': - case 'BUTTON': - return true; - - case 'INPUT': - return includes( INPUT_BUTTON_TYPES, element.type ); - } - - return false; -} - -export default createHigherOrderComponent( ( WrappedComponent ) => { - return class extends Component { - constructor() { - super( ...arguments ); - - this.bindNode = this.bindNode.bind( this ); - this.cancelBlurCheck = this.cancelBlurCheck.bind( this ); - this.queueBlurCheck = this.queueBlurCheck.bind( this ); - this.normalizeButtonFocus = this.normalizeButtonFocus.bind( this ); - } - - componentWillUnmount() { - this.cancelBlurCheck(); - } - - bindNode( node ) { - if ( node ) { - this.node = node; - } else { - delete this.node; - this.cancelBlurCheck(); - } - } - - queueBlurCheck( event ) { - // React does not allow using an event reference asynchronously - // due to recycling behavior, except when explicitly persisted. - event.persist(); - - // Skip blur check if clicking button. See `normalizeButtonFocus`. - if ( this.preventBlurCheck ) { - return; - } - - this.blurCheckTimeout = setTimeout( () => { - if ( 'function' === typeof this.node.handleFocusOutside ) { - this.node.handleFocusOutside( event ); - } - }, 0 ); - } - - cancelBlurCheck() { - clearTimeout( this.blurCheckTimeout ); - } - - /** - * Handles a mousedown or mouseup event to respectively assign and - * unassign a flag for preventing blur check on button elements. Some - * browsers, namely Firefox and Safari, do not emit a focus event on - * button elements when clicked, while others do. The logic here - * intends to normalize this as treating click on buttons as focus. - * - * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus - * - * @param {MouseEvent} event Event for mousedown or mouseup. - */ - normalizeButtonFocus( event ) { - const { type, target } = event; - - const isInteractionEnd = includes( - [ 'mouseup', 'touchend' ], - type - ); - - if ( isInteractionEnd ) { - this.preventBlurCheck = false; - } else if ( isFocusNormalizedButton( target ) ) { - this.preventBlurCheck = true; - } - } - - render() { - // Disable reason: See `normalizeButtonFocus` for browser-specific - // focus event normalization. - - return ( - <View - onFocus={ this.cancelBlurCheck } - onMouseDown={ this.normalizeButtonFocus } - onMouseUp={ this.normalizeButtonFocus } - onTouchStart={ this.normalizeButtonFocus } - onTouchEnd={ this.normalizeButtonFocus } - onBlur={ this.queueBlurCheck } - > - <WrappedComponent ref={ this.bindNode } { ...this.props } /> - </View> - ); - } - }; -}, 'withFocusOutside' ); +import { useCallback, useState } from '@wordpress/element'; +import { + createHigherOrderComponent, + __experimentalUseFocusOutside as useFocusOutside, +} from '@wordpress/compose'; + +export default createHigherOrderComponent( + ( WrappedComponent ) => ( props ) => { + const [ handleFocusOutside, setHandleFocusOutside ] = useState(); + const bindFocusOutsideHandler = useCallback( + ( node ) => + setHandleFocusOutside( () => + node?.handleFocusOutside + ? node.handleFocusOutside.bind( node ) + : undefined + ), + [] + ); + + return ( + <View { ...useFocusOutside( handleFocusOutside ) }> + <WrappedComponent + ref={ bindFocusOutsideHandler } + { ...props } + /> + </View> + ); + }, + 'withFocusOutside' +); diff --git a/packages/components/src/higher-order/with-focus-return/README.md b/packages/components/src/higher-order/with-focus-return/README.md index 13b9053026e468..4521781eec989b 100644 --- a/packages/components/src/higher-order/with-focus-return/README.md +++ b/packages/components/src/higher-order/with-focus-return/README.md @@ -2,8 +2,6 @@ `withFocusReturn` is a higher-order component used typically in scenarios of short-lived elements (modals, dropdowns) where, upon the element's unmounting, focus should be restored to the focused element which had initiated it being rendered. -Optionally, it can be used in combination with a `FocusReturnProvider` which, when rendered toward the top of an application, will remember a history of elements focused during a session. This can provide safeguards for scenarios where one short-lived element triggers the creation of another (e.g. a dropdown menu triggering a modal display). The combined effect of `FocusReturnProvider` and `withFocusReturn` is that focus will be returned to the most recent focused element which is still present in the document. - ## Usage ### `withFocusReturn` @@ -71,17 +69,3 @@ const EnhancedMyComponent = withFocusReturn( { }, } )( MyComponent ); ``` - -### `FocusReturnProvider` - -```jsx -import { FocusReturnProvider } from '@wordpress/components'; - -function App() { - return ( - <FocusReturnProvider> - { /* ... */ } - </FocusReturnProvider> - ); -} -``` diff --git a/packages/components/src/higher-order/with-focus-return/context.js b/packages/components/src/higher-order/with-focus-return/context.js deleted file mode 100644 index 77def70b3fb89a..00000000000000 --- a/packages/components/src/higher-order/with-focus-return/context.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * External dependencies - */ -import { uniq } from 'lodash'; - -/** - * WordPress dependencies - */ -import { createContext, useEffect, useRef, useState } from '@wordpress/element'; - -const { Provider, Consumer } = createContext( { - focusHistory: [], -} ); - -Provider.displayName = 'FocusReturnProvider'; -Consumer.displayName = 'FocusReturnConsumer'; - -/** - * The maximum history length to capture for the focus stack. When exceeded, - * items should be shifted from the stack for each consecutive push. - * - * @type {number} - */ -const MAX_STACK_LENGTH = 100; - -function FocusReturnProvider( { children, className } ) { - const ref = useRef(); - const [ focusHistory, setFocusHistory ] = useState( [] ); - - // Prepend the focus history with the active element on mount. - useEffect( () => { - setFocusHistory( [ - ref.current.ownerDocument.activeElement, - ...focusHistory, - ] ); - }, [] ); - - function onFocus( event ) { - // Push the focused element to the history stack, keeping only unique - // members but preferring the _last_ occurrence of any duplicates. - // Lodash's `uniq` behavior favors the first occurrence, so the array - // is temporarily reversed prior to it being called upon. Uniqueness - // helps avoid situations where, such as in a constrained tabbing area, - // the user changes focus enough within a transient element that the - // stack may otherwise only consist of members pending destruction, at - // which point focus might have been lost. - const nextFocusHistory = uniq( - [ ...focusHistory, event.target ] - .slice( -1 * MAX_STACK_LENGTH ) - .reverse() - ).reverse(); - - setFocusHistory( nextFocusHistory ); - } - - return ( - <Provider value={ focusHistory }> - <div ref={ ref } onFocus={ onFocus } className={ className }> - { children } - </div> - </Provider> - ); -} - -export default FocusReturnProvider; -export { Consumer }; diff --git a/packages/components/src/higher-order/with-focus-return/index.js b/packages/components/src/higher-order/with-focus-return/index.js index 08fa652cb1176f..c2a5db9a87a2ed 100644 --- a/packages/components/src/higher-order/with-focus-return/index.js +++ b/packages/components/src/higher-order/with-focus-return/index.js @@ -1,18 +1,9 @@ -/** - * External dependencies - */ -import { stubTrue, without } from 'lodash'; - /** * WordPress dependencies */ import { Component } from '@wordpress/element'; -import { createHigherOrderComponent } from '@wordpress/compose'; - -/** - * Internal dependencies - */ -import Provider, { Consumer } from './context'; +import { createHigherOrderComponent, useFocusReturn } from '@wordpress/compose'; +import deprecated from '@wordpress/deprecated'; /** * Returns true if the given object is component-like. An object is component- @@ -37,86 +28,35 @@ function isComponentLike( object ) { * describing the component and the * focus return characteristics. * - * @return {WPComponent} Component with the focus restauration behaviour. + * @return {Function} Higher Order Component with the focus restauration behaviour. */ -function withFocusReturn( options ) { - // Normalize as overloaded form `withFocusReturn( options )( Component )` - // or as `withFocusReturn( Component )`. +export default createHigherOrderComponent( ( options ) => { + const HoC = ( { onFocusReturn } = {} ) => ( WrappedComponent ) => { + const WithFocusReturn = ( props ) => { + const ref = useFocusReturn( onFocusReturn ); + return ( + <div ref={ ref }> + <WrappedComponent { ...props } /> + </div> + ); + }; + + return WithFocusReturn; + }; + if ( isComponentLike( options ) ) { const WrappedComponent = options; - return withFocusReturn( {} )( WrappedComponent ); + return HoC()( WrappedComponent ); } - const { onFocusReturn = stubTrue } = options; - - return ( WrappedComponent ) => { - class FocusReturn extends Component { - constructor() { - super( ...arguments ); - - this.ownFocusedElements = new Set(); - this.setIsFocusedFalse = () => ( this.isFocused = false ); - this.setIsFocusedTrue = ( event ) => { - this.ownFocusedElements.add( event.target ); - this.isFocused = true; - }; - } + return HoC( options ); +}, 'withFocusReturn' ); - componentWillUnmount() { - const { isFocused, ownFocusedElements } = this; - - if ( ! isFocused ) { - return; - } - - // Defer to the component's own explicit focus return behavior, - // if specified. The function should return `false` to prevent - // the default behavior otherwise occurring here. This allows - // for support that the `onFocusReturn` decides to allow the - // default behavior to occur under some conditions. - if ( onFocusReturn() === false ) { - return; - } - - const stack = without( - this.props.focusHistory, - ...ownFocusedElements - ); - - let candidate; - - while ( ( candidate = stack.pop() ) ) { - if ( document.body.contains( candidate ) ) { - candidate.focus(); - return; - } - } - } - - render() { - return ( - <div - onFocus={ this.setIsFocusedTrue } - onBlur={ this.setIsFocusedFalse } - > - <WrappedComponent { ...this.props.childProps } /> - </div> - ); - } - } - - return ( props ) => ( - <Consumer> - { ( context ) => ( - <FocusReturn - childProps={ props } - focusHistory={ context } - /> - ) } - </Consumer> - ); - }; -} +export const Provider = ( { children } ) => { + deprecated( 'wp.components.FocusReturnProvider component', { + hint: + 'This provider is not used anymore. You can just remove it from your codebase', + } ); -export default createHigherOrderComponent( withFocusReturn, 'withFocusReturn' ); -export { Provider }; + return children; +}; diff --git a/packages/components/src/higher-order/with-focus-return/test/index.js b/packages/components/src/higher-order/with-focus-return/test/index.js index 6bef3fb8776d94..07f8000de99311 100644 --- a/packages/components/src/higher-order/with-focus-return/test/index.js +++ b/packages/components/src/higher-order/with-focus-return/test/index.js @@ -2,7 +2,7 @@ * External dependencies */ import renderer from 'react-test-renderer'; -import { render } from '@testing-library/react'; +import { render, fireEvent } from '@testing-library/react'; /** * WordPress dependencies @@ -12,7 +12,7 @@ import { Component } from '@wordpress/element'; /** * Internal dependencies */ -import withFocusReturn, { Provider } from '../'; +import withFocusReturn from '../'; class Test extends Component { render() { @@ -70,16 +70,11 @@ describe( 'withFocusReturn()', () => { } ); it( 'should not switch focus back to the bound focus element', () => { - const { unmount } = render( - <Provider> - <Composite /> - </Provider>, - { - container: document.body.appendChild( - document.createElement( 'div' ) - ), - } - ); + const { unmount } = render( <Composite />, { + container: document.body.appendChild( + document.createElement( 'div' ) + ), + } ); // Change activeElement. switchFocusTo.focus(); @@ -91,67 +86,23 @@ describe( 'withFocusReturn()', () => { } ); it( 'should switch focus back when unmounted while having focus', () => { - const { container, unmount } = render( - <Provider> - <Composite /> - </Provider>, - { - container: document.body.appendChild( - document.createElement( 'div' ) - ), - } - ); + const { container, unmount } = render( <Composite />, { + container: document.body.appendChild( + document.createElement( 'div' ) + ), + } ); const textarea = container.querySelector( 'textarea' ); + fireEvent.focusIn( textarea, { target: textarea } ); textarea.focus(); expect( document.activeElement ).toBe( textarea ); // Should return to the activeElement saved with this component. unmount(); + render( <div></div>, { + container, + } ); expect( document.activeElement ).toBe( activeElement ); } ); - - it( 'should switch focus to the most recent still-available focus target', () => { - const TestComponent = ( props ) => ( - <Provider> - <input name="first" /> - { props.renderSecondInput && <input name="second" /> } - { props.renderComposite && <Composite /> } - </Provider> - ); - - const { container, rerender } = render( - <TestComponent renderSecondInput />, - { - container: document.body.appendChild( - document.createElement( 'div' ) - ), - } - ); - - const firstInput = container.querySelector( 'input[name="first"]' ); - firstInput.focus(); - - const secondInput = container.querySelector( - 'input[name="second"]' - ); - secondInput.focus(); - - expect( document.activeElement ).toBe( secondInput ); - - rerender( <TestComponent renderSecondInput renderComposite /> ); - const textarea = container.querySelector( 'textarea' ); - textarea.focus(); - - expect( document.activeElement ).toBe( textarea ); - - rerender( <TestComponent renderComposite /> ); - - expect( document.activeElement ).toBe( textarea ); - - rerender( <TestComponent /> ); - - expect( document.activeElement ).toBe( firstInput ); - } ); } ); } ); diff --git a/packages/components/src/higher-order/with-notices/README.md b/packages/components/src/higher-order/with-notices/README.md index 1ae0f1edab407b..a70d20eca4f75f 100644 --- a/packages/components/src/higher-order/with-notices/README.md +++ b/packages/components/src/higher-order/with-notices/README.md @@ -1,5 +1,38 @@ # withNotices +`withNotices` is a React [higher-order component](https://facebook.github.io/react/docs/higher-order-components.html) used typically in adding the ability to post notice messages within the original component. + +Wrapping the original component with `withNotices` encapsulates the component with the additional props `noticeOperations` and `noticeUI`. + +**noticeOperations** +Contains a number of useful functions to add notices to your site. + +<a name="createNotice" href="#createNotice">#</a> **createNotice** +Function passed down as a prop that adds a new notice. + +_Parameters_ +- _notice_ `object`: Notice to add. + +<a name="createErrorNotice" href="#createErrorNotice">#</a> **createErrorNotice** +Function passed as a prop that adds a new error notice. + +_Parameters_ +- _msg_ `string`: Error message of the notice. + + +<a name="removeAllNotices" href="#removeAllNotices">#</a> **removeAllNotices** +Function that removes all notices. + +<a name="removeNotice" href="#removeNotice">#</a> **removeNotice** +Function that removes notice by ID. + +_Parameters_ +- _id_ `string`: ID of notice to remove. + +<a name="noticeUi" href="#noticeUi">#</a>**noticeUi** +The rendered `NoticeList`. + + ## Usage ```jsx diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 696659ec71103b..aaf2620e259237 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -14,7 +14,7 @@ export { export { default as __experimentalAlignmentMatrixControl } from './alignment-matrix-control'; export { default as Animate, - useAnimate as __unstableUseAnimate, + getAnimateClassName as __unstableGetAnimateClassName, } from './animate'; export { default as AnglePickerControl } from './angle-picker-control'; export { default as Autocomplete } from './autocomplete'; @@ -45,7 +45,11 @@ export { default as DropZone, useDropZone as __unstableUseDropZone, } from './drop-zone'; -export { default as DropZoneProvider } from './drop-zone/provider'; +export { + default as DropZoneProvider, + DropZoneContextProvider as __unstableDropZoneContextProvider, + useDrop as __unstableUseDrop, +} from './drop-zone/provider'; export { default as Dropdown } from './dropdown'; export { default as DropdownMenu } from './dropdown-menu'; export { default as ExternalLink } from './external-link'; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index de7c1c4d99c4a8..f96d21550c1888 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -92,10 +92,12 @@ export { colorsUtils } from './mobile/color-settings/utils'; export { WIDE_ALIGNMENTS, ALIGNMENT_BREAKPOINTS, + alignmentHelpers, } from './mobile/utils/alignments'; // Hooks export { + convertUnitToMobile, useConvertUnitToMobile, getValueAndUnit, } from './mobile/utils/use-unit-converter-to-mobile'; diff --git a/packages/components/src/isolated-event-container/README.md b/packages/components/src/isolated-event-container/README.md index 90ce7624f41679..a14b9bd2f9bce0 100644 --- a/packages/components/src/isolated-event-container/README.md +++ b/packages/components/src/isolated-event-container/README.md @@ -1,5 +1,7 @@ # Isolated Event Container +**Deprecated** + This is a container that prevents certain events from propagating outside of the container. This is used to wrap UI elements such as modals and popovers where the propagated event can cause problems. The event continues to work inside the component. diff --git a/packages/components/src/isolated-event-container/index.js b/packages/components/src/isolated-event-container/index.js index 83a5ad4725d614..d3513953dd1314 100644 --- a/packages/components/src/isolated-event-container/index.js +++ b/packages/components/src/isolated-event-container/index.js @@ -2,12 +2,15 @@ * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; +import deprecated from '@wordpress/deprecated'; function stopPropagation( event ) { event.stopPropagation(); } export default forwardRef( ( { children, ...props }, ref ) => { + deprecated( 'wp.components.IsolatedEventContainer' ); + // Disable reason: this stops certain events from propagating outside of the component. // - onMouseDown is disabled as this can cause interactions with other DOM elements /* eslint-disable jsx-a11y/no-static-element-interactions */ diff --git a/packages/components/src/isolated-event-container/test/index.js b/packages/components/src/isolated-event-container/test/index.js index a629ce9bfdd887..9a83885a61c1bf 100644 --- a/packages/components/src/isolated-event-container/test/index.js +++ b/packages/components/src/isolated-event-container/test/index.js @@ -16,6 +16,7 @@ describe( 'IsolatedEventContainer', () => { expect( isolated.hasClass( 'test' ) ).toBe( true ); expect( isolated.prop( 'onClick' ) ).toBe( 'click' ); + expect( console ).toHaveWarned(); } ); it( 'should stop mousedown event propagation', () => { diff --git a/packages/components/src/mobile/bottom-sheet/range-cell.native.js b/packages/components/src/mobile/bottom-sheet/range-cell.native.js index da50c1206a31e6..f041b2969fea3f 100644 --- a/packages/components/src/mobile/bottom-sheet/range-cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/range-cell.native.js @@ -34,7 +34,7 @@ class BottomSheetRangeCell extends Component { this.onCellPress = this.onCellPress.bind( this ); const { value, defaultValue, minimumValue } = props; - const initialValue = value || defaultValue || minimumValue; + const initialValue = Number( value || defaultValue || minimumValue ); this.state = { accessible: true, @@ -66,11 +66,12 @@ class BottomSheetRangeCell extends Component { } onChange( nextValue ) { - const { onChange } = this.props; + const { onChange, onComplete } = this.props; this.setState( { sliderValue: nextValue, } ); onChange( nextValue ); + onComplete( nextValue ); } render() { @@ -89,6 +90,7 @@ class BottomSheetRangeCell extends Component { thumbTintColor = ! isIOS && '#00669b', preview, cellContainerStyle, + onComplete, shouldDisplayTextInput = true, children, decimalNum, @@ -146,6 +148,7 @@ class BottomSheetRangeCell extends Component { maximumTrackTintColor={ maximumTrackTintColor } thumbTintColor={ thumbTintColor } onValueChange={ this.onChangeValue } + onSlidingComplete={ onComplete } ref={ ( slider ) => { this.sliderRef = slider; } } diff --git a/packages/components/src/mobile/bottom-sheet/range-text-input.native.js b/packages/components/src/mobile/bottom-sheet/range-text-input.native.js index 0be4ca8dbb5633..efb9e64d85125d 100644 --- a/packages/components/src/mobile/bottom-sheet/range-text-input.native.js +++ b/packages/components/src/mobile/bottom-sheet/range-text-input.native.js @@ -39,8 +39,11 @@ class RangeTextInput extends Component { this.onSubmitEditing = this.onSubmitEditing.bind( this ); this.onChangeText = this.onChangeText.bind( this ); - const { value, defaultValue, min } = props; - const initialValue = value || defaultValue || min; + const { value, defaultValue, min, decimalNum } = props; + const initialValue = toFixed( + value || defaultValue || min, + decimalNum + ); const fontScale = this.getFontScale(); @@ -128,13 +131,6 @@ class RangeTextInput extends Component { onChange( validValue ); } - onChangeValue( initialValue ) { - const { decimalNum } = this.props; - initialValue = toFixed( initialValue, decimalNum ); - this.setState( { inputValue: initialValue } ); - this.updateValue( initialValue ); - } - onChangeText( textValue ) { const { decimalNum } = this.props; const inputValue = removeNonDigit( textValue, decimalNum ); @@ -194,7 +190,7 @@ class RangeTextInput extends Component { const valueFinalStyle = [ ! isIOS ? inputBorderStyles : verticalBorderStyle, { - width: 40 * fontScale, + width: 50 * fontScale, }, ]; diff --git a/packages/components/src/mobile/gradient/index.native.js b/packages/components/src/mobile/gradient/index.native.js index ed0e0b76664104..57ccd2aaf739b7 100644 --- a/packages/components/src/mobile/gradient/index.native.js +++ b/packages/components/src/mobile/gradient/index.native.js @@ -3,35 +3,98 @@ */ import { View, Platform } from 'react-native'; import RNLinearGradient from 'react-native-linear-gradient'; +import gradientParser from 'gradient-parser'; /** * WordPress dependencies */ import { colorsUtils } from '@wordpress/components'; import { RadialGradient, Stop, SVG, Defs, Rect } from '@wordpress/primitives'; import { useResizeObserver } from '@wordpress/compose'; +import { useMemo } from '@wordpress/element'; /** * Internal dependencies */ import styles from './style.scss'; -function getGradientAngle( gradientValue ) { - const matchDeg = /(\d+)deg/g; +export function getGradientAngle( gradientValue ) { + const angleBase = 45; + const matchAngle = /\(((\d+deg)|(to\s[^,]+))/; + const angle = matchAngle.exec( gradientValue ) + ? matchAngle.exec( gradientValue )[ 1 ] + : '180deg'; - return Number( matchDeg.exec( gradientValue )[ 1 ] ); + const angleType = angle.includes( 'deg' ) ? 'angle' : 'sideOrCorner'; + + if ( angleType === 'sideOrCorner' ) { + switch ( angle ) { + case 'to top': + return 0; + case 'to top right': + case 'to right top': + return angleBase; + case 'to right': + return 2 * angleBase; + case 'to bottom right': + case 'to right bottom': + return 3 * angleBase; + case 'to bottom': + return 4 * angleBase; + case 'to bottom left': + case 'to left bottom': + return 5 * angleBase; + case 'to left': + return 6 * angleBase; + case 'to top left': + case 'to left top': + return 7 * angleBase; + } + } else if ( angleType === 'angle' ) { + return parseFloat( angle ); + } else return 4 * angleBase; } -function getGradientColorGroup( gradientValue ) { - const matchColorGroup = /(rgba|rgb|#)(.+?)[\%]/g; +export function getGradientColorGroup( gradientValue ) { + const colorNeedParenthesis = [ 'rgb', 'rgba' ]; + + const excludeSideOrCorner = /linear-gradient\(to\s+([a-z\s]+,)/; + + // Parser has some difficulties with angle defined as a side or corner (e.g. `to left`) + // so it's going to be excluded in order to matching color groups + const modifiedGradientValue = gradientValue.replace( + excludeSideOrCorner, + 'linear-gradient(' + ); + + return [].concat( + ...gradientParser.parse( modifiedGradientValue )?.map( ( gradient ) => + gradient.colorStops?.map( ( color, index ) => { + const { type, value, length } = color; + const fallbackLength = `${ + 100 * ( index / ( gradient.colorStops.length - 1 ) ) + }%`; + const colorLength = length + ? `${ length.value }${ length.type }` + : fallbackLength; + + if ( colorNeedParenthesis.includes( type ) ) { + return [ `${ type }(${ value.join( ',' ) })`, colorLength ]; + } else if ( type === 'literal' ) { + return [ value, colorLength ]; + } + return [ `#${ value }`, colorLength ]; + } ) + ) + ); +} - return gradientValue - .match( matchColorGroup ) - .map( ( color ) => color.split( ' ' ) ); +export function getGradientBaseColors( colorGroup ) { + return colorGroup.map( ( color ) => color[ 0 ] ); } -function getGradientBaseColors( gradientValue ) { - return getGradientColorGroup( gradientValue ).map( - ( color ) => color[ 0 ] +export function getColorLocations( colorGroup ) { + return colorGroup.map( + ( location ) => Number( location[ 1 ].replace( '%', '' ) ) / 100 ); } @@ -46,6 +109,18 @@ function Gradient( { const { width = 0, height = 0 } = sizes || {}; const { isGradient, getGradientType, gradients } = colorsUtils; + const colorGroup = useMemo( () => getGradientColorGroup( gradientValue ), [ + gradientValue, + ] ); + + const locations = useMemo( () => getColorLocations( colorGroup ), [ + colorGroup, + ] ); + + const colors = useMemo( () => getGradientBaseColors( colorGroup ), [ + colorGroup, + ] ); + if ( ! gradientValue || ! isGradient( gradientValue ) ) { return null; } @@ -53,16 +128,10 @@ function Gradient( { const isLinearGradient = getGradientType( gradientValue ) === gradients.linear; - const colorGroup = getGradientColorGroup( gradientValue ); - - const locations = colorGroup.map( - ( location ) => Number( location[ 1 ].replace( '%', '' ) ) / 100 - ); - if ( isLinearGradient ) { return ( <RNLinearGradient - colors={ getGradientBaseColors( gradientValue ) } + colors={ colors } useAngle={ true } angle={ getGradientAngle( gradientValue ) } locations={ locations } diff --git a/packages/components/src/mobile/gradient/test/index.native.js b/packages/components/src/mobile/gradient/test/index.native.js new file mode 100644 index 00000000000000..c8b4d458b8c3b9 --- /dev/null +++ b/packages/components/src/mobile/gradient/test/index.native.js @@ -0,0 +1,116 @@ +/** + * Internal dependencies + */ +import { + getGradientAngle, + getGradientBaseColors, + getColorLocations, + getGradientColorGroup, +} from '../index.native'; + +describe( 'getGradientAngle', () => { + it( 'returns default angle (180) when not specified in gradient value', () => { + const gradientValue = 'linear-gradient(#e66465, #9198e5)'; + expect( getGradientAngle( gradientValue ) ).toBe( 180 ); + } ); + it( 'returns correct angle if specified in words in gradient value', () => { + const angleBase = 45; + const sidesOrCorners = { + 'to top': 0, + 'to top right': angleBase, + 'to right top': angleBase, + 'to right': 2 * angleBase, + 'to bottom right': 3 * angleBase, + 'to right bottom': 3 * angleBase, + 'to bottom': 4 * angleBase, + 'to bottom left': 5 * angleBase, + 'to left bottom': 5 * angleBase, + 'to left': 6 * angleBase, + 'to top left': 7 * angleBase, + 'to left top': 7 * angleBase, + }; + const gradientColors = Object.keys( sidesOrCorners ).map( + ( sideOrCorner ) => { + return { + gradientValue: `linear-gradient(${ sideOrCorner }, #e66465, #9198e5)`, + sideOrCorner, + }; + } + ); + + gradientColors.forEach( ( gradientColor ) => { + const { gradientValue, sideOrCorner } = gradientColor; + expect( getGradientAngle( gradientValue ) ).toBe( + sidesOrCorners[ sideOrCorner ] + ); + } ); + } ); + it( 'returns an angle specified in gradient value', () => { + const gradientValue = 'linear-gradient(155deg,#e66465, #9198e5)'; + expect( getGradientAngle( gradientValue ) ).toBe( 155 ); + } ); +} ); + +describe( 'getGradientBaseColors', () => { + it( 'returns array of colors (hex) from gradient value', () => { + const colorGroup = getGradientColorGroup( + 'linear-gradient(#e66465, #9198e5)' + ); + + expect( getGradientBaseColors( colorGroup ) ).toStrictEqual( [ + '#e66465', + '#9198e5', + ] ); + } ); + it( 'returns an array of colors (rgb/rgba) from gradient value', () => { + const colorGroup = getGradientColorGroup( + `linear-gradient(336deg, rgb(0,0,255), rgba(0,0,255,.8) 70.71%)` + ); + expect( getGradientBaseColors( colorGroup ) ).toStrictEqual( [ + 'rgb(0,0,255)', + 'rgba(0,0,255,.8)', + ] ); + } ); + + it( 'return an array of colors (literal) from gradient value', () => { + const colorGroup = getGradientColorGroup( + 'linear-gradient(45deg, blue, red)' + ); + expect( getGradientBaseColors( colorGroup ) ).toStrictEqual( [ + 'blue', + 'red', + ] ); + } ); + + it( 'return an array of colors (mixed) from gradient value', () => { + const colorGroup = getGradientColorGroup( + 'linear-gradient(45deg, blue, #e66465, rgb(0,0,255), rgba(0,0,255,.8))' + ); + expect( getGradientBaseColors( colorGroup ) ).toStrictEqual( [ + 'blue', + '#e66465', + 'rgb(0,0,255)', + 'rgba(0,0,255,.8)', + ] ); + } ); +} ); + +describe( 'getColorLocations', () => { + it( 'returns an array of color locations specified in gradient value', () => { + const colorGroup = getGradientColorGroup( + 'linear-gradient(45deg, red 0%, blue 10%)' + ); + expect( getColorLocations( colorGroup ) ).toStrictEqual( [ 0, 0.1 ] ); + } ); + + it( 'returns an array of color locations adjusted proportionally when not specified in gradient value', () => { + const colorGroup = getGradientColorGroup( + 'linear-gradient(45deg, red, blue, green)' + ); + expect( getColorLocations( colorGroup ) ).toStrictEqual( [ + 0, + 0.5, + 1, + ] ); + } ); +} ); diff --git a/packages/components/src/mobile/link-settings/index.native.js b/packages/components/src/mobile/link-settings/index.native.js index 243547500fb127..4d5b2a496a2999 100644 --- a/packages/components/src/mobile/link-settings/index.native.js +++ b/packages/components/src/mobile/link-settings/index.native.js @@ -158,10 +158,10 @@ function LinkSettings( { function onChangeOpenInNewTab( value ) { const newLinkTarget = value ? '_blank' : undefined; - let updatedRel = rel; - if ( newLinkTarget && ! rel ) { + let updatedRel = linkRelInputValue; + if ( newLinkTarget && ! linkRelInputValue ) { updatedRel = NEW_TAB_REL; - } else if ( ! newLinkTarget && rel === NEW_TAB_REL ) { + } else if ( ! newLinkTarget && linkRelInputValue === NEW_TAB_REL ) { updatedRel = undefined; } @@ -224,26 +224,30 @@ function LinkSettings( { onChange={ onChangeLabel } /> ) } - { options.openInNewTab && ( - <ToggleControl - icon={ showIcon && external } - label={ options.openInNewTab.label } - checked={ linkTarget === '_blank' } - onChange={ onChangeOpenInNewTab } - /> - ) } - { options.linkRel && ( - <TextControl - icon={ showIcon && LinkRelIcon } - label={ options.linkRel.label } - value={ linkRelInputValue } - valuePlaceholder={ options.linkRel.placeholder } - onChange={ onChangeLinkRel } - onSubmit={ onCloseSettingsSheet } - autoCapitalize="none" - autoCorrect={ false } - keyboardType="url" - /> + { !! urlInputValue && ( + <> + { options.openInNewTab && ( + <ToggleControl + icon={ showIcon && external } + label={ options.openInNewTab.label } + checked={ linkTarget === '_blank' } + onChange={ onChangeOpenInNewTab } + /> + ) } + { options.linkRel && ( + <TextControl + icon={ showIcon && LinkRelIcon } + label={ options.linkRel.label } + value={ linkRelInputValue } + valuePlaceholder={ options.linkRel.placeholder } + onChange={ onChangeLinkRel } + onSubmit={ onCloseSettingsSheet } + autoCapitalize="none" + autoCorrect={ false } + keyboardType="url" + /> + ) } + </> ) } </> ); diff --git a/packages/components/src/mobile/picker/index.android.js b/packages/components/src/mobile/picker/index.android.js index 5a6bbfbf08ef2b..2f727b2b92a9b3 100644 --- a/packages/components/src/mobile/picker/index.android.js +++ b/packages/components/src/mobile/picker/index.android.js @@ -64,14 +64,13 @@ export default class Picker extends Component { hideHeader > <PanelBody title={ title } style={ styles.panelBody }> - { options.map( ( option, index ) => ( - <> + { options.map( ( option ) => ( + <View key={ `${ option.label }-${ option.value }` }> { options.length > 1 && option.separated && ( <Separator /> ) } <BottomSheet.Cell icon={ option.icon } - key={ index } leftAlign={ leftAlign } label={ option.label } separatorType={ 'none' } @@ -81,7 +80,7 @@ export default class Picker extends Component { disabled={ option.disabled } style={ option.disabled && styles.disabled } /> - </> + </View> ) ) } { ! hideCancelButton && ( <BottomSheet.Cell diff --git a/packages/components/src/mobile/utils/alignments.native.js b/packages/components/src/mobile/utils/alignments.native.js index 03fdcbd93f5e86..219bdefb8a721c 100644 --- a/packages/components/src/mobile/utils/alignments.native.js +++ b/packages/components/src/mobile/utils/alignments.native.js @@ -3,7 +3,10 @@ export const WIDE_ALIGNMENTS = { wide: 'wide', full: 'full', }, - excludeBlocks: [ 'core/columns', 'core/heading' ], + // `innerContainers`: Group of blocks based on `InnerBlocks` component, + // used to nest other blocks inside + innerContainers: [ 'core/group', 'core/columns', 'core/column' ], + excludeBlocks: [ 'core/heading' ], }; export const ALIGNMENT_BREAKPOINTS = { @@ -11,4 +14,22 @@ export const ALIGNMENT_BREAKPOINTS = { large: 820, medium: 768, small: 680, + mobile: 480, +}; + +const isFullWidth = ( align ) => align === WIDE_ALIGNMENTS.alignments.full; + +const isWideWidth = ( align ) => align === WIDE_ALIGNMENTS.alignments.wide; + +const isWider = ( width, breakpoint ) => + width > ALIGNMENT_BREAKPOINTS[ breakpoint ]; + +const isContainerRelated = ( blockName ) => + WIDE_ALIGNMENTS.innerContainers.includes( blockName ); + +export const alignmentHelpers = { + isFullWidth, + isWideWidth, + isWider, + isContainerRelated, }; diff --git a/packages/components/src/mobile/utils/use-unit-converter-to-mobile.native.js b/packages/components/src/mobile/utils/use-unit-converter-to-mobile.native.js index ec34c1b7880217..cf8a77b8d0e561 100644 --- a/packages/components/src/mobile/utils/use-unit-converter-to-mobile.native.js +++ b/packages/components/src/mobile/utils/use-unit-converter-to-mobile.native.js @@ -35,7 +35,32 @@ const getValueAndUnit = ( value, unit ) => { return undefined; }; +const convertUnitToMobile = ( containerSize, globalStyles, value, unit ) => { + const { width, height } = containerSize; + const { valueToConvert, valueUnit } = getValueAndUnit( value, unit ); + const { fontSize = 16 } = globalStyles || {}; + + switch ( valueUnit ) { + case 'rem': + case 'em': + return valueToConvert * fontSize; + case '%': + return Number( valueToConvert / 100 ) * width; + case 'px': + return Number( valueToConvert ); + case 'vw': + const vw = width / 100; + return Math.round( valueToConvert * vw ); + case 'vh': + const vh = height / 100; + return Math.round( valueToConvert * vh ); + default: + return Number( valueToConvert / 100 ) * width; + } +}; + const useConvertUnitToMobile = ( value, unit ) => { + const { globalStyles: styles } = useContext( GlobalStylesContext ); const [ windowSizes, setWindowSizes ] = useState( Dimensions.get( 'window' ) ); @@ -47,36 +72,21 @@ const useConvertUnitToMobile = ( value, unit ) => { Dimensions.removeEventListener( 'change', onDimensionsChange ); }; }, [] ); - const { globalStyles: styles } = useContext( GlobalStylesContext ); const onDimensionsChange = useCallback( ( { window } ) => { setWindowSizes( window ); }, [] ); return useMemo( () => { - const { width, height } = windowSizes; - const { fontSize = 16 } = styles || {}; - const { valueToConvert, valueUnit } = getValueAndUnit( value, unit ); - switch ( valueUnit ) { - case 'rem': - case 'em': - return valueToConvert * fontSize; - case '%': - return Number( valueToConvert / 100 ) * width; - case 'px': - return Number( valueToConvert ); - case 'vw': - const vw = width / 100; - return Math.round( valueToConvert * vw ); - case 'vh': - const vh = height / 100; - return Math.round( valueToConvert * vh ); - default: - return Number( valueToConvert / 100 ) * width; - } + return convertUnitToMobile( + windowSizes, + styles, + valueToConvert, + valueUnit + ); }, [ windowSizes, value, unit ] ); }; -export { useConvertUnitToMobile, getValueAndUnit }; +export { convertUnitToMobile, useConvertUnitToMobile, getValueAndUnit }; diff --git a/packages/components/src/modal/frame.js b/packages/components/src/modal/frame.js index 25938225598a3b..8047832a6ec444 100644 --- a/packages/components/src/modal/frame.js +++ b/packages/components/src/modal/frame.js @@ -2,87 +2,95 @@ * External dependencies */ import classnames from 'classnames'; +import mergeRefs from 'react-merge-refs'; /** * WordPress dependencies */ -import { Component, createRef } from '@wordpress/element'; +import { Component } from '@wordpress/element'; import { ESCAPE } from '@wordpress/keycodes'; -import { compose } from '@wordpress/compose'; +import { + useFocusReturn, + useFocusOnMount, + useConstrainedTabbing, +} from '@wordpress/compose'; /** * Internal dependencies */ -import IsolatedEventContainer from '../isolated-event-container'; import withFocusOutside from '../higher-order/with-focus-outside'; -import withFocusReturn from '../higher-order/with-focus-return'; -import withConstrainedTabbing from '../higher-order/with-constrained-tabbing'; + +function ModalFrameContent( { + overlayClassName, + contentLabel, + aria: { describedby, labelledby }, + children, + className, + role, + style, + focusOnMount, + shouldCloseOnEsc, + onRequestClose, +} ) { + function handleEscapeKeyDown( event ) { + if ( shouldCloseOnEsc && event.keyCode === ESCAPE ) { + event.stopPropagation(); + if ( onRequestClose ) { + onRequestClose( event ); + } + } + } + const focusOnMountRef = useFocusOnMount( focusOnMount ); + const constrainedTabbingRef = useConstrainedTabbing(); + const focusReturnRef = useFocusReturn(); + + return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions + <div + className={ classnames( + 'components-modal__screen-overlay', + overlayClassName + ) } + onKeyDown={ handleEscapeKeyDown } + > + <div + className={ classnames( 'components-modal__frame', className ) } + style={ style } + ref={ mergeRefs( [ + constrainedTabbingRef, + focusReturnRef, + focusOnMountRef, + ] ) } + role={ role } + aria-label={ contentLabel } + aria-labelledby={ contentLabel ? null : labelledby } + aria-describedby={ describedby } + tabIndex="-1" + > + { children } + </div> + </div> + ); +} class ModalFrame extends Component { constructor() { super( ...arguments ); - - this.containerRef = createRef(); - this.handleKeyDown = this.handleKeyDown.bind( this ); this.handleFocusOutside = this.handleFocusOutside.bind( this ); } - /** - * Focuses the first tabbable element when props.focusOnMount is true. - */ - componentDidMount() { - // Focus on mount - if ( this.props.focusOnMount ) { - this.containerRef.current.focus(); - } - } - /** * Callback function called when clicked outside the modal. * * @param {Object} event Mouse click event. */ handleFocusOutside( event ) { - if ( this.props.shouldCloseOnClickOutside ) { - this.onRequestClose( event ); - } - } - - /** - * Callback function called when a key is pressed. - * - * @param {KeyboardEvent} event Key down event. - */ - handleKeyDown( event ) { - if ( event.keyCode === ESCAPE ) { - this.handleEscapeKeyDown( event ); - } - } - - /** - * Handles a escape key down event. - * - * Calls onRequestClose and prevents propagation of the event outside the modal. - * - * @param {Object} event Key down event. - */ - handleEscapeKeyDown( event ) { - if ( this.props.shouldCloseOnEsc ) { - event.stopPropagation(); - this.onRequestClose( event ); - } - } - - /** - * Calls the onRequestClose callback props when it is available. - * - * @param {Object} event Event object. - */ - onRequestClose( event ) { - const { onRequestClose } = this.props; - if ( onRequestClose ) { - onRequestClose( event ); + if ( + this.props.shouldCloseOnClickOutside && + this.props.onRequestClose + ) { + this.props.onRequestClose( event ); } } @@ -92,46 +100,8 @@ class ModalFrame extends Component { * @return {WPElement} The modal frame element. */ render() { - const { - overlayClassName, - contentLabel, - aria: { describedby, labelledby }, - children, - className, - role, - style, - } = this.props; - - return ( - <IsolatedEventContainer - className={ classnames( - 'components-modal__screen-overlay', - overlayClassName - ) } - onKeyDown={ this.handleKeyDown } - > - <div - className={ classnames( - 'components-modal__frame', - className - ) } - style={ style } - ref={ this.containerRef } - role={ role } - aria-label={ contentLabel } - aria-labelledby={ contentLabel ? null : labelledby } - aria-describedby={ describedby } - tabIndex="-1" - > - { children } - </div> - </IsolatedEventContainer> - ); + return <ModalFrameContent { ...this.props } />; } } -export default compose( [ - withFocusReturn, - withConstrainedTabbing, - withFocusOutside, -] )( ModalFrame ); +export default withFocusOutside( ModalFrame ); diff --git a/packages/components/src/modal/style.scss b/packages/components/src/modal/style.scss index eecdbbbd4558a9..450cb553cf48c5 100644 --- a/packages/components/src/modal/style.scss +++ b/packages/components/src/modal/style.scss @@ -5,7 +5,7 @@ right: 0; bottom: 0; left: 0; - background-color: rgba($black, 0.7); + background-color: rgba($black, 0.35); z-index: z-index(".components-modal__screen-overlay"); // This animates the appearance of the white background. @@ -36,7 +36,7 @@ left: 50%; min-width: $modal-min-width; max-width: calc(100% - #{ $grid-unit-20 } - #{ $grid-unit-20 }); - max-height: calc(100% - #{ $header-height } - #{ $header-height }); + max-height: 90%; transform: translate(-50%, -50%); // Animate the modal frame/contents appearing on the page. diff --git a/packages/components/src/navigation/back-button/index.js b/packages/components/src/navigation/back-button/index.js index 9a5d750681ff3f..c52932bb8e8624 100644 --- a/packages/components/src/navigation/back-button/index.js +++ b/packages/components/src/navigation/back-button/index.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; +import { __, isRTL } from '@wordpress/i18n'; import { Icon, chevronLeft, chevronRight } from '@wordpress/icons'; /** @@ -14,7 +14,6 @@ import { Icon, chevronLeft, chevronRight } from '@wordpress/icons'; */ import { useNavigationContext } from '../context'; import { MenuBackButtonUI } from '../styles/navigation-styles'; -import { getRTL } from '../../utils/rtl'; function NavigationBackButton( { backButtonLabel, className, href, onClick, parentMenu }, @@ -34,12 +33,12 @@ function NavigationBackButton( onClick( event ); } - const animationDirection = getRTL() ? 'left' : 'right'; + const animationDirection = isRTL() ? 'left' : 'right'; if ( parentMenu && ! event.defaultPrevented ) { setActiveMenu( parentMenu, animationDirection ); } }; - const icon = getRTL() ? chevronRight : chevronLeft; + const icon = isRTL() ? chevronRight : chevronLeft; return ( <MenuBackButtonUI className={ classes } diff --git a/packages/components/src/navigation/group/index.js b/packages/components/src/navigation/group/index.js index ec2282d2f1e1f3..bcea1b0a5894a4 100644 --- a/packages/components/src/navigation/group/index.js +++ b/packages/components/src/navigation/group/index.js @@ -15,14 +15,12 @@ import { useState } from '@wordpress/element'; import { NavigationGroupContext } from './context'; import { GroupTitleUI } from '../styles/navigation-styles'; import { useNavigationContext } from '../context'; -import { useRTL } from '../../utils/rtl'; export default function NavigationGroup( { children, className, title } ) { const [ groupId ] = useState( uniqueId( 'group-' ) ); const { navigationTree: { items }, } = useNavigationContext(); - const isRTL = useRTL(); const context = { group: groupId }; @@ -47,7 +45,6 @@ export default function NavigationGroup( { children, className, title } ) { className="components-navigation__group-title" id={ groupTitleId } variant="caption" - isRTL={ isRTL } > { title } </GroupTitleUI> diff --git a/packages/components/src/navigation/index.js b/packages/components/src/navigation/index.js index 137f031e78f7e0..8f9f2bba506c36 100644 --- a/packages/components/src/navigation/index.js +++ b/packages/components/src/navigation/index.js @@ -8,16 +8,16 @@ import { noop } from 'lodash'; * WordPress dependencies */ import { useEffect, useRef, useState } from '@wordpress/element'; +import { isRTL } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useAnimate } from '../animate'; +import { getAnimateClassName } from '../animate'; import { ROOT_MENU } from './constants'; import { NavigationContext } from './context'; import { NavigationUI } from './styles/navigation-styles'; import { useCreateNavigationTree } from './use-create-navigation-tree'; -import { useRTL } from '../utils/rtl'; export default function Navigation( { activeItem, @@ -29,7 +29,7 @@ export default function Navigation( { const [ menu, setMenu ] = useState( activeMenu ); const [ slideOrigin, setSlideOrigin ] = useState(); const navigationTree = useCreateNavigationTree(); - const defaultSlideOrigin = useRTL() ? 'right' : 'left'; + const defaultSlideOrigin = isRTL() ? 'right' : 'left'; const setActiveMenu = ( menuId, slideInOrigin = defaultSlideOrigin ) => { if ( ! navigationTree.getMenu( menuId ) ) { @@ -63,7 +63,7 @@ export default function Navigation( { }; const classes = classnames( 'components-navigation', className ); - const animateClassName = useAnimate( { + const animateClassName = getAnimateClassName( { type: 'slide-in', origin: slideOrigin, } ); diff --git a/packages/components/src/navigation/item/base-content.js b/packages/components/src/navigation/item/base-content.js index 93c8aef9193391..f4b6fc5b41a8a8 100644 --- a/packages/components/src/navigation/item/base-content.js +++ b/packages/components/src/navigation/item/base-content.js @@ -1,12 +1,10 @@ /** * Internal dependencies */ -import { useRTL } from '../../utils'; import { ItemBadgeUI, ItemTitleUI } from '../styles/navigation-styles'; export default function NavigationItemBaseContent( props ) { const { badge, title } = props; - const isRTL = useRTL(); return ( <> @@ -14,7 +12,6 @@ export default function NavigationItemBaseContent( props ) { <ItemTitleUI className="components-navigation__item-title" variant="body.small" - isRTL={ isRTL } as="span" > { title } @@ -22,10 +19,7 @@ export default function NavigationItemBaseContent( props ) { ) } { badge && ( - <ItemBadgeUI - className="components-navigation__item-badge" - isRTL={ isRTL } - > + <ItemBadgeUI className="components-navigation__item-badge"> { badge } </ItemBadgeUI> ) } diff --git a/packages/components/src/navigation/item/index.js b/packages/components/src/navigation/item/index.js index 4514633b3d4593..625a5f3cb7265a 100644 --- a/packages/components/src/navigation/item/index.js +++ b/packages/components/src/navigation/item/index.js @@ -8,6 +8,7 @@ import { noop } from 'lodash'; * WordPress dependencies */ import { Icon, chevronLeft, chevronRight } from '@wordpress/icons'; +import { isRTL } from '@wordpress/i18n'; /** * Internal dependencies @@ -15,7 +16,6 @@ import { Icon, chevronLeft, chevronRight } from '@wordpress/icons'; import Button from '../../button'; import { useNavigationContext } from '../context'; import { ItemUI } from '../styles/navigation-styles'; -import { useRTL } from '../../utils/rtl'; import NavigationItemBaseContent from './base-content'; import NavigationItemBase from './base'; @@ -39,7 +39,6 @@ export default function NavigationItem( props ) { setActiveMenu, navigationTree: { isMenuEmpty }, } = useNavigationContext(); - const isRTL = useRTL(); // If hideIfTargetMenuEmpty prop is true // And the menu we are supposed to navigate to @@ -63,7 +62,7 @@ export default function NavigationItem( props ) { onClick( event ); }; - const icon = isRTL ? chevronLeft : chevronRight; + const icon = isRTL() ? chevronLeft : chevronRight; const baseProps = isText ? restProps : { as: Button, href, onClick: onItemClick, ...restProps }; diff --git a/packages/components/src/navigation/menu/index.js b/packages/components/src/navigation/menu/index.js index 4eadb9d05a8e2b..0890becbfb26cf 100644 --- a/packages/components/src/navigation/menu/index.js +++ b/packages/components/src/navigation/menu/index.js @@ -17,6 +17,7 @@ import { useNavigationContext } from '../context'; import { useNavigationTreeMenu } from './use-navigation-tree-menu'; import NavigationBackButton from '../back-button'; import NavigationMenuTitle from './menu-title'; +import NavigationSearchNoResultsFound from './search-no-results-found'; import { NavigableMenu } from '../../navigable-container'; import { MenuUI } from '../styles/navigation-styles'; @@ -81,7 +82,12 @@ export default function NavigationMenu( props ) { /> <NavigableMenu> - <ul aria-labelledby={ menuTitleId }>{ children }</ul> + <ul aria-labelledby={ menuTitleId }> + { children } + { search && ( + <NavigationSearchNoResultsFound search={ search } /> + ) } + </ul> </NavigableMenu> </MenuUI> </NavigationMenuContext.Provider> diff --git a/packages/components/src/navigation/menu/menu-title.js b/packages/components/src/navigation/menu/menu-title.js index b249bbeb3a3850..2cb860c2758d37 100644 --- a/packages/components/src/navigation/menu/menu-title.js +++ b/packages/components/src/navigation/menu/menu-title.js @@ -8,7 +8,7 @@ import { Icon, search as searchIcon } from '@wordpress/icons'; /** * Internal dependencies */ -import Animate from '../../animate'; +import { getAnimateClassName } from '../../animate'; import Button from '../../button'; import MenuTitleSearch from './menu-title-search'; import { @@ -18,7 +18,6 @@ import { } from '../styles/navigation-styles'; import { useNavigationMenuContext } from './context'; import { SEARCH_FOCUS_DELAY } from '../constants'; -import { useRTL } from '../../utils/rtl'; export default function NavigationMenuTitle( { hasSearch, @@ -30,7 +29,6 @@ export default function NavigationMenuTitle( { const [ isSearching, setIsSearching ] = useState( false ); const { menu } = useNavigationMenuContext(); const searchButtonRef = useRef(); - const isRTL = useRTL(); if ( ! title ) { return null; @@ -57,7 +55,6 @@ export default function NavigationMenuTitle( { as="h2" className="components-navigation__menu-title-heading" variant="title.small" - isRTL={ isRTL } > <span id={ menuTitleId }>{ title }</span> @@ -82,18 +79,19 @@ export default function NavigationMenuTitle( { ) } { isSearching && ( - <Animate type="slide-in" options={ { origin: 'left' } }> - { ( { className: animateClassName } ) => ( - <div className={ animateClassName }> - <MenuTitleSearch - onCloseSearch={ onCloseSearch } - onSearch={ onSearch } - search={ search } - title={ title } - /> - </div> - ) } - </Animate> + <div + className={ getAnimateClassName( { + type: 'slide-in', + origin: 'left', + } ) } + > + <MenuTitleSearch + onCloseSearch={ onCloseSearch } + onSearch={ onSearch } + search={ search } + title={ title } + /> + </div> ) } </MenuTitleUI> ); diff --git a/packages/components/src/navigation/menu/search-no-results-found.js b/packages/components/src/navigation/menu/search-no-results-found.js new file mode 100644 index 00000000000000..07391714c8dff3 --- /dev/null +++ b/packages/components/src/navigation/menu/search-no-results-found.js @@ -0,0 +1,33 @@ +/** + * External dependencies + */ +import { filter } from 'lodash'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { useNavigationContext } from '../context'; +import { ItemBaseUI, ItemUI } from '../styles/navigation-styles'; + +export default function NavigationSearchNoResultsFound( { search } ) { + const { + navigationTree: { items }, + } = useNavigationContext(); + + const resultsCount = filter( items, '_isVisible' ).length; + + if ( ! search || !! resultsCount ) { + return null; + } + + return ( + <ItemBaseUI> + <ItemUI>{ __( 'No results found.' ) } </ItemUI> + </ItemBaseUI> + ); +} diff --git a/packages/components/src/navigation/styles/navigation-styles.js b/packages/components/src/navigation/styles/navigation-styles.js index 65d2c1f2151def..4e5c76ad36206f 100644 --- a/packages/components/src/navigation/styles/navigation-styles.js +++ b/packages/components/src/navigation/styles/navigation-styles.js @@ -3,6 +3,11 @@ */ import styled from '@emotion/styled'; +/** + * WordPress dependencies + */ +import { isRTL } from '@wordpress/i18n'; + /** * Internal dependencies */ @@ -66,8 +71,8 @@ export const MenuTitleHeadingUI = styled( Text )` display: flex; justify-content: space-between; margin-bottom: ${ space( 1 ) }; - padding: ${ ( props ) => - props.isRTL + padding: ${ () => + isRTL() ? `${ space( 0.5 ) } ${ space( 2 ) } ${ space( 0.5 ) } ${ space( 1.5 ) }` @@ -139,8 +144,8 @@ export const MenuTitleSearchUI = styled.div` export const GroupTitleUI = styled( Text )` margin-top: ${ space( 1 ) }; - padding: ${ ( props ) => - props.isRTL + padding: ${ () => + isRTL() ? `${ space( 0.5 ) } ${ space( 2 ) } ${ space( 0.5 ) } 0` : `${ space( 0.5 ) } 0 ${ space( 0.5 ) } ${ space( 2 ) }` }; text-transform: uppercase; @@ -190,8 +195,8 @@ export const ItemUI = styled.div` `; export const ItemBadgeUI = styled.span` - margin-left: ${ ( props ) => ( props.isRTL ? '0' : space( 1 ) ) }; - margin-right: ${ ( props ) => ( props.isRTL ? space( 1 ) : '0' ) }; + margin-left: ${ () => ( isRTL() ? '0' : space( 1 ) ) }; + margin-right: ${ () => ( isRTL() ? space( 1 ) : '0' ) }; display: inline-flex; padding: ${ space( 0.5 ) } ${ space( 1.5 ) }; border-radius: 2px; @@ -210,7 +215,6 @@ export const ItemBadgeUI = styled.span` `; export const ItemTitleUI = styled( Text )` - ${ ( props ) => - props.isRTL ? 'margin-left: auto;' : 'margin-right: auto;' } + ${ () => ( isRTL() ? 'margin-left: auto;' : 'margin-right: auto;' ) } font-size: 13px; `; diff --git a/packages/components/src/number-control/index.js b/packages/components/src/number-control/index.js index b3b7944123d25a..cbcca8ed0e20f1 100644 --- a/packages/components/src/number-control/index.js +++ b/packages/components/src/number-control/index.js @@ -7,6 +7,7 @@ import classNames from 'classnames'; * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; +import { isRTL } from '@wordpress/i18n'; /** * Internal dependencies @@ -16,7 +17,6 @@ import { inputControlActionTypes, composeStateReducers, } from '../input-control/state'; -import { useRTL } from '../utils/style-mixins'; import { add, subtract, roundClamp } from '../utils/math'; import { useJumpStep } from '../utils/hooks'; import { isValueEmpty } from '../utils/values'; @@ -40,7 +40,6 @@ export function NumberControl( }, ref ) { - const isRtl = useRTL(); const baseValue = roundClamp( 0, min, max, step ); const jumpStep = useJumpStep( { @@ -120,7 +119,7 @@ export function NumberControl( case 'e': directionBaseValue = x; - directionModifier = isRtl ? -1 : 1; + directionModifier = isRTL() ? -1 : 1; break; case 's': @@ -130,7 +129,7 @@ export function NumberControl( case 'w': directionBaseValue = x; - directionModifier = isRtl ? 1 : -1; + directionModifier = isRTL() ? 1 : -1; break; } diff --git a/packages/components/src/popover/README.md b/packages/components/src/popover/README.md index a45dfc701ebcd2..f31c90a56ca262 100644 --- a/packages/components/src/popover/README.md +++ b/packages/components/src/popover/README.md @@ -138,7 +138,7 @@ A callback function which is used to override the anchor value computation algor If you need the `DOMRect` object i.e., the position of popover to be calculated on every time, the popover re-renders, then use `getAnchorRect`. -`getAnchorRect` callback function receives a reference to the popover anchor element as a function parameter and it should return a `DOMRect` objcet. +`getAnchorRect` callback function receives a reference to the popover anchor element as a function parameter and it should return a `DOMRect` object. - Type: `Function` - Required: No diff --git a/packages/components/src/popover/detect-outside.js b/packages/components/src/popover/detect-outside.js deleted file mode 100644 index 92a3359d24055f..00000000000000 --- a/packages/components/src/popover/detect-outside.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * WordPress dependencies - */ -import { Component } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import withFocusOutside from '../higher-order/with-focus-outside'; - -class PopoverDetectOutside extends Component { - handleFocusOutside( event ) { - this.props.onFocusOutside( event ); - } - - render() { - return this.props.children; - } -} - -export default withFocusOutside( PopoverDetectOutside ); diff --git a/packages/components/src/popover/index.js b/packages/components/src/popover/index.js index a71c6cc6ad895f..32382b21f22319 100644 --- a/packages/components/src/popover/index.js +++ b/packages/components/src/popover/index.js @@ -2,6 +2,7 @@ * External dependencies */ import classnames from 'classnames'; +import mergeRefs from 'react-merge-refs'; /** * WordPress dependencies @@ -9,31 +10,30 @@ import classnames from 'classnames'; import { useRef, useState, - useEffect, useLayoutEffect, + useCallback, } from '@wordpress/element'; -import { focus, getRectangleFromRange } from '@wordpress/dom'; +import { getRectangleFromRange } from '@wordpress/dom'; import { ESCAPE } from '@wordpress/keycodes'; import deprecated from '@wordpress/deprecated'; -import { useViewportMatch, useResizeObserver } from '@wordpress/compose'; +import { + useViewportMatch, + useResizeObserver, + useFocusOnMount, + __experimentalUseFocusOutside as useFocusOutside, + useConstrainedTabbing, + useFocusReturn, +} from '@wordpress/compose'; import { close } from '@wordpress/icons'; /** * Internal dependencies */ import { computePopoverPosition } from './utils'; -import withFocusReturn from '../higher-order/with-focus-return'; -import withConstrainedTabbing from '../higher-order/with-constrained-tabbing'; -import PopoverDetectOutside from './detect-outside'; import Button from '../button'; import ScrollLock from '../scroll-lock'; -import IsolatedEventContainer from '../isolated-event-container'; import { Slot, Fill, useSlot } from '../slot-fill'; -import { useAnimate } from '../animate'; - -const FocusManaged = withConstrainedTabbing( - withFocusReturn( ( { children } ) => children ) -); +import { getAnimateClassName } from '../animate'; /** * Name of slot in which popover should fill. @@ -71,11 +71,17 @@ function computeAnchorRect( return; } - if ( anchorRef instanceof window.Range ) { + // Duck-type to check if `anchorRef` is an instance of Range + // `anchorRef instanceof window.Range` checks will break across document boundaries + // such as in an iframe + if ( typeof anchorRef?.cloneRange === 'function' ) { return getRectangleFromRange( anchorRef ); } - if ( anchorRef instanceof window.Element ) { + // Duck-type to check if `anchorRef` is an instance of Element + // `anchorRef instanceof window.Element` checks will break across document boundaries + // such as in an iframe + if ( typeof anchorRef?.getBoundingClientRect === 'function' ) { const rect = anchorRef.getBoundingClientRect(); if ( shouldAnchorIncludePadding ) { @@ -144,53 +150,6 @@ function withoutPadding( rect, element ) { }; } -/** - * Hook used to focus the first tabbable element on mount. - * - * @param {boolean|string} focusOnMount Focus on mount mode. - * @param {Object} contentRef Reference to the popover content element. - */ -function useFocusContentOnMount( focusOnMount, contentRef ) { - // Focus handling - useEffect( () => { - /* - * Without the setTimeout, the dom node is not being focused. Related: - * https://stackoverflow.com/questions/35522220/react-ref-with-focus-doesnt-work-without-settimeout-my-example - * - * TODO: Treat the cause, not the symptom. - */ - const focusTimeout = setTimeout( () => { - if ( ! focusOnMount || ! contentRef.current ) { - return; - } - - if ( focusOnMount === 'firstElement' ) { - // Find first tabbable node within content and shift focus, falling - // back to the popover panel itself. - const firstTabbable = focus.tabbable.find( - contentRef.current - )[ 0 ]; - - if ( firstTabbable ) { - firstTabbable.focus(); - } else { - contentRef.current.focus(); - } - - return; - } - - if ( focusOnMount === 'container' ) { - // Focus the popover panel itself so items in the popover are easily - // accessed via keyboard navigation. - contentRef.current.focus(); - } - }, 0 ); - - return () => clearTimeout( focusTimeout ); - }, [] ); -} - /** * Sets or removes an element attribute. * @@ -266,6 +225,7 @@ const Popover = ( { __unstableSlotName = SLOT_NAME, __unstableObserveElement, __unstableBoundaryParent, + __unstableForcePosition, /* eslint-enable no-unused-vars */ ...contentProps } ) => { @@ -355,7 +315,8 @@ const Popover = ( { __unstableStickyBoundaryElement, containerRef.current, relativeOffsetTop, - boundaryElement + boundaryElement, + __unstableForcePosition ); if ( @@ -456,7 +417,17 @@ const Popover = ( { __unstableBoundaryParent, ] ); - useFocusContentOnMount( focusOnMount, contentRef ); + const constrainedTabbingRef = useConstrainedTabbing(); + const focusReturnRef = useFocusReturn(); + const focusOnMountRef = useFocusOnMount( focusOnMount ); + const focusOutsideProps = useFocusOutside( handleOnFocusOutside ); + const allRefs = [ + containerRef, + focusOnMount ? constrainedTabbingRef : null, + focusOnMount ? focusReturnRef : null, + focusOnMount ? focusOnMountRef : null, + ]; + const mergedRefs = useCallback( mergeRefs( allRefs ), allRefs ); // Event handlers const maybeClose = ( event ) => { @@ -529,64 +500,59 @@ const Popover = ( { onClickOutside( clickEvent ); } - const animateClassName = useAnimate( { - type: animate && animateOrigin ? 'appear' : null, - origin: animateOrigin, - } ); + /** @type {false | string} */ + const animateClassName = + Boolean( animate && animateOrigin ) && + getAnimateClassName( { + type: 'appear', + origin: animateOrigin, + } ); // Disable reason: We care to capture the _bubbled_ events from inputs // within popover as inferring close intent. let content = ( - <PopoverDetectOutside onFocusOutside={ handleOnFocusOutside }> - <IsolatedEventContainer - className={ classnames( - 'components-popover', - className, - animateClassName, - { - 'is-expanded': isExpanded, - 'is-without-arrow': noArrow, - 'is-alternate': isAlternate, - } - ) } - { ...contentProps } - onKeyDown={ maybeClose } - ref={ containerRef } - > - { isExpanded && <ScrollLock /> } - { isExpanded && ( - <div className="components-popover__header"> - <span className="components-popover__header-title"> - { headerTitle } - </span> - <Button - className="components-popover__close" - icon={ close } - onClick={ onClose } - /> - </div> - ) } - <div - ref={ contentRef } - className="components-popover__content" - tabIndex="-1" - > - <div style={ { position: 'relative' } }> - { containerResizeListener } - { children } - </div> + // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions + // eslint-disable-next-line jsx-a11y/no-static-element-interactions + <div + className={ classnames( + 'components-popover', + className, + animateClassName, + { + 'is-expanded': isExpanded, + 'is-without-arrow': noArrow, + 'is-alternate': isAlternate, + } + ) } + { ...contentProps } + onKeyDown={ maybeClose } + { ...focusOutsideProps } + ref={ mergedRefs } + tabIndex="-1" + > + { isExpanded && <ScrollLock /> } + { isExpanded && ( + <div className="components-popover__header"> + <span className="components-popover__header-title"> + { headerTitle } + </span> + <Button + className="components-popover__close" + icon={ close } + onClick={ onClose } + /> </div> - </IsolatedEventContainer> - </PopoverDetectOutside> + ) } + <div ref={ contentRef } className="components-popover__content"> + <div style={ { position: 'relative' } }> + { containerResizeListener } + { children } + </div> + </div> + </div> ); - // Apply focus to element as long as focusOnMount is truthy; false is - // the only "disabled" value. - if ( focusOnMount ) { - content = <FocusManaged>{ content }</FocusManaged>; - } - if ( slot.ref ) { content = <Fill name={ __unstableSlotName }>{ content }</Fill>; } diff --git a/packages/components/src/popover/test/__snapshots__/index.js.snap b/packages/components/src/popover/test/__snapshots__/index.js.snap index 2b8bfdd1807182..e59d209c85b129 100644 --- a/packages/components/src/popover/test/__snapshots__/index.js.snap +++ b/packages/components/src/popover/test/__snapshots__/index.js.snap @@ -3,28 +3,20 @@ exports[`Popover should pass additional props to portaled element 1`] = ` <span> <div + class="components-popover components-animate__appear is-from-top is-from-left is-without-arrow" + data-x-axis="right" + data-y-axis="bottom" + role="tooltip" + style="top: 0px; left: 0px;" tabindex="-1" > - <div> - <div> - <div - class="components-popover components-animate__appear is-from-top is-from-left is-without-arrow" - data-x-axis="right" - data-y-axis="bottom" - role="tooltip" - style="top: 0px; left: 0px;" - > - <div - class="components-popover__content" - tabindex="-1" - > - <div - style="position: relative;" - > - Hello - </div> - </div> - </div> + <div + class="components-popover__content" + > + <div + style="position: relative;" + > + Hello </div> </div> </div> @@ -34,27 +26,19 @@ exports[`Popover should pass additional props to portaled element 1`] = ` exports[`Popover should render content 1`] = ` <span> <div + class="components-popover components-animate__appear is-from-top is-from-left is-without-arrow" + data-x-axis="right" + data-y-axis="bottom" + style="top: 0px; left: 0px;" tabindex="-1" > - <div> - <div> - <div - class="components-popover components-animate__appear is-from-top is-from-left is-without-arrow" - data-x-axis="right" - data-y-axis="bottom" - style="top: 0px; left: 0px;" - > - <div - class="components-popover__content" - tabindex="-1" - > - <div - style="position: relative;" - > - Hello - </div> - </div> - </div> + <div + class="components-popover__content" + > + <div + style="position: relative;" + > + Hello </div> </div> </div> diff --git a/packages/components/src/popover/test/index.js b/packages/components/src/popover/test/index.js index d46c54cd03a94f..20d211a5b9273f 100644 --- a/packages/components/src/popover/test/index.js +++ b/packages/components/src/popover/test/index.js @@ -34,7 +34,7 @@ describe( 'Popover', () => { } ); expect( document.activeElement ).toBe( - result.container.querySelector( '.components-popover__content' ) + result.container.querySelector( '.components-popover' ) ); } ); diff --git a/packages/components/src/popover/test/utils.js b/packages/components/src/popover/test/utils.js index 131be264a4c1e6..1a97a148a50a26 100644 --- a/packages/components/src/popover/test/utils.js +++ b/packages/components/src/popover/test/utils.js @@ -154,7 +154,7 @@ describe( 'computePopoverXAxisPosition', () => { } ); } ); - it( "should set a maxWidth if there's not enough space in any direction", () => { + it( "should center popover if there's not enough space in any direction", () => { const anchorRect = { top: 10, left: 400, @@ -172,9 +172,33 @@ describe( 'computePopoverXAxisPosition', () => { expect( computePopoverXAxisPosition( anchorRect, contentSize, 'right' ) ).toEqual( { - contentWidth: 614, - popoverLeft: 410, - xAxis: 'right', + contentWidth: null, + popoverLeft: 512, + xAxis: 'center', + } ); + } ); + + it( 'should set the content width to the viewport width if content is too wide', () => { + const anchorRect = { + top: 10, + left: 400, + bottom: 30, + right: 420, + width: 20, + height: 20, + }; + + const contentSize = { + width: 1500, + height: 300, + }; + + expect( + computePopoverXAxisPosition( anchorRect, contentSize, 'right' ) + ).toEqual( { + contentWidth: 1024, + popoverLeft: 512, + xAxis: 'center', } ); } ); } ); diff --git a/packages/components/src/popover/utils.js b/packages/components/src/popover/utils.js index 196b085c259a99..e807274b774826 100644 --- a/packages/components/src/popover/utils.js +++ b/packages/components/src/popover/utils.js @@ -1,3 +1,8 @@ +/** + * WordPress dependencies + */ +import { isRTL } from '@wordpress/i18n'; + /** * Module constants */ @@ -15,6 +20,7 @@ const HEIGHT_OFFSET = 10; // used by the arrow and a bit of empty space * position. * @param {string} chosenYAxis yAxis to be used. * @param {Element} boundaryElement Boundary element. + * @param {boolean} forcePosition Don't adjust position based on anchor. * * @return {Object} Popover xAxis position and constraints. */ @@ -25,21 +31,21 @@ export function computePopoverXAxisPosition( corner, stickyBoundaryElement, chosenYAxis, - boundaryElement + boundaryElement, + forcePosition ) { const { width } = contentSize; - const isRTL = document.documentElement.dir === 'rtl'; // Correct xAxis for RTL support - if ( xAxis === 'left' && isRTL ) { + if ( xAxis === 'left' && isRTL() ) { xAxis = 'right'; - } else if ( xAxis === 'right' && isRTL ) { + } else if ( xAxis === 'right' && isRTL() ) { xAxis = 'left'; } - if ( corner === 'left' && isRTL ) { + if ( corner === 'left' && isRTL() ) { corner = 'right'; - } else if ( corner === 'right' && isRTL ) { + } else if ( corner === 'right' && isRTL() ) { corner = 'left'; } @@ -86,7 +92,7 @@ export function computePopoverXAxisPosition( let chosenXAxis = xAxis; let contentWidth = null; - if ( ! stickyBoundaryElement ) { + if ( ! stickyBoundaryElement && ! forcePosition ) { if ( xAxis === 'center' && centerAlignment.contentWidth === width ) { chosenXAxis = 'center'; } else if ( xAxis === 'left' && leftAlignment.contentWidth === width ) { @@ -105,7 +111,18 @@ export function computePopoverXAxisPosition( chosenXAxis === 'left' ? leftAlignment.contentWidth : rightAlignment.contentWidth; - contentWidth = chosenWidth !== width ? chosenWidth : null; + + // Limit width of the content to the viewport width + if ( width > window.innerWidth ) { + contentWidth = window.innerWidth; + } + + // If we can't find any alignment options that could fit + // our content, then let's fallback to the center of the viewport. + if ( chosenWidth !== width ) { + chosenXAxis = 'center'; + centerAlignment.popoverLeft = window.innerWidth / 2; + } } } @@ -143,6 +160,7 @@ export function computePopoverXAxisPosition( * @param {Element} anchorRef The anchor element. * @param {Element} relativeOffsetTop If applicable, top offset of the * relative positioned parent container. + * @param {boolean} forcePosition Don't adjust position based on anchor. * * @return {Object} Popover xAxis position and constraints. */ @@ -153,7 +171,8 @@ export function computePopoverYAxisPosition( corner, stickyBoundaryElement, anchorRef, - relativeOffsetTop + relativeOffsetTop, + forcePosition ) { const { height } = contentSize; @@ -206,7 +225,7 @@ export function computePopoverYAxisPosition( let chosenYAxis = yAxis; let contentHeight = null; - if ( ! stickyBoundaryElement ) { + if ( ! stickyBoundaryElement && ! forcePosition ) { if ( yAxis === 'middle' && middleAlignment.contentHeight === height ) { chosenYAxis = 'middle'; } else if ( yAxis === 'top' && topAlignment.contentHeight === height ) { @@ -259,6 +278,7 @@ export function computePopoverYAxisPosition( * @param {number} relativeOffsetTop If applicable, top offset of the * relative positioned parent container. * @param {Element} boundaryElement Boundary element. + * @param {boolean} forcePosition Don't adjust position based on anchor. * * @return {Object} Popover position and constraints. */ @@ -269,7 +289,8 @@ export function computePopoverPosition( stickyBoundaryElement, anchorRef, relativeOffsetTop, - boundaryElement + boundaryElement, + forcePosition ) { const [ yAxis, xAxis = 'center', corner ] = position.split( ' ' ); @@ -280,7 +301,8 @@ export function computePopoverPosition( corner, stickyBoundaryElement, anchorRef, - relativeOffsetTop + relativeOffsetTop, + forcePosition ); const xAxisPosition = computePopoverXAxisPosition( anchorRect, @@ -289,7 +311,8 @@ export function computePopoverPosition( corner, stickyBoundaryElement, yAxisPosition.yAxis, - boundaryElement + boundaryElement, + forcePosition ); return { diff --git a/packages/components/src/range-control/README.md b/packages/components/src/range-control/README.md index f5e38538369823..0ebf28bb67a1eb 100644 --- a/packages/components/src/range-control/README.md +++ b/packages/components/src/range-control/README.md @@ -213,7 +213,7 @@ const MyRangeControl() { #### onChange -A function that receives the new value. The value will be less than `max` and more than `min` unless a reset (enabled by `allowReset`) has occured. In which case the value will be either that of `resetFallbackValue` if it has been specified or otherwise `undefined`. +A function that receives the new value. The value will be less than `max` and more than `min` unless a reset (enabled by `allowReset`) has occurred. In which case the value will be either that of `resetFallbackValue` if it has been specified or otherwise `undefined`. - Type: `function` - Required: Yes diff --git a/packages/components/src/range-control/index.js b/packages/components/src/range-control/index.js index 90f55c3c0cd555..976ec27b40cc97 100644 --- a/packages/components/src/range-control/index.js +++ b/packages/components/src/range-control/index.js @@ -7,7 +7,7 @@ import { clamp, isFinite, noop } from 'lodash'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, isRTL } from '@wordpress/i18n'; import { useRef, useState, forwardRef } from '@wordpress/element'; import { useInstanceId } from '@wordpress/compose'; @@ -33,7 +33,6 @@ import { Thumb, Wrapper, } from './styles/range-control-styles'; -import { useRTL } from '../utils/rtl'; function RangeControl( { @@ -69,8 +68,6 @@ function RangeControl( }, ref ) { - const isRTL = useRTL(); - const [ value, setValue ] = useControlledRangeValue( { min, max, @@ -99,9 +96,7 @@ function RangeControl( const inputSliderValue = isValueReset ? '' : currentValue; - const rangeFillValue = isValueReset - ? floatClamp( max / 2, min, max ) - : value; + const rangeFillValue = isValueReset ? ( max - min ) / 2 + min : value; const calculatedFillValue = ( ( value - min ) / ( max - min ) ) * 100; const fillValue = isValueReset ? 50 : calculatedFillValue; @@ -192,7 +187,7 @@ function RangeControl( }; const offsetStyle = { - [ isRTL ? 'right' : 'left' ]: fillValueOffset, + [ isRTL() ? 'right' : 'left' ]: fillValueOffset, }; return ( @@ -202,7 +197,7 @@ function RangeControl( id={ id } help={ help } > - <Root className="components-range-control__root" isRTL={ isRTL }> + <Root className="components-range-control__root"> { beforeIcon && ( <BeforeIconWrapper> <Icon icon={ beforeIcon } /> diff --git a/packages/components/src/range-control/rail.js b/packages/components/src/range-control/rail.js index 93db3351c68121..8f1794883e2dd5 100644 --- a/packages/components/src/range-control/rail.js +++ b/packages/components/src/range-control/rail.js @@ -1,3 +1,8 @@ +/** + * WordPress dependencies + */ +import { isRTL } from '@wordpress/i18n'; + /** * Internal dependencies */ @@ -58,39 +63,37 @@ function Marks( { } function useMarks( { marks, min = 0, max = 100, step = 1, value = 0 } ) { - const isRTL = document.documentElement.dir === 'rtl'; - if ( ! marks ) { return []; } - const isCustomMarks = Array.isArray( marks ); - - const markCount = Math.round( ( max - min ) / step ); - const marksArray = isCustomMarks - ? marks - : [ ...Array( markCount + 1 ) ].map( ( _, index ) => ( { - value: index, - } ) ); - - const enhancedMarks = marksArray.map( ( mark, index ) => { - const markValue = mark.value !== undefined ? mark.value : value; + const range = max - min; + if ( ! Array.isArray( marks ) ) { + marks = []; + const count = 1 + Math.round( range / step ); + while ( count > marks.push( { value: step * marks.length + min } ) ); + } + const placedMarks = []; + marks.forEach( ( mark, index ) => { + if ( mark.value < min || mark.value > max ) { + return; + } const key = `mark-${ index }`; - const isFilled = markValue * step + min <= value; - const offset = `${ ( markValue / markCount ) * 100 }%`; + const isFilled = mark.value <= value; + const offset = `${ ( ( mark.value - min ) / range ) * 100 }%`; const offsetStyle = { - [ isRTL ? 'right' : 'left' ]: offset, + [ isRTL() ? 'right' : 'left' ]: offset, }; - return { + placedMarks.push( { ...mark, isFilled, key, style: offsetStyle, - }; + } ); } ); - return enhancedMarks; + return placedMarks; } diff --git a/packages/components/src/range-control/stories/index.js b/packages/components/src/range-control/stories/index.js index d3dca6a1ca1a35..f65983161c5a3b 100644 --- a/packages/components/src/range-control/stories/index.js +++ b/packages/components/src/range-control/stories/index.js @@ -7,7 +7,7 @@ import { boolean, number, text } from '@storybook/addon-knobs'; /** * WordPress dependencies */ -import { useEffect, useState } from '@wordpress/element'; +import { useState } from '@wordpress/element'; /** * Internal dependencies @@ -25,7 +25,6 @@ const RangeControlWithState = ( props ) => { }; const DefaultExample = () => { - const [ isRtl, setIsRtl ] = useState( false ); const [ value, setValue ] = useState( undefined ); const props = { @@ -48,26 +47,6 @@ const DefaultExample = () => { onChange: setValue, }; - const rtl = boolean( 'RTL', false ); - - useEffect( () => { - if ( rtl !== isRtl ) { - setIsRtl( rtl ); - } - }, [ rtl, isRtl ] ); - - useEffect( () => { - if ( isRtl ) { - document.documentElement.setAttribute( 'dir', 'rtl' ); - } else { - document.documentElement.setAttribute( 'dir', 'ltr' ); - } - - return () => { - document.documentElement.setAttribute( 'dir', 'ltr' ); - }; - }, [ isRtl ] ); - return ( <Wrapper> <RangeControl { ...props } /> @@ -75,6 +54,11 @@ const DefaultExample = () => { ); }; +const RangeControlLabeledByMarksType = ( props ) => { + const label = Array.isArray( props.marks ) ? 'Custom' : 'Automatic'; + return <RangeControl { ...{ ...props, label } } />; +}; + export const _default = () => { return <DefaultExample />; }; @@ -131,33 +115,51 @@ export const withReset = () => { return <RangeControlWithState label={ label } allowReset />; }; -export const customMarks = () => { - const marks = [ - { - value: 0, - label: '0', - }, - { - value: 1, - label: '1', - }, - { - value: 2, - label: '2', - }, - { - value: 8, - label: '8', - }, - { - value: 10, - label: '10', - }, +export const marks = () => { + const marksBase = [ + { value: 0, label: '0' }, + { value: 1, label: '1' }, + { value: 2, label: '2' }, + { value: 8, label: '8' }, + { value: 10, label: '10' }, ]; + const marksWithDecimal = [ + ...marksBase, + { value: 3.5, label: '3.5' }, + { value: 5.8, label: '5.8' }, + ]; + const marksWithNegatives = [ + ...marksBase, + { value: -1, label: '-1' }, + { value: -2, label: '-2' }, + { value: -4, label: '-4' }, + { value: -8, label: '-8' }, + ]; + const stepInteger = { min: 0, max: 10, step: 1 }; + const stepDecimal = { min: 0, max: 10, step: 0.1 }; + const minNegative = { min: -10, max: 10, step: 1 }; + const rangeNegative = { min: -10, max: -1, step: 1 }; + + // use a short alias to keep formatting to fewer lines + const Range = RangeControlLabeledByMarksType; return ( <Wrapper> - <RangeControl marks={ marks } min={ 0 } max={ 10 } step={ 1 } /> + <h2>Integer Step</h2> + <Range marks { ...stepInteger } /> + <Range marks={ marksBase } { ...stepInteger } /> + + <h2>Decimal Step</h2> + <Range marks { ...stepDecimal } /> + <Range marks={ marksWithDecimal } { ...stepDecimal } /> + + <h2>Negative Minimum</h2> + <Range marks { ...minNegative } /> + <Range marks={ marksWithNegatives } { ...minNegative } /> + + <h2>Negative Range</h2> + <Range marks { ...rangeNegative } /> + <Range marks={ marksWithNegatives } { ...rangeNegative } /> </Wrapper> ); }; diff --git a/packages/components/src/range-control/styles/range-control-styles.js b/packages/components/src/range-control/styles/range-control-styles.js index 0fa3fbbeb233d9..2b4d0f94633670 100644 --- a/packages/components/src/range-control/styles/range-control-styles.js +++ b/packages/components/src/range-control/styles/range-control-styles.js @@ -11,11 +11,11 @@ import NumberControl from '../../number-control'; import { color, reduceMotion, rtl, space } from '../../utils/style-mixins'; const rangeHeight = () => css( { height: 30, minHeight: 30 } ); +const thumbSize = 20; -export const Root = styled.span` +export const Root = styled.div` -webkit-tap-highlight-color: transparent; box-sizing: border-box; - cursor: pointer; align-items: flex-start; display: inline-flex; justify-content: flex-start; @@ -31,7 +31,7 @@ const wrapperColor = ( { color: colorProp = color( 'ui.borderFocus' ) } ) => { const wrapperMargin = ( { marks } ) => css( { marginBottom: marks ? 16 : null } ); -export const Wrapper = styled.span` +export const Wrapper = styled.div` box-sizing: border-box; color: ${ color( 'blue.medium.focus' ) }; display: block; @@ -115,6 +115,7 @@ export const Track = styled.span` export const MarksWrapper = styled.span` box-sizing: border-box; display: block; + pointer-events: none; position: relative; width: 100%; user-select: none; @@ -166,7 +167,7 @@ export const ThumbWrapper = styled.span` align-items: center; box-sizing: border-box; display: flex; - height: 20px; + height: ${ thumbSize }px; justify-content: center; margin-top: 5px; outline: 0; @@ -174,7 +175,7 @@ export const ThumbWrapper = styled.span` position: absolute; top: 0; user-select: none; - width: 20px; + width: ${ thumbSize }px; ${ rtl( { marginLeft: -10 } ) } `; @@ -202,7 +203,6 @@ export const Thumb = styled.span` box-sizing: border-box; height: 100%; outline: 0; - pointer-events: none; position: absolute; user-select: none; width: 100%; @@ -216,13 +216,13 @@ export const InputRange = styled.input` display: block; height: 100%; left: 0; - margin: 0; + margin: 0 -${ thumbSize / 2 }px; opacity: 0; outline: none; position: absolute; right: 0; top: 0; - width: 100%; + width: calc( 100% + ${ thumbSize }px ); `; const tooltipShow = ( { show } ) => { diff --git a/packages/components/src/resizable-box/resize-tooltip/label.js b/packages/components/src/resizable-box/resize-tooltip/label.js index b63e4fd9e50c62..d95c73f335daff 100644 --- a/packages/components/src/resizable-box/resize-tooltip/label.js +++ b/packages/components/src/resizable-box/resize-tooltip/label.js @@ -2,12 +2,12 @@ * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; +import { isRTL } from '@wordpress/i18n'; /** * Internal dependencies */ import { POSITIONS } from './utils'; -import { useRTL } from '../../utils/style-mixins'; import { TooltipWrapper, Tooltip, @@ -21,8 +21,6 @@ function Label( { label, position = POSITIONS.corner, zIndex = 1000, ...props }, ref ) { - const isRTL = useRTL(); - const showLabel = !! label; const isBottom = position === POSITIONS.bottom; @@ -56,8 +54,8 @@ function Label( ...style, position: 'absolute', top: CORNER_OFFSET, - right: isRTL ? null : CORNER_OFFSET, - left: isRTL ? CORNER_OFFSET : null, + right: isRTL() ? null : CORNER_OFFSET, + left: isRTL() ? CORNER_OFFSET : null, }; } diff --git a/packages/components/src/snackbar/index.js b/packages/components/src/snackbar/index.js index e667676218a838..84f78aee2ffd9a 100644 --- a/packages/components/src/snackbar/index.js +++ b/packages/components/src/snackbar/index.js @@ -48,19 +48,52 @@ function Snackbar( actions = [], onRemove = noop, icon = null, + explicitDismiss = false, + // onDismiss is a callback executed when the snackbar is dismissed. + // It is distinct from onRemove, which _looks_ like a callback but is + // actually the function to call to remove the snackbar from the UI. + onDismiss = noop, }, ref ) { + onDismiss = onDismiss || noop; + + function dismissMe( event ) { + if ( event && event.preventDefault ) { + event.preventDefault(); + } + + onDismiss(); + onRemove(); + } + + function onActionClick( event, onClick ) { + event.stopPropagation(); + + onRemove(); + + if ( onClick ) { + onClick( event ); + } + } + useSpokenMessage( spokenMessage, politeness ); + + // Only set up the timeout dismiss if we're not explicitly dismissing. useEffect( () => { const timeoutHandle = setTimeout( () => { - onRemove(); + if ( ! explicitDismiss ) { + onDismiss(); + onRemove(); + } }, NOTICE_TIMEOUT ); return () => clearTimeout( timeoutHandle ); - }, [] ); + }, [ onDismiss, onRemove ] ); - const classes = classnames( className, 'components-snackbar' ); + const classes = classnames( className, 'components-snackbar', { + 'components-snackbar-explicit-dismiss': !! explicitDismiss, + } ); if ( actions && actions.length > 1 ) { // we need to inform developers that snackbar only accepts 1 action warning( @@ -81,11 +114,11 @@ function Snackbar( <div ref={ ref } className={ classes } - onClick={ onRemove } + onClick={ ! explicitDismiss ? dismissMe : noop } tabIndex="0" - role="button" - onKeyPress={ onRemove } - aria-label={ __( 'Dismiss this notice' ) } + role={ ! explicitDismiss ? 'button' : '' } + onKeyPress={ ! explicitDismiss ? dismissMe : noop } + aria-label={ ! explicitDismiss ? __( 'Dismiss this notice' ) : '' } > <div className={ snackbarContentClassnames }> { icon && ( @@ -98,18 +131,27 @@ function Snackbar( key={ index } href={ url } isTertiary - onClick={ ( event ) => { - event.stopPropagation(); - if ( onClick ) { - onClick( event ); - } - } } + onClick={ ( event ) => + onActionClick( event, onClick ) + } className="components-snackbar__action" > { label } </Button> ); } ) } + { explicitDismiss && ( + <span + role="button" + aria-label="Dismiss this notice" + tabIndex="0" + className="components-snackbar__dismiss-button" + onClick={ dismissMe } + onKeyPress={ dismissMe } + > + &#x2715; + </span> + ) } </div> </div> ); diff --git a/packages/components/src/snackbar/stories/index.js b/packages/components/src/snackbar/stories/index.js index 6de47437251bcc..bf634c2131ebc3 100644 --- a/packages/components/src/snackbar/stories/index.js +++ b/packages/components/src/snackbar/stories/index.js @@ -53,3 +53,31 @@ export const withIcon = () => { </Snackbar> ); }; + +export const withExplicitDismiss = () => { + const content = text( + 'Content', + 'Add a cross to explicitly close the snackbar, and do not hide it automatically' + ); + + return <Snackbar explicitDismiss={ true }>{ content }</Snackbar>; +}; + +export const withActionAndExpicitDismiss = () => { + const content = text( + 'Content', + 'Add an action and a cross to explicitly close the snackbar, and do not hide it automatically' + ); + const actions = [ + { + label: text( 'Label', 'Open WP.org' ), + url: text( 'URL', 'https://wordpress.org' ), + }, + ]; + + return ( + <Snackbar actions={ actions } explicitDismiss={ true }> + { content } + </Snackbar> + ); +}; diff --git a/packages/components/src/snackbar/style.scss b/packages/components/src/snackbar/style.scss index 88f3dcfb19f0e8..bfc5c9b7480ace 100644 --- a/packages/components/src/snackbar/style.scss +++ b/packages/components/src/snackbar/style.scss @@ -21,14 +21,23 @@ 0 0 0 3px var(--wp-admin-theme-color); } + &.components-snackbar-explicit-dismiss { + cursor: default; + } + .components-snackbar__content-with-icon { - margin-left: 32px; + margin-left: 24px; } .components-snackbar__icon { position: absolute; top: 24px; - left: 26px; + left: 28px; + } + + .components-snackbar__dismiss-button { + margin-left: 32px; + cursor: pointer; } } diff --git a/packages/components/src/text-highlight/README.md b/packages/components/src/text-highlight/README.md index e8a1e4be5d8429..91ed7021fd4628 100644 --- a/packages/components/src/text-highlight/README.md +++ b/packages/components/src/text-highlight/README.md @@ -1,12 +1,12 @@ # TextHighlight -Highlights occurances of a given string within another string of text. Wraps each match with a [`<mark>` tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark) which provides browser default styling. +Highlights occurrences of a given string within another string of text. Wraps each match with a [`<mark>` tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark) which provides browser default styling. ## Usage -Pass in the `text` and the `highlight` string to be matched against. +Pass in the `text` and the `highlight` string to be matched against. -In the example below, the string `Gutenberg` would be highlighted twice. +In the example below, the string `Gutenberg` would be highlighted twice. ```jsx import { TextHighlight } from '@wordpress/components'; @@ -21,11 +21,11 @@ const MyTextHighlight = () => ( ## Props -The component accepts the following props. +The component accepts the following props. ### text -The string of text to be tested for occurances of then given `highlight`. +The string of text to be tested for occurrences of then given `highlight`. - Type: `String` - Required: Yes diff --git a/packages/components/src/toolbar-button/README.md b/packages/components/src/toolbar-button/README.md index 5d7f8f1fb19551..1ddccac0c75fa0 100644 --- a/packages/components/src/toolbar-button/README.md +++ b/packages/components/src/toolbar-button/README.md @@ -27,7 +27,7 @@ function MyToolbar() { ### Inside BlockControls -If you're working on a custom block and you want to add controls to the block toolbar, you should use [BlockControls](/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md) instead. Optinally wrapping it with [ToolbarGroup](/packages/components/src/toolbar-group/README.md). +If you're working on a custom block and you want to add controls to the block toolbar, you should use [BlockControls](/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md) instead. Optionally wrapping it with [ToolbarGroup](/packages/components/src/toolbar-group/README.md). ```jsx import { BlockControls } from '@wordpress/block-editor'; diff --git a/packages/components/src/toolbar-item/README.md b/packages/components/src/toolbar-item/README.md index db6d38da041688..1df563778a0e11 100644 --- a/packages/components/src/toolbar-item/README.md +++ b/packages/components/src/toolbar-item/README.md @@ -49,7 +49,7 @@ function MyToolbar() { ### Inside BlockControls -If you're working on a custom block and you want to add controls to the block toolbar, you should use [BlockControls](/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md) instead. Optinally wrapping it with [ToolbarGroup](/packages/components/src/toolbar-group/README.md). +If you're working on a custom block and you want to add controls to the block toolbar, you should use [BlockControls](/docs/designers-developers/developers/tutorials/block-tutorial/block-controls-toolbar-and-sidebar.md) instead. Optionally wrapping it with [ToolbarGroup](/packages/components/src/toolbar-group/README.md). ```jsx import { BlockControls } from '@wordpress/block-editor'; diff --git a/packages/components/src/toolbar/toolbar-container.js b/packages/components/src/toolbar/toolbar-container.js index df4f29c5afc799..4e39fb0990677b 100644 --- a/packages/components/src/toolbar/toolbar-container.js +++ b/packages/components/src/toolbar/toolbar-container.js @@ -7,12 +7,12 @@ import { useToolbarState, Toolbar } from 'reakit/Toolbar'; * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; +import { isRTL } from '@wordpress/i18n'; /** * Internal dependencies */ import ToolbarContext from '../toolbar-context'; -import { getRTL } from '../utils/rtl'; function ToolbarContainer( { label, ...props }, ref ) { // https://reakit.io/docs/basic-concepts/#state-hooks @@ -21,7 +21,7 @@ function ToolbarContainer( { label, ...props }, ref ) { const toolbarState = useToolbarState( { loop: true, baseId: props.id, - rtl: getRTL(), + rtl: isRTL(), } ); return ( diff --git a/packages/components/src/tooltip/index.js b/packages/components/src/tooltip/index.js index 28508d4d483d28..adcebdab9dcd9c 100644 --- a/packages/components/src/tooltip/index.js +++ b/packages/components/src/tooltip/index.js @@ -1,16 +1,17 @@ /** * External dependencies */ -import { debounce, includes } from 'lodash'; +import { includes } from 'lodash'; /** * WordPress dependencies */ import { - Component, Children, cloneElement, concatChildren, + useEffect, + useState, } from '@wordpress/element'; /** @@ -18,66 +19,72 @@ import { */ import Popover from '../popover'; import Shortcut from '../shortcut'; +import { useDebounce } from '@wordpress/compose'; /** * Time over children to wait before showing tooltip * * @type {number} */ -const TOOLTIP_DELAY = 700; - -class Tooltip extends Component { - constructor() { - super( ...arguments ); - - this.delayedSetIsOver = debounce( - ( isOver ) => this.setState( { isOver } ), - TOOLTIP_DELAY - ); - - /** - * Prebound `isInMouseDown` handler, created as a constant reference to - * assure ability to remove in component unmount. - * - * @type {Function} - */ - this.cancelIsMouseDown = this.createSetIsMouseDown( false ); - - /** - * Whether a the mouse is currently pressed, used in determining whether - * to handle a focus event as displaying the tooltip immediately. - * - * @type {boolean} - */ - this.isInMouseDown = false; - - this.state = { - isOver: false, - }; - } +export const TOOLTIP_DELAY = 700; - componentWillUnmount() { - this.delayedSetIsOver.cancel(); +const emitToChild = ( children, eventName, event ) => { + if ( Children.count( children ) !== 1 ) { + return; + } - document.removeEventListener( 'mouseup', this.cancelIsMouseDown ); + const child = Children.only( children ); + if ( typeof child.props[ eventName ] === 'function' ) { + child.props[ eventName ]( event ); } +}; - emitToChild( eventName, event ) { - const { children } = this.props; - if ( Children.count( children ) !== 1 ) { - return; - } +function Tooltip( { children, position, text, shortcut } ) { + /** + * Whether a mouse is currently pressed, used in determining whether + * to handle a focus event as displaying the tooltip immediately. + * + * @type {boolean} + */ + const [ isMouseDown, setIsMouseDown ] = useState( false ); + const [ isOver, setIsOver ] = useState( false ); + const delayedSetIsOver = useDebounce( setIsOver, TOOLTIP_DELAY ); + + const createMouseDown = ( event ) => { + // Preserve original child callback behavior + emitToChild( children, 'onMouseDown', event ); + + // On mouse down, the next `mouseup` should revert the value of the + // instance property and remove its own event handler. The bind is + // made on the document since the `mouseup` might not occur within + // the bounds of the element. + document.addEventListener( 'mouseup', cancelIsMouseDown ); + setIsMouseDown( true ); + }; + + const createMouseUp = ( event ) => { + emitToChild( children, 'onMouseUp', event ); + document.removeEventListener( 'mouseup', cancelIsMouseDown ); + setIsMouseDown( false ); + }; + + const createMouseEvent = ( type ) => { + if ( type === 'mouseUp' ) return createMouseUp; + if ( type === 'mouseDown' ) return createMouseDown; + }; - const child = Children.only( children ); - if ( typeof child.props[ eventName ] === 'function' ) { - child.props[ eventName ]( event ); - } - } + /** + * Prebound `isInMouseDown` handler, created as a constant reference to + * assure ability to remove in component unmount. + * + * @type {Function} + */ + const cancelIsMouseDown = createMouseEvent( 'mouseUp' ); - createToggleIsOver( eventName, isDelayed ) { + const createToggleIsOver = ( eventName, isDelayed ) => { return ( event ) => { // Preserve original child callback behavior - this.emitToChild( eventName, event ); + emitToChild( children, eventName, event ); // Mouse events behave unreliably in React for disabled elements, // firing on mouseenter but not mouseleave. Further, the default @@ -92,99 +99,71 @@ class Tooltip extends Component { // A focus event will occur as a result of a mouse click, but it // should be disambiguated between interacting with the button and // using an explicit focus shift as a cue to display the tooltip. - if ( 'focus' === event.type && this.isInMouseDown ) { + if ( 'focus' === event.type && isMouseDown ) { return; } // Needed in case unsetting is over while delayed set pending, i.e. // quickly blur/mouseleave before delayedSetIsOver is called - this.delayedSetIsOver.cancel(); + delayedSetIsOver.cancel(); - const isOver = includes( [ 'focus', 'mouseenter' ], event.type ); - if ( isOver === this.state.isOver ) { + const _isOver = includes( [ 'focus', 'mouseenter' ], event.type ); + if ( _isOver === isOver ) { return; } if ( isDelayed ) { - this.delayedSetIsOver( isOver ); + delayedSetIsOver( _isOver ); } else { - this.setState( { isOver } ); + setIsOver( _isOver ); } }; - } - - /** - * Creates an event callback to handle assignment of the `isInMouseDown` - * instance property in response to a `mousedown` or `mouseup` event. - * - * @param {boolean} isMouseDown Whether handler is to be created for the - * `mousedown` event, as opposed to `mouseup`. - * - * @return {Function} Event callback handler. - */ - createSetIsMouseDown( isMouseDown ) { - return ( event ) => { - // Preserve original child callback behavior - this.emitToChild( - isMouseDown ? 'onMouseDown' : 'onMouseUp', - event + }; + const clearOnUnmount = () => { + delayedSetIsOver.cancel(); + }; + + useEffect( () => clearOnUnmount, [] ); + + if ( Children.count( children ) !== 1 ) { + if ( 'development' === process.env.NODE_ENV ) { + // eslint-disable-next-line no-console + console.error( + 'Tooltip should be called with only a single child element.' ); - - // On mouse down, the next `mouseup` should revert the value of the - // instance property and remove its own event handler. The bind is - // made on the document since the `mouseup` might not occur within - // the bounds of the element. - document[ - isMouseDown ? 'addEventListener' : 'removeEventListener' - ]( 'mouseup', this.cancelIsMouseDown ); - - this.isInMouseDown = isMouseDown; - }; - } - - render() { - const { children, position, text, shortcut } = this.props; - if ( Children.count( children ) !== 1 ) { - if ( 'development' === process.env.NODE_ENV ) { - // eslint-disable-next-line no-console - console.error( - 'Tooltip should be called with only a single child element.' - ); - } - - return children; } - const child = Children.only( children ); - const { isOver } = this.state; - return cloneElement( child, { - onMouseEnter: this.createToggleIsOver( 'onMouseEnter', true ), - onMouseLeave: this.createToggleIsOver( 'onMouseLeave' ), - onClick: this.createToggleIsOver( 'onClick' ), - onFocus: this.createToggleIsOver( 'onFocus' ), - onBlur: this.createToggleIsOver( 'onBlur' ), - onMouseDown: this.createSetIsMouseDown( true ), - children: concatChildren( - child.props.children, - isOver && ( - <Popover - focusOnMount={ false } - position={ position } - className="components-tooltip" - aria-hidden="true" - animate={ false } - noArrow={ true } - > - { text } - <Shortcut - className="components-tooltip__shortcut" - shortcut={ shortcut } - /> - </Popover> - ) - ), - } ); + return children; } + + const child = Children.only( children ); + return cloneElement( child, { + onMouseEnter: createToggleIsOver( 'onMouseEnter', true ), + onMouseLeave: createToggleIsOver( 'onMouseLeave' ), + onClick: createToggleIsOver( 'onClick' ), + onFocus: createToggleIsOver( 'onFocus' ), + onBlur: createToggleIsOver( 'onBlur' ), + onMouseDown: createMouseEvent( 'mouseDown' ), + children: concatChildren( + child.props.children, + isOver && ( + <Popover + focusOnMount={ false } + position={ position } + className="components-tooltip" + aria-hidden="true" + animate={ false } + noArrow={ true } + > + { text } + <Shortcut + className="components-tooltip__shortcut" + shortcut={ shortcut } + /> + </Popover> + ) + ), + } ); } export default Tooltip; diff --git a/packages/components/src/tooltip/test/index.js b/packages/components/src/tooltip/test/index.js index 1bfc61b484ddb5..919dc70e3b9b30 100644 --- a/packages/components/src/tooltip/test/index.js +++ b/packages/components/src/tooltip/test/index.js @@ -2,13 +2,16 @@ * External dependencies */ import { shallow, mount } from 'enzyme'; -import TestUtils from 'react-dom/test-utils'; -import ReactDOM from 'react-dom'; /** * Internal dependencies */ import Tooltip from '../'; +/** + * WordPress dependencies + */ +import { TOOLTIP_DELAY } from '../index.js'; +import { act } from '@testing-library/react'; describe( 'Tooltip', () => { describe( '#render()', () => { @@ -44,7 +47,8 @@ describe( 'Tooltip', () => { </Tooltip> ); - wrapper.setState( { isOver: true } ); + const event = { type: 'focus', currentTarget: {} }; + wrapper.simulate( 'focus', event ); const button = wrapper.find( 'button' ); const popover = wrapper.find( 'Popover' ); @@ -76,11 +80,10 @@ describe( 'Tooltip', () => { const popover = wrapper.find( 'Popover' ); expect( originalFocus ).toHaveBeenCalledWith( event ); - expect( wrapper.state( 'isOver' ) ).toBe( true ); expect( popover ).toHaveLength( 1 ); } ); - it( 'should show not popover on focus as result of mousedown', () => { + it( 'should show not popover on focus as result of mousedown', async () => { const originalOnMouseDown = jest.fn(); const originalOnMouseUp = jest.fn(); const wrapper = mount( @@ -110,11 +113,10 @@ describe( 'Tooltip', () => { button.simulate( event.type, event ); const popover = wrapper.find( 'Popover' ); - expect( wrapper.state( 'isOver' ) ).toBe( false ); expect( popover ).toHaveLength( 0 ); event = new window.MouseEvent( 'mouseup' ); - document.dispatchEvent( event ); + await act( async () => document.dispatchEvent( event ) ); expect( originalOnMouseUp ).toHaveBeenCalledWith( expect.objectContaining( { type: event.type, @@ -124,7 +126,8 @@ describe( 'Tooltip', () => { it( 'should show popover on delayed mouseenter', () => { const originalMouseEnter = jest.fn(); - const wrapper = TestUtils.renderIntoDocument( + jest.useFakeTimers(); + const wrapper = mount( <Tooltip text="Help text"> <button onMouseEnter={ originalMouseEnter } @@ -135,32 +138,18 @@ describe( 'Tooltip', () => { </Tooltip> ); - const button = TestUtils.findRenderedDOMComponentWithTag( - wrapper, - 'button' - ); - // eslint-disable-next-line react/no-find-dom-node - TestUtils.Simulate.mouseEnter( ReactDOM.findDOMNode( button ) ); + const button = wrapper.find( 'button' ); + button.simulate( 'mouseenter', { type: 'mouseenter' } ); + const popoverBeforeTimeout = wrapper.find( 'Popover' ); + expect( popoverBeforeTimeout ).toHaveLength( 0 ); expect( originalMouseEnter ).toHaveBeenCalledTimes( 1 ); - expect( wrapper.state.isOver ).toBe( false ); - expect( - TestUtils.scryRenderedDOMComponentsWithClass( - wrapper, - 'components-popover' - ) - ).toHaveLength( 0 ); // Force delayedSetIsOver to be called - wrapper.delayedSetIsOver.flush(); - - expect( wrapper.state.isOver ).toBe( true ); - expect( - TestUtils.scryRenderedDOMComponentsWithClass( - wrapper, - 'components-popover' - ) - ).toHaveLength( 1 ); + setTimeout( () => { + const popoverAfterTimeout = wrapper.find( 'Popover' ); + expect( popoverAfterTimeout ).toHaveLength( 1 ); + }, TOOLTIP_DELAY ); } ); it( 'should ignore mouseenter on disabled elements', () => { @@ -189,8 +178,6 @@ describe( 'Tooltip', () => { expect( originalMouseEnter ).not.toHaveBeenCalled(); const popover = wrapper.find( 'Popover' ); - wrapper.instance().delayedSetIsOver.flush(); - expect( wrapper.state( 'isOver' ) ).toBe( false ); expect( popover ).toHaveLength( 0 ); } ); @@ -211,13 +198,11 @@ describe( 'Tooltip', () => { const button = wrapper.find( 'button' ); button.simulate( 'mouseenter' ); - button.simulate( 'mouseleave' ); - - wrapper.instance().delayedSetIsOver.flush(); - const popover = wrapper.find( 'Popover' ); - expect( wrapper.state( 'isOver' ) ).toBe( false ); - expect( popover ).toHaveLength( 0 ); + setTimeout( () => { + const popover = wrapper.find( 'Popover' ); + expect( popover ).toHaveLength( 0 ); + }, TOOLTIP_DELAY ); } ); } ); } ); diff --git a/packages/components/src/utils/rtl.js b/packages/components/src/utils/rtl.js index 89fefa7508b65c..7e46a4bf6a2725 100644 --- a/packages/components/src/utils/rtl.js +++ b/packages/components/src/utils/rtl.js @@ -4,29 +4,16 @@ import { css } from '@emotion/core'; import { mapKeys } from 'lodash'; +/** + * WordPress dependencies + */ +import { isRTL } from '@wordpress/i18n'; + const LOWER_LEFT_REGEXP = new RegExp( /-left/g ); const LOWER_RIGHT_REGEXP = new RegExp( /-right/g ); const UPPER_LEFT_REGEXP = new RegExp( /Left/g ); const UPPER_RIGHT_REGEXP = new RegExp( /Right/g ); -/** - * Checks to see whether the document is set to rtl. - * - * @return {boolean} Whether document is RTL. - */ -export function getRTL() { - return !! ( document && document.documentElement.dir === 'rtl' ); -} - -/** - * Simple hook to retrieve RTL direction value - * - * @return {boolean} Whether document is RTL. - */ -export function useRTL() { - return getRTL(); -} - /** * Flips a CSS property from left <-> right. * @@ -83,14 +70,12 @@ export const convertLTRToRTL = ( ltrStyles = {} ) => { */ export function rtl( ltrStyles = {}, rtlStyles ) { return () => { - const isRTL = getRTL(); - if ( rtlStyles ) { // @ts-ignore: `css` types are wrong, it can accept an object: https://emotion.sh/docs/object-styles#with-css - return isRTL ? css( rtlStyles ) : css( ltrStyles ); + return isRTL() ? css( rtlStyles ) : css( ltrStyles ); } // @ts-ignore: `css` types are wrong, it can accept an object: https://emotion.sh/docs/object-styles#with-css - return isRTL ? css( convertLTRToRTL( ltrStyles ) ) : css( ltrStyles ); + return isRTL() ? css( convertLTRToRTL( ltrStyles ) ) : css( ltrStyles ); }; } diff --git a/packages/components/src/utils/style-mixins.js b/packages/components/src/utils/style-mixins.js index 74c9074a6c826f..5b82fd98343540 100644 --- a/packages/components/src/utils/style-mixins.js +++ b/packages/components/src/utils/style-mixins.js @@ -1,6 +1,6 @@ export { color, rgba } from './colors'; export { reduceMotion } from './reduce-motion'; -export { rtl, getRTL, useRTL } from './rtl'; +export { rtl } from './rtl'; export { space } from './space'; export { font } from './font'; export { breakpoint } from './breakpoint'; diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index b2a6831377bdad..c697cb71d98c17 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -11,6 +11,7 @@ { "path": "../primitives" } ], "include": [ + "src/animate/**/*", "src/base-control/**/*", "src/dashicon/**/*", "src/tip/**/*", diff --git a/packages/compose/README.md b/packages/compose/README.md index 7054d34721144b..6fa1842842c048 100644 --- a/packages/compose/README.md +++ b/packages/compose/README.md @@ -132,6 +132,31 @@ _Returns_ - `Array`: Async array. +<a name="useConstrainedTabbing" href="#useConstrainedTabbing">#</a> **useConstrainedTabbing** + +In Dialogs/modals, the tabbing must be constrained to the content of +the wrapper element. This hook adds the behavior to the returned ref. + +_Usage_ + +```js +import { useConstrainedTabbing } from '@wordpress/compose'; + +const ConstrainedTabbingExample = () => { + const constrainedTabbingRef = useConstrainedTabbing() + return ( + <div ref={ constrainedTabbingRef }> + <Button /> + <Button /> + </div> + ); +} +``` + +_Returns_ + +- `(Object|Function)`: Element Ref. + <a name="useCopyOnClick" href="#useCopyOnClick">#</a> **useCopyOnClick** Copies the text to the clipboard when the element is clicked. @@ -161,6 +186,65 @@ _Returns_ - `Function`: Debounced function. +<a name="useFocusOnMount" href="#useFocusOnMount">#</a> **useFocusOnMount** + +Hook used to focus the first tabbable element on mount. + +_Usage_ + +```js +import { useFocusOnMount } from '@wordpress/compose'; + +const WithFocusOnMount = () => { + const ref = useFocusOnMount() + return ( + <div ref={ ref }> + <Button /> + <Button /> + </div> + ); +} +``` + +_Parameters_ + +- _focusOnMount_ `(boolean|string)`: Focus on mount mode. + +_Returns_ + +- `Function`: Ref callback. + +<a name="useFocusReturn" href="#useFocusReturn">#</a> **useFocusReturn** + +When opening modals/sidebars/dialogs, the focus +must move to the opened area and return to the +previously focused element when closed. +The current hook implements the returning behavior. + +_Usage_ + +```js +import { useFocusReturn } from '@wordpress/compose'; + +const WithFocusReturn = () => { + const ref = useFocusReturn() + return ( + <div ref={ ref }> + <Button /> + <Button /> + </div> + ); +} +``` + +_Parameters_ + +- _onFocusReturn_ `?Function`: Overrides the default return behavior. + +_Returns_ + +- `Function`: Element Ref. + <a name="useInstanceId" href="#useInstanceId">#</a> **useInstanceId** Provides a unique instance ID. diff --git a/packages/compose/package.json b/packages/compose/package.json index 76913e4d2d1a4c..2c9f46ba653f73 100644 --- a/packages/compose/package.json +++ b/packages/compose/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/compose", - "version": "3.22.0", + "version": "3.23.0", "description": "WordPress higher-order components (HOCs).", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -25,14 +25,18 @@ "react-native": "src/index", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/deprecated": "file:../deprecated", + "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", "@wordpress/is-shallow-equal": "file:../is-shallow-equal", + "@wordpress/keycodes": "file:../keycodes", "@wordpress/priority-queue": "file:../priority-queue", "clipboard": "^2.0.1", "lodash": "^4.17.19", + "memize": "^1.1.0", "mousetrap": "^1.6.5", + "react-merge-refs": "^1.0.0", "react-resize-aware": "^3.0.1", "use-memo-one": "^1.1.1" }, diff --git a/packages/compose/src/hooks/use-callback-ref/index.js b/packages/compose/src/hooks/use-callback-ref/index.js new file mode 100644 index 00000000000000..0877607027acf6 --- /dev/null +++ b/packages/compose/src/hooks/use-callback-ref/index.js @@ -0,0 +1,20 @@ +/** + * External dependencies + */ +import memize from 'memize'; + +/** + * WordPress dependencies + */ +import { useCallback } from '@wordpress/element'; + +function useCallbackRef( callback, deps ) { + return useCallback( + memize( callback, { + maxSize: 1, + } ), + deps + ); +} + +export default useCallbackRef; diff --git a/packages/compose/src/hooks/use-constrained-tabbing/README.md b/packages/compose/src/hooks/use-constrained-tabbing/README.md new file mode 100644 index 00000000000000..eb795cf654d403 --- /dev/null +++ b/packages/compose/src/hooks/use-constrained-tabbing/README.md @@ -0,0 +1,28 @@ +`useConstrainedTabbing` +====================== + +In Dialogs/modals, the tabbing must be constrained to the content of the wrapper element. To achieve this behavior you can use the `useConstrainedTabbing` hook. + +## Return Object Properties + +### `ref` + +- Type: `Object|Function` + +A function reference that must be passed to the DOM element where constrained tabbing should be enabled. + +## Usage + +```jsx +import { useConstrainedTabbing } from '@wordpress/compose'; + +const ConstrainedTabbingExample = () => { + const ref = useConstrainedTabbing() + return ( + <div ref={ ref }> + <Button /> + <Button /> + </div> + ); +}; +``` diff --git a/packages/compose/src/hooks/use-constrained-tabbing/index.js b/packages/compose/src/hooks/use-constrained-tabbing/index.js new file mode 100644 index 00000000000000..55011151a6a74d --- /dev/null +++ b/packages/compose/src/hooks/use-constrained-tabbing/index.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies + */ +import { TAB } from '@wordpress/keycodes'; +import { focus } from '@wordpress/dom'; + +/** + * Internal dependencies + */ +import useCallbackRef from '../use-callback-ref'; + +/** + * In Dialogs/modals, the tabbing must be constrained to the content of + * the wrapper element. This hook adds the behavior to the returned ref. + * + * @return {Object|Function} Element Ref. + * + * @example + * ```js + * import { useConstrainedTabbing } from '@wordpress/compose'; + * + * const ConstrainedTabbingExample = () => { + * const constrainedTabbingRef = useConstrainedTabbing() + * return ( + * <div ref={ constrainedTabbingRef }> + * <Button /> + * <Button /> + * </div> + * ); + * } + * ``` + */ +function useConstrainedTabbing() { + const ref = useCallbackRef( ( node ) => { + if ( ! node ) { + return; + } + node.addEventListener( 'keydown', ( event ) => { + if ( event.keyCode !== TAB ) { + return; + } + + const tabbables = focus.tabbable.find( node ); + if ( ! tabbables.length ) { + return; + } + const firstTabbable = tabbables[ 0 ]; + const lastTabbable = tabbables[ tabbables.length - 1 ]; + + if ( event.shiftKey && event.target === firstTabbable ) { + event.preventDefault(); + lastTabbable.focus(); + } else if ( ! event.shiftKey && event.target === lastTabbable ) { + event.preventDefault(); + firstTabbable.focus(); + /* + * When pressing Tab and none of the tabbables has focus, the keydown + * event happens on the wrapper div: move focus on the first tabbable. + */ + } else if ( ! tabbables.includes( event.target ) ) { + event.preventDefault(); + firstTabbable.focus(); + } + } ); + }, [] ); + + return ref; +} + +export default useConstrainedTabbing; diff --git a/packages/compose/src/hooks/use-constrained-tabbing/index.native.js b/packages/compose/src/hooks/use-constrained-tabbing/index.native.js new file mode 100644 index 00000000000000..e67ada8e203b91 --- /dev/null +++ b/packages/compose/src/hooks/use-constrained-tabbing/index.native.js @@ -0,0 +1,14 @@ +/** + * WordPress dependencies + */ +import { useRef } from '@wordpress/element'; + +function useConstrainedTabbing() { + const ref = useRef(); + + // Do nothing on mobile as tabbing is not a mobile behavior. + + return ref; +} + +export default useConstrainedTabbing; diff --git a/packages/compose/src/hooks/use-dialog/README.md b/packages/compose/src/hooks/use-dialog/README.md new file mode 100644 index 00000000000000..6112326c41aefa --- /dev/null +++ b/packages/compose/src/hooks/use-dialog/README.md @@ -0,0 +1,44 @@ +`useDialog` +=========== + +React hook to be used on a dialog wrapper to enable the following behaviors: + + - constrained tabbing. + - focus on mount. + - return focus on unmount. + - focus outside. + +## Returned value + +The hooks returns an array composed of the two following values: + +### `ref` + +- Type: `Object|Function` + +A React ref that must be passed to the DOM element where the behavior should be attached. + +### `props` + +- Type: `Object` + +Extra props to apply to the wrapper. + +## Usage + +```jsx +import { __experimentalUseDialog as useDialog } from '@wordpress/compose'; + +const MyDialog = () => { + const [ ref, extraProps ] = useDialog( { + onClose: () => console.log('do something to close the dialog') + } ); + + return ( + <div ref={ ref } {...extraProps}> + <Button /> + <Button /> + </div> + ); +}; +``` diff --git a/packages/compose/src/hooks/use-dialog/index.js b/packages/compose/src/hooks/use-dialog/index.js new file mode 100644 index 00000000000000..da97c877368e63 --- /dev/null +++ b/packages/compose/src/hooks/use-dialog/index.js @@ -0,0 +1,64 @@ +/** + * External dependencies + */ +import mergeRefs from 'react-merge-refs'; + +/** + * WordPress dependencies + */ +import { useRef, useEffect } from '@wordpress/element'; +import { ESCAPE } from '@wordpress/keycodes'; + +/** + * Internal dependencies + */ +import useConstrainedTabbing from '../use-constrained-tabbing'; +import useFocusOnMount from '../use-focus-on-mount'; +import useFocusReturn from '../use-focus-return'; +import useFocusOutside from '../use-focus-outside'; +import useCallbackRef from '../use-callback-ref'; + +/** + * Returns a ref and props to apply to a dialog wrapper to enable the following behaviors: + * - constrained tabbing. + * - focus on mount. + * - return focus on unmount. + * - focus outside. + * + * @param {Object} options Dialog Options. + */ +function useDialog( options ) { + const onClose = useRef(); + useEffect( () => { + onClose.current = options.onClose; + }, [ options.onClose ] ); + const constrainedTabbingRef = useConstrainedTabbing(); + const focusOnMountRef = useFocusOnMount(); + const focusReturnRef = useFocusReturn(); + const focusOutsideProps = useFocusOutside( options.onClose ); + const closeOnEscapeRef = useCallbackRef( ( node ) => { + if ( ! node ) { + return; + } + + node.addEventListener( 'keydown', ( event ) => { + // Close on escape + if ( event.keyCode === ESCAPE && onClose.current ) { + event.stopPropagation(); + onClose.current(); + } + } ); + }, [] ); + + return [ + mergeRefs( [ + constrainedTabbingRef, + focusReturnRef, + focusOnMountRef, + closeOnEscapeRef, + ] ), + focusOutsideProps, + ]; +} + +export default useDialog; diff --git a/packages/compose/src/hooks/use-focus-on-mount/README.md b/packages/compose/src/hooks/use-focus-on-mount/README.md new file mode 100644 index 00000000000000..c5d32f05bd145d --- /dev/null +++ b/packages/compose/src/hooks/use-focus-on-mount/README.md @@ -0,0 +1,28 @@ +`useFocusOnMount` +================= + +Hook used to focus the first tabbable element on mount. + +## Return Object Properties + +### `ref` + +- Type: `Object|Function` + +A React ref that must be passed to the DOM element where the behavior should be attached. + +## Usage + +```jsx +import { useFocusOnMount } from '@wordpress/compose'; + +const WithFocusOnMount = () => { + const ref = useFocusOnMount() + return ( + <div ref={ ref }> + <Button /> + <Button /> + </div> + ); +}; +``` diff --git a/packages/compose/src/hooks/use-focus-on-mount/index.js b/packages/compose/src/hooks/use-focus-on-mount/index.js new file mode 100644 index 00000000000000..54492d8b140f75 --- /dev/null +++ b/packages/compose/src/hooks/use-focus-on-mount/index.js @@ -0,0 +1,60 @@ +/** + * WordPress dependencies + */ +import { useRef, useEffect } from '@wordpress/element'; +import { focus } from '@wordpress/dom'; + +/** + * Internal dependencies + */ +import useCallbackRef from '../use-callback-ref'; + +/** + * Hook used to focus the first tabbable element on mount. + * + * @param {boolean|string} focusOnMount Focus on mount mode. + * @return {Function} Ref callback. + * + * @example + * ```js + * import { useFocusOnMount } from '@wordpress/compose'; + * + * const WithFocusOnMount = () => { + * const ref = useFocusOnMount() + * return ( + * <div ref={ ref }> + * <Button /> + * <Button /> + * </div> + * ); + * } + * ``` + */ +export default function useFocusOnMount( focusOnMount = 'firstElement' ) { + const focusOnMountRef = useRef( focusOnMount ); + useEffect( () => { + focusOnMountRef.current = focusOnMount; + }, [ focusOnMount ] ); + + return useCallbackRef( ( node ) => { + if ( ! node || focusOnMountRef.current === false ) { + return; + } + + if ( node.contains( node.ownerDocument.activeElement ) ) { + return; + } + + let target = node; + + if ( focusOnMountRef.current === 'firstElement' ) { + const firstTabbable = focus.tabbable.find( node )[ 0 ]; + + if ( firstTabbable ) { + target = firstTabbable; + } + } + + target.focus(); + }, [] ); +} diff --git a/packages/compose/src/hooks/use-focus-outside/index.js b/packages/compose/src/hooks/use-focus-outside/index.js new file mode 100644 index 00000000000000..2169d4fd83c371 --- /dev/null +++ b/packages/compose/src/hooks/use-focus-outside/index.js @@ -0,0 +1,191 @@ +/** + * External dependencies + */ +import { includes } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useCallback, useEffect, useRef } from '@wordpress/element'; + +/** + * Input types which are classified as button types, for use in considering + * whether element is a (focus-normalized) button. + * + * @type {string[]} + */ +const INPUT_BUTTON_TYPES = [ 'button', 'submit' ]; + +/** + * @typedef {HTMLButtonElement | HTMLLinkElement | HTMLInputElement} FocusNormalizedButton + */ + +// Disable reason: Rule doesn't support predicate return types +/* eslint-disable jsdoc/valid-types */ +/** + * Returns true if the given element is a button element subject to focus + * normalization, or false otherwise. + * + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus + * + * @param {EventTarget} eventTarget The target from a mouse or touch event. + * + * @return {eventTarget is FocusNormalizedButton} Whether element is a button. + */ +function isFocusNormalizedButton( eventTarget ) { + if ( ! ( eventTarget instanceof window.HTMLElement ) ) { + return false; + } + switch ( eventTarget.nodeName ) { + case 'A': + case 'BUTTON': + return true; + + case 'INPUT': + return includes( + INPUT_BUTTON_TYPES, + /** @type {HTMLInputElement} */ ( eventTarget ).type + ); + } + + return false; +} +/* eslint-enable jsdoc/valid-types */ + +/** + * @typedef {import('react').SyntheticEvent} SyntheticEvent + */ + +/** + * @callback EventCallback + * @param {SyntheticEvent} event input related event. + */ + +/** + * @typedef FocusOutsideReactElement + * @property {EventCallback} handleFocusOutside callback for a focus outside event. + */ + +/** + * @typedef {import('react').MutableRefObject<FocusOutsideReactElement | undefined>} FocusOutsideRef + */ + +/** + * @typedef {Object} FocusOutsideReturnValue + * @property {EventCallback} onFocus An event handler for focus events. + * @property {EventCallback} onBlur An event handler for blur events. + * @property {EventCallback} onMouseDown An event handler for mouse down events. + * @property {EventCallback} onMouseUp An event handler for mouse up events. + * @property {EventCallback} onTouchStart An event handler for touch start events. + * @property {EventCallback} onTouchEnd An event handler for touch end events. + */ + +/** + * A react hook that can be used to check whether focus has moved outside the + * element the event handlers are bound to. + * + * @param {EventCallback} onFocusOutside A callback triggered when focus moves outside + * the element the event handlers are bound to. + * + * @return {FocusOutsideReturnValue} An object containing event handlers. Bind the event handlers + * to a wrapping element element to capture when focus moves + * outside that element. + */ +export default function useFocusOutside( onFocusOutside ) { + const currentOnFocusOutside = useRef( onFocusOutside ); + useEffect( () => { + currentOnFocusOutside.current = onFocusOutside; + }, [ onFocusOutside ] ); + + const preventBlurCheck = useRef( false ); + + /** + * @type {import('react').MutableRefObject<number | undefined>} + */ + const blurCheckTimeoutId = useRef(); + + /** + * Cancel a blur check timeout. + */ + const cancelBlurCheck = useCallback( () => { + clearTimeout( blurCheckTimeoutId.current ); + }, [] ); + + // Cancel blur checks on unmount. + useEffect( () => { + return () => cancelBlurCheck(); + }, [] ); + + // Cancel a blur check if the callback or ref is no longer provided. + useEffect( () => { + if ( ! onFocusOutside ) { + cancelBlurCheck(); + } + }, [ onFocusOutside, cancelBlurCheck ] ); + + /** + * Handles a mousedown or mouseup event to respectively assign and + * unassign a flag for preventing blur check on button elements. Some + * browsers, namely Firefox and Safari, do not emit a focus event on + * button elements when clicked, while others do. The logic here + * intends to normalize this as treating click on buttons as focus. + * + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus + * + * @param {SyntheticEvent} event Event for mousedown or mouseup. + */ + const normalizeButtonFocus = useCallback( ( event ) => { + const { type, target } = event; + const isInteractionEnd = includes( [ 'mouseup', 'touchend' ], type ); + + if ( isInteractionEnd ) { + preventBlurCheck.current = false; + } else if ( isFocusNormalizedButton( target ) ) { + preventBlurCheck.current = true; + } + }, [] ); + + /** + * A callback triggered when a blur event occurs on the element the handler + * is bound to. + * + * Calls the `onFocusOutside` callback in an immediate timeout if focus has + * move outside the bound element and is still within the document. + * + * @param {SyntheticEvent} event Blur event. + */ + const queueBlurCheck = useCallback( ( event ) => { + // React does not allow using an event reference asynchronously + // due to recycling behavior, except when explicitly persisted. + event.persist(); + + // Skip blur check if clicking button. See `normalizeButtonFocus`. + if ( preventBlurCheck.current ) { + return; + } + + blurCheckTimeoutId.current = setTimeout( () => { + // If document is not focused then focus should remain + // inside the wrapped component and therefore we cancel + // this blur event thereby leaving focus in place. + // https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus. + if ( ! document.hasFocus() ) { + event.preventDefault(); + return; + } + + if ( 'function' === typeof currentOnFocusOutside.current ) { + currentOnFocusOutside.current( event ); + } + }, 0 ); + }, [] ); + + return { + onFocus: cancelBlurCheck, + onMouseDown: normalizeButtonFocus, + onMouseUp: normalizeButtonFocus, + onTouchStart: normalizeButtonFocus, + onTouchEnd: normalizeButtonFocus, + onBlur: queueBlurCheck, + }; +} diff --git a/packages/compose/src/hooks/use-focus-outside/index.native.js b/packages/compose/src/hooks/use-focus-outside/index.native.js new file mode 100644 index 00000000000000..6c5c35766379f9 --- /dev/null +++ b/packages/compose/src/hooks/use-focus-outside/index.native.js @@ -0,0 +1,179 @@ +/** + * External dependencies + */ +import { includes } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useCallback, useEffect, useRef } from '@wordpress/element'; + +/** + * Input types which are classified as button types, for use in considering + * whether element is a (focus-normalized) button. + * + * @type {string[]} + */ +const INPUT_BUTTON_TYPES = [ 'button', 'submit' ]; + +/** + * @typedef {HTMLButtonElement | HTMLLinkElement | HTMLInputElement} FocusNormalizedButton + */ + +// Disable reason: Rule doesn't support predicate return types +/* eslint-disable jsdoc/valid-types */ +/** + * Returns true if the given element is a button element subject to focus + * normalization, or false otherwise. + * + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus + * + * @param {EventTarget} eventTarget The target from a mouse or touch event. + * + * @return {eventTarget is FocusNormalizedButton} Whether element is a button. + */ +function isFocusNormalizedButton( eventTarget ) { + switch ( eventTarget.nodeName ) { + case 'A': + case 'BUTTON': + return true; + + case 'INPUT': + return includes( + INPUT_BUTTON_TYPES, + /** @type {HTMLInputElement} */ ( eventTarget ).type + ); + } + + return false; +} +/* eslint-enable jsdoc/valid-types */ + +/** + * @typedef {import('react').SyntheticEvent} SyntheticEvent + */ + +/** + * @callback EventCallback + * @param {SyntheticEvent} event input related event. + */ + +/** + * @typedef FocusOutsideReactElement + * @property {EventCallback} handleFocusOutside callback for a focus outside event. + */ + +/** + * @typedef {import('react').MutableRefObject<FocusOutsideReactElement | undefined>} FocusOutsideRef + */ + +/** + * @typedef {Object} FocusOutsideReturnValue + * @property {EventCallback} onFocus An event handler for focus events. + * @property {EventCallback} onBlur An event handler for blur events. + * @property {EventCallback} onMouseDown An event handler for mouse down events. + * @property {EventCallback} onMouseUp An event handler for mouse up events. + * @property {EventCallback} onTouchStart An event handler for touch start events. + * @property {EventCallback} onTouchEnd An event handler for touch end events. + */ + +/** + * A react hook that can be used to check whether focus has moved outside the + * element the event handlers are bound to. + * + * @param {EventCallback} onFocusOutside A callback triggered when focus moves outside + * the element the event handlers are bound to. + * + * @return {FocusOutsideReturnValue} An object containing event handlers. Bind the event handlers + * to a wrapping element element to capture when focus moves + * outside that element. + */ +export default function useFocusOutside( onFocusOutside ) { + const currentOnFocusOutside = useRef( onFocusOutside ); + useEffect( () => { + currentOnFocusOutside.current = onFocusOutside; + }, [ onFocusOutside ] ); + + const preventBlurCheck = useRef( false ); + + /** + * @type {import('react').MutableRefObject<number | undefined>} + */ + const blurCheckTimeoutId = useRef(); + + /** + * Cancel a blur check timeout. + */ + const cancelBlurCheck = useCallback( () => { + clearTimeout( blurCheckTimeoutId.current ); + }, [] ); + + // Cancel blur checks on unmount. + useEffect( () => { + return () => cancelBlurCheck(); + }, [] ); + + // Cancel a blur check if the callback or ref is no longer provided. + useEffect( () => { + if ( ! onFocusOutside ) { + cancelBlurCheck(); + } + }, [ onFocusOutside, cancelBlurCheck ] ); + + /** + * Handles a mousedown or mouseup event to respectively assign and + * unassign a flag for preventing blur check on button elements. Some + * browsers, namely Firefox and Safari, do not emit a focus event on + * button elements when clicked, while others do. The logic here + * intends to normalize this as treating click on buttons as focus. + * + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus + * + * @param {SyntheticEvent} event Event for mousedown or mouseup. + */ + const normalizeButtonFocus = useCallback( ( event ) => { + const { type, target } = event; + const isInteractionEnd = includes( [ 'mouseup', 'touchend' ], type ); + + if ( isInteractionEnd ) { + preventBlurCheck.current = false; + } else if ( isFocusNormalizedButton( target ) ) { + preventBlurCheck.current = true; + } + }, [] ); + + /** + * A callback triggered when a blur event occurs on the element the handler + * is bound to. + * + * Calls the `onFocusOutside` callback in an immediate timeout if focus has + * move outside the bound element and is still within the document. + * + * @param {SyntheticEvent} event Blur event. + */ + const queueBlurCheck = useCallback( ( event ) => { + // React does not allow using an event reference asynchronously + // due to recycling behavior, except when explicitly persisted. + event.persist(); + + // Skip blur check if clicking button. See `normalizeButtonFocus`. + if ( preventBlurCheck.current ) { + return; + } + + blurCheckTimeoutId.current = setTimeout( () => { + if ( 'function' === typeof currentOnFocusOutside.current ) { + currentOnFocusOutside.current( event ); + } + }, 0 ); + }, [] ); + + return { + onFocus: cancelBlurCheck, + onMouseDown: normalizeButtonFocus, + onMouseUp: normalizeButtonFocus, + onTouchStart: normalizeButtonFocus, + onTouchEnd: normalizeButtonFocus, + onBlur: queueBlurCheck, + }; +} diff --git a/packages/compose/src/hooks/use-focus-outside/test/index.js b/packages/compose/src/hooks/use-focus-outside/test/index.js new file mode 100644 index 00000000000000..90a27102f90813 --- /dev/null +++ b/packages/compose/src/hooks/use-focus-outside/test/index.js @@ -0,0 +1,124 @@ +/** + * External dependencies + */ +import TestUtils from 'react-dom/test-utils'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import useFocusOutside from '../'; +import ReactDOM from 'react-dom'; + +let wrapper, onFocusOutside; + +describe( 'useFocusOutside', () => { + let origHasFocus; + + const FocusOutsideComponent = ( { onFocusOutside: callback } ) => ( + <div { ...useFocusOutside( callback ) }> + <input /> + <input type="button" /> + </div> + ); + + // this is needed because TestUtils does not accept a stateless component. + // anything run through a HOC ends up as a stateless component. + const getTestComponent = ( WrappedComponent, props ) => { + class TestComponent extends Component { + render() { + return <WrappedComponent { ...props } />; + } + } + return <TestComponent />; + }; + + const simulateEvent = ( event, index = 0 ) => { + const element = TestUtils.scryRenderedDOMComponentsWithTag( + wrapper, + 'input' + ); + TestUtils.Simulate[ event ]( element[ index ] ); + }; + + beforeEach( () => { + // Mock document.hasFocus() to always be true for testing + // note: we overide this for some tests. + origHasFocus = document.hasFocus; + document.hasFocus = () => true; + + onFocusOutside = jest.fn(); + wrapper = TestUtils.renderIntoDocument( + getTestComponent( FocusOutsideComponent, { onFocusOutside } ) + ); + } ); + + afterEach( () => { + document.hasFocus = origHasFocus; + } ); + + it( 'should not call handler if focus shifts to element within component', () => { + simulateEvent( 'focus' ); + simulateEvent( 'blur' ); + simulateEvent( 'focus', 1 ); + + jest.runAllTimers(); + + expect( onFocusOutside ).not.toHaveBeenCalled(); + } ); + + it( 'should not call handler if focus transitions via click to button', () => { + simulateEvent( 'focus' ); + simulateEvent( 'mouseDown', 1 ); + simulateEvent( 'blur' ); + + // In most browsers, the input at index 1 would receive a focus event + // at this point, but this is not guaranteed, which is the intention of + // the normalization behavior tested here. + simulateEvent( 'mouseUp', 1 ); + + jest.runAllTimers(); + + expect( onFocusOutside ).not.toHaveBeenCalled(); + } ); + + it( 'should call handler if focus doesn’t shift to element within component', () => { + simulateEvent( 'focus' ); + simulateEvent( 'blur' ); + + jest.runAllTimers(); + + expect( onFocusOutside ).toHaveBeenCalled(); + } ); + + it( 'should not call handler if focus shifts outside the component when the document does not have focus', () => { + // Force document.hasFocus() to return false to simulate the window/document losing focus + // See https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus. + document.hasFocus = () => false; + + simulateEvent( 'focus' ); + simulateEvent( 'blur' ); + + jest.runAllTimers(); + + expect( onFocusOutside ).not.toHaveBeenCalled(); + } ); + + it( 'should cancel check when unmounting while queued', () => { + simulateEvent( 'focus' ); + simulateEvent( 'input' ); + + ReactDOM.unmountComponentAtNode( + // eslint-disable-next-line react/no-find-dom-node + ReactDOM.findDOMNode( wrapper ).parentNode + ); + + jest.runAllTimers(); + + expect( onFocusOutside ).not.toHaveBeenCalled(); + } ); +} ); diff --git a/packages/compose/src/hooks/use-focus-return/README.md b/packages/compose/src/hooks/use-focus-return/README.md new file mode 100644 index 00000000000000..63bd7b088fb3df --- /dev/null +++ b/packages/compose/src/hooks/use-focus-return/README.md @@ -0,0 +1,28 @@ +`useFocusReturn` +================ + +When opening modals/sidebars/dialogs, the focus must move to the opened area and return to the previously focused element when closed. The current hook implements the returning behavior. + +## Return Object Properties + +### `ref` + +- Type: `Function` + +A function reference that must be passed to the DOM element being mounted and which needs to return the focus to its original position when unmounted. + +## Usage + +```jsx +import { useFocusReturn } from '@wordpress/compose'; + +const WithFocusReturn = () => { + const ref = useFocusReturn() + return ( + <div ref={ ref }> + <Button /> + <Button /> + </div> + ); +}; +``` diff --git a/packages/compose/src/hooks/use-focus-return/index.js b/packages/compose/src/hooks/use-focus-return/index.js new file mode 100644 index 00000000000000..2886b08125ad40 --- /dev/null +++ b/packages/compose/src/hooks/use-focus-return/index.js @@ -0,0 +1,76 @@ +/** + * WordPress dependencies + */ +import { useRef, useEffect } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import useCallbackRef from '../use-callback-ref'; + +/** + * When opening modals/sidebars/dialogs, the focus + * must move to the opened area and return to the + * previously focused element when closed. + * The current hook implements the returning behavior. + * + * @param {Function?} onFocusReturn Overrides the default return behavior. + * @return {Function} Element Ref. + * + * @example + * ```js + * import { useFocusReturn } from '@wordpress/compose'; + * + * const WithFocusReturn = () => { + * const ref = useFocusReturn() + * return ( + * <div ref={ ref }> + * <Button /> + * <Button /> + * </div> + * ); + * } + * ``` + */ +function useFocusReturn( onFocusReturn ) { + const ref = useRef(); + const focusedBeforeMount = useRef(); + const onFocusReturnRef = useRef( onFocusReturn ); + useEffect( () => { + onFocusReturnRef.current = onFocusReturn; + }, [ onFocusReturn ] ); + + return useCallbackRef( ( node ) => { + if ( node ) { + // Set ref to be used when unmounting. + ref.current = node; + + // Only set when the node mounts. + if ( focusedBeforeMount.current ) { + return; + } + + focusedBeforeMount.current = node.ownerDocument.activeElement; + } else if ( focusedBeforeMount.current ) { + const isFocused = ref.current.contains( + ref.current.ownerDocument.activeElement + ); + + if ( ! isFocused ) { + return; + } + + // Defer to the component's own explicit focus return behavior, if + // specified. This allows for support that the `onFocusReturn` + // decides to allow the default behavior to occur under some + // conditions. + if ( onFocusReturnRef.current ) { + onFocusReturnRef.current(); + } else { + focusedBeforeMount.current.focus(); + } + } + }, [] ); +} + +export default useFocusReturn; diff --git a/packages/compose/src/hooks/use-media-query/index.js b/packages/compose/src/hooks/use-media-query/index.js index 63168c218a6e5f..8c0f0844d6b1c4 100644 --- a/packages/compose/src/hooks/use-media-query/index.js +++ b/packages/compose/src/hooks/use-media-query/index.js @@ -11,7 +11,12 @@ import { useState, useEffect } from '@wordpress/element'; */ export default function useMediaQuery( query ) { const [ match, setMatch ] = useState( - query && window.matchMedia( query ).matches + () => + !! ( + query && + typeof window !== 'undefined' && + window.matchMedia( query ).matches + ) ); useEffect( () => { diff --git a/packages/compose/src/hooks/use-resize-observer/index.native.js b/packages/compose/src/hooks/use-resize-observer/index.native.js index 26e6f647f5b5a7..477b4b77d993b0 100644 --- a/packages/compose/src/hooks/use-resize-observer/index.native.js +++ b/packages/compose/src/hooks/use-resize-observer/index.native.js @@ -39,7 +39,10 @@ const useResizeObserver = () => { prevState.width !== width || prevState.height !== height ) { - return { width, height }; + return { + width: Math.floor( width ), + height: Math.floor( height ), + }; } return prevState; } ); diff --git a/packages/compose/src/index.js b/packages/compose/src/index.js index 4e855a47abdcd8..df5362ee366038 100644 --- a/packages/compose/src/index.js +++ b/packages/compose/src/index.js @@ -13,8 +13,13 @@ export { default as withSafeTimeout } from './higher-order/with-safe-timeout'; export { default as withState } from './higher-order/with-state'; // Hooks +export { default as useConstrainedTabbing } from './hooks/use-constrained-tabbing'; export { default as useCopyOnClick } from './hooks/use-copy-on-click'; +export { default as __experimentalUseDialog } from './hooks/use-dialog'; export { default as __experimentalUseDragging } from './hooks/use-dragging'; +export { default as useFocusOnMount } from './hooks/use-focus-on-mount'; +export { default as __experimentalUseFocusOutside } from './hooks/use-focus-outside'; +export { default as useFocusReturn } from './hooks/use-focus-return'; export { default as useInstanceId } from './hooks/use-instance-id'; export { default as useKeyboardShortcut } from './hooks/use-keyboard-shortcut'; export { default as useMediaQuery } from './hooks/use-media-query'; diff --git a/packages/compose/src/index.native.js b/packages/compose/src/index.native.js index f18e209be9ba89..04334739cd4e1f 100644 --- a/packages/compose/src/index.native.js +++ b/packages/compose/src/index.native.js @@ -13,11 +13,12 @@ export { default as withSafeTimeout } from './higher-order/with-safe-timeout'; export { default as withState } from './higher-order/with-state'; // Hooks +export { default as useConstrainedTabbing } from './hooks/use-constrained-tabbing'; export { default as __experimentalUseDragging } from './hooks/use-dragging'; +export { default as __experimentalUseFocusOutside } from './hooks/use-focus-outside'; export { default as useInstanceId } from './hooks/use-instance-id'; export { default as useKeyboardShortcut } from './hooks/use-keyboard-shortcut'; export { default as useMediaQuery } from './hooks/use-media-query'; -export { default as usePrevious } from './hooks/use-previous'; export { default as useReducedMotion } from './hooks/use-reduced-motion'; export { default as useViewportMatch } from './hooks/use-viewport-match'; export { default as useAsyncList } from './hooks/use-async-list'; diff --git a/packages/core-data/CHANGELOG.md b/packages/core-data/CHANGELOG.md index 43d507660c82b5..a100051e2f2564 100644 --- a/packages/core-data/CHANGELOG.md +++ b/packages/core-data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.25.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the core data namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/core-data/package.json b/packages/core-data/package.json index 47ddfbab6de835..aae0c81e0aadb8 100644 --- a/packages/core-data/package.json +++ b/packages/core-data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/core-data", - "version": "2.24.1", + "version": "2.25.0", "description": "Access to and manipulation of core WordPress entities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -26,7 +26,7 @@ "{src,build,build-module}/index.js" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/blocks": "file:../blocks", "@wordpress/data": "file:../data", @@ -39,7 +39,7 @@ "equivalent-key-map": "^0.2.2", "lodash": "^4.17.19", "rememo": "^3.0.0", - "uuid": "^8.3.0" + "uuid": "^8.3.0" }, "publishConfig": { "access": "public" diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index 31163cae4dcbf4..2ca38cb0d48d9f 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -389,8 +389,6 @@ export function* saveEntityRecord( }; let updatedRecord; let error; - let persistedEntity; - let currentEdits; try { const path = `${ entity.baseURL }${ recordId ? '/' + recordId : '' @@ -501,51 +499,21 @@ export function* saveEntityRecord( yield receiveAutosaves( persistedRecord.id, updatedRecord ); } } else { - // Auto drafts should be converted to drafts on explicit saves and we should not respect their default title, - // but some plugins break with this behavior so we can't filter it on the server. - let data = record; - if ( - kind === 'postType' && - persistedRecord && - persistedRecord.status === 'auto-draft' - ) { - if ( ! data.status ) { - data = { ...data, status: 'draft' }; - } - if ( ! data.title || data.title === 'Auto Draft' ) { - data = { ...data, title: '' }; - } + let edits = record; + if ( entity.__unstablePrePersist ) { + edits = { + ...edits, + ...entity.__unstablePrePersist( + persistedRecord, + edits + ), + }; } - // Get the full local version of the record before the update, - // to merge it with the edits and then propagate it to subscribers - persistedEntity = yield controls.select( - 'core', - '__experimentalGetEntityRecordNoResolver', - kind, - name, - recordId - ); - currentEdits = yield controls.select( - 'core', - 'getEntityRecordEdits', - kind, - name, - recordId - ); - yield receiveEntityRecords( - kind, - name, - { ...persistedEntity, ...data }, - undefined, - // This must be false or it will trigger a GET request in parallel to the PUT/POST below - false - ); - updatedRecord = yield apiFetch( { path, method: recordId ? 'PUT' : 'POST', - data, + data: edits, } ); yield receiveEntityRecords( kind, @@ -557,34 +525,6 @@ export function* saveEntityRecord( } } catch ( _error ) { error = _error; - - // If we got to the point in the try block where we made an optimistic update, - // we need to roll it back here. - if ( persistedEntity && currentEdits ) { - yield receiveEntityRecords( - kind, - name, - persistedEntity, - undefined, - true - ); - yield editEntityRecord( - kind, - name, - recordId, - { - ...currentEdits, - ...( yield controls.select( - 'core', - 'getEntityRecordEdits', - kind, - name, - recordId - ) ), - }, - { undoIgnore: true } - ); - } } yield { type: 'SAVE_ENTITY_RECORD_FINISH', diff --git a/packages/core-data/src/controls.js b/packages/core-data/src/controls.js new file mode 100644 index 00000000000000..e1e069a8e03062 --- /dev/null +++ b/packages/core-data/src/controls.js @@ -0,0 +1,17 @@ +export function regularFetch( url ) { + return { + type: 'REGULAR_FETCH', + url, + }; +} +const controls = { + async REGULAR_FETCH( { url } ) { + const { data } = await window + .fetch( url ) + .then( ( res ) => res.json() ); + + return data; + }, +}; + +export default controls; diff --git a/packages/core-data/src/entities.js b/packages/core-data/src/entities.js index 8e249afae3ff1c..c3c81b8dbfee03 100644 --- a/packages/core-data/src/entities.js +++ b/packages/core-data/src/entities.js @@ -114,6 +114,69 @@ export const kinds = [ { name: 'taxonomy', loadEntities: loadTaxonomyEntities }, ]; +/** + * Returns a function to be used to retrieve the title of a given post type record. + * + * @param {string} postTypeName PostType name. + * @return {Function} getTitle. + */ +export const getPostTypeTitle = ( postTypeName ) => ( record ) => { + if ( [ 'wp_template_part', 'wp_template' ].includes( postTypeName ) ) { + return ( + record?.title?.rendered || record?.title || startCase( record.slug ) + ); + } + return record?.title?.rendered || record?.title || String( record.id ); +}; + +/** + * Returns a function to be used to retrieve extra edits to apply before persisting a post type. + * + * @param {string} postTypeName PostType name. + * @return {Function} prePersistHandler. + */ +export const getPostTypePrePersistHandler = ( postTypeName ) => ( + persistedRecord, + edits +) => { + const newEdits = {}; + + // Fix template titles. + if ( + [ 'wp_template', 'wp_template_part' ].includes( postTypeName ) && + ! edits.title && + ! persistedRecord.title + ) { + newEdits.title = persistedRecord + ? getPostTypeTitle( postTypeName )( persistedRecord ) + : edits.slug; + } + + // Templates and template parts can only be published. + if ( [ 'wp_template', 'wp_template_part' ].includes( postTypeName ) ) { + newEdits.status = 'publish'; + } + + if ( persistedRecord?.status === 'auto-draft' ) { + // Saving an auto-draft should create a draft by default. + if ( ! edits.status && ! newEdits.status ) { + newEdits.status = 'draft'; + } + + // Fix the auto-draft default title. + if ( + ( ! edits.title || edits.title === 'Auto Draft' ) && + ! newEdits.title && + ( ! persistedRecord?.title || + persistedRecord?.title === 'Auto Draft' ) + ) { + newEdits.title = ''; + } + } + + return newEdits; +}; + /** * Returns the list of post type entities. * @@ -133,16 +196,8 @@ function* loadPostTypeEntities() { selectionEnd: true, }, mergedEdits: { meta: true }, - getTitle( record ) { - if ( [ 'wp_template_part', 'wp_template' ].includes( name ) ) { - return ( - record?.title?.rendered || - record?.title || - startCase( record.slug ) - ); - } - return record?.title?.rendered || record?.title || record.id; - }, + getTitle: getPostTypeTitle( name ), + __unstablePrePersist: getPostTypePrePersistHandler( name ), }; } ); } diff --git a/packages/core-data/src/index.js b/packages/core-data/src/index.js index fc2c051a52165a..c59d5bcf97ce35 100644 --- a/packages/core-data/src/index.js +++ b/packages/core-data/src/index.js @@ -13,6 +13,7 @@ import * as actions from './actions'; import * as resolvers from './resolvers'; import * as locksSelectors from './locks/selectors'; import * as locksActions from './locks/actions'; +import customControls from './controls'; import { defaultEntities, getMethodName } from './entities'; import { STORE_NAME } from './name'; @@ -58,7 +59,7 @@ const entityActions = defaultEntities.reduce( ( result, entity ) => { const storeConfig = { reducer, - controls, + controls: { ...customControls, ...controls }, actions: { ...actions, ...entityActions, ...locksActions }, selectors: { ...selectors, ...entitySelectors, ...locksSelectors }, resolvers: { ...resolvers, ...entityResolvers }, diff --git a/packages/core-data/src/locks/test/selectors.js b/packages/core-data/src/locks/test/selectors.js index 38d2da80a8ca43..2f3f1cf77f707d 100644 --- a/packages/core-data/src/locks/test/selectors.js +++ b/packages/core-data/src/locks/test/selectors.js @@ -124,7 +124,7 @@ describe( '__unstableIsLockAvailable', () => { post: { locks: [], children: { - '16': { + 16: { locks: [ { store: 'core', @@ -145,7 +145,7 @@ describe( '__unstableIsLockAvailable', () => { wp_template_part: { locks: [], children: { - '17': { + 17: { locks: [], children: {}, }, diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 9a1db4022b5580..3458c260d3f603 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -10,6 +10,10 @@ import { addQueryArgs } from '@wordpress/url'; import deprecated from '@wordpress/deprecated'; import { controls } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; +/** + * Internal dependencies + */ +import { regularFetch } from './controls'; /** * Internal dependencies @@ -382,3 +386,38 @@ export function* getAutosaves( postType, postId ) { export function* getAutosave( postType, postId ) { yield controls.resolveSelect( 'core', 'getAutosaves', postType, postId ); } + +/** + * Retrieve the frontend template used for a given link. + * + * @param {string} link Link. + */ +export function* __experimentalGetTemplateForLink( link ) { + // Ideally this should be using an apiFetch call + // We could potentially do so by adding a "filter" to the `wp_template` end point. + // Also it seems the returned object is not a regular REST API post type. + const template = yield regularFetch( + addQueryArgs( link, { + '_wp-find-template': true, + } ) + ); + + if ( template === null ) { + return; + } + + yield getEntityRecord( 'postType', 'wp_template', template.ID ); + const record = yield controls.select( + 'core', + 'getEntityRecord', + 'postType', + 'wp_template', + template.ID + ); + + if ( record ) { + yield receiveEntityRecords( 'postType', 'wp_template', [ record ], { + 'find-template': link, + } ); + } +} diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index d00356fa3483e6..5f4a9501f9bd54 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -740,3 +740,19 @@ export const getReferenceByDistinctEdits = createSelector( state.undo.flattenedUndo, ] ); + +/** + * Retrieve the frontend template used for a given link. + * + * @param {Object} state Editor state. + * @param {string} link Link. + * + * @return {Object?} The template record. + */ +export function __experimentalGetTemplateForLink( state, link ) { + const records = getEntityRecords( state, 'postType', 'wp_template', { + 'find-template': link, + } ); + + return records?.length ? records[ 0 ] : null; +} diff --git a/packages/core-data/src/test/actions.js b/packages/core-data/src/test/actions.js index 4dbad511f4503f..5addc71cfd7fbf 100644 --- a/packages/core-data/src/test/actions.js +++ b/packages/core-data/src/test/actions.js @@ -115,16 +115,7 @@ describe( 'saveEntityRecord', () => { 'SAVE_ENTITY_RECORD_START' ); - // Should select __experimentalGetEntityRecordNoResolver selector (as opposed to getEntityRecord) - // see https://github.com/WordPress/gutenberg/pull/19752#discussion_r368498318. expect( fulfillment.next().value.type ).toBe( '@@data/SELECT' ); - expect( fulfillment.next().value.selectorName ).toBe( - '__experimentalGetEntityRecordNoResolver' - ); - expect( fulfillment.next().value.type ).toBe( '@@data/SELECT' ); - const receiveItems = fulfillment.next().value; - expect( receiveItems.type ).toBe( 'RECEIVE_ITEMS' ); - expect( receiveItems.invalidateCache ).toBe( false ); const { value: apiFetchAction } = fulfillment.next( {} ); expect( apiFetchAction.request ).toEqual( { path: '/wp/v2/posts', @@ -173,11 +164,6 @@ describe( 'saveEntityRecord', () => { 'SAVE_ENTITY_RECORD_START' ); expect( fulfillment.next().value.type ).toBe( '@@data/SELECT' ); - expect( fulfillment.next().value.type ).toBe( '@@data/SELECT' ); - expect( fulfillment.next().value.type ).toBe( '@@data/SELECT' ); - const receiveItems = fulfillment.next().value; - expect( receiveItems.type ).toBe( 'RECEIVE_ITEMS' ); - expect( receiveItems.invalidateCache ).toBe( false ); const { value: apiFetchAction } = fulfillment.next( {} ); expect( apiFetchAction.request ).toEqual( { path: '/wp/v2/posts/10', @@ -222,9 +208,6 @@ describe( 'saveEntityRecord', () => { 'SAVE_ENTITY_RECORD_START' ); expect( fulfillment.next().value.type ).toBe( '@@data/SELECT' ); - expect( fulfillment.next().value.type ).toBe( '@@data/SELECT' ); - expect( fulfillment.next().value.type ).toBe( '@@data/SELECT' ); - expect( fulfillment.next().value.type ).toBe( 'RECEIVE_ITEMS' ); const { value: apiFetchAction } = fulfillment.next( {} ); expect( apiFetchAction.request ).toEqual( { path: '/wp/v2/types/page', diff --git a/packages/core-data/src/test/entities.js b/packages/core-data/src/test/entities.js index e84e99ffb191f1..7d2bf1fecf161e 100644 --- a/packages/core-data/src/test/entities.js +++ b/packages/core-data/src/test/entities.js @@ -1,7 +1,13 @@ /** * Internal dependencies */ -import { getMethodName, defaultEntities, getKindEntities } from '../entities'; +import { + getMethodName, + defaultEntities, + getKindEntities, + getPostTypeTitle, + getPostTypePrePersistHandler, +} from '../entities'; import { addEntities } from '../actions'; describe( 'getMethodName', () => { @@ -79,3 +85,137 @@ describe( 'getKindEntities', () => { expect( end ).toEqual( { done: true, value: fetchedEntities } ); } ); } ); + +describe( 'getPostTypeTitle', () => { + it( 'should prefer the rendered value for titles for regular post types', async () => { + const record = { + id: 10, + title: { + rendered: 'My Title', + }, + }; + expect( getPostTypeTitle( 'post' )( record ) ).toBe( 'My Title' ); + } ); + + it( "should fallback to the title if it's a string", async () => { + const record = { + id: 10, + title: 'My Title', + }; + expect( getPostTypeTitle( 'post' )( record ) ).toBe( 'My Title' ); + } ); + + it( 'should fallback to the id if no title provided', async () => { + const record = { + id: 10, + }; + expect( getPostTypeTitle( 'post' )( record ) ).toBe( '10' ); + } ); + + it( 'should prefer the rendered value for titles for templates', async () => { + const record = { + slug: 'single', + title: { + rendered: 'My Template', + }, + }; + expect( getPostTypeTitle( 'wp_template' )( record ) ).toBe( + 'My Template' + ); + } ); + + it( "should fallback to the title if it's a string for templates", async () => { + const record = { + slug: 'single', + title: 'My Template', + }; + expect( getPostTypeTitle( 'wp_template' )( record ) ).toBe( + 'My Template' + ); + } ); + + it( 'should fallback to the slug if no title provided', async () => { + const record = { + slug: 'single', + }; + expect( getPostTypeTitle( 'wp_template' )( record ) ).toBe( 'Single' ); + } ); +} ); + +describe( 'getPostTypePrePersistHandler', () => { + it( 'set the status to draft and empty the title when saving auto-draft posts', () => { + let record = { + status: 'auto-draft', + }; + const edits = {}; + expect( + getPostTypePrePersistHandler( 'post' )( record, edits ) + ).toEqual( { + status: 'draft', + title: '', + } ); + + record = { + status: 'publish', + }; + expect( + getPostTypePrePersistHandler( 'post' )( record, edits ) + ).toEqual( {} ); + + record = { + status: 'auto-draft', + title: 'Auto Draft', + }; + expect( + getPostTypePrePersistHandler( 'post' )( record, edits ) + ).toEqual( { + status: 'draft', + title: '', + } ); + + record = { + status: 'publish', + title: 'My Title', + }; + expect( + getPostTypePrePersistHandler( 'post' )( record, edits ) + ).toEqual( {} ); + } ); + + it( 'should set the status of templates to publish and fix the title', () => { + let record = { + status: 'auto-draft', + slug: 'single', + }; + const edits = {}; + expect( + getPostTypePrePersistHandler( 'wp_template' )( record, edits ) + ).toEqual( { + status: 'publish', + title: 'Single', + } ); + + record = { + status: 'auto-draft', + }; + expect( + getPostTypePrePersistHandler( 'wp_template_part' )( record, edits ) + ).toEqual( { + status: 'publish', + title: '', + } ); + + record = { + status: 'auto-draft', + slug: 'single', + title: { + rendered: 'My title', + }, + }; + expect( + getPostTypePrePersistHandler( 'wp_template' )( record, edits ) + ).toEqual( { + status: 'publish', + } ); + } ); +} ); diff --git a/packages/create-block/CHANGELOG.md b/packages/create-block/CHANGELOG.md index 499a7624d82648..8bc2f09bfb956e 100644 --- a/packages/create-block/CHANGELOG.md +++ b/packages/create-block/CHANGELOG.md @@ -2,9 +2,33 @@ ## Unreleased +### New Feature + +- Adds the `npmPackages` field to the template configuration. It allows listing remote npm packages that will be installed in the scaffolded project ([#27880](https://github.com/WordPress/gutenberg/pull/27880)). +- Installs WordPress npm dependencies used in the `esnext` template during the scaffolding process ([#27880](https://github.com/WordPress/gutenberg/pull/27880)). + +## 1.0.2 (2020-12-17) + +### Bug Fix + +- Second attempt to fix support for external templates by using a temporary folder when downloading npm package. + +## 1.0.1 (2020-12-17) + +### Bug Fix + +- Fix support for external templates hosted on npm. + +## 1.0.0 (2020-12-17) + +### Breaking Changes + +- Set the minimum required version of WordPress to 5.6.0 to ensure that block is correctly registered with the [Block API version 2](https://make.wordpress.org/core/2020/11/18/block-api-version-2/) ([#26098](https://github.com/WordPress/gutenberg/pull/26098)). + ### New Features - Added basic support for external templates hosted on npm ([#23712](https://github.com/WordPress/gutenberg/pull/23712)). +- Update templates to work with the [Block API version 2](https://make.wordpress.org/core/2020/11/18/block-api-version-2/) ([#26098](https://github.com/WordPress/gutenberg/pull/26098)). ## 0.18.0 (2020-10-30) diff --git a/packages/create-block/README.md b/packages/create-block/README.md index c814bff01e1fdb..2d42d5eb8665e2 100644 --- a/packages/create-block/README.md +++ b/packages/create-block/README.md @@ -148,20 +148,22 @@ module.exports = { The following configurable variables are used with the template files. Template authors can change default values to use when users don't provide their data: +- `apiVersion` (default: `2`) - `slug` (no default) -- `namespace` (default: `create-block`) +- `namespace` (default: `'create-block'`) - `title` (no default) - `description` (no default) - `dashicon` (no default) -- `category` (default: `widgets`) -- `author` (default: `The WordPress Contributors`) -- `license` (default: `GPL-2.0-or-later`) -- `licenseURI` (default: `https://www.gnu.org/licenses/gpl-2.0.html`) -- `version` (default: `0.1.0`) +- `category` (default: `'widgets'`) +- `author` (default: `'The WordPress Contributors'`) +- `license` (default: `'GPL-2.0-or-later'`) +- `licenseURI` (default: `'https://www.gnu.org/licenses/gpl-2.0.html'`) +- `version` (default: `'0.1.0'`) - `wpScripts` (default: `true`) -- `editorScript` (default: `file:./build/index.js`) -- `editorStyle` (default: `file:./build/index.css`) -- `style` (default: `file:./build/style-index.css`) +- `npmDependencies` (default: `[]`) – the list of remote npm packages to be installed in the project with [`npm install`](https://docs.npmjs.com/cli/v6/commands/npm-install). +- `editorScript` (default: `'file:./build/index.js'`) +- `editorStyle` (default: `'file:./build/index.css'`) +- `style` (default: `'file:./build/style-index.css'`) ## WP-CLI diff --git a/packages/create-block/lib/init-block-json.js b/packages/create-block/lib/init-block-json.js index 87905ea55bc43c..694414e73b547f 100644 --- a/packages/create-block/lib/init-block-json.js +++ b/packages/create-block/lib/init-block-json.js @@ -1,7 +1,7 @@ /** * External dependencies */ -const { isEmpty, omitBy } = require( 'lodash' ); +const { omitBy } = require( 'lodash' ); const { join } = require( 'path' ); const { writeFile } = require( 'fs' ).promises; @@ -11,6 +11,7 @@ const { writeFile } = require( 'fs' ).promises; const { info } = require( './log' ); module.exports = async ( { + apiVersion, slug, namespace, title, @@ -23,7 +24,6 @@ module.exports = async ( { style, } ) => { const outputFile = join( process.cwd(), slug, 'block.json' ); - info( '' ); info( 'Creating a "block.json" file.' ); await writeFile( @@ -31,6 +31,7 @@ module.exports = async ( { JSON.stringify( omitBy( { + apiVersion, name: namespace + '/' + slug, title, category, @@ -44,7 +45,7 @@ module.exports = async ( { editorStyle, style, }, - isEmpty + ( value ) => ! value ), null, '\t' diff --git a/packages/create-block/lib/init-package-json.js b/packages/create-block/lib/init-package-json.js index 3ed9d619a8871d..8c2ede2bc3dca9 100644 --- a/packages/create-block/lib/init-package-json.js +++ b/packages/create-block/lib/init-package-json.js @@ -1,14 +1,16 @@ /** * External dependencies */ -const { isEmpty, omitBy } = require( 'lodash' ); +const { command } = require( 'execa' ); +const { isEmpty, omitBy, size } = require( 'lodash' ); +const npmPackageArg = require( 'npm-package-arg' ); const { join } = require( 'path' ); const writePkg = require( 'write-pkg' ); /** * Internal dependencies */ -const { info } = require( './log' ); +const { info, error } = require( './log' ); module.exports = async ( { author, @@ -17,6 +19,7 @@ module.exports = async ( { slug, version, wpScripts, + npmDependencies, } ) => { const cwd = join( process.cwd(), slug ); @@ -44,4 +47,32 @@ module.exports = async ( { isEmpty ) ); + + if ( size( npmDependencies ) ) { + info( '' ); + info( + 'Installing npm dependencies. It might take a couple of minutes...' + ); + for ( const packageArg of npmDependencies ) { + try { + const { type } = npmPackageArg( packageArg ); + if ( + ! [ 'git', 'tag', 'version', 'range', 'remote' ].includes( + type + ) + ) { + throw new Error( + `Provided package type "${ type }" is not supported.` + ); + } + await command( `npm install ${ packageArg }`, { + cwd, + } ); + } catch ( { message } ) { + info( '' ); + info( `Skipping "${ packageArg }" npm dependency. Reason:` ); + error( message ); + } + } + } }; diff --git a/packages/create-block/lib/init-wp-scripts.js b/packages/create-block/lib/init-wp-scripts.js index f375cd82398841..fa1eba8862d3e4 100644 --- a/packages/create-block/lib/init-wp-scripts.js +++ b/packages/create-block/lib/init-wp-scripts.js @@ -13,7 +13,9 @@ module.exports = async ( { slug } ) => { const cwd = join( process.cwd(), slug ); info( '' ); - info( 'Installing packages. It might take a couple of minutes...' ); + info( + 'Installing `@wordpress/scripts` package. It might take a couple of minutes...' + ); await command( 'npm install @wordpress/scripts --save-dev', { cwd, } ); diff --git a/packages/create-block/lib/scaffold.js b/packages/create-block/lib/scaffold.js index f7c6d60f305399..2da6aee7a9e050 100644 --- a/packages/create-block/lib/scaffold.js +++ b/packages/create-block/lib/scaffold.js @@ -18,6 +18,7 @@ const { code, info, success } = require( './log' ); module.exports = async ( blockTemplate, { + apiVersion, namespace, slug, title, @@ -29,6 +30,7 @@ module.exports = async ( licenseURI, version, wpScripts, + npmDependencies, editorScript, editorStyle, style, @@ -42,6 +44,7 @@ module.exports = async ( const { outputTemplates } = blockTemplate; const view = { + apiVersion, namespace, namespaceSnakeCase: snakeCase( namespace ), slug, @@ -55,10 +58,11 @@ module.exports = async ( license, licenseURI, textdomain: slug, + wpScripts, + npmDependencies, editorScript, editorStyle, style, - wpScripts, }; await Promise.all( Object.keys( outputTemplates ).map( async ( outputFile ) => { diff --git a/packages/create-block/lib/templates.js b/packages/create-block/lib/templates.js index cb8b01a303debf..3e1c95fa1a4987 100644 --- a/packages/create-block/lib/templates.js +++ b/packages/create-block/lib/templates.js @@ -3,14 +3,11 @@ */ const { command } = require( 'execa' ); const glob = require( 'fast-glob' ); -const { readFile } = require( 'fs' ).promises; +const { mkdtemp, readFile } = require( 'fs' ).promises; const { fromPairs, isObject } = require( 'lodash' ); +const { tmpdir } = require( 'os' ); const { join } = require( 'path' ); - -/** - * WordPress dependencies - */ -const lazyImport = require( '@wordpress/lazy-import' ); +const rimraf = require( 'rimraf' ).sync; /** * Internal dependencies @@ -40,6 +37,11 @@ const predefinedBlockTemplates = { description: 'Example block written with ESNext standard and JSX support – build step required.', dashicon: 'smiley', + npmDependencies: [ + '@wordpress/block-editor', + '@wordpress/blocks', + '@wordpress/i18n', + ], }, }, }; @@ -94,13 +96,24 @@ const getBlockTemplate = async ( templateName ) => { ); } + let tempCwd; + try { info( '' ); info( 'Downloading template files. It might take some time...' ); - const { defaultValues = {}, templatesPath } = await lazyImport( - templateName - ); + tempCwd = await mkdtemp( join( tmpdir(), 'wp-create-block-' ) ); + + await command( `npm install ${ templateName } --no-save`, { + cwd: tempCwd, + } ); + + const { defaultValues = {}, templatesPath } = require( require.resolve( + templateName, + { + paths: [ tempCwd ], + } + ) ); if ( ! isObject( defaultValues ) || ! templatesPath ) { throw new Error(); } @@ -113,11 +126,16 @@ const getBlockTemplate = async ( templateName ) => { throw new CLIError( `Invalid template definition provided in "${ templateName }" package.` ); + } finally { + if ( tempCwd ) { + rimraf( tempCwd ); + } } }; const getDefaultValues = ( blockTemplate ) => { return { + apiVersion: 2, namespace: 'create-block', category: 'widgets', author: 'The WordPress Contributors', @@ -125,6 +143,7 @@ const getDefaultValues = ( blockTemplate ) => { licenseURI: 'https://www.gnu.org/licenses/gpl-2.0.html', version: '0.1.0', wpScripts: true, + npmDependencies: [], editorScript: 'file:./build/index.js', editorStyle: 'file:./build/index.css', style: 'file:./build/style-index.css', diff --git a/packages/create-block/lib/templates/es5/index.js.mustache b/packages/create-block/lib/templates/es5/index.js.mustache index b0486b43e4679d..318bd7f21dba17 100644 --- a/packages/create-block/lib/templates/es5/index.js.mustache +++ b/packages/create-block/lib/templates/es5/index.js.mustache @@ -20,12 +20,24 @@ */ var __ = wp.i18n.__; + /** + * This hook is used to mark the block wrapper element. + * + * @see https://developer.wordpress.org/block-editor/packages/packages-block-editor/#useBlockProps + */ + var useBlockProps = wp.blockEditor.useBlockProps; + /** * Every block starts by registering a new block type definition. * * @see https://developer.wordpress.org/block-editor/developers/block-api/#registering-a-block */ registerBlockType( '{{namespace}}/{{slug}}', { + /** + * @see https://make.wordpress.org/core/2020/11/18/block-api-version-2/ + */ + apiVersion: {{apiVersion}}, + /** * This is the display title for your block, which can be translated with `i18n` functions. * The block inserter will show this name. @@ -74,14 +86,12 @@ * * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#edit * - * @param {Object} [props] Properties passed from the editor. - * * @return {WPElement} Element to render. */ - edit: function( props ) { + edit: function() { return el( 'p', - { className: props.className }, + useBlockProps(), __( '{{title}} – hello from the editor!', '{{textdomain}}' ) ); }, diff --git a/packages/create-block/lib/templates/es5/readme.txt.mustache b/packages/create-block/lib/templates/es5/readme.txt.mustache index 56262c0b4d2638..0836645a1f9a7e 100644 --- a/packages/create-block/lib/templates/es5/readme.txt.mustache +++ b/packages/create-block/lib/templates/es5/readme.txt.mustache @@ -3,8 +3,8 @@ Contributors: {{author}} {{/author}} Tags: block -Requires at least: 5.5.0 -Tested up to: 5.5.1 +Requires at least: 5.6.0 +Tested up to: 5.6.0 Stable tag: {{version}} Requires PHP: 7.0.0 {{#license}} diff --git a/packages/create-block/lib/templates/esnext/readme.txt.mustache b/packages/create-block/lib/templates/esnext/readme.txt.mustache index 56262c0b4d2638..0836645a1f9a7e 100644 --- a/packages/create-block/lib/templates/esnext/readme.txt.mustache +++ b/packages/create-block/lib/templates/esnext/readme.txt.mustache @@ -3,8 +3,8 @@ Contributors: {{author}} {{/author}} Tags: block -Requires at least: 5.5.0 -Tested up to: 5.5.1 +Requires at least: 5.6.0 +Tested up to: 5.6.0 Stable tag: {{version}} Requires PHP: 7.0.0 {{#license}} diff --git a/packages/create-block/lib/templates/esnext/src/edit.js.mustache b/packages/create-block/lib/templates/esnext/src/edit.js.mustache index 524ffa4f47a1b7..8ed2bb5d381980 100644 --- a/packages/create-block/lib/templates/esnext/src/edit.js.mustache +++ b/packages/create-block/lib/templates/esnext/src/edit.js.mustache @@ -5,6 +5,14 @@ */ import { __ } from '@wordpress/i18n'; +/** + * React hook that is used to mark the block wrapper element. + * It provides all the necessary props like the class name. + * + * @see https://developer.wordpress.org/block-editor/packages/packages-block-editor/#useBlockProps + */ +import { useBlockProps } from '@wordpress/block-editor'; + /** * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files. * Those files can contain any CSS code that gets applied to the editor. @@ -19,14 +27,11 @@ import './editor.scss'; * * @see https://developer.wordpress.org/block-editor/developers/block-api/block-edit-save/#edit * - * @param {Object} [props] Properties passed from the editor. - * @param {string} [props.className] Class name generated for the block. - * * @return {WPElement} Element to render. */ -export default function Edit( { className } ) { +export default function Edit() { return ( - <p className={ className }> + <p { ...useBlockProps() }> { __( '{{title}} – hello from the editor!', '{{textdomain}}' ) } </p> ); diff --git a/packages/create-block/lib/templates/esnext/src/index.js.mustache b/packages/create-block/lib/templates/esnext/src/index.js.mustache index ef9236403bbf21..13ecd405a8be95 100644 --- a/packages/create-block/lib/templates/esnext/src/index.js.mustache +++ b/packages/create-block/lib/templates/esnext/src/index.js.mustache @@ -33,6 +33,11 @@ import save from './save'; * @see https://developer.wordpress.org/block-editor/developers/block-api/#registering-a-block */ registerBlockType( '{{namespace}}/{{slug}}', { + /** + * @see https://make.wordpress.org/core/2020/11/18/block-api-version-2/ + */ + apiVersion: {{apiVersion}}, + /** * This is the display title for your block, which can be translated with `i18n` functions. * The block inserter will show this name. diff --git a/packages/create-block/package.json b/packages/create-block/package.json index ba9a1b586a2179..3af07b5094ef3c 100644 --- a/packages/create-block/package.json +++ b/packages/create-block/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/create-block", - "version": "0.18.0", + "version": "1.0.2", "description": "Generates PHP, JS and CSS code for registering a block for a WordPress plugin.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -41,6 +41,8 @@ "lodash": "^4.17.19", "make-dir": "^3.0.0", "mustache": "^4.0.0", + "npm-package-arg": "^8.0.1", + "rimraf": "^3.0.2", "write-pkg": "^4.0.0" }, "publishConfig": { diff --git a/packages/data-controls/package.json b/packages/data-controls/package.json index b659653c9537db..bda4bbbad2a479 100644 --- a/packages/data-controls/package.json +++ b/packages/data-controls/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data-controls", - "version": "1.19.0", + "version": "1.20.0", "description": "A set of common controls for the @wordpress/data api.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/data": "file:../data", "@wordpress/deprecated": "file:../deprecated" diff --git a/packages/data/CHANGELOG.md b/packages/data/CHANGELOG.md index da3ad5c8ead66f..aed1dfec7c8414 100644 --- a/packages/data/CHANGELOG.md +++ b/packages/data/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.26.0 (2020-12-17) + ### New Features - Added new `register` function for registering a standard `@wordpress/data` store definition ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). @@ -9,6 +11,11 @@ - Extended `select` and `dispatch` functions to accept a data store definition as their first param in addition to a string-based store name value [#26655](https://github.com/WordPress/gutenberg/pull/26655)). - Extended `useDispatch` hook to accept a data store definition as their first param in addition to a string-based store name value [#26655](https://github.com/WordPress/gutenberg/pull/26655)). +### Deprecations + +- `registerGenericStore` has been deprecated. Use `register` instead. +- `registerStore` has been deprecated. Use `register` instead. + ## 4.6.0 (2019-06-12) ### New Feature diff --git a/packages/data/README.md b/packages/data/README.md index 1598cd066fca85..ab6ebf37297b32 100644 --- a/packages/data/README.md +++ b/packages/data/README.md @@ -16,11 +16,11 @@ _This package assumes that your code will run in an **ES2015+** environment. If ## Registering a Store -Use the `registerStore` function to add your own store to the centralized data registry. This function accepts two arguments: a name to identify the module, and an object with values describing how your state is represented, modified, and accessed. At a minimum, you must provide a reducer function describing the shape of your state and how it changes in response to actions dispatched to the store. +Use the `register` function to add your own store to the centralized data registry. This function accepts one argument – a store definition object that can be created with `createReduxStore` factory function. `createReduxStore` accepts two arguments: a name to identify the module, and an object with values describing how your state is represented, modified, and accessed. At a minimum, you must provide a reducer function describing the shape of your state and how it changes in response to actions dispatched to the store. ```js import apiFetch from '@wordpress/api-fetch'; -import { registerStore } from '@wordpress/data'; +import { createReduxStore, register } from '@wordpress/data'; const DEFAULT_STATE = { prices: {}, @@ -51,7 +51,7 @@ const actions = { }, }; -registerStore( 'my-shop', { +const store = createReduxStore( 'my-shop', { reducer( state = DEFAULT_STATE, action ) { switch ( action.type ) { case 'SET_PRICE': @@ -98,18 +98,22 @@ registerStore( 'my-shop', { }, }, } ); + +register( store ); ``` -The return value of `registerStore` is a [Redux-like store object](https://redux.js.org/basics/store) with the following methods: +The return value of `createReduxStore` is the `WPDataStore` object that contains two properties: -- `store.getState()`: Returns the state value of the registered reducer - - _Redux parallel:_ [`getState`](https://redux.js.org/api/store#getstate) -- `store.subscribe( listener: Function )`: Registers a function called any time the value of state changes. - - _Redux parallel:_ [`subscribe`](https://redux.js.org/api/store#subscribelistener) -- `store.dispatch( action: Object )`: Given an action object, calls the registered reducer and updates the state value. - - _Redux parallel:_ [`dispatch`](https://redux.js.org/api/store#dispatchaction) +- `name` (`string`) – the name of the store +- `instantiate` (`Function`) - it returns a [Redux-like store object](https://redux.js.org/basics/store) with the following methods: + - `getState()`: Returns the state value of the registered reducer + - _Redux parallel:_ [`getState`](https://redux.js.org/api/store#getstate) + - `subscribe( listener: Function )`: Registers a function called any time the value of state changes. + - _Redux parallel:_ [`subscribe`](https://redux.js.org/api/store#subscribelistener) + - `dispatch( action: Object )`: Given an action object, calls the registered reducer and updates the state value. + - _Redux parallel:_ [`dispatch`](https://redux.js.org/api/store#dispatchaction) -### Options +### Redux Store Options #### `reducer` @@ -308,7 +312,7 @@ reducing functions into a single reducing function you can pass to registerReduc _Usage_ ```js -import { combineReducers, registerStore } from '@wordpress/data'; +import { combineReducers, createReduxStore, register } from '@wordpress/data'; const prices = ( state = {}, action ) => { return action.type === 'SET_PRICE' ? @@ -325,12 +329,13 @@ const discountPercent = ( state = 0, action ) => { state; }; -registerStore( 'my-shop', { +const store = createReduxStore( 'my-shop', { reducer: combineReducers( { prices, discountPercent, } ), } ); +register( store ); ``` _Parameters_ @@ -479,7 +484,7 @@ dispatch( 'my-shop' ).setPrice( 'hammer', 9.75 ); _Parameters_ -- _name_ `string`: Store name. +- _storeNameOrDefinition_ `(string|WPDataStore)`: Unique namespace identifier for the store or the store definition. _Returns_ @@ -512,7 +517,7 @@ const store = createReduxStore( 'demo', { getValue: ( state ) => state, }, } ); -registry.register( store ); +register( store ); ``` _Parameters_ @@ -521,6 +526,8 @@ _Parameters_ <a name="registerGenericStore" href="#registerGenericStore">#</a> **registerGenericStore** +> **Deprecated** Use `register` instead. + Registers a generic store. _Parameters_ @@ -530,6 +537,8 @@ _Parameters_ <a name="registerStore" href="#registerStore">#</a> **registerStore** +> **Deprecated** Use `register` instead. + Registers a standard `@wordpress/data` store. _Parameters_ @@ -584,7 +593,7 @@ example. <a name="select" href="#select">#</a> **select** -Given the name of a registered store, returns an object of the store's selectors. +Given the name or definition of a registered store, returns an object of the store's selectors. The selector functions are been pre-bound to pass the current state automatically. As a consumer, you need only pass arguments of the selector, if applicable. @@ -598,7 +607,7 @@ select( 'my-shop' ).getPrice( 'hammer' ); _Parameters_ -- _name_ `string`: Store name. +- _storeNameOrDefinition_ `(string|WPDataStore)`: Unique namespace identifier for the store or the store definition. _Returns_ diff --git a/packages/data/package.json b/packages/data/package.json index 90f2181dae19bf..4e3836a9547239 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/data", - "version": "4.25.0", + "version": "4.26.0", "description": "Data module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,14 +24,13 @@ "react-native": "src/index", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:../compose", "@wordpress/deprecated": "file:../deprecated", "@wordpress/element": "file:../element", "@wordpress/is-shallow-equal": "file:../is-shallow-equal", "@wordpress/priority-queue": "file:../priority-queue", "@wordpress/redux-routine": "file:../redux-routine", - "@wordpress/stan": "file:../stan", "equivalent-key-map": "^0.2.2", "is-promise": "^4.0.0", "lodash": "^4.17.19", diff --git a/packages/data/src/atomic-store/index.js b/packages/data/src/atomic-store/index.js deleted file mode 100644 index ed6ed7c0d8f821..00000000000000 --- a/packages/data/src/atomic-store/index.js +++ /dev/null @@ -1,108 +0,0 @@ -/** - * External dependencies - */ -import { mapValues } from 'lodash'; - -/** - * WordPress dependencies - */ -import { - createDerivedAtom, - createAtomRegistry, - createStoreAtomSelector, -} from '@wordpress/stan'; - -/** - * @typedef {import("../types").WPDataAtomicStoreConfig} WPDataAtomicStoreConfig - */ -/** - * @typedef {import("../types").WPDataStore} WPDataStore - */ -/** - * @template T - * @typedef {import('@wordpress/stan/src/types').WPAtom<T>} WPAtom - */ -/** - * @template T - * @typedef {import('@wordpress/stan/src/types').WPAtomSelector<T>} WPAtomSelector - */ - -/** - * - * @param {string} name Store name. - * @param {WPDataAtomicStoreConfig} config Atomic store config. - * @return {WPDataStore} Store. - */ -export default function createAtomicStore( name, config ) { - return { - name, - instantiate: ( registry ) => { - // Having a dedicated atom registry per store allow us to support - // registry inheritance as we won't be retrieving atoms from the wrong registries. - const atomRegistry = createAtomRegistry(); - - // These are used inside of useSelect when mapSelect can merge selectors - // from different stores and different data registries. - const registryAtomSelectors = mapValues( - config.selectors, - ( atomSelector ) => { - return createStoreAtomSelector( - ( ...args ) => ( listener ) => - atomRegistry.subscribe( - atomSelector( ...args ), - listener - ), - ( ...args ) => () => - atomRegistry.get( atomSelector( ...args ) ), - ( ...args ) => ( value ) => - atomRegistry.set( atomSelector( ...args ), value ) - ); - } - ); - - const selectors = mapValues( - registryAtomSelectors, - ( atomSelector, selectorName ) => { - return ( /** @type {any[]} **/ ...args ) => { - return registry.__internalGetAtomResolver() - ? registry.__internalGetAtomResolver()( - atomSelector( ...args ) - ) - : atomRegistry.get( - // @ts-ignore - config.selectors[ selectorName ]( ...args ) - ); - }; - } - ); - - const actions = mapValues( config.actions, ( atomAction ) => { - return ( /** @type {any[]} **/ ...args ) => { - return atomAction( ...args )( { - get: ( atomCreator ) => atomRegistry.get( atomCreator ), - set: ( atomCreator, value ) => - atomRegistry.set( atomCreator, value ), - } ); - }; - } ); - - return { - __internalIsAtomic: true, - getSelectors: () => selectors, - getActions: () => actions, - - // Subscribing to the root atoms allows us - // To refresh the data when all root selector change. - subscribe: ( listener ) => { - const atom = createDerivedAtom( ( { get } ) => { - config.rootAtoms.forEach( ( subatom ) => - get( subatom ) - ); - } ); - - return atomRegistry.subscribe( atom, listener ); - }, - }; - }, - }; -} diff --git a/packages/data/src/components/use-select/index.js b/packages/data/src/components/use-select/index.js index dedc7a6c9f084b..741ee366742e5b 100644 --- a/packages/data/src/components/use-select/index.js +++ b/packages/data/src/components/use-select/index.js @@ -1,22 +1,40 @@ +/** + * External dependencies + */ +import { useMemoOne } from 'use-memo-one'; + /** * WordPress dependencies */ +import { createQueue } from '@wordpress/priority-queue'; import { useLayoutEffect, useRef, - useState, - useMemo, useCallback, + useEffect, + useReducer, + useMemo, } from '@wordpress/element'; import isShallowEqual from '@wordpress/is-shallow-equal'; -import { createDerivedAtom, createAtomRegistry } from '@wordpress/stan'; -import { usePrevious } from '@wordpress/compose'; /** * Internal dependencies */ -import useAsyncMode from '../async-mode-provider/use-async-mode'; import useRegistry from '../registry-provider/use-registry'; +import useAsyncMode from '../async-mode-provider/use-async-mode'; + +/** + * Favor useLayoutEffect to ensure the store subscription callback always has + * the selector from the latest render. If a store update happens between render + * and the effect, this could cause missed/stale updates or inconsistent state. + * + * Fallback to useEffect for server rendered components because currently React + * throws a warning when using useLayoutEffect in that environment. + */ +const useIsomorphicLayoutEffect = + typeof window !== 'undefined' ? useLayoutEffect : useEffect; + +const renderQueue = createQueue(); /** * Custom react hook for retrieving props from registered selectors. @@ -25,14 +43,15 @@ import useRegistry from '../registry-provider/use-registry'; * [rules of hooks](https://reactjs.org/docs/hooks-rules.html). * * @param {Function} _mapSelect Function called on every state change. The - * returned value is exposed to the component - * implementing this hook. The function receives - * the `registry.select` method on the first - * argument and the `registry` on the second - * argument. - * @param {Array} deps If provided, this memoizes the mapSelect so the - * same `mapSelect` is invoked on every state - * change unless the dependencies change. + * returned value is exposed to the component + * implementing this hook. The function receives + * the `registry.select` method on the first + * argument and the `registry` on the second + * argument. + * @param {Array} deps If provided, this memoizes the mapSelect so the + * same `mapSelect` is invoked on every state + * change unless the dependencies change. + * * @example * ```js * import { useSelect } from '@wordpress/data'; @@ -57,51 +76,61 @@ import useRegistry from '../registry-provider/use-registry'; * any price in the state for that currency is retrieved. If the currency prop * doesn't change and other props are passed in that do change, the price will * not change because the dependency is just the currency. + * * @return {Function} A custom react hook. */ export default function useSelect( _mapSelect, deps ) { - const atomRegistry = useMemo( () => createAtomRegistry(), [] ); const mapSelect = useCallback( _mapSelect, deps ); - const previousMapSelect = usePrevious( mapSelect ); - const result = useRef(); const registry = useRegistry(); const isAsync = useAsyncMode(); - const [ , dispatch ] = useState( {} ); - const rerender = () => dispatch( {} ); - const isMountedAndNotUnsubscribing = useRef( true ); - const previousMapError = useRef(); - const shouldSyncCompute = - previousMapSelect !== mapSelect || !! previousMapError.current; - - const atomState = useMemo( () => { - return createDerivedAtom( - ( { get } ) => { - const current = registry.__internalGetAtomResolver(); - registry.__internalSetAtomResolver( get ); - let ret; - try { - ret = mapSelect( registry.select, registry ); - } catch ( error ) { - ret = result.current; - previousMapError.current = error; - } - registry.__internalSetAtomResolver( current ); - return ret; - }, - () => {}, - { isAsync } - )( atomRegistry ); - }, [ isAsync, registry, mapSelect ] ); + // React can sometimes clear the `useMemo` cache. + // We use the cache-stable `useMemoOne` to avoid + // losing queues. + const queueContext = useMemoOne( () => ( { queue: true } ), [ registry ] ); + const [ , forceRender ] = useReducer( ( s ) => s + 1, 0 ); + + const latestMapSelect = useRef(); + const latestIsAsync = useRef( isAsync ); + const latestMapOutput = useRef(); + const latestMapOutputError = useRef(); + const isMountedAndNotUnsubscribing = useRef(); + + // Keep track of the stores being selected in the mapSelect function, + // and only subscribe to those stores later. + const listeningStores = useRef( [] ); + const trapSelect = useCallback( + ( callback ) => + registry.__experimentalMarkListeningStores( + callback, + listeningStores + ), + [ registry ] + ); + + // Generate a "flag" for used in the effect dependency array. + // It's different than just using `mapSelect` since deps could be undefined, + // in that case, we would still want to memoize it. + const depsChangedFlag = useMemo( () => ( {} ), deps || [] ); + + let mapOutput; try { - if ( shouldSyncCompute ) { - result.current = atomState.get(); + if ( + latestMapSelect.current !== mapSelect || + latestMapOutputError.current + ) { + mapOutput = trapSelect( () => + mapSelect( registry.select, registry ) + ); + } else { + mapOutput = latestMapOutput.current; } } catch ( error ) { let errorMessage = `An error occurred while running 'mapSelect': ${ error.message }`; - if ( previousMapError.current ) { + + if ( latestMapOutputError.current ) { errorMessage += `\nThe error may be correlated with this previous error:\n`; - errorMessage += `${ previousMapError.current.stack }\n\n`; + errorMessage += `${ latestMapOutputError.current.stack }\n\n`; errorMessage += 'Original stack trace:'; throw new Error( errorMessage ); @@ -110,33 +139,71 @@ export default function useSelect( _mapSelect, deps ) { console.error( errorMessage ); } } - useLayoutEffect( () => { - previousMapError.current = undefined; + + useIsomorphicLayoutEffect( () => { + latestMapSelect.current = mapSelect; + latestMapOutput.current = mapOutput; + latestMapOutputError.current = undefined; isMountedAndNotUnsubscribing.current = true; + + // This has to run after the other ref updates + // to avoid using stale values in the flushed + // callbacks or potentially overwriting a + // changed `latestMapOutput.current`. + if ( latestIsAsync.current !== isAsync ) { + latestIsAsync.current = isAsync; + renderQueue.flush( queueContext ); + } } ); - useLayoutEffect( () => { + useIsomorphicLayoutEffect( () => { const onStoreChange = () => { - if ( - isMountedAndNotUnsubscribing.current && - ! isShallowEqual( atomState.get(), result.current ) - ) { - result.current = atomState.get(); - rerender(); + if ( isMountedAndNotUnsubscribing.current ) { + try { + const newMapOutput = trapSelect( () => + latestMapSelect.current( registry.select, registry ) + ); + + if ( + isShallowEqual( latestMapOutput.current, newMapOutput ) + ) { + return; + } + latestMapOutput.current = newMapOutput; + } catch ( error ) { + latestMapOutputError.current = error; + } + forceRender(); } }; - const unsubscribe = atomState.subscribe( () => { + + // catch any possible state changes during mount before the subscription + // could be set. + if ( latestIsAsync.current ) { + renderQueue.add( queueContext, onStoreChange ); + } else { onStoreChange(); - } ); + } + + const onChange = () => { + if ( latestIsAsync.current ) { + renderQueue.add( queueContext, onStoreChange ); + } else { + onStoreChange(); + } + }; - // This is necessary if the value changes during mount. - onStoreChange(); + const unsubscribers = listeningStores.current.map( ( storeName ) => + registry.__experimentalSubscribeStore( storeName, onChange ) + ); return () => { isMountedAndNotUnsubscribing.current = false; - unsubscribe(); + // The return value of the subscribe function could be undefined if the store is a custom generic store. + unsubscribers.forEach( ( unsubscribe ) => unsubscribe?.() ); + renderQueue.flush( queueContext ); }; - }, [ atomState ] ); + }, [ registry, trapSelect, depsChangedFlag ] ); - return result.current; + return mapOutput; } diff --git a/packages/data/src/components/use-select/test/index.js b/packages/data/src/components/use-select/test/index.js index 232334af6fb985..4dbfe576a2f5ef 100644 --- a/packages/data/src/components/use-select/test/index.js +++ b/packages/data/src/components/use-select/test/index.js @@ -3,10 +3,16 @@ */ import TestRenderer, { act } from 'react-test-renderer'; +/** + * WordPress dependencies + */ +import { useState, useReducer } from '@wordpress/element'; + /** * Internal dependencies */ import { createRegistry } from '../../../registry'; +import { createRegistrySelector } from '../../../factory'; import { RegistryProvider } from '../../registry-provider'; import useSelect from '../index'; @@ -47,7 +53,8 @@ describe( 'useSelect', () => { const testInstance = renderer.root; // 2 times expected // - 1 for initial mount - expect( selectSpy ).toHaveBeenCalledTimes( 1 ); + // - 1 for after mount before subscription set. + expect( selectSpy ).toHaveBeenCalledTimes( 2 ); expect( TestComponent ).toHaveBeenCalledTimes( 1 ); // ensure expected state was rendered @@ -81,7 +88,7 @@ describe( 'useSelect', () => { } ); const testInstance = renderer.root; - expect( selectSpyFoo ).toHaveBeenCalledTimes( 1 ); + expect( selectSpyFoo ).toHaveBeenCalledTimes( 2 ); expect( selectSpyBar ).toHaveBeenCalledTimes( 0 ); expect( TestComponent ).toHaveBeenCalledTimes( 1 ); @@ -99,7 +106,7 @@ describe( 'useSelect', () => { ); } ); - expect( selectSpyFoo ).toHaveBeenCalledTimes( 1 ); + expect( selectSpyFoo ).toHaveBeenCalledTimes( 2 ); expect( selectSpyBar ).toHaveBeenCalledTimes( 0 ); expect( TestComponent ).toHaveBeenCalledTimes( 2 ); @@ -109,7 +116,6 @@ describe( 'useSelect', () => { } ); // rerender with dependency changed - // rerender with non dependency changed act( () => { renderer.update( <RegistryProvider value={ registry }> @@ -118,8 +124,8 @@ describe( 'useSelect', () => { ); } ); - expect( selectSpyFoo ).toHaveBeenCalledTimes( 1 ); - expect( selectSpyBar ).toHaveBeenCalledTimes( 1 ); + expect( selectSpyFoo ).toHaveBeenCalledTimes( 2 ); + expect( selectSpyBar ).toHaveBeenCalledTimes( 2 ); expect( TestComponent ).toHaveBeenCalledTimes( 3 ); // ensure expected state was rendered @@ -128,10 +134,8 @@ describe( 'useSelect', () => { } ); } ); describe( 'rerenders as expected with various mapSelect return types', () => { - const getComponent = ( mapSelectSpy ) => ( { render } ) => { - const data = useSelect( ( select ) => mapSelectSpy( select ), [ - render, - ] ); + const getComponent = ( mapSelectSpy ) => () => { + const data = useSelect( mapSelectSpy, [] ); return <div data={ data } />; }; let TestComponent; @@ -142,7 +146,10 @@ describe( 'useSelect', () => { beforeEach( () => { registry.registerStore( 'testStore', { - reducer: () => null, + actions: { + forceUpdate: () => ( { type: 'FORCE_UPDATE' } ), + }, + reducer: ( state = {} ) => ( { ...state } ), selectors: { testSelector: selectorSpy, }, @@ -177,7 +184,7 @@ describe( 'useSelect', () => { act( () => { renderer = TestRenderer.create( <RegistryProvider value={ registry }> - <TestComponent render="1" /> + <TestComponent /> </RegistryProvider> ); } ); @@ -191,17 +198,592 @@ describe( 'useSelect', () => { // subscription which should in turn trigger a re-render. act( () => { selectorSpy.mockReturnValue( valueB ); - renderer.update( - <RegistryProvider value={ registry }> - <TestComponent render="2" /> - </RegistryProvider> - ); + registry.dispatch( 'testStore' ).forceUpdate(); } ); expect( testInstance.findByType( 'div' ).props.data ).toEqual( valueB ); - expect( mapSelectSpy ).toHaveBeenCalledTimes( 2 ); + expect( mapSelectSpy ).toHaveBeenCalledTimes( 3 ); } ); } ); + + describe( 're-calls the selector as minimal times as possible', () => { + const counterStore = { + actions: { + increment: () => ( { type: 'INCREMENT' } ), + }, + reducer: ( state, action ) => { + if ( ! state ) { + return { counter: 0 }; + } + if ( action?.type === 'INCREMENT' ) { + return { counter: state.counter + 1 }; + } + return state; + }, + selectors: { + getCounter: ( state ) => state.counter, + }, + }; + + it( 'only calls the selectors it has selected', () => { + registry.registerStore( 'store-1', counterStore ); + registry.registerStore( 'store-2', counterStore ); + + let renderer; + + const selectCount1 = jest.fn(); + const selectCount2 = jest.fn(); + + const TestComponent = jest.fn( () => { + const count1 = useSelect( + ( select ) => + selectCount1() || select( 'store-1' ).getCounter(), + [] + ); + useSelect( + ( select ) => + selectCount2() || select( 'store-2' ).getCounter(), + [] + ); + + return <div data={ count1 } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <TestComponent /> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( selectCount1 ).toHaveBeenCalledTimes( 2 ); + expect( selectCount2 ).toHaveBeenCalledTimes( 2 ); + expect( TestComponent ).toHaveBeenCalledTimes( 1 ); + expect( testInstance.findByType( 'div' ).props.data ).toBe( 0 ); + + act( () => { + registry.dispatch( 'store-2' ).increment(); + } ); + + expect( selectCount1 ).toHaveBeenCalledTimes( 2 ); + expect( selectCount2 ).toHaveBeenCalledTimes( 3 ); + expect( TestComponent ).toHaveBeenCalledTimes( 2 ); + expect( testInstance.findByType( 'div' ).props.data ).toBe( 0 ); + + act( () => { + registry.dispatch( 'store-1' ).increment(); + } ); + + expect( selectCount1 ).toHaveBeenCalledTimes( 3 ); + expect( selectCount2 ).toHaveBeenCalledTimes( 3 ); + expect( TestComponent ).toHaveBeenCalledTimes( 3 ); + expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 ); + + // Test if the unsubscribers get called correctly. + renderer.unmount(); + } ); + + it( 'can subscribe to multiple stores at once', () => { + registry.registerStore( 'store-1', counterStore ); + registry.registerStore( 'store-2', counterStore ); + registry.registerStore( 'store-3', counterStore ); + + let renderer; + + const selectCount1And2 = jest.fn(); + + const TestComponent = jest.fn( () => { + const { count1, count2 } = useSelect( + ( select ) => + selectCount1And2() || { + count1: select( 'store-1' ).getCounter(), + count2: select( 'store-2' ).getCounter(), + }, + [] + ); + + return <div data={ { count1, count2 } } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <TestComponent /> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( selectCount1And2 ).toHaveBeenCalledTimes( 2 ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + count2: 0, + } ); + + act( () => { + registry.dispatch( 'store-2' ).increment(); + } ); + + expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + count2: 1, + } ); + + act( () => { + registry.dispatch( 'store-3' ).increment(); + } ); + + expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + count2: 1, + } ); + } ); + + it( 're-calls the selector when deps changed', () => { + registry.registerStore( 'store-1', counterStore ); + registry.registerStore( 'store-2', counterStore ); + registry.registerStore( 'store-3', counterStore ); + + let renderer, dep, setDep; + const selectCount1AndDep = jest.fn(); + + const TestComponent = jest.fn( () => { + [ dep, setDep ] = useState( 0 ); + const state = useSelect( + ( select ) => + selectCount1AndDep() || { + count1: select( 'store-1' ).getCounter(), + dep, + }, + [ dep ] + ); + + return <div data={ state } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <TestComponent /> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( selectCount1AndDep ).toHaveBeenCalledTimes( 2 ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + dep: 0, + } ); + + act( () => { + setDep( 1 ); + } ); + + expect( selectCount1AndDep ).toHaveBeenCalledTimes( 4 ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + dep: 1, + } ); + + act( () => { + registry.dispatch( 'store-1' ).increment(); + } ); + + expect( selectCount1AndDep ).toHaveBeenCalledTimes( 5 ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 1, + dep: 1, + } ); + } ); + + it( 'handles registry selectors', () => { + const getCount1And2 = createRegistrySelector( + ( select ) => ( state ) => ( { + count1: state.counter, + count2: select( 'store-2' ).getCounter(), + } ) + ); + + registry.registerStore( 'store-1', { + ...counterStore, + selectors: { + ...counterStore.selectors, + getCount1And2, + }, + } ); + registry.registerStore( 'store-2', counterStore ); + + let renderer; + const selectCount1And2 = jest.fn(); + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => + selectCount1And2() || + select( 'store-1' ).getCount1And2(), + [] + ); + + return <div data={ state } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <TestComponent /> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( selectCount1And2 ).toHaveBeenCalledTimes( 2 ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + count2: 0, + } ); + + act( () => { + registry.dispatch( 'store-2' ).increment(); + } ); + + expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + count2: 1, + } ); + } ); + + it( 'handles conditional statements in selectors', () => { + registry.registerStore( 'store-1', counterStore ); + registry.registerStore( 'store-2', counterStore ); + + let renderer, shouldSelectCount1, toggle; + const selectCount1 = jest.fn(); + const selectCount2 = jest.fn(); + + const TestComponent = jest.fn( () => { + [ shouldSelectCount1, toggle ] = useReducer( + ( should ) => ! should, + false + ); + const state = useSelect( + ( select ) => { + if ( shouldSelectCount1 ) { + selectCount1(); + select( 'store-1' ).getCounter(); + return 'count1'; + } + + selectCount2(); + select( 'store-2' ).getCounter(); + return 'count2'; + }, + [ shouldSelectCount1 ] + ); + + return <div data={ state } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <TestComponent /> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( selectCount1 ).toHaveBeenCalledTimes( 0 ); + expect( selectCount2 ).toHaveBeenCalledTimes( 2 ); + expect( testInstance.findByType( 'div' ).props.data ).toBe( + 'count2' + ); + + act( () => { + toggle(); + } ); + + expect( selectCount1 ).toHaveBeenCalledTimes( 2 ); + expect( selectCount2 ).toHaveBeenCalledTimes( 2 ); + expect( testInstance.findByType( 'div' ).props.data ).toBe( + 'count1' + ); + } ); + + it( "handles subscriptions to the parent's stores", () => { + registry.registerStore( 'parent-store', counterStore ); + + const subRegistry = createRegistry( {}, registry ); + subRegistry.registerStore( 'child-store', counterStore ); + + let renderer; + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => ( { + parentCount: select( 'parent-store' ).getCounter(), + childCount: select( 'child-store' ).getCounter(), + } ), + [] + ); + + return <div data={ state } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <RegistryProvider value={ subRegistry }> + <TestComponent /> + </RegistryProvider> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + parentCount: 0, + childCount: 0, + } ); + + act( () => { + registry.dispatch( 'parent-store' ).increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + parentCount: 1, + childCount: 0, + } ); + } ); + + it( 'handles non-existing stores', () => { + registry.registerStore( 'store-1', counterStore ); + + let renderer; + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => ( { + count1: select( 'store-1' ).getCounter(), + blank: select( 'non-existing-store' )?.getCounter(), + } ), + [] + ); + + return <div data={ state } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <TestComponent /> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + blank: undefined, + } ); + + act( () => { + registry.dispatch( 'store-1' ).increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 1, + blank: undefined, + } ); + + // Test if the unsubscribers get called correctly. + renderer.unmount(); + } ); + + it( 'handles registration of a non-existing store during rendering', () => { + let renderer; + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => + select( 'not-yet-registered-store' )?.getCounter(), + [] + ); + + return <div data={ state } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <TestComponent /> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toBe( + undefined + ); + + act( () => { + registry.registerStore( + 'not-yet-registered-store', + counterStore + ); + } ); + + // This is not ideal, but is the way it's working before and we want to prevent breaking changes. + expect( testInstance.findByType( 'div' ).props.data ).toBe( + undefined + ); + + act( () => { + registry.dispatch( 'not-yet-registered-store' ).increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 ); + + // Test if the unsubscribers get called correctly. + renderer.unmount(); + } ); + + it( 'handles registration of a non-existing store of sub-registry during rendering', () => { + let renderer; + + const subRegistry = createRegistry( {}, registry ); + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => + select( + 'not-yet-registered-child-store' + )?.getCounter(), + [] + ); + + return <div data={ state } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <RegistryProvider value={ subRegistry }> + <TestComponent /> + </RegistryProvider> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toBe( + undefined + ); + + act( () => { + registry.registerStore( + 'not-yet-registered-child-store', + counterStore + ); + } ); + + // This is not ideal, but is the way it's working before and we want to prevent breaking changes. + expect( testInstance.findByType( 'div' ).props.data ).toBe( + undefined + ); + + act( () => { + registry + .dispatch( 'not-yet-registered-child-store' ) + .increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 ); + + // Test if the unsubscribers get called correctly. + renderer.unmount(); + } ); + + it( 'handles custom generic stores without a unsubscribe function', () => { + let renderer; + + function createCustomStore() { + let storeChanged = () => {}; + let counter = 0; + + const selectors = { + getCounter: () => counter, + }; + + const actions = { + increment: () => { + counter += 1; + storeChanged(); + }, + }; + + return { + getSelectors() { + return selectors; + }, + getActions() { + return actions; + }, + subscribe( listener ) { + storeChanged = listener; + }, + }; + } + + registry.registerGenericStore( + 'generic-store', + createCustomStore() + ); + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => select( 'generic-store' ).getCounter(), + [] + ); + + return <div data={ state } />; + } ); + + act( () => { + renderer = TestRenderer.create( + <RegistryProvider value={ registry }> + <TestComponent /> + </RegistryProvider> + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toBe( 0 ); + + act( () => { + registry.dispatch( 'generic-store' ).increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 ); + + expect( () => renderer.unmount() ).not.toThrow(); + } ); + } ); } ); diff --git a/packages/data/src/components/with-select/test/index.js b/packages/data/src/components/with-select/test/index.js index f9c67dd2431167..efb094b0b3159c 100644 --- a/packages/data/src/components/with-select/test/index.js +++ b/packages/data/src/components/with-select/test/index.js @@ -62,7 +62,10 @@ describe( 'withSelect', () => { ); } ); const testInstance = testRenderer.root; - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // Expected two times: + // - Once on initial render. + // - Once on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); // Wrapper is the enhanced component. Find props on the rendered child. @@ -122,7 +125,10 @@ describe( 'withSelect', () => { const testInstance = testRenderer.root; expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( mapDispatchToProps ).toHaveBeenCalledTimes( 1 ); // Simulate a click on the button @@ -131,8 +137,16 @@ describe( 'withSelect', () => { } ); expect( testInstance.findByType( 'button' ).props.children ).toBe( 1 ); + // 2 times = + // 1. Initial mount + // 2. When click handler is called expect( mapDispatchToProps ).toHaveBeenCalledTimes( 2 ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 3 ); + // 4 times + // - 1 on initial render + // - 1 on effect before subscription set. + // - 1 on click triggering subscription firing. + // - 1 on rerender. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 4 ); // verifies component only renders twice. expect( OriginalComponent ).toHaveBeenCalledTimes( 2 ); } ); @@ -209,7 +223,7 @@ describe( 'withSelect', () => { expect( testInstance.findByType( 'div' ).props.children ).toBe( 2 ); // Expected 3 times because: // - 1 on initial render - // - 1 on atom subscription (resolve). + // - 1 on effect before subscription set. // - 1 for the rerender because of the mapOutput change detected. expect( mapSelectToProps ).toHaveBeenCalledTimes( 3 ); expect( renderSpy ).toHaveBeenCalledTimes( 2 ); @@ -221,6 +235,10 @@ describe( 'withSelect', () => { } ); testInstance = testRenderer.root; expect( testInstance.findByType( 'div' ).props.children ).toBe( 4 ); + // Expected an additional 3 times because of the unmount and remount: + // - 1 on initial render + // - 1 on effect before subscription set. + // - once for the rerender because of the mapOutput change detected. expect( mapSelectToProps ).toHaveBeenCalledTimes( 6 ); expect( renderSpy ).toHaveBeenCalledTimes( 4 ); } ); @@ -264,7 +282,10 @@ describe( 'withSelect', () => { } ); const testInstance = testRenderer.root; - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); act( () => { @@ -276,7 +297,7 @@ describe( 'withSelect', () => { } ); expect( testInstance.findByType( 'div' ).props.children ).toBe( 10 ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); + expect( mapSelectToProps ).toHaveBeenCalledTimes( 3 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 2 ); } ); @@ -309,7 +330,10 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); act( () => { @@ -320,7 +344,7 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); } ); @@ -355,12 +379,15 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); registry.dispatch( 'demo' ).update(); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); + expect( mapSelectToProps ).toHaveBeenCalledTimes( 3 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); } ); @@ -389,7 +416,10 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); act( () => { @@ -400,7 +430,7 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); + expect( mapSelectToProps ).toHaveBeenCalledTimes( 3 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 2 ); } ); @@ -428,12 +458,15 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); store.dispatch( { type: 'dummy' } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); } ); @@ -473,7 +506,10 @@ describe( 'withSelect', () => { } ); const testInstance = testRenderer.root; - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); expect( @@ -491,7 +527,7 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); + expect( mapSelectToProps ).toHaveBeenCalledTimes( 3 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 2 ); expect( JSON.parse( testInstance.findByType( 'div' ).props.children ) @@ -539,7 +575,10 @@ describe( 'withSelect', () => { } ); const testInstance = testRenderer.root; - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); expect( testInstance.findByType( 'div' ).props.children ).toBe( 'Unknown' @@ -553,7 +592,7 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); + expect( mapSelectToProps ).toHaveBeenCalledTimes( 3 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 2 ); expect( testInstance.findByType( 'div' ).props.children ).toBe( 'OK' ); @@ -565,7 +604,7 @@ describe( 'withSelect', () => { ); } ); - expect( mapSelectToProps ).toHaveBeenCalledTimes( 3 ); + expect( mapSelectToProps ).toHaveBeenCalledTimes( 4 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 3 ); expect( testInstance.findByType( 'div' ).props.children ).toBe( 'Unknown' @@ -615,8 +654,11 @@ describe( 'withSelect', () => { ); } ); - expect( childMapSelectToProps ).toHaveBeenCalledTimes( 1 ); - expect( parentMapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( childMapSelectToProps ).toHaveBeenCalledTimes( 2 ); + expect( parentMapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( ChildOriginalComponent ).toHaveBeenCalledTimes( 1 ); expect( ParentOriginalComponent ).toHaveBeenCalledTimes( 1 ); @@ -624,8 +666,8 @@ describe( 'withSelect', () => { registry.dispatch( 'childRender' ).toggleRender(); } ); - expect( childMapSelectToProps ).toHaveBeenCalledTimes( 1 ); - expect( parentMapSelectToProps ).toHaveBeenCalledTimes( 3 ); + expect( childMapSelectToProps ).toHaveBeenCalledTimes( 2 ); + expect( parentMapSelectToProps ).toHaveBeenCalledTimes( 4 ); expect( ChildOriginalComponent ).toHaveBeenCalledTimes( 1 ); expect( ParentOriginalComponent ).toHaveBeenCalledTimes( 2 ); } ); @@ -663,7 +705,10 @@ describe( 'withSelect', () => { } ); const testInstance = testRenderer.root; - expect( mapSelectToProps ).toHaveBeenCalledTimes( 1 ); + // 2 times: + // - 1 on initial render + // - 1 on effect before subscription set. + expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 1 ); expect( testInstance.findByType( 'div' ).props ).toEqual( { @@ -685,8 +730,12 @@ describe( 'withSelect', () => { </RegistryProvider> ); } ); - - expect( mapSelectToProps ).toHaveBeenCalledTimes( 2 ); + // 4 times: + // - 1 on initial render + // - 1 on effect before subscription set. + // - 1 on re-render + // - 1 on effect before new subscription set (because registry has changed) + expect( mapSelectToProps ).toHaveBeenCalledTimes( 4 ); expect( OriginalComponent ).toHaveBeenCalledTimes( 2 ); expect( testInstance.findByType( 'div' ).props ).toEqual( { diff --git a/packages/data/src/factory.js b/packages/data/src/factory.js index 73dcc3b5d3dd15..400f4ffe15c0a7 100644 --- a/packages/data/src/factory.js +++ b/packages/data/src/factory.js @@ -36,11 +36,11 @@ * @return {Function} Registry selector that can be registered with a store. */ export function createRegistrySelector( registrySelector ) { - // create a selector function that is bound to the registry referenced by `selector.__unstableGetSelect` + // create a selector function that is bound to the registry referenced by `selector.registry` // and that has the same API as a regular selector. Binding it in such a way makes it // possible to call the selector directly from another selector. const selector = ( ...args ) => - registrySelector( selector.__unstableGetSelect )( ...args ); + registrySelector( selector.registry.select )( ...args ); /** * Flag indicating that the selector is a registry selector that needs the correct registry diff --git a/packages/data/src/index.js b/packages/data/src/index.js index 67552b1b487e71..457868239baae3 100644 --- a/packages/data/src/index.js +++ b/packages/data/src/index.js @@ -26,7 +26,6 @@ export { createRegistry } from './registry'; export { createRegistrySelector, createRegistryControl } from './factory'; export { controls } from './controls'; export { default as createReduxStore } from './redux-store'; -export { default as __experimentalCreateAtomicStore } from './atomic-store'; /** * Object of available plugins to use with a registry. @@ -46,7 +45,7 @@ export { plugins }; * * @example * ```js - * import { combineReducers, registerStore } from '@wordpress/data'; + * import { combineReducers, createReduxStore, register } from '@wordpress/data'; * * const prices = ( state = {}, action ) => { * return action.type === 'SET_PRICE' ? @@ -63,12 +62,13 @@ export { plugins }; * state; * }; * - * registerStore( 'my-shop', { + * const store = createReduxStore( 'my-shop', { * reducer: combineReducers( { * prices, * discountPercent, * } ), * } ); + * register( store ); * ``` * * @return {Function} A reducer that invokes every reducer inside the reducers @@ -77,11 +77,12 @@ export { plugins }; export { combineReducers }; /** - * Given the name of a registered store, returns an object of the store's selectors. + * Given the name or definition of a registered store, returns an object of the store's selectors. * The selector functions are been pre-bound to pass the current state automatically. * As a consumer, you need only pass arguments of the selector, if applicable. * - * @param {string} name Store name. + * @param {string|WPDataStore} storeNameOrDefinition Unique namespace identifier for the store + * or the store definition. * * @example * ```js @@ -100,7 +101,8 @@ export const select = defaultRegistry.select; * and modified so that they return promises that resolve to their eventual values, * after any resolvers have ran. * - * @param {string} name Store name. + * @param {string|WPDataStore} storeNameOrDefinition Unique namespace identifier for the store + * or the store definition. * * @example * ```js @@ -121,7 +123,8 @@ export const __experimentalResolveSelect = * Note: Action creators returned by the dispatch will return a promise when * they are called. * - * @param {string} name Store name. + * @param {string|WPDataStore} storeNameOrDefinition Unique namespace identifier for the store + * or the store definition. * * @example * ```js @@ -158,6 +161,8 @@ export const subscribe = defaultRegistry.subscribe; /** * Registers a generic store. * + * @deprecated Use `register` instead. + * * @param {string} key Store registry key. * @param {Object} config Configuration (getSelectors, getActions, subscribe). */ @@ -166,6 +171,8 @@ export const registerGenericStore = defaultRegistry.registerGenericStore; /** * Registers a standard `@wordpress/data` store. * + * @deprecated Use `register` instead. + * * @param {string} storeName Unique namespace identifier for the store. * @param {Object} options Store description (reducer, actions, selectors, resolvers). * @@ -195,7 +202,7 @@ export const use = defaultRegistry.use; * getValue: ( state ) => state, * }, * } ); - * registry.register( store ); + * register( store ); * ``` * * @param {WPDataStore} store Store definition. diff --git a/packages/data/src/redux-store/index.js b/packages/data/src/redux-store/index.js index e0894a8fd7d5d4..edfd3083712ccb 100644 --- a/packages/data/src/redux-store/index.js +++ b/packages/data/src/redux-store/index.js @@ -84,6 +84,7 @@ export default function createReduxStore( key, options ) { const store = instantiateReduxStore( key, options, registry ); const resolversCache = createResolversCache(); + let resolvers; const actions = mapActions( { ...metadataActions, @@ -91,123 +92,34 @@ export default function createReduxStore( key, options ) { }, store ); - - // Inject registry into selectors - // It is important that this injection happens first because __unstableGetSelect - // is injected using a mutation of the original selector function. - const selectorsWithRegistry = mapValues( - options.selectors, - ( selector ) => { - if ( selector.isRegistrySelector ) { - selector.__unstableGetSelect = registry.select; - } - return selector; - } - ); - - // Inject state into selectors - const injectState = ( getState, selector ) => { - const mappedSelector = ( ...args ) => - selector( getState(), ...args ); - mappedSelector.__unstableRegistrySelector = - selector.__unstableRegistrySelector; - return mappedSelector; - }; - const selectorsWithState = { - ...mapValues( metadataSelectors, ( selector ) => - injectState( - () => store.__unstableOriginalGetState().metadata, - selector - ) - ), - ...mapValues( selectorsWithRegistry, ( selector ) => - injectState( - () => store.__unstableOriginalGetState().root, - selector - ) - ), - }; - - // Normalize resolvers - const resolvers = mapValues( options.resolvers, ( resolver ) => { - if ( resolver.fulfill ) { - return resolver; - } - - return { - ...resolver, // copy the enumerable properties of the resolver function - fulfill: resolver, // add the fulfill method - }; - } ); - - // Inject resolvers fullfilment call into selectors. - const selectors = mapValues( - selectorsWithState, - ( selector, selectorName ) => { - const resolver = resolvers[ selectorName ]; - if ( ! resolver ) { - selector.hasResolver = false; - return selector; - } - - async function fulfillSelector( args ) { - const state = store.getState(); - if ( - resolversCache.isRunning( selectorName, args ) || - ( typeof resolver.isFulfilled === 'function' && - resolver.isFulfilled( state, ...args ) ) - ) { - return; - } - - const { metadata } = store.__unstableOriginalGetState(); - - if ( - metadataSelectors.hasStartedResolution( - metadata, - selectorName, - args - ) - ) { - return; + let selectors = mapSelectors( + { + ...mapValues( + metadataSelectors, + ( selector ) => ( state, ...args ) => + selector( state.metadata, ...args ) + ), + ...mapValues( options.selectors, ( selector ) => { + if ( selector.isRegistrySelector ) { + selector.registry = registry; } - resolversCache.markAsRunning( selectorName, args ); - - setTimeout( async () => { - resolversCache.clear( selectorName, args ); - store.dispatch( - metadataActions.startResolution( - selectorName, - args - ) - ); - await fulfillResolver( - store, - resolvers, - selectorName, - ...args - ); - store.dispatch( - metadataActions.finishResolution( - selectorName, - args - ) - ); - } ); - } - - const mappedSelector = ( ...args ) => { - fulfillSelector( args ); - return selector( ...args ); - }; - mappedSelector.__unstableRegistrySelector = - selector.__unstableRegistrySelector; - mappedSelector.hasResolver = true; - - return mappedSelector; - } + return ( state, ...args ) => + selector( state.root, ...args ); + } ), + }, + store ); + if ( options.resolvers ) { + const result = mapResolvers( + options.resolvers, + selectors, + store, + resolversCache + ); + resolvers = result.resolvers; + selectors = result.selectors; + } const getSelectors = () => selectors; const getActions = () => actions; @@ -224,7 +136,7 @@ export default function createReduxStore( key, options ) { store && ( ( listener ) => { let lastState = store.__unstableOriginalGetState(); - store.subscribe( () => { + return store.subscribe( () => { const state = store.__unstableOriginalGetState(); const hasChanged = state !== lastState; lastState = state; @@ -304,6 +216,41 @@ function instantiateReduxStore( key, options, registry ) { ); } +/** + * Maps selectors to a store. + * + * @param {Object} selectors Selectors to register. Keys will be used as the + * public facing API. Selectors will get passed the + * state as first argument. + * @param {Object} store The store to which the selectors should be mapped. + * @return {Object} Selectors mapped to the provided store. + */ +function mapSelectors( selectors, store ) { + const createStateSelector = ( registrySelector ) => { + const selector = function runSelector() { + // This function is an optimized implementation of: + // + // selector( store.getState(), ...arguments ) + // + // Where the above would incur an `Array#concat` in its application, + // the logic here instead efficiently constructs an arguments array via + // direct assignment. + const argsLength = arguments.length; + const args = new Array( argsLength + 1 ); + args[ 0 ] = store.__unstableOriginalGetState(); + for ( let i = 0; i < argsLength; i++ ) { + args[ i + 1 ] = arguments[ i ]; + } + + return registrySelector( ...args ); + }; + selector.hasResolver = false; + return selector; + }; + + return mapValues( selectors, createStateSelector ); +} + /** * Maps actions to dispatch from a given store. * @@ -319,6 +266,93 @@ function mapActions( actions, store ) { return mapValues( actions, createBoundAction ); } +/** + * Returns resolvers with matched selectors for a given namespace. + * Resolvers are side effects invoked once per argument set of a given selector call, + * used in ensuring that the data needs for the selector are satisfied. + * + * @param {Object} resolvers Resolvers to register. + * @param {Object} selectors The current selectors to be modified. + * @param {Object} store The redux store to which the resolvers should be mapped. + * @param {Object} resolversCache Resolvers Cache. + */ +function mapResolvers( resolvers, selectors, store, resolversCache ) { + // The `resolver` can be either a function that does the resolution, or, in more advanced + // cases, an object with a `fullfill` method and other optional methods like `isFulfilled`. + // Here we normalize the `resolver` function to an object with `fulfill` method. + const mappedResolvers = mapValues( resolvers, ( resolver ) => { + if ( resolver.fulfill ) { + return resolver; + } + + return { + ...resolver, // copy the enumerable properties of the resolver function + fulfill: resolver, // add the fulfill method + }; + } ); + + const mapSelector = ( selector, selectorName ) => { + const resolver = resolvers[ selectorName ]; + if ( ! resolver ) { + selector.hasResolver = false; + return selector; + } + + const selectorResolver = ( ...args ) => { + async function fulfillSelector() { + const state = store.getState(); + if ( + resolversCache.isRunning( selectorName, args ) || + ( typeof resolver.isFulfilled === 'function' && + resolver.isFulfilled( state, ...args ) ) + ) { + return; + } + + const { metadata } = store.__unstableOriginalGetState(); + + if ( + metadataSelectors.hasStartedResolution( + metadata, + selectorName, + args + ) + ) { + return; + } + + resolversCache.markAsRunning( selectorName, args ); + + setTimeout( async () => { + resolversCache.clear( selectorName, args ); + store.dispatch( + metadataActions.startResolution( selectorName, args ) + ); + await fulfillResolver( + store, + mappedResolvers, + selectorName, + ...args + ); + store.dispatch( + metadataActions.finishResolution( selectorName, args ) + ); + } ); + } + + fulfillSelector( ...args ); + return selector( ...args ); + }; + selectorResolver.hasResolver = true; + return selectorResolver; + }; + + return { + resolvers: mappedResolvers, + selectors: mapValues( selectors, mapSelector ), + }; +} + /** * Calls a resolver given arguments * diff --git a/packages/data/src/redux-store/test/index.js b/packages/data/src/redux-store/test/index.js index 73c907eaf341c6..0198f5972004bf 100644 --- a/packages/data/src/redux-store/test/index.js +++ b/packages/data/src/redux-store/test/index.js @@ -1,21 +1,10 @@ -/** - * WordPress dependencies - */ -import { createAtomRegistry, createDerivedAtom } from '@wordpress/stan'; - /** * Internal dependencies */ import { createRegistry } from '../../registry'; -import { createRegistryControl, createRegistrySelector } from '../../factory'; +import { createRegistryControl } from '../../factory'; jest.useFakeTimers(); -async function flushImmediatesAndTicks( count = 1 ) { - for ( let i = 0; i < count; i++ ) { - await jest.runAllTicks(); - await jest.runAllImmediates(); - } -} describe( 'controls', () => { let registry; @@ -244,162 +233,4 @@ describe( 'controls', () => { ); } ); } ); - - describe( 'atomSelectors', () => { - const createUseSelectAtom = ( mapSelectToProps ) => { - return createDerivedAtom( ( { get } ) => { - const current = registry.__internalGetAtomResolver(); - registry.__internalSetAtomResolver( get ); - const ret = mapSelectToProps( registry.select ); - registry.__internalSetAtomResolver( current ); - return ret; - } ); - }; - - beforeEach( () => { - registry = createRegistry(); - const action1 = ( value ) => ( { type: 'set', value } ); - registry.registerStore( 'store1', { - reducer: ( state = 'default', action ) => { - if ( action.type === 'set' ) { - return action.value; - } - return state; - }, - actions: { - set: action1, - }, - selectors: { - getValue( state ) { - return state; - }, - }, - } ); - } ); - - it( 'should subscribe to atom selectors', async () => { - const atomRegistry = createAtomRegistry(); - const atom = createUseSelectAtom( ( select ) => { - return { - value: select( 'store1' ).getValue(), - }; - } ); - const unsubscribe = atomRegistry.subscribe( atom, () => {} ); - await flushImmediatesAndTicks(); - expect( atomRegistry.get( atom ).value ).toEqual( 'default' ); - registry.dispatch( 'store1' ).set( 'new' ); - await flushImmediatesAndTicks(); - expect( atomRegistry.get( atom ).value ).toEqual( 'new' ); - unsubscribe(); - } ); - - it( 'should subscribe to not subscribe to unrelated stores', async () => { - const action1 = ( value ) => ( { type: 'set', value } ); - registry.registerStore( 'store2', { - reducer: ( state = 'default', action ) => { - if ( action.type === 'set' ) { - return action.value; - } - return state; - }, - actions: { - set: action1, - }, - selectors: { - getValue( state ) { - return state; - }, - }, - } ); - - const atom = createUseSelectAtom( ( select ) => { - return { - value: select( 'store1' ).getValue(), - }; - } ); - const atomRegistry = createAtomRegistry(); - const update = jest.fn(); - const unsubscribe = atomRegistry.subscribe( atom, update ); - await flushImmediatesAndTicks( 2 ); - expect( atomRegistry.get( atom ).value ).toEqual( 'default' ); - // Reset the call that happens for initialization. - update.mockClear(); - registry.dispatch( 'store2' ).set( 'new' ); - await flushImmediatesAndTicks( 2 ); - expect( update ).not.toHaveBeenCalled(); - unsubscribe(); - } ); - - it( 'should subscribe to sub stores for registry selectors', async () => { - registry.registerStore( 'store2', { - reducer: () => 'none', - actions: {}, - selectors: { - getSubStoreValue: createRegistrySelector( - ( select ) => () => { - return select( 'store1' ).getValue(); - } - ), - }, - } ); - - const atomRegistry = createAtomRegistry(); - const atom = createUseSelectAtom( ( select ) => { - return { - value: select( 'store2' ).getSubStoreValue(), - }; - } ); - - const unsubscribe = atomRegistry.subscribe( atom, () => {} ); - await flushImmediatesAndTicks( 10 ); - expect( atomRegistry.get( atom ).value ).toEqual( 'default' ); - registry.dispatch( 'store1' ).set( 'new' ); - await flushImmediatesAndTicks( 10 ); - expect( atomRegistry.get( atom ).value ).toEqual( 'new' ); - unsubscribe(); - } ); - - it( 'should subscribe to nested sub stores for registry selectors', async () => { - registry.registerStore( 'store2', { - reducer: () => 'none', - actions: {}, - selectors: { - getSubStoreValue: createRegistrySelector( - ( select ) => () => { - return select( 'store1' ).getValue(); - } - ), - }, - } ); - - const getSubStoreValue = createRegistrySelector( - ( select ) => () => { - return select( 'store2' ).getSubStoreValue(); - } - ); - registry.registerStore( 'store3', { - reducer: () => 'none', - actions: {}, - selectors: { - getSubStoreValue, - getAdjacentSelectValue: () => getSubStoreValue(), - }, - } ); - - const atomRegistry = createAtomRegistry(); - const atom = createUseSelectAtom( ( select ) => { - return { - value: select( 'store3' ).getSubStoreValue(), - }; - } ); - - const unsubscribe = atomRegistry.subscribe( atom, () => {} ); - await flushImmediatesAndTicks( 4 ); - expect( atomRegistry.get( atom ).value ).toEqual( 'default' ); - registry.dispatch( 'store1' ).set( 'new' ); - await flushImmediatesAndTicks( 4 ); - expect( atomRegistry.get( atom ).value ).toEqual( 'new' ); - unsubscribe(); - } ); - } ); } ); diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index 0585011160ca9e..6a0ca9fc4a7a4d 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -4,11 +4,6 @@ import { omit, without, mapValues, isObject } from 'lodash'; import memize from 'memize'; -/** - * WordPress dependencies - */ -import { createStoreAtom } from '@wordpress/stan'; - /** * Internal dependencies */ @@ -54,8 +49,8 @@ import createCoreDataStore from './store'; */ export function createRegistry( storeConfigs = {}, parent = null ) { const stores = {}; - const storesAtoms = {}; let listeners = []; + const __experimentalListeningStores = new Set(); /** * Global listener called for each store's update. @@ -79,12 +74,6 @@ export function createRegistry( storeConfigs = {}, parent = null ) { }; }; - /** - * This is used to track the current atom resolver - * and inject it into registry selectors. - */ - let currentAtomResolver; - /** * Calls a selector given the current state and extra arguments. * @@ -97,25 +86,20 @@ export function createRegistry( storeConfigs = {}, parent = null ) { const storeName = isObject( storeNameOrDefinition ) ? storeNameOrDefinition.name : storeNameOrDefinition; - + __experimentalListeningStores.add( storeName ); const store = stores[ storeName ]; if ( store ) { - // If it's not an atomic store subscribe to the store. - if ( - ! store.__internalIsAtomic && - registry.__internalGetAtomResolver() - ) { - registry.__internalGetAtomResolver()( - registry.__internalGetAtomForStore( storeName ) - ); - } - return store.getSelectors(); } - if ( parent ) { - return parent.select( storeName ); - } + return parent && parent.select( storeName ); + } + + function __experimentalMarkListeningStores( callback, ref ) { + __experimentalListeningStores.clear(); + const result = callback.call( this ); + ref.current = Array.from( __experimentalListeningStores ); + return result; } const getResolveSelectors = memize( @@ -165,13 +149,13 @@ export function createRegistry( storeConfigs = {}, parent = null ) { * and modified so that they return promises that resolve to their eventual values, * after any resolvers have ran. * - * @param {string|Object} storeName Unique namespace identifier for the store - * or the store definition. + * @param {string|WPDataStore} storeNameOrDefinition Unique namespace identifier for the store + * or the store definition. * * @return {Object} Each key of the object matches the name of a selector. */ - function __experimentalResolveSelect( storeName ) { - return getResolveSelectors( select( storeName ) ); + function __experimentalResolveSelect( storeNameOrDefinition ) { + return getResolveSelectors( select( storeNameOrDefinition ) ); } /** @@ -226,12 +210,6 @@ export function createRegistry( storeConfigs = {}, parent = null ) { throw new TypeError( 'config.subscribe must be a function' ); } stores[ key ] = config; - storesAtoms[ key ] = createStoreAtom( - config.subscribe, - () => null, - () => {}, - { id: key } - ); config.subscribe( globalListener ); } @@ -244,13 +222,27 @@ export function createRegistry( storeConfigs = {}, parent = null ) { registerGenericStore( store.name, store.instantiate( registry ) ); } - function __internalGetAtomForStore( key ) { - const atom = storesAtoms[ key ]; - if ( atom ) { - return atom; + /** + * Subscribe handler to a store. + * + * @param {string[]} storeName The store name. + * @param {Function} handler The function subscribed to the store. + * @return {Function} A function to unsubscribe the handler. + */ + function __experimentalSubscribeStore( storeName, handler ) { + if ( storeName in stores ) { + return stores[ storeName ].subscribe( handler ); } - return parent.__internalGetAtomForStore( key ); + // Trying to access a store that hasn't been registered, + // this is a pattern rarely used but seen in some places. + // We fallback to regular `subscribe` here for backward-compatibility for now. + // See https://github.com/WordPress/gutenberg/pull/27466 for more info. + if ( ! parent ) { + return subscribe( handler ); + } + + return parent.__experimentalSubscribeStore( storeName, handler ); } let registry = { @@ -263,16 +255,8 @@ export function createRegistry( storeConfigs = {}, parent = null ) { dispatch, use, register, - __internalGetAtomForStore, - __internalGetAtomResolver() { - return currentAtomResolver; - }, - __internalSetAtomResolver( resolver ) { - if ( parent ) { - parent.__internalSetAtomResolver( resolver ); - } - currentAtomResolver = resolver; - }, + __experimentalMarkListeningStores, + __experimentalSubscribeStore, }; /** diff --git a/packages/data/src/types.d.ts b/packages/data/src/types.d.ts new file mode 100644 index 00000000000000..d3456f6fb79bd7 --- /dev/null +++ b/packages/data/src/types.d.ts @@ -0,0 +1,32 @@ +export type WPDataFunctionOrGeneratorArray = Array< Function | Generator >; +export type WPDataFunctionArray = Array< Function >; + +export interface WPDataAttachedStore { + getSelectors: () => WPDataFunctionArray; + getActions: () => WPDataFunctionArray; + subscribe: ( listener: () => void ) => () => void; +} + +export interface WPDataStore { + /** + * Store Name + */ + name: string; + + /** + * Store configuration object. + */ + instantiate: ( registry: WPDataRegistry ) => WPDataAttachedStore; +} + +export interface WPDataReduxStoreConfig { + reducer: ( state: any, action: any ) => any; + actions?: WPDataFunctionOrGeneratorArray; + resolvers?: WPDataFunctionOrGeneratorArray; + selectors?: WPDataFunctionArray; + controls?: WPDataFunctionArray; +} + +export interface WPDataRegistry { + register: ( store: WPDataStore ) => void; +} diff --git a/packages/data/src/types.ts b/packages/data/src/types.ts deleted file mode 100644 index 7663d9c1dca480..00000000000000 --- a/packages/data/src/types.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * WordPress dependencies - */ -import type { - WPAtom, - WPAtomSelector, - WPAtomResolver, - WPAtomUpdater, -} from '@wordpress/stan'; - -export type WPDataFunctionOrGeneratorArray = { - [ key: string ]: Function | Generator; -}; -export type WPDataFunctionArray = { [ key: string ]: Function }; - -export interface WPDataAttachedStore { - getSelectors: () => WPDataFunctionArray; - getActions: () => WPDataFunctionArray; - subscribe: ( listener: () => void ) => () => void; - __internalIsAtomic?: boolean; -} - -export interface WPDataStore { - /** - * Store Name - */ - name: string; - - /** - * Store configuration object. - */ - instantiate: ( registry: WPDataRegistry ) => WPDataAttachedStore; -} - -export interface WPDataReduxStoreConfig { - reducer: ( state: any, action: any ) => any; - actions?: WPDataFunctionOrGeneratorArray; - resolvers?: WPDataFunctionOrGeneratorArray; - selectors?: WPDataFunctionArray; - controls?: WPDataFunctionArray; -} - -export type WPDataAtomicStoreAction< T > = ( - ...args: any[] -) => ( props: { get: WPAtomResolver< T >; set: WPAtomUpdater< T > } ) => void; - -export interface WPDataAtomicStoreConfig { - rootAtoms: Array< WPAtom< any > >; - actions?: { [ key: string ]: WPDataAtomicStoreAction< any > }; - selectors?: { [ key: string ]: (...args:any[]) => WPAtomSelector<any> }; -} - -export interface WPDataRegistry { - /** - * Registers a store. - */ - register: ( store: WPDataStore ) => void; - - /** - * For registry selectors we need to be able to inject the atom resolver. - * This setter/getter allows us to so. - */ - __internalGetAtomResolver: () => WPAtomResolver< any >; - - /** - * Sets the current atom resolver in the registry. - */ - __internalSetAtomResolver: ( resolver: WPAtomResolver< any > ) => void; - - /** - * Retrieve or creates an atom per store. - * This atom allows selectors to refresh when any change happen on that store. - */ - __internalGetAtomForStore: ( storeName: string ) => WPAtom< any >; -} diff --git a/packages/data/tsconfig.json b/packages/data/tsconfig.json deleted file mode 100644 index 75b18c62f6ce5e..00000000000000 --- a/packages/data/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": "src", - "declarationDir": "build-types" - }, - "references": [ - { "path": "../stan" }, - ], - "include": [ - "src/types.ts", - "src/atomic-store/*", - ], - "exclude": [ - "src/**/test" - ] -} diff --git a/packages/date/README.md b/packages/date/README.md index 4708927a010337..954ea388ece67d 100644 --- a/packages/date/README.md +++ b/packages/date/README.md @@ -28,7 +28,7 @@ _Related_ _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|null)`: Date object or ISO string, parsable by moment.js. +- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js. - _timezone_ `(string|number|null)`: Timezone to output result in or a UTC offset. Defaults to timezone from site. _Returns_ @@ -50,7 +50,7 @@ _Related_ _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|null)`: Date object or string, parsable by moment.js. +- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js. - _timezone_ `(string|number|boolean|null)`: Timezone to output result in or a UTC offset. Defaults to timezone from site. Notice: `boolean` is effectively deprecated, but still supported for backward compatibility reasons. _Returns_ @@ -64,7 +64,7 @@ Formats a date. Does not alter the date's timezone. _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|null)`: Date object or ISO string. +- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js. _Returns_ @@ -89,7 +89,7 @@ Formats a date (like `date()` in PHP), in the UTC timezone. _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|null)`: Date object or ISO string. +- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js. _Returns_ @@ -103,7 +103,7 @@ and using the UTC timezone. _Parameters_ - _dateFormat_ `string`: PHP-style formatting string. See php.net/date. -- _dateValue_ `(Date|string|null)`: Date object or ISO string. +- _dateValue_ `(Date|string|Moment|null)`: Date object or string, parsable by moment.js. _Returns_ @@ -115,7 +115,7 @@ Check whether a date is considered in the future according to the WordPress sett _Parameters_ -- _dateValue_ `(string|Date)`: Date String or Date object in the Defined WP Timezone. +- _dateValue_ `string`: Date String or Date object in the Defined WP Timezone. _Returns_ @@ -129,10 +129,6 @@ _Parameters_ - _dateSettings_ `Object`: Settings, including locale data. -<a name="zonedTimeToUtc" href="#zonedTimeToUtc">#</a> **zonedTimeToUtc** - -Undocumented declaration. - <!-- END TOKEN(Autogenerated API docs) --> diff --git a/packages/date/package.json b/packages/date/package.json index 30cd0f04811401..6ec2db11cb397c 100644 --- a/packages/date/package.json +++ b/packages/date/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/date", - "version": "3.12.0", + "version": "3.13.0", "description": "Date module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,9 +22,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", - "date-fns": "^2.16.1", - "date-fns-tz": "^1.0.12", + "@babel/runtime": "^7.12.5", "moment": "^2.22.1", "moment-timezone": "^0.5.31" }, diff --git a/packages/date/src/index.js b/packages/date/src/index.js index 510ae811d0a3ae..4145e3fcd5223d 100644 --- a/packages/date/src/index.js +++ b/packages/date/src/index.js @@ -1,23 +1,13 @@ /** * External dependencies */ -import { - addHours, - format as dateFnsFormat, - getDaysInMonth, - isFuture, - isLeapYear, - parseISO, -} from 'date-fns'; -import { - format as formatTZ, - utcToZonedTime, - zonedTimeToUtc, - toDate, -} from 'date-fns-tz'; -import originalLocale from 'date-fns/locale/en-US/index'; -import buildLocalizeFn from 'date-fns/locale/_lib/buildLocalizeFn'; -import buildFormatLongFn from 'date-fns/locale/_lib/buildFormatLongFn'; +import momentLib from 'moment'; +import 'moment-timezone/moment-timezone'; +import 'moment-timezone/moment-timezone-utils'; + +/** @typedef {import('moment').Moment} Moment */ + +const WP_ZONE = 'WP'; // This regular expression tests positive for UTC offsets as described in ISO 8601. // See: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC @@ -85,13 +75,14 @@ let settings = { }, }, formats: { - time: 'g:i a', + time: 'g: i a', date: 'F j, Y', datetime: 'F j, Y g: i a', datetimeAbbreviated: 'M j, Y g: i a', }, timezone: { offset: '0', string: '', abbr: '' }, }; + /** * Adds a locale to moment, using the format supplied by `wp_localize_script()`. * @@ -99,6 +90,41 @@ let settings = { */ export function setSettings( dateSettings ) { settings = dateSettings; + + // Backup and restore current locale. + const currentLocale = momentLib.locale(); + momentLib.updateLocale( dateSettings.l10n.locale, { + // Inherit anything missing from the default locale. + parentLocale: currentLocale, + months: dateSettings.l10n.months, + monthsShort: dateSettings.l10n.monthsShort, + weekdays: dateSettings.l10n.weekdays, + weekdaysShort: dateSettings.l10n.weekdaysShort, + meridiem( hour, minute, isLowercase ) { + if ( hour < 12 ) { + return isLowercase + ? dateSettings.l10n.meridiem.am + : dateSettings.l10n.meridiem.AM; + } + return isLowercase + ? dateSettings.l10n.meridiem.pm + : dateSettings.l10n.meridiem.PM; + }, + longDateFormat: { + LT: dateSettings.formats.time, + LTS: null, + L: null, + LL: dateSettings.formats.date, + LLL: dateSettings.formats.datetime, + LLLL: null, + }, + // From human_time_diff? + // Set to `(number, withoutSuffix, key, isFuture) => {}` instead. + relativeTime: dateSettings.l10n.relative, + } ); + momentLib.locale( currentLocale ); + + setupWPTimezone(); } /** @@ -110,6 +136,18 @@ export function __experimentalGetSettings() { return settings; } +function setupWPTimezone() { + // Create WP timezone based off dateSettings. + momentLib.tz.add( + momentLib.tz.pack( { + name: WP_ZONE, + abbrs: [ WP_ZONE ], + untils: [ null ], + offsets: [ -settings.timezone.offset * 60 || 0 ], + } ) + ); +} + // Date constants. /** * Number of seconds in one minute. @@ -144,46 +182,41 @@ const HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS; */ const formatMap = { // Day - d: 'dd', - D: 'EEE', - j: 'd', - l: 'EEEE', - N: 'i', + d: 'DD', + D: 'ddd', + j: 'D', + l: 'dddd', + N: 'E', /** * Gets the ordinal suffix. * - * @param {Date} dateValue Date ISO string or object. + * @param {Moment} momentDate Moment instance. * * @return {string} Formatted date. */ - S( dateValue ) { - const num = dateFnsFormat( dateValue, 'd' ); - const withOrdinal = dateFnsFormat( dateValue, 'do' ); + S( momentDate ) { + // Do - D + const num = momentDate.format( 'D' ); + const withOrdinal = momentDate.format( 'Do' ); return withOrdinal.replace( num, '' ); }, - /** - * Returns the day of the week (zero-indexed). - * - * @param {string} dateValue - */ - w( dateValue ) { - return `${ parseInt( dateFnsFormat( dateValue, 'i' ), 10 ) - 1 }`; - }, + + w: 'd', /** * Gets the day of the year (zero-indexed). * - * @param {Date} dateValue Date ISO string or object. + * @param {Moment} momentDate Moment instance. * * @return {string} Formatted date. */ - z( dateValue ) { + z( momentDate ) { // DDD - 1 - return `${ parseInt( dateFnsFormat( dateValue, 'DDD' ), 10 ) - 1 }`; + return '' + parseInt( momentDate.format( 'DDD' ), 10 ) - 1; }, // Week - W: 'II', + W: 'W', // Month F: 'MMMM', @@ -193,58 +226,50 @@ const formatMap = { /** * Gets the days in the month. * - * @param {Date} dateValue Date ISO string or object. + * @param {Moment} momentDate Moment instance. * * @return {string} Formatted date. */ - t( dateValue ) { - return getDaysInMonth( dateValue ); + t( momentDate ) { + return momentDate.daysInMonth(); }, // Year /** * Gets whether the current year is a leap year. * - * @param {Date} dateValue Date ISO string or object. + * @param {Moment} momentDate Moment instance. * * @return {string} Formatted date. */ - L( dateValue ) { - return isLeapYear( dateValue ) ? '1' : '0'; + L( momentDate ) { + return momentDate.isLeapYear() ? '1' : '0'; }, - o: 'R', - Y: 'yyyy', - y: 'yy', + o: 'GGGG', + Y: 'YYYY', + y: 'YY', // Time - a( dateValue ) { - return formatTZ( dateValue, 'aa', { - timeZone: getActualTimezone(), - } ).toLowerCase(); - }, - A: 'bb', + a: 'a', + A: 'A', /** - * Gets the given time in Swatch Internet Time (.beats). + * Gets the current time in Swatch Internet Time (.beats). * - * @param {Date} dateValue Date ISO string or object. + * @param {Moment} momentDate Moment instance. * * @return {string} Formatted date. */ - B( dateValue ) { - const parsedDate = addHours( zonedTimeToUtc( dateValue ), 1 ); - const seconds = parseInt( dateFnsFormat( parsedDate, 's' ), 10 ), - minutes = parseInt( dateFnsFormat( parsedDate, 'm' ), 10 ), - hours = parseInt( dateFnsFormat( parsedDate, 'H' ), 10 ); - - /* - * Rounding up to match results on the same timestamp using - * PHP's date_format. - */ - return Math.ceil( + B( momentDate ) { + const timezoned = momentLib( momentDate ).utcOffset( 60 ); + const seconds = parseInt( timezoned.format( 's' ), 10 ), + minutes = parseInt( timezoned.format( 'm' ), 10 ), + hours = parseInt( timezoned.format( 'H' ), 10 ); + return parseInt( ( seconds + minutes * MINUTE_IN_SECONDS + hours * HOUR_IN_SECONDS ) / - 86.4 + 86.4, + 10 ); }, g: 'h', @@ -256,49 +281,32 @@ const formatMap = { u: 'SSSSSS', v: 'SSS', // Timezone - /** - * Return the timezone identifier for the given date. - * - * @param {Date} dateValue Date ISO string or object. - * - * @return {string} Formatted date. - */ - e( dateValue ) { - return formatTZ( dateValue, 'zzzz', { timeZone: getActualTimezone() } ); - }, + e: 'zz', /** * Gets whether the timezone is in DST currently. * + * @param {Moment} momentDate Moment instance. * * @return {string} Formatted date. */ - I() { - return ''; // @todo - }, - O( dateValue ) { - return formatTZ( dateValue, 'xx', { timeZone: getActualTimezone() } ); - }, - P( dateValue ) { - return formatTZ( dateValue, 'xxx', { timeZone: getActualTimezone() } ); - }, - T( dateValue ) { - return formatTZ( dateValue, 'z', { timeZone: getActualTimezone() } ); + I( momentDate ) { + return momentDate.isDST() ? '1' : '0'; }, + O: 'ZZ', + P: 'Z', + T: 'z', /** * Gets the timezone offset in seconds. * - * @param {Date|string} dateValue Date ISO string or object. + * @param {Moment} momentDate Moment instance. * * @return {string} Formatted date. */ - Z( dateValue ) { + Z( momentDate ) { // Timezone offset in seconds. - const offset = dateFnsFormat( - utcToZonedTime( dateValue, 'UTC' ), - 'XXX' - ); + const offset = momentDate.format( 'Z' ); const sign = offset[ 0 ] === '-' ? -1 : 1; - const parts = offset.substring( 1 ).split( ':' ).map( Number ); + const parts = offset.substring( 1 ).split( ':' ); return ( sign * ( parts[ 0 ] * HOUR_IN_MINUTES + parts[ 1 ] ) * @@ -306,203 +314,50 @@ const formatMap = { ); }, // Full date/time - c( dateValue ) { - return formatTZ( - utcToZonedTime( - zonedTimeToUtc( dateValue, getActualTimezone() ), - 'UTC' - ), // Offsets the time to the correct timezone - "yyyy-MM-dd'T'HH:mm:ssXXX", - { - timeZone: getActualTimezone(), // Adds the timezone offset to the Date object that will be formatted. - } - ); - }, // .toISOString - r( dateValue ) { - return formatTZ( - utcToZonedTime( - zonedTimeToUtc( dateValue, getActualTimezone() ), - 'UTC' - ), // Offsets the time to the correct timezone - 'iii, d MMM yyyy HH:mm:ss XX', - { - timeZone: getActualTimezone(), // Adds the timezone offset to the Date object that will be formatted. - } - ); - }, - U( dateValue ) { - return formatTZ( - zonedTimeToUtc( dateValue, getActualTimezone() ), - 't' - ); - }, + c: 'YYYY-MM-DDTHH:mm:ssZ', // .toISOString + r: 'ddd, D MMM YYYY HH:mm:ss ZZ', + U: 'X', }; /** - * Applies map of PHP formatting tokens into date-fns formatting tokens to the given format and date. + * Formats a date. Does not alter the date's timezone. + * + * @param {string} dateFormat PHP-style formatting string. + * See php.net/date. + * @param {Date|string|Moment|null} dateValue Date object or string, + * parsable by moment.js. * - * @param {string} formatString - * @param {Date|string} dateValue + * @return {string} Formatted date. */ -function translateFormat( formatString, dateValue ) { +export function format( dateFormat, dateValue = new Date() ) { let i, char; let newFormat = []; - - const parsedDate = - typeof dateValue === 'string' ? parseISO( dateValue ) : dateValue; - - for ( i = 0; i < formatString.length; i++ ) { - char = formatString[ i ]; + const momentDate = momentLib( dateValue ); + for ( i = 0; i < dateFormat.length; i++ ) { + char = dateFormat[ i ]; // Is this an escape? if ( '\\' === char ) { // Add next character, then move on. i++; - newFormat.push( "'" + formatString[ i ] + "'" ); + newFormat.push( '[' + dateFormat[ i ] + ']' ); continue; } if ( char in formatMap ) { if ( typeof formatMap[ char ] !== 'string' ) { // If the format is a function, call it. - newFormat.push( "'" + formatMap[ char ]( parsedDate ) + "'" ); + newFormat.push( '[' + formatMap[ char ]( momentDate ) + ']' ); } else { // Otherwise, add as a formatting string. newFormat.push( formatMap[ char ] ); } } else { - newFormat.push( char ); + newFormat.push( '[' + char + ']' ); } } // Join with [] between to separate characters, and replace // unneeded separators with static text. - - newFormat = newFormat.join( '' ); - - return newFormat; -} - -/** - * Build date-fns locale settings from WordPress localization settings. - */ -function getLocalizationSettings() { - const monthValues = { - abbreviated: settings.l10n.monthsShort, - wide: settings.l10n.months, - }; - - const dayValues = { - abbreviated: settings.l10n.weekdaysShort, - wide: settings.l10n.weekdays, - }; - - return { - ...originalLocale.localize, - month: buildLocalizeFn( { - values: monthValues, - defaultWidth: 'wide', - } ), - day: buildLocalizeFn( { - values: dayValues, - defaultWidth: 'wide', - } ), - formatLong: { - date: buildFormatLongFn( { - formats: { - full: settings.formats.date, - defaultWidth: 'full', - }, - } ), - time: buildFormatLongFn( { - formats: { - full: settings.formats.time, - defaultWidth: 'full', - }, - } ), - dateTime: buildFormatLongFn( { - formats: { - full: settings.formats.datetime, - short: settings.formats.datetimeAbbreviated, - defaultWidth: 'full', - }, - } ), - }, - }; -} - -/** - * Returns whether a certain UTC offset is valid or not. - * - * @param {number|string} offset a UTC offset. - * - * @return {boolean} whether a certain UTC offset is valid or not. - */ -function isValidUTCOffset( offset ) { - return VALID_UTC_OFFSET.test( offset ); -} - -/** - * Transform the given integer into a valid UTC Offset in hours. - * - * @param {number} offset A UTC offset as an integer - */ -function integerToUTCOffset( offset ) { - const offsetInHours = offset > 23 ? offset / 60 : offset; - const sign = offset < 0 ? '-' : '+'; - const absoluteOffset = - offsetInHours < 0 ? offsetInHours * -1 : offsetInHours; - - return offsetInHours < 10 - ? `${ sign }0${ absoluteOffset }` - : `${ sign }${ absoluteOffset }`; -} - -/** - * Determines whether or not the given value can be parsed as a UTC offset, - * by checking if it is parseable as an integer and if it isn't a - * valid UTC offset already. - * - * @param {string} offset An offset as an integer or a string. - */ -function shouldParseAsUTCOffset( offset ) { - const isNumber = ! Number.isNaN( Number.parseInt( offset, 10 ) ); - return isNumber && ! isValidUTCOffset( offset ); -} - -/** - * Get a properly formatted timezone from a timezone string or offset. - * Return system timezone or offset if no timezone was given. - * - * @param {string} timezone - */ -function getActualTimezone( timezone = '' ) { - if ( ! timezone ) { - const { string, offset } = settings.timezone; - - if ( string ) { - return string; - } - - if ( shouldParseAsUTCOffset( offset ) ) { - return integerToUTCOffset( offset ); - } - } - - return shouldParseAsUTCOffset( timezone ) - ? integerToUTCOffset( timezone ) - : timezone; -} - -/** - * Formats a date. Does not alter the date's timezone. - * - * @param {string} dateFormat PHP-style formatting string. - * See php.net/date. - * @param {Date|string|null} dateValue Date object or ISO string. - * - * @return {string} Formatted date. - */ -export function format( dateFormat, dateValue = new Date() ) { - const formatString = translateFormat( dateFormat, dateValue ); - return dateFnsFormat( new Date( dateValue ), formatString ); + newFormat = newFormat.join( '[]' ); + return momentDate.format( newFormat ); } /** @@ -510,7 +365,7 @@ export function format( dateFormat, dateValue = new Date() ) { * * @param {string} dateFormat PHP-style formatting string. * See php.net/date. - * @param {Date|string|null} dateValue Date object or ISO string, parsable + * @param {Date|string|Moment|null} dateValue Date object or string, parsable * by moment.js. * @param {string|number|null} timezone Timezone to output result in or a * UTC offset. Defaults to timezone from @@ -522,10 +377,8 @@ export function format( dateFormat, dateValue = new Date() ) { * @return {string} Formatted date in English. */ export function date( dateFormat, dateValue = new Date(), timezone ) { - return format( - dateFormat, - utcToZonedTime( dateValue, getActualTimezone( timezone ) ) - ); + const dateMoment = buildMoment( dateValue, timezone ); + return format( dateFormat, dateMoment ); } /** @@ -533,12 +386,14 @@ export function date( dateFormat, dateValue = new Date(), timezone ) { * * @param {string} dateFormat PHP-style formatting string. * See php.net/date. - * @param {Date|string|null} dateValue Date object or ISO string. + * @param {Date|string|Moment|null} dateValue Date object or string, + * parsable by moment.js. * * @return {string} Formatted date in English. */ export function gmdate( dateFormat, dateValue = new Date() ) { - return format( dateFormat, utcToZonedTime( dateValue, 'UTC' ) ); + const dateMoment = momentLib( dateValue ).utc(); + return format( dateFormat, dateMoment ); } /** @@ -549,7 +404,7 @@ export function gmdate( dateFormat, dateValue = new Date() ) { * * @param {string} dateFormat PHP-style formatting string. * See php.net/date. - * @param {Date|string|null} dateValue Date object or string, parsable by + * @param {Date|string|Moment|null} dateValue Date object or string, parsable by * moment.js. * @param {string|number|boolean|null} timezone Timezone to output result in or a * UTC offset. Defaults to timezone from @@ -571,22 +426,9 @@ export function dateI18n( dateFormat, dateValue = new Date(), timezone ) { timezone = undefined; } - return formatTZ( - utcToZonedTime( - zonedTimeToUtc( dateValue, getActualTimezone() ), - getActualTimezone( timezone ) - ), - translateFormat( dateFormat, dateValue ), - { - timeZone: getActualTimezone( timezone ), - locale: { - ...originalLocale, - locale: settings.l10n.locale, - code: settings.l10n.locale, - localize: getLocalizationSettings(), - }, - } - ); + const dateMoment = buildMoment( dateValue, timezone ); + dateMoment.locale( settings.l10n.locale ); + return format( dateFormat, dateMoment ); } /** @@ -595,37 +437,29 @@ export function dateI18n( dateFormat, dateValue = new Date(), timezone ) { * * @param {string} dateFormat PHP-style formatting string. * See php.net/date. - * @param {Date|string|null} dateValue Date object or ISO string. + * @param {Date|string|Moment|null} dateValue Date object or string, + * parsable by moment.js. * * @return {string} Formatted date. */ export function gmdateI18n( dateFormat, dateValue = new Date() ) { - return formatTZ( - utcToZonedTime( dateValue, 'UTC' ), - translateFormat( dateFormat, dateValue ), - { - timeZone: 'UTC', - locale: { - ...originalLocale, - locale: settings.l10n.locale, - code: settings.l10n.locale, - localize: getLocalizationSettings(), - }, - } - ); + const dateMoment = momentLib( dateValue ).utc(); + dateMoment.locale( settings.l10n.locale ); + return format( dateFormat, dateMoment ); } /** * Check whether a date is considered in the future according to the WordPress settings. * - * @param {string|Date} dateValue Date String or Date object in the Defined WP Timezone. + * @param {string} dateValue Date String or Date object in the Defined WP Timezone. * * @return {boolean} Is in the future. */ export function isInTheFuture( dateValue ) { - const dateObject = toDate( dateValue, { timeZone: getActualTimezone() } ); + const now = momentLib.tz( WP_ZONE ); + const momentObject = momentLib.tz( dateValue, WP_ZONE ); - return isFuture( dateObject ); + return momentObject.isAfter( now ); } /** @@ -636,8 +470,58 @@ export function isInTheFuture( dateValue ) { * @return {Date} Date */ export function getDate( dateString ) { - const actualDate = dateString ? new Date( dateString ) : new Date(); - return toDate( actualDate, { timeZone: getActualTimezone() } ); + if ( ! dateString ) { + return momentLib.tz( WP_ZONE ).toDate(); + } + + return momentLib.tz( dateString, WP_ZONE ).toDate(); +} + +/** + * Creates a moment instance using the given timezone or, if none is provided, using global settings. + * + * @param {Date|string|Moment|null} dateValue Date object or string, parsable + * by moment.js. + * @param {string|number|null} timezone Timezone to output result in or a + * UTC offset. Defaults to timezone from + * site. + * + * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + * @see https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC + * + * @return {Moment} a moment instance. + */ +function buildMoment( dateValue, timezone = '' ) { + const dateMoment = momentLib( dateValue ); + + if ( timezone && ! isUTCOffset( timezone ) ) { + return dateMoment.tz( timezone ); + } + + if ( timezone && isUTCOffset( timezone ) ) { + return dateMoment.utcOffset( timezone ); + } + + if ( settings.timezone.string ) { + return dateMoment.tz( settings.timezone.string ); + } + + return dateMoment.utcOffset( settings.timezone.offset ); +} + +/** + * Returns whether a certain UTC offset is valid or not. + * + * @param {number|string} offset a UTC offset. + * + * @return {boolean} whether a certain UTC offset is valid or not. + */ +function isUTCOffset( offset ) { + if ( 'number' === typeof offset ) { + return true; + } + + return VALID_UTC_OFFSET.test( offset ); } -export { zonedTimeToUtc }; +setupWPTimezone(); diff --git a/packages/date/src/test/index.js b/packages/date/src/test/index.js index 2c5479cfcd4c35..d9b832e528ce6b 100644 --- a/packages/date/src/test/index.js +++ b/packages/date/src/test/index.js @@ -191,332 +191,6 @@ describe( 'Function date', () => { } ); } ); -// Custom formatting token functions, in order to support PHP formatting tokens -describe( 'PHP Format Tokens', () => { - it( 'should support "d" to obtain day of the month, 2 digits with leading zeroes', () => { - const formattedDate = dateNoI18n( 'd', '2019-06-06T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '06' ); - } ); - - it( 'should support "D" to obtain textual representation of a day, three letters', () => { - const formattedDate = dateNoI18n( 'D', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( 'Tue' ); - } ); - - it( 'should support "j" to obtain day of the month without leading zeroes', () => { - const formattedDate = dateNoI18n( 'j', '2019-06-06T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '6' ); - } ); - - it( 'should support "l" to obtain full textual representation of the day of the week', () => { - const formattedDate = dateNoI18n( 'l', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( 'Tuesday' ); - } ); - - it( 'should support "N" to obtain ISO-8601 numeric representation of the day of the week', () => { - const formattedDate = dateNoI18n( 'N', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '2' ); // 2 === Tuesday - } ); - - it( 'should support "S" to obtain ordinal suffix of day of the month', () => { - const formattedDate = dateNoI18n( 'S', '2019-06-18T11:00:00.000Z' ); - - // th for 18th - expect( formattedDate ).toBe( 'th' ); - } ); - - it( 'should support "w" to obtain day of the week starting from 0', () => { - const formattedDate = dateNoI18n( 'w', '2020-01-01T12:00:00.000Z' ); // Wednesday Jan 1st, 2020 - - expect( formattedDate ).toBe( '2' ); - } ); - - it( 'should support "z" to obtain zero-indexed day of the year', () => { - const formattedDate = dateNoI18n( 'z', '2019-01-01' ); - - expect( formattedDate ).toBe( '0' ); - } ); - - it( 'should support "W" to obtain ISO-8601 week number of year, weeks starting on Monday', () => { - const formattedDate = dateNoI18n( 'W', '2019-01-06T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '01' ); - } ); - - it( 'should support "F" to obtain a full textual representation of a month', () => { - const formattedDate = dateNoI18n( 'F', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( 'June' ); - } ); - - it( 'should support "m" to obtain the numeric representation of a month, with leading zeroes', () => { - const formattedDate = dateNoI18n( 'm', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '06' ); - } ); - - it( 'should support "M" to obtain a three letter textual representation of a month', () => { - const formattedDate = dateNoI18n( 'M', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( 'Jun' ); - } ); - - it( 'should "n" to obtain the numeric representation of a month without leading zeroes', () => { - const formattedDate = dateNoI18n( 'n', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '6' ); - } ); - - it( 'should support "t" to obtain the days in a given month', () => { - const formattedDate = dateNoI18n( 't', '2019-02' ); - - expect( formattedDate ).toBe( '28' ); - } ); - - it( 'should support "L" to obtain whether or not the year is a leap year', () => { - const formattedDate = dateNoI18n( 'L', '2020' ); - - expect( formattedDate ).toBe( '1' ); - } ); - - it( 'should support "o" to obtain the ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead.', () => { - const formattedDate = dateNoI18n( 'o', '2019-01-01T11:00:00.000Z' ); - const formattedDatePreviousYear = dateNoI18n( - 'o', - '2017-01-01T11:00:00.000Z' - ); // ISO week number belongs to previous year - - expect( formattedDate ).toBe( '2019' ); - expect( formattedDatePreviousYear ).toBe( '2016' ); - } ); - - it( 'should support "Y" to obtain a full numeric representation of a year', () => { - const formattedDate = dateNoI18n( 'Y', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '2019' ); - } ); - - it( 'should support "y" to obtain a two digit representation of a year', () => { - const formattedDate = dateNoI18n( 'y', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '19' ); - } ); - - it( 'should support "a" to obtain a lowercase ante meridiem and post meridiem', () => { - const formattedDate = dateNoI18n( 'a', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( 'am' ); - } ); - - it( 'should support "A" to obtain uppercase ante meridiem and post meridiem', () => { - const formattedDate = dateNoI18n( 'A', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( 'AM' ); - } ); - - it( 'should support "B" to obtain the time in Swatch Internet Time (.beats)', () => { - const formattedDate = dateNoI18n( 'B', '2020-10-09T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '500' ); - } ); - - it( 'should support "g" to obtain the 12-hour format of an hour without leading zeroes', () => { - const formattedDate = dateNoI18n( 'g', '2019-06-18T14:00:00.000Z' ); - - expect( formattedDate ).toBe( '2' ); - } ); - - it( 'should support "G" to obtain the 24-hour format of an hour without leading zeroes', () => { - const formattedDate = dateNoI18n( 'G', '2019-06-18T09:00:00.000Z' ); - - expect( formattedDate ).toBe( '9' ); - } ); - - it( 'should support "h" 12-hour format of an hour with leading zeroes', () => { - const formattedDate = dateNoI18n( 'h', '2019-06-18T14:00:00.000Z' ); - - expect( formattedDate ).toBe( '02' ); - } ); - - it( 'should support "H" 24-hour format of an hour with leading zeroes', () => { - const formattedDate = dateNoI18n( 'H', '2019-06-18T09:00:00.000Z' ); - - expect( formattedDate ).toBe( '09' ); - } ); - - it( 'should support "i" to obtain the minutes with leading zeroes', () => { - const formattedDate = dateNoI18n( 'i', '2019-06-18T11:01:00.000Z' ); - - expect( formattedDate ).toBe( '01' ); - } ); - - it( 'should support "s" to obtain seconds with leading zeroes', () => { - const formattedDate = dateNoI18n( 's', '2019-06-18T11:00:04.000Z' ); - - expect( formattedDate ).toBe( '04' ); - } ); - - /** - * This format is not fully compatible with JavaScript out of the box, - * as Date doesn't support sub-millisecond precision. - */ - it( 'should support "u" to obtain microseconds', () => { - const formattedDate = dateNoI18n( - 'u', - '2019-06-18T11:00:00.123456789Z' - ); - - expect( formattedDate ).toBe( '123000' ); - } ); - - it( 'should support "v" to obtain milliseconds', () => { - const formattedDate = dateNoI18n( - 'v', - '2019-06-18T11:00:00.123456789Z' - ); - - expect( formattedDate ).toBe( '123' ); - } ); - - it( 'should support "e" to obtain timezone identifier', () => { - const settings = __experimentalGetSettings(); - - setSettings( { - ...settings, - timezone: { offset: -4, string: 'America/New_York' }, - } ); - - const formattedDate = dateNoI18n( 'e', '2020-10-09T11:00:00.000Z' ); - - expect( formattedDate ).toBe( 'Eastern Daylight Time' ); - - setSettings( settings ); - } ); - - it.skip( 'should support "I" to obtain whether or not the timezone is observing DST', () => { - const formattedFall = dateNoI18n( 'I', '2020-10-09T11:00:00.000Z' ); - - expect( formattedFall ).toBe( '1' ); - - const formattedWinter = dateNoI18n( 'I', '2020-01-09T11:00:00.000Z' ); - - expect( formattedWinter ).toBe( '0' ); - } ); - - it( 'should support "O" to obtain difference to Greenwich time (GMT) without colon between hours and minutes', () => { - const settings = __experimentalGetSettings(); - - setSettings( { - ...settings, - timezone: { offset: -6 }, - } ); - - const formattedDate = dateNoI18n( 'O', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '-0600' ); - - setSettings( settings ); - } ); - - it( 'should support "P" to obtain difference to Greenwich time (GMT) without colon between hours and minutes', () => { - const settings = __experimentalGetSettings(); - - setSettings( { - ...settings, - timezone: { offset: -6 }, - } ); - - const formattedDate = dateNoI18n( 'P', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '-06:00' ); - - setSettings( settings ); - } ); - - it( 'should support "T" to obtain the timezone abbreviation for the given date', () => { - const settings = __experimentalGetSettings(); - - setSettings( { - ...settings, - timezone: { offset: -4, string: 'America/New_York' }, - } ); - - const formattedDateStandard = dateNoI18n( - 'T', - '2020-01-01T11:00:00.000Z' - ); - - expect( formattedDateStandard ).toBe( 'EST' ); - - setSettings( settings ); - } ); - - it.skip( 'should support "Z" to obtain timezone offset in seconds', () => { - const settings = __experimentalGetSettings(); - - setSettings( { - ...settings, - timezone: { offset: -1 }, - } ); - - const formattedDate = dateNoI18n( 'Z', '2020-10-09T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '3600' ); - - setSettings( settings ); - } ); - - it( 'should support "c" to obtain ISO 8601 date', () => { - const settings = __experimentalGetSettings(); - - setSettings( { - ...settings, - timezone: { offset: -5, string: 'America/Bogota' }, - } ); - - const formattedDate = dateNoI18n( 'c', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '2019-06-18T11:00:00-05:00' ); - - setSettings( settings ); - } ); - - it( 'should support "r" RFC 2822 formatted date', () => { - const settings = __experimentalGetSettings(); - - setSettings( { - ...settings, - timezone: { offset: -5, string: 'America/Bogota' }, - } ); - - const formattedDate = dateNoI18n( 'r', '2019-06-18T11:00:00.000Z' ); - - expect( formattedDate ).toBe( 'Tue, 18 Jun 2019 11:00:00 -0500' ); - - setSettings( settings ); - } ); - - it( 'should support "U" to get epoc for given date', () => { - const settings = __experimentalGetSettings(); - - setSettings( { - ...settings, - timezone: { string: 'UTC' }, - } ); - - const formattedDate = dateNoI18n( 'U', '2020-10-09T11:00:00.000Z' ); - - expect( formattedDate ).toBe( '1602241200' ); - - setSettings( settings ); - } ); -} ); - describe( 'Function gmdate', () => { it( 'should format date in English, ignoring locale settings', () => { const settings = __experimentalGetSettings(); diff --git a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md index 05c96b1fe2309d..8b596b3945e10e 100644 --- a/packages/dependency-extraction-webpack-plugin/CHANGELOG.md +++ b/packages/dependency-extraction-webpack-plugin/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +## 2.9.0 (2020-12-17) + +### New feature + +- Make the plugin compatible with webpack 5. + ## 2.7.0 (2020-06-15) ### New feature diff --git a/packages/dependency-extraction-webpack-plugin/lib/index.js b/packages/dependency-extraction-webpack-plugin/lib/index.js index 0a44daaa48ee5f..0cc84b99a23846 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/index.js +++ b/packages/dependency-extraction-webpack-plugin/lib/index.js @@ -3,10 +3,9 @@ */ const { createHash } = require( 'crypto' ); const path = require( 'path' ); -const { ExternalsPlugin } = require( 'webpack' ); -const { RawSource } = require( 'webpack-sources' ); -// Ignore reason: json2php is untyped -// @ts-ignore +const webpack = require( 'webpack' ); +// In webpack 5 there is a `webpack.sources` field but for webpack 4 we have to fallback to the `webpack-sources` package. +const { RawSource } = webpack.sources || require( 'webpack-sources' ); const json2php = require( 'json2php' ); /** @@ -17,53 +16,8 @@ const { defaultRequestToHandle, } = require( './util' ); -/** - * Map module request to an external. - * - * @callback RequestToExternal - * - * @param {string} request Module request. - * - * @return {string|string[]|void} Return `undefined` to ignore the request. - * Return `string|string[]` to map the request to an external. - */ - -/** - * Map module request to a script handle. - * - * @callback RequestToHandle - * - * @param {string} request Module request. - * - * @return {string|void} Return `undefined` to use the same name as the module. - * Return `string` to map the request to a specific script handle. - */ - -/** - * @typedef AssetData - * - * @property {string} version String representing a particular build - * @property {string[]} dependencies The script dependencies - */ - -/** - * @typedef Options - * - * @property {boolean} injectPolyfill Force wp-polyfill to be included in each entry point's dependency list. This is like importing `@wordpress/polyfill` for each entry point. - * @property {boolean} useDefaults Set to `false` to disable the default WordPress script request handling. - * @property {'php'|'json'} outputFormat The output format for the generated asset file. - * @property {RequestToExternal|undefined} [requestToExternal] Map module requests to an external. - * @property {RequestToHandle|undefined} [requestToHandle] Map module requests to a script handle. - * @property {string|null} combinedOutputFile This option is useful only when the combineAssets option is enabled. It allows providing a custom output file for the generated single assets file. It's possible to provide a path that is relative to the output directory. - * @property {boolean|undefined} combineAssets By default, one asset file is created for each entry point. When this flag is set to true, all information about assets is combined into a single assets.(json|php) file generated in the output directory. - */ - class DependencyExtractionWebpackPlugin { - /** - * @param {Partial<Options>} options - */ constructor( options ) { - /** @type {Options} */ this.options = Object.assign( { combineAssets: false, @@ -75,32 +29,23 @@ class DependencyExtractionWebpackPlugin { options ); - /** + /* * Track requests that are externalized. * * Because we don't have a closed set of dependencies, we need to track what has * been externalized so we can recognize them in a later phase when the dependency * lists are generated. - * - * @type {Set<string>} */ this.externalizedDeps = new Set(); // Offload externalization work to the ExternalsPlugin. - this.externalsPlugin = new ExternalsPlugin( + this.externalsPlugin = new webpack.ExternalsPlugin( 'window', this.externalizeWpDeps.bind( this ) ); } - /* eslint-disable jsdoc/valid-types */ - /** - * @param {Parameters<WebpackExternalsFunction>[0]} _context - * @param {Parameters<WebpackExternalsFunction>[1]} request - * @param {Parameters<WebpackExternalsFunction>[2]} callback - */ externalizeWpDeps( _context, request, callback ) { - /* eslint-enable jsdoc/valid-types */ let externalRequest; // Handle via options.requestToExternal first @@ -125,10 +70,6 @@ class DependencyExtractionWebpackPlugin { return callback(); } - /** - * @param {string} request - * @return {string} Transformed request - */ mapRequestToDependency( request ) { // Handle via options.requestToHandle first if ( typeof this.options.requestToHandle === 'function' ) { @@ -150,10 +91,6 @@ class DependencyExtractionWebpackPlugin { return request; } - /** - * @param {Object} asset - * @return {string} Stringified asset - */ stringify( asset ) { if ( this.options.outputFormat === 'php' ) { return `<?php return ${ json2php( @@ -164,20 +101,9 @@ class DependencyExtractionWebpackPlugin { return JSON.stringify( asset ); } - /** - * @param {WebpackCompiler} compiler - * @return {void} - */ apply( compiler ) { this.externalsPlugin.apply( compiler ); - // Assert the `string` type for output filename. - // The type indicates the option may be `undefined`. - // However, at this point in compilation, webpack has filled the options in if - // they were not provided. - const outputFilename = /** @type {{filename:string}} */ ( compiler - .options.output ).filename; - compiler.hooks.emit.tap( this.constructor.name, ( compilation ) => { const { combineAssets, @@ -186,7 +112,6 @@ class DependencyExtractionWebpackPlugin { outputFormat, } = this.options; - /** @type {Record<string, AssetData>} */ const combinedAssetsData = {}; // Process each entry point independently. @@ -194,29 +119,35 @@ class DependencyExtractionWebpackPlugin { entrypointName, entrypoint, ] of compilation.entrypoints.entries() ) { - /** @type {Set<string>} */ const entrypointExternalizedWpDeps = new Set(); if ( injectPolyfill ) { entrypointExternalizedWpDeps.add( 'wp-polyfill' ); } + const processModule = ( { userRequest } ) => { + if ( this.externalizedDeps.has( userRequest ) ) { + const scriptDependency = this.mapRequestToDependency( + userRequest + ); + entrypointExternalizedWpDeps.add( scriptDependency ); + } + }; + // Search for externalized modules in all chunks. for ( const chunk of entrypoint.chunks ) { - for ( const { userRequest } of chunk.modulesIterable ) { - if ( this.externalizedDeps.has( userRequest ) ) { - const scriptDependency = this.mapRequestToDependency( - userRequest - ); - entrypointExternalizedWpDeps.add( - scriptDependency - ); + for ( const chunkModule of chunk.modulesIterable ) { + processModule( chunkModule ); + // loop through submodules of ConcatenatedModule + if ( chunkModule.modules ) { + for ( const concatModule of chunkModule.modules ) { + processModule( concatModule ); + } } } } const runtimeChunk = entrypoint.getRuntimeChunk(); - /** @type {AssetData} */ const assetData = { // Get a sorted array so we can produce a stable, stringified representation. dependencies: Array.from( @@ -229,15 +160,18 @@ class DependencyExtractionWebpackPlugin { // Determine a filename for the asset file. const [ filename, query ] = entrypointName.split( '?', 2 ); - const buildFilename = compilation.getPath( outputFilename, { - chunk: runtimeChunk, - filename, - query, - basename: basename( filename ), - contentHash: createHash( 'md4' ) - .update( assetString ) - .digest( 'hex' ), - } ); + const buildFilename = compilation.getPath( + compiler.options.output.filename, + { + chunk: runtimeChunk, + filename, + query, + basename: basename( filename ), + contentHash: createHash( 'md4' ) + .update( assetString ) + .digest( 'hex' ), + } + ); if ( combineAssets ) { combinedAssetsData[ buildFilename ] = assetData; @@ -283,10 +217,6 @@ class DependencyExtractionWebpackPlugin { } } -/** - * @param {string} name - * @return {string} Basename - */ function basename( name ) { if ( ! name.includes( '/' ) ) { return name; @@ -295,8 +225,3 @@ function basename( name ) { } module.exports = DependencyExtractionWebpackPlugin; - -/** - * @typedef {import('webpack').Compiler} WebpackCompiler - * @typedef {import('webpack').ExternalsFunctionElement} WebpackExternalsFunction - */ diff --git a/packages/dependency-extraction-webpack-plugin/lib/types.d.ts b/packages/dependency-extraction-webpack-plugin/lib/types.d.ts new file mode 100644 index 00000000000000..d0c9f049ec6748 --- /dev/null +++ b/packages/dependency-extraction-webpack-plugin/lib/types.d.ts @@ -0,0 +1,18 @@ +import { Compiler } from 'webpack'; + +export = DependencyExtractionWebpackPlugin; + +declare class DependencyExtractionWebpackPlugin { + constructor(options: DependencyExtractionWebpackPluginOptions); + apply(compiler: Compiler): void; +} + +declare interface DependencyExtractionWebpackPluginOptions { + injectPolyfill?: boolean; + useDefaults?: boolean; + outputFormat?: 'php' | 'json'; + requestToExternal?: (request: string) => string | string[] | undefined; + requestToHandle?: (request: string) => string | undefined; + combinedOutputFile?: string | null; + combineAssets?: boolean; +} diff --git a/packages/dependency-extraction-webpack-plugin/lib/util.js b/packages/dependency-extraction-webpack-plugin/lib/util.js index 7748c01e00a75f..a1817f65c7781c 100644 --- a/packages/dependency-extraction-webpack-plugin/lib/util.js +++ b/packages/dependency-extraction-webpack-plugin/lib/util.js @@ -1,19 +1,16 @@ const WORDPRESS_NAMESPACE = '@wordpress/'; -const BUNDLED_PACKAGES = [ - '@wordpress/icons', - '@wordpress/interface', - '@wordpress/stan', -]; +const BUNDLED_PACKAGES = [ '@wordpress/icons', '@wordpress/interface' ]; /** * Default request to global transformation * * Transform @wordpress dependencies: + * - request `@wordpress/api-fetch` becomes `[ 'wp', 'apiFetch' ]` + * - request `@wordpress/i18n` becomes `[ 'wp', 'i18n' ]` * - * request `@wordpress/api-fetch` becomes `wp.apiFetch` - * request `@wordpress/i18n` becomes `wp.i18n` - * - * @type {import('.').RequestToExternal} + * @param {string} request Module request (the module name in `import from`) to be transformed + * @return {string|string[]|undefined} The resulting external definition. Return `undefined` + * to ignore the request. Return `string|string[]` to map the request to an external. */ function defaultRequestToExternal( request ) { switch ( request ) { @@ -53,11 +50,12 @@ function defaultRequestToExternal( request ) { * Default request to WordPress script handle transformation * * Transform @wordpress dependencies: + * - request `@wordpress/i18n` becomes `wp-i18n` + * - request `@wordpress/escape-html` becomes `wp-escape-html` * - * request `@wordpress/i18n` becomes `wp-i18n` - * request `@wordpress/escape-html` becomes `wp-escape-html` - * - * @type {import('.').RequestToHandle} + * @param {string} request Module request (the module name in `import from`) to be transformed + * @return {string|undefined} WordPress script handle to map the request to. Return `undefined` + * to use the same name as the module. */ function defaultRequestToHandle( request ) { switch ( request ) { @@ -80,7 +78,6 @@ function defaultRequestToHandle( request ) { * following numbers. * * @param {string} string Input dash-delimited string. - * * @return {string} Camel-cased string. */ function camelCaseDash( string ) { diff --git a/packages/dependency-extraction-webpack-plugin/package.json b/packages/dependency-extraction-webpack-plugin/package.json index c11ba4edd1aa99..fec6edab4d0944 100644 --- a/packages/dependency-extraction-webpack-plugin/package.json +++ b/packages/dependency-extraction-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dependency-extraction-webpack-plugin", - "version": "2.8.0", + "version": "2.9.0", "description": "Extract WordPress script dependencies from webpack bundles.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,12 +24,14 @@ "build-types" ], "main": "lib/index.js", - "types": "build-types", + "types": "lib/types.d.ts", "dependencies": { "json2php": "^0.0.4", - "webpack": "^4.8.3", "webpack-sources": "^1.3.0" }, + "peerDependencies": { + "webpack": "^4.8.3 || ^5.0.0" + }, "publishConfig": { "access": "public" } diff --git a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap index 245abfbbcdba57..42e271c2c2b1f1 100644 --- a/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap +++ b/packages/dependency-extraction-webpack-plugin/test/__snapshots__/build.js.snap @@ -4,6 +4,11 @@ exports[`Webpack \`combine-assets\` should produce expected output: Asset file s exports[`Webpack \`combine-assets\` should produce expected output: External modules should match snapshot 1`] = ` Array [ + Object { + "externalType": "window", + "request": "lodash", + "userRequest": "lodash", + }, Object { "externalType": "window", "request": Array [ @@ -20,11 +25,6 @@ Array [ ], "userRequest": "@wordpress/token-list", }, - Object { - "externalType": "window", - "request": "lodash", - "userRequest": "lodash", - }, ] `; @@ -32,6 +32,11 @@ exports[`Webpack \`dynamic-import\` should produce expected output: Asset file s exports[`Webpack \`dynamic-import\` should produce expected output: External modules should match snapshot 1`] = ` Array [ + Object { + "externalType": "window", + "request": "lodash", + "userRequest": "lodash", + }, Object { "externalType": "window", "request": Array [ @@ -40,11 +45,6 @@ Array [ ], "userRequest": "@wordpress/blob", }, - Object { - "externalType": "window", - "request": "lodash", - "userRequest": "lodash", - }, ] `; @@ -52,6 +52,11 @@ exports[`Webpack \`function-output-filename\` should produce expected output: As exports[`Webpack \`function-output-filename\` should produce expected output: External modules should match snapshot 1`] = ` Array [ + Object { + "externalType": "window", + "request": "lodash", + "userRequest": "lodash", + }, Object { "externalType": "window", "request": Array [ @@ -60,11 +65,6 @@ Array [ ], "userRequest": "@wordpress/blob", }, - Object { - "externalType": "window", - "request": "lodash", - "userRequest": "lodash", - }, ] `; @@ -94,19 +94,8 @@ exports[`Webpack \`overrides\` should produce expected output: External modules Array [ Object { "externalType": "window", - "request": Array [ - "wp", - "blob", - ], - "userRequest": "@wordpress/blob", - }, - Object { - "externalType": "window", - "request": Array [ - "wp", - "url", - ], - "userRequest": "@wordpress/url", + "request": "rxjs", + "userRequest": "rxjs", }, Object { "externalType": "window", @@ -118,19 +107,10 @@ Array [ }, Object { "externalType": "window", - "request": "rxjs", - "userRequest": "rxjs", - }, -] -`; - -exports[`Webpack \`with-externs\` should produce expected output: Asset file should match snapshot 1`] = `"<?php return array('dependencies' => array('wp-url'), 'version' => '43ee069ad238d0c92526824ddfb9e4fb');"`; - -exports[`Webpack \`with-externs\` should produce expected output: External modules should match snapshot 1`] = ` -Array [ - Object { - "externalType": "var", - "request": "wp.blob", + "request": Array [ + "wp", + "blob", + ], "userRequest": "@wordpress/blob", }, Object { @@ -141,16 +121,6 @@ Array [ ], "userRequest": "@wordpress/url", }, - Object { - "externalType": "var", - "request": "rxjs.operators", - "userRequest": "rxjs/operators", - }, - Object { - "externalType": "var", - "request": "rxjs", - "userRequest": "rxjs", - }, ] `; @@ -158,6 +128,11 @@ exports[`Webpack \`wordpress\` should produce expected output: Asset file should exports[`Webpack \`wordpress\` should produce expected output: External modules should match snapshot 1`] = ` Array [ + Object { + "externalType": "window", + "request": "lodash", + "userRequest": "lodash", + }, Object { "externalType": "window", "request": Array [ @@ -166,11 +141,6 @@ Array [ ], "userRequest": "@wordpress/blob", }, - Object { - "externalType": "window", - "request": "lodash", - "userRequest": "lodash", - }, ] `; @@ -178,6 +148,11 @@ exports[`Webpack \`wordpress-require\` should produce expected output: Asset fil exports[`Webpack \`wordpress-require\` should produce expected output: External modules should match snapshot 1`] = ` Array [ + Object { + "externalType": "window", + "request": "lodash", + "userRequest": "lodash", + }, Object { "externalType": "window", "request": Array [ @@ -186,10 +161,5 @@ Array [ ], "userRequest": "@wordpress/blob", }, - Object { - "externalType": "window", - "request": "lodash", - "userRequest": "lodash", - }, ] `; diff --git a/packages/dependency-extraction-webpack-plugin/test/build.js b/packages/dependency-extraction-webpack-plugin/test/build.js index ae3435222afecb..9f89564543608f 100644 --- a/packages/dependency-extraction-webpack-plugin/test/build.js +++ b/packages/dependency-extraction-webpack-plugin/test/build.js @@ -66,10 +66,18 @@ describe.each( configFixtures )( 'Webpack `%s`', ( configCase ) => { ).toMatchSnapshot( 'Asset file should match snapshot' ); } ); + const compareByModuleIdentifier = ( m1, m2 ) => { + const i1 = m1.identifier(); + const i2 = m2.identifier(); + if ( i1 < i2 ) return -1; + if ( i1 > i2 ) return 1; + return 0; + }; + // Webpack stats external modules should match. const externalModules = stats.compilation.modules - .filter( ( { external } ) => external ) - .sort() + .filter( ( { externalType } ) => externalType ) + .sort( compareByModuleIdentifier ) .map( ( module ) => ( { externalType: module.externalType, request: module.request, diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/with-externs/index.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/with-externs/index.js deleted file mode 100644 index 21fda20b8d8fad..00000000000000 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/with-externs/index.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * WordPress dependencies - */ -import { isBlobURL } from '@wordpress/blob'; -import { isURL } from '@wordpress/url'; - -/** - * External dependencies - */ -import { range } from 'rxjs'; -import { map, filter } from 'rxjs/operators'; - -range( 1, 200 ) - .pipe( - filter( isBlobURL ), - map( ( x ) => x + x ) - ) - .subscribe( ( x ) => isURL( x ) ); diff --git a/packages/dependency-extraction-webpack-plugin/test/fixtures/with-externs/webpack.config.js b/packages/dependency-extraction-webpack-plugin/test/fixtures/with-externs/webpack.config.js deleted file mode 100644 index a1e17a2c06e2f3..00000000000000 --- a/packages/dependency-extraction-webpack-plugin/test/fixtures/with-externs/webpack.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Internal dependencies - */ -const DependencyExtractionWebpackPlugin = require( '../../..' ); - -module.exports = { - externals: { - '@wordpress/blob': 'wp.blob', - 'rxjs/operators': 'rxjs.operators', - rxjs: true, - }, - plugins: [ new DependencyExtractionWebpackPlugin() ], -}; diff --git a/packages/dependency-extraction-webpack-plugin/tsconfig.json b/packages/dependency-extraction-webpack-plugin/tsconfig.json deleted file mode 100644 index 426ab13d0aa8f6..00000000000000 --- a/packages/dependency-extraction-webpack-plugin/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": "lib", - "declarationDir": "build-types" - }, - "include": [ "lib/**/*" ] -} diff --git a/packages/deprecated/CHANGELOG.md b/packages/deprecated/CHANGELOG.md index 9e7a628d7594d3..b5259f707dd458 100644 --- a/packages/deprecated/CHANGELOG.md +++ b/packages/deprecated/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.11.0 (2020-12-17) + ### New Feature - Include TypeScript type declarations ([#26429](https://github.com/WordPress/gutenberg/pull/26429)) diff --git a/packages/deprecated/package.json b/packages/deprecated/package.json index 7f36a564a60226..c0f6610ba91b15 100644 --- a/packages/deprecated/package.json +++ b/packages/deprecated/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/deprecated", - "version": "2.10.0", + "version": "2.11.0", "description": "Deprecation utility for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/hooks": "file:../hooks" }, "publishConfig": { diff --git a/packages/docgen/package.json b/packages/docgen/package.json index 7262827594e7b0..cf08e6ef7c99db 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/docgen", - "version": "1.14.0", + "version": "1.15.0", "description": "Autogenerate public API documentation from exports and JSDoc comments.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "docgen": "./bin/cli.js" }, "dependencies": { - "@babel/core": "^7.11.0", + "@babel/core": "^7.12.9", "doctrine": "^2.1.0", "lodash": "^4.17.19", "mdast-util-inject": "1.1.0", diff --git a/packages/dom-ready/package.json b/packages/dom-ready/package.json index 41bab823616a7b..085ddd5a9b6062 100644 --- a/packages/dom-ready/package.json +++ b/packages/dom-ready/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom-ready", - "version": "2.11.0", + "version": "2.12.0", "description": "Execute callback after the DOM is loaded.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/dom/README.md b/packages/dom/README.md index 6fe8d1ca75825d..d888a810f109d9 100644 --- a/packages/dom/README.md +++ b/packages/dom/README.md @@ -84,7 +84,7 @@ _Parameters_ _Returns_ -- `Set`: A set containing all files. +- `Array<Object>`: An array containing all files. <a name="getOffsetParent" href="#getOffsetParent">#</a> **getOffsetParent** diff --git a/packages/dom/package.json b/packages/dom/package.json index 15160fd48dc8c9..cee55f95b4fc13 100644 --- a/packages/dom/package.json +++ b/packages/dom/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/dom", - "version": "2.15.0", + "version": "2.16.0", "description": "DOM utilities module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "react-native": "src/index", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19" }, "publishConfig": { diff --git a/packages/dom/src/data-transfer.js b/packages/dom/src/data-transfer.js index 48cd3c55d9d41c..6052326c2c2213 100644 --- a/packages/dom/src/data-transfer.js +++ b/packages/dom/src/data-transfer.js @@ -3,16 +3,24 @@ * * @param {DataTransfer} dataTransfer DataTransfer object to inspect. * - * @return {Set} A set containing all files. + * @return {Object[]} An array containing all files. */ export function getFilesFromDataTransfer( dataTransfer ) { - const files = new Set( dataTransfer.files ); + const files = [ ...dataTransfer.files ]; Array.from( dataTransfer.items ).forEach( ( item ) => { const file = item.getAsFile(); - if ( file ) { - files.add( file ); + if ( + file && + ! files.find( + ( { name, type, size } ) => + name === file.name && + type === file.type && + size === file.size + ) + ) { + files.push( file ); } } ); diff --git a/packages/e2e-test-utils/CHANGELOG.md b/packages/e2e-test-utils/CHANGELOG.md index b12671da6d1be1..bab7738c00c9c4 100644 --- a/packages/e2e-test-utils/CHANGELOG.md +++ b/packages/e2e-test-utils/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 4.16.0 (2020-12-17) + ### Nee Features - Added `clickMenuItem` - clicks the item that matches the label in the opened menu. diff --git a/packages/e2e-test-utils/README.md b/packages/e2e-test-utils/README.md index 2bb8f370870dc6..29e20742248576 100644 --- a/packages/e2e-test-utils/README.md +++ b/packages/e2e-test-utils/README.md @@ -40,6 +40,18 @@ _Returns_ - `Promise<boolean>`: Boolean which represents the state of prepublish checks. +<a name="changeSiteTimezone" href="#changeSiteTimezone">#</a> **changeSiteTimezone** + +Visits general settings page and changes the timezone to the given value. + +_Parameters_ + +- _timezone_ `string`: Value of the timezone to set. + +_Returns_ + +- `string`: Value of the previous timezone. + <a name="clearLocalStorage" href="#clearLocalStorage">#</a> **clearLocalStorage** Clears the local storage. @@ -446,6 +458,18 @@ Clicks on the button in the header which opens Document Settings sidebar when it Opens the global block inserter. +<a name="openPreviewPage" href="#openPreviewPage">#</a> **openPreviewPage** + +Opens the preview page of an edited post. + +_Parameters_ + +- _editorPage_ `Page`: puppeteer editor page. + +_Returns_ + +- `Page`: preview page. + <a name="openPublishPanel" href="#openPublishPanel">#</a> **openPublishPanel** Opens the publish panel. @@ -536,6 +560,17 @@ _Parameters_ - _viewport_ `WPViewport`: Viewport name or dimensions object to assign. +<a name="setClipboardData" href="#setClipboardData">#</a> **setClipboardData** + +Sets the clipboard data that can be pasted with +`pressKeyWithModifier( 'primary', 'v' )`. + +_Parameters_ + +- _$1_ `Object`: Options. +- _$1.plainText_ `string`: Plain text to set. +- _$1.html_ `string`: HTML to set. + <a name="setPostContent" href="#setPostContent">#</a> **setPostContent** Sets code editor content diff --git a/packages/e2e-test-utils/package.json b/packages/e2e-test-utils/package.json index fcfcafc1846596..d5c254473949a1 100644 --- a/packages/e2e-test-utils/package.json +++ b/packages/e2e-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-test-utils", - "version": "4.15.0", + "version": "4.16.0", "description": "End-To-End (E2E) test utils for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -29,7 +29,7 @@ "main": "build/index.js", "module": "build-module/index.js", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/keycodes": "file:../keycodes", "@wordpress/url": "file:../url", "lodash": "^4.17.19", diff --git a/packages/e2e-test-utils/src/change-site-timezone.js b/packages/e2e-test-utils/src/change-site-timezone.js new file mode 100644 index 00000000000000..8389eca034a37d --- /dev/null +++ b/packages/e2e-test-utils/src/change-site-timezone.js @@ -0,0 +1,29 @@ +/** + * Internal dependencies + */ +import { visitAdminPage } from './visit-admin-page'; +import { switchUserToAdmin } from './switch-user-to-admin'; +import { switchUserToTest } from './switch-user-to-test'; + +/** + * Visits general settings page and changes the timezone to the given value. + * + * @param {string} timezone Value of the timezone to set. + * + * @return {string} Value of the previous timezone. + */ +export async function changeSiteTimezone( timezone ) { + await switchUserToAdmin(); + await visitAdminPage( 'options-general.php' ); + + const oldTimezone = await page.$eval( + '#timezone_string', + ( element ) => element.options[ element.selectedIndex ].text + ); + await page.select( '#timezone_string', timezone ); + await page.click( '#submit' ); + + await switchUserToTest(); + + return oldTimezone; +} diff --git a/packages/e2e-test-utils/src/click-block-toolbar-button.js b/packages/e2e-test-utils/src/click-block-toolbar-button.js index dc27849e36d5e6..3aa431bfe29ff9 100644 --- a/packages/e2e-test-utils/src/click-block-toolbar-button.js +++ b/packages/e2e-test-utils/src/click-block-toolbar-button.js @@ -15,12 +15,13 @@ export async function clickBlockToolbarButton( label, type = 'ariaLabel' ) { let button; if ( type === 'ariaLabel' ) { - const BUTTON_SELECTOR = `.${ BLOCK_TOOLBAR_SELECTOR } button[aria-label="${ label }"]`; - button = await page.waitForSelector( BUTTON_SELECTOR ); + button = await page.waitForSelector( + `.${ BLOCK_TOOLBAR_SELECTOR } button[aria-label="${ label }"]` + ); } if ( type === 'content' ) { - [ button ] = await page.$x( + button = await page.waitForXPath( `//*[@class='${ BLOCK_TOOLBAR_SELECTOR }']//button[contains(text(), '${ label }')]` ); } diff --git a/packages/e2e-test-utils/src/index.js b/packages/e2e-test-utils/src/index.js index 2c822577d683ca..c70f7fd15f69c7 100644 --- a/packages/e2e-test-utils/src/index.js +++ b/packages/e2e-test-utils/src/index.js @@ -1,6 +1,7 @@ export { activatePlugin } from './activate-plugin'; export { activateTheme } from './activate-theme'; export { arePrePublishChecksEnabled } from './are-pre-publish-checks-enabled'; +export { changeSiteTimezone } from './change-site-timezone'; export { clearLocalStorage } from './clear-local-storage'; export { clickBlockAppender } from './click-block-appender'; export { clickBlockToolbarButton } from './click-block-toolbar-button'; @@ -52,7 +53,10 @@ export { openDocumentSettingsSidebar } from './open-document-settings-sidebar'; export { openPublishPanel } from './open-publish-panel'; export { trashAllPosts } from './posts'; export { pressKeyTimes } from './press-key-times'; -export { pressKeyWithModifier } from './press-key-with-modifier'; +export { + pressKeyWithModifier, + setClipboardData, +} from './press-key-with-modifier'; export { publishPost } from './publish-post'; export { publishPostWithPrePublishChecksDisabled } from './publish-post-with-pre-publish-checks-disabled'; export { saveDraft } from './save-draft'; @@ -71,5 +75,6 @@ export { uninstallPlugin } from './uninstall-plugin'; export { visitAdminPage } from './visit-admin-page'; export { waitForWindowDimensions } from './wait-for-window-dimensions'; export { showBlockToolbar } from './show-block-toolbar'; +export { openPreviewPage } from './preview'; export * from './mocks'; diff --git a/packages/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js index fafd726da6181a..c3d31299f3e42b 100644 --- a/packages/e2e-test-utils/src/inserter.js +++ b/packages/e2e-test-utils/src/inserter.js @@ -59,7 +59,9 @@ export async function toggleGlobalBlockInserter() { async function waitForInserterCloseAndContentFocus() { await page.waitForFunction( () => document.body - .querySelector( '.block-editor-block-list__layout' ) + .querySelector( + '.interface-interface-skeleton__content .block-editor-block-list__layout' + ) .contains( document.activeElement ) ); } diff --git a/packages/e2e-test-utils/src/open-document-settings-sidebar.js b/packages/e2e-test-utils/src/open-document-settings-sidebar.js index ab6f797a4bacca..2ab5f773c3715f 100644 --- a/packages/e2e-test-utils/src/open-document-settings-sidebar.js +++ b/packages/e2e-test-utils/src/open-document-settings-sidebar.js @@ -8,5 +8,6 @@ export async function openDocumentSettingsSidebar() { if ( openButton ) { await openButton.click(); + await page.waitForSelector( '.edit-post-sidebar' ); } } diff --git a/packages/e2e-test-utils/src/press-key-with-modifier.js b/packages/e2e-test-utils/src/press-key-with-modifier.js index abb7f168e60901..b79f27c1d4a72b 100644 --- a/packages/e2e-test-utils/src/press-key-with-modifier.js +++ b/packages/e2e-test-utils/src/press-key-with-modifier.js @@ -81,6 +81,26 @@ async function emulateSelectAll() { } ); } +/** + * Sets the clipboard data that can be pasted with + * `pressKeyWithModifier( 'primary', 'v' )`. + * + * @param {Object} $1 Options. + * @param {string} $1.plainText Plain text to set. + * @param {string} $1.html HTML to set. + */ +export async function setClipboardData( { plainText = '', html = '' } ) { + await page.evaluate( + ( _plainText, _html ) => { + window._clipboardData = new DataTransfer(); + window._clipboardData.setData( 'text/plain', _plainText ); + window._clipboardData.setData( 'text/html', _html ); + }, + plainText, + html + ); +} + async function emulateClipboard( type ) { await page.evaluate( ( _type ) => { if ( _type !== 'paste' ) { diff --git a/packages/e2e-test-utils/src/preview.js b/packages/e2e-test-utils/src/preview.js new file mode 100644 index 00000000000000..90486bb174c65d --- /dev/null +++ b/packages/e2e-test-utils/src/preview.js @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { last } from 'lodash'; + +/** @typedef {import('puppeteer').Page} Page */ + +/** + * Opens the preview page of an edited post. + * + * @param {Page} editorPage puppeteer editor page. + * @return {Page} preview page. + */ +export async function openPreviewPage( editorPage = page ) { + let openTabs = await browser.pages(); + const expectedTabsCount = openTabs.length + 1; + await editorPage.click( '.block-editor-post-preview__button-toggle' ); + await editorPage.waitFor( '.edit-post-header-preview__button-external' ); + await editorPage.click( '.edit-post-header-preview__button-external' ); + + // Wait for the new tab to open. + while ( openTabs.length < expectedTabsCount ) { + await editorPage.waitFor( 1 ); + openTabs = await browser.pages(); + } + + const previewPage = last( openTabs ); + return previewPage; +} diff --git a/packages/e2e-test-utils/src/shared/get-json-response.js b/packages/e2e-test-utils/src/shared/get-json-response.js index 4d8581129de799..c09d6f3a186624 100644 --- a/packages/e2e-test-utils/src/shared/get-json-response.js +++ b/packages/e2e-test-utils/src/shared/get-json-response.js @@ -6,7 +6,7 @@ */ export function getJSONResponse( obj ) { return { - content: 'application/json', + contentType: 'application/json', body: JSON.stringify( obj ), }; } diff --git a/packages/e2e-tests/assets/1024x768_e2e_test_image_size.jpg b/packages/e2e-tests/assets/1024x768_e2e_test_image_size.jpg new file mode 100644 index 00000000000000..694e710f77c527 Binary files /dev/null and b/packages/e2e-tests/assets/1024x768_e2e_test_image_size.jpg differ diff --git a/packages/e2e-tests/assets/10x10_e2e_test_image_z9T8jK.png b/packages/e2e-tests/assets/10x10_e2e_test_image_z9T8jK.png index 4d198c0023578e..a13b8d3415a5a9 100644 Binary files a/packages/e2e-tests/assets/10x10_e2e_test_image_z9T8jK.png and b/packages/e2e-tests/assets/10x10_e2e_test_image_z9T8jK.png differ diff --git a/packages/e2e-tests/config/performance-reporter.js b/packages/e2e-tests/config/performance-reporter.js index 35ca2d8dde188c..d73d348ae4b974 100644 --- a/packages/e2e-tests/config/performance-reporter.js +++ b/packages/e2e-tests/config/performance-reporter.js @@ -28,7 +28,9 @@ class PerformanceReporter { } const results = readFileSync( filepath, 'utf8' ); - const { load, type, focus } = JSON.parse( results ); + const { load, type, focus, inserterOpen, inserterHover } = JSON.parse( + results + ); if ( load && load.length ) { // eslint-disable-next-line no-console @@ -63,6 +65,36 @@ Fastest time to select a block: ${ success( ) }` ); } + if ( inserterOpen && inserterOpen.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Opening Global Inserter Performance:' ) } +Average time to open global inserter: ${ success( + round( average( inserterOpen ) ) + 'ms' + ) } +Slowest time to open global inserter: ${ success( + round( Math.max( ...inserterOpen ) ) + 'ms' + ) } +Fastest time to open global inserter: ${ success( + round( Math.min( ...inserterOpen ) ) + 'ms' + ) }` ); + } + + if ( inserterHover && inserterHover.length ) { + // eslint-disable-next-line no-console + console.log( ` +${ title( 'Inserter Block Item Hover Performance:' ) } +Average time to move mouse between two block item in the inserter: ${ success( + round( average( inserterHover ) ) + 'ms' + ) } +Slowest time to move mouse between two block item in the inserter: ${ success( + round( Math.max( ...inserterHover ) ) + 'ms' + ) } +Fastest time to move mouse between two block item in the inserter: ${ success( + round( Math.min( ...inserterHover ) ) + 'ms' + ) }` ); + } + // eslint-disable-next-line no-console console.log( '' ); } diff --git a/packages/e2e-tests/config/setup-test-framework.js b/packages/e2e-tests/config/setup-test-framework.js index eabeb0fe38da4d..39b2a04ddc7604 100644 --- a/packages/e2e-tests/config/setup-test-framework.js +++ b/packages/e2e-tests/config/setup-test-framework.js @@ -174,54 +174,6 @@ function observeConsoleLogging() { } ); } -/** - * Runs Axe tests when the block editor is found on the current page. - * - * @return {?Promise} Promise resolving once Axe texts are finished. - */ -async function runAxeTestsForBlockEditor() { - if ( ! ( await page.$( '.block-editor' ) ) ) { - return; - } - - await expect( page ).toPassAxeTests( { - // Temporary disabled rules to enable initial integration. - // See: https://github.com/WordPress/gutenberg/pull/15018. - disabledRules: [ - 'aria-allowed-role', - 'aria-allowed-attr', - 'aria-hidden-focus', - 'aria-input-field-name', - 'aria-valid-attr-value', - 'button-name', - 'color-contrast', - 'dlitem', - 'duplicate-id', - 'label', - 'landmark-one-main', - 'link-name', - 'listitem', - 'region', - 'aria-required-children', - 'aria-required-parent', - 'frame-title', - ], - exclude: [ - // Ignores elements created by metaboxes. - '.edit-post-layout__metaboxes', - // Ignores elements created by TinyMCE. - '.mce-container', - // These properties were not included in the 1.1 spec - // through error, they should be allowed on role="row": - // https://github.com/w3c/aria/issues/558 - '[role="treegrid"] [aria-posinset]', - '[role="treegrid"] [aria-setsize]', - // Ignore block previews. - '.block-editor-block-preview__content', - ], - } ); -} - /** * Simulate slow network or throttled CPU if provided via environment variables. */ @@ -271,7 +223,6 @@ beforeAll( async () => { } ); afterEach( async () => { - await runAxeTestsForBlockEditor(); await setupBrowser(); } ); diff --git a/packages/e2e-tests/fixtures/blocks/core__buttons.json b/packages/e2e-tests/fixtures/blocks/core__buttons.json index 29df40b187fd34..0907230ef47504 100644 --- a/packages/e2e-tests/fixtures/blocks/core__buttons.json +++ b/packages/e2e-tests/fixtures/blocks/core__buttons.json @@ -5,6 +5,7 @@ "isValid": true, "attributes": { "contentJustification": "center", + "orientation": "horizontal", "align": "wide" }, "innerBlocks": [ diff --git a/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.html b/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.html new file mode 100644 index 00000000000000..f64dfae11e44a7 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.html @@ -0,0 +1,3 @@ +<!-- wp:navigation {"orientation":"horizontal","style":{"typography":{"textTransform":"var:preset|text-transform|lowercase","textDecoration":"var:preset|text-decoration|strikethrough","fontStyle":"var:preset|font-style|italic","fontWeight":"var:preset|font-weight|100"}}} --> +<!-- wp:navigation-link {"label":"WordPress","url":"https://www.wordpress.org/"} /--> +<!-- /wp:navigation --> diff --git a/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.json b/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.json new file mode 100644 index 00000000000000..c1e7672aea7ca6 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.json @@ -0,0 +1,34 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/navigation", + "isValid": true, + "attributes": { + "orientation": "horizontal", + "showSubmenuIcon": true, + "style": { + "typography": { + "textTransform": "lowercase", + "textDecoration": "line-through", + "fontStyle": "italic", + "fontWeight": "100" + } + } + }, + "innerBlocks": [ + { + "clientId": "_clientId_0", + "name": "core/navigation-link", + "isValid": true, + "attributes": { + "label": "WordPress", + "opensInNewTab": false, + "url": "https://www.wordpress.org/" + }, + "innerBlocks": [], + "originalContent": "" + } + ], + "originalContent": "" + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.parsed.json b/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.parsed.json new file mode 100644 index 00000000000000..11cb1a6b198be9 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.parsed.json @@ -0,0 +1,43 @@ +[ + { + "blockName": "core/navigation", + "attrs": { + "orientation": "horizontal", + "style": { + "typography": { + "textTransform": "var:preset|text-transform|lowercase", + "textDecoration": "var:preset|text-decoration|strikethrough", + "fontStyle": "var:preset|font-style|italic", + "fontWeight": "var:preset|font-weight|100" + } + } + }, + "innerBlocks": [ + { + "blockName": "core/navigation-link", + "attrs": { + "label": "WordPress", + "url": "https://www.wordpress.org/" + }, + "innerBlocks": [], + "innerHTML": "", + "innerContent": [] + } + ], + "innerHTML": "\n\n", + "innerContent": [ + "\n", + null, + "\n" + ] + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n", + "innerContent": [ + "\n" + ] + } +] diff --git a/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.serialized.html b/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.serialized.html new file mode 100644 index 00000000000000..22bf66bf725368 --- /dev/null +++ b/packages/e2e-tests/fixtures/blocks/core__navigation__deprecated.serialized.html @@ -0,0 +1,3 @@ +<!-- wp:navigation {"orientation":"horizontal","style":{"typography":{"textTransform":"lowercase","textDecoration":"line-through","fontStyle":"italic","fontWeight":"100"}}} --> +<!-- wp:navigation-link {"label":"WordPress","url":"https://www.wordpress.org/"} /--> +<!-- /wp:navigation --> diff --git a/packages/e2e-tests/fixtures/blocks/core__query.json b/packages/e2e-tests/fixtures/blocks/core__query.json index 235fcefff51e21..29d1251d872b94 100644 --- a/packages/e2e-tests/fixtures/blocks/core__query.json +++ b/packages/e2e-tests/fixtures/blocks/core__query.json @@ -5,6 +5,7 @@ "isValid": true, "attributes": { "query": { + "inherit": true, "perPage": null, "pages": 1, "offset": 0, diff --git a/packages/e2e-tests/fixtures/blocks/core_buttons__simple__deprecated.json b/packages/e2e-tests/fixtures/blocks/core_buttons__simple__deprecated.json index d77d9752de37b0..1ae83f325b61d5 100644 --- a/packages/e2e-tests/fixtures/blocks/core_buttons__simple__deprecated.json +++ b/packages/e2e-tests/fixtures/blocks/core_buttons__simple__deprecated.json @@ -3,7 +3,9 @@ "clientId": "_clientId_0", "name": "core/buttons", "isValid": true, - "attributes": {}, + "attributes": { + "orientation": "horizontal" + }, "innerBlocks": [ { "clientId": "_clientId_0", diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index 547eb1d772fdf6..4b250980b78a44 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/e2e-tests", - "version": "1.24.4", + "version": "1.25.0", "description": "End-To-End (E2E) tests for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/e2e-tests/plugins/block-variations/index.js b/packages/e2e-tests/plugins/block-variations/index.js index 3a76ae7d42db66..219eedd6bec5e9 100644 --- a/packages/e2e-tests/plugins/block-variations/index.js +++ b/packages/e2e-tests/plugins/block-variations/index.js @@ -40,6 +40,8 @@ }, icon: 'yes-alt', scope: [ 'inserter' ], + isActive: ( { backgroundColor }, variationAttributes ) => + backgroundColor === variationAttributes.backgroundColor, } ); registerBlockVariation( 'core/paragraph', { @@ -52,6 +54,8 @@ }, icon: 'warning', scope: [ 'inserter' ], + isActive: ( { backgroundColor }, variationAttributes ) => + backgroundColor === variationAttributes.backgroundColor, } ); registerBlockVariation( 'core/columns', { diff --git a/packages/e2e-tests/plugins/image-size.php b/packages/e2e-tests/plugins/image-size.php new file mode 100644 index 00000000000000..55cdd022431507 --- /dev/null +++ b/packages/e2e-tests/plugins/image-size.php @@ -0,0 +1,32 @@ +<?php +/** + * Plugin Name: Gutenberg Test Image Size + * Plugin URI: https://github.com/WordPress/gutenberg + * Author: Gutenberg Team + * + * @package gutenberg-test-image-size + */ + +/** + * Registers a custom script for the plugin. + */ +function gutenberg_test_create_image_size() { + if ( function_exists( 'add_image_size' ) ) { + add_image_size( 'custom-size-one', 499 ); + } +} + +/** + * Add custom size + * + * @param Array $sizes Size name. + */ +function gutenberg_test_create_image_size_name( $sizes ) { + $custom_sizes = array( + 'custom-size-one' => 'Custom Size One', + ); + return array_merge( $sizes, $custom_sizes ); +} + +add_action( 'after_setup_theme', 'gutenberg_test_create_image_size' ); +add_filter( 'image_size_names_choose', 'gutenberg_test_create_image_size_name' ); diff --git a/packages/e2e-tests/specs/editor/blocks/__snapshots__/code.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/code.test.js.snap index 50ee83d373c65e..3b30c08206ba0d 100644 --- a/packages/e2e-tests/specs/editor/blocks/__snapshots__/code.test.js.snap +++ b/packages/e2e-tests/specs/editor/blocks/__snapshots__/code.test.js.snap @@ -5,3 +5,10 @@ exports[`Code can be created by three backticks and enter 1`] = ` <pre class=\\"wp-block-code\\"><code>&lt;?php</code></pre> <!-- /wp:code -->" `; + +exports[`Code should paste plain text 1`] = ` +"<!-- wp:code --> +<pre class=\\"wp-block-code\\"><code>&lt;img /> + &lt;br></code></pre> +<!-- /wp:code -->" +`; diff --git a/packages/e2e-tests/specs/editor/blocks/__snapshots__/image.test.js.snap b/packages/e2e-tests/specs/editor/blocks/__snapshots__/image.test.js.snap index d17e4a8d39f6ac..18b856a57a4303 100644 --- a/packages/e2e-tests/specs/editor/blocks/__snapshots__/image.test.js.snap +++ b/packages/e2e-tests/specs/editor/blocks/__snapshots__/image.test.js.snap @@ -1,5 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Image allows changing aspect ratio using the crop tools 1`] = `"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAACQf/xAAgEAAAAwkBAAAAAAAAAAAAAAAABAUBAwYJNTh0d7K1/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AJTJUuninX570U4K8rVU7kPOmgoZKl08U6/PeinBXlaqnch500B//9k="`; + +exports[`Image allows changing aspect ratio using the crop tools 2`] = `"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAGAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAcJ/8QAIBAAAQIFBQAAAAAAAAAAAAAAAAQFAQMGCTU4dHeytf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCU2VNU9U8frvRbjV52yq3cTO0QAP/Z"`; + +exports[`Image allows rotating using the crop tools 1`] = `"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAACQf/xAAgEAAAAwkBAAAAAAAAAAAAAAAABAUBAwYJNTh0d7K1/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AJTJUuninX570U4K8rVU7kPOmgoZKl08U6/PeinBXlaqnch500B//9k="`; + +exports[`Image allows rotating using the crop tools 2`] = `"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAcJ/8QAIRAAAAILAQAAAAAAAAAAAAAAAAUCBAcIFRlVVpOV0dT/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AlMlR6e/mVbUx8ISVHp7+ZVtTHwjV6LGtTW8yXQixrU1vMl0B/9k="`; + +exports[`Image allows zooming using the crop tools 1`] = `"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAACQf/xAAgEAAAAwkBAAAAAAAAAAAAAAAABAUBAwYJNTh0d7K1/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AJTJUuninX570U4K8rVU7kPOmgoZKl08U6/PeinBXlaqnch500B//9k="`; + +exports[`Image allows zooming using the crop tools 2`] = `"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAFAAUDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAaEAEAAQUAAAAAAAAAAAAAAAAABAcJOIW1/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AKptcYJ0y3XYmgA//9k="`; + exports[`Image should drag and drop files into media placeholder 1`] = ` "<!-- wp:image --> <figure class=\\"wp-block-image\\"><img alt=\\"\\"/></figure> diff --git a/packages/e2e-tests/specs/editor/blocks/buttons.test.js b/packages/e2e-tests/specs/editor/blocks/buttons.test.js index 3938bc2edb6c1d..9253d29b1cbd41 100644 --- a/packages/e2e-tests/specs/editor/blocks/buttons.test.js +++ b/packages/e2e-tests/specs/editor/blocks/buttons.test.js @@ -24,7 +24,15 @@ describe( 'Buttons', () => { // Regression: https://github.com/WordPress/gutenberg/pull/19885 await insertBlock( 'Buttons' ); await pressKeyWithModifier( 'primary', 'k' ); + await page.waitForFunction( + () => !! document.activeElement.closest( '.block-editor-url-input' ) + ); await page.keyboard.press( 'Escape' ); + await page.waitForFunction( + () => + document.activeElement === + document.querySelector( '.block-editor-rich-text__editable' ) + ); await page.keyboard.type( 'WordPress' ); expect( await getEditedPostContent() ).toMatchSnapshot(); diff --git a/packages/e2e-tests/specs/editor/blocks/code.test.js b/packages/e2e-tests/specs/editor/blocks/code.test.js index 039358aa0755c3..3747afec5e632d 100644 --- a/packages/e2e-tests/specs/editor/blocks/code.test.js +++ b/packages/e2e-tests/specs/editor/blocks/code.test.js @@ -6,6 +6,8 @@ import { clickBlockAppender, getEditedPostContent, createNewPost, + setClipboardData, + pressKeyWithModifier, } from '@wordpress/e2e-test-utils'; describe( 'Code', () => { @@ -32,4 +34,15 @@ describe( 'Code', () => { // Expect code block to be deleted. expect( await getEditedPostContent() ).toBe( '' ); } ); + + it( 'should paste plain text', async () => { + await insertBlock( 'Code' ); + + // Test to see if HTML and white space is kept. + await setClipboardData( { plainText: '<img />\n\t<br>' } ); + + await pressKeyWithModifier( 'primary', 'v' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); } ); diff --git a/packages/e2e-tests/specs/editor/blocks/image.test.js b/packages/e2e-tests/specs/editor/blocks/image.test.js index 5c4ce8b1cdf4fd..b254413a141df9 100644 --- a/packages/e2e-tests/specs/editor/blocks/image.test.js +++ b/packages/e2e-tests/specs/editor/blocks/image.test.js @@ -14,7 +14,10 @@ import { getEditedPostContent, createNewPost, clickButton, + clickBlockToolbarButton, + clickMenuItem, openDocumentSettingsSidebar, + pressKeyWithModifier, } from '@wordpress/e2e-test-utils'; async function upload( selector ) { @@ -41,6 +44,20 @@ async function waitForImage( filename ) { ); } +async function getSrc( elementHandle ) { + return elementHandle.evaluate( ( node ) => node.src ); +} +async function getDataURL( elementHandle ) { + return elementHandle.evaluate( ( node ) => { + const canvas = document.createElement( 'canvas' ); + const context = canvas.getContext( '2d' ); + canvas.width = node.width; + canvas.height = node.height; + context.drawImage( node, 0, 0 ); + return canvas.toDataURL( 'image/jpeg' ); + } ); +} + describe( 'Image', () => { beforeEach( async () => { await createNewPost(); @@ -161,4 +178,114 @@ describe( 'Image', () => { await waitForImage( fileName ); } ); + + it( 'allows zooming using the crop tools', async () => { + // Insert the block, upload a file and crop. + await insertBlock( 'Image' ); + const filename = await upload( '.wp-block-image input[type="file"]' ); + await waitForImage( filename ); + + // Assert that the image is initially unscaled and unedited. + const initialImage = await page.$( '.wp-block-image img' ); + const initialImageSrc = await getSrc( initialImage ); + const initialImageDataURL = await getDataURL( initialImage ); + expect( initialImageDataURL ).toMatchSnapshot(); + + // Zoom in to twice the amount using the zoom input. + await clickBlockToolbarButton( 'Crop' ); + await clickBlockToolbarButton( 'Zoom' ); + await page.waitForFunction( () => + document.activeElement.classList.contains( + 'components-range-control__slider' + ) + ); + await page.keyboard.press( 'Tab' ); + await page.waitForFunction( () => + document.activeElement.classList.contains( + 'components-input-control__input' + ) + ); + await pressKeyWithModifier( 'primary', 'a' ); + await page.keyboard.type( '200' ); + await page.keyboard.press( 'Escape' ); + await clickBlockToolbarButton( 'Apply', 'content' ); + + // Wait for the cropping tools to disappear. + await page.waitForSelector( + '.wp-block-image img:not( .reactEasyCrop_Image )' + ); + + // Assert that the image is edited. + const updatedImage = await page.$( '.wp-block-image img' ); + const updatedImageSrc = await getSrc( updatedImage ); + expect( initialImageSrc ).not.toEqual( updatedImageSrc ); + const updatedImageDataURL = await getDataURL( updatedImage ); + expect( initialImageDataURL ).not.toEqual( updatedImageDataURL ); + expect( updatedImageDataURL ).toMatchSnapshot(); + } ); + + it( 'allows changing aspect ratio using the crop tools', async () => { + // Insert the block, upload a file and crop. + await insertBlock( 'Image' ); + const filename = await upload( '.wp-block-image input[type="file"]' ); + await waitForImage( filename ); + + // Assert that the image is initially unscaled and unedited. + const initialImage = await page.$( '.wp-block-image img' ); + const initialImageSrc = await getSrc( initialImage ); + const initialImageDataURL = await getDataURL( initialImage ); + expect( initialImageDataURL ).toMatchSnapshot(); + + // Zoom in to twice the amount using the zoom input. + await clickBlockToolbarButton( 'Crop' ); + await clickBlockToolbarButton( 'Aspect Ratio' ); + await page.waitForFunction( () => + document.activeElement.classList.contains( + 'components-menu-item__button' + ) + ); + await clickMenuItem( '16:10' ); + await clickBlockToolbarButton( 'Apply', 'content' ); + + // Wait for the cropping tools to disappear. + await page.waitForSelector( + '.wp-block-image img:not( .reactEasyCrop_Image )' + ); + + // Assert that the image is edited. + const updatedImage = await page.$( '.wp-block-image img' ); + const updatedImageSrc = await getSrc( updatedImage ); + expect( initialImageSrc ).not.toEqual( updatedImageSrc ); + const updatedImageDataURL = await getDataURL( updatedImage ); + expect( initialImageDataURL ).not.toEqual( updatedImageDataURL ); + expect( updatedImageDataURL ).toMatchSnapshot(); + } ); + + it( 'allows rotating using the crop tools', async () => { + // Insert the block, upload a file and crop. + await insertBlock( 'Image' ); + const filename = await upload( '.wp-block-image input[type="file"]' ); + await waitForImage( filename ); + + // Assert that the image is initially unscaled and unedited. + const initialImage = await page.$( '.wp-block-image img' ); + const initialImageDataURL = await getDataURL( initialImage ); + expect( initialImageDataURL ).toMatchSnapshot(); + + // Double the image's size using the zoom input. + await clickBlockToolbarButton( 'Crop' ); + await page.waitForSelector( '.wp-block-image img.reactEasyCrop_Image' ); + await clickBlockToolbarButton( 'Rotate' ); + await clickBlockToolbarButton( 'Apply', 'content' ); + + await page.waitForSelector( + '.wp-block-image img:not( .reactEasyCrop_Image )' + ); + + // Assert that the image is edited. + const updatedImage = await page.$( '.wp-block-image img' ); + const updatedImageDataURL = await getDataURL( updatedImage ); + expect( initialImageDataURL ).not.toEqual( updatedImageDataURL ); + expect( updatedImageDataURL ).toMatchSnapshot(); + } ); } ); diff --git a/packages/e2e-tests/specs/editor/plugins/block-context.test.js b/packages/e2e-tests/specs/editor/plugins/block-context.test.js index 3a96eb3e141f6a..f09df529e70ce0 100644 --- a/packages/e2e-tests/specs/editor/plugins/block-context.test.js +++ b/packages/e2e-tests/specs/editor/plugins/block-context.test.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { last } from 'lodash'; - /** * WordPress dependencies */ @@ -12,29 +7,9 @@ import { deactivatePlugin, insertBlock, saveDraft, + openPreviewPage, } from '@wordpress/e2e-test-utils'; -async function openPreviewPage( editorPage ) { - let openTabs = await browser.pages(); - const expectedTabsCount = openTabs.length + 1; - await editorPage.click( '.block-editor-post-preview__button-toggle' ); - await editorPage.waitFor( '.edit-post-header-preview__button-external' ); - await editorPage.click( '.edit-post-header-preview__button-external' ); - - // Wait for the new tab to open. - while ( openTabs.length < expectedTabsCount ) { - await editorPage.waitFor( 1 ); - openTabs = await browser.pages(); - } - - const previewPage = last( openTabs ); - // Wait for the preview to load. We can't do interstitial detection here, - // because it might load too quickly for us to pick up, so we wait for - // the preview to load by waiting for the content to appear. - await previewPage.waitForSelector( '.entry-content' ); - return previewPage; -} - describe( 'Block context', () => { beforeAll( async () => { await activatePlugin( 'gutenberg-test-block-context' ); @@ -74,6 +49,7 @@ describe( 'Block context', () => { await insertBlock( 'Test Context Provider' ); const editorPage = page; const previewPage = await openPreviewPage( editorPage ); + await previewPage.waitForSelector( '.entry-content' ); // Check default context values are populated. let content = await previewPage.$eval( diff --git a/packages/e2e-tests/specs/editor/plugins/block-variations.js b/packages/e2e-tests/specs/editor/plugins/block-variations.js index ef4a17360284a3..81862f42a5e304 100644 --- a/packages/e2e-tests/specs/editor/plugins/block-variations.js +++ b/packages/e2e-tests/specs/editor/plugins/block-variations.js @@ -7,6 +7,8 @@ import { deactivatePlugin, insertBlock, searchForBlock, + pressKeyWithModifier, + openDocumentSettingsSidebar, } from '@wordpress/e2e-test-utils'; describe( 'Block variations', () => { @@ -103,4 +105,75 @@ describe( 'Block variations', () => { ) ).toHaveLength( 4 ); } ); + // @see @wordpres/block-editor/src/components/use-block-display-information (`useBlockDisplayInformation` hook). + describe( 'testing block display information with matching variations', () => { + const getActiveBreadcrumb = async () => + page.evaluate( + () => + document.querySelector( + '.block-editor-block-breadcrumb__current' + ).textContent + ); + const getFirstNavigationItem = async () => { + await pressKeyWithModifier( 'access', 'o' ); + // This also returns the visually hidden text `(selected block)`. + // For example `Paragraph(selected block)`. In order to hide this + // implementation detail and search for childNodes, we choose to + // test with `String.prototype.startsWith()`. + return page.evaluate( + () => + document.querySelector( + '.block-editor-block-navigation-block-select-button' + ).textContent + ); + }; + const getBlockCardDescription = async () => { + await openDocumentSettingsSidebar(); + return page.evaluate( + () => + document.querySelector( + '.block-editor-block-card__description' + ).textContent + ); + }; + + it( 'should show block information when no matching variation is found', async () => { + await insertBlock( 'Large Quote' ); + const breadcrumb = await getActiveBreadcrumb(); + expect( breadcrumb ).toEqual( 'Quote' ); + const navigationItem = await getFirstNavigationItem(); + expect( navigationItem.startsWith( 'Quote' ) ).toBeTruthy(); + const description = await getBlockCardDescription(); + expect( description ).toEqual( + 'Give quoted text visual emphasis. "In quoting others, we cite ourselves." — Julio Cortázar' + ); + } ); + it( 'should display variations info if all declared', async () => { + await insertBlock( 'Success Message' ); + const breadcrumb = await getActiveBreadcrumb(); + expect( breadcrumb ).toEqual( 'Success Message' ); + const navigationItem = await getFirstNavigationItem(); + expect( + navigationItem.startsWith( 'Success Message' ) + ).toBeTruthy(); + const description = await getBlockCardDescription(); + expect( description ).toEqual( + 'This block displays a success message. This description overrides the default one provided for the Paragraph block.' + ); + } ); + it( 'should display mixed block and variation match information', async () => { + // Warning Message variation is missing the `description`. + await insertBlock( 'Warning Message' ); + const breadcrumb = await getActiveBreadcrumb(); + expect( breadcrumb ).toEqual( 'Warning Message' ); + const navigationItem = await getFirstNavigationItem(); + expect( + navigationItem.startsWith( 'Warning Message' ) + ).toBeTruthy(); + const description = await getBlockCardDescription(); + expect( description ).toEqual( + 'Start with the building block of all narrative.' + ); + } ); + } ); } ); diff --git a/packages/e2e-tests/specs/editor/plugins/image-size.test.js b/packages/e2e-tests/specs/editor/plugins/image-size.test.js new file mode 100644 index 00000000000000..a92ae630465506 --- /dev/null +++ b/packages/e2e-tests/specs/editor/plugins/image-size.test.js @@ -0,0 +1,79 @@ +/** + * External dependencies + */ +import path from 'path'; +import fs from 'fs'; +import os from 'os'; +import { v4 as uuid } from 'uuid'; + +/** + * WordPress dependencies + */ +import { + activatePlugin, + createNewPost, + clickButton, + deactivatePlugin, + insertBlock, + openDocumentSettingsSidebar, +} from '@wordpress/e2e-test-utils'; + +describe( 'changing image size', () => { + beforeEach( async () => { + await activatePlugin( 'gutenberg-test-image-size' ); + await createNewPost(); + } ); + + afterEach( async () => { + await deactivatePlugin( 'gutenberg-test-image-size' ); + } ); + + it( 'should insert and change my image size', async () => { + await insertBlock( 'Image' ); + await clickButton( 'Media Library' ); + + // Wait for media modal to appear and upload image. + await page.waitForSelector( '.media-modal input[type=file]' ); + const inputElement = await page.$( '.media-modal input[type=file]' ); + const testImagePath = path.join( + __dirname, + '..', + '..', + '..', + 'assets', + '1024x768_e2e_test_image_size.jpg' + ); + const filename = uuid(); + const tmpFileName = path.join( os.tmpdir(), filename + '.jpg' ); + fs.copyFileSync( testImagePath, tmpFileName ); + await inputElement.uploadFile( tmpFileName ); + + // Wait for upload to finish. + await page.waitForSelector( + `.media-modal li[aria-label="${ filename }"]` + ); + + // Insert the uploaded image. + await page.click( '.media-modal button.media-button-select' ); + + // Select the new size updated with the plugin. + await openDocumentSettingsSidebar(); + const imageSizeLabel = await page.waitForXPath( + '//label[text()="Image size"]' + ); + await imageSizeLabel.click(); + const imageSizeSelect = await page.evaluateHandle( + () => document.activeElement + ); + await imageSizeSelect.select( 'custom-size-one' ); + + // Verify that the custom size was applied to the image. + await page.waitForSelector( '.wp-block-image.size-custom-size-one' ); + await page.waitForFunction( + () => + document.querySelector( + '.block-editor-image-size-control__width input' + ).value === '499' + ); + } ); +} ); diff --git a/packages/e2e-tests/specs/editor/various/adding-blocks.test.js b/packages/e2e-tests/specs/editor/various/adding-blocks.test.js index b2b1758977054b..8af2b7cd6b5b5e 100644 --- a/packages/e2e-tests/specs/editor/various/adding-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/adding-blocks.test.js @@ -14,6 +14,29 @@ import { /** @typedef {import('puppeteer').ElementHandle} ElementHandle */ +/** + * Waits for all patterns in the inserter to have a height, which should + * indicate they've been parsed and are visible. + * + * This allows a test to wait for the layout in the inserter menu to stabilize + * before attempting to interact with the menu contents. + */ +async function waitForInserterPatternLoad() { + await page.waitForFunction( () => { + const previewElements = document.querySelectorAll( + '.block-editor-block-preview__container' + ); + + if ( ! previewElements.length ) { + return true; + } + + return Array.from( previewElements ).every( + ( previewElement ) => previewElement.offsetHeight > 0 + ); + } ); +} + describe( 'adding blocks', () => { beforeEach( async () => { await createNewPost(); @@ -214,7 +237,15 @@ describe( 'adding blocks', () => { // Insert a paragraph block. await page.waitForSelector( '.block-editor-inserter__search-input' ); - await page.keyboard.type( 'Paragraph' ); + + // Search for the paragraph block if it's not in the list of blocks shown. + if ( ! page.$( '.editor-block-list-item-paragraph' ) ) { + await page.keyboard.type( 'Paragraph' ); + await page.waitForSelector( '.editor-block-list-item-paragraph' ); + await waitForInserterPatternLoad(); + } + + // Add the block. await page.click( '.editor-block-list-item-paragraph' ); await page.keyboard.type( '2' ); @@ -319,4 +350,26 @@ describe( 'adding blocks', () => { await page.keyboard.type( 'Paragraph inside group' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + // Check for regression of https://github.com/WordPress/gutenberg/issues/27586 + it( 'closes the main inserter after inserting a single-use block, like the More block', async () => { + await insertBlock( 'More' ); + await page.waitForSelector( + '.edit-post-header-toolbar__inserter-toggle:not(.is-pressed)' + ); + + // The inserter panel should've closed. + const inserterPanels = await page.$$( + '.edit-post-layout__inserter-panel' + ); + expect( inserterPanels.length ).toBe( 0 ); + + // The editable 'Read More' text should be focused. + const isFocusInBlock = await page.evaluate( () => + document + .querySelector( '[data-type="core/more"]' ) + .contains( document.activeElement ) + ); + expect( isFocusInBlock ).toBe( true ); + } ); } ); diff --git a/packages/e2e-tests/specs/editor/various/change-detection.test.js b/packages/e2e-tests/specs/editor/various/change-detection.test.js index 8bac378e8aaa1a..413aeda6adf09f 100644 --- a/packages/e2e-tests/specs/editor/various/change-detection.test.js +++ b/packages/e2e-tests/specs/editor/various/change-detection.test.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { first } from 'lodash'; + /** * WordPress dependencies */ @@ -10,6 +15,7 @@ import { saveDraft, openDocumentSettingsSidebar, isCurrentURL, + pressKeyTimes, } from '@wordpress/e2e-test-utils'; describe( 'Change detection', () => { @@ -132,9 +138,11 @@ describe( 'Change detection', () => { await page.type( '.editor-post-title__input', '!' ); await Promise.all( [ - page.waitForSelector( '.editor-post-publish-button.is-busy' ), page.waitForSelector( - '.editor-post-publish-button:not( .is-busy )' + '.editor-post-publish-button[aria-disabled="true"]' + ), + page.waitForSelector( + '.editor-post-publish-button[aria-disabled="false"]' ), page.evaluate( () => window.wp.data.dispatch( 'core/editor' ).autosave() @@ -356,6 +364,8 @@ describe( 'Change detection', () => { } ); it( 'consecutive edits to the same attribute should mark the post as dirty after a save', async () => { + const FONT_SIZE_LABEL_SELECTOR = + "//label[contains(text(), 'Font size')]"; // Open the sidebar block settings. await openDocumentSettingsSidebar(); await page.click( '.edit-post-sidebar__panel-tab[data-label="Block"]' ); @@ -372,10 +382,9 @@ describe( 'Change detection', () => { // Increase the paragraph's font size. await page.click( '[data-type="core/paragraph"]' ); - await page.click( '.components-font-size-picker__select' ); - await page.click( - '.components-custom-select-control__item:nth-child(3)' - ); + await first( await page.$x( FONT_SIZE_LABEL_SELECTOR ) ).click(); + await pressKeyTimes( 'ArrowDown', 2 ); + await page.keyboard.press( 'Enter' ); await page.click( '[data-type="core/paragraph"]' ); // Check that the post is dirty. @@ -389,10 +398,9 @@ describe( 'Change detection', () => { // Increase the paragraph's font size again. await page.click( '[data-type="core/paragraph"]' ); - await page.click( '.components-font-size-picker__select' ); - await page.click( - '.components-custom-select-control__item:nth-child(4)' - ); + await first( await page.$x( FONT_SIZE_LABEL_SELECTOR ) ).click(); + await pressKeyTimes( 'ArrowDown', 3 ); + await page.keyboard.press( 'Enter' ); await page.click( '[data-type="core/paragraph"]' ); // Check that the post is dirty. diff --git a/packages/e2e-tests/specs/editor/various/datepicker.test.js b/packages/e2e-tests/specs/editor/various/datepicker.test.js index 6118f05368ae77..f2203e92e9a5dc 100644 --- a/packages/e2e-tests/specs/editor/various/datepicker.test.js +++ b/packages/e2e-tests/specs/editor/various/datepicker.test.js @@ -1,88 +1,137 @@ /** * WordPress dependencies */ -import { createNewPost } from '@wordpress/e2e-test-utils'; +import { createNewPost, changeSiteTimezone } from '@wordpress/e2e-test-utils'; + +async function getInputValue( selector ) { + return page.$eval( selector, ( element ) => element.value ); +} + +async function getSelectedOptionLabel( selector ) { + return page.$eval( + selector, + ( element ) => element.options[ element.selectedIndex ].text + ); +} + +async function getDatePickerValues() { + const year = await getInputValue( '[aria-label="Year"]' ); + const month = await getInputValue( '[aria-label="Month"]' ); + const monthLabel = await getSelectedOptionLabel( '[aria-label="Month"]' ); + const day = await getInputValue( '[aria-label="Day"]' ); + const hours = await getInputValue( '[aria-label="Hours"]' ); + const minutes = await getInputValue( '[aria-label="Minutes"]' ); + const amOrPm = await page.$eval( + '.components-datetime__time-field-am-pm .is-primary', + ( element ) => element.innerText.toLowerCase() + ); + + return { year, month, monthLabel, day, hours, minutes, amOrPm }; +} + +function trimLeadingZero( str ) { + return str[ 0 ] === '0' ? str.slice( 1 ) : str; +} + +function formatDatePickerValues( { + year, + monthLabel, + day, + hours, + minutes, + amOrPm, +} ) { + const dayTrimmed = trimLeadingZero( day ); + const hoursTrimmed = trimLeadingZero( hours ); + return `${ monthLabel } ${ dayTrimmed }, ${ year } ${ hoursTrimmed }:${ minutes } ${ amOrPm }`; +} + +async function getPublishingDate() { + return page.$eval( + '.edit-post-post-schedule__toggle', + ( dateLabel ) => dateLabel.textContent + ); +} describe( 'Datepicker', () => { - beforeEach( async () => { - await createNewPost(); - } ); + [ 'UTC-10', 'UTC', 'UTC+10' ].forEach( ( timezone ) => { + describe( timezone, () => { + let oldTimezone; + beforeEach( async () => { + oldTimezone = await changeSiteTimezone( timezone ); + await createNewPost(); + } ); + afterEach( async () => { + await changeSiteTimezone( oldTimezone ); + } ); - it( 'should show the publishing date as "Immediately" if the date is not altered', async () => { - const publishingDate = await page.$eval( - '.edit-post-post-schedule__toggle', - ( dateLabel ) => dateLabel.textContent - ); + it( 'should show the publishing date as "Immediately" if the date is not altered', async () => { + const publishingDate = await getPublishingDate(); - expect( publishingDate ).toEqual( 'Immediately' ); - } ); + expect( publishingDate ).toEqual( 'Immediately' ); + } ); - it( 'should show the publishing date if the date is in the past', async () => { - // Open the datepicker. - await page.click( '.edit-post-post-schedule__toggle' ); + it( 'should show the publishing date if the date is in the past', async () => { + // Open the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); - // Change the publishing date to a year in the past. - await page.click( '.components-datetime__time-field-year' ); - await page.keyboard.press( 'ArrowDown' ); + // Change the publishing date to a year in the past. + await page.click( '.components-datetime__time-field-year' ); + await page.keyboard.press( 'ArrowDown' ); + const datePickerValues = await getDatePickerValues(); - // Close the datepicker. - await page.click( '.edit-post-post-schedule__toggle' ); + // Close the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); - const publishingDate = await page.$eval( - '.edit-post-post-schedule__toggle', - ( dateLabel ) => dateLabel.textContent - ); + const publishingDate = await getPublishingDate(); - expect( publishingDate ).toMatch( - /[A-Za-z]{3} \d{1,2}, \d{4} \d{1,2}:\d{2} [ap]m/ - ); - } ); + expect( publishingDate ).toBe( + formatDatePickerValues( datePickerValues ) + ); + } ); - it( 'should show the publishing date if the date is in the future', async () => { - // Open the datepicker. - await page.click( '.edit-post-post-schedule__toggle' ); + it( 'should show the publishing date if the date is in the future', async () => { + // Open the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); - // Change the publishing date to a year in the future. - await page.click( '.components-datetime__time-field-year' ); - await page.keyboard.press( 'ArrowUp' ); + // Change the publishing date to a year in the future. + await page.click( '.components-datetime__time-field-year' ); + await page.keyboard.press( 'ArrowUp' ); + const datePickerValues = await getDatePickerValues(); - // Close the datepicker. - await page.click( '.edit-post-post-schedule__toggle' ); + // Close the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); - const publishingDate = await page.$eval( - '.edit-post-post-schedule__toggle', - ( dateLabel ) => dateLabel.textContent - ); + const publishingDate = await getPublishingDate(); - expect( publishingDate ).not.toEqual( 'Immediately' ); - // The expected date format will be "Sep 26, 2018 11:52 pm". - expect( publishingDate ).toMatch( - /[A-Za-z]{3} \d{1,2}, \d{4} \d{1,2}:\d{2} [ap]m/ - ); - } ); + expect( publishingDate ).not.toEqual( 'Immediately' ); + // The expected date format will be "Sep 26, 2018 11:52 pm". + expect( publishingDate ).toBe( + formatDatePickerValues( datePickerValues ) + ); + } ); - it( 'should show the publishing date as "Immediately" if the date is cleared', async () => { - // Open the datepicker. - await page.click( '.edit-post-post-schedule__toggle' ); + it( `should show the publishing date as "Immediately" if the date is cleared`, async () => { + // Open the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); - // Change the publishing date to a year in the future. - await page.click( '.components-datetime__time-field-year' ); - await page.keyboard.press( 'ArrowUp' ); + // Change the publishing date to a year in the future. + await page.click( '.components-datetime__time-field-year' ); + await page.keyboard.press( 'ArrowUp' ); - // Close the datepicker. - await page.click( '.edit-post-post-schedule__toggle' ); + // Close the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); - // Open the datepicker. - await page.click( '.edit-post-post-schedule__toggle' ); + // Open the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); - // Clear the date - await page.click( '.components-datetime__date-reset-button' ); + // Clear the date + await page.click( '.components-datetime__date-reset-button' ); - const publishingDate = await page.$eval( - '.edit-post-post-schedule__toggle', - ( dateLabel ) => dateLabel.textContent - ); + const publishingDate = await getPublishingDate(); - expect( publishingDate ).toEqual( 'Immediately' ); + expect( publishingDate ).toEqual( 'Immediately' ); + } ); + } ); } ); } ); diff --git a/packages/e2e-tests/specs/editor/various/editor-modes.test.js b/packages/e2e-tests/specs/editor/various/editor-modes.test.js index 82e481ead2da25..48056ccde42763 100644 --- a/packages/e2e-tests/specs/editor/various/editor-modes.test.js +++ b/packages/e2e-tests/specs/editor/various/editor-modes.test.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { first } from 'lodash'; + /** * WordPress dependencies */ @@ -54,8 +59,8 @@ describe( 'Editing modes (visual/HTML)', () => { // The font size picker for the paragraph block should appear, even in // HTML editing mode. - const fontSizePicker = await page.$$( - '.edit-post-sidebar .components-font-size-picker__controls' + const fontSizePicker = await page.$x( + "//label[contains(text(), 'Font size')]" ); expect( fontSizePicker ).toHaveLength( 1 ); } ); @@ -73,13 +78,11 @@ describe( 'Editing modes (visual/HTML)', () => { expect( htmlBlockContent ).toEqual( '<p>Hello world!</p>' ); // Change the font size using the sidebar. - await page.click( '.components-font-size-picker__select' ); - await page.click( - '.components-custom-select-control__item:nth-child(5)' - ); - await page.waitForXPath( - `//button[contains(@class, "components-custom-select-control__button") and contains(text(), 'Large')]` - ); + await first( + await page.$x( "//label[contains(text(), 'Font size')]" ) + ).click(); + await pressKeyTimes( 'ArrowDown', 4 ); + await page.keyboard.press( 'Enter' ); // Make sure the HTML content updated. htmlBlockContent = await page.$eval( diff --git a/packages/e2e-tests/specs/editor/various/font-size-picker.test.js b/packages/e2e-tests/specs/editor/various/font-size-picker.test.js index a7125e39f920a8..84edbd564b5352 100644 --- a/packages/e2e-tests/specs/editor/various/font-size-picker.test.js +++ b/packages/e2e-tests/specs/editor/various/font-size-picker.test.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { first } from 'lodash'; + /** * WordPress dependencies */ @@ -9,6 +14,9 @@ import { } from '@wordpress/e2e-test-utils'; describe( 'Font Size Picker', () => { + const FONT_SIZE_LABEL_SELECTOR = "//label[contains(text(), 'Font size')]"; + const CUSTOM_FONT_SIZE_LABEL_SELECTOR = + "//fieldset[legend[contains(text(),'Font size')]]//label[contains(text(), 'Custom')]"; beforeEach( async () => { await createNewPost(); } ); @@ -17,14 +25,25 @@ describe( 'Font Size Picker', () => { // Create a paragraph block with some content. await clickBlockAppender(); await page.keyboard.type( 'Paragraph to be made "large"' ); - await page.click( '.components-font-size-picker__select' ); - await page.click( - '.components-custom-select-control__item:nth-child(5)' - ); - await page.waitForXPath( - `//button[contains(@class, "components-custom-select-control__button") and contains(text(), 'Large')]` + await first( await page.$x( FONT_SIZE_LABEL_SELECTOR ) ).click(); + await pressKeyTimes( 'ArrowDown', 4 ); + await page.keyboard.press( 'Enter' ); + const selectedFontSize = await page.evaluate( + ( selector ) => + document + .evaluate( + selector, + document, + null, + XPathResult.ANY_TYPE, + null + ) + .iterateNext().control.textContent, + FONT_SIZE_LABEL_SELECTOR ); + expect( selectedFontSize ).toBe( 'Large' ); + // Ensure content matches snapshot. const content = await getEditedPostContent(); expect( content ).toMatchSnapshot(); @@ -35,11 +54,10 @@ describe( 'Font Size Picker', () => { await clickBlockAppender(); await page.keyboard.type( 'Paragraph to be made "small"' ); - await page.click( - '.components-font-size-picker__controls .components-font-size-picker__number' - ); + await first( await page.$x( CUSTOM_FONT_SIZE_LABEL_SELECTOR ) ).click(); // This should be the "small" font-size of the editor defaults. await page.keyboard.type( '13' ); + await page.keyboard.press( 'Enter' ); // Ensure content matches snapshot. const content = await getEditedPostContent(); @@ -51,10 +69,9 @@ describe( 'Font Size Picker', () => { await clickBlockAppender(); await page.keyboard.type( 'Paragraph to be made "small"' ); - await page.click( - '.components-font-size-picker__controls .components-font-size-picker__number' - ); + await first( await page.$x( CUSTOM_FONT_SIZE_LABEL_SELECTOR ) ).click(); await page.keyboard.type( '23' ); + await page.keyboard.press( 'Enter' ); // Ensure content matches snapshot. const content = await getEditedPostContent(); @@ -68,17 +85,13 @@ describe( 'Font Size Picker', () => { 'Paragraph with font size reset using button' ); - await page.click( '.components-font-size-picker__select' ); - await page.click( - '.components-custom-select-control__item:nth-child(2)' - ); + await first( await page.$x( FONT_SIZE_LABEL_SELECTOR ) ).click(); + await pressKeyTimes( 'ArrowDown', 2 ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.press( 'Tab' ); + await page.keyboard.press( 'Tab' ); - const resetButton = ( - await page.$x( - '//*[contains(concat(" ", @class, " "), " components-font-size-picker__controls ")]//*[text()=\'Reset\']' - ) - )[ 0 ]; - await resetButton.click(); + await page.keyboard.press( 'Enter' ); // Ensure content matches snapshot. const content = await getEditedPostContent(); @@ -92,17 +105,14 @@ describe( 'Font Size Picker', () => { 'Paragraph with font size reset using input field' ); - await page.click( '.components-font-size-picker__select' ); - await page.click( - '.components-custom-select-control__item:nth-child(3)' - ); + await first( await page.$x( FONT_SIZE_LABEL_SELECTOR ) ).click(); + await pressKeyTimes( 'ArrowDown', 2 ); + await page.keyboard.press( 'Enter' ); - // Clear the custom font size input. - await page.click( - '.components-font-size-picker__controls .components-font-size-picker__number' - ); + await first( await page.$x( CUSTOM_FONT_SIZE_LABEL_SELECTOR ) ).click(); await pressKeyTimes( 'ArrowRight', 5 ); await pressKeyTimes( 'Backspace', 5 ); + await page.keyboard.press( 'Enter' ); // Ensure content matches snapshot. const content = await getEditedPostContent(); @@ -114,18 +124,14 @@ describe( 'Font Size Picker', () => { await clickBlockAppender(); await page.keyboard.type( 'Paragraph to be made "small"' ); - await page.click( - '.components-font-size-picker__controls .components-font-size-picker__number' - ); + await first( await page.$x( CUSTOM_FONT_SIZE_LABEL_SELECTOR ) ).click(); await page.keyboard.type( '23' ); + await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Backspace' ); - - await page.click( - '.components-font-size-picker__controls .components-font-size-picker__number' - ); + await first( await page.$x( CUSTOM_FONT_SIZE_LABEL_SELECTOR ) ).click(); await page.keyboard.press( 'Backspace' ); await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Enter' ); // Ensure content matches snapshot. const content = await getEditedPostContent(); diff --git a/packages/e2e-tests/specs/editor/various/preview.test.js b/packages/e2e-tests/specs/editor/various/preview.test.js index 87c00255c508cf..d0996211a512c9 100644 --- a/packages/e2e-tests/specs/editor/various/preview.test.js +++ b/packages/e2e-tests/specs/editor/various/preview.test.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { last } from 'lodash'; - /** * WordPress dependencies */ @@ -15,31 +10,11 @@ import { deactivatePlugin, publishPost, saveDraft, + openPreviewPage, } from '@wordpress/e2e-test-utils'; /** @typedef {import('puppeteer').Page} Page */ -async function openPreviewPage( editorPage ) { - let openTabs = await browser.pages(); - const expectedTabsCount = openTabs.length + 1; - await editorPage.click( '.block-editor-post-preview__button-toggle' ); - await editorPage.waitFor( '.edit-post-header-preview__button-external' ); - await editorPage.click( '.edit-post-header-preview__button-external' ); - - // Wait for the new tab to open. - while ( openTabs.length < expectedTabsCount ) { - await editorPage.waitFor( 1 ); - openTabs = await browser.pages(); - } - - const previewPage = last( openTabs ); - // Wait for the preview to load. We can't do interstitial detection here, - // because it might load too quickly for us to pick up, so we wait for - // the preview to load by waiting for the title to appear. - await previewPage.waitForSelector( '.entry-title' ); - return previewPage; -} - /** * Given the Page instance for the editor, opens preview drodpdown, and * awaits the presence of the external preview selector. @@ -123,6 +98,7 @@ describe( 'Preview', () => { await editorPage.type( '.editor-post-title__input', 'Hello World' ); const previewPage = await openPreviewPage( editorPage ); + await previewPage.waitForSelector( '.entry-title' ); // When autosave completes for a new post, the URL of the editor should // update to include the ID. Use this to assert on preview URL. @@ -222,6 +198,7 @@ describe( 'Preview', () => { // Open the preview page. const previewPage = await openPreviewPage( editorPage ); + await previewPage.waitForSelector( '.entry-title' ); // Title in preview should match input. let previewTitle = await previewPage.$eval( @@ -282,6 +259,7 @@ describe( 'Preview with Custom Fields enabled', () => { // Open the preview page. const previewPage = await openPreviewPage( editorPage ); + await previewPage.waitForSelector( '.entry-title' ); // Check the title and preview match. let previewTitle = await previewPage.$eval( diff --git a/packages/e2e-tests/specs/editor/various/publish-button.test.js b/packages/e2e-tests/specs/editor/various/publish-button.test.js index 680ca0082c2eda..d9396b7f48c386 100644 --- a/packages/e2e-tests/specs/editor/various/publish-button.test.js +++ b/packages/e2e-tests/specs/editor/various/publish-button.test.js @@ -49,9 +49,10 @@ describe( 'PostPublishButton', () => { await page.$( '.editor-post-publish-button[aria-disabled="true"]' ) ).toBeNull(); - await page.evaluate( () => - window.wp.data.dispatch( 'core/edit-post' ).requestMetaBoxUpdates() - ); + await page.evaluate( () => { + window.wp.data.dispatch( 'core/edit-post' ).requestMetaBoxUpdates(); + return true; + } ); expect( await page.$( '.editor-post-publish-button[aria-disabled="true"]' ) ).not.toBeNull(); diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js index 3cd130ec754ffb..0d7c6d8baf236f 100644 --- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js @@ -8,19 +8,12 @@ import { createNewPost, clickBlockToolbarButton, pressKeyWithModifier, - searchForReusableBlock, getEditedPostContent, trashAllPosts, visitAdminPage, toggleGlobalBlockInserter, } from '@wordpress/e2e-test-utils'; -function waitForAndAcceptDialog() { - return new Promise( ( resolve ) => { - page.once( 'dialog', () => resolve() ); - } ); -} - describe( 'Reusable blocks', () => { beforeAll( async () => { await createNewPost(); @@ -241,35 +234,6 @@ describe( 'Reusable blocks', () => { expect( text ).toMatch( 'Oh! Hello there!' ); } ); - it( 'can be deleted', async () => { - // Insert the reusable block we edited above - await insertReusableBlock( 'Surprised greeting block' ); - - // Delete the block and accept the confirmation dialog - await clickBlockToolbarButton( 'More options' ); - const deleteButton = await page.waitForXPath( - '//button/span[text()="Remove from Reusable blocks"]' - ); - await Promise.all( [ waitForAndAcceptDialog(), deleteButton.click() ] ); - - // Wait for deletion to finish - await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Block deleted."]' - ); - - // Check that we have an empty post again - expect( await getEditedPostContent() ).toBe( '' ); - - // Search for the block in the inserter - await searchForReusableBlock( 'Surprised greeting block' ); - - // Check that we couldn't find it - const items = await page.$$( - '.block-editor-block-types-list__item[aria-label="Surprised greeting block"]' - ); - expect( items ).toHaveLength( 0 ); - } ); - it( 'can be created from multiselection', async () => { await createNewPost(); diff --git a/packages/e2e-tests/specs/editor/various/rtl.test.js b/packages/e2e-tests/specs/editor/various/rtl.test.js index 67f66ee77cc63a..f14219d5b17f64 100644 --- a/packages/e2e-tests/specs/editor/various/rtl.test.js +++ b/packages/e2e-tests/specs/editor/various/rtl.test.js @@ -17,6 +17,7 @@ describe( 'RTL', () => { await createNewPost(); await page.evaluate( () => { document.querySelector( '.is-root-container' ).dir = 'rtl'; + wp.i18n.isRTL = () => true; } ); } ); diff --git a/packages/e2e-tests/specs/editor/various/scheduling.test.js b/packages/e2e-tests/specs/editor/various/scheduling.test.js index 7732f8d8a0e7ce..7cd9bb11140bd4 100644 --- a/packages/e2e-tests/specs/editor/various/scheduling.test.js +++ b/packages/e2e-tests/specs/editor/various/scheduling.test.js @@ -1,11 +1,16 @@ /** * WordPress dependencies */ -import { createNewPost } from '@wordpress/e2e-test-utils'; +import { createNewPost, changeSiteTimezone } from '@wordpress/e2e-test-utils'; -describe( 'Scheduling', () => { - beforeEach( createNewPost ); +async function getPublishButtonText() { + return page.$eval( + '.editor-post-publish-button__button', + ( element ) => element.textContent + ); +} +describe( 'Scheduling', () => { const isDateTimeComponentFocused = () => { return page.evaluate( () => { const dateTimeElement = document.querySelector( @@ -18,7 +23,38 @@ describe( 'Scheduling', () => { } ); }; + [ 'UTC-10', 'UTC', 'UTC+10' ].forEach( ( timezone ) => { + describe( timezone, () => { + let oldTimezone; + beforeEach( async () => { + oldTimezone = await changeSiteTimezone( timezone ); + await createNewPost(); + } ); + afterEach( async () => { + await changeSiteTimezone( oldTimezone ); + } ); + + it( `should change publishing button text from "Publish" to "Schedule"`, async () => { + expect( await getPublishButtonText() ).toBe( 'Publish' ); + + // Open the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); + + // Change the publishing date to a year in the future. + await page.click( '.components-datetime__time-field-year' ); + await page.keyboard.press( 'ArrowUp' ); + + // Close the datepicker. + await page.click( '.edit-post-post-schedule__toggle' ); + + expect( await getPublishButtonText() ).toBe( 'Schedule…' ); + } ); + } ); + } ); + it( 'Should keep date time UI focused when the previous and next month buttons are clicked', async () => { + await createNewPost(); + await page.click( '.edit-post-post-schedule__toggle' ); await page.click( 'div[aria-label="Move backward to switch to the previous month."]' diff --git a/packages/e2e-tests/specs/editor/various/typewriter.test.js b/packages/e2e-tests/specs/editor/various/typewriter.test.js index 464f265a609fe2..d1983e465e785d 100644 --- a/packages/e2e-tests/specs/editor/various/typewriter.test.js +++ b/packages/e2e-tests/specs/editor/various/typewriter.test.js @@ -3,14 +3,6 @@ */ import { createNewPost } from '@wordpress/e2e-test-utils'; -async function waitForTheTypewriterEffectToTrigger() { - // Wait for the browser to be idle - // the typewriter effect uses the same delay. - await page.waitForFunction( () => { - return new Promise( window.requestAnimationFrame ); - } ); -} - describe( 'TypeWriter', () => { beforeEach( async () => { await createNewPost(); @@ -55,8 +47,6 @@ describe( 'TypeWriter', () => { // Now the scroll position should be maintained. await page.keyboard.press( 'Enter' ); - await waitForTheTypewriterEffectToTrigger(); - expect( await getDiff( newPosition ) ).toBeLessThanOrEqual( BUFFER ); // Type until the text wraps. @@ -73,16 +63,12 @@ describe( 'TypeWriter', () => { await page.keyboard.type( 'a' ); } - await waitForTheTypewriterEffectToTrigger(); - expect( await getDiff( newPosition ) ).toBeLessThanOrEqual( BUFFER ); // Pressing backspace will reposition the caret to the previous line. // Scroll position should be adjusted again. await page.keyboard.press( 'Backspace' ); - await waitForTheTypewriterEffectToTrigger(); - expect( await getDiff( newPosition ) ).toBeLessThanOrEqual( BUFFER ); // Should reset scroll position to maintain. @@ -95,8 +81,6 @@ describe( 'TypeWriter', () => { // Should be scrolled to new position. await page.keyboard.press( 'Enter' ); - await waitForTheTypewriterEffectToTrigger(); - expect( await getDiff( positionAfterArrowUp ) ).toBeLessThanOrEqual( BUFFER ); @@ -127,8 +111,6 @@ describe( 'TypeWriter', () => { // Should maintain scroll position. await page.keyboard.press( 'Enter' ); - await waitForTheTypewriterEffectToTrigger(); - expect( await getDiff( initialPosition ) ).toBeLessThanOrEqual( BUFFER ); @@ -150,8 +132,6 @@ describe( 'TypeWriter', () => { // Should maintain scroll position. await page.keyboard.press( 'Enter' ); - await waitForTheTypewriterEffectToTrigger(); - expect( await getDiff( initialPosition ) ).toBeLessThanOrEqual( BUFFER ); @@ -206,8 +186,6 @@ describe( 'TypeWriter', () => { // Should maintain new caret position. await page.keyboard.press( 'Enter' ); - await waitForTheTypewriterEffectToTrigger(); - expect( await getDiff( newBottomPosition ) ).toBeLessThanOrEqual( BUFFER ); @@ -238,8 +216,6 @@ describe( 'TypeWriter', () => { // Should maintain new caret position. await page.keyboard.press( 'Enter' ); - await waitForTheTypewriterEffectToTrigger(); - expect( await getDiff( newTopPosition ) ).toBeLessThanOrEqual( BUFFER ); } ); } ); diff --git a/packages/e2e-tests/specs/experiments/blocks/post-title.test.js b/packages/e2e-tests/specs/experiments/blocks/post-title.test.js new file mode 100644 index 00000000000000..6bd00baba6d827 --- /dev/null +++ b/packages/e2e-tests/specs/experiments/blocks/post-title.test.js @@ -0,0 +1,44 @@ +/** + * WordPress dependencies + */ +import { + activateTheme, + createNewPost, + insertBlock, + pressKeyWithModifier, + saveDraft, +} from '@wordpress/e2e-test-utils'; + +describe( 'Post Title block', () => { + beforeAll( async () => { + await activateTheme( 'tt1-blocks' ); + } ); + + afterAll( async () => { + await activateTheme( 'twentytwentyone' ); + } ); + + beforeEach( async () => { + await createNewPost(); + } ); + + it( 'Can edit the post title', async () => { + // Create a block with some text that will trigger a list creation. + await insertBlock( 'Post Title' ); + + // Select all of the text in the post title block. + await pressKeyWithModifier( 'primary', 'a' ); + + // Create a second list item. + await page.keyboard.type( 'Just tweaking the post title' ); + + await saveDraft(); + await page.reload(); + await page.waitForSelector( '.edit-post-layout' ); + const title = await page.$eval( + '.editor-post-title__input', + ( element ) => element.value + ); + expect( title ).toEqual( 'Just tweaking the post title' ); + } ); +} ); diff --git a/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js b/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js index 4718f530f93213..845b2e4044e98d 100644 --- a/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js +++ b/packages/e2e-tests/specs/experiments/multi-entity-editing.test.js @@ -97,17 +97,6 @@ const openEntitySavePanel = async () => { } // If we made it this far, the panel is opened. - // Expand to view savable entities if necessary. - const reviewChangesButton = await page.$( - '.entities-saved-states__review-changes-button' - ); - const [ needsToOpen ] = await reviewChangesButton.$x( - '//*[contains(text(),"Review changes.")]' - ); - if ( needsToOpen ) { - await reviewChangesButton.click(); - } - return true; }; @@ -137,13 +126,8 @@ const removeErrorMocks = () => { }; describe( 'Multi-entity editor states', () => { - // Setup & Teardown. - const templateName = 'Front Page'; - const templatePartName = 'Test Template Part Name Edit'; - const nestedTPName = 'Test Nested Template Part Name Edit'; - beforeAll( async () => { - await activateTheme( 'twentytwentyone-blocks' ); + await activateTheme( 'tt1-blocks' ); await trashAllPosts( 'wp_template' ); await trashAllPosts( 'wp_template_part' ); } ); @@ -167,7 +151,7 @@ describe( 'Multi-entity editor states', () => { expect( await isEntityDirty( 'front-page' ) ).toBe( false ); // Switch back and make sure it is still clean. - await clickTemplateItem( 'Templates', templateName ); + await clickTemplateItem( 'Templates', 'Front Page' ); await page.waitForSelector( '.wp-block' ); expect( await isEntityDirty( 'header' ) ).toBe( false ); expect( await isEntityDirty( 'front-page' ) ).toBe( false ); @@ -176,6 +160,10 @@ describe( 'Multi-entity editor states', () => { } ); describe( 'Multi-entity edit', () => { + const templatePartName = 'Test Template Part Name Edit'; + const nestedTPName = 'Test Nested Template Part Name Edit'; + const templateName = 'Custom Template'; + beforeAll( async () => { await trashAllPosts( 'wp_template' ); await trashAllPosts( 'wp_template_part' ); @@ -196,10 +184,18 @@ describe( 'Multi-entity editor states', () => { ); await saveAllEntities(); await visitSiteEditor(); - // Waits for the template part to load... + + // Wait for site editor to load. await page.waitForSelector( '.wp-block-template-part .block-editor-block-list__layout' ); + + // Our custom template shows up in the " templates > all" menu; let's use it. + clickTemplateItem( [ 'Templates', 'All' ], templateName ); + await page.waitForXPath( + `//p[contains(@class, "edit-site-document-actions__title") and contains(text(), '${ templateName }')]` + ); + removeErrorMocks(); } ); diff --git a/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js b/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js index 491ec3935577df..7e2a1e127be5ff 100644 --- a/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js +++ b/packages/e2e-tests/specs/experiments/multi-entity-saving.test.js @@ -31,17 +31,6 @@ describe( 'Multi-entity save flow', () => { // Reusable assertions across Post/Site editors. const assertAllBoxesChecked = async () => { - // Expand to view savable entities if necessary. - const reviewChangesButton = await page.$( - '.entities-saved-states__review-changes-button' - ); - const [ needsToOpen ] = await reviewChangesButton.$x( - '//*[contains(text(),"Review changes.")]' - ); - if ( needsToOpen ) { - await reviewChangesButton.click(); - } - const checkedBoxes = await page.$$( checkedBoxSelector ); const checkboxInputs = await page.$$( checkboxInputSelector ); expect( checkedBoxes.length - checkboxInputs.length ).toBe( 0 ); @@ -56,7 +45,7 @@ describe( 'Multi-entity save flow', () => { }; beforeAll( async () => { - await activateTheme( 'twentytwentyone-blocks' ); + await activateTheme( 'tt1-blocks' ); await trashAllPosts( 'wp_template' ); await trashAllPosts( 'wp_template_part' ); } ); @@ -94,7 +83,6 @@ describe( 'Multi-entity save flow', () => { }; it( 'Save flow should work as expected.', async () => { - expect.assertions( 27 ); await createNewPost(); // Edit the page some. await page.click( '.editor-post-title' ); @@ -196,7 +184,6 @@ describe( 'Multi-entity save flow', () => { const saveA11ySelector = '.edit-site-editor__toggle-save-panel-button'; it( 'Save flow should work as expected', async () => { - expect.assertions( 5 ); // Navigate to site editor. const query = addQueryArgs( '', { page: 'gutenberg-edit-site', diff --git a/packages/e2e-tests/specs/experiments/navigation.test.js b/packages/e2e-tests/specs/experiments/navigation.test.js index 7b2a81824de7a6..39eb9d11c3f218 100644 --- a/packages/e2e-tests/specs/experiments/navigation.test.js +++ b/packages/e2e-tests/specs/experiments/navigation.test.js @@ -267,7 +267,7 @@ beforeEach( async () => { afterEach( async () => { await setUpResponseMocking( [] ); } ); -describe( 'Navigation', () => { +describe.skip( 'Navigation', () => { describe( 'Creating from existing Pages', () => { it( 'allows a navigation block to be created using existing pages', async () => { // Mock the response from the Pages endpoint. This is done so that the pages returned are always diff --git a/packages/e2e-tests/specs/experiments/post-editor-template-mode.test.js b/packages/e2e-tests/specs/experiments/post-editor-template-mode.test.js new file mode 100644 index 00000000000000..140f567e4d74fe --- /dev/null +++ b/packages/e2e-tests/specs/experiments/post-editor-template-mode.test.js @@ -0,0 +1,85 @@ +/** + * WordPress dependencies + */ +import { + activateTheme, + createNewPost, + insertBlock, + saveDraft, + trashAllPosts, + openPreviewPage, + openDocumentSettingsSidebar, +} from '@wordpress/e2e-test-utils'; + +describe( 'Post Editor Template mode', () => { + beforeAll( async () => { + await activateTheme( 'tt1-blocks' ); + await trashAllPosts( 'wp_template' ); + await trashAllPosts( 'wp_template_part' ); + } ); + + afterAll( async () => { + await activateTheme( 'twentytwentyone' ); + } ); + + beforeEach( async () => { + await createNewPost(); + } ); + + it( 'Allow to switch to template mode, edit the template and check the result', async () => { + // Create a random post. + await page.type( '.editor-post-title__input', 'Just an FSE Post' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( 'Hello World' ); + + // Unselect the blocks. + await page.evaluate( () => { + wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock(); + } ); + + // Save the post + // Saving shouldn't be necessary but unfortunately, + // there's a template resolution bug forcing us to do so. + await saveDraft(); + await page.reload(); + + // Switch to template mode. + await openDocumentSettingsSidebar(); + const switchLink = await page.waitForSelector( + '.edit-post-post-template button' + ); + await switchLink.click(); + + // Check that we switched properly to edit mode. + await page.waitForXPath( + '//*[contains(@class, "components-snackbar")]/*[text()="Editing template. Changes made here affect all posts and pages that use the template."]' + ); + const title = await page.$eval( + '.edit-post-template-title', + ( el ) => el.innerText + ); + expect( title ).toContain( 'Editing template:' ); + + // Edit the template + await insertBlock( 'Paragraph' ); + await page.keyboard.type( + 'Just a random paragraph added to the template' + ); + + // Save changes + const doneButton = await page.waitForXPath( + `//button[contains(text(), 'Apply')]` + ); + await doneButton.click(); + const saveButton = await page.waitForXPath( + `//div[contains(@class, "entities-saved-states__panel-header")]/button[contains(text(), 'Save')]` + ); + await saveButton.click(); + + // Preview changes + const previewPage = await openPreviewPage(); + await previewPage.waitForXPath( + '//p[contains(text(), "Just a random paragraph added to the template")]' + ); + } ); +} ); diff --git a/packages/e2e-tests/specs/experiments/template-part.test.js b/packages/e2e-tests/specs/experiments/template-part.test.js index e531067f277aaa..112f6bfc596818 100644 --- a/packages/e2e-tests/specs/experiments/template-part.test.js +++ b/packages/e2e-tests/specs/experiments/template-part.test.js @@ -21,7 +21,7 @@ import { navigationPanel } from '../../experimental-features'; describe( 'Template Part', () => { beforeAll( async () => { - await activateTheme( 'twentytwentyone-blocks' ); + await activateTheme( 'tt1-blocks' ); await trashAllPosts( 'wp_template' ); await trashAllPosts( 'wp_template_part' ); } ); diff --git a/packages/e2e-tests/specs/local/demo.test.js b/packages/e2e-tests/specs/local/demo.test.js index 8a12126a939b55..f3a1433d720888 100644 --- a/packages/e2e-tests/specs/local/demo.test.js +++ b/packages/e2e-tests/specs/local/demo.test.js @@ -29,7 +29,11 @@ describe( 'new editor state', () => { onRequestMatch: createJSONResponse( MOCK_VIMEO_RESPONSE ), }, ] ); - await visitAdminPage( 'post-new.php', 'gutenberg-demo' ); + + await Promise.all( [ + visitAdminPage( 'post-new.php', 'gutenberg-demo' ), + page.waitForNavigation( { waitUntil: 'networkidle0' } ), + ] ); } ); it( 'content should load, making the post dirty', async () => { diff --git a/packages/e2e-tests/specs/performance/post-editor.test.js b/packages/e2e-tests/specs/performance/post-editor.test.js index b78e654c932980..34436bb5403063 100644 --- a/packages/e2e-tests/specs/performance/post-editor.test.js +++ b/packages/e2e-tests/specs/performance/post-editor.test.js @@ -11,6 +11,8 @@ import { createNewPost, saveDraft, insertBlock, + openGlobalBlockInserter, + closeGlobalBlockInserter, } from '@wordpress/e2e-test-utils'; function readFile( filePath ) { @@ -51,6 +53,18 @@ function isFocusEvent( item ) { return isKeyEvent( item ) && item.args.data.type === 'focus'; } +function isClickEvent( item ) { + return isKeyEvent( item ) && item.args.data.type === 'click'; +} + +function isMouseOverEvent( item ) { + return isKeyEvent( item ) && item.args.data.type === 'mouseover'; +} + +function isMouseOutEvent( item ) { + return isKeyEvent( item ) && item.args.data.type === 'mouseout'; +} + function getEventDurationsForType( trace, filterFunction ) { return trace.traceEvents .filter( filterFunction ) @@ -69,6 +83,17 @@ function getSelectionEventDurations( trace ) { return [ getEventDurationsForType( trace, isFocusEvent ) ]; } +function getClickEventDurations( trace ) { + return [ getEventDurationsForType( trace, isClickEvent ) ]; +} + +function getHoverEventDurations( trace ) { + return [ + getEventDurationsForType( trace, isMouseOverEvent ), + getEventDurationsForType( trace, isMouseOutEvent ), + ]; +} + jest.setTimeout( 1000000 ); describe( 'Post Editor Performance', () => { @@ -77,6 +102,8 @@ describe( 'Post Editor Performance', () => { load: [], type: [], focus: [], + inserterOpen: [], + inserterHover: [], }; const html = readFile( @@ -110,11 +137,61 @@ describe( 'Post Editor Performance', () => { results.load.push( new Date() - startTime ); } - // Measuring typing performance + // Measure time to open inserter await page.waitForSelector( '.edit-post-layout' ); + const traceFile = __dirname + '/trace.json'; + let traceResults; + for ( let j = 0; j < 10; j++ ) { + await page.tracing.start( { + path: traceFile, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + await openGlobalBlockInserter(); + await page.tracing.stop(); + + traceResults = JSON.parse( readFile( traceFile ) ); + const [ mouseClickEvents ] = getClickEventDurations( traceResults ); + for ( let k = 0; k < mouseClickEvents.length; k++ ) { + results.inserterOpen.push( mouseClickEvents[ k ] ); + } + await closeGlobalBlockInserter(); + } + + // Measure inserter hover performance + const paragraphBlockItem = + '.block-editor-inserter__menu .editor-block-list-item-paragraph'; + const headingBlockItem = + '.block-editor-inserter__menu .editor-block-list-item-heading'; + await openGlobalBlockInserter(); + await page.waitForSelector( paragraphBlockItem ); + await page.hover( paragraphBlockItem ); + await page.hover( headingBlockItem ); + for ( let j = 0; j < 20; j++ ) { + await page.tracing.start( { + path: traceFile, + screenshots: false, + categories: [ 'devtools.timeline' ], + } ); + await page.hover( paragraphBlockItem ); + await page.hover( headingBlockItem ); + await page.tracing.stop(); + + traceResults = JSON.parse( readFile( traceFile ) ); + const [ mouseOverEvents, mouseOutEvents ] = getHoverEventDurations( + traceResults + ); + for ( let k = 0; k < mouseOverEvents.length; k++ ) { + results.inserterHover.push( + mouseOverEvents[ k ] + mouseOutEvents[ k ] + ); + } + } + await closeGlobalBlockInserter(); + + // Measuring typing performance await insertBlock( 'Paragraph' ); i = 200; - const traceFile = __dirname + '/trace.json'; await page.tracing.start( { path: traceFile, screenshots: false, @@ -125,7 +202,7 @@ describe( 'Post Editor Performance', () => { } await page.tracing.stop(); - let traceResults = JSON.parse( readFile( traceFile ) ); + traceResults = JSON.parse( readFile( traceFile ) ); const [ keyDownEvents, keyPressEvents, diff --git a/packages/e2e-tests/specs/performance/site-editor.test.js b/packages/e2e-tests/specs/performance/site-editor.test.js index c0c076eafe16a2..b638c1db1c3027 100644 --- a/packages/e2e-tests/specs/performance/site-editor.test.js +++ b/packages/e2e-tests/specs/performance/site-editor.test.js @@ -18,7 +18,7 @@ jest.setTimeout( 1000000 ); describe( 'Site Editor Performance', () => { beforeAll( async () => { - await activateTheme( 'twentytwentyone-blocks' ); + await activateTheme( 'tt1-blocks' ); await trashAllPosts( 'wp_template' ); await trashAllPosts( 'wp_template', 'auto-draft' ); await trashAllPosts( 'wp_template_part' ); @@ -34,6 +34,8 @@ describe( 'Site Editor Performance', () => { load: [], type: [], focus: [], + inserterOpen: [], + inserterHover: [], }; await visitAdminPage( diff --git a/packages/edit-navigation/package.json b/packages/edit-navigation/package.json index f72ed62c0670de..6f657d2b3b7afb 100644 --- a/packages/edit-navigation/package.json +++ b/packages/edit-navigation/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-navigation", - "version": "1.7.4", + "version": "1.8.0", "private": true, "description": "Module for the Navigation page in WordPress.", "author": "The WordPress Contributors", @@ -24,7 +24,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/block-editor": "file:../block-editor", "@wordpress/block-library": "file:../block-library", diff --git a/packages/edit-navigation/src/components/header/add-menu-form.js b/packages/edit-navigation/src/components/header/add-menu-form.js index 3f47f594bd9626..96fa4ce2f4d46d 100644 --- a/packages/edit-navigation/src/components/header/add-menu-form.js +++ b/packages/edit-navigation/src/components/header/add-menu-form.js @@ -10,6 +10,7 @@ import { useState } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; import { TextControl, Button } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; const menuNameMatches = ( menuName ) => ( menu ) => menu.name.toLowerCase() === menuName.toLowerCase(); @@ -17,9 +18,7 @@ const menuNameMatches = ( menuName ) => ( menu ) => export default function AddMenuForm( { menus, onCreate } ) { const [ menuName, setMenuName ] = useState( '' ); - const { createErrorNotice, createInfoNotice } = useDispatch( - 'core/notices' - ); + const { createErrorNotice, createInfoNotice } = useDispatch( noticesStore ); const [ isCreatingMenu, setIsCreatingMenu ] = useState( false ); diff --git a/packages/edit-navigation/src/components/layout/index.js b/packages/edit-navigation/src/components/layout/index.js index e8b1b56a843904..9eb350b9dade9f 100644 --- a/packages/edit-navigation/src/components/layout/index.js +++ b/packages/edit-navigation/src/components/layout/index.js @@ -3,7 +3,6 @@ */ import { DropZoneProvider, - FocusReturnProvider, Popover, SlotFillProvider, } from '@wordpress/components'; @@ -45,47 +44,45 @@ export default function Layout( { blockEditorSettings } ) { <ErrorBoundary> <SlotFillProvider> <DropZoneProvider> - <FocusReturnProvider> - <BlockEditorKeyboardShortcuts.Register /> - <NavigationEditorShortcuts.Register /> + <BlockEditorKeyboardShortcuts.Register /> + <NavigationEditorShortcuts.Register /> - <Notices /> + <Notices /> - <div className="edit-navigation-layout"> - <Header + <div className="edit-navigation-layout"> + <Header + isPending={ ! navigationPost } + menus={ menus } + selectedMenuId={ selectedMenuId } + onSelectMenu={ selectMenu } + /> + + <BlockEditorProvider + value={ blocks } + onInput={ onInput } + onChange={ onChange } + settings={ { + ...blockEditorSettings, + templateLock: 'all', + hasFixedToolbar: true, + } } + > + <Toolbar isPending={ ! navigationPost } - menus={ menus } - selectedMenuId={ selectedMenuId } - onSelectMenu={ selectMenu } + navigationPost={ navigationPost } /> + <Editor + isPending={ ! navigationPost } + blocks={ blocks } + /> + <InspectorAdditions + menuId={ selectedMenuId } + onDeleteMenu={ deleteMenu } + /> + </BlockEditorProvider> + </div> - <BlockEditorProvider - value={ blocks } - onInput={ onInput } - onChange={ onChange } - settings={ { - ...blockEditorSettings, - templateLock: 'all', - hasFixedToolbar: true, - } } - > - <Toolbar - isPending={ ! navigationPost } - navigationPost={ navigationPost } - /> - <Editor - isPending={ ! navigationPost } - blocks={ blocks } - /> - <InspectorAdditions - menuId={ selectedMenuId } - onDeleteMenu={ deleteMenu } - /> - </BlockEditorProvider> - </div> - - <Popover.Slot /> - </FocusReturnProvider> + <Popover.Slot /> </DropZoneProvider> </SlotFillProvider> </ErrorBoundary> diff --git a/packages/edit-navigation/src/components/layout/shortcuts.js b/packages/edit-navigation/src/components/layout/shortcuts.js index d75d3f176e2e40..9c2e228b4cc0e9 100644 --- a/packages/edit-navigation/src/components/layout/shortcuts.js +++ b/packages/edit-navigation/src/components/layout/shortcuts.js @@ -3,7 +3,10 @@ */ import { useEffect, useCallback } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; -import { useShortcut } from '@wordpress/keyboard-shortcuts'; +import { + useShortcut, + store as keyboardShortcutsStore, +} from '@wordpress/keyboard-shortcuts'; import { __ } from '@wordpress/i18n'; function NavigationEditorShortcuts( { saveBlocks } ) { @@ -41,7 +44,7 @@ function NavigationEditorShortcuts( { saveBlocks } ) { } function RegisterNavigationEditorShortcuts() { - const { registerShortcut } = useDispatch( 'core/keyboard-shortcuts' ); + const { registerShortcut } = useDispatch( keyboardShortcutsStore ); useEffect( () => { registerShortcut( { name: 'core/edit-navigation/save-menu', diff --git a/packages/edit-navigation/src/components/layout/use-menu-notifications.js b/packages/edit-navigation/src/components/layout/use-menu-notifications.js index 34adaa51e67ecd..16e7076c0011d2 100644 --- a/packages/edit-navigation/src/components/layout/use-menu-notifications.js +++ b/packages/edit-navigation/src/components/layout/use-menu-notifications.js @@ -3,6 +3,7 @@ */ import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect } from '@wordpress/element'; +import { store as noticesStore } from '@wordpress/notices'; export default function useMenuNotifications( menuId ) { const { lastSaveError, lastDeleteError } = useSelect( @@ -20,7 +21,7 @@ export default function useMenuNotifications( menuId ) { [ menuId ] ); - const { createErrorNotice } = useDispatch( 'core/notices' ); + const { createErrorNotice } = useDispatch( noticesStore ); const processError = ( error ) => { const document = new window.DOMParser().parseFromString( diff --git a/packages/edit-navigation/src/components/layout/use-navigation-block-editor.js b/packages/edit-navigation/src/components/layout/use-navigation-block-editor.js index d2d5034878ff61..e9108b50bbefc0 100644 --- a/packages/edit-navigation/src/components/layout/use-navigation-block-editor.js +++ b/packages/edit-navigation/src/components/layout/use-navigation-block-editor.js @@ -9,9 +9,10 @@ import { useEntityBlockEditor } from '@wordpress/core-data'; * Internal dependencies */ import { KIND, POST_TYPE } from '../../store/utils'; +import { store as editNavigationStore } from '../../store'; export default function useNavigationBlockEditor( post ) { - const { createMissingMenuItems } = useDispatch( 'core/edit-navigation' ); + const { createMissingMenuItems } = useDispatch( editNavigationStore ); const [ blocks, onInput, _onChange ] = useEntityBlockEditor( KIND, diff --git a/packages/edit-navigation/src/components/layout/use-navigation-editor.js b/packages/edit-navigation/src/components/layout/use-navigation-editor.js index 590f66ac0df9f7..bdc615828de3bf 100644 --- a/packages/edit-navigation/src/components/layout/use-navigation-editor.js +++ b/packages/edit-navigation/src/components/layout/use-navigation-editor.js @@ -4,6 +4,11 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { useState, useEffect } from '@wordpress/element'; +/** + * Internal dependencies + */ +import { store as editNavigationStore } from '../../store'; + export default function useNavigationEditor() { const menus = useSelect( ( select ) => select( 'core' ).getMenus( { per_page: -1 } ), @@ -20,7 +25,7 @@ export default function useNavigationEditor() { const navigationPost = useSelect( ( select ) => - select( 'core/edit-navigation' ).getNavigationPostForMenu( + select( editNavigationStore ).getNavigationPostForMenu( selectedMenuId ), [ selectedMenuId ] diff --git a/packages/edit-navigation/src/components/notices/index.js b/packages/edit-navigation/src/components/notices/index.js index e45eef3dc70622..21e15fde348d38 100644 --- a/packages/edit-navigation/src/components/notices/index.js +++ b/packages/edit-navigation/src/components/notices/index.js @@ -8,11 +8,12 @@ import { filter } from 'lodash'; */ import { NoticeList, SnackbarList } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; +import { store as noticesStore } from '@wordpress/notices'; export default function EditNavigationNotices() { - const { removeNotice } = useDispatch( 'core/notices' ); + const { removeNotice } = useDispatch( noticesStore ); const notices = useSelect( - ( select ) => select( 'core/notices' ).getNotices(), + ( select ) => select( noticesStore ).getNotices(), [] ); const dismissibleNotices = filter( notices, { diff --git a/packages/edit-navigation/src/components/toolbar/save-button.js b/packages/edit-navigation/src/components/toolbar/save-button.js index c5adfef8b8ac75..92132f47198d66 100644 --- a/packages/edit-navigation/src/components/toolbar/save-button.js +++ b/packages/edit-navigation/src/components/toolbar/save-button.js @@ -5,8 +5,13 @@ import { useDispatch } from '@wordpress/data'; import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import { store as editNavigationStore } from '../../store'; + export default function SaveButton( { navigationPost } ) { - const { saveNavigationPost } = useDispatch( 'core/edit-navigation' ); + const { saveNavigationPost } = useDispatch( editNavigationStore ); return ( <Button diff --git a/packages/edit-navigation/src/index.js b/packages/edit-navigation/src/index.js index 174d7f4b1272da..7b0ad0eb061df4 100644 --- a/packages/edit-navigation/src/index.js +++ b/packages/edit-navigation/src/index.js @@ -6,7 +6,6 @@ import { map, set, flatten, omit, partialRight } from 'lodash'; /** * WordPress dependencies */ -import '@wordpress/notices'; import { registerCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, diff --git a/packages/edit-navigation/src/store/actions.js b/packages/edit-navigation/src/store/actions.js index 19e4ba33826340..1b95b2950d37b2 100644 --- a/packages/edit-navigation/src/store/actions.js +++ b/packages/edit-navigation/src/store/actions.js @@ -8,6 +8,7 @@ import { v4 as uuid } from 'uuid'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -96,7 +97,7 @@ export const saveNavigationPost = serializeProcessing( function* ( post ) { throw new Error(); } yield dispatch( - 'core/notices', + noticesStore, 'createSuccessNotice', __( 'Navigation saved.' ), { @@ -105,7 +106,7 @@ export const saveNavigationPost = serializeProcessing( function* ( post ) { ); } catch ( e ) { yield dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', __( 'There was an error.' ), { diff --git a/packages/edit-navigation/src/store/constants.js b/packages/edit-navigation/src/store/constants.js new file mode 100644 index 00000000000000..8617d69ab054c5 --- /dev/null +++ b/packages/edit-navigation/src/store/constants.js @@ -0,0 +1,4 @@ +/** + * Module Constants + */ +export const STORE_NAME = 'core/edit-navigation'; diff --git a/packages/edit-navigation/src/store/controls.js b/packages/edit-navigation/src/store/controls.js index fb327b8fff8fcc..ef050130aafdec 100644 --- a/packages/edit-navigation/src/store/controls.js +++ b/packages/edit-navigation/src/store/controls.js @@ -8,6 +8,7 @@ import { createRegistryControl } from '@wordpress/data'; * Internal dependencies */ import { menuItemsQuery } from './utils'; +import { STORE_NAME } from './constants'; /** * Trigger an API Fetch request. @@ -72,7 +73,7 @@ export function getMenuItemToClientIdMapping( postId ) { export function getNavigationPostForMenu( menuId ) { return { type: 'SELECT', - registryName: 'core/edit-navigation', + registryName: STORE_NAME, selectorName: 'getNavigationPostForMenu', args: [ menuId ], }; @@ -173,7 +174,6 @@ const controls = { ), }; -const getState = ( registry ) => - registry.stores[ 'core/edit-navigation' ].store.getState(); +const getState = ( registry ) => registry.stores[ STORE_NAME ].store.getState(); export default controls; diff --git a/packages/edit-navigation/src/store/index.js b/packages/edit-navigation/src/store/index.js index 049f1785c9d205..b55979e5d11868 100644 --- a/packages/edit-navigation/src/store/index.js +++ b/packages/edit-navigation/src/store/index.js @@ -11,11 +11,7 @@ import * as resolvers from './resolvers'; import * as selectors from './selectors'; import * as actions from './actions'; import controls from './controls'; - -/** - * Module Constants - */ -const STORE_NAME = 'core/edit-navigation'; +import { STORE_NAME } from './constants'; /** * Block editor data store configuration. diff --git a/packages/edit-navigation/src/store/test/actions.js b/packages/edit-navigation/src/store/test/actions.js index a300950e5e14eb..a01cdbedbc0afd 100644 --- a/packages/edit-navigation/src/store/test/actions.js +++ b/packages/edit-navigation/src/store/test/actions.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -346,7 +347,7 @@ describe( 'saveNavigationPost', () => { expect( action.next( { success: true } ).value ).toEqual( dispatch( - 'core/notices', + noticesStore, 'createSuccessNotice', __( 'Navigation saved.' ), { @@ -476,7 +477,7 @@ describe( 'saveNavigationPost', () => { expect( action.next( { success: false } ).value ).toEqual( dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', __( 'There was an error.' ), { diff --git a/packages/edit-navigation/src/store/test/controls.js b/packages/edit-navigation/src/store/test/controls.js index 5e10991c294555..2b784d1e0322ce 100644 --- a/packages/edit-navigation/src/store/test/controls.js +++ b/packages/edit-navigation/src/store/test/controls.js @@ -17,6 +17,7 @@ import controls, { dispatch, } from '../controls'; import { menuItemsQuery } from '../utils'; +import { STORE_NAME } from '../constants'; // Mock it to prevent calling window.fetch in test environment jest.mock( '@wordpress/api-fetch', () => jest.fn( ( request ) => request ) ); @@ -63,7 +64,7 @@ describe( 'getNavigationPostForMenu', () => { it( 'has the correct type and payload', () => { expect( getNavigationPostForMenu( 123 ) ).toEqual( { type: 'SELECT', - registryName: 'core/edit-navigation', + registryName: STORE_NAME, selectorName: 'getNavigationPostForMenu', args: [ 123 ], } ); @@ -144,7 +145,7 @@ describe( 'controls', () => { }; const registry = { stores: { - 'core/edit-navigation': { + [ STORE_NAME ]: { store: { getState: jest.fn( () => state ), }, @@ -157,7 +158,7 @@ describe( 'controls', () => { ).toEqual( [ 'action1', 'action2' ] ); expect( - registry.stores[ 'core/edit-navigation' ].store.getState + registry.stores[ STORE_NAME ].store.getState ).toHaveBeenCalledTimes( 1 ); expect( @@ -167,7 +168,7 @@ describe( 'controls', () => { ).toEqual( [] ); expect( - registry.stores[ 'core/edit-navigation' ].store.getState + registry.stores[ STORE_NAME ].store.getState ).toHaveBeenCalledTimes( 2 ); } ); @@ -181,7 +182,7 @@ describe( 'controls', () => { }; const registry = { stores: { - 'core/edit-navigation': { + [ STORE_NAME ]: { store: { getState: jest.fn( () => state ), }, @@ -194,7 +195,7 @@ describe( 'controls', () => { ).toBe( true ); expect( - registry.stores[ 'core/edit-navigation' ].store.getState + registry.stores[ STORE_NAME ].store.getState ).toHaveBeenCalledTimes( 1 ); expect( @@ -204,7 +205,7 @@ describe( 'controls', () => { ).toBe( false ); expect( - registry.stores[ 'core/edit-navigation' ].store.getState + registry.stores[ STORE_NAME ].store.getState ).toHaveBeenCalledTimes( 2 ); } ); @@ -218,7 +219,7 @@ describe( 'controls', () => { }; const registry = { stores: { - 'core/edit-navigation': { + [ STORE_NAME ]: { store: { getState: jest.fn( () => state ), }, @@ -235,7 +236,7 @@ describe( 'controls', () => { } ); expect( - registry.stores[ 'core/edit-navigation' ].store.getState + registry.stores[ STORE_NAME ].store.getState ).toHaveBeenCalledTimes( 1 ); expect( @@ -245,7 +246,7 @@ describe( 'controls', () => { ).toEqual( {} ); expect( - registry.stores[ 'core/edit-navigation' ].store.getState + registry.stores[ STORE_NAME ].store.getState ).toHaveBeenCalledTimes( 2 ); } ); diff --git a/packages/edit-navigation/src/store/test/selectors.js b/packages/edit-navigation/src/store/test/selectors.js index c058b6b829b082..450283f2661d23 100644 --- a/packages/edit-navigation/src/store/test/selectors.js +++ b/packages/edit-navigation/src/store/test/selectors.js @@ -12,64 +12,81 @@ describe( 'getNavigationPostForMenu', () => { it( 'gets navigation post for menu', () => { const getEditedEntityRecord = jest.fn( () => 'record' ); const hasFinishedResolution = jest.fn( () => true ); - const __unstableGetSelect = jest.fn( () => ( { - getEditedEntityRecord, - hasFinishedResolution, - } ) ); + const registry = { + select: jest.fn( () => ( { + getEditedEntityRecord, + hasFinishedResolution, + } ) ), + }; const menuId = 123; - getNavigationPostForMenu.__unstableGetSelect = __unstableGetSelect; - hasResolvedNavigationPost.__unstableGetSelect = __unstableGetSelect; + const defaultRegistry = getNavigationPostForMenu.registry; + getNavigationPostForMenu.registry = registry; + hasResolvedNavigationPost.registry = registry; expect( getNavigationPostForMenu( 'state', menuId ) ).toBe( 'record' ); - expect( __unstableGetSelect ).toHaveBeenCalledWith( 'core' ); + expect( registry.select ).toHaveBeenCalledWith( 'core' ); expect( getEditedEntityRecord ).toHaveBeenCalledWith( KIND, POST_TYPE, buildNavigationPostId( menuId ) ); + + getNavigationPostForMenu.registry = defaultRegistry; + hasResolvedNavigationPost.registry = defaultRegistry; } ); it( 'returns null if has not resolved navigation post yet', () => { const getEditedEntityRecord = jest.fn( () => 'record' ); const hasFinishedResolution = jest.fn( () => false ); - const __unstableGetSelect = jest.fn( () => ( { - getEditedEntityRecord, - hasFinishedResolution, - } ) ); + const registry = { + select: jest.fn( () => ( { + getEditedEntityRecord, + hasFinishedResolution, + } ) ), + }; const menuId = 123; - getNavigationPostForMenu.__unstableGetSelect = __unstableGetSelect; - hasResolvedNavigationPost.__unstableGetSelect = __unstableGetSelect; + const defaultRegistry = getNavigationPostForMenu.registry; + getNavigationPostForMenu.registry = registry; + hasResolvedNavigationPost.registry = registry; expect( getNavigationPostForMenu( 'state', menuId ) ).toBe( null ); - expect( __unstableGetSelect ).toHaveBeenCalledWith( 'core' ); + expect( registry.select ).toHaveBeenCalledWith( 'core' ); expect( getEditedEntityRecord ).not.toHaveBeenCalled(); + + getNavigationPostForMenu.registry = defaultRegistry; + hasResolvedNavigationPost.registry = defaultRegistry; } ); } ); describe( 'hasResolvedNavigationPost', () => { it( 'returns if it has resolved navigation post yet', () => { const hasFinishedResolution = jest.fn( () => true ); - const __unstableGetSelect = jest.fn( () => ( { - hasFinishedResolution, - } ) ); + const registry = { + select: jest.fn( () => ( { + hasFinishedResolution, + } ) ), + }; const menuId = 123; - hasResolvedNavigationPost.__unstableGetSelect = __unstableGetSelect; + const defaultRegistry = getNavigationPostForMenu.registry; + hasResolvedNavigationPost.registry = registry; expect( hasResolvedNavigationPost( 'state', menuId ) ).toBe( true ); - expect( __unstableGetSelect ).toHaveBeenCalledWith( 'core' ); + expect( registry.select ).toHaveBeenCalledWith( 'core' ); expect( hasFinishedResolution ).toHaveBeenCalledWith( 'getEntityRecord', [ KIND, POST_TYPE, buildNavigationPostId( menuId ) ] ); + + hasResolvedNavigationPost.registry = defaultRegistry; } ); } ); @@ -77,9 +94,11 @@ describe( 'getMenuItemForClientId', () => { it( 'gets menu item for client id', () => { const getMenuItem = jest.fn( () => 'menuItem' ); - const __unstableGetSelect = jest.fn( () => ( { - getMenuItem, - } ) ); + const registry = { + select: jest.fn( () => ( { + getMenuItem, + } ) ), + }; const state = { mapping: { @@ -89,13 +108,16 @@ describe( 'getMenuItemForClientId', () => { }, }; - getMenuItemForClientId.__unstableGetSelect = __unstableGetSelect; + const defaultRegistry = getMenuItemForClientId.registry; + getMenuItemForClientId.registry = registry; expect( getMenuItemForClientId( state, 'postId', 'clientId' ) ).toBe( 'menuItem' ); - expect( __unstableGetSelect ).toHaveBeenCalledWith( 'core' ); + expect( registry.select ).toHaveBeenCalledWith( 'core' ); expect( getMenuItem ).toHaveBeenCalledWith( '123' ); + + getMenuItemForClientId.registry = defaultRegistry; } ); } ); diff --git a/packages/edit-post/README.md b/packages/edit-post/README.md index 485fbdea87427b..1aa9181145c28f 100644 --- a/packages/edit-post/README.md +++ b/packages/edit-post/README.md @@ -507,6 +507,18 @@ _Parameters_ - _settings_ `?Object`: Editor settings object. - _initialEdits_ `Object`: Programmatic edits to apply initially, to be considered as non-user-initiated (bypass for unsaved changes prompt). +<a name="store" href="#store">#</a> **store** + +Store definition for the edit post namespace. + +_Related_ + +- <https://github.com/WordPress/gutenberg/blob/master/packages/data/README.md#createReduxStore> + +_Type_ + +- `Object` + <!-- END TOKEN(Autogenerated API docs) --> diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index bd8bc599f00d10..9f2e1778179f07 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "3.25.4", + "version": "3.26.0", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:../a11y", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/block-editor": "file:../block-editor", @@ -33,6 +33,7 @@ "@wordpress/compose": "file:../compose", "@wordpress/core-data": "file:../core-data", "@wordpress/data": "file:../data", + "@wordpress/data-controls": "file:../data-controls", "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", "@wordpress/hooks": "file:../hooks", @@ -45,14 +46,12 @@ "@wordpress/notices": "file:../notices", "@wordpress/plugins": "file:../plugins", "@wordpress/primitives": "file:../primitives", - "@wordpress/reusable-blocks": "file:../reusable-blocks", "@wordpress/url": "file:../url", "@wordpress/viewport": "file:../viewport", "@wordpress/warning": "file:../warning", "classnames": "^2.2.5", "lodash": "^4.17.19", "memize": "^1.1.0", - "refx": "^3.0.0", "rememo": "^3.0.0" }, "publishConfig": { diff --git a/packages/edit-post/src/components/admin-notices/index.js b/packages/edit-post/src/components/admin-notices/index.js index 05cfa0676a5559..1906f65824f2c0 100644 --- a/packages/edit-post/src/components/admin-notices/index.js +++ b/packages/edit-post/src/components/admin-notices/index.js @@ -3,6 +3,7 @@ */ import { Component } from '@wordpress/element'; import { withDispatch } from '@wordpress/data'; +import { store as noticesStore } from '@wordpress/notices'; /** * Mapping of server-supported notice class names to an equivalent notices @@ -103,7 +104,7 @@ export class AdminNotices extends Component { } export default withDispatch( ( dispatch ) => { - const { createNotice } = dispatch( 'core/notices' ); + const { createNotice } = dispatch( noticesStore ); return { createNotice }; } )( AdminNotices ); diff --git a/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js b/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js index 98eed14996ae60..5f8712b3611f47 100644 --- a/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js +++ b/packages/edit-post/src/components/block-settings-menu/plugin-block-settings-menu-item.js @@ -9,7 +9,6 @@ import { difference } from 'lodash'; import { BlockSettingsMenuControls } from '@wordpress/block-editor'; import { MenuItem } from '@wordpress/components'; import { compose } from '@wordpress/compose'; -import { plugins } from '@wordpress/icons'; const isEverySelectedBlockAllowed = ( selected, allowed ) => difference( selected, allowed ).length === 0; @@ -102,7 +101,7 @@ const PluginBlockSettingsMenuItem = ( { return ( <MenuItem onClick={ compose( onClick, onClose ) } - icon={ icon || plugins } + icon={ icon } label={ small ? label : undefined } role={ role } > diff --git a/packages/edit-post/src/components/device-preview/index.js b/packages/edit-post/src/components/device-preview/index.js index 742790caf0a245..3aaa5d8a04d103 100644 --- a/packages/edit-post/src/components/device-preview/index.js +++ b/packages/edit-post/src/components/device-preview/index.js @@ -8,6 +8,11 @@ import { __ } from '@wordpress/i18n'; import { __experimentalPreviewOptions as PreviewOptions } from '@wordpress/block-editor'; import { useDispatch, useSelect } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + export default function DevicePreview() { const { hasActiveMetaboxes, @@ -16,18 +21,18 @@ export default function DevicePreview() { deviceType, } = useSelect( ( select ) => ( { - hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), - isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), + hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), + isSaving: select( editPostStore ).isSavingMetaBoxes(), isPostSaveable: select( 'core/editor' ).isEditedPostSaveable(), deviceType: select( - 'core/edit-post' + editPostStore ).__experimentalGetPreviewDeviceType(), } ), [] ); const { __experimentalSetPreviewDeviceType: setPreviewDeviceType, - } = useDispatch( 'core/edit-post' ); + } = useDispatch( editPostStore ); return ( <PreviewOptions diff --git a/packages/edit-post/src/components/editor-initialization/test/listener-hooks.js b/packages/edit-post/src/components/editor-initialization/test/listener-hooks.js index ee6c73aead7971..df18799c1cfa86 100644 --- a/packages/edit-post/src/components/editor-initialization/test/listener-hooks.js +++ b/packages/edit-post/src/components/editor-initialization/test/listener-hooks.js @@ -6,7 +6,7 @@ import TestRenderer, { act } from 'react-test-renderer'; /** * WordPress dependencies */ -import { RegistryProvider } from '@wordpress/data'; +import { RegistryProvider, createRegistry } from '@wordpress/data'; /** * Internal dependencies @@ -18,45 +18,62 @@ import { import { STORE_NAME } from '../../../store/constants'; describe( 'listener hook tests', () => { + const storeConfig = { + actions: { + forceUpdate: jest.fn( () => ( { type: 'FORCE_UPDATE' } ) ), + }, + reducer: ( state = {}, action ) => + action.type === 'FORCE_UPDATE' ? { ...state } : state, + }; const mockStores = { 'core/block-editor': { - getBlockSelectionStart: jest.fn(), + ...storeConfig, + selectors: { + getBlockSelectionStart: jest.fn(), + }, }, 'core/editor': { - getCurrentPost: jest.fn(), + ...storeConfig, + selectors: { + getCurrentPost: jest.fn(), + }, }, 'core/viewport': { - isViewportMatch: jest.fn(), + ...storeConfig, + selectors: { + isViewportMatch: jest.fn(), + }, }, [ STORE_NAME ]: { - isEditorSidebarOpened: jest.fn(), - openGeneralSidebar: jest.fn(), - closeGeneralSidebar: jest.fn(), - getActiveGeneralSidebarName: jest.fn(), - }, - }; - let atomResolver; - const registry = { - __internalGetAtomRegistry: () => ( {} ), - __internalGetAtomResolver: () => atomResolver, - __internalSetAtomResolver: ( resolver ) => { - atomResolver = resolver; + ...storeConfig, + actions: { + ...storeConfig.actions, + openGeneralSidebar: jest.fn( () => ( { + type: 'OPEN_GENERAL_SIDEBAR', + } ) ), + closeGeneralSidebar: jest.fn( () => ( { + type: 'CLOSE_GENERAL_SIDEBAR', + } ) ), + }, + selectors: { + isEditorSidebarOpened: jest.fn(), + getActiveGeneralSidebarName: jest.fn(), + }, }, - select: jest - .fn() - .mockImplementation( ( storeName ) => mockStores[ storeName ] ), - dispatch: jest - .fn() - .mockImplementation( ( storeName ) => mockStores[ storeName ] ), - subscribe: () => {}, }; + + let registry; + beforeEach( () => { + registry = createRegistry( mockStores ); + } ); + const setMockReturnValue = ( store, functionName, value ) => { - mockStores[ store ][ functionName ] = jest - .fn() - .mockReturnValue( value ); + mockStores[ store ].selectors[ functionName ].mockReturnValue( value ); }; const getSpyedFunction = ( store, functionName ) => - mockStores[ store ][ functionName ]; + mockStores[ store ].selectors[ functionName ]; + const getSpyedAction = ( store, actionName ) => + mockStores[ store ].actions[ actionName ]; const renderComponent = ( testedHook, id, renderer = null ) => { const TestComponent = ( { postId } ) => { testedHook( postId ); @@ -73,7 +90,10 @@ describe( 'listener hook tests', () => { }; afterEach( () => { Object.values( mockStores ).forEach( ( storeMocks ) => { - Object.values( storeMocks ).forEach( ( mock ) => { + Object.values( storeMocks.selectors ).forEach( ( mock ) => { + mock.mockClear(); + } ); + Object.values( storeMocks.actions || {} ).forEach( ( mock ) => { mock.mockClear(); } ); } ); @@ -88,7 +108,7 @@ describe( 'listener hook tests', () => { getSpyedFunction( STORE_NAME, 'isEditorSidebarOpened' ) ).toHaveBeenCalled(); expect( - getSpyedFunction( STORE_NAME, 'openGeneralSidebar' ) + getSpyedAction( STORE_NAME, 'openGeneralSidebar' ) ).toHaveBeenCalledTimes( 0 ); } ); it( 'opens block sidebar if block is selected', () => { @@ -102,7 +122,7 @@ describe( 'listener hook tests', () => { renderComponent( useBlockSelectionListener, 10 ); } ); expect( - getSpyedFunction( STORE_NAME, 'openGeneralSidebar' ) + getSpyedAction( STORE_NAME, 'openGeneralSidebar' ) ).toHaveBeenCalledWith( 'edit-post/block' ); } ); it( 'opens document sidebar if block is not selected', () => { @@ -116,7 +136,7 @@ describe( 'listener hook tests', () => { renderComponent( useBlockSelectionListener, 10 ); } ); expect( - getSpyedFunction( STORE_NAME, 'openGeneralSidebar' ) + getSpyedAction( STORE_NAME, 'openGeneralSidebar' ) ).toHaveBeenCalledWith( 'edit-post/document' ); } ); } ); @@ -151,6 +171,9 @@ describe( 'listener hook tests', () => { expect( setAttribute ).not.toHaveBeenCalled(); } ); it( 'only calls document query selector once across renders', () => { + setMockReturnValue( 'core/editor', 'getCurrentPost', { + link: 'foo', + } ); act( () => { const renderer = renderComponent( useUpdatePostLinkListener, @@ -159,6 +182,10 @@ describe( 'listener hook tests', () => { renderComponent( useUpdatePostLinkListener, 20, renderer ); } ); expect( mockSelector ).toHaveBeenCalledTimes( 1 ); + act( () => { + registry.dispatch( 'core/editor' ).forceUpdate(); + } ); + expect( mockSelector ).toHaveBeenCalledTimes( 1 ); } ); it( 'only updates the permalink when it changes', () => { setMockReturnValue( 'core/editor', 'getCurrentPost', { @@ -168,6 +195,29 @@ describe( 'listener hook tests', () => { renderComponent( useUpdatePostLinkListener, 10 ); } ); expect( setAttribute ).toHaveBeenCalledTimes( 1 ); + act( () => { + registry.dispatch( 'core/editor' ).forceUpdate(); + } ); + expect( setAttribute ).toHaveBeenCalledTimes( 1 ); + } ); + it( 'updates the permalink when it changes', () => { + setMockReturnValue( 'core/editor', 'getCurrentPost', { + link: 'foo', + } ); + act( () => { + renderComponent( useUpdatePostLinkListener, 10 ); + } ); + expect( setAttribute ).toHaveBeenCalledTimes( 1 ); + expect( setAttribute ).toHaveBeenCalledWith( 'href', 'foo' ); + + setMockReturnValue( 'core/editor', 'getCurrentPost', { + link: 'bar', + } ); + act( () => { + registry.dispatch( 'core/editor' ).forceUpdate(); + } ); + expect( setAttribute ).toHaveBeenCalledTimes( 2 ); + expect( setAttribute ).toHaveBeenCalledWith( 'href', 'bar' ); } ); } ); } ); diff --git a/packages/edit-post/src/components/header/feature-toggle/index.js b/packages/edit-post/src/components/header/feature-toggle/index.js index f0f52e04cf8d18..13f72ae8d75a4e 100644 --- a/packages/edit-post/src/components/header/feature-toggle/index.js +++ b/packages/edit-post/src/components/header/feature-toggle/index.js @@ -13,6 +13,11 @@ import { __ } from '@wordpress/i18n'; import { check } from '@wordpress/icons'; import { speak } from '@wordpress/a11y'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + function FeatureToggle( { onToggle, isActive, @@ -46,11 +51,11 @@ function FeatureToggle( { export default compose( [ withSelect( ( select, { feature } ) => ( { - isActive: select( 'core/edit-post' ).isFeatureActive( feature ), + isActive: select( editPostStore ).isFeatureActive( feature ), } ) ), withDispatch( ( dispatch, ownProps ) => ( { onToggle() { - dispatch( 'core/edit-post' ).toggleFeature( ownProps.feature ); + dispatch( editPostStore ).toggleFeature( ownProps.feature ); }, } ) ), ] )( FeatureToggle ); diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/index.js b/packages/edit-post/src/components/header/fullscreen-mode-close/index.js index d7da08c758470f..391d915d39d729 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/index.js +++ b/packages/edit-post/src/components/header/fullscreen-mode-close/index.js @@ -12,11 +12,16 @@ import { __ } from '@wordpress/i18n'; import { addQueryArgs } from '@wordpress/url'; import { wordpress } from '@wordpress/icons'; -function FullscreenModeClose( { showTooltip } ) { +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + +function FullscreenModeClose( { showTooltip, icon, href } ) { const { isActive, isRequestingSiteIcon, postType, siteIconUrl } = useSelect( ( select ) => { const { getCurrentPostType } = select( 'core/editor' ); - const { isFeatureActive } = select( 'core/edit-post' ); + const { isFeatureActive } = select( editPostStore ); const { isResolving } = select( 'core/data' ); const { getEntityRecord, getPostType } = select( 'core' ); const siteData = @@ -50,16 +55,26 @@ function FullscreenModeClose( { showTooltip } ) { src={ siteIconUrl } /> ); - } else if ( isRequestingSiteIcon ) { + } + + if ( isRequestingSiteIcon ) { buttonIcon = null; } + // Override default icon if custom icon is provided via props. + if ( icon ) { + buttonIcon = <Icon size="36px" icon={ icon } />; + } + return ( <Button className="edit-post-fullscreen-mode-close has-icon" - href={ addQueryArgs( 'edit.php', { - post_type: postType.slug, - } ) } + href={ + href ?? + addQueryArgs( 'edit.php', { + post_type: postType.slug, + } ) + } label={ get( postType, [ 'labels', 'view_items' ], __( 'Back' ) ) } showTooltip={ showTooltip } > diff --git a/packages/edit-post/src/components/header/fullscreen-mode-close/style.scss b/packages/edit-post/src/components/header/fullscreen-mode-close/style.scss index 825e2811d5c979..d24dd609328526 100644 --- a/packages/edit-post/src/components/header/fullscreen-mode-close/style.scss +++ b/packages/edit-post/src/components/header/fullscreen-mode-close/style.scss @@ -11,7 +11,7 @@ background: #23282e; // WP-admin gray. color: $white; border-radius: 0; - height: auto; + height: $header-height; width: $header-height; &:hover { @@ -31,4 +31,3 @@ .edit-post-fullscreen-mode-close_site-icon { width: 36px; } - diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index ad8737767a7f9a..cc2efea4bf657a 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -25,9 +30,15 @@ import { import { plus } from '@wordpress/icons'; import { useRef } from '@wordpress/element'; +/** + * Internal dependencies + */ +import TemplateTitle from '../template-title'; +import { store as editPostStore } from '../../../store'; + function HeaderToolbar() { const inserterButton = useRef(); - const { setIsInserterOpened } = useDispatch( 'core/edit-post' ); + const { setIsInserterOpened } = useDispatch( editPostStore ); const { hasFixedToolbar, isInserterEnabled, @@ -36,6 +47,7 @@ function HeaderToolbar() { previewDeviceType, showIconLabels, isNavigationTool, + isTemplateMode, } = useSelect( ( select ) => { const { hasInserterItems, @@ -43,27 +55,28 @@ function HeaderToolbar() { getBlockSelectionEnd, } = select( 'core/block-editor' ); return { - hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( + hasFixedToolbar: select( editPostStore ).isFeatureActive( 'fixedToolbar' ), // This setting (richEditingEnabled) should not live in the block editor's setting. isInserterEnabled: - select( 'core/edit-post' ).getEditorMode() === 'visual' && + select( editPostStore ).getEditorMode() === 'visual' && select( 'core/editor' ).getEditorSettings() .richEditingEnabled && hasInserterItems( getBlockRootClientId( getBlockSelectionEnd() ) ), - isInserterOpened: select( 'core/edit-post' ).isInserterOpened(), + isInserterOpened: select( editPostStore ).isInserterOpened(), isTextModeEnabled: - select( 'core/edit-post' ).getEditorMode() === 'text', + select( editPostStore ).getEditorMode() === 'text', previewDeviceType: select( - 'core/edit-post' + editPostStore ).__experimentalGetPreviewDeviceType(), - showIconLabels: select( 'core/edit-post' ).isFeatureActive( + showIconLabels: select( editPostStore ).isFeatureActive( 'showIconLabels' ), isNavigationTool: select( 'core/block-editor' ).isNavigationMode(), + isTemplateMode: select( editPostStore ).isEditingTemplate(), }; }, [] ); const isLargeViewport = useViewportMatch( 'medium' ); @@ -206,8 +219,17 @@ characters. */ ) } </div> + <TemplateTitle /> + { displayBlockToolbar && ( - <div className="edit-post-header-toolbar__block-toolbar"> + <div + className={ classnames( + 'edit-post-header-toolbar__block-toolbar', + { + 'is-pushed-down': isTemplateMode, + } + ) } + > <BlockToolbar hideDragHandle /> </div> ) } diff --git a/packages/edit-post/src/components/header/header-toolbar/index.native.js b/packages/edit-post/src/components/header/header-toolbar/index.native.js index 39fa0544fe4a22..26c0717b4f05fc 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.native.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.native.js @@ -23,6 +23,7 @@ import { * Internal dependencies */ import styles from './style.scss'; +import { store as editPostStore } from '../../../store'; function HeaderToolbar( { hasRedo, @@ -110,10 +111,9 @@ export default compose( [ hasUndo: select( 'core/editor' ).hasEditorUndo(), // This setting (richEditingEnabled) should not live in the block editor's setting. showInserter: - select( 'core/edit-post' ).getEditorMode() === 'visual' && + select( editPostStore ).getEditorMode() === 'visual' && select( 'core/editor' ).getEditorSettings().richEditingEnabled, - isTextModeEnabled: - select( 'core/edit-post' ).getEditorMode() === 'text', + isTextModeEnabled: select( editPostStore ).getEditorMode() === 'text', isRTL: select( 'core/block-editor' ).getSettings().isRTL, } ) ), withDispatch( ( dispatch ) => { diff --git a/packages/edit-post/src/components/header/header-toolbar/style.scss b/packages/edit-post/src/components/header/header-toolbar/style.scss index 84de5011a39de3..fc3bd59f4232fe 100644 --- a/packages/edit-post/src/components/header/header-toolbar/style.scss +++ b/packages/edit-post/src/components/header/header-toolbar/style.scss @@ -1,5 +1,6 @@ .edit-post-header-toolbar { display: inline-flex; + flex-grow: 1; align-items: center; border: none; @@ -117,25 +118,27 @@ // Move toolbar into top Editor Bar. @include break-wide { - position: static; - left: auto; - right: auto; - background: none; - border-bottom: none; - - .is-sidebar-opened & { + &:not(.is-pushed-down) { + position: static; + left: auto; right: auto; - } + background: none; + border-bottom: none; - .block-editor-block-toolbar { - border-left: $border-width solid $gray-300; - } + .is-sidebar-opened & { + right: auto; + } - .block-editor-block-toolbar .components-toolbar-group, - .block-editor-block-toolbar .components-toolbar { - $top-toolbar-padding: ($header-height - $grid-unit-60) / 2; - height: $header-height; - padding: $top-toolbar-padding 0; + .block-editor-block-toolbar { + border-left: $border-width solid $gray-300; + } + + .block-editor-block-toolbar .components-toolbar-group, + .block-editor-block-toolbar .components-toolbar { + $top-toolbar-padding: ($header-height - $grid-unit-60) / 2; + height: $header-height; + padding: $top-toolbar-padding 0; + } } } } diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index ba1b3e7d9366ed..6d30a275c10ad5 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -8,10 +8,7 @@ import classnames from 'classnames'; */ import { PostSavedState, PostPreviewButton } from '@wordpress/editor'; import { useSelect } from '@wordpress/data'; -import { - PinnedItems, - __experimentalMainDashboardButton as MainDashboardButton, -} from '@wordpress/interface'; +import { PinnedItems } from '@wordpress/interface'; import { useViewportMatch } from '@wordpress/compose'; /** @@ -22,6 +19,9 @@ import HeaderToolbar from './header-toolbar'; import MoreMenu from './more-menu'; import PostPublishButtonOrToggle from './post-publish-button-or-toggle'; import { default as DevicePreview } from '../device-preview'; +import MainDashboardButton from './main-dashboard-button'; +import TemplateSaveButton from './template-save-button'; +import { store as editPostStore } from '../../store'; function Header( { setEntitiesSavedStatesCallback } ) { const { @@ -30,19 +30,21 @@ function Header( { setEntitiesSavedStatesCallback } ) { isSaving, showIconLabels, hasReducedUI, + isEditingTemplate, } = useSelect( ( select ) => ( { - hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), + hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), isPublishSidebarOpened: select( - 'core/edit-post' + editPostStore ).isPublishSidebarOpened(), - isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), - showIconLabels: select( 'core/edit-post' ).isFeatureActive( + isSaving: select( editPostStore ).isSavingMetaBoxes(), + showIconLabels: select( editPostStore ).isFeatureActive( 'showIconLabels' ), - hasReducedUI: select( 'core/edit-post' ).isFeatureActive( + hasReducedUI: select( editPostStore ).isFeatureActive( 'reducedUI' ), + isEditingTemplate: select( editPostStore ).isEditingTemplate(), } ), [] ); @@ -62,30 +64,35 @@ function Header( { setEntitiesSavedStatesCallback } ) { <HeaderToolbar /> </div> <div className="edit-post-header__settings"> - { ! isPublishSidebarOpened && ( - // This button isn't completely hidden by the publish sidebar. - // We can't hide the whole toolbar when the publish sidebar is open because - // we want to prevent mounting/unmounting the PostPublishButtonOrToggle DOM node. - // We track that DOM node to return focus to the PostPublishButtonOrToggle - // when the publish sidebar has been closed. - <PostSavedState - forceIsDirty={ hasActiveMetaboxes } - forceIsSaving={ isSaving } - showIconLabels={ showIconLabels } - /> + { ! isEditingTemplate && ( + <> + { ! isPublishSidebarOpened && ( + // This button isn't completely hidden by the publish sidebar. + // We can't hide the whole toolbar when the publish sidebar is open because + // we want to prevent mounting/unmounting the PostPublishButtonOrToggle DOM node. + // We track that DOM node to return focus to the PostPublishButtonOrToggle + // when the publish sidebar has been closed. + <PostSavedState + forceIsDirty={ hasActiveMetaboxes } + forceIsSaving={ isSaving } + showIconLabels={ showIconLabels } + /> + ) } + <DevicePreview /> + <PostPreviewButton + forceIsAutosaveable={ hasActiveMetaboxes } + forcePreviewLink={ isSaving ? null : undefined } + /> + <PostPublishButtonOrToggle + forceIsDirty={ hasActiveMetaboxes } + forceIsSaving={ isSaving } + setEntitiesSavedStatesCallback={ + setEntitiesSavedStatesCallback + } + /> + </> ) } - <DevicePreview /> - <PostPreviewButton - forceIsAutosaveable={ hasActiveMetaboxes } - forcePreviewLink={ isSaving ? null : undefined } - /> - <PostPublishButtonOrToggle - forceIsDirty={ hasActiveMetaboxes } - forceIsSaving={ isSaving } - setEntitiesSavedStatesCallback={ - setEntitiesSavedStatesCallback - } - /> + { isEditingTemplate && <TemplateSaveButton /> } { ( isLargeViewport || ! showIconLabels ) && ( <> <PinnedItems.Slot scope="core/edit-post" /> diff --git a/packages/interface/src/components/main-dashboard-button/index.js b/packages/edit-post/src/components/header/main-dashboard-button/index.js similarity index 100% rename from packages/interface/src/components/main-dashboard-button/index.js rename to packages/edit-post/src/components/header/main-dashboard-button/index.js diff --git a/packages/edit-post/src/components/header/mode-switcher/index.js b/packages/edit-post/src/components/header/mode-switcher/index.js index 5ee3ec93747978..0605188d474712 100644 --- a/packages/edit-post/src/components/header/mode-switcher/index.js +++ b/packages/edit-post/src/components/header/mode-switcher/index.js @@ -4,6 +4,12 @@ import { __ } from '@wordpress/i18n'; import { MenuItemsChoice, MenuGroup } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; /** * Set of available mode options. @@ -30,17 +36,17 @@ function ModeSwitcher() { } = useSelect( ( select ) => ( { shortcut: select( - 'core/keyboard-shortcuts' + keyboardShortcutsStore ).getShortcutRepresentation( 'core/edit-post/toggle-mode' ), isRichEditingEnabled: select( 'core/editor' ).getEditorSettings() .richEditingEnabled, isCodeEditingEnabled: select( 'core/editor' ).getEditorSettings() .codeEditingEnabled, - mode: select( 'core/edit-post' ).getEditorMode(), + mode: select( editPostStore ).getEditorMode(), } ), [] ); - const { switchEditorMode } = useDispatch( 'core/edit-post' ); + const { switchEditorMode } = useDispatch( editPostStore ); if ( ! isRichEditingEnabled || ! isCodeEditingEnabled ) { return null; diff --git a/packages/edit-post/src/components/header/post-publish-button-or-toggle.js b/packages/edit-post/src/components/header/post-publish-button-or-toggle.js index bf91d0c23645df..2be75c4d0927f6 100644 --- a/packages/edit-post/src/components/header/post-publish-button-or-toggle.js +++ b/packages/edit-post/src/components/header/post-publish-button-or-toggle.js @@ -10,6 +10,11 @@ import { useViewportMatch, compose } from '@wordpress/compose'; import { withDispatch, withSelect } from '@wordpress/data'; import { PostPublishButton } from '@wordpress/editor'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + export function PostPublishButtonOrToggle( { forceIsDirty, forceIsSaving, @@ -90,12 +95,12 @@ export default compose( 'core/editor' ).isPublishSidebarEnabled(), isPublishSidebarOpened: select( - 'core/edit-post' + editPostStore ).isPublishSidebarOpened(), isScheduled: select( 'core/editor' ).isCurrentPostScheduled(), } ) ), withDispatch( ( dispatch ) => { - const { togglePublishSidebar } = dispatch( 'core/edit-post' ); + const { togglePublishSidebar } = dispatch( editPostStore ); return { togglePublishSidebar, }; diff --git a/packages/edit-post/src/components/header/preferences-menu-item/index.js b/packages/edit-post/src/components/header/preferences-menu-item/index.js index f12ef2c422f908..163ef900f362e5 100644 --- a/packages/edit-post/src/components/header/preferences-menu-item/index.js +++ b/packages/edit-post/src/components/header/preferences-menu-item/index.js @@ -5,6 +5,11 @@ import { withDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { MenuItem } from '@wordpress/components'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + export function PreferencesMenuItem( { openModal } ) { return ( <MenuItem @@ -18,7 +23,7 @@ export function PreferencesMenuItem( { openModal } ) { } export default withDispatch( ( dispatch ) => { - const { openModal } = dispatch( 'core/edit-post' ); + const { openModal } = dispatch( editPostStore ); return { openModal, diff --git a/packages/edit-post/src/components/header/template-save-button/index.js b/packages/edit-post/src/components/header/template-save-button/index.js new file mode 100644 index 00000000000000..48089eb55df50d --- /dev/null +++ b/packages/edit-post/src/components/header/template-save-button/index.js @@ -0,0 +1,49 @@ +/** + * WordPress dependencies + */ +import { EntitiesSavedStates } from '@wordpress/editor'; +import { Button } from '@wordpress/components'; +import { useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { useDispatch } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { ActionsPanelFill } from '../../layout/actions-panel'; +import { store as editPostStore } from '../../../store'; + +function TemplateSaveButton() { + const [ + isEntitiesReviewPanelOpen, + setIsEntitiesReviewPanelOpen, + ] = useState( false ); + const { setIsEditingTemplate } = useDispatch( editPostStore ); + return ( + <> + <Button onClick={ () => setIsEditingTemplate( false ) } isTertiary> + { __( 'Cancel' ) } + </Button> + <Button + isPrimary + onClick={ () => setIsEntitiesReviewPanelOpen( true ) } + aria-expanded={ isEntitiesReviewPanelOpen } + > + { __( 'Apply' ) } + </Button> + <ActionsPanelFill> + <EntitiesSavedStates + isOpen={ isEntitiesReviewPanelOpen } + close={ ( entities ) => { + setIsEntitiesReviewPanelOpen( false ); + if ( entities?.length ) { + setIsEditingTemplate( false ); + } + } } + /> + </ActionsPanelFill> + </> + ); +} + +export default TemplateSaveButton; diff --git a/packages/edit-post/src/components/header/template-title/index.js b/packages/edit-post/src/components/header/template-title/index.js new file mode 100644 index 00000000000000..deb2b905ee9e34 --- /dev/null +++ b/packages/edit-post/src/components/header/template-title/index.js @@ -0,0 +1,43 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; +import { store as editorStore } from '@wordpress/editor'; +import { store as coreStore } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + +function TemplateTitle() { + const { template, isEditing } = useSelect( ( select ) => { + const { getEditedPostAttribute } = select( editorStore ); + const { __experimentalGetTemplateForLink } = select( coreStore ); + const { isEditingTemplate } = select( editPostStore ); + const link = getEditedPostAttribute( 'link' ); + const _isEditing = isEditingTemplate(); + return { + template: _isEditing + ? __experimentalGetTemplateForLink( link ) + : null, + isEditing: _isEditing, + }; + }, [] ); + + if ( ! isEditing || ! template ) { + return null; + } + + return ( + <span className="edit-post-template-title"> + { + /* translators: 1: Template name. */ + sprintf( __( 'Editing template: %s' ), template.slug ) + } + </span> + ); +} + +export default TemplateTitle; diff --git a/packages/edit-post/src/components/header/template-title/style.scss b/packages/edit-post/src/components/header/template-title/style.scss new file mode 100644 index 00000000000000..b4d6132c089757 --- /dev/null +++ b/packages/edit-post/src/components/header/template-title/style.scss @@ -0,0 +1,5 @@ +.edit-post-template-title { + display: inline-flex; + flex-grow: 1; + justify-content: center; +} diff --git a/packages/edit-post/src/components/keyboard-shortcut-help-modal/dynamic-shortcut.js b/packages/edit-post/src/components/keyboard-shortcut-help-modal/dynamic-shortcut.js index f60e0f2aad846d..fe97fba37e14ac 100644 --- a/packages/edit-post/src/components/keyboard-shortcut-help-modal/dynamic-shortcut.js +++ b/packages/edit-post/src/components/keyboard-shortcut-help-modal/dynamic-shortcut.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { useSelect } from '@wordpress/data'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies @@ -14,7 +15,7 @@ function DynamicShortcut( { name } ) { getShortcutKeyCombination, getShortcutDescription, getShortcutAliases, - } = select( 'core/keyboard-shortcuts' ); + } = select( keyboardShortcutsStore ); return { keyCombination: getShortcutKeyCombination( name ), diff --git a/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js b/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js index ebabc14ca3b747..a05e3f20ba4a3d 100644 --- a/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js +++ b/packages/edit-post/src/components/keyboard-shortcut-help-modal/index.js @@ -9,7 +9,10 @@ import { isString } from 'lodash'; */ import { Modal } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useShortcut } from '@wordpress/keyboard-shortcuts'; +import { + useShortcut, + store as keyboardShortcutsStore, +} from '@wordpress/keyboard-shortcuts'; import { withSelect, withDispatch, useSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; @@ -19,6 +22,7 @@ import { compose } from '@wordpress/compose'; import { textFormattingShortcuts } from './config'; import Shortcut from './shortcut'; import DynamicShortcut from './dynamic-shortcut'; +import { store as editPostStore } from '../../store'; const MODAL_NAME = 'edit-post/keyboard-shortcut-help'; @@ -71,7 +75,7 @@ const ShortcutCategorySection = ( { } ) => { const categoryShortcuts = useSelect( ( select ) => { - return select( 'core/keyboard-shortcuts' ).getCategoryShortcuts( + return select( keyboardShortcutsStore ).getCategoryShortcuts( categoryName ); }, @@ -140,10 +144,10 @@ export function KeyboardShortcutHelpModal( { isModalActive, toggleModal } ) { export default compose( [ withSelect( ( select ) => ( { - isModalActive: select( 'core/edit-post' ).isModalActive( MODAL_NAME ), + isModalActive: select( editPostStore ).isModalActive( MODAL_NAME ), } ) ), withDispatch( ( dispatch, { isModalActive } ) => { - const { openModal, closeModal } = dispatch( 'core/edit-post' ); + const { openModal, closeModal } = dispatch( editPostStore ); return { toggleModal: () => diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js index 77ea945af679bd..4e7a46a92bf66f 100644 --- a/packages/edit-post/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js @@ -3,9 +3,17 @@ */ import { useEffect } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; -import { useShortcut } from '@wordpress/keyboard-shortcuts'; +import { + useShortcut, + store as keyboardShortcutsStore, +} from '@wordpress/keyboard-shortcuts'; import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + function KeyboardShortcuts() { const { getBlockSelectionStart, @@ -18,8 +26,8 @@ function KeyboardShortcuts() { return { getBlockSelectionStart: select( 'core/block-editor' ) .getBlockSelectionStart, - getEditorMode: select( 'core/edit-post' ).getEditorMode, - isEditorSidebarOpened: select( 'core/edit-post' ) + getEditorMode: select( editPostStore ).getEditorMode, + isEditorSidebarOpened: select( editPostStore ) .isEditorSidebarOpened, richEditingEnabled: settings.richEditingEnabled, codeEditingEnabled: settings.codeEditingEnabled, @@ -31,8 +39,8 @@ function KeyboardShortcuts() { openGeneralSidebar, closeGeneralSidebar, toggleFeature, - } = useDispatch( 'core/edit-post' ); - const { registerShortcut } = useDispatch( 'core/keyboard-shortcuts' ); + } = useDispatch( editPostStore ); + const { registerShortcut } = useDispatch( keyboardShortcutsStore ); useEffect( () => { registerShortcut( { diff --git a/packages/edit-post/src/components/layout/actions-panel.js b/packages/edit-post/src/components/layout/actions-panel.js index 5ac15a9c253c91..44d3d1944428b9 100644 --- a/packages/edit-post/src/components/layout/actions-panel.js +++ b/packages/edit-post/src/components/layout/actions-panel.js @@ -3,7 +3,7 @@ */ import { EntitiesSavedStates, PostPublishPanel } from '@wordpress/editor'; import { useSelect, useDispatch } from '@wordpress/data'; -import { Button } from '@wordpress/components'; +import { Button, createSlotFill } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useCallback } from '@wordpress/element'; /** @@ -11,6 +11,11 @@ import { useCallback } from '@wordpress/element'; */ import PluginPostPublishPanel from '../sidebar/plugin-post-publish-panel'; import PluginPrePublishPanel from '../sidebar/plugin-pre-publish-panel'; +import { store as editPostStore } from '../../store'; + +const { Fill, Slot } = createSlotFill( 'ActionsPanel' ); + +export const ActionsPanelFill = Fill; export default function ActionsPanel( { setEntitiesSavedStatesCallback, @@ -18,7 +23,7 @@ export default function ActionsPanel( { isEntitiesSavedStatesOpen, } ) { const { closePublishSidebar, togglePublishSidebar } = useDispatch( - 'core/edit-post' + editPostStore ); const { publishSidebarOpened, @@ -28,10 +33,10 @@ export default function ActionsPanel( { } = useSelect( ( select ) => { return { publishSidebarOpened: select( - 'core/edit-post' + editPostStore ).isPublishSidebarOpened(), - hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), - isSavingMetaBoxes: select( 'core/edit-post' ).isSavingMetaBoxes(), + hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), + isSavingMetaBoxes: select( editPostStore ).isSavingMetaBoxes(), hasNonPostEntityChanges: select( 'core/editor' ).hasNonPostEntityChanges(), @@ -92,6 +97,7 @@ export default function ActionsPanel( { isOpen={ isEntitiesSavedStatesOpen } close={ closeEntitiesSavedStates } /> + <Slot bubblesVirtually /> { ! isEntitiesSavedStatesOpen && unmountableContent } </> ); diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 85634df17ce5c1..67628820b61550 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -17,14 +17,18 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { BlockBreadcrumb, __experimentalLibrary as Library, + __unstableUseEditorStyles as useEditorStyles, } from '@wordpress/block-editor'; import { Button, ScrollLock, Popover, - FocusReturnProvider, + __unstableUseDrop as useDrop, } from '@wordpress/components'; -import { useViewportMatch } from '@wordpress/compose'; +import { + useViewportMatch, + __experimentalUseDialog as useDialog, +} from '@wordpress/compose'; import { PluginArea } from '@wordpress/plugins'; import { __ } from '@wordpress/i18n'; import { @@ -32,8 +36,9 @@ import { FullscreenMode, InterfaceSkeleton, } from '@wordpress/interface'; -import { useState, useEffect, useCallback } from '@wordpress/element'; +import { useState, useEffect, useCallback, useRef } from '@wordpress/element'; import { close } from '@wordpress/icons'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies @@ -50,7 +55,7 @@ import SettingsSidebar from '../sidebar/settings-sidebar'; import MetaBoxes from '../meta-boxes'; import WelcomeGuide from '../welcome-guide'; import ActionsPanel from './actions-panel'; -import PopoverWrapper from './popover-wrapper'; +import { store as editPostStore } from '../../store'; const interfaceLabels = { secondarySidebar: __( 'Block library' ), @@ -66,14 +71,14 @@ const interfaceLabels = { footer: __( 'Editor footer' ), }; -function Layout() { +function Layout( { settings } ) { const isMobileViewport = useViewportMatch( 'medium', '<' ); const isHugeViewport = useViewportMatch( 'huge', '>=' ); const { openGeneralSidebar, closeGeneralSidebar, setIsInserterOpened, - } = useDispatch( 'core/edit-post' ); + } = useDispatch( editPostStore ); const { mode, isFullscreenActive, @@ -90,37 +95,37 @@ function Layout() { hasReducedUI, } = useSelect( ( select ) => { return { - hasFixedToolbar: select( 'core/edit-post' ).isFeatureActive( + hasFixedToolbar: select( editPostStore ).isFeatureActive( 'fixedToolbar' ), sidebarIsOpened: !! ( select( 'core/interface' ).getActiveComplementaryArea( - 'core/edit-post' - ) || select( 'core/edit-post' ).isPublishSidebarOpened() + editPostStore.name + ) || select( editPostStore ).isPublishSidebarOpened() ), - isFullscreenActive: select( 'core/edit-post' ).isFeatureActive( + isFullscreenActive: select( editPostStore ).isFeatureActive( 'fullscreenMode' ), - showMostUsedBlocks: select( 'core/edit-post' ).isFeatureActive( + showMostUsedBlocks: select( editPostStore ).isFeatureActive( 'mostUsedBlocks' ), - isInserterOpened: select( 'core/edit-post' ).isInserterOpened(), - mode: select( 'core/edit-post' ).getEditorMode(), + isInserterOpened: select( editPostStore ).isInserterOpened(), + mode: select( editPostStore ).getEditorMode(), isRichEditingEnabled: select( 'core/editor' ).getEditorSettings() .richEditingEnabled, - hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), + hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), previousShortcut: select( - 'core/keyboard-shortcuts' + keyboardShortcutsStore ).getAllShortcutRawKeyCombinations( 'core/edit-post/previous-region' ), nextShortcut: select( - 'core/keyboard-shortcuts' + keyboardShortcutsStore ).getAllShortcutRawKeyCombinations( 'core/edit-post/next-region' ), - showIconLabels: select( 'core/edit-post' ).isFeatureActive( + showIconLabels: select( editPostStore ).isFeatureActive( 'showIconLabels' ), - hasReducedUI: select( 'core/edit-post' ).isFeatureActive( + hasReducedUI: select( editPostStore ).isFeatureActive( 'reducedUI' ), }; @@ -163,6 +168,13 @@ function Layout() { }, [ entitiesSavedStatesCallback ] ); + const ref = useRef(); + + useDrop( ref ); + useEditorStyles( ref, settings.styles ); + const [ inserterDialogRef, inserterDialogProps ] = useDialog( { + onClose: () => setIsInserterOpened( false ), + } ); return ( <> @@ -174,128 +186,118 @@ function Layout() { <EditPostKeyboardShortcuts /> <EditorKeyboardShortcutsRegister /> <SettingsSidebar /> - <FocusReturnProvider> - <InterfaceSkeleton - className={ className } - labels={ interfaceLabels } - header={ - <Header - setEntitiesSavedStatesCallback={ - setEntitiesSavedStatesCallback - } - /> - } - secondarySidebar={ - mode === 'visual' && - isInserterOpened && ( - <PopoverWrapper - className="edit-post-layout__inserter-panel-popover-wrapper" - onClose={ () => setIsInserterOpened( false ) } - > - <div className="edit-post-layout__inserter-panel"> - <div className="edit-post-layout__inserter-panel-header"> - <Button - icon={ close } - onClick={ () => - setIsInserterOpened( false ) - } - /> - </div> - <div className="edit-post-layout__inserter-panel-content"> - <Library - showMostUsedBlocks={ - showMostUsedBlocks - } - showInserterHelpPanel - onSelect={ () => { - if ( isMobileViewport ) { - setIsInserterOpened( - false - ); - } - } } - /> - </div> - </div> - </PopoverWrapper> - ) - } - sidebar={ - ( ! isMobileViewport || sidebarIsOpened ) && ( - <> - { ! isMobileViewport && ! sidebarIsOpened && ( - <div className="edit-post-layout__toogle-sidebar-panel"> - <Button - isSecondary - className="edit-post-layout__toogle-sidebar-panel-button" - onClick={ openSidebarPanel } - aria-expanded={ false } - > - { hasBlockSelected - ? __( 'Open block settings' ) - : __( - 'Open document settings' - ) } - </Button> - </div> - ) } - <ComplementaryArea.Slot scope="core/edit-post" /> - </> - ) - } - content={ - <> - <EditorNotices /> - { ( mode === 'text' || ! isRichEditingEnabled ) && ( - <TextEditor /> - ) } - { isRichEditingEnabled && mode === 'visual' && ( - <VisualEditor /> - ) } - <div className="edit-post-layout__metaboxes"> - <MetaBoxes location="normal" /> - <MetaBoxes location="advanced" /> + <InterfaceSkeleton + ref={ ref } + className={ className } + labels={ interfaceLabels } + header={ + <Header + setEntitiesSavedStatesCallback={ + setEntitiesSavedStatesCallback + } + /> + } + secondarySidebar={ + mode === 'visual' && + isInserterOpened && ( + <div + ref={ inserterDialogRef } + { ...inserterDialogProps } + className="edit-post-layout__inserter-panel" + > + <div className="edit-post-layout__inserter-panel-header"> + <Button + icon={ close } + onClick={ () => + setIsInserterOpened( false ) + } + /> + </div> + <div className="edit-post-layout__inserter-panel-content"> + <Library + showMostUsedBlocks={ showMostUsedBlocks } + showInserterHelpPanel + onSelect={ () => { + if ( isMobileViewport ) { + setIsInserterOpened( false ); + } + } } + /> </div> - { isMobileViewport && sidebarIsOpened && ( - <ScrollLock /> + </div> + ) + } + sidebar={ + ( ! isMobileViewport || sidebarIsOpened ) && ( + <> + { ! isMobileViewport && ! sidebarIsOpened && ( + <div className="edit-post-layout__toogle-sidebar-panel"> + <Button + isSecondary + className="edit-post-layout__toogle-sidebar-panel-button" + onClick={ openSidebarPanel } + aria-expanded={ false } + > + { hasBlockSelected + ? __( 'Open block settings' ) + : __( 'Open document settings' ) } + </Button> + </div> ) } + <ComplementaryArea.Slot scope="core/edit-post" /> </> - } - footer={ - ! hasReducedUI && - ! isMobileViewport && - isRichEditingEnabled && - mode === 'visual' && ( - <div className="edit-post-layout__footer"> - <BlockBreadcrumb /> - </div> - ) - } - actions={ - <ActionsPanel - closeEntitiesSavedStates={ - closeEntitiesSavedStates - } - isEntitiesSavedStatesOpen={ - entitiesSavedStatesCallback - } - setEntitiesSavedStatesCallback={ - setEntitiesSavedStatesCallback - } - /> - } - shortcuts={ { - previous: previousShortcut, - next: nextShortcut, - } } - /> - <ManageBlocksModal /> - <PreferencesModal /> - <KeyboardShortcutHelpModal /> - <WelcomeGuide /> - <Popover.Slot /> - <PluginArea /> - </FocusReturnProvider> + ) + } + content={ + <> + <EditorNotices /> + { ( mode === 'text' || ! isRichEditingEnabled ) && ( + <TextEditor /> + ) } + { isRichEditingEnabled && mode === 'visual' && ( + <VisualEditor /> + ) } + <div className="edit-post-layout__metaboxes"> + <MetaBoxes location="normal" /> + <MetaBoxes location="advanced" /> + </div> + { isMobileViewport && sidebarIsOpened && ( + <ScrollLock /> + ) } + </> + } + footer={ + ! hasReducedUI && + ! isMobileViewport && + isRichEditingEnabled && + mode === 'visual' && ( + <div className="edit-post-layout__footer"> + <BlockBreadcrumb /> + </div> + ) + } + actions={ + <ActionsPanel + closeEntitiesSavedStates={ closeEntitiesSavedStates } + isEntitiesSavedStatesOpen={ + entitiesSavedStatesCallback + } + setEntitiesSavedStatesCallback={ + setEntitiesSavedStatesCallback + } + /> + } + shortcuts={ { + previous: previousShortcut, + next: nextShortcut, + } } + /> + <ManageBlocksModal /> + <PreferencesModal /> + <KeyboardShortcutHelpModal /> + <WelcomeGuide /> + <Popover.Slot /> + <PluginArea /> </> ); } diff --git a/packages/edit-post/src/components/layout/index.native.js b/packages/edit-post/src/components/layout/index.native.js index 3664262f147ae1..14c08553c5b94a 100644 --- a/packages/edit-post/src/components/layout/index.native.js +++ b/packages/edit-post/src/components/layout/index.native.js @@ -31,6 +31,7 @@ import styles from './style.scss'; import headerToolbarStyles from '../header/header-toolbar/style.scss'; import Header from '../header'; import VisualEditor from '../visual-editor'; +import { store as editPostStore } from '../../store'; class Layout extends Component { constructor() { @@ -174,7 +175,7 @@ export default compose( [ const { __unstableIsEditorReady: isEditorReady } = select( 'core/editor' ); - const { getEditorMode } = select( 'core/edit-post' ); + const { getEditorMode } = select( editPostStore ); const { getSettings } = select( 'core/block-editor' ); return { isReady: isEditorReady(), diff --git a/packages/edit-post/src/components/layout/popover-wrapper.js b/packages/edit-post/src/components/layout/popover-wrapper.js deleted file mode 100644 index 375ba0ace1179d..00000000000000 --- a/packages/edit-post/src/components/layout/popover-wrapper.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * WordPress dependencies - */ -import { - withConstrainedTabbing, - withFocusReturn, - withFocusOutside, -} from '@wordpress/components'; -import { Component } from '@wordpress/element'; -import { ESCAPE } from '@wordpress/keycodes'; - -function stopPropagation( event ) { - event.stopPropagation(); -} - -const DetectOutside = withFocusOutside( - class extends Component { - handleFocusOutside( event ) { - this.props.onFocusOutside( event ); - } - - render() { - return this.props.children; - } - } -); - -const FocusManaged = withConstrainedTabbing( - withFocusReturn( ( { children } ) => children ) -); - -export default function PopoverWrapper( { onClose, children, className } ) { - // Event handlers - const maybeClose = ( event ) => { - // Close on escape - if ( event.keyCode === ESCAPE && onClose ) { - event.stopPropagation(); - onClose(); - } - }; - - // Disable reason: this stops certain events from propagating outside of the component. - // - onMouseDown is disabled as this can cause interactions with other DOM elements - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( - <div - className={ className } - onKeyDown={ maybeClose } - onMouseDown={ stopPropagation } - > - <DetectOutside onFocusOutside={ onClose }> - <FocusManaged>{ children }</FocusManaged> - </DetectOutside> - </div> - ); - /* eslint-enable jsx-a11y/no-static-element-interactions */ -} diff --git a/packages/edit-post/src/components/layout/style.scss b/packages/edit-post/src/components/layout/style.scss index b7dd51c31b4258..dfb1dc7fc4c2ae 100644 --- a/packages/edit-post/src/components/layout/style.scss +++ b/packages/edit-post/src/components/layout/style.scss @@ -95,17 +95,6 @@ background-color: $gray-400; } -// Ideally we don't need all these nested divs. -// Removing these requires a refactoring of the different a11y HoCs. -.edit-post-layout__inserter-panel-popover-wrapper { - &, - & > div, - & > div > div, - & > div > div > div { - height: 100%; - } -} - .edit-post-layout__inserter-panel { height: 100%; display: flex; diff --git a/packages/edit-post/src/components/manage-blocks-modal/category.js b/packages/edit-post/src/components/manage-blocks-modal/category.js index 3169115c86b83b..3583f47b9e7211 100644 --- a/packages/edit-post/src/components/manage-blocks-modal/category.js +++ b/packages/edit-post/src/components/manage-blocks-modal/category.js @@ -16,6 +16,7 @@ import { CheckboxControl } from '@wordpress/components'; */ import BlockTypesChecklist from './checklist'; import EditPostSettings from '../edit-post-settings'; +import { store as editPostStore } from '../../store'; function BlockManagerCategory( { instanceId, @@ -84,14 +85,14 @@ function BlockManagerCategory( { export default compose( [ withInstanceId, withSelect( ( select ) => { - const { getPreference } = select( 'core/edit-post' ); + const { getPreference } = select( editPostStore ); return { hiddenBlockTypes: getPreference( 'hiddenBlockTypes' ), }; } ), withDispatch( ( dispatch, ownProps ) => { - const { showBlockTypes, hideBlockTypes } = dispatch( 'core/edit-post' ); + const { showBlockTypes, hideBlockTypes } = dispatch( editPostStore ); return { toggleVisible( blockName, nextIsChecked ) { diff --git a/packages/edit-post/src/components/manage-blocks-modal/index.js b/packages/edit-post/src/components/manage-blocks-modal/index.js index ee88068ad6d6e1..9bba595ed0be6d 100644 --- a/packages/edit-post/src/components/manage-blocks-modal/index.js +++ b/packages/edit-post/src/components/manage-blocks-modal/index.js @@ -10,6 +10,7 @@ import { compose } from '@wordpress/compose'; * Internal dependencies */ import BlockManager from './manager'; +import { store as editPostStore } from '../../store'; /** * Unique identifier for Manage Blocks modal. @@ -37,14 +38,14 @@ export function ManageBlocksModal( { isActive, closeModal } ) { export default compose( [ withSelect( ( select ) => { - const { isModalActive } = select( 'core/edit-post' ); + const { isModalActive } = select( editPostStore ); return { isActive: isModalActive( MODAL_NAME ), }; } ), withDispatch( ( dispatch ) => { - const { closeModal } = dispatch( 'core/edit-post' ); + const { closeModal } = dispatch( editPostStore ); return { closeModal, diff --git a/packages/edit-post/src/components/manage-blocks-modal/manager.js b/packages/edit-post/src/components/manage-blocks-modal/manager.js index f5922f8549bd44..b6df0ee16e41d3 100644 --- a/packages/edit-post/src/components/manage-blocks-modal/manager.js +++ b/packages/edit-post/src/components/manage-blocks-modal/manager.js @@ -6,6 +6,7 @@ import { filter, includes, isArray } from 'lodash'; /** * WordPress dependencies */ +import { store as blocksStore } from '@wordpress/blocks'; import { withSelect } from '@wordpress/data'; import { compose, withState } from '@wordpress/compose'; import { TextControl } from '@wordpress/components'; @@ -15,6 +16,7 @@ import { __, _n, sprintf } from '@wordpress/i18n'; * Internal dependencies */ import BlockManagerCategory from './category'; +import { store as editPostStore } from '../../store'; function BlockManager( { search, @@ -102,8 +104,8 @@ export default compose( [ getCategories, hasBlockSupport, isMatchingSearchTerm, - } = select( 'core/blocks' ); - const { getPreference } = select( 'core/edit-post' ); + } = select( blocksStore ); + const { getPreference } = select( editPostStore ); const hiddenBlockTypes = getPreference( 'hiddenBlockTypes' ); const numberOfHiddenBlocks = isArray( hiddenBlockTypes ) && hiddenBlockTypes.length; diff --git a/packages/edit-post/src/components/meta-boxes/index.js b/packages/edit-post/src/components/meta-boxes/index.js index 35ef20f04a2df3..e5e3e2ac2066df 100644 --- a/packages/edit-post/src/components/meta-boxes/index.js +++ b/packages/edit-post/src/components/meta-boxes/index.js @@ -13,6 +13,7 @@ import { withSelect } from '@wordpress/data'; */ import MetaBoxesArea from './meta-boxes-area'; import MetaBoxVisibility from './meta-box-visibility'; +import { store as editPostStore } from '../../store'; function MetaBoxes( { location, isVisible, metaBoxes } ) { return ( @@ -27,7 +28,7 @@ function MetaBoxes( { location, isVisible, metaBoxes } ) { export default withSelect( ( select, { location } ) => { const { isMetaBoxLocationVisible, getMetaBoxesPerLocation } = select( - 'core/edit-post' + editPostStore ); return { diff --git a/packages/edit-post/src/components/meta-boxes/meta-box-visibility.js b/packages/edit-post/src/components/meta-boxes/meta-box-visibility.js index c6af87bebb2607..488d73e1a76e03 100644 --- a/packages/edit-post/src/components/meta-boxes/meta-box-visibility.js +++ b/packages/edit-post/src/components/meta-boxes/meta-box-visibility.js @@ -4,6 +4,11 @@ import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + class MetaBoxVisibility extends Component { componentDidMount() { this.updateDOM(); @@ -36,7 +41,7 @@ class MetaBoxVisibility extends Component { } export default withSelect( ( select, { id } ) => ( { - isVisible: select( 'core/edit-post' ).isEditorPanelEnabled( + isVisible: select( editPostStore ).isEditorPanelEnabled( `meta-box-${ id }` ), } ) )( MetaBoxVisibility ); diff --git a/packages/edit-post/src/components/meta-boxes/meta-boxes-area/index.js b/packages/edit-post/src/components/meta-boxes/meta-boxes-area/index.js index 239d013be69b98..d5e8a13458bb0e 100644 --- a/packages/edit-post/src/components/meta-boxes/meta-boxes-area/index.js +++ b/packages/edit-post/src/components/meta-boxes/meta-boxes-area/index.js @@ -10,6 +10,11 @@ import { Component } from '@wordpress/element'; import { Spinner } from '@wordpress/components'; import { withSelect } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + class MetaBoxesArea extends Component { /** * @inheritdoc @@ -78,6 +83,6 @@ class MetaBoxesArea extends Component { export default withSelect( ( select ) => { return { - isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), + isSaving: select( editPostStore ).isSavingMetaBoxes(), }; } )( MetaBoxesArea ); diff --git a/packages/edit-post/src/components/preferences-modal/index.js b/packages/edit-post/src/components/preferences-modal/index.js index 1066c718a29d55..e237984e29df7c 100644 --- a/packages/edit-post/src/components/preferences-modal/index.js +++ b/packages/edit-post/src/components/preferences-modal/index.js @@ -29,6 +29,7 @@ import { EnableFeature, } from './options'; import MetaBoxesSection from './meta-boxes-section'; +import { store as editPostStore } from '../../store'; const MODAL_NAME = 'edit-post/preferences'; @@ -154,15 +155,13 @@ export default compose( const postType = getPostType( getEditedPostAttribute( 'type' ) ); return { - isModalActive: select( 'core/edit-post' ).isModalActive( - MODAL_NAME - ), + isModalActive: select( editPostStore ).isModalActive( MODAL_NAME ), isViewable: get( postType, [ 'viewable' ], false ), }; } ), withDispatch( ( dispatch ) => { return { - closeModal: () => dispatch( 'core/edit-post' ).closeModal(), + closeModal: () => dispatch( editPostStore ).closeModal(), }; } ) )( PreferencesModal ); diff --git a/packages/edit-post/src/components/preferences-modal/meta-boxes-section.js b/packages/edit-post/src/components/preferences-modal/meta-boxes-section.js index 802f8a88955a26..54c388616436cb 100644 --- a/packages/edit-post/src/components/preferences-modal/meta-boxes-section.js +++ b/packages/edit-post/src/components/preferences-modal/meta-boxes-section.js @@ -14,6 +14,7 @@ import { withSelect } from '@wordpress/data'; */ import Section from './section'; import { EnableCustomFieldsOption, EnablePanelOption } from './options'; +import { store as editPostStore } from '../../store'; export function MetaBoxesSection( { areCustomFieldsRegistered, @@ -48,7 +49,7 @@ export function MetaBoxesSection( { export default withSelect( ( select ) => { const { getEditorSettings } = select( 'core/editor' ); - const { getAllMetaBoxes } = select( 'core/edit-post' ); + const { getAllMetaBoxes } = select( editPostStore ); return { // This setting should not live in the block editor's store. diff --git a/packages/edit-post/src/components/preferences-modal/options/enable-feature.js b/packages/edit-post/src/components/preferences-modal/options/enable-feature.js index 4868b2f09a7b72..315dd2ed656e50 100644 --- a/packages/edit-post/src/components/preferences-modal/options/enable-feature.js +++ b/packages/edit-post/src/components/preferences-modal/options/enable-feature.js @@ -8,16 +8,16 @@ import { withSelect, withDispatch } from '@wordpress/data'; * Internal dependencies */ import BaseOption from './base'; +import { store as editPostStore } from '../../../store'; export default compose( withSelect( ( select, { featureName } ) => { - const { isFeatureActive } = select( 'core/edit-post' ); + const { isFeatureActive } = select( editPostStore ); return { isChecked: isFeatureActive( featureName ), }; } ), withDispatch( ( dispatch, { featureName } ) => ( { - onChange: () => - dispatch( 'core/edit-post' ).toggleFeature( featureName ), + onChange: () => dispatch( editPostStore ).toggleFeature( featureName ), } ) ) )( BaseOption ); diff --git a/packages/edit-post/src/components/preferences-modal/options/enable-panel.js b/packages/edit-post/src/components/preferences-modal/options/enable-panel.js index 57d134c0e6ef68..0a25b4ad4d86a7 100644 --- a/packages/edit-post/src/components/preferences-modal/options/enable-panel.js +++ b/packages/edit-post/src/components/preferences-modal/options/enable-panel.js @@ -8,11 +8,12 @@ import { withSelect, withDispatch } from '@wordpress/data'; * Internal dependencies */ import BaseOption from './base'; +import { store as editPostStore } from '../../../store'; export default compose( withSelect( ( select, { panelName } ) => { const { isEditorPanelEnabled, isEditorPanelRemoved } = select( - 'core/edit-post' + editPostStore ); return { isRemoved: isEditorPanelRemoved( panelName ), @@ -22,6 +23,6 @@ export default compose( ifCondition( ( { isRemoved } ) => ! isRemoved ), withDispatch( ( dispatch, { panelName } ) => ( { onChange: () => - dispatch( 'core/edit-post' ).toggleEditorPanelEnabled( panelName ), + dispatch( editPostStore ).toggleEditorPanelEnabled( panelName ), } ) ) )( BaseOption ); diff --git a/packages/edit-post/src/components/sidebar/discussion-panel/index.js b/packages/edit-post/src/components/sidebar/discussion-panel/index.js index 47ea5ae4e500b5..cc72cb245b0576 100644 --- a/packages/edit-post/src/components/sidebar/discussion-panel/index.js +++ b/packages/edit-post/src/components/sidebar/discussion-panel/index.js @@ -11,6 +11,11 @@ import { import { compose } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + /** * Module Constants */ @@ -47,17 +52,15 @@ function DiscussionPanel( { isEnabled, isOpened, onTogglePanel } ) { export default compose( [ withSelect( ( select ) => { return { - isEnabled: select( 'core/edit-post' ).isEditorPanelEnabled( - PANEL_NAME - ), - isOpened: select( 'core/edit-post' ).isEditorPanelOpened( + isEnabled: select( editPostStore ).isEditorPanelEnabled( PANEL_NAME ), + isOpened: select( editPostStore ).isEditorPanelOpened( PANEL_NAME ), }; } ), withDispatch( ( dispatch ) => ( { onTogglePanel() { - return dispatch( 'core/edit-post' ).toggleEditorPanelOpened( + return dispatch( editPostStore ).toggleEditorPanelOpened( PANEL_NAME ); }, diff --git a/packages/edit-post/src/components/sidebar/featured-image/index.js b/packages/edit-post/src/components/sidebar/featured-image/index.js index 63a95fb76178b5..3c6fd8e286d002 100644 --- a/packages/edit-post/src/components/sidebar/featured-image/index.js +++ b/packages/edit-post/src/components/sidebar/featured-image/index.js @@ -12,6 +12,11 @@ import { PostFeaturedImage, PostFeaturedImageCheck } from '@wordpress/editor'; import { compose } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + /** * Module Constants */ @@ -43,7 +48,7 @@ const applyWithSelect = withSelect( ( select ) => { const { getEditedPostAttribute } = select( 'core/editor' ); const { getPostType } = select( 'core' ); const { isEditorPanelEnabled, isEditorPanelOpened } = select( - 'core/edit-post' + editPostStore ); return { @@ -54,7 +59,7 @@ const applyWithSelect = withSelect( ( select ) => { } ); const applyWithDispatch = withDispatch( ( dispatch ) => { - const { toggleEditorPanelOpened } = dispatch( 'core/edit-post' ); + const { toggleEditorPanelOpened } = dispatch( editPostStore ); return { onTogglePanel: partial( toggleEditorPanelOpened, PANEL_NAME ), diff --git a/packages/edit-post/src/components/sidebar/page-attributes/index.js b/packages/edit-post/src/components/sidebar/page-attributes/index.js index 97e7199ef41871..bdbf94c6f2de53 100644 --- a/packages/edit-post/src/components/sidebar/page-attributes/index.js +++ b/packages/edit-post/src/components/sidebar/page-attributes/index.js @@ -17,6 +17,11 @@ import { } from '@wordpress/editor'; import { withSelect, withDispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + /** * Module Constants */ @@ -55,7 +60,7 @@ export function PageAttributes( { const applyWithSelect = withSelect( ( select ) => { const { getEditedPostAttribute } = select( 'core/editor' ); const { isEditorPanelEnabled, isEditorPanelOpened } = select( - 'core/edit-post' + editPostStore ); const { getPostType } = select( 'core' ); return { @@ -66,7 +71,7 @@ const applyWithSelect = withSelect( ( select ) => { } ); const applyWithDispatch = withDispatch( ( dispatch ) => { - const { toggleEditorPanelOpened } = dispatch( 'core/edit-post' ); + const { toggleEditorPanelOpened } = dispatch( editPostStore ); return { onTogglePanel: partial( toggleEditorPanelOpened, PANEL_NAME ), diff --git a/packages/edit-post/src/components/sidebar/plugin-document-setting-panel/index.js b/packages/edit-post/src/components/sidebar/plugin-document-setting-panel/index.js index 681897d5f26716..b7ceebd3031f5d 100644 --- a/packages/edit-post/src/components/sidebar/plugin-document-setting-panel/index.js +++ b/packages/edit-post/src/components/sidebar/plugin-document-setting-panel/index.js @@ -15,6 +15,7 @@ import warning from '@wordpress/warning'; * Internal dependencies */ import { EnablePluginDocumentSettingPanelOption } from '../../preferences-modal/options'; +import { store as editPostStore } from '../../../store'; export const { Fill, Slot } = createSlotFill( 'PluginDocumentSettingPanel' ); @@ -115,15 +116,15 @@ const PluginDocumentSettingPanel = compose( } ), withSelect( ( select, { panelName } ) => { return { - opened: select( 'core/edit-post' ).isEditorPanelOpened( panelName ), - isEnabled: select( 'core/edit-post' ).isEditorPanelEnabled( + opened: select( editPostStore ).isEditorPanelOpened( panelName ), + isEnabled: select( editPostStore ).isEditorPanelEnabled( panelName ), }; } ), withDispatch( ( dispatch, { panelName } ) => ( { onToggle() { - return dispatch( 'core/edit-post' ).toggleEditorPanelOpened( + return dispatch( editPostStore ).toggleEditorPanelOpened( panelName ); }, diff --git a/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js b/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js index 27a1d7733f1b80..b1f6cb517145ad 100644 --- a/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js +++ b/packages/edit-post/src/components/sidebar/plugin-sidebar/index.js @@ -4,6 +4,12 @@ import { ComplementaryArea } from '@wordpress/interface'; import { useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; /** * Renders a sidebar when activated. The contents within the `PluginSidebar` will appear as content within the sidebar. @@ -78,9 +84,9 @@ export default function PluginSidebarEditPost( { className, ...props } ) { 'title' ), shortcut: select( - 'core/keyboard-shortcuts' + keyboardShortcutsStore ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' ), - showIconLabels: select( 'core/edit-post' ).isFeatureActive( + showIconLabels: select( editPostStore ).isFeatureActive( 'showIconLabels' ), }; diff --git a/packages/edit-post/src/components/sidebar/post-excerpt/index.js b/packages/edit-post/src/components/sidebar/post-excerpt/index.js index 1502bc7e7a3de2..208fa733fc7289 100644 --- a/packages/edit-post/src/components/sidebar/post-excerpt/index.js +++ b/packages/edit-post/src/components/sidebar/post-excerpt/index.js @@ -10,6 +10,11 @@ import { import { compose } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + /** * Module Constants */ @@ -36,17 +41,15 @@ function PostExcerpt( { isEnabled, isOpened, onTogglePanel } ) { export default compose( [ withSelect( ( select ) => { return { - isEnabled: select( 'core/edit-post' ).isEditorPanelEnabled( - PANEL_NAME - ), - isOpened: select( 'core/edit-post' ).isEditorPanelOpened( + isEnabled: select( editPostStore ).isEditorPanelEnabled( PANEL_NAME ), + isOpened: select( editPostStore ).isEditorPanelOpened( PANEL_NAME ), }; } ), withDispatch( ( dispatch ) => ( { onTogglePanel() { - return dispatch( 'core/edit-post' ).toggleEditorPanelOpened( + return dispatch( editPostStore ).toggleEditorPanelOpened( PANEL_NAME ); }, diff --git a/packages/edit-post/src/components/sidebar/post-link/index.js b/packages/edit-post/src/components/sidebar/post-link/index.js index 3a73af2e6da7dd..c765f3a320b62d 100644 --- a/packages/edit-post/src/components/sidebar/post-link/index.js +++ b/packages/edit-post/src/components/sidebar/post-link/index.js @@ -13,6 +13,11 @@ import { compose, ifCondition, withState } from '@wordpress/compose'; import { cleanForSlug } from '@wordpress/editor'; import { safeDecodeURIComponent } from '@wordpress/url'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + /** * Module Constants */ @@ -133,7 +138,7 @@ export default compose( [ getEditedPostSlug, } = select( 'core/editor' ); const { isEditorPanelEnabled, isEditorPanelOpened } = select( - 'core/edit-post' + editPostStore ); const { getPostType } = select( 'core' ); @@ -161,7 +166,7 @@ export default compose( [ return isEnabled && postLink && isViewable && hasPermalinkParts; } ), withDispatch( ( dispatch ) => { - const { toggleEditorPanelOpened } = dispatch( 'core/edit-post' ); + const { toggleEditorPanelOpened } = dispatch( editPostStore ); const { editPost } = dispatch( 'core/editor' ); return { onTogglePanel: () => toggleEditorPanelOpened( PANEL_NAME ), diff --git a/packages/edit-post/src/components/sidebar/post-status/index.js b/packages/edit-post/src/components/sidebar/post-status/index.js index 11311e91849c2b..43fb7d1646fec1 100644 --- a/packages/edit-post/src/components/sidebar/post-status/index.js +++ b/packages/edit-post/src/components/sidebar/post-status/index.js @@ -18,6 +18,8 @@ import PostSlug from '../post-slug'; import PostFormat from '../post-format'; import PostPendingStatus from '../post-pending-status'; import PluginPostStatusInfo from '../plugin-post-status-info'; +import PostTemplate from '../post-template'; +import { store as editPostStore } from '../../../store'; /** * Module Constants @@ -35,6 +37,7 @@ function PostStatus( { isOpened, onTogglePanel } ) { <PluginPostStatusInfo.Slot> { ( fills ) => ( <> + <PostTemplate /> <PostVisibility /> <PostSchedule /> <PostFormat /> @@ -56,7 +59,7 @@ export default compose( [ // We use isEditorPanelRemoved to hide the panel if it was programatically removed. We do // not use isEditorPanelEnabled since this panel should not be disabled through the UI. const { isEditorPanelRemoved, isEditorPanelOpened } = select( - 'core/edit-post' + editPostStore ); return { isRemoved: isEditorPanelRemoved( PANEL_NAME ), @@ -66,7 +69,7 @@ export default compose( [ ifCondition( ( { isRemoved } ) => ! isRemoved ), withDispatch( ( dispatch ) => ( { onTogglePanel() { - return dispatch( 'core/edit-post' ).toggleEditorPanelOpened( + return dispatch( editPostStore ).toggleEditorPanelOpened( PANEL_NAME ); }, diff --git a/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js b/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js index 59febc55554a39..aaa92ae63bcfba 100644 --- a/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js +++ b/packages/edit-post/src/components/sidebar/post-taxonomies/taxonomy-panel.js @@ -10,6 +10,11 @@ import { compose } from '@wordpress/compose'; import { PanelBody } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + function TaxonomyPanel( { isEnabled, taxonomy, @@ -44,16 +49,16 @@ export default compose( return { panelName, isEnabled: slug - ? select( 'core/edit-post' ).isEditorPanelEnabled( panelName ) + ? select( editPostStore ).isEditorPanelEnabled( panelName ) : false, isOpened: slug - ? select( 'core/edit-post' ).isEditorPanelOpened( panelName ) + ? select( editPostStore ).isEditorPanelOpened( panelName ) : false, }; } ), withDispatch( ( dispatch, ownProps ) => ( { onTogglePanel: () => { - dispatch( 'core/edit-post' ).toggleEditorPanelOpened( + dispatch( editPostStore ).toggleEditorPanelOpened( ownProps.panelName ); }, diff --git a/packages/edit-post/src/components/sidebar/post-template/index.js b/packages/edit-post/src/components/sidebar/post-template/index.js new file mode 100644 index 00000000000000..38168cc6794b79 --- /dev/null +++ b/packages/edit-post/src/components/sidebar/post-template/index.js @@ -0,0 +1,91 @@ +/** + * WordPress dependencies + */ +import { __, sprintf } from '@wordpress/i18n'; +import { PanelRow, Button } from '@wordpress/components'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { createInterpolateElement } from '@wordpress/element'; +import { store as editorStore } from '@wordpress/editor'; +import { store as coreStore } from '@wordpress/core-data'; +import { store as noticesStore } from '@wordpress/notices'; + +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + +function PostTemplate() { + const { template, isEditing, isFSETheme } = useSelect( ( select ) => { + const { + getEditedPostAttribute, + getCurrentPostType, + getCurrentPost, + } = select( editorStore ); + const { __experimentalGetTemplateForLink } = select( coreStore ); + const { isEditingTemplate } = select( editPostStore ); + const link = getEditedPostAttribute( 'link' ); + const isFSEEnabled = select( editorStore ).getEditorSettings() + .isFSETheme; + return { + template: + isFSEEnabled && + link && + getCurrentPost().status !== 'auto-draft' && + getCurrentPostType() !== 'wp_template' + ? __experimentalGetTemplateForLink( link ) + : null, + isEditing: isEditingTemplate(), + isFSETheme: isFSEEnabled, + }; + }, [] ); + const { setIsEditingTemplate } = useDispatch( editPostStore ); + const { createSuccessNotice } = useDispatch( noticesStore ); + + if ( ! isFSETheme || ! template ) { + return null; + } + + return ( + <PanelRow className="edit-post-post-template"> + <span>{ __( 'Template' ) }</span> + { ! isEditing && ( + <span className="edit-post-post-template__value"> + { createInterpolateElement( + sprintf( + /* translators: 1: Template name. */ + __( '%s (<a>Edit</a>)' ), + template.slug + ), + { + a: ( + <Button + isLink + onClick={ () => { + setIsEditingTemplate( true ); + createSuccessNotice( + __( + 'Editing template. Changes made here affect all posts and pages that use the template.' + ), + { + type: 'snackbar', + } + ); + } } + > + { __( 'Edit' ) } + </Button> + ), + } + ) } + </span> + ) } + { isEditing && ( + <span className="edit-post-post-template__value"> + { template.slug } + </span> + ) } + </PanelRow> + ); +} + +export default PostTemplate; diff --git a/packages/edit-post/src/components/sidebar/post-template/style.scss b/packages/edit-post/src/components/sidebar/post-template/style.scss new file mode 100644 index 00000000000000..d625e897224368 --- /dev/null +++ b/packages/edit-post/src/components/sidebar/post-template/style.scss @@ -0,0 +1,13 @@ +.edit-post-post-template { + width: 100%; + justify-content: left; + + span { + display: block; + width: 45%; + } +} + +.edit-post-post-template__value { + padding-left: 6px; +} diff --git a/packages/edit-post/src/components/sidebar/settings-header/index.js b/packages/edit-post/src/components/sidebar/settings-header/index.js index 6a144d4daeb2b4..b614ec0ebaf102 100644 --- a/packages/edit-post/src/components/sidebar/settings-header/index.js +++ b/packages/edit-post/src/components/sidebar/settings-header/index.js @@ -5,8 +5,13 @@ import { Button } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { useDispatch, useSelect } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../../store'; + const SettingsHeader = ( { sidebarName } ) => { - const { openGeneralSidebar } = useDispatch( 'core/edit-post' ); + const { openGeneralSidebar } = useDispatch( editPostStore ); const openDocumentSettings = () => openGeneralSidebar( 'edit-post/document' ); const openBlockSettings = () => openGeneralSidebar( 'edit-post/block' ); diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js index c4548ba61820b3..34395f1bb94f05 100644 --- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js +++ b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js @@ -4,6 +4,7 @@ import { BlockInspector } from '@wordpress/block-editor'; import { cog } from '@wordpress/icons'; import { Platform } from '@wordpress/element'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; /** * Internal dependencies @@ -22,6 +23,7 @@ import PluginDocumentSettingPanel from '../plugin-document-setting-panel'; import PluginSidebarEditPost from '../../sidebar/plugin-sidebar'; import { __ } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; +import { store as editPostStore } from '../../../store'; const SIDEBAR_ACTIVE_BY_DEFAULT = Platform.select( { web: true, @@ -38,7 +40,7 @@ const SettingsSidebar = () => { // should contain the sidebar that will be active when the toggle button is pressed. If a block // is selected, that should be edit-post/block otherwise it's edit-post/document. let sidebar = select( 'core/interface' ).getActiveComplementaryArea( - 'core/edit-post' + editPostStore.name ); if ( ! [ 'edit-post/document', 'edit-post/block' ].includes( sidebar ) @@ -49,7 +51,7 @@ const SettingsSidebar = () => { sidebar = 'edit-post/document'; } const shortcut = select( - 'core/keyboard-shortcuts' + keyboardShortcutsStore ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' ); return { sidebarName: sidebar, keyboardShortcut: shortcut }; }, [] ); diff --git a/packages/edit-post/src/components/text-editor/index.js b/packages/edit-post/src/components/text-editor/index.js index 30102330e53776..c056b72a30ea68 100644 --- a/packages/edit-post/src/components/text-editor/index.js +++ b/packages/edit-post/src/components/text-editor/index.js @@ -12,6 +12,11 @@ import { __ } from '@wordpress/i18n'; import { displayShortcut } from '@wordpress/keycodes'; import { compose } from '@wordpress/compose'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + function TextEditor( { onExit, isRichEditingEnabled } ) { return ( <div className="edit-post-text-editor"> @@ -44,7 +49,7 @@ export default compose( withDispatch( ( dispatch ) => { return { onExit() { - dispatch( 'core/edit-post' ).switchEditorMode( 'visual' ); + dispatch( editPostStore ).switchEditorMode( 'visual' ); }, }; } ) diff --git a/packages/edit-post/src/components/visual-editor/block-inspector-button.js b/packages/edit-post/src/components/visual-editor/block-inspector-button.js index 5a57dcd63e5ae7..e5e6b1e02f90a0 100644 --- a/packages/edit-post/src/components/visual-editor/block-inspector-button.js +++ b/packages/edit-post/src/components/visual-editor/block-inspector-button.js @@ -10,21 +10,27 @@ import { __ } from '@wordpress/i18n'; import { MenuItem } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { speak } from '@wordpress/a11y'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; + +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; export function BlockInspectorButton( { onClick = noop, small = false } ) { const { shortcut, areAdvancedSettingsOpened } = useSelect( ( select ) => ( { shortcut: select( - 'core/keyboard-shortcuts' + keyboardShortcutsStore ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' ), areAdvancedSettingsOpened: - select( 'core/edit-post' ).getActiveGeneralSidebarName() === + select( editPostStore ).getActiveGeneralSidebarName() === 'edit-post/block', } ), [] ); const { openGeneralSidebar, closeGeneralSidebar } = useDispatch( - 'core/edit-post' + editPostStore ); const speakMessage = () => { diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index a177981e04f175..4829c5d483de1c 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -2,8 +2,8 @@ * WordPress dependencies */ import { - PostTitle, VisualEditorGlobalKeyboardShortcuts, + PostTitle, } from '@wordpress/editor'; import { WritingFlow, @@ -15,6 +15,7 @@ import { __unstableUseScrollMultiSelectionIntoView as useScrollMultiSelectionIntoView, __experimentalBlockSettingsMenuFirstItem, __experimentalUseResizeCanvas as useResizeCanvas, + __unstableUseCanvasClickRedirect as useCanvasClickRedirect, } from '@wordpress/block-editor'; import { Popover } from '@wordpress/components'; import { useRef } from '@wordpress/element'; @@ -24,19 +25,38 @@ import { useRef } from '@wordpress/element'; */ import BlockInspectorButton from './block-inspector-button'; import { useSelect } from '@wordpress/data'; +import { store as editPostStore } from '../../store'; export default function VisualEditor() { const ref = useRef(); - const deviceType = useSelect( ( select ) => { - return select( 'core/edit-post' ).__experimentalGetPreviewDeviceType(); + const { deviceType, isTemplateMode } = useSelect( ( select ) => { + const { + isEditingTemplate, + __experimentalGetPreviewDeviceType, + } = select( editPostStore ); + return { + deviceType: __experimentalGetPreviewDeviceType(), + isTemplateMode: isEditingTemplate(), + }; }, [] ); - const inlineStyles = useResizeCanvas( deviceType ); + const hasMetaBoxes = useSelect( + ( select ) => select( editPostStore ).hasMetaBoxes(), + [] + ); + const desktopCanvasStyles = { + height: '100%', + // Add a constant padding for the typewritter effect. When typing at the + // bottom, there needs to be room to scroll up. + paddingBottom: hasMetaBoxes ? null : '40vh', + }; + const resizedCanvasStyles = useResizeCanvas( deviceType ); useScrollMultiSelectionIntoView( ref ); useBlockSelectionClearer( ref ); useTypewriter( ref ); useClipboardHandler( ref ); useTypingObserver( ref ); + useCanvasClickRedirect( ref ); return ( <div className="edit-post-visual-editor"> @@ -46,12 +66,14 @@ export default function VisualEditor() { ref={ ref } className="editor-styles-wrapper" tabIndex="-1" - style={ inlineStyles } + style={ resizedCanvasStyles || desktopCanvasStyles } > <WritingFlow> - <div className="edit-post-visual-editor__post-title-wrapper"> - <PostTitle /> - </div> + { ! isTemplateMode && ( + <div className="edit-post-visual-editor__post-title-wrapper"> + <PostTitle /> + </div> + ) } <BlockList /> </WritingFlow> </div> diff --git a/packages/edit-post/src/components/visual-editor/style.scss b/packages/edit-post/src/components/visual-editor/style.scss index 4534cdefe6886c..6894839d78b0b5 100644 --- a/packages/edit-post/src/components/visual-editor/style.scss +++ b/packages/edit-post/src/components/visual-editor/style.scss @@ -1,7 +1,5 @@ .edit-post-visual-editor { position: relative; - // Default background color so that grey .edit-post-editor-regions__content color doesn't show through. - background-color: $white; // The button element easily inherits styles that are meant for the editor style. // These rules enhance the specificity to reduce that inheritance. @@ -27,20 +25,16 @@ } } -.editor-styles-wrapper, -.editor-styles-wrapper > .block-editor-writing-flow__click-redirect { - height: 100%; -} +.editor-styles-wrapper { + // Default background color so that grey .edit-post-editor-regions__content + // color doesn't show through. + background-color: $white; -.edit-post-visual-editor .block-editor-writing-flow__click-redirect { - // Allow the page to be scrolled with the last block in the middle. - min-height: 40vh; - width: 100%; -} + cursor: text; -// Hide the extra space when there are metaboxes. -.has-metaboxes .edit-post-visual-editor .block-editor-writing-flow__click-redirect { - height: 0; + > * { + cursor: auto; + } } // Ideally this wrapper div is not needed but if we want to match the positioning of blocks @@ -58,6 +52,6 @@ // Apply default block margin below the post title. // This ensures the first block on the page is in a good position. // This rule can be retired once the title becomes an actual block. - margin-bottom: ($block-padding * 2) + $block-spacing; // This matches 2em in the vanilla style. + margin-bottom: $default-block-margin; } } diff --git a/packages/edit-post/src/components/welcome-guide/index.js b/packages/edit-post/src/components/welcome-guide/index.js index 56c30202af1729..78481c9467f86d 100644 --- a/packages/edit-post/src/components/welcome-guide/index.js +++ b/packages/edit-post/src/components/welcome-guide/index.js @@ -16,15 +16,15 @@ import { DocumentationImage, InserterIconImage, } from './images'; +import { store as editPostStore } from '../../store'; export default function WelcomeGuide() { const isActive = useSelect( - ( select ) => - select( 'core/edit-post' ).isFeatureActive( 'welcomeGuide' ), + ( select ) => select( editPostStore ).isFeatureActive( 'welcomeGuide' ), [] ); - const { toggleFeature } = useDispatch( 'core/edit-post' ); + const { toggleFeature } = useDispatch( editPostStore ); if ( ! isActive ) { return null; diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js index d7b01fb351f623..5487f88b4fd36f 100644 --- a/packages/edit-post/src/editor.js +++ b/packages/edit-post/src/editor.js @@ -1,25 +1,24 @@ /** * External dependencies */ -import memize from 'memize'; import { size, map, without, omit } from 'lodash'; /** * WordPress dependencies */ -import { withSelect, withDispatch } from '@wordpress/data'; +import { store as blocksStore } from '@wordpress/blocks'; +import { useSelect, useDispatch } from '@wordpress/data'; import { EditorProvider, ErrorBoundary, PostLockedModal, } from '@wordpress/editor'; -import { StrictMode, Component } from '@wordpress/element'; +import { StrictMode, useMemo } from '@wordpress/element'; import { KeyboardShortcuts, SlotFillProvider, - DropZoneProvider, + __unstableDropZoneContextProvider as DropZoneContextProvider, } from '@wordpress/components'; -import { compose } from '@wordpress/compose'; /** * Internal dependencies @@ -28,31 +27,78 @@ import preventEventDiscovery from './prevent-event-discovery'; import Layout from './components/layout'; import EditorInitialization from './components/editor-initialization'; import EditPostSettings from './components/edit-post-settings'; +import { store as editPostStore } from './store'; -class Editor extends Component { - constructor() { - super( ...arguments ); - - this.getEditorSettings = memize( this.getEditorSettings, { - maxSize: 1, - } ); - } - - getEditorSettings( - settings, +function Editor( { + postId, + postType, + settings, + initialEdits, + onError, + ...props +} ) { + const { hasFixedToolbar, focusMode, hasReducedUI, hasThemeStyles, + post, + preferredStyleVariations, hiddenBlockTypes, blockTypes, - preferredStyleVariations, __experimentalLocalAutosaveInterval, - __experimentalSetIsInserterOpened, - updatePreferredStyleVariations, - keepCaretInsideBlock - ) { - settings = { + keepCaretInsideBlock, + isTemplateMode, + template, + } = useSelect( ( select ) => { + const { + isFeatureActive, + getPreference, + __experimentalGetPreviewDeviceType, + isEditingTemplate, + } = select( editPostStore ); + const { getEntityRecord, __experimentalGetTemplateForLink } = select( + 'core' + ); + const { getEditorSettings, getCurrentPost } = select( 'core/editor' ); + const { getBlockTypes } = select( blocksStore ); + const postObject = getEntityRecord( 'postType', postType, postId ); + const isFSETheme = getEditorSettings().isFSETheme; + + return { + hasFixedToolbar: + isFeatureActive( 'fixedToolbar' ) || + __experimentalGetPreviewDeviceType() !== 'Desktop', + focusMode: isFeatureActive( 'focusMode' ), + hasReducedUI: isFeatureActive( 'reducedUI' ), + hasThemeStyles: isFeatureActive( 'themeStyles' ), + preferredStyleVariations: getPreference( + 'preferredStyleVariations' + ), + hiddenBlockTypes: getPreference( 'hiddenBlockTypes' ), + blockTypes: getBlockTypes(), + __experimentalLocalAutosaveInterval: getPreference( + 'localAutosaveInterval' + ), + keepCaretInsideBlock: isFeatureActive( 'keepCaretInsideBlock' ), + isTemplateMode: isEditingTemplate(), + template: + isFSETheme && + postObject && + getCurrentPost().status !== 'auto-draft' && + postType !== 'wp_template' + ? __experimentalGetTemplateForLink( postObject.link ) + : null, + post: postObject, + }; + } ); + + const { updatePreferredStyleVariations, setIsInserterOpened } = useDispatch( + editPostStore + ); + + const editorSettings = useMemo( () => { + const result = { ...( hasThemeStyles ? settings : omit( settings, [ 'defaultEditorStyles' ] ) ), @@ -66,7 +112,7 @@ class Editor extends Component { __experimentalLocalAutosaveInterval, // This is marked as experimental to give time for the quick inserter to mature. - __experimentalSetIsInserterOpened, + __experimentalSetIsInserterOpened: setIsInserterOpened, keepCaretInsideBlock, styles: hasThemeStyles ? settings.styles @@ -83,121 +129,61 @@ class Editor extends Component { ? map( blockTypes, 'name' ) : settings.allowedBlockTypes || []; - settings.allowedBlockTypes = without( + result.allowedBlockTypes = without( defaultAllowedBlockTypes, ...hiddenBlockTypes ); } - return settings; - } - - render() { - const { - settings, - hasFixedToolbar, - focusMode, - hasReducedUI, - hasThemeStyles, - post, - postId, - initialEdits, - onError, - hiddenBlockTypes, - blockTypes, - preferredStyleVariations, - __experimentalLocalAutosaveInterval, - setIsInserterOpened, - updatePreferredStyleVariations, - keepCaretInsideBlock, - ...props - } = this.props; - - if ( ! post ) { - return null; - } - - const editorSettings = this.getEditorSettings( - settings, - hasFixedToolbar, - focusMode, - hasReducedUI, - hasThemeStyles, - hiddenBlockTypes, - blockTypes, - preferredStyleVariations, - __experimentalLocalAutosaveInterval, - setIsInserterOpened, - updatePreferredStyleVariations, - keepCaretInsideBlock - ); + return result; + }, [ + settings, + hasFixedToolbar, + focusMode, + hasReducedUI, + hasThemeStyles, + hiddenBlockTypes, + blockTypes, + preferredStyleVariations, + __experimentalLocalAutosaveInterval, + setIsInserterOpened, + updatePreferredStyleVariations, + keepCaretInsideBlock, + ] ); - return ( - <StrictMode> - <EditPostSettings.Provider value={ settings }> - <SlotFillProvider> - <DropZoneProvider> - <EditorProvider - settings={ editorSettings } - post={ post } - initialEdits={ initialEdits } - useSubRegistry={ false } - { ...props } - > - <ErrorBoundary onError={ onError }> - <EditorInitialization postId={ postId } /> - <Layout /> - <KeyboardShortcuts - shortcuts={ preventEventDiscovery } - /> - </ErrorBoundary> - <PostLockedModal /> - </EditorProvider> - </DropZoneProvider> - </SlotFillProvider> - </EditPostSettings.Provider> - </StrictMode> - ); + if ( ! post ) { + return null; } -} -export default compose( [ - withSelect( ( select, { postId, postType } ) => { - const { - isFeatureActive, - getPreference, - __experimentalGetPreviewDeviceType, - } = select( 'core/edit-post' ); - const { getEntityRecord } = select( 'core' ); - const { getBlockTypes } = select( 'core/blocks' ); + return ( + <StrictMode> + <EditPostSettings.Provider value={ settings }> + <SlotFillProvider> + <DropZoneContextProvider> + <EditorProvider + settings={ editorSettings } + post={ post } + initialEdits={ initialEdits } + useSubRegistry={ false } + __unstableTemplate={ + isTemplateMode ? template : undefined + } + { ...props } + > + <ErrorBoundary onError={ onError }> + <EditorInitialization postId={ postId } /> + <Layout settings={ settings } /> + <KeyboardShortcuts + shortcuts={ preventEventDiscovery } + /> + </ErrorBoundary> + <PostLockedModal /> + </EditorProvider> + </DropZoneContextProvider> + </SlotFillProvider> + </EditPostSettings.Provider> + </StrictMode> + ); +} - return { - hasFixedToolbar: - isFeatureActive( 'fixedToolbar' ) || - __experimentalGetPreviewDeviceType() !== 'Desktop', - focusMode: isFeatureActive( 'focusMode' ), - hasReducedUI: isFeatureActive( 'reducedUI' ), - hasThemeStyles: isFeatureActive( 'themeStyles' ), - post: getEntityRecord( 'postType', postType, postId ), - preferredStyleVariations: getPreference( - 'preferredStyleVariations' - ), - hiddenBlockTypes: getPreference( 'hiddenBlockTypes' ), - blockTypes: getBlockTypes(), - __experimentalLocalAutosaveInterval: getPreference( - 'localAutosaveInterval' - ), - keepCaretInsideBlock: isFeatureActive( 'keepCaretInsideBlock' ), - }; - } ), - withDispatch( ( dispatch ) => { - const { - updatePreferredStyleVariations, - setIsInserterOpened, - } = dispatch( 'core/edit-post' ); - return { - updatePreferredStyleVariations, - setIsInserterOpened, - }; - } ), -] )( Editor ); +export default Editor; diff --git a/packages/edit-post/src/editor.native.js b/packages/edit-post/src/editor.native.js index 7dc1a6baad2335..c4c0affe8243c6 100644 --- a/packages/edit-post/src/editor.native.js +++ b/packages/edit-post/src/editor.native.js @@ -10,7 +10,12 @@ import { I18nManager } from 'react-native'; */ import { Component } from '@wordpress/element'; import { EditorProvider } from '@wordpress/editor'; -import { parse, serialize, rawHandler } from '@wordpress/blocks'; +import { + parse, + serialize, + rawHandler, + store as blocksStore, +} from '@wordpress/blocks'; import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { subscribeSetFocusOnTitle } from '@wordpress/react-native-bridge'; @@ -21,6 +26,7 @@ import { Preview } from '@wordpress/block-editor'; * Internal dependencies */ import Layout from './components/layout'; +import { store as editPostStore } from './store'; class Editor extends Component { constructor( props ) { @@ -168,8 +174,8 @@ export default compose( [ getEditorMode, getPreference, __experimentalGetPreviewDeviceType, - } = select( 'core/edit-post' ); - const { getBlockTypes } = select( 'core/blocks' ); + } = select( editPostStore ); + const { getBlockTypes } = select( blocksStore ); return { hasFixedToolbar: @@ -182,7 +188,7 @@ export default compose( [ }; } ), withDispatch( ( dispatch ) => { - const { switchEditorMode } = dispatch( 'core/edit-post' ); + const { switchEditorMode } = dispatch( editPostStore ); return { switchEditorMode, }; diff --git a/packages/edit-post/src/index.js b/packages/edit-post/src/index.js index 0e0f3d8399a09b..e7759c013ad024 100644 --- a/packages/edit-post/src/index.js +++ b/packages/edit-post/src/index.js @@ -4,9 +4,6 @@ import '@wordpress/core-data'; import '@wordpress/block-editor'; import '@wordpress/editor'; -import '@wordpress/keyboard-shortcuts'; -import '@wordpress/reusable-blocks'; -import '@wordpress/notices'; import { registerCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, @@ -18,7 +15,7 @@ import { render, unmountComponentAtNode } from '@wordpress/element'; */ import './hooks'; import './plugins'; -import './store'; +export { store } from './store'; import Editor from './editor'; /** @@ -162,3 +159,4 @@ export { default as PluginPrePublishPanel } from './components/sidebar/plugin-pr export { default as PluginSidebar } from './components/sidebar/plugin-sidebar'; export { default as PluginSidebarMoreMenuItem } from './components/header/plugin-sidebar-more-menu-item'; export { default as __experimentalFullscreenModeClose } from './components/header/fullscreen-mode-close'; +export { default as __experimentalMainDashboardButton } from './components/header/main-dashboard-button'; diff --git a/packages/edit-post/src/index.native.js b/packages/edit-post/src/index.native.js index 6d09caa3d83094..ff3a028bf719c8 100644 --- a/packages/edit-post/src/index.native.js +++ b/packages/edit-post/src/index.native.js @@ -2,15 +2,13 @@ * WordPress dependencies */ import '@wordpress/core-data'; -import '@wordpress/notices'; import '@wordpress/format-library'; -import '@wordpress/reusable-blocks'; import { render } from '@wordpress/element'; /** * Internal dependencies */ -import './store'; +export { store } from './store'; import Editor from './editor'; let editorInitialized = false; diff --git a/packages/edit-post/src/plugins/copy-content-menu-item/index.js b/packages/edit-post/src/plugins/copy-content-menu-item/index.js index a070a9f75079d5..01198d4bd57709 100644 --- a/packages/edit-post/src/plugins/copy-content-menu-item/index.js +++ b/packages/edit-post/src/plugins/copy-content-menu-item/index.js @@ -6,6 +6,7 @@ import { withDispatch, withSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { useCopyOnClick, compose, ifCondition } from '@wordpress/compose'; import { useRef, useEffect } from '@wordpress/element'; +import { store as noticesStore } from '@wordpress/notices'; function CopyContentMenuItem( { createNotice, editedPostContent } ) { const ref = useRef(); @@ -36,7 +37,7 @@ export default compose( ), } ) ), withDispatch( ( dispatch ) => { - const { createNotice } = dispatch( 'core/notices' ); + const { createNotice } = dispatch( noticesStore ); return { createNotice, diff --git a/packages/edit-post/src/plugins/keyboard-shortcuts-help-menu-item/index.js b/packages/edit-post/src/plugins/keyboard-shortcuts-help-menu-item/index.js index 73d3b09683f501..69f7b35c3f1167 100644 --- a/packages/edit-post/src/plugins/keyboard-shortcuts-help-menu-item/index.js +++ b/packages/edit-post/src/plugins/keyboard-shortcuts-help-menu-item/index.js @@ -6,6 +6,11 @@ import { withDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { displayShortcut } from '@wordpress/keycodes'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + export function KeyboardShortcutsHelpMenuItem( { openModal } ) { return ( <MenuItem @@ -20,7 +25,7 @@ export function KeyboardShortcutsHelpMenuItem( { openModal } ) { } export default withDispatch( ( dispatch ) => { - const { openModal } = dispatch( 'core/edit-post' ); + const { openModal } = dispatch( editPostStore ); return { openModal, diff --git a/packages/edit-post/src/plugins/manage-blocks-menu-item/index.js b/packages/edit-post/src/plugins/manage-blocks-menu-item/index.js index 4275f0a36988e3..64340588509274 100644 --- a/packages/edit-post/src/plugins/manage-blocks-menu-item/index.js +++ b/packages/edit-post/src/plugins/manage-blocks-menu-item/index.js @@ -5,6 +5,11 @@ import { MenuItem } from '@wordpress/components'; import { withDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + export function ManageBlocksMenuItem( { openModal } ) { return ( <MenuItem @@ -18,7 +23,7 @@ export function ManageBlocksMenuItem( { openModal } ) { } export default withDispatch( ( dispatch ) => { - const { openModal } = dispatch( 'core/edit-post' ); + const { openModal } = dispatch( editPostStore ); return { openModal, diff --git a/packages/edit-post/src/plugins/welcome-guide-menu-item/index.js b/packages/edit-post/src/plugins/welcome-guide-menu-item/index.js index de7d00cab912e4..1969311e6c2461 100644 --- a/packages/edit-post/src/plugins/welcome-guide-menu-item/index.js +++ b/packages/edit-post/src/plugins/welcome-guide-menu-item/index.js @@ -5,8 +5,13 @@ import { useDispatch } from '@wordpress/data'; import { MenuItem } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import { store as editPostStore } from '../../store'; + export default function WelcomeGuideMenuItem() { - const { toggleFeature } = useDispatch( 'core/edit-post' ); + const { toggleFeature } = useDispatch( editPostStore ); return ( <MenuItem onClick={ () => toggleFeature( 'welcomeGuide' ) }> diff --git a/packages/edit-post/src/store/actions.js b/packages/edit-post/src/store/actions.js index dfb1373bb05c51..b828ecdc54d6fb 100644 --- a/packages/edit-post/src/store/actions.js +++ b/packages/edit-post/src/store/actions.js @@ -1,13 +1,21 @@ /** * External dependencies */ -import { castArray } from 'lodash'; +import { castArray, reduce } from 'lodash'; /** * WordPress dependencies */ -import { controls } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { apiFetch } from '@wordpress/data-controls'; +import { controls, dispatch, select, subscribe } from '@wordpress/data'; +import { speak } from '@wordpress/a11y'; +/** + * Internal dependencies + */ +import { getMetaBoxContainer } from '../utils/meta-boxes'; +import { store as editPostStore } from '.'; /** * Returns an action object used in signalling that the user opened an editor sidebar. * @@ -19,7 +27,7 @@ export function* openGeneralSidebar( name ) { yield controls.dispatch( 'core/interface', 'enableComplementaryArea', - 'core/edit-post', + editPostStore.name, name ); } @@ -33,7 +41,7 @@ export function* closeGeneralSidebar() { yield controls.dispatch( 'core/interface', 'disableComplementaryArea', - 'core/edit-post' + editPostStore.name ); } @@ -153,11 +161,22 @@ export function toggleFeature( feature ) { }; } -export function switchEditorMode( mode ) { - return { +export function* switchEditorMode( mode ) { + yield { type: 'SWITCH_MODE', mode, }; + + // Unselect blocks when we switch to the code editor. + if ( mode !== 'visual' ) { + yield controls.dispatch( 'core/block-editor', 'clearSelectedBlock' ); + } + + const message = + mode === 'visual' + ? __( 'Visual editor selected' ) + : __( 'Code editor selected' ); + speak( message, 'assertive' ); } /** @@ -234,30 +253,136 @@ export function showBlockTypes( blockNames ) { }; } +let saveMetaboxUnsubscribe; + /** * Returns an action object used in signaling * what Meta boxes are available in which location. * * @param {Object} metaBoxesPerLocation Meta boxes per location. * - * @return {Object} Action object. + * @yield {Object} Action object. */ -export function setAvailableMetaBoxesPerLocation( metaBoxesPerLocation ) { - return { +export function* setAvailableMetaBoxesPerLocation( metaBoxesPerLocation ) { + yield { type: 'SET_META_BOXES_PER_LOCATIONS', metaBoxesPerLocation, }; + + const postType = yield controls.select( + 'core/editor', + 'getCurrentPostType' + ); + if ( window.postboxes.page !== postType ) { + window.postboxes.add_postbox_toggles( postType ); + } + + let wasSavingPost = yield controls.select( 'core/editor', 'isSavingPost' ); + let wasAutosavingPost = yield controls.select( + 'core/editor', + 'isAutosavingPost' + ); + + // Meta boxes are initialized once at page load. It is not necessary to + // account for updates on each state change. + // + // See: https://github.com/WordPress/WordPress/blob/5.1.1/wp-admin/includes/post.php#L2307-L2309 + const hasActiveMetaBoxes = yield controls.select( + editPostStore.name, + 'hasMetaBoxes' + ); + + // First remove any existing subscription in order to prevent multiple saves + if ( !! saveMetaboxUnsubscribe ) { + saveMetaboxUnsubscribe(); + } + + // Save metaboxes when performing a full save on the post. + saveMetaboxUnsubscribe = subscribe( () => { + const isSavingPost = select( 'core/editor' ).isSavingPost(); + const isAutosavingPost = select( 'core/editor' ).isAutosavingPost(); + + // Save metaboxes on save completion, except for autosaves that are not a post preview. + const shouldTriggerMetaboxesSave = + hasActiveMetaBoxes && + wasSavingPost && + ! isSavingPost && + ! wasAutosavingPost; + + // Save current state for next inspection. + wasSavingPost = isSavingPost; + wasAutosavingPost = isAutosavingPost; + + if ( shouldTriggerMetaboxesSave ) { + dispatch( editPostStore.name ).requestMetaBoxUpdates(); + } + } ); } /** * Returns an action object used to request meta box update. * - * @return {Object} Action object. + * @yield {Object} Action object. */ -export function requestMetaBoxUpdates() { - return { +export function* requestMetaBoxUpdates() { + yield { type: 'REQUEST_META_BOX_UPDATES', }; + + // Saves the wp_editor fields + if ( window.tinyMCE ) { + window.tinyMCE.triggerSave(); + } + + // Additional data needed for backward compatibility. + // If we do not provide this data, the post will be overridden with the default values. + const post = yield controls.select( 'core/editor', 'getCurrentPost' ); + const additionalData = [ + post.comment_status ? [ 'comment_status', post.comment_status ] : false, + post.ping_status ? [ 'ping_status', post.ping_status ] : false, + post.sticky ? [ 'sticky', post.sticky ] : false, + post.author ? [ 'post_author', post.author ] : false, + ].filter( Boolean ); + + // We gather all the metaboxes locations data and the base form data + const baseFormData = new window.FormData( + document.querySelector( '.metabox-base-form' ) + ); + const activeMetaBoxLocations = yield controls.select( + editPostStore.name, + 'getActiveMetaBoxLocations' + ); + const formDataToMerge = [ + baseFormData, + ...activeMetaBoxLocations.map( + ( location ) => + new window.FormData( getMetaBoxContainer( location ) ) + ), + ]; + + // Merge all form data objects into a single one. + const formData = reduce( + formDataToMerge, + ( memo, currentFormData ) => { + for ( const [ key, value ] of currentFormData ) { + memo.append( key, value ); + } + return memo; + }, + new window.FormData() + ); + additionalData.forEach( ( [ key, value ] ) => + formData.append( key, value ) + ); + + // Save the metaboxes + yield apiFetch( { + url: window._wpMetaBoxUrl, + method: 'POST', + body: formData, + parse: false, + } ); + yield controls.dispatch( editPostStore.name, 'metaBoxUpdatesSuccess' ); } /** @@ -297,3 +422,16 @@ export function setIsInserterOpened( value ) { value, }; } + +/** + * Returns an action object used to switch to template editing. + * + * @param {boolean} value Is editing template. + * @return {Object} Action object. + */ +export function setIsEditingTemplate( value ) { + return { + type: 'SET_IS_EDITING_TEMPLATE', + value, + }; +} diff --git a/packages/edit-post/src/store/effects.js b/packages/edit-post/src/store/effects.js deleted file mode 100644 index 0e69bd005dd6f9..00000000000000 --- a/packages/edit-post/src/store/effects.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * External dependencies - */ -import { reduce } from 'lodash'; - -/** - * WordPress dependencies - */ -import { select, subscribe, dispatch } from '@wordpress/data'; -import { speak } from '@wordpress/a11y'; -import { __ } from '@wordpress/i18n'; -import apiFetch from '@wordpress/api-fetch'; - -/** - * Internal dependencies - */ -import { metaBoxUpdatesSuccess, requestMetaBoxUpdates } from './actions'; -import { getActiveMetaBoxLocations } from './selectors'; -import { getMetaBoxContainer } from '../utils/meta-boxes'; - -let saveMetaboxUnsubscribe; - -const effects = { - SET_META_BOXES_PER_LOCATIONS( action, store ) { - // Allow toggling metaboxes panels - // We need to wait for all scripts to load - // If the meta box loads the post script, it will already trigger this. - // After merge in Core, make sure to drop the timeout and update the postboxes script - // to avoid the double binding. - setTimeout( () => { - const postType = select( 'core/editor' ).getCurrentPostType(); - if ( window.postboxes.page !== postType ) { - window.postboxes.add_postbox_toggles( postType ); - } - } ); - - let wasSavingPost = select( 'core/editor' ).isSavingPost(); - let wasAutosavingPost = select( 'core/editor' ).isAutosavingPost(); - - // Meta boxes are initialized once at page load. It is not necessary to - // account for updates on each state change. - // - // See: https://github.com/WordPress/WordPress/blob/5.1.1/wp-admin/includes/post.php#L2307-L2309 - const hasActiveMetaBoxes = select( 'core/edit-post' ).hasMetaBoxes(); - - // First remove any existing subscription in order to prevent multiple saves - if ( !! saveMetaboxUnsubscribe ) { - saveMetaboxUnsubscribe(); - } - - // Save metaboxes when performing a full save on the post. - saveMetaboxUnsubscribe = subscribe( () => { - const isSavingPost = select( 'core/editor' ).isSavingPost(); - const isAutosavingPost = select( 'core/editor' ).isAutosavingPost(); - - // Save metaboxes on save completion, except for autosaves that are not a post preview. - const shouldTriggerMetaboxesSave = - hasActiveMetaBoxes && - wasSavingPost && - ! isSavingPost && - ! wasAutosavingPost; - - // Save current state for next inspection. - wasSavingPost = isSavingPost; - wasAutosavingPost = isAutosavingPost; - - if ( shouldTriggerMetaboxesSave ) { - store.dispatch( requestMetaBoxUpdates() ); - } - } ); - }, - REQUEST_META_BOX_UPDATES( action, store ) { - // Saves the wp_editor fields - if ( window.tinyMCE ) { - window.tinyMCE.triggerSave(); - } - - const state = store.getState(); - - // Additional data needed for backward compatibility. - // If we do not provide this data, the post will be overridden with the default values. - const post = select( 'core/editor' ).getCurrentPost( state ); - const additionalData = [ - post.comment_status - ? [ 'comment_status', post.comment_status ] - : false, - post.ping_status ? [ 'ping_status', post.ping_status ] : false, - post.sticky ? [ 'sticky', post.sticky ] : false, - post.author ? [ 'post_author', post.author ] : false, - ].filter( Boolean ); - - // We gather all the metaboxes locations data and the base form data - const baseFormData = new window.FormData( - document.querySelector( '.metabox-base-form' ) - ); - const formDataToMerge = [ - baseFormData, - ...getActiveMetaBoxLocations( state ).map( - ( location ) => - new window.FormData( getMetaBoxContainer( location ) ) - ), - ]; - - // Merge all form data objects into a single one. - const formData = reduce( - formDataToMerge, - ( memo, currentFormData ) => { - for ( const [ key, value ] of currentFormData ) { - memo.append( key, value ); - } - return memo; - }, - new window.FormData() - ); - additionalData.forEach( ( [ key, value ] ) => - formData.append( key, value ) - ); - - // Save the metaboxes - apiFetch( { - url: window._wpMetaBoxUrl, - method: 'POST', - body: formData, - parse: false, - } ).then( () => store.dispatch( metaBoxUpdatesSuccess() ) ); - }, - SWITCH_MODE( action ) { - // Unselect blocks when we switch to the code editor. - if ( action.mode !== 'visual' ) { - dispatch( 'core/block-editor' ).clearSelectedBlock(); - } - - const message = - action.mode === 'visual' - ? __( 'Visual editor selected' ) - : __( 'Code editor selected' ); - speak( message, 'assertive' ); - }, -}; - -export default effects; diff --git a/packages/edit-post/src/store/index.js b/packages/edit-post/src/store/index.js index 7d08516f251a42..c461e54c601fc6 100644 --- a/packages/edit-post/src/store/index.js +++ b/packages/edit-post/src/store/index.js @@ -2,12 +2,12 @@ * WordPress dependencies */ import { createReduxStore, registerStore } from '@wordpress/data'; +import { controls } from '@wordpress/data-controls'; /** * Internal dependencies */ import reducer from './reducer'; -import applyMiddlewares from './middlewares'; import * as actions from './actions'; import * as selectors from './selectors'; import { STORE_NAME } from './constants'; @@ -16,6 +16,7 @@ const storeConfig = { reducer, actions, selectors, + controls, persist: [ 'preferences' ], }; @@ -29,6 +30,4 @@ const storeConfig = { export const store = createReduxStore( STORE_NAME, storeConfig ); // Ideally we use register instead of register store. -// We shouuld be able to make the switch once we remove the effects. -const instantiatedStore = registerStore( STORE_NAME, storeConfig ); -applyMiddlewares( instantiatedStore ); +registerStore( STORE_NAME, storeConfig ); diff --git a/packages/edit-post/src/store/middlewares.js b/packages/edit-post/src/store/middlewares.js deleted file mode 100644 index 06cacf026d58c2..00000000000000 --- a/packages/edit-post/src/store/middlewares.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * External dependencies - */ -import { flowRight } from 'lodash'; -import refx from 'refx'; - -/** - * Internal dependencies - */ -import effects from './effects'; - -/** - * Applies the custom middlewares used specifically in the editor module. - * - * @param {Object} store Store Object. - * - * @return {Object} Update Store Object. - */ -function applyMiddlewares( store ) { - const middlewares = [ refx( effects ) ]; - - let enhancedDispatch = () => { - throw new Error( - 'Dispatching while constructing your middleware is not allowed. ' + - 'Other middleware would not be applied to this dispatch.' - ); - }; - let chain = []; - - const middlewareAPI = { - getState: store.getState, - dispatch: ( ...args ) => enhancedDispatch( ...args ), - }; - chain = middlewares.map( ( middleware ) => middleware( middlewareAPI ) ); - enhancedDispatch = flowRight( ...chain )( store.dispatch ); - - store.dispatch = enhancedDispatch; - return store; -} - -export default applyMiddlewares; diff --git a/packages/edit-post/src/store/reducer.js b/packages/edit-post/src/store/reducer.js index 934de97aa73c08..1f1e762f728135 100644 --- a/packages/edit-post/src/store/reducer.js +++ b/packages/edit-post/src/store/reducer.js @@ -252,6 +252,20 @@ function isInserterOpened( state = false, action ) { return state; } +/** + * Reducer tracking whether the inserter is open. + * + * @param {boolean} state + * @param {Object} action + */ +function isEditingTemplate( state = false, action ) { + switch ( action.type ) { + case 'SET_IS_EDITING_TEMPLATE': + return action.value; + } + return state; +} + const metaBoxes = combineReducers( { isSaving: isSavingMetaBoxes, locations: metaBoxLocations, @@ -265,4 +279,5 @@ export default combineReducers( { removedPanels, deviceType, isInserterOpened, + isEditingTemplate, } ); diff --git a/packages/edit-post/src/store/selectors.js b/packages/edit-post/src/store/selectors.js index 68dcc83671ed00..69d1ded82cf568 100644 --- a/packages/edit-post/src/store/selectors.js +++ b/packages/edit-post/src/store/selectors.js @@ -324,3 +324,14 @@ export function __experimentalGetPreviewDeviceType( state ) { export function isInserterOpened( state ) { return state.isInserterOpened; } + +/** + * Returns true if the template editing mode is enabled. + * + * @param {Object} state Global application state. + * + * @return {boolean} Whether we're editing the template. + */ +export function isEditingTemplate( state ) { + return state.isEditingTemplate; +} diff --git a/packages/edit-post/src/store/test/actions.js b/packages/edit-post/src/store/test/actions.js index 31877f378bb286..fc398c3aeb2117 100644 --- a/packages/edit-post/src/store/test/actions.js +++ b/packages/edit-post/src/store/test/actions.js @@ -1,3 +1,8 @@ +/** + * WordPress dependencies + */ +import { controls } from '@wordpress/data'; + /** * Internal dependencies */ @@ -95,9 +100,17 @@ describe( 'actions', () => { } ); describe( 'requestMetaBoxUpdates', () => { - it( 'should return the REQUEST_META_BOX_UPDATES action', () => { - expect( requestMetaBoxUpdates() ).toEqual( { - type: 'REQUEST_META_BOX_UPDATES', + it( 'should yield the REQUEST_META_BOX_UPDATES action', () => { + const fulfillment = requestMetaBoxUpdates(); + expect( fulfillment.next() ).toEqual( { + done: false, + value: { + type: 'REQUEST_META_BOX_UPDATES', + }, + } ); + expect( fulfillment.next() ).toEqual( { + done: false, + value: controls.select( 'core/editor', 'getCurrentPost' ), } ); } ); } ); diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss index cc64fba010a678..f902e64e5848ba 100644 --- a/packages/edit-post/src/style.scss +++ b/packages/edit-post/src/style.scss @@ -3,6 +3,7 @@ @import "./components/header/fullscreen-mode-close/style.scss"; @import "./components/header/header-toolbar/style.scss"; @import "./components/header/more-menu/style.scss"; +@import "./components/header/template-title/style.scss"; @import "./components/keyboard-shortcut-help-modal/style.scss"; @import "./components/layout/style.scss"; @import "./components/manage-blocks-modal/style.scss"; @@ -15,6 +16,7 @@ @import "./components/sidebar/post-schedule/style.scss"; @import "./components/sidebar/post-slug/style.scss"; @import "./components/sidebar/post-status/style.scss"; +@import "./components/sidebar/post-template/style.scss"; @import "./components/sidebar/post-visibility/style.scss"; @import "./components/sidebar/settings-header/style.scss"; @import "./components/text-editor/style.scss"; @@ -68,12 +70,12 @@ body.block-editor-page { right: 0; bottom: 0; left: 0; - min-height: calc(100vh - #{ $admin-bar-height-big }); + min-height: calc(100vh - #{$admin-bar-height-big}); } // The WP header height changes at this breakpoint. @include break-medium { - min-height: calc(100vh - #{ $admin-bar-height }); + min-height: calc(100vh - #{$admin-bar-height}); body.is-fullscreen-mode & { min-height: 100vh; diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index c9b20a337e0174..5c6290a0d46f23 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-site", - "version": "1.15.4", + "version": "1.16.0", "description": "Edit Site Page module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:../a11y", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/block-editor": "file:../block-editor", diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js index 4e88b651e09c51..dccde836ecabf6 100644 --- a/packages/edit-site/src/components/block-editor/index.js +++ b/packages/edit-site/src/components/block-editor/index.js @@ -24,12 +24,12 @@ import { SidebarInspectorFill } from '../sidebar'; export default function BlockEditor( { setIsInserterOpen } ) { const { settings, templateType, page } = useSelect( ( select ) => { - const { getSettings, getTemplateType, getPage } = select( + const { getSettings, getEditedPostType, getPage } = select( 'core/edit-site' ); return { settings: getSettings( setIsInserterOpen ), - templateType: getTemplateType(), + templateType: getEditedPostType(), page: getPage(), }; }, diff --git a/packages/edit-site/src/components/editor/global-styles-provider.js b/packages/edit-site/src/components/editor/global-styles-provider.js index 544bd7f20eb2e5..db62624c076775 100644 --- a/packages/edit-site/src/components/editor/global-styles-provider.js +++ b/packages/edit-site/src/components/editor/global-styles-provider.js @@ -13,19 +13,24 @@ import { useEffect, useMemo, } from '@wordpress/element'; -import { __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY } from '@wordpress/blocks'; +import { + __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, + store as blocksStore, +} from '@wordpress/blocks'; import { useEntityProp } from '@wordpress/core-data'; import { useSelect, useDispatch } from '@wordpress/data'; /** * Internal dependencies */ -import { default as getGlobalStyles } from './global-styles-renderer'; import { - GLOBAL_CONTEXT, + GLOBAL_CONTEXT_NAME, + GLOBAL_CONTEXT_SELECTOR, + GLOBAL_CONTEXT_SUPPORTS, getValueFromVariable, getPresetVariable, } from './utils'; +import getGlobalStyles from './global-styles-renderer'; const EMPTY_CONTENT = '{}'; @@ -75,7 +80,10 @@ const extractSupportKeys = ( supports ) => { const getContexts = ( blockTypes ) => { const result = { - ...GLOBAL_CONTEXT, + [ GLOBAL_CONTEXT_NAME ]: { + selector: GLOBAL_CONTEXT_SELECTOR, + supports: GLOBAL_CONTEXT_SUPPORTS, + }, }; // Add contexts from block metadata. @@ -118,7 +126,7 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { const [ content, setContent ] = useGlobalStylesEntityContent(); const { blockTypes, settings } = useSelect( ( select ) => { return { - blockTypes: select( 'core/blocks' ).getBlockTypes(), + blockTypes: select( blocksStore ).getBlockTypes(), settings: select( 'core/edit-site' ).getSettings(), }; } ); @@ -200,7 +208,18 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { css: getGlobalStyles( contexts, mergedStyles, - STYLE_PROPERTY + STYLE_PROPERTY, + 'cssVariables' + ), + isGlobalStyles: true, + __experimentalNoWrapper: true, + }, + { + css: getGlobalStyles( + contexts, + mergedStyles, + STYLE_PROPERTY, + 'blockStyles' ), isGlobalStyles: true, }, diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js index e0dc01f4c9777a..9fb1fb457d762f 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -6,11 +6,7 @@ import { get, kebabCase, reduce, startsWith } from 'lodash'; /** * Internal dependencies */ -import { - PRESET_CATEGORIES, - PRESET_CLASSES, - LINK_COLOR_DECLARATION, -} from './utils'; +import { LINK_COLOR_DECLARATION, PRESET_METADATA } from './utils'; function compileStyleValue( uncompiledValue ) { const VARIABLE_REFERENCE_PREFIX = 'var:'; @@ -26,141 +22,160 @@ function compileStyleValue( uncompiledValue ) { return uncompiledValue; } -export default ( blockData, tree, metadata ) => { - const styles = []; - // Can this be converted to a context, as the global context? - // See comment in the server. - styles.push( LINK_COLOR_DECLARATION ); - - /** - * Transform given style tree into a set of style declarations. - * - * @param {Object} blockSupports What styles the block supports. - * @param {Object} blockStyles Block styles. - * - * @return {Array} An array of style declarations. - */ - const getBlockStylesDeclarations = ( blockSupports, blockStyles = {} ) => { - const declarations = []; - Object.keys( metadata ).forEach( ( key ) => { - const cssProperty = key.startsWith( '--' ) ? key : kebabCase( key ); - if ( - blockSupports.includes( key ) && - get( blockStyles, metadata[ key ].value, false ) - ) { +/** + * Transform given preset tree into a set of style declarations. + * + * @param {Object} blockPresets + * + * @return {Array} An array of style declarations. + */ +function getBlockPresetsDeclarations( blockPresets = {} ) { + return reduce( + PRESET_METADATA, + ( declarations, { path, valueKey, cssVarInfix } ) => { + const preset = get( blockPresets, path, [] ); + preset.forEach( ( value ) => { declarations.push( - `${ cssProperty }: ${ compileStyleValue( - get( blockStyles, metadata[ key ].value ) - ) }` + `--wp--preset--${ cssVarInfix }--${ value.slug }: ${ value[ valueKey ] }` ); - } - } ); - - return declarations; - }; + } ); + return declarations; + }, + [] + ); +} - /** - * Transform given preset tree into a set of preset class declarations. - * - * @param {string} blockSelector - * @param {Object} blockPresets - * @return {string} CSS declarations for the preset classes. - */ - const getBlockPresetClasses = ( blockSelector, blockPresets = {} ) => { - return reduce( - PRESET_CLASSES, - ( declarations, { path, key, property }, classSuffix ) => { +/** + * Transform given preset tree into a set of preset class declarations. + * + * @param {string} blockSelector + * @param {Object} blockPresets + * @return {string} CSS declarations for the preset classes. + */ +function getBlockPresetClasses( blockSelector, blockPresets = {} ) { + return reduce( + PRESET_METADATA, + ( declarations, { path, valueKey, classes } ) => { + if ( ! classes ) { + return declarations; + } + classes.forEach( ( { classSuffix, propertyName } ) => { const presets = get( blockPresets, path, [] ); presets.forEach( ( preset ) => { const slug = preset.slug; - const value = preset[ key ]; + const value = preset[ valueKey ]; const classSelectorToUse = `.has-${ slug }-${ classSuffix }`; const selectorToUse = `${ blockSelector }${ classSelectorToUse }`; - declarations += `${ selectorToUse } {${ property }: ${ value };}`; - } ); - return declarations; - }, - '' - ); - }; - - /** - * Transform given preset tree into a set of style declarations. - * - * @param {Object} blockPresets - * - * @return {Array} An array of style declarations. - */ - const getBlockPresetsDeclarations = ( blockPresets = {} ) => { - return reduce( - PRESET_CATEGORIES, - ( declarations, { path, key }, category ) => { - const preset = get( blockPresets, path, [] ); - preset.forEach( ( value ) => { - declarations.push( - `--wp--preset--${ kebabCase( category ) }--${ - value.slug - }: ${ value[ key ] }` - ); + declarations += `${ selectorToUse } {${ propertyName }: ${ value };}`; } ); - return declarations; - }, - [] - ); - }; - - const flattenTree = ( input, prefix, token ) => { - let result = []; - Object.keys( input ).forEach( ( key ) => { - const newKey = prefix + kebabCase( key.replace( '/', '-' ) ); - const newLeaf = input[ key ]; + } ); + return declarations; + }, + '' + ); +} - if ( newLeaf instanceof Object ) { - const newPrefix = newKey + token; - result = [ - ...result, - ...flattenTree( newLeaf, newPrefix, token ), - ]; - } else { - result.push( `${ newKey }: ${ newLeaf }` ); - } - } ); - return result; - }; +function flattenTree( input = {}, prefix, token ) { + let result = []; + Object.keys( input ).forEach( ( key ) => { + const newKey = prefix + kebabCase( key.replace( '/', '-' ) ); + const newLeaf = input[ key ]; - const getCustomDeclarations = ( blockCustom = {} ) => { - if ( Object.keys( blockCustom ).length === 0 ) { - return []; + if ( newLeaf instanceof Object ) { + const newPrefix = newKey + token; + result = [ ...result, ...flattenTree( newLeaf, newPrefix, token ) ]; + } else { + result.push( `${ newKey }: ${ newLeaf }` ); } + } ); + return result; +} - return flattenTree( blockCustom, '--wp--custom--', '--' ); - }; +/** + * Transform given style tree into a set of style declarations. + * + * @param {Object} blockSupports What styles the block supports. + * @param {Object} blockStyles Block styles. + * @param {Object} metadata Block styles metadata information. + * + * @return {Array} An array of style declarations. + */ +function getBlockStylesDeclarations( + blockSupports, + blockStyles = {}, + metadata +) { + return reduce( + metadata, + ( declarations, { value }, key ) => { + const cssProperty = key.startsWith( '--' ) ? key : kebabCase( key ); + if ( + blockSupports.includes( key ) && + get( blockStyles, value, false ) + ) { + declarations.push( + `${ cssProperty }: ${ compileStyleValue( + get( blockStyles, value ) + ) }` + ); + } + return declarations; + }, + [] + ); +} - Object.keys( blockData ).forEach( ( context ) => { - const blockSelector = blockData[ context ].selector; +export default ( blockData, tree, metadata, type = 'all' ) => { + return reduce( + blockData, + ( styles, { selector }, context ) => { + if ( type === 'all' || type === 'cssVariables' ) { + const variableDeclarations = [ + ...getBlockPresetsDeclarations( tree?.[ context ] ), + ...flattenTree( + tree?.[ context ]?.settings?.custom, + '--wp--custom--', + '--' + ), + ]; - const blockDeclarations = [ - ...getBlockStylesDeclarations( - blockData[ context ].supports, - tree?.[ context ]?.styles - ), - ...getBlockPresetsDeclarations( tree?.[ context ]?.settings ), - ...getCustomDeclarations( tree?.[ context ]?.settings?.custom ), - ]; - if ( blockDeclarations.length > 0 ) { - styles.push( - `${ blockSelector } { ${ blockDeclarations.join( ';' ) } }` - ); - } + if ( variableDeclarations.length > 0 ) { + styles.push( + `${ selector } { ${ variableDeclarations.join( + ';' + ) } }` + ); + } + } + if ( type === 'all' || type === 'blockStyles' ) { + const blockStyleDeclarations = getBlockStylesDeclarations( + blockData[ context ].supports, + tree?.[ context ]?.styles, + metadata + ); - const presetClasses = getBlockPresetClasses( - blockSelector, - tree?.[ context ]?.settings - ); - if ( presetClasses ) { - styles.push( presetClasses ); - } - } ); + if ( blockStyleDeclarations.length > 0 ) { + styles.push( + `${ selector } { ${ blockStyleDeclarations.join( + ';' + ) } }` + ); + } - return styles.join( '' ); + const presetClasses = getBlockPresetClasses( + selector, + tree?.[ context ] + ); + if ( presetClasses ) { + styles.push( presetClasses ); + } + } + return styles; + }, + // Can this be converted to a context, as the global context? + // See comment in the server. + type === 'all' || type === 'blockStyles' + ? [ LINK_COLOR_DECLARATION ] + : [] + ).join( '' ); }; diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 93067f58455a2d..964390eee4de5d 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -1,13 +1,18 @@ /** * WordPress dependencies */ -import { useEffect, useState, useMemo, useCallback } from '@wordpress/element'; +import { + useEffect, + useState, + useMemo, + useCallback, + useRef, +} from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; import { SlotFillProvider, DropZoneProvider, Popover, - FocusReturnProvider, Button, } from '@wordpress/components'; import { EntityProvider } from '@wordpress/core-data'; @@ -15,7 +20,7 @@ import { BlockContextProvider, BlockSelectionClearer, BlockBreadcrumb, - __unstableEditorStyles as EditorStyles, + __unstableUseEditorStyles as useEditorStyles, __experimentalUseResizeCanvas as useResizeCanvas, __experimentalLibrary as Library, } from '@wordpress/block-editor'; @@ -28,7 +33,10 @@ import { EntitiesSavedStates, UnsavedChangesWarning } from '@wordpress/editor'; import { __ } from '@wordpress/i18n'; import { PluginArea } from '@wordpress/plugins'; import { close } from '@wordpress/icons'; -import { useViewportMatch } from '@wordpress/compose'; +import { + useViewportMatch, + __experimentalUseDialog as useDialog, +} from '@wordpress/compose'; /** * Internal dependencies @@ -40,6 +48,7 @@ import BlockEditor from '../block-editor'; import KeyboardShortcuts from '../keyboard-shortcuts'; import GlobalStylesProvider from './global-styles-provider'; import NavigationSidebar from '../navigation-sidebar'; +import URLQueryController from '../url-query-controller'; const interfaceLabels = { secondarySidebar: __( 'Block Library' ), @@ -57,54 +66,43 @@ function Editor() { templateType, page, template, - select, isNavigationOpen, - } = useSelect( ( _select ) => { + } = useSelect( ( select ) => { const { isFeatureActive, isInserterOpened, __experimentalGetPreviewDeviceType, getSettings, - getTemplateId, - getTemplatePartId, - getTemplateType, + getEditedPostType, + getEditedPostId, getPage, isNavigationOpened, - } = _select( 'core/edit-site' ); - const _templateId = getTemplateId(); - const _templatePartId = getTemplatePartId(); - const _templateType = getTemplateType(); + } = select( 'core/edit-site' ); + const postType = getEditedPostType(); + const postId = getEditedPostId(); // The currently selected entity to display. Typically template or template part. - let _entityId; - if ( _templateType ) { - _entityId = - _templateType === 'wp_template' ? _templateId : _templatePartId; - } - return { isInserterOpen: isInserterOpened(), isFullscreenActive: isFeatureActive( 'fullscreenMode' ), deviceType: __experimentalGetPreviewDeviceType(), - sidebarIsOpened: !! _select( + sidebarIsOpened: !! select( 'core/interface' ).getActiveComplementaryArea( 'core/edit-site' ), settings: getSettings(), - templateType: _templateType, + templateType: postType, page: getPage(), - template: _templateType - ? _select( 'core' ).getEntityRecord( + template: postId + ? select( 'core' ).getEntityRecord( 'postType', - _templateType, - _entityId + postType, + postId ) : null, - select: _select, - entityId: _entityId, + entityId: postId, isNavigationOpen: isNavigationOpened(), }; }, [] ); - const { editEntityRecord } = useDispatch( 'core' ); const { updateEditorSettings } = useDispatch( 'core/editor' ); const { setPage, setIsInserterOpened } = useDispatch( 'core/edit-site' ); @@ -127,35 +125,9 @@ function Editor() { () => setIsEntitiesSavedStatesOpen( true ), [] ); - const closeEntitiesSavedStates = useCallback( - ( entitiesToSave ) => { - if ( entitiesToSave ) { - const { getEditedEntityRecord } = select( 'core' ); - const { - __experimentalGetTemplateInfo: getTemplateInfo, - } = select( 'core/editor' ); - entitiesToSave.forEach( ( { kind, name, key } ) => { - const record = getEditedEntityRecord( kind, name, key ); - - if ( 'postType' === kind && name === 'wp_template' ) { - const { title } = getTemplateInfo( record ); - return editEntityRecord( kind, name, key, { - status: 'publish', - title, - } ); - } - - const edits = record.slug - ? { status: 'publish', title: record.slug } - : { status: 'publish' }; - - editEntityRecord( kind, name, key, edits ); - } ); - } - setIsEntitiesSavedStatesOpen( false ); - }, - [ select ] - ); + const closeEntitiesSavedStates = useCallback( () => { + setIsEntitiesSavedStatesOpen( false ); + }, [] ); // Set default query for misplaced Query Loop blocks, and // provide the root `queryContext` for top-level Query Loop @@ -191,10 +163,17 @@ function Editor() { }, [ isNavigationOpen ] ); const isMobile = useViewportMatch( 'medium', '<' ); + const ref = useRef(); + + useEditorStyles( ref, settings.styles ); + + const [ inserterDialogRef, inserterDialogProps ] = useDialog( { + onClose: () => setIsInserterOpened( false ), + } ); return ( <> - <EditorStyles styles={ settings.styles } /> + <URLQueryController /> <FullscreenMode isActive={ isFullscreenActive } /> <UnsavedChangesWarning /> <SlotFillProvider> @@ -202,146 +181,129 @@ function Editor() { <EntityProvider kind="root" type="site"> <EntityProvider kind="postType" - type={ 'wp_template' } - id={ - templateType === 'wp_template' ? entityId : null - } + type={ templateType } + id={ entityId } > <EntityProvider kind="postType" - type="wp_template_part" + type="wp_global_styles" id={ - templateType === 'wp_template_part' - ? entityId - : null + settings.__experimentalGlobalStylesUserEntityId } > - <EntityProvider - kind="postType" - type="wp_global_styles" - id={ - settings.__experimentalGlobalStylesUserEntityId - } - > - <BlockContextProvider - value={ blockContext } + <BlockContextProvider value={ blockContext }> + <GlobalStylesProvider + baseStyles={ + settings.__experimentalGlobalStylesBaseStyles + } > - <FocusReturnProvider> - <GlobalStylesProvider - baseStyles={ - settings.__experimentalGlobalStylesBaseStyles - } - > - <KeyboardShortcuts.Register /> - <SidebarComplementaryAreaFills /> - <InterfaceSkeleton - labels={ interfaceLabels } - drawer={ - <NavigationSidebar /> - } - secondarySidebar={ - isInserterOpen ? ( - <div className="edit-site-editor__inserter-panel"> - <div className="edit-site-editor__inserter-panel-header"> - <Button - icon={ - close - } - onClick={ () => - setIsInserterOpened( - false - ) - } - /> - </div> - <div className="edit-site-editor__inserter-panel-content"> - <Library - showInserterHelpPanel - onSelect={ () => { - if ( - isMobile - ) { - setIsInserterOpened( - false - ); - } - } } - /> - </div> - </div> - ) : null - } - sidebar={ - sidebarIsOpened && ( - <ComplementaryArea.Slot scope="core/edit-site" /> - ) - } - header={ - <Header - openEntitiesSavedStates={ - openEntitiesSavedStates - } - /> - } - content={ - <BlockSelectionClearer - className="edit-site-visual-editor" - style={ - inlineStyles - } - > - <Notices /> - <Popover.Slot name="block-toolbar" /> - { template && ( - <BlockEditor - setIsInserterOpen={ - setIsInserterOpened - } - /> - ) } - <KeyboardShortcuts /> - </BlockSelectionClearer> - } - actions={ - <> - <EntitiesSavedStates - isOpen={ - isEntitiesSavedStatesOpen - } - close={ - closeEntitiesSavedStates + <KeyboardShortcuts.Register /> + <SidebarComplementaryAreaFills /> + <InterfaceSkeleton + ref={ ref } + labels={ interfaceLabels } + drawer={ <NavigationSidebar /> } + secondarySidebar={ + isInserterOpen ? ( + <div + ref={ + inserterDialogRef + } + { ...inserterDialogProps } + className="edit-site-editor__inserter-panel" + > + <div className="edit-site-editor__inserter-panel-header"> + <Button + icon={ close } + onClick={ () => + setIsInserterOpened( + false + ) } /> - { ! isEntitiesSavedStatesOpen && ( - <div className="edit-site-editor__toggle-save-panel"> - <Button - isSecondary - className="edit-site-editor__toggle-save-panel-button" - onClick={ - openEntitiesSavedStates - } - aria-expanded={ + </div> + <div className="edit-site-editor__inserter-panel-content"> + <Library + showInserterHelpPanel + onSelect={ () => { + if ( + isMobile + ) { + setIsInserterOpened( false - } - > - { __( - 'Open save panel' - ) } - </Button> - </div> - ) } - </> - } - footer={ - <BlockBreadcrumb /> + ); + } + } } + /> + </div> + </div> + ) : null + } + sidebar={ + sidebarIsOpened && ( + <ComplementaryArea.Slot scope="core/edit-site" /> + ) + } + header={ + <Header + openEntitiesSavedStates={ + openEntitiesSavedStates } /> - <Popover.Slot /> - <PluginArea /> - </GlobalStylesProvider> - </FocusReturnProvider> - </BlockContextProvider> - </EntityProvider> + } + content={ + <BlockSelectionClearer + className="edit-site-visual-editor" + style={ inlineStyles } + > + <Notices /> + <Popover.Slot name="block-toolbar" /> + { template && ( + <BlockEditor + setIsInserterOpen={ + setIsInserterOpened + } + /> + ) } + <KeyboardShortcuts /> + </BlockSelectionClearer> + } + actions={ + <> + <EntitiesSavedStates + isOpen={ + isEntitiesSavedStatesOpen + } + close={ + closeEntitiesSavedStates + } + /> + { ! isEntitiesSavedStatesOpen && ( + <div className="edit-site-editor__toggle-save-panel"> + <Button + isSecondary + className="edit-site-editor__toggle-save-panel-button" + onClick={ + openEntitiesSavedStates + } + aria-expanded={ + false + } + > + { __( + 'Open save panel' + ) } + </Button> + </div> + ) } + </> + } + footer={ <BlockBreadcrumb /> } + /> + <Popover.Slot /> + <PluginArea /> + </GlobalStylesProvider> + </BlockContextProvider> </EntityProvider> </EntityProvider> </EntityProvider> diff --git a/packages/edit-site/src/components/editor/style.scss b/packages/edit-site/src/components/editor/style.scss index 66c0a582f5760f..cb8048a51cd5c4 100644 --- a/packages/edit-site/src/components/editor/style.scss +++ b/packages/edit-site/src/components/editor/style.scss @@ -21,6 +21,7 @@ } .edit-site-visual-editor { + position: relative; background-color: $white; } diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/editor/utils.js index 88b5a6952f64c9..1bd54dc32d675b 100644 --- a/packages/edit-site/src/components/editor/utils.js +++ b/packages/edit-site/src/components/editor/utils.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { get, find, camelCase, kebabCase, isString } from 'lodash'; +import { get, find, forEach, camelCase, isString } from 'lodash'; /** * WordPress dependencies */ @@ -10,57 +10,79 @@ import { useSelect } from '@wordpress/data'; /* Supporting data */ export const GLOBAL_CONTEXT_NAME = 'global'; export const GLOBAL_CONTEXT_SELECTOR = ':root'; -export const GLOBAL_CONTEXT = { - [ GLOBAL_CONTEXT_NAME ]: { - selector: GLOBAL_CONTEXT_SELECTOR, - supports: [ - '--wp--style--color--link', - 'background', - 'backgroundColor', - 'color', - 'fontFamily', - 'fontSize', - 'fontStyle', - 'fontWeight', - 'lineHeight', - 'textDecoration', - 'textTransform', +export const GLOBAL_CONTEXT_SUPPORTS = [ + '--wp--style--color--link', + 'background', + 'backgroundColor', + 'color', + 'fontFamily', + 'fontSize', + 'fontStyle', + 'fontWeight', + 'lineHeight', + 'textDecoration', + 'textTransform', +]; + +export const PRESET_METADATA = [ + { + path: [ 'settings', 'color', 'palette' ], + valueKey: 'color', + cssVarInfix: 'color', + classes: [ + { classSuffix: 'color', propertyName: 'color' }, + { + classSuffix: 'background-color', + propertyName: 'background-color', + }, ], }, -}; - -export const PRESET_CATEGORIES = { - color: { path: [ 'color', 'palette' ], key: 'color' }, - gradient: { path: [ 'color', 'gradients' ], key: 'gradient' }, - fontSize: { path: [ 'typography', 'fontSizes' ], key: 'size' }, - fontFamily: { path: [ 'typography', 'fontFamilies' ], key: 'fontFamily' }, - fontStyle: { path: [ 'typography', 'fontStyles' ], key: 'slug' }, - fontWeight: { path: [ 'typography', 'fontWeights' ], key: 'slug' }, - textDecoration: { path: [ 'typography', 'textDecorations' ], key: 'value' }, - textTransform: { path: [ 'typography', 'textTransforms' ], key: 'slug' }, -}; -export const PRESET_CLASSES = { - color: { ...PRESET_CATEGORIES.color, property: 'color' }, - 'background-color': { - ...PRESET_CATEGORIES.color, - property: 'background-color', + { + path: [ 'settings', 'color', 'gradients' ], + valueKey: 'gradient', + cssVarInfix: 'gradient', + classes: [ + { + classSuffix: 'gradient-background', + propertyName: 'background', + }, + ], }, - 'gradient-background': { - ...PRESET_CATEGORIES.gradient, - property: 'background', + { + path: [ 'settings', 'typography', 'fontSizes' ], + valueKey: 'size', + cssVarInfix: 'font-size', + classes: [ { classSuffix: 'font-size', propertyName: 'font-size' } ], }, - 'font-size': { - ...PRESET_CATEGORIES.fontSize, - property: 'font-size', + { + path: [ 'settings', 'typography', 'fontFamilies' ], + valueKey: 'fontFamily', + cssVarInfix: 'font-family', + classes: [], }, -}; +]; -const STYLE_PROPERTIES_TO_PRESETS = { +const STYLE_PROPERTIES_TO_CSS_VAR_INFIX = { backgroundColor: 'color', LINK_COLOR: 'color', background: 'gradient', }; +function getPresetMetadataFromStyleProperty( styleProperty ) { + if ( ! getPresetMetadataFromStyleProperty.MAP ) { + getPresetMetadataFromStyleProperty.MAP = {}; + PRESET_METADATA.forEach( ( { cssVarInfix }, index ) => { + getPresetMetadataFromStyleProperty.MAP[ camelCase( cssVarInfix ) ] = + PRESET_METADATA[ index ]; + } ); + forEach( STYLE_PROPERTIES_TO_CSS_VAR_INFIX, ( value, key ) => { + getPresetMetadataFromStyleProperty.MAP[ key ] = + getPresetMetadataFromStyleProperty.MAP[ value ]; + } ); + } + return getPresetMetadataFromStyleProperty.MAP[ styleProperty ]; +} + export const LINK_COLOR = '--wp--style--color--link'; export const LINK_COLOR_DECLARATION = `a { color: var(${ LINK_COLOR }, #00e); }`; @@ -87,26 +109,21 @@ export function getPresetVariable( styles, blockName, propertyName, value ) { if ( ! value ) { return value; } - const presetCategory = - STYLE_PROPERTIES_TO_PRESETS[ propertyName ] || propertyName; - if ( ! presetCategory ) { - return value; - } - const presetData = PRESET_CATEGORIES[ presetCategory ]; + const presetData = getPresetMetadataFromStyleProperty( propertyName ); if ( ! presetData ) { return value; } - const { key, path } = presetData; + const { valueKey, path, cssVarInfix } = presetData; const presets = - get( styles, [ blockName, 'settings', ...path ] ) ?? - get( styles, [ GLOBAL_CONTEXT, 'settings', ...path ] ); + get( styles, [ blockName, ...path ] ) ?? + get( styles, [ GLOBAL_CONTEXT_NAME, ...path ] ); const presetObject = find( presets, ( preset ) => { - return preset[ key ] === value; + return preset[ valueKey ] === value; } ); if ( ! presetObject ) { return value; } - return `var:preset|${ kebabCase( presetCategory ) }|${ presetObject.slug }`; + return `var:preset|${ cssVarInfix }|${ presetObject.slug }`; } function getValueFromPresetVariable( @@ -116,13 +133,13 @@ function getValueFromPresetVariable( [ presetType, slug ] ) { presetType = camelCase( presetType ); - const presetData = PRESET_CATEGORIES[ presetType ]; + const presetData = getPresetMetadataFromStyleProperty( presetType ); if ( ! presetData ) { return variable; } const presets = - get( styles, [ blockName, 'settings', ...presetData.path ] ) ?? - get( styles, [ GLOBAL_CONTEXT_NAME, 'settings', ...presetData.path ] ); + get( styles, [ blockName, ...presetData.path ] ) ?? + get( styles, [ GLOBAL_CONTEXT_NAME, ...presetData.path ] ); if ( ! presets ) { return variable; } @@ -130,8 +147,8 @@ function getValueFromPresetVariable( return preset.slug === slug; } ); if ( presetObject ) { - const { key } = presetData; - const result = presetObject[ key ]; + const { valueKey } = presetData; + const result = presetObject[ valueKey ]; return getValueFromVariable( styles, blockName, result ); } return variable; diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index 2afaf384d962d3..487b2ea757af11 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -36,9 +36,8 @@ export default function Header( { openEntitiesSavedStates } ) { const { __experimentalGetPreviewDeviceType, isFeatureActive, - getTemplateId, - getTemplatePartId, - getTemplateType, + getEditedPostType, + getEditedPostId, isInserterOpened, } = select( 'core/edit-site' ); const { getEntityRecord } = select( 'core' ); @@ -46,29 +45,20 @@ export default function Header( { openEntitiesSavedStates } ) { 'core/editor' ); - const _templateType = getTemplateType(); - const _template = getEntityRecord( - 'postType', - 'wp_template', - getTemplateId() - ); - const _templatePart = getEntityRecord( - 'postType', - 'wp_template_part', - getTemplatePartId() - ); - + const postType = getEditedPostType(); + const postId = getEditedPostId(); + const record = getEntityRecord( 'postType', postType, postId ); const _entityTitle = - 'wp_template' === _templateType - ? getTemplateInfo( _template ).title - : _templatePart?.slug; + 'wp_template' === postType + ? getTemplateInfo( record ).title + : record?.slug; return { deviceType: __experimentalGetPreviewDeviceType(), entityTitle: _entityTitle, hasFixedToolbar: isFeatureActive( 'fixedToolbar' ), - template: _template, - templateType: _templateType, + template: record, + templateType: postType, isInserterOpen: isInserterOpened(), }; }, [] ); diff --git a/packages/edit-site/src/components/header/style.scss b/packages/edit-site/src/components/header/style.scss index 8d08537805bb50..130f2a069b744a 100644 --- a/packages/edit-site/src/components/header/style.scss +++ b/packages/edit-site/src/components/header/style.scss @@ -1,3 +1,5 @@ +$header-toolbar-min-width: 335px; + .edit-site-header { align-items: center; background-color: $white; @@ -5,39 +7,27 @@ height: $header-height; box-sizing: border-box; width: 100%; + justify-content: space-between; @include break-medium() { - padding-left: 60px; - transition: padding-left 20ms linear; - transition-delay: 80ms; - @include reduce-motion("transition"); + body.is-fullscreen-mode & { + padding-left: 60px; + transition: padding-left 20ms linear; + transition-delay: 80ms; + @include reduce-motion("transition"); + } + } .edit-site-header_start, .edit-site-header_end { - flex: 1 0; display: flex; } - @include break-medium() { - .edit-site-header_start { - // Flex basis prevents the header_start toolbar - // from collapsing when shrinking the viewport. - flex-basis: calc(#{$header-toolbar-min-width} - #{$header-height}); - } - - .edit-site-header_end { - // Flex basis prevents the header_end toolbar - // from collapsing when shrinking the viewport - flex-basis: $header-toolbar-min-width; - } - } - .edit-site-header_center { display: flex; align-items: center; height: 100%; - flex-shrink: 1; // Flex items will, by default, refuse to shrink below a minimum // intrinsic width. In order to shrink this flexbox item, and // subsequently truncate child text, we set an explicit min-width. @@ -56,9 +46,18 @@ body.is-navigation-sidebar-open { padding-left: 0; transition: padding-left 20ms linear; transition-delay: 0ms; + } +} - .edit-site-header_start { - flex-basis: $header-toolbar-min-width; +// Centred document title on small screens with sidebar open +@media ( max-width: #{ ($break-large - 1) } ) { + body.is-navigation-sidebar-open .edit-site-header { + .edit-site-header-toolbar__inserter-toggle ~ .components-button, + .edit-site-header_end .components-button:not(.is-primary) { + display: none; + } + .edit-site-save-button__button { + margin-right: 0; } } } diff --git a/packages/edit-site/src/components/keyboard-shortcuts/index.js b/packages/edit-site/src/components/keyboard-shortcuts/index.js index a0588857ea2451..75fe5b085c31d6 100644 --- a/packages/edit-site/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-site/src/components/keyboard-shortcuts/index.js @@ -2,7 +2,10 @@ * WordPress dependencies */ import { useEffect } from '@wordpress/element'; -import { useShortcut } from '@wordpress/keyboard-shortcuts'; +import { + useShortcut, + store as keyboardShortcutsStore, +} from '@wordpress/keyboard-shortcuts'; import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; @@ -31,7 +34,7 @@ function KeyboardShortcuts() { } function KeyboardShortcutsRegister() { // Registering the shortcuts - const { registerShortcut } = useDispatch( 'core/keyboard-shortcuts' ); + const { registerShortcut } = useDispatch( keyboardShortcutsStore ); useEffect( () => { registerShortcut( { name: 'core/edit-site/undo', diff --git a/packages/edit-site/src/components/main-dashboard-button/index.js b/packages/edit-site/src/components/main-dashboard-button/index.js new file mode 100644 index 00000000000000..efc3bfaad19e87 --- /dev/null +++ b/packages/edit-site/src/components/main-dashboard-button/index.js @@ -0,0 +1,28 @@ +/** + * WordPress dependencies + */ +import { + __experimentalUseSlot as useSlot, + createSlotFill, +} from '@wordpress/components'; + +const slotName = '__experimentalMainDashboardButton'; + +const { Fill, Slot: MainDashboardButtonSlot } = createSlotFill( slotName ); + +const MainDashboardButton = Fill; + +const Slot = ( { children } ) => { + const slot = useSlot( slotName ); + const hasFills = Boolean( slot.fills && slot.fills.length ); + + if ( ! hasFills ) { + return children; + } + + return <MainDashboardButtonSlot bubblesVirtually />; +}; + +MainDashboardButton.Slot = Slot; + +export default MainDashboardButton; diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/constants.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/constants.js index 5738942e4779d9..e0201af3420970 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/constants.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/constants.js @@ -11,6 +11,16 @@ export const TEMPLATES_POSTS = [ 'home', 'single' ]; export const TEMPLATES_STATUSES = [ 'publish', 'draft', 'auto-draft' ]; +export const TEMPLATES_NEW_OPTIONS = [ + 'front-page', + 'single-post', + 'page', + 'archive', + 'search', + '404', + 'index', +]; + export const MENU_ROOT = 'root'; export const MENU_CONTENT_CATEGORIES = 'content-categories'; export const MENU_CONTENT_PAGES = 'content-pages'; diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/index.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/index.js index f3601aeee66940..9db6e54cc87fe5 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/index.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/index.js @@ -35,7 +35,9 @@ const NavigationPanel = ( { isOpen } ) => { // from a separate component (such as document actions in the header). const panelRef = useRef(); useEffect( () => { - panelRef.current.focus(); + if ( isOpen ) { + panelRef.current.focus(); + } }, [ templatesActiveMenu ] ); return ( diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/template-parts.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/template-parts.js index ad597671aba41f..c2d8a919714d9a 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/template-parts.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/template-parts.js @@ -9,17 +9,24 @@ import { map } from 'lodash'; import { __ } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { - __experimentalNavigationItem as NavigationItem, __experimentalNavigationMenu as NavigationMenu, + __experimentalNavigationItem as NavigationItem, } from '@wordpress/components'; +import { useState, useCallback } from '@wordpress/element'; /** * Internal dependencies */ import TemplateNavigationItem from '../template-navigation-item'; import { MENU_ROOT, MENU_TEMPLATE_PARTS } from '../constants'; +import SearchResults from '../search-results'; export default function TemplatePartsMenu() { + const [ search, setSearch ] = useState( '' ); + const onSearch = useCallback( ( value ) => { + setSearch( value ); + } ); + const templateParts = useSelect( ( select ) => { const unfilteredTemplateParts = select( 'core' ).getEntityRecords( 'postType', 'wp_template_part', { @@ -38,15 +45,25 @@ export default function TemplatePartsMenu() { menu={ MENU_TEMPLATE_PARTS } title={ __( 'Template Parts' ) } parentMenu={ MENU_ROOT } + hasSearch={ true } + onSearch={ onSearch } + search={ search } > - { map( templateParts, ( templatePart ) => ( - <TemplateNavigationItem - item={ templatePart } - key={ `wp_template_part-${ templatePart.id }` } - /> - ) ) } + { search && ( + <SearchResults items={ templateParts } search={ search } /> + ) } + + { ! search && + map( templateParts, ( templatePart ) => ( + <TemplateNavigationItem + item={ templatePart } + key={ `wp_template_part-${ templatePart.id }` } + /> + ) ) } - { ! templateParts && <NavigationItem title={ __( 'Loading…' ) } /> } + { ! search && templateParts === null && ( + <NavigationItem title={ __( 'Loading…' ) } isText /> + ) } </NavigationMenu> ); } diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-pages.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-pages.js index 1b000d83e12f0b..d4816219a02399 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-pages.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-pages.js @@ -20,15 +20,15 @@ import { MENU_TEMPLATES, MENU_TEMPLATES_PAGES } from '../constants'; export default function TemplatesPagesMenu( { templates } ) { const defaultTemplate = templates?.find( ( { slug } ) => slug === 'page' ); - const specificTemplates = templates?.filter( ( { slug } ) => - slug.startsWith( 'page-' ) - ); + const specificTemplates = + templates?.filter( ( { slug } ) => slug.startsWith( 'page-' ) ) ?? []; return ( <NavigationMenu menu={ MENU_TEMPLATES_PAGES } title={ __( 'Pages' ) } parentMenu={ MENU_TEMPLATES } + isEmpty={ ! defaultTemplate && specificTemplates.length === 0 } > <NavigationGroup title={ _x( 'Specific', 'specific templates' ) }> { map( specificTemplates, ( template ) => ( diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-posts.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-posts.js index 5af56fe7a58d07..02225ee5bfb247 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-posts.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates-posts.js @@ -23,18 +23,20 @@ import { } from '../constants'; export default function TemplatesPostsMenu( { templates } ) { - const generalTemplates = templates?.filter( ( { slug } ) => - TEMPLATES_POSTS.includes( slug ) - ); - const specificTemplates = templates?.filter( ( { slug } ) => - slug.startsWith( 'post-' ) - ); + const generalTemplates = + templates?.filter( ( { slug } ) => TEMPLATES_POSTS.includes( slug ) ) ?? + []; + const specificTemplates = + templates?.filter( ( { slug } ) => slug.startsWith( 'post-' ) ) ?? []; return ( <NavigationMenu menu={ MENU_TEMPLATES_POSTS } title={ __( 'Posts' ) } parentMenu={ MENU_TEMPLATES } + isEmpty={ + generalTemplates.length === 0 && specificTemplates.length === 0 + } > <NavigationGroup title={ _x( 'Specific', 'specific templates' ) }> { map( specificTemplates, ( template ) => ( diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js index f1f188b6d7a3b8..c3f15d44d10ed2 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/menus/templates.js @@ -12,6 +12,7 @@ import { } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { __, _x } from '@wordpress/i18n'; +import { useState, useCallback } from '@wordpress/element'; /** * Internal dependencies @@ -30,8 +31,14 @@ import { import TemplatesAllMenu from './templates-all'; import NewTemplateDropdown from '../new-template-dropdown'; import TemplateNavigationItem from '../template-navigation-item'; +import SearchResults from '../search-results'; export default function TemplatesMenu() { + const [ search, setSearch ] = useState( '' ); + const onSearch = useCallback( ( value ) => { + setSearch( value ); + } ); + const templates = useSelect( ( select ) => select( 'core' ).getEntityRecords( 'postType', 'wp_template', { @@ -51,25 +58,43 @@ export default function TemplatesMenu() { title={ __( 'Templates' ) } titleAction={ <NewTemplateDropdown /> } parentMenu={ MENU_ROOT } + hasSearch={ true } + onSearch={ onSearch } + search={ search } > - { map( generalTemplates, ( template ) => ( - <TemplateNavigationItem - item={ template } - key={ `wp_template-${ template.id }` } - /> - ) ) } - <NavigationItem - navigateToMenu={ MENU_TEMPLATES_ALL } - title={ _x( 'All', 'all templates' ) } - /> - <NavigationItem - navigateToMenu={ MENU_TEMPLATES_PAGES } - title={ __( 'Pages' ) } - /> - <NavigationItem - navigateToMenu={ MENU_TEMPLATES_POSTS } - title={ __( 'Posts' ) } - /> + { search && ( + <SearchResults items={ templates } search={ search } /> + ) } + + { ! search && ( + <> + <NavigationItem + navigateToMenu={ MENU_TEMPLATES_ALL } + title={ _x( 'All', 'all templates' ) } + /> + <NavigationItem + navigateToMenu={ MENU_TEMPLATES_PAGES } + title={ __( 'Pages' ) } + hideIfTargetMenuEmpty + /> + <NavigationItem + navigateToMenu={ MENU_TEMPLATES_POSTS } + title={ __( 'Posts' ) } + hideIfTargetMenuEmpty + /> + { map( generalTemplates, ( template ) => ( + <TemplateNavigationItem + item={ template } + key={ `wp_template-${ template.id }` } + /> + ) ) } + </> + ) } + + { ! search && templates === null && ( + <NavigationItem title={ __( 'Loading…' ) } isText /> + ) } + <TemplatesPostsMenu templates={ templates } /> <TemplatesPagesMenu templates={ templates } /> <TemplatesAllMenu templates={ templates } /> diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js index 88903d2d2d40cb..c428472a051f2d 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/new-template-dropdown.js @@ -20,7 +20,7 @@ import { Icon, plus } from '@wordpress/icons'; * Internal dependencies */ import getClosestAvailableTemplate from '../../../utils/get-closest-available-template'; -import { TEMPLATES_STATUSES } from './constants'; +import { TEMPLATES_NEW_OPTIONS, TEMPLATES_STATUSES } from './constants'; export default function NewTemplateDropdown() { const { defaultTemplateTypes, templates } = useSelect( ( select ) => { @@ -59,9 +59,15 @@ export default function NewTemplateDropdown() { const missingTemplates = filter( defaultTemplateTypes, - ( template ) => ! includes( existingTemplateSlugs, template.slug ) + ( template ) => + includes( TEMPLATES_NEW_OPTIONS, template.slug ) && + ! includes( existingTemplateSlugs, template.slug ) ); + if ( ! missingTemplates.length ) { + return null; + } + return ( <DropdownMenu className="edit-site-navigation-panel__new-template-dropdown" @@ -77,7 +83,7 @@ export default function NewTemplateDropdown() { } } > { ( { onClose } ) => ( - <NavigableMenu> + <NavigableMenu className="edit-site-navigation-panel__new-template-popover"> <MenuGroup label={ __( 'Add Template' ) }> { map( missingTemplates, diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/search-results.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/search-results.js new file mode 100644 index 00000000000000..39e7b60cb1781c --- /dev/null +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/search-results.js @@ -0,0 +1,73 @@ +/** + * External dependencies + */ +import { map } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useMemo } from '@wordpress/element'; +import { __experimentalNavigationGroup as NavigationGroup } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { normalizedSearch } from './utils'; +import { useSelect } from '@wordpress/data'; +import TemplateNavigationItem from './template-navigation-item'; + +export default function SearchResults( { items, search } ) { + const itemType = items?.length > 0 ? items[ 0 ].type : null; + + const itemInfos = useSelect( + ( select ) => { + if ( itemType === 'wp_template' ) { + const { + __experimentalGetTemplateInfo: getTemplateInfo, + } = select( 'core/editor' ); + + return items.map( ( item ) => ( { + slug: item.slug, + ...getTemplateInfo( item ), + } ) ); + } + + return items.map( ( item ) => ( { + slug: item.slug, + title: item.title?.rendered, + description: item.excerpt?.rendered, + } ) ); + }, + [ items, itemType ] + ); + + const itemsFiltered = useMemo( () => { + if ( items === null || search.length === 0 ) { + return []; + } + + return items.filter( ( { slug } ) => { + const { title, description } = itemInfos.find( + ( info ) => info.slug === slug + ); + + return ( + normalizedSearch( slug, search ) || + normalizedSearch( title, search ) || + normalizedSearch( description, search ) + ); + } ); + }, [ items, itemInfos, search ] ); + + return ( + <NavigationGroup title={ __( 'Search results' ) }> + { map( itemsFiltered, ( item ) => ( + <TemplateNavigationItem + item={ item } + key={ `${ item.type }-${ item.id }` } + /> + ) ) } + </NavigationGroup> + ); +} diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss index c4b53d124fd426..d4fe4b544deca2 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/style.scss @@ -114,10 +114,14 @@ } .edit-site-navigation-panel__new-template-dropdown { - display: flex; margin: 0 0 0 $grid-unit-15; button { margin: 0; } } +.edit-site-navigation-panel__new-template-popover { + @include break-small() { + min-width: 300px; + } +} diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/templates-navigation.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/templates-navigation.js index 44dd8765be8554..d20c32286c52a0 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/templates-navigation.js +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/templates-navigation.js @@ -7,7 +7,6 @@ import { __experimentalNavigationItem as NavigationItem, __experimentalNavigationBackButton as NavigationBackButton, } from '@wordpress/components'; -import { __experimentalMainDashboardButton as MainDashboardButton } from '@wordpress/interface'; import { useDispatch, useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; @@ -16,37 +15,29 @@ import { __ } from '@wordpress/i18n'; */ import TemplatesMenu from './menus/templates'; import TemplatePartsMenu from './menus/template-parts'; +import MainDashboardButton from '../../main-dashboard-button'; import { MENU_ROOT, MENU_TEMPLATE_PARTS, MENU_TEMPLATES } from './constants'; export default function TemplatesNavigation() { - const { templateId, templatePartId, templateType, activeMenu } = useSelect( - ( select ) => { - const { - getTemplateId, - getTemplatePartId, - getTemplateType, - getNavigationPanelActiveMenu, - } = select( 'core/edit-site' ); + const { postId, postType, activeMenu } = useSelect( ( select ) => { + const { + getEditedPostType, + getEditedPostId, + getNavigationPanelActiveMenu, + } = select( 'core/edit-site' ); - return { - templateId: getTemplateId(), - templatePartId: getTemplatePartId(), - templateType: getTemplateType(), - activeMenu: getNavigationPanelActiveMenu(), - }; - }, - [] - ); + return { + postId: getEditedPostId(), + postType: getEditedPostType(), + activeMenu: getNavigationPanelActiveMenu(), + }; + }, [] ); const { setNavigationPanelActiveMenu } = useDispatch( 'core/edit-site' ); return ( <Navigation - activeItem={ - 'wp_template' === templateType - ? `${ templateType }-${ templateId }` - : `${ templateType }-${ templatePartId }` - } + activeItem={ `${ postType }-${ postId }` } activeMenu={ activeMenu } onActivateMenu={ setNavigationPanelActiveMenu } > diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-panel/utils.js b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/utils.js new file mode 100644 index 00000000000000..251b90cfe44dfd --- /dev/null +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-panel/utils.js @@ -0,0 +1,11 @@ +/** + * External dependencies + */ +import { deburr } from 'lodash'; + +// @see packages/block-editor/src/components/inserter/search-items.js +export const normalizeInput = ( input ) => + deburr( input ).replace( /^\//, '' ).toLowerCase(); + +export const normalizedSearch = ( title, search ) => + -1 !== normalizeInput( title ).indexOf( normalizeInput( search ) ); diff --git a/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/style.scss b/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/style.scss index 0b51e8536a2f25..bd94f618b87669 100644 --- a/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/style.scss +++ b/packages/edit-site/src/components/navigation-sidebar/navigation-toggle/style.scss @@ -1,15 +1,19 @@ .edit-site-navigation-toggle { + align-items: center; + background: $gray-900; + border-radius: 0; display: none; + position: absolute; + z-index: z-index(".edit-site-navigation-toggle"); + height: $header-height + $border-width; // Cover header border + width: $header-height; @include break-medium() { - align-items: center; - background: $gray-900; - border-radius: 0; display: flex; - position: absolute; - z-index: z-index(".edit-site-navigation-toggle"); - height: $header-height + $border-width; // Cover header border - width: $header-height; + } + + body.is-navigation-sidebar-open & { + display: flex; } } diff --git a/packages/edit-site/src/components/notices/index.js b/packages/edit-site/src/components/notices/index.js index 8243f4b13cf84a..5d053ad47eb68d 100644 --- a/packages/edit-site/src/components/notices/index.js +++ b/packages/edit-site/src/components/notices/index.js @@ -3,16 +3,17 @@ */ import { useSelect, useDispatch } from '@wordpress/data'; import { SnackbarList } from '@wordpress/components'; +import { store as noticesStore } from '@wordpress/notices'; export default function Notices() { const notices = useSelect( ( select ) => - select( 'core/notices' ) + select( noticesStore ) .getNotices() .filter( ( notice ) => notice.type === 'snackbar' ), [] ); - const { removeNotice } = useDispatch( 'core/notices' ); + const { removeNotice } = useDispatch( noticesStore ); return ( <SnackbarList className="edit-site-notices" diff --git a/packages/edit-site/src/components/sidebar/typography-panel.js b/packages/edit-site/src/components/sidebar/typography-panel.js index 1d76ec77894d6d..529115d8b5adf5 100644 --- a/packages/edit-site/src/components/sidebar/typography-panel.js +++ b/packages/edit-site/src/components/sidebar/typography-panel.js @@ -30,15 +30,13 @@ function useHasLineHeightControl( { supports, name } ) { } function useHasAppearenceControl( { supports, name } ) { - const fontStyles = useEditorFeature( 'typography.fontStyles', name ); - const fontWeights = useEditorFeature( 'typography.fontWeights', name ); - const hasFontAppearance = !! fontStyles?.length && !! fontWeights?.length; - - return ( - hasFontAppearance && - supports.includes( 'fontStyle' ) && - supports.includes( 'fontWeight' ) - ); + const hasFontStyles = + useEditorFeature( 'typography.customFontStyle', name ) && + supports.includes( 'fontStyle' ); + const hasFontWeights = + useEditorFeature( 'typography.customFontWeight', name ) && + supports.includes( 'fontWeight' ); + return hasFontStyles || hasFontWeights; } export default function TypographyPanel( { @@ -52,8 +50,12 @@ export default function TypographyPanel( { name ); const fontFamilies = useEditorFeature( 'typography.fontFamilies', name ); - const fontStyles = useEditorFeature( 'typography.fontStyles', name ); - const fontWeights = useEditorFeature( 'typography.fontWeights', name ); + const hasFontStyles = + useEditorFeature( 'typography.customFontStyle', name ) && + supports.includes( 'fontStyle' ); + const hasFontWeights = + useEditorFeature( 'typography.customFontWeight', name ) && + supports.includes( 'fontWeight' ); const hasLineHeightEnabled = useHasLineHeightControl( { supports, name } ); const hasAppearenceControl = useHasAppearenceControl( { supports, name } ); @@ -92,11 +94,12 @@ export default function TypographyPanel( { fontStyle: getStyleProperty( name, 'fontStyle' ), fontWeight: getStyleProperty( name, 'fontWeight' ), } } - options={ { fontStyles, fontWeights } } onChange={ ( { fontStyle, fontWeight } ) => { setStyleProperty( name, 'fontStyle', fontStyle ); setStyleProperty( name, 'fontWeight', fontWeight ); } } + hasFontStyles={ hasFontStyles } + hasFontWeights={ hasFontWeights } /> ) } </PanelBody> diff --git a/packages/edit-site/src/components/url-query-controller/index.js b/packages/edit-site/src/components/url-query-controller/index.js new file mode 100644 index 00000000000000..3ff703bf0debd4 --- /dev/null +++ b/packages/edit-site/src/components/url-query-controller/index.js @@ -0,0 +1,71 @@ +/** + * WordPress dependencies + */ +import { useEffect } from '@wordpress/element'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { getQueryArg, addQueryArgs, removeQueryArgs } from '@wordpress/url'; + +export default function URLQueryController() { + const { setTemplate, setTemplatePart, showHomepage, setPage } = useDispatch( + 'core/edit-site' + ); + + // Set correct entity on load. + useEffect( () => { + const url = window.location.href; + const postId = getQueryArg( url, 'postId' ); + + if ( ! postId ) { + showHomepage(); + return; + } + + const postType = getQueryArg( url, 'postType' ); + if ( 'page' === postType || 'post' === postType ) { + setPage( { context: { postType, postId } } ); // Resolves correct template based on ID. + } else if ( 'wp_template' === postType ) { + setTemplate( postId ); + } else if ( 'wp_template_part' === postType ) { + setTemplatePart( postId ); + } else { + showHomepage(); + } + }, [] ); + + // Update page URL when context changes. + const pageContext = useCurrentPageContext(); + useEffect( () => { + const newUrl = pageContext + ? addQueryArgs( window.location.href, pageContext ) + : removeQueryArgs( window.location.href, 'postType', 'postId' ); + + window.history.replaceState( {}, '', newUrl ); + }, [ pageContext ] ); + + return null; +} + +function useCurrentPageContext() { + return useSelect( ( select ) => { + const { getEditedPostType, getEditedPostId, getPage } = select( + 'core/edit-site' + ); + + const page = getPage(); + let _postId = getEditedPostId(), + _postType = getEditedPostType(); + // This doesn't seem right to me, + // we shouldn't be using the "page" and the "template" in the same way. + // This need to be investigated. + if ( page?.context?.postId && page?.context?.postType ) { + _postId = page.context.postId; + _postType = page.context.postType; + } + + if ( _postId && _postType ) { + return { postId: _postId, postType: _postType }; + } + + return null; + } ); +} diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 67f6e15128d3a4..9f5bb993511460 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -4,7 +4,6 @@ import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; import { __ } from '@wordpress/i18n'; -import '@wordpress/notices'; import { registerCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, @@ -67,4 +66,5 @@ export function initialize( id, settings ) { render( <Editor />, document.getElementById( id ) ); } +export { default as __experimentalMainDashboardButton } from './components/main-dashboard-button'; export { default as __experimentalNavigationToggle } from './components/navigation-sidebar/navigation-toggle'; diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 4d4dabb32a8721..3c3031f539d432 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -114,7 +114,8 @@ export function setHomeTemplateId( homeTemplateId ) { } /** - * Resolves the template for a page and displays both. + * Resolves the template for a page and displays both. If no path is given, attempts + * to use the postId to generate a path like `?p=${ postId }`. * * @param {Object} page The page object. * @param {string} page.type The page type. @@ -125,6 +126,9 @@ export function setHomeTemplateId( homeTemplateId ) { * @return {number} The resolved template ID for the page route. */ export function* setPage( page ) { + if ( ! page.path && page.context?.postId ) { + page.path = `?p=${ page.context.postId }`; + } const templateId = yield findTemplate( page.path ); yield { type: 'SET_PAGE', diff --git a/packages/edit-site/src/store/index.js b/packages/edit-site/src/store/index.js index 25a90fabe0aaa6..2fa2e95eb1dc7e 100644 --- a/packages/edit-site/src/store/index.js +++ b/packages/edit-site/src/store/index.js @@ -23,7 +23,5 @@ export default function registerEditSiteStore( initialState ) { initialState, } ); - store.dispatch( actions.showHomepage() ); - return store; } diff --git a/packages/edit-site/src/store/reducer.js b/packages/edit-site/src/store/reducer.js index 5700958226eab0..0a89862d23ec87 100644 --- a/packages/edit-site/src/store/reducer.js +++ b/packages/edit-site/src/store/reducer.js @@ -69,72 +69,28 @@ export function settings( state = {}, action ) { } /** - * Reducer returning the template ID. + * Reducer keeping track of the currently edited Post Type, + * Post Id and the context provided to fill the content of the block editor. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ -export function templateId( state, action ) { +export function editedPost( state = {}, action ) { switch ( action.type ) { case 'SET_TEMPLATE': case 'SET_PAGE': - return action.templateId; - } - - return state; -} - -/** - * Reducer returning the template part ID. - * - * @param {Object} state Current state. - * @param {Object} action Dispatched action. - * - * @return {Object} Updated state. - */ -export function templatePartId( state, action ) { - switch ( action.type ) { - case 'SET_TEMPLATE_PART': - return action.templatePartId; - } - - return state; -} - -/** - * Reducer returning the template type. - * - * @param {Object} state Current state. - * @param {Object} action Dispatched action. - * - * @return {Object} Updated state. - */ -export function templateType( state = 'wp_template', action ) { - switch ( action.type ) { - case 'SET_TEMPLATE': - case 'SET_PAGE': - return 'wp_template'; + return { + type: 'wp_template', + id: action.templateId, + page: action.page, + }; case 'SET_TEMPLATE_PART': - return 'wp_template_part'; - } - - return state; -} - -/** - * Reducer returning the page being edited. - * - * @param {Object} state Current state. - * @param {Object} action Dispatched action. - * - * @return {Object} Updated state. - */ -export function page( state, action ) { - switch ( action.type ) { - case 'SET_PAGE': - return action.page; + return { + type: 'wp_template_part', + id: action.templatePartId, + }; } return state; @@ -224,10 +180,7 @@ export default combineReducers( { preferences, deviceType, settings, - templateId, - templatePartId, - templateType, - page, + editedPost, homeTemplateId, navigationPanel, blockInserterPanel, diff --git a/packages/edit-site/src/store/selectors.js b/packages/edit-site/src/store/selectors.js index 36509472c1a5b1..4bcfd07dc2926e 100644 --- a/packages/edit-site/src/store/selectors.js +++ b/packages/edit-site/src/store/selectors.js @@ -56,6 +56,7 @@ export const getSettings = createSelector( ( state, setIsInserterOpen ) => { const settings = { ...state.settings, + outlineMode: true, focusMode: isFeatureActive( state, 'focusMode' ), hasFixedToolbar: isFeatureActive( state, 'fixedToolbar' ), __experimentalSetIsInserterOpened: setIsInserterOpen, @@ -95,36 +96,25 @@ export function getHomeTemplateId( state ) { } /** - * Returns the current template ID. + * Returns the current edited post type (wp_template or wp_template_part). * * @param {Object} state Global application state. * * @return {number?} Template ID. */ -export function getTemplateId( state ) { - return state.templateId; +export function getEditedPostType( state ) { + return state.editedPost.type; } /** - * Returns the current template part ID. + * Returns the ID of the currently edited template or template part. * * @param {Object} state Global application state. * - * @return {number?} Template part ID. + * @return {number?} Post ID. */ -export function getTemplatePartId( state ) { - return state.templatePartId; -} - -/** - * Returns the current template type. - * - * @param {Object} state Global application state. - * - * @return {string?} Template type. - */ -export function getTemplateType( state ) { - return state.templateType; +export function getEditedPostId( state ) { + return state.editedPost.id; } /** @@ -135,7 +125,7 @@ export function getTemplateType( state ) { * @return {Object} Page. */ export function getPage( state ) { - return state.page; + return state.editedPost.page; } /** diff --git a/packages/edit-site/src/store/test/reducer.js b/packages/edit-site/src/store/test/reducer.js index ff725b0c2e861d..9f8263786464bd 100644 --- a/packages/edit-site/src/store/test/reducer.js +++ b/packages/edit-site/src/store/test/reducer.js @@ -10,10 +10,7 @@ import { preferences, settings, homeTemplateId, - templateId, - templatePartId, - templateType, - page, + editedPost, navigationPanel, blockInserterPanel, } from '../reducer'; @@ -87,108 +84,51 @@ describe( 'state', () => { } ); } ); - describe( 'templateId()', () => { + describe( 'editedPost()', () => { it( 'should apply default state', () => { - expect( templateId( undefined, {} ) ).toEqual( undefined ); + expect( editedPost( undefined, {} ) ).toEqual( {} ); } ); it( 'should default to returning the same state', () => { const state = {}; - expect( templateId( state, {} ) ).toBe( state ); + expect( editedPost( state, {} ) ).toBe( state ); } ); it( 'should update when a template is set', () => { expect( - templateId( 1, { - type: 'SET_TEMPLATE', - templateId: 2, - } ) - ).toEqual( 2 ); - } ); - - it( 'should update when a page is set', () => { - expect( - templateId( 1, { - type: 'SET_PAGE', - templateId: 2, - } ) - ).toEqual( 2 ); - } ); - } ); - - describe( 'templatePartId()', () => { - it( 'should apply default state', () => { - expect( templatePartId( undefined, {} ) ).toEqual( undefined ); - } ); - - it( 'should default to returning the same state', () => { - const state = {}; - expect( templatePartId( state, {} ) ).toBe( state ); - } ); - - it( 'should update when a template part is set', () => { - expect( - templatePartId( 1, { - type: 'SET_TEMPLATE_PART', - templatePartId: 2, - } ) - ).toEqual( 2 ); - } ); - } ); - - describe( 'templateType()', () => { - it( 'should apply default state', () => { - expect( templateType( undefined, {} ) ).toEqual( 'wp_template' ); - } ); - - it( 'should default to returning the same state', () => { - const state = {}; - expect( templateType( state, {} ) ).toBe( state ); - } ); - - it( 'should update when a template is set', () => { - expect( - templateType( undefined, { - type: 'SET_TEMPLATE', - } ) - ).toEqual( 'wp_template' ); + editedPost( + { id: 1, type: 'wp_template' }, + { + type: 'SET_TEMPLATE', + templateId: 2, + } + ) + ).toEqual( { id: 2, type: 'wp_template' } ); } ); it( 'should update when a page is set', () => { expect( - templateType( undefined, { - type: 'SET_PAGE', - } ) - ).toEqual( 'wp_template' ); + editedPost( + { id: 1, type: 'wp_template' }, + { + type: 'SET_PAGE', + templateId: 2, + page: {}, + } + ) + ).toEqual( { id: 2, type: 'wp_template', page: {} } ); } ); it( 'should update when a template part is set', () => { expect( - templateType( undefined, { - type: 'SET_TEMPLATE_PART', - } ) - ).toEqual( 'wp_template_part' ); - } ); - } ); - - describe( 'page()', () => { - it( 'should apply default state', () => { - expect( page( undefined, {} ) ).toEqual( undefined ); - } ); - - it( 'should default to returning the same state', () => { - const state = {}; - expect( page( state, {} ) ).toBe( state ); - } ); - - it( 'should set the page', () => { - const newPage = {}; - expect( - page( undefined, { - type: 'SET_PAGE', - page: newPage, - } ) - ).toBe( newPage ); + editedPost( + { id: 1, type: 'wp_template' }, + { + type: 'SET_TEMPLATE_PART', + templatePartId: 2, + } + ) + ).toEqual( { id: 2, type: 'wp_template_part' } ); } ); } ); diff --git a/packages/edit-site/src/store/test/selectors.js b/packages/edit-site/src/store/test/selectors.js index c9a26c0a24c190..e24b640f07afb1 100644 --- a/packages/edit-site/src/store/test/selectors.js +++ b/packages/edit-site/src/store/test/selectors.js @@ -6,9 +6,8 @@ import { getCanUserCreateMedia, getSettings, getHomeTemplateId, - getTemplateId, - getTemplatePartId, - getTemplateType, + getEditedPostType, + getEditedPostId, getPage, getNavigationPanelActiveMenu, isNavigationOpened, @@ -17,9 +16,9 @@ import { describe( 'selectors', () => { const canUser = jest.fn( () => true ); - getCanUserCreateMedia.__unstableGetSelect = jest.fn( () => ( { - canUser, - } ) ); + getCanUserCreateMedia.registry = { + select: jest.fn( () => ( { canUser } ) ), + }; describe( 'isFeatureActive', () => { it( 'is tolerant to an undefined features preference', () => { @@ -70,7 +69,7 @@ describe( 'selectors', () => { it( "selects `canUser( 'create', 'media' )` from the core store", () => { expect( getCanUserCreateMedia() ).toBe( true ); expect( - getCanUserCreateMedia.__unstableGetSelect + getCanUserCreateMedia.registry.select ).toHaveBeenCalledWith( 'core' ); expect( canUser ).toHaveBeenCalledWith( 'create', 'media' ); } ); @@ -83,6 +82,7 @@ describe( 'selectors', () => { const state = { settings: {}, preferences: {} }; const setInserterOpened = () => {}; expect( getSettings( state, setInserterOpened ) ).toEqual( { + outlineMode: true, focusMode: false, hasFixedToolbar: false, __experimentalSetIsInserterOpened: setInserterOpened, @@ -101,6 +101,7 @@ describe( 'selectors', () => { }; const setInserterOpened = () => {}; expect( getSettings( state, setInserterOpened ) ).toEqual( { + outlineMode: true, key: 'value', focusMode: true, hasFixedToolbar: true, @@ -117,31 +118,25 @@ describe( 'selectors', () => { } ); } ); - describe( 'getTemplateId', () => { + describe( 'getEditedPostId', () => { it( 'returns the template ID', () => { - const state = { templateId: {} }; - expect( getTemplateId( state ) ).toBe( state.templateId ); + const state = { editedPost: { id: 10 } }; + expect( getEditedPostId( state ) ).toBe( 10 ); } ); } ); - describe( 'getTemplatePartId', () => { - it( 'returns the template part ID', () => { - const state = { templatePartId: {} }; - expect( getTemplatePartId( state ) ).toBe( state.templatePartId ); - } ); - } ); - - describe( 'getTemplateType', () => { + describe( 'getEditedPostType', () => { it( 'returns the template type', () => { - const state = { templateType: {} }; - expect( getTemplateType( state ) ).toBe( state.templateType ); + const state = { editedPost: { type: 'wp_template' } }; + expect( getEditedPostType( state ) ).toBe( 'wp_template' ); } ); } ); describe( 'getPage', () => { it( 'returns the page object', () => { - const state = { page: {} }; - expect( getPage( state ) ).toBe( state.page ); + const page = {}; + const state = { editedPost: { page } }; + expect( getPage( state ) ).toBe( page ); } ); } ); diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index a22f1f81e35ae0..6e35cadc3e5481 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-widgets", - "version": "1.1.4", + "version": "1.2.0", "description": "Widgets Page module for WordPress..", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/block-editor": "file:../block-editor", "@wordpress/block-library": "file:../block-library", diff --git a/packages/edit-widgets/src/blocks/legacy-widget/block.json b/packages/edit-widgets/src/blocks/legacy-widget/block.json index 49b8c20eacd123..934ea46ca1493d 100644 --- a/packages/edit-widgets/src/blocks/legacy-widget/block.json +++ b/packages/edit-widgets/src/blocks/legacy-widget/block.json @@ -28,5 +28,6 @@ }, "parent": [ "core/widget-area" - ] + ], + "editorStyle": "wp-block-legacy-widget-editor" } diff --git a/packages/edit-widgets/src/blocks/widget-area/block.json b/packages/edit-widgets/src/blocks/widget-area/block.json index e5d4d713a3bd9e..fdec21c57e9311 100644 --- a/packages/edit-widgets/src/blocks/widget-area/block.json +++ b/packages/edit-widgets/src/blocks/widget-area/block.json @@ -15,5 +15,7 @@ "customClassName": false, "reusable": false, "__experimentalToolbar": false - } + }, + "editorStyle": "wp-block-widget-area-editor", + "style": "wp-block-widget-area" } diff --git a/packages/edit-widgets/src/components/keyboard-shortcuts/index.js b/packages/edit-widgets/src/components/keyboard-shortcuts/index.js index 6494d83cafc27d..3b0e00e5a155e3 100644 --- a/packages/edit-widgets/src/components/keyboard-shortcuts/index.js +++ b/packages/edit-widgets/src/components/keyboard-shortcuts/index.js @@ -2,7 +2,10 @@ * WordPress dependencies */ import { useEffect } from '@wordpress/element'; -import { useShortcut } from '@wordpress/keyboard-shortcuts'; +import { + useShortcut, + store as keyboardShortcutsStore, +} from '@wordpress/keyboard-shortcuts'; import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; @@ -42,7 +45,7 @@ function KeyboardShortcuts() { function KeyboardShortcutsRegister() { // Registering the shortcuts - const { registerShortcut } = useDispatch( 'core/keyboard-shortcuts' ); + const { registerShortcut } = useDispatch( keyboardShortcutsStore ); useEffect( () => { registerShortcut( { name: 'core/edit-widgets/undo', diff --git a/packages/edit-widgets/src/components/layout/interface.js b/packages/edit-widgets/src/components/layout/interface.js index 9d680a48f3224c..db6440afbd6291 100644 --- a/packages/edit-widgets/src/components/layout/interface.js +++ b/packages/edit-widgets/src/components/layout/interface.js @@ -2,10 +2,16 @@ * WordPress dependencies */ import { Button } from '@wordpress/components'; -import { useViewportMatch } from '@wordpress/compose'; +import { + __experimentalUseDialog as useDialog, + useViewportMatch, +} from '@wordpress/compose'; import { close } from '@wordpress/icons'; -import { __experimentalLibrary as Library } from '@wordpress/block-editor'; -import { useEffect } from '@wordpress/element'; +import { + __experimentalLibrary as Library, + __unstableUseEditorStyles as useEditorStyles, +} from '@wordpress/block-editor'; +import { useEffect, useRef } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { InterfaceSkeleton, ComplementaryArea } from '@wordpress/interface'; import { __ } from '@wordpress/i18n'; @@ -15,7 +21,6 @@ import { __ } from '@wordpress/i18n'; */ import Header from '../header'; import WidgetAreasBlockEditorContent from '../widget-areas-block-editor-content'; -import PopoverWrapper from './popover-wrapper'; import useWidgetLibraryInsertionPoint from '../../hooks/use-widget-library-insertion-point'; const interfaceLabels = { @@ -41,6 +46,9 @@ function Interface( { blockEditorSettings } ) { ).getActiveComplementaryArea( 'core/edit-widgets' ), isInserterOpened: !! select( 'core/edit-widgets' ).isInserterOpened(), } ) ); + const ref = useRef(); + + useEditorStyles( ref, blockEditorSettings.styles ); // Inserter and Sidebars are mutually exclusive useEffect( () => { @@ -55,41 +63,41 @@ function Interface( { blockEditorSettings } ) { } }, [ isInserterOpened, isHugeViewport ] ); + const [ inserterDialogRef, inserterDialogProps ] = useDialog( { + onClose: () => setIsInserterOpened( false ), + } ); + return ( <InterfaceSkeleton + ref={ ref } labels={ interfaceLabels } header={ <Header /> } secondarySidebar={ isInserterOpened && ( - <PopoverWrapper - className="edit-widgets-layout__inserter-panel-popover-wrapper" - onClose={ () => setIsInserterOpened( false ) } + <div + ref={ inserterDialogRef } + { ...inserterDialogProps } + className="edit-widgets-layout__inserter-panel" > - <div className="edit-widgets-layout__inserter-panel"> - <div className="edit-widgets-layout__inserter-panel-header"> - <Button - icon={ close } - onClick={ () => - setIsInserterOpened( false ) - } - /> - </div> - <div className="edit-widgets-layout__inserter-panel-content"> - <Library - showInserterHelpPanel - onSelect={ () => { - if ( isMobileViewport ) { - setIsInserterOpened( false ); - } - } } - rootClientId={ rootClientId } - __experimentalInsertionIndex={ - insertionIndex + <div className="edit-widgets-layout__inserter-panel-header"> + <Button + icon={ close } + onClick={ () => setIsInserterOpened( false ) } + /> + </div> + <div className="edit-widgets-layout__inserter-panel-content"> + <Library + showInserterHelpPanel + onSelect={ () => { + if ( isMobileViewport ) { + setIsInserterOpened( false ); } - /> - </div> + } } + rootClientId={ rootClientId } + __experimentalInsertionIndex={ insertionIndex } + /> </div> - </PopoverWrapper> + </div> ) } sidebar={ diff --git a/packages/edit-widgets/src/components/layout/popover-wrapper.js b/packages/edit-widgets/src/components/layout/popover-wrapper.js deleted file mode 100644 index 375ba0ace1179d..00000000000000 --- a/packages/edit-widgets/src/components/layout/popover-wrapper.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * WordPress dependencies - */ -import { - withConstrainedTabbing, - withFocusReturn, - withFocusOutside, -} from '@wordpress/components'; -import { Component } from '@wordpress/element'; -import { ESCAPE } from '@wordpress/keycodes'; - -function stopPropagation( event ) { - event.stopPropagation(); -} - -const DetectOutside = withFocusOutside( - class extends Component { - handleFocusOutside( event ) { - this.props.onFocusOutside( event ); - } - - render() { - return this.props.children; - } - } -); - -const FocusManaged = withConstrainedTabbing( - withFocusReturn( ( { children } ) => children ) -); - -export default function PopoverWrapper( { onClose, children, className } ) { - // Event handlers - const maybeClose = ( event ) => { - // Close on escape - if ( event.keyCode === ESCAPE && onClose ) { - event.stopPropagation(); - onClose(); - } - }; - - // Disable reason: this stops certain events from propagating outside of the component. - // - onMouseDown is disabled as this can cause interactions with other DOM elements - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( - <div - className={ className } - onKeyDown={ maybeClose } - onMouseDown={ stopPropagation } - > - <DetectOutside onFocusOutside={ onClose }> - <FocusManaged>{ children }</FocusManaged> - </DetectOutside> - </div> - ); - /* eslint-enable jsx-a11y/no-static-element-interactions */ -} diff --git a/packages/edit-widgets/src/components/layout/style.scss b/packages/edit-widgets/src/components/layout/style.scss index 4b7c34cf9451d3..b42fc1a39aad42 100644 --- a/packages/edit-widgets/src/components/layout/style.scss +++ b/packages/edit-widgets/src/components/layout/style.scss @@ -1,15 +1,3 @@ - -// Ideally we don't need all these nested divs. -// Removing these requires a refactoring of the different a11y HoCs. -.edit-widgets-layout__inserter-panel-popover-wrapper { - &, - & > div, - & > div > div, - & > div > div > div { - height: 100%; - } -} - .edit-widgets-layout__inserter-panel { height: 100%; display: flex; diff --git a/packages/edit-widgets/src/components/notices/index.js b/packages/edit-widgets/src/components/notices/index.js index 6e45a3e8005715..0e15bbf8a15bf1 100644 --- a/packages/edit-widgets/src/components/notices/index.js +++ b/packages/edit-widgets/src/components/notices/index.js @@ -8,17 +8,18 @@ import { filter } from 'lodash'; */ import { SnackbarList } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; +import { store as noticesStore } from '@wordpress/notices'; function Notices() { const { notices } = useSelect( ( select ) => { return { - notices: select( 'core/notices' ).getNotices(), + notices: select( noticesStore ).getNotices(), }; }, [] ); const snackbarNotices = filter( notices, { type: 'snackbar', } ); - const { removeNotice } = useDispatch( 'core/notices' ); + const { removeNotice } = useDispatch( noticesStore ); return ( <SnackbarList diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js index 0fbc2959971119..27545b176bf43b 100644 --- a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js +++ b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js @@ -6,18 +6,13 @@ import { defaultTo } from 'lodash'; /** * WordPress dependencies */ -import { - DropZoneProvider, - SlotFillProvider, - FocusReturnProvider, -} from '@wordpress/components'; +import { DropZoneProvider, SlotFillProvider } from '@wordpress/components'; import { uploadMedia } from '@wordpress/media-utils'; import { useDispatch, useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; import { BlockEditorProvider, BlockEditorKeyboardShortcuts, - __unstableEditorStyles as EditorStyles, } from '@wordpress/block-editor'; import { ReusableBlocksMenuItems } from '@wordpress/reusable-blocks'; @@ -86,26 +81,23 @@ export default function WidgetAreasBlockEditorProvider( { return ( <> - <EditorStyles styles={ settings.styles } /> <BlockEditorKeyboardShortcuts.Register /> <KeyboardShortcuts.Register /> <SlotFillProvider> <DropZoneProvider> - <FocusReturnProvider> - <BlockEditorProvider - value={ blocks } - onInput={ onInput } - onChange={ onChange } - settings={ settings } - useSubRegistry={ false } - { ...props } - > - { children } - <ReusableBlocksMenuItems - rootClientId={ widgetAreaId } - /> - </BlockEditorProvider> - </FocusReturnProvider> + <BlockEditorProvider + value={ blocks } + onInput={ onInput } + onChange={ onChange } + settings={ settings } + useSubRegistry={ false } + { ...props } + > + { children } + <ReusableBlocksMenuItems + rootClientId={ widgetAreaId } + /> + </BlockEditorProvider> </DropZoneProvider> </SlotFillProvider> </> diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js index 303b4994a79ee9..e2d3a4813fd94e 100644 --- a/packages/edit-widgets/src/index.js +++ b/packages/edit-widgets/src/index.js @@ -5,14 +5,12 @@ import { registerBlockType, unstable__bootstrapServerSideBlockDefinitions, // eslint-disable-line camelcase } from '@wordpress/blocks'; -import '@wordpress/notices'; import { render } from '@wordpress/element'; import { registerCoreBlocks, __experimentalGetCoreBlocks, __experimentalRegisterExperimentalCoreBlocks, } from '@wordpress/block-library'; -import '@wordpress/reusable-blocks'; /** * Internal dependencies diff --git a/packages/edit-widgets/src/store/actions.js b/packages/edit-widgets/src/store/actions.js index 3b0614dce94df8..feebe301bf5091 100644 --- a/packages/edit-widgets/src/store/actions.js +++ b/packages/edit-widgets/src/store/actions.js @@ -8,6 +8,7 @@ import { invert } from 'lodash'; */ import { __, sprintf } from '@wordpress/i18n'; import { dispatch as dataDispatch } from '@wordpress/data'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -57,7 +58,7 @@ export function* saveEditedWidgetAreas() { try { yield* saveWidgetAreas( editedWidgetAreas ); yield dispatch( - 'core/notices', + noticesStore, 'createSuccessNotice', __( 'Widgets saved.' ), { @@ -66,7 +67,7 @@ export function* saveEditedWidgetAreas() { ); } catch ( e ) { yield dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', /* translators: %s: The error message. */ sprintf( __( 'There was an error. %s' ), e.message ), diff --git a/packages/editor/CHANGELOG.md b/packages/editor/CHANGELOG.md index edfc30a3744fcb..38cf1fd0162509 100644 --- a/packages/editor/CHANGELOG.md +++ b/packages/editor/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 9.25.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the editor namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/editor/package.json b/packages/editor/package.json index 9044cf8baf4fa4..30137e42835d23 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/editor", - "version": "9.24.3", + "version": "9.25.0", "description": "Building blocks for WordPress editors.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -27,7 +27,7 @@ "{src,build,build-module}/{index.js,store/index.js,hooks/**}" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/autop": "file:../autop", "@wordpress/blob": "file:../blob", @@ -59,7 +59,6 @@ "lodash": "^4.17.19", "memize": "^1.1.0", "react-autosize-textarea": "^7.1.0", - "redux-optimist": "^1.0.0", "refx": "^3.0.0", "rememo": "^3.0.0" }, diff --git a/packages/editor/src/components/convert-to-group-buttons/index.js b/packages/editor/src/components/convert-to-group-buttons/index.js index 21b6e92c1a9f96..d948a1447b50aa 100644 --- a/packages/editor/src/components/convert-to-group-buttons/index.js +++ b/packages/editor/src/components/convert-to-group-buttons/index.js @@ -3,7 +3,7 @@ */ import { MenuItem } from '@wordpress/components'; import { _x } from '@wordpress/i18n'; -import { switchToBlockType } from '@wordpress/blocks'; +import { switchToBlockType, store as blocksStore } from '@wordpress/blocks'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { BlockSettingsMenuControls } from '@wordpress/block-editor'; @@ -60,7 +60,7 @@ export default compose( [ getSelectedBlockClientIds, } = select( 'core/block-editor' ); - const { getGroupingBlockName } = select( 'core/blocks' ); + const { getGroupingBlockName } = select( blocksStore ); const clientIds = getSelectedBlockClientIds(); const groupingBlockName = getGroupingBlockName(); diff --git a/packages/editor/src/components/editor-notices/index.js b/packages/editor/src/components/editor-notices/index.js index fdab17a01b523c..734bb2dae2e015 100644 --- a/packages/editor/src/components/editor-notices/index.js +++ b/packages/editor/src/components/editor-notices/index.js @@ -9,6 +9,7 @@ import { filter } from 'lodash'; import { NoticeList, SnackbarList } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -52,9 +53,9 @@ export function EditorNotices( { notices, onRemove } ) { export default compose( [ withSelect( ( select ) => ( { - notices: select( 'core/notices' ).getNotices(), + notices: select( noticesStore ).getNotices(), } ) ), withDispatch( ( dispatch ) => ( { - onRemove: dispatch( 'core/notices' ).removeNotice, + onRemove: dispatch( noticesStore ).removeNotice, } ) ), ] )( EditorNotices ); diff --git a/packages/editor/src/components/entities-saved-states/index.js b/packages/editor/src/components/entities-saved-states/index.js index f7b0ee16ff06dc..d4d31398b94bc2 100644 --- a/packages/editor/src/components/entities-saved-states/index.js +++ b/packages/editor/src/components/entities-saved-states/index.js @@ -6,7 +6,7 @@ import { some, groupBy } from 'lodash'; /** * WordPress dependencies */ -import { Button } from '@wordpress/components'; +import { Button, withFocusReturn } from '@wordpress/components'; import { __, sprintf, _n } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; import { useState, useCallback } from '@wordpress/element'; @@ -30,18 +30,24 @@ const PLACEHOLDER_PHRASES = { // 0 is a back up, but should never be observed. 0: __( 'There are no changes.' ), /* translators: placeholders represent pre-translated singular/plural entity names (page, post, template, site, etc.) */ - 1: __( 'Changes have been made to your %s.' ), + 1: __( 'The following changes have been made to your %s.' ), /* translators: placeholders represent pre-translated singular/plural entity names (page, post, template, site, etc.) */ - 2: __( 'Changes have been made to your %1$s and %2$s.' ), + 2: __( 'The following changes have been made to your %1$s and %2$s.' ), /* translators: placeholders represent pre-translated singular/plural entity names (page, post, template, site, etc.) */ - 3: __( 'Changes have been made to your %1$s, %2$s, and %3$s.' ), + 3: __( + 'The following changes have been made to your %1$s, %2$s, and %3$s.' + ), /* translators: placeholders represent pre-translated singular/plural entity names (page, post, template, site, etc.) */ - 4: __( 'Changes have been made to your %1$s, %2$s, %3$s, and %4$s.' ), + 4: __( + 'The following changes have been made to your %1$s, %2$s, %3$s, and %4$s.' + ), /* translators: placeholders represent pre-translated singular/plural entity names (page, post, template, site, etc.) */ - 5: __( 'Changes have been made to your %1$s, %2$s, %3$s, %4$s, and %5$s.' ), + 5: __( + 'The following changes have been made to your %1$s, %2$s, %3$s, %4$s, and %5$s.' + ), }; -export default function EntitiesSavedStates( { isOpen, close } ) { +function EntitiesSavedStates( { isOpen, close } ) { const { dirtyEntityRecords } = useSelect( ( select ) => { return { dirtyEntityRecords: select( @@ -69,7 +75,7 @@ export default function EntitiesSavedStates( { isOpen, close } ) { const placeholderPhrase = PLACEHOLDER_PHRASES[ entityNamesForPrompt.length ] || // Fallback for edge case that should not be observed (more than 5 entity types edited). - __( 'Changes have been made to multiple entity types.' ); + __( 'The following changes have been made to multiple entities.' ); // eslint-disable-next-line @wordpress/valid-sprintf const promptPhrase = sprintf( placeholderPhrase, ...entityNamesForPrompt ); @@ -114,9 +120,6 @@ export default function EntitiesSavedStates( { isOpen, close } ) { } ); }; - const [ isReviewing, setIsReviewing ] = useState( false ); - const toggleIsReviewing = () => setIsReviewing( ( value ) => ! value ); - // Explicitly define this with no argument passed. Using `close` on // its own will use the event object in place of the expected saved entities. const dismissPanel = useCallback( () => close(), [ close ] ); @@ -146,31 +149,21 @@ export default function EntitiesSavedStates( { isOpen, close } ) { <div className="entities-saved-states__text-prompt"> <strong>{ __( 'Are you ready to save?' ) }</strong> <p>{ promptPhrase }</p> - <p> - <Button - onClick={ toggleIsReviewing } - isLink - className="entities-saved-states__review-changes-button" - > - { isReviewing - ? __( 'Hide changes.' ) - : __( 'Review changes.' ) } - </Button> - </p> </div> - { isReviewing && - partitionedSavables.map( ( list ) => { - return ( - <EntityTypeList - key={ list[ 0 ].name } - list={ list } - closePanel={ dismissPanel } - unselectedEntities={ unselectedEntities } - setUnselectedEntities={ setUnselectedEntities } - /> - ); - } ) } + { partitionedSavables.map( ( list ) => { + return ( + <EntityTypeList + key={ list[ 0 ].name } + list={ list } + closePanel={ dismissPanel } + unselectedEntities={ unselectedEntities } + setUnselectedEntities={ setUnselectedEntities } + /> + ); + } ) } </div> ) : null; } + +export default withFocusReturn( EntitiesSavedStates ); diff --git a/packages/editor/src/components/entities-saved-states/style.scss b/packages/editor/src/components/entities-saved-states/style.scss index 9a347550fc9ad9..e2e9203dec07ba 100644 --- a/packages/editor/src/components/entities-saved-states/style.scss +++ b/packages/editor/src/components/entities-saved-states/style.scss @@ -18,7 +18,6 @@ } @include break-medium() { - z-index: z-index(".entities-saved-states__panel {greater than small}"); top: $admin-bar-height; left: auto; width: $sidebar-width; @@ -57,7 +56,6 @@ } .entities-saved-states__text-prompt { - border-bottom: $border-width solid $gray-300; padding: $grid-unit-20; padding-bottom: $grid-unit-05; } diff --git a/packages/editor/src/components/global-keyboard-shortcuts/register-shortcuts.js b/packages/editor/src/components/global-keyboard-shortcuts/register-shortcuts.js index 4879a83d1d5ddd..ed65b49d3cc8c3 100644 --- a/packages/editor/src/components/global-keyboard-shortcuts/register-shortcuts.js +++ b/packages/editor/src/components/global-keyboard-shortcuts/register-shortcuts.js @@ -5,10 +5,11 @@ import { useEffect } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { BlockEditorKeyboardShortcuts } from '@wordpress/block-editor'; +import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; function EditorKeyboardShortcutsRegister() { // Registering the shortcuts - const { registerShortcut } = useDispatch( 'core/keyboard-shortcuts' ); + const { registerShortcut } = useDispatch( keyboardShortcutsStore ); useEffect( () => { registerShortcut( { name: 'core/editor/save', diff --git a/packages/editor/src/components/local-autosave-monitor/index.js b/packages/editor/src/components/local-autosave-monitor/index.js index aaf697bc70d946..80573eec4598ba 100644 --- a/packages/editor/src/components/local-autosave-monitor/index.js +++ b/packages/editor/src/components/local-autosave-monitor/index.js @@ -11,6 +11,7 @@ import { ifCondition, usePrevious } from '@wordpress/compose'; import { useSelect, useDispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { parse } from '@wordpress/blocks'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -62,7 +63,7 @@ function useAutosaveNotice() { [] ); - const { createWarningNotice, removeNotice } = useDispatch( 'core/notices' ); + const { createWarningNotice, removeNotice } = useDispatch( noticesStore ); const { editPost, resetEditorBlocks } = useDispatch( 'core/editor' ); useEffect( () => { diff --git a/packages/editor/src/components/post-publish-button/index.js b/packages/editor/src/components/post-publish-button/index.js index 46ebe017edbfd7..01d98a50541945 100644 --- a/packages/editor/src/components/post-publish-button/index.js +++ b/packages/editor/src/components/post-publish-button/index.js @@ -94,6 +94,7 @@ export class PostPublishButton extends Component { isPublished, isSaveable, isSaving, + isAutoSaving, isToggle, onSave, onStatusChange, @@ -147,7 +148,7 @@ export class PostPublishButton extends Component { const buttonProps = { 'aria-disabled': isButtonDisabled && ! hasNonPostEntityChanges, className: 'editor-post-publish-button', - isBusy: isSaving && isPublished, + isBusy: ! isAutoSaving && isSaving && isPublished, isPrimary: true, onClick: this.createOnClick( onClickButton ), }; @@ -197,6 +198,7 @@ export default compose( [ withSelect( ( select ) => { const { isSavingPost, + isAutosavingPost, isEditedPostBeingScheduled, getEditedPostVisibility, isCurrentPostPublished, @@ -208,8 +210,10 @@ export default compose( [ getCurrentPostId, hasNonPostEntityChanges, } = select( 'core/editor' ); + const _isAutoSaving = isAutosavingPost(); return { - isSaving: isSavingPost(), + isSaving: isSavingPost() || _isAutoSaving, + isAutoSaving: _isAutoSaving, isBeingScheduled: isEditedPostBeingScheduled(), visibility: getEditedPostVisibility(), isSaveable: isEditedPostSaveable(), diff --git a/packages/editor/src/components/post-saved-state/index.js b/packages/editor/src/components/post-saved-state/index.js index c3a0bf0c15c452..1103b0f7468785 100644 --- a/packages/editor/src/components/post-saved-state/index.js +++ b/packages/editor/src/components/post-saved-state/index.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { - __unstableUseAnimate as useAnimate, + __unstableGetAnimateClassName as getAnimateClassName, Button, } from '@wordpress/components'; import { usePrevious, useViewportMatch } from '@wordpress/compose'; @@ -76,8 +76,7 @@ export default function PostSavedState( { isSaveable: isEditedPostSaveable(), isScheduled: isCurrentPostScheduled(), hasPublishAction: - getCurrentPost()?.[ '_links' ]?.[ 'wp:action-publish' ] ?? - false, + getCurrentPost()?._links?.[ 'wp:action-publish' ] ?? false, }; }, [ forceIsDirty, forceIsSaving ] @@ -100,18 +99,21 @@ export default function PostSavedState( { return () => clearTimeout( timeoutId ); }, [ isSaving ] ); - const animateClassName = useAnimate( { type: 'loading' } ); - if ( isSaving ) { // TODO: Classes generation should be common across all return // paths of this function, including proper naming convention for // the "Save Draft" button. - const classes = classnames( 'editor-post-saved-state', 'is-saving', { - 'is-autosaving': isAutosaving, - } ); + const classes = classnames( + 'editor-post-saved-state', + 'is-saving', + getAnimateClassName( { type: 'loading' } ), + { + 'is-autosaving': isAutosaving, + } + ); return ( - <span className={ classnames( classes, animateClassName ) }> + <span className={ classes }> <Icon icon={ cloud } /> { isAutosaving ? __( 'Autosaving' ) : __( 'Saving' ) } </span> diff --git a/packages/editor/src/components/post-schedule/index.js b/packages/editor/src/components/post-schedule/index.js index 115d5b96f15781..06266f1bd68d9f 100644 --- a/packages/editor/src/components/post-schedule/index.js +++ b/packages/editor/src/components/post-schedule/index.js @@ -9,7 +9,6 @@ import { useRef } from '@wordpress/element'; export function PostSchedule( { date, onUpdateDate } ) { const ref = useRef(); - const settings = __experimentalGetSettings(); // To know if the current timezone is a 12 hour time with look for "a" in the time format // We also make sure this a is not escaped by a "/" diff --git a/packages/editor/src/components/post-schedule/label.js b/packages/editor/src/components/post-schedule/label.js index d8a2a1b47bc2ab..5f4c2cf7c5c777 100644 --- a/packages/editor/src/components/post-schedule/label.js +++ b/packages/editor/src/components/post-schedule/label.js @@ -2,22 +2,18 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; - -import { dateI18n, __experimentalGetSettings } from '@wordpress/date'; - +import { format, __experimentalGetSettings } from '@wordpress/date'; import { withSelect } from '@wordpress/data'; export function PostScheduleLabel( { date, isFloating } ) { - if ( isFloating || ! date ) { - return __( 'Immediately' ); - } - const settings = __experimentalGetSettings(); - return dateI18n( - `${ settings.formats.date } ${ settings.formats.time }`, - date - ); + return date && ! isFloating + ? format( + `${ settings.formats.date } ${ settings.formats.time }`, + date + ) + : __( 'Immediately' ); } export default withSelect( ( select ) => { diff --git a/packages/editor/src/components/post-title/index.js b/packages/editor/src/components/post-title/index.js index d98e32ee00650a..493931b5003e3e 100644 --- a/packages/editor/src/components/post-title/index.js +++ b/packages/editor/src/components/post-title/index.js @@ -61,7 +61,7 @@ export default function PostTitle() { isFocusMode: focusMode, hasFixedToolbar: _hasFixedToolbar, }; - }, [] ); + } ); useEffect( () => { const { ownerDocument } = ref.current; diff --git a/packages/editor/src/components/post-trash/check.js b/packages/editor/src/components/post-trash/check.js index dda336346a0cb8..bd4eb2f9efce62 100644 --- a/packages/editor/src/components/post-trash/check.js +++ b/packages/editor/src/components/post-trash/check.js @@ -18,7 +18,7 @@ export default withSelect( ( select ) => { const { getPostType, canUser } = select( 'core' ); const postId = getCurrentPostId(); const postType = getPostType( getCurrentPostType() ); - const resource = postType?.[ 'rest_base' ] || ''; + const resource = postType?.rest_base || ''; // eslint-disable-line camelcase return { isNew: isEditedPostNew(), diff --git a/packages/editor/src/components/post-visibility/style.scss b/packages/editor/src/components/post-visibility/style.scss index 78eddc859e6ac2..d43341ac3d1289 100644 --- a/packages/editor/src/components/post-visibility/style.scss +++ b/packages/editor/src/components/post-visibility/style.scss @@ -21,16 +21,19 @@ .editor-post-visibility__dialog-info { margin-top: 0; - margin-left: 28px; + margin-left: $grid-unit-40; } // Remove bottom margin on the last label only. .editor-post-visibility__choice:last-child .editor-post-visibility__dialog-info { margin-bottom: 0; } +} +.editor-post-visibility__dialog-password { .editor-post-visibility__dialog-password-input[type="text"] { @include input-control; - margin-left: 28px; + margin-left: $grid-unit * 4.5; + margin-top: $grid-unit-10; } } diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index afa93a98a3ca77..2f056ea3dbd727 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -1,145 +1,79 @@ -/** - * External dependencies - */ -import { map, pick, defaultTo, flatten, partialRight } from 'lodash'; -import memize from 'memize'; - /** * WordPress dependencies */ -import { compose } from '@wordpress/compose'; -import { Component } from '@wordpress/element'; -import { withDispatch, withSelect } from '@wordpress/data'; +import { useEffect, useLayoutEffect, useMemo } from '@wordpress/element'; +import { useDispatch, useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { EntityProvider } from '@wordpress/core-data'; import { BlockEditorProvider, BlockContextProvider, - __unstableEditorStyles as EditorStyles, } from '@wordpress/block-editor'; -import apiFetch from '@wordpress/api-fetch'; -import { addQueryArgs } from '@wordpress/url'; -import { decodeEntities } from '@wordpress/html-entities'; import { ReusableBlocksMenuItems } from '@wordpress/reusable-blocks'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies */ import withRegistryProvider from './with-registry-provider'; -import { mediaUpload } from '../../utils'; import ConvertToGroupButtons from '../convert-to-group-buttons'; - -/** - * Fetches link suggestions from the API. This function is an exact copy of a function found at: - * - * packages/edit-navigation/src/index.js - * - * It seems like there is no suitable package to import this from. Ideally it would be either part of core-data. - * Until we refactor it, just copying the code is the simplest solution. - * - * @param {string} search - * @param {Object} [searchArguments] - * @param {number} [searchArguments.isInitialSuggestions] - * @param {number} [searchArguments.type] - * @param {number} [searchArguments.subtype] - * @param {number} [searchArguments.page] - * @param {Object} [editorSettings] - * @param {boolean} [editorSettings.disablePostFormats=false] - * @return {Promise<Object[]>} List of suggestions - */ - -const fetchLinkSuggestions = async ( - search, - { isInitialSuggestions, type, subtype, page, perPage: perPageArg } = {}, - { disablePostFormats = false } = {} -) => { - const perPage = perPageArg || isInitialSuggestions ? 3 : 20; - - const queries = []; - - if ( ! type || type === 'post' ) { - queries.push( - apiFetch( { - path: addQueryArgs( '/wp/v2/search', { - search, - page, - per_page: perPage, - type: 'post', - subtype, - } ), - } ).catch( () => [] ) // fail by returning no results - ); - } - - if ( ! type || type === 'term' ) { - queries.push( - apiFetch( { - path: addQueryArgs( '/wp/v2/search', { - search, - page, - per_page: perPage, - type: 'term', - subtype, - } ), - } ).catch( () => [] ) - ); - } - - if ( ! disablePostFormats && ( ! type || type === 'post-format' ) ) { - queries.push( - apiFetch( { - path: addQueryArgs( '/wp/v2/search', { - search, - page, - per_page: perPage, - type: 'post-format', - subtype, - } ), - } ).catch( () => [] ) - ); - } - - return Promise.all( queries ).then( ( results ) => { - return map( - flatten( results ) - .filter( ( result ) => !! result.id ) - .slice( 0, perPage ), - ( result ) => ( { - id: result.id, - url: result.url, - title: decodeEntities( result.title ) || __( '(no title)' ), - type: result.subtype || result.type, - } ) - ); - } ); -}; - -class EditorProvider extends Component { - constructor( props ) { - super( ...arguments ); - - this.getBlockEditorSettings = memize( this.getBlockEditorSettings, { - maxSize: 1, - } ); - - this.getDefaultBlockContext = memize( this.getDefaultBlockContext, { - maxSize: 1, - } ); - +import usePostContentEditor from './use-post-content-editor'; +import { store as editorStore } from '../../store'; +import useBlockEditorSettings from './use-block-editor-settings'; + +function EditorProvider( { + __unstableTemplate, + post, + settings, + recovery, + initialEdits, + children, +} ) { + const defaultBlockContext = useMemo( () => { + if ( post.type === 'wp_template' ) { + return {}; + } + return { postId: post.id, postType: post.type }; + }, [ post.id, post.type ] ); + const { selectionEnd, selectionStart, isReady } = useSelect( ( select ) => { + const { + getEditorSelectionStart, + getEditorSelectionEnd, + __unstableIsEditorReady, + } = select( editorStore ); + return { + isReady: __unstableIsEditorReady(), + selectionStart: getEditorSelectionStart(), + selectionEnd: getEditorSelectionEnd(), + }; + }, [] ); + const { id, type } = __unstableTemplate ?? post; + const blockEditorProps = usePostContentEditor( type, id ); + const editorSettings = useBlockEditorSettings( + settings, + !! __unstableTemplate + ); + const { + updatePostLock, + setupEditor, + updateEditorSettings, + __experimentalTearDownEditor, + __unstableSetupTemplate, + } = useDispatch( editorStore ); + const { createWarningNotice } = useDispatch( noticesStore ); + + // Iniitialize and tear down the editor. + // Ideally this should be synced on each change and not just something you do once. + useLayoutEffect( () => { // Assume that we don't need to initialize in the case of an error recovery. - if ( props.recovery ) { + if ( recovery ) { return; } - props.updatePostLock( props.settings.postLock ); - props.setupEditor( - props.post, - props.initialEdits, - props.settings.template - ); - - if ( props.settings.autosave ) { - props.createWarningNotice( + updatePostLock( settings.postLock ); + setupEditor( post, initialEdits, settings.template ); + if ( settings.autosave ) { + createWarningNotice( __( 'There is an autosave of this post that is more recent than the version below.' ), @@ -148,214 +82,53 @@ class EditorProvider extends Component { actions: [ { label: __( 'View the autosave' ), - url: props.settings.autosave.editLink, + url: settings.autosave.editLink, }, ], } ); } - } - getBlockEditorSettings( - settings, - reusableBlocks, - hasUploadPermissions, - canUserUseUnfilteredHTML, - undo, - shouldInsertAtTheTop - ) { - return { - ...pick( settings, [ - '__experimentalBlockDirectory', - '__experimentalBlockPatterns', - '__experimentalBlockPatternCategories', - '__experimentalFeatures', - '__experimentalGlobalStylesUserEntityId', - '__experimentalGlobalStylesBaseStyles', - '__experimentalPreferredStyleVariations', - '__experimentalSetIsInserterOpened', - 'alignWide', - 'allowedBlockTypes', - 'availableLegacyWidgets', - 'bodyPlaceholder', - 'codeEditingEnabled', - 'colors', - 'disableCustomColors', - 'disableCustomFontSizes', - 'disableCustomGradients', - 'enableCustomUnits', - 'enableCustomLineHeight', - 'focusMode', - 'fontSizes', - 'gradients', - 'hasFixedToolbar', - 'hasReducedUI', - 'imageEditing', - 'imageSizes', - 'imageDimensions', - 'isRTL', - 'keepCaretInsideBlock', - 'maxWidth', - 'onUpdateDefaultBlockStyles', - 'styles', - 'template', - 'templateLock', - 'titlePlaceholder', - ] ), - mediaUpload: hasUploadPermissions ? mediaUpload : undefined, - __experimentalReusableBlocks: reusableBlocks, - __experimentalFetchLinkSuggestions: partialRight( - fetchLinkSuggestions, - settings - ), - __experimentalCanUserUseUnfilteredHTML: canUserUseUnfilteredHTML, - __experimentalUndo: undo, - __experimentalShouldInsertAtTheTop: shouldInsertAtTheTop, + return () => { + __experimentalTearDownEditor(); }; - } - - getDefaultBlockContext( postId, postType ) { - return { postId, postType }; - } + }, [] ); - componentDidMount() { - this.props.updateEditorSettings( this.props.settings ); - } + // Synchronize the editor settings as they change + useEffect( () => { + updateEditorSettings( settings ); + }, [ settings ] ); - componentDidUpdate( prevProps ) { - if ( this.props.settings !== prevProps.settings ) { - this.props.updateEditorSettings( this.props.settings ); + // Synchronize the template as it changes + useEffect( () => { + if ( __unstableTemplate ) { + __unstableSetupTemplate( __unstableTemplate ); } - } + }, [ __unstableTemplate?.id ] ); - componentWillUnmount() { - this.props.tearDownEditor(); + if ( ! isReady ) { + return null; } - render() { - const { - canUserUseUnfilteredHTML, - children, - post, - blocks, - resetEditorBlocks, - selectionStart, - selectionEnd, - isReady, - settings, - reusableBlocks, - resetEditorBlocksWithoutUndoLevel, - hasUploadPermissions, - isPostTitleSelected, - undo, - } = this.props; - - if ( ! isReady ) { - return null; - } - - const editorSettings = this.getBlockEditorSettings( - settings, - reusableBlocks, - hasUploadPermissions, - canUserUseUnfilteredHTML, - undo, - isPostTitleSelected - ); - - const defaultBlockContext = this.getDefaultBlockContext( - post.id, - post.type - ); - - return ( - <> - <EditorStyles styles={ settings.styles } /> - <EntityProvider kind="root" type="site"> - <EntityProvider - kind="postType" - type={ post.type } - id={ post.id } + return ( + <EntityProvider kind="root" type="site"> + <EntityProvider kind="postType" type={ post.type } id={ post.id }> + <BlockContextProvider value={ defaultBlockContext }> + <BlockEditorProvider + { ...blockEditorProps } + selectionStart={ selectionStart } + selectionEnd={ selectionEnd } + settings={ editorSettings } + useSubRegistry={ false } > - <BlockContextProvider value={ defaultBlockContext }> - <BlockEditorProvider - value={ blocks } - onInput={ resetEditorBlocksWithoutUndoLevel } - onChange={ resetEditorBlocks } - selectionStart={ selectionStart } - selectionEnd={ selectionEnd } - settings={ editorSettings } - useSubRegistry={ false } - > - { children } - <ReusableBlocksMenuItems /> - <ConvertToGroupButtons /> - </BlockEditorProvider> - </BlockContextProvider> - </EntityProvider> - </EntityProvider> - </> - ); - } + { children } + <ReusableBlocksMenuItems /> + <ConvertToGroupButtons /> + </BlockEditorProvider> + </BlockContextProvider> + </EntityProvider> + </EntityProvider> + ); } -export default compose( [ - withRegistryProvider, - withSelect( ( select ) => { - const { - canUserUseUnfilteredHTML, - __unstableIsEditorReady: isEditorReady, - getEditorBlocks, - getEditorSelectionStart, - getEditorSelectionEnd, - isPostTitleSelected, - } = select( 'core/editor' ); - const { canUser } = select( 'core' ); - - return { - canUserUseUnfilteredHTML: canUserUseUnfilteredHTML(), - isReady: isEditorReady(), - blocks: getEditorBlocks(), - selectionStart: getEditorSelectionStart(), - selectionEnd: getEditorSelectionEnd(), - reusableBlocks: select( 'core' ).getEntityRecords( - 'postType', - 'wp_block', - { per_page: -1 } - ), - hasUploadPermissions: defaultTo( - canUser( 'create', 'media' ), - true - ), - // This selector is only defined on mobile. - isPostTitleSelected: isPostTitleSelected && isPostTitleSelected(), - }; - } ), - withDispatch( ( dispatch ) => { - const { - setupEditor, - updatePostLock, - resetEditorBlocks, - updateEditorSettings, - __experimentalTearDownEditor, - undo, - } = dispatch( 'core/editor' ); - const { createWarningNotice } = dispatch( 'core/notices' ); - - return { - setupEditor, - updatePostLock, - createWarningNotice, - resetEditorBlocks, - updateEditorSettings, - resetEditorBlocksWithoutUndoLevel( blocks, options ) { - resetEditorBlocks( blocks, { - ...options, - __unstableShouldCreateUndoLevel: false, - } ); - }, - tearDownEditor: __experimentalTearDownEditor, - undo, - }; - } ), -] )( EditorProvider ); +export default withRegistryProvider( EditorProvider ); diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js new file mode 100644 index 00000000000000..9338189d1ecec0 --- /dev/null +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -0,0 +1,208 @@ +/** + * External dependencies + */ +import { map, pick, defaultTo, flatten, partialRight } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useMemo } from '@wordpress/element'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { store as coreStore } from '@wordpress/core-data'; +import apiFetch from '@wordpress/api-fetch'; +import { addQueryArgs } from '@wordpress/url'; +import { decodeEntities } from '@wordpress/html-entities'; + +/** + * Internal dependencies + */ +import { mediaUpload } from '../../utils'; +import { store as editorStore } from '../../store'; + +/** + * Fetches link suggestions from the API. This function is an exact copy of a function found at: + * + * packages/edit-navigation/src/index.js + * + * It seems like there is no suitable package to import this from. Ideally it would be either part of core-data. + * Until we refactor it, just copying the code is the simplest solution. + * + * @param {string} search + * @param {Object} [searchArguments] + * @param {number} [searchArguments.isInitialSuggestions] + * @param {number} [searchArguments.type] + * @param {number} [searchArguments.subtype] + * @param {number} [searchArguments.page] + * @param {Object} [editorSettings] + * @param {boolean} [editorSettings.disablePostFormats=false] + * @return {Promise<Object[]>} List of suggestions + */ + +const fetchLinkSuggestions = async ( + search, + { isInitialSuggestions, type, subtype, page, perPage: perPageArg } = {}, + { disablePostFormats = false } = {} +) => { + const perPage = perPageArg || isInitialSuggestions ? 3 : 20; + + const queries = []; + + if ( ! type || type === 'post' ) { + queries.push( + apiFetch( { + path: addQueryArgs( '/wp/v2/search', { + search, + page, + per_page: perPage, + type: 'post', + subtype, + } ), + } ).catch( () => [] ) // fail by returning no results + ); + } + + if ( ! type || type === 'term' ) { + queries.push( + apiFetch( { + path: addQueryArgs( '/wp/v2/search', { + search, + page, + per_page: perPage, + type: 'term', + subtype, + } ), + } ).catch( () => [] ) + ); + } + + if ( ! disablePostFormats && ( ! type || type === 'post-format' ) ) { + queries.push( + apiFetch( { + path: addQueryArgs( '/wp/v2/search', { + search, + page, + per_page: perPage, + type: 'post-format', + subtype, + } ), + } ).catch( () => [] ) + ); + } + + return Promise.all( queries ).then( ( results ) => { + return map( + flatten( results ) + .filter( ( result ) => !! result.id ) + .slice( 0, perPage ), + ( result ) => ( { + id: result.id, + url: result.url, + title: decodeEntities( result.title ) || __( '(no title)' ), + type: result.subtype || result.type, + } ) + ); + } ); +}; + +/** + * React hook used to compute the block editor settings to use for the post editor. + * + * @param {Object} settings EditorProvider settings prop. + * @param {boolean} hasTemplate Whether template mode is enabled. + * + * @return {Object} Block Editor Settings. + */ +function useBlockEditorSettings( settings, hasTemplate ) { + const { + reusableBlocks, + hasUploadPermissions, + canUseUnfilteredHTML, + isTitleSelected, + } = useSelect( ( select ) => { + const { canUserUseUnfilteredHTML, isPostTitleSelected } = select( + editorStore + ); + const { canUser } = select( coreStore ); + + return { + canUseUnfilteredHTML: canUserUseUnfilteredHTML(), + reusableBlocks: select( 'core' ).getEntityRecords( + 'postType', + 'wp_block', + { per_page: -1 } + ), + hasUploadPermissions: defaultTo( + canUser( 'create', 'media' ), + true + ), + // This selector is only defined on mobile. + isTitleSelected: isPostTitleSelected && isPostTitleSelected(), + }; + }, [] ); + + const { undo } = useDispatch( editorStore ); + + return useMemo( + () => ( { + ...pick( settings, [ + '__experimentalBlockDirectory', + '__experimentalBlockPatterns', + '__experimentalBlockPatternCategories', + '__experimentalFeatures', + '__experimentalGlobalStylesUserEntityId', + '__experimentalGlobalStylesBaseStyles', + '__experimentalPreferredStyleVariations', + '__experimentalSetIsInserterOpened', + 'alignWide', + 'allowedBlockTypes', + 'availableLegacyWidgets', + 'bodyPlaceholder', + 'codeEditingEnabled', + 'colors', + 'disableCustomColors', + 'disableCustomFontSizes', + 'disableCustomGradients', + 'enableCustomUnits', + 'enableCustomLineHeight', + 'focusMode', + 'fontSizes', + 'gradients', + 'hasFixedToolbar', + 'hasReducedUI', + 'imageEditing', + 'imageSizes', + 'imageDimensions', + 'isRTL', + 'keepCaretInsideBlock', + 'maxWidth', + 'onUpdateDefaultBlockStyles', + 'styles', + 'template', + 'templateLock', + 'titlePlaceholder', + ] ), + mediaUpload: hasUploadPermissions ? mediaUpload : undefined, + __experimentalReusableBlocks: reusableBlocks, + __experimentalFetchLinkSuggestions: partialRight( + fetchLinkSuggestions, + settings + ), + __experimentalCanUserUseUnfilteredHTML: canUseUnfilteredHTML, + __experimentalUndo: undo, + __experimentalShouldInsertAtTheTop: isTitleSelected, + outlineMode: hasTemplate, + } ), + [ + settings, + hasUploadPermissions, + reusableBlocks, + canUseUnfilteredHTML, + undo, + isTitleSelected, + hasTemplate, + ] + ); +} + +export default useBlockEditorSettings; diff --git a/packages/editor/src/components/provider/use-post-content-editor.js b/packages/editor/src/components/provider/use-post-content-editor.js new file mode 100644 index 00000000000000..d220803eb287e8 --- /dev/null +++ b/packages/editor/src/components/provider/use-post-content-editor.js @@ -0,0 +1,77 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { useCallback } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import serializeBlocks from '../../store/utils/serialize-blocks'; + +/** + * This hook provides the required props to edit the "content" of a given postType and postId. + * + * @param {string} postType Post Type. + * @param {string} postId Post Id. + * + * @return {Object} BlockEditorProvider props. + */ +function usePostContentEditor( postType, postId ) { + const blocks = useSelect( + ( select ) => { + const { getEditedEntityRecord } = select( coreStore ); + return getEditedEntityRecord( 'postType', postType, postId ).blocks; + }, + [ postType, postId ] + ); + const { __unstableCreateUndoLevel, editEntityRecord } = useDispatch( + coreStore + ); + + const onChange = useCallback( + ( newBlocks, options ) => { + const { + __unstableShouldCreateUndoLevel, + selectionStart, + selectionEnd, + } = options; + const edits = { blocks: newBlocks, selectionStart, selectionEnd }; + + if ( __unstableShouldCreateUndoLevel !== false ) { + const noChange = blocks === edits.blocks; + if ( noChange ) { + return __unstableCreateUndoLevel( + 'postType', + postType, + postId + ); + } + + // We create a new function here on every persistent edit + // to make sure the edit makes the post dirty and creates + // a new undo level. + edits.content = ( { blocks: blocksForSerialization = [] } ) => + serializeBlocks( blocksForSerialization ); + } + + editEntityRecord( 'postType', postType, postId, edits ); + }, + [ blocks, postId, postType ] + ); + + const onInput = useCallback( + ( newBlocks, options ) => { + onChange( newBlocks, { + ...options, + __unstableShouldCreateUndoLevel: false, + } ); + }, + [ onChange ] + ); + + return { value: blocks, onChange, onInput }; +} + +export default usePostContentEditor; diff --git a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js index 51d5bf4ac5fda0..a32597790eb123 100644 --- a/packages/editor/src/hooks/custom-sources-backwards-compatibility.js +++ b/packages/editor/src/hooks/custom-sources-backwards-compatibility.js @@ -6,6 +6,7 @@ import { pickBy, mapValues, isEmpty, mapKeys } from 'lodash'; /** * WordPress dependencies */ +import { store as blocksStore } from '@wordpress/blocks'; import { select as globalSelect, useSelect } from '@wordpress/data'; import { useEntityProp } from '@wordpress/core-data'; import { useMemo } from '@wordpress/element'; @@ -135,7 +136,7 @@ addFilter( // // In the future, we could support updating block settings, at which point this // implementation could use that mechanism instead. -globalSelect( 'core/blocks' ) +globalSelect( blocksStore ) .getBlockTypes() - .map( ( { name } ) => globalSelect( 'core/blocks' ).getBlockType( name ) ) + .map( ( { name } ) => globalSelect( blocksStore ).getBlockType( name ) ) .forEach( shimAttributeSource ); diff --git a/packages/editor/src/index.js b/packages/editor/src/index.js index 0a58cd215cd754..d84cb1fcf21b4d 100644 --- a/packages/editor/src/index.js +++ b/packages/editor/src/index.js @@ -2,11 +2,7 @@ * WordPress dependencies */ import '@wordpress/block-editor'; -import '@wordpress/blocks'; import '@wordpress/core-data'; -import '@wordpress/keyboard-shortcuts'; -import '@wordpress/notices'; -import '@wordpress/reusable-blocks'; import '@wordpress/rich-text'; /** diff --git a/packages/editor/src/index.native.js b/packages/editor/src/index.native.js index d3d3d52c24cee3..35fcddf0b80030 100644 --- a/packages/editor/src/index.native.js +++ b/packages/editor/src/index.native.js @@ -2,7 +2,6 @@ * WordPress dependencies */ import '@wordpress/block-editor'; -import '@wordpress/blocks'; import '@wordpress/core-data'; import '@wordpress/rich-text'; diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index b369c28f9a3185..29366827a659d2 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -10,15 +10,12 @@ import deprecated from '@wordpress/deprecated'; import { controls } from '@wordpress/data'; import { apiFetch } from '@wordpress/data-controls'; import { parse, synchronizeBlocksWithTemplate } from '@wordpress/blocks'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies */ -import { - STORE_NAME, - POST_UPDATE_TRANSACTION_ID, - TRASH_POST_NOTICE_ID, -} from './constants'; +import { STORE_NAME, TRASH_POST_NOTICE_ID } from './constants'; import { getNotificationArgumentsForSaveSuccess, getNotificationArgumentsForSaveFail, @@ -54,7 +51,6 @@ export function* setupEditor( post, edits, template ) { } yield resetPost( post ); - yield { type: 'SETUP_EDITOR', post, @@ -65,7 +61,6 @@ export function* setupEditor( post, edits, template ) { __unstableShouldCreateUndoLevel: false, } ); yield setupEditorState( post ); - if ( edits && Object.keys( edits ).some( @@ -78,6 +73,27 @@ export function* setupEditor( post, edits, template ) { } } +/** + * Initiliazes an FSE template into the core-data store. + * We could avoid this action entirely by having a fallback if the edit is undefined. + * + * @param {Object} template Template object. + */ +export function* __unstableSetupTemplate( template ) { + const blocks = parse( template.content.raw ); + yield controls.dispatch( + 'core', + 'editEntityRecord', + 'postType', + template.type, + template.id, + { + blocks, + }, + { __unstableShouldCreateUndoLevel: false } + ); +} + /** * Returns an action object signalling that the editor is being destroyed and * that any necessary state or side-effect cleanup should occur. @@ -158,14 +174,15 @@ export function __experimentalRequestPostUpdateFinish( options = {} ) { * Returns an action object used in signalling that a patch of updates for the * latest version of the post have been received. * - * @param {Object} edits Updated post fields. - * * @return {Object} Action object. + * @deprecated since Gutenberg 9.7.0. */ -export function updatePost( edits ) { +export function updatePost() { + deprecated( "wp.data.dispatch( 'core/editor' ).updatePost", { + alternative: 'User the core entitires store instead', + } ); return { - type: 'UPDATE_POST', - edits, + type: 'DO_NOTHING', }; } @@ -206,21 +223,6 @@ export function* editPost( edits, options ) { ); } -/** - * Returns action object produced by the updatePost creator augmented by - * an optimist option that signals optimistically applying updates. - * - * @param {Object} edits Updated post fields. - * - * @return {Object} Action object. - */ -export function __experimentalOptimisticUpdatePost( edits ) { - return { - ...updatePost( edits ), - optimist: { id: POST_UPDATE_TRANSACTION_ID }, - }; -} - /** * Action generator for saving the current post in the editor. * @@ -280,7 +282,7 @@ export function* savePost( options = {} ) { } ); if ( args.length ) { yield controls.dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', ...args ); @@ -302,7 +304,7 @@ export function* savePost( options = {} ) { } ); if ( args.length ) { yield controls.dispatch( - 'core/notices', + noticesStore, 'createSuccessNotice', ...args ); @@ -356,7 +358,7 @@ export function* trashPost() { postTypeSlug ); yield controls.dispatch( - 'core/notices', + noticesStore, 'removeNotice', TRASH_POST_NOTICE_ID ); @@ -370,7 +372,7 @@ export function* trashPost() { yield controls.dispatch( STORE_NAME, 'savePost' ); } catch ( error ) { yield controls.dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', ...getNotificationArgumentsForTrashFail( { error } ) ); diff --git a/packages/editor/src/store/constants.js b/packages/editor/src/store/constants.js index 073efe20455410..7882ba53e64db3 100644 --- a/packages/editor/src/store/constants.js +++ b/packages/editor/src/store/constants.js @@ -13,7 +13,6 @@ export const EDIT_MERGE_PROPERTIES = new Set( [ 'meta' ] ); */ export const STORE_NAME = 'core/editor'; -export const POST_UPDATE_TRANSACTION_ID = 'post-update'; export const SAVE_POST_NOTICE_ID = 'SAVE_POST_NOTICE_ID'; export const TRASH_POST_NOTICE_ID = 'TRASH_POST_NOTICE_ID'; export const PERMALINK_POSTNAME_REGEX = /%(?:postname|pagename)%/; diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index 071b2492945aa3..07b2a74376a252 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import optimist from 'redux-optimist'; import { omit, keys, isEqual } from 'lodash'; /** @@ -86,7 +85,6 @@ export function postId( state = null, action ) { switch ( action.type ) { case 'SETUP_EDITOR_STATE': case 'RESET_POST': - case 'UPDATE_POST': return action.post.id; } @@ -97,7 +95,6 @@ export function postType( state = null, action ) { switch ( action.type ) { case 'SETUP_EDITOR_STATE': case 'RESET_POST': - case 'UPDATE_POST': return action.post.type; } @@ -284,17 +281,15 @@ export function editorSettings( state = EDITOR_SETTINGS_DEFAULTS, action ) { return state; } -export default optimist( - combineReducers( { - postId, - postType, - preferences, - saving, - postLock, - template, - postSavingLock, - isReady, - editorSettings, - postAutosavingLock, - } ) -); +export default combineReducers( { + postId, + postType, + preferences, + saving, + postLock, + template, + postSavingLock, + isReady, + editorSettings, + postAutosavingLock, +} ); diff --git a/packages/editor/src/store/reducer.native.js b/packages/editor/src/store/reducer.native.js index 3036cb5704a480..d9b3fbcd67888a 100644 --- a/packages/editor/src/store/reducer.native.js +++ b/packages/editor/src/store/reducer.native.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import optimist from 'redux-optimist'; - /** * WordPress dependencies */ @@ -85,19 +80,17 @@ export function notices( state = [], action ) { return state; } -export default optimist( - combineReducers( { - postId, - postType, - postTitle, - preferences, - saving, - postLock, - postSavingLock, - template, - isReady, - editorSettings, - clipboard, - notices, - } ) -); +export default combineReducers( { + postId, + postType, + postTitle, + preferences, + saving, + postLock, + postSavingLock, + template, + isReady, + editorSettings, + clipboard, + notices, +} ); diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index 337d560998c5cf..9c5a92d25da234 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -33,7 +33,6 @@ import { Platform } from '@wordpress/element'; import { PREFERENCES_DEFAULTS } from './defaults'; import { EDIT_MERGE_PROPERTIES, - POST_UPDATE_TRANSACTION_ID, PERMALINK_POSTNAME_REGEX, ONE_MINUTE_IN_MS, AUTOSAVE_PROPERTIES, @@ -1001,26 +1000,6 @@ export const getEditedPostContent = createRegistrySelector( } ); -/** - * Returns state object prior to a specified optimist transaction ID, or `null` - * if the transaction corresponding to the given ID cannot be found. - * - * @param {Object} state Current global application state. - * @param {Object} transactionId Optimist transaction ID. - * - * @return {Object} Global application state prior to transaction. - */ -export function getStateBeforeOptimisticTransaction( state, transactionId ) { - const transaction = find( - state.optimist, - ( entry ) => - entry.beforeState && - get( entry.action, [ 'optimist', 'id' ] ) === transactionId - ); - - return transaction ? transaction.beforeState : null; -} - /** * Returns true if the post is being published, or false otherwise. * @@ -1029,28 +1008,10 @@ export function getStateBeforeOptimisticTransaction( state, transactionId ) { * @return {boolean} Whether post is being published. */ export function isPublishingPost( state ) { - if ( ! isSavingPost( state ) ) { - return false; - } - - // Saving is optimistic, so assume that current post would be marked as - // published if publishing - if ( ! isCurrentPostPublished( state ) ) { - return false; - } - - // Use post update transaction ID to retrieve the state prior to the - // optimistic transaction - const stateBeforeRequest = getStateBeforeOptimisticTransaction( - state, - POST_UPDATE_TRANSACTION_ID - ); - - // Consider as publishing when current post prior to request was not - // considered published return ( - !! stateBeforeRequest && - ! isCurrentPostPublished( null, stateBeforeRequest.currentPost ) + isSavingPost( state ) && + ! isCurrentPostPublished( state ) && + getEditedPostAttribute( state, 'status' ) === 'publish' ); } @@ -1142,28 +1103,6 @@ export function getPermalinkParts( state ) { }; } -/** - * Returns true if an optimistic transaction is pending commit, for which the - * before state satisfies the given predicate function. - * - * @param {Object} state Editor state. - * @param {Function} predicate Function given state, returning true if match. - * - * @return {boolean} Whether predicate matches for some history. - */ -export function inSomeHistory( state, predicate ) { - const { optimist } = state; - - // In recursion, optimist state won't exist. Assume exhausted options. - if ( ! optimist ) { - return false; - } - - return optimist.some( - ( { beforeState } ) => beforeState && predicate( beforeState ) - ); -} - /** * Returns whether the post is locked. * @@ -1325,6 +1264,32 @@ export function getEditorSettings( state ) { * Backward compatibility */ +/** + * Returns state object prior to a specified optimist transaction ID, or `null` + * if the transaction corresponding to the given ID cannot be found. + * + * @deprecated since Gutenberg 9.7.0. + */ +export function getStateBeforeOptimisticTransaction() { + deprecated( "select('core/editor').getStateBeforeOptimisticTransaction", { + hint: 'No state history is kept on this store anymore', + } ); + + return null; +} +/** + * Returns true if an optimistic transaction is pending commit, for which the + * before state satisfies the given predicate function. + * + * @deprecated since Gutenberg 9.7.0. + */ +export function inSomeHistory() { + deprecated( "select('core/editor').inSomeHistory", { + hint: 'No state history is kept on this store anymore', + } ); + return false; +} + function getBlockEditorSelector( name ) { return createRegistrySelector( ( select ) => ( state, ...args ) => { deprecated( "`wp.data.select( 'core/editor' )." + name + '`', { diff --git a/packages/editor/src/store/test/actions.js b/packages/editor/src/store/test/actions.js index 90da7d94b3094f..822d653676869c 100644 --- a/packages/editor/src/store/test/actions.js +++ b/packages/editor/src/store/test/actions.js @@ -3,16 +3,13 @@ */ import { apiFetch } from '@wordpress/data-controls'; import { controls } from '@wordpress/data'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies */ import * as actions from '../actions'; -import { - STORE_NAME, - TRASH_POST_NOTICE_ID, - POST_UPDATE_TRANSACTION_ID, -} from '../constants'; +import { STORE_NAME, TRASH_POST_NOTICE_ID } from '../constants'; const postType = { rest_base: 'posts', @@ -195,7 +192,7 @@ describe( 'Post generator actions', () => { const { value } = fulfillment.next( postType ); expect( value ).toEqual( controls.dispatch( - 'core/notices', + noticesStore, 'createSuccessNotice', 'Updated Post', { @@ -310,7 +307,7 @@ describe( 'Post generator actions', () => { const { value } = fulfillment.next( postType ); expect( value ).toEqual( controls.dispatch( - 'core/notices', + noticesStore, 'removeNotice', TRASH_POST_NOTICE_ID ) @@ -338,7 +335,7 @@ describe( 'Post generator actions', () => { const { value } = fulfillment.throw( error ); expect( value ).toEqual( controls.dispatch( - 'core/notices', + noticesStore, 'createErrorNotice', 'Trashing failed', { @@ -458,17 +455,6 @@ describe( 'Editor actions', () => { } ); } ); - describe( 'updatePost', () => { - it( 'should return the UPDATE_POST action', () => { - const edits = {}; - const result = actions.updatePost( edits ); - expect( result ).toEqual( { - type: 'UPDATE_POST', - edits, - } ); - } ); - } ); - describe( 'editPost', () => { it( 'should edit the relevant entity record', () => { const edits = { format: 'sample' }; @@ -497,18 +483,6 @@ describe( 'Editor actions', () => { } ); } ); - describe( 'optimisticUpdatePost', () => { - it( 'should return the UPDATE_POST action with optimist property', () => { - const edits = {}; - const result = actions.__experimentalOptimisticUpdatePost( edits ); - expect( result ).toEqual( { - type: 'UPDATE_POST', - edits, - optimist: { id: POST_UPDATE_TRANSACTION_ID }, - } ); - } ); - } ); - describe( 'redo', () => { it( 'should yield the REDO action', () => { const fulfillment = actions.redo(); diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js index d7ef6c5de3293b..e886d019a81f90 100644 --- a/packages/editor/src/store/test/selectors.js +++ b/packages/editor/src/store/test/selectors.js @@ -22,7 +22,6 @@ import { RawHTML } from '@wordpress/element'; */ import * as _selectors from '../selectors'; import { PREFERENCES_DEFAULTS } from '../defaults'; -import { POST_UPDATE_TRANSACTION_ID } from '../constants'; const selectors = { ..._selectors }; const selectorNames = Object.keys( selectors ); @@ -116,7 +115,7 @@ selectorNames.forEach( ( name ) => { selectorNames.forEach( ( otherName ) => { if ( _selectors[ otherName ].isRegistrySelector ) { - _selectors[ otherName ].__unstableGetSelect = select; + _selectors[ otherName ].registry = { select }; } } ); @@ -125,8 +124,9 @@ selectorNames.forEach( ( name ) => { selectors[ name ].isRegistrySelector = _selectors[ name ].isRegistrySelector; if ( selectors[ name ].isRegistrySelector ) { - selectors[ name ].__unstableGetSelect = - _selectors[ name ].__unstableGetSelect; + selectors[ name ].registry = { + select: () => _selectors[ name ].registry.select(), + }; } } ); const { @@ -160,8 +160,6 @@ const { didPostSaveRequestFail, getSuggestedPostFormat, getEditedPostContent, - getStateBeforeOptimisticTransaction, - isPublishingPost, isPublishSidebarEnabled, isPermalinkEditable, getPermalink, @@ -414,7 +412,6 @@ describe( 'selectors', () => { describe( 'isEditedPostDirty', () => { it( 'should return false when blocks state not dirty nor edits exist', () => { const state = { - optimist: [], editor: { present: { blocks: { @@ -431,7 +428,6 @@ describe( 'selectors', () => { it( 'should return true when blocks state dirty', () => { const state = { - optimist: [], editor: { present: { blocks: { @@ -448,7 +444,6 @@ describe( 'selectors', () => { it( 'should return true when edits exist', () => { const state = { - optimist: [], editor: { present: { blocks: { @@ -2502,163 +2497,6 @@ describe( 'selectors', () => { } ); } ); - describe( 'getStateBeforeOptimisticTransaction', () => { - it( 'should return null if no transaction can be found', () => { - const beforeState = getStateBeforeOptimisticTransaction( - { - optimist: [], - }, - 'foo' - ); - - expect( beforeState ).toBe( null ); - } ); - - it( 'should return null if a transaction with ID can be found, but lacks before state', () => { - const beforeState = getStateBeforeOptimisticTransaction( - { - optimist: [ - { - action: { - optimist: { - id: 'foo', - }, - }, - }, - ], - }, - 'foo' - ); - - expect( beforeState ).toBe( null ); - } ); - - it( 'should return the before state matching the given transaction id', () => { - const expectedBeforeState = {}; - const beforeState = getStateBeforeOptimisticTransaction( - { - optimist: [ - { - beforeState: expectedBeforeState, - action: { - optimist: { - id: 'foo', - }, - }, - }, - ], - }, - 'foo' - ); - - expect( beforeState ).toBe( expectedBeforeState ); - } ); - } ); - - describe( 'isPublishingPost', () => { - it( 'should return false if the post is not being saved', () => { - const isPublishing = isPublishingPost( { - optimist: [], - saving: { - requesting: false, - }, - currentPost: { - status: 'publish', - }, - } ); - - expect( isPublishing ).toBe( false ); - } ); - - it( 'should return false if the current post is not considered published', () => { - const isPublishing = isPublishingPost( { - optimist: [], - saving: { - requesting: true, - }, - currentPost: { - status: 'draft', - }, - } ); - - expect( isPublishing ).toBe( false ); - } ); - - it( 'should return false if the optimistic transaction cannot be found', () => { - const isPublishing = isPublishingPost( { - optimist: [], - saving: { - requesting: true, - }, - currentPost: { - status: 'publish', - }, - } ); - - expect( isPublishing ).toBe( false ); - } ); - - it( 'should return false if the current post prior to request was already published', () => { - const isPublishing = isPublishingPost( { - optimist: [ - { - beforeState: { - saving: { - requesting: false, - }, - currentPost: { - status: 'publish', - }, - }, - action: { - optimist: { - id: POST_UPDATE_TRANSACTION_ID, - }, - }, - }, - ], - saving: { - requesting: true, - }, - currentPost: { - status: 'publish', - }, - } ); - - expect( isPublishing ).toBe( false ); - } ); - - it( 'should return true if the current post prior to request was not published', () => { - const isPublishing = isPublishingPost( { - optimist: [ - { - beforeState: { - saving: { - requesting: false, - }, - currentPost: { - status: 'draft', - }, - }, - action: { - optimist: { - id: POST_UPDATE_TRANSACTION_ID, - }, - }, - }, - ], - saving: { - requesting: true, - }, - currentPost: { - status: 'publish', - }, - } ); - - expect( isPublishing ).toBe( true ); - } ); - } ); - describe( 'isPermalinkEditable', () => { it( 'should be false if there is no permalink', () => { const state = { diff --git a/packages/element/package.json b/packages/element/package.json index 834e077d676a21..b08290de1f1260 100644 --- a/packages/element/package.json +++ b/packages/element/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/element", - "version": "2.18.0", + "version": "2.19.0", "description": "Element React module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -25,7 +25,7 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@types/react": "^16.9.0", "@types/react-dom": "^16.9.0", "@wordpress/escape-html": "file:../escape-html", diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index 093b73e90686b9..10f295f27e2aa5 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,9 +2,26 @@ ## Unreleased +## 3.0.0 (2020-12-17) + +### Breaking Changes + +- `wp-env start` is now the only command which writes to the docker configuration files. Previously, running any command would also parse the config and then write it to the correct location. Now, other commands still parse the config, but they will not overwrite the confugiration which was set by wp-env start. This allows parameters to be passed to wp-env start which can affect the configuration. + ### Enhancement -- Update nodegit dependency to 0.27.0, the earlier version does not have pre-built binaries for Node 14.15.0 LTS. Upgrading provides support without requiring building nodegit locally. +- Update nodegit dependency to 0.27.0, the earlier version does not have pre-built binaries for Node 14.15.0 LTS. Upgrading provides support without requiring building nodegit locally. +- Allow WP_HOME wp-config value to be set to a custom port other than the default for the docker instance. +- Append the instance URL to output of `wp-env start`. + +### New feature + +- Add support for setting the PHP version used for the WordPress instance. For example, test PHP 8 with `"phpVersion": 8.0` in wp-env.json. +- Add Xdebug 3 to the development environment. You can enable Xdebug with `wp-env start --xdebug` (for debug mode) or `wp-env start --xdebug=develop,coverage` for custom modes. + +### Bug Fixes + +- ZIP-based plugin sources are now downloaded to a directory using the basename of the URL instead of the full URL path. This prevents HTML encoded characters in the URL (like "/") from being improperly encoded into the filesystem. This fixes the issue where many .zip sources broke because files with these badly formatted characters were not loaded as assets. ## 2.0.0 (2020-09-03) diff --git a/packages/env/README.md b/packages/env/README.md index e3533179c196fd..d5667f6753a147 100644 --- a/packages/env/README.md +++ b/packages/env/README.md @@ -188,6 +188,51 @@ wp-env start --debug ... ``` +## Using Xdebug + +Xdebug is installed in the wp-env environment, but it is turned off by default. To enable Xdebug, you can use the `--xdebug` flag with the `wp-env start` command. Here is a reference to how the flag works: + +```sh +# Sets the Xdebug mode to "debug" (for step debugging): +wp-env start --xdebug + +# Sets the Xdebug mode to "off": +wp-env start + +# Enables each of the Xdebug modes listed: +wp-env start --xdebug=profile,trace,debug +``` + +You can see a reference on each of the Xdebug modes and what they do in the [Xdebug documentation](https://xdebug.org/docs/all_settings#mode). + +### Xdebug IDE support + +To connect to Xdebug from your IDE, you can use these IDE settings. This bit of JSON was tested for VS Code's `launch.json` format (which you can [learn more about here](https://code.visualstudio.com/docs/editor/debugging#_launchjson-attributes)) along with [this PHP Debug extension](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug). Its path mapping also points to a specific plugin -- you should update this to point to the source you are working with inside of the wp-env instance. + +You should only have to translate `port` and `pathMappings` to the format used by your own IDE. + +```json +{ + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9003, + "pathMappings": { + "/var/www/html/wp-content/plugins/gutenberg": "${workspaceRoot}/" + } +} +``` + +Once your IDEs Xdebug settings have been enabled, you should just have to launch the debugger, put a breakpoint on any line of PHP code, and then refresh your browser! + +Here is a summary: + +1. Start wp-env with xdebug enabled: `wp-env start --xdebug` +2. Install a suitable Xdebug extension for your IDE if it does not include one already. +3. Configure the IDE debugger to use port `9003` and the correct source files in wp-env. +4. Launch the debugger and put a breakpoint on any line of PHP code. +5. Refresh the URL wp-env is running at and the breakpoint should trigger. + ## Command reference `wp-env` creates generated files in the `wp-env` home directory. By default, this is `~/.wp-env`. The exception is Linux, where files are placed at `~/wp-env` [for compatibility with Snap Packages](https://github.com/WordPress/gutenberg/issues/20180#issuecomment-587046325). The `wp-env` home directory contains a subdirectory for each project named `/$md5_of_project_path`. To change the `wp-env` home directory, set the `WP_ENV_HOME` environment variable. For example, running `WP_ENV_HOME="something" wp-env start` will download the project files to the directory `./something/$md5_of_project_path` (relative to the current directory). @@ -202,12 +247,20 @@ wp-env start Starts WordPress for development on port 8888 (override with WP_ENV_PORT) and tests on port 8889 (override with WP_ENV_TESTS_PORT). The current working directory must be a WordPress installation, a plugin, a theme, or contain a -.wp-env.json file. After first install, use the '--update' flag to download updates -to mapped sources and to re-apply WordPress configuration options. +.wp-env.json file. After first install, use the '--update' flag to download +updates to mapped sources and to re-apply WordPress configuration options. Options: + --help Show help [boolean] + --version Show version number [boolean] + --debug Enable debug output. [boolean] [default: false] --update Download source updates and apply WordPress configuration. [boolean] [default: false] + --xdebug Enables Xdebug. If not passed, Xdebug is turned off. If no modes + are set, uses "debug". You may set multiple Xdebug modes by passing + them in a comma-separated list: `--xdebug=develop,coverage`. See + https://xdebug.org/docs/all_settings#mode for information about + Xdebug modes. [string] ``` ### `wp-env stop` @@ -328,14 +381,15 @@ You can customize the WordPress installation, plugins and themes that the develo `.wp-env.json` supports six fields for options applicable to both the tests and development instances. -| Field | Type | Default | Description | -| ------------ | -------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | -| `"core"` | `string\|null` | `null` | The WordPress installation to use. If `null` is specified, `wp-env` will use the latest production release of WordPress. | -| `"plugins"` | `string[]` | `[]` | A list of plugins to install and activate in the environment. | -| `"themes"` | `string[]` | `[]` | A list of themes to install in the environment. | -| `"port"` | `integer` | `8888` (`8889` for the tests instance) | The primary port number to use for the installation. You'll access the instance through the port: 'http://localhost:8888'. | -| `"config"` | `Object` | See below. | Mapping of wp-config.php constants to their desired values. | -| `"mappings"` | `Object` | `"{}"` | Mapping of WordPress directories to local directories to be mounted in the WordPress instance. | +| Field | Type | Default | Description | +| -------------- | -------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `"core"` | `string\|null` | `null` | The WordPress installation to use. If `null` is specified, `wp-env` will use the latest production release of WordPress. | +| `"phpVersion"` | `string\|null` | `null` | The PHP version to use. If `null` is specified, `wp-env` will use the default version used with production release of WordPress. | +| `"plugins"` | `string[]` | `[]` | A list of plugins to install and activate in the environment. | +| `"themes"` | `string[]` | `[]` | A list of themes to install in the environment. | +| `"port"` | `integer` | `8888` (`8889` for the tests instance) | The primary port number to use for the installation. You'll access the instance through the port: 'http://localhost:8888'. | +| `"config"` | `Object` | See below. | Mapping of wp-config.php constants to their desired values. | +| `"mappings"` | `Object` | `"{}"` | Mapping of WordPress directories to local directories to be mounted in the WordPress instance. | _Note: the port number environment variables (`WP_ENV_PORT` and `WP_ENV_TESTS_PORT`) take precedent over the .wp-env.json values._ @@ -508,4 +562,15 @@ You can tell `wp-env` to use a custom port number so that your instance does not } ``` +#### Specific PHP Version + +You can tell `wp-env` to use a specific PHP version for compatibility and testing. This can also be set via the environment variable `WP_ENV_PHP_VERSION`. + +```json +{ + "phpVersion": "7.2", + "plugins": [ "." ] +} +``` + <br/><br/><p align="center"><img src="https://s.w.org/style/images/codeispoetry.png?1" alt="Code is Poetry." /></p> diff --git a/packages/env/lib/build-docker-compose-config.js b/packages/env/lib/build-docker-compose-config.js index 54e047fcaff9b0..8f97225a7a1b08 100644 --- a/packages/env/lib/build-docker-compose-config.js +++ b/packages/env/lib/build-docker-compose-config.js @@ -113,6 +113,51 @@ module.exports = function buildDockerComposeConfig( config ) { const developmentPorts = `\${WP_ENV_PORT:-${ config.env.development.port }}:80`; const testsPorts = `\${WP_ENV_TESTS_PORT:-${ config.env.tests.port }}:80`; + // Set the WordPress, WP-CLI, PHPUnit PHP version if defined. + const developmentPhpVersion = config.env.development.phpVersion + ? config.env.development.phpVersion + : ''; + const testsPhpVersion = config.env.tests.phpVersion + ? config.env.tests.phpVersion + : ''; + + // Set the WordPress images with the PHP version tag. + const developmentWpImage = `wordpress${ + developmentPhpVersion ? ':php' + developmentPhpVersion : '' + }`; + const testsWpImage = `wordpress${ + testsPhpVersion ? ':php' + testsPhpVersion : '' + }`; + // Set the WordPress CLI images with the PHP version tag. + const developmentWpCliImage = `wordpress:cli${ + ! developmentPhpVersion || developmentPhpVersion.length === 0 + ? '' + : '-php' + developmentPhpVersion + }`; + const testsWpCliImage = `wordpress:cli${ + ! testsPhpVersion || testsPhpVersion.length === 0 + ? '' + : '-php' + testsPhpVersion + }`; + + // Defaults are to use the most recent version of PHPUnit that provides + // support for the specified version of PHP. + // PHP Unit is assumed to be for Tests so use the testsPhpVersion. + let phpunitTag = 'latest'; + const phpunitPhpVersion = '-php-' + testsPhpVersion + '-fpm'; + if ( testsPhpVersion === '5.6' ) { + phpunitTag = '5' + phpunitPhpVersion; + } else if ( testsPhpVersion === '7.0' ) { + phpunitTag = '6' + phpunitPhpVersion; + } else if ( testsPhpVersion === '7.1' ) { + phpunitTag = '7' + phpunitPhpVersion; + } else if ( [ '7.2', '7.3', '7.4' ].indexOf( testsPhpVersion ) >= 0 ) { + phpunitTag = '8' + phpunitPhpVersion; + } else if ( testsPhpVersion === '8.0' ) { + phpunitTag = '9' + phpunitPhpVersion; + } + const phpunitImage = `wordpressdevelop/phpunit:${ phpunitTag }`; + // The www-data user in wordpress:cli has a different UID (82) to the // www-data user in wordpress (33). Ensure we use the wordpress www-data // user for CLI commands. @@ -131,8 +176,9 @@ module.exports = function buildDockerComposeConfig( config ) { volumes: [ 'mysql:/var/lib/mysql' ], }, wordpress: { + build: '.', depends_on: [ 'mysql' ], - image: 'wordpress', + image: developmentWpImage, ports: [ developmentPorts ], environment: { WORDPRESS_DB_NAME: 'wordpress', @@ -141,7 +187,7 @@ module.exports = function buildDockerComposeConfig( config ) { }, 'tests-wordpress': { depends_on: [ 'mysql' ], - image: 'wordpress', + image: testsWpImage, ports: [ testsPorts ], environment: { WORDPRESS_DB_NAME: 'tests-wordpress', @@ -150,13 +196,13 @@ module.exports = function buildDockerComposeConfig( config ) { }, cli: { depends_on: [ 'wordpress' ], - image: 'wordpress:cli', + image: developmentWpCliImage, volumes: developmentMounts, user: cliUser, }, 'tests-cli': { depends_on: [ 'tests-wordpress' ], - image: 'wordpress:cli', + image: testsWpCliImage, volumes: testsMounts, user: cliUser, }, @@ -165,7 +211,7 @@ module.exports = function buildDockerComposeConfig( config ) { volumes: [ `${ config.configDirectoryPath }:/app` ], }, phpunit: { - image: 'wordpressdevelop/phpunit:${LOCAL_PHP-latest}', + image: phpunitImage, depends_on: [ 'tests-wordpress' ], volumes: [ ...testsMounts, diff --git a/packages/env/lib/cli.js b/packages/env/lib/cli.js index aa94fd7c5b72dd..2ba659969fe143 100644 --- a/packages/env/lib/cli.js +++ b/packages/env/lib/cli.js @@ -11,6 +11,7 @@ const terminalLink = require( 'terminal-link' ); * Internal dependencies */ const env = require( './env' ); +const parseXdebugMode = require( './parse-xdebug-mode' ); // Colors const boldWhite = chalk.bold.white; @@ -99,6 +100,12 @@ module.exports = function cli() { 'Download source updates and apply WordPress configuration.', default: false, } ); + args.option( 'xdebug', { + describe: + 'Enables Xdebug. If not passed, Xdebug is turned off. If no modes are set, uses "debug". You may set multiple Xdebug modes by passing them in a comma-separated list: `--xdebug=develop,coverage`. See https://xdebug.org/docs/all_settings#mode for information about Xdebug modes.', + coerce: parseXdebugMode, + type: 'string', + } ); }, withSpinner( env.start ) ); diff --git a/packages/env/lib/commands/start.js b/packages/env/lib/commands/start.js index 139932fc8ee2e8..0d2b2b717b0053 100644 --- a/packages/env/lib/commands/start.js +++ b/packages/env/lib/commands/start.js @@ -42,12 +42,18 @@ const CONFIG_CACHE_KEY = 'config_checksum'; * @param {Object} options.spinner A CLI spinner which indicates progress. * @param {boolean} options.debug True if debug mode is enabled. * @param {boolean} options.update If true, update sources. + * @param {string} options.xdebug The Xdebug mode to set. */ -module.exports = async function start( { spinner, debug, update } ) { +module.exports = async function start( { spinner, debug, update, xdebug } ) { spinner.text = 'Reading configuration.'; await checkForLegacyInstall( spinner ); - const config = await initConfig( { spinner, debug } ); + const config = await initConfig( { + spinner, + debug, + xdebug, + writeChanges: true, + } ); if ( ! config.detectedLocalConfig ) { const { configDirectoryPath } = config; @@ -175,7 +181,10 @@ module.exports = async function start( { spinner, debug, update } ) { } ); } - spinner.text = 'WordPress started.'; + const siteUrl = config.env.development.config.WP_SITEURL; + spinner.text = 'WordPress started'.concat( + siteUrl ? ` at ${ siteUrl }.` : '.' + ); }; /** diff --git a/packages/env/lib/config/config.js b/packages/env/lib/config/config.js index eac10983632fbf..784ad2e9af2728 100644 --- a/packages/env/lib/config/config.js +++ b/packages/env/lib/config/config.js @@ -26,6 +26,7 @@ const md5 = require( '../md5' ); * @property {boolean} detectedLocalConfig If true, wp-env detected local config and used it. * @property {Object.<string, WPServiceConfig>} env Specific config for different environments. * @property {boolean} debug True if debug mode is enabled. + * @property {string} phpVersion Version of PHP to use in the environments, of the format 0.0. */ /** @@ -69,6 +70,7 @@ module.exports = async function readConfig( configPath ) { // Default configuration which is overridden by .wp-env.json files. const defaultConfiguration = { core: null, + phpVersion: null, plugins: [], themes: [], port: 8888, @@ -253,6 +255,12 @@ function withOverrides( config ) { getNumberFromEnvVariable( 'WP_ENV_TESTS_PORT' ) || config.env.tests.port; + // Override PHP version with environment variable. + config.env.development.phpVersion = + process.env.WP_ENV_PHP_VERSION || config.env.development.phpVersion; + config.env.tests.phpVersion = + process.env.WP_ENV_PHP_VERSION || config.env.tests.phpVersion; + const updateEnvUrl = ( configKey ) => { [ 'development', 'tests' ].forEach( ( envKey ) => { try { diff --git a/packages/env/lib/config/parse-config.js b/packages/env/lib/config/parse-config.js index a32859b0feeb09..a0877fcb059451 100644 --- a/packages/env/lib/config/parse-config.js +++ b/packages/env/lib/config/parse-config.js @@ -35,6 +35,7 @@ const HOME_PATH_PREFIX = `~${ path.sep }`; module.exports = function parseConfig( config, options ) { return { port: config.port, + phpVersion: config.phpVersion, coreSource: includeTestsPath( parseSourceString( config.core, options ), options @@ -103,7 +104,8 @@ function parseSourceString( sourceString, { workDirectoryPath } ) { ); const basename = wpOrgFields ? encodeURIComponent( wpOrgFields[ 1 ] ) - : encodeURIComponent( zipFields[ 1 ] ); + : encodeURIComponent( path.basename( zipFields[ 1 ] ) ); + return { type: 'zip', url: sourceString, diff --git a/packages/env/lib/config/test/__snapshots__/config.js.snap b/packages/env/lib/config/test/__snapshots__/config.js.snap index 1420e8ab83f8d1..db2748f02c3329 100644 --- a/packages/env/lib/config/test/__snapshots__/config.js.snap +++ b/packages/env/lib/config/test/__snapshots__/config.js.snap @@ -22,6 +22,7 @@ Object { }, "coreSource": null, "mappings": Object {}, + "phpVersion": null, "pluginSources": Array [], "port": 2000, "themeSources": Array [], @@ -43,6 +44,7 @@ Object { }, "coreSource": null, "mappings": Object {}, + "phpVersion": null, "pluginSources": Array [], "port": 1000, "themeSources": Array [], diff --git a/packages/env/lib/config/test/config.js b/packages/env/lib/config/test/config.js index 7c784af07f8a5e..bc9b793da86282 100644 --- a/packages/env/lib/config/test/config.js +++ b/packages/env/lib/config/test/config.js @@ -450,6 +450,63 @@ describe( 'readConfig', () => { expect( config.env.tests ).toMatchObject( matchObj ); } ); + it( 'should parse zip sources', async () => { + readFile.mockImplementation( () => + Promise.resolve( + JSON.stringify( { + plugins: [ + 'https://www.example.com/test/path/to/gutenberg.zip', + 'https://www.example.com/test/path/to/gutenberg.8.1.0.zip', + 'https://www.example.com/test/path/to/twentytwenty.zip', + 'https://www.example.com/test/path/to/twentytwenty.1.3.zip', + 'https://example.com/twentytwenty.1.3.zip', + ], + } ) + ) + ); + const config = await readConfig( '.wp-env.json' ); + const matchObj = { + pluginSources: [ + { + type: 'zip', + url: + 'https://www.example.com/test/path/to/gutenberg.zip', + path: expect.stringMatching( /^\/.*gutenberg$/ ), + basename: 'gutenberg', + }, + { + type: 'zip', + url: + 'https://www.example.com/test/path/to/gutenberg.8.1.0.zip', + path: expect.stringMatching( /^\/.*gutenberg.8.1.0$/ ), + basename: 'gutenberg.8.1.0', + }, + { + type: 'zip', + url: + 'https://www.example.com/test/path/to/twentytwenty.zip', + path: expect.stringMatching( /^\/.*twentytwenty$/ ), + basename: 'twentytwenty', + }, + { + type: 'zip', + url: + 'https://www.example.com/test/path/to/twentytwenty.1.3.zip', + path: expect.stringMatching( /^\/.*twentytwenty.1.3$/ ), + basename: 'twentytwenty.1.3', + }, + { + type: 'zip', + url: 'https://example.com/twentytwenty.1.3.zip', + path: expect.stringMatching( /^\/.*twentytwenty.1.3$/ ), + basename: 'twentytwenty.1.3', + }, + ], + }; + expect( config.env.development ).toMatchObject( matchObj ); + expect( config.env.tests ).toMatchObject( matchObj ); + } ); + it( 'should throw a validaton error if there is an unknown source', async () => { readFile.mockImplementation( () => Promise.resolve( JSON.stringify( { plugins: [ 'invalid' ] } ) ) diff --git a/packages/env/lib/config/validate-config.js b/packages/env/lib/config/validate-config.js index 6a50904cb52f81..c3aa849e6322d1 100644 --- a/packages/env/lib/config/validate-config.js +++ b/packages/env/lib/config/validate-config.js @@ -70,6 +70,19 @@ function validateConfig( config, envLocation ) { ); } } + + if ( + config.phpVersion && + ! ( + typeof config.phpVersion === 'string' && + config.phpVersion.length === 3 + ) + ) { + throw new ValidationError( + `Invalid .wp-env.json: "${ envPrefix }phpVersion" must be a string of the format "0.0".` + ); + } + return config; } diff --git a/packages/env/lib/download-sources.js b/packages/env/lib/download-sources.js index 6238fb8bb85c19..d74f33d826484d 100644 --- a/packages/env/lib/download-sources.js +++ b/packages/env/lib/download-sources.js @@ -14,7 +14,6 @@ const path = require( 'path' ); const pipeline = util.promisify( require( 'stream' ).pipeline ); const extractZip = util.promisify( require( 'extract-zip' ) ); const rimraf = util.promisify( require( 'rimraf' ) ); -const copyDir = util.promisify( require( 'copy-dir' ) ); /** * @typedef {import('./config').Config} Config @@ -195,18 +194,41 @@ async function downloadZipSource( source, { onProgress, spinner, debug } ) { ); await pipeline( responseStream, zipFile ); - log( 'Extracting to temporary folder.' ); - const dirName = `${ source.path }.temp`; - await extractZip( zipName, { dir: dirName } ); - - log( 'Copying to mounted folder and cleaning up.' ); - await Promise.all( [ - rimraf( zipName ), - ...( await fs.promises.readdir( dirName ) ).map( ( file ) => - copyDir( path.join( dirName, file ), source.path ) - ), - ] ); - await rimraf( dirName ); + log( 'Extracting to temporary directory.' ); + const tempDir = `${ source.path }.temp`; + await extractZip( zipName, { dir: tempDir } ); + + const files = ( + await Promise.all( [ + rimraf( zipName ), + rimraf( source.path ), + fs.promises.readdir( tempDir ), + ] ) + )[ 2 ]; + + /** + * The plugin container is the extracted directory which is the direct parent + * of the contents of the plugin. It seems a zip file can have two fairly + * common approaches to where the content lives: + * 1. The .zip is the direct container of the files. So after extraction, the + * extraction directory contains plugin contents. + * 2. The .zip contains a directory with the same name which is the container. + * So after extraction, the extraction directory contains another directory. + * That subdirectory is the actual container of the plugin contents. + * + * We support both situations with the following check. + */ + let pluginContainer = tempDir; + const firstSubItem = path.join( tempDir, files[ 0 ] ); + if ( + files.length === 1 && + ( await fs.promises.lstat( firstSubItem ) ).isDirectory() + ) { + // In this case, only one sub directory exists, so use that as the container. + pluginContainer = firstSubItem; + } + await fs.promises.rename( pluginContainer, source.path ); + await rimraf( tempDir ); onProgress( 1 ); } diff --git a/packages/env/lib/init-config.js b/packages/env/lib/init-config.js index 02f4f3ed5e6cd7..68e16781d94e9e 100644 --- a/packages/env/lib/init-config.js +++ b/packages/env/lib/init-config.js @@ -2,8 +2,10 @@ * External dependencies */ const path = require( 'path' ); -const fs = require( 'fs' ).promises; +const { writeFile, mkdir } = require( 'fs' ).promises; +const { existsSync } = require( 'fs' ); const yaml = require( 'js-yaml' ); +const os = require( 'os' ); /** * Internal dependencies @@ -20,23 +22,32 @@ const buildDockerComposeConfig = require( './build-docker-compose-config' ); * ./.wp-env.json, creates ~/.wp-env, and creates ~/.wp-env/docker-compose.yml. * * @param {Object} options - * @param {Object} options.spinner A CLI spinner which indicates progress. - * @param {boolean} options.debug True if debug mode is enabled. - * + * @param {Object} options.spinner A CLI spinner which indicates progress. + * @param {boolean} options.debug True if debug mode is enabled. + * @param {string} options.xdebug The Xdebug mode to set. Defaults to "off". + * @param {boolean} options.writeChanges If true, writes the parsed config to the + * required docker files like docker-compose + * and Dockerfile. By default, this is false + * and only the `start` command writes any + * changes. * @return {WPConfig} The-env config object. */ -module.exports = async function initConfig( { spinner, debug } ) { +module.exports = async function initConfig( { + spinner, + debug, + xdebug = 'off', + writeChanges = false, +} ) { const configPath = path.resolve( '.wp-env.json' ); const config = await readConfig( configPath ); config.debug = debug; - await fs.mkdir( config.workDirectoryPath, { recursive: true } ); + // Adding this to the config allows the start command to understand that the + // config has changed when only the xdebug param has changed. This is needed + // so that Docker will rebuild the image whenever the xdebug flag changes. + config.xdebug = xdebug; const dockerComposeConfig = buildDockerComposeConfig( config ); - await fs.writeFile( - config.dockerComposeConfigPath, - yaml.dump( dockerComposeConfig ) - ); if ( config.debug ) { spinner.info( @@ -53,5 +64,55 @@ module.exports = async function initConfig( { spinner, debug } ) { spinner.start(); } + /** + * We avoid writing changes most of the time so that we can better pass params + * to the start command. For example, say you start wp-env with Xdebug enabled. + * If you then run another command, like opening bash in the wp instance, it + * would turn off Xdebug in the Dockerfile because it wouldn't have the --xdebug + * arg. This basically makes it such that wp-env start is the only command + * which updates any of the Docker configuration. + */ + if ( writeChanges ) { + await mkdir( config.workDirectoryPath, { recursive: true } ); + + await writeFile( + config.dockerComposeConfigPath, + yaml.dump( dockerComposeConfig ) + ); + + await writeFile( + path.resolve( config.workDirectoryPath, 'Dockerfile' ), + dockerFileContents( + dockerComposeConfig.services.wordpress.image, + xdebug + ) + ); + } else if ( ! existsSync( config.workDirectoryPath ) ) { + spinner.fail( + 'wp-env has not yet been initalized. Please run `wp-env start` to install the WordPress instance before using any other commands. This is only necessary to set up the environment for the first time; it is typically not necessary for the instance to be running after that in order to use other commands.' + ); + process.exit( 1 ); + } + return config; }; + +function dockerFileContents( image, xdebugMode ) { + const isLinux = os.type() === 'Linux'; + // Discover client host does not appear to work on macOS with Docker. + const clientDetectSettings = isLinux + ? 'xdebug.discover_client_host=true' + : 'xdebug.client_host="host.docker.internal"'; + + return `FROM ${ image } + +RUN apt -qy install $PHPIZE_DEPS \\ + && pecl install xdebug \\ + && docker-php-ext-enable xdebug + +RUN touch /usr/local/etc/php/php.ini +RUN echo 'xdebug.start_with_request=yes' >> /usr/local/etc/php/php.ini +RUN echo 'xdebug.mode=${ xdebugMode }' >> /usr/local/etc/php/php.ini +RUN echo '${ clientDetectSettings }' >> /usr/local/etc/php/php.ini +`; +} diff --git a/packages/env/lib/parse-xdebug-mode.js b/packages/env/lib/parse-xdebug-mode.js new file mode 100644 index 00000000000000..2be7c8a5be5cd3 --- /dev/null +++ b/packages/env/lib/parse-xdebug-mode.js @@ -0,0 +1,47 @@ +// See https://xdebug.org/docs/all_settings#mode +const XDEBUG_MODES = [ + 'develop', + 'coverage', + 'debug', + 'gcstats', + 'profile', + 'trace', +]; + +/** + * Custom parsing for the Xdebug mode set via yargs. This function ensures two things: + * 1. If the --xdebug flag was set by itself, default to 'debug'. + * 2. If the --xdebug flag includes modes, make sure they are accepted by Xdebug. + * + * Note: ideally, we would also have this handle the case where no xdebug flag + * is set (and then turn Xdebug off). However, yargs does not pass 'undefined' + * to the coerce callback, so we cannot handle that case here. + * + * @param {string} value The user-set mode of Xdebug + * @return {string} The Xdebug mode to use with defaults applied. + */ +module.exports = function parseXdebugMode( value ) { + if ( typeof value !== 'string' ) { + throwXdebugModeError( value ); + } + + if ( value.length === 0 ) { + return 'debug'; + } + + const modes = value.split( ',' ); + modes.forEach( ( userMode ) => { + if ( ! XDEBUG_MODES.some( ( realMode ) => realMode === userMode ) ) { + throwXdebugModeError( userMode ); + } + } ); + return value; +}; + +function throwXdebugModeError( value ) { + throw new Error( + `"${ value }" is not a mode recognized by Xdebug. Valid modes are: ${ XDEBUG_MODES.join( + ', ' + ) }` + ); +} diff --git a/packages/env/package.json b/packages/env/package.json index 70b426e84a16d0..211b8a3c38e577 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/env", - "version": "2.1.0", + "version": "3.0.0", "description": "A zero-config, self contained local WordPress environment for development and testing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/env/test/parse-xdebug-mode.js b/packages/env/test/parse-xdebug-mode.js new file mode 100644 index 00000000000000..532fc6b6daae8a --- /dev/null +++ b/packages/env/test/parse-xdebug-mode.js @@ -0,0 +1,34 @@ +/** + * Internal dependencies + */ +const parseXdebugMode = require( '../lib/parse-xdebug-mode' ); + +describe( 'parseXdebugMode', () => { + it( 'throws an error if the passed value is not a string', () => { + expect( () => parseXdebugMode() ).toThrow( + 'is not a mode recognized by Xdebug' + ); + } ); + + it( 'sets the Xdebug mode to "debug" if no mode is specified', () => { + const result = parseXdebugMode( '' ); + expect( result ).toEqual( 'debug' ); + } ); + + it( 'throws an error if a given mode is not recognized, including the invalid mode in the output', () => { + const fakeMode = 'fake-mode-123'; + expect.assertions( 2 ); + // Single mode: + expect( () => parseXdebugMode( fakeMode ) ).toThrow( fakeMode ); + + // Many modes: + expect( () => + parseXdebugMode( `debug,profile,${ fakeMode }` ) + ).toThrow( fakeMode ); + } ); + + it( 'returns all modes passed', () => { + const result = parseXdebugMode( 'debug,profile,trace' ); + expect( result ).toEqual( 'debug,profile,trace' ); + } ); +} ); diff --git a/packages/escape-html/package.json b/packages/escape-html/package.json index 1bd7278bb2933f..24ed133d809943 100644 --- a/packages/escape-html/package.json +++ b/packages/escape-html/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/escape-html", - "version": "1.10.0", + "version": "1.11.0", "description": "Escape HTML utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index fbe29bb0447f93..7654f6d445f718 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -2,6 +2,20 @@ ## Unreleased +## 7.4.0 (2020-12-17) + +### New Feature + +- Add `no-unsafe-wp-apis` rule to discourage usage of unsafe APIs ([#27301](https://github.com/WordPress/gutenberg/pull/27301)). + +### Enhancements + +- The bundled `wp-prettier` dependency has been upgraded from `2.0.5` to `2.2.1`. + +### Documentation + +- Include a note about the minimum version required for `node` (10.0.0) and `npm` (6.9.0). + ## 7.2.1 (2020-09-17) ### Bug Fixes diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index a47c932d403541..cd53db818d9777 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -10,6 +10,8 @@ Install the module npm install @wordpress/eslint-plugin --save-dev ``` +**Note**: This package requires `node` 10.0.0 or later, and `npm` 6.9.0 or later. It is not compatible with older versions. + ## Usage To opt-in to the default configuration, extend your own project's `.eslintrc` file: @@ -30,15 +32,15 @@ There is also `recommended-with-formatting` ruleset for projects that want to op Alternatively, you can opt-in to only the more granular rulesets offered by the plugin. These include: -- `custom` -- `es5` -- `esnext` -- `jsdoc` -- `jsx-a11y` -- `react` -- `i18n` -- `test-e2e` -- `test-unit` +- `custom` +- `es5` +- `esnext` +- `jsdoc` +- `jsx-a11y` +- `react` +- `i18n` +- `test-e2e` +- `test-unit` For example, if your project does not use React, you could consider extending including only the ESNext rules in your project using the following `extends` definition: @@ -54,21 +56,21 @@ The granular rulesets will not define any environment globals. As such, if they ### Rules -Rule|Description|Recommended ----|---|--- -[dependency-group](/packages/eslint-plugin/docs/rules/dependency-group.md)|Enforce dependencies docblocks formatting|✓ -[gutenberg-phase](docs/rules/gutenberg-phase.md)|Governs the use of the `process.env.GUTENBERG_PHASE` constant|✓ -[no-unused-vars-before-return](/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md)|Disallow assigning variable values if unused before a return|✓ -[react-no-unsafe-timeout](/packages/eslint-plugin/docs/rules/react-no-unsafe-timeout.md)|Disallow unsafe `setTimeout` in component| -[valid-sprintf](/packages/eslint-plugin/docs/rules/valid-sprintf.md)|Enforce valid sprintf usage|✓ -[no-base-control-with-label-without-id](/packages/eslint-plugin/docs/rules/no-base-control-with-label-without-id.md)| Disallow the usage of BaseControl component with a label prop set but omitting the id property|✓ -[no-unguarded-get-range-at](/packages/eslint-plugin/docs/rules/no-unguarded-get-range-at.md)|Disallow the usage of unguarded `getRangeAt` calls|✓ -[i18n-ellipsis](/packages/eslint-plugin/docs/rules/i18n-ellipsis.md)|Disallow using three dots in translatable strings|✓ -[i18n-no-collapsible-whitespace](/packages/eslint-plugin/docs/rules/i18n-no-collapsible-whitespace.md)|Disallow collapsible whitespace in translatable strings|✓ -[i18n-no-placeholders-only](/packages/eslint-plugin/docs/rules/i18n-no-placeholders-only.md)|Prevent using only placeholders in translatable strings|✓ -[i18n-no-variables](/packages/eslint-plugin/docs/rules/i18n-no-variables.md)|Enforce string literals as translation function arguments|✓ -[i18n-text-domain](/packages/eslint-plugin/docs/rules/i18n-text-domain.md)|Enforce passing valid text domains|✓ -[i18n-translator-comments](/packages/eslint-plugin/docs/rules/i18n-translator-comments.md)|Enforce adding translator comments|✓ +| Rule | Description | Recommended | +| -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ----------- | +| [dependency-group](/packages/eslint-plugin/docs/rules/dependency-group.md) | Enforce dependencies docblocks formatting | ✓ | +| [gutenberg-phase](docs/rules/gutenberg-phase.md) | Governs the use of the `process.env.GUTENBERG_PHASE` constant | ✓ | +| [no-unused-vars-before-return](/packages/eslint-plugin/docs/rules/no-unused-vars-before-return.md) | Disallow assigning variable values if unused before a return | ✓ | +| [react-no-unsafe-timeout](/packages/eslint-plugin/docs/rules/react-no-unsafe-timeout.md) | Disallow unsafe `setTimeout` in component | +| [valid-sprintf](/packages/eslint-plugin/docs/rules/valid-sprintf.md) | Enforce valid sprintf usage | ✓ | +| [no-base-control-with-label-without-id](/packages/eslint-plugin/docs/rules/no-base-control-with-label-without-id.md) | Disallow the usage of BaseControl component with a label prop set but omitting the id property | ✓ | +| [no-unguarded-get-range-at](/packages/eslint-plugin/docs/rules/no-unguarded-get-range-at.md) | Disallow the usage of unguarded `getRangeAt` calls | ✓ | +| [i18n-ellipsis](/packages/eslint-plugin/docs/rules/i18n-ellipsis.md) | Disallow using three dots in translatable strings | ✓ | +| [i18n-no-collapsible-whitespace](/packages/eslint-plugin/docs/rules/i18n-no-collapsible-whitespace.md) | Disallow collapsible whitespace in translatable strings | ✓ | +| [i18n-no-placeholders-only](/packages/eslint-plugin/docs/rules/i18n-no-placeholders-only.md) | Prevent using only placeholders in translatable strings | ✓ | +| [i18n-no-variables](/packages/eslint-plugin/docs/rules/i18n-no-variables.md) | Enforce string literals as translation function arguments | ✓ | +| [i18n-text-domain](/packages/eslint-plugin/docs/rules/i18n-text-domain.md) | Enforce passing valid text domains | ✓ | +| [i18n-translator-comments](/packages/eslint-plugin/docs/rules/i18n-translator-comments.md) | Enforce adding translator comments | ✓ | ### Legacy diff --git a/packages/eslint-plugin/docs/rules/no-unsafe-wp-apis.md b/packages/eslint-plugin/docs/rules/no-unsafe-wp-apis.md new file mode 100644 index 00000000000000..59213648efcab5 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-unsafe-wp-apis.md @@ -0,0 +1,43 @@ +# Prevent unsafe API usage (no-unsafe-wp-apis) + +Prevent unsafe APIs from `@wordpress/*` packages from being imported. + +This includes experimental and unstable APIs which are expected to change and likely to cause issues in application code. +See the [documentation](https://github.com/WordPress/gutenberg/blob/master/docs/contributors/coding-guidelines.md#experimental-and-unstable-apis). + +> **There is no support commitment for experimental and unstable APIs.** They can and will be removed or changed without advance warning, including as part of a minor or patch release. As an external consumer, you should avoid these APIs. +> … +> +> - An **experimental API** is one which is planned for eventual public availability, but is subject to further experimentation, testing, and discussion. +> - An **unstable API** is one which serves as a means to an end. It is not desired to ever be converted into a public API. + +## Rule details + +Examples of **incorrect** code for this rule: + +```js +import { __experimentalFeature } from '@wordpress/foo'; +import { __unstableFeature } from '@wordpress/bar'; +``` + +Examples of **correct** code for this rule: + +```js +import { registerBlockType } from '@wordpress/blocks'; +``` + +## Options + +The rule can be configured via an object. +This should be an object where the keys are import package names and the values are arrays of allowed unsafe imports. + +#### Example configuration + +```json +{ + "@wordpress/no-unsafe-wp-apis": [ + "error", + { "@wordpress/block-editor": [ "__experimentalBlock" ] } + ] +} +``` diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 4b03086220c42e..594577a2671a70 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/eslint-plugin", - "version": "7.3.0", + "version": "7.4.0", "description": "ESLint plugin for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -19,6 +19,10 @@ "bugs": { "url": "https://github.com/WordPress/gutenberg/issues" }, + "engines": { + "node": ">=10", + "npm": ">=6.9" + }, "files": [ "configs", "rules", @@ -38,7 +42,7 @@ "eslint-plugin-react": "^7.20.0", "eslint-plugin-react-hooks": "^4.0.4", "globals": "^12.0.0", - "prettier": "npm:wp-prettier@2.0.5", + "prettier": "npm:wp-prettier@2.2.1-beta-1", "requireindex": "^1.2.0" }, "peerDependencies": { diff --git a/packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js b/packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js new file mode 100644 index 00000000000000..ed7b0ce1684a4b --- /dev/null +++ b/packages/eslint-plugin/rules/__tests__/no-unsafe-wp-apis.js @@ -0,0 +1,119 @@ +/** + * External dependencies + */ +import { RuleTester } from 'eslint'; + +/** + * Internal dependencies + */ +import rule from '../no-unsafe-wp-apis'; + +const ruleTester = new RuleTester( { + parserOptions: { + sourceType: 'module', + ecmaVersion: 6, + }, +} ); + +const options = [ + { '@wordpress/package': [ '__experimentalSafe', '__unstableSafe' ] }, +]; + +ruleTester.run( 'no-unsafe-wp-apis', rule, { + valid: [ + { code: "import _ from 'lodash';", options }, + { code: "import { map } from 'lodash';", options }, + { code: "import { __experimentalFoo } from 'lodash';", options }, + { code: "import { __unstableFoo } from 'lodash';", options }, + { code: "import _, { __unstableFoo } from 'lodash';", options }, + { code: "import * as _ from 'lodash';", options }, + + { code: "import _ from './x';", options }, + { code: "import { map } from './x';", options }, + { code: "import { __experimentalFoo } from './x';", options }, + { code: "import { __unstableFoo } from './x';", options }, + { code: "import _, { __unstableFoo } from './x';", options }, + { code: "import * as _ from './x';", options }, + + { code: "import s from '@wordpress/package';", options }, + { code: "import { feature } from '@wordpress/package';", options }, + { + code: "import { __experimentalSafe } from '@wordpress/package';", + options, + }, + { + code: "import { __unstableSafe } from '@wordpress/package';", + options, + }, + { + code: + "import { feature, __experimentalSafe } from '@wordpress/package';", + options, + }, + { + code: "import s, { __experimentalSafe } from '@wordpress/package';", + options, + }, + { code: "import * as s from '@wordpress/package';", options }, + ], + + invalid: [ + { + code: "import { __experimentalUnsafe } from '@wordpress/package';", + options, + errors: [ + { + message: `Usage of \`__experimentalUnsafe\` from \`@wordpress/package\` is not allowed. +See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`, + type: 'ImportSpecifier', + }, + ], + }, + { + code: "import { __experimentalSafe } from '@wordpress/unsafe';", + options, + errors: [ + { + message: `Usage of \`__experimentalSafe\` from \`@wordpress/unsafe\` is not allowed. +See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`, + type: 'ImportSpecifier', + }, + ], + }, + { + code: + "import { feature, __experimentalSafe } from '@wordpress/unsafe';", + options, + errors: [ + { + message: `Usage of \`__experimentalSafe\` from \`@wordpress/unsafe\` is not allowed. +See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`, + type: 'ImportSpecifier', + }, + ], + }, + { + code: + "import s, { __experimentalUnsafe } from '@wordpress/package';", + options, + errors: [ + { + message: `Usage of \`__experimentalUnsafe\` from \`@wordpress/package\` is not allowed. +See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`, + type: 'ImportSpecifier', + }, + ], + }, + { + code: "import { __unstableFeature } from '@wordpress/package';", + options, + errors: [ + { + message: `Usage of \`__unstableFeature\` from \`@wordpress/package\` is not allowed. +See https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`, + type: 'ImportSpecifier', + }, + ], + }, + ], +} ); diff --git a/packages/eslint-plugin/rules/dependency-group.js b/packages/eslint-plugin/rules/dependency-group.js index eaaf716ccade5d..52380418ac520c 100644 --- a/packages/eslint-plugin/rules/dependency-group.js +++ b/packages/eslint-plugin/rules/dependency-group.js @@ -1,7 +1,8 @@ /** @typedef {import('estree').Comment} Comment */ /** @typedef {import('estree').Node} Node */ -module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( { +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { meta: { type: 'layout', docs: { @@ -254,4 +255,4 @@ module.exports = /** @type {import('eslint').Rule.RuleModule} */ ( { }, }; }, -} ); +}; diff --git a/packages/eslint-plugin/rules/no-unsafe-wp-apis.js b/packages/eslint-plugin/rules/no-unsafe-wp-apis.js new file mode 100644 index 00000000000000..65bcc2170d45ba --- /dev/null +++ b/packages/eslint-plugin/rules/no-unsafe-wp-apis.js @@ -0,0 +1,88 @@ +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + type: 'problem', + meta: { + schema: [ + { + type: 'object', + additionalProperties: false, + patternProperties: { + '^@wordpress\\/[a-zA-Z0-9_-]+$': { + type: 'array', + uniqueItems: true, + minItems: 1, + items: { + type: 'string', + pattern: '^(?:__experimental|__unstable)', + }, + }, + }, + }, + ], + }, + create( context ) { + /** @type {AllowedImportsMap} */ + const allowedImports = + ( context.options && + typeof context.options[ 0 ] === 'object' && + context.options[ 0 ] ) || + {}; + const reporter = makeListener( { allowedImports, context } ); + + return { ImportDeclaration: reporter }; + }, +}; + +/** + * @param {Object} _ + * @param {AllowedImportsMap} _.allowedImports + * @param {import('eslint').Rule.RuleContext} _.context + * + * @return {(node: Node) => void} Listener function + */ +function makeListener( { allowedImports, context } ) { + return function reporter( node ) { + if ( node.type !== 'ImportDeclaration' ) { + return; + } + if ( typeof node.source.value !== 'string' ) { + return; + } + + const sourceModule = node.source.value.trim(); + + // Ignore non-WordPress packages + if ( ! sourceModule.startsWith( '@wordpress/' ) ) { + return; + } + + const allowedImportNames = allowedImports[ sourceModule ] || []; + + node.specifiers.forEach( ( specifierNode ) => { + if ( specifierNode.type !== 'ImportSpecifier' ) { + return; + } + + const importedName = specifierNode.imported.name; + + if ( + ! importedName.startsWith( '__unstable' ) && + ! importedName.startsWith( '__experimental' ) + ) { + return; + } + + if ( allowedImportNames.includes( importedName ) ) { + return; + } + + context.report( { + message: `Usage of \`${ importedName }\` from \`${ sourceModule }\` is not allowed.\nSee https://developer.wordpress.org/block-editor/contributors/develop/coding-guidelines/#experimental-and-unstable-apis for details.`, + node: specifierNode, + } ); + } ); + }; +} + +/** @typedef {import('estree').Node} Node */ +/** @typedef {Record<string, string[]|undefined>} AllowedImportsMap */ diff --git a/packages/eslint-plugin/tsconfig.json b/packages/eslint-plugin/tsconfig.json index 0f0a4598184f80..d292d97510c414 100644 --- a/packages/eslint-plugin/tsconfig.json +++ b/packages/eslint-plugin/tsconfig.json @@ -1,13 +1,12 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + "module": "CommonJS", "rootDir": "rules", "declarationDir": "build-types" }, // NOTE: This package is being progressively typed. You are encouraged to // expand this array with files which can be type-checked. At some point in // the future, this can be simplified to an `includes` of `src/**/*`. - "files": [ - "rules/dependency-group.js" - ] + "files": [ "rules/dependency-group.js", "rules/no-unsafe-wp-apis.js" ] } diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 75572c2c1860d7..1cd137a7066256 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "1.25.3", + "version": "1.26.0", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:../a11y", "@wordpress/block-editor": "file:../block-editor", "@wordpress/components": "file:../components", diff --git a/packages/format-library/src/code/index.js b/packages/format-library/src/code/index.js index 772fe127816210..be3c0c6f9278ba 100644 --- a/packages/format-library/src/code/index.js +++ b/packages/format-library/src/code/index.js @@ -7,7 +7,7 @@ import { RichTextToolbarButton } from '@wordpress/block-editor'; import { code as codeIcon } from '@wordpress/icons'; const name = 'core/code'; -const title = __( 'Inline Code' ); +const title = __( 'Inline code' ); export const code = { name, diff --git a/packages/format-library/src/image/index.js b/packages/format-library/src/image/index.js index b813a92bd9a85f..f7a52edf1af168 100644 --- a/packages/format-library/src/image/index.js +++ b/packages/format-library/src/image/index.js @@ -10,7 +10,6 @@ import { RichTextToolbarButton, MediaUploadCheck, } from '@wordpress/block-editor'; -import { LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes'; import { keyboardReturn } from '@wordpress/icons'; const ALLOWED_MEDIA_TYPES = [ 'image' ]; @@ -34,21 +33,6 @@ export const image = { edit: Edit, }; -function stopKeyPropagation( event ) { - event.stopPropagation(); -} - -function onKeyDown( event ) { - if ( - [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( event.keyCode ) > - -1 - ) { - // Stop the key event from propagating up to - // ObserveTyping.startTypingInTextField. - event.stopPropagation(); - } -} - function InlineUI( { value, onChange, activeObjectAttributes, contentRef } ) { const { style } = activeObjectAttributes; const [ width, setWidth ] = useState( style.replace( /\D/g, '' ) ); @@ -64,14 +48,8 @@ function InlineUI( { value, onChange, activeObjectAttributes, contentRef } ) { focusOnMount={ false } anchorRef={ anchorRef } > - { - // Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar - /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ - } <form className="block-editor-format-toolbar__image-container-content" - onKeyPress={ stopKeyPropagation } - onKeyDown={ onKeyDown } onSubmit={ ( event ) => { const newReplacements = value.replacements.slice(); @@ -105,7 +83,6 @@ function InlineUI( { value, onChange, activeObjectAttributes, contentRef } ) { type="submit" /> </form> - { /* eslint-enable jsx-a11y/no-noninteractive-element-interactions */ } </Popover> ); } diff --git a/packages/format-library/src/keyboard/index.js b/packages/format-library/src/keyboard/index.js index 45bd784a7954c4..d6a260f8e07dd0 100644 --- a/packages/format-library/src/keyboard/index.js +++ b/packages/format-library/src/keyboard/index.js @@ -7,7 +7,7 @@ import { RichTextToolbarButton } from '@wordpress/block-editor'; import { button } from '@wordpress/icons'; const name = 'core/keyboard'; -const title = __( 'Keyboard Input' ); +const title = __( 'Keyboard input' ); export const keyboard = { name, diff --git a/packages/format-library/src/link/inline.js b/packages/format-library/src/link/inline.js index fef83cac47a05d..4c78c9d1475a99 100644 --- a/packages/format-library/src/link/inline.js +++ b/packages/format-library/src/link/inline.js @@ -1,12 +1,7 @@ -/** - * External dependencies - */ -import { uniqueId } from 'lodash'; - /** * WordPress dependencies */ -import { useMemo, useState } from '@wordpress/element'; +import { useState, useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { withSpokenMessages, Popover } from '@wordpress/components'; import { prependHTTP } from '@wordpress/url'; @@ -35,22 +30,6 @@ function InlineLinkUI( { stopAddingLink, contentRef, } ) { - /** - * A unique key is generated when switching between editing and not editing - * a link, based on: - * - * - This component may be rendered _either_ when a link is active _or_ - * when adding or editing a link. - * - It's only desirable to shift focus into the Popover when explicitly - * adding or editing a link, not when in the inline boundary of a link. - * - Focus behavior can only be controlled on a Popover at the time it - * mounts, so a new instance of the component must be mounted to - * programmatically enact the focusOnMount behavior. - * - * @type {string} - */ - const mountingKey = useMemo( uniqueId, [ addingLink ] ); - /** * Pending settings to be applied to the next link. When inserting a new * link, toggle values cannot be applied immediately, because there is not @@ -146,11 +125,14 @@ function InlineLinkUI( { const anchorRef = useAnchorRef( { ref: contentRef, value, settings } ); + // The focusOnMount prop shouldn't evolve during render of a Popover + // otherwise it causes a render of the content. + const focusOnMount = useRef( addingLink ? 'firstElement' : false ); + return ( <Popover - key={ mountingKey } anchorRef={ anchorRef } - focusOnMount={ addingLink ? 'firstElement' : false } + focusOnMount={ focusOnMount.current } onClose={ stopAddingLink } position="bottom center" > diff --git a/packages/format-library/src/link/modal-screens/link-settings-screen.native.js b/packages/format-library/src/link/modal-screens/link-settings-screen.native.js index b69d347e065c1f..b4bba800d69fee 100644 --- a/packages/format-library/src/link/modal-screens/link-settings-screen.native.js +++ b/packages/format-library/src/link/modal-screens/link-settings-screen.native.js @@ -137,6 +137,8 @@ const LinkSettingsScreen = ( { }, [ navigation, route.params?.text, text ] ); return useMemo( () => { + const shouldShowLinkOptions = !! inputValue; + return ( <> <BottomSheet.LinkCell @@ -150,20 +152,25 @@ const LinkSettingsScreen = ( { placeholder={ __( 'Add link text' ) } onChangeValue={ setText } onSubmit={ submit } + separatorType={ shouldShowLinkOptions ? undefined : 'none' } /> - <BottomSheet.SwitchCell - icon={ external } - label={ __( 'Open in new tab' ) } - value={ opensInNewWindow } - onValueChange={ setOpensInNewWindows } - separatorType={ 'fullWidth' } - /> - <BottomSheet.Cell - label={ __( 'Remove link' ) } - labelStyle={ styles.clearLinkButton } - separatorType={ 'none' } - onPress={ removeLink } - /> + { shouldShowLinkOptions && ( + <> + <BottomSheet.SwitchCell + icon={ external } + label={ __( 'Open in new tab' ) } + value={ opensInNewWindow } + onValueChange={ setOpensInNewWindows } + separatorType={ 'fullWidth' } + /> + <BottomSheet.Cell + label={ __( 'Remove link' ) } + labelStyle={ styles.clearLinkButton } + separatorType={ 'none' } + onPress={ removeLink } + /> + </> + ) } </> ); }, [ inputValue, text, opensInNewWindow, listProps.safeAreaBottomInset ] ); diff --git a/packages/format-library/src/text-color/index.js b/packages/format-library/src/text-color/index.js index f7ffdc33b9e4d0..b3083b35b79965 100644 --- a/packages/format-library/src/text-color/index.js +++ b/packages/format-library/src/text-color/index.js @@ -21,7 +21,7 @@ import { removeFormat } from '@wordpress/rich-text'; import { default as InlineColorUI, getActiveColor } from './inline'; const name = 'core/text-color'; -const title = __( 'Text Color' ); +const title = __( 'Text color' ); const EMPTY_ARRAY = []; @@ -87,7 +87,10 @@ function TextColorEdit( { onClose={ disableIsAddingColor } activeAttributes={ activeAttributes } value={ value } - onChange={ onChange } + onChange={ ( ...args ) => { + onChange( ...args ); + disableIsAddingColor(); + } } contentRef={ contentRef } /> ) } diff --git a/packages/hooks/CHANGELOG.md b/packages/hooks/CHANGELOG.md index 7e9b150d0f8b27..a81cb38f27ae7e 100644 --- a/packages/hooks/CHANGELOG.md +++ b/packages/hooks/CHANGELOG.md @@ -2,10 +2,16 @@ ## Unreleased +## 2.11.0 (2020-12-17) + ### New Feature - Include TypeScript type declarations ([#26430](https://github.com/WordPress/gutenberg/pull/26430)) +### Bug Fix + +- Fix: Use own instance's `doAction` method for built-in `hookAdded` and `hookRemoved` hooks ([#26498](https://github.com/WordPress/gutenberg/pull/26498)) + ## 2.6.0 (2019-08-29) ### New Feature diff --git a/packages/hooks/package.json b/packages/hooks/package.json index d13101c077dd4b..deaec9d2b00e1f 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/hooks", - "version": "2.10.0", + "version": "2.11.0", "description": "WordPress hooks library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "react-native": "src/index", "types": "build-types", "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/hooks/src/createAddHook.js b/packages/hooks/src/createAddHook.js index 9205921ba85461..54af960956d432 100644 --- a/packages/hooks/src/createAddHook.js +++ b/packages/hooks/src/createAddHook.js @@ -3,7 +3,6 @@ */ import validateNamespace from './validateNamespace.js'; import validateHookName from './validateHookName.js'; -import { doAction } from './'; /** * @callback AddHook @@ -13,18 +12,21 @@ import { doAction } from './'; * @param {string} hookName Name of hook to add * @param {string} namespace The unique namespace identifying the callback in the form `vendor/plugin/function`. * @param {import('.').Callback} callback Function to call when the hook is run - * @param {number} [priority=10] Priority of this hook + * @param {number} [priority=10] Priority of this hook */ /** * Returns a function which, when invoked, will add a hook. * - * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Hooks instance. + * @param {import('.').StoreKey} storeKey * * @return {AddHook} Function that adds a new hook. */ -function createAddHook( hooks ) { +function createAddHook( hooks, storeKey ) { return function addHook( hookName, namespace, callback, priority = 10 ) { + const hooksStore = hooks[ storeKey ]; + if ( ! validateHookName( hookName ) ) { return; } @@ -50,9 +52,9 @@ function createAddHook( hooks ) { const handler = { callback, priority, namespace }; - if ( hooks[ hookName ] ) { + if ( hooksStore[ hookName ] ) { // Find the correct insert index of the new hook. - const handlers = hooks[ hookName ].handlers; + const handlers = hooksStore[ hookName ].handlers; /** @type {number} */ let i; @@ -74,7 +76,7 @@ function createAddHook( hooks ) { // we're adding would come after the current callback, there's no // problem; otherwise we need to increase the execution index of // any other runs by 1 to account for the added element. - hooks.__current.forEach( ( hookInfo ) => { + hooksStore.__current.forEach( ( hookInfo ) => { if ( hookInfo.name === hookName && hookInfo.currentIndex >= i @@ -84,14 +86,20 @@ function createAddHook( hooks ) { } ); } else { // This is the first hook of its type. - hooks[ hookName ] = { + hooksStore[ hookName ] = { handlers: [ handler ], runs: 0, }; } if ( hookName !== 'hookAdded' ) { - doAction( 'hookAdded', hookName, namespace, callback, priority ); + hooks.doAction( + 'hookAdded', + hookName, + namespace, + callback, + priority + ); } }; } diff --git a/packages/hooks/src/createCurrentHook.js b/packages/hooks/src/createCurrentHook.js index 17f72df9e0e356..a5565dc98e2cd8 100644 --- a/packages/hooks/src/createCurrentHook.js +++ b/packages/hooks/src/createCurrentHook.js @@ -3,13 +3,19 @@ * currently running hook, or `null` if no hook of the given type is currently * running. * - * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Hooks instance. + * @param {import('.').StoreKey} storeKey * * @return {() => string | null} Function that returns the current hook name or null. */ -function createCurrentHook( hooks ) { +function createCurrentHook( hooks, storeKey ) { return function currentHook() { - return hooks.__current[ hooks.__current.length - 1 ]?.name ?? null; + const hooksStore = hooks[ storeKey ]; + + return ( + hooksStore.__current[ hooksStore.__current.length - 1 ]?.name ?? + null + ); }; } diff --git a/packages/hooks/src/createDidHook.js b/packages/hooks/src/createDidHook.js index bdc34131378778..c52aafbe656355 100644 --- a/packages/hooks/src/createDidHook.js +++ b/packages/hooks/src/createDidHook.js @@ -17,18 +17,21 @@ import validateHookName from './validateHookName.js'; * Returns a function which, when invoked, will return the number of times a * hook has been called. * - * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Hooks instance. + * @param {import('.').StoreKey} storeKey * * @return {DidHook} Function that returns a hook's call count. */ -function createDidHook( hooks ) { +function createDidHook( hooks, storeKey ) { return function didHook( hookName ) { + const hooksStore = hooks[ storeKey ]; + if ( ! validateHookName( hookName ) ) { return; } - return hooks[ hookName ] && hooks[ hookName ].runs - ? hooks[ hookName ].runs + return hooksStore[ hookName ] && hooksStore[ hookName ].runs + ? hooksStore[ hookName ].runs : 0; }; } diff --git a/packages/hooks/src/createDoingHook.js b/packages/hooks/src/createDoingHook.js index 384c163a8c887e..52afbce3ba497c 100644 --- a/packages/hooks/src/createDoingHook.js +++ b/packages/hooks/src/createDoingHook.js @@ -12,21 +12,24 @@ * Returns a function which, when invoked, will return whether a hook is * currently being executed. * - * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Hooks instance. + * @param {import('.').StoreKey} storeKey * * @return {DoingHook} Function that returns whether a hook is currently * being executed. */ -function createDoingHook( hooks ) { +function createDoingHook( hooks, storeKey ) { return function doingHook( hookName ) { + const hooksStore = hooks[ storeKey ]; + // If the hookName was not passed, check for any current hook. if ( 'undefined' === typeof hookName ) { - return 'undefined' !== typeof hooks.__current[ 0 ]; + return 'undefined' !== typeof hooksStore.__current[ 0 ]; } // Return the __current hook. - return hooks.__current[ 0 ] - ? hookName === hooks.__current[ 0 ].name + return hooksStore.__current[ 0 ] + ? hookName === hooksStore.__current[ 0 ].name : false; }; } diff --git a/packages/hooks/src/createHasHook.js b/packages/hooks/src/createHasHook.js index 177aa8080b4e67..36a8c0450626e3 100644 --- a/packages/hooks/src/createHasHook.js +++ b/packages/hooks/src/createHasHook.js @@ -13,24 +13,27 @@ * Returns a function which, when invoked, will return whether any handlers are * attached to a particular hook. * - * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. + * @param {import('.').Hooks} hooks Hooks instance. + * @param {import('.').StoreKey} storeKey * * @return {HasHook} Function that returns whether any handlers are * attached to a particular hook and optional namespace. */ -function createHasHook( hooks ) { +function createHasHook( hooks, storeKey ) { return function hasHook( hookName, namespace ) { + const hooksStore = hooks[ storeKey ]; + // Use the namespace if provided. if ( 'undefined' !== typeof namespace ) { return ( - hookName in hooks && - hooks[ hookName ].handlers.some( + hookName in hooksStore && + hooksStore[ hookName ].handlers.some( ( hook ) => hook.namespace === namespace ) ); } - return hookName in hooks; + return hookName in hooksStore; }; } diff --git a/packages/hooks/src/createHooks.js b/packages/hooks/src/createHooks.js index 6acdb2288405b1..361383a3a97fc9 100644 --- a/packages/hooks/src/createHooks.js +++ b/packages/hooks/src/createHooks.js @@ -9,37 +9,51 @@ import createCurrentHook from './createCurrentHook'; import createDoingHook from './createDoingHook'; import createDidHook from './createDidHook'; +/** + * Internal class for constructing hooks. Use `createHooks()` function + * + * Note, it is necessary to expose this class to make its type public. + * + * @private + */ +export class _Hooks { + constructor() { + /** @type {import('.').Store} actions */ + this.actions = Object.create( null ); + this.actions.__current = []; + + /** @type {import('.').Store} filters */ + this.filters = Object.create( null ); + this.filters.__current = []; + + this.addAction = createAddHook( this, 'actions' ); + this.addFilter = createAddHook( this, 'filters' ); + this.removeAction = createRemoveHook( this, 'actions' ); + this.removeFilter = createRemoveHook( this, 'filters' ); + this.hasAction = createHasHook( this, 'actions' ); + this.hasFilter = createHasHook( this, 'filters' ); + this.removeAllActions = createRemoveHook( this, 'actions', true ); + this.removeAllFilters = createRemoveHook( this, 'filters', true ); + this.doAction = createRunHook( this, 'actions' ); + this.applyFilters = createRunHook( this, 'filters', true ); + this.currentAction = createCurrentHook( this, 'actions' ); + this.currentFilter = createCurrentHook( this, 'filters' ); + this.doingAction = createDoingHook( this, 'actions' ); + this.doingFilter = createDoingHook( this, 'filters' ); + this.didAction = createDidHook( this, 'actions' ); + this.didFilter = createDidHook( this, 'filters' ); + } +} + +/** @typedef {_Hooks} Hooks */ + /** * Returns an instance of the hooks object. + * + * @return {Hooks} A Hooks instance. */ function createHooks() { - /** @type {import('.').Hooks} */ - const actions = Object.create( null ); - /** @type {import('.').Hooks} */ - const filters = Object.create( null ); - actions.__current = []; - filters.__current = []; - - return { - addAction: createAddHook( actions ), - addFilter: createAddHook( filters ), - removeAction: createRemoveHook( actions ), - removeFilter: createRemoveHook( filters ), - hasAction: createHasHook( actions ), - hasFilter: createHasHook( filters ), - removeAllActions: createRemoveHook( actions, true ), - removeAllFilters: createRemoveHook( filters, true ), - doAction: createRunHook( actions ), - applyFilters: createRunHook( filters, true ), - currentAction: createCurrentHook( actions ), - currentFilter: createCurrentHook( filters ), - doingAction: createDoingHook( actions ), - doingFilter: createDoingHook( filters ), - didAction: createDidHook( actions ), - didFilter: createDidHook( filters ), - actions, - filters, - }; + return new _Hooks(); } export default createHooks; diff --git a/packages/hooks/src/createRemoveHook.js b/packages/hooks/src/createRemoveHook.js index f76d2ea8acd8ac..0b649310f1d599 100644 --- a/packages/hooks/src/createRemoveHook.js +++ b/packages/hooks/src/createRemoveHook.js @@ -3,7 +3,6 @@ */ import validateNamespace from './validateNamespace.js'; import validateHookName from './validateHookName.js'; -import { doAction } from './'; /** * @callback RemoveHook @@ -21,15 +20,18 @@ import { doAction } from './'; * Returns a function which, when invoked, will remove a specified hook or all * hooks by the given name. * - * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. - * @param {boolean} [removeAll=false] Whether to remove all callbacks for a hookName, - * without regard to namespace. Used to create - * `removeAll*` functions. + * @param {import('.').Hooks} hooks Hooks instance. + * @param {import('.').StoreKey} storeKey + * @param {boolean} [removeAll=false] Whether to remove all callbacks for a hookName, + * without regard to namespace. Used to create + * `removeAll*` functions. * * @return {RemoveHook} Function that removes hooks. */ -function createRemoveHook( hooks, removeAll = false ) { +function createRemoveHook( hooks, storeKey, removeAll = false ) { return function removeHook( hookName, namespace ) { + const hooksStore = hooks[ storeKey ]; + if ( ! validateHookName( hookName ) ) { return; } @@ -39,21 +41,21 @@ function createRemoveHook( hooks, removeAll = false ) { } // Bail if no hooks exist by this name - if ( ! hooks[ hookName ] ) { + if ( ! hooksStore[ hookName ] ) { return 0; } let handlersRemoved = 0; if ( removeAll ) { - handlersRemoved = hooks[ hookName ].handlers.length; - hooks[ hookName ] = { - runs: hooks[ hookName ].runs, + handlersRemoved = hooksStore[ hookName ].handlers.length; + hooksStore[ hookName ] = { + runs: hooksStore[ hookName ].runs, handlers: [], }; } else { // Try to find the specified callback to remove. - const handlers = hooks[ hookName ].handlers; + const handlers = hooksStore[ hookName ].handlers; for ( let i = handlers.length - 1; i >= 0; i-- ) { if ( handlers[ i ].namespace === namespace ) { handlers.splice( i, 1 ); @@ -63,7 +65,7 @@ function createRemoveHook( hooks, removeAll = false ) { // comes after the current callback, there's no problem; // otherwise we need to decrease the execution index of any // other runs by 1 to account for the removed element. - hooks.__current.forEach( ( hookInfo ) => { + hooksStore.__current.forEach( ( hookInfo ) => { if ( hookInfo.name === hookName && hookInfo.currentIndex >= i @@ -74,8 +76,9 @@ function createRemoveHook( hooks, removeAll = false ) { } } } + if ( hookName !== 'hookRemoved' ) { - doAction( 'hookRemoved', hookName, namespace ); + hooks.doAction( 'hookRemoved', hookName, namespace ); } return handlersRemoved; diff --git a/packages/hooks/src/createRunHook.js b/packages/hooks/src/createRunHook.js index 084eff8f2ed4c7..da0e4308713422 100644 --- a/packages/hooks/src/createRunHook.js +++ b/packages/hooks/src/createRunHook.js @@ -3,30 +3,33 @@ * registered to a hook of the specified type, optionally returning the final * value of the call chain. * - * @param {import('.').Hooks} hooks Stored hooks, keyed by hook name. - * @param {boolean} [returnFirstArg=false] Whether each hook callback is expected to - * return its first argument. + * @param {import('.').Hooks} hooks Hooks instance. + * @param {import('.').StoreKey} storeKey + * @param {boolean} [returnFirstArg=false] Whether each hook callback is expected to + * return its first argument. * * @return {(hookName:string, ...args: unknown[]) => unknown} Function that runs hook callbacks. */ -function createRunHook( hooks, returnFirstArg = false ) { +function createRunHook( hooks, storeKey, returnFirstArg = false ) { return function runHooks( hookName, ...args ) { - if ( ! hooks[ hookName ] ) { - hooks[ hookName ] = { + const hooksStore = hooks[ storeKey ]; + + if ( ! hooksStore[ hookName ] ) { + hooksStore[ hookName ] = { handlers: [], runs: 0, }; } - hooks[ hookName ].runs++; + hooksStore[ hookName ].runs++; - const handlers = hooks[ hookName ].handlers; + const handlers = hooksStore[ hookName ].handlers; // The following code is stripped from production builds. if ( 'production' !== process.env.NODE_ENV ) { // Handle any 'all' hooks registered. - if ( 'hookAdded' !== hookName && hooks.all ) { - handlers.push( ...hooks.all.handlers ); + if ( 'hookAdded' !== hookName && hooksStore.all ) { + handlers.push( ...hooksStore.all.handlers ); } } @@ -39,7 +42,7 @@ function createRunHook( hooks, returnFirstArg = false ) { currentIndex: 0, }; - hooks.__current.push( hookInfo ); + hooksStore.__current.push( hookInfo ); while ( hookInfo.currentIndex < handlers.length ) { const handler = handlers[ hookInfo.currentIndex ]; @@ -52,7 +55,7 @@ function createRunHook( hooks, returnFirstArg = false ) { hookInfo.currentIndex++; } - hooks.__current.pop(); + hooksStore.__current.pop(); if ( returnFirstArg ) { return args[ 0 ]; diff --git a/packages/hooks/src/index.js b/packages/hooks/src/index.js index 15561d318993ce..68059d3e8fc66e 100644 --- a/packages/hooks/src/index.js +++ b/packages/hooks/src/index.js @@ -16,7 +16,6 @@ import createHooks from './createHooks'; * @typedef Hook * @property {Handler[]} handlers Array of handlers * @property {number} runs Run counter - * */ /** @@ -26,7 +25,15 @@ import createHooks from './createHooks'; */ /** - * @typedef {Record<string, Hook> & {__current: Current[]}} Hooks + * @typedef {Record<string, Hook> & {__current: Current[]}} Store + */ + +/** + * @typedef {'actions' | 'filters'} StoreKey + */ + +/** + * @typedef {import('./createHooks').Hooks} Hooks */ const { diff --git a/packages/hooks/src/test/index.test.js b/packages/hooks/src/test/index.test.js index c428c1cd59dc61..da86cc5e56f213 100644 --- a/packages/hooks/src/test/index.test.js +++ b/packages/hooks/src/test/index.test.js @@ -742,6 +742,23 @@ test( 'adding an action triggers a hookAdded action passing all callback details actionA, 9 ); + + // Private instance. + const hooksPrivateInstance = createHooks(); + + removeAction( 'hookAdded', 'my_callback' ); + hookAddedSpy.mockClear(); + + hooksPrivateInstance.addAction( 'hookAdded', 'my_callback', hookAddedSpy ); + hooksPrivateInstance.addAction( 'testAction', 'my_callback2', actionA, 9 ); + + expect( hookAddedSpy ).toHaveBeenCalledTimes( 1 ); + expect( hookAddedSpy ).toHaveBeenCalledWith( + 'testAction', + 'my_callback2', + actionA, + 9 + ); } ); test( 'adding a filter triggers a hookAdded action passing all callback details', () => { @@ -757,6 +774,23 @@ test( 'adding a filter triggers a hookAdded action passing all callback details' filterA, 8 ); + + // Private instance. + const hooksPrivateInstance = createHooks(); + + removeAction( 'hookAdded', 'my_callback' ); + hookAddedSpy.mockClear(); + + hooksPrivateInstance.addAction( 'hookAdded', 'my_callback', hookAddedSpy ); + hooksPrivateInstance.addFilter( 'testFilter', 'my_callback3', filterA, 8 ); + + expect( hookAddedSpy ).toHaveBeenCalledTimes( 1 ); + expect( hookAddedSpy ).toHaveBeenCalledWith( + 'testFilter', + 'my_callback3', + filterA, + 8 + ); } ); test( 'removing an action triggers a hookRemoved action passing all callback details', () => { @@ -772,6 +806,27 @@ test( 'removing an action triggers a hookRemoved action passing all callback det 'testAction', 'my_callback2' ); + + // Private instance. + const hooksPrivateInstance = createHooks(); + + removeAction( 'hookRemoved', 'my_callback' ); + hookRemovedSpy.mockClear(); + + hooksPrivateInstance.addAction( + 'hookRemoved', + 'my_callback', + hookRemovedSpy + ); + + hooksPrivateInstance.addAction( 'testAction', 'my_callback2', actionA, 9 ); + hooksPrivateInstance.removeAction( 'testAction', 'my_callback2' ); + + expect( hookRemovedSpy ).toHaveBeenCalledTimes( 1 ); + expect( hookRemovedSpy ).toHaveBeenCalledWith( + 'testAction', + 'my_callback2' + ); } ); test( 'removing a filter triggers a hookRemoved action passing all callback details', () => { @@ -787,6 +842,27 @@ test( 'removing a filter triggers a hookRemoved action passing all callback deta 'testFilter', 'my_callback3' ); + + // Private instance. + const hooksPrivateInstance = createHooks(); + + removeAction( 'hookRemoved', 'my_callback' ); + hookRemovedSpy.mockClear(); + + hooksPrivateInstance.addAction( + 'hookRemoved', + 'my_callback', + hookRemovedSpy + ); + + hooksPrivateInstance.addFilter( 'testFilter', 'my_callback3', filterA, 8 ); + hooksPrivateInstance.removeFilter( 'testFilter', 'my_callback3' ); + + expect( hookRemovedSpy ).toHaveBeenCalledTimes( 1 ); + expect( hookRemovedSpy ).toHaveBeenCalledWith( + 'testFilter', + 'my_callback3' + ); } ); test( 'add an all filter and run it any hook to trigger it', () => { diff --git a/packages/html-entities/package.json b/packages/html-entities/package.json index f8740f94784034..088c3eb796ba2a 100644 --- a/packages/html-entities/package.json +++ b/packages/html-entities/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/html-entities", - "version": "2.9.0", + "version": "2.10.0", "description": "HTML entity utilities for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "react-native": "src/index", "types": "build-types", "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/i18n/CHANGELOG.md b/packages/i18n/CHANGELOG.md index a9632e02d581d5..dc47abc9590a5e 100644 --- a/packages/i18n/CHANGELOG.md +++ b/packages/i18n/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.17.0 (2020-12-17) + ### Enhancements - Improve type declarations for translation functions ([#26171](https://github.com/WordPress/gutenberg/pull/26171)) diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 3ab57d62b2b8c4..b3d187e3a8a2b6 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/i18n", - "version": "3.16.0", + "version": "3.17.0", "description": "WordPress internationalization (i18n) library.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -26,7 +26,7 @@ "pot-to-php": "./tools/pot-to-php.js" }, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "gettext-parser": "^1.3.1", "lodash": "^4.17.19", "memize": "^1.1.0", diff --git a/packages/icons/package.json b/packages/icons/package.json index a4234a349e499f..41e6f1dfeac2e7 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/icons", - "version": "2.8.0", + "version": "2.9.0", "description": "WordPress Icons package, based on dashicon.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -25,7 +25,7 @@ "react-native": "src/index", "types": "build-types", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/element": "file:../element", "@wordpress/primitives": "file:../primitives" }, diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js index c583c3533c4a3d..7140f8a9a3d94d 100644 --- a/packages/icons/src/index.js +++ b/packages/icons/src/index.js @@ -171,6 +171,9 @@ export { default as tableRowBefore } from './library/table-row-before'; export { default as tableRowDelete } from './library/table-row-delete'; export { default as table } from './library/table'; export { default as tag } from './library/tag'; +export { default as templatePartFooter } from './library/template-part-footer'; +export { default as templatePartHeader } from './library/template-part-header'; +export { default as templatePartSidebar } from './library/template-part-sidebar'; export { default as textColor } from './library/text-color'; export { default as tablet } from './library/tablet'; export { default as title } from './library/title'; diff --git a/packages/icons/src/library/backup.js b/packages/icons/src/library/backup.js index 3a50f31efd6a76..39b0b541bc2980 100644 --- a/packages/icons/src/library/backup.js +++ b/packages/icons/src/library/backup.js @@ -4,8 +4,8 @@ import { SVG, Path } from '@wordpress/primitives'; const backup = ( - <SVG xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 24 24"> - <Path d="M13.65 2.88c3.93 2.01 5.48 6.84 3.47 10.77s-6.83 5.48-10.77 3.47c-1.87-.96-3.2-2.56-3.86-4.4l1.64-1.03c.45 1.57 1.52 2.95 3.08 3.76 3.01 1.54 6.69.35 8.23-2.66 1.55-3.01.36-6.69-2.65-8.24C9.78 3.01 6.1 4.2 4.56 7.21l1.88.97-4.95 3.08-.39-5.82 1.78.91C4.9 2.4 9.75.89 13.65 2.88zm-4.36 7.83C9.11 10.53 9 10.28 9 10c0-.07.03-.12.04-.19h-.01L10 5l.97 4.81L14 13l-4.5-2.12.02-.02c-.08-.04-.16-.09-.23-.15z" /> + <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <Path d="M5.5 12h1.75l-2.5 3-2.5-3H4a8 8 0 113.134 6.35l.907-1.194A6.5 6.5 0 105.5 12zm9.53 1.97l-2.28-2.28V8.5a.75.75 0 00-1.5 0V12a.747.747 0 00.218.529l1.282-.84-1.28.842 2.5 2.5a.75.75 0 101.06-1.061z" /> </SVG> ); diff --git a/packages/icons/src/library/template-part-footer.js b/packages/icons/src/library/template-part-footer.js new file mode 100644 index 00000000000000..bffc0f19716914 --- /dev/null +++ b/packages/icons/src/library/template-part-footer.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const templatePartFooter = ( + <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <Path + fill-rule="evenodd" + d="M18 5.5h-8v8h8.5V6a.5.5 0 00-.5-.5zm-9.5 8h-3V6a.5.5 0 01.5-.5h2.5v8zM6 4h12a2 2 0 012 2v12a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z" + clip-rule="evenodd" + /> + </SVG> +); + +export default templatePartFooter; diff --git a/packages/icons/src/library/template-part-header.js b/packages/icons/src/library/template-part-header.js new file mode 100644 index 00000000000000..757eabe4acd323 --- /dev/null +++ b/packages/icons/src/library/template-part-header.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const templatePartHeader = ( + <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <Path d="M18.5 10.5H10v8h8a.5.5 0 00.5-.5v-7.5zm-10 0h-3V18a.5.5 0 00.5.5h2.5v-8zM6 4h12a2 2 0 012 2v12a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z" /> + </SVG> +); + +export default templatePartHeader; diff --git a/packages/icons/src/library/template-part-sidebar.js b/packages/icons/src/library/template-part-sidebar.js new file mode 100644 index 00000000000000..4156a163a88ba0 --- /dev/null +++ b/packages/icons/src/library/template-part-sidebar.js @@ -0,0 +1,12 @@ +/** + * WordPress dependencies + */ +import { SVG, Path } from '@wordpress/primitives'; + +const templatePartSidebar = ( + <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <Path d="M18 5.5H6a.5.5 0 00-.5.5v3h13V6a.5.5 0 00-.5-.5zm.5 5H10v8h8a.5.5 0 00.5-.5v-7.5zM6 4h12a2 2 0 012 2v12a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2z" /> + </SVG> +); + +export default templatePartSidebar; diff --git a/packages/interface/CHANGELOG.md b/packages/interface/CHANGELOG.md index 1522d57767261a..7813a50413a382 100644 --- a/packages/interface/CHANGELOG.md +++ b/packages/interface/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 0.11.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the interface namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/interface/package.json b/packages/interface/package.json index bf410cf0f7dbd3..af8338a7e00c81 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/interface", - "version": "0.10.4", + "version": "0.11.0", "description": "Interface module for WordPress. The package contains shared functionality across the modern JavaScript-based WordPress screens.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -28,7 +28,7 @@ "{src,build,build-module}/{index.js,store/index.js}" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/components": "file:../components", "@wordpress/data": "file:../data", "@wordpress/deprecated": "file:../deprecated", diff --git a/packages/interface/src/components/index.js b/packages/interface/src/components/index.js index d8ba05d8230dc2..971b42522ae3c4 100644 --- a/packages/interface/src/components/index.js +++ b/packages/interface/src/components/index.js @@ -3,5 +3,4 @@ export { default as ComplementaryAreaMoreMenuItem } from './complementary-area-m export { default as FullscreenMode } from './fullscreen-mode'; export { default as InterfaceSkeleton } from './interface-skeleton'; export { default as PinnedItems } from './pinned-items'; -export { default as __experimentalMainDashboardButton } from './main-dashboard-button'; export { default as ActionItem } from './action-item'; diff --git a/packages/is-shallow-equal/CHANGELOG.md b/packages/is-shallow-equal/CHANGELOG.md index bdd95fb63468e7..48d756b410d2b7 100644 --- a/packages/is-shallow-equal/CHANGELOG.md +++ b/packages/is-shallow-equal/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.0.0 (2020-12-17) + ### Breaking Change - Re-write using ES Modules causing CJS default import to change from `require('@wordpress/is-shallow-equal)` to `require('@wordpress/is-shallow-equal).default`. ([#26833](https://github.com/WordPress/gutenberg/pull/26833)) diff --git a/packages/is-shallow-equal/package.json b/packages/is-shallow-equal/package.json index 515b9bbe0ad482..4ef398147a46f6 100644 --- a/packages/is-shallow-equal/package.json +++ b/packages/is-shallow-equal/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/is-shallow-equal", - "version": "2.3.0", + "version": "3.0.0", "description": "Test for shallow equality between two objects or arrays.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -33,7 +33,7 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/jest-console/package.json b/packages/jest-console/package.json index ca2eeebc9e558b..3edb7ba0099b08 100644 --- a/packages/jest-console/package.json +++ b/packages/jest-console/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-console", - "version": "3.9.0", + "version": "3.10.0", "description": "Custom Jest matchers for the Console object.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -30,7 +30,7 @@ "main": "build/index.js", "module": "build-module/index.js", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "jest-matcher-utils": "^25.3.0", "lodash": "^4.17.19" }, diff --git a/packages/jest-preset-default/package.json b/packages/jest-preset-default/package.json index 069696b3a23e1a..30119ca6e90a01 100644 --- a/packages/jest-preset-default/package.json +++ b/packages/jest-preset-default/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-preset-default", - "version": "6.5.0", + "version": "6.6.0", "description": "Default Jest preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/jest-puppeteer-axe/CHANGELOG.md b/packages/jest-puppeteer-axe/CHANGELOG.md index 3445543152499c..058cbb84b23e5a 100644 --- a/packages/jest-puppeteer-axe/CHANGELOG.md +++ b/packages/jest-puppeteer-axe/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.0.0 (2020-12-17) + ### Breaking Changes - Migrated `axe-puppeteer` to its new package [@axe-core/puppeteer](https://github.com/dequelabs/axe-core-npm/tree/develop/packages/puppeteer) that contains breaking changes. diff --git a/packages/jest-puppeteer-axe/package.json b/packages/jest-puppeteer-axe/package.json index 01380b154eadea..9d28f825985126 100644 --- a/packages/jest-puppeteer-axe/package.json +++ b/packages/jest-puppeteer-axe/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/jest-puppeteer-axe", - "version": "1.10.0", + "version": "2.0.0", "description": "Axe API integration with Jest and Puppeteer.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -32,7 +32,7 @@ "module": "build-module/index.js", "dependencies": { "@axe-core/puppeteer": "^4.0.0", - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "peerDependencies": { "jest": ">=24", diff --git a/packages/keyboard-shortcuts/CHANGELOG.md b/packages/keyboard-shortcuts/CHANGELOG.md index 25ddc903fc4593..45aa3791d40bf0 100644 --- a/packages/keyboard-shortcuts/CHANGELOG.md +++ b/packages/keyboard-shortcuts/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.13.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the keyboard shortcuts namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/keyboard-shortcuts/package.json b/packages/keyboard-shortcuts/package.json index 162dff23cbd727..2905118d8c7a33 100644 --- a/packages/keyboard-shortcuts/package.json +++ b/packages/keyboard-shortcuts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keyboard-shortcuts", - "version": "1.12.0", + "version": "1.13.0", "description": "Handling keyboard shortcuts.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,12 +22,11 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", "@wordpress/element": "file:../element", "@wordpress/keycodes": "file:../keycodes", - "@wordpress/stan": "file:../stan", "lodash": "^4.17.19", "rememo": "^3.0.0" }, diff --git a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js index f6b0f4e584b90f..7447209d5ba713 100644 --- a/packages/keyboard-shortcuts/src/hooks/use-shortcut.js +++ b/packages/keyboard-shortcuts/src/hooks/use-shortcut.js @@ -4,6 +4,11 @@ import { useSelect } from '@wordpress/data'; import { useKeyboardShortcut } from '@wordpress/compose'; +/** + * Internal dependencies + */ +import { store as keyboardShortcutsStore } from '../store'; + /** * Attach a keyboard shortcut handler. * @@ -15,7 +20,7 @@ function useShortcut( name, callback, options ) { const shortcuts = useSelect( ( select ) => { return select( - 'core/keyboard-shortcuts' + keyboardShortcutsStore ).getAllShortcutRawKeyCombinations( name ); }, [ name ] diff --git a/packages/keyboard-shortcuts/src/store/actions.js b/packages/keyboard-shortcuts/src/store/actions.js index 865ab9a3a796ab..b4cb625287d8fb 100644 --- a/packages/keyboard-shortcuts/src/store/actions.js +++ b/packages/keyboard-shortcuts/src/store/actions.js @@ -1,13 +1,3 @@ -/** - * External dependencies - */ -import { omit } from 'lodash'; - -/** - * Internal dependencies - */ -import { shortcutsByNameAtom, shortcutNamesAtom } from './atoms'; - /** @typedef {import('@wordpress/keycodes').WPKeycodeModifier} WPKeycodeModifier */ /** @@ -34,40 +24,37 @@ import { shortcutsByNameAtom, shortcutNamesAtom } from './atoms'; /** * Returns an action object used to register a new keyboard shortcut. * - * @param {Function} get Atom resover. - * @param {Function} set Atom updater. - * @param {WPShortcutConfig} config Shortcut config. + * @param {WPShortcutConfig} config Shortcut config. + * + * @return {Object} action. */ -export const registerShortcut = ( config ) => ( { get, set } ) => { - const shortcutNames = get( shortcutNamesAtom ); - const hasShortcut = shortcutNames.includes( config.name ); - if ( ! hasShortcut ) { - set( shortcutNamesAtom, [ ...shortcutNames, config.name ] ); - } - const shortcutsByName = get( shortcutsByNameAtom ); - set( shortcutsByNameAtom, { - ...shortcutsByName, - [ config.name ]: config, - } ); -}; +export function registerShortcut( { + name, + category, + description, + keyCombination, + aliases, +} ) { + return { + type: 'REGISTER_SHORTCUT', + name, + category, + keyCombination, + aliases, + description, + }; +} /** * Returns an action object used to unregister a keyboard shortcut. * - * @param {Function} get get atom value. - * @param {Function} set set atom value. - * @param {string} name Shortcut name. + * @param {string} name Shortcut name. + * + * @return {Object} action. */ -export const unregisterShortcut = ( name ) => ( { get, set } ) => { - const shortcutNames = get( shortcutNamesAtom ); - set( - shortcutNamesAtom, - shortcutNames.filter( ( n ) => n !== name ) - ); - const shortcutByNames = get( shortcutsByNameAtom ); - set( shortcutsByNameAtom, omit( shortcutByNames, [ name ] ) ); - - // The atom will remain in the family atoms - // We need to build a way to remove it automatically once the parent atom changes. - // atomRegistry.deleteAtom( shortcutByNames[ name ] ); -}; +export function unregisterShortcut( name ) { + return { + type: 'UNREGISTER_SHORTCUT', + name, + }; +} diff --git a/packages/keyboard-shortcuts/src/store/atoms.js b/packages/keyboard-shortcuts/src/store/atoms.js deleted file mode 100644 index d5a3a159252ef2..00000000000000 --- a/packages/keyboard-shortcuts/src/store/atoms.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * WordPress dependencies - */ -import { createAtom, createDerivedAtom } from '@wordpress/stan'; - -export const shortcutNamesAtom = createAtom( [], { id: 'shortcut-names' } ); -export const shortcutsByNameAtom = createAtom( - {}, - { id: 'shortcuts-by-name' } -); -export const shortcutsAtom = createDerivedAtom( - ( { get } ) => { - const shortcutsByName = get( shortcutsByNameAtom ); - return get( shortcutNamesAtom ).map( ( id ) => shortcutsByName[ id ] ); - }, - () => {}, - { id: 'shortcuts' } -); - -export const rootAtoms = [ shortcutNamesAtom, shortcutsByNameAtom ]; diff --git a/packages/keyboard-shortcuts/src/store/index.js b/packages/keyboard-shortcuts/src/store/index.js index c4f385d89b4b3e..0f40e3f4d74829 100644 --- a/packages/keyboard-shortcuts/src/store/index.js +++ b/packages/keyboard-shortcuts/src/store/index.js @@ -1,15 +1,17 @@ /** * WordPress dependencies */ -import { __experimentalCreateAtomicStore, register } from '@wordpress/data'; +import { createReduxStore, register } from '@wordpress/data'; /** * Internal dependencies */ -import { rootAtoms } from './atoms'; +import reducer from './reducer'; import * as actions from './actions'; import * as selectors from './selectors'; +const STORE_NAME = 'core/keyboard-shortcuts'; + /** * Store definition for the keyboard shortcuts namespace. * @@ -17,13 +19,10 @@ import * as selectors from './selectors'; * * @type {Object} */ -export const store = __experimentalCreateAtomicStore( - 'core/keyboard-shortcuts', - { - rootAtoms, - actions, - selectors, - } -); +export const store = createReduxStore( STORE_NAME, { + reducer, + actions, + selectors, +} ); register( store ); diff --git a/packages/keyboard-shortcuts/src/store/reducer.js b/packages/keyboard-shortcuts/src/store/reducer.js new file mode 100644 index 00000000000000..7489d529bf26ad --- /dev/null +++ b/packages/keyboard-shortcuts/src/store/reducer.js @@ -0,0 +1,33 @@ +/** + * External dependencies + */ +import { omit } from 'lodash'; + +/** + * Reducer returning the registered shortcuts + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +function reducer( state = {}, action ) { + switch ( action.type ) { + case 'REGISTER_SHORTCUT': + return { + ...state, + [ action.name ]: { + category: action.category, + keyCombination: action.keyCombination, + aliases: action.aliases, + description: action.description, + }, + }; + case 'UNREGISTER_SHORTCUT': + return omit( state, action.name ); + } + + return state; +} + +export default reducer; diff --git a/packages/keyboard-shortcuts/src/store/selectors.js b/packages/keyboard-shortcuts/src/store/selectors.js index 9f467cbeb99e8c..4bf4c4c9b9c5aa 100644 --- a/packages/keyboard-shortcuts/src/store/selectors.js +++ b/packages/keyboard-shortcuts/src/store/selectors.js @@ -1,6 +1,7 @@ /** * External dependencies */ +import createSelector from 'rememo'; import { compact } from 'lodash'; /** @@ -11,12 +12,6 @@ import { shortcutAriaLabel, rawShortcut, } from '@wordpress/keycodes'; -import { createAtomSelector } from '@wordpress/stan'; - -/** - * Internal dependencies - */ -import { shortcutsAtom, shortcutsByNameAtom } from './atoms'; /** @typedef {import('./actions').WPShortcutKeyCombination} WPShortcutKeyCombination */ @@ -64,105 +59,99 @@ function getKeyCombinationRepresentation( shortcut, representation ) { : shortcut.character; } -/** - * Returns the shortcut object for a given shortcut name. - * - * @param {string} name Shortcut name. - * @return {WPShortcutKeyCombination?} Key combination. - */ -const getShortcut = createAtomSelector( ( name ) => ( { get } ) => { - return get( shortcutsByNameAtom )[ name ]; -} ); - /** * Returns the main key combination for a given shortcut name. * - * @param {string} name Shortcut name. + * @param {Object} state Global state. + * @param {string} name Shortcut name. + * * @return {WPShortcutKeyCombination?} Key combination. */ -export const getShortcutKeyCombination = createAtomSelector( - ( name ) => ( { get } ) => { - const shortcut = get( getShortcut( name ) ); - return shortcut ? shortcut.keyCombination : null; - } -); +export function getShortcutKeyCombination( state, name ) { + return state[ name ] ? state[ name ].keyCombination : null; +} /** * Returns a string representing the main key combination for a given shortcut name. * + * @param {Object} state Global state. * @param {string} name Shortcut name. * @param {keyof FORMATTING_METHODS} representation Type of representation * (display, raw, ariaLabel). * * @return {string?} Shortcut representation. */ -export const getShortcutRepresentation = createAtomSelector( - ( name, representation = 'display' ) => ( { get } ) => { - const shortcut = get( getShortcutKeyCombination( name ) ); - return getKeyCombinationRepresentation( shortcut, representation ); - } -); +export function getShortcutRepresentation( + state, + name, + representation = 'display' +) { + const shortcut = getShortcutKeyCombination( state, name ); + return getKeyCombinationRepresentation( shortcut, representation ); +} /** * Returns the shortcut description given its name. * - * @param {string} name Shortcut name. + * @param {Object} state Global state. + * @param {string} name Shortcut name. * * @return {string?} Shortcut description. */ -export const getShortcutDescription = createAtomSelector( - ( name ) => ( { get } ) => { - const shortcut = get( getShortcut( name ) ); - return shortcut ? shortcut.description : null; - } -); +export function getShortcutDescription( state, name ) { + return state[ name ] ? state[ name ].description : null; +} /** * Returns the aliases for a given shortcut name. * - * @param {string} name Shortcut name. + * @param {Object} state Global state. + * @param {string} name Shortcut name. * * @return {WPShortcutKeyCombination[]} Key combinations. */ -export const getShortcutAliases = createAtomSelector( - ( name ) => ( { get } ) => { - const shortcut = get( getShortcut( name ) ); - return shortcut && shortcut.aliases ? shortcut.aliases : EMPTY_ARRAY; - } -); +export function getShortcutAliases( state, name ) { + return state[ name ] && state[ name ].aliases + ? state[ name ].aliases + : EMPTY_ARRAY; +} /** * Returns the raw representation of all the keyboard combinations of a given shortcut name. * - * @param {string} name Shortcut name. + * @param {Object} state Global state. + * @param {string} name Shortcut name. * * @return {string[]} Shortcuts. */ -export const getAllShortcutRawKeyCombinations = createAtomSelector( - ( name ) => ( { get } ) => { +export const getAllShortcutRawKeyCombinations = createSelector( + ( state, name ) => { return compact( [ getKeyCombinationRepresentation( - get( getShortcutKeyCombination( name ) ), + getShortcutKeyCombination( state, name ), 'raw' ), - ...get( getShortcutAliases( name ) ).map( ( combination ) => + ...getShortcutAliases( state, name ).map( ( combination ) => getKeyCombinationRepresentation( combination, 'raw' ) ), ] ); - } + }, + ( state, name ) => [ state[ name ] ] ); /** * Returns the shortcut names list for a given category name. * - * @param {string} categoryName Category name. + * @param {Object} state Global state. + * @param {string} name Category name. * * @return {string[]} Shortcut names. */ -export const getCategoryShortcuts = createAtomSelector( - ( categoryName ) => ( { get } ) => { - return ( get( shortcutsAtom ) || [] ) - .filter( ( shortcut ) => shortcut.category === categoryName ) - .map( ( { name } ) => name ); - } +export const getCategoryShortcuts = createSelector( + ( state, categoryName ) => { + return Object.entries( state ) + .filter( ( [ , shortcut ] ) => shortcut.category === categoryName ) + .map( ( [ name ] ) => name ); + }, + ( state ) => [ state ] ); diff --git a/packages/keycodes/package.json b/packages/keycodes/package.json index 536e77bd6d92b4..f6a899955be713 100644 --- a/packages/keycodes/package.json +++ b/packages/keycodes/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/keycodes", - "version": "2.16.0", + "version": "2.17.0", "description": "Keycodes utilities for WordPress. Used to check for keyboard events across browsers/operating systems.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "react-native": "src/index", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/i18n": "file:../i18n", "lodash": "^4.17.19" }, diff --git a/packages/lazy-import/CHANGELOG.md b/packages/lazy-import/CHANGELOG.md index 551a536b84645d..7bf5ddc244a457 100644 --- a/packages/lazy-import/CHANGELOG.md +++ b/packages/lazy-import/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.2.0 (2020-12-17) + ### New Feature - Allow local paths as an option when trying to import a specific file from the package ([#23751](https://github.com/WordPress/gutenberg/pull/23751)). diff --git a/packages/lazy-import/package.json b/packages/lazy-import/package.json index 367fa2d76739e2..a8cb34a777b67e 100644 --- a/packages/lazy-import/package.json +++ b/packages/lazy-import/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/lazy-import", - "version": "1.1.0", + "version": "1.2.0", "description": "Lazily import a module, installing it automatically if missing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/list-reusable-blocks/package.json b/packages/list-reusable-blocks/package.json index 7e61f085427c85..55f42c9703b6e9 100644 --- a/packages/list-reusable-blocks/package.json +++ b/packages/list-reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/list-reusable-blocks", - "version": "1.24.1", + "version": "1.25.0", "description": "Adding Export/Import support to the reusable blocks listing.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "main": "build/index.js", "module": "build-module/index.js", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", diff --git a/packages/media-utils/package.json b/packages/media-utils/package.json index 9cab8621a76926..e16276c50d6360 100644 --- a/packages/media-utils/package.json +++ b/packages/media-utils/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/media-utils", - "version": "1.18.0", + "version": "1.19.0", "description": "WordPress Media Upload Utils.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "main": "build/index.js", "module": "build-module/index.js", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/blob": "file:../blob", "@wordpress/element": "file:../element", diff --git a/packages/notices/CHANGELOG.md b/packages/notices/CHANGELOG.md index 1f6ed1d38a6736..1f4a1068b1055b 100644 --- a/packages/notices/CHANGELOG.md +++ b/packages/notices/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.12.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the notices namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/notices/package.json b/packages/notices/package.json index 1fc7c40f86fc4e..9ce7f2878b9f7b 100644 --- a/packages/notices/package.json +++ b/packages/notices/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/notices", - "version": "2.11.0", + "version": "2.12.0", "description": "State management for notices.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/a11y": "file:../a11y", "@wordpress/data": "file:../data", "lodash": "^4.17.19" diff --git a/packages/notices/src/store/actions.js b/packages/notices/src/store/actions.js index c8250a4cbd20c3..ad25f01379fe25 100644 --- a/packages/notices/src/store/actions.js +++ b/packages/notices/src/store/actions.js @@ -41,6 +41,11 @@ import { DEFAULT_CONTEXT, DEFAULT_STATUS } from './constants'; * @param {Array<WPNoticeAction>} [options.actions] User actions to be * presented with notice. * @param {Object} [options.icon] An icon displayed with the notice. + * @param {boolean} [options.explicitDismiss] Whether the notice includes + * an explict dismiss button and + * can't be dismissed by clicking + * the body of the notice. + * @param {Function} [options.onDismiss] Called when the notice is dismissed. * * @return {Object} Action object. */ @@ -54,6 +59,8 @@ export function createNotice( status = DEFAULT_STATUS, content, options = {} ) { type = 'default', __unstableHTML, icon = null, + explicitDismiss = false, + onDismiss = null, } = options; // The supported value shape of content is currently limited to plain text @@ -74,6 +81,8 @@ export function createNotice( status = DEFAULT_STATUS, content, options = {} ) { actions, type, icon, + explicitDismiss, + onDismiss, }, }; } diff --git a/packages/notices/src/store/index.js b/packages/notices/src/store/index.js index 35c5e1d981b475..12b5b3a66e7965 100644 --- a/packages/notices/src/store/index.js +++ b/packages/notices/src/store/index.js @@ -10,8 +10,6 @@ import reducer from './reducer'; import * as actions from './actions'; import * as selectors from './selectors'; -const STORE_NAME = 'core/notices'; - /** * Store definition for the notices namespace. * @@ -19,7 +17,7 @@ const STORE_NAME = 'core/notices'; * * @type {Object} */ -export const store = createReduxStore( STORE_NAME, { +export const store = createReduxStore( 'core/notices', { reducer, actions, selectors, diff --git a/packages/notices/src/store/test/actions.js b/packages/notices/src/store/test/actions.js index 2edb0eb172a10a..1deb16807b4340 100644 --- a/packages/notices/src/store/test/actions.js +++ b/packages/notices/src/store/test/actions.js @@ -75,6 +75,8 @@ describe( 'actions', () => { actions: [], type: 'default', icon: '🌮', + explicitDismiss: false, + onDismiss: null, }, } ); } ); @@ -104,6 +106,8 @@ describe( 'actions', () => { actions: [], type: 'default', icon: null, + explicitDismiss: false, + onDismiss: null, }, } ); } ); diff --git a/packages/notices/src/store/test/reducer.js b/packages/notices/src/store/test/reducer.js index f356d13f39e106..2016d7c12d8397 100644 --- a/packages/notices/src/store/test/reducer.js +++ b/packages/notices/src/store/test/reducer.js @@ -34,6 +34,8 @@ describe( 'reducer', () => { actions: [], type: 'default', icon: null, + explicitDismiss: false, + onDismiss: null, }, ], } ); @@ -57,6 +59,8 @@ describe( 'reducer', () => { actions: [], type: 'default', icon: null, + explicitDismiss: false, + onDismiss: null, }, ], } ); @@ -81,6 +85,8 @@ describe( 'reducer', () => { actions: [], type: 'default', icon: null, + explicitDismiss: false, + onDismiss: null, }, { id: expect.any( String ), @@ -92,6 +98,8 @@ describe( 'reducer', () => { actions: [], type: 'default', icon: null, + explicitDismiss: false, + onDismiss: null, }, ], } ); @@ -156,6 +164,8 @@ describe( 'reducer', () => { actions: [], type: 'default', icon: null, + explicitDismiss: false, + onDismiss: null, }, ], } ); diff --git a/packages/nux/CHANGELOG.md b/packages/nux/CHANGELOG.md index 61be951fcd8d4a..3cfbc928097af2 100644 --- a/packages/nux/CHANGELOG.md +++ b/packages/nux/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.24.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the core data namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/nux/package.json b/packages/nux/package.json index 11131a75aef3f1..e631333dbb2b22 100644 --- a/packages/nux/package.json +++ b/packages/nux/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/nux", - "version": "3.23.1", + "version": "3.24.0", "description": "NUX (New User eXperience) module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -27,7 +27,7 @@ "{src,build,build-module}/{index.js,store/index.js}" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 37c3ee207f4ce3..1d6dda8a5adba5 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/plugins", - "version": "2.23.0", + "version": "2.24.0", "description": "Plugins module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:../compose", "@wordpress/element": "file:../element", "@wordpress/hooks": "file:../hooks", diff --git a/packages/postcss-plugins-preset/package.json b/packages/postcss-plugins-preset/package.json index c2cc049fec4604..c18b9498a19aa3 100644 --- a/packages/postcss-plugins-preset/package.json +++ b/packages/postcss-plugins-preset/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/postcss-plugins-preset", - "version": "1.5.1", + "version": "1.6.0", "description": "PostCSS sharable plugins preset for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/primitives/package.json b/packages/primitives/package.json index 7a378704d4d178..6552304620fba6 100644 --- a/packages/primitives/package.json +++ b/packages/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/primitives", - "version": "1.10.0", + "version": "1.11.0", "description": "WordPress cross-platform primitives.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -27,7 +27,7 @@ ], "types": "build-types", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/element": "file:../element", "classnames": "^2.2.5" }, diff --git a/packages/priority-queue/package.json b/packages/priority-queue/package.json index 5157a4baa15517..f99bce5acc1fbc 100644 --- a/packages/priority-queue/package.json +++ b/packages/priority-queue/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/priority-queue", - "version": "1.9.0", + "version": "1.10.0", "description": "Generic browser priority queue.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -25,7 +25,7 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/project-management-automation/package.json b/packages/project-management-automation/package.json index 11e61992207d3b..39857cbd361b53 100644 --- a/packages/project-management-automation/package.json +++ b/packages/project-management-automation/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/project-management-automation", - "version": "1.10.0", + "version": "1.11.0", "description": "GitHub Action that implements various automation to assist with managing the Gutenberg GitHub repository.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,7 +24,7 @@ "dependencies": { "@actions/core": "^1.0.0", "@actions/github": "^1.0.0", - "@babel/runtime": "^7.11.2" + "@babel/runtime": "^7.12.5" }, "publishConfig": { "access": "public" diff --git a/packages/react-native-aztec/README.md b/packages/react-native-aztec/README.md index 737b207e8d92d9..2df9c3d276065d 100644 --- a/packages/react-native-aztec/README.md +++ b/packages/react-native-aztec/README.md @@ -236,7 +236,7 @@ RCTAztecView adds the following custom behaviours to the TextView class: Android uses a native [`ReactAztecText`](https://github.com/WordPress/gutenberg/blob/7532a485b400f86638145b71f94f6f717e5add25/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecText.java#L50) view, which extends [`AztecText`](https://github.com/wordpress-mobile/AztecEditor-Android/blob/437ecec9034003c32b9b8b0b00ec76cb5b248679/aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt#L130) from the [Aztec Library for Android](https://github.com/wordpress-mobile/AztecEditor-Android). All interactions between -the native `ReactAztecText` view and the Javascript code are handled by the [`ReactAztecManager`](https://github.com/WordPress/gutenberg/blob/7532a485b400f86638145b71f94f6f717e5add25/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java#L62) +the native `ReactAztecText` view and the JavaScript code are handled by the [`ReactAztecManager`](https://github.com/WordPress/gutenberg/blob/7532a485b400f86638145b71f94f6f717e5add25/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java#L62) view manager. # License diff --git a/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index 7d39098ae8c809..edf30a01154e86 100644 --- a/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -41,11 +41,11 @@ import com.facebook.react.views.text.ReactFontManager; import com.facebook.react.views.text.ReactTextUpdate; import com.facebook.react.views.textinput.ReactContentSizeChangedEvent; -import com.facebook.react.views.textinput.ReactTextChangedEvent; import com.facebook.react.views.textinput.ReactTextInputEvent; import com.facebook.react.views.textinput.ReactTextInputManager; import com.facebook.react.views.textinput.ScrollWatcher; +import org.wordpress.aztec.Constants; import org.wordpress.aztec.formatting.LinkFormatter; import org.wordpress.aztec.glideloader.GlideImageLoader; import org.wordpress.aztec.glideloader.GlideVideoThumbnailLoader; @@ -677,7 +677,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { if (mPreviousText.length() == 0 - && !TextUtils.isEmpty(newText) + && !isTextEmpty(newText) && !TextUtils.isEmpty(mEditText.getTagName()) && mEditText.getSelectedStyles().isEmpty()) { @@ -691,6 +691,12 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { } } + // This accounts for the END_OF_BUFFER_MARKER that is added to blocks to maintain the styling, if the only char + // is the zero width marker then it is considered "empty" + private boolean isTextEmpty(String text) { + return text.length() == 0 || (text.length() == 1 && text.charAt(0) == Constants.INSTANCE.getEND_OF_BUFFER_MARKER()); + } + @Override public void afterTextChanged(Editable s) {} } diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index acebacb8bf509c..e93e21885f98b5 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.41.1", + "version": "1.43.0", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergBridgeJS2Parent.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergBridgeJS2Parent.java index cd9dcc79ed2b9b..6c94893d143df9 100644 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergBridgeJS2Parent.java +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/GutenbergBridgeJS2Parent.java @@ -83,6 +83,7 @@ enum MediaType { VIDEO("video"), MEDIA("media"), AUDIO("audio"), + ANY("any"), OTHER("other"); String name; @@ -165,7 +166,9 @@ void gutenbergDidRequestUnsupportedBlockFallback(ReplaceUnsupportedBlockCallback void gutenbergDidSendButtonPressedAction(String buttonType); - void onAddMention(Consumer<String> onSuccess); + void onShowUserSuggestions(Consumer<String> onResult); + + void onShowXpostSuggestions(Consumer<String> onResult); void setStarterPageTemplatesTooltipShown(boolean tooltipShown); diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java index 4a6b844b2f5c10..d00dc5a566a74a 100644 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/ReactNativeGutenbergBridge/RNReactNativeGutenbergBridgeModule.java @@ -323,8 +323,13 @@ private OtherMediaOptionsReceivedCallback getNewOtherMediaReceivedCallback(final } @ReactMethod - public void addMention(Promise promise) { - mGutenbergBridgeJS2Parent.onAddMention(promise::resolve); + public void showUserSuggestions(Promise promise) { + mGutenbergBridgeJS2Parent.onShowUserSuggestions(promise::resolve); + } + + @ReactMethod + public void showXpostSuggestions(Promise promise) { + mGutenbergBridgeJS2Parent.onShowXpostSuggestions(promise::resolve); } @ReactMethod diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/AddMentionUtil.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/AddMentionUtil.java deleted file mode 100644 index 837f10e1562835..00000000000000 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/AddMentionUtil.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.wordpress.mobile.WPAndroidGlue; - -import androidx.core.util.Consumer; - -public interface AddMentionUtil { - void getMention(Consumer<String> onResult); -} diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt index f9f65dd9c20838..7049bc099c8221 100644 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/GutenbergProps.kt @@ -5,6 +5,7 @@ import android.os.Bundle data class GutenbergProps @JvmOverloads constructor( val enableMediaFilesCollectionBlocks: Boolean, val enableMentions: Boolean, + val enableXPosts: Boolean, val enableUnsupportedBlockEditor: Boolean, val canEnableUnsupportedBlockEditor: Boolean, val localeSlug: String, @@ -38,6 +39,7 @@ data class GutenbergProps @JvmOverloads constructor( fun getUpdatedCapabilitiesProps() = Bundle().apply { putBoolean(PROP_CAPABILITIES_MENTIONS, enableMentions) + putBoolean(PROP_CAPABILITIES_XPOSTS, enableXPosts) putBoolean(PROP_CAPABILITIES_MEDIAFILES_COLLECTION_BLOCK, enableMediaFilesCollectionBlocks) putBoolean(PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR, enableUnsupportedBlockEditor) putBoolean(PROP_CAPABILITIES_CAN_ENABLE_UNSUPPORTED_BLOCK_EDITOR, canEnableUnsupportedBlockEditor) @@ -68,6 +70,7 @@ data class GutenbergProps @JvmOverloads constructor( const val PROP_CAPABILITIES = "capabilities" const val PROP_CAPABILITIES_MEDIAFILES_COLLECTION_BLOCK = "mediaFilesCollectionBlock" const val PROP_CAPABILITIES_MENTIONS = "mentions" + const val PROP_CAPABILITIES_XPOSTS = "xposts" const val PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR = "unsupportedBlockEditor" const val PROP_CAPABILITIES_CAN_ENABLE_UNSUPPORTED_BLOCK_EDITOR = "canEnableUnsupportedBlockEditor" const val PROP_CAPABILITIES_MODAL_LAYOUT_PICKER = "modalLayoutPicker" diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/ShowSuggestionsUtil.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/ShowSuggestionsUtil.java new file mode 100644 index 00000000000000..cd78ded5b6b242 --- /dev/null +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/ShowSuggestionsUtil.java @@ -0,0 +1,8 @@ +package org.wordpress.mobile.WPAndroidGlue; + +import androidx.core.util.Consumer; + +public interface ShowSuggestionsUtil { + void showUserSuggestions(Consumer<String> onResult); + void showXpostSuggestions(Consumer<String> onResult); +} diff --git a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java index 637ce489ea6044..6feeb61581b287 100644 --- a/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java +++ b/packages/react-native-bridge/android/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java @@ -109,7 +109,7 @@ public class WPAndroidGlueCode { private CountDownLatch mGetContentCountDownLatch; private WeakReference<View> mLastFocusedView = null; private RequestExecutor mRequestExecutor; - private AddMentionUtil mAddMentionUtil; + private ShowSuggestionsUtil mShowSuggestionsUtil; private @Nullable Bundle mEditorTheme = null; private static OkHttpHeaderInterceptor sAddCookiesInterceptor = new OkHttpHeaderInterceptor(); @@ -138,6 +138,7 @@ public interface OnMediaLibraryButtonListener { void onMediaLibraryImageButtonClicked(boolean allowMultipleSelection); void onMediaLibraryVideoButtonClicked(boolean allowMultipleSelection); void onMediaLibraryMediaButtonClicked(boolean allowMultipleSelection); + void onMediaLibraryFileButtonClicked(boolean allowMultipleSelection); void onUploadPhotoButtonClicked(boolean allowMultipleSelection); void onCapturePhotoButtonClicked(); void onUploadVideoButtonClicked(boolean allowMultipleSelection); @@ -244,6 +245,8 @@ public void requestMediaPickFromMediaLibrary(MediaSelectedCallback mediaSelected mOnMediaLibraryButtonListener.onMediaLibraryVideoButtonClicked(allowMultipleSelection); } else if (mediaType == MediaType.MEDIA) { mOnMediaLibraryButtonListener.onMediaLibraryMediaButtonClicked(allowMultipleSelection); + } else if (mediaType == MediaType.ANY) { + mOnMediaLibraryButtonListener.onMediaLibraryFileButtonClicked(allowMultipleSelection); } } @@ -353,7 +356,7 @@ public void getOtherMediaPickerOptions(OtherMediaOptionsReceivedCallback otherMe if (mediaType == MediaType.IMAGE || mediaType == MediaType.MEDIA) { ArrayList<MediaOption> otherMediaImageOptions = mOnMediaLibraryButtonListener.onGetOtherMediaImageOptions(); otherMediaOptionsReceivedCallback.onOtherMediaOptionsReceived(otherMediaImageOptions); - } else { + } else if (mediaType == MediaType.ANY) { ArrayList<MediaOption> otherMediaFileOptions = mOnMediaLibraryButtonListener.onGetOtherMediaFileOptions(); otherMediaOptionsReceivedCallback.onOtherMediaOptionsReceived(otherMediaFileOptions); } @@ -408,8 +411,12 @@ public void gutenbergDidSendButtonPressedAction(String buttonType) { } @Override - public void onAddMention(Consumer<String> onSuccess) { - mAddMentionUtil.getMention(onSuccess); + public void onShowUserSuggestions(Consumer<String> onResult) { + mShowSuggestionsUtil.showUserSuggestions(onResult); + } + + @Override public void onShowXpostSuggestions(Consumer<String> onResult) { + mShowSuggestionsUtil.showXpostSuggestions(onResult); } @Override @@ -528,7 +535,7 @@ public void attachToContainer(ViewGroup viewGroup, OnLogGutenbergUserEventListener onLogGutenbergUserEventListener, OnGutenbergDidRequestUnsupportedBlockFallbackListener onGutenbergDidRequestUnsupportedBlockFallbackListener, OnGutenbergDidSendButtonPressedActionListener onGutenbergDidSendButtonPressedActionListener, - AddMentionUtil addMentionUtil, + ShowSuggestionsUtil showSuggestionsUtil, OnStarterPageTemplatesTooltipShownEventListener onStarterPageTemplatesTooltipListener, OnMediaFilesCollectionBasedBlockEditorListener onMediaFilesCollectionBasedBlockEditorListener, boolean isDarkMode) { @@ -546,7 +553,7 @@ public void attachToContainer(ViewGroup viewGroup, mOnLogGutenbergUserEventListener = onLogGutenbergUserEventListener; mOnGutenbergDidRequestUnsupportedBlockFallbackListener = onGutenbergDidRequestUnsupportedBlockFallbackListener; mOnGutenbergDidSendButtonPressedActionListener = onGutenbergDidSendButtonPressedActionListener; - mAddMentionUtil = addMentionUtil; + mShowSuggestionsUtil = showSuggestionsUtil; mOnStarterPageTemplatesTooltipShownListener = onStarterPageTemplatesTooltipListener; mOnMediaFilesCollectionBasedBlockEditorListener = onMediaFilesCollectionBasedBlockEditorListener; diff --git a/packages/react-native-bridge/index.js b/packages/react-native-bridge/index.js index f7d76c91d53cd5..e5bc1db51a0ab2 100644 --- a/packages/react-native-bridge/index.js +++ b/packages/react-native-bridge/index.js @@ -279,8 +279,12 @@ export function logUserEvent( event, properties ) { return RNReactNativeGutenbergBridge.logUserEvent( event, properties ); } -export function addMention() { - return RNReactNativeGutenbergBridge.addMention(); +export function showUserSuggestions() { + return RNReactNativeGutenbergBridge.showUserSuggestions(); +} + +export function showXpostSuggestions() { + return RNReactNativeGutenbergBridge.showXpostSuggestions(); } export function requestStarterPageTemplatesTooltipShown( callback ) { diff --git a/packages/react-native-bridge/ios/Gutenberg.swift b/packages/react-native-bridge/ios/Gutenberg.swift index 7a194e53e31a54..b52699d7ce921c 100644 --- a/packages/react-native-bridge/ios/Gutenberg.swift +++ b/packages/react-native-bridge/ios/Gutenberg.swift @@ -258,6 +258,7 @@ extension Gutenberg { case video case audio case other + case any } } diff --git a/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift b/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift index 2e624791346908..753515f600a7e3 100644 --- a/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift +++ b/packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift @@ -18,6 +18,7 @@ public struct MediaInfo: Encodable { public enum Capabilities: String { case mediaFilesCollectionBlock case mentions + case xposts case unsupportedBlockEditor case canEnableUnsupportedBlockEditor case modalLayoutPicker @@ -225,6 +226,10 @@ public protocol GutenbergBridgeDelegate: class { /// - Parameter callback: Completion handler to be called with an user mention or an error func gutenbergDidRequestMention(callback: @escaping (Swift.Result<String, NSError>) -> Void) + /// Tells the delegate that the editor requested a mention + /// - Parameter callback: Completion handler to be called with an xpost or an error + func gutenbergDidRequestXpost(callback: @escaping (Swift.Result<String, NSError>) -> Void) + /// Tells the delegate that the editor requested to show the tooltip func gutenbergDidRequestStarterPageTemplatesTooltipShown() -> Bool diff --git a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.m b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.m index aafdce5c869aa0..9443cae473c1f3 100644 --- a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.m +++ b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.m @@ -20,7 +20,8 @@ @interface RCT_EXTERN_MODULE(RNReactNativeGutenbergBridge, NSObject) RCT_EXTERN_METHOD(requestMediaEditor:(NSString *)mediaUrl callback:(RCTResponseSenderBlock)callback) RCT_EXTERN_METHOD(logUserEvent:(NSString *)event properties:(NSDictionary *)properties) RCT_EXTERN_METHOD(requestUnsupportedBlockFallback:(NSString *)content blockId:(NSString *)blockId blockName:(NSString *)blockName blockTitle:(NSString *)blockTitle) -RCT_EXTERN_METHOD(addMention:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)rejecter) +RCT_EXTERN_METHOD(showUserSuggestions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)rejecter) +RCT_EXTERN_METHOD(showXpostSuggestions:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)rejecter) RCT_EXTERN_METHOD(requestStarterPageTemplatesTooltipShown:(RCTResponseSenderBlock)callback) RCT_EXTERN_METHOD(setStarterPageTemplatesTooltipShown:(BOOL)tooltipShown) RCT_EXTERN_METHOD(requestMediaFilesEditorLoad:(NSArray<NSString *> *)mediaFiles blockId:(NSString *)blockId) diff --git a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift index 3d8168ba17e8d4..927a59eb8e0fbe 100644 --- a/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift +++ b/packages/react-native-bridge/ios/RNReactNativeGutenbergBridge.swift @@ -267,7 +267,7 @@ public class RNReactNativeGutenbergBridge: RCTEventEmitter { } @objc - func addMention(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) { + func showUserSuggestions(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) { self.delegate?.gutenbergDidRequestMention(callback: { (result) in switch result { case .success(let mention): @@ -278,6 +278,18 @@ public class RNReactNativeGutenbergBridge: RCTEventEmitter { }) } + @objc + func showXpostSuggestions(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) { + self.delegate?.gutenbergDidRequestXpost(callback: { (result) in + switch result { + case .success(let mention): + resolver([mention]) + case .failure(let error): + rejecter(error.domain, "\(error.code)", error) + } + }) + } + @objc func requestStarterPageTemplatesTooltipShown(_ callback: @escaping RCTResponseSenderBlock) { callback([self.delegate?.gutenbergDidRequestStarterPageTemplatesTooltipShown() ?? false]) diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index d1faedb24b5c9c..26fefff3a9e0b5 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.41.1", + "version": "1.43.0", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 24caa6a16beb7c..095ed5bbc8fd57 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,10 +11,22 @@ For each user feature we should also add a importance categorization label to i ## Unreleased +* [***] Full-width and wide alignment support for Columns + +## 1.43.0 +* [***] New Block: File [#27228] +* [**] Fix issue where a blocks would disappear when deleting all of the text inside without requiring the extra backspace to remove the block. [#27583] + +## 1.42.0 * [***] Adding support for selecting different unit of value in Cover and Columns blocks [#26161] * [**] Button block - Add link picker to the block settings [#26206] * [**] Support to render background/text colors in Group, Paragraph and Quote blocks [#25994] * [*] Fix theme colors syncing with the editor [#26821] +* [**] Fix issue where a blocks would disappear when deleting all of the text inside without requiring the extra backspace to remove the block. [#27583] + +## 1.44.0 + +* [***] Add support for cross-posting between sites ## 1.41.0 diff --git a/packages/react-native-editor/__device-tests__/.eslintrc.js b/packages/react-native-editor/__device-tests__/.eslintrc.js new file mode 100644 index 00000000000000..8be64e4c826a0b --- /dev/null +++ b/packages/react-native-editor/__device-tests__/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + extends: '../.eslintrc.js', + globals: { + editorPage: true, // Defined in 'jest_ui_test_environment.js' + }, +}; diff --git a/packages/react-native-editor/__device-tests__/README.md b/packages/react-native-editor/__device-tests__/README.md index 0d7a11cbd38be1..030f2947dc744a 100644 --- a/packages/react-native-editor/__device-tests__/README.md +++ b/packages/react-native-editor/__device-tests__/README.md @@ -67,4 +67,4 @@ After the build is complete, an appium server is fired up on port 4723 and the d ----- -To read more about writing your own tests please read the [contributing guide](https://github.com/wordpress-mobile/gutenberg-mobile/blob/develop/__device-tests__/CONTRIBUTING.md) +To read more about writing your own tests please read the [contributing guide](https://github.com/WordPress/gutenberg/blob/master/packages/react-native-editor/__device-tests__/CONTRIBUTING.md) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js index 2d8ce89ffda3ef..8721f11c793554 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js @@ -1,50 +1,15 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { - setupDriver, - isLocalEnvironment, - stopDriver, - isAndroid, - swipeDown, - clickMiddleOfElement, -} from './helpers/utils'; +import { blockNames } from './pages/editor-page'; +import { isAndroid, swipeDown, clickMiddleOfElement } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor tests for Block insertion', () => { - let driver; - let editorPage; - let allPassed = true; - const paragraphBlockName = 'Paragraph'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - // wait for the block editor to load - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to insert block into post', async () => { - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); let paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -54,29 +19,33 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { // Should have 3 paragraph blocks at this point paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); await paragraphBlockElement.click(); - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 3 ); await paragraphBlockElement.click(); await editorPage.sendTextToParagraphBlock( 3, testData.mediumText ); - await editorPage.verifyHtmlContent( testData.blockInsertionHtml ); + const html = await editorPage.getHtmlContent(); + + expect( testData.blockInsertionHtml.toLowerCase() ).toBe( + html.toLowerCase() + ); // wait for the block editor to load and for accessibility ids to update - await driver.sleep( 3000 ); + await editorPage.driver.sleep( 3000 ); // Workaround for now since deleting the first element causes a crash on CI for Android if ( isAndroid() ) { paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 3, { autoscroll: true, @@ -84,37 +53,43 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { ); await paragraphBlockElement.click(); - await editorPage.removeBlockAtPosition( paragraphBlockName, 3 ); + await editorPage.removeBlockAtPosition( blockNames.paragraph, 3 ); for ( let i = 3; i > 0; i-- ) { // wait for accessibility ids to update - await driver.sleep( 1000 ); + await editorPage.driver.sleep( 1000 ); paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, i, { autoscroll: true, } ); await paragraphBlockElement.click(); - await editorPage.removeBlockAtPosition( paragraphBlockName, i ); + await editorPage.removeBlockAtPosition( + blockNames.paragraph, + i + ); } } else { for ( let i = 4; i > 0; i-- ) { // wait for accessibility ids to update - await driver.sleep( 1000 ); + await editorPage.driver.sleep( 1000 ); paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph + ); + await clickMiddleOfElement( + editorPage.driver, + paragraphBlockElement ); - await clickMiddleOfElement( driver, paragraphBlockElement ); - await editorPage.removeBlockAtPosition( paragraphBlockName ); + await editorPage.removeBlockAtPosition( blockNames.paragraph ); } } } ); it( 'should be able to insert block at the beginning of post from the title', async () => { - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); let paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -127,29 +102,24 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { await editorPage.dismissKeyboard(); } - await swipeDown( driver ); + await swipeDown( editorPage.driver ); const titleElement = await editorPage.getTitleElement( { autoscroll: true, } ); await titleElement.click(); await titleElement.click(); - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); - await clickMiddleOfElement( driver, paragraphBlockElement ); + await clickMiddleOfElement( editorPage.driver, paragraphBlockElement ); await editorPage.sendTextToParagraphBlock( 1, testData.mediumText ); await paragraphBlockElement.click(); - await editorPage.verifyHtmlContent( - testData.blockInsertionHtmlFromTitle - ); - } ); + const html = await editorPage.getHtmlContent(); - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + expect( testData.blockInsertionHtmlFromTitle.toLowerCase() ).toBe( + html.toLowerCase() + ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js index e7b0b25688a272..b57ea2588d7ee6 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-columns.test.js @@ -1,38 +1,10 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; +import { blockNames } from './pages/editor-page'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor Columns Block test', () => { - let driver; - let editorPage; - let allPassed = true; - const columnsBlockName = 'Columns'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to handle a columns width unit from web', async () => { await editorPage.setHtmlContent( testData.columnsWithDifferentUnitsHtml @@ -42,13 +14,6 @@ describe( 'Gutenberg Editor Columns Block test', () => { await columnsBlock.click(); expect( columnsBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( columnsBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.columns ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js index 5d91c634fd74e1..9ae321e3b39820 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js @@ -1,59 +1,30 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; +import { blockNames } from './pages/editor-page'; +import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor Cover Block test', () => { - let driver; - let editorPage; - let allPassed = true; - const coverBlockName = 'Cover'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - - it( 'should convert height properly', async () => { + it( 'should displayed properly and have properly converted height (ios only)', async () => { await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); const coverBlock = await editorPage.getBlockAtPosition( - coverBlockName + blockNames.cover ); - const { height } = await coverBlock.getSize(); - // Height is set to 20rem, where 1rem is 16. - // There is also block's vertical padding equal 32. - // Finally, the total height should be 20 * 16 + 32 = 352 - expect( height ).toBe( 352 ); + + // Temporarily this test is skipped on Android,due to the inconsistency of the results, + // which are related to getting values in raw pixels instead of density pixels on Android. + if ( ! isAndroid() ) { + const { height } = await coverBlock.getSize(); + // Height is set to 20rem, where 1rem is 16. + // There is also block's vertical padding equal 32. + // Finally, the total height should be 20 * 16 + 32 = 352 + expect( height ).toBe( 352 ); + } await coverBlock.click(); expect( coverBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( coverBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.cover ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-file.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-file.test.js new file mode 100644 index 00000000000000..040e93d8ac2587 --- /dev/null +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-file.test.js @@ -0,0 +1,26 @@ +/** + * Internal dependencies + */ +import { blockNames } from './pages/editor-page'; +import testData from './helpers/test-data'; + +describe( 'Gutenberg Editor File Block tests @canary', () => { + it( 'should be able to add a file block', async () => { + await editorPage.addNewBlock( blockNames.file ); + const block = await editorPage.getFirstBlockVisible(); + await expect( block ).toBeTruthy(); + } ); + + it( 'should add a file to the block ', async () => { + const block = await editorPage.getFirstBlockVisible(); + + block.click(); + await editorPage.driver.sleep( 1000 ); + await editorPage.chooseMediaLibrary(); + + const html = await editorPage.getHtmlContent(); + expect( testData.fileBlockPlaceholder.toLowerCase() ).toBe( + html.toLowerCase() + ); + } ); +} ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-gallery.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-gallery.test.js index 5872ad69803d0f..37211a5db2e0bb 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-gallery.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-gallery.test.js @@ -1,51 +1,16 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; - -jest.setTimeout( 1000000 ); +import { blockNames } from './pages/editor-page'; describe( 'Gutenberg Editor Gallery Block tests', () => { - let driver; - let editorPage; - let allPassed = true; - const galleryBlockName = 'Gallery'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add a gallery block', async () => { - await editorPage.addNewBlock( galleryBlockName ); + await editorPage.addNewBlock( blockNames.gallery ); const galleryBlock = await editorPage.getBlockAtPosition( - galleryBlockName + blockNames.gallery ); expect( galleryBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( galleryBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.gallery ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-heading.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-heading.test.js index 3a40a08df918fa..507ec388652945 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-heading.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-heading.test.js @@ -1,48 +1,15 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { - setupDriver, - isLocalEnvironment, - stopDriver, - isAndroid, -} from './helpers/utils'; +import { blockNames } from './pages/editor-page'; +import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor tests @canary', () => { - let driver; - let editorPage; - let allPassed = true; - const paragraphBlockName = 'Paragraph'; - const headingBlockName = 'Heading'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to create a post with heading and paragraph blocks', async () => { - await editorPage.addNewBlock( headingBlockName ); + await editorPage.addNewBlock( blockNames.heading ); let headingBlockElement = await editorPage.getBlockAtPosition( - headingBlockName + blockNames.heading ); if ( isAndroid() ) { await headingBlockElement.click(); @@ -53,9 +20,9 @@ describe( 'Gutenberg Editor tests @canary', () => { false ); - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); let paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); await editorPage.typeTextToParagraphBlock( @@ -63,9 +30,9 @@ describe( 'Gutenberg Editor tests @canary', () => { testData.mediumText ); - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 3 ); await editorPage.typeTextToParagraphBlock( @@ -73,9 +40,9 @@ describe( 'Gutenberg Editor tests @canary', () => { testData.mediumText ); - await editorPage.addNewBlock( headingBlockName ); + await editorPage.addNewBlock( blockNames.heading ); headingBlockElement = await editorPage.getBlockAtPosition( - headingBlockName, + blockNames.heading, 4 ); await editorPage.typeTextToParagraphBlock( @@ -83,9 +50,9 @@ describe( 'Gutenberg Editor tests @canary', () => { testData.heading ); - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 5 ); await editorPage.typeTextToParagraphBlock( @@ -93,11 +60,4 @@ describe( 'Gutenberg Editor tests @canary', () => { testData.mediumText ); } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); - } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-image.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-image.test.js index a96e61a1c133e4..d0f28f6c68249f 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-image.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-image.test.js @@ -1,49 +1,16 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { - setupDriver, - isLocalEnvironment, - stopDriver, - isAndroid, - clickMiddleOfElement, - swipeUp, -} from './helpers/utils'; +import { blockNames } from './pages/editor-page'; +import { isAndroid, clickMiddleOfElement, swipeUp } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor Image Block tests @canary', () => { - let driver; - let editorPage; - let allPassed = true; - const imageBlockName = 'Image'; - const paragraphBlockName = 'Paragraph'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add an image block', async () => { - await editorPage.addNewBlock( imageBlockName ); - let imageBlock = await editorPage.getBlockAtPosition( imageBlockName ); + await editorPage.addNewBlock( blockNames.image ); + let imageBlock = await editorPage.getBlockAtPosition( + blockNames.image + ); // Can only add image from media library on iOS if ( ! isAndroid() ) { @@ -52,21 +19,21 @@ describe( 'Gutenberg Editor Image Block tests @canary', () => { // Workaround because of #952 const titleElement = await editorPage.getTitleElement(); - await clickMiddleOfElement( driver, titleElement ); + await clickMiddleOfElement( editorPage.driver, titleElement ); await editorPage.dismissKeyboard(); // end workaround imageBlock = await editorPage.getBlockAtPosition( imageBlock ); - await swipeUp( driver, imageBlock ); + await swipeUp( editorPage.driver, imageBlock ); await editorPage.enterCaptionToSelectedImageBlock( testData.imageCaption, true ); await editorPage.dismissKeyboard(); } - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); const paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); if ( isAndroid() ) { @@ -77,14 +44,11 @@ describe( 'Gutenberg Editor Image Block tests @canary', () => { // skip HTML check for Android since we couldn't add image from media library if ( ! isAndroid() ) { - await editorPage.verifyHtmlContent( testData.imageShorteHtml ); - } - } ); + const html = await editorPage.getHtmlContent(); - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); + expect( testData.imageShorteHtml.toLowerCase() ).toBe( + html.toLowerCase() + ); } - await stopDriver( driver ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-latest-posts.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-latest-posts.test.js index 160965d1afe967..a5b8ec8d1a9eda 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-latest-posts.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-latest-posts.test.js @@ -1,51 +1,16 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; - -jest.setTimeout( 1000000 ); +import { blockNames } from './pages/editor-page'; describe( 'Gutenberg Editor Latest Post Block tests', () => { - let driver; - let editorPage; - let allPassed = true; - const lastPostBlockName = 'Latest Posts'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add a Latests-Posts block', async () => { - await editorPage.addNewBlock( lastPostBlockName ); + await editorPage.addNewBlock( blockNames.latestPosts ); const latestPostsBlock = await editorPage.getBlockAtPosition( - lastPostBlockName + blockNames.latestPosts ); expect( latestPostsBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( lastPostBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.latestPosts ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-canary.test.js index 09fc2ce7d1d5b3..89d04d0d2cb1cc 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-canary.test.js @@ -1,47 +1,15 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { - isAndroid, - isLocalEnvironment, - setupDriver, - stopDriver, -} from './helpers/utils'; +import { blockNames } from './pages/editor-page'; +import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor tests for List block @canary', () => { - let driver; - let editorPage; - let allPassed = true; - const listBlockName = 'List'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add a new List block', async () => { - await editorPage.addNewBlock( listBlockName ); + await editorPage.addNewBlock( blockNames.list ); const listBlockElement = await editorPage.getBlockAtPosition( - listBlockName + blockNames.list ); // Click List block on Android to force EditText focus if ( isAndroid() ) { @@ -64,13 +32,14 @@ describe( 'Gutenberg Editor tests for List block @canary', () => { ); // switch to html and verify html - await editorPage.verifyHtmlContent( testData.listHtml ); + const html = await editorPage.getHtmlContent(); + expect( testData.listHtml.toLowerCase() ).toBe( html.toLowerCase() ); } ); // This test depends on being run immediately after 'should be able to add a new List block' it( 'should update format to ordered list, using toolbar button', async () => { let listBlockElement = await editorPage.getBlockAtPosition( - listBlockName + blockNames.list ); // Click List block to force EditText focus @@ -80,18 +49,15 @@ describe( 'Gutenberg Editor tests for List block @canary', () => { await editorPage.clickOrderedListToolBarButton(); // switch to html and verify html - await editorPage.verifyHtmlContent( testData.listHtmlOrdered ); - + const html = await editorPage.getHtmlContent(); + expect( testData.listHtmlOrdered.toLowerCase() ).toBe( + html.toLowerCase() + ); // Remove list block to return editor to empty state - listBlockElement = await editorPage.getBlockAtPosition( listBlockName ); + listBlockElement = await editorPage.getBlockAtPosition( + blockNames.list + ); await listBlockElement.click(); - await editorPage.removeBlockAtPosition( listBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.list ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js index a39edd83c9fc29..a08fab00043b4c 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js @@ -1,47 +1,15 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { - setupDriver, - isLocalEnvironment, - stopDriver, - isAndroid, -} from './helpers/utils'; +import { blockNames } from './pages/editor-page'; +import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor tests for List block (end)', () => { - let driver; - let editorPage; - let allPassed = true; - const listBlockName = 'List'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to end a List block', async () => { - await editorPage.addNewBlock( listBlockName ); + await editorPage.addNewBlock( blockNames.list ); const listBlockElement = await editorPage.getBlockAtPosition( - listBlockName + blockNames.list ); // Click List block on Android to force EditText focus @@ -61,13 +29,9 @@ describe( 'Gutenberg Editor tests for List block (end)', () => { // send an Enter await editorPage.sendTextToListBlock( listBlockElement, '\n' ); - await editorPage.verifyHtmlContent( testData.listEndedHtml ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + const html = await editorPage.getHtmlContent(); + expect( testData.listEndedHtml.toLowerCase() ).toBe( + html.toLowerCase() + ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 3ce5a3b9fece8a..dbde8dbcfaf6f2 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -1,48 +1,15 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { - backspace, - isAndroid, - isLocalEnvironment, - setupDriver, - stopDriver, -} from './helpers/utils'; - -jest.setTimeout( 1000000 ); +import { blockNames } from './pages/editor-page'; +import { backspace, isAndroid } from './helpers/utils'; describe( 'Gutenberg Editor tests for List block', () => { - let driver; - let editorPage; - let allPassed = true; - const listBlockName = 'List'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 it( 'should handle spaces in a list', async () => { - await editorPage.addNewBlock( listBlockName ); + await editorPage.addNewBlock( blockNames.list ); let listBlockElement = await editorPage.getBlockAtPosition( - listBlockName + blockNames.list ); // Click List block on Android to force EditText focus if ( isAndroid() ) { @@ -59,20 +26,18 @@ describe( 'Gutenberg Editor tests for List block', () => { await editorPage.sendTextToListBlock( listBlockElement, backspace ); // switch to html and verify html - await editorPage.verifyHtmlContent( `<!-- wp:list --> + const html = await editorPage.getHtmlContent(); + expect( + `<!-- wp:list --> <ul><li> a</li></ul> -<!-- /wp:list -->` ); +<!-- /wp:list -->` + ).toBe( html.toLowerCase() ); // Remove list block to reset editor to clean state - listBlockElement = await editorPage.getBlockAtPosition( listBlockName ); + listBlockElement = await editorPage.getBlockAtPosition( + blockNames.list + ); await listBlockElement.click(); - await editorPage.removeBlockAtPosition( listBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.list ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-more.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-more.test.js index 7b6aed2b2266c3..115a849df3df61 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-more.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-more.test.js @@ -1,51 +1,16 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; - -jest.setTimeout( 1000000 ); +import { blockNames } from './pages/editor-page'; describe( 'Gutenberg Editor Spacer Block test', () => { - let driver; - let editorPage; - let allPassed = true; - const moreBlockName = 'More'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add an separator block', async () => { - await editorPage.addNewBlock( moreBlockName ); + await editorPage.addNewBlock( blockNames.more ); const separatorBlock = await editorPage.getBlockAtPosition( - moreBlockName + blockNames.more ); expect( separatorBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( moreBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.more ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js index d7ca8fa0e32b70..f6ab1e191fc541 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js @@ -1,50 +1,20 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; +import { blockNames } from './pages/editor-page'; import { backspace, - setupDriver, - isLocalEnvironment, clickMiddleOfElement, clickBeginningOfElement, - stopDriver, isAndroid, } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor tests for Paragraph Block', () => { - let driver; - let editorPage; - let allPassed = true; - const paragraphBlockName = 'Paragraph'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add a new Paragraph block', async () => { - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); const paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -54,13 +24,13 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { paragraphBlockElement, testData.shortText ); - await editorPage.removeBlockAtPosition( paragraphBlockName ); + await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); it( 'should be able to split one paragraph block into two', async () => { - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); const paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -73,15 +43,21 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { const textViewElement = await editorPage.getTextViewForParagraphBlock( paragraphBlockElement ); - await clickMiddleOfElement( driver, textViewElement ); + await clickMiddleOfElement( editorPage.driver, textViewElement ); await editorPage.typeTextToParagraphBlock( paragraphBlockElement, '\n', false ); expect( - ( await editorPage.hasBlockAtPosition( 1, paragraphBlockName ) ) && - ( await editorPage.hasBlockAtPosition( 2, paragraphBlockName ) ) + ( await editorPage.hasBlockAtPosition( + 1, + blockNames.paragraph + ) ) && + ( await editorPage.hasBlockAtPosition( + 2, + blockNames.paragraph + ) ) ).toBe( true ); const text0 = await editorPage.getTextForParagraphBlockAtPosition( 1 ); @@ -92,14 +68,14 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { new RegExp( `${ text0 + text1 }|${ text0 } ${ text1 }` ) ); - await editorPage.removeBlockAtPosition( paragraphBlockName, 2 ); - await editorPage.removeBlockAtPosition( paragraphBlockName ); + await editorPage.removeBlockAtPosition( blockNames.paragraph, 2 ); + await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); it( 'should be able to merge 2 paragraph blocks into 1', async () => { - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); let paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -112,20 +88,26 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { let textViewElement = await editorPage.getTextViewForParagraphBlock( paragraphBlockElement ); - await clickMiddleOfElement( driver, textViewElement ); + await clickMiddleOfElement( editorPage.driver, textViewElement ); await editorPage.typeTextToParagraphBlock( paragraphBlockElement, '\n' ); expect( - ( await editorPage.hasBlockAtPosition( 1, paragraphBlockName ) ) && - ( await editorPage.hasBlockAtPosition( 2, paragraphBlockName ) ) + ( await editorPage.hasBlockAtPosition( + 1, + blockNames.paragraph + ) ) && + ( await editorPage.hasBlockAtPosition( + 2, + blockNames.paragraph + ) ) ).toBe( true ); const text0 = await editorPage.getTextForParagraphBlockAtPosition( 1 ); const text1 = await editorPage.getTextForParagraphBlockAtPosition( 2 ); paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); if ( isAndroid() ) { @@ -135,7 +117,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { textViewElement = await editorPage.getTextViewForParagraphBlock( paragraphBlockElement ); - await clickBeginningOfElement( driver, textViewElement ); + await clickBeginningOfElement( editorPage.driver, textViewElement ); await editorPage.typeTextToParagraphBlock( paragraphBlockElement, backspace @@ -145,15 +127,15 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { expect( text0 + text1 ).toMatch( text ); expect( - await editorPage.hasBlockAtPosition( 2, paragraphBlockName ) + await editorPage.hasBlockAtPosition( 2, blockNames.paragraph ) ).toBe( false ); - await editorPage.removeBlockAtPosition( paragraphBlockName ); + await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); it( 'should be able to create a post with multiple paragraph blocks', async () => { - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); const paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -162,7 +144,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { await editorPage.sendTextToParagraphBlock( 1, testData.longText ); for ( let i = 3; i > 0; i-- ) { - await editorPage.removeBlockAtPosition( paragraphBlockName, i ); + await editorPage.removeBlockAtPosition( blockNames.paragraph, i ); } } ); @@ -180,11 +162,11 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { // // Merge paragraphs const secondParagraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); await clickBeginningOfElement( - driver, + editorPage.driver, secondParagraphBlockElement ); await editorPage.typeTextToParagraphBlock( @@ -198,7 +180,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { ); expect( text.length ).not.toEqual( 0 ); - await editorPage.removeBlockAtPosition( paragraphBlockName ); + await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); // Based on https://github.com/wordpress-mobile/gutenberg-mobile/pull/1507 @@ -214,11 +196,11 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { // // Merge paragraphs const secondParagraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); await clickBeginningOfElement( - driver, + editorPage.driver, secondParagraphBlockElement ); await editorPage.typeTextToParagraphBlock( @@ -232,14 +214,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { ); expect( text.length ).not.toEqual( 0 ); - await editorPage.removeBlockAtPosition( paragraphBlockName ); + await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); } - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); - } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js index e79dc67494e970..95c94efebaefcc 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js @@ -1,54 +1,33 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; +import { blockNames } from './pages/editor-page'; import { - setupDriver, - isLocalEnvironment, longPressMiddleOfElement, tapSelectAllAboveElement, tapCopyAboveElement, tapPasteAboveElement, - stopDriver, isAndroid, } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor paste tests', () => { // skip iOS for now if ( ! isAndroid() ) { - it( 'skips the tests on any platform other than Android', async () => {} ); + it( 'skips the tests on any platform other than Android', async () => { + expect( true ).toBe( true ); + } ); return; } - let driver; - let editorPage; - let allPassed = true; - const paragraphBlockName = 'Paragraph'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - beforeAll( async () => { - driver = await setupDriver(); - await driver.setClipboard( '', 'plaintext' ); - editorPage = new EditorPage( driver ); + await editorPage.driver.setClipboard( '', 'plaintext' ); } ); it( 'copies plain text from one paragraph block and pastes in another', async () => { - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); const paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -63,18 +42,18 @@ describe( 'Gutenberg Editor paste tests', () => { ); // copy content to clipboard - await longPressMiddleOfElement( driver, textViewElement ); - await tapSelectAllAboveElement( driver, textViewElement ); - await tapCopyAboveElement( driver, textViewElement ); + await longPressMiddleOfElement( editorPage.driver, textViewElement ); + await tapSelectAllAboveElement( editorPage.driver, textViewElement ); + await tapCopyAboveElement( editorPage.driver, textViewElement ); // create another paragraph block - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); if ( isAndroid() ) { // On Andrdoid 10 a new auto-suggestion popup is appearing to let the user paste text recently put in the clipboard. Let's dismiss it. await editorPage.dismissAndroidClipboardSmartSuggestion(); } const paragraphBlockElement2 = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); if ( isAndroid() ) { @@ -86,8 +65,8 @@ describe( 'Gutenberg Editor paste tests', () => { ); // paste into second paragraph block - await longPressMiddleOfElement( driver, textViewElement2 ); - await tapPasteAboveElement( driver, textViewElement2 ); + await longPressMiddleOfElement( editorPage.driver, textViewElement2 ); + await tapPasteAboveElement( editorPage.driver, textViewElement2 ); const text = await editorPage.getTextForParagraphBlockAtPosition( 2 ); expect( text ).toBe( testData.pastePlainText ); @@ -97,7 +76,7 @@ describe( 'Gutenberg Editor paste tests', () => { // create paragraph block with styled text by editing html await editorPage.setHtmlContent( testData.pasteHtmlText ); const paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -108,18 +87,18 @@ describe( 'Gutenberg Editor paste tests', () => { ); // copy content to clipboard - await longPressMiddleOfElement( driver, textViewElement ); - await tapSelectAllAboveElement( driver, textViewElement ); - await tapCopyAboveElement( driver, textViewElement ); + await longPressMiddleOfElement( editorPage.driver, textViewElement ); + await tapSelectAllAboveElement( editorPage.driver, textViewElement ); + await tapCopyAboveElement( editorPage.driver, textViewElement ); // create another paragraph block - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); if ( isAndroid() ) { // On Andrdoid 10 a new auto-suggestion popup is appearing to let the user paste text recently put in the clipboard. Let's dismiss it. await editorPage.dismissAndroidClipboardSmartSuggestion(); } const paragraphBlockElement2 = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); if ( isAndroid() ) { @@ -131,17 +110,13 @@ describe( 'Gutenberg Editor paste tests', () => { ); // paste into second paragraph block - await longPressMiddleOfElement( driver, textViewElement2 ); - await tapPasteAboveElement( driver, textViewElement2 ); + await longPressMiddleOfElement( editorPage.driver, textViewElement2 ); + await tapPasteAboveElement( editorPage.driver, textViewElement2 ); // check styled text by verifying html contents - await editorPage.verifyHtmlContent( testData.pasteHtmlTextResult ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + const html = await editorPage.getHtmlContent(); + expect( testData.pasteHtmlTextResult.toLowerCase() ).toBe( + html.toLowerCase() + ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js index 85e426aa5dff3f..8e6c960f299cfc 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js @@ -1,48 +1,15 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { - setupDriver, - isLocalEnvironment, - stopDriver, - isAndroid, - toggleOrientation, -} from './helpers/utils'; +import { blockNames } from './pages/editor-page'; +import { isAndroid, toggleOrientation } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor tests', () => { - let driver; - let editorPage; - let allPassed = true; - const paragraphBlockName = 'Paragraph'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add blocks , rotate device and continue adding blocks', async () => { - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); let paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName + blockNames.paragraph ); if ( isAndroid() ) { await paragraphBlockElement.click(); @@ -53,26 +20,26 @@ describe( 'Gutenberg Editor tests', () => { testData.mediumText ); - await toggleOrientation( driver ); + await toggleOrientation( editorPage.driver ); // On Android the keyboard hides the add block button, let's hide it after rotation if ( isAndroid() ) { - await driver.hideDeviceKeyboard(); + await editorPage.driver.hideDeviceKeyboard(); } - await editorPage.addNewBlock( paragraphBlockName ); + await editorPage.addNewBlock( blockNames.paragraph ); if ( isAndroid() ) { - await driver.hideDeviceKeyboard(); + await editorPage.driver.hideDeviceKeyboard(); } paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); while ( ! paragraphBlockElement ) { - await driver.hideDeviceKeyboard(); + await editorPage.driver.hideDeviceKeyboard(); paragraphBlockElement = await editorPage.getBlockAtPosition( - paragraphBlockName, + blockNames.paragraph, 2 ); } @@ -80,15 +47,11 @@ describe( 'Gutenberg Editor tests', () => { paragraphBlockElement, testData.mediumText ); - await toggleOrientation( driver ); - - await editorPage.verifyHtmlContent( testData.deviceRotationHtml ); - } ); + await toggleOrientation( editorPage.driver ); - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + const html = await editorPage.getHtmlContent(); + expect( testData.deviceRotationHtml.toLowerCase() ).toBe( + html.toLowerCase() + ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-separator.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-separator.test.js index 438e1a5c1c9c5b..34c0551bcbd464 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-separator.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-separator.test.js @@ -1,51 +1,16 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; - -jest.setTimeout( 1000000 ); +import { blockNames } from './pages/editor-page'; describe( 'Gutenberg Editor Separator Block test', () => { - let driver; - let editorPage; - let allPassed = true; - const separatorBlockName = 'Separator'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add an separator block', async () => { - await editorPage.addNewBlock( separatorBlockName ); + await editorPage.addNewBlock( blockNames.separator ); const separatorBlock = await editorPage.getBlockAtPosition( - separatorBlockName + blockNames.separator ); expect( separatorBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( separatorBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.separator ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js index 2aa09cc523bb15..febd3fb8f6d81e 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js @@ -1,51 +1,16 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; - -jest.setTimeout( 1000000 ); +import { blockNames } from './pages/editor-page'; describe( 'Gutenberg Editor Spacer Block test', () => { - let driver; - let editorPage; - let allPassed = true; - const spacerBlockName = 'Spacer'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add an separator block', async () => { - await editorPage.addNewBlock( spacerBlockName ); + await editorPage.addNewBlock( blockNames.spacer ); const separatorBlock = await editorPage.getBlockAtPosition( - spacerBlockName + blockNames.spacer ); expect( separatorBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( spacerBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.spacer ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js index 3a1dc715cf44a5..6e1be2a13dddb6 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js @@ -1,37 +1,9 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; import testData from './helpers/test-data'; -jest.setTimeout( 1000000 ); - describe( 'Gutenberg Editor Unsupported Block Editor Tests', () => { - let driver; - let editorPage; - let allPassed = true; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to open the unsupported block web view editor', async () => { await editorPage.setHtmlContent( testData.unsupportedBlockHtml ); @@ -48,11 +20,4 @@ describe( 'Gutenberg Editor Unsupported Block Editor Tests', () => { editorPage.getUnsupportedBlockWebView() ).resolves.toBeTruthy(); } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); - } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-verse.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-verse.test.js index bc8412a3ee832f..93068088f84896 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-verse.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-verse.test.js @@ -1,51 +1,16 @@ /** * Internal dependencies */ -import EditorPage from './pages/editor-page'; -import { setupDriver, isLocalEnvironment, stopDriver } from './helpers/utils'; - -jest.setTimeout( 1000000 ); +import { blockNames } from './pages/editor-page'; describe( 'Gutenberg Editor Verse Block Tests', () => { - let driver; - let editorPage; - let allPassed = true; - const verseBlockName = 'Verse'; - - // Use reporter for setting status for saucelabs Job - if ( ! isLocalEnvironment() ) { - const reporter = { - specDone: async ( result ) => { - allPassed = allPassed && result.status !== 'failed'; - }, - }; - - jasmine.getEnv().addReporter( reporter ); - } - - beforeAll( async () => { - driver = await setupDriver(); - editorPage = new EditorPage( driver ); - } ); - - it( 'should be able to see visual editor', async () => { - await expect( editorPage.getBlockList() ).resolves.toBe( true ); - } ); - it( 'should be able to add a verse block', async () => { - await editorPage.addNewBlock( verseBlockName ); + await editorPage.addNewBlock( blockNames.verse ); const verseBlock = await editorPage.getBlockAtPosition( - verseBlockName + blockNames.verse ); expect( verseBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( verseBlockName ); - } ); - - afterAll( async () => { - if ( ! isLocalEnvironment() ) { - driver.sauceJobStatus( allPassed ); - } - await stopDriver( driver ); + await editorPage.removeBlockAtPosition( blockNames.verse ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/helpers/appium-local.js b/packages/react-native-editor/__device-tests__/helpers/appium-local.js index 71a0cd44aea18b..be44a83f83f6ae 100644 --- a/packages/react-native-editor/__device-tests__/helpers/appium-local.js +++ b/packages/react-native-editor/__device-tests__/helpers/appium-local.js @@ -1,10 +1,10 @@ /** * External dependencies */ -import childProcess from 'child_process'; +const childProcess = require( 'child_process' ); // Spawns an appium process -export const start = ( localAppiumPort ) => +const start = ( localAppiumPort ) => new Promise( ( resolve, reject ) => { const appium = childProcess.spawn( 'appium', [ '--port', @@ -40,14 +40,14 @@ export const start = ( localAppiumPort ) => } ); } ); -export const stop = async ( appium ) => { +const stop = async ( appium ) => { if ( ! appium ) { return; } await appium.kill( 'SIGINT' ); }; -export default { +module.exports = { start, stop, }; diff --git a/packages/react-native-editor/__device-tests__/helpers/caps.js b/packages/react-native-editor/__device-tests__/helpers/caps.js index 7f93b87eb07cb3..804ef802fb5c48 100644 --- a/packages/react-native-editor/__device-tests__/helpers/caps.js +++ b/packages/react-native-editor/__device-tests__/helpers/caps.js @@ -13,7 +13,6 @@ const ios = { exports.iosLocal = { ...ios, - platformVersion: '14.0', deviceName: 'iPhone 11', }; diff --git a/packages/react-native-editor/__device-tests__/helpers/test-data.js b/packages/react-native-editor/__device-tests__/helpers/test-data.js index b7afde4caf025d..389928f570cb0d 100644 --- a/packages/react-native-editor/__device-tests__/helpers/test-data.js +++ b/packages/react-native-editor/__device-tests__/helpers/test-data.js @@ -132,3 +132,7 @@ exports.coverHeightWithRemUnit = `<!-- wp:cover {"customOverlayColor":"#ffffff", <p class="has-text-align-center"></p> <!-- /wp:paragraph --></div></div> <!-- /wp:cover -->`; + +exports.fileBlockPlaceholder = `<!-- wp:file {"id":3,"href":"https://wordpress.org/latest.zip"} --> +<div class="wp-block-file"><a href="https://wordpress.org/latest.zip">WordPress.zip</a><a href="https://wordpress.org/latest.zip" class="wp-block-file__button" download>Download</a></div> +<!-- /wp:file -->`; diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 7f45e747a22ada..b298c390eb0f0d 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -1,21 +1,19 @@ /** * External dependencies */ -import childProcess from 'child_process'; +const childProcess = require( 'child_process' ); // eslint-disable-next-line import/no-extraneous-dependencies -import wd from 'wd'; -import crypto from 'crypto'; -import path from 'path'; -import fs from 'fs'; - +const wd = require( 'wd' ); +const crypto = require( 'crypto' ); +const path = require( 'path' ); /** * Internal dependencies */ -import serverConfigs from './serverConfigs'; -import { iosServer, iosLocal, android } from './caps'; -import AppiumLocal from './appium-local'; +const serverConfigs = require( './serverConfigs' ); +const { iosServer, iosLocal, android } = require( './caps' ); +const AppiumLocal = require( './appium-local' ); // eslint-disable-next-line import/no-extraneous-dependencies -import _ from 'underscore'; +const _ = require( 'underscore' ); // Platform setup const defaultPlatform = 'android'; @@ -37,8 +35,6 @@ const localIOSAppPath = process.env.IOS_APP_PATH || defaultIOSAppPath; const localAppiumPort = serverConfigs.local.port; // Port to spawn appium process for local runs let appiumProcess; -let iOSScreenRecordingProcess; -let androidScreenRecordingProcess; const backspace = '\u0008'; @@ -59,119 +55,6 @@ const isLocalEnvironment = () => { return testEnvironment.toLowerCase() === 'local'; }; -const isMacOSEnvironment = () => { - return process.platform === 'darwin'; -}; - -const IOS_RECORDINGS_DIR = './ios-screen-recordings'; -const ANDROID_RECORDINGS_DIR = './android-screen-recordings'; - -const getScreenRecordingFileNameBase = ( testPath, id ) => { - const suiteName = path.basename( testPath, '.test.js' ); - return `${ suiteName }.${ id }`; -}; - -jasmine.getEnv().addReporter( { - specStarted: ( { testPath, id } ) => { - if ( ! isMacOSEnvironment() ) { - return; - } - - const fileName = - getScreenRecordingFileNameBase( testPath, id ) + '.mp4'; - - if ( isAndroid() ) { - if ( ! fs.existsSync( ANDROID_RECORDINGS_DIR ) ) { - fs.mkdirSync( ANDROID_RECORDINGS_DIR ); - } - - androidScreenRecordingProcess = childProcess.spawn( 'adb', [ - 'shell', - 'screenrecord', - '--verbose', - '--bit-rate', - '1M', - '--size', - '720x1280', - `/sdcard/${ fileName }`, - ] ); - - androidScreenRecordingProcess.stderr.on( 'data', ( data ) => { - // eslint-disable-next-line no-console - console.log( `Android screen recording error => ${ data }` ); - } ); - - return; - } - - if ( ! fs.existsSync( IOS_RECORDINGS_DIR ) ) { - fs.mkdirSync( IOS_RECORDINGS_DIR ); - } - - iOSScreenRecordingProcess = childProcess.spawn( - 'xcrun', - [ - 'simctl', - 'io', - 'booted', - 'recordVideo', - '--mask=black', - '--force', - fileName, - ], - { - cwd: IOS_RECORDINGS_DIR, - } - ); - }, - specDone: ( { testPath, id, status } ) => { - if ( ! isMacOSEnvironment() ) { - return; - } - - const fileNameBase = getScreenRecordingFileNameBase( testPath, id ); - - if ( isAndroid() ) { - androidScreenRecordingProcess.kill( 'SIGINT' ); - // wait for kill - childProcess.execSync( 'sleep 1' ); - - try { - childProcess.execSync( - `adb pull /sdcard/${ fileNameBase }.mp4 ${ ANDROID_RECORDINGS_DIR }` - ); - } catch ( error ) { - // Some (old) Android devices don't support screen recording or - // sometimes the initial `should be able to see visual editor` - // tests are too fast and a recording is not generated. This is - // when `adb pull` can't find the recording file. In these cases - // we ignore the errors and keep running the tests. - // eslint-disable-next-line no-console - console.log( - `Android screen recording error => ${ error.stdout }` - ); - } - - const oldPath = `${ ANDROID_RECORDINGS_DIR }/${ fileNameBase }.mp4`; - const newPath = `${ ANDROID_RECORDINGS_DIR }/${ fileNameBase }.${ status }.mp4`; - - if ( fs.existsSync( oldPath ) ) { - fs.renameSync( oldPath, newPath ); - } - return; - } - - iOSScreenRecordingProcess.kill( 'SIGINT' ); - - const oldPath = `${ IOS_RECORDINGS_DIR }/${ fileNameBase }.mp4`; - const newPath = `${ IOS_RECORDINGS_DIR }/${ fileNameBase }.${ status }.mp4`; - - if ( fs.existsSync( oldPath ) ) { - fs.renameSync( oldPath, newPath ); - } - }, -} ); - // Initialises the driver and desired capabilities for appium const setupDriver = async () => { const branch = process.env.CIRCLE_BRANCH || ''; diff --git a/packages/react-native-editor/__device-tests__/pages/editor-page.js b/packages/react-native-editor/__device-tests__/pages/editor-page.js index 91c54fc9e190d0..f3cdbac9565cf4 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -1,16 +1,23 @@ /** * Internal dependencies */ -import { +const { + setupDriver, + stopDriver, isAndroid, swipeUp, swipeDown, typeString, toggleHtmlMode, swipeFromTo, -} from '../helpers/utils'; +} = require( '../helpers/utils' ); -export default class EditorPage { +const initializeEditorPage = async () => { + const driver = await setupDriver(); + return new EditorPage( driver ); +}; + +class EditorPage { driver; accessibilityIdKey; accessibilityIdXPathAttrib; @@ -130,16 +137,16 @@ export default class EditorPage { return await this.driver.elementByXPath( blockLocator ); } - // Converts to lower case and checks for a match to lowercased html content + // Returns html content // Ensure to take additional steps to handle text being changed by auto correct - async verifyHtmlContent( html ) { + async getHtmlContent() { await toggleHtmlMode( this.driver, true ); const htmlContentView = await this.getTextViewForHtmlViewContent(); const text = await htmlContentView.text(); - expect( text.toLowerCase() ).toBe( html.toLowerCase() ); await toggleHtmlMode( this.driver, false ); + return text; } // set html editor content explicitly @@ -238,7 +245,9 @@ export default class EditorPage { blockAccessibilityLabel ); const size = await this.driver.getWindowSize(); - const height = size.height - 5; + // The virtual home button covers the bottom 34 in portrait and 21 on landscape on iOS. + // We start dragging a bit above it to not trigger home button. + const height = size.height - 50; while ( ! ( await blockButton.isDisplayed() ) ) { await this.driver.execute( 'mobile: dragFromToForDuration', { @@ -536,4 +545,30 @@ export default class EditorPage { this.driver.setImplicitWaitTimeout( 5000 ); return element; } + + async stopDriver() { + await stopDriver( this.driver ); + } + + async sauceJobStatus( allPassed ) { + await this.driver.sauceJobStatus( allPassed ); + } } + +const blockNames = { + paragraph: 'Paragraph', + gallery: 'Gallery', + columns: 'Columns', + cover: 'Cover', + heading: 'Heading', + image: 'Image', + latestPosts: 'Latest Posts', + list: 'List', + more: 'More', + separator: 'Separator', + spacer: 'Spacer', + verse: 'Verse', + file: 'File', +}; + +module.exports = { initializeEditorPage, blockNames }; diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java index 83bbea9e5d5a15..ed3fdb7616676a 100644 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java @@ -29,6 +29,7 @@ protected Bundle getLaunchOptions() { Bundle bundle = new Bundle(); Bundle capabilities = new Bundle(); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_MENTIONS, true); + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_XPOSTS, true); capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR, true); bundle.putBundle(GutenbergProps.PROP_CAPABILITIES, capabilities); return bundle; diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java index 3c5fa9517cb14d..afa269ee7ce1c6 100644 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java @@ -72,10 +72,18 @@ public void requestMediaPickFromDeviceLibrary(MediaSelectedCallback mediaSelecte @Override public void requestMediaPickFromMediaLibrary(MediaSelectedCallback mediaSelectedCallback, Boolean allowMultipleSelection, MediaType mediaType) { List<RNMedia> rnMediaList = new ArrayList<>(); - if (mediaType == MediaType.IMAGE) { - rnMediaList.add(new Media(1, "https://cldup.com/cXyG__fTLN.jpg", "image", "Mountain","")); - } else if (mediaType == MediaType.VIDEO) { - rnMediaList.add(new Media(2, "https://i.cloudup.com/YtZFJbuQCE.mov", "video", "Cloudup","" )); + + switch (mediaType) { + case IMAGE: + rnMediaList.add(new Media(1, "https://cldup.com/cXyG__fTLN.jpg", "image", "Mountain", "")); + break; + case VIDEO: + rnMediaList.add(new Media(2, "https://i.cloudup.com/YtZFJbuQCE.mov", "video", "Cloudup", "")); + case ANY: + case OTHER: + rnMediaList.add(new Media(3, "https://wordpress.org/latest.zip", "zip", "WordPress latest version", "WordPress.zip")); + break; + } mediaSelectedCallback.onMediaFileSelected(rnMediaList); } @@ -111,7 +119,7 @@ public void editorDidAutosave() { @Override public void getOtherMediaPickerOptions(OtherMediaOptionsReceivedCallback otherMediaOptionsReceivedCallback, MediaType mediaType) { - if (mediaType == MediaType.OTHER) { + if (mediaType == MediaType.ANY) { ArrayList<MediaOption> mediaOptions = new ArrayList<>(); mediaOptions.add(new MediaOption("1", "Choose from device")); otherMediaOptionsReceivedCallback.onOtherMediaOptionsReceived(mediaOptions); @@ -181,8 +189,13 @@ public void gutenbergDidRequestUnsupportedBlockFallback(ReplaceUnsupportedBlockC } @Override - public void onAddMention(Consumer<String> onSuccess) { - onSuccess.accept("matt"); + public void onShowUserSuggestions(Consumer<String> onResult) { + onResult.accept("matt"); + } + + @Override + public void onShowXpostSuggestions(Consumer<String> onResult) { + onResult.accept("ma.tt"); } @Override diff --git a/packages/react-native-editor/ios/DocumentsMediaSource.swift b/packages/react-native-editor/ios/DocumentsMediaSource.swift index 5e352ea9b68c6b..5914cf65e0134b 100644 --- a/packages/react-native-editor/ios/DocumentsMediaSource.swift +++ b/packages/react-native-editor/ios/DocumentsMediaSource.swift @@ -65,8 +65,10 @@ extension Gutenberg.MediaType { return String(kUTTypeMovie) case .audio: return String(kUTTypeAudio) - case .other: - return String(kUTTypeItem) - } + case .any: + return String(kUTTypeItem) + case .other: + return nil + } } } diff --git a/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift b/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift index 3f393e60b329dc..b0002e0e7b5917 100644 --- a/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift +++ b/packages/react-native-editor/ios/GutenbergDemo/GutenbergViewController.swift @@ -100,6 +100,8 @@ extension GutenbergViewController: GutenbergBridgeDelegate { } else { callback([MediaInfo(id: 2, url: "https://i.cloudup.com/YtZFJbuQCE.mov", type: "video", caption: "Cloudup")]) } + case .other, .any: + callback([MediaInfo(id: 3, url: "https://wordpress.org/latest.zip", type: "zip", caption: "WordPress latest version", title: "WordPress.zip")]) default: break } @@ -110,7 +112,7 @@ extension GutenbergViewController: GutenbergBridgeDelegate { print("Gutenberg did request a device media picker, opening the camera picker") pickAndUpload(from: .camera, filter: currentFilter, callback: callback) - case .filesApp: + case .filesApp, .otherApps: pickAndUploadFromFilesApp(filter: currentFilter, callback: callback) default: break } @@ -223,6 +225,10 @@ extension GutenbergViewController: GutenbergBridgeDelegate { callback(.success("matt")) } + func gutenbergDidRequestXpost(callback: @escaping (Result<String, NSError>) -> Void) { + callback(.success("ma.tt")) + } + func gutenbergDidRequestStarterPageTemplatesTooltipShown() -> Bool { return false; } @@ -296,6 +302,7 @@ extension GutenbergViewController: GutenbergBridgeDataSource { func gutenbergCapabilities() -> [Capabilities : Bool] { return [ .mentions: true, + .xposts: true, .unsupportedBlockEditor: unsupportedBlockEnabled, .canEnableUnsupportedBlockEditor: unsupportedBlockCanBeActivated, .mediaFilesCollectionBlock: true, @@ -311,12 +318,13 @@ extension GutenbergViewController: GutenbergBridgeDataSource { } func gutenbergMediaSources() -> [Gutenberg.MediaSource] { - return [.filesApp] + return [.filesApp, .otherApps] } } extension Gutenberg.MediaSource { - static let filesApp = Gutenberg.MediaSource(id: "files-app", label: "Pick a file", types: [.image, .video, .audio, .other]) + static let filesApp = Gutenberg.MediaSource(id: "files-app", label: "Choose from device", types: [.any]) + static let otherApps = Gutenberg.MediaSource(id: "other-apps", label: "Other Apps", types: [.image, .video, .audio, .other]) } //MARK: - Navigation bar diff --git a/packages/react-native-editor/ios/GutenbergDemo/MediaUploadCoordinator.swift b/packages/react-native-editor/ios/GutenbergDemo/MediaUploadCoordinator.swift index 9571d532d92c87..2655af936b466b 100644 --- a/packages/react-native-editor/ios/GutenbergDemo/MediaUploadCoordinator.swift +++ b/packages/react-native-editor/ios/GutenbergDemo/MediaUploadCoordinator.swift @@ -20,6 +20,7 @@ class MediaUploadCoordinator: NSObject { func upload(url: URL) -> Int32? { //Make sure the media is not larger than a 32 bits to number to avoid problems when bridging to JS + successfullUpload = true let mediaID = Int32(truncatingIfNeeded:UUID().uuidString.hash) let progress = Progress(parent: nil, userInfo: [ProgressUserInfoKey.mediaID: mediaID, ProgressUserInfoKey.mediaURL: url]) progress.totalUnitCount = 100 diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index 2d1357ca1dce44..5735431de050df 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -21,7 +21,7 @@ PODS: - DoubleConversion - glog - glog (0.3.5) - - Gutenberg (1.41.1): + - Gutenberg (1.43.0): - React-Core (= 0.61.5) - React-CoreModules (= 0.61.5) - React-RCTImage (= 0.61.5) @@ -253,7 +253,7 @@ PODS: - React-Core - RNSVG (9.13.6-gb): - React-Core - - RNTAztecView (1.41.1): + - RNTAztecView (1.43.0): - React-Core - WordPress-Aztec-iOS (~> 1.19.3) - WordPress-Aztec-iOS (1.19.3) @@ -402,7 +402,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 118d0d177724c2d67f08a59136eb29ef5943ec75 Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 glog: 1f3da668190260b06b429bb211bfbee5cd790c28 - Gutenberg: 103bee9a4d6c33edd2321dfc38b7fa51741d2afb + Gutenberg: 0bf0e3308e75b4c4693447d3ce672410e978616e RCTRequired: b153add4da6e7dbc44aebf93f3cf4fcae392ddf1 RCTTypeSafety: 9aa1b91d7f9310fc6eadc3cf95126ffe818af320 React: b6a59ef847b2b40bb6e0180a97d0ca716969ac78 @@ -435,7 +435,7 @@ SPEC CHECKSUMS: RNReanimated: f05baf4cd76b6eab2e4d7e2b244424960b968918 RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 46c4b680fe18237fa01eb7d7b311d77618fde31f - RNTAztecView: ebd2ceeafca809bd1fde98a757ab6c7e4a634747 + RNTAztecView: 2b2423ec5349be4170212c6d81345525b4d9f41c WordPress-Aztec-iOS: b7ac8b30f746992e85d9668453ac87c2cdcecf4f Yoga: f2a7cd4280bfe2cca5a7aed98ba0eb3d1310f18b diff --git a/test/native/jest_ui.config.js b/packages/react-native-editor/jest_ui.config.js similarity index 79% rename from test/native/jest_ui.config.js rename to packages/react-native-editor/jest_ui.config.js index 15855a1a261e0b..fbbb9f7ff89208 100644 --- a/test/native/jest_ui.config.js +++ b/packages/react-native-editor/jest_ui.config.js @@ -10,7 +10,7 @@ if ( process.env.TEST_RN_PLATFORM ) { module.exports = { verbose: true, - rootDir: '../../', + rootDir: './', haste: { defaultPlatform: rnPlatform, platforms: [ 'android', 'ios', 'native' ], @@ -19,6 +19,8 @@ module.exports = { '^.+\\.(js|ts|tsx)$': 'babel-jest', }, timers: 'real', - setupFiles: [], + setupFilesAfterEnv: [ './jest_ui_setup_after_env.js' ], + testEnvironment: './jest_ui_test_environment.js', testMatch: [ '**/__device-tests__/**/*.test.[jt]s?(x)' ], + reporters: [ 'default', 'jest-junit' ], }; diff --git a/packages/react-native-editor/jest_ui_setup_after_env.js b/packages/react-native-editor/jest_ui_setup_after_env.js new file mode 100644 index 00000000000000..3f90091bf77552 --- /dev/null +++ b/packages/react-native-editor/jest_ui_setup_after_env.js @@ -0,0 +1,146 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ); +const path = require( 'path' ); +const childProcess = require( 'child_process' ); + +/** + * Internal dependencies + */ +const { + isAndroid, + isLocalEnvironment, +} = require( './__device-tests__/helpers/utils' ); + +jest.setTimeout( 1000000 ); // in milliseconds + +let iOSScreenRecordingProcess; +let androidScreenRecordingProcess; + +const isMacOSEnvironment = () => { + return process.platform === 'darwin'; +}; + +const IOS_RECORDINGS_DIR = './ios-screen-recordings'; +const ANDROID_RECORDINGS_DIR = './android-screen-recordings'; + +const getScreenRecordingFileNameBase = ( testPath, id ) => { + const suiteName = path.basename( testPath, '.test.js' ); + return `${ suiteName }.${ id }`; +}; + +let allPassed = true; + +jasmine.getEnv().addReporter( { + specStarted: ( { testPath, id } ) => { + if ( ! isMacOSEnvironment() ) { + return; + } + + const fileName = + getScreenRecordingFileNameBase( testPath, id ) + '.mp4'; + + if ( isAndroid() ) { + if ( ! fs.existsSync( ANDROID_RECORDINGS_DIR ) ) { + fs.mkdirSync( ANDROID_RECORDINGS_DIR ); + } + + androidScreenRecordingProcess = childProcess.spawn( 'adb', [ + 'shell', + 'screenrecord', + '--verbose', + '--bit-rate', + '1M', + '--size', + '720x1280', + `/sdcard/${ fileName }`, + ] ); + + androidScreenRecordingProcess.stderr.on( 'data', ( data ) => { + // eslint-disable-next-line no-console + console.log( `Android screen recording error => ${ data }` ); + } ); + + return; + } + + if ( ! fs.existsSync( IOS_RECORDINGS_DIR ) ) { + fs.mkdirSync( IOS_RECORDINGS_DIR ); + } + + iOSScreenRecordingProcess = childProcess.spawn( + 'xcrun', + [ + 'simctl', + 'io', + 'booted', + 'recordVideo', + '--mask=black', + '--force', + fileName, + ], + { + cwd: IOS_RECORDINGS_DIR, + } + ); + }, + specDone: ( { testPath, id, status } ) => { + allPassed = allPassed && status !== 'failed'; + + if ( ! isMacOSEnvironment() ) { + return; + } + + const fileNameBase = getScreenRecordingFileNameBase( testPath, id ); + + if ( isAndroid() ) { + androidScreenRecordingProcess.kill( 'SIGINT' ); + // wait for kill + childProcess.execSync( 'sleep 1' ); + + try { + childProcess.execSync( + `adb pull /sdcard/${ fileNameBase }.mp4 ${ ANDROID_RECORDINGS_DIR }` + ); + } catch ( error ) { + // Some (old) Android devices don't support screen recording or + // sometimes the initial `should be able to see visual editor` + // tests are too fast and a recording is not generated. This is + // when `adb pull` can't find the recording file. In these cases + // we ignore the errors and keep running the tests. + // eslint-disable-next-line no-console + console.log( + `Android screen recording error => ${ error.stdout }` + ); + } + + const oldPath = `${ ANDROID_RECORDINGS_DIR }/${ fileNameBase }.mp4`; + const newPath = `${ ANDROID_RECORDINGS_DIR }/${ fileNameBase }.${ status }.mp4`; + + if ( fs.existsSync( oldPath ) ) { + fs.renameSync( oldPath, newPath ); + } + return; + } + + iOSScreenRecordingProcess.kill( 'SIGINT' ); + + const oldPath = `${ IOS_RECORDINGS_DIR }/${ fileNameBase }.mp4`; + const newPath = `${ IOS_RECORDINGS_DIR }/${ fileNameBase }.${ status }.mp4`; + + if ( fs.existsSync( oldPath ) ) { + fs.renameSync( oldPath, newPath ); + } + }, + suiteDone() { + if ( ! isLocalEnvironment() ) { + global.editorPage.sauceJobStatus( allPassed ); + } + }, +} ); + +it( 'should be able to see visual editor', async () => { + // wait for the block editor to load + await expect( global.editorPage.getBlockList() ).resolves.toBe( true ); +} ); diff --git a/packages/react-native-editor/jest_ui_test_environment.js b/packages/react-native-editor/jest_ui_test_environment.js new file mode 100644 index 00000000000000..052aa95da0781f --- /dev/null +++ b/packages/react-native-editor/jest_ui_test_environment.js @@ -0,0 +1,26 @@ +/** + * Internal dependencies + */ +const { + initializeEditorPage, +} = require( './__device-tests__/pages/editor-page' ); + +/** + * External dependencies + */ +// eslint-disable-next-line import/no-extraneous-dependencies +const JSDOMEnvironment = require( 'jest-environment-jsdom' ); + +class CustomEnvironment extends JSDOMEnvironment { + async setup() { + await super.setup(); + this.global.editorPage = await initializeEditorPage(); + } + + async teardown() { + await this.global.editorPage.stopDriver(); + await super.teardown(); + } +} + +module.exports = CustomEnvironment; diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index b501f2cb9deb1f..7bbfa5f4e03c37 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,129 +1,129 @@ { - "name": "@wordpress/react-native-editor", - "version": "1.41.1", - "description": "Mobile WordPress gutenberg editor.", - "author": "The WordPress Contributors", - "license": "GPL-2.0-or-later", - "keywords": [ - "wordpress", - "react-native" - ], - "private": true, - "config": { - "jsfiles": "./*.js src/*.js src/**/*.js src/**/**/*.js", - "scssfiles": "src/*.scss src/**/*.scss" - }, - "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/react-native-editor/README.md", - "repository": { - "type": "git", - "url": "https://github.com/WordPress/gutenberg.git", - "directory": "packages/react-native-editor" - }, - "bugs": { - "url": "https://github.com/WordPress/gutenberg/issues" - }, - "engines": { - "node": ">=10", - "npm": ">=6.9" - }, - "main": "src/index.js", - "react-native": "src/index", - "dependencies": { - "@babel/runtime": "^7.11.2", - "@react-native-community/blur": "3.6.0", - "@react-native-community/masked-view": "git+https://github.com/wordpress-mobile/react-native-masked-view.git#f65a51a3320e58404d7f38d967bfd1f42b439ca9", - "@react-native-community/slider": "git+https://github.com/wordpress-mobile/react-native-slider.git#d263ff16cdd9fb7352b354342522ff030f220f42", - "@react-navigation/core": "5.12.0", - "@react-navigation/native": "5.7.0", - "@react-navigation/routers": "5.4.9", - "@react-navigation/stack": "5.6.2", - "@wordpress/api-fetch": "file:../api-fetch", - "@wordpress/block-editor": "file:../block-editor", - "@wordpress/block-library": "file:../block-library", - "@wordpress/blocks": "file:../blocks", - "@wordpress/components": "file:../components", - "@wordpress/data": "file:../data", - "@wordpress/edit-post": "file:../edit-post", - "@wordpress/element": "file:../element", - "@wordpress/hooks": "file:../hooks", - "@wordpress/i18n": "file:../i18n", - "@wordpress/react-native-aztec": "file:../react-native-aztec", - "@wordpress/react-native-bridge": "file:../react-native-bridge", - "fast-average-color": "^4.3.0", - "jed": "^1.1.1", - "jsc-android": "^241213.1.0", - "jsdom-jscore-rn": "git+https://github.com/iamcco/jsdom-jscore-rn.git#a562f3d57c27c13e5bfc8cf82d496e69a3ba2800", - "metro-react-native-babel-preset": "0.57.0", - "metro-react-native-babel-transformer": "0.56.0", - "node-fetch": "^2.6.0", - "react-native": "0.61.5", - "react-native-dark-mode": "git+https://github.com/wordpress-mobile/react-native-dark-mode.git#f09bf1480e7b34536413ab3300f29e4375edb2c6", - "react-native-gesture-handler": "git+https://github.com/wordpress-mobile/react-native-gesture-handler.git#b80e959908b383a26d6e35d992d6d529efad0b16", - "react-native-get-random-values": "git+https://github.com/wordpress-mobile/react-native-get-random-values.git#f03f2c16414aff4ea76064dcd00a9e3c6efc838d", - "react-native-hr": "git+https://github.com/Riglerr/react-native-hr.git#2d01a5cf77212d100e8b99e0310cce5234f977b3", - "react-native-hsv-color-picker": "git+https://github.com/wordpress-mobile/react-native-hsv-color-picker", - "react-native-keyboard-aware-scroll-view": "git+https://github.com/wordpress-mobile/react-native-keyboard-aware-scroll-view.git#gb-v0.8.8", - "react-native-linear-gradient": "git+https://github.com/wordpress-mobile/react-native-linear-gradient.git#52bf43077171cff8714ce3e0155f3ebb7f55bc37", - "react-native-modal": "^6.5.0", - "react-native-reanimated": "git+https://github.com/wordpress-mobile/react-native-reanimated.git#ed48f510fba751cd75da7629e92276166766be91", - "react-native-safe-area": "^0.5.0", - "react-native-safe-area-context": "git+https://github.com/wordpress-mobile/react-native-safe-area-context.git#1e3c0d34f31b59fb79f71ec0b4c39c513f684871", - "react-native-sass-transformer": "^1.1.1", - "react-native-screens": "git+https://github.com/wordpress-mobile/react-native-screens.git#b3d907bda2695743f35fd874261dcd27e0912eca", - "react-native-svg": "git+https://github.com/wordpress-mobile/react-native-svg.git#a628e92990a2404e30a0086f168bd2b5b7b4ce96", - "react-native-url-polyfill": "^1.1.2", - "react-native-video": "git+https://github.com/wordpress-mobile/react-native-video.git#1b964b107863351ed744fc104d7952bbec3e2d4f" - }, - "publishConfig": { - "access": "public" - }, - "scripts": { - "start": "react-native start", - "start:reset": "npm run clean:runtime && npm run start -- --reset-cache", - "start:debug": "node --inspect-brk node_modules/.bin/react-native start", - "prern-bundle": "cd ../.. && patch-package --patch-dir packages/react-native-editor/metro-patch", - "rn-bundle": "react-native bundle", - "postrn-bundle": "cd ../.. && patch-package --reverse --patch-dir packages/react-native-editor/metro-patch", - "prebundle": "npm run i18n-cache:force", - "bundle": "npm run bundle:android && npm run bundle:ios", - "bundle:android": "mkdir -p bundle/android && npm run rn-bundle -- --platform android --dev false --entry-file index.js --assets-dest bundle/android --bundle-output bundle/android/App.text.js --sourcemap-output bundle/android/App.text.js.map && ./gutenberg/node_modules/hermes-engine/`node -e \"const platform=require('os').platform();console.log(platform === 'darwin' ? 'osx-bin' : (platform === 'linux' ? 'linux64-bin' : (platform === 'win32' ? 'win64-bin' : 'unsupported-os')));\"`/hermes -emit-binary -O -out bundle/android/App.js bundle/android/App.text.js -output-source-map", - "bundle:ios": "mkdir -p bundle/ios && npm run rn-bundle -- --platform ios --dev false --entry-file index.js --assets-dest bundle/ios --bundle-output bundle/ios/App.js --sourcemap-output bundle/ios/App.js.map", - "i18n-cache": "node i18n-cache/index.js", - "i18n-cache:force": "cross-env REFRESH_I18N_CACHE=1 node i18n-cache/index.js", - "postinstall": "npm run i18n-cache", - "android": "react-native run-android", - "prewpandroid": "rm -Rf $TMPDIR/gbmobile-wpandroidfakernroot && mkdir $TMPDIR/gbmobile-wpandroidfakernroot && ln -s $(cd \"$(dirname \"../../../../../\")\"; pwd) $TMPDIR/gbmobile-wpandroidfakernroot/android", - "wpandroid": "npm run android -- --root $TMPDIR/gbmobile-wpandroidfakernroot --variant wasabiDebug --appIdSuffix beta --appFolder WordPress --main-activity=ui.WPLaunchActivity", - "preios": "cd ios && (bundle check --path=vendor/bundle > /dev/null || bundle install) && bundle exec pod install --repo-update", - "preios:carthage": "cd ../react-native-aztec && npm run install-aztec-ios", - "preios:carthage:update": "cd ../react-native-aztec && npm run update-aztec-ios", - "preios:xcode10": "cd ../../node_modules/react-native && ./scripts/ios-install-third-party.sh && cd third-party/glog-0.3.5 && [ -f libglog.pc ] || ../../scripts/ios-configure-glog.sh", - "ios": "react-native run-ios", - "ios:fast": "react-native run-ios", - "test": "cross-env NODE_ENV=test jest --verbose --config ../../test/native/jest.config.js", - "test:debug": "cross-env NODE_ENV=test node --inspect-brk jest --runInBand --verbose --config ../../test/native/jest.config.js", - "device-tests": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --no-cache --maxWorkers=3 --reporters=default --reporters=jest-junit --verbose --config ../../test/native/jest_ui.config.js", - "device-tests-canary": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --no-cache --maxWorkers=2 --testNamePattern=@canary --reporters=default --reporters=jest-junit --verbose --config ../../test/native/jest_ui.config.js", - "device-tests:local": "cross-env NODE_ENV=test jest --runInBand --reporters=default --reporters=jest-junit --detectOpenHandles --verbose --forceExit --config ../../test/native/jest_ui.config.js", - "device-tests:debug": "cross-env NODE_ENV=test node $NODE_DEBUG_OPTION --inspect-brk node_modules/jest/bin/jest --runInBand --reporters=default --reporters=jest-junit --detectOpenHandles --verbose --config ../../test/native/jest_ui.config.js", - "test:e2e:bundle:android": "mkdir -p android/app/src/main/assets && npm run rn-bundle -- --reset-cache --platform android --dev false --minify false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res", - "test:e2e:build-app:android": "cd android && ./gradlew clean && ./gradlew assembleDebug", - "test:e2e:android:local": "npm run test:e2e:bundle:android && npm run test:e2e:build-app:android && TEST_RN_PLATFORM=android npm run device-tests:local", - "test:e2e:bundle:ios": "mkdir -p ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app && npm run rn-bundle -- --reset-cache --platform=ios --dev=false --minify false --entry-file=index.js --bundle-output=./ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app/main.jsbundle --assets-dest=./ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app", - "test:e2e:build-app:ios": "npm run preios && SKIP_BUNDLING=true xcodebuild -workspace ios/GutenbergDemo.xcworkspace -configuration Release -scheme GutenbergDemo -destination 'platform=iOS Simulator,name=iPhone 11' -derivedDataPath ios/build/GutenbergDemo", - "test:e2e:ios:local": "npm run test:e2e:bundle:ios && npm run test:e2e:build-app:ios && TEST_RN_PLATFORM=ios npm run device-tests:local", - "build:gutenberg": "cd gutenberg && npm ci && npm run build", - "clean": "npm run clean:build-artifacts; npm run clean:aztec; npm run clean:haste; npm run clean:jest; npm run clean:metro; npm run clean:react; npm run clean:watchman", - "clean:runtime": "npm run clean:haste; npm run clean:react; npm run clean:metro; npm run clean:jest; npm run clean:watchman; npm run clean:babel-cache", - "clean:build-artifacts": "rm -rf ./ios/build && rm -rf ./ios/Pods", - "clean:aztec": "cd ../react-native-aztec && npm run clean", - "clean:haste": "rm -rf /tmp/haste-map-react-native-packager-*", - "clean:install": "npm run clean; npm install", - "clean:jest": "jest --clearCache --config ../../test/native/jest.config.js; rm -rf $TMPDIR/jest_*", - "clean:metro": "rm -rf $TMPDIR/metro-cache-*;", - "clean:react": "rm -rf $TMPDIR/react-*", - "clean:watchman": "command -v watchman >/dev/null 2>&1 && watchman watch-del-all; true", - "clean:babel-cache": "rm -rf ../../node_modules/.cache/babel-loader/*", - "clean:i18n-cache": "rm -rf ./i18n-cache/data/*.json && rm -f ./i18n-cache/index.native.js" - } + "name": "@wordpress/react-native-editor", + "version": "1.43.0", + "description": "Mobile WordPress gutenberg editor.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "react-native" + ], + "private": true, + "config": { + "jsfiles": "./*.js src/*.js src/**/*.js src/**/**/*.js", + "scssfiles": "src/*.scss src/**/*.scss" + }, + "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/react-native-editor/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/react-native-editor" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "engines": { + "node": ">=10", + "npm": ">=6.9" + }, + "main": "src/index.js", + "react-native": "src/index", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@react-native-community/blur": "3.6.0", + "@react-native-community/masked-view": "git+https://github.com/wordpress-mobile/react-native-masked-view.git#f65a51a3320e58404d7f38d967bfd1f42b439ca9", + "@react-native-community/slider": "git+https://github.com/wordpress-mobile/react-native-slider.git#d263ff16cdd9fb7352b354342522ff030f220f42", + "@react-navigation/core": "5.12.0", + "@react-navigation/native": "5.7.0", + "@react-navigation/routers": "5.4.9", + "@react-navigation/stack": "5.6.2", + "@wordpress/api-fetch": "file:../api-fetch", + "@wordpress/block-editor": "file:../block-editor", + "@wordpress/block-library": "file:../block-library", + "@wordpress/blocks": "file:../blocks", + "@wordpress/components": "file:../components", + "@wordpress/data": "file:../data", + "@wordpress/edit-post": "file:../edit-post", + "@wordpress/element": "file:../element", + "@wordpress/hooks": "file:../hooks", + "@wordpress/i18n": "file:../i18n", + "@wordpress/react-native-aztec": "file:../react-native-aztec", + "@wordpress/react-native-bridge": "file:../react-native-bridge", + "fast-average-color": "^4.3.0", + "jed": "^1.1.1", + "jsc-android": "^241213.1.0", + "jsdom-jscore-rn": "git+https://github.com/iamcco/jsdom-jscore-rn.git#a562f3d57c27c13e5bfc8cf82d496e69a3ba2800", + "metro-react-native-babel-preset": "0.57.0", + "metro-react-native-babel-transformer": "0.56.0", + "node-fetch": "^2.6.0", + "react-native": "0.61.5", + "react-native-dark-mode": "git+https://github.com/wordpress-mobile/react-native-dark-mode.git#f09bf1480e7b34536413ab3300f29e4375edb2c6", + "react-native-gesture-handler": "git+https://github.com/wordpress-mobile/react-native-gesture-handler.git#b80e959908b383a26d6e35d992d6d529efad0b16", + "react-native-get-random-values": "git+https://github.com/wordpress-mobile/react-native-get-random-values.git#f03f2c16414aff4ea76064dcd00a9e3c6efc838d", + "react-native-hr": "git+https://github.com/Riglerr/react-native-hr.git#2d01a5cf77212d100e8b99e0310cce5234f977b3", + "react-native-hsv-color-picker": "git+https://github.com/wordpress-mobile/react-native-hsv-color-picker", + "react-native-keyboard-aware-scroll-view": "git+https://github.com/wordpress-mobile/react-native-keyboard-aware-scroll-view.git#gb-v0.8.8", + "react-native-linear-gradient": "git+https://github.com/wordpress-mobile/react-native-linear-gradient.git#52bf43077171cff8714ce3e0155f3ebb7f55bc37", + "react-native-modal": "^6.5.0", + "react-native-reanimated": "git+https://github.com/wordpress-mobile/react-native-reanimated.git#ed48f510fba751cd75da7629e92276166766be91", + "react-native-safe-area": "^0.5.0", + "react-native-safe-area-context": "git+https://github.com/wordpress-mobile/react-native-safe-area-context.git#1e3c0d34f31b59fb79f71ec0b4c39c513f684871", + "react-native-sass-transformer": "^1.1.1", + "react-native-screens": "git+https://github.com/wordpress-mobile/react-native-screens.git#b3d907bda2695743f35fd874261dcd27e0912eca", + "react-native-svg": "git+https://github.com/wordpress-mobile/react-native-svg.git#a628e92990a2404e30a0086f168bd2b5b7b4ce96", + "react-native-url-polyfill": "^1.1.2", + "react-native-video": "git+https://github.com/wordpress-mobile/react-native-video.git#1b964b107863351ed744fc104d7952bbec3e2d4f" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "start": "react-native start", + "start:reset": "npm run clean:runtime && npm run start -- --reset-cache", + "start:debug": "node --inspect-brk node_modules/.bin/react-native start", + "prern-bundle": "cd ../.. && patch-package --patch-dir packages/react-native-editor/metro-patch", + "rn-bundle": "react-native bundle", + "postrn-bundle": "cd ../.. && patch-package --reverse --patch-dir packages/react-native-editor/metro-patch", + "prebundle": "npm run i18n-cache:force", + "bundle": "npm run bundle:android && npm run bundle:ios", + "bundle:android": "mkdir -p bundle/android && npm run rn-bundle -- --platform android --dev false --entry-file index.js --assets-dest bundle/android --bundle-output bundle/android/App.text.js --sourcemap-output bundle/android/App.text.js.map && ./gutenberg/node_modules/hermes-engine/`node -e \"const platform=require('os').platform();console.log(platform === 'darwin' ? 'osx-bin' : (platform === 'linux' ? 'linux64-bin' : (platform === 'win32' ? 'win64-bin' : 'unsupported-os')));\"`/hermes -emit-binary -O -out bundle/android/App.js bundle/android/App.text.js -output-source-map", + "bundle:ios": "mkdir -p bundle/ios && npm run rn-bundle -- --platform ios --dev false --entry-file index.js --assets-dest bundle/ios --bundle-output bundle/ios/App.js --sourcemap-output bundle/ios/App.js.map", + "i18n-cache": "node i18n-cache/index.js", + "i18n-cache:force": "cross-env REFRESH_I18N_CACHE=1 node i18n-cache/index.js", + "postinstall": "npm run i18n-cache", + "android": "react-native run-android", + "prewpandroid": "rm -Rf $TMPDIR/gbmobile-wpandroidfakernroot && mkdir $TMPDIR/gbmobile-wpandroidfakernroot && ln -s $(cd \"$(dirname \"../../../../../\")\"; pwd) $TMPDIR/gbmobile-wpandroidfakernroot/android", + "wpandroid": "npm run android -- --root $TMPDIR/gbmobile-wpandroidfakernroot --variant wasabiDebug --appIdSuffix beta --appFolder WordPress --main-activity=ui.WPLaunchActivity", + "preios": "cd ios && (bundle check --path=vendor/bundle > /dev/null || bundle install) && bundle exec pod install --repo-update", + "preios:carthage": "cd ../react-native-aztec && npm run install-aztec-ios", + "preios:carthage:update": "cd ../react-native-aztec && npm run update-aztec-ios", + "preios:xcode10": "cd ../../node_modules/react-native && ./scripts/ios-install-third-party.sh && cd third-party/glog-0.3.5 && [ -f libglog.pc ] || ../../scripts/ios-configure-glog.sh", + "ios": "react-native run-ios", + "ios:fast": "react-native run-ios", + "test": "cross-env NODE_ENV=test jest --verbose --config ../../test/native/jest.config.js", + "test:debug": "cross-env NODE_ENV=test node --inspect-brk jest --runInBand --verbose --config ../../test/native/jest.config.js", + "device-tests": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --no-cache --maxWorkers=3 --verbose --config ./jest_ui.config.js", + "device-tests-canary": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --no-cache --maxWorkers=2 --testNamePattern=@canary --verbose --config ./jest_ui.config.js", + "device-tests:local": "cross-env NODE_ENV=test jest --runInBand --detectOpenHandles --verbose --forceExit --config ./jest_ui.config.js", + "device-tests:debug": "cross-env NODE_ENV=test node $NODE_DEBUG_OPTION --inspect-brk node_modules/jest/bin/jest --runInBand --detectOpenHandles --verbose --config ./jest_ui.config.js", + "test:e2e:bundle:android": "mkdir -p android/app/src/main/assets && npm run rn-bundle -- --reset-cache --platform android --dev false --minify false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res", + "test:e2e:build-app:android": "cd android && ./gradlew clean && ./gradlew assembleDebug", + "test:e2e:android:local": "npm run test:e2e:bundle:android && npm run test:e2e:build-app:android && TEST_RN_PLATFORM=android npm run device-tests:local", + "test:e2e:bundle:ios": "mkdir -p ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app && npm run rn-bundle -- --reset-cache --platform=ios --dev=false --minify false --entry-file=index.js --bundle-output=./ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app/main.jsbundle --assets-dest=./ios/build/GutenbergDemo/Build/Products/Release-iphonesimulator/GutenbergDemo.app", + "test:e2e:build-app:ios": "npm run preios && SKIP_BUNDLING=true xcodebuild -workspace ios/GutenbergDemo.xcworkspace -configuration Release -scheme GutenbergDemo -destination 'platform=iOS Simulator,name=iPhone 11' -derivedDataPath ios/build/GutenbergDemo", + "test:e2e:ios:local": "npm run test:e2e:bundle:ios && npm run test:e2e:build-app:ios && TEST_RN_PLATFORM=ios npm run device-tests:local", + "build:gutenberg": "cd gutenberg && npm ci && npm run build", + "clean": "npm run clean:build-artifacts; npm run clean:aztec; npm run clean:haste; npm run clean:jest; npm run clean:metro; npm run clean:react; npm run clean:watchman", + "clean:runtime": "npm run clean:haste; npm run clean:react; npm run clean:metro; npm run clean:jest; npm run clean:watchman; npm run clean:babel-cache", + "clean:build-artifacts": "rm -rf ./ios/build && rm -rf ./ios/Pods", + "clean:aztec": "cd ../react-native-aztec && npm run clean", + "clean:haste": "rm -rf /tmp/haste-map-react-native-packager-*", + "clean:install": "npm run clean; npm install", + "clean:jest": "jest --clearCache --config ../../test/native/jest.config.js; rm -rf $TMPDIR/jest_*", + "clean:metro": "rm -rf $TMPDIR/metro-cache-*;", + "clean:react": "rm -rf $TMPDIR/react-*", + "clean:watchman": "command -v watchman >/dev/null 2>&1 && watchman watch-del-all; true", + "clean:babel-cache": "rm -rf ../../node_modules/.cache/babel-loader/*", + "clean:i18n-cache": "rm -rf ./i18n-cache/data/*.json && rm -f ./i18n-cache/index.native.js" + } } diff --git a/packages/redux-routine/package.json b/packages/redux-routine/package.json index aa669a5429444d..2f678bd30efb4e 100644 --- a/packages/redux-routine/package.json +++ b/packages/redux-routine/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/redux-routine", - "version": "3.12.0", + "version": "3.13.0", "description": "Redux middleware for generator coroutines.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -25,7 +25,7 @@ "react-native": "src/index", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "is-promise": "^4.0.0", "lodash": "^4.17.19", "rungen": "^0.3.2" diff --git a/packages/redux-routine/src/runtime.js b/packages/redux-routine/src/runtime.js index ac1db84f6c738e..d534baae4efc01 100644 --- a/packages/redux-routine/src/runtime.js +++ b/packages/redux-routine/src/runtime.js @@ -46,7 +46,6 @@ export default function createRuntime( controls = {}, dispatch ) { if ( ! isAction( value ) ) { return false; } - dispatch( value ); next(); return true; diff --git a/packages/reusable-blocks/CHANGELOG.md b/packages/reusable-blocks/CHANGELOG.md index 1245f57a2a7928..aca49274631b71 100644 --- a/packages/reusable-blocks/CHANGELOG.md +++ b/packages/reusable-blocks/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 1.1.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the reusable blocks namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/reusable-blocks/README.md b/packages/reusable-blocks/README.md index 8d6848c93e041c..140e18ef70ed9b 100644 --- a/packages/reusable-blocks/README.md +++ b/packages/reusable-blocks/README.md @@ -72,8 +72,10 @@ return ( This package also provides convenient utilities for managing reusable blocks through redux actions: ```js +import { store as reusableBlocksStore } from '@wordpress/reusable-blocks'; + function MyConvertToStaticButton( { clientId } ) { - const { __experimentalConvertBlockToStatic } = useDispatch( 'core/reusable-blocks' ); + const { __experimentalConvertBlockToStatic } = useDispatch( reusableBlocksStore ); return ( <button onClick={() => __experimentalConvertBlockToStatic( clientId )} > Convert to static @@ -82,7 +84,7 @@ function MyConvertToStaticButton( { clientId } ) { } function MyConvertToReusableButton( { clientId } ) { - const { __experimentalConvertBlocksToReusable } = useDispatch( 'core/reusable-blocks' ); + const { __experimentalConvertBlocksToReusable } = useDispatch( reusableBlocksStore ); return ( <button onClick={() => __experimentalConvertBlocksToReusable( [ clientId ] )} > Convert to reusable @@ -91,7 +93,7 @@ function MyConvertToReusableButton( { clientId } ) { } function MyDeleteReusableBlockButton( { id } ) { - const { __experimentalDeleteReusableBlock } = useDispatch( 'core/reusable-blocks' ); + const { __experimentalDeleteReusableBlock } = useDispatch( reusableBlocksStore ); return ( <button onClick={() => __experimentalDeleteReusableBlock( id )} > Delete reusable block diff --git a/packages/reusable-blocks/package.json b/packages/reusable-blocks/package.json index 053f169e56128d..2c755d25abe693 100644 --- a/packages/reusable-blocks/package.json +++ b/packages/reusable-blocks/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/reusable-blocks", - "version": "1.0.3", + "version": "1.1.0", "description": "Reusable blocks utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -35,6 +35,7 @@ "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", "@wordpress/notices": "file:../notices", + "@wordpress/url": "file:../url", "lodash": "^4.17.19" }, "publishConfig": { diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/index.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/index.js index ed68dd7fec7717..ce5883402896de 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/index.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/index.js @@ -7,7 +7,7 @@ import { withSelect } from '@wordpress/data'; * Internal dependencies */ import ReusableBlockConvertButton from './reusable-block-convert-button'; -import ReusableBlockDeleteButton from './reusable-block-delete-button'; +import ReusableBlocksManageButton from './reusable-blocks-manage-button'; function ReusableBlocksMenuItems( { clientIds, rootClientId } ) { return ( @@ -17,7 +17,7 @@ function ReusableBlocksMenuItems( { clientIds, rootClientId } ) { rootClientId={ rootClientId } /> { clientIds.length === 1 && ( - <ReusableBlockDeleteButton clientId={ clientIds[ 0 ] } /> + <ReusableBlocksManageButton clientId={ clientIds[ 0 ] } /> ) } </> ); diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js index bd24b76261cc93..5b34cc3a850dfc 100644 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js @@ -8,6 +8,7 @@ import { MenuItem } from '@wordpress/components'; import { reusableBlock } from '@wordpress/icons'; import { useDispatch, useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -72,7 +73,7 @@ export default function ReusableBlockConvertButton( { } = useDispatch( store ); const { createSuccessNotice, createErrorNotice } = useDispatch( - 'core/notices' + noticesStore ); const onConvert = useCallback( async function () { diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-delete-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-delete-button.js deleted file mode 100644 index ac3abd0bb471cb..00000000000000 --- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-delete-button.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * WordPress dependencies - */ -import { MenuItem } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { isReusableBlock } from '@wordpress/blocks'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { useCallback } from '@wordpress/element'; -import { BlockSettingsMenuControls } from '@wordpress/block-editor'; - -/** - * Internal dependencies - */ -import { store } from '../../store'; - -function ReusableBlockDeleteButton( { clientId } ) { - const { isVisible, isDisabled, block } = useSelect( - ( select ) => { - const { getBlock } = select( 'core/block-editor' ); - const { canUser } = select( 'core' ); - const blockObj = getBlock( clientId ); - - const reusableBlock = - blockObj && isReusableBlock( blockObj ) - ? select( 'core' ).getEntityRecord( - 'postType', - 'wp_block', - blockObj.attributes.ref - ) - : null; - - return { - block: blockObj, - isVisible: - !! reusableBlock && - ( reusableBlock.isTemporary || - !! canUser( 'delete', 'blocks', reusableBlock.id ) ), - isDisabled: reusableBlock && reusableBlock.isTemporary, - }; - }, - [ clientId ] - ); - - const { - __experimentalDeleteReusableBlock: deleteReusableBlock, - } = useDispatch( store ); - - const { createSuccessNotice, createErrorNotice } = useDispatch( - 'core/notices' - ); - const onDelete = useCallback( - async function () { - try { - await deleteReusableBlock( block.attributes.ref ); - createSuccessNotice( __( 'Block deleted.' ), { - type: 'snackbar', - } ); - } catch ( error ) { - createErrorNotice( error.message, { - type: 'snackbar', - } ); - } - }, - [ block ] - ); - - if ( ! isVisible ) { - return null; - } - - return ( - <BlockSettingsMenuControls> - { ( { onClose } ) => ( - <MenuItem - disabled={ isDisabled } - onClick={ () => { - // eslint-disable-next-line no-alert - const hasConfirmed = window.confirm( - // eslint-disable-next-line @wordpress/i18n-no-collapsible-whitespace - __( - 'Are you sure you want to delete this Reusable Block?\n\n' + - 'It will be permanently removed from all posts and pages that use it.' - ) - ); - if ( hasConfirmed ) { - onDelete(); - onClose(); - } - } } - > - { __( 'Remove from Reusable blocks' ) } - </MenuItem> - ) } - </BlockSettingsMenuControls> - ); -} - -export default ReusableBlockDeleteButton; diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js new file mode 100644 index 00000000000000..ec4df75146bc08 --- /dev/null +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-blocks-manage-button.js @@ -0,0 +1,47 @@ +/** + * WordPress dependencies + */ +import { MenuItem } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { isReusableBlock } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; +import { BlockSettingsMenuControls } from '@wordpress/block-editor'; +import { addQueryArgs } from '@wordpress/url'; + +function ReusableBlocksManageButton( { clientId } ) { + const { isVisible } = useSelect( + ( select ) => { + const { getBlock } = select( 'core/block-editor' ); + const { canUser } = select( 'core' ); + const reusableBlock = getBlock( clientId ); + + return { + isVisible: + !! reusableBlock && + isReusableBlock( reusableBlock ) && + !! canUser( + 'update', + 'blocks', + reusableBlock.attributes.ref + ), + }; + }, + [ clientId ] + ); + + if ( ! isVisible ) { + return null; + } + + return ( + <BlockSettingsMenuControls> + <MenuItem + href={ addQueryArgs( 'edit.php', { post_type: 'wp_block' } ) } + > + { __( 'Manage Reusable blocks' ) } + </MenuItem> + </BlockSettingsMenuControls> + ); +} + +export default ReusableBlocksManageButton; diff --git a/packages/reusable-blocks/src/index.js b/packages/reusable-blocks/src/index.js index 2faa13104e51ae..2c29c97f829920 100644 --- a/packages/reusable-blocks/src/index.js +++ b/packages/reusable-blocks/src/index.js @@ -3,7 +3,6 @@ */ import '@wordpress/block-editor'; import '@wordpress/core-data'; -import '@wordpress/notices'; export { store } from './store'; export * from './components'; diff --git a/packages/reusable-blocks/src/store/controls.js b/packages/reusable-blocks/src/store/controls.js index 8b9b9613c05217..308cb68934338c 100644 --- a/packages/reusable-blocks/src/store/controls.js +++ b/packages/reusable-blocks/src/store/controls.js @@ -10,6 +10,11 @@ import { import { createRegistryControl } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import { store as reusableBlocksStore } from './index.js'; + /** * Convert a reusable block to a static block effect handler * @@ -94,7 +99,7 @@ const controls = { .dispatch( 'core/block-editor' ) .replaceBlocks( clientIds, newBlock ); registry - .dispatch( 'core/reusable-blocks' ) + .dispatch( reusableBlocksStore ) .__experimentalSetEditingReusableBlock( newBlock.clientId, true diff --git a/packages/rich-text/CHANGELOG.md b/packages/rich-text/CHANGELOG.md index 1d9457496ed6dd..b8e57b8dedc465 100644 --- a/packages/rich-text/CHANGELOG.md +++ b/packages/rich-text/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 3.24.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the rich-text namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/rich-text/package.json b/packages/rich-text/package.json index 219908bad0d01a..d12a91197d2e2b 100644 --- a/packages/rich-text/package.json +++ b/packages/rich-text/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/rich-text", - "version": "3.23.0", + "version": "3.24.0", "description": "Rich text value and manipulation API.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -26,7 +26,7 @@ "{src,build,build-module}/{index.js,store/index.js}" ], "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", "@wordpress/deprecated": "file:../deprecated", diff --git a/packages/rich-text/src/component/index.js b/packages/rich-text/src/component/index.js index e7d6b6cbb577d1..625da9012fd05c 100644 --- a/packages/rich-text/src/component/index.js +++ b/packages/rich-text/src/component/index.js @@ -374,7 +374,7 @@ function RichText( } if ( onPaste ) { - const files = [ ...getFilesFromDataTransfer( clipboardData ) ]; + const files = getFilesFromDataTransfer( clipboardData ); onPaste( { value: removeEditorOnlyFormats( record.current ), diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index 7e59d11c094181..2d3f3819dbac13 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -8,7 +8,10 @@ */ import RCTAztecView from '@wordpress/react-native-aztec'; import { View, Platform } from 'react-native'; -import { addMention } from '@wordpress/react-native-bridge'; +import { + showUserSuggestions, + showXpostSuggestions, +} from '@wordpress/react-native-bridge'; import { get, pickBy, debounce } from 'lodash'; import memize from 'memize'; @@ -17,14 +20,13 @@ import memize from 'memize'; */ import { BlockFormatControls } from '@wordpress/block-editor'; import { Component } from '@wordpress/element'; -import { Toolbar, ToolbarButton } from '@wordpress/components'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; import { childrenBlock } from '@wordpress/blocks'; import { decodeEntities } from '@wordpress/html-entities'; import { BACKSPACE, DELETE, ENTER } from '@wordpress/keycodes'; import { isURL } from '@wordpress/url'; -import { Icon, atSymbol } from '@wordpress/icons'; +import { atSymbol, plus } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; /** @@ -43,6 +45,8 @@ import { removeLineSeparator } from '../remove-line-separator'; import { isCollapsed } from '../is-collapsed'; import { remove } from '../remove'; import styles from './style.scss'; +import ToolbarButtonWithOptions from './toolbar-button-with-options'; +import { store as richTextStore } from '../store'; const unescapeSpaces = ( text ) => { return text.replace( /&nbsp;|&#160;/gi, ' ' ); @@ -82,7 +86,6 @@ export class RichText extends Component { this.onKeyDown = this.onKeyDown.bind( this ); this.handleEnter = this.handleEnter.bind( this ); this.handleDelete = this.handleDelete.bind( this ); - this.handleMention = this.handleMention.bind( this ); this.onPaste = this.onPaste.bind( this ); this.onFocus = this.onFocus.bind( this ); this.onBlur = this.onBlur.bind( this ); @@ -100,7 +103,16 @@ export class RichText extends Component { ); this.valueToFormat = this.valueToFormat.bind( this ); this.getHtmlToRender = this.getHtmlToRender.bind( this ); - this.showMention = this.showMention.bind( this ); + this.handleSuggestionFunc = this.handleSuggestionFunc.bind( this ); + this.handleUserSuggestion = this.handleSuggestionFunc( + showUserSuggestions, + '@' + ).bind( this ); + this.handleXpostSuggestion = this.handleSuggestionFunc( + showXpostSuggestions, + '+' + ).bind( this ); + this.suggestionOptions = this.suggestionOptions.bind( this ); this.insertString = this.insertString.bind( this ); this.state = { activeFormats: [], @@ -321,7 +333,7 @@ export class RichText extends Component { this.handleDelete( event ); this.handleEnter( event ); - this.handleMention( event ); + this.handleTriggerKeyCodes( event ); } handleEnter( event ) { @@ -400,33 +412,66 @@ export class RichText extends Component { this.lastAztecEventType = 'input'; } - handleMention( event ) { + handleTriggerKeyCodes( event ) { const { keyCode } = event; + const triggeredOption = this.suggestionOptions().find( ( option ) => { + const triggeredKeyCode = option.triggerChar.charCodeAt( 0 ); + return triggeredKeyCode === keyCode; + } ); - if ( keyCode !== '@'.charCodeAt( 0 ) ) { - return; - } - const record = this.getRecord(); - const text = getTextContent( record ); - // Only start the mention UI if the selection is on the start of text or the character before is a space - if ( - text.length === 0 || - record.start === 0 || - text.charAt( record.start - 1 ) === ' ' - ) { - this.showMention(); - } else { - this.insertString( record, '@' ); + if ( triggeredOption ) { + const record = this.getRecord(); + const text = getTextContent( record ); + // Only respond to the trigger if the selection is on the start of text or line + // or if the character before is a space + const useTrigger = + text.length === 0 || + record.start === 0 || + text.charAt( record.start - 1 ) === '\n' || + text.charAt( record.start - 1 ) === ' '; + + if ( useTrigger && triggeredOption.onClick ) { + triggeredOption.onClick(); + } else { + this.insertString( record, triggeredOption.triggerChar ); + } } } - showMention() { - const record = this.getRecord(); - addMention() - .then( ( mentionUserId ) => { - this.insertString( record, `@${ mentionUserId } ` ); - } ) - .catch( () => {} ); + suggestionOptions() { + const { areMentionsSupported, areXPostsSupported } = this.props; + const allOptions = [ + { + supported: areMentionsSupported, + title: __( 'Insert mention' ), + onClick: this.handleUserSuggestion, + triggerChar: '@', + value: 'mention', + label: __( 'Mention' ), + icon: atSymbol, + }, + { + supported: areXPostsSupported, + title: __( 'Insert crosspost' ), + onClick: this.handleXpostSuggestion, + triggerChar: '+', + value: 'crosspost', + label: __( 'Crosspost' ), + icon: plus, + }, + ]; + return allOptions.filter( ( op ) => op.supported ); + } + + handleSuggestionFunc( suggestionFunction, prefix ) { + return () => { + const record = this.getRecord(); + suggestionFunction() + .then( ( suggestion ) => { + this.insertString( record, `${ prefix }${ suggestion } ` ); + } ) + .catch( () => {} ); + }; } /** @@ -685,7 +730,7 @@ export class RichText extends Component { } componentWillUnmount() { - if ( this._editor.isFocused() && this.props.shouldBlurOnUnmount ) { + if ( this._editor.isFocused() ) { this._editor.blur(); } } @@ -757,7 +802,6 @@ export class RichText extends Component { withoutInteractiveFormatting, accessibilityLabel, disableEditingMenu = false, - isMentionsSupported, } = this.props; const record = this.getRecord(); @@ -873,11 +917,9 @@ export class RichText extends Component { onFocus={ this.onFocus } onBlur={ this.onBlur } onKeyDown={ this.onKeyDown } - triggerKeyCodes={ - disableEditingMenu === false && isMentionsSupported - ? [ '@' ] - : [] - } + triggerKeyCodes={ this.suggestionOptions().map( + ( op ) => op.triggerChar + ) } onPaste={ this.onPaste } activeFormats={ this.getActiveFormatNames( record ) } onContentSizeChange={ this.onContentSizeChange } @@ -918,18 +960,9 @@ export class RichText extends Component { onFocus={ () => {} } /> <BlockFormatControls> - { - // eslint-disable-next-line no-undef - isMentionsSupported && ( - <Toolbar> - <ToolbarButton - title={ __( 'Insert mention' ) } - icon={ <Icon icon={ atSymbol } /> } - onClick={ this.showMention } - /> - </Toolbar> - ) - } + <ToolbarButtonWithOptions + options={ this.suggestionOptions() } + /> </BlockFormatControls> </> ) } @@ -955,9 +988,10 @@ export default compose( [ get( parentBlock, [ 'attributes', 'childrenStyles' ] ) || {}; return { - formatTypes: select( 'core/rich-text' ).getFormatTypes(), - isMentionsSupported: + formatTypes: select( richTextStore ).getFormatTypes(), + areMentionsSupported: getSettings( 'capabilities' ).mentions === true, + areXPostsSupported: getSettings( 'capabilities' ).xposts === true, ...{ parentBlockStyles }, }; } ), diff --git a/packages/rich-text/src/component/toolbar-button-with-options.native.js b/packages/rich-text/src/component/toolbar-button-with-options.native.js new file mode 100644 index 00000000000000..3936bdd98c2872 --- /dev/null +++ b/packages/rich-text/src/component/toolbar-button-with-options.native.js @@ -0,0 +1,61 @@ +/** + * WordPress dependencies + */ +import { Picker, ToolbarGroup, ToolbarButton } from '@wordpress/components'; +import { useRef } from '@wordpress/element'; +import { Icon } from '@wordpress/icons'; + +/** + * Toolbar button component that, upon a long press, opens a Picker + * to allow selecting from among multiple options. + * + * @param {Object} props Component props. + * @param {Object} props.options Options to pick from. + */ +function ToolbarButtonWithOptions( { options } ) { + const picker = useRef(); + + function presentPicker() { + if ( picker.current ) { + picker.current.presentPicker(); + } + } + + function onValueSelected( selectedValue ) { + const selectedOption = options.find( + ( op ) => op.value === selectedValue + ); + if ( selectedOption ) { + selectedOption.onClick(); + } + } + + if ( ! options || options.length === 0 ) { + return null; + } + const firstOption = options[ 0 ]; + const enablePicker = options.length > 1; + + return ( + <> + <ToolbarGroup> + <ToolbarButton + title={ firstOption.title } + icon={ <Icon icon={ firstOption.icon } /> } + onClick={ firstOption.onClick } + onLongPress={ enablePicker && presentPicker } + /> + </ToolbarGroup> + { enablePicker && ( + <Picker + ref={ picker } + options={ options } + onChange={ onValueSelected } + hideCancelButton + /> + ) } + </> + ); +} + +export default ToolbarButtonWithOptions; diff --git a/packages/rich-text/src/component/use-format-types.js b/packages/rich-text/src/component/use-format-types.js index 285e5ee540eaf2..c1df61ac093c12 100644 --- a/packages/rich-text/src/component/use-format-types.js +++ b/packages/rich-text/src/component/use-format-types.js @@ -2,9 +2,13 @@ * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as richTextStore } from '../store'; function formatTypesSelector( select ) { - return select( 'core/rich-text' ).getFormatTypes(); + return select( richTextStore ).getFormatTypes(); } /** diff --git a/packages/rich-text/src/get-format-type.js b/packages/rich-text/src/get-format-type.js index 7b266f66779a7d..48e41d41b9c795 100644 --- a/packages/rich-text/src/get-format-type.js +++ b/packages/rich-text/src/get-format-type.js @@ -2,6 +2,10 @@ * WordPress dependencies */ import { select } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as richTextStore } from './store'; /** @typedef {import('./register-format-type').RichTextFormatType} RichTextFormatType */ @@ -13,5 +17,5 @@ import { select } from '@wordpress/data'; * @return {RichTextFormatType|undefined} Format type. */ export function getFormatType( name ) { - return select( 'core/rich-text' ).getFormatType( name ); + return select( richTextStore ).getFormatType( name ); } diff --git a/packages/rich-text/src/get-format-types.js b/packages/rich-text/src/get-format-types.js index 8f2d88f0040995..74bec3a355aa62 100644 --- a/packages/rich-text/src/get-format-types.js +++ b/packages/rich-text/src/get-format-types.js @@ -2,6 +2,10 @@ * WordPress dependencies */ import { select } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as richTextStore } from './store'; /** @typedef {import('./register-format-type').RichTextFormatType} RichTextFormatType */ @@ -11,5 +15,5 @@ import { select } from '@wordpress/data'; * @return {Array<RichTextFormatType>} Format settings. */ export function getFormatTypes() { - return select( 'core/rich-text' ).getFormatTypes(); + return select( richTextStore ).getFormatTypes(); } diff --git a/packages/rich-text/src/register-format-type.js b/packages/rich-text/src/register-format-type.js index 80e9d1680bc7fc..e3c096804e0ee6 100644 --- a/packages/rich-text/src/register-format-type.js +++ b/packages/rich-text/src/register-format-type.js @@ -2,7 +2,10 @@ * WordPress dependencies */ import { select, dispatch } from '@wordpress/data'; - +/** + * Internal dependencies + */ +import { store as richTextStore } from './store'; /** * @typedef {Object} WPFormat * @@ -44,7 +47,7 @@ export function registerFormatType( name, settings ) { return; } - if ( select( 'core/rich-text' ).getFormatType( settings.name ) ) { + if ( select( richTextStore ).getFormatType( settings.name ) ) { window.console.error( 'Format "' + settings.name + '" is already registered.' ); @@ -76,7 +79,7 @@ export function registerFormatType( name, settings ) { if ( settings.className === null ) { const formatTypeForBareElement = select( - 'core/rich-text' + richTextStore ).getFormatTypeForBareElement( settings.tagName ); if ( formatTypeForBareElement ) { @@ -87,7 +90,7 @@ export function registerFormatType( name, settings ) { } } else { const formatTypeForClassName = select( - 'core/rich-text' + richTextStore ).getFormatTypeForClassName( settings.className ); if ( formatTypeForClassName ) { @@ -119,7 +122,7 @@ export function registerFormatType( name, settings ) { return; } - dispatch( 'core/rich-text' ).addFormatTypes( settings ); + dispatch( richTextStore ).addFormatTypes( settings ); return settings; } diff --git a/packages/rich-text/src/test/register-format-type.js b/packages/rich-text/src/test/register-format-type.js index 9983c48f85f39d..0d044d28facfae 100644 --- a/packages/rich-text/src/test/register-format-type.js +++ b/packages/rich-text/src/test/register-format-type.js @@ -9,6 +9,7 @@ import { select } from '@wordpress/data'; import { registerFormatType } from '../register-format-type'; import { unregisterFormatType } from '../unregister-format-type'; import { getFormatType } from '../get-format-type'; +import { store as richTextStore } from '../store'; describe( 'registerFormatType', () => { beforeAll( () => { @@ -17,7 +18,7 @@ describe( 'registerFormatType', () => { } ); afterEach( () => { - select( 'core/rich-text' ) + select( richTextStore ) .getFormatTypes() .forEach( ( { name } ) => { unregisterFormatType( name ); diff --git a/packages/rich-text/src/unregister-format-type.js b/packages/rich-text/src/unregister-format-type.js index 97ee623c9383ea..519e33362d5b68 100644 --- a/packages/rich-text/src/unregister-format-type.js +++ b/packages/rich-text/src/unregister-format-type.js @@ -3,6 +3,11 @@ */ import { select, dispatch } from '@wordpress/data'; +/** + * Internal dependencies + */ +import { store as richTextStore } from './store'; + /** @typedef {import('./register-format-type').RichTextFormatType} RichTextFormatType */ /** @@ -15,14 +20,14 @@ import { select, dispatch } from '@wordpress/data'; * otherwise `undefined`. */ export function unregisterFormatType( name ) { - const oldFormat = select( 'core/rich-text' ).getFormatType( name ); + const oldFormat = select( richTextStore ).getFormatType( name ); if ( ! oldFormat ) { window.console.error( `Format ${ name } is not registered.` ); return; } - dispatch( 'core/rich-text' ).removeFormatTypes( name ); + dispatch( richTextStore ).removeFormatTypes( name ); return oldFormat; } diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index ebd28463357d56..ac9b0bdc414f92 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -2,9 +2,17 @@ ## Unreleased +## 12.6.0 (2020-12-17) + ### Enhancements - Autoformat TypeScript files (`*.ts` and `*.tsx`) in `format-js` script (#27138)[https://github.com/WordPress/gutenberg/pull/27138]. +- The bundled `wp-prettier` dependency has been upgraded from `2.0.5` to `2.2.1`. +- The bundled Babel dependency has been upgraded from `7.11` to `7.12`. + +### Internal + +- The bundled `ignore-emit-webpack-plugin` dependency has been updated from requiring `2.0.3` to requiring `^2.0.6`. ## 12.5.0 (2020-10-30) diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 5bda105e84d1dc..acb25c9db17236 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/scripts", - "version": "12.5.0", + "version": "12.6.0", "description": "Collection of reusable scripts for WordPress development.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -50,7 +50,7 @@ "dir-glob": "^3.0.1", "eslint": "^7.1.0", "eslint-plugin-markdown": "^1.0.2", - "ignore-emit-webpack-plugin": "2.0.3", + "ignore-emit-webpack-plugin": "^2.0.6", "jest": "^25.3.0", "jest-puppeteer": "^4.4.0", "markdownlint": "^0.18.0", @@ -59,7 +59,7 @@ "minimist": "^1.2.0", "npm-package-json-lint": "^5.0.0", "postcss-loader": "^3.0.0", - "prettier": "npm:wp-prettier@2.0.5", + "prettier": "npm:wp-prettier@2.2.1-beta-1", "puppeteer": "npm:puppeteer-core@3.0.0", "read-pkg-up": "^1.0.1", "resolve-bin": "^0.4.0", diff --git a/packages/server-side-render/README.md b/packages/server-side-render/README.md index bfe883037b8159..abf1329af3bf63 100644 --- a/packages/server-side-render/README.md +++ b/packages/server-side-render/README.md @@ -127,6 +127,7 @@ If you pass `attributes` to `ServerSideRender`, the block must also be registere register_block_type( 'core/archives', array( + 'apiVersion' => 2, 'attributes' => array( 'showPostCounts' => array( 'type' => 'boolean', diff --git a/packages/server-side-render/package.json b/packages/server-side-render/package.json index 80b45c5d2ff6e9..682c4065566ba3 100644 --- a/packages/server-side-render/package.json +++ b/packages/server-side-render/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/server-side-render", - "version": "1.19.1", + "version": "1.20.0", "description": "The component used with WordPress to server-side render a preview of dynamic blocks to display in the editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/api-fetch": "file:../api-fetch", "@wordpress/components": "file:../components", "@wordpress/data": "file:../data", diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json index 948031c53a42d2..0cafc0838f4270 100644 --- a/packages/shortcode/package.json +++ b/packages/shortcode/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/shortcode", - "version": "2.11.0", + "version": "2.12.0", "description": "Shortcode module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19", "memize": "^1.1.0" }, diff --git a/packages/shortcode/src/index.js b/packages/shortcode/src/index.js index 429b2eb722175f..e1e6a62e52b46d 100644 --- a/packages/shortcode/src/index.js +++ b/packages/shortcode/src/index.js @@ -90,29 +90,23 @@ export function next( tag, text, index = 0 ) { * @return {string} Text with shortcodes replaced. */ export function replace( tag, text, callback ) { - return text.replace( regexp( tag ), function ( - match, - left, - $3, - attrs, - slash, - content, - closing, - right - ) { - // If both extra brackets exist, the shortcode has been properly - // escaped. - if ( left === '[' && right === ']' ) { - return match; - } + return text.replace( + regexp( tag ), + function ( match, left, $3, attrs, slash, content, closing, right ) { + // If both extra brackets exist, the shortcode has been properly + // escaped. + if ( left === '[' && right === ']' ) { + return match; + } - // Create the match object and pass it through the callback. - const result = callback( fromMatch( arguments ) ); + // Create the match object and pass it through the callback. + const result = callback( fromMatch( arguments ) ); - // Make sure to return any of the extra brackets if they weren't used to - // escape the shortcode. - return result ? left + result + right : match; - } ); + // Make sure to return any of the extra brackets if they weren't used to + // escape the shortcode. + return result || result === '' ? left + result + right : match; + } + ); } /** diff --git a/packages/shortcode/src/test/index.js b/packages/shortcode/src/test/index.js index 6d2af91ee70b47..e385dacb0870b0 100644 --- a/packages/shortcode/src/test/index.js +++ b/packages/shortcode/src/test/index.js @@ -219,6 +219,30 @@ describe( 'shortcode', () => { ); expect( result2 ).toBe( 'this has the [foo] shortcode' ); } ); + + it( 'should replace shortcode with a number', () => { + const result1 = replace( 'foo', 'hello [foo] world', () => 3 ); + expect( result1 ).toBe( 'hello 3 world' ); + + const result2 = replace( + 'foo', + 'hello [foo bar=bar baz="baz" qux]delete me[/foo] world', + () => 4 + ); + expect( result2 ).toBe( 'hello 4 world' ); + } ); + + it( 'should replace shortcode with an empty string', () => { + const result1 = replace( 'foo', 'hello [foo] world', () => '' ); + expect( result1 ).toBe( 'hello world' ); + + const result2 = replace( + 'foo', + 'hello [foo bar=bar baz="baz" qux]delete me[/foo] world', + () => '' + ); + expect( result2 ).toBe( 'hello world' ); + } ); } ); describe( 'attrs', () => { diff --git a/packages/stan/CHANGELOG.md b/packages/stan/CHANGELOG.md deleted file mode 100644 index 1a910fdfc5f081..00000000000000 --- a/packages/stan/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -<!-- Learn how to maintain this file at https://github.com/WordPress/gutenberg/tree/master/packages#maintaining-changelogs. --> - -## Unreleased - -Initial release. \ No newline at end of file diff --git a/packages/stan/README.md b/packages/stan/README.md deleted file mode 100644 index 98711d027853ee..00000000000000 --- a/packages/stan/README.md +++ /dev/null @@ -1,216 +0,0 @@ -# Stan - -"stan" stands for "state" in Polish 🇵🇱. It's a framework agnostic library to manage distributed state in JavaScript application. It is highly inspired by the equivalent [Recoil](https://recoiljs.org/) and [jotai](https://jotai.surge.sh) - -It share the same goals as Recoil and Jotai: - -- Based on atoms (or observables) which means it's highly performant at scale - only what needs to update gets updated. -- Shares with Jotai the goal of maintaining a very light API surface. -- Supports async and sync state. - -Unlike these frameworks, it has the following goals that justified the creation of a separate library: - -- It is independent from other libraries like React. You can create binding for any of your desired frameworks. -- It needs to be flexible enough to offer bindings for `@wordpress/data` consumer API (`useSelect` and `useDispatch`). - -## Installation - -Install the module - -```bash -npm install @wordpress/stan --save -``` - -_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods. Learn more about it in [Babel docs](https://babeljs.io/docs/en/next/caveats)._ - -## Getting started - -Stan is based on the concept of "atoms". Atoms are discrete state units and your application state is a tree of atoms that depend on each other. - -### Creating basic atoms - -Let's create some basic atoms: - -```js -import { createAtom } from '@wordpress/stan'; - -// Creates an atom with an initial value of 1. -// The value can be of any type. -const counter = createAtom( 1 ); -``` - -### Manipulating atoms - -In this example we created an atom that can hold a counter that starts with `1`. -To manipulate we need a registry. The registry is the container of all atom states. - -```js -import { createAtomRegistry } from '@wordpress/stan'; - -const registry = createAtomRegistry(); - -// Read the counter. -console.log( registry.get( counter ) ); // prints 1. - -// Modify the value of the counter -registry.set( counter, 10 ); - -console.log( registry.get( counter ) ); // prints 10. -``` - -### Subscribing to changes - -Each atom is an observable to which we can subscribe: - -```js -registry.subscribe( counter, () => { - console.log( registry.get( counter ) ); -} ); - -registry.set( counter, 2 ); // prints 2. -registry.set( counter, 4 ); // prints 4. -``` - -### Derived atoms - -Atoms can also derive their value based on other atoms. We call these "derived atoms". - -```js -import { createAtom, createDerivedAtom } from '@wordpress/stan'; - -const counter1 = createAtom( 1 ); -const counter2 = createAtom( 2 ); -const sum = createDerivedAtom( - ( { get } ) => get( counter1 ) + get( counter2 ) -); -``` - -In the example above, we create two simple counter atoms and third derived "sum" atom which value is the sum of both counters. - -```js -console.log( registry.get( sum ) ); // prints 3. - -// Adding a listener automatically triggers the refreshing of the value. -// If the atom has no subscriber, it will only attempt a resolution when initially read. -// But it won't bother refreshing its value, if any of its dependencies change. -// This property (laziness) is important for performance reasons. -registry.subscribe( sum, () => { - console.log( registry.get( sum ) ); -} ); - -// This edits counter1, triggering a resolution of sumInstance which triggers the console.log above. -registry.set( counter1, 2 ); // now both counters equal 2 which means sum will print 4. -registry.set( counter1, 4 ); // prints 6 -``` - -### Async derived atoms - -Derived atoms can use async functions to compute their values. They can for instance trigger some REST API call and returns a promise. - -```js -const sum2 = createDerivedAtom( - async ( { get } ) => { - const val1 = await Promise.resolve( 10 ); - return val1 * get( counter ); - } -); -``` - -The value of async atoms will be equal to `null` until the resolution function finishes. - -### Bindings - -It is important to note that stan instance and registries are low-level APIs meant to be used by developers to build bindings for frameworks of their choice. In general, a higher-level API is preferred. - -Currently available bindings: - -- `@wordpress/data`: WordPress data users can continue to use their existing high-level APIs `useSelect`/`useDispatch` (selectors and actions) to access the atoms. The selectors are just high-level atoms that can rely on lower-level ones and the actions are just functions that trigger atom setters. The API for `@wordpress/data` store authors to bridge the gap is still experimental. - -## API Reference - -<!-- START TOKEN(Autogenerated API docs) --> - -<a name="createAtom" href="#createAtom">#</a> **createAtom** - -Creates a basic atom. - -_Parameters_ - -- _initialValue_ `T`: Initial value in the atom. \* -- _config_ `[WPCommonAtomConfig]`: Common Atom config. - -_Returns_ - -- `WPAtom<T>`: Created atom. - -<a name="createAtomRegistry" href="#createAtomRegistry">#</a> **createAtomRegistry** - -Creates a new Atom Registry. - -_Parameters_ - -- _onAdd_ `RegistryListener`: -- _onDelete_ `RegistryListener`: - -_Returns_ - -- `WPAtomRegistry`: Atom Registry. - -<a name="createAtomSelector" href="#createAtomSelector">#</a> **createAtomSelector** - -_Parameters_ - -- _resolver_ `WPAtomSelectorResolver<T>`: Atom resolver. -- _updater_ `WPAtomSelectorUpdater<T>`: Atom updater. -- _atomConfig_ `[WPCommonAtomConfig]`: Common Atom config. - -_Returns_ - -- (unknown type): Atom selector creator. - -<a name="createDerivedAtom" href="#createDerivedAtom">#</a> **createDerivedAtom** - -Creates a derived atom. - -_Parameters_ - -- _resolver_ `WPDerivedAtomResolver<T>`: Atom resolver. -- _updater_ `WPDerivedAtomUpdater<T>`: Atom updater. -- _config_ `[WPCommonAtomConfig]`: Common Atom config. - -_Returns_ - -- `WPAtom<T>`: Created atom. - -<a name="createStoreAtom" href="#createStoreAtom">#</a> **createStoreAtom** - -Creates a store atom. - -_Parameters_ - -- _subscribe_ (unknown type): Subscribe to state changes. -- _get_ (unknown type): Get the state value. -- _dispatch_ (unknown type): Dispatch store changes, -- _config_ `[WPCommonAtomConfig]`: Common Atom config. - -_Returns_ - -- `WPAtom<T>`: Store Atom. - -<a name="createStoreAtomSelector" href="#createStoreAtomSelector">#</a> **createStoreAtomSelector** - -_Parameters_ - -- _subscribe_ (unknown type): Subscribe to state changes. -- _get_ (unknown type): Get the state value. -- _dispatch_ (unknown type): Dispatch store changes, -- _atomConfig_ `[WPCommonAtomConfig]`: Common Atom config. - -_Returns_ - -- (unknown type): Atom selector creator. - - -<!-- END TOKEN(Autogenerated API docs) --> - -<br/><br/><p align="center"><img src="https://s.w.org/style/images/codeispoetry.png?1" alt="Code is Poetry." /></p> diff --git a/packages/stan/package.json b/packages/stan/package.json deleted file mode 100644 index f5150de878443c..00000000000000 --- a/packages/stan/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@wordpress/stan", - "version": "0.0.1", - "description": "WordPress state library based on atoms.", - "author": "The WordPress Contributors", - "license": "GPL-2.0-or-later", - "keywords": [ - "wordpress", - "gutenberg", - "state", - "stan", - "recoil" - ], - "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/stan/README.md", - "repository": { - "type": "git", - "url": "https://github.com/WordPress/gutenberg.git", - "directory": "packages/stan" - }, - "bugs": { - "url": "https://github.com/WordPress/gutenberg/issues" - }, - "main": "build/index.js", - "module": "build-module/index.js", - "react-native": "src/index", - "types": "build-types", - "sideEffects": false, - "dependencies": { - "@babel/runtime": "^7.11.2", - "@wordpress/priority-queue": "file:../priority-queue", - "equivalent-key-map": "^0.2.2", - "lodash": "^4.17.19" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/stan/src/atom.js b/packages/stan/src/atom.js deleted file mode 100644 index a4ecda1c874006..00000000000000 --- a/packages/stan/src/atom.js +++ /dev/null @@ -1,44 +0,0 @@ -/** @typedef {import('./types').WPCommonAtomConfig} WPCommonAtomConfig */ -/** - * @template T - * @typedef {import("./types").WPAtom<T>} WPAtom - */ - -/** - * Creates a basic atom. - * - * @template T - * @param {T} initialValue Initial value in the atom. * - * @param {WPCommonAtomConfig=} config Common Atom config. - * @return {WPAtom<T>} Created atom. - */ -export const createAtom = ( initialValue, config = {} ) => () => { - let value = initialValue; - - /** - * @type {Set<() => void>} - */ - const listeners = new Set(); - - return { - id: config.id, - type: 'root', - set( newValue ) { - value = newValue; - listeners.forEach( ( l ) => l() ); - }, - get() { - return value; - }, - async resolve() { - return value; - }, - subscribe( listener ) { - listeners.add( listener ); - return () => { - listeners.delete( listener ); - }; - }, - isResolved: true, - }; -}; diff --git a/packages/stan/src/derived.js b/packages/stan/src/derived.js deleted file mode 100644 index c678cf187f555a..00000000000000 --- a/packages/stan/src/derived.js +++ /dev/null @@ -1,203 +0,0 @@ -/** - * External dependencies - */ -import { noop } from 'lodash'; - -/** - * WordPress dependencies - */ -import { createQueue } from '@wordpress/priority-queue'; - -const resolveQueue = createQueue(); - -/** @typedef {import('./types').WPCommonAtomConfig} WPCommonAtomConfig */ -/** - * @template T - * @typedef {import("./types").WPDerivedAtomResolver<T>} WPDerivedAtomResolver - */ -/** - * @template T - * @typedef {import("./types").WPDerivedAtomUpdater<T>} WPDerivedAtomUpdater - */ -/** - * @template T - * @typedef {import("./types").WPAtom<T>} WPAtom - */ -/** - * @template T - * @typedef {import("./types").WPAtomState<T>} WPAtomState - */ - -/** - * Creates a derived atom. - * - * @template T - * @param {WPDerivedAtomResolver<T>} resolver Atom resolver. - * @param {WPDerivedAtomUpdater<T>} updater Atom updater. - * @param {WPCommonAtomConfig=} config Common Atom config. - * @return {WPAtom<T>} Created atom. - */ -export const createDerivedAtom = ( resolver, updater = noop, config = {} ) => ( - registry -) => { - /** - * @type {any} - */ - let value = null; - - /** - * @type {Set<() => void>} - */ - const listeners = new Set(); - - /** - * @type {(WPAtomState<any>)[]} - */ - let dependencies = []; - let isListening = false; - let isResolved = false; - const context = {}; - - const dependenciesUnsubscribeMap = new WeakMap(); - - const notifyListeners = () => { - listeners.forEach( ( l ) => l() ); - }; - - const refresh = () => { - if ( listeners.size > 0 ) { - if ( config.isAsync ) { - resolveQueue.add( context, resolve ); - } else { - resolve(); - } - } else { - isListening = false; - } - }; - - /** - * @param {WPAtomState<any>} atomState - */ - const addDependency = ( atomState ) => { - if ( ! dependenciesUnsubscribeMap.has( atomState ) ) { - dependenciesUnsubscribeMap.set( - atomState, - atomState.subscribe( refresh ) - ); - } - }; - - const resolve = () => { - /** - * @type {(WPAtomState<any>)[]} - */ - const updatedDependencies = []; - const updatedDependenciesMap = new WeakMap(); - let result; - const unresolved = []; - try { - result = resolver( { - get: ( atom ) => { - const atomState = registry.__unstableGetAtomState( atom ); - // It is important to add the dependency as soon as it's used - // because it's important to retrigger the resolution if the dependency - // changes before the resolution finishes. - addDependency( atomState ); - updatedDependenciesMap.set( atomState, true ); - updatedDependencies.push( atomState ); - const ret = atomState.get(); - if ( ! atomState.isResolved ) { - unresolved.push( atomState ); - } - return ret; - }, - } ); - } catch ( error ) { - if ( unresolved.length === 0 ) { - throw error; - } - } - - function removeExtraDependencies() { - const removedDependencies = dependencies.filter( - ( d ) => ! updatedDependenciesMap.has( d ) - ); - dependencies = updatedDependencies; - removedDependencies.forEach( ( d ) => { - const unsubscribe = dependenciesUnsubscribeMap.get( d ); - dependenciesUnsubscribeMap.delete( d ); - if ( unsubscribe ) { - unsubscribe(); - } - } ); - } - - /** - * @param {any} newValue - */ - function checkNewValue( newValue ) { - if ( unresolved.length === 0 ) { - isResolved = true; - } - - if ( unresolved.length === 0 && newValue !== value ) { - value = newValue; - notifyListeners(); - } - } - - if ( result instanceof Promise ) { - // Should make this promise cancelable. - result - .then( ( newValue ) => { - removeExtraDependencies(); - checkNewValue( newValue ); - } ) - .catch( ( error ) => { - if ( unresolved.length === 0 ) { - throw error; - } - removeExtraDependencies(); - } ); - } else { - removeExtraDependencies(); - checkNewValue( result ); - } - }; - - return { - id: config.id, - type: 'derived', - get() { - if ( ! isListening ) { - isListening = true; - resolve(); - } - return value; - }, - async set( action ) { - await updater( - { - get: ( atom ) => registry.get( atom ), - set: ( atom, arg ) => registry.set( atom, arg ), - }, - action - ); - }, - resolve, - subscribe( listener ) { - if ( ! isListening ) { - isListening = true; - resolve(); - } - listeners.add( listener ); - return () => { - listeners.delete( listener ); - }; - }, - get isResolved() { - return isResolved; - }, - }; -}; diff --git a/packages/stan/src/index.js b/packages/stan/src/index.js deleted file mode 100644 index e7f82fb62126e1..00000000000000 --- a/packages/stan/src/index.js +++ /dev/null @@ -1,25 +0,0 @@ -export { createAtom } from './atom'; -export { createDerivedAtom } from './derived'; -export { createAtomSelector } from './selector'; -export { createStoreAtom, createStoreAtomSelector } from './store'; -export { createAtomRegistry } from './registry'; - -/** - * @template T - * @typedef {import('./types').WPAtom<T>} WPAtom - */ -/** - * @template T - * @typedef {import('./types').WPAtomSelector<T>} WPAtomSelector - */ -/** - * @template T - * @typedef {import('./types').WPAtomResolver<T>} WPAtomResolver - */ -/** - * @typedef {import('./types').WPAtomRegistry} WPAtomRegistry - */ -/** - * @template T - * @typedef {import('./types').WPAtomUpdater<T>} WPAtomUpdater - */ diff --git a/packages/stan/src/registry.js b/packages/stan/src/registry.js deleted file mode 100644 index 3961e3c4f24fd8..00000000000000 --- a/packages/stan/src/registry.js +++ /dev/null @@ -1,151 +0,0 @@ -/** - * External dependencies - */ -import { noop, isObject } from 'lodash'; -// @ts-ignore -import EquivalentKeyMap from 'equivalent-key-map'; - -/** @typedef {import('./types').WPAtomRegistry} WPAtomRegistry */ -/** - * @template T - * @typedef {import("./types").WPAtomSelectorResolver<T>} WPAtomSelectorResolver - */ -/** - * @template T - * @typedef {import("./types").WPAtomState<T>} WPAtomState - */ -/** - * @template T - * @typedef {import("./types").WPAtom<T>} WPAtom - */ -/** - * @template T - * @typedef {import("./types").WPAtomSelector<T>} WPAtomSelector - */ -/** - * @template T - * @typedef {import("./types").WPAtomSelectorConfig<T>} WPAtomSelectorConfig - */ -/** - * @typedef {( atomState: import('./types').WPAtomState<any> ) => void} RegistryListener - */ - -/** - * @template T - * @param {WPAtom<any>|WPAtomSelector<T>} maybeAtomSelector - * @return {boolean} maybeAtomSelector Returns `true` when atom selector detected. - */ -export function isAtomSelector( maybeAtomSelector ) { - return ( - isObject( maybeAtomSelector ) && - /** @type {WPAtomSelector<any>} */ ( maybeAtomSelector ).type === - 'selector' - ); -} - -/** - * Creates a new Atom Registry. - * - * @param {RegistryListener} onAdd - * @param {RegistryListener} onDelete - * - * @return {WPAtomRegistry} Atom Registry. - */ -export const createAtomRegistry = ( onAdd = noop, onDelete = noop ) => { - const atoms = new WeakMap(); - const selectors = new WeakMap(); - - /** - * @template T - * @param {WPAtom<T>|WPAtomSelector<T>} atom Atom. - * @return {WPAtomState<T>} Atom state. - */ - const getAtomState = ( atom ) => { - if ( isAtomSelector( atom ) ) { - const { - config, - args, - } = /** @type {WPAtomSelector<any>} */ ( atom ); - return selectorRegistry.getAtomSelector( config, args ); - } - - if ( ! atoms.get( atom ) ) { - const atomState = /** @type {WPAtom<any>} */ ( atom )( registry ); - atoms.set( atom, atomState ); - onAdd( atomState ); - } - - return atoms.get( atom ); - }; - - const selectorRegistry = { - /** - * @template T - * @param {WPAtomSelectorConfig<T>} atomSelectorConfig - * @param {any[]} args - * @return {WPAtomState<T>} Atom state. - */ - getAtomSelector( atomSelectorConfig, args ) { - if ( ! selectors.get( atomSelectorConfig ) ) { - selectors.set( atomSelectorConfig, new EquivalentKeyMap() ); - } - - const selectorsMap = selectors.get( atomSelectorConfig ); - if ( ! selectorsMap.has( args ) ) { - const atomCreator = atomSelectorConfig.createAtom( ...args ); - selectorsMap.set( args, atomCreator( registry ) ); - } - - return selectorsMap.get( args ); - }, - - /** - * @template T - * @param {WPAtomSelectorConfig<T>} atomSelectorConfig - * @param {any[]} args - */ - deleteAtomSelector( atomSelectorConfig, args ) { - if ( - selectors.has( atomSelectorConfig ) && - selectors.get( atomSelectorConfig ).has( args ) - ) { - selectors.get( atomSelectorConfig ).delete( args ); - } - }, - }; - - /** @type {WPAtomRegistry} */ - const registry = { - __unstableGetAtomState: getAtomState, - - get( atom ) { - return getAtomState( atom ).get(); - }, - - set( atom, value ) { - return getAtomState( atom ).set( value ); - }, - - subscribe( atom, listener ) { - return getAtomState( atom ).subscribe( listener ); - }, - - // This shouldn't be necessary since we rely on week map - // But the legacy selectors/actions API requires us to know when - // some atoms are removed entirely to unsubscribe. - delete( atom ) { - if ( isAtomSelector( atom ) ) { - const { - config, - args, - } = /** @type {WPAtomSelector<any>} */ ( atom ); - return selectorRegistry.deleteAtomSelector( config, args ); - } - const atomState = atoms.get( atom ); - atoms.delete( atom ); - onDelete( atomState ); - }, - }; - - return registry; -}; diff --git a/packages/stan/src/selector.js b/packages/stan/src/selector.js deleted file mode 100644 index d7955db33e8bc2..00000000000000 --- a/packages/stan/src/selector.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Internal dependencies - */ -import { createDerivedAtom } from './derived'; - -/** @typedef {import('./types').WPCommonAtomConfig} WPCommonAtomConfig */ -/** - * @template T - * @typedef {import("./types").WPAtomSelectorResolver<T>} WPAtomSelectorResolver - */ -/** - * @template T - * @typedef {import("./types").WPAtomSelectorUpdater<T>} WPAtomSelectorUpdater - */ -/** - * @template T - * @typedef {import("./types").WPAtom<T>} WPAtom - */ -/** - * @template T - * @typedef {import("./types").WPAtomSelector<T>} WPAtomSelector - */ - -/** - * @template T - * @param {WPAtomSelectorResolver<T>} resolver Atom resolver. - * @param {WPAtomSelectorUpdater<T>} updater Atom updater. - * @param {WPCommonAtomConfig=} atomConfig Common Atom config. - * @return {(...args:any[]) => WPAtomSelector<T>} Atom selector creator. - */ -export const createAtomSelector = ( resolver, updater, atomConfig = {} ) => { - const config = { - /** - * @param {...any[]} args Selector arguments - * @return {WPAtom<any>} Atom. - */ - createAtom( ...args ) { - return createDerivedAtom( - resolver( ...args ), - updater ? updater( ...args ) : undefined, - { - ...atomConfig, - } - ); - }, - }; - - return ( ...args ) => { - return { - type: 'selector', - config, - args, - }; - }; -}; diff --git a/packages/stan/src/store.js b/packages/stan/src/store.js deleted file mode 100644 index 6fd74103122c23..00000000000000 --- a/packages/stan/src/store.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @template T - * @typedef {import("./types").WPAtom<T>} WPAtom - */ -/** - * @typedef {import("./types").WPCommonAtomConfig} WPCommonAtomConfig - */ -/** - * @template T - * @typedef {import("./types").WPAtomSelector<T>} WPAtomSelector - */ - -/** - * Creates a store atom. - * - * @template T - * @param {(listener: () => void) => (() => void)} subscribe Subscribe to state changes. - * @param {() => T} get Get the state value. - * @param {(action: any) => void} dispatch Dispatch store changes, - * @param {WPCommonAtomConfig=} config Common Atom config. - * @return {WPAtom<T>} Store Atom. - */ -export const createStoreAtom = ( - subscribe, - get, - dispatch, - config = {} -) => () => { - return { - id: config.id, - type: 'store', - get() { - return get(); - }, - set( action ) { - dispatch( action ); - }, - subscribe: ( l ) => { - return subscribe( l ); - }, - isResolved: true, - }; -}; - -/** - * @template T - * @param {(...args:any[]) => (listener: () => void) => (() => void)} subscribe Subscribe to state changes. - * @param {(...args:any[]) => () => T} get Get the state value. - * @param {(...args:any[]) => (action: any) => void} dispatch Dispatch store changes, - * @param {WPCommonAtomConfig=} atomConfig Common Atom config. - * @return {(...args:any[]) => WPAtomSelector<T>} Atom selector creator. - */ -export const createStoreAtomSelector = ( - subscribe, - get, - dispatch, - atomConfig -) => { - const config = { - /** - * @param {...any[]} args Selector arguments - * @return {WPAtom<any>} Atom. - */ - createAtom( ...args ) { - return createStoreAtom( - subscribe( ...args ), - get( ...args ), - dispatch( ...args ), - { - ...atomConfig, - } - ); - }, - }; - - return ( ...args ) => { - return { - type: 'selector', - config, - args, - }; - }; -}; diff --git a/packages/stan/src/test/atom.js b/packages/stan/src/test/atom.js deleted file mode 100644 index 74985576995fec..00000000000000 --- a/packages/stan/src/test/atom.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Internal dependencies - */ -import { createAtomRegistry, createAtom } from '../'; - -describe( 'atoms', () => { - it( 'should allow getting and setting atom values', () => { - const count = createAtom( 1 ); - const registry = createAtomRegistry(); - expect( registry.get( count ) ).toEqual( 1 ); - registry.set( count, 2 ); - expect( registry.get( count ) ).toEqual( 2 ); - } ); - - it( 'should allow subscribing to atom changes', () => { - const count = createAtom( 1 ); - const registry = createAtomRegistry(); - const listener = jest.fn(); - registry.subscribe( count, listener ); - expect( registry.get( count ) ).toEqual( 1 ); - registry.set( count, 2 ); - expect( listener ).toHaveBeenCalledTimes( 1 ); - expect( registry.get( count ) ).toEqual( 2 ); - registry.set( count, 3 ); - expect( listener ).toHaveBeenCalledTimes( 2 ); - expect( registry.get( count ) ).toEqual( 3 ); - } ); -} ); diff --git a/packages/stan/src/test/derived.js b/packages/stan/src/test/derived.js deleted file mode 100644 index fc2c2777c970d2..00000000000000 --- a/packages/stan/src/test/derived.js +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Internal dependencies - */ -import { createAtomRegistry, createAtom, createDerivedAtom } from '../'; - -async function flushImmediatesAndTicks( count = 1 ) { - for ( let i = 0; i < count; i++ ) { - await jest.runAllTicks(); - await jest.runAllImmediates(); - } -} - -jest.useFakeTimers(); - -describe( 'creating derived atoms', () => { - it( 'should allow creating derived atom', async () => { - const count1 = createAtom( 1 ); - const count2 = createAtom( 1 ); - const count3 = createAtom( 1 ); - const sum = createDerivedAtom( - ( { get } ) => get( count1 ) + get( count2 ) + get( count3 ) - ); - const registry = createAtomRegistry(); - - // Atoms don't compute any value unless there's a subscriber. - const unsubscribe = registry.subscribe( sum, () => {} ); - expect( registry.get( sum ) ).toEqual( 3 ); - registry.set( count1, 2 ); - expect( registry.get( sum ) ).toEqual( 4 ); - unsubscribe(); - } ); - - it( 'should allow async derived atoms', async () => { - const count1 = createAtom( 1 ); - const sum = createDerivedAtom( async ( { get } ) => { - const value = await Promise.resolve( 10 ); - return get( count1 ) + value; - } ); - const registry = createAtomRegistry(); - // Atoms don't compute any value unless there's a subscriber. - const unsubscribe = registry.subscribe( sum, () => {} ); - await flushImmediatesAndTicks(); - expect( registry.get( sum ) ).toEqual( 11 ); - unsubscribe(); - } ); - - it( 'should allow parallel async atoms', async () => { - const count1 = createDerivedAtom( async () => { - await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); - return 10; - } ); - const count2 = createDerivedAtom( async () => { - await new Promise( ( resolve ) => setTimeout( resolve, 1000 ) ); - return 10; - } ); - const sum = createDerivedAtom( async ( { get } ) => { - return get( count1 ) + get( count2 ); - } ); - const registry = createAtomRegistry(); - // Atoms don't compute any value unless there's a subscriber. - const unsubscribe = registry.subscribe( sum, () => {} ); - await jest.advanceTimersByTime( 1000 ); - await flushImmediatesAndTicks(); - expect( registry.get( sum ) ).toEqual( 20 ); - unsubscribe(); - } ); - - it( 'should allow nesting derived atoms', async () => { - const count1 = createAtom( 1 ); - const count2 = createAtom( 10 ); - const asyncCount = createDerivedAtom( async ( { get } ) => { - return ( await get( count2 ) ) * 2; - } ); - const sum = createDerivedAtom( async ( { get } ) => { - return get( count1 ) + get( asyncCount ); - } ); - const registry = createAtomRegistry(); - // Atoms don't compute any value unless there's a subscriber. - const unsubscribe = registry.subscribe( sum, () => {} ); - await flushImmediatesAndTicks( 2 ); - expect( registry.get( sum ) ).toEqual( 21 ); - unsubscribe(); - } ); - - it( 'should only compute derived atoms when they have subscribers or when you try to retrieve their value', () => { - const mock = jest.fn(); - mock.mockImplementation( () => 10 ); - const count1 = createAtom( 1 ); - const count2 = createAtom( 1 ); - const sum = createDerivedAtom( - ( { get } ) => get( count1 ) + get( count2 ) + mock() - ); - const registry = createAtomRegistry(); - // Creating an atom or adding it to the registry don't trigger its resolution - expect( mock ).not.toHaveBeenCalled(); - expect( registry.get( sum ) ).toEqual( 12 ); - // Calling "get" triggers a resolution. - expect( mock ).toHaveBeenCalledTimes( 1 ); - - // This shouldn't trigger the resolution because the atom has no listener. - registry.set( count1, 2 ); - expect( mock ).toHaveBeenCalledTimes( 1 ); - - // Subscribing triggers the resolution again. - const unsubscribe = registry.subscribe( sum, () => {} ); - expect( mock ).toHaveBeenCalledTimes( 2 ); - expect( registry.get( sum ) ).toEqual( 13 ); - unsubscribe(); - } ); - - it( 'should notify subscribers on change', () => { - const count1 = createAtom( 1 ); - const count2 = createAtom( 1 ); - const sum = createDerivedAtom( - ( { get } ) => get( count1 ) + get( count2 ) - ); - const registry = createAtomRegistry(); - const listener = jest.fn(); - const unsubscribe = registry.subscribe( sum, listener ); - - registry.set( count1, 2 ); - expect( listener ).toHaveBeenCalledTimes( 1 ); - - registry.set( count2, 2 ); - expect( listener ).toHaveBeenCalledTimes( 2 ); - - unsubscribe(); - } ); -} ); - -describe( 'updating derived atoms', () => { - it( 'should allow derived atoms to update dependencies', () => { - const count1 = createAtom( 1 ); - const count2 = createAtom( 1 ); - const sum = createDerivedAtom( - ( { get } ) => get( count1 ) + get( count2 ), - ( { set }, value ) => { - set( count1, value / 2 ); - set( count2, value / 2 ); - } - ); - const registry = createAtomRegistry(); - registry.set( sum, 4 ); - expect( registry.get( count1 ) ).toEqual( 2 ); - expect( registry.get( count2 ) ).toEqual( 2 ); - } ); - - it( 'should allow nested derived atoms to update dependencies', () => { - const count1 = createAtom( 1 ); - const count2 = createAtom( 1 ); - const sum = createDerivedAtom( - ( { get } ) => get( count1 ) + get( count2 ), - ( { set }, value ) => { - set( count1, value / 2 ); - set( count2, value / 2 ); - } - ); - const multiply = createDerivedAtom( - ( { get } ) => get( sum ) * 3, - ( { set }, value ) => { - set( sum, value / 3 ); - } - ); - const registry = createAtomRegistry(); - registry.set( multiply, 18 ); - expect( registry.get( count1 ) ).toEqual( 3 ); - expect( registry.get( count2 ) ).toEqual( 3 ); - } ); -} ); diff --git a/packages/stan/src/test/selector.js b/packages/stan/src/test/selector.js deleted file mode 100644 index 3483e02f56f052..00000000000000 --- a/packages/stan/src/test/selector.js +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Internal dependencies - */ -import { createAtomRegistry, createAtom, createAtomSelector } from '../'; - -describe( 'creating and subscribing to atom selectoors', () => { - it( 'should allow adding and removing items into/from selectors', () => { - const itemsByIdAtom = createAtom( {} ); - const itemSelector = createAtomSelector( ( key ) => ( { get } ) => - get( itemsByIdAtom )[ key ] - ); - - const registry = createAtomRegistry(); - // Retrieve atom selector - const firstItem = registry.__unstableGetAtomState( itemSelector( 1 ) ); - expect( firstItem ).toBe( - registry.__unstableGetAtomState( itemSelector( 1 ) ) - ); - // Atoms don't compute any value unless there's a subscriber. - const unsubscribe = registry.subscribe( itemSelector( 1 ), () => {} ); - expect( firstItem.get() ).toBe( undefined ); - - // Add some items - registry.set( itemsByIdAtom, { - 1: { name: 'first' }, - 2: { name: 'second' }, - } ); - - // Should update the value automatically as we set the items. - expect( registry.get( itemSelector( 1 ) ) ).toEqual( { - name: 'first', - } ); - - // Remove items - registry.set( itemsByIdAtom, { - 2: { name: 'second' }, - } ); - - // Should update the value automatically as we unset the items. - expect( registry.get( itemSelector( 1 ) ) ).toBe( undefined ); - unsubscribe(); - } ); - - it( 'should allow creating selectors based on other selectors', () => { - const itemsByIdAtom = createAtom( {} ); - const itemSelector = createAtomSelector( ( key ) => ( { get } ) => { - return get( itemsByIdAtom )[ key ]; - } ); - // Atom selector that depends on another atom selector. - const itemNameSelector = createAtomSelector( ( key ) => ( { get } ) => { - return get( itemSelector( key ) )?.name; - } ); - - const registry = createAtomRegistry(); - registry.set( itemsByIdAtom, { - 1: { name: 'first' }, - 2: { name: 'second' }, - } ); - - // Atoms don't compute any value unless there's a subscriber. - const unsubscribe = registry.subscribe( - itemNameSelector( 1 ), - () => {} - ); - expect( registry.get( itemNameSelector( 1 ) ) ).toEqual( 'first' ); - unsubscribe(); - } ); - - it( 'should allow creating multi argument atom selectors', () => { - const itemsByIdAtom = createAtom( {} ); - const itemPropertySelector = createAtomSelector( - ( key, property ) => ( { get } ) => { - return get( itemsByIdAtom )[ key ][ property ]; - } - ); - - const registry = createAtomRegistry(); - registry.set( itemsByIdAtom, { - 1: { name: 'first' }, - 2: { name: 'second' }, - } ); - - // Retrieve atom selector - const firstItemName = registry.__unstableGetAtomState( - itemPropertySelector( 1, 'name' ) - ); - expect( firstItemName ).toBe( - registry.__unstableGetAtomState( itemPropertySelector( 1, 'name' ) ) - ); - - // Atoms don't compute any value unless there's a subscriber. - const unsubscribe = registry.subscribe( - itemPropertySelector( 1, 'name' ), - () => {} - ); - expect( registry.get( itemPropertySelector( 1, 'name' ) ) ).toEqual( - 'first' - ); - registry.set( itemsByIdAtom, { - 1: { name: 'first modified' }, - 2: { name: 'second' }, - } ); - expect( registry.get( itemPropertySelector( 1, 'name' ) ) ).toEqual( - 'first modified' - ); - unsubscribe(); - } ); - - it( 'should not recompute a selector dependency if its untouched', () => { - const itemsByIdAtom = createAtom( {} ); - const itemSelector = createAtomSelector( ( key ) => ( { get } ) => { - return get( itemsByIdAtom )[ key ]; - } ); - const itemNameSelector = createAtomSelector( ( key ) => ( { get } ) => { - return get( itemSelector( key ) )?.name; - } ); - - const registry = createAtomRegistry(); - const initialItems = { - 1: { name: 'first' }, - 2: { name: 'second' }, - }; - registry.set( itemsByIdAtom, initialItems ); - - const name1Listener = jest.fn(); - const name2Listener = jest.fn(); - - const name1 = itemNameSelector( 1 ); - const name2 = itemNameSelector( 2 ); - - const unsubscribe = registry.subscribe( name1, name1Listener ); - const unsubscribe2 = registry.subscribe( name2, name2Listener ); - - // If I update item 1, item 2 dedendencies shouldn't recompute. - registry.set( itemsByIdAtom, { - ...initialItems, - 1: { name: 'updated first' }, - } ); - - expect( registry.get( name1 ) ).toEqual( 'updated first' ); - expect( registry.get( name2 ) ).toEqual( 'second' ); - expect( name1Listener ).toHaveBeenCalledTimes( 1 ); - expect( name2Listener ).not.toHaveBeenCalled(); - - unsubscribe(); - unsubscribe2(); - } ); -} ); - -describe( 'updating atom selectors', () => { - it( 'should allow atom selectors to update dependencies', () => { - const itemsByIdAtom = createAtom( {} ); - const itemSelector = createAtomSelector( - ( key ) => ( { get } ) => get( itemsByIdAtom )[ key ], - ( key ) => ( { get, set }, value ) => { - set( itemsByIdAtom, { - ...get( itemsByIdAtom ), - [ key ]: value, - } ); - } - ); - const registry = createAtomRegistry(); - registry.set( itemSelector( 1 ), { name: 'first' } ); - expect( registry.get( itemsByIdAtom ) ).toEqual( { - 1: { name: 'first' }, - } ); - } ); - - it( 'should allow updating nested atom selectors', () => { - const itemsByIdAtom = createAtom( {} ); - const itemSelector = createAtomSelector( - ( key ) => ( { get } ) => get( itemsByIdAtom )[ key ], - ( key ) => ( { get, set }, value ) => { - set( itemsByIdAtom, { - ...get( itemsByIdAtom ), - [ key ]: value, - } ); - } - ); - const itemNameSelector = createAtomSelector( - ( key ) => ( { get } ) => get( itemSelector( key ) ).name, - ( key ) => ( { get, set }, value ) => { - set( itemSelector( key ), { - ...get( itemSelector( key ) ), - name: value, - } ); - } - ); - const registry = createAtomRegistry(); - registry.set( itemNameSelector( 1 ), 'first' ); - expect( registry.get( itemsByIdAtom ) ).toEqual( { - 1: { name: 'first' }, - } ); - } ); -} ); diff --git a/packages/stan/src/types.ts b/packages/stan/src/types.ts deleted file mode 100644 index 66566995900226..00000000000000 --- a/packages/stan/src/types.ts +++ /dev/null @@ -1,130 +0,0 @@ -export type WPAtomListener = () => void; - -export interface WPCommonAtomConfig { - /** - * Optinal id used for debug. - */ - id?: string; - - /** - * Whether the atom is sync or async. - */ - isAsync?: boolean; -} - -export interface WPAtomState< T > { - /** - * Optional atom id used for debug. - */ - id?: string; - - /** - * Atom type. - */ - type: string; - - /** - * Whether the atom state value is resolved or not. - */ - readonly isResolved: boolean; - - /** - * Atom state setter, used to modify one or multiple atom values. - */ - set: ( t: any ) => void; - - /** - * Retrieves the current value of the atom state. - */ - get: () => T; - - /** - * Subscribes to the value changes of the atom state. - */ - subscribe: ( listener: WPAtomListener ) => () => void; -} - -export type WPAtom< T > = ( registry: WPAtomRegistry ) => WPAtomState< T >; - -export interface WPAtomSelectorConfig< T > { - /** - * Creates an atom for the given key - */ - createAtom: ( ...args: any[] ) => WPAtom< T >; -} - -export interface WPAtomSelector< T > { - /** - * Type which value is "selector" to indicate that this is a selector. - */ - type: string; - - /** - * Selector config used for this item. - */ - config: WPAtomSelectorConfig< T >; - - /** - * Selector args - */ - args: any[]; -} - -export interface WPAtomRegistry { - /** - * Reads an atom vale. - */ - get: < T >( atom: WPAtom< T > | WPAtomSelector< T > ) => T; - - /** - * Update an atom value. - */ - set: < T >( atom: WPAtom< T > | WPAtomSelector< T >, value: any ) => void; - - /** - * Retrieves or creates an atom from the registry. - */ - subscribe: < T >( - atom: WPAtom< T > | WPAtomSelector< T >, - listener: WPAtomListener - ) => () => void; - - /** - * Removes an atom from the registry. - */ - delete: < T >( atom: WPAtom< T > | WPAtomSelector< T > ) => void; - - /** - * Retrieves the atom state for a given atom. - * This shouldn't be used directly, prefer the other methods. - */ - __unstableGetAtomState: < T >( - atom: WPAtom< T > | WPAtomSelector< T > - ) => WPAtomState< any >; -} - -export type WPAtomResolver< T > = ( - atom: WPAtom< T > | WPAtomSelector< T > -) => T; - -export type WPAtomUpdater< T > = ( - atom: WPAtom< T > | WPAtomSelector< T >, - value: any -) => void; - -export type WPDerivedAtomResolver< T > = ( props: { - get: WPAtomResolver< T >; -} ) => T; - -export type WPDerivedAtomUpdater< T > = ( - props: { get: WPAtomResolver< T >; set: WPAtomUpdater< T > }, - value: any -) => void; - -export type WPAtomSelectorResolver< T > = ( - ...args: any[] -) => WPDerivedAtomResolver< T >; - -export type WPAtomSelectorUpdater< T > = ( - ...args: any[] -) => WPDerivedAtomUpdater< T >; diff --git a/packages/stan/tsconfig.json b/packages/stan/tsconfig.json deleted file mode 100644 index 397b9f48383d59..00000000000000 --- a/packages/stan/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": "src", - "declarationDir": "build-types" - }, - "include": [ "src/**/*" ], - "references": [ - { "path": "../priority-queue" } - ] -} diff --git a/packages/stan/.npmrc b/packages/stylelint-config/.npmrc similarity index 100% rename from packages/stan/.npmrc rename to packages/stylelint-config/.npmrc diff --git a/packages/stylelint-config/CHANGELOG.md b/packages/stylelint-config/CHANGELOG.md new file mode 100644 index 00000000000000..7c2e4e55926745 --- /dev/null +++ b/packages/stylelint-config/CHANGELOG.md @@ -0,0 +1,250 @@ +<!-- Learn how to maintain this file at https://github.com/WordPress/gutenberg/tree/master/packages#maintaining-changelogs. --> + +## Unreleased + +### Breaking Change + +- Removed stylelint `^10.1.0`, `^11.0.0`, and `^12.0.0` as peer dependency. + +### Internal + +- Imported from `WordPress-Coding-Standards/stylelint-config-wordpress` repository to `WordPress/gutenberg` ([#22777](https://github.com/WordPress/gutenberg/pull/22777)) + +## 17.0.0 (2020-05-31) + +- Updated: `stylelint-scss` to `3.17.2`. +- Updated: `stylelint` to `13.0.0`. + +## 16.0.0 (2019-12-31) + +- Fixed: `selector-class-pattern` rule regex to account for numerals, case detection, and ensure kebab-case over snake_case. +- Fixed: `selector-id-pattern` rule regex to account for numerals, case detection, and ensure kebab-case over snake_case. +- Updated: `stylelint-config-recommended-scss` to `4.1.0`. +- Updated: `stylelint-find-rules` to `2.2.0`. +- Updated: `stylelint-scss` to `3.13.0`. +- Updated: `stylelint` to `11.0.0`. + +## 15.0.0 (2019-10-05) + +- Added: NodeJS 12.x.x support. +- Updated: `stylelint` to `11.0.0`. +- Removed: `stylelint < 10.1.0` compatibility. +- Updated: `stylelint-config-recommended` to `3.0.0`. +- Updated: `stylelint-config-recommended-scss` to `4.0.0`. +- Updated: Bump minimum Node.js required version to `10.0.0`. + +## 14.0.0 (2019-04-18) + +- Updated: `stylelint` to `10.0.0`. +- Updated: `stylelint-scss` to `3.6.0`. +- Updated: `stylelint-config-recommended` to `2.2.0`. + +## 13.1.0 (2018-08-19) + +- Added: Added SCSS _shared config_ `extends` tests. +- Changed: `stylelint-config-wordpress/scss` now extends [`stylelint-config-recommended-scss`](https://github.com/kristerkari/stylelint-config-recommended-scss) (the net result of this change results in no rule changes for this SCSS config). +- Updated: `stylelint-scss` to `3.3.0`. +- Updated: `stylelint` to `9.5.0`. + +## 13.0.0 (2018-03-19) + +- Added: stylelint `9.1.3` support. +- Changed: Updated `stylelint` peer dependency version to `^9.1.3`. +- Changed: Improved `no-duplicate-selectors` tests. +- Removed: Jest snapshots. +- Removed: `stylelint < 9.1.3` compatibility. +- Updated: `selector-pseudo-element-colon-notation` to use `double` +- Updated: `stylelint-config-recommended` to `2.1.0`. +- Updated: `stylelint-scss` to `2.1.0`. +- Updated: Bump minimum Node.js required version to `8.9.3`. + +## 12.0.0 (2017-07-18) + +- Changed: `stylelint-config-wordpress` now extends [`stylelint-config-recommended`](https://github.com/stylelint/stylelint-config-recommended), which turns on the `at-rule-no-unknown`, `block-no-empty`, `comment-no-empty`, `declaration-block-no-ignored-properties`, `declaration-block-no-redundant-longhand-properties`, `font-family-no-duplicate-names`, `media-feature-name-no-unknown`, `no-empty-source` rule. These rules are part of stylelint's [possible errors](https://github.com/stylelint/stylelint/blob/master/docs/user-guide/rules.md#possible-errors) rules. +- Removed: `stylelint-scss < 1.5.1` compatibility. +- Removed: Removed style guide docs. +- Removed: `at-rule-no-unknown` custom `ignoreAtRules` options in `stylelint-config-wordpress/scss` shared config. +- Added: `scss/at-rule-no-unknown` rule in `stylelint-config-wordpress/scss` shared config. +- Added: NodeJS 8.x.x support. +- Added: npm 5.x.x support. +- Added: Jest snapshots to help detect and prevent regressions. + +## 11.0.0 (2017-05-16) + +- Added: `declaration-property-unit-whitelist` rule to allow `px` and exclude `%` and `em` units in `line-height` values. +- Changed: Relocated repo to https://github.com/WordPress-Coding-Standards. +- Fixed: Include CSS config `at-rule-empty-line-before` rules in SCSS config. + +## 10.0.2 (2017-04-29) + +- Added: Added `import` to `ignoreAtRules` option in `at-rule-empty-line-before` rule for SCSS config. + +## 10.0.1 (2017-04-21) + +- Removed: `rule-non-nested-empty-line-before` rule from SCSS config. This rule is deprecated in stylelint v8, the new `rule-empty-line-before` rule already exists in the primary config. + +## 10.0.0 (2017-04-21) + +- Added: `scss/selector-no-redundant-nesting-selector` rule in `stylelint-config-wordpress/scss` shared config. +- Added: `selector-no-empty` rule. +- Added: NodeJS 7.x.x support +- Fixed: Added `stylelint-scss` plugin @if/@else placement rules. +- Fixed: Ignore `relative` keyword names in `font-weight-notation` rule. +- Fixed: Ignore proprietary `DXImageTransform.Microsoft` MS filters +- Fixed: Removed `@debug` from `ignoreAtRules` array of `at-rule-no-unknown` rule in `stylelint-config-wordpress/scss` chared config. +- Deprecated `blockless-group` option for `at-rule-empty-line-before` rule. Use the new `blockless-after-blockless` option instead. +- Deprecated `media-feature-no-missing-punctuation` rule. +- Deprecated `rule-nested-empty-line-before` and `rule-non-nested-empty-line-before` rules. Use the new `rule-empty-line-before` rule instead. +- Deprecated `selector-no-empty` rule. +- Refactor: Switch from AVA to Jest for tests. +- Refactor: Switch from eslint-plugin-ava to eslint-plugin-jest. +- Removed: `stylelint < 7.10.1` compatibility. +- Removed: `stylelint-scss < 1.4.4` compatibility. +- Removed: NodeJS 4.x support, `stylelint` and `stylelint-config-wordpress` now require NodeJS > 6.9.1 LTS or greater + +## 9.1.1 (2016-09-30) + +- Fixed: Re-releasing failed npmjs.com 9.1.0 release as 9.1.1. + +## 9.1.0 (2016-09-30) + +- Added: `stylelint-config-wordpress/scss` preset. + +## 9.0.0 (2016-09-10) + +- Removed: `stylelint < 7.2.0` compatibility. +- Removed: NodeJS 0.12.x support, `stylelint` and `stylelint-config-wordpress` now require NodeJS > 4.2.1 LTS or greater +- Added: `at-rule-no-unknown` rule. +- Added: `selector-attribute-quotes` rule. +- Added: `font-weight-notation` rule. +- Added: `max-line-length` rule. +- Added: `property-no-unknown` rule. +- Added: `selector-class-pattern` rule. +- Added: `selector-id-pattern` rule. +- Deprecated `no-missing-eof-newline` rule. Use the new `no-missing-end-of-source-newline` rule instead. +- Fixed `font-family-name-quotes` test warning message in `values.js`. + +## 8.0.0 (2016-06-14) + +- Removed: `stylelint < 6.6.0` compatibility. +- Removed: `number-zero-length-no-unit` rule. +- Added: `length-zero-no-unit` rule. +- Added: `value-keyword-case` rule. + +## 7.1.1 (2016-05-30) + +- Fixed: Re-releasing failed npmjs.com 7.0.0 release as 7.1.1. + +## 7.1.0 (2016-05-30) + +- Fixed: `font-family-name-quotes` rule deprecated option `double-where-recommended` to new `always-where-recommended` option. +- Fixed: `function-url-quotes` rule deprecated option `none` to new `never` option. +- Removed: `stylelint < 6.5.1` compatibility. +- Changed: Improved tests and documentation. +- Added: `comment-empty-line-before` rule. + +## 7.0.0 (2016-05-20) + +- Added: `keyframe-declaration-no-important` rule. +- Added: `selector-pseudo-class-no-unknown` rule. +- Added: `selector-pseudo-element-no-unknown` rule. +- Added: `selector-type-no-unknown` rule. + +## 6.0.0 (2016-05-17) + +- Added: `at-rule-name-space-after` rule. +- Added: `no-extra-semicolons` rule. +- Added: `selector-attribute-operator-space-after` rule. +- Added: `selector-attribute-operator-space-before` rule. +- Added: `selector-max-empty-liness` rule. + +## 5.0.0 (2016-04-24) + +- Added: `at-rule-name-case` rule. +- Added: `declaration-block-no-duplicate-properties` rule. +- Added: `function-max-empty-lines` rule. +- Added: `function-name-case` rule. +- Added: `property-case` rule. +- Added: `selector-attribute-brackets-space-inside` rule. +- Added: `selector-pseudo-class-case` rule. +- Added: `selector-pseudo-class-parentheses-space-inside` rule. +- Added: `selector-pseudo-element-case` rule. +- Added: `shorthand-property-no-redundant-values` rule. +- Added: `unit-case` rule. +- Added: `unit-no-unknown` rule. + +## 4.0.0 (2016-03-25) + +- Removed: `stylelint < 5.2.0` compatibility. +- Added: `at-rule-semicolon-newline-after` rule. +- Added: `selector-type-case` rule. + +## 3.0.1 (2016-03-10) + +- Added: `stylelint` version `^4.5.0` as a peer dependency to `peerDependencies` in `package.json` + +## 3.0.0 (2016-03-08) + +- Removed: `stylelint < 4.5.0` compatibility. +- Deprecated: `rule-no-shorthand-property-overrides` rule. Use the new `declaration-block-no-shorthand-property-overrides` rule instead. +- Deprecated: `rule-trailing-semicolon` rule. Use the new `declaration-block-trailing-semicolon` rule instead. +- Added: `color-named` rule. +- Added: `declaration-block-no-shorthand-property-overrides` rule. +- Added: `declaration-block-trailing-semicolon` rule. +- Added: `string-no-newline` rule. + +## 2.1.0 (2016-03-03) + +- Added: `max-empty-lines` rule, limits the number of adjacent empty lines to 2. +- Changed: `rule-nested-empty-line-before` rule option `ignore: ["after-comment"]`. +- Removed all vendor prefixes, lets autoprefixer handle vendor prefixes: + - Removed: `at-rule-no-vendor-prefix` + - Removed: `media-feature-name-no-vendor-prefix` + - Removed: `property-no-vendor-prefix` + - Removed: `selector-no-vendor-prefix` + - Removed: `value-no-vendor-prefix` + +## 2.0.2 (2016-02-17) + +- Fixed another npmjs.com release issue + +## 2.0.1 (2016-02-17) + +- Fixed npmjs.com release + +## 2.0.0 (2016-02-17) + +- Removed: `media-query-parentheses-space-inside` rule. +- Removed: `stylelint < 4.3.4` compatibility. +- Added: `font-family-name-quotes` rule with double quotes where recommended option. +- Added: `media-feature-no-missing-punctuation` rule. +- Added: `no-invalid-double-slash-comments` rule. + +## 1.1.1 (2016-01-19) + +- Changed: `rule-non-nested-empty-line-before` with option `ignore: ["after-comment"],`. + +## 1.1.0 (2016-01-18) + +- Added: `selector-pseudo-element-colon-notation` with option `single` + +## 1.0.1 (2015-12-11) + +- Changed: config syntax. + +## 1.0.0 (2015-12-11) + +- Removed: `stylelint < 3.0.0` compatibility. +- Changed: renamed the `function-space-after` rule to `function-whitespace-after`. +- Changed: `at-rule-empty-line-before` with option `ignore: ["after-comment"],`. +- Changed: `declaration-colon-space-after` with option `always-single-line`. +- Added: `declaration-colon-newline-after` with option `always-multi-line`. +- Added: `function-linear-gradient-no-nonstandard-direction`. + +## 0.2.0 (2015-09-04) + +- Fixed: No quotes for URLs -> `"function-url-quotes": [ 2, "none" ]`. + +## 0.1.0 (2015-08-01) + +Initial release. diff --git a/packages/stylelint-config/LICENSE b/packages/stylelint-config/LICENSE new file mode 100644 index 00000000000000..8673b137beef47 --- /dev/null +++ b/packages/stylelint-config/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 stylelint + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/packages/stylelint-config/README.md b/packages/stylelint-config/README.md new file mode 100644 index 00000000000000..0989d54a20d08f --- /dev/null +++ b/packages/stylelint-config/README.md @@ -0,0 +1,57 @@ +# stylelint config + +[stylelint](https://stylelint.io/) configuration rules to ensure your CSS is compliant with the [WordPress CSS Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/css/). + +## Installation + +```bash +$ npm install @wordpress/stylelint-config --save-dev +``` + +## Usage + +If you've installed `@wordpress/stylelint-config` locally within your project, just set your `stylelint` config to: + +```json +{ + "extends": "@wordpress/stylelint-config" +} +``` + +If you've globally installed `@wordpress/stylelint-config` using the `-g` flag, then you'll need to use the absolute path to `@wordpress/stylelint-config` in your config: + +```json +{ + "extends": "/absolute/path/to/@wordpress/stylelint-config" +} +``` + +## Presets + +In addition to the default preset, there is also a SCSS preset. This preset extends both `@wordpress/stylelint-config` and [`stylelint-config-recommended-scss`](https://github.com/kristerkari/stylelint-config-recommended-scss). + +### SCSS + +```json +{ + "extends": [ "@wordpress/stylelint-config/scss" ] +} +``` + +## Extending the config + +Simply add a `"rules"` key to your config and add your overrides there. + +For example, to change the `indentation` to four spaces and turn off the `number-leading-zero` rule: + +```json +{ + "extends": "@wordpress/stylelint-config", + "rules": { + "indentation": 4, + "number-leading-zero": null + } +} +``` + +<br/><br/><p align="center"><img src="https://s.w.org/style/images/codeispoetry.png?1" alt="Code is Poetry." /></p> diff --git a/packages/stylelint-config/index.js b/packages/stylelint-config/index.js new file mode 100644 index 00000000000000..490ab35d5b1312 --- /dev/null +++ b/packages/stylelint-config/index.js @@ -0,0 +1,131 @@ +'use strict'; + +module.exports = { + extends: 'stylelint-config-recommended', + rules: { + 'at-rule-empty-line-before': [ + 'always', + { + except: [ 'blockless-after-blockless' ], + ignore: [ 'after-comment' ], + }, + ], + 'at-rule-name-case': 'lower', + 'at-rule-name-space-after': 'always-single-line', + 'at-rule-no-unknown': true, + 'at-rule-semicolon-newline-after': 'always', + 'block-closing-brace-newline-after': 'always', + 'block-closing-brace-newline-before': 'always', + 'block-opening-brace-newline-after': 'always', + 'block-opening-brace-space-before': 'always', + 'color-hex-case': 'lower', + 'color-hex-length': 'short', + 'color-named': 'never', + 'comment-empty-line-before': [ + 'always', + { + ignore: [ 'stylelint-commands' ], + }, + ], + 'declaration-bang-space-after': 'never', + 'declaration-bang-space-before': 'always', + 'declaration-block-no-duplicate-properties': [ + true, + { + ignore: [ 'consecutive-duplicates' ], + }, + ], + 'declaration-block-semicolon-newline-after': 'always', + 'declaration-block-semicolon-space-before': 'never', + 'declaration-block-trailing-semicolon': 'always', + 'declaration-colon-newline-after': 'always-multi-line', + 'declaration-colon-space-after': 'always-single-line', + 'declaration-colon-space-before': 'never', + 'declaration-property-unit-whitelist': { + 'line-height': [ 'px' ], + }, + 'font-family-name-quotes': 'always-where-recommended', + 'font-weight-notation': [ + 'numeric', + { + ignore: [ 'relative' ], + }, + ], + 'function-comma-space-after': 'always', + 'function-comma-space-before': 'never', + 'function-max-empty-lines': 1, + 'function-name-case': [ + 'lower', + { + ignoreFunctions: [ '/^DXImageTransform.Microsoft.*$/' ], + }, + ], + 'function-parentheses-space-inside': 'never', + 'function-url-quotes': 'never', + 'function-whitespace-after': 'always', + indentation: 'tab', + 'length-zero-no-unit': true, + 'max-empty-lines': 2, + 'max-line-length': [ + 80, + { + ignore: 'non-comments', + ignorePattern: [ + '/(https?://[0-9,a-z]*.*)|(^description\\:.+)|(^tags\\:.+)/i', + ], + }, + ], + 'media-feature-colon-space-after': 'always', + 'media-feature-colon-space-before': 'never', + 'media-feature-range-operator-space-after': 'always', + 'media-feature-range-operator-space-before': 'always', + 'media-query-list-comma-newline-after': 'always-multi-line', + 'media-query-list-comma-space-after': 'always-single-line', + 'media-query-list-comma-space-before': 'never', + 'no-eol-whitespace': true, + 'no-missing-end-of-source-newline': true, + 'number-leading-zero': 'always', + 'number-no-trailing-zeros': true, + 'property-case': 'lower', + 'rule-empty-line-before': [ + 'always', + { + ignore: [ 'after-comment' ], + }, + ], + 'selector-attribute-brackets-space-inside': 'never', + 'selector-attribute-operator-space-after': 'never', + 'selector-attribute-operator-space-before': 'never', + 'selector-attribute-quotes': 'always', + 'selector-class-pattern': [ + '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', + { + message: + 'Selector should use lowercase and separate words with hyphens (selector-class-pattern)', + }, + ], + 'selector-id-pattern': [ + '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$', + { + message: + 'Selector should use lowercase and separate words with hyphens (selector-id-pattern)', + }, + ], + 'selector-combinator-space-after': 'always', + 'selector-combinator-space-before': 'always', + 'selector-list-comma-newline-after': 'always', + 'selector-list-comma-space-before': 'never', + 'selector-max-empty-lines': 0, + 'selector-pseudo-class-case': 'lower', + 'selector-pseudo-class-parentheses-space-inside': 'never', + 'selector-pseudo-element-case': 'lower', + 'selector-pseudo-element-colon-notation': 'double', + 'selector-type-case': 'lower', + 'string-quotes': 'double', + 'unit-case': 'lower', + 'value-keyword-case': 'lower', + 'value-list-comma-newline-after': 'always-multi-line', + 'value-list-comma-space-after': 'always-single-line', + 'value-list-comma-space-before': 'never', + }, +}; diff --git a/packages/stylelint-config/package.json b/packages/stylelint-config/package.json new file mode 100644 index 00000000000000..8e7a90f8de2c16 --- /dev/null +++ b/packages/stylelint-config/package.json @@ -0,0 +1,52 @@ +{ + "name": "@wordpress/stylelint-config", + "version": "17.0.0", + "description": "stylelint config for WordPress development.", + "author": "The WordPress Contributors", + "license": "MIT", + "keywords": [ + "stylelint", + "stylelint-config", + "stylelint-config-wordpress", + "wordpress" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/stylelint-config/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/stylelint-config" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "files": [ + "CHANGELOG.md", + "LICENSE", + "README.md", + "index.js", + "scss.js" + ], + "main": "index.js", + "dependencies": { + "stylelint-config-recommended": "^3.0.0", + "stylelint-config-recommended-scss": "^4.2.0", + "stylelint-scss": "^3.17.2" + }, + "peerDependencies": { + "stylelint": "^13.0.0" + }, + "npmpackagejsonlint": { + "extends": "@wordpress/npm-package-json-lint-config", + "rules": { + "valid-values-license": [ + "error", + [ + "MIT" + ] + ] + } + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/stylelint-config/scss.js b/packages/stylelint-config/scss.js new file mode 100644 index 00000000000000..09940642b218b1 --- /dev/null +++ b/packages/stylelint-config/scss.js @@ -0,0 +1,36 @@ +'use strict'; + +module.exports = { + extends: [ './', 'stylelint-config-recommended-scss' ].map( + require.resolve + ), + + plugins: [ 'stylelint-scss' ], + + rules: { + // @wordpress/stylelint-config CSS overrides. + 'at-rule-empty-line-before': [ + 'always', + { + except: [ 'blockless-after-blockless' ], + ignore: [ 'after-comment' ], + ignoreAtRules: [ 'else' ], + }, + ], + + 'block-opening-brace-space-before': 'always', + 'block-closing-brace-newline-after': [ + 'always', + { + ignoreAtRules: [ 'if', 'else' ], + }, + ], + 'at-rule-name-space-after': 'always', + 'scss/at-else-closing-brace-newline-after': 'always-last-in-chain', + 'scss/at-else-closing-brace-space-after': 'always-intermediate', + 'scss/at-else-empty-line-before': 'never', + 'scss/at-if-closing-brace-newline-after': 'always-last-in-chain', + 'scss/at-if-closing-brace-space-after': 'always-intermediate', + 'scss/selector-no-redundant-nesting-selector': true, + }, +}; diff --git a/packages/stylelint-config/test/.stylelintrc.json b/packages/stylelint-config/test/.stylelintrc.json new file mode 100644 index 00000000000000..99a3abd90cbccb --- /dev/null +++ b/packages/stylelint-config/test/.stylelintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "stylelint-config-wordpress/scss", + "ignoreFiles": [ + "*-invalid.scss" + ] +} diff --git a/packages/stylelint-config/test/__snapshots__/commenting.js.snap b/packages/stylelint-config/test/__snapshots__/commenting.js.snap new file mode 100644 index 00000000000000..82a5c3efe8ff03 --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/commenting.js.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid commenting css snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 1, + "line": 9, + "rule": "comment-empty-line-before", + "severity": "error", + "text": "Expected empty line before comment (comment-empty-line-before)", + }, + Object { + "column": 1, + "line": 18, + "rule": "comment-empty-line-before", + "severity": "error", + "text": "Expected empty line before comment (comment-empty-line-before)", + }, + Object { + "column": 131, + "line": 24, + "rule": "max-line-length", + "severity": "error", + "text": "Expected line length to be no more than 80 characters (max-line-length)", + }, +] +`; diff --git a/packages/stylelint-config/test/__snapshots__/functions.js.snap b/packages/stylelint-config/test/__snapshots__/functions.js.snap new file mode 100644 index 00000000000000..0e75e70fcf7b49 --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/functions.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid functions css snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 9, + "line": 4, + "rule": "function-name-case", + "severity": "error", + "text": "Expected \\"Calc\\" to be \\"calc\\" (function-name-case)", + }, +] +`; diff --git a/packages/stylelint-config/test/__snapshots__/index.js.snap b/packages/stylelint-config/test/__snapshots__/index.js.snap new file mode 100644 index 00000000000000..b0df67be5d2f93 --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/index.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid css snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 7, + "line": 2, + "rule": "number-leading-zero", + "severity": "error", + "text": "Expected a leading zero (number-leading-zero)", + }, +] +`; diff --git a/packages/stylelint-config/test/__snapshots__/media-queries.js.snap b/packages/stylelint-config/test/__snapshots__/media-queries.js.snap new file mode 100644 index 00000000000000..e5f02f1c72b828 --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/media-queries.js.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid media queries css snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 1, + "line": 31, + "rule": "at-rule-no-unknown", + "severity": "error", + "text": "Unexpected unknown at-rule \\"@mdia\\" (at-rule-no-unknown)", + }, + Object { + "column": 26, + "line": 1, + "rule": "media-feature-colon-space-after", + "severity": "error", + "text": "Expected single space after \\":\\" (media-feature-colon-space-after)", + }, + Object { + "column": 27, + "line": 6, + "rule": "media-feature-colon-space-after", + "severity": "error", + "text": "Expected single space after \\":\\" (media-feature-colon-space-after)", + }, + Object { + "column": 27, + "line": 6, + "rule": "media-feature-colon-space-before", + "severity": "error", + "text": "Unexpected whitespace before \\":\\" (media-feature-colon-space-before)", + }, + Object { + "column": 17, + "line": 11, + "rule": "media-feature-name-no-unknown", + "severity": "error", + "text": "Unexpected unknown media feature name \\"max-width 699px\\" (media-feature-name-no-unknown)", + }, + Object { + "column": 28, + "line": 16, + "rule": "media-feature-range-operator-space-after", + "severity": "error", + "text": "Expected single space after range operator (media-feature-range-operator-space-after)", + }, + Object { + "column": 29, + "line": 21, + "rule": "media-feature-range-operator-space-after", + "severity": "error", + "text": "Expected single space after range operator (media-feature-range-operator-space-after)", + }, + Object { + "column": 25, + "line": 16, + "rule": "media-feature-range-operator-space-before", + "severity": "error", + "text": "Expected single space before range operator (media-feature-range-operator-space-before)", + }, + Object { + "column": 25, + "line": 26, + "rule": "media-feature-range-operator-space-before", + "severity": "error", + "text": "Expected single space before range operator (media-feature-range-operator-space-before)", + }, + Object { + "column": 27, + "line": 36, + "rule": "media-query-list-comma-space-before", + "severity": "error", + "text": "Unexpected whitespace before \\",\\" (media-query-list-comma-space-before)", + }, + Object { + "column": 27, + "line": 40, + "rule": "media-query-list-comma-space-before", + "severity": "error", + "text": "Unexpected whitespace before \\",\\" (media-query-list-comma-space-before)", + }, +] +`; diff --git a/packages/stylelint-config/test/__snapshots__/properties.js.snap b/packages/stylelint-config/test/__snapshots__/properties.js.snap new file mode 100644 index 00000000000000..cb4c5a5bf2a9c4 --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/properties.js.snap @@ -0,0 +1,55 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid properties css snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 13, + "line": 2, + "rule": "color-hex-case", + "severity": "error", + "text": "Expected \\"#FFFFFF\\" to be \\"#ffffff\\" (color-hex-case)", + }, + Object { + "column": 13, + "line": 2, + "rule": "color-hex-length", + "severity": "error", + "text": "Expected \\"#FFFFFF\\" to be \\"#FFF\\" (color-hex-length)", + }, + Object { + "column": 2, + "line": 5, + "rule": "declaration-block-no-shorthand-property-overrides", + "severity": "error", + "text": "Unexpected shorthand \\"margin\\" after \\"margin-left\\" (declaration-block-no-shorthand-property-overrides)", + }, + Object { + "column": 13, + "line": 2, + "rule": "declaration-colon-space-after", + "severity": "error", + "text": "Expected single space after \\":\\" with a single-line declaration (declaration-colon-space-after)", + }, + Object { + "column": 2, + "line": 6, + "rule": "property-no-unknown", + "severity": "error", + "text": "Unexpected unknown property \\"argin\\" (property-no-unknown)", + }, + Object { + "column": 15, + "line": 4, + "rule": "unit-case", + "severity": "error", + "text": "Expected \\"PX\\" to be \\"px\\" (unit-case)", + }, + Object { + "column": 11, + "line": 3, + "rule": "value-keyword-case", + "severity": "error", + "text": "Expected \\"BLOCK\\" to be \\"block\\" (value-keyword-case)", + }, +] +`; diff --git a/packages/stylelint-config/test/__snapshots__/selectors-scss.js.snap b/packages/stylelint-config/test/__snapshots__/selectors-scss.js.snap new file mode 100644 index 00000000000000..30e2617192d204 --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/selectors-scss.js.snap @@ -0,0 +1,48 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid selectors scss snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 2, + "line": 3, + "rule": "scss/selector-no-redundant-nesting-selector", + "severity": "error", + "text": "Unnecessary nesting selector (&) (scss/selector-no-redundant-nesting-selector)", + }, + Object { + "column": 2, + "line": 10, + "rule": "scss/selector-no-redundant-nesting-selector", + "severity": "error", + "text": "Unnecessary nesting selector (&) (scss/selector-no-redundant-nesting-selector)", + }, + Object { + "column": 2, + "line": 17, + "rule": "scss/selector-no-redundant-nesting-selector", + "severity": "error", + "text": "Unnecessary nesting selector (&) (scss/selector-no-redundant-nesting-selector)", + }, + Object { + "column": 2, + "line": 24, + "rule": "scss/selector-no-redundant-nesting-selector", + "severity": "error", + "text": "Unnecessary nesting selector (&) (scss/selector-no-redundant-nesting-selector)", + }, + Object { + "column": 2, + "line": 31, + "rule": "scss/selector-no-redundant-nesting-selector", + "severity": "error", + "text": "Unnecessary nesting selector (&) (scss/selector-no-redundant-nesting-selector)", + }, + Object { + "column": 10, + "line": 31, + "rule": "selector-pseudo-element-colon-notation", + "severity": "error", + "text": "Expected double colon pseudo-element notation (selector-pseudo-element-colon-notation)", + }, +] +`; diff --git a/packages/stylelint-config/test/__snapshots__/selectors.js.snap b/packages/stylelint-config/test/__snapshots__/selectors.js.snap new file mode 100644 index 00000000000000..6debd6f6f38b0d --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/selectors.js.snap @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid selectors css snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 15, + "line": 18, + "rule": "declaration-property-unit-whitelist", + "severity": "error", + "text": "Unexpected unit \\"%\\" for property \\"line-height\\" (declaration-property-unit-whitelist)", + }, + Object { + "column": 1, + "line": 25, + "rule": "selector-class-pattern", + "severity": "error", + "text": "Selector should use lowercase and separate words with hyphens (selector-class-pattern)", + }, + Object { + "column": 1, + "line": 1, + "rule": "selector-id-pattern", + "severity": "error", + "text": "Selector should use lowercase and separate words with hyphens (selector-id-pattern)", + }, + Object { + "column": 1, + "line": 5, + "rule": "selector-id-pattern", + "severity": "error", + "text": "Selector should use lowercase and separate words with hyphens (selector-id-pattern)", + }, + Object { + "column": 4, + "line": 9, + "rule": "selector-id-pattern", + "severity": "error", + "text": "Selector should use lowercase and separate words with hyphens (selector-id-pattern)", + }, + Object { + "column": 1, + "line": 21, + "rule": "selector-id-pattern", + "severity": "error", + "text": "Selector should use lowercase and separate words with hyphens (selector-id-pattern)", + }, + Object { + "column": 10, + "line": 29, + "rule": "selector-pseudo-element-colon-notation", + "severity": "error", + "text": "Expected double colon pseudo-element notation (selector-pseudo-element-colon-notation)", + }, + Object { + "column": 12, + "line": 17, + "rule": "string-quotes", + "severity": "error", + "text": "Expected double quotes (string-quotes)", + }, +] +`; diff --git a/packages/stylelint-config/test/__snapshots__/structure.js.snap b/packages/stylelint-config/test/__snapshots__/structure.js.snap new file mode 100644 index 00000000000000..45da6df9648187 --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/structure.js.snap @@ -0,0 +1,62 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid structure css snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 45, + "line": 7, + "rule": "block-closing-brace-newline-before", + "severity": "error", + "text": "Expected newline before \\"}\\" (block-closing-brace-newline-before)", + }, + Object { + "column": 14, + "line": 7, + "rule": "block-opening-brace-newline-after", + "severity": "error", + "text": "Expected newline after \\"{\\" (block-opening-brace-newline-after)", + }, + Object { + "column": 32, + "line": 7, + "rule": "declaration-block-semicolon-newline-after", + "severity": "error", + "text": "Expected newline after \\";\\" (declaration-block-semicolon-newline-after)", + }, + Object { + "column": 12, + "line": 1, + "rule": "selector-list-comma-newline-after", + "severity": "error", + "text": "Expected newline after \\",\\" (selector-list-comma-newline-after)", + }, + Object { + "column": 25, + "line": 1, + "rule": "selector-list-comma-newline-after", + "severity": "error", + "text": "Expected newline after \\",\\" (selector-list-comma-newline-after)", + }, + Object { + "column": 3, + "line": 4, + "rule": "indentation", + "severity": "error", + "text": "Expected indentation of 0 tabs (indentation)", + }, + Object { + "column": 3, + "line": 2, + "rule": "indentation", + "severity": "error", + "text": "Expected indentation of 1 tab (indentation)", + }, + Object { + "column": 3, + "line": 3, + "rule": "indentation", + "severity": "error", + "text": "Expected indentation of 1 tab (indentation)", + }, +] +`; diff --git a/packages/stylelint-config/test/__snapshots__/values.js.snap b/packages/stylelint-config/test/__snapshots__/values.js.snap new file mode 100644 index 00000000000000..20c229db69e230 --- /dev/null +++ b/packages/stylelint-config/test/__snapshots__/values.js.snap @@ -0,0 +1,69 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`flags warnings with invalid values css snapshot matches warnings 1`] = ` +Array [ + Object { + "column": 16, + "line": 2, + "rule": "declaration-block-trailing-semicolon", + "severity": "error", + "text": "Expected a trailing semicolon (declaration-block-trailing-semicolon)", + }, + Object { + "column": 13, + "line": 2, + "rule": "declaration-colon-space-after", + "severity": "error", + "text": "Expected single space after \\":\\" with a single-line declaration (declaration-colon-space-after)", + }, + Object { + "column": 15, + "line": 12, + "rule": "declaration-property-unit-whitelist", + "severity": "error", + "text": "Unexpected unit \\"em\\" for property \\"line-height\\" (declaration-property-unit-whitelist)", + }, + Object { + "column": 15, + "line": 10, + "rule": "font-family-name-quotes", + "severity": "error", + "text": "Expected quotes around \\"Times New Roman\\" (font-family-name-quotes)", + }, + Object { + "column": 15, + "line": 11, + "rule": "font-weight-notation", + "severity": "error", + "text": "Expected numeric font-weight notation (font-weight-notation)", + }, + Object { + "column": 11, + "line": 6, + "rule": "length-zero-no-unit", + "severity": "error", + "text": "Unexpected unit (length-zero-no-unit)", + }, + Object { + "column": 15, + "line": 6, + "rule": "length-zero-no-unit", + "severity": "error", + "text": "Unexpected unit (length-zero-no-unit)", + }, + Object { + "column": 24, + "line": 6, + "rule": "length-zero-no-unit", + "severity": "error", + "text": "Unexpected unit (length-zero-no-unit)", + }, + Object { + "column": 1, + "line": 15, + "rule": "no-duplicate-selectors", + "severity": "error", + "text": "Unexpected duplicate selector \\".selector\\", first used at line 1 (no-duplicate-selectors)", + }, +] +`; diff --git a/packages/stylelint-config/test/commenting-invalid.css b/packages/stylelint-config/test/commenting-invalid.css new file mode 100644 index 00000000000000..192f8ceae6036c --- /dev/null +++ b/packages/stylelint-config/test/commenting-invalid.css @@ -0,0 +1,24 @@ +/** +* #.# Section title +* +* Description of section, whether or not it has media queries, etc. +*/ +.selector { + float: left; +} +/** +* #.# Another section title +* +* Description of section, whether or not it has media queries, long comments +* should manually break the line length at 80 characters. +*/ +.another-selector { + float: right; +} +/* This is a comment about this selector */ +.one-more-another-selector { + position: absolute; + top: 0 !important; /* I should explain why this is so !important */ +} + +/* Comments shouldn't have a line length greater than 80 characters, they should manually break the line length at 80 characters */ diff --git a/packages/stylelint-config/test/commenting-valid.css b/packages/stylelint-config/test/commenting-valid.css new file mode 100644 index 00000000000000..e145e49471c7d9 --- /dev/null +++ b/packages/stylelint-config/test/commenting-valid.css @@ -0,0 +1,29 @@ +/** +* #.# Section title +* +* Description of section, whether or not it has media queries, etc. +*/ + +.selector { + float: left; +} + + +/** +* #.# Another section title +* +* Description of section, whether or not it has media queries, long comments +* should manually break the line length at 80 characters. +*/ + +.another-selector { + float: right; +} + +/* This is a comment about this selector */ +.one-more-selector { + position: absolute; + top: 0 !important; /* I should explain why this is so !important */ +} + +/* Long comments should manually break the line length at 80 characters. */ diff --git a/packages/stylelint-config/test/commenting.js b/packages/stylelint-config/test/commenting.js new file mode 100644 index 00000000000000..1a23a47434fc10 --- /dev/null +++ b/packages/stylelint-config/test/commenting.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/commenting-valid.css', + 'utf-8' + ), + invalidCss = fs.readFileSync( + './packages/stylelint-config/test/commenting-invalid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid commenting css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid commenting css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidCss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 3 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/css-invalid.css b/packages/stylelint-config/test/css-invalid.css new file mode 100644 index 00000000000000..9b98086cd5f687 --- /dev/null +++ b/packages/stylelint-config/test/css-invalid.css @@ -0,0 +1,3 @@ +a { + top: .2em; +} diff --git a/packages/stylelint-config/test/css-valid.css b/packages/stylelint-config/test/css-valid.css new file mode 100644 index 00000000000000..363d12aed3ed66 --- /dev/null +++ b/packages/stylelint-config/test/css-valid.css @@ -0,0 +1,3 @@ +a { + top: 0.2em; +} diff --git a/packages/stylelint-config/test/functions-invalid.css b/packages/stylelint-config/test/functions-invalid.css new file mode 100644 index 00000000000000..33a93985335cf0 --- /dev/null +++ b/packages/stylelint-config/test/functions-invalid.css @@ -0,0 +1,5 @@ +/* function-name-case */ + +a { + width: Calc(5% - 10em); +} diff --git a/packages/stylelint-config/test/functions-valid.css b/packages/stylelint-config/test/functions-valid.css new file mode 100644 index 00000000000000..8219b38b2005c8 --- /dev/null +++ b/packages/stylelint-config/test/functions-valid.css @@ -0,0 +1,7 @@ +/* function-name-case */ + +a { + width: calc(5% - 10em); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#1e8cbe", endColorstr="#0074a2", GradientType=0); + filter: progid:DXImageTransform.Microsoft.Alpha(opacity=100); +} diff --git a/packages/stylelint-config/test/functions.js b/packages/stylelint-config/test/functions.js new file mode 100644 index 00000000000000..38b0fbf37a3a87 --- /dev/null +++ b/packages/stylelint-config/test/functions.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/functions-valid.css', + 'utf-8' + ), + invalidCss = fs.readFileSync( + './packages/stylelint-config/test/functions-invalid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid functions css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid functions css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidCss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 1 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/index.js b/packages/stylelint-config/test/index.js new file mode 100644 index 00000000000000..dfc857e8a45041 --- /dev/null +++ b/packages/stylelint-config/test/index.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/css-valid.css', + 'utf-8' + ), + invalidCss = fs.readFileSync( + './packages/stylelint-config/test/css-invalid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidCss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 1 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/media-queries-invalid.css b/packages/stylelint-config/test/media-queries-invalid.css new file mode 100644 index 00000000000000..c89a2776bb807b --- /dev/null +++ b/packages/stylelint-config/test/media-queries-invalid.css @@ -0,0 +1,43 @@ +@media all and (max-width:699px) { + + /* Your selectors */ +} + +@media all and (max-width :699px) { + + /* Your selectors */ +} + +@media all and (max-width 699px) { + + /* Your selectors */ +} + +@media all and (max-width>=699px) { + + /* Your selectors */ +} + +@media all and (max-width >=699px) { + + /* Your selectors */ +} + +@media all and (max-width>= 699px) { + + /* Your selectors */ +} + +@mdia all and (max-width>= 699px) { + + /* Your selectors */ +} + +@media screen and (color) , projection and (color) { + top: 0.2em; +} + +@media screen and (color) , + projection and (color) { + top: 0.2em; +} diff --git a/packages/stylelint-config/test/media-queries-valid.css b/packages/stylelint-config/test/media-queries-valid.css new file mode 100644 index 00000000000000..b373e6d8f15369 --- /dev/null +++ b/packages/stylelint-config/test/media-queries-valid.css @@ -0,0 +1,13 @@ +@media all and (max-width: 699px) and (min-width: 520px) { + + /* Your selectors */ +} + +@media screen and (color), + projection and (color) { + top: 0.2em; +} + +@media screen and (color), projection and (color) { + top: 0.2em; +} diff --git a/packages/stylelint-config/test/media-queries.js b/packages/stylelint-config/test/media-queries.js new file mode 100644 index 00000000000000..a3b59aa0fb5413 --- /dev/null +++ b/packages/stylelint-config/test/media-queries.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/media-queries-valid.css', + 'utf-8' + ), + invalidCss = fs.readFileSync( + './packages/stylelint-config/test/media-queries-invalid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid media queries css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid media queries css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidCss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 11 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/properties-invalid.css b/packages/stylelint-config/test/properties-invalid.css new file mode 100644 index 00000000000000..336b739f753b63 --- /dev/null +++ b/packages/stylelint-config/test/properties-invalid.css @@ -0,0 +1,7 @@ +#selector-1 { + background:#FFFFFF; + display: BLOCK; + margin-left: 20PX; + margin: 0; + argin: 20px; +} diff --git a/packages/stylelint-config/test/properties-valid.css b/packages/stylelint-config/test/properties-valid.css new file mode 100644 index 00000000000000..0fffa37ca44493 --- /dev/null +++ b/packages/stylelint-config/test/properties-valid.css @@ -0,0 +1,6 @@ +#selector-1 { + background: #fff; + display: block; + margin: 0; + margin-left: 20px; +} diff --git a/packages/stylelint-config/test/properties.js b/packages/stylelint-config/test/properties.js new file mode 100644 index 00000000000000..35cedc7697515d --- /dev/null +++ b/packages/stylelint-config/test/properties.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/properties-valid.css', + 'utf-8' + ), + invalidCss = fs.readFileSync( + './packages/stylelint-config/test/properties-invalid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid properties css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid properties css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidCss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 7 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/scss-invalid.scss b/packages/stylelint-config/test/scss-invalid.scss new file mode 100644 index 00000000000000..c73e35ab61a25a --- /dev/null +++ b/packages/stylelint-config/test/scss-invalid.scss @@ -0,0 +1,32 @@ +@unknown { + display: block; +} + +a { + + @debug 1; +} + +@if $foo == block { + display: block; +} + +@else{ + display: inline-block; +} + +b { + + @include foo; + + @include bar; +} + +p { + + /* Test `stylelint-config-recommended` inherited `no-extra-semicolons` rule */ + @include foo;; + + /* Test `stylelint-config-wordpress` inherited `number-leading-zero` rule */ + top: .2em; +} diff --git a/packages/stylelint-config/test/scss-valid.scss b/packages/stylelint-config/test/scss-valid.scss new file mode 100644 index 00000000000000..e424c7cfe37a49 --- /dev/null +++ b/packages/stylelint-config/test/scss-valid.scss @@ -0,0 +1,32 @@ +@import "path/to/foo.scss"; + +@function fooBar { + + @return 1; +} + +@mixin foo { + + @content; +} + +$map: ( + "foo": 1, + "bar": 2, + "baz": 3 +); + +@if $foo == block { + display: block; +} @else { + display: inline-block; +} + +@import "../some/url"; +@import "../another/url"; + +b { + + @include foo; + @include bar; +} diff --git a/packages/stylelint-config/test/scss.js b/packages/stylelint-config/test/scss.js new file mode 100644 index 00000000000000..c64537ee07e6b9 --- /dev/null +++ b/packages/stylelint-config/test/scss.js @@ -0,0 +1,67 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../scss' ), + validScss = fs.readFileSync( + './packages/stylelint-config/test/scss-valid.scss', + 'utf-8' + ), + invalidScss = fs.readFileSync( + './packages/stylelint-config/test/scss-invalid.scss', + 'utf-8' + ); + +describe( 'flags no warnings with valid scss', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validScss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid scss', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidScss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 8 ) + ); + } ); + + // ToDo: Fix snapshot, as results differ between Node.js v10 & v12 + // it( 'snapshot matches warnings', () => { + // return result.then( ( data ) => ( + // expect( data.results[ 0 ].warnings ).toMatchSnapshot() + // ) ); + // } ); +} ); diff --git a/packages/stylelint-config/test/selectors-invalid.css b/packages/stylelint-config/test/selectors-invalid.css new file mode 100644 index 00000000000000..bf0716916420ff --- /dev/null +++ b/packages/stylelint-config/test/selectors-invalid.css @@ -0,0 +1,31 @@ +#commentForm { /* Avoid camelcase. */ + margin: 0; +} + +#comment_form { /* Avoid underscores. */ + margin: 0; +} + +div#comment_form { /* Avoid over-qualification. */ + margin: 0; +} + +#c1-xr { /* What is a c1-xr?! Use a better name. */ + margin: 0; +} + +input[type='text'] { /* Should be [type="text"] */ + line-height: 110% /* Also doubly incorrect */ +} + +#Selector { + color: #000; +} + +.selectorA { + color: #000; +} + +.selector:after { + color: #000; +} diff --git a/packages/stylelint-config/test/selectors-invalid.scss b/packages/stylelint-config/test/selectors-invalid.scss new file mode 100644 index 00000000000000..d1bb44f841dbac --- /dev/null +++ b/packages/stylelint-config/test/selectors-invalid.scss @@ -0,0 +1,34 @@ +.foo { + + & > .bar { + margin: 0; + } +} + +a { + + & a { + margin: 0; + } +} + +div { + + & > a { + margin: 0; + } +} + +p { + + & .class { + margin: 0; + } +} + +span { + + & .class:after { + margin: 0; + } +} diff --git a/packages/stylelint-config/test/selectors-scss.js b/packages/stylelint-config/test/selectors-scss.js new file mode 100644 index 00000000000000..94a85ff86543e0 --- /dev/null +++ b/packages/stylelint-config/test/selectors-scss.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../scss' ), + validScss = fs.readFileSync( + './packages/stylelint-config/test/selectors-valid.scss', + 'utf-8' + ), + invalidScss = fs.readFileSync( + './packages/stylelint-config/test/selectors-invalid.scss', + 'utf-8' + ); + +describe( 'flags no warnings with valid selectors scss', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validScss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid selectors scss', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidScss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 6 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/selectors-valid.css b/packages/stylelint-config/test/selectors-valid.css new file mode 100644 index 00000000000000..19a87ff51f35d8 --- /dev/null +++ b/packages/stylelint-config/test/selectors-valid.css @@ -0,0 +1,19 @@ +#comment-form { + margin: 1em 0; +} + +input[type="text"] { + line-height: 1.1; +} + +.selector::after { + color: #000; +} + +.selector-class { + color: #000; +} + +#selector-id { + color: #000; +} diff --git a/packages/stylelint-config/test/selectors-valid.scss b/packages/stylelint-config/test/selectors-valid.scss new file mode 100644 index 00000000000000..a6d4db9172be4c --- /dev/null +++ b/packages/stylelint-config/test/selectors-valid.scss @@ -0,0 +1,37 @@ +.foo { + + > .bar { + margin: 0; + } +} + +a { + + &.foo { + margin: 0; + } +} + +div { + + .foo > & { + margin: 0; + } +} + +p { + + &, + .foo, + .baz { + margin: 0; + } +} + +span { + + &, + .foo::after { + color: #000; + } +} diff --git a/packages/stylelint-config/test/selectors.js b/packages/stylelint-config/test/selectors.js new file mode 100644 index 00000000000000..337d1d00eed311 --- /dev/null +++ b/packages/stylelint-config/test/selectors.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/selectors-valid.css', + 'utf-8' + ), + invalidCss = fs.readFileSync( + './packages/stylelint-config/test/selectors-invalid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid selectors css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid selectors css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidCss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 8 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/structure-invalid.css b/packages/stylelint-config/test/structure-invalid.css new file mode 100644 index 00000000000000..78820847ae9d89 --- /dev/null +++ b/packages/stylelint-config/test/structure-invalid.css @@ -0,0 +1,7 @@ +#selector-1, #selector-2, #selector-3 { + background: #fff; + color: #000; + } + + +#selector-1 { background: #fff; color: #000; } diff --git a/packages/stylelint-config/test/structure-valid.css b/packages/stylelint-config/test/structure-valid.css new file mode 100644 index 00000000000000..b7b28949f3cd52 --- /dev/null +++ b/packages/stylelint-config/test/structure-valid.css @@ -0,0 +1,29 @@ +#selector-1, +#selector-2, +#selector-3 { + background: #fff; + color: #000; +} + +#selector-4 { + background: #fff; + color: #000; +} + +h1, +.heading-size-1 { + background: #fff; + color: #000; +} + +h2, +.heading-size-2 { + background: #fff; + color: #000; +} + +h3, +.heading-size-3 { + background: #fff; + color: #000; +} diff --git a/packages/stylelint-config/test/structure.js b/packages/stylelint-config/test/structure.js new file mode 100644 index 00000000000000..7ec03a3b667bac --- /dev/null +++ b/packages/stylelint-config/test/structure.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/structure-valid.css', + 'utf-8' + ), + invalidCss = fs.readFileSync( + './packages/stylelint-config/test/structure-invalid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid structure css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid structure css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidCss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 8 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/themes-valid.css b/packages/stylelint-config/test/themes-valid.css new file mode 100644 index 00000000000000..de9a2fb8193a6c --- /dev/null +++ b/packages/stylelint-config/test/themes-valid.css @@ -0,0 +1,16 @@ +/* +Theme Name: Twenty Ten +Theme URI: https://wordpress.org/themes/twentyten/themes/twentyten/themes/twentyten/themes/twentyten/ +Description: The 2010 theme for WordPress is stylish, customizable, simple, and readable -- make it yours with a custom menu, header image, and background. Twenty Ten supports six widgetized areas (two in the sidebar, four in the footer) and featured images (thumbnails for gallery posts and custom header images for posts and pages). It includes stylesheets for print and the admin Visual Editor, special styles for posts in the "Asides" and "Gallery" categories, and has an optional one-column page template that removes the sidebar. +Author: the WordPress team +Author URI: https://wordpress.org/ +Version: 2.3 +License: GNU General Public License v2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html +Tags: blog, two-columns, custom-header, custom-background, threaded-comments, sticky-post, translation-ready, microformats, rtl-language-support, editor-style, custom-menu, flexible-header, featured-images, footer-widgets, featured-image-header +Text Domain: twentyten +*/ + +a { + top: 0.2em; +} diff --git a/packages/stylelint-config/test/themes.js b/packages/stylelint-config/test/themes.js new file mode 100644 index 00000000000000..f0016832b24d7b --- /dev/null +++ b/packages/stylelint-config/test/themes.js @@ -0,0 +1,35 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/themes-valid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); diff --git a/packages/stylelint-config/test/values-invalid.css b/packages/stylelint-config/test/values-invalid.css new file mode 100644 index 00000000000000..d9ce259e58c8c0 --- /dev/null +++ b/packages/stylelint-config/test/values-invalid.css @@ -0,0 +1,17 @@ +.selector { /* Avoid missing space and semicolon */ + background:#fff +} + +.another-selector { /* Avoid adding a unit on a zero value */ + margin: 0px 0px 20px 0px; +} + +.one-more-selector { + font-family: Times New Roman, serif; /* Quote font names when required */ + font-weight: bold; /* Avoid named font weights */ + line-height: 1.4em; +} + +.selector { /* Avoid duplicate selectors */ + background: #fff; +} diff --git a/packages/stylelint-config/test/values-valid.css b/packages/stylelint-config/test/values-valid.css new file mode 100644 index 00000000000000..ca9ccb5f188c29 --- /dev/null +++ b/packages/stylelint-config/test/values-valid.css @@ -0,0 +1,15 @@ +.selector { /* Correct usage of quotes */ + background-image: url(images/bg.png); + font-family: "Helvetica Neue", sans-serif; + font-weight: 700; + line-height: 14px; +} + +.another-selector { /* Correct usage of zero values */ + font-family: Georgia, serif; + font-weight: bolder; /* Ignore relative font weights */ + line-height: 1.4; + text-shadow: + 0 -1px 0 rgba(0, 0, 0, 0.5), + 0 1px 0 #fff; +} diff --git a/packages/stylelint-config/test/values.js b/packages/stylelint-config/test/values.js new file mode 100644 index 00000000000000..97cbc58e02f3b4 --- /dev/null +++ b/packages/stylelint-config/test/values.js @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/values-valid.css', + 'utf-8' + ), + invalidCss = fs.readFileSync( + './packages/stylelint-config/test/values-invalid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid values css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); + +describe( 'flags warnings with invalid values css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: invalidCss, + config, + } ); + } ); + + it( 'did error', () => { + return result.then( ( data ) => expect( data.errored ).toBeTruthy() ); + } ); + + it( 'flags correct number of warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 9 ) + ); + } ); + + it( 'snapshot matches warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toMatchSnapshot() + ); + } ); +} ); diff --git a/packages/stylelint-config/test/vendor-prefixes-valid.css b/packages/stylelint-config/test/vendor-prefixes-valid.css new file mode 100644 index 00000000000000..e46698570d9ffd --- /dev/null +++ b/packages/stylelint-config/test/vendor-prefixes-valid.css @@ -0,0 +1,5 @@ +.sample-output { + -webkit-box-shadow: inset 0 0 1px 1px #eee; + -moz-box-shadow: inset 0 0 1px 1px #eee; + box-shadow: inset 0 0 1px 1px #eee; +} diff --git a/packages/stylelint-config/test/vendor-prefixes.js b/packages/stylelint-config/test/vendor-prefixes.js new file mode 100644 index 00000000000000..e3ec4d2a7e5dfc --- /dev/null +++ b/packages/stylelint-config/test/vendor-prefixes.js @@ -0,0 +1,35 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ), + stylelint = require( 'stylelint' ); + +/** + * Internal dependencies + */ +const config = require( '../' ), + validCss = fs.readFileSync( + './packages/stylelint-config/test/vendor-prefixes-valid.css', + 'utf-8' + ); + +describe( 'flags no warnings with valid vendor prefixes css', () => { + let result; + + beforeEach( () => { + result = stylelint.lint( { + code: validCss, + config, + } ); + } ); + + it( 'did not error', () => { + return result.then( ( data ) => expect( data.errored ).toBeFalsy() ); + } ); + + it( 'flags no warnings', () => { + return result.then( ( data ) => + expect( data.results[ 0 ].warnings ).toHaveLength( 0 ) + ); + } ); +} ); diff --git a/packages/token-list/package.json b/packages/token-list/package.json index 8ca60e3892d460..7f925bccc5a061 100644 --- a/packages/token-list/package.json +++ b/packages/token-list/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/token-list", - "version": "1.13.0", + "version": "1.14.0", "description": "Constructable, plain JavaScript DOMTokenList implementation, supporting non-browser runtimes.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "react-native": "src/index", "types": "build-types", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19" }, "publishConfig": { diff --git a/packages/url/CHANGELOG.md b/packages/url/CHANGELOG.md index e31ef4fedde793..7dc8a515a74b0a 100644 --- a/packages/url/CHANGELOG.md +++ b/packages/url/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### New Feature + +- Add optional argument `maxLength` for truncating URL in `filterURLForDisplay` + ## 2.16.0 (2020-06-15) ### New Feature diff --git a/packages/url/README.md b/packages/url/README.md index be964361ac1def..d369f6ed7cd62d 100644 --- a/packages/url/README.md +++ b/packages/url/README.md @@ -37,6 +37,36 @@ _Returns_ - `string`: URL with arguments applied. +<a name="buildQueryString" href="#buildQueryString">#</a> **buildQueryString** + +Generates URL-encoded query string using input query data. + +It is intended to behave equivalent as PHP's `http_build_query`, configured +with encoding type PHP_QUERY_RFC3986 (spaces as `%20`). + +_Usage_ + +```js +const queryString = buildQueryString( { + simple: 'is ok', + arrays: [ 'are', 'fine', 'too' ], + objects: { + evenNested: { + ok: 'yes', + }, + }, +} ); +// "simple=is%20ok&arrays%5B0%5D=are&arrays%5B1%5D=fine&arrays%5B2%5D=too&objects%5BevenNested%5D%5Bok%5D=yes" +``` + +_Parameters_ + +- _data_ `Record<string,*>`: Data to encode. + +_Returns_ + +- `string`: Query string. + <a name="cleanForSlug" href="#cleanForSlug">#</a> **cleanForSlug** Performs some basic cleanup of a string for use as a post slug. @@ -66,11 +96,13 @@ _Usage_ ```js const displayUrl = filterURLForDisplay( 'https://www.wordpress.org/gutenberg/' ); // wordpress.org/gutenberg +const imageUrl = filterURLForDisplay( 'https://www.wordpress.org/wp-content/uploads/img.png', 20 ); // …ent/uploads/img.png ``` _Parameters_ - _url_ `string`: Original URL. +- _maxLength_ `(number|null)`: URL length. _Returns_ @@ -188,7 +220,27 @@ _Parameters_ _Returns_ -- `(QueryArgParsed|undefined)`: Query arg value. +- `(QueryArgParsed|void)`: Query arg value. + +<a name="getQueryArgs" href="#getQueryArgs">#</a> **getQueryArgs** + +Returns an object of query arguments of the given URL. If the given URL is +invalid or has no querystring, an empty object is returned. + +_Usage_ + +```js +const foo = getQueryArgs( 'https://wordpress.org?foo=bar&bar=baz' ); +// { "foo": "bar", "bar": "baz" } +``` + +_Parameters_ + +- _url_ `string`: URL. + +_Returns_ + +- `QueryArgs`: Query args object. <a name="getQueryString" href="#getQueryString">#</a> **getQueryString** diff --git a/packages/url/package.json b/packages/url/package.json index 01d14070995d0c..1b007599bc227d 100644 --- a/packages/url/package.json +++ b/packages/url/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/url", - "version": "2.19.0", + "version": "2.20.0", "description": "WordPress URL utilities.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -24,9 +24,8 @@ "types": "build-types", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19", - "qs": "^6.5.2", "react-native-url-polyfill": "^1.1.2" }, "publishConfig": { diff --git a/packages/url/src/add-query-args.js b/packages/url/src/add-query-args.js index 2d48c0be5a06b8..859785d44544a7 100644 --- a/packages/url/src/add-query-args.js +++ b/packages/url/src/add-query-args.js @@ -1,7 +1,8 @@ /** - * External dependencies + * Internal dependencies */ -import { parse, stringify } from 'qs'; +import { getQueryArgs } from './get-query-args'; +import { buildQueryString } from './build-query-string'; /** * Appends arguments as querystring to the provided URL. If the URL already @@ -31,14 +32,11 @@ export function addQueryArgs( url = '', args ) { const queryStringIndex = url.indexOf( '?' ); if ( queryStringIndex !== -1 ) { // Merge into existing query arguments. - args = Object.assign( - parse( url.substr( queryStringIndex + 1 ) ), - args - ); + args = Object.assign( getQueryArgs( url ), args ); // Change working base URL to omit previous query arguments. baseUrl = baseUrl.substr( 0, queryStringIndex ); } - return baseUrl + '?' + stringify( args ); + return baseUrl + '?' + buildQueryString( args ); } diff --git a/packages/url/src/build-query-string.js b/packages/url/src/build-query-string.js new file mode 100644 index 00000000000000..51216a9f791d99 --- /dev/null +++ b/packages/url/src/build-query-string.js @@ -0,0 +1,61 @@ +/** + * Generates URL-encoded query string using input query data. + * + * It is intended to behave equivalent as PHP's `http_build_query`, configured + * with encoding type PHP_QUERY_RFC3986 (spaces as `%20`). + * + * @example + * ```js + * const queryString = buildQueryString( { + * simple: 'is ok', + * arrays: [ 'are', 'fine', 'too' ], + * objects: { + * evenNested: { + * ok: 'yes', + * }, + * }, + * } ); + * // "simple=is%20ok&arrays%5B0%5D=are&arrays%5B1%5D=fine&arrays%5B2%5D=too&objects%5BevenNested%5D%5Bok%5D=yes" + * ``` + * + * @param {Record<string,*>} data Data to encode. + * + * @return {string} Query string. + */ +export function buildQueryString( data ) { + let string = ''; + + const stack = Array.from( Object.entries( data ) ); + + let pair; + while ( ( pair = stack.shift() ) ) { + let [ key, value ] = pair; + + // Support building deeply nested data, from array or object values. + const hasNestedData = + Array.isArray( value ) || ( value && value.constructor === Object ); + + if ( hasNestedData ) { + // Push array or object values onto the stack as composed of their + // original key and nested index or key, retaining order by a + // combination of Array#reverse and Array#unshift onto the stack. + const valuePairs = Object.entries( value ).reverse(); + for ( const [ member, memberValue ] of valuePairs ) { + stack.unshift( [ `${ key }[${ member }]`, memberValue ] ); + } + } else if ( value !== undefined ) { + // Null is treated as special case, equivalent to empty string. + if ( value === null ) { + value = ''; + } + + string += + '&' + [ key, value ].map( encodeURIComponent ).join( '=' ); + } + } + + // Loop will concatenate with leading `&`, but it's only expected for all + // but the first query parameter. This strips the leading `&`, while still + // accounting for the case that the string may in-fact be empty. + return string.substr( 1 ); +} diff --git a/packages/url/src/filter-url-for-display.js b/packages/url/src/filter-url-for-display.js index 978594aced536c..fd3eccc99cbf74 100644 --- a/packages/url/src/filter-url-for-display.js +++ b/packages/url/src/filter-url-for-display.js @@ -2,22 +2,53 @@ * Returns a URL for display. * * @param {string} url Original URL. + * @param {number|null} maxLength URL length. * * @example * ```js * const displayUrl = filterURLForDisplay( 'https://www.wordpress.org/gutenberg/' ); // wordpress.org/gutenberg + * const imageUrl = filterURLForDisplay( 'https://www.wordpress.org/wp-content/uploads/img.png', 20 ); // …ent/uploads/img.png * ``` * * @return {string} Displayed URL. */ -export function filterURLForDisplay( url ) { +export function filterURLForDisplay( url, maxLength = null ) { // Remove protocol and www prefixes. - const filteredURL = url.replace( /^(?:https?:)\/\/(?:www\.)?/, '' ); + let filteredURL = url.replace( /^(?:https?:)\/\/(?:www\.)?/, '' ); // Ends with / and only has that single slash, strip it. if ( filteredURL.match( /^[^\/]+\/$/ ) ) { - return filteredURL.replace( '/', '' ); + filteredURL = filteredURL.replace( '/', '' ); } - return filteredURL; + const mediaRegexp = /([\w|:])*\.(?:jpg|jpeg|gif|png|svg)/; + + if ( + ! maxLength || + filteredURL.length <= maxLength || + ! filteredURL.match( mediaRegexp ) + ) { + return filteredURL; + } + + // If the file is not greater than max length, return last portion of URL. + filteredURL = filteredURL.split( '?' )[ 0 ]; + const urlPieces = filteredURL.split( '/' ); + const file = urlPieces[ urlPieces.length - 1 ]; + if ( file.length <= maxLength ) { + return '…' + filteredURL.slice( -maxLength ); + } + + // If the file is greater than max length, truncate the file. + const index = file.lastIndexOf( '.' ); + const [ fileName, extension ] = [ + file.slice( 0, index ), + file.slice( index + 1 ), + ]; + const truncatedFile = fileName.slice( -3 ) + '.' + extension; + return ( + file.slice( 0, maxLength - truncatedFile.length - 1 ) + + '…' + + truncatedFile + ); } diff --git a/packages/url/src/get-query-arg.js b/packages/url/src/get-query-arg.js index f6e184f0798ce3..d81a6249a1c0ae 100644 --- a/packages/url/src/get-query-arg.js +++ b/packages/url/src/get-query-arg.js @@ -1,7 +1,7 @@ /** - * External dependencies + * Internal dependencies */ -import { parse } from 'qs'; +import { getQueryArgs } from './get-query-args'; /* eslint-disable jsdoc/valid-types */ /** @@ -24,14 +24,8 @@ import { parse } from 'qs'; * const foo = getQueryArg( 'https://wordpress.org?foo=bar&bar=baz', 'foo' ); // bar * ``` * - * @return {QueryArgParsed|undefined} Query arg value. + * @return {QueryArgParsed|void} Query arg value. */ export function getQueryArg( url, arg ) { - const queryStringIndex = url.indexOf( '?' ); - const query = - queryStringIndex !== -1 - ? parse( url.substr( queryStringIndex + 1 ) ) - : {}; - - return query[ arg ]; + return getQueryArgs( url )[ arg ]; } diff --git a/packages/url/src/get-query-args.js b/packages/url/src/get-query-args.js new file mode 100644 index 00000000000000..273f7f380c8ef1 --- /dev/null +++ b/packages/url/src/get-query-args.js @@ -0,0 +1,94 @@ +/** + * Internal dependencies + */ +import { getQueryString } from './get-query-string'; + +/** @typedef {import('./get-query-arg').QueryArgParsed} QueryArgParsed */ + +/** + * @typedef {Record<string,QueryArgParsed>} QueryArgs + */ + +/** + * Sets a value in object deeply by a given array of path segments. Mutates the + * object reference. + * + * @param {Record<string,*>} object Object in which to assign. + * @param {string[]} path Path segment at which to set value. + * @param {*} value Value to set. + */ +function setPath( object, path, value ) { + const length = path.length; + const lastIndex = length - 1; + for ( let i = 0; i < length; i++ ) { + let key = path[ i ]; + + if ( ! key && Array.isArray( object ) ) { + // If key is empty string and next value is array, derive key from + // the current length of the array. + key = object.length.toString(); + } + + // If the next key in the path is numeric (or empty string), it will be + // created as an array. Otherwise, it will be created as an object. + const isNextKeyArrayIndex = ! isNaN( Number( path[ i + 1 ] ) ); + + object[ key ] = + i === lastIndex + ? // If at end of path, assign the intended value. + value + : // Otherwise, advance to the next object in the path, creating + // it if it does not yet exist. + object[ key ] || ( isNextKeyArrayIndex ? [] : {} ); + + if ( Array.isArray( object[ key ] ) && ! isNextKeyArrayIndex ) { + // If we current key is non-numeric, but the next value is an + // array, coerce the value to an object. + object[ key ] = { ...object[ key ] }; + } + + // Update working reference object to the next in the path. + object = object[ key ]; + } +} + +/** + * Returns an object of query arguments of the given URL. If the given URL is + * invalid or has no querystring, an empty object is returned. + * + * @param {string} url URL. + * + * @example + * ```js + * const foo = getQueryArgs( 'https://wordpress.org?foo=bar&bar=baz' ); + * // { "foo": "bar", "bar": "baz" } + * ``` + * + * @return {QueryArgs} Query args object. + */ +export function getQueryArgs( url ) { + return ( + ( getQueryString( url ) || '' ) + // Normalize space encoding, accounting for PHP URL encoding + // corresponding to `application/x-www-form-urlencoded`. + // + // See: https://tools.ietf.org/html/rfc1866#section-8.2.1 + .replace( /\+/g, '%20' ) + .split( '&' ) + .reduce( ( accumulator, keyValue ) => { + const [ key, value = '' ] = keyValue + .split( '=' ) + // Filtering avoids decoding as `undefined` for value, where + // default is restored in destructuring assignment. + .filter( Boolean ) + .map( decodeURIComponent ); + + if ( key ) { + const segments = key.replace( /\]/g, '' ).split( '[' ); + setPath( accumulator, segments, value ); + } + + return accumulator; + }, {} ) + ); +} diff --git a/packages/url/src/get-query-string.js b/packages/url/src/get-query-string.js index e1437f4e78a131..8624ce5c6c8b9d 100644 --- a/packages/url/src/get-query-string.js +++ b/packages/url/src/get-query-string.js @@ -13,7 +13,7 @@ export function getQueryString( url ) { let query; try { - query = new URL( url ).search.substring( 1 ); + query = new URL( url, 'http://example.com' ).search.substring( 1 ); } catch ( error ) {} if ( query ) { diff --git a/packages/url/src/index.js b/packages/url/src/index.js index 5175803dfb1e9e..f060ae8152897d 100644 --- a/packages/url/src/index.js +++ b/packages/url/src/index.js @@ -7,12 +7,14 @@ export { isValidAuthority } from './is-valid-authority'; export { getPath } from './get-path'; export { isValidPath } from './is-valid-path'; export { getQueryString } from './get-query-string'; +export { buildQueryString } from './build-query-string'; export { isValidQueryString } from './is-valid-query-string'; export { getPathAndQueryString } from './get-path-and-query-string'; export { getFragment } from './get-fragment'; export { isValidFragment } from './is-valid-fragment'; export { addQueryArgs } from './add-query-args'; export { getQueryArg } from './get-query-arg'; +export { getQueryArgs } from './get-query-args'; export { hasQueryArg } from './has-query-arg'; export { removeQueryArgs } from './remove-query-args'; export { prependHTTP } from './prepend-http'; diff --git a/packages/url/src/remove-query-args.js b/packages/url/src/remove-query-args.js index 796e46e0ab980c..07fd5467808e00 100644 --- a/packages/url/src/remove-query-args.js +++ b/packages/url/src/remove-query-args.js @@ -1,7 +1,8 @@ /** - * External dependencies + * Internal dependencies */ -import { parse, stringify } from 'qs'; +import { getQueryArgs } from './get-query-args'; +import { buildQueryString } from './build-query-string'; /** * Removes arguments from the query string of the url @@ -18,14 +19,13 @@ import { parse, stringify } from 'qs'; */ export function removeQueryArgs( url, ...args ) { const queryStringIndex = url.indexOf( '?' ); - const query = - queryStringIndex !== -1 - ? parse( url.substr( queryStringIndex + 1 ) ) - : {}; - const baseUrl = - queryStringIndex !== -1 ? url.substr( 0, queryStringIndex ) : url; + if ( queryStringIndex === -1 ) { + return url; + } + const query = getQueryArgs( url ); + const baseURL = url.substr( 0, queryStringIndex ); args.forEach( ( arg ) => delete query[ arg ] ); - - return baseUrl + '?' + stringify( query ); + const queryString = buildQueryString( query ); + return queryString ? baseURL + '?' + queryString : baseURL; } diff --git a/packages/url/src/test/index.test.js b/packages/url/src/test/index.test.js index 9699e24ec0a067..f4814b0b0bc83b 100644 --- a/packages/url/src/test/index.test.js +++ b/packages/url/src/test/index.test.js @@ -16,6 +16,7 @@ import { getPath, isValidPath, getQueryString, + buildQueryString, isValidQueryString, getFragment, isValidFragment, @@ -27,6 +28,7 @@ import { safeDecodeURI, filterURLForDisplay, cleanForSlug, + getQueryArgs, } from '../'; import wptData from './fixtures/wpt-data'; @@ -288,6 +290,14 @@ describe( 'getQueryString', () => { ).toBe( 'foo=bar&foo=baz?test' ); } ); + it( 'returns the query string of a path', () => { + expect( getQueryString( '/wp-json/wp/v2/posts?type=page' ) ).toBe( + 'type=page' + ); + + expect( getQueryString( '/wp-json/wp/v2/posts' ) ).toBeUndefined(); + } ); + it( 'returns undefined when the provided does not contain a url query string', () => { expect( getQueryString( '' ) ).toBeUndefined(); expect( @@ -313,6 +323,56 @@ describe( 'getQueryString', () => { } ); } ); +describe( 'buildQueryString', () => { + it( 'builds simple strings', () => { + const data = { + foo: 'bar', + baz: 'boom', + cow: 'milk', + php: 'hypertext processor', + }; + + expect( buildQueryString( data ) ).toBe( + 'foo=bar&baz=boom&cow=milk&php=hypertext%20processor' + ); + } ); + + it( 'builds complex data', () => { + const data = { + user: { + name: 'Bob Smith', + age: 47, + sex: 'M', + dob: '5/12/1956', + }, + pastimes: [ 'golf', 'opera', 'poker', 'rap' ], + children: { + bobby: { age: 12, sex: 'M' }, + sally: { age: 8, sex: 'F' }, + }, + }; + + expect( buildQueryString( data ) ).toBe( + 'user%5Bname%5D=Bob%20Smith&user%5Bage%5D=47&user%5Bsex%5D=M&user%5Bdob%5D=5%2F12%2F1956&pastimes%5B0%5D=golf&pastimes%5B1%5D=opera&pastimes%5B2%5D=poker&pastimes%5B3%5D=rap&children%5Bbobby%5D%5Bage%5D=12&children%5Bbobby%5D%5Bsex%5D=M&children%5Bsally%5D%5Bage%5D=8&children%5Bsally%5D%5Bsex%5D=F' + ); + } ); + + it( 'builds falsey values', () => { + const data = { + empty: '', + null: null, + undefined, + zero: 0, + }; + + expect( buildQueryString( data ) ).toBe( 'empty=&null=&zero=0' ); + } ); + + it( 'builds an empty object as an empty string', () => { + expect( buildQueryString( {} ) ).toBe( '' ); + } ); +} ); + describe( 'isValidQueryString', () => { it( 'returns true if the query string is valid', () => { expect( isValidQueryString( 'test' ) ).toBe( true ); @@ -520,6 +580,116 @@ describe( 'addQueryArgs', () => { } ); } ); +describe( 'getQueryArgs', () => { + it( 'should parse simple query arguments', () => { + const url = 'https://andalouses.example/beach?foo=bar&baz=quux'; + + expect( getQueryArgs( url ) ).toEqual( { + foo: 'bar', + baz: 'quux', + } ); + } ); + + it( 'should accumulate array of values', () => { + const url = + 'https://andalouses.example/beach?foo[]=zero&foo[]=one&foo[]=two'; + + expect( getQueryArgs( url ) ).toEqual( { + foo: [ 'zero', 'one', 'two' ], + } ); + } ); + + it( 'should accumulate keyed array of values', () => { + const url = + 'https://andalouses.example/beach?foo[1]=one&foo[0]=zero&foo[]=two'; + + expect( getQueryArgs( url ) ).toEqual( { + foo: [ 'zero', 'one', 'two' ], + } ); + } ); + + it( 'should accumulate object of values', () => { + const url = + 'https://andalouses.example/beach?foo[zero]=0&foo[one]=1&foo[]=empty'; + + expect( getQueryArgs( url ) ).toEqual( { + foo: { + '': 'empty', + zero: '0', + one: '1', + }, + } ); + } ); + + it( 'normalizes mixed numeric and named keys', () => { + const url = 'https://andalouses.example/beach?foo[0]=0&foo[one]=1'; + + expect( getQueryArgs( url ) ).toEqual( { + foo: { + 0: '0', + one: '1', + }, + } ); + } ); + + it( 'should return empty object for URL without querystring', () => { + const urlWithoutQuerystring = 'https://andalouses.example/beach'; + const urlWithEmptyQuerystring = 'https://andalouses.example/beach?'; + const invalidURL = 'example'; + + expect( getQueryArgs( invalidURL ) ).toEqual( {} ); + expect( getQueryArgs( urlWithoutQuerystring ) ).toEqual( {} ); + expect( getQueryArgs( urlWithEmptyQuerystring ) ).toEqual( {} ); + } ); + + it( 'should gracefully handle empty keys and values', () => { + const url = 'https://andalouses.example/beach?&foo'; + + expect( getQueryArgs( url ) ).toEqual( { + foo: '', + } ); + } ); + + describe( 'reverses buildQueryString', () => { + it( 'unbuilds simple strings', () => { + const data = { + foo: 'bar', + baz: 'boom', + cow: 'milk', + php: 'hypertext processor', + }; + + expect( + getQueryArgs( + 'https://example.com/?foo=bar&baz=boom&cow=milk&php=hypertext%20processor' + ) + ).toEqual( data ); + } ); + + it( 'unbuilds complex data, with stringified values', () => { + const data = { + user: { + name: 'Bob Smith', + age: '47', + sex: 'M', + dob: '5/12/1956', + }, + pastimes: [ 'golf', 'opera', 'poker', 'rap' ], + children: { + bobby: { age: '12', sex: 'M' }, + sally: { age: '8', sex: 'F' }, + }, + }; + + expect( + getQueryArgs( + 'https://example.com/?user%5Bname%5D=Bob%20Smith&user%5Bage%5D=47&user%5Bsex%5D=M&user%5Bdob%5D=5%2F12%2F1956&pastimes%5B0%5D=golf&pastimes%5B1%5D=opera&pastimes%5B2%5D=poker&pastimes%5B3%5D=rap&children%5Bbobby%5D%5Bage%5D=12&children%5Bbobby%5D%5Bsex%5D=M&children%5Bsally%5D%5Bage%5D=8&children%5Bsally%5D%5Bsex%5D=F' + ) + ).toEqual( data ); + } ); + } ); +} ); + describe( 'getQueryArg', () => { it( 'should get the value of an existing query arg', () => { const url = 'https://andalouses.example/beach?foo=bar&bar=baz'; @@ -543,6 +713,7 @@ describe( 'getQueryArg', () => { const url = 'https://andalouses.example/beach?foo=bar&bar=baz#foo'; expect( getQueryArg( url, 'foo' ) ).toEqual( 'bar' ); + expect( getQueryArg( url, 'bar' ) ).toEqual( 'baz' ); } ); } ); @@ -567,6 +738,12 @@ describe( 'hasQueryArg', () => { } ); describe( 'removeQueryArgs', () => { + it( 'should not change URL without a querystring', () => { + const url = 'https://andalouses.example/beach'; + + expect( removeQueryArgs( url, 'baz', 'test' ) ).toEqual( url ); + } ); + it( 'should not change URL not containing query args', () => { const url = 'https://andalouses.example/beach?foo=bar&bar=baz'; @@ -581,6 +758,14 @@ describe( 'removeQueryArgs', () => { ); } ); + it( 'should not leave ? char after removing all query args', () => { + const url = 'https://andalouses.example/beach?foo=bar&bar=baz'; + + expect( removeQueryArgs( url, 'foo', 'bar' ) ).toEqual( + 'https://andalouses.example/beach' + ); + } ); + it( 'should remove array query arg', () => { const url = 'https://andalouses.example/beach?foo[]=bar&foo[]=baz&bar=foobar'; @@ -699,6 +884,54 @@ describe( 'filterURLForDisplay', () => { const url = filterURLForDisplay( 'http://www.wordpress.org/something' ); expect( url ).toBe( 'wordpress.org/something' ); } ); + it( 'should preserve the original url if no argument max length', () => { + const url = filterURLForDisplay( + 'http://www.wordpress.org/wp-content/uploads/myimage.jpg' + ); + expect( url ).toBe( 'wordpress.org/wp-content/uploads/myimage.jpg' ); + } ); + it( 'should preserve the original url if the url is short enough', () => { + const url = filterURLForDisplay( + 'http://www.wordpress.org/ig.jpg', + 20 + ); + expect( url ).toBe( 'wordpress.org/ig.jpg' ); + } ); + it( 'should return ellipsis, upper level pieces url, and filename when the url is long enough but filename is short enough', () => { + const url = filterURLForDisplay( + 'http://www.wordpress.org/wp-content/uploads/myimage.jpg', + 20 + ); + expect( url ).toBe( '…/uploads/myimage.jpg' ); + } ); + it( 'should return filename split by ellipsis plus three characters when filename is long enough', () => { + const url = filterURLForDisplay( + 'http://www.wordpress.org/wp-content/uploads/superlongtitlewithextension.jpeg', + 20 + ); + expect( url ).toBe( 'superlongti…ion.jpeg' ); + } ); + it( 'should remove query arguments', () => { + const url = filterURLForDisplay( + 'http://www.wordpress.org/wp-content/uploads/myimage.jpeg?query_args=a', + 20 + ); + expect( url ).toBe( '…uploads/myimage.jpeg' ); + } ); + it( 'should preserve the original url when it is not a file', () => { + const url = filterURLForDisplay( + 'http://www.wordpress.org/wp-content/url/', + 20 + ); + expect( url ).toBe( 'wordpress.org/wp-content/url/' ); + } ); + it( 'should return file split by ellipsis when the file name has multiple periods', () => { + const url = filterURLForDisplay( + 'http://www.wordpress.org/wp-content/uploads/filename.2020.12.20.png', + 20 + ); + expect( url ).toBe( 'filename.202….20.png' ); + } ); } ); describe( 'cleanForSlug', () => { diff --git a/packages/viewport/CHANGELOG.md b/packages/viewport/CHANGELOG.md index ef834d12a913f5..300741034fb2f3 100644 --- a/packages/viewport/CHANGELOG.md +++ b/packages/viewport/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 2.25.0 (2020-12-17) + ### New Feature - Added a store definition `store` for the viewport namespace to use with `@wordpress/data` API ([#26655](https://github.com/WordPress/gutenberg/pull/26655)). diff --git a/packages/viewport/package.json b/packages/viewport/package.json index e428301295c934..6d296a500ea027 100644 --- a/packages/viewport/package.json +++ b/packages/viewport/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/viewport", - "version": "2.24.0", + "version": "2.25.0", "description": "Viewport module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -22,7 +22,7 @@ "module": "build-module/index.js", "react-native": "src/index", "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", "lodash": "^4.17.19" diff --git a/packages/wordcount/package.json b/packages/wordcount/package.json index e2b24e71327971..b70656d694b29d 100644 --- a/packages/wordcount/package.json +++ b/packages/wordcount/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/wordcount", - "version": "2.13.0", + "version": "2.14.0", "description": "WordPress word count utility.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -23,7 +23,7 @@ "react-native": "src/index", "sideEffects": false, "dependencies": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19" }, "publishConfig": { diff --git a/phpunit-watcher.yml.dist b/phpunit-watcher.yml.dist new file mode 100644 index 00000000000000..f2f9da5fbcdbfd --- /dev/null +++ b/phpunit-watcher.yml.dist @@ -0,0 +1,8 @@ +watch: + directories: + - ./lib/ + - ./phpunit/ + +notifications: + passingTests: false + failingTests: false diff --git a/phpunit/class-register-block-type-from-metadata-test.php b/phpunit/class-register-block-type-from-metadata-test.php deleted file mode 100644 index 6b4a6d8f063c27..00000000000000 --- a/phpunit/class-register-block-type-from-metadata-test.php +++ /dev/null @@ -1,216 +0,0 @@ -<?php -/** - * Test `register_block_type_from_metadata`. - * - * @package Gutenberg - */ - -/** - * This test can be removed when plugin support requires WordPress 5.5.0+. - * - * @see https://core.trac.wordpress.org/ticket/50263 - * @see https://core.trac.wordpress.org/changeset/48141 - */ -class Register_Block_Type_From_Metadata_Test extends WP_UnitTestCase { - - function test_does_not_remove_block_asset_path_prefix() { - $result = remove_block_asset_path_prefix( 'script-handle' ); - - $this->assertSame( 'script-handle', $result ); - } - - function test_removes_block_asset_path_prefix() { - $result = remove_block_asset_path_prefix( 'file:./block.js' ); - - $this->assertSame( './block.js', $result ); - } - - function test_generate_block_asset_handle() { - $block_name = 'unit-tests/my-block'; - - $this->assertSame( - 'unit-tests-my-block-editor-script', - generate_block_asset_handle( $block_name, 'editorScript' ) - ); - $this->assertSame( - 'unit-tests-my-block-script', - generate_block_asset_handle( $block_name, 'script' ) - ); - $this->assertSame( - 'unit-tests-my-block-editor-style', - generate_block_asset_handle( $block_name, 'editorStyle' ) - ); - $this->assertSame( - 'unit-tests-my-block-style', - generate_block_asset_handle( $block_name, 'style' ) - ); - } - - function test_field_not_found_register_block_script_handle() { - $result = register_block_script_handle( array(), 'script' ); - - $this->assertFalse( $result ); - } - - function test_empty_value_register_block_script_handle() { - $metadata = array( 'script' => '' ); - $result = register_block_script_handle( $metadata, 'script' ); - - $this->assertFalse( $result ); - } - - /** - * @expectedIncorrectUsage register_block_script_handle - */ - function test_missing_asset_file_register_block_script_handle() { - $metadata = array( - 'file' => __FILE__, - 'name' => 'unit-tests/test-block', - 'script' => 'file:./fixtures/missing-asset.js', - ); - $result = register_block_script_handle( $metadata, 'script' ); - - $this->assertFalse( $result ); - } - - function test_handle_passed_register_block_script_handle() { - $metadata = array( - 'editorScript' => 'test-script-handle', - ); - $result = register_block_script_handle( $metadata, 'editorScript' ); - - $this->assertSame( 'test-script-handle', $result ); - } - - function test_success_register_block_script_handle() { - $metadata = array( - 'file' => __FILE__, - 'name' => 'unit-tests/test-block', - 'script' => 'file:./fixtures/block.js', - ); - $result = register_block_script_handle( $metadata, 'script' ); - - $this->assertSame( 'unit-tests-test-block-script', $result ); - } - - function test_field_not_found_register_block_style_handle() { - $result = register_block_style_handle( array(), 'style' ); - - $this->assertFalse( $result ); - } - - function test_empty_value_found_register_block_style_handle() { - $metadata = array( 'style' => '' ); - $result = register_block_style_handle( $metadata, 'style' ); - - $this->assertFalse( $result ); - } - - function test_handle_passed_register_block_style_handle() { - $metadata = array( - 'style' => 'test-style-handle', - ); - $result = register_block_style_handle( $metadata, 'style' ); - - $this->assertSame( 'test-style-handle', $result ); - } - - function test_success_register_block_style_handle() { - $metadata = array( - 'file' => __FILE__, - 'name' => 'unit-tests/test-block', - 'style' => 'file:./fixtures/block.css', - ); - $result = register_block_style_handle( $metadata, 'style' ); - - $this->assertSame( 'unit-tests-test-block-style', $result ); - } - - /** - * Tests that the function returns false when the `block.json` is not found - * in the WordPress core. - */ - function test_metadata_not_found_in_wordpress_core() { - $result = register_block_type_from_metadata( 'unknown' ); - - $this->assertFalse( $result ); - } - - /** - * Tests that the function returns false when the `block.json` is not found - * in the current directory. - */ - function test_metadata_not_found_in_the_current_directory() { - $result = register_block_type_from_metadata( __DIR__ ); - - $this->assertFalse( $result ); - } - - /** - * Tests that the function returns the registered block when the `block.json` - * is found in the fixtures directory. - */ - function test_block_registers_with_metadata_fixture() { - $result = register_block_type_from_metadata( - __DIR__ . '/fixtures' - ); - - $this->assertInstanceOf( 'WP_Block_Type', $result ); - $this->assertSame( 'my-plugin/notice', $result->name ); - $this->assertSame( 'Notice', $result->title ); - $this->assertSame( 'common', $result->category ); - $this->assertEqualSets( array( 'core/group' ), $result->parent ); - $this->assertSame( 'star', $result->icon ); - $this->assertSame( 'Shows warning, error or success notices…', $result->description ); - $this->assertEqualSets( array( 'alert', 'message' ), $result->keywords ); - $this->assertEquals( - array( - 'message' => array( - 'type' => 'string', - 'source' => 'html', - 'selector' => '.message', - ), - ), - $result->attributes - ); - $this->assertEquals( - array( - 'my-plugin/message' => 'message', - ), - $result->provides_context - ); - $this->assertEqualSets( array( 'groupId' ), $result->uses_context ); - $this->assertEquals( - array( - 'align' => true, - ), - $result->supports - ); - $this->assertEquals( - array( - array( - 'name' => 'default', - 'label' => 'Default', - 'isDefault' => true, - ), - array( - 'name' => 'other', - 'label' => 'Other', - ), - ), - $result->styles - ); - $this->assertEquals( - array( - 'attributes' => array( - 'message' => 'This is a notice!', - ), - ), - $result->example - ); - $this->assertSame( 'my-plugin-notice-editor-script', $result->editor_script ); - $this->assertSame( 'my-plugin-notice-script', $result->script ); - $this->assertSame( 'my-plugin-notice-editor-style', $result->editor_style ); - $this->assertSame( 'my-plugin-notice-style', $result->style ); - } -} diff --git a/phpunit/class-wp-block-list-test.php b/phpunit/class-wp-block-list-test.php deleted file mode 100644 index 5f0d98d5b1afe0..00000000000000 --- a/phpunit/class-wp-block-list-test.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php -/** - * Test WP_Block_List class. - * - * @package Gutenberg - */ - -/** - * This test file can be removed when plugin support requires WordPress 5.5.0+. - * - * @see https://core.trac.wordpress.org/ticket/49926 - * @see https://core.trac.wordpress.org/changeset/48159 - */ -class WP_Block_List_Test extends WP_UnitTestCase { - - /** - * Fake block type registry. - * - * @var WP_Block_Type_Registry - */ - private $registry = null; - - /** - * Set up each test method. - */ - public function setUp() { - parent::setUp(); - - $this->registry = new WP_Block_Type_Registry(); - $this->registry->register( 'core/example', array() ); - } - - /** - * Tear down each test method. - */ - public function tearDown() { - parent::tearDown(); - - $this->registry = null; - } - - function test_array_access() { - $parsed_blocks = parse_blocks( '<!-- wp:example /-->' ); - $context = array(); - $blocks = new WP_Block_List( $parsed_blocks, $context, $this->registry ); - - // Test "offsetExists". - $this->assertTrue( isset( $blocks[0] ) ); - - // Test "offsetGet". - $this->assertEquals( 'core/example', $blocks[0]->name ); - - // Test "offsetSet". - $parsed_blocks[0]['blockName'] = 'core/updated'; - $blocks[0] = new WP_Block( $parsed_blocks[0], $context, $this->registry ); - $this->assertEquals( 'core/updated', $blocks[0]->name ); - - // Test "offsetUnset". - unset( $blocks[0] ); - $this->assertFalse( isset( $blocks[0] ) ); - } - - function test_iterable() { - $parsed_blocks = parse_blocks( '<!-- wp:example --><!-- wp:example /--><!-- /wp:example -->' ); - $context = array(); - $blocks = new WP_Block_List( $parsed_blocks, $context, $this->registry ); - $assertions = 0; - - foreach ( $blocks as $block ) { - $this->assertEquals( 'core/example', $block->name ); - $assertions++; - foreach ( $block->inner_blocks as $inner_block ) { - $this->assertEquals( 'core/example', $inner_block->name ); - $assertions++; - } - } - - $blocks->rewind(); - while ( $blocks->valid() ) { - $key = $blocks->key(); - $block = $blocks->current(); - $this->assertEquals( 0, $key ); - $assertions++; - $this->assertEquals( 'core/example', $block->name ); - $assertions++; - $blocks->next(); - } - - $this->assertEquals( 4, $assertions ); - } - - function test_countable() { - $parsed_blocks = parse_blocks( '<!-- wp:example /-->' ); - $context = array(); - $blocks = new WP_Block_List( $parsed_blocks, $context, $this->registry ); - - $this->assertEquals( 1, count( $blocks ) ); - } - -} diff --git a/phpunit/class-wp-block-test.php b/phpunit/class-wp-block-test.php deleted file mode 100644 index 3c7752118d09af..00000000000000 --- a/phpunit/class-wp-block-test.php +++ /dev/null @@ -1,355 +0,0 @@ -<?php -/** - * Test WP_Block class. - * - * @package Gutenberg - */ - -/** - * This test file can be removed when plugin support requires WordPress 5.5.0+. - * - * @see https://core.trac.wordpress.org/ticket/49926 - * @see https://core.trac.wordpress.org/changeset/48159 - */ -class WP_Block_Test extends WP_UnitTestCase { - - /** - * Fake block type registry. - * - * @var WP_Block_Type_Registry - */ - private $registry = null; - - /** - * Set up each test method. - */ - public function setUp() { - parent::setUp(); - - $this->registry = new WP_Block_Type_Registry(); - } - - /** - * Tear down each test method. - */ - public function tearDown() { - parent::tearDown(); - - $this->registry = null; - } - - function filter_render_block( $content, $parsed_block ) { - return 'Original: "' . $content . '", from block "' . $parsed_block['blockName'] . '"'; - } - - function test_constructor_assigns_properties_from_parsed_block() { - $this->registry->register( 'core/example', array() ); - - $parsed_blocks = parse_blocks( '<!-- wp:example {"ok":true} -->a<!-- wp:example /-->b<!-- /wp:example -->' ); - $parsed_block = $parsed_blocks[0]; - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertSame( $parsed_block, $block->parsed_block ); - $this->assertEquals( $parsed_block['blockName'], $block->name ); - $this->assertEquals( $parsed_block['attrs'], $block->attributes ); - $this->assertEquals( $parsed_block['innerContent'], $block->inner_content ); - $this->assertEquals( $parsed_block['innerHTML'], $block->inner_html ); - } - - function test_constructor_assigns_block_type_from_registry() { - $block_type_settings = array( - 'attributes' => array( - 'defaulted' => array( - 'type' => 'number', - 'default' => 10, - ), - ), - ); - $this->registry->register( 'core/example', $block_type_settings ); - - $parsed_block = array( 'blockName' => 'core/example' ); - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertInstanceOf( WP_Block_Type::class, $block->block_type ); - $this->assertEquals( - $block_type_settings['attributes'], - $block->block_type->attributes - ); - } - - function test_lazily_assigns_attributes_with_defaults() { - $this->registry->register( - 'core/example', - array( - 'attributes' => array( - 'defaulted' => array( - 'type' => 'number', - 'default' => 10, - ), - ), - ) - ); - - $parsed_block = array( - 'blockName' => 'core/example', - 'attrs' => array( - 'explicit' => 20, - ), - ); - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertEquals( - array( - 'defaulted' => 10, - 'explicit' => 20, - ), - $block->attributes - ); - } - - function test_lazily_assigns_attributes_with_only_defaults() { - $this->registry->register( - 'core/example', - array( - 'attributes' => array( - 'defaulted' => array( - 'type' => 'number', - 'default' => 10, - ), - ), - ) - ); - - $parsed_block = array( - 'blockName' => 'core/example', - 'attrs' => array(), - ); - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertEquals( array( 'defaulted' => 10 ), $block->attributes ); - // Intentionally call a second time, to ensure property was assigned. - $this->assertEquals( array( 'defaulted' => 10 ), $block->attributes ); - } - - function test_constructor_assigns_context_from_block_type() { - $this->registry->register( - 'core/example', - array( - 'uses_context' => array( 'requested' ), - ) - ); - - $parsed_block = array( 'blockName' => 'core/example' ); - $context = array( - 'requested' => 'included', - 'unrequested' => 'not included', - ); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertEquals( array( 'requested' => 'included' ), $block->context ); - } - - function test_constructor_maps_inner_blocks() { - $this->registry->register( 'core/example', array() ); - - $parsed_blocks = parse_blocks( '<!-- wp:example {"ok":true} -->a<!-- wp:example /-->b<!-- /wp:example -->' ); - $parsed_block = $parsed_blocks[0]; - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertCount( 1, $block->inner_blocks ); - $this->assertInstanceOf( WP_Block::class, $block->inner_blocks[0] ); - $this->assertEquals( 'core/example', $block->inner_blocks[0]->name ); - } - - function test_constructor_prepares_context_for_inner_blocks() { - $this->registry->register( - 'core/outer', - array( - 'attributes' => array( - 'recordId' => array( - 'type' => 'number', - ), - ), - 'provides_context' => array( - 'core/recordId' => 'recordId', - ), - ) - ); - $this->registry->register( - 'core/inner', - array( - 'uses_context' => array( 'core/recordId' ), - ) - ); - - $parsed_blocks = parse_blocks( '<!-- wp:outer {"recordId":10} --><!-- wp:inner /--><!-- /wp:outer -->' ); - $parsed_block = $parsed_blocks[0]; - $context = array( 'unrequested' => 'not included' ); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertCount( 0, $block->context ); - $this->assertEquals( - array( 'core/recordId' => 10 ), - $block->inner_blocks[0]->context - ); - } - - function test_constructor_assigns_merged_context() { - $this->registry->register( - 'core/example', - array( - 'attributes' => array( - 'value' => array( - 'type' => array( 'string', 'null' ), - ), - ), - 'provides_context' => array( - 'core/value' => 'value', - ), - 'uses_context' => array( 'core/value' ), - ) - ); - - $parsed_blocks = parse_blocks( - '<!-- wp:example {"value":"merged"} -->' . - '<!-- wp:example {"value":null} -->' . - '<!-- wp:example /-->' . - '<!-- /wp:example -->' . - '<!-- /wp:example -->' - ); - $parsed_block = $parsed_blocks[0]; - $context = array( 'core/value' => 'original' ); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertEquals( - array( 'core/value' => 'original' ), - $block->context - ); - $this->assertEquals( - array( 'core/value' => 'merged' ), - $block->inner_blocks[0]->context - ); - $this->assertEquals( - array( 'core/value' => null ), - $block->inner_blocks[0]->inner_blocks[0]->context - ); - } - - function test_render_static_block_type_returns_own_content() { - $this->registry->register( 'core/static', array() ); - $this->registry->register( - 'core/dynamic', - array( - 'render_callback' => function() { - return 'b'; - }, - ) - ); - - $parsed_blocks = parse_blocks( '<!-- wp:static -->a<!-- wp:dynamic /-->c<!-- /wp:static -->' ); - $parsed_block = $parsed_blocks[0]; - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertSame( 'abc', $block->render() ); - } - - function test_render_passes_block_for_render_callback() { - $this->registry->register( - 'core/greeting', - array( - 'render_callback' => function( $attributes, $content, $block ) { - return sprintf( 'Hello from %s', $block->name ); - }, - ) - ); - - $parsed_blocks = parse_blocks( '<!-- wp:greeting /-->' ); - $parsed_block = $parsed_blocks[0]; - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertSame( 'Hello from core/greeting', $block->render() ); - } - - function test_render_applies_render_block_filter() { - $this->registry->register( 'core/example', array() ); - - add_filter( 'render_block', array( $this, 'filter_render_block' ), 10, 2 ); - - $parsed_blocks = parse_blocks( '<!-- wp:example -->Static<!-- wp:example -->Inner<!-- /wp:example --><!-- /wp:example -->' ); - $parsed_block = $parsed_blocks[0]; - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $rendered_content = $block->render(); - - remove_filter( 'render_block', array( $this, 'filter_render_block' ) ); - - $this->assertSame( 'Original: "StaticOriginal: "Inner", from block "core/example"", from block "core/example"', $rendered_content ); - - } - - function test_passes_attributes_to_render_callback() { - $this->registry->register( - 'core/greeting', - array( - 'attributes' => array( - 'toWhom' => array( - 'type' => 'string', - ), - 'punctuation' => array( - 'type' => 'string', - 'default' => '!', - ), - ), - 'render_callback' => function( $block_attributes ) { - return sprintf( - 'Hello %s%s', - $block_attributes['toWhom'], - $block_attributes['punctuation'] - ); - }, - ) - ); - - $parsed_blocks = parse_blocks( '<!-- wp:greeting {"toWhom":"world"} /-->' ); - $parsed_block = $parsed_blocks[0]; - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertSame( 'Hello world!', $block->render() ); - } - - function test_passes_content_to_render_callback() { - $this->registry->register( - 'core/outer', - array( - 'render_callback' => function( $block_attributes, $content ) { - return $content; - }, - ) - ); - $this->registry->register( - 'core/inner', - array( - 'render_callback' => function() { - return 'b'; - }, - ) - ); - - $parsed_blocks = parse_blocks( '<!-- wp:outer -->a<!-- wp:inner /-->c<!-- /wp:outer -->' ); - $parsed_block = $parsed_blocks[0]; - $context = array(); - $block = new WP_Block( $parsed_block, $context, $this->registry ); - - $this->assertSame( 'abc', $block->render() ); - } - -} diff --git a/phpunit/class-theme-json-legacy-settings-test.php b/phpunit/class-wp-theme-json-legacy-settings-test.php similarity index 100% rename from phpunit/class-theme-json-legacy-settings-test.php rename to phpunit/class-wp-theme-json-legacy-settings-test.php diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 8682d0618ae546..d7573d21dac2da 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -8,6 +8,39 @@ class WP_Theme_JSON_Test extends WP_UnitTestCase { + function test_user_data_is_escaped() { + $theme_json = new WP_Theme_JSON( + array( + 'global' => array( + 'styles' => array( + 'color' => array( + 'background' => 'green', + 'gradient' => 'linear-gradient(10deg,rgba(6,147,227,1) 0%,rgb(61,132,163) 37%,rgb(155,81,224) 100%)', + 'link' => 'linear-gradient(10deg,rgba(6,147,227,1) 0%,rgb(61,132,163) 37%,rgb(155,81,224) 100%)', + 'text' => 'var:preset|color|dark-gray', + ), + ), + ), + ), + true + ); + $result = $theme_json->get_raw_data(); + + $expected = array( + 'global' => array( + 'styles' => array( + 'color' => array( + 'background' => 'green', + 'gradient' => 'linear-gradient(10deg,rgba(6,147,227,1) 0%,rgb(61,132,163) 37%,rgb(155,81,224) 100%)', + 'text' => 'var:preset|color|dark-gray', + ), + ), + ), + ); + + $this->assertEqualSetsWithIndex( $expected, $result ); + } + function test_contexts_not_valid_are_skipped() { $theme_json = new WP_Theme_JSON( array( @@ -31,20 +64,6 @@ function test_contexts_not_valid_are_skipped() { $expected = array( 'global' => array( - 'selector' => ':root', - 'supports' => array( - '--wp--style--color--link', - 'background', - 'backgroundColor', - 'color', - 'fontFamily', - 'fontSize', - 'fontStyle', - 'fontWeight', - 'lineHeight', - 'textDecoration', - 'textTransform', - ), 'settings' => array( 'color' => array( 'custom' => 'false', @@ -86,20 +105,6 @@ function test_properties_not_valid_are_skipped() { $expected = array( 'global' => array( - 'selector' => ':root', - 'supports' => array( - '--wp--style--color--link', - 'background', - 'backgroundColor', - 'color', - 'fontFamily', - 'fontSize', - 'fontStyle', - 'fontWeight', - 'lineHeight', - 'textDecoration', - 'textTransform', - ), 'settings' => array( 'color' => array( 'custom' => 'false', @@ -116,32 +121,6 @@ function test_properties_not_valid_are_skipped() { $this->assertEqualSetsWithIndex( $expected, $result ); } - function test_metadata_is_attached() { - $theme_json = new WP_Theme_JSON( array( 'global' => array() ) ); - $result = $theme_json->get_raw_data(); - - $expected = array( - 'global' => array( - 'selector' => ':root', - 'supports' => array( - '--wp--style--color--link', - 'background', - 'backgroundColor', - 'color', - 'fontFamily', - 'fontSize', - 'fontStyle', - 'fontWeight', - 'lineHeight', - 'textDecoration', - 'textTransform', - ), - ), - ); - - $this->assertEqualSetsWithIndex( $expected, $result ); - } - function test_get_settings() { // See schema at WP_Theme_JSON::SCHEMA. $theme_json = new WP_Theme_JSON( @@ -215,10 +194,18 @@ function test_get_stylesheet() { ) ); - $result = $theme_json->get_stylesheet(); - $stylesheet = ':root{--wp--style--color--link: #111;color: var(--wp--preset--color--grey);--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.has-grey-color{color: grey;}.has-grey-background-color{background-color: grey;}'; - - $this->assertEquals( $stylesheet, $result ); + $this->assertEquals( + $theme_json->get_stylesheet(), + ':root{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}:root{--wp--style--color--link: #111;color: var(--wp--preset--color--grey);}.has-grey-color{color: grey;}.has-grey-background-color{background-color: grey;}' + ); + $this->assertEquals( + $theme_json->get_stylesheet( 'block_styles' ), + ':root{--wp--style--color--link: #111;color: var(--wp--preset--color--grey);}.has-grey-color{color: grey;}.has-grey-background-color{background-color: grey;}' + ); + $this->assertEquals( + $theme_json->get_stylesheet( 'css_variables' ), + ':root{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}' + ); } public function test_merge_incoming_data() { @@ -344,40 +331,18 @@ public function test_merge_incoming_data() { ), ), 'typography' => array( - 'fontSizes' => array( + 'fontSizes' => array( array( 'slug' => 'fontSize', 'size' => 'fontSize', ), ), - 'fontFamilies' => array( + 'fontFamilies' => array( array( 'slug' => 'fontFamily', 'fontFamily' => 'fontFamily', ), ), - 'fontStyles' => array( - array( - 'slug' => 'fontStyle', - ), - ), - 'fontWeights' => array( - array( - 'slug' => 'fontWeight', - ), - ), - 'textDecorations' => array( - array( - 'slug' => 'textDecoration', - 'value' => 'textDecoration', - ), - ), - 'textTransforms' => array( - array( - 'slug' => 'textTransform', - 'value' => 'textTransform', - ), - ), ), ), ), @@ -385,20 +350,6 @@ public function test_merge_incoming_data() { $expected = array( 'global' => array( - 'selector' => ':root', - 'supports' => array( - '--wp--style--color--link', - 'background', - 'backgroundColor', - 'color', - 'fontFamily', - 'fontSize', - 'fontStyle', - 'fontWeight', - 'lineHeight', - 'textDecoration', - 'textTransform', - ), 'settings' => array( 'color' => array( 'custom' => 'true', @@ -417,40 +368,18 @@ public function test_merge_incoming_data() { ), ), 'typography' => array( - 'fontSizes' => array( + 'fontSizes' => array( array( 'slug' => 'fontSize', 'size' => 'fontSize', ), ), - 'fontFamilies' => array( + 'fontFamilies' => array( array( 'slug' => 'fontFamily', 'fontFamily' => 'fontFamily', ), ), - 'fontStyles' => array( - array( - 'slug' => 'fontStyle', - ), - ), - 'fontWeights' => array( - array( - 'slug' => 'fontWeight', - ), - ), - 'textDecorations' => array( - array( - 'slug' => 'textDecoration', - 'value' => 'textDecoration', - ), - ), - 'textTransforms' => array( - array( - 'slug' => 'textTransform', - 'value' => 'textTransform', - ), - ), ), ), 'styles' => array( @@ -460,14 +389,6 @@ public function test_merge_incoming_data() { ), ), 'core/paragraph' => array( - 'selector' => 'p', - 'supports' => array( - '--wp--style--color--link', - 'backgroundColor', - 'color', - 'fontSize', - 'lineHeight', - ), 'settings' => array( 'color' => array( 'custom' => 'false', @@ -484,13 +405,6 @@ public function test_merge_incoming_data() { ), ), 'core/list' => array( - 'selector' => '.wp-block-list', - 'supports' => array( - 'background', - 'backgroundColor', - 'color', - 'fontSize', - ), 'settings' => array( 'color' => array( 'custom' => 'false', diff --git a/readme.txt b/readme.txt index ec43e70b20b58f..2de8eb8c71c1e1 100644 --- a/readme.txt +++ b/readme.txt @@ -1,6 +1,6 @@ === Gutenberg === Contributors: matveb, joen, karmatosed -Requires at least: 5.4.0 +Requires at least: 5.5.0 Tested up to: 5.5 Requires PHP: 5.6 Stable tag: V.V.V @@ -57,4 +57,4 @@ View <a href="https://developer.wordpress.org/block-editor/principles/versions-i == Changelog == -To read the changelog for Gutenberg 9.4.1, please navigate to the <a href="https://github.com/WordPress/gutenberg/releases/tag/v9.4.1">release page</a>. +To read the changelog for Gutenberg 9.6.2, please navigate to the <a href="https://github.com/WordPress/gutenberg/releases/tag/v9.6.2">release page</a>. diff --git a/storybook/.babelrc b/storybook/.babelrc index b2b3e792e2f972..fe77bd84598748 100644 --- a/storybook/.babelrc +++ b/storybook/.babelrc @@ -3,10 +3,5 @@ "plugins": [ "babel-plugin-emotion", "babel-plugin-inline-json-import" - ], - "env": { - "test": { - "plugins": [ "babel-plugin-require-context-hook" ] - } - } + ] } diff --git a/test/native/__mocks__/styleMock.js b/test/native/__mocks__/styleMock.js index de672610c66eb0..f01d424bb9d162 100644 --- a/test/native/__mocks__/styleMock.js +++ b/test/native/__mocks__/styleMock.js @@ -96,4 +96,14 @@ module.exports = { scrollableContent: { paddingBottom: 20, }, + buttonText: { + color: 'white', + }, + placeholderTextColor: { + color: 'white', + }, + defaultButton: { + paddingLeft: 10, + paddingRight: 10, + }, }; diff --git a/test/unit/config/register-context.js b/test/unit/config/register-context.js deleted file mode 100644 index b73423550f42c6..00000000000000 --- a/test/unit/config/register-context.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * External dependencies - */ -import registerRequireContextHook from 'babel-plugin-require-context-hook/register'; - -registerRequireContextHook(); diff --git a/test/unit/jest.config.js b/test/unit/jest.config.js index fa37927d7c7c48..ee73efecd04029 100644 --- a/test/unit/jest.config.js +++ b/test/unit/jest.config.js @@ -19,7 +19,6 @@ module.exports = { setupFiles: [ '<rootDir>/test/unit/config/global-mocks.js', '<rootDir>/test/unit/config/gutenberg-phase.js', - '<rootDir>/test/unit/config/register-context.js', ], testURL: 'http://localhost', testPathIgnorePatterns: [ diff --git a/tsconfig.base.json b/tsconfig.base.json index 94d0f395dc985d..495796402bdf85 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -13,9 +13,6 @@ "emitDeclarationOnly": true, "isolatedModules": true, - /* Prevent type-only imports from impacting the dependency graph */ - "importsNotUsedAsValues": "error", - /* Strict Type-Checking Options */ "strict": true, @@ -40,8 +37,8 @@ "**/*.ios.js", "**/*.native.js", "**/benchmark", - "**/build-*/**", - "**/build/**", + "packages/*/build-*/**", + "packages/*/build/**", "**/test/**", "packages/**/react-native-*/**" ] diff --git a/tsconfig.json b/tsconfig.json index 22e0a1276f5215..003daf1af802cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,10 +6,8 @@ { "path": "packages/blob" }, { "path": "packages/block-editor" }, { "path": "packages/components" }, - { "path": "packages/data" }, { "path": "packages/deprecated" }, { "path": "packages/element" }, - { "path": "packages/dependency-extraction-webpack-plugin" }, { "path": "packages/dom-ready" }, { "path": "packages/escape-html" }, { "path": "packages/eslint-plugin" }, @@ -23,7 +21,6 @@ { "path": "packages/primitives" }, { "path": "packages/priority-queue" }, { "path": "packages/project-management-automation" }, - { "path": "packages/stan" }, { "path": "packages/token-list" }, { "path": "packages/url" }, { "path": "packages/warning" }, diff --git a/webpack.config.js b/webpack.config.js index 511667e05ac0be..de8be6fab09e98 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -30,11 +30,7 @@ const { } = process.env; const WORDPRESS_NAMESPACE = '@wordpress/'; -const BUNDLED_PACKAGES = [ - '@wordpress/icons', - '@wordpress/interface', - '@wordpress/stan', -]; +const BUNDLED_PACKAGES = [ '@wordpress/icons', '@wordpress/interface' ]; const gutenbergPackages = Object.keys( dependencies ) .filter( @@ -45,6 +41,29 @@ const gutenbergPackages = Object.keys( dependencies ) ) .map( ( packageName ) => packageName.replace( WORDPRESS_NAMESPACE, '' ) ); +const stylesTransform = ( content ) => { + if ( mode === 'production' ) { + return postcss( [ + require( 'cssnano' )( { + preset: [ + 'default', + { + discardComments: { + removeAll: true, + }, + }, + ], + } ), + ] ) + .process( content, { + from: 'src/app.css', + to: 'dest/app.css', + } ) + .then( ( result ) => result.css ); + } + return content; +}; + module.exports = { optimization: { // Only concatenate modules in production, when not analyzing bundles. @@ -144,30 +163,43 @@ module.exports = { from: `./packages/${ packageName }/build-style/*.css`, to: `./build/${ packageName }/`, flatten: true, - transform: ( content ) => { - if ( mode === 'production' ) { - return postcss( [ - require( 'cssnano' )( { - preset: [ - 'default', - { - discardComments: { - removeAll: true, - }, - }, - ], - } ), - ] ) - .process( content, { - from: 'src/app.css', - to: 'dest/app.css', - } ) - .then( ( result ) => result.css ); - } - return content; - }, + transform: stylesTransform, } ) ) ), + new CopyWebpackPlugin( [ + { + from: './packages/block-library/build-style/*/style.css', + test: new RegExp( + `([\\w-]+)${ escapeRegExp( sep ) }style\\.css$` + ), + to: 'build/block-library/blocks/[1]/style.css', + transform: stylesTransform, + }, + { + from: './packages/block-library/build-style/*/style-rtl.css', + test: new RegExp( + `([\\w-]+)${ escapeRegExp( sep ) }style-rtl\\.css$` + ), + to: 'build/block-library/blocks/[1]/style-rtl.css', + transform: stylesTransform, + }, + { + from: './packages/block-library/build-style/*/editor.css', + test: new RegExp( + `([\\w-]+)${ escapeRegExp( sep ) }editor\\.css$` + ), + to: 'build/block-library/blocks/[1]/editor.css', + transform: stylesTransform, + }, + { + from: './packages/block-library/build-style/*/editor-rtl.css', + test: new RegExp( + `([\\w-]+)${ escapeRegExp( sep ) }editor-rtl\\.css$` + ), + to: 'build/block-library/blocks/[1]/editor-rtl.css', + transform: stylesTransform, + }, + ] ), new CopyWebpackPlugin( Object.entries( { './packages/block-library/src/': 'build/block-library/blocks/',