Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a588880bf | ||
|
|
e2c2392a70 | ||
|
|
5160b5ae22 | ||
|
|
8be847b68a | ||
|
|
e7ed24f1ef | ||
|
|
13e324b52d | ||
|
|
5c7b221799 | ||
|
|
7398a30223 | ||
|
|
637b9fd225 | ||
|
|
88442708c2 | ||
|
|
d7759317c4 | ||
|
|
fd3045eb9d | ||
|
|
09ae22f329 | ||
|
|
365e3b3bce | ||
|
|
550849c93f | ||
|
|
8128902cd6 | ||
|
|
b350049ca7 | ||
|
|
e0e07929e8 | ||
|
|
128b196220 | ||
|
|
5b806dff27 | ||
|
|
9c5d358c3e | ||
|
|
ab89ee1a4f | ||
|
|
dc82e5f653 | ||
|
|
7fb3a81a66 | ||
|
|
d958b14b8d | ||
|
|
0a6c75eb93 | ||
|
|
7c5a4fc682 | ||
|
|
ca8caa8504 | ||
|
|
339dfe0b08 | ||
|
|
b9cefd325b | ||
|
|
fa974c68bd | ||
|
|
3c47e0e040 | ||
|
|
34a9a97201 | ||
|
|
f6847e3aea | ||
|
|
e27021cbf4 | ||
|
|
3dc6413ea4 | ||
|
|
53c04a1ad9 | ||
|
|
b2486380fb | ||
|
|
9f41ddaaa2 | ||
|
|
ef4d8c6e8d | ||
|
|
a6245b87f6 | ||
|
|
fa4541e85c | ||
|
|
376f5626a4 | ||
|
|
e17dc90983 | ||
|
|
a63385343a | ||
|
|
3c3206d8e2 | ||
|
|
f6c8feae6e | ||
|
|
98a007a17e | ||
|
|
3e0d5b9ede | ||
|
|
555082bb1d | ||
|
|
5c95c352c3 | ||
|
|
3b4758ca5c | ||
|
|
699671bde5 | ||
|
|
7facbf35b2 | ||
|
|
b54fe54b4a | ||
|
|
796339dcfe | ||
|
|
1721a322c2 | ||
|
|
3125097b83 | ||
|
|
aa0c40fca1 | ||
|
|
2d61027bbe | ||
|
|
2dac3cae81 | ||
|
|
43d3df2ffd | ||
|
|
44ffaeee2c | ||
|
|
0bf4e2c781 | ||
|
|
d342b82219 | ||
|
|
227581ebcf | ||
|
|
eb74d6c033 | ||
|
|
a2035a68e4 | ||
|
|
1b055af545 | ||
|
|
5b79947178 | ||
|
|
9e329b999e | ||
|
|
e5e0c3dd28 | ||
|
|
e810422429 | ||
|
|
8b2c590309 | ||
|
|
d92ef0c807 | ||
|
|
faa14b7add | ||
|
|
abbc8ddad0 | ||
|
|
551e03b256 | ||
|
|
f65f80160f | ||
|
|
5438c14bdb | ||
|
|
8f3d84ec60 | ||
|
|
c3df930bea | ||
|
|
4513a45306 | ||
|
|
a94a83dc4f | ||
|
|
e1e5c8352c | ||
|
|
98ceb00bb4 | ||
|
|
77974839b0 | ||
|
|
706341011a | ||
|
|
4a26810956 | ||
|
|
92fd318aa8 | ||
|
|
bc2555ddc4 | ||
|
|
8ee9794304 | ||
|
|
30388ffa4f | ||
|
|
fdea0a1620 | ||
|
|
41e2909782 | ||
|
|
4048ed8c84 | ||
|
|
7a21b17d0c | ||
|
|
72bc6f3005 | ||
|
|
a8a7e0c92f | ||
|
|
a11fcfe373 | ||
|
|
52a3600819 | ||
|
|
bec6223e6e | ||
|
|
333b91ccfd | ||
|
|
a3266fe641 | ||
|
|
10449a21b5 | ||
|
|
29f5d2a872 | ||
|
|
f8b3e4961c | ||
|
|
cca701e954 | ||
|
|
89c0807cba | ||
|
|
544a4c745d | ||
|
|
f6e9a7dd23 | ||
|
|
69132b6f43 | ||
|
|
aa88e2c6a6 | ||
|
|
b89734b0e9 | ||
|
|
d5eb058d00 | ||
|
|
fe308b6b58 | ||
|
|
ab14942c80 | ||
|
|
908cb2e5c6 | ||
|
|
6935aa0820 | ||
|
|
4ec8ac16a8 | ||
|
|
bfe2877d0b | ||
|
|
9d50e9a165 | ||
|
|
eb496cfe75 | ||
|
|
dfd01da913 | ||
|
|
c8bdb6ce09 | ||
|
|
3169c03e66 | ||
|
|
26323cccf2 | ||
|
|
2bb9a61539 | ||
|
|
4efd3fcbad | ||
|
|
170705440a | ||
|
|
a52fdaa032 | ||
|
|
6d972da227 | ||
|
|
96bd677ef9 | ||
|
|
b77bc7a8b4 | ||
|
|
7f06f55f83 | ||
|
|
04381439c2 | ||
|
|
7b658c60c7 | ||
|
|
0c68103eb6 | ||
|
|
65c787d0da | ||
|
|
0b40daf936 | ||
|
|
75607ebbe3 | ||
|
|
58b178f679 | ||
|
|
ffeebb03bd | ||
|
|
7c1f0d2739 | ||
|
|
ac0c6a4305 | ||
|
|
ff776dc90c | ||
|
|
516baedad9 | ||
|
|
d83ec5a0b5 | ||
|
|
1c8ba1d1eb | ||
|
|
16024769c8 | ||
|
|
ed9316266e | ||
|
|
103f55bd6e | ||
|
|
009d9db74f | ||
|
|
416bc85455 | ||
|
|
88a5ab8052 | ||
|
|
31454f7ca0 | ||
|
|
bfd0712c83 | ||
|
|
3c9d482a02 | ||
|
|
5c3694ec19 | ||
|
|
997abfeb27 | ||
|
|
df77245f0d | ||
|
|
5e2ea2ee64 | ||
|
|
655af4ed31 | ||
|
|
455808fe26 | ||
|
|
c2dbdc6279 | ||
|
|
327983fc8c | ||
|
|
93477a19d6 | ||
|
|
a87b08d791 | ||
|
|
866af2f766 | ||
|
|
656e8c2c77 |
No files matched your search
@@ -30,29 +30,38 @@ Then go to [http://localhost:8601/](http://localhost:8601/) - the playground out
|
||||
|
||||
## Developing alongside other Scratch repositories
|
||||
|
||||
### Linking this code to another project's `node_modules/scratch-gui`
|
||||
### Getting another repo to point to this code
|
||||
|
||||
|
||||
If you wish to develop `scratch-gui` alongside other scratch repositories that depend on it, you may wish
|
||||
to have the other repositories use your local `scratch-gui` build instead of fetching the current production
|
||||
version of the scratch-gui that is found by default using `npm install`.
|
||||
|
||||
Here's how to link your local `scratch-gui` code to another project's `node_modules/scratch-gui`.
|
||||
|
||||
#### Configuration
|
||||
|
||||
If you wish to develop scratch-gui alongside other scratch repositories that depend on it, you may wish
|
||||
to have the other repositories use your local scratch-gui build instead of fetching the current production
|
||||
version of the scratch-gui that is found by default using `npm install`.
|
||||
1. In your local `scratch-gui` repository's top level:
|
||||
1. Make sure you have run `npm install`
|
||||
2. Build the `dist` directory by running `BUILD_MODE=dist npm run build`
|
||||
3. Establish a link to this repository by running `npm link`
|
||||
|
||||
To do this:
|
||||
1. Make sure you have run `npm install` from this (scratch-gui) repository's top level
|
||||
2. Make sure you have run `npm install` from the top level of each repository (such as scratch-www) that depends on scratch-gui
|
||||
3. From this (scratch-gui) repository's top level, build the `dist` directory by running `BUILD_MODE=dist npm run build`
|
||||
4. From this (scratch-gui) repository's top level, establish a link to this repository by running `npm link`
|
||||
5. From the top level of each repository that depends on scratch-gui, run `npm link scratch-gui`
|
||||
6. Build or run the repositories that depend on scratch-gui
|
||||
2. From the top level of each repository (such as `scratch-www`) that depends on `scratch-gui`:
|
||||
1. Make sure you have run `npm install`
|
||||
2. Run `npm link scratch-gui`
|
||||
3. Build or run the repositoriy
|
||||
|
||||
Instead of `BUILD_MODE=dist npm run build` you can also use `BUILD_MODE=dist npm run watch`, however this may be unreliable.
|
||||
#### Using `npm run watch`
|
||||
|
||||
Instead of `BUILD_MODE=dist npm run build`, you can use `BUILD_MODE=dist npm run watch` instead. This will watch for changes to your `scratch-gui` code, and automatically rebuild when there are changes. Sometimes this has been unreliable; if you are having problems, try going back to `BUILD_MODE=dist npm run build` until you resolve them.
|
||||
|
||||
#### Oh no! It didn't work!
|
||||
* Follow the recipe above step by step and don't change the order. It is especially important to run npm first because installing after the linking will reset the linking.
|
||||
* Make sure the repositories are siblings on your machine's file tree.
|
||||
* If you have multiple Terminal tabs or windows open for the different Scratch repositories, make sure to use the same node version in all of them.
|
||||
* In the worst case unlink the repositories by running `npm unlink` in both, and start over.
|
||||
|
||||
If you can't get linking to work right, try:
|
||||
* Follow the recipe above step by step and don't change the order. It is especially important to run `npm install` _before_ `npm link`, because installing after the linking will reset the linking.
|
||||
* Make sure the repositories are siblings on your machine's file tree, like `.../.../MY_SCRATCH_DEV_DIRECTORY/scratch-gui/` and `.../.../MY_SCRATCH_DEV_DIRECTORY/scratch-www/`.
|
||||
* Consistent node.js version: If you have multiple Terminal tabs or windows open for the different Scratch repositories, make sure to use the same node version in all of them.
|
||||
* If nothing else works, unlink the repositories by running `npm unlink` in both, and start over.
|
||||
|
||||
## Testing
|
||||
### Documentation
|
||||
@@ -121,6 +130,66 @@ 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
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Ignoring optional dependencies
|
||||
|
||||
When running `npm install`, you can get warnings about optionsl dependencies:
|
||||
|
||||
```
|
||||
npm WARN optional Skipping failed optional dependency /chokidar/fsevents:
|
||||
npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.2.7
|
||||
```
|
||||
|
||||
You can suppress them by adding the `no-optional` switch:
|
||||
|
||||
```
|
||||
npm install --no-optional
|
||||
```
|
||||
|
||||
Further reading: [Stack Overflow](https://stackoverflow.com/questions/36725181/not-compatible-with-your-operating-system-or-architecture-fsevents1-0-11)
|
||||
|
||||
### Resolving dependencies
|
||||
|
||||
When installing for the first time, you can get warnings which need to be resolved:
|
||||
|
||||
```
|
||||
npm WARN eslint-config-scratch@5.0.0 requires a peer of babel-eslint@^8.0.1 but none was installed.
|
||||
npm WARN eslint-config-scratch@5.0.0 requires a peer of eslint@^4.0 but none was installed.
|
||||
npm WARN scratch-paint@0.2.0-prerelease.20190318170811 requires a peer of react-intl-redux@^0.7 but none was installed.
|
||||
npm WARN scratch-paint@0.2.0-prerelease.20190318170811 requires a peer of react-responsive@^4 but none was installed.
|
||||
```
|
||||
|
||||
You can check which versions are available:
|
||||
|
||||
```
|
||||
npm view react-intl-redux@0.* version
|
||||
```
|
||||
|
||||
You will neet do install the required version:
|
||||
|
||||
```
|
||||
npm install --no-optional --save-dev react-intl-redux@^0.7
|
||||
```
|
||||
|
||||
The dependency itself might have more missing dependencies, which will show up like this:
|
||||
|
||||
```
|
||||
user@machine:~/sources/scratch/scratch-gui (491-translatable-library-objects)$ npm install --no-optional --save-dev react-intl-redux@^0.7
|
||||
scratch-gui@0.1.0 /media/cuideigin/Linux/sources/scratch/scratch-gui
|
||||
├── react-intl-redux@0.7.0
|
||||
└── UNMET PEER DEPENDENCY react-responsive@5.0.0
|
||||
```
|
||||
|
||||
You will need to install those as well:
|
||||
|
||||
```
|
||||
npm install --no-optional --save-dev react-responsive@^5.0.0
|
||||
```
|
||||
|
||||
Further reading: [Stack Overflow](https://stackoverflow.com/questions/46602286/npm-requires-a-peer-of-but-all-peers-are-in-package-json-and-node-modules)
|
||||
|
||||
|
||||
## Publishing to GitHub Pages
|
||||
You can publish the GUI to github.io so that others on the Internet can view it.
|
||||
[Read the wiki for a step-by-step guide.](https://github.com/LLK/scratch-gui/wiki/Publishing-to-GitHub-Pages)
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"babel-loader": "^8.0.4",
|
||||
"base64-loader": "1.0.0",
|
||||
"bowser": "1.9.4",
|
||||
"chromedriver": "2.44.1",
|
||||
"chromedriver": "74.0.0",
|
||||
"classnames": "2.2.6",
|
||||
"computed-style-to-inline-style": "3.0.0",
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
@@ -56,7 +56,7 @@
|
||||
"eslint": "^5.0.1",
|
||||
"eslint-config-scratch": "^5.0.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-react": "7.11.1",
|
||||
"eslint-plugin-react": "^7.12.4",
|
||||
"file-loader": "2.0.0",
|
||||
"get-float-time-domain-data": "0.1.0",
|
||||
"get-user-media-promise": "1.1.4",
|
||||
@@ -90,7 +90,7 @@
|
||||
"react-dom": "16.2.0",
|
||||
"react-draggable": "3.0.5",
|
||||
"react-ga": "2.5.3",
|
||||
"react-intl": "2.8.0",
|
||||
"react-intl": "2.9.0",
|
||||
"react-modal": "3.6.1",
|
||||
"react-popover": "0.5.10",
|
||||
"react-redux": "5.0.7",
|
||||
@@ -105,13 +105,13 @@
|
||||
"redux-throttle": "0.1.1",
|
||||
"rimraf": "^2.6.1",
|
||||
"scratch-audio": "0.1.0-prerelease.20190114210212",
|
||||
"scratch-blocks": "0.1.0-prerelease.1556058260",
|
||||
"scratch-l10n": "3.2.20190417195644",
|
||||
"scratch-paint": "0.2.0-prerelease.20190416185326",
|
||||
"scratch-render": "0.1.0-prerelease.20190419202157",
|
||||
"scratch-storage": "1.2.2",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20190419183947",
|
||||
"scratch-vm": "0.2.0-prerelease.20190417203423",
|
||||
"scratch-l10n": "3.3.20190605143948",
|
||||
"scratch-blocks": "0.1.0-prerelease.1559136797",
|
||||
"scratch-paint": "0.2.0-prerelease.20190524133325",
|
||||
"scratch-render": "0.1.0-prerelease.20190605151415",
|
||||
"scratch-storage": "1.3.2",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20190523193400",
|
||||
"scratch-vm": "0.2.0-prerelease.20190604212312",
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"startaudiocontext": "1.2.1",
|
||||
"style-loader": "^0.23.0",
|
||||
|
||||
@@ -2,10 +2,18 @@
|
||||
@import "../../css/colors.css";
|
||||
@import "../../css/z-index.css";
|
||||
|
||||
.card-container-overlay {
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
z-index: $z-index-card;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
position:absolute;
|
||||
pointer-events: auto;
|
||||
z-index: $z-index-card;
|
||||
margin: 0.5rem 2rem;
|
||||
min-width: 468px;
|
||||
}
|
||||
|
||||
.left-card, .right-card {
|
||||
@@ -104,11 +112,38 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-buttons-hidden {
|
||||
border-bottom: 0px;
|
||||
}
|
||||
|
||||
.header-buttons-right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.header-buttons img {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.shrink-expand-button {
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.shrink-expand-button:hover, .all-button:hover {
|
||||
background-color: $ui-black-transparent;
|
||||
}
|
||||
|
||||
.remove-button, .all-button {
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.75rem;
|
||||
@@ -127,6 +162,7 @@
|
||||
}
|
||||
|
||||
.step-body {
|
||||
width: 100%;
|
||||
background: $ui-white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -142,6 +178,8 @@
|
||||
|
||||
.step-image {
|
||||
max-width: 450px;
|
||||
max-height: 200px;
|
||||
object-fit: contain;
|
||||
background: #F9F9F9;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0.5rem;
|
||||
@@ -188,7 +226,7 @@
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
height: 1rem;
|
||||
height: 1.25rem;
|
||||
margin: .125rem 0; /* To offset the .25rem difference in icon size */
|
||||
}
|
||||
|
||||
@@ -240,14 +278,6 @@
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.video-cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.steps-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -267,3 +297,7 @@
|
||||
background: $ui-white;
|
||||
box-shadow: 0px 0px 0px 2px $ui-black-transparent;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, {Fragment} from 'react';
|
||||
import classNames from 'classnames';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
import Draggable from 'react-draggable';
|
||||
|
||||
import styles from './card.css';
|
||||
|
||||
import shrinkIcon from './icon--shrink.svg';
|
||||
import expandIcon from './icon--expand.svg';
|
||||
|
||||
import rightArrow from './icon--next.svg';
|
||||
import leftArrow from './icon--prev.svg';
|
||||
|
||||
@@ -14,8 +18,8 @@ import closeIcon from './icon--close.svg';
|
||||
import {translateVideo} from '../../lib/libraries/decks/translate-video.js';
|
||||
import {translateImage} from '../../lib/libraries/decks/translate-image.js';
|
||||
|
||||
const CardHeader = ({onCloseCards, onShowAll, totalSteps, step}) => (
|
||||
<div className={styles.headerButtons}>
|
||||
const CardHeader = ({onCloseCards, onShrinkExpandCards, onShowAll, totalSteps, step, expanded}) => (
|
||||
<div className={expanded ? styles.headerButtons : classNames(styles.headerButtons, styles.headerButtonsHidden)}>
|
||||
<div
|
||||
className={styles.allButton}
|
||||
onClick={onShowAll}
|
||||
@@ -41,49 +45,104 @@ const CardHeader = ({onCloseCards, onShowAll, totalSteps, step}) => (
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={styles.removeButton}
|
||||
onClick={onCloseCards}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Close"
|
||||
description="Title for button to close how-to card"
|
||||
id="gui.cards.close"
|
||||
/>
|
||||
<img
|
||||
className={styles.closeIcon}
|
||||
src={closeIcon}
|
||||
/>
|
||||
<div className={styles.headerButtonsRight}>
|
||||
<div
|
||||
className={styles.shrinkExpandButton}
|
||||
onClick={onShrinkExpandCards}
|
||||
>
|
||||
<img
|
||||
draggable={false}
|
||||
src={expanded ? shrinkIcon : expandIcon}
|
||||
/>
|
||||
{expanded ?
|
||||
<FormattedMessage
|
||||
defaultMessage="Shrink"
|
||||
description="Title for button to shrink how-to card"
|
||||
id="gui.cards.shrink"
|
||||
/> :
|
||||
<FormattedMessage
|
||||
defaultMessage="Expand"
|
||||
description="Title for button to expand how-to card"
|
||||
id="gui.cards.expand"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
className={styles.removeButton}
|
||||
onClick={onCloseCards}
|
||||
>
|
||||
<img
|
||||
className={styles.closeIcon}
|
||||
src={closeIcon}
|
||||
/>
|
||||
<FormattedMessage
|
||||
defaultMessage="Close"
|
||||
description="Title for button to close how-to card"
|
||||
id="gui.cards.close"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Video step needs to know if the card is being dragged to cover the video
|
||||
// so that the mouseup is not swallowed by the iframe.
|
||||
const VideoStep = ({video, dragging}) => (
|
||||
<div className={styles.stepVideo}>
|
||||
{dragging ? (
|
||||
<div className={styles.videoCover} />
|
||||
) : null}
|
||||
<iframe
|
||||
allowFullScreen
|
||||
allowTransparency="true"
|
||||
frameBorder="0"
|
||||
height="257"
|
||||
scrolling="no"
|
||||
src={`https://fast.wistia.net/embed/iframe/${video}?seo=false&videoFoam=true`}
|
||||
title="📹"
|
||||
width="466"
|
||||
/>
|
||||
<script
|
||||
async
|
||||
src="https://fast.wistia.net/assets/external/E-v1.js"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
class VideoStep extends React.Component {
|
||||
|
||||
componentDidMount () {
|
||||
const script = document.createElement('script');
|
||||
script.src = `https://fast.wistia.com/embed/medias/${this.props.video}.jsonp`;
|
||||
script.async = true;
|
||||
script.setAttribute('id', 'wistia-video-content');
|
||||
document.body.appendChild(script);
|
||||
|
||||
const script2 = document.createElement('script');
|
||||
script2.src = 'https://fast.wistia.com/assets/external/E-v1.js';
|
||||
script2.async = true;
|
||||
script2.setAttribute('id', 'wistia-video-api');
|
||||
document.body.appendChild(script2);
|
||||
}
|
||||
|
||||
// We use the Wistia API here to update or pause the video dynamically:
|
||||
// https://wistia.com/support/developers/player-api
|
||||
componentDidUpdate (prevProps) {
|
||||
// Get a handle on the currently loaded video
|
||||
const video = window.Wistia.api(prevProps.video);
|
||||
|
||||
// Reset the video source if a new video has been chosen from the library
|
||||
if (prevProps.video !== this.props.video) {
|
||||
video.replaceWith(this.props.video);
|
||||
}
|
||||
|
||||
// Pause the video if the modal is being shrunken
|
||||
if (!this.props.expanded) {
|
||||
video.pause();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
const script = document.getElementById('wistia-video-content');
|
||||
script.parentNode.removeChild(script);
|
||||
|
||||
const script2 = document.getElementById('wistia-video-api');
|
||||
script2.parentNode.removeChild(script2);
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={styles.stepVideo}>
|
||||
<div
|
||||
className={`wistia_embed wistia_async_${this.props.video}`}
|
||||
id="video-div"
|
||||
style={{height: `257px`, width: `466px`}}
|
||||
>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
VideoStep.propTypes = {
|
||||
dragging: PropTypes.bool.isRequired,
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
video: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
@@ -107,13 +166,13 @@ ImageStep.propTypes = {
|
||||
title: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
const NextPrevButtons = ({isRtl, onNextStep, onPrevStep}) => (
|
||||
const NextPrevButtons = ({isRtl, onNextStep, onPrevStep, expanded}) => (
|
||||
<Fragment>
|
||||
{onNextStep ? (
|
||||
<div>
|
||||
<div className={isRtl ? styles.leftCard : styles.rightCard} />
|
||||
<div className={expanded ? (isRtl ? styles.leftCard : styles.rightCard) : styles.hidden} />
|
||||
<div
|
||||
className={isRtl ? styles.leftButton : styles.rightButton}
|
||||
className={expanded ? (isRtl ? styles.leftButton : styles.rightButton) : styles.hidden}
|
||||
onClick={onNextStep}
|
||||
>
|
||||
<img
|
||||
@@ -125,9 +184,9 @@ const NextPrevButtons = ({isRtl, onNextStep, onPrevStep}) => (
|
||||
) : null}
|
||||
{onPrevStep ? (
|
||||
<div>
|
||||
<div className={isRtl ? styles.rightCard : styles.leftCard} />
|
||||
<div className={expanded ? (isRtl ? styles.rightCard : styles.leftCard) : styles.hidden} />
|
||||
<div
|
||||
className={isRtl ? styles.rightButton : styles.leftButton}
|
||||
className={expanded ? (isRtl ? styles.rightButton : styles.leftButton) : styles.hidden}
|
||||
onClick={onPrevStep}
|
||||
>
|
||||
<img
|
||||
@@ -141,13 +200,16 @@ const NextPrevButtons = ({isRtl, onNextStep, onPrevStep}) => (
|
||||
);
|
||||
|
||||
NextPrevButtons.propTypes = {
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
isRtl: PropTypes.bool,
|
||||
onNextStep: PropTypes.func,
|
||||
onPrevStep: PropTypes.func
|
||||
};
|
||||
CardHeader.propTypes = {
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
onCloseCards: PropTypes.func.isRequired,
|
||||
onShowAll: PropTypes.func.isRequired,
|
||||
onShrinkExpandCards: PropTypes.func.isRequired,
|
||||
step: PropTypes.number,
|
||||
totalSteps: PropTypes.number
|
||||
};
|
||||
@@ -219,78 +281,113 @@ const Cards = props => {
|
||||
locale,
|
||||
onActivateDeckFactory,
|
||||
onCloseCards,
|
||||
onShrinkExpandCards,
|
||||
onDrag,
|
||||
onStartDrag,
|
||||
onEndDrag,
|
||||
onShowAll,
|
||||
onNextStep,
|
||||
onPrevStep,
|
||||
showVideos,
|
||||
step,
|
||||
expanded,
|
||||
...posProps
|
||||
} = props;
|
||||
let {x, y} = posProps;
|
||||
|
||||
if (activeDeckId === null) return;
|
||||
|
||||
// Tutorial cards need to calculate their own dragging bounds
|
||||
// to allow for dragging the cards off the left, right and bottom
|
||||
// edges of the workspace.
|
||||
const cardHorizontalDragOffset = 400; // ~80% of card width
|
||||
const cardVerticalDragOffset = expanded ? 257 : 0; // ~80% of card height, if expanded
|
||||
const menuBarHeight = 48; // TODO: get pre-calculated from elsewhere?
|
||||
const wideCardWidth = 500;
|
||||
|
||||
if (x === 0 && y === 0) {
|
||||
// initialize positions
|
||||
x = isRtl ? -292 : 292;
|
||||
// The tallest cards are about 385px high, and the default position is pinned
|
||||
x = isRtl ? (-190 - wideCardWidth - cardHorizontalDragOffset) : 292;
|
||||
x += cardHorizontalDragOffset;
|
||||
// The tallest cards are about 320px high, and the default position is pinned
|
||||
// to near the bottom of the blocks palette to allow room to work above.
|
||||
const tallCardHeight = 385;
|
||||
const tallCardHeight = 320;
|
||||
const bottomMargin = 60; // To avoid overlapping the backpack region
|
||||
y = window.innerHeight - tallCardHeight - bottomMargin;
|
||||
y = window.innerHeight - tallCardHeight - bottomMargin - menuBarHeight;
|
||||
}
|
||||
|
||||
const steps = content[activeDeckId].steps;
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
bounds="parent"
|
||||
position={{x: x, y: y}}
|
||||
onDrag={onDrag}
|
||||
onStart={onStartDrag}
|
||||
onStop={onEndDrag}
|
||||
// Custom overlay to act as the bounding parent for the draggable, using values from above
|
||||
<div
|
||||
className={styles.cardContainerOverlay}
|
||||
style={{
|
||||
width: `${window.innerWidth + (2 * cardHorizontalDragOffset)}px`,
|
||||
height: `${window.innerHeight - menuBarHeight + cardVerticalDragOffset}px`,
|
||||
top: `${menuBarHeight}px`,
|
||||
left: `${-cardHorizontalDragOffset}px`
|
||||
}}
|
||||
>
|
||||
<div className={styles.cardContainer}>
|
||||
<div className={styles.card}>
|
||||
<CardHeader
|
||||
step={step}
|
||||
totalSteps={steps.length}
|
||||
onCloseCards={onCloseCards}
|
||||
onShowAll={onShowAll}
|
||||
/>
|
||||
<div className={styles.stepBody}>
|
||||
{steps[step].deckIds ? (
|
||||
<PreviewsStep
|
||||
content={content}
|
||||
deckIds={steps[step].deckIds}
|
||||
onActivateDeckFactory={onActivateDeckFactory}
|
||||
onShowAll={onShowAll}
|
||||
/>
|
||||
) : (
|
||||
steps[step].video ? (
|
||||
<VideoStep
|
||||
dragging={dragging}
|
||||
video={translateVideo(steps[step].video, locale)}
|
||||
<Draggable
|
||||
bounds="parent"
|
||||
cancel="#video-div" // disable dragging on video div
|
||||
position={{x: x, y: y}}
|
||||
onDrag={onDrag}
|
||||
onStart={onStartDrag}
|
||||
onStop={onEndDrag}
|
||||
>
|
||||
<div className={styles.cardContainer}>
|
||||
<div className={styles.card}>
|
||||
<CardHeader
|
||||
expanded={expanded}
|
||||
step={step}
|
||||
totalSteps={steps.length}
|
||||
onCloseCards={onCloseCards}
|
||||
onShowAll={onShowAll}
|
||||
onShrinkExpandCards={onShrinkExpandCards}
|
||||
/>
|
||||
<div className={expanded ? styles.stepBody : styles.hidden}>
|
||||
{steps[step].deckIds ? (
|
||||
<PreviewsStep
|
||||
content={content}
|
||||
deckIds={steps[step].deckIds}
|
||||
onActivateDeckFactory={onActivateDeckFactory}
|
||||
onShowAll={onShowAll}
|
||||
/>
|
||||
) : (
|
||||
<ImageStep
|
||||
image={translateImage(steps[step].image, locale)}
|
||||
title={steps[step].title}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{steps[step].trackingPixel && steps[step].trackingPixel}
|
||||
steps[step].video ? (
|
||||
showVideos ? (
|
||||
<VideoStep
|
||||
dragging={dragging}
|
||||
expanded={expanded}
|
||||
video={translateVideo(steps[step].video, locale)}
|
||||
/>
|
||||
) : ( // Else show the deck image and title
|
||||
<ImageStep
|
||||
image={content[activeDeckId].img}
|
||||
title={content[activeDeckId].name}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<ImageStep
|
||||
image={translateImage(steps[step].image, locale)}
|
||||
title={steps[step].title}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{steps[step].trackingPixel && steps[step].trackingPixel}
|
||||
</div>
|
||||
<NextPrevButtons
|
||||
expanded={expanded}
|
||||
isRtl={isRtl}
|
||||
onNextStep={step < steps.length - 1 ? onNextStep : null}
|
||||
onPrevStep={step > 0 ? onPrevStep : null}
|
||||
/>
|
||||
</div>
|
||||
<NextPrevButtons
|
||||
isRtl={isRtl}
|
||||
onNextStep={step < steps.length - 1 ? onNextStep : null}
|
||||
onPrevStep={step > 0 ? onPrevStep : null}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Draggable>
|
||||
</Draggable>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -309,6 +406,7 @@ Cards.propTypes = {
|
||||
})
|
||||
}),
|
||||
dragging: PropTypes.bool.isRequired,
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
isRtl: PropTypes.bool.isRequired,
|
||||
locale: PropTypes.string.isRequired,
|
||||
onActivateDeckFactory: PropTypes.func.isRequired,
|
||||
@@ -318,10 +416,21 @@ Cards.propTypes = {
|
||||
onNextStep: PropTypes.func.isRequired,
|
||||
onPrevStep: PropTypes.func.isRequired,
|
||||
onShowAll: PropTypes.func,
|
||||
onShrinkExpandCards: PropTypes.func.isRequired,
|
||||
onStartDrag: PropTypes.func,
|
||||
showVideos: PropTypes.bool,
|
||||
step: PropTypes.number.isRequired,
|
||||
x: PropTypes.number,
|
||||
y: PropTypes.number
|
||||
};
|
||||
|
||||
export default Cards;
|
||||
Cards.defaultProps = {
|
||||
showVideos: true
|
||||
};
|
||||
|
||||
export {
|
||||
Cards as default,
|
||||
// Others exported for testability
|
||||
ImageStep,
|
||||
VideoStep
|
||||
};
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>General/Check Copy</title>
|
||||
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
|
||||
<title>Extensions/Connection/Close</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M10,7.73577763 L12.2675846,5.46819305 C12.8909754,4.84480221 13.8988379,4.84194841 14.5284447,5.47155526 C15.1536925,6.096803 15.1476685,7.11655392 14.531807,7.73241542 L12.2642224,10 L14.531807,12.2675846 C15.1551978,12.8909754 15.1580516,13.8988379 14.5284447,14.5284447 C13.903197,15.1536925 12.8834461,15.1476685 12.2675846,14.531807 L10,12.2642224 L7.73241542,14.531807 C7.10902458,15.1551978 6.10116212,15.1580516 5.47155526,14.5284447 C4.84630752,13.903197 4.85233155,12.8834461 5.46819305,12.2675846 L7.73577763,10 L5.46819305,7.73241542 C4.84480221,7.10902458 4.84194841,6.10116212 5.47155526,5.47155526 C6.096803,4.84630752 7.11655392,4.85233155 7.73241542,5.46819305 L10,7.73577763 Z" id="path-1"></path>
|
||||
<path d="M15.464935,15.467297 C14.7644059,16.1777705 13.6185877,16.1777705 12.9081142,15.467297 L9.99881899,12.5580018 L7.08841885,15.467297 C6.38236506,16.1733508 5.23765187,16.1733508 4.53159807,15.467297 C4.17912364,15.1148226 4.00012409,14.6485398 4.00012409,14.1888866 C4.00012409,13.7281285 4.17912364,13.2629506 4.53159807,12.9104762 L7.44089328,10.001181 L4.52717833,7.08636112 C4.17359897,6.73278176 3.99459941,6.26760391 4.00012409,5.80242606 C4.00012409,5.34166795 4.17359897,4.88201477 4.52717833,4.52954034 C5.23212719,3.82348655 6.37684038,3.82348655 7.08399911,4.52954034 L9.99881899,7.44325529 L12.9125339,4.52954034 C13.6185877,3.82348655 14.7644059,3.82348655 15.4704597,4.52954034 C16.1765134,5.2344892 16.1765134,6.38030733 15.4704597,7.08636112 L12.5545348,10.001181 L15.4704597,12.914896 C16.1765134,13.6209497 16.1765134,14.7557185 15.464935,15.467297" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="General/Check-Copy" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Extensions/Connection/Close" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<use id="close-icon" fill="#FFFFFF" xlink:href="#path-1"></use>
|
||||
<g id="Color/White" mask="url(#mask-2)" fill="#FFFFFF">
|
||||
<use id="close" fill="#FFFFFF" xlink:href="#path-1"></use>
|
||||
<g id="White" mask="url(#mask-2)" fill="#FFFFFF">
|
||||
<rect id="Color" x="0" y="0" width="20" height="20"></rect>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
|
||||
<title>Tutorials/Expand</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M4.5,2 L15.5,2 C16.8807119,2 18,3.11928813 18,4.5 L18,5.5 C18,5.77614237 17.7761424,6 17.5,6 L2.5,6 C2.22385763,6 2,5.77614237 2,5.5 L2,4.5 C2,3.11928813 3.11928813,2 4.5,2 Z M2.5,7 L17.5,7 C17.7761424,7 18,7.22385763 18,7.5 L18,15.5 C18,16.8807119 16.8807119,18 15.5,18 L4.5,18 C3.11928813,18 2,16.8807119 2,15.5 L2,7.5 C2,7.22385763 2.22385763,7 2.5,7 Z M9.65509466,15.1636146 C9.745416,15.2533569 9.86700241,15.3031495 9.99727357,15.3031495 C10.1275447,15.3031495 10.2491311,15.2533569 10.3394525,15.1636146 L12.1574588,13.3456082 C12.3004676,13.2025994 12.3415754,12.9993764 12.2657286,12.8158388 C12.1898819,12.6334592 12.0109762,12.5153467 11.8100691,12.5153467 L11.1506075,12.5153467 L10.7899012,9.98808625 C10.7464775,9.64474938 10.4703026,9.36567961 10.1124912,9.30951827 C10.0783312,9.30546539 10.0435922,9.30314946 10.0094322,9.30314946 C9.61282891,9.30314946 9.27586085,9.59785176 9.22548933,9.98519134 L8.8740467,12.5153467 L8.18447804,12.5153467 C7.9858869,12.5153467 7.81450796,12.6265114 7.73692425,12.8065751 C7.65876155,12.9866388 7.69639544,13.1962306 7.83708829,13.3456082 L9.65509466,15.1636146 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="Tutorials/Expand" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<g id="Combined-Shape" fill-rule="nonzero"></g>
|
||||
<g id="White" mask="url(#mask-2)" fill="#FFFFFF">
|
||||
<rect id="Color" x="0" y="0" width="20" height="20"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
|
||||
<title>Tutorials/Shink</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<path d="M2.5,8 C2.22385763,8 2,7.77614237 2,7.5 C2,7.22385763 2.22385763,7 2.5,7 L3.5,7 C3.77614237,7 4,7.22385763 4,7.5 C4,7.77614237 3.77614237,8 3.5,8 L2.5,8 Z M5.5,8 C5.22385763,8 5,7.77614237 5,7.5 C5,7.22385763 5.22385763,7 5.5,7 L6.5,7 C6.77614237,7 7,7.22385763 7,7.5 C7,7.77614237 6.77614237,8 6.5,8 L5.5,8 Z M8.5,8 C8.22385763,8 8,7.77614237 8,7.5 C8,7.22385763 8.22385763,7 8.5,7 L9.5,7 C9.77614237,7 10,7.22385763 10,7.5 C10,7.77614237 9.77614237,8 9.5,8 L8.5,8 Z M11.5,8 C11.2238576,8 11,7.77614237 11,7.5 C11,7.22385763 11.2238576,7 11.5,7 L12.5,7 C12.7761424,7 13,7.22385763 13,7.5 C13,7.77614237 12.7761424,8 12.5,8 L11.5,8 Z M14.5,8 C14.2238576,8 14,7.77614237 14,7.5 C14,7.22385763 14.2238576,7 14.5,7 L15.5,7 C15.7761424,7 16,7.22385763 16,7.5 C16,7.77614237 15.7761424,8 15.5,8 L14.5,8 Z M17,7.5 C17,7.22385763 17.2238576,7 17.5,7 C17.7761424,7 18,7.22385763 18,7.5 L18,8.5 C18,8.77614237 17.7761424,9 17.5,9 C17.2238576,9 17,8.77614237 17,8.5 L17,7.5 Z M17,10.5 C17,10.2238576 17.2238576,10 17.5,10 C17.7761424,10 18,10.2238576 18,10.5 L18,11.5 C18,11.7761424 17.7761424,12 17.5,12 C17.2238576,12 17,11.7761424 17,11.5 L17,10.5 Z M17,13.5 C17,13.2238576 17.2238576,13 17.5,13 C17.7761424,13 18,13.2238576 18,13.5 L18,14.5 C18,14.7761424 17.7761424,15 17.5,15 C17.2238576,15 17,14.7761424 17,14.5 L17,13.5 Z M16.8130884,16.2258789 C16.9469778,15.9843665 17.2513011,15.8971208 17.4928135,16.0310102 C17.7343259,16.1648996 17.8215716,16.4692229 17.6876822,16.7107353 C17.4827416,17.0804113 17.1867239,17.3929429 16.8289716,17.6178178 C16.59518,17.7647739 16.286523,17.6943798 16.1395669,17.4605882 C15.9926108,17.2267966 16.0630049,16.9181396 16.2967965,16.7711835 C16.5118233,16.6360225 16.6900031,16.4479027 16.8130884,16.2258789 Z M14.5614675,17 C14.8376098,17 15.0614675,17.2238576 15.0614675,17.5 C15.0614675,17.7761424 14.8376098,18 14.5614675,18 L13.5614675,18 C13.2853251,18 13.0614675,17.7761424 13.0614675,17.5 C13.0614675,17.2238576 13.2853251,17 13.5614675,17 L14.5614675,17 Z M11.5614675,17 C11.8376098,17 12.0614675,17.2238576 12.0614675,17.5 C12.0614675,17.7761424 11.8376098,18 11.5614675,18 L10.5614675,18 C10.2853251,18 10.0614675,17.7761424 10.0614675,17.5 C10.0614675,17.2238576 10.2853251,17 10.5614675,17 L11.5614675,17 Z M8.56146746,17 C8.83760983,17 9.06146746,17.2238576 9.06146746,17.5 C9.06146746,17.7761424 8.83760983,18 8.56146746,18 L7.56146746,18 C7.28532508,18 7.06146746,17.7761424 7.06146746,17.5 C7.06146746,17.2238576 7.28532508,17 7.56146746,17 L8.56146746,17 Z M5.56146746,17 C5.83760983,17 6.06146746,17.2238576 6.06146746,17.5 C6.06146746,17.7761424 5.83760983,18 5.56146746,18 L4.56146746,18 C4.28532508,18 4.06146746,17.7761424 4.06146746,17.5 C4.06146746,17.2238576 4.28532508,17 4.56146746,17 L5.56146746,17 Z M3.22779886,16.2951753 C3.37445671,16.5291541 3.30366909,16.8377211 3.06969027,16.9843789 C2.83571145,17.1310368 2.52714447,17.0602491 2.38048662,16.8262703 C2.15682122,16.4694332 2.02609629,16.0609535 2.00352115,15.6335963 C1.98895424,15.3578384 2.20069137,15.1224836 2.47644927,15.1079167 C2.75220716,15.0933498 2.98756192,15.3050869 3.00212882,15.5808448 C3.01567642,15.8373067 3.0937811,16.0813625 3.22779886,16.2951753 Z M3,13.6229349 C3,13.8990773 2.77614237,14.1229349 2.5,14.1229349 C2.22385763,14.1229349 2,13.8990773 2,13.6229349 L2,12.6229349 C2,12.3467925 2.22385763,12.1229349 2.5,12.1229349 C2.77614237,12.1229349 3,12.3467925 3,12.6229349 L3,13.6229349 Z M3,10.6229349 C3,10.8990773 2.77614237,11.1229349 2.5,11.1229349 C2.22385763,11.1229349 2,10.8990773 2,10.6229349 L2,9.62293492 C2,9.34679254 2.22385763,9.12293492 2.5,9.12293492 C2.77614237,9.12293492 3,9.34679254 3,9.62293492 L3,10.6229349 Z M3,7.62293492 C3,7.89907729 2.77614237,8.12293492 2.5,8.12293492 C2.22385763,8.12293492 2,7.89907729 2,7.62293492 L2,7.5 C2,7.22385763 2.22385763,7 2.5,7 C2.77614237,7 3,7.22385763 3,7.5 L3,7.62293492 Z M4.5,2 L15.5,2 C16.8807119,2 18,3.11928813 18,4.5 L18,5.5 C18,5.77614237 17.7761424,6 17.5,6 L2.5,6 C2.22385763,6 2,5.77614237 2,5.5 L2,4.5 C2,3.11928813 3.11928813,2 4.5,2 Z M10.3449053,9.44268434 L12.1629117,11.2606907 C12.3036046,11.4100683 12.3412384,11.6196601 12.2630758,11.7997238 C12.185492,11.9797875 12.0141131,12.0909522 11.815522,12.0909522 L11.1259533,12.0909522 L10.7745107,14.6211076 C10.7241392,15.0084472 10.3871711,15.3031495 9.99056779,15.3031495 C9.9564078,15.3031495 9.92166882,15.3008335 9.88750883,15.2967806 C9.52969739,15.2406193 9.25352253,14.9615495 9.21009882,14.6182127 L8.84939246,12.0909522 L8.18993091,12.0909522 C7.98902384,12.0909522 7.81011812,11.9728397 7.73427135,11.7904601 C7.65842459,11.6069225 7.69953238,11.4036995 7.84254116,11.2606907 L9.66054753,9.44268434 C9.75086886,9.35294199 9.87245528,9.30314946 10.0027264,9.30314946 C10.1329976,9.30314946 10.254584,9.35294199 10.3449053,9.44268434 Z" id="path-1"></path>
|
||||
</defs>
|
||||
<g id="Tutorials/Shink" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<g id="Rectangle" fill-rule="nonzero"></g>
|
||||
<g id="White" mask="url(#mask-2)" fill="#FFFFFF">
|
||||
<rect id="Color" x="0" y="0" width="20" height="20"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -1,50 +0,0 @@
|
||||
import {connect} from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import React from 'react';
|
||||
|
||||
const ConfirmReplaceHOC = function (WrappedComponent) {
|
||||
class ConfirmReplaceProject extends React.PureComponent {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
bindAll(this, [
|
||||
'confirmReadyToReplaceProject'
|
||||
]);
|
||||
}
|
||||
|
||||
confirmReadyToReplaceProject (message) {
|
||||
let readyToReplaceProject = true;
|
||||
if (this.props.projectChanged && !this.props.canCreateNew) {
|
||||
readyToReplaceProject = confirm(message); // eslint-disable-line no-alert
|
||||
}
|
||||
return readyToReplaceProject;
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
projectChanged,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...props
|
||||
} = this.props;
|
||||
return (<WrappedComponent
|
||||
confirmReadyToReplaceProject={this.confirmReadyToReplaceProject}
|
||||
{...props}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmReplaceProject.propTypes = {
|
||||
canCreateNew: PropTypes.bool,
|
||||
projectChanged: PropTypes.bool
|
||||
};
|
||||
|
||||
const _mapStateToProps = state => ({
|
||||
projectChanged: state.scratchGui.projectChanged
|
||||
});
|
||||
|
||||
return connect(_mapStateToProps)(ConfirmReplaceProject);
|
||||
};
|
||||
|
||||
export default ConfirmReplaceHOC;
|
||||
@@ -28,7 +28,7 @@ import LoginDropdown from './login-dropdown.jsx';
|
||||
import SB3Downloader from '../../containers/sb3-downloader.jsx';
|
||||
import DeletionRestorer from '../../containers/deletion-restorer.jsx';
|
||||
import TurboMode from '../../containers/turbo-mode.jsx';
|
||||
import ConfirmReplaceHOC from './confirm-replace-hoc.jsx';
|
||||
import MenuBarHOC from '../../containers/menu-bar-hoc.jsx';
|
||||
|
||||
import {openTipsLibrary} from '../../reducers/modals';
|
||||
import {setPlayer} from '../../reducers/mode';
|
||||
@@ -192,8 +192,8 @@ class MenuBar extends React.Component {
|
||||
this.props.onRequestCloseFile();
|
||||
}
|
||||
handleClickSeeCommunity (waitForUpdate) {
|
||||
if (this.props.canSave && this.props.projectChanged) { // save before transitioning to project page
|
||||
this.props.autoUpdateProject();
|
||||
if (this.props.shouldSaveBeforeTransition()) {
|
||||
this.props.autoUpdateProject(); // save before transitioning to project page
|
||||
waitForUpdate(true); // queue the transition to project page
|
||||
} else {
|
||||
waitForUpdate(false); // immediately transition to project page
|
||||
@@ -705,6 +705,7 @@ MenuBar.propTypes = {
|
||||
canSave: PropTypes.bool,
|
||||
canShare: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
confirmReadyToReplaceProject: PropTypes.func,
|
||||
editMenuOpen: PropTypes.bool,
|
||||
enableCommunity: PropTypes.bool,
|
||||
fileMenuOpen: PropTypes.bool,
|
||||
@@ -714,6 +715,7 @@ MenuBar.propTypes = {
|
||||
isShowingProject: PropTypes.bool,
|
||||
isUpdating: PropTypes.bool,
|
||||
languageMenuOpen: PropTypes.bool,
|
||||
locale: PropTypes.string.isRequired,
|
||||
loginMenuOpen: PropTypes.bool,
|
||||
onClickAccount: PropTypes.func,
|
||||
onClickEdit: PropTypes.func,
|
||||
@@ -741,7 +743,9 @@ MenuBar.propTypes = {
|
||||
projectTitle: PropTypes.string,
|
||||
renderLogin: PropTypes.func,
|
||||
sessionExists: PropTypes.bool,
|
||||
shouldSaveBeforeTransition: PropTypes.func,
|
||||
showComingSoon: PropTypes.bool,
|
||||
userOwnsProject: PropTypes.bool,
|
||||
username: PropTypes.string,
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
};
|
||||
@@ -794,7 +798,7 @@ const mapDispatchToProps = dispatch => ({
|
||||
|
||||
export default compose(
|
||||
injectIntl,
|
||||
ConfirmReplaceHOC,
|
||||
MenuBarHOC,
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
|
||||
@@ -2,7 +2,6 @@ import bindAll from 'lodash.bindall';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {defineMessages, injectIntl, intlShape} from 'react-intl';
|
||||
import {connect} from 'react-redux';
|
||||
import VM from 'scratch-vm';
|
||||
|
||||
import backdropLibraryContent from '../lib/libraries/backdrops.json';
|
||||
@@ -33,7 +32,7 @@ class BackdropLibrary extends React.Component {
|
||||
bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
|
||||
skinId: null
|
||||
};
|
||||
this.props.vm.setEditingTarget(this.props.stageID);
|
||||
// Do not switch to stage, just add the backdrop
|
||||
this.props.vm.addBackdrop(item.md5, vmBackdrop);
|
||||
}
|
||||
render () {
|
||||
@@ -53,17 +52,7 @@ class BackdropLibrary extends React.Component {
|
||||
BackdropLibrary.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
onRequestClose: PropTypes.func,
|
||||
stageID: PropTypes.string.isRequired,
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
stageID: state.scratchGui.targets.stage.id
|
||||
});
|
||||
|
||||
const mapDispatchToProps = () => ({});
|
||||
|
||||
export default injectIntl(connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(BackdropLibrary));
|
||||
export default injectIntl(BackdropLibrary);
|
||||
@@ -5,6 +5,7 @@ import React from 'react';
|
||||
import {
|
||||
activateDeck,
|
||||
closeCards,
|
||||
shrinkExpandCards,
|
||||
nextStep,
|
||||
prevStep,
|
||||
dragCard,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
|
||||
import CardsComponent from '../components/cards/cards.jsx';
|
||||
import {loadImageData} from '../lib/libraries/decks/translate-image.js';
|
||||
import {notScratchDesktop} from '../lib/isScratchDesktop';
|
||||
|
||||
class Cards extends React.Component {
|
||||
componentDidMount () {
|
||||
@@ -46,11 +48,13 @@ const mapStateToProps = state => ({
|
||||
content: state.scratchGui.cards.content,
|
||||
activeDeckId: state.scratchGui.cards.activeDeckId,
|
||||
step: state.scratchGui.cards.step,
|
||||
expanded: state.scratchGui.cards.expanded,
|
||||
x: state.scratchGui.cards.x,
|
||||
y: state.scratchGui.cards.y,
|
||||
isRtl: state.locales.isRtl,
|
||||
locale: state.locales.locale,
|
||||
dragging: state.scratchGui.cards.dragging
|
||||
dragging: state.scratchGui.cards.dragging,
|
||||
showVideos: notScratchDesktop()
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
@@ -60,6 +64,7 @@ const mapDispatchToProps = dispatch => ({
|
||||
dispatch(closeCards());
|
||||
},
|
||||
onCloseCards: () => dispatch(closeCards()),
|
||||
onShrinkExpandCards: () => dispatch(shrinkExpandCards()),
|
||||
onNextStep: () => dispatch(nextStep()),
|
||||
onPrevStep: () => dispatch(prevStep()),
|
||||
onDrag: (e_, data) => dispatch(dragCard(data.x, data.y)),
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import {connect} from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import React from 'react';
|
||||
|
||||
const MenuBarHOC = function (WrappedComponent) {
|
||||
class MenuBarContainer extends React.PureComponent {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
bindAll(this, [
|
||||
'confirmReadyToReplaceProject',
|
||||
'shouldSaveBeforeTransition'
|
||||
]);
|
||||
}
|
||||
confirmReadyToReplaceProject (message) {
|
||||
let readyToReplaceProject = true;
|
||||
if (this.props.projectChanged && !this.props.canCreateNew) {
|
||||
readyToReplaceProject = this.props.confirmWithMessage(message);
|
||||
}
|
||||
return readyToReplaceProject;
|
||||
}
|
||||
shouldSaveBeforeTransition () {
|
||||
return (this.props.canSave && this.props.projectChanged);
|
||||
}
|
||||
render () {
|
||||
const {
|
||||
/* eslint-disable no-unused-vars */
|
||||
projectChanged,
|
||||
/* eslint-enable no-unused-vars */
|
||||
...props
|
||||
} = this.props;
|
||||
return (<WrappedComponent
|
||||
confirmReadyToReplaceProject={this.confirmReadyToReplaceProject}
|
||||
shouldSaveBeforeTransition={this.shouldSaveBeforeTransition}
|
||||
{...props}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
|
||||
MenuBarContainer.propTypes = {
|
||||
canCreateNew: PropTypes.bool,
|
||||
canSave: PropTypes.bool,
|
||||
confirmWithMessage: PropTypes.func,
|
||||
projectChanged: PropTypes.bool
|
||||
};
|
||||
MenuBarContainer.defaultProps = {
|
||||
// default to using standard js confirm
|
||||
confirmWithMessage: message => (confirm(message)) // eslint-disable-line no-alert
|
||||
};
|
||||
const mapStateToProps = state => ({
|
||||
projectChanged: state.scratchGui.projectChanged
|
||||
});
|
||||
const mapDispatchToProps = () => ({});
|
||||
// Allow incoming props to override redux-provided props. Used to mock in tests.
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(
|
||||
{}, stateProps, dispatchProps, ownProps
|
||||
);
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
mergeProps
|
||||
)(MenuBarContainer);
|
||||
};
|
||||
|
||||
export default MenuBarHOC;
|
||||
@@ -50,7 +50,7 @@ class StageSelector extends React.Component {
|
||||
'setFileInput'
|
||||
]);
|
||||
}
|
||||
addBackdropFromLibraryItem (item) {
|
||||
addBackdropFromLibraryItem (item, shouldActivateTab = true) {
|
||||
const vmBackdrop = {
|
||||
name: item.name,
|
||||
md5: item.md5,
|
||||
@@ -59,25 +59,30 @@ class StageSelector extends React.Component {
|
||||
bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
|
||||
skinId: null
|
||||
};
|
||||
this.handleNewBackdrop(vmBackdrop);
|
||||
this.handleNewBackdrop(vmBackdrop, shouldActivateTab);
|
||||
}
|
||||
handleClick () {
|
||||
this.props.onSelect(this.props.id);
|
||||
}
|
||||
handleNewBackdrop (backdrops_) {
|
||||
handleNewBackdrop (backdrops_, shouldActivateTab = true) {
|
||||
const backdrops = Array.isArray(backdrops_) ? backdrops_ : [backdrops_];
|
||||
return Promise.all(backdrops.map(backdrop =>
|
||||
this.props.vm.addBackdrop(backdrop.md5, backdrop)
|
||||
)).then(() =>
|
||||
this.props.onActivateTab(COSTUMES_TAB_INDEX)
|
||||
);
|
||||
)).then(() => {
|
||||
if (shouldActivateTab) {
|
||||
return this.props.onActivateTab(COSTUMES_TAB_INDEX);
|
||||
}
|
||||
});
|
||||
}
|
||||
handleSurpriseBackdrop () {
|
||||
handleSurpriseBackdrop (e) {
|
||||
e.stopPropagation(); // Prevent click from falling through to selecting stage.
|
||||
// @todo should this not add a backdrop you already have?
|
||||
const item = backdropLibraryContent[Math.floor(Math.random() * backdropLibraryContent.length)];
|
||||
this.addBackdropFromLibraryItem(item);
|
||||
this.addBackdropFromLibraryItem(item, false);
|
||||
}
|
||||
handleEmptyBackdrop () {
|
||||
handleEmptyBackdrop (e) {
|
||||
e.stopPropagation(); // Prevent click from falling through to stage selector, select it manually below
|
||||
this.props.vm.setEditingTarget(this.props.id);
|
||||
this.handleNewBackdrop(emptyCostume(this.props.intl.formatMessage(sharedMessages.backdrop, {index: 1})));
|
||||
}
|
||||
handleBackdropUpload (e) {
|
||||
@@ -85,6 +90,7 @@ class StageSelector extends React.Component {
|
||||
this.props.onShowImporting();
|
||||
handleFileUpload(e.target, (buffer, fileType, fileName, fileIndex, fileCount) => {
|
||||
costumeUpload(buffer, fileType, storage, vmCostumes => {
|
||||
this.props.vm.setEditingTarget(this.props.id);
|
||||
vmCostumes.forEach((costume, i) => {
|
||||
costume.name = `${fileName}${i ? i + 1 : ''}`;
|
||||
});
|
||||
@@ -96,7 +102,8 @@ class StageSelector extends React.Component {
|
||||
}, this.props.onCloseImporting);
|
||||
}, this.props.onCloseImporting);
|
||||
}
|
||||
handleFileUploadClick () {
|
||||
handleFileUploadClick (e) {
|
||||
e.stopPropagation(); // Prevent click from selecting the stage, that is handled manually in backdrop upload
|
||||
this.fileInput.click();
|
||||
}
|
||||
handleMouseEnter () {
|
||||
|
||||
@@ -423,6 +423,7 @@ class Stage extends React.Component {
|
||||
Stage.propTypes = {
|
||||
isColorPicking: PropTypes.bool,
|
||||
isFullScreen: PropTypes.bool.isRequired,
|
||||
isStarted: PropTypes.bool,
|
||||
micIndicator: PropTypes.bool,
|
||||
onActivateColorPicker: PropTypes.func,
|
||||
onDeactivateColorPicker: PropTypes.func,
|
||||
|
||||
@@ -105,7 +105,10 @@ class TargetPane extends React.Component {
|
||||
}
|
||||
}
|
||||
handleSurpriseSpriteClick () {
|
||||
const item = spriteLibraryContent[Math.floor(Math.random() * spriteLibraryContent.length)];
|
||||
const surpriseSprites = spriteLibraryContent.filter(sprite =>
|
||||
(sprite.tags.indexOf('letters') === -1) && (sprite.tags.indexOf('numbers') === -1)
|
||||
);
|
||||
const item = surpriseSprites[Math.floor(Math.random() * surpriseSprites.length)];
|
||||
randomizeSpritePosition(item);
|
||||
this.props.vm.addSprite(JSON.stringify(item.json))
|
||||
.then(this.handleActivateBlocksTab);
|
||||
|
||||
@@ -62,10 +62,16 @@ class TipsLibrary extends React.PureComponent {
|
||||
}
|
||||
render () {
|
||||
const decksLibraryThumbnailData = Object.keys(decksLibraryContent)
|
||||
.filter(id =>
|
||||
.filter(id => {
|
||||
if (notScratchDesktop()) return true; // Do not filter anything in online editor
|
||||
const deck = decksLibraryContent[id];
|
||||
// Scratch Desktop doesn't want tutorials with `requiredProjectId`
|
||||
notScratchDesktop() || !decksLibraryContent[id].hasOwnProperty('requiredProjectId')
|
||||
)
|
||||
if (deck.hasOwnProperty('requiredProjectId')) return false;
|
||||
// Scratch Desktop should not load tutorials that are _only_ videos
|
||||
if (deck.steps.filter(s => s.title).length === 0) return false;
|
||||
// Allow any other tutorials
|
||||
return true;
|
||||
})
|
||||
.map(id => ({
|
||||
rawURL: decksLibraryContent[id].img,
|
||||
id: id,
|
||||
|
||||
@@ -113,7 +113,8 @@ const AppStateHOC = function (WrappedComponent, localesOnly) {
|
||||
}
|
||||
AppStateWrapper.propTypes = {
|
||||
isFullScreen: PropTypes.bool,
|
||||
isPlayerOnly: PropTypes.bool
|
||||
isPlayerOnly: PropTypes.bool,
|
||||
showTelemetryModal: PropTypes.bool
|
||||
};
|
||||
return AppStateWrapper;
|
||||
};
|
||||
|
||||
@@ -331,5 +331,15 @@ export default function (vm) {
|
||||
return collator.compare(str1, str2);
|
||||
};
|
||||
|
||||
// Blocks wants to know if 3D CSS transforms are supported. The cross
|
||||
// section of browsers Scratch supports and browsers that support 3D CSS
|
||||
// transforms will make the return always true.
|
||||
//
|
||||
// Shortcutting to true lets us skip an expensive style recalculation when
|
||||
// first loading the Scratch editor.
|
||||
ScratchBlocks.utils.is3dSupported = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
return ScratchBlocks;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<svg version="1.1" id="cat" x="0px" y="0px" width="95px" height="111px" viewBox="0 0 95 111" enable-background="new 0 0 95 111" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g>
|
||||
<g id="Layer_3">
|
||||
<path fill="#FAA51D" stroke="#000000" d="M22.462,79.039c-2.415-0.451-5.304-1.309-7.742-3.503
		C9.268,70.629,7.526,62.535,3.672,64.622c-3.856,2.088-3.782,15.165,8.353,19.194c4.182,1.391,7.998,1.396,11.091,1.312
		c0.811-0.025,7.717-0.654,10.079-4.074c2.361-3.42,0.719-4.272-0.09-4.744C32.295,75.838,25.878,79.677,22.462,79.039z"/>
|
||||
<path fill="#FFFFFF" d="M4.236,64.877c-1.989,0.613-3.075,4.998-2.076,8.484c0.998,3.49,2.634,5.022,3.863,6.398
		c1.528,1.038-0.72-2.402,1.361-4.15c2.075-1.744,5.733-0.914,5.733-0.914s-2.909-3.987-4.57-6.396
		C6.975,65.988,6.359,64.375,4.236,64.877z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FAA51D" d="M38.217,86.756c0,0-8.832,6.2-17.071,8.412l0.086,0.215c1.247,1.824,5.87,7.497-0.334,9.496
		c-5.333,1.717-15.12-13.104-10.821-15.902c2.626-1.713,4.892-0.252,4.892-0.252s3.474-1.07,6.001-2.345
		c4.303-2.161,5.784-3.453,5.784-3.453s4.184-4.306,6.856-4.137C36.281,78.96,41.669,83.504,38.217,86.756z"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M21.232,95.383c1.247,1.824,5.87,7.497-0.334,9.496
		c-5.333,1.717-15.329-13.344-11.03-16.145c2.626-1.713,5.101-0.01,5.101-0.01s3.474-1.072,6.001-2.348
		c4.303-2.161,5.784-3.453,5.784-3.453"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M38.217,86.756c0,0-10.123,7.107-18.804,8.819"/>
|
||||
</g>
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M52.169,74.885c0,0,1.235,0.165,4.744,3.676
	c3.509,3.508,6.026,2.16,8.911,0.724c2.877-1.443,10.537-6.126,6.49-9.817c-4.049-3.688-6.207,1.146-9.715,2.405
	c-3.512,1.26-5.061-2.487-6.858-4.287c-0.589-0.593-1.188-1.099-1.729-1.505c0,0-0.971-0.76-1.906,2.79
	C51.172,72.412,50.162,73.415,52.169,74.885z"/>
|
||||
<g id="Layer_2_1_">
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M46.753,82.012c1.188-0.912,2.397-2.402,3.951-4.713
		c1.296-1.927,2.7-5.578,2.7-5.578c0.875-2.521,1.934-6.576-1.902-7.296c-1.553-0.291-4.079-0.098-7.67-0.776
		c-3.593-0.681-6.798-2.522-9.517,2.233c-2.718,4.757-9.59,8.271-1.056,16.563c0,0,4.901,3.842,10.764,9.639
		c4.831,4.775,12.045,10.602,12.045,10.602s18.972,2.188,19.535-0.693c1.922-9.79-14.777-6.911-14.777-6.911
		s-4.605-3.933-6.725-5.794c-3.478-3.059-11.125-10.771-11.125-10.771"/>
|
||||
<path fill="#FFFFFF" d="M51.253,75.434c0,0,2.47-2.66-2.469-5.317c-4.939-2.657-7.213-0.017-8.739,1.521
		c-2.644,2.655,3.443,6.611,3.443,6.611l3.176,3.204c0,0,1.738-1.647,2.499-2.979C50.036,77.26,51.253,75.434,51.253,75.434"/>
|
||||
</g>
|
||||
<g id="Layer_8"/>
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M29.926,73.218c0.749-0.571,2.889-2.202,4.854-3.657
	c2.428-1.799,6.117-5.849,1.077-7.646c-5.04-1.801-7.507,1.604-11.519,4.946c-2.159,1.801-5.308,2.699-4.319,6.209
	c0.993,3.511,4.862,13.408,11.789,10.17c6.929-3.239-1.799-9.18-3.06-11.157"/>
|
||||
<g id="Layer_2">
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M52.709,14.156c-1.54-0.143-4.75-0.316-6.518-0.231
		c-4.728,0.225-9.224,1.928-9.224,1.928L23.949,7.357l2.235,18.906c0.646-0.782-10.555,12.804-3.479,24.224
		c7.08,11.426,22.233,16.518,40.988,12.792c18.755-3.729,23.229-14.531,21.986-20.246c-1.242-5.714-8.322-7.823-8.322-7.823
		s-0.09-4.48-3.328-9.97c-1.926-3.268-8.348-8.041-8.348-8.041L62.822,5.647l-7.452,7.204L52.709,14.156z"/>
|
||||
<path fill="#FFFFFF" d="M76.42,35.066l-2.482-2.064l-9.115,2.661c0,0,0,3.419-4.367,4.367c-4.37,0.951-11.211-2.277-11.211-2.277
		L41.46,41.17c0,0-8.437,0.928-8.739,6.081C32.048,58.704,46.1,63.479,51.425,63.783c2.905,0.167,8.235-0.338,12.277-1.141
		c17.752-3.234,22.551-13.919,21.31-19.635c-1.242-5.714-7.978-7.196-7.978-7.196L76.42,35.066z"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M10.673,46.155c0,0,4.107,0.374,5.974,0.268
		c1.865-0.107,5.492-0.587,5.492-0.587"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M81.656,40.671c0,0,4.549-0.743,6.859-1.549
		c2.715-0.942,4.543-2.545,4.543-2.545"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M22.337,41.909c0,0-2.384-1.777-6.117-3.43
		c-4.134-1.831-6.405-2.303-6.405-2.303"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M82.117,46.622c0,0,2.726,1.104,5.533,1.385
		c2.77,0.276,4.646,0.11,4.646,0.11"/>
|
||||
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" d="M52.35,14.212
		c2.84,0.7,3.887,1.469,3.887,1.469"/>
|
||||
<line fill="none" stroke="#000000" x1="33.898" y1="13.684" x2="39.956" y2="18.042"/>
|
||||
</g>
|
||||
<g id="Layer_5">
|
||||
<path fill="#FFFFFF" stroke="#231F20" d="M71.84,25.366c2.924,4.479,3.033,9.591,0.242,11.415
		c-2.793,1.825-7.426-0.332-10.354-4.813c-2.933-4.48-3.037-9.589-0.244-11.415C64.275,18.73,68.913,20.884,71.84,25.366z"/>
|
||||
<path fill="#231F20" d="M71.089,32.522c0,1.08-0.802,1.956-1.8,1.956c-0.993,0-1.803-0.877-1.803-1.956
		c0-1.08,0.81-1.958,1.803-1.958C70.287,30.564,71.089,31.442,71.089,32.522"/>
|
||||
</g>
|
||||
<g id="Layer_7">
|
||||
<path fill="#FFFFFF" stroke="#231F20" d="M47.867,28.619c2.926,4.48,2.619,9.862-0.681,12.015
		c-3.302,2.159-8.351,0.272-11.276-4.208c-2.928-4.48-2.624-9.86,0.678-12.017C39.891,22.253,44.938,24.137,47.867,28.619z"/>
|
||||
<path fill="#231F20" d="M46.079,34.507c0,1.081-0.803,1.957-1.801,1.957c-0.992,0-1.803-0.878-1.803-1.957
		c0-1.08,0.811-1.957,1.803-1.957C45.274,32.55,46.079,33.427,46.079,34.507"/>
|
||||
</g>
|
||||
<path fill="#5E4A42" stroke="#000000" d="M59.766,37.926c1.854,0,4.555-0.284,4.697,0.569c0.143,0.855-1.709,4.203-2.988,4.345
	c-1.283,0.142-6.125-2.353-6.195-3.919C55.206,37.355,58.055,37.926,59.766,37.926z"/>
|
||||
<g id="Layer_4">
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M46.774,45.235c0,0,10.347,3.054,14.217,3.897
		c3.868,0.842,10.851,1.684,10.851,1.684s-7.99,10.245-17.328,7.644C45.176,55.863,45.345,49.975,46.774,45.235z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.5 KiB |
@@ -1,37 +0,0 @@
|
||||
<svg version="1.1" id="cat" x="0px" y="0px" width="95px" height="111px" viewBox="0 0 95 111" enable-background="new 0 0 95 111" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g>
|
||||
<g id="Layer_8">
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M23.754,82.506c-2.871-1.402-10.511-2.518-14.771-6.695
		c-9.431-9.246-3.824-21.732,0.704-21.797c3.611-0.061,3.363,5.482,4.622,11.145c0.875,3.939,1.673,6.988,6.407,9.674
		c4.735,2.688,6.918,2.916,6.918,2.916s3.258,0.791,1.596,2.562C27.568,82.076,23.754,82.506,23.754,82.506z"/>
|
||||
<path fill="#FFFFFF" d="M14.291,66.369c-0.115-0.459-0.222-0.928-0.33-1.413c-1.172-5.271-0.941-10.432-4.303-10.377
		c-3.433,0.05-7.53,7.771-4.19,15.285c0.193-0.143,0.399-0.336,0.569-0.588c0.451-0.664,0.821-2.969,2.654-3.688
		C9.989,65.082,12.778,65.869,14.291,66.369z"/>
|
||||
</g>
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M52.543,61.473c0,0,1.455,2.342,1.519,4.969
	c0.065,2.723-0.351,6.984,1.804,8.158c3.381,1.842,9.959-0.463,10.174,5.191c0.211,5.652-10.152,3.623-13.299,2.547
	c-2.695-0.926-4.559-1.758-5.18-5.182c0,0-3.764-13.969-0.196-15.59C50.933,59.945,52.543,61.473,52.543,61.473z"/>
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M14.61,89.584c0,0,1.504,10.391-5.922,8.387
	c-5.586-1.51-5.705-20.68-0.406-20.691c3.237-0.006,4.425,2.859,4.425,2.859s4.385,4.812,6.95,4.312
	c2.565-0.502,7.978-6.703,7.978-6.703s2.875-2.539,6.146,0.023c3.271,2.561,1.521,6.178,1.521,6.178s-6.07,7.072-11.014,8.072
	c-4.945,1.002-9.375-2.568-11.58-3.451"/>
|
||||
<g id="Layer_6">
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M42.435,76.238c0,0,4.866,4.062,8.166,7.52
		c4.384,4.592-3.339,15.039-3.339,15.039s11.234,6.467,2.998,11c-2.657,1.467-15.661-13.225-15.661-13.225s7.979-7.475,4.972-10.172
		c-4.964-4.449-10.771-4.959-11.6-9.824c-0.502-2.957,0.613-4.502,2.658-7.053c1.354-1.689,2.084-3.182,2.084-3.182
		s1.135-7.238,4.289-8.254c3.153-1.014,8.842,0.057,10.758,0.395c1.914,0.338,6.645,1.877,4.617,9.922c0,0-1.451,3.771-2.789,5.762
		c-1.604,2.389-2.854,3.924-4.08,4.867"/>
|
||||
<path fill="#FFFFFF" d="M50.182,72.631c0,0,2.55-2.748-2.551-5.494c-5.102-2.746-7.449-0.016-9.024,1.572
		c-2.731,2.742,3.334,7.453,3.334,7.453l3.383,2.564c0,0,1.916-1.582,2.701-2.957C48.807,74.396,50.182,72.631,50.182,72.631"/>
|
||||
</g>
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M24.428,65.395c0.939,0.057,3.625,0.213,6.064,0.391
	c3.014,0.217,8.451-0.451,5.801-5.098c-2.649-4.65-6.74-3.674-11.961-3.754c-2.812-0.043-5.785-1.412-7.322,1.895
	c-1.534,3.312-5.051,13.338,2.313,15.396c7.366,2.057,4.618-8.137,4.95-10.457"/>
|
||||
<g id="Layer_2">
|
||||
<path fill="#FAA51D" stroke="#231F20" stroke-width="1.2" d="M51.709,10.156c-1.54-0.143-4.75-0.316-6.518-0.231
		c-4.728,0.225-9.224,1.928-9.224,1.928L22.949,3.357l2.235,18.906c0.646-0.782-10.555,12.804-3.479,24.224
		c7.08,11.426,22.233,16.518,40.988,12.792c18.755-3.728,23.229-14.531,21.986-20.246c-1.242-5.714-8.322-7.823-8.322-7.823
		s-0.09-4.48-3.328-9.97c-1.926-3.268-8.348-8.041-8.348-8.041L61.822,1.647L54.37,8.851L51.709,10.156z"/>
|
||||
<path fill="#FFFFFF" d="M75.42,31.066l-2.483-2.064l-9.115,2.661c0,0,0,3.419-4.367,4.367c-4.37,0.951-11.211-2.277-11.211-2.277
		l-7.784,3.417c0,0-8.437,0.928-8.739,6.081C31.048,54.704,45.1,59.478,50.425,59.783c2.905,0.167,8.235-0.337,12.277-1.14
		c17.752-3.235,22.551-13.92,21.309-19.635c-1.242-5.714-7.977-7.196-7.977-7.196L75.42,31.066z"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M9.673,42.155c0,0,4.107,0.374,5.974,0.268
		c1.865-0.107,5.492-0.587,5.492-0.587"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M80.656,36.671c0,0,4.549-0.743,6.859-1.549
		c2.715-0.942,4.543-2.545,4.543-2.545"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M21.337,37.909c0,0-2.384-1.777-6.117-3.43
		c-4.134-1.831-6.405-2.303-6.405-2.303"/>
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M81.117,42.622c0,0,2.726,1.104,5.533,1.385
		c2.77,0.276,4.647,0.11,4.647,0.11"/>
|
||||
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-miterlimit="10" d="M51.349,10.212
		c2.84,0.7,3.888,1.469,3.888,1.469"/>
|
||||
<line fill="none" stroke="#000000" x1="32.898" y1="9.684" x2="38.956" y2="14.042"/>
|
||||
</g>
|
||||
<g id="Layer_5">
|
||||
<path fill="#FFFFFF" stroke="#231F20" d="M70.84,21.366c2.924,4.479,3.033,9.591,0.242,11.415
		c-2.793,1.825-7.426-0.332-10.354-4.813c-2.932-4.48-3.037-9.589-0.244-11.415C63.275,14.73,67.913,16.884,70.84,21.366z"/>
|
||||
<path fill="#231F20" d="M70.089,28.522c0,1.08-0.802,1.956-1.8,1.956c-0.993,0-1.803-0.877-1.803-1.956
		c0-1.08,0.81-1.958,1.803-1.958C69.287,26.564,70.089,27.442,70.089,28.522"/>
|
||||
</g>
|
||||
<g id="Layer_7">
|
||||
<path fill="#FFFFFF" stroke="#231F20" d="M46.867,24.619c2.926,4.48,2.619,9.862-0.681,12.015
		c-3.302,2.159-8.351,0.272-11.276-4.208c-2.928-4.48-2.624-9.86,0.678-12.017C38.891,18.253,43.938,20.137,46.867,24.619z"/>
|
||||
<path fill="#231F20" d="M45.079,30.507c0,1.081-0.803,1.957-1.801,1.957c-0.992,0-1.803-0.878-1.803-1.957
		c0-1.08,0.811-1.957,1.803-1.957C44.274,28.55,45.079,29.427,45.079,30.507"/>
|
||||
</g>
|
||||
<path fill="#5E4A42" stroke="#000000" d="M58.766,33.926c1.854,0,4.555-0.284,4.697,0.569c0.143,0.855-1.709,4.203-2.988,4.345
	c-1.283,0.142-6.125-2.353-6.195-3.919C54.206,33.355,57.055,33.926,58.766,33.926z"/>
|
||||
<g id="Layer_4">
|
||||
<path fill="none" stroke="#231F20" stroke-width="1.2" d="M45.774,41.235c0,0,10.347,3.054,14.217,3.897
		c3.868,0.842,10.851,1.684,10.851,1.684s-7.99,10.245-17.328,7.644C44.176,51.863,44.345,45.975,45.774,41.235z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
@@ -0,0 +1,42 @@
|
||||
<svg width="96px" height="101px" viewBox="0 0 96 101" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 52.5 (67469) - http://www.bohemiancoding.com/sketch -->
|
||||
<g>
|
||||
<title>costume1.1</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="costume1" transform="translate(-13.000000, -10.000000)">
|
||||
<g id="costume1.1" transform="translate(13.000000, 10.000000)">
|
||||
<g id="tail" transform="translate(0.000000, 59.000000)">
|
||||
<path d="M21.9,14.8 C19.5,14.3 16.6,13.5 14.2,11.3 C8.7,6.4 7,-1.7 3.2,0.4 C-0.7,2.5 -0.6,15.6 11.6,19.6 C15.8,21 19.6,21 22.7,20.9 C23.5,20.9 30.4,20.2 32.8,16.8 C35.2,13.4 33.5,12.5 32.7,12.1 C31.8,11.6 25.3,15.4 21.9,14.8 Z" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.8,0.6 C1.8,1.2 0.8,5.4 1.8,8.9 C2.8,12.4 4.4,14.2 5.7,15.5 C5.5,14.8 5.1,12.6 6.8,11.3 C8.9,9.6 12.6,10.5 12.6,10.5 C12.6,10.5 9.5,6.7 7.9,4 C6.3,1.7 5.8,0.2 3.8,0.6 Z" id="detail" fill="#FFFFFF"/>
|
||||
</g>
|
||||
<path d="M37.7,81.5 C35.9,82.7 29.7,87.1 21.8,89.6 L21.4,89.7 C21,89.8 20.8,90.3 21,90.7 C22.7,93.1 25.8,97.9 20.3,99.6 C15,101.3 5.1,87.2 9.3,83.5 C11.2,82.1 12.9,82.8 13.8,83.2 C14.3,83.4 14.8,83.4 15.3,83.3 C16.5,82.9 18.7,82.1 20.4,81.2 C24.7,79 25.7,78.1 27.7,76.6 C29.7,75.1 34.3,71.4 38,74.6 C41.2,77.3 39.4,80.3 37.7,81.5 Z" id="leg" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M53.6,60.7 C54.1,61.1 60.2,68.3 62.2,66.5 C64.6,64.4 67.9,60.3 71.5,63.6 C75.1,66.9 68.3,72.5 65.4,74 C58.5,77.1 52.9,71.2 51.7,69.6 C50.5,68 48.4,65.3 48.4,62.7 C48.5,59.9 51.9,59.2 53.6,60.7 Z" id="arm" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="body-and-leg" transform="translate(28.000000, 57.000000)">
|
||||
<path d="M18.2,19.7 C19.4,18.8 20.6,17.3 22.2,15 C23.5,13.1 24.9,9.4 24.9,9.4 C25.8,6.9 26.4,2.1 23.1,2.2 C20.9,2.3 18.9,2 15.5,1.5 C9.5,0.3 8.4,-0.5 5.9,3.6 C3.2,8.4 -3.7,11.9 4.8,20.2 C4.8,20.2 9.7,24 15.6,29.8 C19.6,33.7 25.9,39.3 28.1,41.2 C28.6,41.6 29.2,41.8 29.8,41.9 C39.5,42.8 46.7,41.8 46.7,37.5 C46.7,30.3 32.4,32.8 32.4,32.8 C32.4,32.8 27.8,28.9 25.7,27 L18.2,19.7 Z" id="body" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M22.6,13 C22.6,13 24.5,10.5 20.2,7.8 C15.7,4.9 14,8.1 12.2,10.5 C10.2,13.6 12.2,15.1 14.2,16.9 C15.8,18.4 17.3,19.6 17.3,19.6 C17.3,19.6 20.4,17.5 22.6,13 Z" id="tummy" fill="#FFFFFF"/>
|
||||
</g>
|
||||
<path d="M30.2,68.4 C32.4,71.2 35.8,74.7 31.5,77.6 C25.6,80.9 20.7,70.9 19.7,67.4 C18.8,64.3 21.4,62.3 23.6,60.6 C27.9,57.5 31.5,54.7 35.5,56.2 C40.5,58 36.9,62 34.4,63.8 C32.9,64.9 31.4,66.1 30.3,66.8 C30,67.3 29.9,67.9 30.2,68.4 Z" id="arm" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="head" transform="translate(14.000000, 0.000000)">
|
||||
<path d="M39.1,9 C36.8,8.6 34.4,8.4 31.6,8.6 C26.9,8.8 22.4,10.5 22.4,10.5 L10.3,2.6 C9.9,2.4 9.4,2.7 9.5,3.1 L11.6,21 C12.2,20.2 1,33.8 8.1,45.2 C15.2,56.6 30.3,61.7 49.1,58 C67.9,54.3 72.3,43.5 71.1,37.8 C69.9,32.1 62.8,30 62.8,30 C62.8,30 62.7,25.5 59.5,20 C57.6,16.7 51.2,12 51.2,12 L48.6,1.3 C48.5,0.9 48,0.8 47.7,1 L39.1,9 Z" stroke="#001026" stroke-width="1.2" fill="#FFAB19"/>
|
||||
<path d="M62.5,30.4 C62.5,30.4 69.4,32.2 70.6,37.9 C71.8,43.6 67,53.9 48.4,57.5 C24.2,62.5 12.7,48.1 19.4,37.5 C26.1,26.8 37.6,35.9 46,35.3 C53.2,34.8 54,28.5 62.5,30.4 Z" id="face" fill="#FFFFFF"/>
|
||||
<path d="M31,41.1 C31,40.7 31.4,40.4 31.8,40.5 C33.7,41.2 39.1,42.8 45.1,43.2 C50.5,43.5 53.7,43.2 55.2,42.9 C55.7,42.8 56.1,43.3 55.9,43.8 C55,46.5 51.2,54 40.7,53.4 C31.6,52.4 30.7,46 31,41.1 Z" id="mouth" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M69,35.4 C69,35.4 76.2,35.3 80.9,31.5" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M69.4,41.3 C69.4,41.3 73.3,43.2 79.6,42.7" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M45.6,32.7 C47.7,32.7 49.9,32.9 50,33.6 C50.1,35 48.6,37.8 47,37.9 C45.2,38.1 41,35.6 41,34 C40.9,32.8 43.6,32.7 45.6,32.7 Z" id="nose" stroke="#001026" stroke-width="1.2" fill="#001026" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M0.6,31.2 C0.6,31.2 9.2,34 12.7,37.1" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M1.3,41.2 C1.3,41.2 8.7,42.3 13,40.6" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="eye" transform="translate(44.000000, 15.000000)">
|
||||
<path d="M13.4,6 C16.3,10.5 16.4,15.6 13.6,17.4 C10.8,19.2 6.2,17.1 3.2,12.6 C0.3,8.1 0.2,3 3,1.2 C5.8,-0.7 10.5,1.5 13.4,6 Z" id="pupil" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13,11.7 C13,12.8 12.2,13.7 11.2,13.7 C10.2,13.7 9.4,12.8 9.4,11.7 C9.4,10.6 10.2,9.7 11.2,9.7 C12.2,9.7 13,10.6 13,11.7" id="pupil" fill="#001026"/>
|
||||
</g>
|
||||
<g id="eye" transform="translate(19.000000, 18.000000)">
|
||||
<path d="M13.6,5.8 C16.6,10.2 16.4,15.6 13.7,17.5 C10.4,19.4 6,18 3,13.6 C-0.1,9.2 -0.3,3.5 2.8,1.3 C5.9,-1 10.6,1.4 13.6,5.8 Z" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13,11.6 C13,12.7 12.2,13.6 11.2,13.6 C10.2,13.6 9.4,12.7 9.4,11.6 C9.4,10.5 10.2,9.6 11.2,9.6 C12.2,9.7 13,10.5 13,11.6" id="pupil" fill="#001026"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1,42 @@
|
||||
<svg width="93px" height="106px" viewBox="0 0 93 106" version="1.1" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 52.5 (67469) - http://www.bohemiancoding.com/sketch -->
|
||||
<g>
|
||||
<title>costume2.1</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="cotsume2" transform="translate(-16.000000, -10.000000)">
|
||||
<g id="costume2.1" transform="translate(16.250000, 9.500000)">
|
||||
<g id="tail" transform="translate(0.000000, 53.000000)">
|
||||
<path d="M12,14.8 C8.9,7 10.6,0 6.3,0.4 C1.9,0.8 -3.2,12.5 6.4,21.1 C15.3,29 24.4,26.1 28,24 C31.5,21.8 28.8,20.6 28.2,19.8 C27.6,19 16.4,26.1 12,14.8 Z" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.8,0.9 C4.3,0.5 2.1,4.1 1.6,7.6 C1.4,11.2 2,13.3 2.7,15 C2.8,14.3 3.7,11.9 6.6,11.4 C9,11 10.6,13 10.6,13 C10.6,13 9.6,10 9.1,5.6 C8.7,2.9 8.8,1.3 6.8,0.9 Z" id="detail" fill="#FFFFFF"/>
|
||||
</g>
|
||||
<path d="M32.9,83.2 C32.2,84.1 30.2,87.6 25.4,90.6 C22.1,92.7 13.6,89.2 13.6,89.2 C14.5,98.4 7,98.2 5.6,95.2 C4.5,92.8 4.7,82.5 6.9,81.7 C10.3,80.5 14.9,82.5 19,82.5 C20.2,82.5 21.5,79.9 24.3,77.1 C26.3,75.1 28.9,71.4 33.1,74 C36.7,76.3 34.8,80.5 32.9,83.2 Z" id="leg" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M50.1,62 C50.4,64.2 50.6,72.6 53,73.9 C55.3,75.1 62.3,73.1 62.7,78 C63.1,82.8 56.6,82.9 51.2,81.4 C44.2,79.5 41,68.8 45,62.2 C46.5,59.9 49.8,59.7 50.1,62 Z" id="arm" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="body-and-leg" transform="translate(24.000000, 57.000000)">
|
||||
<path d="M18.2,21.3 C19.4,20.4 21.2,18 22.8,15.6 C24.1,13.7 25.5,10 25.5,10 C26.4,7.5 27,2.7 23.7,2.8 C21.5,2.9 19.5,2.6 16.1,2.1 C10.1,0.9 8.9,-0.3 6.5,4.2 C2,12.5 -4,18.4 4.2,23.2 C6.2,24.4 9.4,26.4 12.2,28.4 C14.2,30.3 11.2,34.4 9.1,36.8 C8.2,37.9 8.2,39.5 9.3,40.6 C16.6,47.7 18.8,50.2 21.6,47.1 C25.4,42.9 19.4,38.8 19.4,38.8 C19.4,38.8 24.2,31.3 23.6,28 C22.9,24.8 18.2,21.3 18.2,21.3 Z" id="body" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M22.7284271,14.6028611 C22.7284271,14.6028611 24.6284271,12.1028611 20.3284271,9.40286109 C15.8284271,6.50286109 14.1284271,9.70286109 12.3284271,12.1028611 C10.3284271,15.2028611 12.3284271,16.7028611 14.3284271,18.5028611 C15.9284271,20.0028611 17.4284271,21.2028611 17.4284271,21.2028611 C17.4284271,21.2028611 20.5284271,19.1028611 22.7284271,14.6028611 Z" id="tummy" fill="#FFFFFF"/>
|
||||
</g>
|
||||
<path d="M32.3,65.2 C30.1,66.2 26,64.4 24.9,64.6 C24.6,64.7 22.8,69.9 21.7,73 C20.2,77.4 13.4,76.1 13,71.5 C12.7,67.2 16.6,58.9 17.9,57.4 C20.8,53.9 30.6591797,56.7082962 37.2463379,58.456543" id="arm" stroke="#001026" stroke-width="1.2" fill="#FFAB19" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="head" transform="translate(10.750000, 0.500000)">
|
||||
<path d="M39.1,9 C36.8,8.6 34.4,8.4 31.6,8.6 C26.9,8.8 22.4,10.5 22.4,10.5 L10.3,2.6 C9.9,2.4 9.4,2.7 9.5,3.1 L11.6,21 C12.2,20.2 1,33.8 8.1,45.2 C15.2,56.6 30.3,61.7 49.1,58 C67.9,54.3 72.3,43.5 71.1,37.8 C69.9,32.1 62.8,30 62.8,30 C62.8,30 62.7,25.5 59.5,20 C57.6,16.7 51.2,12 51.2,12 L48.6,1.3 C48.5,0.9 48,0.8 47.7,1 L39.1,9 Z" stroke="#001026" stroke-width="1.2" fill="#FFAB19"/>
|
||||
<path d="M62.5,30.4 C62.5,30.4 69.4,32.2 70.6,37.9 C71.8,43.6 67,53.9 48.4,57.5 C24.2,62.5 12.7,48.1 19.4,37.5 C26.1,26.8 37.6,35.9 46,35.3 C53.2,34.8 54,28.5 62.5,30.4 Z" id="face" fill="#FFFFFF"/>
|
||||
<path d="M31,41.1 C31,40.7 31.4,40.4 31.8,40.5 C33.7,41.2 39.1,42.8 45.1,43.2 C50.5,43.5 53.7,43.2 55.2,42.9 C55.7,42.8 56.1,43.3 55.9,43.8 C55,46.5 51.2,54 40.7,53.4 C31.6,52.4 30.7,46 31,41.1 Z" id="mouth" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M69,35.4 C69,35.4 76.2,35.3 80.9,31.5" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M69.4,41.3 C69.4,41.3 73.3,43.2 79.6,42.7" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M45.6,32.7 C47.7,32.7 49.9,32.9 50,33.6 C50.1,35 48.6,37.8 47,37.9 C45.2,38.1 41,35.6 41,34 C40.9,32.8 43.6,32.7 45.6,32.7 Z" id="nose" stroke="#001026" stroke-width="1.2" fill="#001026" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M0.6,31.2 C0.6,31.2 9.2,34 12.7,37.1" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M1.3,41.2 C1.3,41.2 8.7,42.3 13,40.6" id="whisker" stroke="#001026" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="eye" transform="translate(44.000000, 15.000000)">
|
||||
<path d="M13.4,6 C16.3,10.5 16.4,15.6 13.6,17.4 C10.8,19.2 6.2,17.1 3.2,12.6 C0.3,8.1 0.2,3 3,1.2 C5.8,-0.7 10.5,1.5 13.4,6 Z" id="pupil" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13,11.7 C13,12.8 12.2,13.7 11.2,13.7 C10.2,13.7 9.4,12.8 9.4,11.7 C9.4,10.6 10.2,9.7 11.2,9.7 C12.2,9.7 13,10.6 13,11.7" id="pupil" fill="#001026"/>
|
||||
</g>
|
||||
<g id="eye" transform="translate(19.000000, 18.000000)">
|
||||
<path d="M13.6,5.8 C16.6,10.2 16.4,15.6 13.7,17.5 C10.4,19.4 6,18 3,13.6 C-0.1,9.2 -0.3,3.5 2.8,1.3 C5.9,-1 10.6,1.4 13.6,5.8 Z" stroke="#001026" stroke-width="1.2" fill="#FFFFFF" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13,11.6 C13,12.7 12.2,13.6 11.2,13.6 C10.2,13.6 9.4,12.7 9.4,11.6 C9.4,10.5 10.2,9.6 11.2,9.6 C12.2,9.7 13,10.5 13,11.6" id="pupil" fill="#001026"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@@ -4,8 +4,8 @@ import projectData from './project-data';
|
||||
import popWav from '!arraybuffer-loader!./83a9787d4cb6f3b7632b4ddfebf74367.wav';
|
||||
import meowWav from '!arraybuffer-loader!./83c36d806dc92327b9e7049a565c6bff.wav';
|
||||
import backdrop from '!raw-loader!./cd21514d0531fdffb22204e0ec5ed84a.svg';
|
||||
import costume1 from '!raw-loader!./09dc888b0b7df19f70d81588ae73420e.svg';
|
||||
import costume2 from '!raw-loader!./3696356a03a8d938318876a593572843.svg';
|
||||
import costume1 from '!raw-loader!./b7853f557e4426412e64bb3da6531a99.svg';
|
||||
import costume2 from '!raw-loader!./e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg';
|
||||
/* eslint-enable import/no-unresolved */
|
||||
|
||||
const defaultProject = translator => {
|
||||
@@ -40,12 +40,12 @@ const defaultProject = translator => {
|
||||
dataFormat: 'SVG',
|
||||
data: encoder.encode(backdrop)
|
||||
}, {
|
||||
id: '09dc888b0b7df19f70d81588ae73420e',
|
||||
id: 'b7853f557e4426412e64bb3da6531a99',
|
||||
assetType: 'ImageVector',
|
||||
dataFormat: 'SVG',
|
||||
data: encoder.encode(costume1)
|
||||
}, {
|
||||
id: '3696356a03a8d938318876a593572843',
|
||||
id: 'e6ddc55a6ddd9cc9d84fe0b4c21e016f',
|
||||
assetType: 'ImageVector',
|
||||
dataFormat: 'SVG',
|
||||
data: encoder.encode(costume2)
|
||||
|
||||
@@ -240,7 +240,7 @@ export default [
|
||||
/>
|
||||
),
|
||||
featured: true,
|
||||
disabled: true,
|
||||
disabled: false,
|
||||
bluetoothRequired: true,
|
||||
internetConnectionRequired: true,
|
||||
launchPeripheralConnectionFlow: true,
|
||||
|
||||
@@ -115,8 +115,10 @@ const ProjectFetcherHOC = function (WrappedComponent) {
|
||||
assetHost: PropTypes.string,
|
||||
canSave: PropTypes.bool,
|
||||
intl: intlShape.isRequired,
|
||||
isCreatingNew: PropTypes.bool,
|
||||
isFetchingWithId: PropTypes.bool,
|
||||
isLoadingProject: PropTypes.bool,
|
||||
isShowingProject: PropTypes.bool,
|
||||
loadingState: PropTypes.oneOf(LoadingStates),
|
||||
onActivateTab: PropTypes.func,
|
||||
onError: PropTypes.func,
|
||||
|
||||
@@ -343,6 +343,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
|
||||
}
|
||||
|
||||
ProjectSaverComponent.propTypes = {
|
||||
autoSaveIntervalSecs: PropTypes.number.isRequired,
|
||||
autoSaveTimeoutId: PropTypes.number,
|
||||
canCreateNew: PropTypes.bool,
|
||||
canSave: PropTypes.bool,
|
||||
@@ -358,12 +359,16 @@ const ProjectSaverHOC = function (WrappedComponent) {
|
||||
isShowingWithoutId: PropTypes.bool,
|
||||
isUpdating: PropTypes.bool,
|
||||
loadingState: PropTypes.oneOf(LoadingStates),
|
||||
locale: PropTypes.string.isRequired,
|
||||
onAutoUpdateProject: PropTypes.func,
|
||||
onCreateProject: PropTypes.func,
|
||||
onCreatedProject: PropTypes.func,
|
||||
onProjectError: PropTypes.func,
|
||||
onProjectTelemetryEvent: PropTypes.func,
|
||||
onRemixing: PropTypes.func,
|
||||
onSetProjectSaver: PropTypes.func.isRequired,
|
||||
onSetProjectThumbnailer: PropTypes.func.isRequired,
|
||||
onSetProjectUnchanged: PropTypes.func.isRequired,
|
||||
onShowAlert: PropTypes.func,
|
||||
onShowCopySuccessAlert: PropTypes.func,
|
||||
onShowCreatingCopyAlert: PropTypes.func,
|
||||
@@ -377,6 +382,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
|
||||
projectChanged: PropTypes.bool,
|
||||
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
reduxProjectTitle: PropTypes.string,
|
||||
setAutoSaveTimeoutId: PropTypes.func.isRequired,
|
||||
vm: PropTypes.instanceOf(VM).isRequired
|
||||
};
|
||||
ProjectSaverComponent.defaultProps = {
|
||||
|
||||
@@ -60,9 +60,9 @@ const vmListenerHOC = function (WrappedComponent) {
|
||||
this.props.vm.postIOData('userData', {username: this.props.username});
|
||||
}
|
||||
|
||||
// Re-request a targets update when the shouldEmitTargetsUpdate state changes to true
|
||||
// Re-request a targets update when the shouldEmitUpdate state changes to true
|
||||
// i.e. when the editor transitions out of fullscreen/player only modes
|
||||
if (this.props.shouldEmitTargetsUpdate && !prevProps.shouldEmitTargetsUpdate) {
|
||||
if (this.props.shouldEmitUpdates && !prevProps.shouldEmitUpdates) {
|
||||
this.props.vm.emitTargetsUpdate(false /* Emit the event, but do not trigger project change */);
|
||||
}
|
||||
}
|
||||
@@ -148,8 +148,10 @@ const vmListenerHOC = function (WrappedComponent) {
|
||||
onKeyUp: PropTypes.func,
|
||||
onMicListeningUpdate: PropTypes.func.isRequired,
|
||||
onMonitorsUpdate: PropTypes.func.isRequired,
|
||||
onProjectChanged: PropTypes.func.isRequired,
|
||||
onProjectRunStart: PropTypes.func.isRequired,
|
||||
onProjectRunStop: PropTypes.func.isRequired,
|
||||
onProjectSaved: PropTypes.func.isRequired,
|
||||
onRuntimeStarted: PropTypes.func.isRequired,
|
||||
onShowExtensionAlert: PropTypes.func.isRequired,
|
||||
onTargetsUpdate: PropTypes.func.isRequired,
|
||||
|
||||
@@ -107,6 +107,7 @@ const vmManagerHOC = function (WrappedComponent) {
|
||||
fontsLoaded: PropTypes.bool,
|
||||
isLoadingWithId: PropTypes.bool,
|
||||
isPlayerOnly: PropTypes.bool,
|
||||
isStarted: PropTypes.bool,
|
||||
loadingState: PropTypes.oneOf(LoadingStates),
|
||||
locale: PropTypes.string,
|
||||
messages: PropTypes.objectOf(PropTypes.string),
|
||||
|
||||
@@ -3,6 +3,7 @@ import analytics from '../lib/analytics';
|
||||
import decks from '../lib/libraries/decks/index.jsx';
|
||||
|
||||
const CLOSE_CARDS = 'scratch-gui/cards/CLOSE_CARDS';
|
||||
const SHRINK_EXPAND_CARDS = 'scratch-gui/cards/SHRINK_EXPAND_CARDS';
|
||||
const VIEW_CARDS = 'scratch-gui/cards/VIEW_CARDS';
|
||||
const ACTIVATE_DECK = 'scratch-gui/cards/ACTIVATE_DECK';
|
||||
const NEXT_STEP = 'scratch-gui/cards/NEXT_STEP';
|
||||
@@ -18,6 +19,7 @@ const initialState = {
|
||||
step: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
expanded: true,
|
||||
dragging: false
|
||||
};
|
||||
|
||||
@@ -28,6 +30,10 @@ const reducer = function (state, action) {
|
||||
return Object.assign({}, state, {
|
||||
visible: false
|
||||
});
|
||||
case SHRINK_EXPAND_CARDS:
|
||||
return Object.assign({}, state, {
|
||||
expanded: !state.expanded
|
||||
});
|
||||
case VIEW_CARDS:
|
||||
return Object.assign({}, state, {
|
||||
visible: true
|
||||
@@ -36,6 +42,9 @@ const reducer = function (state, action) {
|
||||
return Object.assign({}, state, {
|
||||
activeDeckId: action.activeDeckId,
|
||||
step: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
expanded: true,
|
||||
visible: true
|
||||
});
|
||||
case NEXT_STEP:
|
||||
@@ -92,6 +101,10 @@ const closeCards = function () {
|
||||
return {type: CLOSE_CARDS};
|
||||
};
|
||||
|
||||
const shrinkExpandCards = function () {
|
||||
return {type: SHRINK_EXPAND_CARDS};
|
||||
};
|
||||
|
||||
const nextStep = function () {
|
||||
return {type: NEXT_STEP};
|
||||
};
|
||||
@@ -118,6 +131,7 @@ export {
|
||||
activateDeck,
|
||||
viewCards,
|
||||
closeCards,
|
||||
shrinkExpandCards,
|
||||
nextStep,
|
||||
prevStep,
|
||||
dragCard,
|
||||
|
||||
@@ -107,6 +107,7 @@ const initTutorialCard = function (currentState, deckId) {
|
||||
visible: true,
|
||||
content: decks,
|
||||
activeDeckId: deckId,
|
||||
expanded: true,
|
||||
step: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
||||
@@ -8,6 +8,11 @@ const {By, until, Button} = webdriver;
|
||||
|
||||
const USE_HEADLESS = process.env.USE_HEADLESS !== 'no';
|
||||
|
||||
// The main reason for this timeout is so that we can control the timeout message and report details;
|
||||
// if we hit the Jasmine default timeout then we get a terse message that we can't control.
|
||||
// The Jasmine default timeout is 30 seconds so make sure this is lower.
|
||||
const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000;
|
||||
|
||||
class SeleniumHelper {
|
||||
constructor () {
|
||||
bindAll(this, [
|
||||
@@ -21,13 +26,12 @@ class SeleniumHelper {
|
||||
'getSauceDriver',
|
||||
'getLogs',
|
||||
'loadUri',
|
||||
'rightClickText',
|
||||
'waitUntilGone'
|
||||
'rightClickText'
|
||||
]);
|
||||
}
|
||||
|
||||
elementIsVisible (element) {
|
||||
return this.driver.wait(until.elementIsVisible(element));
|
||||
elementIsVisible (element, timeoutMessage = 'elementIsVisible timed out') {
|
||||
return this.driver.wait(until.elementIsVisible(element), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage);
|
||||
}
|
||||
|
||||
get scope () {
|
||||
@@ -79,8 +83,12 @@ class SeleniumHelper {
|
||||
return this.driver;
|
||||
}
|
||||
|
||||
findByXpath (xpath) {
|
||||
return this.driver.wait(until.elementLocated(By.xpath(xpath), 5 * 1000));
|
||||
findByXpath (xpath, timeoutMessage = `findByXpath timed out for path: ${xpath}`) {
|
||||
return this.driver.wait(until.elementLocated(By.xpath(xpath)), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage)
|
||||
.then(el => (
|
||||
this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS, `${xpath} is not visible`)
|
||||
.then(() => el)
|
||||
));
|
||||
}
|
||||
|
||||
findByText (text, scope) {
|
||||
@@ -120,10 +128,6 @@ class SeleniumHelper {
|
||||
return this.clickXpath(`//button//*[contains(text(), '${text}')]`);
|
||||
}
|
||||
|
||||
waitUntilGone (element) {
|
||||
return this.driver.wait(until.stalenessOf(element));
|
||||
}
|
||||
|
||||
getLogs (whitelist) {
|
||||
if (!whitelist) {
|
||||
// Default whitelist
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('Working with backdrops', () => {
|
||||
await driver.quit();
|
||||
});
|
||||
|
||||
test('Adding a backdrop from the library', async () => {
|
||||
test('Adding a backdrop from the library should not switch to stage', async () => {
|
||||
await loadUri(uri);
|
||||
|
||||
// Start on the sounds tab of sprite1 to test switching behavior
|
||||
@@ -37,12 +37,11 @@ describe('Working with backdrops', () => {
|
||||
await el.sendKeys('blue');
|
||||
await clickText('Blue Sky'); // Adds the backdrop
|
||||
|
||||
// Make sure the stage is selected and the sound tab remains selected.
|
||||
// This is different from Scratch2 which selected backdrop tab automatically
|
||||
// See issue #3500
|
||||
await clickText('pop', scope.soundsTab);
|
||||
// Make sure the sprite is still selected, and that the tab has not changed
|
||||
await clickText('Meow', scope.soundsTab);
|
||||
|
||||
// Make sure the backdrop was actually added by going to the backdrops tab
|
||||
await clickXpath('//span[text()="Stage"]');
|
||||
await clickText('Backdrops');
|
||||
await clickText('Blue Sky', scope.costumesTab);
|
||||
|
||||
@@ -50,7 +49,48 @@ describe('Working with backdrops', () => {
|
||||
await expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
test('Adding multiple backdrops at the same time', async () => {
|
||||
test('Adding backdrop via paint should switch to stage', async () => {
|
||||
await loadUri(uri);
|
||||
|
||||
const buttonXpath = '//button[@aria-label="Choose a Backdrop"]';
|
||||
const paintXpath = `${buttonXpath}/following-sibling::div//button[@aria-label="Paint"]`;
|
||||
|
||||
const el = await findByXpath(buttonXpath);
|
||||
await driver.actions().mouseMove(el)
|
||||
.perform();
|
||||
await driver.sleep(500); // Wait for thermometer menu to come up
|
||||
await clickXpath(paintXpath);
|
||||
|
||||
// Stage should become selected and costume tab activated
|
||||
await findByText('backdrop2', scope.costumesTab);
|
||||
|
||||
const logs = await getLogs();
|
||||
await expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
test('Adding backdrop via surprise should not switch to stage', async () => {
|
||||
await loadUri(uri);
|
||||
|
||||
// Start on the sounds tab of sprite1 to test switching behavior
|
||||
await clickText('Sounds');
|
||||
|
||||
const buttonXpath = '//button[@aria-label="Choose a Backdrop"]';
|
||||
const surpriseXpath = `${buttonXpath}/following-sibling::div//button[@aria-label="Surprise"]`;
|
||||
|
||||
const el = await findByXpath(buttonXpath);
|
||||
await driver.actions().mouseMove(el)
|
||||
.perform();
|
||||
await driver.sleep(500); // Wait for thermometer menu to come up
|
||||
await clickXpath(surpriseXpath);
|
||||
|
||||
// Make sure the sprite is still selected, and that the tab has not changed
|
||||
await clickText('Meow', scope.soundsTab);
|
||||
|
||||
const logs = await getLogs();
|
||||
await expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
test('Adding multiple backdrops from file should switch to stage', async () => {
|
||||
const files = [
|
||||
path.resolve(__dirname, '../fixtures/gh-3582-png.png'),
|
||||
path.resolve(__dirname, '../fixtures/100-100.svg')
|
||||
@@ -67,7 +107,7 @@ describe('Working with backdrops', () => {
|
||||
const input = await findByXpath(fileXpath);
|
||||
await input.sendKeys(files.join('\n'));
|
||||
|
||||
await clickXpath('//span[text()="Stage"]');
|
||||
// Should have been switched to stage/costume tab already
|
||||
await findByText('gh-3582-png', scope.costumesTab);
|
||||
await findByText('100-100', scope.costumesTab);
|
||||
|
||||
|
||||
@@ -4,15 +4,13 @@ import path from 'path';
|
||||
import SeleniumHelper from '../helpers/selenium-helper';
|
||||
|
||||
const {
|
||||
findByText,
|
||||
clickButton,
|
||||
clickText,
|
||||
clickXpath,
|
||||
findByXpath,
|
||||
getDriver,
|
||||
getLogs,
|
||||
loadUri,
|
||||
waitUntilGone
|
||||
loadUri
|
||||
} = new SeleniumHelper();
|
||||
|
||||
let driver;
|
||||
@@ -28,10 +26,9 @@ describe('player example', () => {
|
||||
await driver.quit();
|
||||
});
|
||||
|
||||
test('Load a project by ID', async () => {
|
||||
test.skip('Player: load a project by ID', async () => {
|
||||
const projectId = '96708228';
|
||||
await loadUri(`${uri}#${projectId}`);
|
||||
await waitUntilGone(findByText('Loading'));
|
||||
await clickXpath('//img[@title="Go"]');
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await clickXpath('//img[@title="Stop"]');
|
||||
@@ -59,7 +56,7 @@ describe('blocks example', () => {
|
||||
await driver.quit();
|
||||
});
|
||||
|
||||
test('Load a project by ID', async () => {
|
||||
test.skip('Blocks: load a project by ID', async () => {
|
||||
const projectId = '96708228';
|
||||
await loadUri(`${uri}#${projectId}`);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
@@ -9,8 +9,7 @@ const {
|
||||
getDriver,
|
||||
loadUri,
|
||||
rightClickText,
|
||||
scope,
|
||||
waitUntilGone
|
||||
scope
|
||||
} = new SeleniumHelper();
|
||||
|
||||
const uri = path.resolve(__dirname, '../../build/index.html');
|
||||
@@ -78,7 +77,6 @@ describe('Menu bar settings', () => {
|
||||
await clickText('File');
|
||||
const input = await findByXpath('//input[@accept=".sb,.sb2,.sb3"]');
|
||||
await input.sendKeys(path.resolve(__dirname, '../fixtures/project1.sb3'));
|
||||
await waitUntilGone(findByText('Loading'));
|
||||
// No replace alert since no changes were made
|
||||
await findByText('project1-sprite');
|
||||
});
|
||||
@@ -95,7 +93,6 @@ describe('Menu bar settings', () => {
|
||||
await input.sendKeys(path.resolve(__dirname, '../fixtures/project1.sb3'));
|
||||
await driver.switchTo().alert()
|
||||
.accept();
|
||||
await waitUntilGone(findByText('Loading'));
|
||||
await findByText('project1-sprite');
|
||||
});
|
||||
});
|
||||
@@ -9,8 +9,7 @@ const {
|
||||
getDriver,
|
||||
getLogs,
|
||||
loadUri,
|
||||
scope,
|
||||
waitUntilGone
|
||||
scope
|
||||
} = new SeleniumHelper();
|
||||
|
||||
const uri = path.resolve(__dirname, '../../build/index.html');
|
||||
@@ -33,13 +32,14 @@ describe('Loading scratch gui', () => {
|
||||
await clickText('Oops! Something went wrong.');
|
||||
});
|
||||
|
||||
test('Load a project by ID directly through url', async () => {
|
||||
// skipping because it relies on network speed, and tests a method
|
||||
// of loading projects that we are not actively using anymore
|
||||
test.skip('Load a project by ID directly through url', async () => {
|
||||
await driver.quit(); // Reset driver to test hitting # url directly
|
||||
driver = getDriver();
|
||||
|
||||
const projectId = '96708228';
|
||||
await loadUri(`${uri}#${projectId}`);
|
||||
await waitUntilGone(findByText('Loading'));
|
||||
await clickXpath('//img[@title="Go"]');
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await clickXpath('//img[@title="Stop"]');
|
||||
@@ -47,7 +47,9 @@ describe('Loading scratch gui', () => {
|
||||
await expect(logs).toEqual([]);
|
||||
});
|
||||
|
||||
test('Load a project by ID (fullscreen)', async () => {
|
||||
// skipping because it relies on network speed, and tests a method
|
||||
// of loading projects that we are not actively using anymore
|
||||
test.skip('Load a project by ID (fullscreen)', async () => {
|
||||
await driver.quit(); // Reset driver to test hitting # url directly
|
||||
driver = getDriver();
|
||||
|
||||
@@ -60,7 +62,6 @@ describe('Loading scratch gui', () => {
|
||||
.setSize(1920, 1080);
|
||||
const projectId = '96708228';
|
||||
await loadUri(`${uri}#${projectId}`);
|
||||
await waitUntilGone(findByText('Loading'));
|
||||
await clickXpath('//img[@title="Full Screen Control"]');
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
await clickXpath('//img[@title="Go"]');
|
||||
@@ -77,7 +78,6 @@ describe('Loading scratch gui', () => {
|
||||
|
||||
test('Creating new project resets active tab to Code tab', async () => {
|
||||
await loadUri(uri);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await findByXpath('//*[span[text()="Costumes"]]');
|
||||
await clickText('Costumes');
|
||||
await clickXpath(
|
||||
@@ -85,13 +85,12 @@ describe('Loading scratch gui', () => {
|
||||
'contains(@class, "menu-bar_hoverable")][span[text()="File"]]'
|
||||
);
|
||||
await clickXpath('//li[span[text()="New"]]');
|
||||
await findByXpath('//*[div[@class="scratchCategoryMenu"]]');
|
||||
await findByXpath('//div[@class="scratchCategoryMenu"]');
|
||||
await clickText('Operators', scope.blocksTab);
|
||||
});
|
||||
|
||||
test('Not logged in->made no changes to project->create new project should not show alert', async () => {
|
||||
await loadUri(uri);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await clickXpath(
|
||||
'//div[contains(@class, "menu-bar_menu-bar-item") and ' +
|
||||
'contains(@class, "menu-bar_hoverable")][span[text()="File"]]'
|
||||
@@ -103,10 +102,10 @@ describe('Loading scratch gui', () => {
|
||||
|
||||
test('Not logged in->made a change to project->create new project should show alert', async () => {
|
||||
await loadUri(uri);
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await clickText('Sounds');
|
||||
await clickXpath('//button[@aria-label="Choose a Sound"]');
|
||||
await clickText('A Bass', scope.modal); // Should close the modal
|
||||
await findByText('1.28'); // length of A Bass sound
|
||||
await clickXpath(
|
||||
'//div[contains(@class, "menu-bar_menu-bar-item") and ' +
|
||||
'contains(@class, "menu-bar_hoverable")][span[text()="File"]]'
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
|
||||
|
||||
// Mock this utility because it uses dynamic imports that do not work with jest
|
||||
jest.mock('../../../src/lib/libraries/decks/translate-image.js', () => {});
|
||||
|
||||
import Cards, {ImageStep, VideoStep} from '../../../src/components/cards/cards.jsx';
|
||||
|
||||
describe('Cards component', () => {
|
||||
const defaultProps = () => ({
|
||||
activeDeckId: 'id1',
|
||||
content: {
|
||||
id1: {
|
||||
name: 'id1 - name',
|
||||
img: 'id1 - img',
|
||||
steps: [{video: 'videoUrl'}]
|
||||
}
|
||||
},
|
||||
dragging: false,
|
||||
expanded: true,
|
||||
isRtl: false,
|
||||
locale: 'en',
|
||||
onActivateDeckFactory: jest.fn(),
|
||||
onCloseCards: jest.fn(),
|
||||
onDrag: jest.fn(),
|
||||
onEndDrag: jest.fn(),
|
||||
onNextStep: jest.fn(),
|
||||
onPrevStep: jest.fn(),
|
||||
onShowAll: jest.fn(),
|
||||
onShrinkExpandCards: jest.fn(),
|
||||
onStartDrag: jest.fn(),
|
||||
showVideos: true,
|
||||
step: 0,
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
|
||||
test('showVideos=true shows the video step', () => {
|
||||
const component = mountWithIntl(
|
||||
<Cards
|
||||
{...defaultProps()}
|
||||
showVideos
|
||||
/>
|
||||
);
|
||||
expect(component.find(ImageStep).exists()).toEqual(false);
|
||||
expect(component.find(VideoStep).exists()).toEqual(true);
|
||||
});
|
||||
|
||||
test('showVideos=false shows the title image/name instead of video step', () => {
|
||||
const component = mountWithIntl(
|
||||
<Cards
|
||||
{...defaultProps()}
|
||||
showVideos={false}
|
||||
/>
|
||||
);
|
||||
expect(component.find(VideoStep).exists()).toEqual(false);
|
||||
|
||||
const imageStep = component.find(ImageStep);
|
||||
expect(imageStep.props().image).toEqual('id1 - img');
|
||||
expect(imageStep.props().title).toEqual('id1 - name');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,164 @@
|
||||
import React from 'react';
|
||||
import {mount} from 'enzyme';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import MenuBarHOC from '../../../src/containers/menu-bar-hoc.jsx';
|
||||
|
||||
describe('Menu Bar HOC', () => {
|
||||
const mockStore = configureStore();
|
||||
let store;
|
||||
|
||||
beforeEach(() => {
|
||||
store = mockStore({
|
||||
scratchGui: {
|
||||
projectChanged: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Logged in user who IS owner and HAS changed project will NOT be prompted to save', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
canCreateNew
|
||||
canSave
|
||||
projectChanged
|
||||
// assume the user will click "cancel" on the confirm dialog
|
||||
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
|
||||
});
|
||||
|
||||
test('Logged in user who IS owner and has NOT changed project will NOT be prompted to save', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
canCreateNew
|
||||
canSave
|
||||
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
|
||||
projectChanged={false}
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
|
||||
});
|
||||
|
||||
test('Logged in user who is NOT owner and HAS changed project will NOT be prompted to save', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
canCreateNew
|
||||
projectChanged
|
||||
canSave={false}
|
||||
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
|
||||
});
|
||||
|
||||
test('Logged OUT user who HAS changed project WILL be prompted to save', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
projectChanged
|
||||
canCreateNew={false}
|
||||
canSave={false}
|
||||
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().confirmReadyToReplaceProject('message')).toBe(false);
|
||||
});
|
||||
|
||||
test('Logged OUT user who has NOT changed project WILL NOT be prompted to save', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
canCreateNew={false}
|
||||
canSave={false}
|
||||
confirmWithMessage={() => (false)} // eslint-disable-line react/jsx-no-bind
|
||||
projectChanged={false}
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().confirmReadyToReplaceProject('message')).toBe(true);
|
||||
});
|
||||
|
||||
test('Logged in user who IS owner and HAS changed project SHOULD save before transition to project page', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
canSave
|
||||
projectChanged
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().shouldSaveBeforeTransition()).toBe(true);
|
||||
});
|
||||
|
||||
test('Logged in user who IS owner and has NOT changed project should NOT save before transition', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
canSave
|
||||
projectChanged={false}
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().shouldSaveBeforeTransition()).toBe(false);
|
||||
});
|
||||
|
||||
test('Logged in user who is NOT owner and HAS changed project should NOT save before transition', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
projectChanged
|
||||
canSave={false}
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().shouldSaveBeforeTransition()).toBe(false);
|
||||
});
|
||||
|
||||
test('Logged in user who is NOT owner and has NOT changed project should NOT save before transition', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = MenuBarHOC(Component);
|
||||
const wrapper = mount(
|
||||
<WrappedComponent
|
||||
canSave={false}
|
||||
projectChanged={false}
|
||||
store={store}
|
||||
/>
|
||||
);
|
||||
const child = wrapper.find(Component);
|
||||
expect(child.props().projectChanged).toBeUndefined();
|
||||
expect(child.props().shouldSaveBeforeTransition()).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import defaultProjectGenerator from '../../../src/lib/default-project/index.js';
|
||||
|
||||
describe('defaultProject', () => {
|
||||
// This test ensures that the assets referenced in the default project JSON
|
||||
// do not get out of sync with the raw assets that are included alongside.
|
||||
// see https://github.com/LLK/scratch-gui/issues/4844
|
||||
test('assets referenced by the project are included', () => {
|
||||
const translatorFn = () => '';
|
||||
const defaultProject = defaultProjectGenerator(translatorFn);
|
||||
const includedAssetIds = defaultProject.map(obj => obj.id);
|
||||
const projectData = JSON.parse(defaultProject[0].data);
|
||||
projectData.targets.forEach(target => {
|
||||
target.costumes.forEach(costume => {
|
||||
expect(includedAssetIds.includes(costume.assetId)).toBe(true);
|
||||
});
|
||||
target.sounds.forEach(sound => {
|
||||
expect(includedAssetIds.includes(sound.assetId)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||