From 66347c83a44f0ca683588f34d83398a57f263aee Mon Sep 17 00:00:00 2001 From: Paul Kaplan <pkaplan@media.mit.edu> Date: Fri, 6 Apr 2018 10:51:23 -0400 Subject: [PATCH] Add details to sprite tile and update styling --- src/components/asset-panel/icon--sound.svg | Bin 503 -> 2012 bytes src/components/asset-panel/selector.jsx | 1 + .../sprite-selector-item.css | 23 +++++++++++++----- .../sprite-selector-item.jsx | 8 +++++- src/containers/costume-tab.jsx | 13 +++++++++- src/containers/sound-tab.jsx | 3 ++- .../sprite-selector-item.test.jsx.snap | 19 ++++++++++++--- .../components/sprite-selector-item.test.jsx | 8 ++++-- 8 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/components/asset-panel/icon--sound.svg b/src/components/asset-panel/icon--sound.svg index c7e6081b50edbea029005aa7a483318912d3874f..152272535f732e16f1bdb88d458563481f7daf09 100644 GIT binary patch literal 2012 zcmZuy!EPfr488AHsMAXe*fJ$bq!`3rph4S9_LTOrw`@FX8*%Kw_Qv`AKFM}H3uF)= zGjHfe^6_zW`}zH4?cVy`ezo09yWpvF{pM*qUu_oC?%#j@8kg?#?9=W3ZQ+iq`C*9| zqSyD%E&J7CdHDbFZPg$D*uGCYa^zzD>xQ(Ub4aw=PrK#e@Om?u9FIqTWWU`lCXt9H zNN^6hd0($K&mRQ_Gn=%x-RzUYb^FVB?EdaI{qFIw-QBo{=l<}tbjdus-*O0|zq_#; zWc;>W_Ajf)bA$eA`!d;|els4QDG#f|x}QC4e{ANrlheZ~*Sz09&F*&n<Ds8Ji^KAq z&fiYjt4q}P{j4JRcS06!HJ^5kuygyvZu{I%yUljfU!KMnVLSJIwO(Id*8B19#~RW7 zTff=P=jX0l=dX__0K8v9^hrymTt>K+cB#9=J%^Mm=@14QQZ6OOhDME)k_*9`MImk! zIVH-WQUsq_`9vH|h&a?(E?LOZ2)CLghAb78oVnC&2w)ZF+=>WNiYkJsl7&zyP{bKE zk8Bo-=#Zj#MUe#ZR>UP=@f>pr5=sp|m!@8eP%P3)yR@P8wH2b!iby%8R7UEfMa>k@ zGJC2K(SRnVA|-%<JyQ~B0kot-iL};`e9#&lM&=_0Vy?kg6wQ@FW(7)IDbQzRHmfzT z4-q{}(>(`d$!IzHLXzW8lLPp{JiodxAwzi*EfAA^;dX&K`<SR0f%*yqCCdeKsHmF6 z(keEQ%q9&CC6c7pUWS9B<qD^gnb1e_buxA;&X5&aRaDeMJh>{DoP;Yw_Sup_mO6e4 zuu??2D{3Bnf^J9v-Ax&+)=Ic!Qy(a?aE>+jQkWnCtfrx%moCI3kb_92fIJx$L1OWN z6T=c{BYPByiVbL#s7WZhAfC`r^@PB<DWw=06pHa<lY)+_1+fvr$ah7Xw~N8OLy9U| zRDuCo6~_2Z#Wp0tSd1ZC#$r;;RkojI^ua*uq^a7FVS^EV7-859x7-il%Azb<ckCJr zz3?G0OvE6fiIrrdstY&*RsjnnAcc8H2vzJmLFId?Yb+F+Ric^-Vf!Isf!tgdJ$h9* z2*eq#NM;N@t5Ho*hPvtsB#G8OHW&0hdA7(f_%Qa4(Gn{xi7KuXNFIDKMxn*Ehme#6 z8+Ff^28&=X3lwIoCY%6<#BRW))pv-}fO0N*2Oz4ETwrb3d;s5}BYfLbqYhzouK}MR zt8PMzF=t8+w<*NBD%DN+%;5^|exNI4g-_81Djf^~p9)s&RfHq2^Fl?iCU%gA=wReu uyLNWmZC}2vHvN43uzY;Qv2i*je$ATy)ZF2;sfWS&w3#e$TG!Kb_US*9#>GVd literal 503 zcmX|8!ES>v4E+@=J7eO6B+gWN*lCCLx;t7KurWZupxW0jp-?HYWBb`pemPA`1-GBJ zn{rXl^YEOe>$>8qu^+2cNh#BS5U`Zv)bw30NWj`$=Q<ael*25bE}N>JAK}uJ>q~#j zg#-!Cgd@(U3Z{ANzsp=a5rql&DA@-^#8Axjk#3u={OX%PHuk^WRlsM{wnytV7Z>eO zvj@`XtbbDql{TOlt+cOpA&=7QnhbjOEji2}yrrS{d&8edcfL3)V<g3pg(h7niIN@c zE^Z)`kTHoO%#6O6Fo#vF8|@0^kuBV9#1a@}4r~sCjd{J>t(q8g%&}^{zgR*?U=>=T zLNj`!V0RlYFlNMw%EEGbt5`nV21pnU`3TUK4~@FMD<ASk9Y2cUkTGnXR^f*KJpTjv Cl7G(t diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx index ff818415a..6c3f72edb 100644 --- a/src/components/asset-panel/selector.jsx +++ b/src/components/asset-panel/selector.jsx @@ -42,6 +42,7 @@ const Selector = props => { assetId={item.assetId} className={styles.listItem} costumeURL={item.url} + details={item.details} id={index} key={`asset-${index}`} name={item.name} diff --git a/src/components/sprite-selector-item/sprite-selector-item.css b/src/components/sprite-selector-item/sprite-selector-item.css index 8567d1352..b02b4bfdb 100644 --- a/src/components/sprite-selector-item/sprite-selector-item.css +++ b/src/components/sprite-selector-item/sprite-selector-item.css @@ -10,24 +10,26 @@ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 0.8rem; - background: white; color: $text-primary; border-width: 2px; border-style: solid; border-color: $ui-black-transparent; border-radius: $space; + text-align: center; cursor: pointer; transition: 0.25s ease-out; } .sprite-selector-item.is-selected { - border: 2px solid $motion-primary; box-shadow: 0px 0px 0px 4px $motion-transparent; + border: 2px solid $motion-primary; + background: $ui-white; } .sprite-selector-item:hover { border: 2px solid $motion-primary; + background: $ui-white; } .sprite-image { @@ -35,9 +37,13 @@ user-select: none; } -.sprite-name { +.sprite-info { + padding: 0.2rem 0.2rem 0.1rem ; + border-bottom-left-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + font-size: 0.625rem; - margin: 0.15rem; + color: $text-primary; user-select: none; /* @@ -50,6 +56,10 @@ min-width: 0; } +.is-selected .sprite-info { + background: $motion-primary; + color: $ui-white; +} .delete-button { position: absolute; top: 0.125rem; @@ -59,8 +69,9 @@ .number { position: absolute; - top: 0.125rem; - left: 0.125rem; + top: 0.15rem; + left: 0.15rem; font-size: 0.625rem; + font-weight: bold; z-index: 2; } diff --git a/src/components/sprite-selector-item/sprite-selector-item.jsx b/src/components/sprite-selector-item/sprite-selector-item.jsx index 35eeebe00..2cfdd1e26 100644 --- a/src/components/sprite-selector-item/sprite-selector-item.jsx +++ b/src/components/sprite-selector-item/sprite-selector-item.jsx @@ -42,7 +42,12 @@ const SpriteSelectorItem = props => ( width={32} /> ) : null} - <div className={styles.spriteName}>{props.name}</div> + <div className={styles.spriteInfo}> + <div className={styles.spriteName}>{props.name}</div> + {props.details ? ( + <div className={styles.spriteDetails}>{props.details}</div> + ) : null} + </div> {props.onDuplicateButtonClick || props.onDeleteButtonClick ? ( <ContextMenu id={`${props.name}-${contextMenuId++}`}> {props.onDuplicateButtonClick ? ( @@ -73,6 +78,7 @@ SpriteSelectorItem.propTypes = { costumeURL: PropTypes.string, name: PropTypes.string.isRequired, number: PropTypes.number, + details: PropTypes.string, onClick: PropTypes.func, onDeleteButtonClick: PropTypes.func, onDuplicateButtonClick: PropTypes.func, diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx index 37fd8a12e..61337c540 100644 --- a/src/containers/costume-tab.jsx +++ b/src/containers/costume-tab.jsx @@ -158,6 +158,11 @@ class CostumeTab extends React.Component { }; this.props.vm.addCostume(item.md5, vmCostume); } + formatCostumeDetails (size) { + // Round up width and height for scratch-flash compatibility + // https://github.com/LLK/scratch-flash/blob/9fbac92ef3d09ceca0c0782f8a08deaa79e4df69/src/ui/media/MediaInfo.as#L224-L237 + return `${Math.ceil(size[0])} x ${Math.ceil(size[1])}` + } render () { const { intl, @@ -184,6 +189,12 @@ class CostumeTab extends React.Component { const addLibraryFunc = target.isStage ? onNewLibraryBackdropClick : onNewLibraryCostumeClick; const addLibraryIcon = target.isStage ? addLibraryBackdropIcon : addLibraryCostumeIcon; + const costumeData = (target.costumes || []).map((costume) => ({ + name: costume.name, + assetId: costume.assetId, + details: costume.size ? this.formatCostumeDetails(costume.size) : null + })); + return ( <AssetPanel buttons={[ @@ -211,7 +222,7 @@ class CostumeTab extends React.Component { onClick: this.handleNewBlankCostume } ]} - items={target.costumes || []} + items={costumeData} selectedItemIndex={this.state.selectedCostumeIndex} onDeleteClick={target.costumes.length > 1 ? this.handleDeleteCostume : null} onDuplicateClick={this.handleDuplicateCostume} diff --git a/src/containers/sound-tab.jsx b/src/containers/sound-tab.jsx index b0b4e3d22..3662b1f66 100644 --- a/src/containers/sound-tab.jsx +++ b/src/containers/sound-tab.jsx @@ -115,7 +115,8 @@ class SoundTab extends React.Component { const sounds = sprite.sounds ? sprite.sounds.map(sound => ( { url: soundIcon, - name: sound.name + name: sound.name, + details: (sound.sampleCount / sound.rate).toFixed(2) } )) : []; diff --git a/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap b/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap index eaca8d936..2c276ff65 100644 --- a/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap +++ b/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`SpriteSelectorItemComponent matches snapshot when given a number to show 1`] = ` +exports[`SpriteSelectorItemComponent matches snapshot when given a number and details to show 1`] = ` <div className="react-contextmenu-wrapper ponies undefined" onClick={[Function]} @@ -44,7 +44,16 @@ exports[`SpriteSelectorItemComponent matches snapshot when given a number to sho <div className={undefined} > - Pony sprite + <div + className={undefined} + > + Pony sprite + </div> + <div + className={undefined} + > + 480 x 360 + </div> </div> <nav className="react-contextmenu" @@ -118,7 +127,11 @@ exports[`SpriteSelectorItemComponent matches snapshot when selected 1`] = ` <div className={undefined} > - Pony sprite + <div + className={undefined} + > + Pony sprite + </div> </div> <nav className="react-contextmenu" diff --git a/test/unit/components/sprite-selector-item.test.jsx b/test/unit/components/sprite-selector-item.test.jsx index 5848e0ca9..1bf52fe97 100644 --- a/test/unit/components/sprite-selector-item.test.jsx +++ b/test/unit/components/sprite-selector-item.test.jsx @@ -12,6 +12,7 @@ describe('SpriteSelectorItemComponent', () => { let onDeleteButtonClick; let selected; let number; + let details; // Wrap this in a function so it gets test specific states and can be reused. const getComponent = function () { @@ -19,6 +20,7 @@ describe('SpriteSelectorItemComponent', () => { <SpriteSelectorItemComponent className={className} costumeURL={costumeURL} + details={details} name={name} number={number} selected={selected} @@ -35,8 +37,9 @@ describe('SpriteSelectorItemComponent', () => { onClick = jest.fn(); onDeleteButtonClick = jest.fn(); selected = true; - // Reset number to undefined since it is an optional prop + // Reset to undefined since they are optional props number = undefined; // eslint-disable-line no-undefined + details = undefined; // eslint-disable-line no-undefined }); test('matches snapshot when selected', () => { @@ -44,8 +47,9 @@ describe('SpriteSelectorItemComponent', () => { expect(component.toJSON()).toMatchSnapshot(); }); - test('matches snapshot when given a number to show', () => { + test('matches snapshot when given a number and details to show', () => { number = 5; + details = '480 x 360'; const component = componentWithIntl(getComponent()); expect(component.toJSON()).toMatchSnapshot(); }); -- GitLab