From 043e31a4728b89289cc4093d22c7eca5a35e1276 Mon Sep 17 00:00:00 2001 From: Chris Garrity <chrisg@media.mit.edu> Date: Wed, 31 Jul 2019 10:25:37 -0400 Subject: [PATCH] Refactor * split play-button into container and component * revised sound library tile: * play button in upper right * smaller sound icon (make the tile less loaded) --- .../library-item/lib-icon--sound-rtl.svg | Bin 0 -> 2050 bytes .../library-item/lib-icon--sound.svg | Bin 0 -> 2059 bytes src/components/library-item/library-item.jsx | 2 +- src/components/library/library.jsx | 2 +- src/components/play-button/play-button.css | 4 +- src/components/play-button/play-button.jsx | 159 +++++------------- src/containers/play-button.jsx | 115 +++++++++++++ src/containers/sound-library.jsx | 4 +- 8 files changed, 163 insertions(+), 123 deletions(-) create mode 100644 src/components/library-item/lib-icon--sound-rtl.svg create mode 100644 src/components/library-item/lib-icon--sound.svg create mode 100644 src/containers/play-button.jsx diff --git a/src/components/library-item/lib-icon--sound-rtl.svg b/src/components/library-item/lib-icon--sound-rtl.svg new file mode 100644 index 0000000000000000000000000000000000000000..db193366a3f7c592381ca6382f49de2ba2abc5a2 GIT binary patch literal 2050 zcmZuyO>ZMN488BKP^Xs`7|WDMQDR_wff{X5z<cSYm%XL&)HdR^1KS&?{q_6gk9q=R z3<DB}LsIWOQlpQrU)S!X+wE7|&A17k8rN+exAWCzF>e04|7}p3kCP9h{ma4~SM$RX zK19#2jazoB#q#j~_GQ%_f7!l{8*=2}QvXdqaA+J-ZT914c{n`Z4Ts0$(I44wcZ*>p zq9IaTT<%`itIgAUj={`^r?<`IgTpiWVK8vNcbjhaaM<qd+~=q6@VInIeRMySpgI2R z2Cin@Bjx_|)<15)y20QQ;IKNZyUFM6x6OQb4zwQ5R)6mvcK^Ej)w*+kJZ?9`KksLw z;o0?^ci!zEC(~W`aOmbh=CHh^_M_qH)!A#|R`YT5Zty`3KDY*N-2SlJK6T?}v)y#p z-QWs&<NjH#*VmVIJ=lF)!@GOwHrx69g2oAv=LZynte=E@HVH9stK|9gh?}zcl=0)x zH*@h^Qb{4U$$ZwB@ulPJb4-Fwn+ld1Q)Gk?FP6DRPV6&LN$|ZHC3{h_LT@5S^uj3> ze2-W`Nm*JHS&9;<a&>8Bgk)H9X3Ejwqhv5sXeIbq*kX+!g0+=^L%<_aiekaRS`tg5 z+*XG`A&WR+ScAk8TS-1=!WuXroQVWAZ3XpkiE41ZSrh7woRs?!Qu1tSQPQc1YdQ$d zQc#rEs|=7?f(-#~rvSPoXCtj8L{AYe7AqB@Xif&_;Dwkez2WVBHov$}OdcoF2ygLv ze%v%7ch+E<`{tOvsTNdSt%1oRv4fS(g5=UF`;c;u0CO6nFRE72#Hob<Rd79H1v3>W z0@hmua7z?p!0}K+gv<(Q%j%P4mY5vs4WuPlu2#xa^EnPar3f9BYDHP#T%nZc%_<tP zty(O}1PC#E0kaW@gPJunrz*AR6~{5QDwV(-SRs{YP(nez(n@6s$}g^GOi&6%Ayjq? z06K_6hQd+9Et`TG3yDhgy%q+{AXYPk?VSSnMz$(UM8I6lkc>%%Jw+p_&g2twVSU5g zyL5tPXtaO~1$F4Okc&njG3}cX4`)MGhpuE`%o19VfN!uabt+P3MQ^vL2m_i4+|4v% z&DnCQ&<k6Z+G6zpp<v*Kwho8d&S)kDggHqOsnD}PW1%>OsDWS>&~&rTGCCS9n;@*7 zd79ayo&XT~=8Qgs2n@Amb#*765gpLF8Mfd_iv>)jY2DQk6Yv_Yw{UfI5oCeG_Hy^E zGgO)ZdC_p1a~J1nJyw9az(voy_K3!Sr}k$S5=<Niyhs$23T#3#0gY<K&3fo+#w4>M zK(WUJDq>Lq{;tQG7B8Y4F%SAyOfrm<Iss=rbC3Bi&G~LwK0M>TIA0GxLfpURblzTW b2;3+Cb-Qgd84c^raJlt{3tZ~;%AI@%Ucl45 literal 0 HcmV?d00001 diff --git a/src/components/library-item/lib-icon--sound.svg b/src/components/library-item/lib-icon--sound.svg new file mode 100644 index 0000000000000000000000000000000000000000..2ffc760131cccf23d7e0346985252c5e765ccefa GIT binary patch literal 2059 zcmZuyO>g5i5WVlOVAV?t$kdR-8IpnR1!}ZK0q><rFMG?zsx8Ek0^6IU{q=qHN38%+ z5WspIy&1lFLry=xJ#EcPyW4j|KdYRj%C!B%u<H8ttorNjw@Io#&p%D~FKhGKtqvQ+ z5Iw(DX47`-&Efyom#%&NWq6xaWXK?-{8wXQR~e-0_p@qqI6U7p&Fkx{ePuiB){Q5k z1}V-VH*ednfBY!W<(!-2YBm34@JxT0Ow8|X-|p@Y!|ul1KDLL4jS;op{FIzV|FfBx zl5vle`{Sy87@o{zawa%*hiyB*9lrOg=0vn@mfg4A{q7%g*KJ$#$HUM!%ez0P%_;Ph zch&A6=8Ii>e`r@w=CC=Zw$tXgbc$M=ZZ)euB%c)W$%VW!`@?Q{Y-d$J^zG$sa*@0; ze|OvMWwHF8?7nXi-M+N_uv(qfI4bgdk7AJZi)8CbQcST{vV44kSxB;yRdaX;OSCys zU`i#}C5@8V01e~_s@GskF=o<2AvQWf5a499s3gRSbwMR$t|5OG2edT8g(T6X8ta@j z`yj^}NbnJbTD%ROMM0441q{hXP98;$83NKqa>1WUxMT~#dQ=kwg0F=IUv}?9cH><H zXMN)AQQ87K!qzBztpoIlOwk)X1+X5$l$78iqacexVhKgPjM~jY#R_>X`UdEvloNsA ziVE9Elmy`Qd+ZW4UOa=rS#<#dL7i-)B8NjTK@t2x-BI5@?&eqXMYGLGIHH}w>gfS> zMn&qB-@%G*`6z+31dGq;crrjkh%Q85P|vyO28w8jidA3<F8btLf)(G1MCKahLtY_( z5$jn=T`8H^Mene`NH!+tAWJPECLc)52n@w1Q3~so04;LNj<ra9O~%<Ol0xwkd?}10 zGDwE$#=BtfdP*?sQHYcoHE<1>B1uG>qq8w+ayb-KLge64nzwpvn6DXTB<~$`h_Mtz z#n{DxlF&S7Mkri|DN#6WL;%TLNd<HPn6M=%Tm*Iu&k>gz$TJFqlGo0C*iN)riY#as zI2ar;smG?5BN!y`suUM5#&GGbvd7>;Z-o{rxkeW_C@kVlV5p^x2G>wF<(xt?ODLNK zl@_Z&7DvJv9bvJFV@m00&KM{pbeh>>P6EMg#mHqU&{QT2rlOU{)8m3JJ?1Vt4IZwz z1~0r?l$KzKG3;?dl;LvfFo)$0Ql;!LCri9x_8BYirdgC%hqDdNi3q*Y!8(u)Vabeg zkI*G5b#M`mg?a=p%C>M`Ingl;3E*m6S-H3b5+%aX1HDpKcwCC30%x2TkZ~j~qk}|D zFwbJt37@PAYKCB=uAw5qU|<;Eq1VoTt&VrgVtD%2_3dhMySabH#c{ePe#BJ$8s+qQ czAA9B{M+t^YCdhs6>+})nl*0sa`(>v1Cv75<^TWy literal 0 HcmV?d00001 diff --git a/src/components/library-item/library-item.jsx b/src/components/library-item/library-item.jsx index 504be2b66..74a65aa9a 100644 --- a/src/components/library-item/library-item.jsx +++ b/src/components/library-item/library-item.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import Box from '../box/box.jsx'; -import PlayButton from '../play-button/play-button.jsx'; +import PlayButton from '../../containers/play-button.jsx'; import styles from './library-item.css'; import classNames from 'classnames'; diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 259e922ff..f85def50f 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -73,7 +73,7 @@ class LibraryComponent extends React.Component { handleTagClick (tag) { if (this.state.playingItem === null) { this.setState({ - ilterQuery: '', + filterQuery: '', selectedTag: tag.toLowerCase() }); } else { diff --git a/src/components/play-button/play-button.css b/src/components/play-button/play-button.css index fbc901e66..17d2b6317 100644 --- a/src/components/play-button/play-button.css +++ b/src/components/play-button/play-button.css @@ -34,9 +34,9 @@ } [dir="ltr"] .play-button { - left: .5rem; + right: .5rem; } [dir="rtl"] .play-button { - right: .5rem; + left: .5rem; } diff --git a/src/components/play-button/play-button.jsx b/src/components/play-button/play-button.jsx index 8071ce849..15f8226ac 100644 --- a/src/components/play-button/play-button.jsx +++ b/src/components/play-button/play-button.jsx @@ -1,7 +1,6 @@ import PropTypes from 'prop-types'; import React from 'react'; import classNames from 'classnames'; -import bindAll from 'lodash.bindall'; import {defineMessages, injectIntl, intlShape} from 'react-intl'; @@ -23,126 +22,52 @@ const messages = defineMessages({ } }); -class PlayButton extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'handleClick', - 'handleMouseDown', - 'handleMouseEnter', - 'handleMouseLeave', - 'handleTouchStart', - 'setButtonRef' - ]); - this.state = { - touchStarted: false - }; - } - getDerivedStateFromProps (props, state) { - // if touchStarted is true and it's not playing, the sound must have ended. - // reset the touchStarted state to allow the sound to be replayed - if (state.touchStarted && !props.isPlaying) { - return { - touchStarted: false - }; - } - return null; // nothing changed - } - componentDidMount () { - // Touch start - this.buttonRef.addEventListener('touchstart', this.handleTouchStart); - } - componentWillUnmount () { - this.buttonRef.removeEventListener('touchstart', this.handleTouchStart); - } - handleClick (e) { - // stop the click from propagating out of the button - e.stopPropagation(); - } - handleMouseDown (e) { - // prevent default (focus) on mouseDown - e.preventDefault(); - if (this.props.isPlaying) { - // stop sound and reset touch state - this.props.onStop(); - if (this.state.touchstarted) this.setState({touchStarted: false}); - } else { - this.props.onPlay(); - if (this.state.touchstarted) { - // started on touch, but now clicked mouse - this.setState({touchStarted: false}); - } - } - } - handleTouchStart (e) { - if (this.props.isPlaying) { - // If playing, stop sound, and reset touch state - e.preventDefault(); - this.setState({touchStarted: false}); - this.props.onStop(); - } else { - // otherwise start playing, and set touch state - e.preventDefault(); - this.setState({touchStarted: true}); - this.props.onPlay(); - } - } - handleMouseEnter (e) { - // start the sound if it's not already playing - e.preventDefault(); - if (!this.props.isPlaying) { - this.props.onPlay(); - } - } - handleMouseLeave () { - // stop the sound unless it was started by touch - if (this.props.isPlaying && !this.state.touchstarted) { - this.props.onStop(); - } - } - setButtonRef (ref) { - this.buttonRef = ref; - } - render () { - const { - className, - intl, - isPlaying, - onPlay, // eslint-disable-line no-unused-vars - onStop // eslint-disable-line no-unused-vars - } = this.props; - const label = isPlaying ? - intl.formatMessage(messages.stop) : - intl.formatMessage(messages.play); +const PlayButtonComponent = ({ + className, + intl, + isPlaying, + onClick, + onMouseDown, + onMouseEnter, + onMouseLeave, + setButtonRef, + ...props +}) => { + const label = isPlaying ? + intl.formatMessage(messages.stop) : + intl.formatMessage(messages.play); - return ( - <div - aria-label={label} - className={classNames(styles.playButton, className, { - [styles.playing]: isPlaying - })} - ref={this.setButtonRef} - onClick={this.handleClick} - onMouseDown={this.handleMouseDown} - onMouseEnter={this.handleMouseEnter} - onMouseLeave={this.handleMouseLeave} - > - <img - className={styles.playIcon} - draggable={false} - src={isPlaying ? stopIcon : playIcon} - /> - </div> - ); - } -} + return ( + <div + aria-label={label} + className={classNames(styles.playButton, className, { + [styles.playing]: isPlaying + })} + onClick={onClick} + onMouseDown={onMouseDown} + onMouseEnter={onMouseEnter} + onMouseLeave={onMouseLeave} + ref={setButtonRef} + {...props} + > + <img + className={styles.playIcon} + draggable={false} + src={isPlaying ? stopIcon : playIcon} + /> + </div> + ); +}; -PlayButton.propTypes = { +PlayButtonComponent.propTypes = { className: PropTypes.string, intl: intlShape, isPlaying: PropTypes.bool.isRequired, - onPlay: PropTypes.func.isRequired, - onStop: PropTypes.func.isRequired + onClick: PropTypes.func.isRequired, + onMouseDown: PropTypes.func.isRequired, + onMouseEnter: PropTypes.func.isRequired, + onMouseLeave: PropTypes.func.isRequired, + setButtonRef: PropTypes.func.isRequired }; -export default injectIntl(PlayButton); +export default injectIntl(PlayButtonComponent); diff --git a/src/containers/play-button.jsx b/src/containers/play-button.jsx new file mode 100644 index 000000000..f0ed8b6a2 --- /dev/null +++ b/src/containers/play-button.jsx @@ -0,0 +1,115 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import bindAll from 'lodash.bindall'; + +import PlayButtonComponent from '../components/play-button/play-button.jsx'; + +class PlayButton extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleClick', + 'handleMouseDown', + 'handleMouseEnter', + 'handleMouseLeave', + 'handleTouchStart', + 'setButtonRef' + ]); + this.state = { + touchStarted: false + }; + } + getDerivedStateFromProps (props, state) { + // if touchStarted is true and it's not playing, the sound must have ended. + // reset the touchStarted state to allow the sound to be replayed + if (state.touchStarted && !props.isPlaying) { + return { + touchStarted: false + }; + } + return null; // nothing changed + } + componentDidMount () { + // Touch start + this.buttonRef.addEventListener('touchstart', this.handleTouchStart); + } + componentWillUnmount () { + this.buttonRef.removeEventListener('touchstart', this.handleTouchStart); + } + handleClick (e) { + // stop the click from propagating out of the button + e.stopPropagation(); + } + handleMouseDown (e) { + // prevent default (focus) on mouseDown + e.preventDefault(); + if (this.props.isPlaying) { + // stop sound and reset touch state + this.props.onStop(); + if (this.state.touchstarted) this.setState({touchStarted: false}); + } else { + this.props.onPlay(); + if (this.state.touchstarted) { + // started on touch, but now clicked mouse + this.setState({touchStarted: false}); + } + } + } + handleTouchStart (e) { + if (this.props.isPlaying) { + // If playing, stop sound, and reset touch state + e.preventDefault(); + this.setState({touchStarted: false}); + this.props.onStop(); + } else { + // otherwise start playing, and set touch state + e.preventDefault(); + this.setState({touchStarted: true}); + this.props.onPlay(); + } + } + handleMouseEnter (e) { + // start the sound if it's not already playing + e.preventDefault(); + if (!this.props.isPlaying) { + this.props.onPlay(); + } + } + handleMouseLeave () { + // stop the sound unless it was started by touch + if (this.props.isPlaying && !this.state.touchstarted) { + this.props.onStop(); + } + } + setButtonRef (ref) { + this.buttonRef = ref; + } + render () { + const { + className, + isPlaying, + onPlay, // eslint-disable-line no-unused-vars + onStop // eslint-disable-line no-unused-vars + } = this.props; + return ( + <PlayButtonComponent + className={className} + isPlaying={isPlaying} + onClick={this.handleClick} + onMouseDown={this.handleMouseDown} + onMouseEnter={this.handleMouseEnter} + onMouseLeave={this.handleMouseLeave} + setButtonRef={this.setButtonRef} + /> + ); + } +} + +PlayButton.propTypes = { + className: PropTypes.string, + isPlaying: PropTypes.bool.isRequired, + onPlay: PropTypes.func.isRequired, + onStop: PropTypes.func.isRequired +}; + +export default PlayButton; diff --git a/src/containers/sound-library.jsx b/src/containers/sound-library.jsx index d3b9782d1..cdb5c7c6a 100644 --- a/src/containers/sound-library.jsx +++ b/src/containers/sound-library.jsx @@ -7,8 +7,8 @@ import AudioEngine from 'scratch-audio'; import LibraryComponent from '../components/library/library.jsx'; -import soundIcon from '../components/asset-panel/icon--sound.svg'; -import soundIconRtl from '../components/asset-panel/icon--sound-rtl.svg'; +import soundIcon from '../components/library-item/lib-icon--sound.svg'; +import soundIconRtl from '../components/library-item/lib-icon--sound-rtl.svg'; import soundLibraryContent from '../lib/libraries/sounds.json'; import soundTags from '../lib/libraries/sound-tags'; -- GitLab