From 893beb5a15783a4ec31832d3087166335f5b6c4e Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 13 Dec 2018 21:57:12 -0500 Subject: [PATCH] added library-item container, moved costume rotation to library items --- src/components/library-item/library-item.jsx | 34 ++--- src/components/library/library.jsx | 63 ++++----- src/containers/library-item.jsx | 134 +++++++++++++++++++ src/containers/sprite-library.jsx | 50 +------ test/integration/costumes.test.js | 17 +++ 5 files changed, 187 insertions(+), 111 deletions(-) create mode 100644 src/containers/library-item.jsx diff --git a/src/components/library-item/library-item.jsx b/src/components/library-item/library-item.jsx index 03a9d6b78..c9ba6ea55 100644 --- a/src/components/library-item/library-item.jsx +++ b/src/components/library-item/library-item.jsx @@ -10,24 +10,14 @@ import classNames from 'classnames'; import bluetoothIconURL from './bluetooth.svg'; import internetConnectionIconURL from './internet-connection.svg'; -class LibraryItem extends React.PureComponent { +class LibraryItemComponent extends React.PureComponent { constructor (props) { super(props); bindAll(this, [ - 'handleBlur', 'handleClick', - 'handleFocus', - 'handleKeyPress', - 'handleMouseEnter', - 'handleMouseLeave' + 'handleKeyPress' ]); } - handleBlur () { - this.props.onBlur(this.props.id); - } - handleFocus () { - this.props.onFocus(this.props.id); - } handleClick (e) { if (!this.props.disabled) { this.props.onSelect(this.props.id); @@ -40,12 +30,6 @@ class LibraryItem extends React.PureComponent { this.props.onSelect(this.props.id); } } - handleMouseEnter () { - this.props.onMouseEnter(this.props.id); - } - handleMouseLeave () { - this.props.onMouseLeave(this.props.id); - } render () { return this.props.featured ? (
{/* Layers of wrapping is to prevent layout thrashing on animation */} @@ -168,7 +152,7 @@ class LibraryItem extends React.PureComponent { } } -LibraryItem.propTypes = { +LibraryItemComponent.propTypes = { bluetoothRequired: PropTypes.bool, collaborator: PropTypes.string, description: PropTypes.oneOfType([ @@ -194,8 +178,8 @@ LibraryItem.propTypes = { onSelect: PropTypes.func.isRequired }; -LibraryItem.defaultProps = { +LibraryItemComponent.defaultProps = { disabled: false }; -export default LibraryItem; +export default LibraryItemComponent; diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 8ecb76181..9e43ddd6a 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import {defineMessages, injectIntl, intlShape} from 'react-intl'; -import LibraryItem from '../library-item/library-item.jsx'; +import LibraryItem from '../../containers/library-item.jsx'; import Modal from '../../containers/modal.jsx'; import Divider from '../divider/divider.jsx'; import Filter from '../filter/filter.jsx'; @@ -33,11 +33,9 @@ class LibraryComponent extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'handleBlur', 'handleClose', 'handleFilterChange', 'handleFilterClear', - 'handleFocus', 'handleMouseEnter', 'handleMouseLeave', 'handleSelect', @@ -56,12 +54,6 @@ class LibraryComponent extends React.Component { this.scrollToTop(); } } - handleBlur (id) { - this.handleMouseLeave(id); - } - handleFocus (id) { - this.handleMouseEnter(id); - } handleSelect (id) { this.handleClose(); this.props.onItemSelected(this.getFilteredData()[id]); @@ -76,10 +68,10 @@ class LibraryComponent extends React.Component { selectedTag: tag.toLowerCase() }); } - handleMouseEnter (id) { + handleMouseEnter (id) { // no longer used for sprite costume switching, only for other libraries if (this.props.onItemMouseEnter) this.props.onItemMouseEnter(this.getFilteredData()[id]); } - handleMouseLeave (id) { + handleMouseLeave (id) { // no longer used for sprite costume switching, only for other libraries if (this.props.onItemMouseLeave) this.props.onItemMouseLeave(this.getFilteredData()[id]); } handleFilterChange (event) { @@ -172,33 +164,28 @@ class LibraryComponent extends React.Component { })} ref={this.setFilteredDataRef} > - {this.getFilteredData().map((dataItem, index) => { - const scratchURL = dataItem.md5 ? - `https://cdn.assets.scratch.mit.edu/internalapi/asset/${dataItem.md5}/get/` : - dataItem.rawURL; - return ( -
); diff --git a/src/containers/library-item.jsx b/src/containers/library-item.jsx new file mode 100644 index 000000000..7f81b4389 --- /dev/null +++ b/src/containers/library-item.jsx @@ -0,0 +1,134 @@ +import bindAll from 'lodash.bindall'; +import PropTypes from 'prop-types'; +import React from 'react'; +import {injectIntl} from 'react-intl'; + +import LibraryItemComponent from '../components/library-item/library-item.jsx'; + +class LibraryItem extends React.PureComponent { + constructor (props) { + super(props); + bindAll(this, [ + 'handleBlur', + 'handleFocus', + 'handleMouseEnter', + 'handleMouseLeave', + 'rotateIcon', + 'startRotatingIcons', + 'stopRotatingIcons' + ]); + this.state = { + iconIndex: 0, + isRotatingIcon: false + }; + } + componentWillUnmount () { + clearInterval(this.intervalId); + } + handleBlur (id) { + this.handleMouseLeave(id); + } + handleFocus (id) { + this.handleMouseEnter(id); + } + handleMouseEnter () { + this.props.onMouseEnter(this.props.id); + if (this.props.icons && this.props.icons.length) { + this.stopRotatingIcons(); + this.setState({ + isRotatingIcon: true + }, this.startRotatingIcons); + } + } + handleMouseLeave () { + this.props.onMouseLeave(this.props.id); + if (this.props.icons && this.props.icons.length) { + this.setState({ + isRotatingIcon: false + }, this.stopRotatingIcons); + } + } + startRotatingIcons () { + this.rotateIcon(); + this.intervalId = setInterval(this.rotateIcon, 300); + } + stopRotatingIcons () { + if (this.intervalId) { + this.intervalId = clearInterval(this.intervalId); + } + } + rotateIcon () { + const nextIconIndex = (this.state.iconIndex + 1) % this.props.icons.length; + this.setState({iconIndex: nextIconIndex}); + } + curIconMd5 () { + if (this.props.icons && + this.state.isRotatingIcon && + this.state.iconIndex < this.props.icons.length && + this.props.icons[this.state.iconIndex] && + this.props.icons[this.state.iconIndex].baseLayerMD5) { + return this.props.icons[this.state.iconIndex].baseLayerMD5; + } + return this.props.iconMd5; + } + render () { + const iconMd5 = this.curIconMd5(); + const iconURL = iconMd5 ? + `https://cdn.assets.scratch.mit.edu/internalapi/asset/${iconMd5}/get/` : + this.props.iconRawURL; + return ( +