diff --git a/package-lock.json b/package-lock.json index db2d55f6..6a2cfeb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,7 +54,7 @@ "raf": "3.4.1", "react": "16.14.0", "react-dom": "16.14.0", - "react-intl": "2.9.0", + "react-intl": "^6.6.8", "react-intl-redux": "2.4.1", "react-popover": "0.5.10", "react-redux": "5.1.2", @@ -83,7 +83,7 @@ "peerDependencies": { "react": "^16", "react-dom": "^16", - "react-intl": "^2", + "react-intl": "^6", "react-intl-redux": "^0.7 || ^2.0.0", "react-popover": "^0.5", "react-redux": "^5", @@ -2829,6 +2829,100 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", + "integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", + "dev": true, + "dependencies": { + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz", + "integrity": "sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/icu-skeleton-parser": "1.8.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz", + "integrity": "sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl": { + "version": "2.10.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.4.tgz", + "integrity": "sha512-56483O+HVcL0c7VucAS2tyH020mt9XTozZO67cwtGg0a7KWDukS/FzW3OnvaHmTHDuYsoPIzO+ZHVfU6fT/bJw==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "@formatjs/intl-displaynames": "6.6.8", + "@formatjs/intl-listformat": "7.5.7", + "intl-messageformat": "10.5.14", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "6.6.8", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.8.tgz", + "integrity": "sha512-Lgx6n5KxN16B3Pb05z3NLEBQkGoXnGjkTBNCZI+Cn17YjHJ3fhCeEJJUqRlIZmJdmaXQhjcQVDp6WIiNeRYT5g==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-listformat": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.7.tgz", + "integrity": "sha512-MG2TSChQJQT9f7Rlv+eXwUFiG24mKSzmF144PLb8m8OixyXqn4+YWU+5wZracZGCgVTVmx8viCf7IH3QXoiB2g==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", + "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -5482,6 +5576,16 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dev": true, + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -5565,6 +5669,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.3.6", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.6.tgz", + "integrity": "sha512-CnGaRYNu2iZlkGXGrOYtdg5mLK8neySj0woZ4e2wF/eli2E6Sazmq5X+Nrj6OBrrFVQfJWTUFeqAzoRhWQXYvg==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -9183,6 +9303,12 @@ "dev": true, "license": "MIT" }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, "node_modules/cyclist": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.2.tgz", @@ -14965,21 +15091,16 @@ "node": ">= 0.10" } }, - "node_modules/intl-format-cache": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.2.9.tgz", - "integrity": "sha512-Zv/u8wRpekckv0cLkwpVdABYST4hZNTDaX7reFetrYTJwxExR2VyTqQm+l0WmL0Qo8Mjb9Tf33qnfj0T7pjxdQ==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/intl-messageformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", - "integrity": "sha512-I+tSvHnXqJYjDfNmY95tpFMj30yoakC6OXAo+wu/wTMy6tA/4Fd4mvV7Uzs4cqK/Ap29sHhwjcY+78a8eifcXw==", + "version": "10.5.14", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz", + "integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "intl-messageformat-parser": "1.4.0" + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "tslib": "^2.4.0" } }, "node_modules/intl-messageformat-parser": { @@ -14990,25 +15111,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/intl-messageformat/node_modules/intl-messageformat-parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", - "integrity": "sha512-/XkqFHKezO6UcF4Av2/Lzfrez18R0jyw7kRFhSeB/YRakdrgSc9QfFZUwNJI9swMwMoNPygK1ArC5wdFSjPw+A==", - "deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/intl-relativeformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.2.0.tgz", - "integrity": "sha512-4bV/7kSKaPEmu6ArxXf9xjv1ny74Zkwuey8Pm01NH4zggPP7JHwg2STk8Y3JdspCKRDriwIyLRfEXnj2ZLr4Bw==", - "deprecated": "This package has been deprecated, please see migration guide at 'https://github.com/formatjs/formatjs/tree/master/packages/intl-relativeformat#migration-guide'", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "intl-messageformat": "^2.0.0" - } - }, "node_modules/into-stream": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", @@ -26408,21 +26510,30 @@ } }, "node_modules/react-intl": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.9.0.tgz", - "integrity": "sha512-27jnDlb/d2A7mSJwrbOBnUgD+rPep+abmoJE511Tf8BnoONIAUehy/U1zZCHGO17mnOwMWxqN4qC0nW11cD6rA==", + "version": "6.6.8", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.8.tgz", + "integrity": "sha512-M0pkhzcgV31h++2901BiRXWl69hp2zPyLxRrSwRjd1ErXbNoubz/f4M6DrRTd4OiSUrT4ajRQzrmtS5plG4FtA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "hoist-non-react-statics": "^3.3.0", - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.1.0", - "invariant": "^2.1.1" + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "@formatjs/intl": "2.10.4", + "@formatjs/intl-displaynames": "6.6.8", + "@formatjs/intl-listformat": "7.5.7", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "10.5.14", + "tslib": "^2.4.0" }, "peerDependencies": { - "prop-types": "^15.5.4", - "react": "^0.14.9 || ^15.0.0 || ^16.0.0" + "react": "^16.6.0 || 17 || 18", + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/react-intl-redux": { @@ -26430,7 +26541,6 @@ "resolved": "https://registry.npmjs.org/react-intl-redux/-/react-intl-redux-2.4.1.tgz", "integrity": "sha512-EYTNmHJTnTam4phQj1nTdJvcdVjz+F56nLl6JtpqWsKzG5ZnQh/hoqLLJUjP0dgeNKSESIcjhYsTyBWDUwjo0A==", "dev": true, - "license": "MIT", "dependencies": { "@babel/runtime": "^7.17.9", "prop-types": "^15.8.1" @@ -31912,6 +32022,12 @@ "node": ">=4" } }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true + }, "node_modules/tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", diff --git a/package.json b/package.json index 34ab4872..f739018f 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "peerDependencies": { "react": "^16", "react-dom": "^16", - "react-intl": "^2", + "react-intl": "^6", "react-intl-redux": "^0.7 || ^2.0.0", "react-popover": "^0.5", "react-redux": "^5", @@ -81,7 +81,7 @@ "raf": "3.4.1", "react": "16.14.0", "react-dom": "16.14.0", - "react-intl": "2.9.0", + "react-intl": "^6.6.8", "react-intl-redux": "2.4.1", "react-popover": "0.5.10", "react-redux": "5.1.2", @@ -121,7 +121,10 @@ "moduleNameMapper": { "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/fileMock.js", "\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js" - } + }, + "transformIgnorePatterns": [ + "/node_modules/(?!intl-messageformat|intl-messageformat-parser).+\\.js$" + ] }, "jest-junit": { "outputDirectory": "./test/results" diff --git a/src/components/color-picker/color-picker.jsx b/src/components/color-picker/color-picker.jsx index 398c5cc6..d09c90e1 100644 --- a/src/components/color-picker/color-picker.jsx +++ b/src/components/color-picker/color-picker.jsx @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl'; +import {defineMessages, FormattedMessage, injectIntl} from 'react-intl'; +import intlShape from '../../lib/intl-shape.js'; import classNames from 'classnames'; import parseColor from 'parse-color'; diff --git a/src/components/coming-soon/coming-soon.jsx b/src/components/coming-soon/coming-soon.jsx index fa9285b0..5644d926 100644 --- a/src/components/coming-soon/coming-soon.jsx +++ b/src/components/coming-soon/coming-soon.jsx @@ -4,7 +4,8 @@ See #13 */ import bindAll from 'lodash.bindall'; import classNames from 'classnames'; -import {defineMessages, injectIntl, intlShape, FormattedMessage} from 'react-intl'; +import {defineMessages, injectIntl, FormattedMessage} from 'react-intl'; +import intlShape from '../../lib/intl-shape.js'; import PropTypes from 'prop-types'; import React from 'react'; import ReactTooltip from 'react-tooltip'; diff --git a/src/components/fixed-tools/fixed-tools.jsx b/src/components/fixed-tools/fixed-tools.jsx index b1d325fa..bd675fcb 100644 --- a/src/components/fixed-tools/fixed-tools.jsx +++ b/src/components/fixed-tools/fixed-tools.jsx @@ -11,7 +11,7 @@ import BufferedInputHOC from '../forms/buffered-input-hoc.jsx'; import Button from '../button/button.jsx'; import ButtonGroup from '../button-group/button-group.jsx'; import Dropdown from '../dropdown/dropdown.jsx'; -import {defineMessages, injectIntl, intlShape} from 'react-intl'; +import {defineMessages, useIntl} from 'react-intl'; import Formats, {isVector} from '../../lib/format'; import Input from '../forms/input.jsx'; import InputGroup from '../input-group/input-group.jsx'; @@ -87,13 +87,14 @@ const messages = defineMessages({ const FixedToolsComponent = props => { const redoDisabled = !props.canRedo(); const undoDisabled = !props.canUndo(); + const intl = useIntl(); return ( <div className={styles.row}> {/* Name field */} <InputGroup> <MediaQuery minWidth={layout.fullSizeEditorMinWidth}> - <Label text={props.intl.formatMessage(messages.costume)}> + <Label text={intl.formatMessage(messages.costume)}> <BufferedInput className={styles.costumeInput} type="text" @@ -128,7 +129,7 @@ const FixedToolsComponent = props => { onClick={props.onUndo} > <img - alt={props.intl.formatMessage(messages.undo)} + alt={intl.formatMessage(messages.undo)} className={classNames( styles.buttonGroupButtonIcon, styles.undoIcon @@ -150,7 +151,7 @@ const FixedToolsComponent = props => { onClick={props.onRedo} > <img - alt={props.intl.formatMessage(messages.redo)} + alt={intl.formatMessage(messages.redo)} className={styles.buttonGroupButtonIcon} draggable={false} src={redoIcon} @@ -164,16 +165,16 @@ const FixedToolsComponent = props => { <InputGroup className={styles.modDashedBorder}> <LabeledIconButton disabled={!shouldShowGroup()} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={groupIcon} - title={props.intl.formatMessage(messages.group)} + title={intl.formatMessage(messages.group)} onClick={props.onGroup} /> <LabeledIconButton disabled={!shouldShowUngroup()} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={ungroupIcon} - title={props.intl.formatMessage(messages.ungroup)} + title={intl.formatMessage(messages.ungroup)} onClick={props.onUngroup} /> </InputGroup> : null @@ -184,16 +185,16 @@ const FixedToolsComponent = props => { <InputGroup className={styles.modDashedBorder}> <LabeledIconButton disabled={!shouldShowBringForward()} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={sendForwardIcon} - title={props.intl.formatMessage(messages.forward)} + title={intl.formatMessage(messages.forward)} onClick={props.onSendForward} /> <LabeledIconButton disabled={!shouldShowSendBackward()} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={sendBackwardIcon} - title={props.intl.formatMessage(messages.backward)} + title={intl.formatMessage(messages.backward)} onClick={props.onSendBackward} /> </InputGroup> : null @@ -204,16 +205,16 @@ const FixedToolsComponent = props => { <InputGroup className={styles.row}> <LabeledIconButton disabled={!shouldShowBringForward()} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={sendFrontIcon} - title={props.intl.formatMessage(messages.front)} + title={intl.formatMessage(messages.front)} onClick={props.onSendToFront} /> <LabeledIconButton disabled={!shouldShowSendBackward()} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={sendBackIcon} - title={props.intl.formatMessage(messages.back)} + title={intl.formatMessage(messages.back)} onClick={props.onSendToBack} /> </InputGroup> @@ -252,7 +253,7 @@ const FixedToolsComponent = props => { draggable={false} src={sendFrontIcon} /> - <span>{props.intl.formatMessage(messages.front)}</span> + <span>{intl.formatMessage(messages.front)}</span> </Button> <Button className={classNames(styles.modMenuItem, { @@ -266,7 +267,7 @@ const FixedToolsComponent = props => { draggable={false} src={sendBackIcon} /> - <span>{props.intl.formatMessage(messages.back)}</span> + <span>{intl.formatMessage(messages.back)}</span> </Button> {/* To be rotation point */} @@ -285,7 +286,7 @@ const FixedToolsComponent = props => { } tipSize={.01} > - {props.intl.formatMessage(messages.more)} + {intl.formatMessage(messages.more)} </Dropdown> </InputGroup> </MediaQuery> : null @@ -298,7 +299,6 @@ FixedToolsComponent.propTypes = { canRedo: PropTypes.func.isRequired, canUndo: PropTypes.func.isRequired, format: PropTypes.oneOf(Object.keys(Formats)), - intl: intlShape, name: PropTypes.string, onGroup: PropTypes.func.isRequired, onRedo: PropTypes.func.isRequired, @@ -321,4 +321,4 @@ const mapStateToProps = state => ({ export default connect( mapStateToProps -)(injectIntl(FixedToolsComponent)); +)(FixedToolsComponent); diff --git a/src/components/mode-tools/mode-tools.jsx b/src/components/mode-tools/mode-tools.jsx index 78771edb..a86f4ea4 100644 --- a/src/components/mode-tools/mode-tools.jsx +++ b/src/components/mode-tools/mode-tools.jsx @@ -12,7 +12,7 @@ import {setShapesFilled} from '../../reducers/fill-bitmap-shapes'; import FontDropdown from '../../containers/font-dropdown.jsx'; import LiveInputHOC from '../forms/live-input-hoc.jsx'; import Label from '../forms/label.jsx'; -import {defineMessages, injectIntl, intlShape} from 'react-intl'; +import {defineMessages, useIntl} from 'react-intl'; import Input from '../forms/input.jsx'; import InputGroup from '../input-group/input-group.jsx'; import LabeledIconButton from '../labeled-icon-button/labeled-icon-button.jsx'; @@ -43,6 +43,7 @@ import {MAX_STROKE_WIDTH} from '../../reducers/stroke-width'; const LiveInput = LiveInputHOC(Input); const ModeToolsComponent = props => { + const intl = useIntl(); const messages = defineMessages({ brushSize: { defaultMessage: 'Size', @@ -122,7 +123,7 @@ const ModeToolsComponent = props => { <div className={classNames(props.className, styles.modeTools)}> <div> <img - alt={props.intl.formatMessage(currentMessage)} + alt={intl.formatMessage(currentMessage)} className={styles.modeToolsIcon} draggable={false} src={currentIcon} @@ -151,7 +152,7 @@ const ModeToolsComponent = props => { <div className={classNames(props.className, styles.modeTools)}> <div> <img - alt={props.intl.formatMessage(messages.eraserSize)} + alt={intl.formatMessage(messages.eraserSize)} className={styles.modeToolsIcon} draggable={false} src={currentIcon} @@ -175,24 +176,24 @@ const ModeToolsComponent = props => { <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}> <LabeledIconButton disabled={!props.hasSelectedUncurvedPoints} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={curvedPointIcon} - title={props.intl.formatMessage(messages.curved)} + title={intl.formatMessage(messages.curved)} onClick={props.onCurvePoints} /> <LabeledIconButton disabled={!props.hasSelectedUnpointedPoints} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={straightPointIcon} - title={props.intl.formatMessage(messages.pointed)} + title={intl.formatMessage(messages.pointed)} onClick={props.onPointPoints} /> </InputGroup> <InputGroup className={classNames(styles.modLabeledIconHeight)}> <LabeledIconButton - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={deleteIcon} - title={props.intl.formatMessage(messages.delete)} + title={intl.formatMessage(messages.delete)} onClick={props.onDelete} /> </InputGroup> @@ -205,38 +206,38 @@ const ModeToolsComponent = props => { <div className={classNames(props.className, styles.modeTools)}> <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}> <LabeledIconButton - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={copyIcon} - title={props.intl.formatMessage(messages.copy)} + title={intl.formatMessage(messages.copy)} onClick={props.onCopyToClipboard} /> <LabeledIconButton disabled={!(props.clipboardItems.length > 0)} - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={pasteIcon} - title={props.intl.formatMessage(messages.paste)} + title={intl.formatMessage(messages.paste)} onClick={props.onPasteFromClipboard} /> </InputGroup> <InputGroup className={classNames(styles.modDashedBorder, styles.modLabeledIconHeight)}> <LabeledIconButton - hideLabel={hideLabel(props.intl.locale)} + hideLabel={hideLabel(intl.locale)} imgSrc={deleteIcon} - title={props.intl.formatMessage(messages.delete)} + title={intl.formatMessage(messages.delete)} onClick={props.onDelete} /> </InputGroup> <InputGroup className={classNames(styles.modLabeledIconHeight)}> <LabeledIconButton - hideLabel={props.intl.locale !== 'en'} + hideLabel={intl.locale !== 'en'} imgSrc={flipHorizontalIcon} - title={props.intl.formatMessage(messages.flipHorizontal)} + title={intl.formatMessage(messages.flipHorizontal)} onClick={props.onFlipHorizontal} /> <LabeledIconButton - hideLabel={props.intl.locale !== 'en'} + hideLabel={intl.locale !== 'en'} imgSrc={flipVerticalIcon} - title={props.intl.formatMessage(messages.flipVertical)} + title={intl.formatMessage(messages.flipVertical)} onClick={props.onFlipVertical} /> </InputGroup> @@ -266,7 +267,7 @@ const ModeToolsComponent = props => { <LabeledIconButton highlighted={props.fillBitmapShapes} imgSrc={fillIcon} - title={props.intl.formatMessage(messages.filled)} + title={intl.formatMessage(messages.filled)} onClick={props.onFillShapes} /> </InputGroup> @@ -274,13 +275,13 @@ const ModeToolsComponent = props => { <LabeledIconButton highlighted={!props.fillBitmapShapes} imgSrc={outlineIcon} - title={props.intl.formatMessage(messages.outlined)} + title={intl.formatMessage(messages.outlined)} onClick={props.onOutlineShapes} /> </InputGroup> {props.fillBitmapShapes ? null : ( <InputGroup> - <Label text={props.intl.formatMessage(messages.thickness)}> + <Label text={intl.formatMessage(messages.thickness)}> <LiveInput range small @@ -315,7 +316,6 @@ ModeToolsComponent.propTypes = { format: PropTypes.oneOf(Object.keys(Formats)), hasSelectedUncurvedPoints: PropTypes.bool, hasSelectedUnpointedPoints: PropTypes.bool, - intl: intlShape.isRequired, mode: PropTypes.string.isRequired, onBitBrushSliderChange: PropTypes.func.isRequired, onBitEraserSliderChange: PropTypes.func.isRequired, @@ -367,4 +367,4 @@ const mapDispatchToProps = dispatch => ({ export default connect( mapStateToProps, mapDispatchToProps -)(injectIntl(ModeToolsComponent)); +)(ModeToolsComponent); diff --git a/src/components/paint-editor/paint-editor.jsx b/src/components/paint-editor/paint-editor.jsx index c94d3585..7ac4e062 100644 --- a/src/components/paint-editor/paint-editor.jsx +++ b/src/components/paint-editor/paint-editor.jsx @@ -1,6 +1,6 @@ import paper from '@scratch/paper'; import classNames from 'classnames'; -import {defineMessages, injectIntl, intlShape} from 'react-intl'; +import {defineMessages, useIntl} from 'react-intl'; import React from 'react'; import PropTypes from 'prop-types'; @@ -55,56 +55,29 @@ const messages = defineMessages({ } }); -const PaintEditorComponent = props => ( - <div - className={styles.editorContainer} - dir={props.rtl ? 'rtl' : 'ltr'} - > - {props.canvas !== null ? ( // eslint-disable-line no-negated-condition - <div className={styles.editorContainerTop}> - {/* First row */} - <div className={styles.row}> - <FixedToolsContainer - canRedo={props.canRedo} - canUndo={props.canUndo} - name={props.name} - onRedo={props.onRedo} - onUndo={props.onUndo} - onUpdateImage={props.onUpdateImage} - onUpdateName={props.onUpdateName} - /> - </div> - {/* Second Row */} - {isVector(props.format) ? +const PaintEditorComponent = props => { + const intl = useIntl(); + return ( + <div + className={styles.editorContainer} + dir={props.rtl ? 'rtl' : 'ltr'} + > + {props.canvas !== null ? ( // eslint-disable-line no-negated-condition + <div className={styles.editorContainerTop}> + {/* First row */} <div className={styles.row}> - <InputGroup - className={classNames( - styles.row, - styles.modDashedBorder, - styles.modLabeledIconHeight - )} - > - {/* fill */} - <FillColorIndicatorComponent - className={styles.modMarginAfter} - onUpdateImage={props.onUpdateImage} - /> - {/* stroke */} - <StrokeColorIndicatorComponent - onUpdateImage={props.onUpdateImage} - /> - {/* stroke width */} - <StrokeWidthIndicatorComponent - onUpdateImage={props.onUpdateImage} - /> - </InputGroup> - <InputGroup className={styles.modModeTools}> - <ModeToolsContainer - onUpdateImage={props.onUpdateImage} - /> - </InputGroup> - </div> : - isBitmap(props.format) ? + <FixedToolsContainer + canRedo={props.canRedo} + canUndo={props.canUndo} + name={props.name} + onRedo={props.onRedo} + onUndo={props.onUndo} + onUpdateImage={props.onUpdateImage} + onUpdateName={props.onUpdateName} + /> + </div> + {/* Second Row */} + {isVector(props.format) ? <div className={styles.row}> <InputGroup className={classNames( @@ -118,136 +91,151 @@ const PaintEditorComponent = props => ( className={styles.modMarginAfter} onUpdateImage={props.onUpdateImage} /> + {/* stroke */} + <StrokeColorIndicatorComponent + onUpdateImage={props.onUpdateImage} + /> + {/* stroke width */} + <StrokeWidthIndicatorComponent + onUpdateImage={props.onUpdateImage} + /> </InputGroup> <InputGroup className={styles.modModeTools}> <ModeToolsContainer onUpdateImage={props.onUpdateImage} /> </InputGroup> - </div> : null - } - </div> - ) : null} - - <div className={styles.topAlignRow}> - {/* Modes */} - {props.canvas !== null && isVector(props.format) ? ( // eslint-disable-line no-negated-condition - <div className={styles.modeSelector}> - <SelectMode - onUpdateImage={props.onUpdateImage} - /> - <ReshapeMode - onUpdateImage={props.onUpdateImage} - /> - <BrushMode - onUpdateImage={props.onUpdateImage} - /> - <EraserMode - onUpdateImage={props.onUpdateImage} - /> - <FillMode - onUpdateImage={props.onUpdateImage} - /> - <TextMode - textArea={props.textArea} - onUpdateImage={props.onUpdateImage} - /> - <LineMode - onUpdateImage={props.onUpdateImage} - /> - <OvalMode - onUpdateImage={props.onUpdateImage} - /> - <RectMode - onUpdateImage={props.onUpdateImage} - /> - </div> - ) : null} - - {props.canvas !== null && isBitmap(props.format) ? ( // eslint-disable-line no-negated-condition - <div className={styles.modeSelector}> - <BitBrushMode - onUpdateImage={props.onUpdateImage} - /> - <BitLineMode - onUpdateImage={props.onUpdateImage} - /> - <BitOvalMode - onUpdateImage={props.onUpdateImage} - /> - <BitRectMode - onUpdateImage={props.onUpdateImage} - /> - <TextMode - isBitmap - textArea={props.textArea} - onUpdateImage={props.onUpdateImage} - /> - <BitFillMode - onUpdateImage={props.onUpdateImage} - /> - <BitEraserMode - onUpdateImage={props.onUpdateImage} - /> - <BitSelectMode - onUpdateImage={props.onUpdateImage} - /> - </div> - ) : null} - - <div className={styles.controlsContainer}> - {/* Canvas */} - <ScrollableCanvas - canvas={props.canvas} - hideScrollbars={props.isEyeDropping} - style={styles.canvasContainer} - > - <PaperCanvas - canvasRef={props.setCanvas} - image={props.image} - imageFormat={props.imageFormat} - imageId={props.imageId} - rotationCenterX={props.rotationCenterX} - rotationCenterY={props.rotationCenterY} - zoomLevelId={props.zoomLevelId} - onUpdateImage={props.onUpdateImage} - /> - <textarea - className={styles.textArea} - ref={props.setTextArea} - spellCheck={false} - /> - {props.isEyeDropping && - props.colorInfo !== null && - !props.colorInfo.hideLoupe ? ( - <Box className={styles.colorPickerWrapper}> - <Loupe - colorInfo={props.colorInfo} - pixelRatio={paper.project.view.pixelRatio} - /> - </Box> - ) : null - } - </ScrollableCanvas> - <div className={styles.canvasControls}> - {isVector(props.format) ? - <Button - className={styles.bitmapButton} - onClick={props.onSwitchToBitmap} - > - <img - className={styles.bitmapButtonIcon} - draggable={false} - src={bitmapIcon} - /> - <span className={styles.buttonText}> - {props.intl.formatMessage(messages.bitmap)} - </span> - </Button> : + </div> : isBitmap(props.format) ? + <div className={styles.row}> + <InputGroup + className={classNames( + styles.row, + styles.modDashedBorder, + styles.modLabeledIconHeight + )} + > + {/* fill */} + <FillColorIndicatorComponent + className={styles.modMarginAfter} + onUpdateImage={props.onUpdateImage} + /> + </InputGroup> + <InputGroup className={styles.modModeTools}> + <ModeToolsContainer + onUpdateImage={props.onUpdateImage} + /> + </InputGroup> + </div> : null + } + </div> + ) : null} + + <div className={styles.topAlignRow}> + {/* Modes */} + {props.canvas !== null && isVector(props.format) ? ( // eslint-disable-line no-negated-condition + <div className={styles.modeSelector}> + <SelectMode + onUpdateImage={props.onUpdateImage} + /> + <ReshapeMode + onUpdateImage={props.onUpdateImage} + /> + <BrushMode + onUpdateImage={props.onUpdateImage} + /> + <EraserMode + onUpdateImage={props.onUpdateImage} + /> + <FillMode + onUpdateImage={props.onUpdateImage} + /> + <TextMode + textArea={props.textArea} + onUpdateImage={props.onUpdateImage} + /> + <LineMode + onUpdateImage={props.onUpdateImage} + /> + <OvalMode + onUpdateImage={props.onUpdateImage} + /> + <RectMode + onUpdateImage={props.onUpdateImage} + /> + </div> + ) : null} + + {props.canvas !== null && isBitmap(props.format) ? ( // eslint-disable-line no-negated-condition + <div className={styles.modeSelector}> + <BitBrushMode + onUpdateImage={props.onUpdateImage} + /> + <BitLineMode + onUpdateImage={props.onUpdateImage} + /> + <BitOvalMode + onUpdateImage={props.onUpdateImage} + /> + <BitRectMode + onUpdateImage={props.onUpdateImage} + /> + <TextMode + isBitmap + textArea={props.textArea} + onUpdateImage={props.onUpdateImage} + /> + <BitFillMode + onUpdateImage={props.onUpdateImage} + /> + <BitEraserMode + onUpdateImage={props.onUpdateImage} + /> + <BitSelectMode + onUpdateImage={props.onUpdateImage} + /> + </div> + ) : null} + + <div className={styles.controlsContainer}> + {/* Canvas */} + <ScrollableCanvas + canvas={props.canvas} + hideScrollbars={props.isEyeDropping} + style={styles.canvasContainer} + > + <PaperCanvas + canvasRef={props.setCanvas} + image={props.image} + imageFormat={props.imageFormat} + imageId={props.imageId} + rotationCenterX={props.rotationCenterX} + rotationCenterY={props.rotationCenterY} + zoomLevelId={props.zoomLevelId} + onUpdateImage={props.onUpdateImage} + /> + <textarea + className={styles.textArea} + ref={props.setTextArea} + spellCheck={false} + /> + {props.isEyeDropping && + props.colorInfo !== null && + !props.colorInfo.hideLoupe ? ( + <Box className={styles.colorPickerWrapper}> + <Loupe + colorInfo={props.colorInfo} + pixelRatio={paper.project.view.pixelRatio} + /> + </Box> + ) : null + } + </ScrollableCanvas> + <div className={styles.canvasControls}> + {isVector(props.format) ? <Button className={styles.bitmapButton} - onClick={props.onSwitchToVector} + onClick={props.onSwitchToBitmap} > <img className={styles.bitmapButtonIcon} @@ -255,53 +243,68 @@ const PaintEditorComponent = props => ( src={bitmapIcon} /> <span className={styles.buttonText}> - {props.intl.formatMessage(messages.vector)} + {intl.formatMessage(messages.bitmap)} </span> - </Button> : null - } - {/* Zoom controls */} - <InputGroup className={styles.zoomControls}> - <ButtonGroup> - <Button - className={styles.buttonGroupButton} - onClick={props.onZoomOut} - > - <img - alt="Zoom Out" - className={styles.buttonGroupButtonIcon} - draggable={false} - src={zoomOutIcon} - /> - </Button> - <Button - className={styles.buttonGroupButton} - onClick={props.onZoomReset} - > - <img - alt="Zoom Reset" - className={styles.buttonGroupButtonIcon} - draggable={false} - src={zoomResetIcon} - /> - </Button> - <Button - className={styles.buttonGroupButton} - onClick={props.onZoomIn} - > - <img - alt="Zoom In" - className={styles.buttonGroupButtonIcon} - draggable={false} - src={zoomInIcon} - /> - </Button> - </ButtonGroup> - </InputGroup> + </Button> : + isBitmap(props.format) ? + <Button + className={styles.bitmapButton} + onClick={props.onSwitchToVector} + > + <img + className={styles.bitmapButtonIcon} + draggable={false} + src={bitmapIcon} + /> + <span className={styles.buttonText}> + {intl.formatMessage(messages.vector)} + </span> + </Button> : null + } + {/* Zoom controls */} + <InputGroup className={styles.zoomControls}> + <ButtonGroup> + <Button + className={styles.buttonGroupButton} + onClick={props.onZoomOut} + > + <img + alt="Zoom Out" + className={styles.buttonGroupButtonIcon} + draggable={false} + src={zoomOutIcon} + /> + </Button> + <Button + className={styles.buttonGroupButton} + onClick={props.onZoomReset} + > + <img + alt="Zoom Reset" + className={styles.buttonGroupButtonIcon} + draggable={false} + src={zoomResetIcon} + /> + </Button> + <Button + className={styles.buttonGroupButton} + onClick={props.onZoomIn} + > + <img + alt="Zoom In" + className={styles.buttonGroupButtonIcon} + draggable={false} + src={zoomInIcon} + /> + </Button> + </ButtonGroup> + </InputGroup> + </div> </div> </div> </div> - </div> -); + ); +}; PaintEditorComponent.propTypes = { canRedo: PropTypes.func.isRequired, @@ -315,7 +318,6 @@ PaintEditorComponent.propTypes = { ]), imageFormat: PropTypes.string, imageId: PropTypes.string, - intl: intlShape, isEyeDropping: PropTypes.bool, name: PropTypes.string, onRedo: PropTypes.func.isRequired, @@ -336,4 +338,4 @@ PaintEditorComponent.propTypes = { zoomLevelId: PropTypes.string }; -export default injectIntl(PaintEditorComponent); +export default PaintEditorComponent; diff --git a/src/components/tool-select-base/tool-select-base.jsx b/src/components/tool-select-base/tool-select-base.jsx index d9b2be00..8c866f64 100644 --- a/src/components/tool-select-base/tool-select-base.jsx +++ b/src/components/tool-select-base/tool-select-base.jsx @@ -1,31 +1,34 @@ import classNames from 'classnames'; import React from 'react'; import PropTypes from 'prop-types'; -import {injectIntl, intlShape} from 'react-intl'; +import {useIntl} from 'react-intl'; import Button from '../button/button.jsx'; import styles from './tool-select-base.css'; -const ToolSelectComponent = props => ( - <Button - className={ - classNames(props.className, styles.modToolSelect, { - [styles.isSelected]: props.isSelected - }) - } - disabled={props.disabled} - title={props.intl.formatMessage(props.imgDescriptor)} - onClick={props.onMouseDown} - > - <img - alt={props.intl.formatMessage(props.imgDescriptor)} - className={styles.toolSelectIcon} - draggable={false} - src={props.imgSrc} - /> - </Button> -); +const ToolSelectComponent = props => { + const intl = useIntl(); + return ( + <Button + className={ + classNames(props.className, styles.modToolSelect, { + [styles.isSelected]: props.isSelected + }) + } + disabled={props.disabled} + title={intl.formatMessage(props.imgDescriptor)} + onClick={props.onMouseDown} + > + <img + alt={intl.formatMessage(props.imgDescriptor)} + className={styles.toolSelectIcon} + draggable={false} + src={props.imgSrc} + /> + </Button> + ); +}; ToolSelectComponent.propTypes = { className: PropTypes.string, @@ -36,9 +39,8 @@ ToolSelectComponent.propTypes = { id: PropTypes.string }).isRequired, imgSrc: PropTypes.string.isRequired, - intl: intlShape.isRequired, isSelected: PropTypes.bool.isRequired, onMouseDown: PropTypes.func.isRequired }; -export default injectIntl(ToolSelectComponent); +export default ToolSelectComponent; diff --git a/src/containers/color-indicator.jsx b/src/containers/color-indicator.jsx index 10b5faf0..e6c9869b 100644 --- a/src/containers/color-indicator.jsx +++ b/src/containers/color-indicator.jsx @@ -2,7 +2,8 @@ import PropTypes from 'prop-types'; import React from 'react'; import bindAll from 'lodash.bindall'; import parseColor from 'parse-color'; -import {injectIntl, intlShape} from 'react-intl'; +import {injectIntl} from 'react-intl'; +import intlShape from '../lib/intl-shape.js'; import {getSelectedLeafItems} from '../helper/selection'; import Formats, {isBitmap} from '../lib/format'; diff --git a/src/lib/intl-shape.js b/src/lib/intl-shape.js new file mode 100644 index 00000000..d970032c --- /dev/null +++ b/src/lib/intl-shape.js @@ -0,0 +1,10 @@ +import PropTypes from 'prop-types'; + +// intlShape was removed in react-intl@3 and replaced with a TypeScript interface. +// These are some of the commonly used properties from the intl object. +const intlShape = PropTypes.shape({ + locale: PropTypes.string.isRequired, + formatMessage: PropTypes.func.isRequired +}); + +export default intlShape; diff --git a/src/playground/reducers/intl.js b/src/playground/reducers/intl.js index fc753d97..1a7463fe 100644 --- a/src/playground/reducers/intl.js +++ b/src/playground/reducers/intl.js @@ -1,15 +1,8 @@ -import {addLocaleData} from 'react-intl'; import {updateIntl as superUpdateIntl} from 'react-intl-redux'; import {IntlProvider, intlReducer} from 'react-intl-redux'; -import localeData from 'scratch-l10n'; import paintMessages from 'scratch-l10n/locales/paint-editor-msgs'; -Object.keys(localeData).forEach(locale => { - // TODO: will need to handle locales not in the default intl - see www/custom-locales - addLocaleData(localeData[locale].localeData); -}); - const intlInitialState = { intl: { defaultLocale: 'en', diff --git a/test/__mocks__/react-intl.js b/test/__mocks__/react-intl.js index 181caea9..e99bfdf9 100644 --- a/test/__mocks__/react-intl.js +++ b/test/__mocks__/react-intl.js @@ -11,7 +11,6 @@ const intl = { formatRelative: ({defaultMessage}) => defaultMessage, formatNumber: ({defaultMessage}) => defaultMessage, formatPlural: ({defaultMessage}) => defaultMessage, - formatHTMLMessage: ({defaultMessage}) => defaultMessage, now: () => 0 }; diff --git a/webpack.config.js b/webpack.config.js index 0b6b68fa..63e99e38 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,7 +17,12 @@ const base = { rules: [{ test: /\.jsx?$/, loader: 'babel-loader', - include: path.resolve(__dirname, 'src'), + include: [ + path.resolve(__dirname, 'src'), + path.join(__dirname, 'node_modules/react-intl'), + path.join(__dirname, 'node_modules/intl-messageformat'), + path.join(__dirname, 'node_modules/intl-messageformat-parser') + ], options: { plugins: ['transform-object-rest-spread'], presets: [