merge-upstream: Initial merge of upstream/develop. Compiles but very broken.

This commit is contained in:
Muffin committed 2023-12-22 19:26:58 -06:00
commit 7f4b4fb9d3
199 files changed
+48972 -9876

No files matched your search

-329
View File
@@ -1,329 +0,0 @@
version: 2.1
orbs:
browser-tools: circleci/browser-tools@1.2.4
aliases:
- &save_git_cache
save_cache:
paths:
- .git
key: v3-git-{{ .Revision }}
- &restore_git_cache
restore_cache:
keys:
- v3-git-{{ .Revision }}
- v3-git-
- &save_build_cache
save_cache:
paths:
- build
key: v3-build-{{ .Revision }}
- &restore_build_cache
restore_cache:
keys:
- v3-build-{{ .Revision }}
- &save_dist_cache
save_cache:
paths:
- dist
key: v3-dist-{{ .Revision }}
- &restore_dist_cache
restore_cache:
keys:
- v3-dist-{{ .Revision }}
- &save_npm_cache
save_cache:
paths:
- node_modules
key: v3-npm-{{ checksum "package-lock.json" }}
- &restore_npm_cache
restore_cache:
keys:
- v3-npm-{{ checksum "package-lock.json" }}
- v3-npm-
- &defaults
docker:
- image: cimg/node:12.22.11-browsers
auth:
username: $DOCKERHUB_USERNAME
password: $DOCKERHUB_PASSWORD
working_directory: ~/repo
jobs:
build-test-no-cache:
<<: *defaults
environment:
JEST_JUNIT_OUTPUT_DIR: test-results
NODE_OPTIONS: --max-old-space-size=4000
steps:
- *restore_git_cache
- checkout
- run: npm ci
- run:
name: Lint
command: npm run test:lint -- --quiet --output-file test-results/eslint-results.xml --format junit
- run:
name: Unit
environment:
JEST_JUNIT_OUTPUT_NAME: unit-results.xml
command: npm run test:unit -- --reporters="default" --reporters="jest-junit" --coverage --coverageReporters=text --coverageReporters=lcov --maxWorkers="2"
- run:
name: Build
environment:
NODE_ENV: production
command: npm run build
- browser-tools/install-chrome
- browser-tools/install-chromedriver
- run:
name: Integration
environment:
JEST_JUNIT_OUTPUT_NAME: integration-results.xml
command: |
google-chrome --version
chromedriver --version
npm run test:integration -- --reporters="default" --reporters="jest-junit"
- store_artifacts:
path: coverage
- store_test_results:
path: test-results
setup:
<<: *defaults
steps:
- *restore_git_cache
- checkout
- run: npm ci
- *save_git_cache
- *save_npm_cache
lint:
<<: *defaults
steps:
- *restore_git_cache
- checkout
- *restore_npm_cache
- run:
name: Lint
command: npm run test:lint -- --quiet --output-file test-results/eslint/results.xml --format junit
- store_test_results:
path: test-results
unit:
<<: *defaults
environment:
JEST_JUNIT_OUTPUT_NAME: results.xml
steps:
- *restore_git_cache
- checkout
- *restore_npm_cache
- run:
name: Unit
environment:
JEST_JUNIT_OUTPUT_DIR: test-results/unit
command: npm run test:unit -- --reporters="default" --reporters="jest-junit" --coverage --coverageReporters=text --coverageReporters=lcov --maxWorkers="2"
- store_artifacts:
path: coverage
- store_test_results:
path: test-results
build:
<<: *defaults
environment:
NODE_ENV: production
NODE_OPTIONS: --max-old-space-size=4000
steps:
- *restore_git_cache
- checkout
- *restore_npm_cache
- run:
name: Build
command: npm run build
- *save_build_cache
- *save_dist_cache
store_build:
<<: *defaults
steps:
- *restore_build_cache
- store_artifacts:
path: build
store_dist:
<<: *defaults
steps:
- *restore_dist_cache
- store_artifacts:
path: dist
integration:
<<: *defaults
parallelism: 2
environment:
JEST_JUNIT_OUTPUT_NAME: results.txt
steps:
- *restore_git_cache
- checkout
- *restore_npm_cache
- *restore_build_cache
- browser-tools/install-chrome
- browser-tools/install-chromedriver
- run:
name: Integration
environment:
JEST_JUNIT_OUTPUT_DIR: test-results/integration
command: |
google-chrome --version
chromedriver --version
export TESTFILES=$(circleci tests glob "test/integration/*.test.js" | circleci tests split --split-by=timings)
$(npm bin)/jest ${TESTFILES} --reporters="default" --reporters="jest-junit" --runInBand
- store_test_results:
path: test-results
deploy-npm:
<<: *defaults
environment:
NODE_OPTIONS: --max-old-space-size=4000
steps:
- *restore_git_cache
- *restore_dist_cache
- checkout
- run: |
echo export RELEASE_VERSION="0.1.0-prerelease.$(date +'%Y%m%d%H%M%S')" >> $BASH_ENV
echo export NPM_TAG=latest >> $BASH_ENV
if [ "$CIRCLE_BRANCH" == "master" ]
then echo export NPM_TAG=stable >> $BASH_ENV
fi
if [[ "$CIRCLE_BRANCH" == hotfix/* ]] # double brackets are important for matching the wildcard
then echo export NPM_TAG=hotfix >> $BASH_ENV
fi
- run: npm version --no-git-tag-version $RELEASE_VERSION
- run: |
npm set //registry.npmjs.org/:_authToken=$NPM_TOKEN
npm publish --tag $NPM_TAG
- run: git tag $RELEASE_VERSION
- run: git push $CIRCLE_REPOSITORY_URL $RELEASE_VERSION
deploy-gh-pages:
<<: *defaults
steps:
- *restore_git_cache
- checkout
- *restore_npm_cache
- *restore_build_cache
- run: |
git config --global user.email $(git log --pretty=format:"%ae" -n1)
git config --global user.name $(git log --pretty=format:"%an" -n1)
- run: npm run deploy -- -e $CIRCLE_BRANCH
push-translations:
<<: *defaults
steps:
- *restore_git_cache
- checkout
- *restore_npm_cache
- run: npm run i18n:src
- run: npm run i18n:push
workflows:
version: 2
push-translations:
triggers:
- schedule:
cron: 0 0 * * * # daily at 12 UTC, 8 ET
filters:
branches:
only:
- develop
jobs:
- setup:
context:
- dockerhub-credentials
- push-translations:
context:
- dockerhub-credentials
requires:
- setup
build-test-no-deploy:
jobs:
- build-test-no-cache:
context:
- dockerhub-credentials
filters:
branches:
ignore:
- master
- develop
- /^hotfix\/.*/
build-test-deploy:
jobs:
- setup:
context:
- dockerhub-credentials
filters:
branches:
only:
- master
- develop
- /^hotfix\/.*/
- lint:
context:
- dockerhub-credentials
requires:
- setup
- unit:
context:
- dockerhub-credentials
requires:
- setup
- build:
context:
- dockerhub-credentials
requires:
- setup
- integration:
context:
- dockerhub-credentials
requires:
- build
- store_build:
context:
- dockerhub-credentials
requires:
- build
filters:
branches:
only:
- master
- develop
- /^hotfix\/.*/
- store_dist:
context:
- dockerhub-credentials
requires:
- build
filters:
branches:
only:
- master
- develop
- /^hotfix\/.*/
- deploy-npm:
context:
- dockerhub-credentials
requires:
- lint
- unit
- integration
- build
filters:
branches:
only:
- master
- develop
- /^hotfix\/.*/
- deploy-gh-pages:
context:
- dockerhub-credentials
requires:
- lint
- unit
- integration
- build
filters:
branches:
ignore:
- /^dependabot/.*/
- /^renovate/.*/
- /^pull/.*/ # don't deploy to gh pages on PRs.
+4 -1
View File
@@ -1,7 +1,10 @@
node_modules/*
build/*
dist/*
# Files imported from upstream
test/*
src/examples/*
# Files vendored from upstreams
src/addons/addons
src/addons/libraries
src/addons/api-libraries
+1 -1
View File
@@ -1,3 +1,3 @@
module.exports = {
extends: ['scratch', 'scratch/node']
extends: ['scratch', 'scratch/node', 'scratch/es6']
};
+238
View File
@@ -0,0 +1,238 @@
name: CI/CD
on:
workflow_dispatch:
push:
concurrency:
group: "${{ github.workflow }} @ ${{ github.head_ref || github.ref }}"
cancel-in-progress: true
permissions:
contents: write # publish a GitHub release
pages: write # deploy to GitHub Pages
issues: write # comment on released issues
pull-requests: write # comment on released pull requests
jobs:
setup:
runs-on: ubuntu-latest
env:
DETECT_CHROMEDRIVER_VERSION: "true"
JEST_JUNIT_OUTPUT_DIR: test-results
NODE_OPTIONS: --max-old-space-size=4000
steps:
- uses: actions/checkout@v4
- uses: wagoid/commitlint-github-action@v5
if: github.ref_name == 'develop'
- uses: actions/setup-node@v3
with:
cache: "npm"
node-version-file: ".nvmrc"
- name: Info
run: |
cat <<EOF
Node version: $(node --version)
NPM version: $(npm --version)
GitHub ref: ${{ github.ref }}
GitHub head ref: ${{ github.head_ref }}
EOF
- name: Install Dependencies
run: npm ci
- name: Lint
run: |
npm run test:lint -- --quiet --output-file test-results/eslint/results.xml --format junit
- name: Store Lint Results
uses: actions/upload-artifact@v3
with:
name: lint-output
path: ./test-results/*
- name: Cache node_modules
id: cache-nodemodules
uses: actions/cache@v3
with:
path:
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- name: Cache src/generated
id: cache-generated
uses: actions/cache@v3
with:
path:
src/generated
key: ${{ runner.os }}-generated-${{ hashFiles('package-lock.json') }}
- name: Cache static/microbit
id: cache-static
uses: actions/cache@v3
with:
path:
static/microbit
key: ${{ runner.os }}-microbit-${{ hashFiles('package-lock.json') }}
test-unit:
needs: setup
runs-on: ubuntu-latest
env:
JEST_JUNIT_OUTPUT_NAME: unit-results.xml
JEST_JUNIT_OUTPUT_DIR: test-results/unit
steps:
- uses: actions/checkout@v4
- name: Cache NPM dependencies
uses: actions/cache@v3
with:
path:
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- name: Run Unit Tests
run: npm run test:unit -- --reporters="default" --reporters="jest-junit" --coverage --coverageReporters=text --coverageReporters=lcov --maxWorkers="2"
- name: Store Unit Test Results
uses: actions/upload-artifact@v3
with:
name: unit-test-output
path: ./test-results/*
build:
needs: [setup, test-unit]
env:
NODE_OPTIONS: --max-old-space-size=4000
DETECT_CHROMEDRIVER_VERSION: "true"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
cache: "npm"
node-version-file: ".nvmrc"
- name: Retrieve node_modules
uses: actions/cache@v3
with:
path:
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- name: Retrieve src/generated
uses: actions/cache@v3
with:
path:
src/generated
key: ${{ runner.os }}-generated-${{ hashFiles('package-lock.json') }}
- name: Retireve static/microbit
uses: actions/cache@v3
with:
path:
static/microbit
key: ${{ runner.os }}-microbit-${{ hashFiles('package-lock.json') }}
- name: Run Build
env:
NODE_ENV: production
run: npm run build
- name: Cache Build Directory
uses: actions/cache@v3
with:
path:
./build
key: ${{ runner.os }}-build-${{ hashFiles('package-lock.json') }}
- name: Cache Dist Directory
uses: actions/cache@v3
with:
path:
./dist
key: ${{ runner.os }}-dist-${{ hashFiles('package-lock.json') }}
- name: Store Build Output
uses: actions/upload-artifact@v3
with:
name: build-output
path: ./build
- name: Store Dist Output
uses: actions/upload-artifact@v3
with:
name: dist-output
path: ./dist
test-integration:
needs: build
runs-on: ubuntu-latest
env:
JEST_JUNIT_OUTPUT_NAME: results.txt
JEST_JUNIT_OUTPUT_DIR: test-results/integration
steps:
- uses: actions/checkout@v4
- name: Install Chrome Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgconf-2-4 libatk1.0-0 libatk-bridge2.0-0 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm-dev libnss3-dev libxss-dev libasound2
- name: Retrieve npm dependencies
uses: actions/cache@v3
with:
path:
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- name: Retrieve Build
uses: actions/cache@v3
with:
path:
./build
key: ${{ runner.os }}-build-${{ hashFiles('package-lock.json') }}
- uses: browser-actions/setup-chrome@v1
with:
chrome-version: stable
id: setup-chrome
- run: |
echo Installed chromium version: ${{ steps.setup-chrome.outputs.chrome-version }}
${{ steps.setup-chrome.outputs.chrome-path }} --version
- name: Run Integration Tests
run: npm run test:integration -- --reporters="default" --reporters="jest-junit"
- name: Store Integration Test Results
uses: actions/upload-artifact@v3
with:
name: integration-test-output
path: ./test-results/*
deploy-npm:
needs: [test-integration, test-unit]
if: ${{ startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/hotfix') || startsWith(github.ref, 'refs/heads/beta') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Retrieve npm dependencies
uses: actions/cache@v3
with:
path:
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- name: Retrieve Dist Directory
uses: actions/cache@v3
with:
path:
./dist
key: ${{ runner.os }}-dist-${{ hashFiles('package-lock.json') }}
- run: |
if [[ ${{contains(github.ref, 'hotfix')}} ]]; then
sed -e "s|hotfix/REPLACE|${{ github.ref_name }}|" --in-place release.config.js
fi
- name: Semantic Release
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx --no -- semantic-release
deploy-gh-pages:
needs: [test-integration, test-unit]
if: ${{ startsWith(github.ref, 'refs/heads/master') || startsWith(github.ref, 'refs/heads/hotfix') || startsWith(github.ref, 'refs/heads/beta') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Retrieve npm dependencies
uses: actions/cache@v3
with:
path:
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- name: Retrieve Build Directory
uses: actions/cache@v3
with:
path:
./build
key: ${{ runner.os }}-build-${{ hashFiles('package-lock.json') }}
- name: Deploy playground to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./build
full_commit_message: "Build for ${{ github.sha }} ${{ github.event.head_commit.message }}"
+37
View File
@@ -0,0 +1,37 @@
name: Daily Traslation Update
on:
workflow_dispatch: # Allows you to run this workflow manually from the Actions tab
schedule:
- cron: 0 0 * * * # daily at midnight UTC = 7-8pm US Eastern
concurrency:
group: '${{ github.workflow }}'
cancel-in-progress: true
permissions:
contents: write
jobs:
daily-tx-pull:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: 'develop'
- uses: actions/setup-node@v3
with:
cache: 'npm'
node-version-file: '.nvmrc'
- name: Info
run: |
cat <<EOF
Node version: $(node --version)
NPM version: $(npm --version)
GitHub ref: ${{ github.ref }}
GitHub head ref: ${{ github.head_ref }}
EOF
- name: Setup
run: npm --production=false ci
- name: Extract translations
run: npm run i18n:src
- name: Push translations
env:
TX_TOKEN: ${{ secrets.TX_TOKEN }}
run: npm run i18n:push
+9
View File
@@ -17,3 +17,12 @@ npm-*
# Generated translation files
/translations
/locale
# Other generated source
/src/generated
# Downloaded during "npm install"
/static/microbit
# for act
.secrets
+1
View File
@@ -0,0 +1 @@
* text eol=lf
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install commitlint --edit "$1"
+1
View File
@@ -0,0 +1 @@
v16
+4962
View File
File diff suppressed because it is too large. Load diff
+7 -2
View File
@@ -162,6 +162,12 @@ If you want to watch the browser as it runs the test, rather than running headle
USE_HEADLESS=no $(npm bin)/jest --runInBand test/integration/backpack.test.js
```
_Note: If you are seeing failed tests related to `chromedriver` being incompatible with your version of Chrome, you may need to update `chromedriver` with:_
```bash
npm install chromedriver@{version}
```
## Troubleshooting
### Ignoring optional dependencies
@@ -289,6 +295,5 @@ Here's what will happen in the project state machine:
7. The `SHOWING_WITH_ID` state. Now the project appears normally and is playable and editable.
## Donate
We provide [Scratch](https://scratch.mit.edu) free of charge, and want to keep it that way! Please consider making a [donation](https://secure.donationpay.org/scratchfoundation/) to support our continued engineering, design, community, and resource development efforts. Donations of any size are appreciated. Thank you!
We provide [Scratch](https://scratch.mit.edu) free of charge, and want to keep it that way! Please consider making a [donation](https://www.scratchfoundation.org/donate) to support our continued engineering, design, community, and resource development efforts. Donations of any size are appreciated. Thank you!
-->
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
ignores: [message => message.startsWith('chore(release):')]
};
+36618 -8375
View File
File diff suppressed because it is too large. Load diff
+48 -33
View File
@@ -1,15 +1,23 @@
{
"name": "scratch-gui",
"version": "0.1.0",
"description": "GraphicaL User Interface for creating and running Scratch 3.0 projects",
"version": "3.2.37",
"description": "Graphical User Interface for creating and running Scratch 3.0 projects",
"author": "Massachusetts Institute of Technology",
"license": "GPL-3.0",
"homepage": "https://github.com/LLK/scratch-gui#readme",
"repository": {
"type": "git",
"url": "https://github.com/LLK/scratch-gui.git"
},
"main": "./dist/scratch-gui.js",
"scripts": {
"build": "npm run clean && webpack --colors --bail",
"clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist",
"deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"[skip ci] Build for $(git log --pretty=format:%H -n1)\"",
"prepublish": "node scripts/prepublish.mjs",
"prune": "./prune-gh-pages.sh",
"i18n:push": "tx-push-src scratch-editor interface translations/en.json",
"i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push",
"i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/",
"start": "webpack-dev-server",
"test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration",
"test:integration": "jest --maxWorkers=4 test[\\\\/]integration",
@@ -18,26 +26,28 @@
"test:smoke": "jest --runInBand test[\\\\/]smoke",
"watch": "webpack --colors --watch"
},
"author": "Massachusetts Institute of Technology",
"license": "GPL-3.0",
"private": true,
"homepage": "https://github.com/LLK/scratch-gui#readme",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/LLK/scratch-gui.git"
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"dependencies": {
"@microbit/microbit-universal-hex": "0.2.2",
"@turbowarp/scratch-l10n": "^3.1000.20230326022803",
"arraybuffer-loader": "^1.0.6",
"autoprefixer": "^9.0.1",
"balance-text": "3.3.1",
"base64-loader": "1.0.0",
"bowser": "1.9.4",
"cat-blocks": "npm:scratch-blocks@0.1.0-prerelease.20220318143026",
"classnames": "2.2.6",
"computed-style-to-inline-style": "3.0.0",
"cookie": "0.5.0",
"copy-webpack-plugin": "6.4.1",
"core-js": "2.5.7",
"css-loader": "^1.0.0",
"dapjs": "2.3.0",
"es6-object-assign": "1.1.0",
"file-loader": "2.0.0",
"get-float-time-domain-data": "0.1.0",
"get-user-media-promise": "1.1.4",
"immutable": "3.8.2",
@@ -59,9 +69,9 @@
"prop-types": "^15.5.10",
"query-string": "^5.1.1",
"raw-loader": "^0.5.1",
"react": "16.2.0",
"react": "^16.0.0",
"react-contextmenu": "2.9.4",
"react-dom": "16.2.1",
"react-dom": "^16.0.0",
"react-draggable": "3.0.5",
"react-ga": "2.5.3",
"react-intl": "2.9.0",
@@ -76,15 +86,14 @@
"react-virtualized": "9.20.1",
"redux": "3.7.2",
"redux-throttle": "0.1.1",
"scratch-audio": "git+https://github.com/TurboWarp/scratch-audio.git#develop",
"scratch-audio": "git+https://github.com/TurboWarp/scratch-audio.git#merge-upstream",
"scratch-blocks": "git+https://github.com/TurboWarp/scratch-blocks.git#develop-builds",
"@turbowarp/scratch-l10n": "^3.1000.20230326022803",
"scratch-paint": "git+https://github.com/TurboWarp/scratch-paint.git#develop",
"scratch-render": "git+https://github.com/TurboWarp/scratch-render.git#develop",
"scratch-render-fonts": "1.0.0-prerelease.20210401210003",
"scratch-storage": "git+https://github.com/TurboWarp/scratch-storage.git#develop",
"scratch-svg-renderer": "git+https://github.com/TurboWarp/scratch-svg-renderer.git#develop",
"scratch-vm": "git+https://github.com/TurboWarp/scratch-vm.git#develop",
"scratch-paint": "github:TurboWarp/scratch-paint#merge-upstream",
"scratch-render": "git+https://github.com/TurboWarp/scratch-render.git#merge-upstream",
"scratch-render-fonts": "1.0.0-prerelease.20231017225105",
"scratch-storage": "git+https://github.com/TurboWarp/scratch-storage.git#merge-upstream",
"scratch-svg-renderer": "0.2.0",
"scratch-vm": "git+https://github.com/TurboWarp/scratch-vm.git#merge-upstream",
"startaudiocontext": "1.2.1",
"style-loader": "^0.23.0",
"text-encoding": "0.7.0",
@@ -104,34 +113,40 @@
"@babel/plugin-transform-async-to-generator": "7.14.5",
"@babel/preset-env": "7.14.8",
"@babel/preset-react": "7.14.5",
"@commitlint/cli": "17.8.1",
"@commitlint/config-conventional": "17.8.1",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.3",
"babel-loader": "8.2.2",
"chromedriver": "105.0.0",
"babel-eslint": "10.1.0",
"babel-loader": "8.3.0",
"chromedriver": "117.0.3",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.3.0",
"eslint": "5.16.0",
"eslint-config-scratch": "6.0.0",
"enzyme-adapter-react-16": "1.15.7",
"eslint": "8.55.0",
"eslint-config-scratch": "9.0.3",
"eslint-import-resolver-webpack": "0.11.1",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jest": "22.17.0",
"eslint-plugin-react": "7.24.0",
"eslint-plugin-react": "7.33.2",
"file-loader": "6.2.0",
"gh-pages": "3.2.3",
"html-webpack-plugin": "^4.2.0",
"jest": "21.2.1",
"jest-junit": "7.0.0",
"mkdirp": "1.0.3",
"raf": "3.4.1",
"react-test-renderer": "16.2.0",
"react-test-renderer": "16.14.0",
"redux-mock-store": "1.5.3",
"rimraf": "2.7.1",
"scratch-semantic-release-config": "1.0.8",
"selenium-webdriver": "3.6.0",
"semantic-release": "19.0.5",
"uglifyjs-webpack-plugin": "1.3.0",
"url-loader": "^4.1.1",
"url-loader": "4.1.1",
"web-audio-test-api": "0.5.2",
"webpack": "4.46.0",
"webpack": "4.47.0",
"webpack-cli": "3.3.12",
"webpack-dev-server": "3.11.2"
"webpack-dev-server": "3.11.2",
"yauzl": "2.10.0"
},
"jest": {
"setupFiles": [
+19
View File
@@ -0,0 +1,19 @@
module.exports = {
extends: 'scratch-semantic-release-config',
branches: [
{
name: 'develop'
// default channel
},
{
name: 'hotfix/REPLACE', // replace with actual hotfix branch name
channel: 'hotfix',
prerelease: 'hotfix'
},
{
name: 'beta',
channel: 'beta',
prerelease: true
}
]
};
+5 -1
View File
@@ -2,6 +2,10 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>LLK/scratch-renovate-config:conservative"
"github>scratchfoundation/scratch-renovate-config"
],
"ignoreDeps": [
"cat-blocks"
]
}
+8
View File
@@ -0,0 +1,8 @@
const path = require('path');
module.exports = {
extends: [path.resolve(__dirname, '..', '.eslintrc.js')],
rules: {
// NPM scripts are allowed to use console.log & friends
'no-console': 'off'
}
};
+126
View File
@@ -0,0 +1,126 @@
// From the NPM docs:
// "If you need to perform operations on your package before it is used, in a way that is not dependent on the
// operating system or architecture of the target system, use a prepublish script."
// Once this step is complete, a developer should be able to work without an Internet connection.
// See also: https://docs.npmjs.com/cli/using-npm/scripts
import fs from 'fs';
import path from 'path';
import crossFetch from 'cross-fetch';
import yauzl from 'yauzl';
import {fileURLToPath} from 'url';
/** @typedef {import('yauzl').Entry} ZipEntry */
/** @typedef {import('yauzl').ZipFile} ZipFile */
// these aren't set in ESM mode
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// base/root path for the project
const basePath = path.join(__dirname, '..');
/**
* Extract the first matching file from a zip buffer.
* The path within the zip file is ignored: the destination path is `${destinationDirectory}/${basename(entry.name)}`.
* Prints warnings if more than one matching file is found.
* @param {function(ZipEntry): boolean} filter Returns true if the entry should be extracted.
* @param {string} relativeDestDir The directory to extract to, relative to `basePath`.
* @param {Buffer} zipBuffer A buffer containing the zip file.
* @returns {Promise<string>} A Promise for the base name of the written file (without directory).
*/
const extractFirstMatchingFile = (filter, relativeDestDir, zipBuffer) => new Promise((resolve, reject) => {
try {
let extractedFileName;
yauzl.fromBuffer(zipBuffer, {lazyEntries: true}, (zipError, zipfile) => {
if (zipError) {
throw zipError;
}
zipfile.readEntry();
zipfile.on('end', () => {
resolve(extractedFileName);
});
zipfile.on('entry', entry => {
if (!filter(entry)) {
// ignore non-matching file
return zipfile.readEntry();
}
if (extractedFileName) {
console.warn(`Multiple matching files found. Ignoring: ${entry.fileName}`);
return zipfile.readEntry();
}
extractedFileName = entry.fileName;
console.info(`Found matching file: ${entry.fileName}`);
zipfile.openReadStream(entry, (fileError, readStream) => {
if (fileError) {
throw fileError;
}
const baseName = path.basename(entry.fileName);
const relativeDestFile = path.join(relativeDestDir, baseName);
console.info(`Extracting ${relativeDestFile}`);
const absoluteDestDir = path.join(basePath, relativeDestDir);
fs.mkdirSync(absoluteDestDir, {recursive: true});
const absoluteDestFile = path.join(basePath, relativeDestFile);
const outStream = fs.createWriteStream(absoluteDestFile);
readStream.on('end', () => {
outStream.close();
zipfile.readEntry();
});
readStream.pipe(outStream);
});
});
});
} catch (error) {
reject(error);
}
});
const downloadMicrobitHex = async () => {
const url = 'https://downloads.scratch.mit.edu/microbit/scratch-microbit.hex.zip';
console.info(`Downloading ${url}`);
const response = await crossFetch(url);
const zipBuffer = Buffer.from(await response.arrayBuffer());
const relativeHexDir = path.join('static', 'microbit');
const hexFileName = await extractFirstMatchingFile(
entry => /\.hex$/.test(entry.fileName),
path.join('static', 'microbit'),
zipBuffer
);
const relativeHexFile = path.join(relativeHexDir, hexFileName);
const relativeGeneratedDir = path.join('src', 'generated');
const relativeGeneratedFile = path.join(relativeGeneratedDir, 'microbit-hex-url.cjs');
const absoluteGeneratedDir = path.join(basePath, relativeGeneratedDir);
fs.mkdirSync(absoluteGeneratedDir, {recursive: true});
const absoluteGeneratedFile = path.join(basePath, relativeGeneratedFile);
const requirePath = `./${path
.relative(relativeGeneratedDir, relativeHexFile)
.split(path.win32.sep)
.join(path.posix.sep)}`;
fs.writeFileSync(
absoluteGeneratedFile,
[
'// This file is generated by scripts/prepublish.mjs',
'// Do not edit this file directly',
'// This file relies on a loader to turn this `require` into a URL',
`module.exports = require('${requirePath}');`,
'' // final newline
].join('\n')
);
console.info(`Wrote ${relativeGeneratedFile}`);
};
const prepublish = async () => {
await downloadMicrobitHex();
};
prepublish().then(
() => {
console.info('Prepublish script complete');
process.exit(0);
},
e => {
console.error(e);
process.exit(1);
}
);
+18 -2
View File
@@ -9,17 +9,33 @@ module.exports = {
process: true
},
rules: {
// BEGIN: these caused trouble after upgrading eslint-plugin-react from 7.24.0 to 7.33.2
'react/forbid-prop-types': 'off',
'react/no-unknown-property': 'off',
// END: these caused trouble after upgrading eslint-plugin-react from 7.24.0 to 7.33.2
'no-warning-comments': 'off',
'import/no-mutable-exports': 'error',
'import/no-commonjs': 'error',
'import/no-amd': 'error',
'import/no-nodejs-modules': 'error',
'react/jsx-no-literals': 'error',
'no-confusing-arrow': ['error', {
'allowParens': true
allowParens: true
}]
},
overrides: [
{
files: ['**/.eslintrc.js'],
env: {
node: true
},
rules: {
'import/no-commonjs': 'off'
}
}
],
settings: {
react: {
'react': {
version: '16.2' // Prevent 16.3 lifecycle method errors
},
'import/resolver': {
+8 -8
View File
@@ -37,7 +37,7 @@ a[href] {
}
a:active, a:focus {
outline: none;
box-shadow: 0 0 0 3px $motion-transparent;
box-shadow: 0 0 0 3px $looks-transparent;
}
.container {
@@ -104,7 +104,7 @@ a:active, a:focus {
background: #151515;
}
.button:active, .button:focus-within {
box-shadow: 0 0 0 3px $motion-transparent;
box-shadow: 0 0 0 3px $looks-transparent;
}
.search-container {
@@ -115,7 +115,7 @@ a:active, a:focus {
border-radius: 4px;
}
.search-container:focus-within {
box-shadow: 0 0 0 3px $motion-transparent;
box-shadow: 0 0 0 3px $looks-transparent;
}
.search-input {
color: inherit;
@@ -171,7 +171,7 @@ a:active, a:focus {
border-color: white;
}
.feedback-button-outer:active, .feedback-button-outer:focus-within {
box-shadow: 0 0 0 3px $motion-transparent;
box-shadow: 0 0 0 3px $looks-transparent;
}
.feedback-button-inner {
text-decoration: none;
@@ -308,7 +308,7 @@ a:active, a:focus {
}
.reset-button:active, .reset-button:focus-within {
outline: none;
box-shadow: 0 0 0 3px $motion-transparent;
box-shadow: 0 0 0 3px $looks-transparent;
}
.reset-button-image {
padding: 1px 0;
@@ -356,7 +356,7 @@ a:active, a:focus {
}
.switch:active, .switch:focus-within {
outline: none;
box-shadow: 0 0 0 3px $motion-transparent;
box-shadow: 0 0 0 3px $looks-transparent;
}
.tag-container {
@@ -487,7 +487,7 @@ a:active, a:focus {
.setting input:focus, .setting input:active {
outline: none;
border-color: $motion-primary;
box-shadow: 0 0 0 3px $motion-transparent;
box-shadow: 0 0 0 3px $looks-transparent;
}
.setting input[type="checkbox"] {
height: 20px;
@@ -579,7 +579,7 @@ a:active, a:focus {
}
.select-option:active, .select-option:focus-within {
outline: none;
box-shadow: 0 0 0 3px $motion-transparent;
box-shadow: 0 0 0 3px $looks-transparent;
z-index: 10;
}
+5 -5
View File
@@ -20,7 +20,7 @@ $more-button-size: 2.25rem;
justify-content: center;
cursor: pointer;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
background: $motion-primary;
background: $looks-secondary;
outline: none;
border: none;
transition: background-color 0.2s;
@@ -46,14 +46,14 @@ button::-moz-focus-inner {
border-radius: 100%;
width: $main-button-size;
height: $main-button-size;
box-shadow: 0 0 0 4px $motion-transparent;
box-shadow: 0 0 0 4px $looks-transparent;
z-index: $z-index-add-button;
transition: transform, box-shadow 0.5s;
}
.main-button:hover {
transform: scale(1.1);
box-shadow: 0 0 0 6px $motion-transparent;
box-shadow: 0 0 0 6px $looks-transparent;
}
.main-icon {
@@ -73,7 +73,7 @@ button::-moz-focus-inner {
*/
overflow-y: hidden;
background: $motion-tertiary;
background: $looks-secondary-dark;
border-top-left-radius: $more-button-size;
border-top-right-radius: $more-button-size;
width: $more-button-size;
@@ -116,7 +116,7 @@ button::-moz-focus-inner {
.more-button {
width: $more-button-size;
height: $more-button-size;
background: $motion-tertiary;
background: $looks-secondary-dark;
}
.more-icon {
+1 -1
View File
@@ -21,7 +21,7 @@
align-items: center;
justify-content: space-around;
padding: 0.75rem 0;
color: $motion-primary;
color: $looks-secondary;
text-align: center;
background: none;
pointer-events: none;
@@ -41,7 +41,7 @@ $hover-scale: 1.25;
}
.selection-background {
background: $motion-primary;
background: $looks-secondary;
opacity: 0.5;
}
@@ -64,7 +64,7 @@ $hover-scale: 1.25;
}
.selector .trim-line {
border: 1px solid $motion-tertiary;
border: 1px solid $looks-secondary;
}
.playhead-container {
@@ -83,7 +83,7 @@ $hover-scale: 1.25;
*/
width: 100%;
height: 100%;
border-left: 1px solid $motion-primary;
border-left: 1px solid $looks-secondary;
border-top: none;
border-bottom: none;
border-right: none;
@@ -118,7 +118,7 @@ $hover-scale: 1.25;
}
.trimmer .trim-handle {
filter: hue-rotate(150deg);
filter: hue-rotate(-240deg) brightness(1.35);
}
.trim-handle img {
+1 -1
View File
@@ -98,7 +98,7 @@
}
.more {
background: $motion-primary;
background: $looks-secondary;
color: $ui-white;
border: none;
outline: none;
+19 -4
View File
@@ -22,7 +22,7 @@
user-select: none;
width: 500px;
color: $text-primary-light;
color: $text-primary;
overflow: hidden;
}
@@ -52,11 +52,26 @@
flex-direction: column;
gap: 0.25rem;
}
[theme="dark"] .body {
.button-row button {
border: 1px solid $looks-secondary;
border-radius: 0.25rem;
padding: 0.5rem 2rem;
background: $looks-secondary;
color: white;
font-weight: bold;
font-size: 0.875rem;
cursor: pointer;
}
.faq-link-text {
margin: 2rem 0 .5rem 0;
font-size: .875rem;
color: $text-primary;
background: $ui-primary;
}
.body a {
color: $motion-primary !important;
.faq-link {
color: $looks-secondary;
text-decoration: none;
}
+2 -2
View File
@@ -214,7 +214,7 @@
}
.deck-name {
color: $motion-primary;
color: $looks-secondary;
font-weight: bold;
font-size: 0.85rem;
margin: .625rem 0px;
@@ -260,7 +260,7 @@
.see-all-button {
cursor: pointer;
padding: 0.5rem 1rem;
background-color: $motion-primary;
background-color: $looks-secondary;
color: white;
font-weight: bold;
border-radius: 0.25rem;
+1 -1
View File
@@ -28,7 +28,7 @@
.small {
width: 0.825rem;
height: 0.825rem;
background-color: $motion-primary;
background-color: $looks-secondary;
color: $ui-white;
}
@@ -4,14 +4,17 @@ import React from 'react';
import keyMirror from 'keymirror';
import classNames from 'classnames';
import BalancedFormattedMessage from '../../containers/balanced-formatted-message.jsx';
import Box from '../box/box.jsx';
import Dots from './dots.jsx';
import closeIcon from '../close-button/icon--close.svg';
import radarIcon from './icons/searching.png';
import bluetoothIcon from './icons/bluetooth-white.svg';
import backIcon from './icons/back.svg';
import bluetoothIcon from './icons/bluetooth-white.svg';
import enterUpdateIcon from './icons/enter-update.svg';
import radarIcon from './icons/searching.png';
import warningIcon from './icons/warning.svg';
import styles from './connection-modal.css';
@@ -21,8 +24,13 @@ const PHASES = keyMirror({
notfound: null
});
const AutoScanningStep = props => (
<Box className={styles.body}>
const AutoScanningStep = props => {
// Offer to update both during scan and after a failed scan, as long there's an update function.
// It's possible the scan will find "some" device but not the desired device,
// so don't limit the update offer to just the PHASES.notfound case.
const showUpdate = !!(props.onUpdatePeripheral &&
(props.phase === PHASES.pressbutton || props.phase === PHASES.notfound));
return (<Box className={styles.body}>
<Box className={styles.activityArea}>
<div className={styles.activityAreaInfo}>
<div className={styles.centeredRow}>
@@ -51,13 +59,18 @@ const AutoScanningStep = props => (
</React.Fragment>
)}
{props.phase === PHASES.notfound && (
<Box className={styles.instructions}>
<React.Fragment>
<img
className={styles.helpStepImage}
src={warningIcon}
/>
<FormattedMessage
className={styles.helpStepText}
defaultMessage="No devices found"
description="Text shown when no devices could be found"
id="gui.connection.auto-scanning.noPeripheralsFound"
/>
</Box>
</React.Fragment>
)}
</div>
</div>
@@ -79,6 +92,15 @@ const AutoScanningStep = props => (
/>
)}
</Box>
{showUpdate && (
<Box className={classNames(styles.bottomAreaItem, styles.instructions)}>
<BalancedFormattedMessage
defaultMessage="If you don't see your device, you may need to update it to work with Scratch."
description="Prompt for updating a peripheral device"
id="gui.connection.auto-scanning.updatePeripheralPrompt"
/>
</Box>
)}
<Dots
className={styles.bottomAreaItem}
counter={0}
@@ -136,15 +158,32 @@ const AutoScanningStep = props => (
/>
</button>
)}
{showUpdate && (
<button
className={classNames(styles.bottomAreaItem, styles.connectionButton)}
onClick={props.onUpdatePeripheral}
>
<FormattedMessage
defaultMessage="Update my Device"
description="Button to enter the peripheral update mode"
id="gui.connection.auto-scanning.updatePeripheralButton"
/>
<img
className={styles.buttonIconRight}
src={enterUpdateIcon}
/>
</button>
)}
</Box>
</Box>
</Box>
);
</Box>);
};
AutoScanningStep.propTypes = {
connectionTipIconURL: PropTypes.string,
onRefresh: PropTypes.func,
onStartScan: PropTypes.func,
onUpdatePeripheral: PropTypes.func,
phase: PropTypes.oneOf(Object.keys(PHASES))
};
@@ -3,10 +3,11 @@
.modal-content {
width: 480px;
line-height: 1.75;
}
.header {
background-color: $pen-primary;
background-color: $pen-tertiary;
}
.body {
@@ -80,8 +81,8 @@
border-radius: 0.25rem;
font-weight: 600;
font-size: 0.85rem;
background: $motion-primary;
border: $motion-primary;
background: $looks-secondary;
border: $looks-secondary;
color: white;
cursor: pointer;
}
@@ -172,9 +173,9 @@
right: -15px;
left: -15px;
padding: 5px 5px;
background-color: $motion-primary;
background-color: $looks-secondary;
border-radius: 100%;
box-shadow: 0px 0px 0px 4px $motion-transparent;
box-shadow: 0px 0px 0px 4px $looks-transparent;
/* animation: pulse-blue-ring 1s infinite ease-in-out alternate; */
animation: wiggle 0.5s infinite ease-in-out alternate;
@@ -182,7 +183,7 @@
@keyframes pulse-blue-ring {
100% {
box-shadow: 0px 0px 0px 8px $motion-light-transparent;
box-shadow: 0px 0px 0px 8px $looks-light-transparent;
}
}
@@ -208,9 +209,9 @@
.bluetooth-centered-icon {
position: absolute;
padding: 5px 5px;
background-color: $motion-primary;
background-color: $looks-secondary;
border-radius: 100%;
box-shadow: 0px 0px 0px 2px $motion-transparent;
box-shadow: 0px 0px 0px 2px $looks-transparent;
}
.peripheral-tile-widgets {
@@ -220,7 +221,7 @@
.activityArea {
height: 165px;
background-color: $motion-light-transparent;
background-color: $looks-light-transparent;
display: flex;
justify-content: center;
align-items: center;
@@ -255,6 +256,10 @@
max-width: 50px;
}
.help-step-image {
max-width: 40px;
}
[dir="ltr"] .help-step-image {
margin-right: 0.5rem;
}
@@ -264,7 +269,7 @@
}
.help-step-number {
background: $pen-primary;
background: $pen-tertiary;
border-radius: 100%;
display: flex;
justify-content: center;
@@ -297,7 +302,7 @@
.connection-button {
padding: 0.6rem 0.75rem;
border-radius: 0.5rem;
background: $motion-primary;
background: $looks-secondary;
color: white;
font-weight: 600;
font-size: 0.85rem;
@@ -309,7 +314,7 @@
}
.connection-button:disabled {
background: $motion-transparent;
background: $looks-transparent;
}
.segmented-button {
@@ -390,7 +395,7 @@
display: flex;
padding: 0.25rem 0.1rem;
border-radius: 1rem;
background: $motion-light-transparent;
background: $looks-light-transparent;
}
.dots-holder-success {
@@ -409,11 +414,11 @@
}
.inactive-step-dot {
background: $motion-transparent;
background: $looks-transparent;
}
.active-step-dot {
background: $motion-primary;
background: $looks-secondary;
}
.success-dot {
@@ -423,3 +428,21 @@
.error-dot {
background: $error-primary;
}
.scratch-link-error {
align-items: center;
display: flex;
flex-direction: column;
justify-content: space-around;
height: 100%;
padding-top: .5rem;
padding-bottom: .5rem;
width: 90%;
}
.scratch-link-error-details {
display: flex;
resize: none;
height: 60%;
width: inherit;
}
@@ -11,6 +11,7 @@ import ConnectingStep from './connecting-step.jsx';
import ConnectedStep from './connected-step.jsx';
import ErrorStep from './error-step.jsx';
import UnavailableStep from './unavailable-step.jsx';
import UpdatePeripheralStep from './update-peripheral-step.jsx';
import styles from './connection-modal.css';
@@ -19,7 +20,8 @@ const PHASES = keyMirror({
connecting: null,
connected: null,
error: null,
unavailable: null
unavailable: null,
updatePeripheral: null
});
const ConnectionModalComponent = props => (
@@ -39,6 +41,7 @@ const ConnectionModalComponent = props => (
{props.phase === PHASES.connected && <ConnectedStep {...props} />}
{props.phase === PHASES.error && <ErrorStep {...props} />}
{props.phase === PHASES.unavailable && <UnavailableStep {...props} />}
{props.phase === PHASES.updatePeripheral && <UpdatePeripheralStep {...props} />}
</Box>
</Modal>
);
@@ -0,0 +1 @@
<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M15.784 5.587v6.14c-.015.49-.305.907-.741 1.08a1.143 1.143 0 0 1-1.248-.265l-1.164-1.164-4.866 3.679a1.887 1.887 0 0 1-2.639-.357c-.51-.699-.504-1.636.002-2.29l3.658-4.876L7.673 6.42c-.34-.339-.442-.84-.262-1.277a1.145 1.145 0 0 1 1.077-.711h6.14c.304 0 .594.122.813.342.22.22.342.51.343.813z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 437 B

@@ -0,0 +1 @@
<svg width="14" height="12" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.404 4.111a3.66 3.66 0 0 1 .897 3.069c-.194 1.305-1.158 2.551-2.505 3.068h-.008a.901.901 0 0 0-.56 1.051c.111.483.602.789 1.084.678 2.015-.466 3.775-2.093 4.41-4.196a6.495 6.495 0 0 0 .128-3.272 6.517 6.517 0 0 0-1.236-2.61l.846-.848A.618.618 0 0 0 13.02 0H8.983c-.34 0-.619.28-.619.619v4.043c0 .551.661.822 1.05.44l.99-.99zM3.596 7.89a3.663 3.663 0 0 1-.898-3.069c.195-1.305 1.16-2.552 2.506-3.068h.008A.902.902 0 0 0 5.771.7.908.908 0 0 0 4.688.023C2.673.489.913 2.116.278 4.219a6.481 6.481 0 0 0-.127 3.27c.203.942.635 1.84 1.236 2.611l-.847.848C.16 11.338.43 12 .98 12h4.038c.338 0 .617-.28.617-.62V7.338a.617.617 0 0 0-1.049-.44l-.99.99z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 790 B

@@ -0,0 +1 @@
<svg width="50" height="50" viewBox="-2 -2 48 48" xmlns="http://www.w3.org/2000/svg"><circle cx="22" cy="22" r="23.5" fill="#855cd6" opacity=".25"/><circle cx="22" cy="22" r="18" fill="#855cd6"/><path d="M25.404 20.111a3.66 3.66 0 0 1 .897 3.069c-.193 1.305-1.158 2.551-2.505 3.068h-.008a.901.901 0 0 0-.56 1.051.909.909 0 0 0 1.084.678c2.015-.466 3.775-2.093 4.41-4.196a6.495 6.495 0 0 0 .128-3.272 6.518 6.518 0 0 0-1.236-2.61l.846-.848A.618.618 0 0 0 28.02 16h-4.037c-.34 0-.619.28-.619.619v4.043c0 .551.661.822 1.05.44zm-6.808 3.778a3.662 3.662 0 0 1-.897-3.069c.194-1.305 1.159-2.552 2.505-3.068h.008a.902.902 0 0 0 .559-1.051.908.908 0 0 0-1.083-.678c-2.015.466-3.775 2.093-4.41 4.196a6.48 6.48 0 0 0-.127 3.27 6.51 6.51 0 0 0 1.236 2.611l-.847.848c-.38.39-.11 1.052.44 1.052h4.038c.338 0 .618-.28.618-.62v-4.042a.617.617 0 0 0-1.05-.44z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 865 B

@@ -0,0 +1 @@
<svg width="44" height="44" fill="none" xmlns="http://www.w3.org/2000/svg"><g filter="url(#filter0_d_2828_26434)"><rect x="4" y="4" width="36" height="36" rx="18" fill="#FF8C1A"/><circle cx="22" cy="22" r="10" fill="#FF8C1A"/><path fill-rule="evenodd" clip-rule="evenodd" d="M22 23.424a1.44 1.44 0 0 0-1.452 1.43c0 .791.65 1.431 1.452 1.431a1.44 1.44 0 0 0 1.452-1.43c0-.79-.65-1.43-1.452-1.43zm.834-1.303c-.37.815-1.298.815-1.668 0l-1.18-2.586c-.368-.81.094-1.821.835-1.821h2.358c.74 0 1.203 1.011.834 1.82l-1.18 2.587z" fill="#fff"/></g><defs><filter id="filter0_d_2828_26434" x="0" y="0" width="44" height="44" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/><feMorphology radius="4" operator="dilate" in="SourceAlpha" result="effect1_dropShadow_2828_26434"/><feOffset/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 0.54902 0 0 0 0 0.101961 0 0 0 0.15 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_2828_26434"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_2828_26434" result="shape"/></filter></defs></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -3,17 +3,21 @@ import PropTypes from 'prop-types';
import React from 'react';
import classNames from 'classnames';
import BalancedFormattedMessage from '../../containers/balanced-formatted-message.jsx';
import Box from '../box/box.jsx';
import PeripheralTile from './peripheral-tile.jsx';
import Dots from './dots.jsx';
import enterUpdateIcon from './icons/enter-update.svg';
import radarIcon from './icons/searching.png';
import refreshIcon from './icons/refresh.svg';
import warningIcon from './icons/warning.svg';
import styles from './connection-modal.css';
const ScanningStep = props => (
<Box className={styles.body}>
const ScanningStep = props => {
const showUpdate = !!(props.onUpdatePeripheral && !props.scanning);
return (<Box className={styles.body}>
<Box className={styles.activityArea}>
{props.scanning ? (
props.peripheralList.length === 0 ? (
@@ -45,8 +49,13 @@ const ScanningStep = props => (
</div>
)
) : (
<Box className={styles.instructions}>
<Box className={styles.centeredRow}>
<img
className={styles.helpStepImage}
src={warningIcon}
/>
<FormattedMessage
className={styles.helpStepText}
defaultMessage="No devices found"
description="Text shown when no devices could be found"
id="gui.connection.scanning.noPeripheralsFound"
@@ -56,39 +65,71 @@ const ScanningStep = props => (
</Box>
<Box className={styles.bottomArea}>
<Box className={classNames(styles.bottomAreaItem, styles.instructions)}>
<FormattedMessage
defaultMessage="Select your device in the list above."
description="Prompt for choosing a device to connect to"
id="gui.connection.scanning.instructions"
/>
{(props.scanning || props.peripheralList.length > 0) && (
// Show this message if we're still scanning OR if we've found devices
<FormattedMessage
defaultMessage="Select your device in the list above."
description="Prompt for choosing a device to connect to"
id="gui.connection.scanning.instructions"
/>
)}
{showUpdate && (
// Show this message if we're done scanning AND we can update
// Note that it's possible the list includes devices but does not include the desired device,
// so don't limit this message to the (props.peripheralList.length === 0) case
<BalancedFormattedMessage
defaultMessage="If you don't see your device, you may need to update it to work with Scratch."
description="Prompt for updating a peripheral device"
id="gui.connection.scanning.updatePeripheralPrompt"
/>
)}
</Box>
<Dots
className={styles.bottomAreaItem}
counter={0}
total={3}
/>
<button
className={classNames(styles.bottomAreaItem, styles.connectionButton)}
onClick={props.onRefresh}
>
<FormattedMessage
defaultMessage="Refresh"
description="Button in prompt for starting a search"
id="gui.connection.search"
/>
<img
className={styles.buttonIconRight}
src={refreshIcon}
/>
</button>
<Box className={classNames(styles.bottomAreaItem, styles.buttonRow)}>
<button
className={styles.connectionButton}
onClick={props.onRefresh}
>
<FormattedMessage
defaultMessage="Refresh"
description="Button in prompt for starting a search"
id="gui.connection.search"
/>
<img
className={styles.buttonIconRight}
src={refreshIcon}
/>
</button>
{showUpdate && (
<button
className={styles.connectionButton}
onClick={props.onUpdatePeripheral}
>
<FormattedMessage
defaultMessage="Update my Device"
description="Button to enter the peripheral update mode"
id="gui.connection.scanning.updatePeripheralButton"
/>
<img
className={styles.buttonIconRight}
src={enterUpdateIcon}
/>
</button>
)}
</Box>
</Box>
</Box>
);
</Box>);
};
ScanningStep.propTypes = {
connectionSmallIconURL: PropTypes.string,
onConnecting: PropTypes.func,
onRefresh: PropTypes.func,
onUpdatePeripheral: PropTypes.func,
peripheralList: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
rssi: PropTypes.number,
@@ -0,0 +1,262 @@
import 'regenerator-runtime/runtime';
import {FormattedMessage} from 'react-intl';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import React from 'react';
import bindAll from 'lodash.bindall';
import keyMirror from 'keymirror';
import BalancedFormattedMessage from '../../containers/balanced-formatted-message.jsx';
import Box from '../box/box.jsx';
import ProgressRingComponent from '../progress-ring/progress-ring.jsx';
import backIcon from './icons/back.svg';
import sendUpdateIcon from './icons/send-update.svg';
import sendUpdateGlyph from './icons/send-update-white.svg';
import styles from './connection-modal.css';
/** @enum{string} UPDATE_ACTIVITY */
const UPDATE_ACTIVITY = keyMirror({
getReady: null,
sendUpdate: null,
results: null
});
const microBitFirmwareUrl = 'https://microbit.org/get-started/user-guide/firmware/';
class UpdatePeripheralStep extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleSendUpdate'
]);
this.state = {
/** @type {UPDATE_ACTIVITY} */
activity: UPDATE_ACTIVITY.getReady,
/** @type {number} */
progressPercentage: 0,
/** @type {Error?} */
err: null,
/** @type {any} */
res: null
};
}
async handleSendUpdate () {
this.setState({
activity: UPDATE_ACTIVITY.sendUpdate,
progress: 0,
err: null,
res: null
});
try {
const res = await this.props.onSendPeripheralUpdate(progress => {
// On my computer, I get a progress update every 0.005% or so.
// Rendering the progress ring is a little expensive, so filtering updates here reduces the CPU load.
// Updating every 1% doesn't look very smooth, but 0.5% (1/200) looks good to me.
this.setState({progressPercentage: Math.floor(progress * 200) / 2});
});
this.setState({
activity: UPDATE_ACTIVITY.results,
res
});
} catch (err) {
this.setState({
activity: UPDATE_ACTIVITY.results,
err
});
}
}
renderGetReady () {
return (<Box className={styles.activityArea}>
<Box className={styles.scratchLinkHelp}>
<Box className={styles.scratchLinkHelpStep}>
<Box className={styles.helpStepNumber}>
{'1'}
</Box>
<img
className={styles.helpStepImage}
src={this.props.connectionSmallIconURL}
/>
{
// The instructions for getting the peripheral ready for the update process will vary
// depending on the peripheral. Should we get this from the extension somehow?
}
<FormattedMessage
className={styles.helpStepText}
defaultMessage="Connect your {extensionName} to this device using a USB cable."
description="Instructions to connect the micro:bit to the computer for the update process"
id="gui.connection.updatePeripheral.microBitConnect"
values={{
extensionName: this.props.name
}}
/>
</Box>
<Box className={styles.scratchLinkHelpStep}>
<Box className={styles.helpStepNumber}>
{'2'}
</Box>
<img
className={styles.helpStepImage}
src={sendUpdateIcon}
/>
<FormattedMessage
defaultMessage="Press &quot;Do Update&quot; and allow the update to complete."
description="Instructions to press the button to begin the update process"
id="gui.connection.updatePeripheral.pressUpdate"
/>
</Box>
</Box>
</Box>);
}
renderSendUpdate () {
return (<Box className={styles.activityArea}>
<ProgressRingComponent
sizePx={36}
value={this.state.progressPercentage}
max={100}
/>
<FormattedMessage
defaultMessage="Updating {progressPercentage}%"
description="Progress message while updating the peripheral"
id="gui.connection.updatePeripheral.progress"
values={{
progressPercentage: Math.floor(this.state.progressPercentage)
}}
/>
</Box>);
}
renderResults () {
let resultsContent;
if (this.state.err === null) {
resultsContent = (<FormattedMessage
defaultMessage="Update successful!"
description="Message to indicate that the peripheral update was successful"
id="gui.connection.updatePeripheral.updateSuccessful"
/>);
} else if (this.state.err.message === 'No valid interfaces found.') {
// this is a special case where the micro:bit's communication firmware is too old to support WebUSB
resultsContent = (<BalancedFormattedMessage
defaultMessage="Please visit this link to update your micro:bit firmware: {microBitFirmwareLink}"
description="Message to indicate that the special micro:bit interface firmware needs to be updated"
id="gui.connection.updatePeripheral.updateMicroBitFirmware"
values={{
microBitFirmwareLink: <a
rel="noopener noreferrer"
target="_blank"
href={microBitFirmwareUrl}
>
{microBitFirmwareUrl}
</a>
}}
/>);
} else {
resultsContent = (
<Box className={styles.scratchLinkError}>
<FormattedMessage
className={styles.centeredRow}
defaultMessage="Update failed."
description="Message to indicate that the peripheral update failed"
id="gui.connection.updatePeripheral.updateFailed"
/>
<textarea
className={styles.scratchLinkErrorDetails}
readOnly
>
{this.state.err.message}
</textarea>
</Box>
);
}
return (<Box className={styles.activityArea}>
{resultsContent}
</Box>);
}
render () {
const showGetReady = this.state.activity === UPDATE_ACTIVITY.getReady;
const showSendUpdate = this.state.activity === UPDATE_ACTIVITY.sendUpdate;
const showResults = this.state.activity === UPDATE_ACTIVITY.results;
const showBadResults = showResults && !!this.state.err;
return (
<Box className={styles.body}>
{showGetReady && this.renderGetReady()}
{showSendUpdate && this.renderSendUpdate()}
{showResults && this.renderResults()}
<Box className={styles.bottomArea}>
{!showResults &&
<BalancedFormattedMessage
className={styles.bottomAreaItem}
defaultMessage={'Do not leave or reload Scratch or disconnect your {extensionName} ' +
'until the update is complete.'}
description="Notice to not disrupt the peripheral update process"
id="gui.connection.updatePeripheral.doNotDisconnect"
values={{
extensionName: this.props.name
}}
/>
}
{!showSendUpdate &&
<Box className={classNames(styles.bottomAreaItem, styles.buttonRow)}>
<button
className={styles.connectionButton}
onClick={this.props.onScanning}
>
<img
className={classNames(styles.buttonIconLeft, styles.buttonIconBack)}
src={backIcon}
/>
<FormattedMessage
defaultMessage="Go Back"
description="Button to leave the peripheral update process"
id="gui.connection.updatePeripheral.goBackButton"
/>
</button>
{(showGetReady || showBadResults) &&
<button
className={styles.connectionButton}
onClick={this.handleSendUpdate}
>
{showGetReady &&
<FormattedMessage
defaultMessage="Do Update"
description="Button to start the peripheral update"
id="gui.connection.updatePeripheral.updateNowButton"
/>
}
{showBadResults &&
<FormattedMessage
defaultMessage="Try Again"
description="Button to try the peripheral update again"
id="gui.connection.updatePeripheral.updateAgainButton"
/>
}
<img
className={styles.buttonIconRight}
src={sendUpdateGlyph}
/>
</button>
}
</Box>
}
</Box>
</Box>
);
}
}
UpdatePeripheralStep.propTypes = {
connectionSmallIconURL: PropTypes.string,
name: PropTypes.string.isRequired,
onScanning: PropTypes.func.isRequired,
onSendPeripheralUpdate: PropTypes.func.isRequired
};
export default UpdatePeripheralStep;
+1 -1
View File
@@ -28,7 +28,7 @@
}
.menu-item:hover {
background: $motion-primary;
background: $looks-secondary;
color: white;
}
@@ -10,15 +10,17 @@
justify-content: center;
align-items: center;
}
[theme="dark"] .crash-wrapper {
background-color: $motion-primary-dark;
}
.body {
width: 50%;
color: white;
text-align: center;
}
/* scratch-www was overriding the colors */
.body p, .body h2 {
color: inherit;
}
.reloadButton {
border: 1px solid $motion-primary;
border-radius: 0.25rem;
@@ -60,8 +60,8 @@
}
.option-card:hover {
border: 2px solid $motion-primary;
box-shadow: 0px 0px 0px 4px $motion-transparent;
border: 2px solid $looks-secondary;
box-shadow: 0px 0px 0px 4px $looks-transparent;
}
.option-card + .option-card {
@@ -99,8 +99,8 @@
}
.button-row button.ok-button {
background: $motion-primary;
border: $motion-primary;
background: $looks-secondary;
border: $looks-secondary;
color: white;
}
@@ -18,8 +18,8 @@
overflow: hidden; /* Mask the icon animation */
width: 1.75rem;
height: 1.75rem;
box-shadow: 0px 0px 0px 2px $motion-transparent;
background-color: $motion-primary;
box-shadow: 0px 0px 0px 2px $looks-transparent;
background-color: $looks-secondary;
color: $ui-white;
border-radius: 50%;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+2 -2
View File
@@ -35,7 +35,7 @@ $dial-size: 40px;
}
.gauge-path {
fill: $motion-transparent;
stroke: $motion-primary;
fill: $looks-transparent;
stroke: $looks-secondary;
stroke-width: 1px;
}
@@ -1,46 +1,4 @@
@import "../../css/colors.css";
/* Copied from scratch-paint color-picker.css. */
:global(.Popover-body) {
background: white;
border: 1px solid #ddd;
padding: 4px;
border-radius: 4px;
padding: 4px;
box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, .3);
}
:global(.Popover-tipShape) {
fill: white;
stroke: #ddd;
}
.button-row {
display: flex;
flex-direction: row;
justify-content: center;
}
.icon-button {
margin: 0.25rem;
border: none;
background: none;
outline: none;
cursor: pointer;
user-select: none;
}
.icon-button:active > img {
width: 20px;
height: 20px;
transform: scale(1.15);
}
.icon-button > img {
transition: transform 0.1s;
filter: grayscale(100%);
}
.icon-button.active > img {
filter: none;
margin-bottom: 1rem;
}
@@ -1,12 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Popover from 'react-popover';
import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'react-intl';
import Label from '../forms/label.jsx';
import Input from '../forms/input.jsx';
import BufferedInputHOC from '../forms/buffered-input-hoc.jsx';
import ToggleButtons from '../toggle-buttons/toggle-buttons.jsx';
import Dial from './dial.jsx';
import styles from './direction-picker.css';
@@ -62,44 +62,29 @@ const DirectionPicker = props => (
direction={props.direction}
onChange={props.onChangeDirection}
/>
<div className={styles.buttonRow}>
<button
className={classNames(styles.iconButton, {
[styles.active]: props.rotationStyle === RotationStyles.ALL_AROUND
})}
title={props.intl.formatMessage(messages.allAround)}
onClick={props.onClickAllAround}
>
<img
draggable={false}
src={allAroundIcon}
/>
</button>
<button
className={classNames(styles.iconButton, {
[styles.active]: props.rotationStyle === RotationStyles.LEFT_RIGHT
})}
title={props.intl.formatMessage(messages.leftRight)}
onClick={props.onClickLeftRight}
>
<img
draggable={false}
src={leftRightIcon}
/>
</button>
<button
className={classNames(styles.iconButton, {
[styles.active]: props.rotationStyle === RotationStyles.DONT_ROTATE
})}
title={props.intl.formatMessage(messages.dontRotate)}
onClick={props.onClickDontRotate}
>
<img
draggable={false}
src={dontRotateIcon}
/>
</button>
</div>
<ToggleButtons
className={styles.buttonRow}
buttons={[
{
handleClick: props.onClickAllAround,
icon: allAroundIcon,
isSelected: props.rotationStyle === RotationStyles.ALL_AROUND,
title: props.intl.formatMessage(messages.allAround)
},
{
handleClick: props.onClickLeftRight,
icon: leftRightIcon,
isSelected: props.rotationStyle === RotationStyles.LEFT_RIGHT,
title: props.intl.formatMessage(messages.leftRight)
},
{
handleClick: props.onClickDontRotate,
icon: dontRotateIcon,
isSelected: props.rotationStyle === RotationStyles.DONT_ROTATE,
title: props.intl.formatMessage(messages.dontRotate)
}
]}
/>
</div>
}
isOpen={props.popoverOpen}
+1 -1
View File
@@ -29,7 +29,7 @@
margin-top: -50%;
padding: 0.25rem;
border: 2px solid $motion-primary;
border: 2px solid $looks-secondary;
background: $ui-white;
border-radius: 0.5rem;
+1 -1
View File
@@ -38,7 +38,7 @@
}
.filter:focus-within {
box-shadow: 0 0 0 .25rem $motion-transparent;
box-shadow: 0 0 0 .25rem $looks-transparent;
}
/*
+5 -5
View File
@@ -35,18 +35,18 @@
}
.input-form:hover {
border-color: $motion-primary;
border-color: $looks-secondary;
}
[theme="dark"] .input-form:hover {
border-color: $motion-primary-dark;
border-color: $motion-primary;
}
.input-form:focus {
border-color: $motion-primary;
box-shadow: 0 0 0 0.25rem $motion-transparent;
border-color: $looks-secondary;
box-shadow: 0 0 0 0.25rem $looks-transparent;
}
[theme="dark"] .input-form:focus {
border-color: $motion-primary-dark;
border-color: $motion-primary;
}
.input-small {
+2 -2
View File
@@ -11,9 +11,9 @@
}
.green-flag:hover {
background-color: $motion-light-transparent;
background-color: $looks-light-transparent;
}
.green-flag.is-active {
background-color: $motion-transparent;
background-color: $looks-transparent;
}
+3 -3
View File
@@ -130,7 +130,7 @@
.tab.is-selected {
height: 90%;
color: $motion-primary;
color: $looks-secondary;
background-color: $ui-white;
z-index: 4; /* Make sure selected is always above */
}
@@ -245,9 +245,9 @@
left: 0;
right: 0;
z-index: $z-index-extension-button;
background: $motion-primary;
background: $looks-secondary;
border: 1px solid $motion-primary;
border: 1px solid $looks-secondary;
box-sizing: content-box; /* To match scratch-block vertical toolbox borders */
}
+20 -2
View File
@@ -39,6 +39,7 @@ import TWFontsModal from '../../containers/tw-fonts-modal.jsx';
import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants';
import {resolveStageSize} from '../../lib/screen-utils';
import {themeMap} from '../../lib/themes';
import {isRendererSupported, isBrowserSupported} from '../../lib/tw-environment-support-prober';
@@ -81,9 +82,11 @@ const GUIComponent = props => {
backdropLibraryVisible,
backpackHost,
backpackVisible,
blocksId,
blocksTabVisible,
cardsVisible,
canChangeLanguage,
canChangeTheme,
canCreateNew,
canEditTitle,
canManageFiles,
@@ -108,6 +111,7 @@ const GUIComponent = props => {
isShared,
isWindowFullScreen,
isTelemetryEnabled,
isTotallyNormal,
loading,
logo,
renderLogin,
@@ -144,6 +148,7 @@ const GUIComponent = props => {
stageSizeMode,
targetIsStage,
telemetryModalVisible,
theme,
tipsLibraryVisible,
usernameModalVisible,
settingsModalVisible,
@@ -270,6 +275,7 @@ const GUIComponent = props => {
authorThumbnailUrl={authorThumbnailUrl}
authorUsername={authorUsername}
canChangeLanguage={canChangeLanguage}
canChangeTheme={canChangeTheme}
canCreateCopy={canCreateCopy}
canCreateNew={canCreateNew}
canEditTitle={canEditTitle}
@@ -280,6 +286,7 @@ const GUIComponent = props => {
className={styles.menuBarPosition}
enableCommunity={enableCommunity}
isShared={isShared}
isTotallyNormal={isTotallyNormal}
logo={logo}
renderLogin={renderLogin}
showComingSoon={showComingSoon}
@@ -362,14 +369,16 @@ const GUIComponent = props => {
<TabPanel className={tabClassNames.tabPanel}>
<Box className={styles.blocksWrapper}>
<Blocks
key={`${blocksId}/${theme}`}
canUseCloud={canUseCloud}
grow={1}
isVisible={blocksTabVisible}
options={{
media: `${basePath}static/blocks-media/`
media: `${basePath}static/${themeMap[theme].blocksMediaFolder}/`
}}
stageSize={stageSize}
onOpenCustomExtensionModal={onOpenCustomExtensionModal}
theme={theme}
vm={vm}
/>
</Box>
@@ -439,7 +448,9 @@ GUIComponent.propTypes = {
backpackVisible: PropTypes.bool,
basePath: PropTypes.string,
blocksTabVisible: PropTypes.bool,
blocksId: PropTypes.string,
canChangeLanguage: PropTypes.bool,
canChangeTheme: PropTypes.bool,
canCreateCopy: PropTypes.bool,
canCreateNew: PropTypes.bool,
canEditTitle: PropTypes.bool,
@@ -466,6 +477,7 @@ GUIComponent.propTypes = {
isRtl: PropTypes.bool,
isShared: PropTypes.bool,
isWindowFullScreen: PropTypes.bool,
isTotallyNormal: PropTypes.bool,
loading: PropTypes.bool,
logo: PropTypes.string,
onActivateCostumesTab: PropTypes.func,
@@ -501,6 +513,7 @@ GUIComponent.propTypes = {
stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)),
targetIsStage: PropTypes.bool,
telemetryModalVisible: PropTypes.bool,
theme: PropTypes.string,
tipsLibraryVisible: PropTypes.bool,
usernameModalVisible: PropTypes.bool,
settingsModalVisible: PropTypes.bool,
@@ -512,7 +525,9 @@ GUIComponent.defaultProps = {
backpackHost: null,
backpackVisible: false,
basePath: './',
blocksId: 'original',
canChangeLanguage: true,
canChangeTheme: true,
canCreateNew: false,
canEditTitle: false,
canManageFiles: true,
@@ -524,6 +539,7 @@ GUIComponent.defaultProps = {
enableCommunity: false,
isCreating: false,
isShared: false,
isTotallyNormal: false,
loading: false,
showComingSoon: false,
stageSizeMode: STAGE_SIZE_MODES.large
@@ -533,7 +549,9 @@ const mapStateToProps = state => ({
customStageSize: state.scratchGui.customStageSize,
isWindowFullScreen: state.scratchGui.tw.isWindowFullScreen,
// This is the button's mode, as opposed to the actual current state
stageSizeMode: state.scratchGui.stageSize.stageSize
blocksId: state.scratchGui.timeTravel.year.toString(),
stageSizeMode: state.scratchGui.stageSize.stageSize,
theme: state.scratchGui.theme.theme
});
export default injectIntl(connect(
+2 -2
View File
@@ -7,7 +7,7 @@
cursor: pointer;
font-size: 0.75rem;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: $motion-primary;
color: $looks-secondary;
border-radius: 0.5rem;
}
@@ -26,5 +26,5 @@
}
.container:active {
background-color: $motion-light-transparent;
background-color: $looks-light-transparent;
}
@@ -4,13 +4,13 @@
/* Position the language select over the language icon, and make it transparent */
.language-select {
position: absolute;
width: $language-selector-width;
width: 100%;
height: $menu-bar-height;
opacity: 0;
user-select: none;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: .875rem;
color: $text-primary-light;
color: $text-primary;
background: $ui-white;
}
[theme="dark"] .language-select {
+1 -1
View File
@@ -32,7 +32,7 @@
.library-item:hover {
border-width: 2px;
border-color: $motion-primary;
border-color: $looks-secondary;
}
.hidden {
+1 -1
View File
@@ -25,7 +25,7 @@
justify-content: flex-start;
align-items: center;
height: calc($library-filter-bar-height + 2rem); /* padding */
background-color: $motion-transparent;
background-color: $looks-transparent;
padding: 0 1rem;
font-size: .875rem;
}
+1 -1
View File
@@ -17,7 +17,7 @@
color: white;
}
[theme="dark"] .background {
background-color: $motion-primary-dark;
background-color: $motion-primary;
}
.fullscreen {
+1
View File
@@ -0,0 +1 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="238.5" height="108.5" viewBox="0,0,238.5,108.5"><g transform="translate(-120.75,-125.75)"><g data-paper-data="{&quot;isPaintingLayer&quot;:true}" fill-rule="nonzero" stroke="none" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" style="mix-blend-mode: normal"><image x="241.5" y="251.5" transform="scale(0.5,0.5)" width="477" height="217" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAd0AAADZCAYAAABl2+rMAAAAAXNSR0IArs4c6QAAHZJJREFUeF7t3Xl8VOW9x/GE7IkJJIFAIEAIFBe0qLhc0LrvivdqARUXXEq1bm2v2+vVl1ev3upttS5tX9XWXXFBautLEdypO64IKioYEUkkLIEsZA8h99Xf7xx7M2Qyz8w8M3POzOf88zPwzHPOeT8nfH3mbOnT797Rm+bD5fbxp8lWb/v2GanDiwf12YuGtgz5OT1nlNSiSm1/8bIbfLi3bLJtgUdOel263PL576U21yyWOmKIHkfuL0VHT7r83JFWIXXUwQ9JPfvZqbY3if4QQCAFBNL9Frq3jDxMhqWr/l2p40ZouBbm6z+Ogcumxh3yR/VthVKzh/5I6sgD9R/b05/QUGZJDYG/TNVx3/DBNVLLhuZJLS/qkjpoUP/HUc2mHj2OWtRp9CGPST3/lVNTA469RAABKwKELqFr5UDySyeErl9Giu1EIDkFfBO69x76oIzApvcvljphhH4NmJfT/8wkcLi6t+sXhjUNBVLbe0ukjpx6l9Q5i45IzhFO8b2aP3u9CGz44Eqpbeufkzq2VGeuwb4hCca2vlH/prFnotSq4/8hddYjg1Ncmt1HAAETAUKX0DU5TnzbhtD17dCx4QgkpYDnQ3fef3wg8NXP6rnYcc4Mt6So74VT4Y5OXYOe692wVevIf/uj1Llvzg23K9p7UODhE3UGuv6dn0rN2rFRqjvDzck2+4YkcNfcyw5X1eXIXxWM0+Pl0hW/8aACm4QAAl4TIHQJXa8dk1a2h9C1wkgnCCBgWcCzobvgnCbZ1bUvHik1f/vnUscMi26GG+jX0KIz3TXrtZZN1nN/l3z8a8vUdBcPgfsOf1RW893bOgMt0VP4aWPK9LhJT49shhu47Y3OcfO1c9yMO3ahNDn3+aPjsZusAwEEfCpA6BK6Pj10+99sQjephpOdQSDpBDwbun/a51rB3lZ9t9SJIzqkZmbYmakEjmRLu17dvKpGr2odMn6m1F9+NS/pBj0Zd+ieg/4ku/Xd0iukjizVmW25U2O1z2s36TckXXlTpF753ZuxWhX9IoBAEggQus4gErr+PpoJXX+PH1uPQKoIeC50H5+5SuyrF+4ntaJYZ55DB9s9lxtsgDu6dMa7cq1zH2eF3r975Tp9TCCLtwTu3v9m2aANH90otdK5ur00yqvbTfeyrUOPly/W6fEy5jB9TOQFS0437YJ2CCCQQgKEbsBgE7r+OvoJXX+NF1uLQKoLeC50/7jXf8qYtK29X+ruo3QGEe+lx7khc3m1M+MddbhswpU1z8d7U1hfPwJ/PuAW+dO6D6+TOmGUfhMyuCA+34gEbtK3G/U46ciaIPWqTZ8ybggggMBOAoRukIOC0PX2bwuh6+3xYesQQKB/Ac+E7qOnfCxbWL1QX5nm3o8b8lxu/pm6Zz01WjvfsDrWvb16zm7ZVzqTKZ6grwj8xeqHra6HzswE7jtUz5nWvHmR1JjNcMM8rgLP7VYcrM/0nvvG+WY7RisEEEgJAUI3xDATut76PSB0vTUebA0CCIQn4JnQ/f3uF8qWd9TOlxryXG6RXrWaljur7x43/1J/7tAnBNlaupy3FH26Rme8w/a6XOqlK/TcIktsBR46Ts+lf/PSKVJjdpXyYL3fNy3n2L471OTMWEN8k+Ke221LHy2fv2br6tjC0DsCCPhKgNA1HC5C1xAqRs0I3RjB0i0CCMRVIOGhu2BOs+zwqgVjpZYP7pRaNiTIVag5hyjQ4AcGhmqcoX/ftdwqaKtzX+aXzn2Z5fvfJP1f9J4+CYnFrsC8k5dKh9XP6dXjo4bqcTGixPJVygU/0w0vCDKOvXqcptXr/eNpvfokqsAl8PioPGqBNDnvpZPtwtAbAgj4UoDQDXPYCN0wwaJsTuhGCcjHEUDAUwIJD90HjnpKQL5dcpbUvcZlSM3OCnjGcrozsyl5VQEz9JxZ0MW9mnmrvqUo2Mwk0tFocl6UUO28ZabiR/qM6Lmvnxdpl3zu/wk8NkPfKrX67/tKLSvW8R9t+S1TaZnjdK0lL5v5d+jMNa35VwO2X12r5/6zKvTJVD//8kGz/mmFAAJJLUDoRji8hG6EcIYfI3QNoWiGAAK+Ekh46P5h958I2I4Nj0utGh7kXF2oc27B2Ftv079p1Zmo7aW+Sc/tfbtR65jDdEZzwZIzbK8qJfqbf0at7OcXT+qTnYoL9RuPqnL9BsT6UvKsdpm5R3hdbzlQ2/ds6fdzGxv0eNjYki/12tb68PqnNQIIJKUAoRvlsBK6UQIGfJzQtetJbwgg4C2BhIfuzYXlIjKysElqSbC3w5S+pnIZFeEJuledbj16wJlJeJ3u3Lpui85s1jt17JFPyM/nv6z3lbIMLLBgzjZp8MUTZVILsrT9xNExmuHmTtcVFN0R2dCEOLfb2a1PMvvsGz23W3m0Xrtw3osnRbY+PoUAAkkhQOhaGkZCNzpIQjc6Pz6NAAL+EEhY6LrPWv7qGX3W8t4TdEaTMSjgquVoZyTuOBhedRrtsK3bpDObzY060xl3zNNSz33h+Gi7TurP31So32AM6tZzn5MqM2O7v5F+c+JuleF9u6u/c65iHqlPTvv5Kp7ZHduBpXcEvC1A6FoeH0I3MlBCNzI3PoUAAv4SSFjo3r3//4pU51c3Sq0aEeTcXbBn4UbqHOKq00i7DfzcmvU6w2lo0Rlv1fH67OA5i/TJSiwq8NuyvfU42Pql1L0nxHiGa+ubE3cAQzzre3Ojc66/KVs+8V/tjQw9AgiksAChG6PBJ3TNYAldMydaIYBAcggkLHRvGb6/CA7PXCm1uDDg/lz3CVTDLL+lpe0+HbmW38RlBFfV6Iy3vVtXV3mcPlHrnGenxWX9Xl3J7yr0SWHb1r8tdcrEGM9wXYhoz+UGgna+qH/SdEm/1Nt79JuOFV/rcVB13CKpcxY7T0rz6gCxXQggEBMBQjcmrP/qlNDtH5jQJXRj/KtH9wh4UiBhoXtjjj6pZ/cKvVo5J/BZy7bPvbn8hled2h6tz77ZLl32ZpdKHXuUvu/3rKf12cLuMn/2evnPrm1rtDZ/7VQ959nVpN8MdLes05/b6pyq9znbXjJz8qTL7AK9nzo7f4TWol2lZg3+of5cqM8w/r7uUik/z3gwp88m3VE1U35uXKv7P3m8nsvPzAi4at32jpi+nSrS9W6eqJ8M8vahT9bqud2y/W6X+tO3Lop0TXwOAQR8LEDoBvlH0vaYEroqSugSurZ/t+gPAT8JxD10n5ilM7cv/zZJ6pQfBDmXV3SzOubq/Y3Wl5brtcu2x6x3PVCHH1frjDe7QGeMO3rapHa16ftaM50Zf5bDkuWc6s5yLu7OytQZoftzptsuRjPFnh16TrJbNzutW09NpnVvD/hz5/Wy7t9vd57IlJmtj5bKytf7cNsavpG6R6XuUF52jGe47mAUz9P/ytL7wq0vTedrl51v9Nv1audtVDmV+t7eyz5xnglufUPoEAEEvCxA6BK6Ax6fhK7hry+hawhFMwRSWyDuofvA0fqEpo1vz5Y6aUyQ+3NtX2UaOM7dS/VPGs5OyBHQ0q4zxe9ntA7DoMAnciVk66JfaW9v/zNk92rewQVB3iYV/ar79hCrq+ADtzPEVfHrNupXBJ1FR0i94lu9ipkFAQRSS4DQJXRjcsQTun1ZCd2YHGZ0ioDvBOIeundNuUH/j79a75MdH/ie1HjNTNyh2qTvbWVJUoFsfeJV2hB9y0/MlhDfnLjv193cqefyf9WoV6ezIIBAagkQuoRuch/xhG5yjy97h4DPBOIeurdX6ntMc7e9IrViWMA53Xj9I+ner7u5732yPhs/NjeUQKbeP5xW8nKoltH9fYjjqalVL++udq5ivqGnI7r18WkEEPClAKFL6PrywDXeaELXmIqGCCAQe4G4h+5NRWNkr8oL9L2pQwcHXMUaqydRBVom6D7d2A8pa+hXwPbbqoIxBzld0dGlV3OvXKtXMe/64+VSZ/91NwYMAQRSSIDQjfN9uil0bHlrVwldb40HW4NAigrEPXSvH5Qr1BMr9FxuYX7AE4kK9Ik9aQVX2BkS91xb5wvaX8cCrV0602BJMQH3m5Q850lntp9Q1ThjwOPro9X6aC/eNpRixx27i4AjQOhyKKSWAKGbWuPN3iLgMYHkC133fsl2Z0bb6Tz5J04vNvDY+LI5oQQy9K1P3z/jO/c0/TlDnxUd9sJMN2wyPoBAKgkQuqk02uzrzgKELkcFAgjEUcC/oeueq3VntO33KlvPljjysaqkFXDvF8+bq7uYc6zZrjLTNXOiFQIpKkDopujAs9shBAhdDhEEEIiBgH9Ct6dWd7/VeQ8p52pjcDjQZVAB92tod+brXv2cXtT3I8x0OYgQQGAAAUKXwwMBEwFC10SJNgggEELAe6Gbc4hucr5zLs09Z9uxLine truncated

After

Width:  |  Height:  |  Size: 11 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="15" height="12" viewBox="0 0 15 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.1715 11.403C4.74533 11.403 4.31916 11.2398 3.99453 10.9152L0.798242 7.71892C0.147331 7.06801 0.147331 6.01589 0.798242 5.36498C1.44915 4.71407 2.50127 4.71407 3.15218 5.36498L5.1715 7.38431L11.4692 1.08828C12.1184 0.43737 13.1722 0.43737 13.8231 1.08828C14.4724 1.73753 14.4724 2.7913 13.8231 3.44222L6.34847 10.9152C6.02384 11.2398 5.59767 11.403 5.1715 11.403Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 534 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.64469 14.507C9.55861 14.5931 9.46145 14.6484 9.34525 14.7008L7.37873 15.5266C7.30728 15.283 7.13034 14.9734 6.80329 14.6463C6.48721 14.3302 6.18853 14.1643 5.95378 14.0819L6.76975 12.1253C6.81217 11.9991 6.88828 11.9031 6.97347 11.8179C6.9933 11.818 7.01213 11.7992 7.022 11.7893L7.04979 11.7615C7.18251 11.6288 7.36896 11.548 7.58743 11.519C8.14278 11.4502 8.78656 11.7138 9.27215 12.1994C9.75773 12.685 10.0213 13.3288 9.94158 13.8731C9.90955 14.0527 9.85041 14.2375 9.75757 14.3742C9.72988 14.4219 9.69222 14.4595 9.65366 14.4981L9.64469 14.507ZM12.8686 5.94274C13.1752 5.63606 13.6458 5.57036 14.1274 5.71486C14.4066 5.79859 14.5232 6.1447 14.3322 6.3357L9.75179 10.9161C9.64239 11.0255 9.46488 11.0435 9.32122 10.9518C9.17277 10.859 9.01934 10.7791 8.86473 10.7123C8.61252 10.6036 8.52489 10.2864 8.70154 10.1098L12.8686 5.94274ZM15.5288 8.60299L11.3627 12.7691C11.1896 12.9422 10.8712 12.8677 10.7657 12.6223C10.6976 12.4591 10.6127 12.3007 10.5148 12.1472C10.4181 11.9967 10.4422 11.8091 10.556 11.6952L15.1257 7.1256C15.3149 6.93639 15.6609 7.05104 15.7465 7.33036C15.896 7.81701 15.8373 8.29452 15.5288 8.60299ZM17.3497 4.12182C16.1781 2.95023 14.4881 2.73979 13.5762 3.65174L6.22044 11.0075C6.0402 11.1877 5.8809 11.3889 5.78441 11.6349L4.23312 15.3599C4.03804 15.8302 4.18906 16.417 4.6218 16.8497C5.05454 17.2825 5.64137 17.4335 6.11165 17.2384L9.83661 15.6871C10.0817 15.5896 10.2828 15.4303 10.4631 15.2501L17.8188 7.89438C18.7308 6.98243 18.5213 5.29341 17.3497 4.12182Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.98116 1H15.0196C16.4042 1 17.6158 2.21154 17.6158 3.59615V10.8654H13.635C11.385 10.8654 9.65424 12.7692 9.65424 14.8462V18.8269H4.98116C3.59655 18.8269 2.38501 17.6154 2.38501 16.2308V3.59615C2.38501 2.21154 3.59655 1 4.98116 1ZM6.01963 9.82692H8.78886C9.48116 9.82692 10.0004 9.48077 10.0004 8.78846C10.0004 8.09615 9.48116 7.75 8.96193 7.75H6.01963C5.50039 7.75 4.98116 8.26923 4.98116 8.78846C4.98116 9.30769 5.50039 9.82692 6.01963 9.82692ZM14.1542 5.84615H6.01963C5.32732 5.84615 4.98116 5.32692 4.98116 4.80769C4.98116 4.28846 5.50039 3.76923 6.01963 3.76923H14.1542C14.6735 3.76923 15.1927 4.28846 15.1927 4.80769C15.0196 5.32692 14.6735 5.84615 14.1542 5.84615ZM17.4427 12.25H13.4619C12.0773 12.25 10.8658 13.4615 10.8658 15.0192V19L17.4427 12.25Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 927 B

@@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.27206 2.68377C8.40818 2.27543 8.79032 2 9.22075 2H10.7792C11.2097 2 11.5918 2.27543 11.7279 2.68377L12.3219 4.4658C12.7914 4.663 13.2304 4.91818 13.6302 5.22238L15.4721 4.84547C15.8938 4.75917 16.3234 4.9524 16.5386 5.32516L17.3178 6.67485C17.533 7.04761 17.4856 7.51627 17.2 7.83832L15.9529 9.24468C15.984 9.49208 16 9.74417 16 10C16 10.2558 15.984 10.5079 15.9529 10.7553L17.2 12.1617C17.4856 12.4837 17.533 12.9524 17.3178 13.3252L16.5386 14.6748C16.3234 15.0476 15.8938 15.2408 15.4721 15.1545L13.6302 14.7776C13.2304 15.0818 12.7914 15.337 12.3219 15.5342L11.7279 17.3162C11.5918 17.7246 11.2097 18 10.7792 18H9.22075C8.79032 18 8.40818 17.7246 8.27206 17.3162L7.67805 15.5342C7.20862 15.337 6.76955 15.0818 6.36982 14.7776L4.52792 15.1545C4.10623 15.2408 3.67663 15.0476 3.46141 14.6748L2.68217 13.3252C2.46696 12.9524 2.51442 12.4837 2.8 12.1617L4.04708 10.7553C4.016 10.5079 3.99999 10.2558 3.99999 10C3.99999 9.74416 4.016 9.49207 4.04707 9.24466L2.8 7.83832C2.51442 7.51627 2.46696 7.04761 2.68217 6.67485L3.46141 5.32516C3.67663 4.9524 4.10623 4.75917 4.52792 4.84547L6.3698 5.22238C6.76954 4.91818 7.20861 4.663 7.67805 4.4658L8.27206 2.68377ZM9.99999 8C8.89542 8 7.99999 8.89543 7.99999 10C7.99999 11.1046 8.89542 12 9.99999 12C11.1046 12 12 11.1046 12 10C12 8.89543 11.1046 8 9.99999 8Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+130
View File
@@ -0,0 +1,130 @@
import classNames from 'classnames';
import bindAll from 'lodash.bindall';
import PropTypes from 'prop-types';
import React from 'react';
import {FormattedMessage} from 'react-intl';
import {connect} from 'react-redux';
import locales from 'scratch-l10n';
import check from './check.svg';
import {MenuItem, Submenu} from '../menu/menu.jsx';
import languageIcon from '../language-selector/language-icon.svg';
import {languageMenuOpen, openLanguageMenu} from '../../reducers/menus.js';
import {selectLocale} from '../../reducers/locales.js';
import styles from './settings-menu.css';
import dropdownCaret from './dropdown-caret.svg';
class LanguageMenu extends React.PureComponent {
constructor (props) {
super(props);
bindAll(this, [
'setRef',
'handleMouseOver'
]);
}
componentDidUpdate (prevProps) {
// If the submenu has been toggled open, try scrolling the selected option into view.
if (!prevProps.menuOpen && this.props.menuOpen && this.selectedRef) {
this.selectedRef.scrollIntoView({block: 'center'});
}
}
setRef (component) {
this.selectedRef = component;
}
handleMouseOver () {
// If we are using hover rather than clicks for submenus, scroll the selected option into view
if (!this.props.menuOpen && this.selectedRef) {
this.selectedRef.scrollIntoView({block: 'center'});
}
}
render () {
return (
<MenuItem
expanded={this.props.menuOpen}
>
<div
className={styles.option}
onClick={this.props.onRequestOpen}
onMouseOver={this.handleMouseOver}
>
<img
className={styles.icon}
src={languageIcon}
/>
<span className={styles.submenuLabel}>
<FormattedMessage
defaultMessage="Language"
description="Language sub-menu"
id="gui.menuBar.language"
/>
</span>
<img
className={styles.expandCaret}
src={dropdownCaret}
/>
</div>
<Submenu
className={styles.languageSubmenu}
place={this.props.isRtl ? 'left' : 'right'}
>
{
Object.keys(locales)
.map(locale => (
<MenuItem
key={locale}
className={styles.languageMenuItem}
// eslint-disable-next-line react/jsx-no-bind
onClick={() => this.props.onChangeLanguage(locale)}
>
<img
className={classNames(styles.check, {
[styles.selected]: this.props.currentLocale === locale
})}
src={check}
{...(this.props.currentLocale === locale && {ref: this.setRef})}
/>
{locales[locale].name}
</MenuItem>
))
}
</Submenu>
</MenuItem>
);
}
}
LanguageMenu.propTypes = {
currentLocale: PropTypes.string,
isRtl: PropTypes.bool,
label: PropTypes.string,
menuOpen: PropTypes.bool,
onChangeLanguage: PropTypes.func,
onRequestCloseSettings: PropTypes.func,
onRequestOpen: PropTypes.func
};
const mapStateToProps = state => ({
currentLocale: state.locales.locale,
isRtl: state.locales.isRtl,
menuOpen: languageMenuOpen(state),
messagesByLocale: state.locales.messagesByLocale
});
const mapDispatchToProps = (dispatch, ownProps) => ({
onChangeLanguage: locale => {
dispatch(selectLocale(locale));
ownProps.onRequestCloseSettings();
},
onRequestOpen: () => dispatch(openLanguageMenu())
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(LanguageMenu);
+30 -29
View File
@@ -27,11 +27,11 @@
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: $menu-bar-standard-font-size;
font-weight: bold;
background-color: $motion-primary;
background-color: $looks-secondary;
color: $ui-white;
}
[theme="dark"] .menu-bar {
background-color: $motion-primary-dark;
background-color: $motion-primary;
}
.main-menu {
@@ -42,7 +42,9 @@
align-items: center;
flex-grow: 1;
}
.inactive {
opacity: 0;
}
.scratch-logo {
height: 1.6rem;
vertical-align: middle;
@@ -52,21 +54,6 @@
cursor: pointer;
}
.language-icon {
height: 1.5rem;
vertical-align: middle;
}
.language-caret {
margin: 0 .125rem;
}
.language-menu {
box-sizing: border-box;
display: inline-flex;
width: $language-selector-width;
}
.menu-bar-item {
display: flex;
padding: 0 0.25rem;
@@ -106,24 +93,21 @@
}
.file-group .menu-bar-item {
margin: 0 .25rem;
padding: 0 0.75rem;
}
.menu-bar-item.language-menu {
padding: 0 0.5rem;
}
.menu-bar-menu {
margin-top: $menu-bar-height;
z-index: $z-index-menu-bar;
}
.feedback-link {
color: $motion-primary !important;
color: $looks-secondary;
text-decoration: none;
}
[theme="dark"] .feedback-link {
color: $motion-primary-dark !important;
color: $motion-primary !important;
}
.feedback-button {
@@ -164,6 +148,7 @@
}
.account-info-group .menu-bar-item {
margin: 0 .25rem;
padding: 0 0.75rem;
}
@@ -173,7 +158,11 @@
}
.help-icon {
margin-right: 0.35rem;
margin: 0 .25rem 0 0;
}
[dir="rtl"] .help-icon {
margin: 0 0 0 .25rem;
}
.account-nav-menu, .mystuff-button {
@@ -231,8 +220,20 @@
vertical-align: middle;
}
/* tw: styles to override <a> styles */
.menu-item-link {
color: $ui-white !important;
text-decoration: none;
.collapsible-label {
margin: 0 .5rem 0 .25rem;
}
[dir="rtl"] .collapsible-label {
margin: 0 0.25rem 0 0.5rem;
}
@media only screen and (max-width: 1024px) {
.tutorials-label, .collapsible-label {
display: none;
}
.help-icon {
margin-right: 0;
}
}
+175 -177
View File
@@ -15,7 +15,7 @@ import CommunityButton from './community-button.jsx';
import ShareButton from './share-button.jsx';
import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
import Divider from '../divider/divider.jsx';
import LanguageSelector from '../../containers/language-selector.jsx';
import SaveStatus from './save-status.jsx';
import ProjectWatcher from '../../containers/project-watcher.jsx';
import MenuBarMenu from './menu-bar-menu.jsx';
import {MenuItem, MenuSection} from '../menu/menu.jsx';
@@ -25,6 +25,7 @@ import SB3Downloader from '../../containers/sb3-downloader.jsx';
import DeletionRestorer from '../../containers/deletion-restorer.jsx';
import TurboMode from '../../containers/turbo-mode.jsx';
import MenuBarHOC from '../../containers/menu-bar-hoc.jsx';
import SettingsMenu from './settings-menu.jsx';
import FramerateChanger from '../../containers/tw-framerate-changer.jsx';
import ChangeUsername from '../../containers/tw-change-username.jsx';
@@ -33,6 +34,14 @@ import TWSaveStatus from './tw-save-status.jsx';
import {openTipsLibrary, openSettingsModal, openRestorePointModal} from '../../reducers/modals';
import {setPlayer} from '../../reducers/mode';
import {
isTimeTravel220022BC,
isTimeTravel1920,
isTimeTravel1990,
isTimeTravel2020,
isTimeTravelNow,
setTimeTravel
} from '../../reducers/time-travel';
import {
autoUpdateProject,
getIsUpdating,
@@ -55,15 +64,15 @@ import {
openEditMenu,
closeEditMenu,
editMenuOpen,
openErrorsMenu,
closeErrorsMenu,
errorsMenuOpen,
openLanguageMenu,
closeLanguageMenu,
languageMenuOpen,
openLoginMenu,
closeLoginMenu,
loginMenuOpen
loginMenuOpen,
openModeMenu,
closeModeMenu,
modeMenuOpen,
settingsMenuOpen,
openSettingsMenu,
closeSettingsMenu
} from '../../reducers/menus';
import {setFileHandle} from '../../reducers/tw.js';
@@ -76,12 +85,15 @@ import mystuffIcon from './icon--mystuff.png';
import profileIcon from './icon--profile.png';
import remixIcon from './icon--remix.svg';
import dropdownCaret from './dropdown-caret.svg';
import languageIcon from '../language-selector/language-icon.svg';
import aboutIcon from './icon--about.svg';
import errorIcon from './tw-error.svg';
import themeIcon from './tw-moon.svg';
import fileIcon from './icon--file.svg';
import editIcon from './icon--edit.svg';
import scratchLogo from './scratch-logo.svg';
import ninetiesLogo from './nineties_logo.svg';
import catLogo from './cat_logo.svg';
import prehistoricLogo from './prehistoric-logo.svg';
import oldtimeyLogo from './oldtimey-logo.svg';
import sharedMessages from '../../lib/shared-messages';
@@ -89,11 +101,6 @@ import SeeInsideButton from './tw-see-inside.jsx';
import { notScratchDesktop } from '../../lib/isScratchDesktop.js';
const ariaMessages = defineMessages({
language: {
id: 'gui.menuBar.LanguageSelector',
defaultMessage: 'language selector',
description: 'accessibility text for the language selection menu'
},
tutorials: {
id: 'gui.menuBar.tutorialsLibrary',
defaultMessage: 'Tutorials',
@@ -209,8 +216,8 @@ class MenuBar extends React.Component {
'handleClickRestorePoints',
'handleClickSeeCommunity',
'handleClickShare',
'handleSetMode',
'handleKeyPress',
'handleLanguageMouseUp',
'handleRestoreOption',
'getSaveToComputerHandler',
'restoreOptionMessage'
@@ -282,6 +289,36 @@ class MenuBar extends React.Component {
}
}
}
handleSetMode (mode) {
return () => {
// Turn on/off filters for modes.
if (mode === '1920') {
document.documentElement.style.filter = 'brightness(.9)contrast(.8)sepia(1.0)';
document.documentElement.style.height = '100%';
} else if (mode === '1990') {
document.documentElement.style.filter = 'hue-rotate(40deg)';
document.documentElement.style.height = '100%';
} else {
document.documentElement.style.filter = '';
document.documentElement.style.height = '';
}
// Change logo for modes
if (mode === '1990') {
document.getElementById('logo_img').src = ninetiesLogo;
} else if (mode === '2020') {
document.getElementById('logo_img').src = catLogo;
} else if (mode === '1920') {
document.getElementById('logo_img').src = oldtimeyLogo;
} else if (mode === '220022BC') {
document.getElementById('logo_img').src = prehistoricLogo;
} else {
document.getElementById('logo_img').src = this.props.logo;
}
this.props.onSetTimeTravelMode(mode);
};
}
handleRestoreOption (restoreFun) {
return () => {
restoreFun();
@@ -305,11 +342,6 @@ class MenuBar extends React.Component {
}
};
}
handleLanguageMouseUp (e) {
if (!this.props.languageMenuOpen) {
this.props.onClickLanguage(e);
}
}
restoreOptionMessage (deletedItem) {
switch (deletedItem) {
case 'Sprite':
@@ -446,105 +478,26 @@ class MenuBar extends React.Component {
>
<div className={styles.mainMenu}>
<div className={styles.fileGroup}>
{this.props.onClickLogo ? (
<div className={classNames(styles.menuBarItem)}>
<img
alt="Scratch"
className={classNames(styles.scratchLogo, {
[styles.clickable]: typeof this.props.onClickLogo !== 'undefined'
})}
draggable={false}
src={this.props.logo}
onClick={this.props.onClickLogo}
/>
</div>
) : null}
{(this.props.canChangeLanguage) && (<div
className={classNames(styles.menuBarItem, styles.hoverable, styles.languageMenu)}
>
<div>
<img
className={styles.languageIcon}
src={languageIcon}
width="24"
height="24"
/>
<img
className={styles.languageCaret}
src={dropdownCaret}
width="8"
height="5"
/>
</div>
<LanguageSelector label={this.props.intl.formatMessage(ariaMessages.language)} />
</div>)}
{/* tw: theme toggler */}
{this.props.onClickTheme && (
<div
className={classNames(styles.menuBarItem, styles.hoverable)}
onMouseUp={this.props.onClickTheme}
>
<img
src={themeIcon}
width="24"
height="24"
draggable={false}
/>
</div>
)}
{/* tw: display compile errors */}
{this.props.compileErrors.length > 0 && <div>
<div
className={classNames(styles.menuBarItem, styles.hoverable, {
[styles.active]: this.props.errorsMenuOpen
<div className={classNames(styles.menuBarItem)}>
<img
id="logo_img"
alt="Scratch"
className={classNames(styles.scratchLogo, {
[styles.clickable]: typeof this.props.onClickLogo !== 'undefined'
})}
onMouseUp={this.props.onClickErrors}
>
<div className={classNames(styles.errorsMenu)}>
<img
className={styles.languageIcon}
src={errorIcon}
/>
<img
className={styles.languageCaret}
src={dropdownCaret}
/>
</div>
<MenuBarMenu
className={classNames(styles.menuBarMenu)}
open={this.props.errorsMenuOpen}
place={this.props.isRtl ? 'left' : 'right'}
onRequestClose={this.props.onRequestCloseErrors}
>
<MenuSection>
<MenuItemLink href="https://scratch.mit.edu/users/GarboMuffin/#comments">
<FormattedMessage
defaultMessage="Some scripts could not be compiled."
description="Link in error menu"
id="tw.menuBar.reportError1"
/>
</MenuItemLink>
<MenuItemLink href="https://scratch.mit.edu/users/GarboMuffin/#comments">
<FormattedMessage
defaultMessage="This is a bug. Please report it."
description="Link in error menu"
id="tw.menuBar.reportError2"
/>
</MenuItemLink>
</MenuSection>
<MenuSection>
{this.props.compileErrors.map(({id, sprite, error}) => (
<MenuItem key={id}>
{this.props.intl.formatMessage(twMessages.compileError, {
sprite,
error
})}
</MenuItem>
))}
</MenuSection>
</MenuBarMenu>
</div>
</div>}
draggable={false}
src={this.props.logo}
onClick={this.props.onClickLogo}
/>
</div>
{(this.props.canChangeTheme || this.props.canChangeLanguage) && (<SettingsMenu
canChangeLanguage={this.props.canChangeLanguage}
canChangeTheme={this.props.canChangeTheme}
isRtl={this.props.isRtl}
onRequestClose={this.props.onRequestCloseSettings}
onRequestOpen={this.props.onClickSettings}
settingsMenuOpen={this.props.settingsMenuOpen}
/>)}
{(this.props.canManageFiles) && (
<div
className={classNames(styles.menuBarItem, styles.hoverable, {
@@ -552,11 +505,15 @@ class MenuBar extends React.Component {
})}
onMouseUp={this.props.onClickFile}
>
<FormattedMessage
defaultMessage="File"
description="Text for file dropdown menu"
id="gui.menuBar.file"
/>
<img src={fileIcon} />
<span className={styles.collapsibleLabel}>
<FormattedMessage
defaultMessage="File"
description="Text for file dropdown menu"
id="gui.menuBar.file"
/>
</span>
<img src={dropdownCaret} />
<MenuBarMenu
className={classNames(styles.menuBarMenu)}
open={this.props.fileMenuOpen}
@@ -691,13 +648,15 @@ class MenuBar extends React.Component {
})}
onMouseUp={this.props.onClickEdit}
>
<div className={classNames(styles.editMenu)}>
<img src={editIcon} />
<span className={styles.collapsibleLabel}>
<FormattedMessage
defaultMessage="Edit"
description="Text for edit dropdown menu"
id="gui.menuBar.edit"
/>
</div>
</span>
<img src={dropdownCaret} />
<MenuBarMenu
className={classNames(styles.menuBarMenu)}
open={this.props.editMenuOpen}
@@ -797,36 +756,56 @@ class MenuBar extends React.Component {
</MenuItem>
</MenuSection>
</MenuBarMenu>
</div>
{this.props.onClickAddonSettings && (
{this.props.isTotallyNormal && (
<div
className={classNames(styles.menuBarItem, styles.hoverable)}
onMouseUp={this.props.onClickAddonSettings}
className={classNames(styles.menuBarItem, styles.hoverable, {
[styles.active]: this.props.modeMenuOpen
})}
onMouseUp={this.props.onClickMode}
>
<div>
<div className={classNames(styles.editMenu)}>
<FormattedMessage
// Note: this string is used by scratch-vm for the addons blocks category
defaultMessage="Addons"
description="Menu bar item for addon settings"
id="tw.menuBar.addons"
defaultMessage="Mode"
description="Mode menu item in the menu bar"
id="gui.menuBar.modeMenu"
/>
</div>
<MenuBarMenu
className={classNames(styles.menuBarMenu)}
open={this.props.modeMenuOpen}
place={this.props.isRtl ? 'left' : 'right'}
onRequestClose={this.props.onRequestCloseMode}
>
<MenuSection>
<MenuItem onClick={this.handleSetMode('NOW')}>
<span className={classNames({[styles.inactive]: !this.props.modeNow})}>
{'✓'}
</span>
{' '}
<FormattedMessage
defaultMessage="Normal mode"
description="April fools: resets editor to not have any pranks"
id="gui.menuBar.normalMode"
/>
</MenuItem>
<MenuItem onClick={this.handleSetMode('2020')}>
<span className={classNames({[styles.inactive]: !this.props.mode2020})}>
{'✓'}
</span>
{' '}
<FormattedMessage
defaultMessage="Caturday mode"
description="April fools: Cat blocks mode"
id="gui.menuBar.caturdayMode"
/>
</MenuItem>
</MenuSection>
</MenuBarMenu>
</div>
)}
<div
className={classNames(styles.menuBarItem, styles.hoverable)}
onMouseUp={this.props.onClickSettings}
>
<div>
<FormattedMessage
defaultMessage="Advanced"
description="Text for advanced settings menu item"
id="tw.menuBar.advanced"
/>
</div>
</div>
</div>
<Divider className={classNames(styles.divider)} />
{this.props.canEditTitle ? (
<div className={classNames(styles.menuBarItem, styles.growable)}>
<MenuBarItemTooltip
@@ -923,6 +902,22 @@ class MenuBar extends React.Component {
</Button>
</a>
</div>
<Divider className={classNames(styles.divider)} />
<div className={styles.fileGroup}>
<div
aria-label={this.props.intl.formatMessage(ariaMessages.tutorials)}
className={classNames(styles.menuBarItem, styles.hoverable)}
onClick={this.props.onOpenTipLibrary}
>
<img
className={styles.helpIcon}
src={helpIcon}
/>
<span className={styles.tutorialsLabel}>
<FormattedMessage {...ariaMessages.tutorials} />
</span>
</div>
</div>
</div>
<div className={styles.accountInfoGroup}>
@@ -947,6 +942,7 @@ MenuBar.propTypes = {
authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
autoUpdateProject: PropTypes.func,
canChangeLanguage: PropTypes.bool,
canChangeTheme: PropTypes.bool,
canCreateCopy: PropTypes.bool,
canCreateNew: PropTypes.bool,
canEditTitle: PropTypes.bool,
@@ -961,6 +957,7 @@ MenuBar.propTypes = {
id: PropTypes.number
})),
confirmReadyToReplaceProject: PropTypes.func,
currentLocale: PropTypes.string.isRequired,
editMenuOpen: PropTypes.bool,
enableCommunity: PropTypes.bool,
fileMenuOpen: PropTypes.bool,
@@ -970,11 +967,17 @@ MenuBar.propTypes = {
isRtl: PropTypes.bool,
isShared: PropTypes.bool,
isShowingProject: PropTypes.bool,
isTotallyNormal: PropTypes.bool,
isUpdating: PropTypes.bool,
languageMenuOpen: PropTypes.bool,
locale: PropTypes.string.isRequired,
loginMenuOpen: PropTypes.bool,
logo: PropTypes.string,
mode1920: PropTypes.bool,
mode1990: PropTypes.bool,
mode2020: PropTypes.bool,
mode220022BC: PropTypes.bool,
modeMenuOpen: PropTypes.bool,
modeNow: PropTypes.bool,
onClickAbout: PropTypes.oneOfType([
PropTypes.func, // button mode: call this callback when the About button is clicked
PropTypes.arrayOf( // menu mode: list of items in the About menu
@@ -991,29 +994,29 @@ MenuBar.propTypes = {
onClickRestorePoints: PropTypes.func,
onClickEdit: PropTypes.func,
onClickFile: PropTypes.func,
onClickLanguage: PropTypes.func,
onClickLogin: PropTypes.func,
onClickLogo: PropTypes.func,
onClickMode: PropTypes.func,
onClickNew: PropTypes.func,
onClickNewWindow: PropTypes.func,
onClickRemix: PropTypes.func,
onClickSave: PropTypes.func,
onClickSaveAsCopy: PropTypes.func,
onClickSettings: PropTypes.func,
onClickErrors: PropTypes.func,
onRequestCloseErrors: PropTypes.func,
onLogOut: PropTypes.func,
onOpenRegistration: PropTypes.func,
onOpenTipLibrary: PropTypes.func,
onProjectTelemetryEvent: PropTypes.func,
onRequestOpenAbout: PropTypes.func,
onRequestCloseAbout: PropTypes.func,
onRequestCloseAccount: PropTypes.func,
onRequestCloseEdit: PropTypes.func,
onRequestCloseFile: PropTypes.func,
onRequestCloseLanguage: PropTypes.func,
onRequestCloseLogin: PropTypes.func,
onRequestCloseMode: PropTypes.func,
onRequestCloseSettings: PropTypes.func,
onRequestOpenAbout: PropTypes.func,
onSeeCommunity: PropTypes.func,
onSetTimeTravelMode: PropTypes.func,
onShare: PropTypes.func,
onStartSelectingFileUpload: PropTypes.func,
onToggleLoginOpen: PropTypes.func,
@@ -1021,11 +1024,11 @@ MenuBar.propTypes = {
projectTitle: PropTypes.string,
renderLogin: PropTypes.func,
sessionExists: PropTypes.bool,
errorsMenuOpen: PropTypes.bool,
settingsMenuOpen: PropTypes.bool,
shouldSaveBeforeTransition: PropTypes.func,
showComingSoon: PropTypes.bool,
userOwnsProject: PropTypes.bool,
username: PropTypes.string,
userOwnsProject: PropTypes.bool,
vm: PropTypes.instanceOf(VM).isRequired
};
@@ -1040,26 +1043,28 @@ const mapStateToProps = (state, ownProps) => {
return {
aboutMenuOpen: aboutMenuOpen(state),
accountMenuOpen: accountMenuOpen(state),
authorThumbnailUrl: state.scratchGui.tw.author.thumbnail,
authorUsername: state.scratchGui.tw.author.username,
compileErrors: state.scratchGui.tw.compileErrors,
currentLocale: state.locales.locale,
fileMenuOpen: fileMenuOpen(state),
editMenuOpen: editMenuOpen(state),
isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
isRtl: state.locales.isRtl,
isUpdating: getIsUpdating(loadingState),
isShowingProject: getIsShowingProject(loadingState),
languageMenuOpen: languageMenuOpen(state),
locale: state.locales.locale,
loginMenuOpen: loginMenuOpen(state),
projectId: state.scratchGui.projectState.projectId,
modeMenuOpen: modeMenuOpen(state),
projectTitle: state.scratchGui.projectTitle,
sessionExists: state.session && typeof state.session.session !== 'undefined',
errorsMenuOpen: errorsMenuOpen(state),
settingsMenuOpen: settingsMenuOpen(state),
username: user ? user.username : null,
userOwnsProject: ownProps.authorUsername && user &&
(ownProps.authorUsername === user.username),
vm: state.scratchGui.vm
vm: state.scratchGui.vm,
mode220022BC: isTimeTravel220022BC(state),
mode1920: isTimeTravel1920(state),
mode1990: isTimeTravel1990(state),
mode2020: isTimeTravel2020(state),
modeNow: isTimeTravelNow(state)
};
};
@@ -1073,27 +1078,20 @@ const mapDispatchToProps = dispatch => ({
onRequestCloseFile: () => dispatch(closeFileMenu()),
onClickEdit: () => dispatch(openEditMenu()),
onRequestCloseEdit: () => dispatch(closeEditMenu()),
onClickLanguage: () => dispatch(openLanguageMenu()),
onRequestCloseLanguage: () => dispatch(closeLanguageMenu()),
onClickLogin: () => dispatch(openLoginMenu()),
onRequestCloseLogin: () => dispatch(closeLoginMenu()),
onClickErrors: () => dispatch(openErrorsMenu()),
onRequestCloseErrors: () => dispatch(closeErrorsMenu()),
onClickMode: () => dispatch(openModeMenu()),
onRequestCloseMode: () => dispatch(closeModeMenu()),
onRequestOpenAbout: () => dispatch(openAboutMenu()),
onRequestCloseAbout: () => dispatch(closeAboutMenu()),
onClickNew: needSave => {
dispatch(requestNewProject(needSave));
dispatch(setFileHandle(null));
},
onClickSettings: () => dispatch(openSettingsMenu()),
onRequestCloseSettings: () => dispatch(closeSettingsMenu()),
onClickNew: needSave => dispatch(requestNewProject(needSave)),
onClickRemix: () => dispatch(remixProject()),
onClickSave: () => dispatch(manualUpdateProject()),
onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()),
onClickRestorePoints: () => dispatch(openRestorePointModal()),
onClickSettings: () => {
dispatch(openSettingsModal());
dispatch(closeEditMenu());
},
onSeeCommunity: () => dispatch(setPlayer(true))
onSeeCommunity: () => dispatch(setPlayer(true)),
onSetTimeTravelMode: mode => dispatch(setTimeTravel(mode))
});
export default compose(
File diff suppressed because it is too large. Load diff

After

Width:  |  Height:  |  Size: 2.0 MiB

+76
View File
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 66.1 24.7" style="enable-background:new 0 0 66.1 24.7;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#F9A83A;}
</style>
<path class="st0" d="M6.1,20.6c-0.5,0-0.9-0.1-1.4-0.2H0v-9.3h0.1C0.1,10.8,0,10.5,0,10.2c0-1.5,0.5-2.8,1.5-3.9
c1.1-1.1,2.6-1.7,4.2-1.7c0.6,0,1.3,0.1,1.8,0.2l5.6,0.3l-2,6.8c0.6,0.8,1,1.7,1,2.7c0.1,1.5-0.4,2.9-1.4,4
c-1.1,1.2-2.8,1.9-4.6,1.9L6.1,20.6z"/>
<path class="st0" d="M14,20.6c-1.8,0-3.5-0.7-4.8-2C8.1,17.4,7.3,15.8,7,14l-0.6-0.1V8.7L8,8.5c1.4-2.4,3.6-3.8,6.1-3.8
c3.5,0,5.4,2.5,6,3.9l1.3,3.3l-1.8,0.3v0.6l2.7,0.5l-2.3,3.7C18.7,19.3,16.5,20.6,14,20.6z"/>
<path class="st0" d="M24.5,20.6c-0.5,0-1-0.1-1.5-0.2l-2.1,0h-5l-0.7-1.9c-0.1-0.3-0.2-0.6-0.3-0.9L14.5,15l1.5-0.4l0-0.4l-0.7-0.1
v-3h-1.3l1.8-6.2l5.8,0c0.6,0,1.2,0,1.7,0.1l3.6-2.8l-0.9,4c0.7,0.5,1.3,1.2,1.7,2.1h1.2l0,3.2V14l-1.2,0.2c0,0,0,0,0,0l2.1,4
l-2.1,1.5c-0.3,0.2-1.4,0.9-3,0.9L24.5,20.6z"/>
<path class="st0" d="M34.5,20.4H24.2L22.6,15l1.3-0.3l-0.2,0l0.2-3.2l0-3.2l1.4,0c1.1-2.2,3.1-3.3,5.8-3.3h5.7l1.5,6.2h-1.1V14
l-0.7,0.1l0,0.4l1.4,0.3l-0.4,2.7c0,0.3-0.2,0.7-0.2,0.7l-0.6,2.2L34.5,20.4z"/>
<path class="st0" d="M41.7,20.4h-5.2l-0.6-2.1c0,0-0.1-0.4-0.2-0.7l-0.4-2.7l1.4-0.3v-0.1h-3.9l0.1-3.1c0,0,0-0.2,0-0.5l-1.6-0.2
l0.9-3.3l0.7-2.4h14.6l1.7,5.8l-1.7,0.2c0,0.2,0,0.3,0,0.4l0.2,3.2l-4,0v0.1l1.4,0.3l-0.4,2.7c0,0.3-0.2,0.7-0.2,0.7l-0.6,2.2
L41.7,20.4z"/>
<path class="st0" d="M50,20.6c-1.8,0-3.5-0.7-4.8-2c-1.2-1.2-1.9-2.8-2.2-4.6l-0.6-0.1V8.7L44,8.5c1.4-2.4,3.6-3.8,6.1-3.8
c3.5,0,5.4,2.5,6,3.9l1.3,3.3l-1.8,0.3v0.6l2.7,0.5L56,17.1C54.7,19.3,52.5,20.6,50,20.6z"/>
<polygon class="st0" points="51.4,20.4 51,17.9 50.2,15 51.9,14.6 51.9,14.1 51,13.9 51.2,11.1 49.9,11.1 51.7,4.9 64.3,4.9
66.1,11.1 64.7,11.1 64.7,13.9 64,14.1 64,14.5 65.9,15 64.3,20.4 "/>
<path class="st1" d="M6.1,19.4c-0.5,0-0.9-0.1-1.3-0.2H1.2v-6.9h0.7c-0.4-0.6-0.6-1.3-0.7-2.1c0-1.1,0.4-2.2,1.2-3
c0.9-0.9,2.1-1.4,3.4-1.4c0.6,0,1.2,0.1,1.6,0.2l4.3,0.2l-1.8,6c0.6,0.6,1.1,1.4,1.2,2.5c0.1,1.2-0.3,2.3-1.1,3.1
c-0.9,1-2.3,1.5-3.7,1.5L6.1,19.4z"/>
<path class="st1" d="M14,19.4c-1.5,0-2.9-0.6-3.9-1.6c-1.2-1.2-1.8-2.8-2-4.7l-0.5-0.1V9.7l1.2-0.2c0,0,0-0.1,0.1-0.1
c1.1-2.3,3-3.6,5.3-3.6c2.9,0,4.4,2,4.9,3.2l0.8,2l-1.5,0.3v1h0.2v1.6l1.9,0.4l-1.4,2.2C18.2,17.8,16.7,19.4,14,19.4z"/>
<path class="st1" d="M24.5,19.5c-0.9,0-1.6-0.3-2.1-0.6l-0.1,0.4l-1.3,0h-4.2l-0.4-1.1c-0.1-0.2-0.1-0.4-0.2-0.6l-0.3-1.6l1.3-0.3
l0-2.4L16.4,13V9.9h-0.9l1.1-3.8l4.9,0c0.8,0,1.5,0.1,2,0.2L25,5.2l-0.3,1.6c1.3,0.7,2,1.8,2.3,2.7h0.8l0,2V13l-0.9,0.1
c-0.3,0.8-0.8,1.6-1.4,2.1c0,0,0,0,0,0l1.2-0.4l1.6,3l-1.3,0.9c-0.2,0.1-1,0.7-2.3,0.7L24.5,19.5z"/>
<path class="st1" d="M34.5,19.2h-9.4l-1-3.4l1.5-0.3l0-2.1l-0.6,0l0.1-1.9V9.4l1,0c1.3-3.3,4.1-3.3,5-3.3h4.8l0.9,3.8H36V13
l-0.7,0.1l0,2.4l1.3,0.3l-0.2,1.6c0,0.2-0.1,0.5-0.1,0.5l-0.4,1.3L34.5,19.2z"/>
<path class="st1" d="M41.7,19.2h-4.3L37,17.9c0,0-0.1-0.3-0.1-0.5l-0.2-1.6l1.3-0.3l0-2.2h-3.9l0.1-1.8c0,0,0-0.5,0-1.6l-1.3-0.2
l0.6-2l0.5-1.5h12.8l1.1,3.6l-1.4,0.2c0,1.1,0,1.5,0,1.5l0.1,1.9l-3.9,0l0,2.2l1.3,0.3l-0.2,1.6c0,0.2-0.1,0.5-0.1,0.5l-0.4,1.3
L41.7,19.2z"/>
<path class="st1" d="M50,19.4c-1.5,0-2.9-0.6-3.9-1.6c-1.2-1.2-1.8-2.8-2-4.7l-0.5-0.1V9.7l1.2-0.2c0,0,0-0.1,0.1-0.1
c1.1-2.3,3-3.6,5.3-3.6c2.9,0,4.4,2,4.9,3.2l0.8,2l-1.5,0.3v1h0.2v1.6l1.9,0.4L55,16.5C54.2,17.8,52.7,19.4,50,19.4z"/>
<path class="st1" d="M52.4,19.2l-0.3-1.4l0-0.1l-0.5-1.8l1.5-0.4l0-2.4L52.3,13l0.2-3h-1l1.1-3.8h10.8l1.1,3.8h-1v3l-0.7,0.1v2.4
l1.5,0.3l-1,3.4H52.4z M62.9,12.7L62.9,12.7z"/>
<path class="st0" d="M8.1,10.9C8.1,9.3,7.4,8,6,8c-1.9,0-2.5,2.5-1,3.5c0.8,0.5,1.8,0.3,2,0l0.2,0.2c-0.2,0.2,0,0.7,0.3,0.9
c0.6,0.4,1.7,1.1,1.8,2.3c0.2,2-1.7,2.8-3.2,2.7c-1.3-0.1-1.7-0.7-2.2-0.7c-0.5,0-0.5,0.5-0.5,0.5H3v-3.3h0.5c0,1.5,1.1,3.1,2.7,3.1
c1.3,0,2.1-0.8,2-2.2c-0.2-1.6-2.4-1.9-2.9-1.6L5,13.3c0.2-0.2,0.1-0.6-0.2-0.7C4,12.1,3,11.3,3,10.1c0-1.4,1.2-2.5,2.8-2.5
c1.9,0,2.7,1.2,2.7,1.2l0.2-0.9l0.7,0l-0.9,3H8.1z"/>
<path class="st0" d="M17.5,15.5c-1.2,1.9-2.7,2.1-3.5,2.1c-2.6,0-4.1-2.4-4.1-5.2c0-0.6-0.2-0.8-0.5-0.9v-0.2c0.5-0.1,0.8-0.5,1-1
c0.6-1.2,1.7-2.6,3.7-2.6c2.4,0,3.3,2,3.3,2c-0.2,0-0.8,0.5-0.8,1.5h-0.4c0-1.8-0.6-3.1-2.3-3.1c-1.4,0-2.1,1.3-2.2,1.8
c-0.1,0.7,0.1,1.4,0.5,1.4v0.2c-0.5,0.1-0.9,0.4-0.9,1.6c0,1.4,0.6,4.1,2.7,4.1c1.6,0,2.3-1.3,2.2-3.1h0.4
C16.7,15.1,17.4,15.5,17.5,15.5z"/>
<path class="st0" d="M23.1,14.6c0.1,0.8,1.2,2.9,2.7,2.4l0.1,0.3c0,0-0.5,0.4-1.3,0.4c-1.3,0-2.1-0.9-2.8-2.4l0.3-0.4
c-1.5,0-2.1-0.5-2.1-0.5s-0.1,1-0.1,1.5c0,0.9,0.4,1.1,1.1,1.3c0,0.1-0.1,0.3-0.1,0.3h-3c0,0-0.1-0.2-0.1-0.3
c0.7-0.2,1.1-0.4,1.1-1.3v-3.2c0-0.8-0.4-1.1-0.7-1.2v-0.2h0c0.4,0,0.7-0.9,0.7-1.6c0-1.5-1.1-1.5-1.1-1.5l0.1-0.3h3.3
c0.8,0,3.5-0.1,4,2.5c0.1,0.4,0.3,0.8,0.6,0.8h0v0.2c-0.3,0-0.5,0.3-0.7,0.9C24.8,13.7,23.8,14.3,23.1,14.6z M23.6,11.4v-0.2h0
c0.3,0,0.8-0.4,0.6-1.3c-0.2-1.4-1.7-1.6-2.4-1.6c-0.4,0-1.7,0.1-1.9,1.4c-0.2,1.5,0.8,1.5,0.8,1.5v0.2c-0.4,0.1-0.8,0.3-0.8,1.2
c0,1.1,0.9,1.8,1.9,1.8c1.3,0,2.4-0.7,2.4-1.8C24.3,12,24.1,11.6,23.6,11.4z M22.2,10.7c0.2,0.6,0.6,0.7,0.6,1.3
c0,0.7-0.8,1.1-0.8,1.1s0.2-0.6,0-1.1c-0.2-0.4-0.5-0.6-0.5-1.2c0-0.6,0.7-1.2,0.7-1.2S22.1,10.2,22.2,10.7z"/>
<path class="st0" d="M34.5,17.1c0,0.1-0.1,0.3-0.1,0.3h-3c0,0,0-0.2-0.1-0.3c0.7-0.2,1.1-0.4,1.1-1.3c0-1.7-1.5-3.5-3.3-3.5l0.1-0.3
c1.4-0.1,2.5,0.5,3.2,1.4V9.9c0-1.2-0.6-1.6-1.7-1.6c-2.1,0-2.4,2.2-2.4,3v4.6c0,0.9,0.4,1.1,1.1,1.3l-0.1,0.3h-3l-0.1-0.3
c0.7-0.2,1.1-0.4,1.1-1.3v-3.2c0,0,0-1.2-0.5-1.2v-0.2c0.5,0,0.6-0.5,0.6-0.5c0.8-2.9,2.6-2.9,3.6-2.9h3.3l0.1,0.3c0,0-1,0-1.1,1.5
c0,1,0.3,1.6,0.7,1.6v0.2c-0.5,0.1-0.7,0.6-0.7,1.2v3.2C33.5,16.8,33.9,17,34.5,17.1z"/>
<path class="st0" d="M44.5,9.5c0,1.5,0,2,0,2h-0.3c0,0,0.3-1.5-1-1.5l0-0.3c0.7,0,0.7-1.5-0.8-1.5c-1.1,0-1.6,0.5-1.7,1.4
c-0.1,1,0.3,1.5,0.7,1.5v0.2c-0.3,0.1-0.7,0.3-0.7,1.2v3.2c0,0.9,0.4,1.1,1.1,1.3c0,0.1-0.1,0.3-0.1,0.3h-3c0,0,0-0.2-0.1-0.3
c0.7-0.2,1.1-0.4,1.1-1.3v-3.2c0-0.8-0.3-1.1-0.7-1.2v-0.2c0.4-0.1,0.8-0.5,0.7-1.5c-0.1-0.9-0.6-1.4-1.8-1.4
c-1.4,0-1.4,1.4-0.7,1.4l0,0.3c-1.3,0-1,1.4-1,1.5h-0.3c0,0,0-0.5,0-2c0-1.1-0.4-1.3-0.8-1.3l0.1-0.3h10.1l0.1,0.3
C44.9,8.2,44.5,8.4,44.5,9.5z"/>
<path class="st0" d="M53.5,15.5c-1.2,1.9-2.7,2.1-3.5,2.1c-2.6,0-4.1-2.4-4.1-5.2c0-0.6-0.2-0.8-0.5-0.9v-0.2c0.5-0.1,0.8-0.5,1-1
c0.6-1.2,1.7-2.6,3.7-2.6c2.4,0,3.3,2,3.3,2c-0.2,0-0.8,0.5-0.8,1.5h-0.4c0-1.8-0.6-3.1-2.3-3.1c-1.4,0-2.1,1.3-2.2,1.8
c-0.1,0.7,0.1,1.4,0.6,1.4v0.2c-0.5,0.1-0.9,0.4-0.9,1.6c0,1.4,0.6,4.1,2.7,4.1c1.6,0,2.3-1.3,2.2-3.1h0.4
C52.7,15.1,53.3,15.5,53.5,15.5z"/>
<path class="st0" d="M62.1,17.1l-0.1,0.3h-3c0,0,0-0.2-0.1-0.3c0.7-0.2,1.1-0.4,1.1-1.3V13c0-0.9-0.6-1.4-1.2-1.4h-1.7
c-0.9,0-1.3,0.4-1.3,1.4v2.9c0,0.9,0.4,1.1,1.1,1.3l-0.1,0.3h-3c0,0,0-0.2-0.1-0.3c0.7-0.2,1.1-0.4,1.1-1.3v-3.2
c0-0.8-0.3-1.1-0.7-1.2l0-0.2c0.4,0,0.7-0.9,0.7-1.6c0-1.5-1.1-1.5-1.1-1.5l0.1-0.3h3l0.1,0.3c0,0-1.1,0-1.1,1.5
c0,1.2,0.4,1.6,1.3,1.6h1.7c0.7,0,1.2-0.7,1.2-1.6c0-1.5-1.1-1.5-1.1-1.5l0.1-0.3h3l0.1,0.3c0,0-1.1,0-1.1,1.5
c0,0.8,0.3,1.6,0.7,1.6v0.2c-0.3,0.1-0.7,0.5-0.7,1.3v3.1C61.1,16.8,61.4,17,62.1,17.1z"/>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 66.1 24.7" style="enable-background:new 0 0 66.1 24.7;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#F9A83A;}
</style>
<path class="st0" d="M12.1,21.3c-0.7-0.2-1.5-0.3-2.4-0.3H9.1c-0.2,0-0.8,0-2.7,0.2L4,21.4l-1-2.2c-0.8-1.8-1.1-2.5-1.2-3.1
l-0.1-0.4v-1.1l0.4-0.8l0,0c0.2-0.4,0.4-0.7,0.7-0.9c-0.9-2.3-1-3.2-1-3.9V5.3h2.5L4.6,5l0.5-0.2c6.6-2.1,7.2-2.1,7.8-2.1h0.9l1.6,1
l0.1,0c1.8,1.3,3.1,2.5,3.4,4.1c0.5,1.4,0.6,1.8,0.6,2.5v2.5L19.1,13l0.7,2.4v1c0,2.5-1.7,3.2-5.7,4.8l-1.1,0.4L12.1,21.3z"/>
<path class="st0" d="M19.4,22.7c-0.7,0-1.3-0.1-1.9-0.3h-1.9l-0.8-2.2c-1.4-3.6-1.7-4.8-1.9-5.4l-0.3-1.5l0.8-1.1
c0.1-0.2,0.3-0.6,0.7-1.5l0.3-0.6l0.5-0.4c0.9-0.8,2.2-1.4,4.4-1.9c0.6-0.2,1.1-0.2,1.6-0.2h0.1c0.5,0,1.2,0.1,2.5,0.4l1.3,0.3
l0.7,1.2c0.5,0.9,1,1.9,1.3,3c0.1,0.4,0.2,0.9,0.2,1.3v2.4l0,0C27,16.5,27,16.8,27,17v0.6l-0.2,0.6c-0.6,1.7-1.6,2.5-2.4,2.9
c-0.5,0.3-1.2,0.6-2.5,1c-0.8,0.4-1.6,0.6-2.4,0.6H19.4z"/>
<path class="st0" d="M27,17.5l-0.5-0.9c-0.1,0-0.2,0.1-0.3,0.1c-0.6,0.3-1.2,0.5-2,0.5h-2.4L20.9,15c-0.5-1.5-0.7-2.1-0.7-2.8
c0-0.8,0-2,0-3.7c-0.2-0.8-0.3-1.1-0.3-1.5V4.7L22,3.9c1.4-0.6,2.3-0.9,3.1-0.9c0.3,0,0.8-0.1,1.4-0.1c2.4,0,6.1,0,6.8,3
C34,7.6,34.2,8.3,34.2,9c0,0.7-0.1,1.4-0.4,2c0,0,0,0,0,0.1l1.8,5.5h-3.4l-0.8,0.3c-1.7,0.6-2,0.7-2.6,0.7H27z"/>
<path class="st0" d="M36.2,22.6c-1,0-1.8-0.4-2.5-1.1c-0.1,0-0.1,0-0.2,0c-0.9,0.6-1.8,0.8-2.4,1l-0.4,0.1h-3.3l-0.9-2
c-0.5-1.1-0.7-1.8-0.7-2.6v-2.7l0.4-0.8c0.6-1.1,1.8-2.7,3.7-5l0.6-0.7l0.9-0.3c1.1-0.4,2-0.7,2.8-0.9l0.4-0.1h2
c2.5,0,3,1.3,4.2,5.2h0l0,0.1c0.1,0.2,0.3,1.1,0.3,1.1v0.3c0.1,0.5,0.3,1.1,0.5,1.9l0.2,0.5v4.7l-2.2,0.8c-0.8,0.3-1.4,0.4-2,0.4
H36.2z"/>
<path class="st0" d="M36.4,18.6c-0.7-1.4-1.3-3-1.7-5c-0.1-0.2-0.1-0.4-0.2-0.5l-0.3-0.2c-1-0.7-1.3-1.6-2.1-3.9l-1-2.9l2.7-1.3
C35.5,4,37,3.5,38.4,3.5c0.1,0,0.7,0,4.8-0.4l0.3,0l0.3,0c0.5,0,0.9,0.1,1.4,0.1c2.9,0,3.6,2.6,4.3,5.5l0.5,2.5l-1.5,1.3
c-0.4,0.4-1,0.7-2.1,1.2V14l-0.2,0.5c-0.1,0.2-0.2,0.7-0.2,1.4c0,3.1-3.3,3.9-5.8,4.5L37.6,21L36.4,18.6z"/>
<path class="st0" d="M46.2,23.8c-0.7,0-1.3-0.1-2-0.3h-1.9l-0.8-2.2c-1.4-3.6-1.7-4.8-1.9-5.4l-0.3-1.5l0.8-1.1
c0.1-0.2,0.3-0.6,0.7-1.5l0.3-0.6l0.5-0.4c0.9-0.8,2.2-1.4,4.4-1.9c0.6-0.2,1.1-0.2,1.6-0.2h0.1c0.5,0,1.2,0.1,2.5,0.4l1.3,0.3
l0.7,1.2c0.5,0.9,1,1.9,1.3,3c0.1,0.4,0.2,0.9,0.2,1.3v2.4l0,0c0.1,0.3,0.1,0.6,0.1,0.8v0.6l-0.2,0.6c-0.6,1.7-1.6,2.5-2.4,2.9
c-0.5,0.3-1.2,0.6-2.5,1c-0.8,0.4-1.6,0.6-2.4,0.6H46.2z"/>
<path class="st0" d="M50.9,22.5c-2.3-0.5-3.1-2.7-3.8-4.9L46.9,17v-1.2l0.1-0.4c0.6-2.3,0.8-3.3,0.9-3.6c0-0.4-0.1-0.8-0.1-1.6
l-0.1-2.3l2.1-0.9c1.8-0.8,2.6-1,3.4-1h0.9L55,6.3c0.2,0.1,0.3,0.2,0.5,0.3c1-0.6,2.3-1,3.4-1.3l0.4-0.1h0.6c2.5,0,3.6,2.2,4.2,4
h1.3l-1.1,4.2c0,0.1-0.1,0.4-0.1,1.1c0,0.2,0,0.3,0,0.5v1.1c0,0.1,0,0.3,0.1,0.7l0.1,0.3v0.3c0,2.7-2.3,3.6-5.3,4.7l-0.6,0.2h-2.7
l-0.6-1c-0.6,0.3-1.5,0.6-2.7,1l-0.9,0.3L50.9,22.5z"/>
<path class="st1" d="M12.5,19.7c-0.9-0.2-1.8-0.4-2.9-0.4H9.1c-0.3,0-1.1,0-2.9,0.2l-1.2,0.1l-0.5-1.1c-0.8-1.8-1-2.4-1.1-2.7l0-0.2
v-0.6l0.2-0.4c0.2-0.4,0.6-0.7,1.3-1.1c-1.3-3.3-1.4-4.1-1.4-4.6V7h1.3C4.9,6.9,5,6.8,5.2,6.7l0.2-0.1l0.2-0.1c6.3-2,6.9-2,7.3-2
h0.5l1.2,0.7l0.1,0c1.8,1.3,2.6,2.2,2.7,3.1C18,9.8,18,10,18,10.3v1.3L17,12l1.1,3.7v0.7c0,1.4-0.9,1.7-4.6,3.2l-0.5,0.2L12.5,19.7z
"/>
<path class="st1" d="M19.4,21c-0.6,0-1.1-0.1-1.6-0.3h-1.1l-0.4-1.1c-1.4-3.5-1.7-4.6-1.8-5.1l-0.1-0.7l0.4-0.6c0,0,0,0,0,0
c0,0,0.2-0.3,0.8-1.6l0.1-0.3l0.2-0.2c0.7-0.6,1.8-1.1,3.7-1.5c0.5-0.1,0.8-0.2,1.2-0.2h0.1c0.3,0,0.8,0.1,2.1,0.4l0.7,0.2l0.4,0.6
c0.5,0.7,0.8,1.6,1.1,2.5c0.1,0.3,0.2,0.6,0.2,0.9V15l-0.4,0.2c0.1,0.2,0.2,0.5,0.2,0.7c0.2,0.4,0.3,0.8,0.3,1.2v0.3l-0.1,0.3
c-0.4,1.1-1.1,1.7-1.6,2c-0.4,0.2-1,0.5-2.4,0.9c-0.6,0.3-1.1,0.5-1.8,0.5H19.4z"/>
<path class="st1" d="M28,15.8l-0.3-0.5c-0.2-0.1-0.5-0.3-1.2-0.6c-0.2,0.2-0.6,0.3-1,0.4c-0.6,0.3-1,0.4-1.4,0.4h-1.2l-0.4-1.1
c-0.5-1.4-0.6-1.8-0.6-2.2c0-0.8,0-2.1,0-3.9c-0.3-1-0.3-1-0.3-1.3V5.9l1-0.4c1.5-0.6,2.2-0.8,2.6-0.8c0.2,0,0.6-0.1,1.3-0.1
c2.3,0,4.8,0,5.2,1.8c0.7,1.8,0.8,2.2,0.8,2.6c0,0.7-0.2,1.4-0.5,1.9c0.1,0.2,0.2,0.4,0.2,0.6l1.1,3.4h-1.7l-0.2,0.2L31,15.2
c-1.6,0.5-1.8,0.6-2.1,0.6H28z"/>
<path class="st1" d="M36.2,20.9c-0.8,0-1.3-0.4-1.6-0.9c-0.6-0.1-1.1-0.2-1.7-0.2c-0.6,0.7-1.7,0.9-2.3,1.1l-0.2,0h-2l-0.4-1
c-0.4-1-0.6-1.5-0.6-1.9v-2.2l0.2-0.4c0.6-1,1.7-2.6,3.6-4.8l0.3-0.4l0.4-0.2c1-0.4,1.9-0.7,2.6-0.8l0.2,0h1.8
c1.4,0,1.5,0.5,2.8,4.8l0.1,0.2v0.3c0,0,0,0,0,0c0,0,0.1,0.5,0.6,2.2l0.1,0.2v3.3L39,20.6c-0.6,0.2-1,0.3-1.4,0.3H36.2z"/>
<path class="st1" d="M37.9,17.8c-0.7-1.3-1.2-2.9-1.6-4.7c-0.2-0.4-0.3-0.9-0.3-1.3l-0.4,0l-0.4-0.3c-0.5-0.4-0.7-0.8-1.5-3.1
L33.2,7l1.4-0.7c1.5-0.7,2.8-1.1,3.8-1.1c0.1,0,0.9,0,4.9-0.4l0.1,0l0.1,0c0.5,0,1,0.1,1.5,0.1c1.6,0,2,1.5,2.7,4.2l0.3,1.4
l-0.7,0.6c-0.3,0.3-0.8,0.6-2.3,1.3l-0.4,0.2l0,0c0.1,0.5,0.1,0.6,0.1,0.8v0.3L44.6,14c-0.2,0.5-0.3,1.1-0.3,1.9
c0,1.7-1.9,2.2-4.5,2.9L38.5,19L37.9,17.8z"/>
<path class="st1" d="M46.2,22.1c-0.6,0-1.1-0.1-1.6-0.3h-1.1l-0.4-1.1c-1.4-3.5-1.7-4.6-1.8-5.1l-0.1-0.7l0.5-0.6c0,0,0,0,0,0
c0,0,0.2-0.3,0.8-1.6l0.1-0.3l0.2-0.2c0.7-0.6,1.8-1.1,3.7-1.5c0.5-0.1,0.8-0.2,1.2-0.2h0.1c0.3,0,0.8,0.1,2.1,0.4l0.7,0.2l0.4,0.6
c0.5,0.7,0.8,1.6,1.1,2.5c0.1,0.3,0.2,0.6,0.2,0.9v1.2l-0.4,0.2c0.1,0.2,0.2,0.5,0.2,0.7c0.2,0.4,0.3,0.8,0.3,1.2v0.3l-0.1,0.3
c-0.4,1.1-1.1,1.7-1.6,2c-0.4,0.2-1,0.5-2.4,0.9c-0.6,0.3-1.1,0.5-1.8,0.5H46.2z"/>
<path class="st1" d="M51.3,20.8c-1.2-0.2-1.7-1.3-2.5-3.8l-0.1-0.3v-0.7l0.1-0.2c0.7-2.8,0.9-3.8,0.9-4.1c0-0.3-0.1-0.8-0.1-1.7
l0-1.2l1.1-0.5c1.7-0.8,2.3-0.9,2.8-0.9h0.5l0.4,0.2c0.4,0.3,0.7,0.6,1.1,1.5C55.8,8,57.6,7.4,59.3,7l0.2-0.1h0.4
c1.6,0,2.2,1.6,2.8,3.7l0.1,0.2v0.1h0.4l-0.5,2.1c0,0,0,0.3-0.2,1.4c0,0.1,0,0.3,0,0.5v1.2c0,0,0,0.2,0.2,0.9l0,0.2v0.2
c0,1.4-1.3,2-4.2,3.1l-0.3,0.1h-1.4l-0.5-1c-0.2-0.5-0.4-0.9-0.6-1.3v0.7L55,19.6c-0.5,0.3-1.4,0.7-2.8,1.1l-0.4,0.1L51.3,20.8z"/>
<path class="st0" d="M13,6.2l0.7,0.4c1.4,1,2.1,1.7,2.1,2.1c0.4,1,0.6,1.5,0.6,1.6v0.1L14,11.3c-1.1-1.1-1.8-1.7-2.1-1.7
C11,9.8,10.4,10,10.1,10l0.1,0.1c0,0.2,0,0.6-0.1,1l0.1,0.1h1c2,0,3.4,0.4,4.1,1.1l1.1,3.6v0.5c0,0.1-1.2,0.7-3.5,1.6
c-1-0.3-2.1-0.4-3.3-0.4H9.1c-0.5,0-1.5,0.1-3,0.2c-0.6-1.2-0.9-2-1-2.3v-0.1c0.1-0.2,0.9-0.6,2.3-1.1c0.3,0,0.8,0,1.4,0.1l0.5-0.1
l1.9,0.1c0.1,0,0.2-0.1,0.2-0.1h0.3c0-0.3-0.3-0.5-0.8-0.5c-2.6-0.3-4-0.5-4.2-0.5c-1-2.5-1.5-4-1.5-4.5V8.7c0.1,0,0.4-0.2,1-0.5
C10.2,6.8,12.5,6.2,13,6.2z"/>
<path class="st0" d="M20.8,11h0.1c0.2,0,0.8,0.1,1.7,0.3c0.4,0.6,0.7,1.3,0.9,2.1c0.1,0.2,0.1,0.4,0.1,0.4c-1,0.4-1.6,0.5-2,0.5
c0,0.1-0.1,0.1-0.2,0.1h0C21.2,14.1,21,14,20.9,14h-0.4c-0.2,0-0.4,0-0.7,0c-0.4,0.9-0.7,1.5-0.7,1.7c0.7,0.6,1.3,0.9,1.7,1h0.1
c0.3,0,0.5-0.3,0.6-0.8c0.1,0,0.6-0.2,1.5-0.5c0.1,0,0.2,0.3,0.4,1c0.1,0.2,0.2,0.4,0.2,0.6v0c-0.2,0.5-0.5,0.9-0.9,1.1
c-0.2,0.2-0.9,0.4-2.2,0.8c-0.3,0.2-0.7,0.3-1.1,0.3h-0.1c-0.5,0-0.9-0.1-1.3-0.4L18,19h-0.2c-1-2.5-1.5-4.1-1.7-4.8
c0.1-0.1,0.4-0.7,1-1.9c0.4-0.4,1.4-0.8,2.9-1.2C20.4,11,20.6,11,20.8,11z"/>
<path class="st0" d="M26.5,6.3c2.3,0,3.5,0.2,3.5,0.5c0.5,1.3,0.8,2,0.8,2.2c0,0.7-0.3,1.2-0.9,1.4c-0.5,0.2-0.9,0.4-1,0.5
c1,0.3,1.5,0.6,1.5,1l0.4,1.3c-0.1,0-0.2,0.1-0.5,0.4c-1,0.3-1.5,0.5-1.5,0.5c-0.1-0.2-1.3-0.8-3.4-1.7l0,0.7c0,0.2-0.2,0.4-0.7,0.4
c-0.4,0.2-0.7,0.3-0.8,0.3c-0.3-0.9-0.5-1.5-0.5-1.6c0-0.8,0-2.2,0-4.2c-0.2-0.7-0.3-1-0.3-1c1.1-0.5,1.8-0.7,2.1-0.7
C25.5,6.3,25.9,6.3,26.5,6.3z M25.7,8.4v0.7c1.8-0.1,2.7-0.2,2.7-0.3c0-0.1,0-0.1,0-0.2v0c-0.7-0.1-1.5-0.1-2.4-0.1H25.7z"/>
<path class="st0" d="M34.9,10.9h1.6c0.1,0,0.5,1.2,1.2,3.6c0,0.3,0.2,1.2,0.7,2.7V19c-0.4,0.1-0.6,0.2-0.8,0.2h-1.5
c-0.1,0-0.2-0.3-0.4-0.8c-1.1-0.2-2.3-0.4-3.5-0.4c-0.3,0-0.5,0.2-0.6,0.5c0,0.2-0.5,0.4-1.4,0.6h-0.7c-0.3-0.7-0.4-1.1-0.4-1.2
v-1.8c0.5-0.9,1.6-2.4,3.4-4.5C33.5,11.3,34.3,11.1,34.9,10.9z M34.7,13.4c0,0.1-0.5,0.9-1.6,2.4c0.1,0.1,0.9,0.2,2.1,0.4
C35.1,14.9,34.9,14,34.7,13.4L34.7,13.4z"/>
<path class="st0" d="M43.4,6.5c0.5,0,1.1,0.1,1.6,0.1c0.2,0,0.5,1,1.1,3l0.1,0.3c-0.2,0.2-0.9,0.5-1.9,1c-4.7-0.4-7.4-0.7-8.1-0.8
C36,10,35.7,9.3,35.3,7.9c1.3-0.6,2.3-0.9,3.1-0.9C38.7,6.9,40.4,6.8,43.4,6.5z M40,10.7c0.2,0,0.9,0.2,2.2,0.5
c0.1,0,0.2,0.2,0.3,0.5c0.3,1.1,0.5,1.7,0.5,1.7c-0.2,0.7-0.4,1.5-0.4,2.4c0,0.3-1.1,0.8-3.2,1.3c-0.6-1.2-1.1-2.7-1.5-4.6
c-0.2-0.3-0.3-0.6-0.3-0.9C37.7,11.6,38.5,11.3,40,10.7z"/>
<path class="st0" d="M47.7,12.1h0.1c0.2,0,0.8,0.1,1.7,0.3c0.4,0.6,0.7,1.3,0.9,2.1c0.1,0.2,0.1,0.4,0.1,0.4c-1,0.4-1.6,0.5-2,0.5
c0,0.1-0.1,0.1-0.2,0.1h0c-0.2-0.3-0.3-0.4-0.4-0.4h-0.4c-0.2,0-0.4,0-0.7,0c-0.4,0.9-0.7,1.5-0.7,1.7c0.7,0.6,1.3,0.9,1.7,1h0.1
c0.3,0,0.5-0.3,0.6-0.8c0.1,0,0.6-0.2,1.5-0.5c0.1,0,0.2,0.3,0.4,1c0.1,0.2,0.2,0.4,0.2,0.6v0c-0.2,0.5-0.5,0.9-0.9,1.1
c-0.2,0.2-0.9,0.4-2.2,0.8c-0.3,0.2-0.7,0.3-1.1,0.3h-0.1c-0.5,0-0.9-0.1-1.3-0.4l-0.1,0h-0.2c-1-2.5-1.5-4.1-1.7-4.8
c0.1-0.1,0.4-0.7,1-1.9c0.4-0.4,1.4-0.8,2.9-1.2C47.2,12.1,47.5,12.1,47.7,12.1z"/>
<path class="st0" d="M59.8,8.6h0.2c0.3,0,0.7,0.8,1.2,2.5c0,0.7,0,1.1-0.1,1.4c0,0,0,0.1,0.1,0.1c0,0.2-0.1,0.8-0.3,2
c0,0.1,0.1,0.2,0.1,0.3c0,0,0,0.1-0.1,0.1l0.1,0.3v0.8c0,0.2,0.1,0.6,0.2,1.3c0,0.2-1,0.7-3.1,1.5h-0.1c-0.7-1.5-1.1-2.5-1.1-3.1
v-0.1l-0.6-0.1h-1.1c-0.7,0-1,0.1-1,0.3c0,0.5,0,1.1-0.1,1.6c0,0.2,0.1,0.3,0.1,0.6c-0.4,0.3-1.3,0.6-2.5,1
c-0.3-0.1-0.7-0.9-1.3-2.6v-0.2c0.7-2.5,1-4,1-4.6c0-0.1-0.1-0.6-0.1-1.6c1.1-0.5,1.8-0.8,2.1-0.8c0.2,0.1,0.5,0.9,1,2.5V13
c0.1,0,1-0.1,2.6-0.2v-0.1c0-0.6,0.1-1.1,0.1-1.5v-0.1c-0.2-0.7-0.3-1-0.3-1.1C56.7,9.7,57.7,9.2,59.8,8.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

+67
View File
@@ -0,0 +1,67 @@
.icon {
width: 1.5rem;
}
.theme-label {
flex: 1;
}
.option {
display: flex;
align-items: center;
gap: .5rem;
}
.check {
margin: 0 .25rem 0 0;
visibility: hidden;
}
[dir="rtl"] .check {
margin: 0 0 0 .25rem;
}
.selected {
visibility: visible;
}
.submenu-label {
flex: 1;
}
.dropdown-label {
margin: 0 0.5rem 0 0.25rem;
}
[dir="rtl"] .dropdown-label {
margin: 0 0.25rem 0 0.5rem;
}
.expand-caret {
transform: rotate(-90deg);
}
[dir="rtl"] .expand-caret {
transform: rotate(90deg);
}
/* Extra padding to deal with scrollbar overlapping options in Firefox */
[dir="ltr"] .language-menu-item {
padding-right: 1rem;
}
[dir="rtl"] .language-menu-item {
padding-left: 1rem;
}
.language-submenu > ul {
max-height: calc(100vh - 5rem); /* Fallback if dvh not supported */
max-height: calc(100dvh - 5rem);
}
@media only screen and (max-width: 1024px) {
.dropdown-label {
display: none;
}
}
+65
View File
@@ -0,0 +1,65 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import {FormattedMessage} from 'react-intl';
import LanguageMenu from './language-menu.jsx';
import MenuBarMenu from './menu-bar-menu.jsx';
import ThemeMenu from './theme-menu.jsx';
import {MenuSection} from '../menu/menu.jsx';
import menuBarStyles from './menu-bar.css';
import styles from './settings-menu.css';
import dropdownCaret from './dropdown-caret.svg';
import settingsIcon from './icon--settings.svg';
const SettingsMenu = ({
canChangeLanguage,
canChangeTheme,
isRtl,
onRequestClose,
onRequestOpen,
settingsMenuOpen
}) => (
<div
className={classNames(menuBarStyles.menuBarItem, menuBarStyles.hoverable, menuBarStyles.themeMenu, {
[menuBarStyles.active]: settingsMenuOpen
})}
onMouseUp={onRequestOpen}
>
<img
src={settingsIcon}
/>
<span className={styles.dropdownLabel}>
<FormattedMessage
defaultMessage="Settings"
description="Settings menu"
id="gui.menuBar.settings"
/>
</span>
<img src={dropdownCaret} />
<MenuBarMenu
className={menuBarStyles.menuBarMenu}
open={settingsMenuOpen}
place={isRtl ? 'left' : 'right'}
onRequestClose={onRequestClose}
>
<MenuSection>
{canChangeLanguage && <LanguageMenu onRequestCloseSettings={onRequestClose} />}
{canChangeTheme && <ThemeMenu onRequestCloseSettings={onRequestClose} />}
</MenuSection>
</MenuBarMenu>
</div>
);
SettingsMenu.propTypes = {
canChangeLanguage: PropTypes.bool,
canChangeTheme: PropTypes.bool,
isRtl: PropTypes.bool,
onRequestClose: PropTypes.func,
onRequestOpen: PropTypes.func,
settingsMenuOpen: PropTypes.bool
};
export default SettingsMenu;
+118
View File
@@ -0,0 +1,118 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import {FormattedMessage} from 'react-intl';
import {connect} from 'react-redux';
import check from './check.svg';
import {MenuItem, Submenu} from '../menu/menu.jsx';
import {DEFAULT_THEME, HIGH_CONTRAST_THEME, themeMap} from '../../lib/themes';
import {persistTheme} from '../../lib/themes/themePersistance';
import {openThemeMenu, themeMenuOpen} from '../../reducers/menus.js';
import {setTheme} from '../../reducers/theme.js';
import styles from './settings-menu.css';
import dropdownCaret from './dropdown-caret.svg';
const ThemeMenuItem = props => {
const themeInfo = themeMap[props.theme];
return (
<MenuItem onClick={props.onClick}>
<div className={styles.option}>
<img
className={classNames(styles.check, {[styles.selected]: props.isSelected})}
src={check}
/>
<img
className={styles.icon}
src={themeInfo.icon}
/>
<FormattedMessage {...themeInfo.label} />
</div>
</MenuItem>);
};
ThemeMenuItem.propTypes = {
isSelected: PropTypes.bool,
onClick: PropTypes.func,
theme: PropTypes.string
};
const ThemeMenu = ({
isRtl,
menuOpen,
onChangeTheme,
onRequestOpen,
theme
}) => {
const enabledThemes = [DEFAULT_THEME, HIGH_CONTRAST_THEME];
const themeInfo = themeMap[theme];
return (
<MenuItem expanded={menuOpen}>
<div
className={styles.option}
onClick={onRequestOpen}
>
<img
src={themeInfo.icon}
style={{width: 24}}
/>
<span className={styles.submenuLabel}>
<FormattedMessage
defaultMessage="Color Mode"
description="Color mode sub-menu"
id="gui.menuBar.colorMode"
/>
</span>
<img
className={styles.expandCaret}
src={dropdownCaret}
/>
</div>
<Submenu place={isRtl ? 'left' : 'right'}>
{enabledThemes.map(enabledTheme => (
<ThemeMenuItem
key={enabledTheme}
isSelected={theme === enabledTheme}
// eslint-disable-next-line react/jsx-no-bind
onClick={() => onChangeTheme(enabledTheme)}
theme={enabledTheme}
/>)
)}
</Submenu>
</MenuItem>
);
};
ThemeMenu.propTypes = {
isRtl: PropTypes.bool,
menuOpen: PropTypes.bool,
onChangeTheme: PropTypes.func,
// eslint-disable-next-line react/no-unused-prop-types
onRequestCloseSettings: PropTypes.func,
onRequestOpen: PropTypes.func,
theme: PropTypes.string
};
const mapStateToProps = state => ({
isRtl: state.locales.isRtl,
menuOpen: themeMenuOpen(state),
theme: state.scratchGui.theme.theme
});
const mapDispatchToProps = (dispatch, ownProps) => ({
onChangeTheme: theme => {
dispatch(setTheme(theme));
ownProps.onRequestCloseSettings();
persistTheme(theme);
},
onRequestOpen: () => dispatch(openThemeMenu())
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ThemeMenu);
+56 -3
View File
@@ -4,7 +4,7 @@
position: absolute;
border: 1px solid $ui-black-transparent;
border-radius: 0 0 8px 8px;
background-color: $motion-primary;
background-color: $looks-secondary;
padding: 0;
margin: 0;
min-width: 186px;
@@ -15,7 +15,7 @@
box-shadow: 0 8px 8px 0 $ui-black-transparent-default;
}
[theme="dark"] .menu {
background-color: $motion-primary-dark;
background-color: $motion-primary;
}
.menu.left {
@@ -34,10 +34,12 @@
font-size: .75rem;
margin: 0;
font-weight: bold;
position: relative;
}
.menu-item.active,
.menu-item:hover {
.menu-item:hover,
.menu-item.expanded {
background-color: $ui-black-transparent;
}
@@ -48,3 +50,54 @@
.menu-section {
border-top: 1px solid $ui-black-transparent;
}
.submenu {
display: none;
position: absolute;
top: -1px;
min-width: 186px;
max-width: 260px;
height: max-content;
overflow: hidden;
border-radius: 8px;
}
.submenu.right {
left: 100%;
}
.submenu.left {
right: 100%;
}
.submenu > .menu {
position: relative;
overflow-y: auto;
overflow-x: hidden;
scrollbar-color: $ui-black-transparent $looks-secondary;
border-radius: 8px;
}
.menu-item.expanded > .submenu {
display: block;
}
:not(:has(> .menu-item.expanded)) > .menu-item:hover > .submenu {
display: block;
}
/* Fallback styling for webkit browsers that don't support scrollbar-color */
.submenu > .menu::-webkit-scrollbar {
width: 12px;
}
.submenu > .menu::-webkit-scrollbar-track {
background: $looks-secondary;
}
.submenu > .menu::-webkit-scrollbar-thumb {
background-color: $ui-black-transparent;
border-radius: 12px;
border: 3px solid $looks-secondary;
}
+32 -2
View File
@@ -33,16 +33,44 @@ MenuComponent.propTypes = {
};
const Submenu = ({children, className, place, ...props}) => (
<div
className={classNames(
styles.submenu,
className,
{
[styles.left]: place === 'left',
[styles.right]: place === 'right'
}
)}
>
<MenuComponent
place={place}
{...props}
>
{children}
</MenuComponent>
</div>
);
Submenu.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
place: PropTypes.oneOf(['left', 'right'])
};
const MenuItem = ({
children,
className,
expanded = false,
onClick
}) => (
<li
className={classNames(
styles.menuItem,
styles.hoverable,
className
className,
{[styles.expanded]: expanded}
)}
onClick={onClick}
>
@@ -53,6 +81,7 @@ const MenuItem = ({
MenuItem.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
expanded: PropTypes.bool,
onClick: PropTypes.func
};
@@ -80,5 +109,6 @@ MenuSection.propTypes = {
export {
MenuComponent as default,
MenuItem,
MenuSection
MenuSection,
Submenu
};
+4 -4
View File
@@ -25,7 +25,7 @@
user-select: none;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: $text-primary-light;
color: $text-primary;
overflow: hidden;
}
@@ -67,14 +67,14 @@ $sides: 20rem;
box-sizing: border-box;
width: 100%;
background-color: $motion-primary;
background-color: $looks-secondary;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 1rem;
font-weight: normal;
}
[theme="dark"] .header {
background-color: $motion-primary-dark;
background-color: $motion-primary;
}
[theme="dark"] .full-screen .header {
background-color: $motion-primary;
+8 -2
View File
@@ -10,7 +10,10 @@ const DefaultMonitor = ({categoryColor, label, value}) => (
</div>
<div
className={styles.value}
style={{background: categoryColor}}
style={{
background: categoryColor.background,
color: categoryColor.text
}}
>
{value}
</div>
@@ -19,7 +22,10 @@ const DefaultMonitor = ({categoryColor, label, value}) => (
);
DefaultMonitor.propTypes = {
categoryColor: PropTypes.string.isRequired,
categoryColor: PropTypes.shape({
background: PropTypes.string.isRequired,
text: PropTypes.string.isRequired
}).isRequired,
label: PropTypes.string.isRequired,
value: PropTypes.oneOfType([
PropTypes.string,
+8 -2
View File
@@ -6,7 +6,10 @@ const LargeMonitor = ({categoryColor, value}) => (
<div className={styles.largeMonitor}>
<div
className={styles.largeValue}
style={{background: categoryColor}}
style={{
background: categoryColor.background,
color: categoryColor.text
}}
>
{value}
</div>
@@ -14,7 +17,10 @@ const LargeMonitor = ({categoryColor, value}) => (
);
LargeMonitor.propTypes = {
categoryColor: PropTypes.string,
categoryColor: PropTypes.shape({
background: PropTypes.string.isRequired,
text: PropTypes.string.isRequired
}).isRequired,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
@@ -41,7 +41,10 @@ class ListMonitorScroller extends React.Component {
<div
className={styles.listValue}
dataIndex={index}
style={{background: this.props.categoryColor}}
style={{
background: this.props.categoryColor.background,
color: this.props.categoryColor.text
}}
onClick={this.props.draggable ? this.handleEventFactory(index) : null}
>
{this.props.draggable && this.props.activeIndex === index ? (
@@ -51,6 +54,7 @@ class ListMonitorScroller extends React.Component {
autoComplete={false}
className={classNames(styles.listInput, 'no-drag')}
spellCheck={false}
style={{color: this.props.categoryColor.text}}
type="text"
value={this.props.activeValue}
onBlur={this.props.onDeactivate}
@@ -97,7 +101,10 @@ class ListMonitorScroller extends React.Component {
ListMonitorScroller.propTypes = {
activeIndex: PropTypes.number,
activeValue: PropTypes.string,
categoryColor: PropTypes.string,
categoryColor: PropTypes.shape({
background: PropTypes.string.isRequired,
text: PropTypes.string.isRequired
}).isRequired,
draggable: PropTypes.bool,
height: PropTypes.number,
onActivate: PropTypes.func,
+4 -1
View File
@@ -54,7 +54,10 @@ const ListMonitor = ({draggable, label, width, height, value, onResizeMouseDown,
ListMonitor.propTypes = {
activeIndex: PropTypes.number,
categoryColor: PropTypes.string.isRequired,
categoryColor: PropTypes.shape({
background: PropTypes.string.isRequired,
text: PropTypes.string.isRequired
}).isRequired,
draggable: PropTypes.bool.isRequired,
height: PropTypes.number,
label: PropTypes.string.isRequired,
+5 -10
View File
@@ -4,7 +4,7 @@
.monitor-container {
position: absolute;
background: $ui-primary-light; /* tw: do not look different in dark mode */
background: $ui-primary;
z-index: $z-index-monitor;
border: 1px solid $ui-black-transparent-default;
border-radius: calc($space / 2);
@@ -43,7 +43,6 @@
align-items: center;
min-width: 40px;
text-align: center;
color: white;
margin: 0 5px;
border-radius: calc($space / 2);
padding: 0 2px;
@@ -56,7 +55,6 @@
min-width: 3rem;
padding: 0.1rem 0.25rem;
text-align: center;
color: white;
font-size: 1rem;
white-space: pre-wrap;
transform: translateZ(0); /* Fixes flickering in Safari */
@@ -84,12 +82,12 @@
padding: 3px;
font-size: 0.75rem;
font-weight: bold;
color: $text-primary-light; /* tw: do not look different in dark mode */
color: $text-primary;
width: 100%;
}
.list-body {
background: $ui-primary-light; /* tw: do not look different in dark mode */
background: $ui-primary;
width: 100%;
display: flex;
flex-direction: column;
@@ -109,14 +107,13 @@
.list-index {
font-weight: bold;
color: $text-primary-light; /* tw: do not look different in dark mode */
color: $text-primary;
margin: 0 3px;
}
.list-value {
min-width: 40px;
text-align: left;
color: white;
margin: 0 3px;
border-radius: calc($space / 2);
border: 1px solid $ui-black-transparent-default;
@@ -133,7 +130,7 @@
font-size: 0.75rem;
font-weight: bold;
border-top: 1px solid $ui-black-transparent-default;
color: $text-primary-light; /* tw: do not look different in dark mode */
color: $text-primary; /* tw: do not look different in dark mode */
}
.list-empty {
@@ -163,7 +160,6 @@
padding: 3px 5px;
border: 0;
background: none;
color: $ui-white;
outline: none;
font-size: 0.75rem;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
@@ -176,7 +172,6 @@
padding: 0;
padding-right: 5px;
cursor: pointer;
color: $ui-white;
}
.add-button {
+22 -13
View File
@@ -10,17 +10,19 @@ import DefaultMonitor from './default-monitor.jsx';
import LargeMonitor from './large-monitor.jsx';
import SliderMonitor from '../../containers/slider-monitor.jsx';
import ListMonitor from '../../containers/list-monitor.jsx';
import {getColorsForTheme} from '../../lib/themes/index.js';
import styles from './monitor.css';
const categories = {
data: '#FF8C1A',
sensing: '#5CB1D6',
sound: '#CF63CF',
looks: '#9966FF',
motion: '#4C97FF',
list: '#FC662C',
extension: '#0FBD8C'
// Map category name to color name used in scratch-blocks Blockly.Colours
const categoryColorMap = {
data: 'data',
sensing: 'sensing',
sound: 'sounds',
looks: 'looks',
motion: 'motion',
list: 'data_lists',
extension: 'pen'
};
const modes = {
@@ -30,6 +32,14 @@ const modes = {
list: ListMonitor
};
const getCategoryColor = (theme, category) => {
const colors = getColorsForTheme(theme);
return {
background: colors[categoryColorMap[category]].primary,
text: colors.text
};
};
const MonitorComponent = props => (
<ContextMenuTrigger
// TW: if export is defined, we always show it, even outside of the editor
@@ -52,7 +62,7 @@ const MonitorComponent = props => (
data-opcode={props.opcode}
>
{React.createElement(modes[props.mode], {
categoryColor: categories[props.category],
categoryColor: getCategoryColor(props.theme, props.category),
...props
})}
</Box>
@@ -125,12 +135,10 @@ const MonitorComponent = props => (
);
MonitorComponent.categories = categories;
const monitorModes = Object.keys(modes);
MonitorComponent.propTypes = {
category: PropTypes.oneOf(Object.keys(categories)),
category: PropTypes.oneOf(Object.keys(categoryColorMap)),
componentRef: PropTypes.func.isRequired,
draggable: PropTypes.bool.isRequired,
id: PropTypes.string.isRequired,
@@ -145,7 +153,8 @@ MonitorComponent.propTypes = {
onSetModeToDefault: PropTypes.func,
onSetModeToLarge: PropTypes.func,
onSetModeToSlider: PropTypes.func,
onSliderPromptOpen: PropTypes.func
onSliderPromptOpen: PropTypes.func,
theme: PropTypes.string.isRequired
};
MonitorComponent.defaultProps = {
+8 -2
View File
@@ -12,7 +12,10 @@ const SliderMonitor = ({categoryColor, isDiscrete, label, min, max, value, onSli
</div>
<div
className={styles.value}
style={{background: categoryColor}}
style={{
background: categoryColor.background,
color: categoryColor.text
}}
>
{value}
</div>
@@ -33,7 +36,10 @@ const SliderMonitor = ({categoryColor, isDiscrete, label, min, max, value, onSli
);
SliderMonitor.propTypes = {
categoryColor: PropTypes.string.isRequired,
categoryColor: PropTypes.shape({
background: PropTypes.string.isRequired,
text: PropTypes.string.isRequired
}).isRequired,
isDiscrete: PropTypes.bool,
label: PropTypes.string.isRequired,
max: PropTypes.number,
@@ -0,0 +1,9 @@
.nineties-mode {
position: absolute;
width: 100%;
height: 100%;
background: transparent url('./shapes.svg');
z-index: 50;
opacity: .2;
pointer-events: none;
}
@@ -0,0 +1,23 @@
import PropTypes from 'prop-types';
import React from 'react';
import styles from './nineties-mode.css';
import {connect} from 'react-redux';
import {isTimeTravel1990} from '../../reducers/time-travel';
const NinetiesMode = props => {
if (!props.show) return null;
return <div className={styles.ninetiesMode} />;
};
NinetiesMode.propTypes = {
show: PropTypes.bool
};
const mapStateToProps = state => ({
// This is the button's mode, as opposed to the actual current state
show: isTimeTravel1990(state)
});
export default connect(
mapStateToProps
)(NinetiesMode);
+1
View File
@@ -0,0 +1 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="453.58105" height="339.14542" viewBox="0,0,453.58105,339.14542"><g transform="translate(-3.82096,-1.28768)"><g data-paper-data="{&quot;isPaintingLayer&quot;:true}" fill-rule="nonzero" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" style="mix-blend-mode: normal"><path d="M151.40202,81.94226c0,10.17727 -8.25031,18.42759 -18.42759,18.42759c-10.17728,0 -18.42758,-8.25031 -18.42758,-18.42759c0,-10.17727 8.25031,-18.42759 18.42758,-18.42759c10.17728,0 18.42759,8.25031 18.42759,18.42759z" fill="#000000" stroke="#000000" stroke-width="0" stroke-linecap="butt"/><path d="M150.11,80.16574c0,10.45342 -8.47417,18.92759 -18.92759,18.92759c-10.45342,0 -18.92759,-8.47417 -18.92759,-18.92759c0,-10.45342 8.47417,-18.92759 18.92759,-18.92759c10.45342,0 18.92759,8.47417 18.92759,18.92759z" fill="#ac83ff" stroke="#000000" stroke-width="0" stroke-linecap="butt"/><path d="M135.60486,158.54925l14.32795,-7.67569l-3.07028,-21.49192l17.39822,10.74596l7.16397,-11.76938l7.16397,11.76939l14.83966,-7.67568l-0.51172,15.35137l15.86309,5.62884l8.5314,3.45171l-19.72574,7.33393l8.5984,19.21996l-20.99022,-9.35709l-1.01158,14.41497l-8.3455,-14.41497l-14.16208,14.92075l-0.50579,-18.20838l-16.69102,8.09261l7.83972,-15.42655z" fill="#000000" stroke="#f3f08f" stroke-width="0" stroke-linecap="round"/><path d="M130.85777,153.54925l14.32795,-7.67569l-3.07028,-21.49192l17.39822,10.74596l7.16397,-11.76938l7.16397,11.76939l14.83966,-7.67569l-0.51172,15.35137l15.86309,5.62884l8.5314,3.45171l-19.72574,7.33393l8.5984,19.21996l-20.99022,-9.35709l-1.01158,14.41497l-8.3455,-14.41497l-14.16208,14.92075l-0.50579,-18.20838l-16.69102,8.09261l7.83972,-15.42655z" fill="#f3f08f" stroke="#f3f08f" stroke-width="0.5" stroke-linecap="round"/><g fill="#000000" stroke="none" stroke-width="0" stroke-linecap="butt"><path d="M205.68026,240.55048l12.09111,-30.27749l5.16652,2.34355c0,0 -1.25391,2.67795 -2.11553,4.83555c-2.03374,5.0927 -9.97558,25.44194 -9.97558,25.44194z"/><path d="M217.94432,210.15941l24.04307,9.07595l-1.76964,4.10689c0,0 -2.12929,-0.94814 -3.84262,-1.5949c-4.04407,-1.52658 -20.20045,-7.48104 -20.20045,-7.48104z"/><path d="M251.19347,191.96892l-10.65459,31.49393l-5.38944,-2.10355c0,0 1.13023,-2.79564 1.88949,-5.03992c1.79211,-5.29731 8.76511,-26.454 8.76511,-26.454z"/><path d="M275.23943,204.09497l-30.83675,-8.84898l1.78443,-5.28963c0,0 2.74562,0.96329 4.94308,1.59387c5.18677,1.48841 25.89367,7.25511 25.89367,7.25511z"/><path d="M287.72415,174.16786l-12.35728,29.0723l-4.9549,-2.38018c0,0 1.27168,-2.56743 2.15227,-4.63915c2.0785,-4.88999 10.20501,-24.43315 10.20501,-24.43315z"/></g><g fill="#79cae0" stroke="none" stroke-width="0" stroke-linecap="butt"><path d="M204.53741,237.05048l12.09111,-30.27749l5.16652,2.34355c0,0 -1.25391,2.67795 -2.11553,4.83555c-2.03374,5.0927 -9.97558,25.44194 -9.97558,25.44194z"/><path d="M216.80146,206.65941l24.04307,9.07595l-1.76964,4.10689c0,0 -2.12929,-0.94814 -3.84262,-1.5949c-4.04407,-1.52658 -20.20045,-7.48104 -20.20045,-7.48104z"/><path d="M250.05062,188.46892l-10.65459,31.49393l-5.38944,-2.10355c0,0 1.13023,-2.79564 1.88949,-5.03992c1.79211,-5.29731 8.76511,-26.454 8.76511,-26.454z"/><path d="M274.09657,200.59497l-30.83675,-8.84898l1.78444,-5.28963c0,0 2.74562,0.96329 4.94308,1.59387c5.18677,1.48841 25.89367,7.25511 25.89367,7.25511z"/><path d="M286.58128,170.66786l-12.35728,29.0723l-4.9549,-2.38018c0,0 1.27168,-2.56743 2.15227,-4.63915c2.0785,-4.88999 10.20501,-24.43315 10.20501,-24.43315z"/></g><path d="M261.402,102.17879c0,7.8375 -6.35355,14.19106 -14.19106,14.19106c-7.83751,0 -14.19105,-6.35355 -14.19105,-14.19106c0,-7.8375 6.35355,-14.19106 14.19105,-14.19106c7.83751,0 14.19106,6.35355 14.19106,14.19106z" fill="#000000" stroke="#000000" stroke-width="0" stroke-linecap="butt"/><path d="M259.63694,101.19574c0,7.83751 -6.35355,14.19106 -14.19106,14.19106c-7.83751,0 -14.19106,-6.35355 -14.19106,-14.19106c0,-7.83751 6.35355,-14.19106 14.19106,-14.19106c7.83751,0 14.19106,6.35355 14.19106,14.19106z" fill="#79cae0" stroke="#000000" stroke-width="0" stroke-linecap="butt"/><path d="M83.00001,250.50002v-49h50v49z" fill="#000000" stroke="none" stroke-width="0" stroke-linecap="butt"/><path d="M80.00001,247.50002v-49h50v49z" fill="#f79a96" stroke="none" stroke-width="0" stroke-linecap="butt"/><g stroke="none" stroke-width="0" stroke-linecap="round"><path d="M163.94596,39.50782l12.33918,-5.92173l-2.64411,-16.58084l14.98329,8.29042l6.16959,-9.07998l6.16959,9.07999l12.77987,-5.92172l-0.4407,11.84346l13.66125,4.3426l7.34722,2.66297l-16.98775,5.65807l7.40492,14.82804l-18.07671,-7.21891l-0.87117,11.12103l-7.18712,-11.12103l-12.19634,11.51124l-0.43559,-14.04761l-14.37425,6.24338l6.75154,-11.90145z" fill="#000000"/><path d="M159.85777,35.65037l12.33919,-5.92174l-2.64411,-16.58084l14.98329,8.29042l6.16959,-9.07998l6.16959,9.07999l12.77987,-5.92173l-0.4407,11.84346l13.66125,4.3426l7.34721,2.66297l-1Line truncated

After

Width:  |  Height:  |  Size: 13 KiB

@@ -0,0 +1,9 @@
.oldtimey-mode {
position: absolute;
width: 100%;
height: 100%;
background: transparent url('./oldtimey.png') center center;
background-size: 100% 100%;
z-index: 500;
pointer-events: none;
}
@@ -0,0 +1,35 @@
import PropTypes from 'prop-types';
import React from 'react';
import styles from './oldtimey-mode.css';
import oldtimeySound from './projector2.mp3';
import {connect} from 'react-redux';
import {isTimeTravel1920} from '../../reducers/time-travel';
const OldTimeyMode = props => {
if (!props.show) return null;
return (
<div className={styles.oldtimeyMode}>
<audio
src={oldtimeySound}
ref={audio => {
audio && (audio.volume = 0.1); // eslint-disable-line no-unused-expressions
}}
autoPlay
loop
/>
</div>
);
};
OldTimeyMode.propTypes = {
show: PropTypes.bool
};
const mapStateToProps = state => ({
// This is the button's mode, as opposed to the actual current state
show: isTimeTravel1920(state)
});
export default connect(
mapStateToProps
)(OldTimeyMode);
Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

@@ -0,0 +1,49 @@
.prehistoric-mode {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
.prehistoric-background{
position: absolute;
width: 100%;
height: 100%;
opacity: 90%;
z-index: 50;
pointer-events: none;
background: transparent url('./background.png') center center;
animation:jitter 2.5s linear infinite;
}
@keyframes jitter{
0%{transform:scale(1.0385841106898348);}
5% {transform:scale(1.045596657989466);}
10% {transform:scale(1.029923116529907);}
15% {transform:scale(1.059010318189378);}
20% {transform:scale(1.0868915738701577);}
25% {transform:scale(1.0771183924324506);}
30% {transform:scale(1.0780695942284886);}
35% {transform:scale(1.081268381642509);}
40% {transform:scale(1.0446864380239975);}
45% {transform:scale(1.0822226558561991);}
50% {transform:scale(1.0703574969484024);}
55% {transform:scale(1.0131626458532954);}
60% {transform:scale(1.0638351688496082);}
65% {transform:scale(1.0254684750877292);}
70% {transform:scale(1.0409969735315965);}
75% {transform:scale(1.029863208069294);}
80% {transform:scale(1.043175299252114);}
85% {transform:scale(1.022196668538089);}
90% {transform:scale(1.042465864842523);}
95% {transform:scale(1.04090035265691);}
100% {transform:scale(1.0385841106898348);}
}
.torch {
position: absolute;
pointer-events: none;
opacity: 80%;
z-index: 50;
}
@@ -0,0 +1,68 @@
import PropTypes from 'prop-types';
import React from 'react';
import bindAll from 'lodash.bindall';
import styles from './prehistoric-mode.css';
import {connect} from 'react-redux';
import {isTimeTravel220022BC} from '../../reducers/time-travel';
import torch from './torch.gif';
class PrehistoricMode extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleMouseMove'
]);
this.state = {
x: window.innerWidth / 2,
y: window.innerHeight / 2
};
}
componentDidMount () {
document.addEventListener('pointermove', this.handleMouseMove);
}
componentWillUnmount () {
document.removeEventListener('pointermove', this.handleMouseMove);
}
handleMouseMove (event) {
this.setState({x: event.clientX, y: event.clientY});
}
render () {
if (!this.props.show) return null;
return (
<div
className={styles.prehistoricMode}
>
<div
className={styles.prehistoricBackground}
style={{
backgroundPosition: `${this.state.x + 2000}px ${this.state.y + 2000}px`
}}
/><img
className={styles.torch}
style={{
left: `${this.state.x - 35}px`,
top: `${this.state.y - 220}px`
}}
src={torch}
/>
</div>
);
}
}
PrehistoricMode.propTypes = {
show: PropTypes.bool
};
const mapStateToProps = state => ({
// This is the button's mode, as opposed to the actual current state
show: isTimeTravel220022BC(state)
});
export default connect(
mapStateToProps
)(PrehistoricMode);
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@@ -0,0 +1,18 @@
@import "../../css/colors.css";
.progress-ring {
margin: 0.5rem;
}
.progress-ring-svg {
fill: transparent;
}
.progress-ring .progress-ring-ring {
stroke: $looks-light-transparent;
}
.progress-ring .progress-ring-value {
stroke: $looks-secondary;
stroke-linecap: round;
}
@@ -0,0 +1,81 @@
/**
* This progress ring is like a progress bar (`<progress />`), but circular.
* Like `<progress />`, it has a `value` prop and a `max` prop. The `value`
* prop is the current progress, and the `max` prop is the maximum progress.
* The `value` prop can be any number between 0 and `max`, inclusive.
* The `max` prop can be any number greater than 0, and defaults to 1.
* Indeterminate progress is not supported.
* The required `sizePx` prop is the outer diameter of the ring, in pixels.
* The optional `strokeWidthPx` prop is the width of the ring, in pixels.
*/
import PropTypes from 'prop-types';
import React from 'react';
import classNames from 'classnames';
import styles from './progress-ring.css';
const ProgressRingComponent = ({
className,
max = 1,
sizePx,
strokeWidthPx,
value,
...props
}) => {
if (typeof strokeWidthPx === 'undefined') {
strokeWidthPx = sizePx / 6;
}
const center = sizePx / 2;
const diameter = (sizePx - strokeWidthPx);
const radius = diameter / 2;
const circumference = Math.PI * diameter;
const offset = circumference * (1 - (value / max));
const ringSvgProps = {
cx: center,
cy: center,
r: radius
};
return (
<div
className={classNames(styles.progressRing, className)}
style={{width: sizePx, height: sizePx}}
{...props}
>
<svg
className={classNames(styles.progressRingSvg, className)}
viewBox={`0 0 ${sizePx} ${sizePx}`}
>
<circle
className={styles.progressRingRing}
{...ringSvgProps}
style={{
strokeWidth: `${strokeWidthPx}px`
}}
/>
<circle
className={styles.progressRingValue}
{...ringSvgProps}
style={{
strokeDasharray: circumference,
strokeDashoffset: offset,
strokeWidth: `${strokeWidthPx}px`
}}
transform={`rotate(-90 ${center} ${center})`}
/>
</svg>
</div>
);
};
ProgressRingComponent.propTypes = {
className: PropTypes.string,
max: PropTypes.number, // default = 1
sizePx: PropTypes.number.isRequired,
strokeWidthPx: PropTypes.number, // default = sizePx/6
value: PropTypes.number.isRequired
};
export default ProgressRingComponent;
+2 -2
View File
@@ -72,8 +72,8 @@
}
.button-row button.ok-button {
background: $motion-primary;
border: $motion-primary;
background: $looks-secondary;
border: $looks-secondary;
color: white;
}
+3 -14
View File
@@ -14,7 +14,7 @@
font-size: 0.75rem;
font-weight: bold;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
color: $text-primary-light;
color: $text-primary;
padding-bottom: $space;
}
@@ -23,17 +23,6 @@
position: relative;
}
/* tw: input should not look different in dark mode */
[theme="dark"] .question-input input {
color: $text-primary-light;
background: $ui-secondary-light;
border-color: $ui-tertiary-light;
}
[theme="dark"] .question-input input:hover,
[theme="dark"] .question-input input:focus {
border-color: $motion-primary;
}
.question-submit-button {
position: absolute;
top: calc($space / 2);
@@ -45,7 +34,7 @@
border-radius: 100%;
color: white;
background: $motion-primary;
background: $looks-secondary;
}
[dir="ltr"] .question-submit-button {
@@ -71,7 +60,7 @@
}
.question-input > input:focus {
box-shadow: 0px 0px 0px 3px $motion-transparent;
box-shadow: 0px 0px 0px 3px $looks-transparent;
}
.question-submit-button-icon {
+1 -1
View File
@@ -5,7 +5,7 @@
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Desktop---1280x720" transform="translate(-446.000000, -454.000000)" fill="#4C97FF">
<g id="Desktop---1280x720" transform="translate(-446.000000, -454.000000)" fill="#855CD6">
<g id="Step-4---Playing-Trim" transform="translate(0.000000, 42.000000)">
<g id="Re-record" transform="translate(434.000000, 400.000000)">
<path d="M23.6951773,18.0886272 L20.5085204,21.2806841 C20.2691162,21.5146883 19.9451104,21.6466907 19.6085044,21.6466907 C19.2730984,21.6466907 18.9490926,21.5146883 18.7084883,21.2806841 L15.5230315,18.0886272 C15.157025,17.7226207 15.049023,17.1766109 15.2470266,16.7026025 C15.4450301,16.228594 15.9010382,15.9225886 16.4170474,15.9225886 L17.5630679,15.9225886 C17.5330673,15.6465836 17.455066,15.3345781 17.3230636,15.0045722 C17.2816629,14.9085705 17.2390621,14.8125688 17.1910612,14.716567 C17.1250601,14.6085651 17.1316602,14.5785646 17.0410586,14.4465622 C16.897056,14.2305584 16.7650536,14.0685555 16.6084509,13.8825522 C16.2910452,13.5405461 15.9070383,13.2465408 15.4990311,13.030537 C15.0850237,12.8145331 14.653016,12.6825308 14.2690091,12.6105295 C13.8910024,12.5445283 13.5429962,12.5385282 13.3389925,12.5385282 C13.2369907,12.5325281 13.1049884,12.5565285 13.0389872,12.5625286 C12.9669859,12.5685287 12.9249852,12.5745288 12.9249852,12.5745288 C12.4989776,12.6165296 12.1149707,12.304524 12.07297,11.8785164 C12.0369693,11.51851 12.2469731,11.1945042 12.5649787,11.0745021 C12.5649787,11.0745021 12.6069795,11.0565018 12.6729807,11.0325013 C12.750982,11.0085009 12.8229833,10.9665001 12.9909863,10.9184993 C13.3269923,10.8164975 13.7529999,10.7084955 14.3110099,10.6544946 C14.8630197,10.6064937 15.5290316,10.6184939 16.2316441,10.7624965 C16.9330566,10.9124992 17.6710698,11.1945042 18.355082,11.6025115 C18.6790878,11.8125152 19.027094,12.0585196 19.2970988,12.2985239 C19.417101,12.3885255 19.6211046,12.5925292 19.7411067,12.7185314 C19.8791092,12.862534 20.0051114,13.0065365 20.1317137,13.1565392 C20.6171224,13.7565499 20.9771288,14.4045615 21.211133,14.992572 C21.3491354,15.328578 21.4451371,15.6465836 21.5171384,15.9225886 L22.8011613,15.9225886 C23.3171705,15.9225886 23.7731787,16.228594 23.9711822,16.7026025 C24.1691857,17.1766109 24.0611838,17.7226207 23.6951773,18.0886272" id="re-record" transform="translate(18.068963, 16.137926) scale(-1, 1) rotate(-45.000000) translate(-18.068963, -16.137926) "></path>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

+1 -1
View File
@@ -18,7 +18,7 @@
<g id="Play-/-Record-/-Stop" transform="translate(623.000000, 307.000000)">
<g id="play" transform="translate(21.849131, 22.002656) rotate(90.000000) translate(-21.849131, -22.002656) ">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use stroke="#4280D7" stroke-width="1" fill="#4C97FF" fill-rule="evenodd" xlink:href="#path-1"></use>
<use stroke="#6736B5" stroke-width="1" fill="#855CD6" fill-rule="evenodd" xlink:href="#path-1"></use>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -18,11 +18,11 @@
<g id="Play-/-Record-/-Stop" transform="translate(623.000000, 307.000000)">
<g id="stop-playback">
<use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
<use fill="#4C97FF" fill-rule="evenodd" xlink:href="#path-1"></use>
<rect stroke="#4280D7" stroke-width="1" x="0.5" y="0.5" width="43" height="43" rx="4"></rect>
<use fill="#855CD6" fill-rule="evenodd" xlink:href="#path-1"></use>
<rect stroke="#6736B5" stroke-width="1" x="0.5" y="0.5" width="43" height="43" rx="4"></rect>
</g>
</g>
</g>
</g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

+6 -5
View File
@@ -50,7 +50,7 @@
}
.playing-text {
color: $motion-primary;
color: $looks-secondary;
}
.recording-text {
@@ -92,12 +92,12 @@
border: 1px solid $ui-black-transparent;
font-weight: 600;
font-size: 0.85rem;
color: $motion-primary;
color: $looks-secondary;
}
.button-row button.ok-button {
background: $motion-primary;
border: $motion-primary;
background: $looks-secondary;
border: $looks-secondary;
color: white;
}
@@ -109,7 +109,8 @@
text-align: center;
}
.record-button {
/* Increase specificity to make sure this wins out over normalize.css */
svg.record-button {
overflow: visible;
}
@@ -49,8 +49,8 @@
}
.button-row button.ok-button {
background: $motion-primary;
border: $motion-primary;
background: $looks-secondary;
border: $looks-secondary;
color: white;
}
+34 -25
View File
@@ -83,9 +83,24 @@ $border-radius: 0.25rem;
border: 1px solid $ui-black-transparent;
cursor: pointer;
font-size: 0.85rem;
transition: 0.2s;
user-select: none;
margin: 0px;
position: relative;
}
.button:active {
background: $looks-transparent;
}
.button:focus::before {
content: "";
position: absolute;
top: -1px;
right: -1px;
bottom: -1px;
left: -1px;
border: 1px solid $looks-secondary;
z-index: 1;
}
[theme="dark"] .button {
background-color: $ui-primary;
@@ -108,7 +123,7 @@ $border-radius: 0.25rem;
height: 3rem;
width: 3rem;
outline: none;
background: $motion-primary;
background: $looks-secondary;
border-radius: 100%;
border: 4px solid $ui-white-dim;
cursor: pointer;
@@ -213,33 +228,27 @@ $border-radius: 0.25rem;
border-radius: 0;
}
[dir="ltr"] .button-group .button {
border-left: none;
}
[dir="rtl"] .button-group .button {
.button:not(:last-child) {
border-right: none;
}
[dir="ltr"] .button-group .button:last-of-type {
border-top-right-radius: $border-radius;
border-bottom-right-radius: $border-radius;
}
[dir="ltr"] .button-group .button:first-of-type {
border-left: 1px solid $ui-black-transparent;
border-top-left-radius: $border-radius;
border-bottom-left-radius: $border-radius;
}
[dir="rtl"] .button-group .button:last-of-type {
border-top-left-radius: $border-radius;
border-bottom-left-radius: $border-radius;
}
[dir="rtl"] .button-group .button:first-of-type {
[dir="rtl"] .button:not(:last-child) {
border-left: none;
border-right: 1px solid $ui-black-transparent;
border-top-right-radius: $border-radius;
border-bottom-right-radius: $border-radius;
}
.button-group .button:first-child,
[dir="rtl"] .button-group .button:last-child,
.button-group .button:first-child:focus::before,
[dir="rtl"] .button-group .button:last-child:focus::before {
border-radius: $form-radius 0 0 $form-radius;
}
.button-group .button:last-child,
[dir="rtl"] .button-group .button:first-child,
.button-group .button:last-child:focus::before,
[dir="rtl"] .button-group .button:first-child:focus::before {
border-radius: 0 $form-radius $form-radius 0;
}
.button:disabled > img {
Loaded 100 of 199 files, more files were not shown because too many files have changed in this diff. Show more