From f3d1a91533a17241a064312da08f15c944c7511c Mon Sep 17 00:00:00 2001 From: Chris Garrity <chrisg@media.mit.edu> Date: Tue, 18 Jun 2019 10:16:55 -0400 Subject: [PATCH] Add play buttons to sound library tiles for touch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add prop to library items to show a play button. If a play button is present the ‘play on hover’ only applies over the icon rather than the full tile. --- src/components/library-item/icon--play.svg | Bin 0 -> 2150 bytes src/components/library-item/library-item.css | 43 +++++++++++++++++++ src/components/library-item/library-item.jsx | 38 +++++++++++++--- src/components/library/library.jsx | 5 ++- src/containers/library-item.jsx | 10 ++++- src/containers/sound-library.jsx | 1 + 6 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 src/components/library-item/icon--play.svg diff --git a/src/components/library-item/icon--play.svg b/src/components/library-item/icon--play.svg new file mode 100644 index 0000000000000000000000000000000000000000..7c4f45df771c93f5487fb64479c751600fd001ba GIT binary patch literal 2150 zcmb7F-*2Nh5Pt7p;d6D;Rtg6H0OJa>+U;rYrPZ!3yD#@fgA=$CjKDPMf4^fO32oDd zbEU=}+ut`c{$@x%A9fXb)@@gsI>nR(80mU#^0MBh_}9;GUW`#^vpUZzQ|lDh27g{& zCEfED?aSO2DHf_ZU{vUGTi6tb%&neFz5i+sDfU7XAfb=|m>CPj2zcu*#f7!at?%#m zd$Q-mv|FDALEr=abmaC>mG#r5gH$Qy54(7Ig<vFqcpmy&*SgKDX>ZZ$N!xXS1SbrA z;wp&L4f4>8@zE4|S7uYZWNmhSH{S8Q3DTCf(#xjGUJ`$tk5swt*2}wAXIAHs30q8T zB=Lu><Ec$I0uBB(8Jx>g{6HmPjEX3vi<odBloEoYJF1A3Q6#8Z2ol5*kD`zx#))EF z1fg_R#RINHgjhs`6rqge9O7OCgoZ3oQY<(nR0MIT(4AmJ29ilZ7d#{!=;8<-B_LA7 zDw1r$V-kl{Ix8HIl0^}hbL+iyPVhJgqPh8w5hYX@#A<F0h*C^SE*O{{AB(9#oP@%; z?wmNec7Hh)|6)`^qFll}ikFG+X0uFg%F5~%Ifc=P$UiZ9aZ^P&OebMd4veroN|Qp6 z5v<3%zv|M0{><ZldTqa&zIKua&5p$58IMiMa<At`ve6HwZ3<JF?F(wNyzBvkkq{%( zjIv8{URIzAz=48VnZ8}?FI7`y7`3|VE2xpK$a1q^HK3mSdk=985ze8{kxs1NH=9md zltaK!p*aK#KaSu6)*GJj>YQh^yyLhtyT){-)idB%m%+~7gmjMD^!9CpuP(zIZuE)b z0(oWHhs?I+0X=6`uOY7i^p4?=FcpL98I2$vXKM(SFoH6fS?TEjp$KwhUmECIy8(Ef zu}i+4JUDv?P7g{OEv~kY1)XdMh5pF48WajWvNcabf2`r@$CY;!;-I<8HBYw*h?Up& z6`a=3x;A+}Q`rRjm+qddX*|!PEDjEmxlr1y?lz{~rFfV+m3{IeXb;daVJwixg`5^Q zGt5Clqq{4s8;=9*7gs-^cyHBjx4MHa=KXBT-3Khei2^#w$o7_CM)Cu?rRRJ9=(TBc zn5-a@4}j<1^WY=r9kv@&a$Gn50FO};3q?6yAm%(}Q5fBz)>xM-bqwQX%;lKC*%fbg z=S}`b2gd(N-|68MQ~X$E>!;~SO_Qxl3#AEi7*&(vzO6o8ADhVyeA3*<?6zq2CdJq1 w?goHo&CRLYe4sn(>-yU;K9tyL|ANZ?H+$l5-@EnB_{wlv43ort$d^~Y0TNA0e*gdg literal 0 HcmV?d00001 diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css index 5e726a363..128c9f93e 100644 --- a/src/components/library-item/library-item.css +++ b/src/components/library-item/library-item.css @@ -7,6 +7,7 @@ align-items: center; justify-content: flex-start; flex-basis: 160px; + position: relative; height: 160px; max-width: 160px; margin: $space; @@ -200,3 +201,45 @@ [dir="rtl"] .coming-soon-text { transform: translate(calc(-2 * $space), calc(2 * $space)); } + +.play-button { + display: flex; + align-items: center; + justify-content: center; + + overflow: hidden; /* Mask the icon animation */ + width: 1.5rem; + height: 1.5rem; + background-color: $motion-primary; + color: $ui-white; + border-radius: 50%; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + user-select: none; + cursor: pointer; + transition: all 0.15s ease-out; +} + +.play-button { + position: absolute; + position: absolute; + top: 0.125rem; + z-index: auto; +} + +.play-button:focus { + outline: none; +} + +.play-icon { + width: 50%; +} + +[dir="ltr"] .play-button { + right: 0.125rem; + padding-left: .125rem; +} + +[dir="rtl"] .play-button { + left: 0.125rem; + padding-right: .125rem; +} diff --git a/src/components/library-item/library-item.jsx b/src/components/library-item/library-item.jsx index 5af6fb159..3101e4279 100644 --- a/src/components/library-item/library-item.jsx +++ b/src/components/library-item/library-item.jsx @@ -8,6 +8,11 @@ import classNames from 'classnames'; import bluetoothIconURL from './bluetooth.svg'; import internetConnectionIconURL from './internet-connection.svg'; +import playIcon from './icon--play.svg'; + +const preventClick = e => { + e.stopPropagation(); +}; /* eslint-disable react/prefer-stateless-function */ class LibraryItemComponent extends React.PureComponent { @@ -115,12 +120,16 @@ class LibraryItemComponent extends React.PureComponent { onClick={this.props.onClick} onFocus={this.props.onFocus} onKeyPress={this.props.onKeyPress} - onMouseEnter={this.props.onMouseEnter} - onMouseLeave={this.props.onMouseLeave} + onMouseEnter={this.props.showPlayButton ? null : this.props.onMouseEnter} + onMouseLeave={this.props.showPlayButton ? null : this.props.onMouseLeave} > {/* Layers of wrapping is to prevent layout thrashing on animation */} <Box className={styles.libraryItemImageContainerWrapper}> - <Box className={styles.libraryItemImageContainer}> + <Box + className={styles.libraryItemImageContainer} + onMouseEnter={this.props.showPlayButton ? this.props.onMouseEnter : null} + onMouseLeave={this.props.showPlayButton ? this.props.onMouseLeave : null} + > <img className={styles.libraryItemImage} src={this.props.iconURL} @@ -128,6 +137,22 @@ class LibraryItemComponent extends React.PureComponent { </Box> </Box> <span className={styles.libraryItemName}>{this.props.name}</span> + {this.props.showPlayButton ? ( + <div + aria-label="Play" + className={styles.playButton} + role="button" + tabIndex="0" + onClick={preventClick} + onMouseDown={this.props.onPlay} + > + <img + className={styles.playIcon} + draggable={false} + src={playIcon} + /> + </div> + ) : null} </Box> ); } @@ -158,11 +183,14 @@ LibraryItemComponent.propTypes = { onFocus: PropTypes.func.isRequired, onKeyPress: PropTypes.func.isRequired, onMouseEnter: PropTypes.func.isRequired, - onMouseLeave: PropTypes.func.isRequired + onMouseLeave: PropTypes.func.isRequired, + onPlay: PropTypes.func.isRequired, + showPlayButton: PropTypes.bool }; LibraryItemComponent.defaultProps = { - disabled: false + disabled: false, + showPlayButton: false }; export default LibraryItemComponent; diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 13736d60f..3be73b34c 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -187,6 +187,7 @@ class LibraryComponent extends React.Component { internetConnectionRequired={dataItem.internetConnectionRequired} key={typeof dataItem.name === 'string' ? dataItem.name : dataItem.rawURL} name={dataItem.name} + showPlayButton={this.props.showPlayButton} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onSelect={this.handleSelect} @@ -227,12 +228,14 @@ LibraryComponent.propTypes = { onItemMouseLeave: PropTypes.func, onItemSelected: PropTypes.func, onRequestClose: PropTypes.func, + showPlayButton: PropTypes.bool, tags: PropTypes.arrayOf(PropTypes.shape(TagButton.propTypes)), title: PropTypes.string.isRequired }; LibraryComponent.defaultProps = { - filterable: true + filterable: true, + showPlayButton: false }; export default injectIntl(LibraryComponent); diff --git a/src/containers/library-item.jsx b/src/containers/library-item.jsx index 6177b916f..3ffa81485 100644 --- a/src/containers/library-item.jsx +++ b/src/containers/library-item.jsx @@ -15,6 +15,7 @@ class LibraryItem extends React.PureComponent { 'handleKeyPress', 'handleMouseEnter', 'handleMouseLeave', + 'handlePlay', 'rotateIcon', 'startRotatingIcons', 'stopRotatingIcons' @@ -62,6 +63,10 @@ class LibraryItem extends React.PureComponent { }, this.stopRotatingIcons); } } + handlePlay (e) { + e.stopPropagation(); // To prevent from bubbling back to handleClick + this.props.onMouseEnter(this.props.id); + } startRotatingIcons () { this.rotateIcon(); this.intervalId = setInterval(this.rotateIcon, 300); @@ -105,12 +110,14 @@ class LibraryItem extends React.PureComponent { insetIconURL={this.props.insetIconURL} internetConnectionRequired={this.props.internetConnectionRequired} name={this.props.name} + showPlayButton={this.props.showPlayButton} onBlur={this.handleBlur} onClick={this.handleClick} onFocus={this.handleFocus} onKeyPress={this.handleKeyPress} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} + onPlay={this.handlePlay} /> ); } @@ -143,7 +150,8 @@ LibraryItem.propTypes = { ]), onMouseEnter: PropTypes.func.isRequired, onMouseLeave: PropTypes.func.isRequired, - onSelect: PropTypes.func.isRequired + onSelect: PropTypes.func.isRequired, + showPlayButton: PropTypes.bool }; export default injectIntl(LibraryItem); diff --git a/src/containers/sound-library.jsx b/src/containers/sound-library.jsx index e49f1057a..677d93dfb 100644 --- a/src/containers/sound-library.jsx +++ b/src/containers/sound-library.jsx @@ -141,6 +141,7 @@ class SoundLibrary extends React.PureComponent { return ( <LibraryComponent + showPlayButton data={soundLibraryThumbnailData} id="soundLibrary" tags={soundTags} -- GitLab