From d589d2dec09f8a14fdff1c632990a5eaba34a8c8 Mon Sep 17 00:00:00 2001 From: Nick Doiron Date: Tue, 3 Sep 2019 10:47:54 -0700 Subject: [PATCH] Right-to-left CSS (using module for conversion) (#7072) * Create RTL stylesheets using `gulp-rtl` * Handle RTL stylesheet special cases Certain blocks of Sass were set to bypass "rtlcss" using ignore comments. Certain icons had to be flipped 180 degrees. * Switch stylesheets when locale changes A second stylesheet has been added to each HTML page for use with right-to-left locales. It is disabled by default. It is enabled on startup if a RTL locale is set, and when switching to a RTL locale. Similarly the LTR stylesheet is disabled when a RTL locale is used. Unfortunately there is an unpleasant flash of unstyled content when switching between a LTR and a RTL locale. There is also a slightly longer page load time when using a RTL locale (<1s difference). We couldn't think of an easy way to avoid these problems. * Set `dir="auto"` as default on `TextFields` --- app/home.html | 3 +- app/notification.html | 3 +- app/popup.html | 3 +- app/scripts/controllers/preferences.js | 7 +- app/scripts/metamask-controller.js | 4 +- gulpfile.js | 9 +++ package.json | 2 + .../confirm-page-container-header/index.scss | 4 ++ .../advanced-gas-inputs/index.scss | 3 + .../advanced-tab-content/index.scss | 5 ++ .../components/ui/currency-display/index.scss | 3 +- ui/app/components/ui/editable-label.js | 1 + ui/app/components/ui/identicon/index.scss | 2 + .../ui/sender-to-recipient/index.scss | 51 +++++++++++++++ .../sender-to-recipient.component.js | 6 +- .../ui/text-field/text-field.component.js | 7 +- .../ui/unit-input/unit-input.component.js | 1 + ui/app/css/itcss/components/modal.scss | 2 + .../css/itcss/components/newui-sections.scss | 12 ++++ ui/app/css/itcss/components/sections.scss | 2 + ui/app/css/itcss/components/tab-bar.scss | 4 ++ ui/app/helpers/utils/switch-direction.js | 30 +++++++++ ui/app/pages/add-token/token-list/index.scss | 2 + ui/app/pages/first-time-flow/index.scss | 2 + .../confirm-seed-phrase/index.scss | 2 + ui/app/pages/routes/index.js | 4 ++ .../add-recipient/ens-input.component.js | 1 + ui/app/pages/send/send.scss | 4 ++ .../send/to-autocomplete/to-autocomplete.js | 1 + .../settings/contact-list-tab/index.scss | 4 ++ ui/app/pages/settings/index.scss | 12 ++++ ui/app/pages/settings/networks-tab/index.scss | 8 +++ ui/app/store/actions.js | 7 +- ui/index.html | 1 + ui/index.js | 5 ++ yarn.lock | 65 ++++++++++++++++++- 36 files changed, 268 insertions(+), 14 deletions(-) create mode 100644 ui/app/helpers/utils/switch-direction.js diff --git a/app/home.html b/app/home.html index ac17ed307..649727bbd 100644 --- a/app/home.html +++ b/app/home.html @@ -4,7 +4,8 @@ MetaMask - + +
diff --git a/app/notification.html b/app/notification.html index eba290c15..453716d1c 100644 --- a/app/notification.html +++ b/app/notification.html @@ -28,7 +28,8 @@ margin-top: 1rem; } - + +
diff --git a/app/popup.html b/app/popup.html index 3a6709eaf..59e99be43 100644 --- a/app/popup.html +++ b/app/popup.html @@ -4,7 +4,8 @@ MetaMask - + +
diff --git a/app/scripts/controllers/preferences.js b/app/scripts/controllers/preferences.js index 4ed3afb6c..b3523cc76 100644 --- a/app/scripts/controllers/preferences.js +++ b/app/scripts/controllers/preferences.js @@ -211,7 +211,12 @@ class PreferencesController { * */ setCurrentLocale (key) { - this.store.updateState({ currentLocale: key }) + const textDirection = (['ar', 'dv', 'fa', 'he', 'ku'].includes(key)) ? 'rtl' : 'auto' + this.store.updateState({ + currentLocale: key, + textDirection: textDirection, + }) + return textDirection } /** diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b9b44ea80..28537a8eb 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1723,8 +1723,8 @@ module.exports = class MetamaskController extends EventEmitter { */ setCurrentLocale (key, cb) { try { - this.preferencesController.setCurrentLocale(key) - cb(null) + const direction = this.preferencesController.setCurrentLocale(key) + cb(null, direction) } catch (err) { cb(err) } diff --git a/gulpfile.js b/gulpfile.js index 578a64aa2..1969541bf 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,6 +19,8 @@ const gulpStylelint = require('gulp-stylelint') const stylefmt = require('gulp-stylefmt') const uglify = require('gulp-uglify-es').default const pify = require('pify') +const rtlcss = require('gulp-rtlcss') +const rename = require('gulp-rename') const gulpMultiProcess = require('gulp-multi-process') const endOfStream = pify(require('end-of-stream')) @@ -274,6 +276,10 @@ function createScssBuildTask ({ src, dest, devMode, pattern }) { .pipe(sourcemaps.write()) .pipe(autoprefixer()) .pipe(gulp.dest(dest)) + .pipe(rtlcss()) + .pipe(rename({ suffix: '-rtl' })) + .pipe(sourcemaps.write()) + .pipe(gulp.dest(dest)) } function buildScss () { @@ -281,6 +287,9 @@ function createScssBuildTask ({ src, dest, devMode, pattern }) { .pipe(sass().on('error', sass.logError)) .pipe(autoprefixer()) .pipe(gulp.dest(dest)) + .pipe(rtlcss()) + .pipe(rename({ suffix: '-rtl' })) + .pipe(gulp.dest(dest)) } } diff --git a/package.json b/package.json index 512058239..66d917a5b 100644 --- a/package.json +++ b/package.json @@ -218,7 +218,9 @@ "gulp-json-editor": "^2.2.1", "gulp-livereload": "4.0.0", "gulp-multi-process": "^1.3.1", + "gulp-rename": "^1.4.0", "gulp-replace": "^0.6.1", + "gulp-rtlcss": "^1.4.0", "gulp-sass": "^4.0.0", "gulp-sourcemaps": "^2.6.0", "gulp-stylefmt": "^1.1.0", diff --git a/ui/app/components/app/confirm-page-container/confirm-page-container-header/index.scss b/ui/app/components/app/confirm-page-container/confirm-page-container-header/index.scss index be77edbdf..44c721446 100644 --- a/ui/app/components/app/confirm-page-container/confirm-page-container-header/index.scss +++ b/ui/app/components/app/confirm-page-container/confirm-page-container-header/index.scss @@ -15,6 +15,10 @@ display: flex; justify-content: center; align-items: center; + + [dir='rtl'] & img { + transform: rotate(180deg); + } } &__back-button { diff --git a/ui/app/components/app/gas-customization/advanced-gas-inputs/index.scss b/ui/app/components/app/gas-customization/advanced-gas-inputs/index.scss index 50953cbe5..a2352a332 100644 --- a/ui/app/components/app/gas-customization/advanced-gas-inputs/index.scss +++ b/ui/app/components/app/gas-customization/advanced-gas-inputs/index.scss @@ -47,6 +47,8 @@ } &__input { + /*rtl:ignore*/ + direction: ltr; border: 1px solid $dusty-gray; border-radius: 4px; color: $mid-gray; @@ -69,6 +71,7 @@ &__input-arrows { position: absolute; top: 7px; + /*rtl:ignore*/ right: 0px; width: 17px; height: 24px; diff --git a/ui/app/components/app/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss b/ui/app/components/app/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss index e35b6d594..c107b5400 100644 --- a/ui/app/components/app/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss +++ b/ui/app/components/app/gas-customization/gas-modal-page-container/advanced-tab-content/index.scss @@ -36,6 +36,8 @@ } &__time-remaining { + /*rtl:ignore*/ + direction: ltr; color: #313A5E; font-size: 16px; @@ -137,6 +139,8 @@ } &__input { + /*rtl:ignore*/ + direction: ltr; border: 1px solid $dusty-gray; border-radius: 4px; color: $mid-gray; @@ -159,6 +163,7 @@ &__input-arrows { position: absolute; top: 7px; + /*rtl:ignore*/ right: 0px; width: 17px; height: 24px; diff --git a/ui/app/components/ui/currency-display/index.scss b/ui/app/components/ui/currency-display/index.scss index 6d069eb05..074a65df3 100644 --- a/ui/app/components/ui/currency-display/index.scss +++ b/ui/app/components/ui/currency-display/index.scss @@ -3,10 +3,11 @@ align-items: center; &__text { + /*rtl:ignore*/ + direction: ltr; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - } &__suffix { diff --git a/ui/app/components/ui/editable-label.js b/ui/app/components/ui/editable-label.js index eb41ec50c..8eb10e174 100644 --- a/ui/app/components/ui/editable-label.js +++ b/ui/app/components/ui/editable-label.js @@ -37,6 +37,7 @@ class EditableLabel extends Component { h('input.large-input.editable-label__input', { type: 'text', required: true, + dir: 'auto', value: this.state.value, onKeyPress: (event) => { if (event.key === 'Enter') { diff --git a/ui/app/components/ui/identicon/index.scss b/ui/app/components/ui/identicon/index.scss index 4c8213f01..6abf57a19 100644 --- a/ui/app/components/ui/identicon/index.scss +++ b/ui/app/components/ui/identicon/index.scss @@ -1,4 +1,6 @@ .identicon { + /*rtl:ignore*/ + direction: ltr; display: flex; flex-shrink: 0; align-items: center; diff --git a/ui/app/components/ui/sender-to-recipient/index.scss b/ui/app/components/ui/sender-to-recipient/index.scss index b21e4e1bb..641015a81 100644 --- a/ui/app/components/ui/sender-to-recipient/index.scss +++ b/ui/app/components/ui/sender-to-recipient/index.scss @@ -50,6 +50,10 @@ display: flex; align-items: center; justify-content: center; + + [dir='rtl'] & { + transform: rotate(180deg); + } } &__arrow-circle { @@ -70,6 +74,19 @@ text-overflow: ellipsis; white-space: nowrap; font-size: .875rem; + + [dir='rtl'] & { + /*rtl:ignore*/ + direction: ltr; + /*rtl:ignore*/ + text-align: right; + + span { + display: block; + /*rtl:ignore*/ + direction: rtl; + } + } } } } @@ -102,12 +119,29 @@ text-overflow: ellipsis; white-space: nowrap; font-size: .5rem; + + [dir='rtl'] & { + /*rtl:ignore*/ + direction: ltr; + /*rtl:ignore*/ + text-align: right; + + span { + display: block; + /*rtl:ignore*/ + direction: rtl; + } + } } &__arrow-container { display: flex; justify-content: center; align-items: center; + + [dir='rtl'] & { + transform: rotate(180deg); + } } } } @@ -137,12 +171,29 @@ text-overflow: ellipsis; white-space: nowrap; font-size: .6875rem; + + [dir='rtl'] & { + /*rtl:ignore*/ + direction: ltr; + /*rtl:ignore*/ + text-align: right; + + span { + display: block; + /*rtl:ignore*/ + direction: rtl; + } + } } &__arrow-container { display: flex; justify-content: center; align-items: center; + + [dir='rtl'] & { + transform: rotate(180deg); + } } } } diff --git a/ui/app/components/ui/sender-to-recipient/sender-to-recipient.component.js b/ui/app/components/ui/sender-to-recipient/sender-to-recipient.component.js index 933f1b007..c8e7a1870 100644 --- a/ui/app/components/ui/sender-to-recipient/sender-to-recipient.component.js +++ b/ui/app/components/ui/sender-to-recipient/sender-to-recipient.component.js @@ -66,7 +66,8 @@ export default class SenderToRecipient extends PureComponent { onHidden={() => this.setState({ senderAddressCopied: false })} >
- { addressOnly ? `${t('from')}: ${checksummedSenderAddress}` : senderName } + { addressOnly ? `${t('from')}: ` : '' } + { addressOnly ? checksummedSenderAddress : senderName }
) @@ -112,9 +113,10 @@ export default class SenderToRecipient extends PureComponent { onHidden={() => this.setState({ recipientAddressCopied: false })} >
+ { addressOnly ? `${t('to')}: ` : '' } { addressOnly - ? `${t('to')}: ${checksummedRecipientAddress}` + ? checksummedRecipientAddress : (recipientNickname || recipientName || this.context.t('newContract')) }
diff --git a/ui/app/components/ui/text-field/text-field.component.js b/ui/app/components/ui/text-field/text-field.component.js index ac7712c65..12a97ee4d 100644 --- a/ui/app/components/ui/text-field/text-field.component.js +++ b/ui/app/components/ui/text-field/text-field.component.js @@ -67,7 +67,7 @@ const styles = { } const TextField = props => { - const { error, classes, material, startAdornment, largeLabel, ...textFieldProps } = props + const { error, classes, material, startAdornment, largeLabel, dir, ...textFieldProps } = props return ( { underline: material ? classes.materialUnderline : '', focused: material ? '' : classes.inputFocused, }, + inputProps: { + dir, + }, }} {...textFieldProps} /> @@ -99,11 +102,13 @@ const TextField = props => { TextField.defaultProps = { error: null, + dir: 'auto', } TextField.propTypes = { error: PropTypes.string, classes: PropTypes.object, + dir: PropTypes.string, material: PropTypes.bool, startAdornment: PropTypes.element, largeLabel: PropTypes.bool, diff --git a/ui/app/components/ui/unit-input/unit-input.component.js b/ui/app/components/ui/unit-input/unit-input.component.js index 9085a0677..01a2e7cf1 100644 --- a/ui/app/components/ui/unit-input/unit-input.component.js +++ b/ui/app/components/ui/unit-input/unit-input.component.js @@ -84,6 +84,7 @@ export default class UnitInput extends PureComponent {
{ + if (direction === 'auto') { + direction = 'ltr' + } + let updatedLink + Array.from(document.getElementsByTagName('link')) + .filter(link => link.rel === 'stylesheet') + .forEach(link => { + if (link.title === direction && link.disabled) { + link.disabled = false + updatedLink = link + } else if (link.title !== direction && !link.disabled) { + link.disabled = true + } + }) + if (updatedLink) { + return new Promise((resolve, reject) => { + updatedLink.onload = () => { + resolve() + } + updatedLink.onerror = () => reject(new Error(`Failed to load '${direction}' stylesheet`)) + }) + } +} + +export default switchDirection diff --git a/ui/app/pages/add-token/token-list/index.scss b/ui/app/pages/add-token/token-list/index.scss index b7787a18e..bad4ec30f 100644 --- a/ui/app/pages/add-token/token-list/index.scss +++ b/ui/app/pages/add-token/token-list/index.scss @@ -58,6 +58,8 @@ } &__token-name { + /*rtl:ignore*/ + direction: ltr; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/ui/app/pages/first-time-flow/index.scss b/ui/app/pages/first-time-flow/index.scss index dec80cb60..c674551f4 100644 --- a/ui/app/pages/first-time-flow/index.scss +++ b/ui/app/pages/first-time-flow/index.scss @@ -73,6 +73,8 @@ } &__textarea { + /*rtl:ignore*/ + direction: ltr; font-size: 1rem; font-family: Roboto; height: 190px; diff --git a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss index f025a503f..279cdbbc5 100644 --- a/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss +++ b/ui/app/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss @@ -64,6 +64,8 @@ } &__selected-seed-words { + /*rtl:ignore*/ + direction: ltr; display: flex; flex-flow: row wrap; min-height: 161px; diff --git a/ui/app/pages/routes/index.js b/ui/app/pages/routes/index.js index 2f7caf3bf..79438174e 100644 --- a/ui/app/pages/routes/index.js +++ b/ui/app/pages/routes/index.js @@ -170,6 +170,7 @@ class Routes extends Component { const { isLoading, alertMessage, + textDirection, loadingMessage, network, provider, @@ -208,6 +209,7 @@ class Routes extends Component { return (
setMouseUserState(true)} onKeyDown={e => { if (e.keyCode === 9) { @@ -323,6 +325,7 @@ Routes.propTypes = { isLoading: PropTypes.bool, loadingMessage: PropTypes.string, alertMessage: PropTypes.string, + textDirection: PropTypes.string, network: PropTypes.string, provider: PropTypes.object, frequentRpcListDetail: PropTypes.array, @@ -360,6 +363,7 @@ function mapStateToProps (state) { sidebar, alertOpen, alertMessage, + textDirection: state.metamask.textDirection, isLoading, loadingMessage, isUnlocked: state.metamask.isUnlocked, diff --git a/ui/app/pages/send/send-content/add-recipient/ens-input.component.js b/ui/app/pages/send/send-content/add-recipient/ens-input.component.js index 483d5d344..8f8023fd0 100644 --- a/ui/app/pages/send/send-content/add-recipient/ens-input.component.js +++ b/ui/app/pages/send/send-content/add-recipient/ens-input.component.js @@ -155,6 +155,7 @@ export default class EnsInput extends Component { onChange(event.target.value), onFocus: event => this.handleInputEvent(event), diff --git a/ui/app/pages/settings/contact-list-tab/index.scss b/ui/app/pages/settings/contact-list-tab/index.scss index c7e99095f..aad50c419 100644 --- a/ui/app/pages/settings/contact-list-tab/index.scss +++ b/ui/app/pages/settings/contact-list-tab/index.scss @@ -197,6 +197,10 @@ width: 30px; opacity: .5; background-repeat: no-repeat; + + [dir='rtl'] & { + transform: rotate(180deg); + } } } } diff --git a/ui/app/pages/settings/index.scss b/ui/app/pages/settings/index.scss index 041d12f72..550fd1e7d 100644 --- a/ui/app/pages/settings/index.scss +++ b/ui/app/pages/settings/index.scss @@ -97,6 +97,10 @@ background-position: center; margin-right: 16px; cursor: pointer; + + [dir='rtl'] & { + transform: rotate(180deg); + } } } @@ -244,4 +248,12 @@ } } } + + .toggle-button { + /*rtl:ignore*/ + direction: ltr; + [dir='rtl'] & { + justify-content: flex-end; + } + } } diff --git a/ui/app/pages/settings/networks-tab/index.scss b/ui/app/pages/settings/networks-tab/index.scss index bf83c7a14..b9f4245f9 100644 --- a/ui/app/pages/settings/networks-tab/index.scss +++ b/ui/app/pages/settings/networks-tab/index.scss @@ -38,6 +38,10 @@ cursor: pointer; position: absolute; margin-left: 10px; + + [dir='rtl'] & { + transform: rotate(180deg); + } } } @@ -190,6 +194,10 @@ position: absolute; width: 24px; height: 24px; + + [dir='rtl'] & { + transform: rotate(180deg); + } } } diff --git a/ui/app/store/actions.js b/ui/app/store/actions.js index 2b0b54c60..87b01a74c 100644 --- a/ui/app/store/actions.js +++ b/ui/app/store/actions.js @@ -10,6 +10,7 @@ const ethUtil = require('ethereumjs-util') const { fetchLocale } = require('../helpers/utils/i18n-helper') const { getMethodDataAsync } = require('../helpers/utils/transactions.util') const { fetchSymbolAndDecimals } = require('../helpers/utils/token-util') +import switchDirection from '../helpers/utils/switch-direction' const log = require('loglevel') const { ENVIRONMENT_TYPE_NOTIFICATION } = require('../../../app/scripts/lib/enums') const { hasUnconfirmedTransactions } = require('../helpers/utils/confirm-tx.util') @@ -2597,11 +2598,13 @@ function updateCurrentLocale (key) { return fetchLocale(key) .then((localeMessages) => { log.debug(`background.setCurrentLocale`) - background.setCurrentLocale(key, (err) => { - dispatch(actions.hideLoadingIndication()) + background.setCurrentLocale(key, (err, textDirection) => { if (err) { + dispatch(actions.hideLoadingIndication()) return dispatch(actions.displayWarning(err.message)) } + switchDirection(textDirection) + dispatch(actions.hideLoadingIndication()) dispatch(actions.setCurrentLocale(key)) dispatch(actions.setLocaleMessages(localeMessages)) }) diff --git a/ui/index.html b/ui/index.html index 9dfaefbb3..38515e09b 100644 --- a/ui/index.html +++ b/ui/index.html @@ -12,6 +12,7 @@ +
diff --git a/ui/index.js b/ui/index.js index db9292761..0f15594d2 100644 --- a/ui/index.js +++ b/ui/index.js @@ -5,6 +5,7 @@ const actions = require('./app/store/actions') const configureStore = require('./app/store/store') const txHelper = require('./lib/tx-helper') const { fetchLocale } = require('./app/helpers/utils/i18n-helper') +import switchDirection from './app/helpers/utils/switch-direction' const log = require('loglevel') module.exports = launchMetamaskUi @@ -33,6 +34,10 @@ async function startApp (metamaskState, backgroundConnection, opts) { : {} const enLocaleMessages = await fetchLocale('en') + if (metamaskState.textDirection === 'rtl') { + await switchDirection('rtl') + } + const store = configureStore({ activeTab: opts.activeTab, diff --git a/yarn.lock b/yarn.lock index 7ec582150..9ec8f2dfd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5728,6 +5728,11 @@ colors@^1.1.0, colors@^1.1.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== +colors@~0.6.0-1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" + integrity sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w= + columnify@1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" @@ -5806,6 +5811,11 @@ commander@^2.8.1, commander@~2.8.1: dependencies: graceful-readlink ">= 1.0.0" +commander@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" + integrity sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E= + commander@~2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" @@ -9851,6 +9861,14 @@ findup-sync@^3.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" +findup@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/findup/-/findup-0.1.5.tgz#8ad929a3393bac627957a7e5de4623b06b0e2ceb" + integrity sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs= + dependencies: + colors "~0.6.0-1" + commander "~2.1.0" + fined@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/fined/-/fined-1.1.0.tgz#b37dc844b76a2f5e7081e884f7c0ae344f153476" @@ -11039,6 +11057,11 @@ gulp-multi-process@^1.3.1: dependencies: async.queue "^0.5.2" +gulp-rename@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.4.0.tgz#de1c718e7c4095ae861f7296ef4f3248648240bd" + integrity sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg== + gulp-replace@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/gulp-replace/-/gulp-replace-0.6.1.tgz#11bf8c8fce533e33e2f6a8f2f430b955ba0be066" @@ -11048,6 +11071,16 @@ gulp-replace@^0.6.1: readable-stream "^2.0.1" replacestream "^4.0.0" +gulp-rtlcss@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/gulp-rtlcss/-/gulp-rtlcss-1.4.0.tgz#a1e4caef39182af03778cb9c18add9d3ee5e1c97" + integrity sha512-66UmUSacTzdV3L0KcsdwzExEu1+dTfNlq3emUZGgHPLgUaCrsZUgZwjsgKjPwkYJUZOucLpjOxAkB37k+H80Kw== + dependencies: + plugin-error "^1.0.1" + rtlcss "^2.4.0" + through2 "^2.0.5" + vinyl-sourcemaps-apply "^0.2.1" + gulp-sass@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/gulp-sass/-/gulp-sass-4.0.1.tgz#7f43d117eb2d303524968a1b48494af1bc64d1d9" @@ -16768,6 +16801,15 @@ postcss@^6.0.1, postcss@^6.0.19: source-map "^0.6.1" supports-color "^5.2.0" +postcss@^6.0.14: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.5, postcss@^7.0.6: version "7.0.17" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" @@ -18010,7 +18052,7 @@ readable-stream@1.1, readable-stream@1.1.x, "readable-stream@>=1.1.13-1 <1.2.0-0 isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -18883,6 +18925,17 @@ rtcpeerconnection-shim@^1.2.10: dependencies: sdp "^2.6.0" +rtlcss@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/rtlcss/-/rtlcss-2.4.0.tgz#482ea28f2b9fe06dd0ab3057997be9af13da84c1" + integrity sha512-hdjFhZ5FCI0ABOfyXOMOhBtwPWtANLCG7rOiOcRf+yi5eDdxmDjqBruWouEnwVdzfh/TWF6NNncIEsigOCFZOA== + dependencies: + chalk "^2.3.0" + findup "^0.1.5" + mkdirp "^0.5.1" + postcss "^6.0.14" + strip-json-comments "^2.0.0" + run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" @@ -20573,7 +20626,7 @@ strip-json-comments@1.0.x: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" integrity sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E= -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= @@ -21276,6 +21329,14 @@ through2@^1.1.1: readable-stream ">=1.1.13-1 <1.2.0-0" xtend ">=4.0.0 <4.1.0-0" +through2@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + through2@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/through2/-/through2-0.2.3.tgz#eb3284da4ea311b6cc8ace3653748a52abf25a3f"