diff --git a/src/components/button/button.css b/src/components/button/button.css index 9ce746c52140b73be1ba889fae9c028ab7cfe2bb..9ba2fbd93b4b48dd489f6249e2cafd8abcfc2eec 100644 --- a/src/components/button/button.css +++ b/src/components/button/button.css @@ -11,10 +11,17 @@ } .icon { - margin-right: .5rem; height: 1.5rem; } +[dir="ltr"] .icon { + margin-right: .5rem; +} + +[dir="rtl"] .icon { + margin-left: .5rem; +} + .content { white-space: nowrap; } diff --git a/src/components/camera-modal/camera-modal.css b/src/components/camera-modal/camera-modal.css index 4ea962fbafd68bc7ecdf3736d96930070a46f0de..7522fbb2ec286f0491d62d1fd6a67a7ebb5b2ed4 100644 --- a/src/components/camera-modal/camera-modal.css +++ b/src/components/camera-modal/camera-modal.css @@ -135,6 +135,10 @@ $main-button-size: 2.75rem; color: $ui-white; } +[dir="rtl"] .retake-button img { + transform: scaleX(-1); +} + @keyframes flash { 0% { opacity: 1; } 100% { opacity: 0; } diff --git a/src/components/camera-modal/camera-modal.jsx b/src/components/camera-modal/camera-modal.jsx index f115a6f8a75f1c77286f15365137509313a8783f..8c499c3624922e7d84ac1b00e75cf8f9205c29c0 100644 --- a/src/components/camera-modal/camera-modal.jsx +++ b/src/components/camera-modal/camera-modal.jsx @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {defineMessages, injectIntl, intlShape} from 'react-intl'; import Box from '../box/box.jsx'; -import Modal from '../modal/modal.jsx'; +import Modal from '../../containers/modal.jsx'; import styles from './camera-modal.css'; import backIcon from './icon--back.svg'; import cameraIcon from '../action-menu/icon--camera.svg'; @@ -79,7 +79,7 @@ const CameraModal = ({intl, ...props}) => ( {props.capture ? <Box className={styles.buttonRow}> <button - className={styles.cancelButton} + className={styles.retakeButton} key="retake-button" onClick={props.onBack} > diff --git a/src/components/connection-modal/connection-modal.css b/src/components/connection-modal/connection-modal.css index b8ca0634b32c81d1504f6f6bd915e1f2dc6380ea..5660a44eb0834a8e0a25d3be015aa29a182d173e 100644 --- a/src/components/connection-modal/connection-modal.css +++ b/src/components/connection-modal/connection-modal.css @@ -51,10 +51,14 @@ align-items: center; } -.device-tile-image { +[dir="ltr"] .device-tile-image { margin-right: 0.5rem; } +[dir="rtl"] .device-tile-image { + margin-left: 0.5rem; +} + .device-tile-name-wrapper { display: flex; flex-direction: column; @@ -89,9 +93,16 @@ align-items: flex-end; width: 22px; height: 16px; +} + +[dir="ltr"] .signal-strength-meter { margin-right: 1rem; } +[dir="rtl"] .signal-strength-meter { + margin-left: 1rem; +} + .signal-bar { width: 4px; border-radius: 4px; @@ -110,17 +121,23 @@ .radar { width: 40px; height: 40px; - margin-right: 0.5rem; animation: spin 4s linear infinite; } +[dir="ltr"] .radar { + margin-right: .5rem; +} + +[dir="rtl"] .radar { + margin-left: .5rem; +} + @keyframes spin { 100% { transform: rotate(360deg); } } - .device-activity { position: relative; } @@ -134,6 +151,7 @@ position: absolute; top: -5px; right: -15px; + left: -15px; padding: 5px 5px; background-color: $motion-primary; border-radius: 100%; @@ -199,14 +217,26 @@ margin-left: 3rem; } +[dir="ltr"] .scratch-link-help-step { + margin-left: 3rem; +} + +[dir="rtl"] .scratch-link-help-step { + margin-right: 3rem; +} + .scratch-link-icon { max-width: 50px; } -.help-step-image { +[dir="ltr"] .help-step-image { margin-right: 0.5rem; } +[dir="rtl"] .help-step-image { + margin-left: 0.5rem; +} + .help-step-number { background: $pen-primary; border-radius: 100%; @@ -215,11 +245,18 @@ align-items: center; color: $ui-white; font-weight: bold; - margin-right: 0.5rem; min-width: 2rem; height: 2rem; } +[dir="ltr"] .help-step-number { + margin-right: 0.5rem; +} + +[dir="rtl"] .help-step-number { + margin-left: 0.5rem; +} + .button-row { font-weight: bolder; text-align: center; @@ -265,14 +302,26 @@ border-bottom-left-radius: 0; } -.button-icon-right { +[dir="ltr"] .button-icon-right { margin-left: 0.5rem; } +[dir="rtl"] .button-icon-right { + margin-right: 0.5rem; +} -.button-icon-left { +[dir="ltr"] .button-icon-left { margin-right: 0.5rem; } +[dir="rtl"] .button-icon-left { + margin-left: 0.5rem; +} + +/* reverse back arrow icon for RTL, don't reverse other connection icons */ +[dir="rtl"] .button-icon-back { + transform: scaleX(-1); +} + .red-button { background: $red-primary; } diff --git a/src/components/connection-modal/connection-modal.jsx b/src/components/connection-modal/connection-modal.jsx index c6566e954db7c3b5de3b32a8de3a2d6439115838..b8a5feea7d84137816923816377ee114592f1836 100644 --- a/src/components/connection-modal/connection-modal.jsx +++ b/src/components/connection-modal/connection-modal.jsx @@ -3,7 +3,7 @@ import React from 'react'; import keyMirror from 'keymirror'; import Box from '../box/box.jsx'; -import Modal from '../modal/modal.jsx'; +import Modal from '../../containers/modal.jsx'; import ScanningStep from '../../containers/scanning-step.jsx'; import ConnectingStep from './connecting-step.jsx'; diff --git a/src/components/connection-modal/error-step.jsx b/src/components/connection-modal/error-step.jsx index 194e95678fed2fae311e583c35f0a676acc58aa8..a5a5ce1b70085d66bfd351599673da58da171821 100644 --- a/src/components/connection-modal/error-step.jsx +++ b/src/components/connection-modal/error-step.jsx @@ -1,5 +1,6 @@ import {FormattedMessage} from 'react-intl'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import React from 'react'; import Box from '../box/box.jsx'; @@ -39,7 +40,7 @@ const ErrorStep = props => ( onClick={props.onScanning} > <img - className={styles.buttonIconLeft} + className={classNames(styles.buttonIconLeft, styles.buttonIconBack)} src={backIcon} /> <FormattedMessage diff --git a/src/components/connection-modal/unavailable-step.jsx b/src/components/connection-modal/unavailable-step.jsx index 975a66debe7d0b14b25a87c632c04a494775ac4d..ae89404a1952d8ccded43103dc0b1b6af50cf036 100644 --- a/src/components/connection-modal/unavailable-step.jsx +++ b/src/components/connection-modal/unavailable-step.jsx @@ -1,5 +1,6 @@ import {FormattedMessage} from 'react-intl'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import React from 'react'; import Box from '../box/box.jsx'; @@ -64,7 +65,7 @@ const UnavailableStep = props => ( onClick={props.onScanning} > <img - className={styles.buttonIconLeft} + className={classNames(styles.buttonIconLeft, styles.buttonIconBack)} src={backIcon} /> <FormattedMessage diff --git a/src/components/custom-procedures/custom-procedures.css b/src/components/custom-procedures/custom-procedures.css index 6c8967a12d513023f337c3b58d5070281a761170..1716baab306a5b5b4a877831aaff8ee8dfbe5dda 100644 --- a/src/components/custom-procedures/custom-procedures.css +++ b/src/components/custom-procedures/custom-procedures.css @@ -91,6 +91,10 @@ color: white; } -.button-row button + button { +[dir="ltr"] .button-row button + button { margin-left: 0.5rem; } + +[dir="rtl"] .button-row button + button { + margin-right: 0.5rem; +} diff --git a/src/components/custom-procedures/custom-procedures.jsx b/src/components/custom-procedures/custom-procedures.jsx index 81756e762ee9e8ffd64492a35b59b2849aada0a7..7d2f3e1a5352afb855d6f5e59bae83164e37ff6a 100644 --- a/src/components/custom-procedures/custom-procedures.jsx +++ b/src/components/custom-procedures/custom-procedures.jsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; -import Modal from '../modal/modal.jsx'; +import Modal from '../../containers/modal.jsx'; import Box from '../box/box.jsx'; import {defineMessages, injectIntl, intlShape, FormattedMessage} from 'react-intl'; diff --git a/src/components/filter/filter.css b/src/components/filter/filter.css index b67e60795c29a100c7e67982b8dafeb8bb46c28b..fffef4812eca008d8d3e3dc30c9a540e75192eef 100644 --- a/src/components/filter/filter.css +++ b/src/components/filter/filter.css @@ -18,13 +18,22 @@ .filter-icon { position: absolute; top: 0; - left: 0; height: 1rem; width: 1rem; +} + +[dir="ltr"] .filter-icon { + left: 0; margin: 0.75rem 0.75rem 0.75rem 1rem; } +[dir="rtl"] .filter-icon { + right: 0; + margin: 0.75rem 1rem 0.75rem 0.75rem; + transform: scaleX(-1); +} + .filter:focus-within { box-shadow: 0 0 0 .25rem $motion-transparent; } @@ -36,7 +45,6 @@ opacity: 0; position: absolute; top: 0; - right: 0; display: flex; justify-content: center; @@ -53,6 +61,14 @@ transition: opacity 0.05s linear; } +[dir="ltr"] .x-icon-wrapper { + right: 0; +} + +[dir="rtl"] .x-icon-wrapper { + left: 0; +} + /* Shown state */ @@ -96,13 +112,27 @@ font-size: 0.75rem; letter-spacing: 0.15px; cursor: text; +} + +[dir="ltr"] .filter-input { padding: .625rem 2rem .625rem 3rem; } +[dir="rtl"] .filter-input { + padding: .625rem 3rem .625rem 2rem; +} + .filter-input::placeholder { opacity: .5; - padding: 0 0 0 0.25rem; color: $text-primary; font-size: 0.875rem; letter-spacing: 0.15px; } + +[dir="ltr"] .filter-input::placeholder { + padding: 0 0 0 0.25rem; +} + +[dir="rtl"] .filter-input::placeholder { + padding: 0 0.25rem 0 0; +} diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index 3b318f40ddd7b0697e33d90aa31b505c519a0394..1bb4f196343e94658a56c140447ee91ce4a739aa 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -100,13 +100,24 @@ $sides: 20rem; user-select: none; letter-spacing: 0.4px; cursor: default; +} + +[dir="ltr"] .header-item-title { margin: 0 -$sides 0 0; } -.full-screen .header-item-title { +[dir="rtl"] .header-item-title { + margin: 0 0 0 -$sides; +} + +.full-screen [dir="ltr"] .header-item-title { margin: 0 0 0 -$sides; } +.full-screen [dir="rtl"] .header-item-title { + margin: 0 -$sides 0 0; +} + .header-item-close { flex-basis: $sides; justify-content: flex-end; @@ -120,17 +131,36 @@ $sides: 20rem; .back-button { font-weight: normal; + padding-right: 0; padding-left: 0; } +[dir="rtl"] .back-button img { + transform: scaleX(-1); +} + .header-item-help { padding: 0; - margin-right: -4.75rem; z-index: 1; } +[dir="ltr"] .header-item-help { + margin-right: -4.75rem; +} + +[dir="rtl"] .header-item-help { + margin-left: -4.75rem; +} + .help-button { font-weight: normal; - padding-right: 0; font-size: 0.75rem; } + +[dir="ltr"] .help-button { + padding-right: 0; +} + +[dir="rtl"] .help-button { + padding-left: 0; +} diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index acce21993d86b23137104f5f7bddc1215c1d26bf..32fe0502854d4676a2b617f5ccb04f46660a00d0 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -24,6 +24,7 @@ const ModalComponent = props => ( onRequestClose={props.onRequestClose} > <Box + dir={props.isRtl ? 'rtl' : 'ltr'} direction="column" grow={1} > @@ -103,6 +104,7 @@ ModalComponent.propTypes = { fullScreen: PropTypes.bool, headerClassName: PropTypes.string, headerImage: PropTypes.string, + isRtl: PropTypes.bool, onHelp: PropTypes.func, onRequestClose: PropTypes.func }; diff --git a/src/components/prompt/prompt.css b/src/components/prompt/prompt.css index 78e3ecb27c6b3d2583528ed1dc6b1df6f36dca6e..ae523e188013ad9c79433ed908f4623665551a50 100644 --- a/src/components/prompt/prompt.css +++ b/src/components/prompt/prompt.css @@ -60,10 +60,14 @@ color: white; } -.button-row button + button { +[dir="ltr"] .button-row button + button { margin-left: 0.5rem; } +[dir="rtl"] .button-row button + button { + margin-right: 0.5rem; +} + .more-options { border-top: 1px dashed hsla(0, 0%, 0%, .25); overflow: visible; diff --git a/src/components/prompt/prompt.jsx b/src/components/prompt/prompt.jsx index 3e5ec7950fc1be25a4e5cc89297a9828237fd649..42de1599939801b8896374f032525be7c77982a9 100644 --- a/src/components/prompt/prompt.jsx +++ b/src/components/prompt/prompt.jsx @@ -4,7 +4,7 @@ import React from 'react'; import Box from '../box/box.jsx'; import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx'; -import Modal from '../modal/modal.jsx'; +import Modal from '../../containers/modal.jsx'; import styles from './prompt.css'; diff --git a/src/components/record-modal/playback-step.jsx b/src/components/record-modal/playback-step.jsx index 40167172deac8f7167c88d2046b89a92d5162886..8f76b506f33e8e38c00869c0a2d06bf6f6fcc471 100644 --- a/src/components/record-modal/playback-step.jsx +++ b/src/components/record-modal/playback-step.jsx @@ -87,7 +87,7 @@ const PlaybackStep = props => ( </Box> <Box className={styles.buttonRow}> <button - className={styles.cancelButton} + className={styles.rerecordButton} onClick={props.onBack} > <img diff --git a/src/components/record-modal/record-modal.css b/src/components/record-modal/record-modal.css index 79ce9c606a995d69642eabc76b064ba06cc50aa1..ae9d8afcc0d0b2d8b5235b7c86b30c5fa3bb15b6 100644 --- a/src/components/record-modal/record-modal.css +++ b/src/components/record-modal/record-modal.css @@ -118,3 +118,7 @@ opacity: 0.2; transition: 0.1s; } + +[dir="rtl"] .rerecord-button img { + transform: scaleX(-1); +} diff --git a/src/components/record-modal/record-modal.jsx b/src/components/record-modal/record-modal.jsx index 2adc9c14774b9fcf8992c78a53a531c26328429d..5f4086c1c60a35f7a46e011d49248f3849d93de6 100644 --- a/src/components/record-modal/record-modal.jsx +++ b/src/components/record-modal/record-modal.jsx @@ -4,7 +4,7 @@ import Box from '../box/box.jsx'; import {defineMessages, injectIntl, intlShape} from 'react-intl'; import RecordingStep from '../../containers/recording-step.jsx'; import PlaybackStep from '../../containers/playback-step.jsx'; -import Modal from '../modal/modal.jsx'; +import Modal from '../../containers/modal.jsx'; import styles from './record-modal.css'; const messages = defineMessages({ diff --git a/src/containers/custom-procedures.jsx b/src/containers/custom-procedures.jsx index 879800aae880313299ab53601e6a1f9f0f667d54..525422720a8324b69d47ccad362943f7f8a6e988 100644 --- a/src/containers/custom-procedures.jsx +++ b/src/containers/custom-procedures.jsx @@ -32,7 +32,8 @@ class CustomProcedures extends React.Component { this.blocks = blocksRef; const workspaceConfig = defaultsDeep({}, CustomProcedures.defaultOptions, - this.props.options + this.props.options, + {rtl: this.props.isRtl} ); // @todo This is a hack to make there be no toolbox. @@ -117,6 +118,7 @@ class CustomProcedures extends React.Component { } CustomProcedures.propTypes = { + isRtl: PropTypes.bool, mutator: PropTypes.instanceOf(Element), onRequestClose: PropTypes.func.isRequired, options: PropTypes.shape({ @@ -147,6 +149,7 @@ CustomProcedures.defaultProps = { }; const mapStateToProps = state => ({ + isRtl: state.locales.isRtl, mutator: state.scratchGui.customProcedures.mutator }); diff --git a/src/containers/modal.jsx b/src/containers/modal.jsx index 67b14c9ff4494b1e5f354f7befb0b6b0faa9946b..30dc4277b56a8a7aabcf2dd204d3be9c14c5b072 100644 --- a/src/containers/modal.jsx +++ b/src/containers/modal.jsx @@ -1,6 +1,7 @@ import bindAll from 'lodash.bindall'; import PropTypes from 'prop-types'; import React from 'react'; +import {connect} from 'react-redux'; import ModalComponent from '../components/modal/modal.jsx'; @@ -47,8 +48,15 @@ class Modal extends React.Component { Modal.propTypes = { id: PropTypes.string.isRequired, + isRtl: PropTypes.bool, onRequestClose: PropTypes.func, onRequestOpen: PropTypes.func }; -export default Modal; +const mapStateToProps = state => ({ + isRtl: state.locales.isRtl +}); + +export default connect( + mapStateToProps +)(Modal);