Merge branch 'develop' into touch-share

This commit is contained in:
Paul Kaplan authored and GitHub committed 2019-06-26 10:35:13 -04:00
commit 18241a5cbd
75 files changed
+1550 -545

No files matched your search

+2 -2
View File
@@ -105,9 +105,9 @@
"redux-throttle": "0.1.1",
"rimraf": "^2.6.1",
"scratch-audio": "0.1.0-prerelease.20190114210212",
"scratch-l10n": "3.3.20190612144059",
"scratch-l10n": "3.3.20190619145905",
"scratch-blocks": "0.1.0-prerelease.1559136797",
"scratch-paint": "0.2.0-prerelease.20190524133325",
"scratch-paint": "0.2.0-prerelease.20190624133806",
"scratch-render": "0.1.0-prerelease.20190605151415",
"scratch-storage": "1.3.2",
"scratch-svg-renderer": "0.2.0-prerelease.20190523193400",
+3
View File
@@ -170,6 +170,9 @@
align-items: center;
position: relative;
text-align: center;
/* Min height prevents layout changing when images change */
min-height: 256px;
}
.step-video {
+4
View File
@@ -104,6 +104,9 @@ class VideoStep extends React.Component {
// We use the Wistia API here to update or pause the video dynamically:
// https://wistia.com/support/developers/player-api
componentDidUpdate (prevProps) {
// Ensure the wistia API is loaded and available
if (!(window.Wistia && window.Wistia.api)) return;
// Get a handle on the currently loaded video
const video = window.Wistia.api(prevProps.video);
@@ -155,6 +158,7 @@ const ImageStep = ({title, image}) => (
<img
className={styles.stepImage}
draggable={false}
key={image} /* Use src as key to prevent hanging around on slow connections */
src={image}
/>
</div>
+2 -1
View File
@@ -91,7 +91,8 @@ LoupeComponent.propTypes = {
color: PropTypes.shape({
r: PropTypes.number,
g: PropTypes.number,
b: PropTypes.number
b: PropTypes.number,
a: PropTypes.number
}),
data: PropTypes.instanceOf(Uint8Array),
width: PropTypes.number,
@@ -20,7 +20,6 @@
cursor: pointer;
user-select: none;
touch-action: none;
}
.sprite-selector-item.is-selected {
@@ -23,7 +23,7 @@ const SpriteSelectorItem = props => (
onMouseDown: props.onMouseDown,
onTouchStart: props.onMouseDown
}}
disable={props.dragging}
disable={props.preventContextMenu}
id={`${props.name}-${contextMenuId}`}
ref={props.componentRef}
>
@@ -93,7 +93,6 @@ SpriteSelectorItem.propTypes = {
componentRef: PropTypes.func,
costumeURL: PropTypes.string,
details: PropTypes.string,
dragging: PropTypes.bool,
name: PropTypes.string.isRequired,
number: PropTypes.number,
onClick: PropTypes.func,
@@ -103,6 +102,7 @@ SpriteSelectorItem.propTypes = {
onMouseDown: PropTypes.func,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
preventContextMenu: PropTypes.bool,
selected: PropTypes.bool.isRequired
};
@@ -110,7 +110,8 @@ SpriteList.propTypes = {
editingTarget: PropTypes.string,
hoveredTarget: PropTypes.shape({
hoveredSprite: PropTypes.string,
receivedBlocks: PropTypes.bool
receivedBlocks: PropTypes.bool,
sprite: PropTypes.string
}),
items: PropTypes.arrayOf(PropTypes.shape({
costume: PropTypes.shape({
@@ -68,6 +68,8 @@
padding-left: calc($space / 2);
padding-right: calc($space / 2);
padding-bottom: $space;
overflow-x: hidden;
}
.add-button {
+5 -1
View File
@@ -52,6 +52,7 @@ class GUI extends React.Component {
setIsScratchDesktop(this.props.isScratchDesktop);
this.setReduxTitle(this.props.projectTitle);
this.props.onStorageInit(storage);
this.props.onVmInit(this.props.vm);
}
componentDidUpdate (prevProps) {
if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) {
@@ -92,6 +93,7 @@ class GUI extends React.Component {
onStorageInit,
onUpdateProjectId,
onUpdateReduxProjectTitle,
onVmInit,
projectHost,
projectId,
projectTitle,
@@ -131,6 +133,7 @@ GUI.propTypes = {
onUpdateProjectId: PropTypes.func,
onUpdateProjectTitle: PropTypes.func,
onUpdateReduxProjectTitle: PropTypes.func,
onVmInit: PropTypes.func,
projectHost: PropTypes.string,
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
projectTitle: PropTypes.string,
@@ -142,7 +145,8 @@ GUI.defaultProps = {
isScratchDesktop: false,
onStorageInit: storageInstance => storageInstance.addOfficialScratchWebStores(),
onProjectLoaded: () => {},
onUpdateProjectId: () => {}
onUpdateProjectId: () => {},
onVmInit: (/* vm */) => {}
};
const mapStateToProps = state => {
+30 -38
View File
@@ -6,14 +6,12 @@ import {connect} from 'react-redux';
import {setHoveredSprite} from '../reducers/hovered-target';
import {updateAssetDrag} from '../reducers/asset-drag';
import storage from '../lib/storage';
import {getEventXY} from '../lib/touch-utils';
import VM from 'scratch-vm';
import getCostumeUrl from '../lib/get-costume-url';
import DragRecognizer from '../lib/drag-recognizer';
import SpriteSelectorItemComponent from '../components/sprite-selector-item/sprite-selector-item.jsx';
const dragThreshold = 3; // Same as the block drag threshold
class SpriteSelectorItem extends React.PureComponent {
constructor (props) {
super(props);
@@ -27,10 +25,18 @@ class SpriteSelectorItem extends React.PureComponent {
'handleMouseEnter',
'handleMouseLeave',
'handleMouseDown',
'handleMouseMove',
'handleMouseUp',
'handleDragEnd',
'handleDrag',
'handleTouchEnd'
]);
this.dragRecognizer = new DragRecognizer({
onDrag: this.handleDrag,
onDragEnd: this.handleDragEnd
});
}
componentWillUnmount () {
this.dragRecognizer.reset();
}
componentDidMount () {
document.addEventListener('touchend', this.handleTouchEnd);
@@ -44,19 +50,7 @@ class SpriteSelectorItem extends React.PureComponent {
return getCostumeUrl(this.props.asset);
}
handleTouchEnd (e) {
const {x, y} = getEventXY(e);
const {top, left, bottom, right} = this.ref.getBoundingClientRect();
if (x >= left && x <= right && y >= top && y <= bottom) {
this.handleMouseEnter();
}
}
handleMouseUp () {
this.initialOffset = null;
window.removeEventListener('mouseup', this.handleMouseUp);
window.removeEventListener('mousemove', this.handleMouseMove);
window.removeEventListener('touchend', this.handleMouseUp);
window.removeEventListener('touchmove', this.handleMouseMove);
handleDragEnd () {
if (this.props.dragging) {
this.props.onDrag({
img: null,
@@ -70,29 +64,26 @@ class SpriteSelectorItem extends React.PureComponent {
this.noClick = false;
});
}
handleMouseMove (e) {
const currentOffset = getEventXY(e);
const dx = currentOffset.x - this.initialOffset.x;
const dy = currentOffset.y - this.initialOffset.y;
if (Math.sqrt((dx * dx) + (dy * dy)) > dragThreshold) {
this.props.onDrag({
img: this.getCostumeData(),
currentOffset: currentOffset,
dragging: true,
dragType: this.props.dragType,
index: this.props.index,
payload: this.props.dragPayload
});
this.noClick = true;
handleDrag (currentOffset) {
this.props.onDrag({
img: this.getCostumeData(),
currentOffset: currentOffset,
dragging: true,
dragType: this.props.dragType,
index: this.props.index,
payload: this.props.dragPayload
});
this.noClick = true;
}
handleTouchEnd (e) {
const {x, y} = getEventXY(e);
const {top, left, bottom, right} = this.ref.getBoundingClientRect();
if (x >= left && x <= right && y >= top && y <= bottom) {
this.handleMouseEnter();
}
e.preventDefault();
}
handleMouseDown (e) {
this.initialOffset = getEventXY(e);
window.addEventListener('mouseup', this.handleMouseUp);
window.addEventListener('mousemove', this.handleMouseMove);
window.addEventListener('touchend', this.handleMouseUp);
window.addEventListener('touchmove', this.handleMouseMove);
this.dragRecognizer.start(e);
}
handleClick (e) {
e.preventDefault();
@@ -143,6 +134,7 @@ class SpriteSelectorItem extends React.PureComponent {
<SpriteSelectorItemComponent
componentRef={this.setRef}
costumeURL={this.getCostumeData()}
preventContextMenu={this.dragRecognizer.gestureInProgress()}
onClick={this.handleClick}
onDeleteButtonClick={onDeleteButtonClick ? this.handleDelete : null}
onDuplicateButtonClick={onDuplicateButtonClick ? this.handleDuplicate : null}
+125
View File
@@ -0,0 +1,125 @@
import bindAll from 'lodash.bindall';
import {getEventXY} from '../lib/touch-utils';
class DragRecognizer {
/* Gesture states */
static get STATE_UNIDENTIFIED () {
return 'unidentified';
}
static get STATE_SCROLL () {
return 'scroll';
}
static get STATE_DRAG () {
return 'drag';
}
constructor ({
onDrag = (() => {}),
onDragEnd = (() => {}),
touchDragAngle = 70, // Angle and distance thresholds are the same as scratch-blocks
distanceThreshold = 3
}) {
this._onDrag = onDrag;
this._onDragEnd = onDragEnd;
this._touchDragAngle = touchDragAngle;
this._distanceThreshold = distanceThreshold;
this._initialOffset = null;
this._gestureState = DragRecognizer.STATE_UNIDENTIFIED;
bindAll(this, [
'start',
'gestureInProgress',
'reset',
'_handleMove',
'_handleEnd'
]);
}
start (event) {
this._initialOffset = getEventXY(event);
this._bindListeners();
}
gestureInProgress () {
return this._gestureState !== DragRecognizer.STATE_UNIDENTIFIED;
}
reset () {
this._unbindListeners();
this._initialOffset = null;
this._gestureState = DragRecognizer.STATE_UNIDENTIFIED;
}
//
// Internal functions
//
_bindListeners () {
window.addEventListener('mouseup', this._handleEnd);
window.addEventListener('mousemove', this._handleMove);
window.addEventListener('touchend', this._handleEnd);
// touchmove must be marked as non-passive, or else it cannot prevent scrolling
window.addEventListener('touchmove', this._handleMove, {passive: false});
}
_unbindListeners () {
window.removeEventListener('mouseup', this._handleEnd);
window.removeEventListener('mousemove', this._handleMove);
window.removeEventListener('touchend', this._handleEnd);
window.removeEventListener('touchmove', this._handleMove, {passive: false});
}
_handleMove (event) {
// For gestures identified as vertical scrolls, do not process movement events
if (this._isScroll()) return;
const currentOffset = getEventXY(event);
// Try to identify this gesture if it hasn't been identified already
if (!this.gestureInProgress()) {
const dx = currentOffset.x - this._initialOffset.x;
const dy = currentOffset.y - this._initialOffset.y;
const dragDistance = Math.sqrt((dx * dx) + (dy * dy));
if (dragDistance < this._distanceThreshold) return;
// For touch moves, additionally check if the angle suggests drag vs. scroll
if (event.type === 'touchmove') {
// Direction goes from -180 to 180, with 0 toward the right.
let angle = Math.atan2(dy, dx) / Math.PI * 180;
// Fold over horizontal axis, range now 0 to 180
angle = Math.abs(angle);
// Fold over vertical axis, range now 0 to 90
if (angle > 90) angle = 180 - angle;
if (angle > this._touchDragAngle) {
this._gestureState = DragRecognizer.STATE_SCROLL;
} else {
this._gestureState = DragRecognizer.STATE_DRAG;
}
} else {
// Mouse moves are always considered drags
this._gestureState = DragRecognizer.STATE_DRAG;
}
}
if (this._isDrag()) {
this._onDrag(currentOffset, this._initialOffset);
event.preventDefault();
}
}
_handleEnd () {
this._onDragEnd();
this.reset();
}
_isDrag () {
return this._gestureState === DragRecognizer.STATE_DRAG;
}
_isScroll () {
return this._gestureState === DragRecognizer.STATE_SCROLL;
}
}
export default DragRecognizer;
+162 -1
View File
@@ -90,6 +90,86 @@ import videoPet from './steps/video-pet.en.gif';
import videoAnimate from './steps/video-animate.en.gif';
import videoPop from './steps/video-pop.en.gif';
// Make it Fly
import flyChooseBackdrop from './steps/fly-choose-backdrop.en.gif';
import flyChooseCharacter from './steps/fly-choose-character.en.gif';
import flySaySomething from './steps/fly-say-something.en.gif';
import flyMoveArrows from './steps/fly-make-interactive.en.gif';
import flyChooseObject from './steps/fly-object-to-collect.en.gif';
import flyFlyingObject from './steps/fly-flying-heart.en.gif';
import flySelectFlyingSprite from './steps/fly-select-flyer.en.jpg';
import flyAddScore from './steps/fly-add-score.en.gif';
import flyKeepScore from './steps/fly-keep-score.en.gif';
import flyAddScenery from './steps/fly-choose-scenery.en.gif';
import flyMoveScenery from './steps/fly-move-scenery.en.gif';
import flySwitchLooks from './steps/fly-switch-costume.en.gif';
// Pong
import pongAddBackdrop from './steps/pong-add-backdrop.en.png';
import pongAddBallSprite from './steps/pong-add-ball-sprite.en.png';
import pongBounceAround from './steps/pong-bounce-around.en.gif';
import pongAddPaddle from './steps/pong-add-a-paddle.en.gif';
import pongMoveThePaddle from './steps/pong-move-the-paddle.en.gif';
import pongSelectBallSprite from './steps/pong-select-ball.en.png';
import pongAddMoreCodeToBall from './steps/pong-add-code-to-ball.en.png';
import pongAddAScore from './steps/pong-add-score.en.gif';
import pongChooseScoreFromMenu from './steps/pong-choose-score.en.png';
import pongInsertChangeScoreBlock from './steps/pong-insert-change-score.en.png';
import pongResetScore from './steps/pong-reset-score.en.png';
import pongAddLineSprite from './steps/pong-add-line.en.gif';
import pongGameOver from './steps/pong-game-over.en.png';
// Imagine If
import imagineTypeWhatYouWant from './steps/imagine-type-what-you-want.en.png';
import imagineClickGreenFlag from './steps/imagine-click-green-flag.en.png';
import imagineChooseBackdrop from './steps/imagine-choose-backdrop.en.png';
import imagineChooseSprite from './steps/imagine-choose-any-sprite.en.png';
import imagineFlyAround from './steps/imagine-fly-around.en.png';
import imagineChooseAnotherSprite from './steps/imagine-choose-another-sprite.en.png';
import imagineLeftRight from './steps/imagine-left-right.en.png';
import imagineUpDown from './steps/imagine-up-down.en.png';
import imagineChangeCostumes from './steps/imagine-change-costumes.en.png';
import imagineGlideToPoint from './steps/imagine-glide-to-point.en.png';
import imagineGrowShrink from './steps/imagine-grow-shrink.en.png';
import imagineChooseAnotherBackdrop from './steps/imagine-choose-another-backdrop.en.png';
import imagineSwitchBackdrops from './steps/imagine-switch-backdrops.en.gif';
import imagineRecordASound from './steps/imagine-record-a-sound.en.gif';
import imagineChooseSound from './steps/imagine-choose-sound.en.png';
// Add a Backdrop
import addBackdrop from './steps/add-backdrop.en.png';
// Add Effectsf
import addEffects from './steps/add-effects.en.png';
// Hide and Show
import hideAndShow from './steps/hide-show.en.png';
// Switch Costumes
import switchCostumes from './steps/switch-costumes.en.png';
// Change Size
import changeSize from './steps/change-size.en.png';
// Spin
import spinTurn from './steps/spin-turn.en.png';
import spinPointInDirection from './steps/spin-point-in-direction.en.png';
// Record a Sound
import recordASoundSoundsTab from './steps/record-a-sound-sounds-tab.en.png';
import recordASoundClickRecord from './steps/record-a-sound-click-record.en.png';
import recordASoundPressRecordButton from './steps/record-a-sound-press-record-button.en.png';
import recordASoundChooseSound from './steps/record-a-sound-choose-sound.en.png';
import recordASoundPlayYourSound from './steps/record-a-sound-play-your-sound.en.png';
// Use Arrow Keys
import moveArrowKeysLeftRight from './steps/move-arrow-keys-left-right.en.png';
import moveArrowKeysUpDown from './steps/move-arrow-keys-up-down.en.png';
// Glide Around
import glideAroundBackAndForth from './steps/glide-around-back-and-forth.en.png';
import glideAroundPoint from './steps/glide-around-point.en.png';
const enImages = {
// Intro
introMove: introMove,
@@ -181,8 +261,89 @@ const enImages = {
videoAddExtension: videoAddExtension,
videoPet: videoPet,
videoAnimate: videoAnimate,
videoPop: videoPop
videoPop: videoPop,
// Make it Fly
flyChooseBackdrop: flyChooseBackdrop,
flyChooseCharacter: flyChooseCharacter,
flySaySomething: flySaySomething,
flyMoveArrows: flyMoveArrows,
flyChooseObject: flyChooseObject,
flyFlyingObject: flyFlyingObject,
flySelectFlyingSprite: flySelectFlyingSprite,
flyAddScore: flyAddScore,
flyKeepScore: flyKeepScore,
flyAddScenery: flyAddScenery,
flyMoveScenery: flyMoveScenery,
flySwitchLooks: flySwitchLooks,
// Pong
pongAddBackdrop: pongAddBackdrop,
pongAddBallSprite: pongAddBallSprite,
pongBounceAround: pongBounceAround,
pongAddPaddle: pongAddPaddle,
pongMoveThePaddle: pongMoveThePaddle,
pongSelectBallSprite: pongSelectBallSprite,
pongAddMoreCodeToBall: pongAddMoreCodeToBall,
pongAddAScore: pongAddAScore,
pongChooseScoreFromMenu: pongChooseScoreFromMenu,
pongInsertChangeScoreBlock: pongInsertChangeScoreBlock,
pongResetScore: pongResetScore,
pongAddLineSprite: pongAddLineSprite,
pongGameOver: pongGameOver,
// Imagine If
imagineTypeWhatYouWant: imagineTypeWhatYouWant,
imagineClickGreenFlag: imagineClickGreenFlag,
imagineChooseBackdrop: imagineChooseBackdrop,
imagineChooseSprite: imagineChooseSprite,
imagineFlyAround: imagineFlyAround,
imagineChooseAnotherSprite: imagineChooseAnotherSprite,
imagineLeftRight: imagineLeftRight,
imagineUpDown: imagineUpDown,
imagineChangeCostumes: imagineChangeCostumes,
imagineGlideToPoint: imagineGlideToPoint,
imagineGrowShrink: imagineGrowShrink,
imagineChooseAnotherBackdrop: imagineChooseAnotherBackdrop,
imagineSwitchBackdrops: imagineSwitchBackdrops,
imagineRecordASound: imagineRecordASound,
imagineChooseSound: imagineChooseSound,
// Add a Backdrop
addBackdrop: addBackdrop,
// Add Effects
addEffects: addEffects,
// Hide and Show
hideAndShow: hideAndShow,
// Switch Costumes
switchCostumes: switchCostumes,
// Change Size
changeSize: changeSize,
// Spin
spinTurn: spinTurn,
spinPointInDirection: spinPointInDirection,
// Record a Sound
recordASoundSoundsTab: recordASoundSoundsTab,
recordASoundClickRecord: recordASoundClickRecord,
recordASoundPressRecordButton: recordASoundPressRecordButton,
recordASoundChooseSound: recordASoundChooseSound,
recordASoundPlayYourSound: recordASoundPlayYourSound,
// Use Arrow Keys
moveArrowKeysLeftRight: moveArrowKeysLeftRight,
moveArrowKeysUpDown: moveArrowKeysUpDown,
// Glide Around
glideAroundBackAndForth: glideAroundBackAndForth,
glideAroundPoint: glideAroundPoint
};
export {enImages};
File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

@@ -74,6 +74,12 @@ const videos = {
'en': '3jvl8zgjo2',
'ja': 'q7zq9vu0wb',
'ja-Hira': 'q7zq9vu0wb'
},
'make-it-fly': {
en: 'zbtdx2dem9'
},
'pong-game': {
en: '8m48dv0ens'
}
};
+2 -1
View File
@@ -75,7 +75,8 @@ describe('blocks example', () => {
await expect(projectRequests).toEqual(['https://projects.scratch.mit.edu/96708228']);
});
test('Change categories', async () => {
// skipping per https://github.com/LLK/scratch-gui/issues/4902 until we have better approach
test.skip('Change categories', async () => {
await loadUri(`${uri}`);
await clickText('Looks');
await clickText('Sound');
+109
View File
@@ -0,0 +1,109 @@
import DragRecognizer from '../../../src/lib/drag-recognizer';
describe('DragRecognizer', () => {
let onDrag;
let onDragEnd;
let dragRecognizer;
beforeEach(() => {
onDrag = jest.fn();
onDragEnd = jest.fn();
dragRecognizer = new DragRecognizer({onDrag, onDragEnd});
});
afterEach(() => {
dragRecognizer.reset();
});
test('start -> small drag', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
window.dispatchEvent(new MouseEvent('mousemove', {clientX: 101, clientY: 101}));
expect(onDrag).not.toBeCalled();
});
test('start -> large vertical touch move -> scroll, not drag', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 106, clientY: 150}));
expect(onDrag).not.toBeCalled();
});
test('start -> large vertical mouse move -> mouse moves always drag)', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
window.dispatchEvent(new MouseEvent('mousemove', {clientX: 100, clientY: 150}));
expect(onDrag).toBeCalled();
});
test('start -> large horizontal touch move -> drag', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
expect(onDrag).toBeCalled();
});
test('after starting a scroll, it cannot become a drag', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 100, clientY: 110}));
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 100, clientY: 100}));
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 110, clientY: 100}));
expect(onDrag).not.toBeCalled();
});
test('start -> end unbinds', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
expect(onDrag).toHaveBeenCalledTimes(1);
window.dispatchEvent(new MouseEvent('touchend', {clientX: 150, clientY: 106}));
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
expect(onDrag).toHaveBeenCalledTimes(1); // Still 1
});
test('start -> reset unbinds', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
expect(onDrag).toHaveBeenCalledTimes(1);
dragRecognizer.reset();
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 150, clientY: 106}));
expect(onDrag).toHaveBeenCalledTimes(1); // Still 1
});
test('scrolls do not call prevent default', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
const event = new MouseEvent('touchmove', {clientX: 100, clientY: 110});
event.preventDefault = jest.fn();
window.dispatchEvent(event);
expect(event.preventDefault).toHaveBeenCalledTimes(0);
});
test('confirmed drags have preventDefault called on them', () => {
dragRecognizer.start({clientX: 100, clientY: 100});
const event = new MouseEvent('touchmove', {clientX: 150, clientY: 106});
event.preventDefault = jest.fn();
window.dispatchEvent(event);
expect(event.preventDefault).toHaveBeenCalled();
});
test('multiple horizontal drag angles', () => {
// +45 from horizontal => drag
dragRecognizer.start({clientX: 0, clientY: 0});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 10, clientY: 10}));
expect(onDrag).toHaveBeenCalledTimes(1);
dragRecognizer.reset();
// -45 from horizontal => drag
dragRecognizer.start({clientX: 0, clientY: 0});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: 10, clientY: -10}));
expect(onDrag).toHaveBeenCalledTimes(2);
dragRecognizer.reset();
// +135 from horizontal => drag
dragRecognizer.start({clientX: 0, clientY: 0});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: -10, clientY: 10}));
expect(onDrag).toHaveBeenCalledTimes(3);
dragRecognizer.reset();
// -135 from horizontal => drag
dragRecognizer.start({clientX: 0, clientY: 0});
window.dispatchEvent(new MouseEvent('touchmove', {clientX: -10, clientY: -10}));
expect(onDrag).toHaveBeenCalledTimes(4);
dragRecognizer.reset();
});
});