From 0bb0341698e0edef9fc73dbaa6ec79c956152e89 Mon Sep 17 00:00:00 2001 From: steven dale Date: Wed, 10 May 2017 18:07:31 -0400 Subject: [PATCH 01/43] Full height/width modal for libraries --- src/components/library/library.jsx | 2 +- src/components/modal/modal.css | 19 +++++++++++++++++++ src/components/modal/modal.jsx | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 16327c10c..8800025bd 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -33,7 +33,7 @@ class LibraryComponent extends React.Component { visible={this.props.visible} onRequestClose={this.props.onRequestClose} > -

{this.props.title}

+ //

{this.props.title}

{this.props.data.map((dataItem, itemId) => { const scratchURL = dataItem.md5 ? diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index 25a134f5e..044a914b3 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -40,3 +40,22 @@ right: 1rem; z-index: 2; } + +.full-modal-content { + // same as .modal-children + position: absolute; + outline: none; + overflow-y: scroll; + -webkit-overflow-scrolling: 'touch'; + border: 1px solid #ccc; + padding: 0; + user-select: none; + + // different from .modal-children + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: 0; + +} diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index 3a3fc25db..7d8582d1b 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -11,7 +11,7 @@ class ModalComponent extends React.Component { render () { return ( Date: Wed, 10 May 2017 18:15:25 -0400 Subject: [PATCH 02/43] Removed hover grow transition --- src/components/library-item/library-item.css | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css index e25c478d2..055a65a4b 100644 --- a/src/components/library-item/library-item.css +++ b/src/components/library-item/library-item.css @@ -23,14 +23,11 @@ .library-item:hover { border-width: 2px; border-color: #1dacf4; - transition: 0.1s ease-out; - transform: scale(1.02, 1.02); } .library-item.is-selected { border-width: 2px; border-color: #1dacf4; - transition: 0.25s ease-out; } .library-item-image-container { @@ -47,12 +44,12 @@ margin: 0.25rem 0; text-align: center; - /* + /* For truncating overflowing text gracefully Min-width is for a bug: https://css-tricks.com/flexbox-truncated-text */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - min-width: 0; + min-width: 0; } From c2322d3aa821c544272cf21a797c915346bab79d Mon Sep 17 00:00:00 2001 From: steven dale Date: Thu, 11 May 2017 08:46:53 -0400 Subject: [PATCH 03/43] wip --- src/components/library/icon--search.svg | 12 ++++ src/components/library/library.css | 75 ++++++++++++++++++++++--- src/components/library/library.jsx | 20 ++++++- src/components/modal/modal.css | 17 +++--- src/components/modal/modal.jsx | 2 +- src/css/units.css | 2 +- 6 files changed, 107 insertions(+), 21 deletions(-) create mode 100644 src/components/library/icon--search.svg diff --git a/src/components/library/icon--search.svg b/src/components/library/icon--search.svg new file mode 100644 index 000000000..5c21921a6 --- /dev/null +++ b/src/components/library/icon--search.svg @@ -0,0 +1,12 @@ + + + + + + + diff --git a/src/components/library/library.css b/src/components/library/library.css index b8147a173..b9c6ea90b 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -1,16 +1,14 @@ +@import "../../css/units.css"; +@import "../../css/colors.css"; + .library-scroll-grid { display: flex; flex-grow: 1; - justify-content: space-between; flex-wrap: wrap; - overflow-y: scroll; - height: calc(100% - 6rem); /* @todo: currently estimate, fix precision */ + justify-content: space-between; - /* - Gives sprites a bit of room so they don't get cut off when they grow on hover - @todo: sync as a var, with the transform defined on .library-item:hover - */ - padding: 0.15rem; + overflow-y: scroll; + /* height: calc(100% - $menu-bar-height); */ } .modal-header { @@ -22,3 +20,64 @@ font-weight: normal; color: #8e8f95; } + +.full-modal-header { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: flex-start; + align-content: center; + + box-sizing: border-box; + /* height: $menu-bar-height; */ + width: 100%; + background-color: $blue; + + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 1rem; + font-weight: normal; +} + + +.modal-header-item { + display: flex; + align-items: center; + padding: 1rem 1.5rem; + /* line-height: $menu-bar-height; */ + cursor: pointer; + text-decoration: none; + color: white; + user-select: none; +} + +.header-item-search { + display: flex; + flex-basis: 15rem; + justify-content: flex-end; +} + +.header-item-title { + flex-grow: 1; + justify-content: center; +} + +.header-item-close { + flex-basis: 15rem; + justify-content: flex-end; +} + +.input-search { + /* position: relative; */ + outline: none; + line-height: 2rem; + border-radius: 10rem; + font-size: 1.25rem; + -webkit-appearance: none; + border: 0; + background: rgba(0,0,0,.10); + color: white; + padding: 0.25rem 1rem; + + background-position: 50% 50%; + background-size: fit; +} diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 8800025bd..7f340e2a7 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -1,9 +1,12 @@ +const classNames = require('classnames'); const bindAll = require('lodash.bindall'); const PropTypes = require('prop-types'); const React = require('react'); const LibraryItem = require('../library-item/library-item.jsx'); const ModalComponent = require('../modal/modal.jsx'); +const searchIcon = require('./icon--search.svg'); +const searchIconUrl = "url(" + searchIcon + ")"; const styles = require('./library.css'); @@ -33,7 +36,22 @@ class LibraryComponent extends React.Component { visible={this.props.visible} onRequestClose={this.props.onRequestClose} > - //

{this.props.title}

+
+
+ + + +
+
{this.props.title}
+
right
+
+
{this.props.data.map((dataItem, itemId) => { const scratchURL = dataItem.md5 ? diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index 044a914b3..01bd3542a 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -42,20 +42,17 @@ } .full-modal-content { - // same as .modal-children position: absolute; outline: none; overflow-y: scroll; -webkit-overflow-scrolling: 'touch'; - border: 1px solid #ccc; - padding: 0; user-select: none; + height: 100%; +} - // different from .modal-children - top: 0; - right: 0; - bottom: 0; - left: 0; - border-radius: 0; - +.full-modal-children { + overflow: hidden; + height: 100%; + z-index: 0; + background: $ui-pane-gray; } diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index 7d8582d1b..e505ce8dc 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -23,7 +23,7 @@ class ModalComponent extends React.Component { onClick={this.props.onRequestClose} /> {this.props.children} diff --git a/src/css/units.css b/src/css/units.css index ab4393ac1..3a1a577b7 100644 --- a/src/css/units.css +++ b/src/css/units.css @@ -3,5 +3,5 @@ $space: 0.5rem; $sprites-per-row: 5; $menu-bar-height: 3rem; -$sprite-info-height: 5.25rem; /* TODO: SpriteInfo isn't explicitly set to this height yet */ +$sprite-info-height: 5.25rem; /* @todo: SpriteInfo isn't explicitly set to this height yet */ $stage-menu-height: 3rem; From 9c31968e471ecac48dfc3f0adb088362f61b8397 Mon Sep 17 00:00:00 2001 From: steven dale Date: Thu, 11 May 2017 11:59:24 -0400 Subject: [PATCH 04/43] filter component: first pass --- .../{icon--search.svg => icon--filter.svg} | 0 src/components/library/icon--x.svg | 5 ++ src/components/library/library.css | 87 ++++++++++++++----- src/components/library/library.jsx | 42 ++++++--- 4 files changed, 100 insertions(+), 34 deletions(-) rename src/components/library/{icon--search.svg => icon--filter.svg} (100%) create mode 100644 src/components/library/icon--x.svg diff --git a/src/components/library/icon--search.svg b/src/components/library/icon--filter.svg similarity index 100% rename from src/components/library/icon--search.svg rename to src/components/library/icon--filter.svg diff --git a/src/components/library/icon--x.svg b/src/components/library/icon--x.svg new file mode 100644 index 000000000..cb474c202 --- /dev/null +++ b/src/components/library/icon--x.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/library/library.css b/src/components/library/library.css index b9c6ea90b..fd308de7b 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -8,7 +8,6 @@ justify-content: space-between; overflow-y: scroll; - /* height: calc(100% - $menu-bar-height); */ } .modal-header { @@ -29,7 +28,6 @@ align-content: center; box-sizing: border-box; - /* height: $menu-bar-height; */ width: 100%; background-color: $blue; @@ -38,46 +36,93 @@ font-weight: normal; } - .modal-header-item { display: flex; align-items: center; - padding: 1rem 1.5rem; - /* line-height: $menu-bar-height; */ + padding: 1rem; cursor: pointer; text-decoration: none; color: white; user-select: none; } -.header-item-search { +/* + Modal header has 3 items: + + |filter title x| + + Use the same width for both side item containers, + so that title remains centered +*/ +$sides: 20rem; + +.header-item-filter { display: flex; - flex-basis: 15rem; + flex-basis: $sides; justify-content: flex-end; } .header-item-title { flex-grow: 1; + flex-shrink: 0; justify-content: center; } .header-item-close { - flex-basis: 15rem; + flex-basis: $sides; justify-content: flex-end; } -.input-search { - /* position: relative; */ - outline: none; - line-height: 2rem; - border-radius: 10rem; - font-size: 1.25rem; - -webkit-appearance: none; - border: 0; - background: rgba(0,0,0,.10); - color: white; - padding: 0.25rem 1rem; +/* @todo: make filter component */ +.filter-component { + display: flex; + flex-direction: row; + align-content: center; + align-items: center; - background-position: 50% 50%; - background-size: fit; + padding: $space; + background: rgba(0, 0, 0, 0.10); + border: 1px solid rgba(255, 255, 255, 0.10); + border-radius: 10rem; + cursor: initial; +} + +.filter-icon { + height: 1rem; + width: 1rem; + margin: 0 0.5rem 0 0.75rem; + cursor: initial; +} + +.x-icon { + position: relative; + margin: 0 0.75rem 0 0.5rem; +} + +.x-icon:hover:before { + position: absolute; + height: 1.25rem; + width: 1.25rem; + background: rgba(255, 255, 255, 0.10); +} + +.filter-input { + flex-grow: 1; + line-height: 1.25rem; + background-color: transparent; + -webkit-appearance: none; + outline: none; + border: 0; + color: white; + font-size: 0.75rem; + letter-spacing: 0.15px; + cursor: text; +} + +.filter-input::placeholder { + color: white; + opacity: 0.75; + font-size: 0.75rem; + letter-spacing: 0.15px; + padding: 0 0 0 0.25rem; } diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 7f340e2a7..7ab8ae938 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -5,8 +5,8 @@ const React = require('react'); const LibraryItem = require('../library-item/library-item.jsx'); const ModalComponent = require('../modal/modal.jsx'); -const searchIcon = require('./icon--search.svg'); -const searchIconUrl = "url(" + searchIcon + ")"; +const filterIcon = require('./icon--filter.svg'); +const xIcon = require('./icon--x.svg'); const styles = require('./library.css'); @@ -37,19 +37,35 @@ class LibraryComponent extends React.Component { onRequestClose={this.props.onRequestClose} >
-
- - +
- + + + + +
-
{this.props.title}
-
right
+
{this.props.title}
+
From c91ff51c0653a6e3fcfeea7d10efb236ec9b9936 Mon Sep 17 00:00:00 2001 From: steven dale Date: Thu, 11 May 2017 15:43:15 -0400 Subject: [PATCH 05/43] pass on form styling, x icon hover --- src/components/library-item/library-item.css | 2 +- src/components/library/library.css | 56 ++++++++++++++------ src/components/library/library.jsx | 23 +++++--- 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css index 055a65a4b..916c3a5c5 100644 --- a/src/components/library-item/library-item.css +++ b/src/components/library-item/library-item.css @@ -7,7 +7,7 @@ justify-content: center; flex-basis: 160px; height: 160px; - margin: calc($space / 2); + margin: $space; padding: 1rem 1rem 0 1rem; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; color: #575e75; diff --git a/src/components/library/library.css b/src/components/library/library.css index fd308de7b..a36e56a4b 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -5,9 +5,11 @@ display: flex; flex-grow: 1; flex-wrap: wrap; - justify-content: space-between; + justify-content: center; + height: 100%; overflow-y: scroll; + padding: 1rem 1rem 5rem 1rem; } .modal-header { @@ -20,6 +22,9 @@ color: #8e8f95; } +/* + @todo: move to Modal component? +*/ .full-modal-header { display: flex; flex-direction: row; @@ -40,7 +45,6 @@ display: flex; align-items: center; padding: 1rem; - cursor: pointer; text-decoration: none; color: white; user-select: none; @@ -48,7 +52,6 @@ /* Modal header has 3 items: - |filter title x| Use the same width for both side item containers, @@ -66,6 +69,9 @@ $sides: 20rem; flex-grow: 1; flex-shrink: 0; justify-content: center; + user-select: none; + cursor: default; + letter-spacing: 0.4px; } .header-item-close { @@ -76,34 +82,50 @@ $sides: 20rem; /* @todo: make filter component */ .filter-component { display: flex; + box-sizing: border-box; flex-direction: row; align-content: center; align-items: center; - padding: $space; + padding: 0.3rem 0.5rem; background: rgba(0, 0, 0, 0.10); border: 1px solid rgba(255, 255, 255, 0.10); border-radius: 10rem; - cursor: initial; + + user-select: none; + transition: all 0.15s ease-out; +} + +.filter-component:hover { + background: rgba(0, 0, 0, 0.15); } .filter-icon { - height: 1rem; - width: 1rem; + height: 0.9rem; + width: 0.9rem; margin: 0 0.5rem 0 0.75rem; - cursor: initial; +} + +.x-icon-wrapper { + display: flex; + justify-content: center; + align-items: center; + + height: 1.25rem; + width: 1.25rem; + margin: 0 0.25rem 0 0.5rem; + + border-radius: 50%; + cursor: pointer; +} + +.x-icon-wrapper:hover { + background-color: rgba(255, 255, 255, 0.2); } .x-icon { - position: relative; - margin: 0 0.75rem 0 0.5rem; -} - -.x-icon:hover:before { - position: absolute; - height: 1.25rem; - width: 1.25rem; - background: rgba(255, 255, 255, 0.10); + margin: 0.25rem; + user-select: none; } .filter-input { diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 7ab8ae938..4ad36c29f 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -54,18 +54,29 @@ class LibraryComponent extends React.Component { placeholder='what are you looking for?' > - +
+ +
{this.props.title}
-
+ )} + > + {this.props.title} +
+
+
From cc6665f8e28c9092f28f3aa87d14e0b1ef2e3e9d Mon Sep 17 00:00:00 2001 From: steven dale Date: Thu, 11 May 2017 16:48:49 -0400 Subject: [PATCH 06/43] css cleanup --- src/components/library/library.css | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/components/library/library.css b/src/components/library/library.css index a36e56a4b..efb17da80 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -30,7 +30,6 @@ flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; - align-content: center; box-sizing: border-box; width: 100%; @@ -70,8 +69,8 @@ $sides: 20rem; flex-shrink: 0; justify-content: center; user-select: none; - cursor: default; letter-spacing: 0.4px; + cursor: default; } .header-item-close { @@ -82,9 +81,7 @@ $sides: 20rem; /* @todo: make filter component */ .filter-component { display: flex; - box-sizing: border-box; flex-direction: row; - align-content: center; align-items: center; padding: 0.3rem 0.5rem; @@ -93,13 +90,18 @@ $sides: 20rem; border-radius: 10rem; user-select: none; - transition: all 0.15s ease-out; } .filter-component:hover { background: rgba(0, 0, 0, 0.15); } +/* @todo: move this animation to onClick? */ +.filter-component:hover .x-icon-wrapper .x-icon { + left: 0; + opacity: 1; +} + .filter-icon { height: 0.9rem; width: 0.9rem; @@ -111,6 +113,7 @@ $sides: 20rem; justify-content: center; align-items: center; + overflow: hidden; /* Mask the icon animation */ height: 1.25rem; width: 1.25rem; margin: 0 0.25rem 0 0.5rem; @@ -124,8 +127,16 @@ $sides: 20rem; } .x-icon { + position: relative; + left: 10px; + opacity: 0; + margin: 0.25rem; user-select: none; + + /* @todo: transforms instead of left, for smoother perf? */ + transition: left 0.09s cubic-bezier(0.78, 1, 1, 1), + opacity 0.04s ease; } .filter-input { From 914cfa29e32b6f6deb8756788e57ff7f07bac899 Mon Sep 17 00:00:00 2001 From: steven dale Date: Fri, 12 May 2017 12:26:21 -0400 Subject: [PATCH 07/43] Updated transition to not reveal edge. Refactored to better performing transform property. --- src/components/library/library.css | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/library/library.css b/src/components/library/library.css index efb17da80..8866bc32a 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -98,7 +98,7 @@ $sides: 20rem; /* @todo: move this animation to onClick? */ .filter-component:hover .x-icon-wrapper .x-icon { - left: 0; + transform: translateX(0); opacity: 1; } @@ -128,15 +128,14 @@ $sides: 20rem; .x-icon { position: relative; - left: 10px; + transform: translateX(8px); opacity: 0; margin: 0.25rem; user-select: none; - /* @todo: transforms instead of left, for smoother perf? */ - transition: left 0.09s cubic-bezier(0.78, 1, 1, 1), - opacity 0.04s ease; + transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1), + opacity 0.05s linear; } .filter-input { From 44b1d091811202a3d772a2882aec57869e277109 Mon Sep 17 00:00:00 2001 From: steven dale Date: Fri, 12 May 2017 12:44:05 -0400 Subject: [PATCH 08/43] Optimized svg --- src/components/library/icon--filter.svg | 13 ++----------- src/components/library/icon--x.svg | 4 +--- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/components/library/icon--filter.svg b/src/components/library/icon--filter.svg index 5c21921a6..2a0c75fcb 100644 --- a/src/components/library/icon--filter.svg +++ b/src/components/library/icon--filter.svg @@ -1,12 +1,3 @@ - - - - - - + + diff --git a/src/components/library/icon--x.svg b/src/components/library/icon--x.svg index cb474c202..313a523cd 100644 --- a/src/components/library/icon--x.svg +++ b/src/components/library/icon--x.svg @@ -1,5 +1,3 @@ - - - + From 7ba981d4b8dfa501b29813e25fd55fc1f2439fe9 Mon Sep 17 00:00:00 2001 From: steven dale Date: Mon, 15 May 2017 12:22:01 -0400 Subject: [PATCH 09/43] wiring filter component, onClick + onBlur handlers --- src/components/filter/filter.css | 80 ++++++++++++++++++ src/components/filter/filter.jsx | 52 ++++++++++++ .../{library => filter}/icon--filter.svg | 0 .../{library => filter}/icon--x.svg | 0 src/components/library/library.css | 81 ------------------- src/components/library/library.jsx | 28 +------ src/containers/filter.jsx | 39 +++++++++ 7 files changed, 174 insertions(+), 106 deletions(-) create mode 100644 src/components/filter/filter.css create mode 100644 src/components/filter/filter.jsx rename src/components/{library => filter}/icon--filter.svg (100%) rename src/components/{library => filter}/icon--x.svg (100%) create mode 100644 src/containers/filter.jsx diff --git a/src/components/filter/filter.css b/src/components/filter/filter.css new file mode 100644 index 000000000..5a8b12df4 --- /dev/null +++ b/src/components/filter/filter.css @@ -0,0 +1,80 @@ +@import "../../css/units.css"; +@import "../../css/colors.css"; + +.filter { + display: flex; + flex-direction: row; + align-items: center; + + padding: 0.3rem 0.5rem; + background: rgba(0, 0, 0, 0.10); + border: 1px solid rgba(255, 255, 255, 0.10); + border-radius: 10rem; + + user-select: none; +} + +.filter:hover { + background: rgba(0, 0, 0, 0.15); +} + +/* @todo: move this animation to onClick? */ +.filter:hover .x-icon-wrapper .x-icon { + transform: translateX(0); + opacity: 1; +} + +.filter-icon { + height: 0.9rem; + width: 0.9rem; + margin: 0 0.5rem 0 0.75rem; +} + +.x-icon-wrapper { + display: flex; + justify-content: center; + align-items: center; + + overflow: hidden; /* Mask the icon animation */ + height: 1.25rem; + width: 1.25rem; + margin: 0 0.25rem 0 0.5rem; + + border-radius: 50%; + cursor: pointer; +} + +.x-icon-wrapper:hover { + background-color: rgba(255, 255, 255, 0.2); +} + +.x-icon { + position: relative; + transform: translateX(0.5rem); + opacity: 0; + margin: 0.25rem; + user-select: none; + transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1), + opacity 0.05s linear; +} + +.filter-input { + flex-grow: 1; + line-height: 1.25rem; + background-color: transparent; + -webkit-appearance: none; + outline: none; + border: 0; + color: white; + font-size: 0.75rem; + letter-spacing: 0.15px; + cursor: text; +} + +.filter-input::placeholder { + opacity: 0.75; + padding: 0 0 0 0.25rem; + color: white; + font-size: 0.75rem; + letter-spacing: 0.15px; +} diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx new file mode 100644 index 000000000..2a615bd33 --- /dev/null +++ b/src/components/filter/filter.jsx @@ -0,0 +1,52 @@ +const classNames = require('classnames'); +const PropTypes = require('prop-types'); +const React = require('react'); + +const filterIcon = require('./icon--filter.svg'); +const xIcon = require('./icon--x.svg'); +const styles = require('./filter.css'); + +const FilterComponent = props => { + const { + onClick, + onBlur, + placeholderText + } = props; + return ( +
+ + + +
+ +
+
+ ); +}; + +FilterComponent.propTypes = { + onClick: PropTypes.func.isRequired, + onBlur: PropTypes.func.isRequired, + placeholderText: PropTypes.string +}; +FilterComponent.defaultProps = { + placeholderText: 'what are you looking for?' +}; +module.exports = FilterComponent; diff --git a/src/components/library/icon--filter.svg b/src/components/filter/icon--filter.svg similarity index 100% rename from src/components/library/icon--filter.svg rename to src/components/filter/icon--filter.svg diff --git a/src/components/library/icon--x.svg b/src/components/filter/icon--x.svg similarity index 100% rename from src/components/library/icon--x.svg rename to src/components/filter/icon--x.svg diff --git a/src/components/library/library.css b/src/components/library/library.css index 8866bc32a..b76393282 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -77,84 +77,3 @@ $sides: 20rem; flex-basis: $sides; justify-content: flex-end; } - -/* @todo: make filter component */ -.filter-component { - display: flex; - flex-direction: row; - align-items: center; - - padding: 0.3rem 0.5rem; - background: rgba(0, 0, 0, 0.10); - border: 1px solid rgba(255, 255, 255, 0.10); - border-radius: 10rem; - - user-select: none; -} - -.filter-component:hover { - background: rgba(0, 0, 0, 0.15); -} - -/* @todo: move this animation to onClick? */ -.filter-component:hover .x-icon-wrapper .x-icon { - transform: translateX(0); - opacity: 1; -} - -.filter-icon { - height: 0.9rem; - width: 0.9rem; - margin: 0 0.5rem 0 0.75rem; -} - -.x-icon-wrapper { - display: flex; - justify-content: center; - align-items: center; - - overflow: hidden; /* Mask the icon animation */ - height: 1.25rem; - width: 1.25rem; - margin: 0 0.25rem 0 0.5rem; - - border-radius: 50%; - cursor: pointer; -} - -.x-icon-wrapper:hover { - background-color: rgba(255, 255, 255, 0.2); -} - -.x-icon { - position: relative; - transform: translateX(8px); - opacity: 0; - - margin: 0.25rem; - user-select: none; - - transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1), - opacity 0.05s linear; -} - -.filter-input { - flex-grow: 1; - line-height: 1.25rem; - background-color: transparent; - -webkit-appearance: none; - outline: none; - border: 0; - color: white; - font-size: 0.75rem; - letter-spacing: 0.15px; - cursor: text; -} - -.filter-input::placeholder { - color: white; - opacity: 0.75; - font-size: 0.75rem; - letter-spacing: 0.15px; - padding: 0 0 0 0.25rem; -} diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 4ad36c29f..a9683ea99 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -5,8 +5,7 @@ const React = require('react'); const LibraryItem = require('../library-item/library-item.jsx'); const ModalComponent = require('../modal/modal.jsx'); -const filterIcon = require('./icon--filter.svg'); -const xIcon = require('./icon--x.svg'); +const Filter = require('../../containers/filter.jsx'); const styles = require('./library.css'); @@ -38,29 +37,8 @@ class LibraryComponent extends React.Component { >
-
- - - -
- -
-
+ +
+ ); + } +} + +module.exports = Filter; From af6aae7a363dc105accfdab369a462d8571996c0 Mon Sep 17 00:00:00 2001 From: steven dale Date: Mon, 15 May 2017 15:52:48 -0400 Subject: [PATCH 10/43] determining active state --- src/components/filter/filter.css | 49 +++++++++++++++++++++++--------- src/components/filter/filter.jsx | 11 +++++-- src/containers/filter.jsx | 14 ++++----- 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/components/filter/filter.css b/src/components/filter/filter.css index 5a8b12df4..1855fcfa5 100644 --- a/src/components/filter/filter.css +++ b/src/components/filter/filter.css @@ -18,19 +18,18 @@ background: rgba(0, 0, 0, 0.15); } -/* @todo: move this animation to onClick? */ -.filter:hover .x-icon-wrapper .x-icon { - transform: translateX(0); - opacity: 1; -} - .filter-icon { height: 0.9rem; width: 0.9rem; margin: 0 0.5rem 0 0.75rem; } +/* + Hidden state +*/ .x-icon-wrapper { + opacity: 0; + display: flex; justify-content: center; align-items: center; @@ -42,22 +41,46 @@ border-radius: 50%; cursor: pointer; -} - -.x-icon-wrapper:hover { background-color: rgba(255, 255, 255, 0.2); + + pointer-events: none; + cursor: default; + + /* Why doesn't this work? */ + /* transition: opacity 0.05s linear; */ } +/* + Shown state +*/ +.filter.is-active .x-icon-wrapper { + pointer-events: auto; + cursor: pointer; + opacity: 1; +} + +/* + Hidden state +*/ .x-icon { position: relative; - transform: translateX(0.5rem); - opacity: 0; margin: 0.25rem; user-select: none; - transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1), - opacity 0.05s linear; + /* transform: translateX(0.5rem); */ + /* transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1); */ + + /* transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1), + opacity 0.05s linear; */ } +/* + Shown state +*/ +.filter.is-active .x-icon-wrapper .x-icon { + transform: translateX(0); +} + + .filter-input { flex-grow: 1; line-height: 1.25rem; diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx index 2a615bd33..9273c769d 100644 --- a/src/components/filter/filter.jsx +++ b/src/components/filter/filter.jsx @@ -8,16 +8,19 @@ const styles = require('./filter.css'); const FilterComponent = props => { const { + active, onClick, onBlur, placeholderText } = props; return (
@@ -42,11 +45,13 @@ const FilterComponent = props => { }; FilterComponent.propTypes = { + active: PropTypes.bool, onClick: PropTypes.func.isRequired, onBlur: PropTypes.func.isRequired, placeholderText: PropTypes.string }; FilterComponent.defaultProps = { + active: false, placeholderText: 'what are you looking for?' }; module.exports = FilterComponent; diff --git a/src/containers/filter.jsx b/src/containers/filter.jsx index d48421bb4..93b85a59b 100644 --- a/src/containers/filter.jsx +++ b/src/containers/filter.jsx @@ -12,23 +12,19 @@ class Filter extends React.Component { 'handleBlur' ]); this.state = { - selected: false + active: false }; } - handleClick (e) { - this.state.selected = true; - console.log(this.state.selected); - // add class here, to show X icon + handleClick () { + this.setState({active: true}); } handleBlur () { - this.state.selected = false; - console.log(this.state.selected); - // remove class + this.setState({active: false}); } render () { return ( From d6064b4d0e95a6a7e9b8b113e66e82a4f74f8f2f Mon Sep 17 00:00:00 2001 From: steven dale Date: Mon, 15 May 2017 16:25:27 -0400 Subject: [PATCH 11/43] updating filter to typeahead (update as you type) --- src/components/filter/filter.jsx | 6 +++++- src/containers/filter.jsx | 13 +++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx index 9273c769d..c6d67727b 100644 --- a/src/components/filter/filter.jsx +++ b/src/components/filter/filter.jsx @@ -11,7 +11,9 @@ const FilterComponent = props => { active, onClick, onBlur, - placeholderText + onChange, + placeholderText, + filterQuery } = props; return (
{ className={styles.filterInput} type='text' placeholder={placeholderText} + value={filterQuery} + onChange={onChange} >
diff --git a/src/containers/filter.jsx b/src/containers/filter.jsx index 93b85a59b..d11e53f8b 100644 --- a/src/containers/filter.jsx +++ b/src/containers/filter.jsx @@ -9,12 +9,19 @@ class Filter extends React.Component { super(props); bindAll(this, [ 'handleClick', - 'handleBlur' + 'handleBlur', + 'updateSearch' ]); this.state = { - active: false + active: false, + filterQuery: '' }; } + updateSearch(event) { + this.setState({ + filterQuery: event.target.value + }) + } handleClick () { this.setState({active: true}); } @@ -25,8 +32,10 @@ class Filter extends React.Component { return ( ); } From 88c5635e8cd7bbe854a8c7864155c80870fbc67c Mon Sep 17 00:00:00 2001 From: steven dale Date: Mon, 15 May 2017 16:36:15 -0400 Subject: [PATCH 12/43] x icon now correctly shows when at least one character in search query --- src/components/filter/filter.jsx | 9 +-------- src/containers/filter.jsx | 16 ++++++---------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx index c6d67727b..13e8dce6d 100644 --- a/src/components/filter/filter.jsx +++ b/src/components/filter/filter.jsx @@ -9,8 +9,6 @@ const styles = require('./filter.css'); const FilterComponent = props => { const { active, - onClick, - onBlur, onChange, placeholderText, filterQuery @@ -19,12 +17,9 @@ const FilterComponent = props => {
{ FilterComponent.propTypes = { active: PropTypes.bool, - onClick: PropTypes.func.isRequired, - onBlur: PropTypes.func.isRequired, placeholderText: PropTypes.string }; FilterComponent.defaultProps = { diff --git a/src/containers/filter.jsx b/src/containers/filter.jsx index d11e53f8b..8173dba64 100644 --- a/src/containers/filter.jsx +++ b/src/containers/filter.jsx @@ -8,8 +8,6 @@ class Filter extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'handleClick', - 'handleBlur', 'updateSearch' ]); this.state = { @@ -21,20 +19,18 @@ class Filter extends React.Component { this.setState({ filterQuery: event.target.value }) - } - handleClick () { - this.setState({active: true}); - } - handleBlur () { - this.setState({active: false}); + if(event.target.value){ + this.setState({active: true}); + } + else { + this.setState({active: false}); + } } render () { return ( ); From 54577b4147464b54aff33c397af8b7631e3f5ee9 Mon Sep 17 00:00:00 2001 From: steven dale Date: Mon, 15 May 2017 16:51:17 -0400 Subject: [PATCH 13/43] using closebutton component --- src/components/filter/filter.jsx | 1 - src/components/library/library.jsx | 9 +++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx index 13e8dce6d..ab6ce85e4 100644 --- a/src/components/filter/filter.jsx +++ b/src/components/filter/filter.jsx @@ -18,7 +18,6 @@ const FilterComponent = props => { className={classNames({ [styles.filter]: true, [styles.isActive]: active - // styles.headerItemTitle })} >
- - +
+
From c13573d0ad3cf20f43d26e5bb964e1042f5b429d Mon Sep 17 00:00:00 2001 From: steven dale Date: Tue, 16 May 2017 10:34:23 -0400 Subject: [PATCH 14/43] moving stuff into modal --- src/components/library/library.css | 60 +----------------------------- src/components/library/library.jsx | 28 -------------- src/components/modal/modal.css | 57 ++++++++++++++++++++++++++++ src/components/modal/modal.jsx | 33 +++++++++++++--- 4 files changed, 86 insertions(+), 92 deletions(-) diff --git a/src/components/library/library.css b/src/components/library/library.css index b76393282..feb83192b 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -12,7 +12,7 @@ padding: 1rem 1rem 5rem 1rem; } -.modal-header { +/*.modal-header { width: 100%; margin-bottom: 2rem; padding: 0.5rem; @@ -20,60 +20,4 @@ font-size: 1.5rem; font-weight: normal; color: #8e8f95; -} - -/* - @todo: move to Modal component? -*/ -.full-modal-header { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: flex-start; - - box-sizing: border-box; - width: 100%; - background-color: $blue; - - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 1rem; - font-weight: normal; -} - -.modal-header-item { - display: flex; - align-items: center; - padding: 1rem; - text-decoration: none; - color: white; - user-select: none; -} - -/* - Modal header has 3 items: - |filter title x| - - Use the same width for both side item containers, - so that title remains centered -*/ -$sides: 20rem; - -.header-item-filter { - display: flex; - flex-basis: $sides; - justify-content: flex-end; -} - -.header-item-title { - flex-grow: 1; - flex-shrink: 0; - justify-content: center; - user-select: none; - letter-spacing: 0.4px; - cursor: default; -} - -.header-item-close { - flex-basis: $sides; - justify-content: flex-end; -} +}*/ diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index f28b10bf4..fc0118a9b 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -5,8 +5,6 @@ const React = require('react'); const LibraryItem = require('../library-item/library-item.jsx'); const ModalComponent = require('../modal/modal.jsx'); -const Filter = require('../../containers/filter.jsx'); -const CloseButton = require('../close-button/close-button.jsx'); const styles = require('./library.css'); @@ -36,32 +34,6 @@ class LibraryComponent extends React.Component { visible={this.props.visible} onRequestClose={this.props.onRequestClose} > -
-
- -
-
- {this.props.title} -
-
- -
-
-
{this.props.data.map((dataItem, itemId) => { const scratchURL = dataItem.md5 ? diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index 01bd3542a..d12d3eb1f 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -56,3 +56,60 @@ z-index: 0; background: $ui-pane-gray; } + + +/* + @todo: move to Modal component? +*/ +.full-modal-header { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: flex-start; + + box-sizing: border-box; + width: 100%; + background-color: $blue; + + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 1rem; + font-weight: normal; +} + +.modal-header-item { + display: flex; + align-items: center; + padding: 1rem; + text-decoration: none; + color: white; + user-select: none; +} + +/* + Modal header has 3 items: + |filter title x| + + Use the same width for both side item containers, + so that title remains centered +*/ +$sides: 20rem; + +.header-item-filter { + display: flex; + flex-basis: $sides; + justify-content: flex-end; +} + +.header-item-title { + flex-grow: 1; + flex-shrink: 0; + justify-content: center; + user-select: none; + letter-spacing: 0.4px; + cursor: default; +} + +.header-item-close { + flex-basis: $sides; + justify-content: flex-end; +} diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index e505ce8dc..2bda21d13 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -1,9 +1,11 @@ +const classNames = require('classnames'); const PropTypes = require('prop-types'); const React = require('react'); const ReactModal = require('react-modal'); const Box = require('../box/box.jsx'); const CloseButton = require('../close-button/close-button.jsx'); +const Filter = require('../../containers/filter.jsx'); const styles = require('./modal.css'); @@ -16,16 +18,36 @@ class ModalComponent extends React.Component { isOpen={this.props.visible} overlayClassName={styles.modalOverlay} ref={m => (this.modal = m)} - onRequestClose={this.props.onRequestClose} > - +
+
+ +
+
+ {this.props.contentLabel} +
+
+ +
+
{this.props.children}
@@ -36,7 +58,6 @@ class ModalComponent extends React.Component { ModalComponent.propTypes = { children: PropTypes.node, contentLabel: PropTypes.string.isRequired, - onRequestClose: PropTypes.func.isRequired, visible: PropTypes.bool.isRequired }; From 0b499bf24e6db8dfa92022557da9da8cada51118 Mon Sep 17 00:00:00 2001 From: steven dale Date: Tue, 16 May 2017 13:43:07 -0400 Subject: [PATCH 15/43] * Library component: moved header logic + css to Modal component. Updated sprite alignment * Close button component: large: added similar transition to add button * Filter component: added autofocs, made full width --- src/components/close-button/close-button.css | 44 +++++----- src/components/close-button/close-button.jsx | 8 +- src/components/filter/filter.css | 8 +- src/components/filter/filter.jsx | 1 + src/components/library/library.css | 13 +-- src/components/modal/modal.css | 87 ++++++++------------ src/components/modal/modal.jsx | 10 +-- 7 files changed, 78 insertions(+), 93 deletions(-) diff --git a/src/components/close-button/close-button.css b/src/components/close-button/close-button.css index ce9cc99ac..58ad7abb0 100644 --- a/src/components/close-button/close-button.css +++ b/src/components/close-button/close-button.css @@ -6,40 +6,46 @@ align-items: center; justify-content: center; - color: gray; - background-color: $blue; - + overflow: hidden; /* Mask the icon animation */ + background-color: rgba(0, 0, 0, 0.1); border-radius: 50%; - border-color: #dbdbdb; - border-style: solid; - + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - cursor: pointer; user-select: none; - transition: all 0.15s ease-out; /* @todo: standardize with var */ + cursor: pointer; + transition: all 0.15s ease-out; +} + +.close-button.large:hover { + transform: scale(1.1, 1.1); + box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15); } .small { width: 1rem; height: 1rem; - border-width: 1px; + color: #FFF; + background-color: $blue; } .large { - width: 2.75rem; - height: 2.75rem; - border-width: 2px; + width: 2rem; + height: 2rem; } -.close-button:hover { - transform: scale(1.1, 1.1); -} - -/* Same icon as Sprite Selector Add button, but rotated. - @todo: reuse? -*/ .close-icon { + position: relative; + margin: 0.25rem; + user-select: none; transform-origin: 50%; transform: rotate(45deg); +} + +.small .close-icon { width: 40%; } + +.large .close-icon { + width: 0.75rem; + height: 0.75rem; +} diff --git a/src/components/close-button/close-button.jsx b/src/components/close-button/close-button.jsx index f7c7fc141..c2d78c796 100644 --- a/src/components/close-button/close-button.jsx +++ b/src/components/close-button/close-button.jsx @@ -11,8 +11,8 @@ const CloseButton = props => ( styles.closeButton, props.className, { - [styles.large]: props.size === CloseButton.SIZE_LARGE, - [styles.small]: props.size === CloseButton.SIZE_SMALL + [styles.small]: props.size === CloseButton.SIZE_SMALL, + [styles.large]: props.size === CloseButton.SIZE_LARGE } )} onClick={props.onClick} @@ -24,13 +24,13 @@ const CloseButton = props => (
); -CloseButton.SIZE_LARGE = 'large'; CloseButton.SIZE_SMALL = 'small'; +CloseButton.SIZE_LARGE = 'large'; CloseButton.propTypes = { className: PropTypes.string, onClick: PropTypes.func.isRequired, - size: PropTypes.oneOf([CloseButton.SIZE_LARGE, CloseButton.SIZE_SMALL]) + size: PropTypes.oneOf([CloseButton.SIZE_SMALL, CloseButton.SIZE_LARGE]) }; CloseButton.defaultProps = { diff --git a/src/components/filter/filter.css b/src/components/filter/filter.css index 1855fcfa5..8d64baac2 100644 --- a/src/components/filter/filter.css +++ b/src/components/filter/filter.css @@ -5,12 +5,16 @@ display: flex; flex-direction: row; align-items: center; + /* + Should we use width 100% instead, for cases when component's + parent is not a flexbox container? + */ + flex-grow: 1; padding: 0.3rem 0.5rem; background: rgba(0, 0, 0, 0.10); border: 1px solid rgba(255, 255, 255, 0.10); border-radius: 10rem; - user-select: none; } @@ -37,7 +41,7 @@ overflow: hidden; /* Mask the icon animation */ height: 1.25rem; width: 1.25rem; - margin: 0 0.25rem 0 0.5rem; + margin: 0 0.25rem 0 0.5rem; /* @todo: move to parent to make component*/ border-radius: 50%; cursor: pointer; diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx index ab6ce85e4..0d5b743a1 100644 --- a/src/components/filter/filter.jsx +++ b/src/components/filter/filter.jsx @@ -25,6 +25,7 @@ const FilterComponent = props => { src={filterIcon} > (this.modal = m)} > -
-
+
+
@@ -37,12 +36,11 @@ class ModalComponent extends React.Component {
From a71b751c4cebde07a8c51f5f6aaed53befc78123 Mon Sep 17 00:00:00 2001 From: steven dale Date: Tue, 16 May 2017 14:22:39 -0400 Subject: [PATCH 16/43] lint fixes --- src/components/filter/filter.jsx | 11 ++++++----- src/components/library/library.jsx | 1 - src/components/modal/modal.jsx | 3 ++- src/containers/filter.jsx | 15 +++++++-------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx index 0d5b743a1..af501c68b 100644 --- a/src/components/filter/filter.jsx +++ b/src/components/filter/filter.jsx @@ -23,21 +23,20 @@ const FilterComponent = props => { + /> - + />
+ />
); @@ -45,6 +44,8 @@ const FilterComponent = props => { FilterComponent.propTypes = { active: PropTypes.bool, + filterQuery: PropTypes.string, + onChange: PropTypes.func, placeholderText: PropTypes.string }; FilterComponent.defaultProps = { diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index fc0118a9b..540eef09b 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -1,4 +1,3 @@ -const classNames = require('classnames'); const bindAll = require('lodash.bindall'); const PropTypes = require('prop-types'); const React = require('react'); diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index 86fde1ae3..b29af276b 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -24,7 +24,7 @@ class ModalComponent extends React.Component { >
- +
); } From e060a6397ea885cc53ea91be8d83d96ce9bf2636 Mon Sep 17 00:00:00 2001 From: steven dale Date: Tue, 16 May 2017 15:44:40 -0400 Subject: [PATCH 17/43] FilterComponent: x icon animations --- src/components/filter/filter.css | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/filter/filter.css b/src/components/filter/filter.css index 8d64baac2..c2d02063d 100644 --- a/src/components/filter/filter.css +++ b/src/components/filter/filter.css @@ -50,8 +50,7 @@ pointer-events: none; cursor: default; - /* Why doesn't this work? */ - /* transition: opacity 0.05s linear; */ + transition: opacity 0.05s linear; } /* @@ -70,11 +69,8 @@ position: relative; margin: 0.25rem; user-select: none; - /* transform: translateX(0.5rem); */ - /* transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1); */ - - /* transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1), - opacity 0.05s linear; */ + transform: translateX(0.5rem); + transition: transform 0.085s cubic-bezier(0.78, 1, 1, 1); } /* From 3b1cdedf756c92518a9c00187ba9b8c91239dad7 Mon Sep 17 00:00:00 2001 From: steven dale Date: Tue, 16 May 2017 15:59:15 -0400 Subject: [PATCH 18/43] CloseButton: large: updating size to sync up with h/w of filter in same row --- src/components/close-button/close-button.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/close-button/close-button.css b/src/components/close-button/close-button.css index 58ad7abb0..66b79d8a6 100644 --- a/src/components/close-button/close-button.css +++ b/src/components/close-button/close-button.css @@ -29,8 +29,8 @@ } .large { - width: 2rem; - height: 2rem; + width: 1.75rem; + height: 1.75rem; } .close-icon { From a0334a899e0668e5009687c62e426fe16d2f1afa Mon Sep 17 00:00:00 2001 From: steven dale Date: Wed, 17 May 2017 13:11:51 -0400 Subject: [PATCH 19/43] * Filter: Replaced hover circle with grow transform for X icon. Removed duplicate prop. * Library: Centered sprites --- src/components/filter/filter.css | 10 +++++----- src/components/library-item/library-item.css | 1 + src/components/library/library.css | 9 ++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/filter/filter.css b/src/components/filter/filter.css index c2d02063d..fa8e072e1 100644 --- a/src/components/filter/filter.css +++ b/src/components/filter/filter.css @@ -44,12 +44,8 @@ margin: 0 0.25rem 0 0.5rem; /* @todo: move to parent to make component*/ border-radius: 50%; - cursor: pointer; - background-color: rgba(255, 255, 255, 0.2); - pointer-events: none; cursor: default; - transition: opacity 0.05s linear; } @@ -60,6 +56,11 @@ pointer-events: auto; cursor: pointer; opacity: 1; + transition: opacity 0.05s linear; +} + +.filter.is-active .x-icon-wrapper:hover { + transform: scale(1.2, 1.2); } /* @@ -80,7 +81,6 @@ transform: translateX(0); } - .filter-input { flex-grow: 1; line-height: 1.25rem; diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css index 916c3a5c5..288b76054 100644 --- a/src/components/library-item/library-item.css +++ b/src/components/library-item/library-item.css @@ -6,6 +6,7 @@ align-items: center; justify-content: center; flex-basis: 160px; + max-width: 160px; height: 160px; margin: $space; padding: 1rem 1rem 0 1rem; diff --git a/src/components/library/library.css b/src/components/library/library.css index 5490d9e96..c885e490d 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -3,12 +3,11 @@ .library-scroll-grid { display: flex; + justify-content: center; + background: $ui-pane-gray; flex-grow: 1; flex-wrap: wrap; - justify-content: flex-start; - - height: 100%; overflow-y: scroll; - padding: 1rem 1rem 5rem 1rem; - background: $ui-pane-gray; + height: 100%; + padding: 0.5rem; } From ef8de74e0b22ac3145a59f9426e1e54ab49c57b5 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 16 May 2017 11:25:54 -0400 Subject: [PATCH 20/43] Remove selected state from libraries, adding on first click --- src/components/library-item/library-item.css | 10 ++-------- src/components/library-item/library-item.jsx | 9 ++------- src/components/library/library.jsx | 15 ++------------- 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css index e25c478d2..c702492f8 100644 --- a/src/components/library-item/library-item.css +++ b/src/components/library-item/library-item.css @@ -27,12 +27,6 @@ transform: scale(1.02, 1.02); } -.library-item.is-selected { - border-width: 2px; - border-color: #1dacf4; - transition: 0.25s ease-out; -} - .library-item-image-container { height: 100px; } @@ -47,12 +41,12 @@ margin: 0.25rem 0; text-align: center; - /* + /* For truncating overflowing text gracefully Min-width is for a bug: https://css-tricks.com/flexbox-truncated-text */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - min-width: 0; + min-width: 0; } diff --git a/src/components/library-item/library-item.jsx b/src/components/library-item/library-item.jsx index 31e722b0e..6abe41b97 100644 --- a/src/components/library-item/library-item.jsx +++ b/src/components/library-item/library-item.jsx @@ -1,4 +1,3 @@ -const classNames = require('classnames'); const bindAll = require('lodash.bindall'); const PropTypes = require('prop-types'); const React = require('react'); @@ -18,10 +17,7 @@ class LibraryItem extends React.Component { render () { return ( @@ -40,8 +36,7 @@ LibraryItem.propTypes = { iconURL: PropTypes.string.isRequired, id: PropTypes.number.isRequired, name: PropTypes.string.isRequired, - onSelect: PropTypes.func.isRequired, - selected: PropTypes.bool.isRequired + onSelect: PropTypes.func.isRequired }; module.exports = LibraryItem; diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 16327c10c..3be748703 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -11,19 +11,10 @@ class LibraryComponent extends React.Component { constructor (props) { super(props); bindAll(this, ['handleSelect']); - this.state = {selectedItem: null}; } handleSelect (id) { - if (this.state.selectedItem === id) { - // Double select: select as the library's value. - this.props.onRequestClose(); - this.props.onItemSelected(this.props.data[id]); - } else { - if (this.props.onItemChosen) { - this.props.onItemChosen(this.props.data[id]); - } - } - this.setState({selectedItem: id}); + this.props.onRequestClose(); + this.props.onItemSelected(this.props.data[id]); } render () { if (!this.props.visible) return null; @@ -45,7 +36,6 @@ class LibraryComponent extends React.Component { id={itemId} key={`item_${itemId}`} name={dataItem.name} - selected={this.state.selectedItem === itemId} onSelect={this.handleSelect} /> ); @@ -67,7 +57,6 @@ LibraryComponent.propTypes = { }) /* eslint-enable react/no-unused-prop-types, lines-around-comment */ ), - onItemChosen: PropTypes.func, onItemSelected: PropTypes.func, onRequestClose: PropTypes.func, title: PropTypes.string.isRequired, From 37425e4649dbc93610bb186fe246ea5851b32522 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 16 May 2017 11:54:04 -0400 Subject: [PATCH 21/43] Add hover hooks to libraries and hook up sound hover action --- src/components/library-item/library-item.jsx | 16 +++++++++++++++- src/components/library/library.jsx | 16 +++++++++++++++- src/containers/sound-library.jsx | 14 +++++++++++--- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/components/library-item/library-item.jsx b/src/components/library-item/library-item.jsx index 6abe41b97..8e113c9be 100644 --- a/src/components/library-item/library-item.jsx +++ b/src/components/library-item/library-item.jsx @@ -8,17 +8,29 @@ const styles = require('./library-item.css'); class LibraryItem extends React.Component { constructor (props) { super(props); - bindAll(this, ['handleClick']); + bindAll(this, [ + 'handleClick', + 'handleMouseEnter', + 'handleMouseLeave' + ]); } handleClick (e) { this.props.onSelect(this.props.id); e.preventDefault(); } + handleMouseEnter () { + this.props.onMouseEnter(this.props.id); + } + handleMouseLeave () { + this.props.onMouseLeave(this.props.id); + } render () { return ( ); @@ -57,6 +69,8 @@ LibraryComponent.propTypes = { }) /* eslint-enable react/no-unused-prop-types, lines-around-comment */ ), + onItemMouseEnter: PropTypes.func, + onItemMouseLeave: PropTypes.func, onItemSelected: PropTypes.func, onRequestClose: PropTypes.func, title: PropTypes.string.isRequired, diff --git a/src/containers/sound-library.jsx b/src/containers/sound-library.jsx index d478d141a..f267959a8 100644 --- a/src/containers/sound-library.jsx +++ b/src/containers/sound-library.jsx @@ -14,14 +14,18 @@ class SoundLibrary extends React.Component { super(props); bindAll(this, [ 'handleItemSelected', - 'handleItemChosen' + 'handleItemMouseEnter', + 'handleItemMouseLeave' ]); } componentDidMount () { this.audioEngine = new AudioEngine(); this.player = this.audioEngine.createPlayer(); } - handleItemChosen (soundItem) { + componentWillReceiveProps (newProps) { + if (this.player && !newProps.visible) this.player.stopAllSounds(); + } + handleItemMouseEnter (soundItem) { const md5ext = soundItem._md5; const idParts = md5ext.split('.'); const md5 = idParts[0]; @@ -39,6 +43,9 @@ class SoundLibrary extends React.Component { this.player.playSound(soundItem._md5); }); } + handleItemMouseLeave () { + this.player.stopAllSounds(); + } handleItemSelected (soundItem) { const vmSound = { format: soundItem.format, @@ -68,7 +75,8 @@ class SoundLibrary extends React.Component { data={soundLibraryThumbnailData} title="Sound Library" visible={this.props.visible} - onItemChosen={this.handleItemChosen} + onItemMouseEnter={this.handleItemMouseEnter} + onItemMouseLeave={this.handleItemMouseLeave} onItemSelected={this.handleItemSelected} onRequestClose={this.props.onRequestClose} /> From 0448ed0ac1d9215ce94cfd8bd16506ae39bf532c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 16 May 2017 11:55:05 -0400 Subject: [PATCH 22/43] Rename Libary => Library --- src/containers/backdrop-library.jsx | 4 ++-- src/containers/costume-library.jsx | 4 ++-- src/containers/sound-library.jsx | 4 ++-- src/containers/sprite-library.jsx | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/containers/backdrop-library.jsx b/src/containers/backdrop-library.jsx index b19921b4c..62e9cd473 100644 --- a/src/containers/backdrop-library.jsx +++ b/src/containers/backdrop-library.jsx @@ -4,7 +4,7 @@ const React = require('react'); const VM = require('scratch-vm'); const backdropLibraryContent = require('../lib/libraries/backdrops.json'); -const LibaryComponent = require('../components/library/library.jsx'); +const LibraryComponent = require('../components/library/library.jsx'); class BackdropLibrary extends React.Component { @@ -26,7 +26,7 @@ class BackdropLibrary extends React.Component { } render () { return ( - Date: Tue, 16 May 2017 12:02:44 -0400 Subject: [PATCH 23/43] Add comment to explain edge case --- src/containers/sound-library.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/containers/sound-library.jsx b/src/containers/sound-library.jsx index 53aaf04da..53aa6bde0 100644 --- a/src/containers/sound-library.jsx +++ b/src/containers/sound-library.jsx @@ -23,6 +23,7 @@ class SoundLibrary extends React.Component { this.player = this.audioEngine.createPlayer(); } componentWillReceiveProps (newProps) { + // Stop playing sounds if the library closes without a mouseleave (e.g. by using the escape key) if (this.player && !newProps.visible) this.player.stopAllSounds(); } handleItemMouseEnter (soundItem) { From db403dcad9dc3a71fe2729bb06eb5f0d67e2069b Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 26 Apr 2017 14:04:48 -0400 Subject: [PATCH 24/43] Update shadow block values with debounced targetsUpdate handler --- package.json | 1 + src/containers/blocks.jsx | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d221cc7c..bd02cf068 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "gh-pages": "^0.12.0", "html-webpack-plugin": "2.28.0", "lodash.bindall": "4.4.0", + "lodash.debounce": "4.0.8", "lodash.defaultsdeep": "4.6.0", "lodash.isequal": "4.5.0", "lodash.omit": "4.5.0", diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 487bed480..a2146d3f8 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -1,4 +1,5 @@ const bindAll = require('lodash.bindall'); +const debounce = require('lodash.debounce'); const defaultsDeep = require('lodash.defaultsdeep'); const PropTypes = require('prop-types'); const React = require('react'); @@ -30,6 +31,7 @@ class Blocks extends React.Component { 'onScriptGlowOff', 'onBlockGlowOn', 'onBlockGlowOff', + 'onTargetsUpdate', 'onVisualReport', 'onWorkspaceUpdate', 'onWorkspaceMetricsChange', @@ -40,6 +42,7 @@ class Blocks extends React.Component { workspaceMetrics: {}, prompt: null }; + this.onTargetsUpdate = debounce(this.onTargetsUpdate, 100); } componentDidMount () { const workspaceConfig = defaultsDeep({}, Blocks.defaultOptions, this.props.options); @@ -85,6 +88,7 @@ class Blocks extends React.Component { this.props.vm.addListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); this.props.vm.addListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.addListener('workspaceUpdate', this.onWorkspaceUpdate); + this.props.vm.addListener('targetsUpdate', this.onTargetsUpdate); } detachVM () { this.props.vm.removeListener('SCRIPT_GLOW_ON', this.onScriptGlowOn); @@ -93,8 +97,25 @@ class Blocks extends React.Component { this.props.vm.removeListener('BLOCK_GLOW_OFF', this.onBlockGlowOff); this.props.vm.removeListener('VISUAL_REPORT', this.onVisualReport); this.props.vm.removeListener('workspaceUpdate', this.onWorkspaceUpdate); + this.props.vm.removeListener('targetsUpdate', this.onTargetsUpdate); + } + updateToolboxBlockValue (id, value) { + this.workspace + .getFlyout() + .getWorkspace() + .getBlockById(id) + .inputList[0] + .fieldRow[0] + .setValue(value); + } + onTargetsUpdate () { + if (this.props.vm.editingTarget) { + ['glide', 'move', 'set'].forEach(prefix => { + this.updateToolboxBlockValue(`${prefix}x`, this.props.vm.editingTarget.x.toFixed(0)); + this.updateToolboxBlockValue(`${prefix}y`, this.props.vm.editingTarget.y.toFixed(0)); + }); + } } - onWorkspaceMetricsChange () { const target = this.props.vm.editingTarget; if (target && target.id) { From 188a92e636db4350f3253df1f9cff145b6b486aa Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 22 May 2017 14:39:01 -0400 Subject: [PATCH 25/43] Consume opcode and transform to category and label for monitors --- src/lib/monitor-adapter.js | 31 ++++++++++++++++++ src/lib/opcode-labels.js | 67 ++++++++++++++++++++++++++++++++++++++ src/reducers/monitors.js | 4 ++- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/lib/monitor-adapter.js create mode 100644 src/lib/opcode-labels.js diff --git a/src/lib/monitor-adapter.js b/src/lib/monitor-adapter.js new file mode 100644 index 000000000..eccf540be --- /dev/null +++ b/src/lib/monitor-adapter.js @@ -0,0 +1,31 @@ +/** + * Convert monitors from VM format to what the GUI needs to render. + * - Convert opcode to a label and a category + * - Add missing XY position data if needed + */ +const log = require('./log'); + +const OpcodeLabels = require('../lib/opcode-labels.js'); + +const PADDING = 5; +const MONITOR_HEIGHT = 23; + +const isUndefined = a => typeof a === 'undefined'; + +module.exports = function ({id, opcode, value, x, y}, monitorIndex) { + + // Look up category and label from opcode label file + let opcodeData = OpcodeLabels[opcode]; + if (isUndefined(opcodeData)) { + log.error(`No category/label found for opcode: ${opcode}`); + opcodeData = {category: 'data', label: opcode}; + } + const {label, category} = opcodeData; + + // Simple layout if x or y are undefined + // @todo scratch2 has a more complex layout behavior we may want to adopt + if (isUndefined(x)) x = PADDING; + if (isUndefined(y)) y = PADDING + (monitorIndex * (PADDING + MONITOR_HEIGHT)); + + return {id, label, category, value, x, y}; +}; diff --git a/src/lib/opcode-labels.js b/src/lib/opcode-labels.js new file mode 100644 index 000000000..402d6e56e --- /dev/null +++ b/src/lib/opcode-labels.js @@ -0,0 +1,67 @@ +module.exports = { + // Motion + motion_direction: { + category: 'motion', + label: 'direction' + }, + motion_xposition: { + category: 'motion', + label: 'x position' + }, + motion_yposition: { + category: 'motion', + label: 'y position' + }, + + // Looks + looks_size: { + category: 'looks', + label: 'size' + }, + looks_costumeorder: { + category: 'looks', + label: 'costume #' + }, + looks_backdroporder: { + category: 'looks', + label: 'backdrop #' + }, + looks_backdropname: { + category: 'looks', + label: 'backdrop name' + }, + + // Data + data_variable: { + category: 'data', + label: 'Variable _' // @todo placeholder for params + }, + + // Sound + sound_volume: { + category: 'sound', + label: 'volume' + }, + sound_tempo: { + category: 'sound', + label: 'tempo' + }, + + // Sensing + sensing_loudness: { + category: 'sensing', + label: 'loundness' + }, + sensing_of: { + category: 'sensing', + label: '_ of _' // @todo placeholder for params + }, + sensing_current: { + category: 'sensing', + label: 'current _' // @todo placeholder for param + }, + sensing_timer: { + category: 'timer', + label: 'timer' + } +}; diff --git a/src/reducers/monitors.js b/src/reducers/monitors.js index e8b4fa947..18e9529f4 100644 --- a/src/reducers/monitors.js +++ b/src/reducers/monitors.js @@ -1,3 +1,5 @@ +const monitorAdapter = require('../lib/monitor-adapter.js'); + const UPDATE_MONITORS = 'scratch-gui/monitors/UPDATE_MONITORS'; const initialState = []; @@ -6,7 +8,7 @@ const reducer = function (state, action) { if (typeof state === 'undefined') state = initialState; switch (action.type) { case UPDATE_MONITORS: - return [...action.monitors]; + return action.monitors.map(monitorAdapter); default: return state; } From d580598558b7b083c41113b491ef9540e7189e00 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Mon, 22 May 2017 15:18:17 -0400 Subject: [PATCH 26/43] Add dynamic labelling --- src/components/monitor/monitor.jsx | 2 +- src/lib/monitor-adapter.js | 13 ++++++++----- src/lib/opcode-labels.js | 10 +++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/monitor/monitor.jsx b/src/components/monitor/monitor.jsx index edd0fb043..54b1ebe8a 100644 --- a/src/components/monitor/monitor.jsx +++ b/src/components/monitor/monitor.jsx @@ -7,7 +7,7 @@ const styles = require('./monitor.css'); const categories = { data: '#FF8C1A', sensing: '#5CB1D6', - sounds: '#CF63CF', + sound: '#CF63CF', looks: '#9966FF', motion: '#4C97FF' }; diff --git a/src/lib/monitor-adapter.js b/src/lib/monitor-adapter.js index eccf540be..f458f67c6 100644 --- a/src/lib/monitor-adapter.js +++ b/src/lib/monitor-adapter.js @@ -12,15 +12,18 @@ const MONITOR_HEIGHT = 23; const isUndefined = a => typeof a === 'undefined'; -module.exports = function ({id, opcode, value, x, y}, monitorIndex) { - - // Look up category and label from opcode label file +module.exports = function ({id, opcode, params, value, x, y}, monitorIndex) { let opcodeData = OpcodeLabels[opcode]; if (isUndefined(opcodeData)) { - log.error(`No category/label found for opcode: ${opcode}`); + log.error(`No data found for opcode: ${opcode}`); opcodeData = {category: 'data', label: opcode}; } - const {label, category} = opcodeData; + + // Use labelFn with fields if provided for dynamic labelling (e.g. variables) + let {label, category, labelFn} = opcodeData; + if (!isUndefined(labelFn)) { + label = labelFn(params); + } // Simple layout if x or y are undefined // @todo scratch2 has a more complex layout behavior we may want to adopt diff --git a/src/lib/opcode-labels.js b/src/lib/opcode-labels.js index 402d6e56e..9dbdcb59f 100644 --- a/src/lib/opcode-labels.js +++ b/src/lib/opcode-labels.js @@ -34,7 +34,7 @@ module.exports = { // Data data_variable: { category: 'data', - label: 'Variable _' // @todo placeholder for params + labelFn: params => params.VARIABLE }, // Sound @@ -50,18 +50,18 @@ module.exports = { // Sensing sensing_loudness: { category: 'sensing', - label: 'loundness' + label: 'loudness' }, sensing_of: { category: 'sensing', - label: '_ of _' // @todo placeholder for params + labelFn: params => `${params.PROPERTY} of ${params.OBJECT}` }, sensing_current: { category: 'sensing', - label: 'current _' // @todo placeholder for param + labelFn: params => params.CURRENTMENU.toLowerCase() }, sensing_timer: { - category: 'timer', + category: 'sensing', label: 'timer' } }; From 9d41f09e1b6db773e163fd0488cd402ac0fed21d Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 23 May 2017 09:55:13 -0400 Subject: [PATCH 27/43] Use a function instead of object lookup to simplify defaults --- src/lib/monitor-adapter.js | 15 +++------------ src/lib/opcode-labels.js | 10 +++++++++- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/lib/monitor-adapter.js b/src/lib/monitor-adapter.js index f458f67c6..25d80c39a 100644 --- a/src/lib/monitor-adapter.js +++ b/src/lib/monitor-adapter.js @@ -3,8 +3,6 @@ * - Convert opcode to a label and a category * - Add missing XY position data if needed */ -const log = require('./log'); - const OpcodeLabels = require('../lib/opcode-labels.js'); const PADDING = 5; @@ -13,17 +11,10 @@ const MONITOR_HEIGHT = 23; const isUndefined = a => typeof a === 'undefined'; module.exports = function ({id, opcode, params, value, x, y}, monitorIndex) { - let opcodeData = OpcodeLabels[opcode]; - if (isUndefined(opcodeData)) { - log.error(`No data found for opcode: ${opcode}`); - opcodeData = {category: 'data', label: opcode}; - } + let {label, category, labelFn} = OpcodeLabels(opcode); - // Use labelFn with fields if provided for dynamic labelling (e.g. variables) - let {label, category, labelFn} = opcodeData; - if (!isUndefined(labelFn)) { - label = labelFn(params); - } + // Use labelFn if provided for dynamic labelling (e.g. variables) + if (!isUndefined(labelFn)) label = labelFn(params); // Simple layout if x or y are undefined // @todo scratch2 has a more complex layout behavior we may want to adopt diff --git a/src/lib/opcode-labels.js b/src/lib/opcode-labels.js index 9dbdcb59f..bebe14743 100644 --- a/src/lib/opcode-labels.js +++ b/src/lib/opcode-labels.js @@ -1,4 +1,4 @@ -module.exports = { +const opcodeMap = { // Motion motion_direction: { category: 'motion', @@ -65,3 +65,11 @@ module.exports = { label: 'timer' } }; + +module.exports = function (opcode) { + if (opcode in opcodeMap) return opcodeMap[opcode]; + return { + category: 'data', + label: opcode + }; +}; From eafe0921014decdeab2aab333f903f3e24f94a42 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 25 May 2017 11:43:43 -0400 Subject: [PATCH 28/43] Use vm#createVariable --- src/containers/blocks.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 487bed480..4309c1199 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -127,7 +127,6 @@ class Blocks extends React.Component { if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { this.onWorkspaceMetricsChange(); } - this.ScratchBlocks.Events.disable(); this.workspace.clear(); @@ -152,6 +151,7 @@ class Blocks extends React.Component { } handlePromptCallback (data) { this.state.prompt.callback(data); + this.props.vm.createVariable(data); this.handlePromptClose(); } handlePromptClose () { From 4d573a3a6cf525737e7d9a3d23b256c3bfeec2d2 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 25 May 2017 14:17:48 -0400 Subject: [PATCH 29/43] Merge filter container with library component and get rid of 'active' state of filter component --- src/components/filter/filter.jsx | 5 +--- src/components/library/library.jsx | 17 +++++++++++-- src/components/modal/modal.jsx | 9 +++++-- src/containers/filter.jsx | 39 ------------------------------ 4 files changed, 23 insertions(+), 47 deletions(-) delete mode 100644 src/containers/filter.jsx diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx index af501c68b..57186904a 100644 --- a/src/components/filter/filter.jsx +++ b/src/components/filter/filter.jsx @@ -8,7 +8,6 @@ const styles = require('./filter.css'); const FilterComponent = props => { const { - active, onChange, placeholderText, filterQuery @@ -17,7 +16,7 @@ const FilterComponent = props => {
0 })} > { }; FilterComponent.propTypes = { - active: PropTypes.bool, filterQuery: PropTypes.string, onChange: PropTypes.func, placeholderText: PropTypes.string }; FilterComponent.defaultProps = { - active: false, placeholderText: 'what are you looking for?' }; module.exports = FilterComponent; diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 540eef09b..331d94f2c 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -10,8 +10,14 @@ const styles = require('./library.css'); class LibraryComponent extends React.Component { constructor (props) { super(props); - bindAll(this, ['handleSelect']); - this.state = {selectedItem: null}; + bindAll(this, [ + 'handleSelect', + 'handleFilterChange' + ]); + this.state = { + selectedItem: null, + filterQuery: '' + }; } handleSelect (id) { if (this.state.selectedItem === id) { @@ -25,12 +31,19 @@ class LibraryComponent extends React.Component { } this.setState({selectedItem: id}); } + handleFilterChange (event) { + this.setState({ + filterQuery: event.target.value + }); + } render () { if (!this.props.visible) return null; return (
diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index b29af276b..ba3070bbf 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -5,7 +5,7 @@ const ReactModal = require('react-modal'); const Box = require('../box/box.jsx'); const CloseButton = require('../close-button/close-button.jsx'); -const Filter = require('../../containers/filter.jsx'); +const Filter = require('../filter/filter.jsx'); const styles = require('./modal.css'); @@ -24,7 +24,10 @@ class ModalComponent extends React.Component { >
- +
- ); - } -} - -module.exports = Filter; From 6935de3c258d07d9497b50a4e68f9785fccd5b96 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 25 May 2017 14:31:36 -0400 Subject: [PATCH 30/43] filter items! Ignore case except turkish --- src/components/library/library.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 331d94f2c..4d737d390 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -47,7 +47,9 @@ class LibraryComponent extends React.Component { onRequestClose={this.props.onRequestClose} >
- {this.props.data.map((dataItem, itemId) => { + {this.props.data.filter(dataItem => + dataItem.name.toLowerCase().indexOf(this.state.filterQuery.toLowerCase()) !== -1 + ).map((dataItem, itemId) => { const scratchURL = dataItem.md5 ? `https://cdn.assets.scratch.mit.edu/internalapi/asset/${dataItem.md5}/get/` : dataItem.rawURL; @@ -71,6 +73,7 @@ class LibraryComponent extends React.Component { LibraryComponent.propTypes = { data: PropTypes.arrayOf( /* eslint-disable react/no-unused-prop-types, lines-around-comment */ + // An item in the library PropTypes.shape({ // @todo remove md5/rawURL prop from library, refactor to use storage md5: PropTypes.string, From e16f794050e6225462a409d6ae1c27c14f5e839e Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 25 May 2017 14:47:30 -0400 Subject: [PATCH 31/43] fix css so that when the library doesn't fill the screen, the modal does not shrink --- src/components/modal/modal.css | 2 ++ src/components/modal/modal.jsx | 1 + 2 files changed, 3 insertions(+) diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index 138685c6d..ef1a5a74e 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -19,6 +19,8 @@ -webkit-overflow-scrolling: 'touch'; user-select: none; height: 100%; + width: 100%; + display: flex; } /* diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index ba3070bbf..750be7c11 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -21,6 +21,7 @@ class ModalComponent extends React.Component { >
From f54701c5f77174f729e5b9bb5422ad4943dfce38 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 25 May 2017 14:55:40 -0400 Subject: [PATCH 32/43] add the right sprite to the stage --- src/components/library/library.jsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 4d737d390..27cfe4116 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -23,10 +23,10 @@ class LibraryComponent extends React.Component { if (this.state.selectedItem === id) { // Double select: select as the library's value. this.props.onRequestClose(); - this.props.onItemSelected(this.props.data[id]); + this.props.onItemSelected(this.getFilteredData()[id]); } else { if (this.props.onItemChosen) { - this.props.onItemChosen(this.props.data[id]); + this.props.onItemChosen(this.getFilteredData()[id]); } } this.setState({selectedItem: id}); @@ -36,6 +36,10 @@ class LibraryComponent extends React.Component { filterQuery: event.target.value }); } + getFilteredData () { + return this.props.data.filter(dataItem => + dataItem.name.toLowerCase().indexOf(this.state.filterQuery.toLowerCase()) !== -1); + } render () { if (!this.props.visible) return null; return ( @@ -47,19 +51,17 @@ class LibraryComponent extends React.Component { onRequestClose={this.props.onRequestClose} >
- {this.props.data.filter(dataItem => - dataItem.name.toLowerCase().indexOf(this.state.filterQuery.toLowerCase()) !== -1 - ).map((dataItem, itemId) => { + {this.getFilteredData().map((dataItem, index) => { const scratchURL = dataItem.md5 ? `https://cdn.assets.scratch.mit.edu/internalapi/asset/${dataItem.md5}/get/` : dataItem.rawURL; return ( ); From 95ddb56afdf95890241f8239a1ac562512d75ba3 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 25 May 2017 14:58:19 -0400 Subject: [PATCH 33/43] fix a warning on grow box --- src/components/modal/modal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index 750be7c11..a6af18b81 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -21,7 +21,7 @@ class ModalComponent extends React.Component { >
From 616df9be2c8516aaa64c6e70feeb7d5ab767aa24 Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 25 May 2017 15:26:31 -0400 Subject: [PATCH 34/43] make there not be two scroll bars sometimes --- src/components/library/library.css | 5 +++-- src/components/modal/modal.css | 3 ++- src/css/units.css | 2 ++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/library/library.css b/src/components/library/library.css index c885e490d..73f615191 100644 --- a/src/components/library/library.css +++ b/src/components/library/library.css @@ -4,10 +4,11 @@ .library-scroll-grid { display: flex; justify-content: center; + align-content: flex-start; background: $ui-pane-gray; flex-grow: 1; flex-wrap: wrap; - overflow-y: scroll; - height: 100%; + overflow-y: auto; + height: calc(100% - $library-header-height); padding: 0.5rem; } diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index ef1a5a74e..f6b9b2363 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -15,7 +15,7 @@ .full-modal-content { position: absolute; outline: none; - overflow-y: scroll; + overflow-y: auto; -webkit-overflow-scrolling: 'touch'; user-select: none; height: 100%; @@ -37,6 +37,7 @@ $sides: 20rem; flex-direction: row; flex-wrap: nowrap; justify-content: flex-start; + height: $library-header-height; box-sizing: border-box; width: 100%; diff --git a/src/css/units.css b/src/css/units.css index 3a1a577b7..3f9aaba17 100644 --- a/src/css/units.css +++ b/src/css/units.css @@ -5,3 +5,5 @@ $sprites-per-row: 5; $menu-bar-height: 3rem; $sprite-info-height: 5.25rem; /* @todo: SpriteInfo isn't explicitly set to this height yet */ $stage-menu-height: 3rem; + +$library-header-height: 4.375rem; \ No newline at end of file From c7f05a807ce557b0339990052f8ffd3a65591bfb Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 25 May 2017 15:42:20 -0400 Subject: [PATCH 35/43] implement the close button on filter --- src/components/filter/filter.jsx | 7 ++++++- src/components/library/library.jsx | 9 ++++++--- src/components/modal/modal.jsx | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/components/filter/filter.jsx b/src/components/filter/filter.jsx index 57186904a..6e140eb80 100644 --- a/src/components/filter/filter.jsx +++ b/src/components/filter/filter.jsx @@ -9,6 +9,7 @@ const styles = require('./filter.css'); const FilterComponent = props => { const { onChange, + onClear, placeholderText, filterQuery } = props; @@ -31,7 +32,10 @@ const FilterComponent = props => { value={filterQuery} onChange={onChange} /> -
+
{ FilterComponent.propTypes = { filterQuery: PropTypes.string, onChange: PropTypes.func, + onClear: PropTypes.func, placeholderText: PropTypes.string }; FilterComponent.defaultProps = { diff --git a/src/components/library/library.jsx b/src/components/library/library.jsx index 9b03b0b8d..ad33743be 100644 --- a/src/components/library/library.jsx +++ b/src/components/library/library.jsx @@ -12,6 +12,7 @@ class LibraryComponent extends React.Component { super(props); bindAll(this, [ 'handleFilterChange', + 'handleFilterClear', 'handleMouseEnter', 'handleMouseLeave', 'handleSelect' @@ -32,9 +33,10 @@ class LibraryComponent extends React.Component { if (this.props.onItemMouseLeave) this.props.onItemMouseLeave(this.getFilteredData()[id]); } handleFilterChange (event) { - this.setState({ - filterQuery: event.target.value - }); + this.setState({filterQuery: event.target.value}); + } + handleFilterClear () { + this.setState({filterQuery: ''}); } getFilteredData () { return this.props.data.filter(dataItem => @@ -48,6 +50,7 @@ class LibraryComponent extends React.Component { filterQuery={this.state.filterQuery} visible={this.props.visible} onFilterChange={this.handleFilterChange} + onFilterClear={this.handleFilterClear} onRequestClose={this.props.onRequestClose} >
diff --git a/src/components/modal/modal.jsx b/src/components/modal/modal.jsx index a6af18b81..2a743bd74 100644 --- a/src/components/modal/modal.jsx +++ b/src/components/modal/modal.jsx @@ -28,6 +28,7 @@ class ModalComponent extends React.Component {
Date: Thu, 25 May 2017 15:49:14 -0400 Subject: [PATCH 36/43] paul does svg magic to fix the proportions of the x --- src/components/filter/icon--x.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/filter/icon--x.svg b/src/components/filter/icon--x.svg index 313a523cd..e78a2a8e2 100644 --- a/src/components/filter/icon--x.svg +++ b/src/components/filter/icon--x.svg @@ -1,3 +1,3 @@ - + From ec2fd08be949642bd03cdcb0050754659c2cb4fb Mon Sep 17 00:00:00 2001 From: DD Liu Date: Thu, 25 May 2017 16:40:13 -0400 Subject: [PATCH 37/43] remove unused code --- src/components/modal/modal.css | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/components/modal/modal.css b/src/components/modal/modal.css index f6b9b2363..a72a4f06d 100644 --- a/src/components/modal/modal.css +++ b/src/components/modal/modal.css @@ -77,27 +77,3 @@ $sides: 20rem; justify-content: flex-end; } - -/*.base-modal-content { - position: absolute; - outline: none; - overflow-y: scroll; - -webkit-overflow-scrolling: 'touch'; - border: 1px solid #ccc; - padding: 0; - top: 5%; - right: 5%; - bottom: 5%; - left: 5%; - border-radius: $space; - user-select: none; -}*/ - - -/* -.base-close-button { - position: absolute; - top: 1rem; - right: 1rem; - z-index: 2; -}*/ From 843182cdc03393d07b31a803e2e6e5d0d1c7547a Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Thu, 25 May 2017 16:45:03 -0400 Subject: [PATCH 38/43] Add comment about when auto layout doesn't work --- src/lib/monitor-adapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/monitor-adapter.js b/src/lib/monitor-adapter.js index 25d80c39a..8dcc802a4 100644 --- a/src/lib/monitor-adapter.js +++ b/src/lib/monitor-adapter.js @@ -18,6 +18,7 @@ module.exports = function ({id, opcode, params, value, x, y}, monitorIndex) { // Simple layout if x or y are undefined // @todo scratch2 has a more complex layout behavior we may want to adopt + // @todo e.g. this does not work well when monitors have already been moved if (isUndefined(x)) x = PADDING; if (isUndefined(y)) y = PADDING + (monitorIndex * (PADDING + MONITOR_HEIGHT)); From 6a87934f7094a951349defadad18ca0c966bd452 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Wed, 24 May 2017 09:27:40 -0400 Subject: [PATCH 39/43] Use hover to cycle costumes --- src/components/library-item/library-item.jsx | 3 ++ src/containers/sprite-library.jsx | 41 +++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/components/library-item/library-item.jsx b/src/components/library-item/library-item.jsx index 8e113c9be..52c8ca1a5 100644 --- a/src/components/library-item/library-item.jsx +++ b/src/components/library-item/library-item.jsx @@ -14,6 +14,9 @@ class LibraryItem extends React.Component { 'handleMouseLeave' ]); } + shouldComponentUpdate (nextProps) { + return this.props.iconURL !== nextProps.iconURL; + } handleClick (e) { this.props.onSelect(this.props.id); e.preventDefault(); diff --git a/src/containers/sprite-library.jsx b/src/containers/sprite-library.jsx index 0a1bcaa3e..6738bacd8 100644 --- a/src/containers/sprite-library.jsx +++ b/src/containers/sprite-library.jsx @@ -11,18 +11,55 @@ class SpriteLibrary extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'handleItemSelect' + 'handleItemSelect', + 'handleMouseEnter', + 'handleMouseLeave', + 'rotateCostume' ]); + this.state = { + activeSprite: null, + costumeIndex: 0, + sprites: spriteLibraryContent + }; + } + componentWillReceiveProps (newProps) { + if (!newProps.visible) clearInterval(this.intervalId); } handleItemSelect (item) { this.props.vm.addSprite2(JSON.stringify(item.json)); } + handleMouseEnter (item) { + this.setState({activeSprite: item}); + if (this.intervalId) clearInterval(this.intervalId); + this.intervalId = setInterval(this.rotateCostume, 300); + } + handleMouseLeave () { + this.intervalId = clearInterval(this.intervalId); + } + rotateCostume () { + const costumes = this.state.activeSprite.json.costumes; + const nextCostumeIndex = (this.state.costumeIndex + 1) % costumes.length; + this.setState({ + costumeIndex: nextCostumeIndex, + sprites: this.state.sprites.map(sprite => { + if (sprite.name === this.state.activeSprite.name) { + return { + ...sprite, + md5: sprite.json.costumes[nextCostumeIndex].baseLayerMD5 + }; + } + return sprite; + }) + }); + } render () { return ( From be12263b2a1cab43793d3a00c761cbbdb97132e1 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 26 May 2017 08:06:50 -0400 Subject: [PATCH 40/43] Use setState callback for rotating costumes --- src/containers/sprite-library.jsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/containers/sprite-library.jsx b/src/containers/sprite-library.jsx index 6738bacd8..fb9a6ce03 100644 --- a/src/containers/sprite-library.jsx +++ b/src/containers/sprite-library.jsx @@ -14,7 +14,9 @@ class SpriteLibrary extends React.Component { 'handleItemSelect', 'handleMouseEnter', 'handleMouseLeave', - 'rotateCostume' + 'rotateCostume', + 'startRotatingCostumes', + 'stopRotatingCostumes' ]); this.state = { activeSprite: null, @@ -29,11 +31,17 @@ class SpriteLibrary extends React.Component { this.props.vm.addSprite2(JSON.stringify(item.json)); } handleMouseEnter (item) { - this.setState({activeSprite: item}); - if (this.intervalId) clearInterval(this.intervalId); - this.intervalId = setInterval(this.rotateCostume, 300); + this.stopRotatingCostumes(); + this.setState({activeSprite: item}, this.startRotatingCostumes); } handleMouseLeave () { + this.stopRotatingCostumes(); + } + startRotatingCostumes () { + if (!this.state.activeSprite) return; + this.intervalId = setInterval(this.rotateCostume, 300); + } + stopRotatingCostumes () { this.intervalId = clearInterval(this.intervalId); } rotateCostume () { From 36a48effa9e333d055cf5c879e46154deb5c87a3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 26 May 2017 10:50:10 -0400 Subject: [PATCH 41/43] Fix repainting library with css magic --- src/components/library-item/library-item.css | 10 +++++++++- src/components/library-item/library-item.jsx | 13 ++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/components/library-item/library-item.css b/src/components/library-item/library-item.css index 3102d3d2d..12253a552 100644 --- a/src/components/library-item/library-item.css +++ b/src/components/library-item/library-item.css @@ -26,8 +26,16 @@ border-color: #1dacf4; } -.library-item-image-container { +.library-item-image-container-wrapper { height: 100px; + width: 100%; + position: relative; +} + +.library-item-image-container { + position: absolute; + height: 100px; + width: 100%; } .library-item-image { diff --git a/src/components/library-item/library-item.jsx b/src/components/library-item/library-item.jsx index 52c8ca1a5..cdb1415a1 100644 --- a/src/components/library-item/library-item.jsx +++ b/src/components/library-item/library-item.jsx @@ -35,11 +35,14 @@ class LibraryItem extends React.Component { onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} > - - + {/* Layers of wrapping is to prevent layout thrashing on animation */} + + + + {this.props.name} From 698e838c8ac255560d952f7cee9e6fce180ef13c Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 26 May 2017 10:53:00 -0400 Subject: [PATCH 42/43] Use PureComponent for performance boost on libraries --- src/containers/costume-library.jsx | 2 +- src/containers/sound-library.jsx | 2 +- src/containers/sprite-library.jsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/containers/costume-library.jsx b/src/containers/costume-library.jsx index d0a004ba6..f245c098f 100644 --- a/src/containers/costume-library.jsx +++ b/src/containers/costume-library.jsx @@ -7,7 +7,7 @@ const costumeLibraryContent = require('../lib/libraries/costumes.json'); const LibraryComponent = require('../components/library/library.jsx'); -class CostumeLibrary extends React.Component { +class CostumeLibrary extends React.PureComponent { constructor (props) { super(props); bindAll(this, [ diff --git a/src/containers/sound-library.jsx b/src/containers/sound-library.jsx index 53aa6bde0..d82512225 100644 --- a/src/containers/sound-library.jsx +++ b/src/containers/sound-library.jsx @@ -9,7 +9,7 @@ const soundIcon = require('../components/asset-panel/icon--sound.svg'); const soundLibraryContent = require('../lib/libraries/sounds.json'); -class SoundLibrary extends React.Component { +class SoundLibrary extends React.PureComponent { constructor (props) { super(props); bindAll(this, [ diff --git a/src/containers/sprite-library.jsx b/src/containers/sprite-library.jsx index fb9a6ce03..26a18c511 100644 --- a/src/containers/sprite-library.jsx +++ b/src/containers/sprite-library.jsx @@ -7,7 +7,7 @@ const spriteLibraryContent = require('../lib/libraries/sprites.json'); const LibraryComponent = require('../components/library/library.jsx'); -class SpriteLibrary extends React.Component { +class SpriteLibrary extends React.PureComponent { constructor (props) { super(props); bindAll(this, [ From cefdc9feaa86b560b4c614305f128b905a56d366 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 26 May 2017 10:53:22 -0400 Subject: [PATCH 43/43] Rotate costumes instantly instead of waiting --- src/containers/sprite-library.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/containers/sprite-library.jsx b/src/containers/sprite-library.jsx index 26a18c511..6395b421a 100644 --- a/src/containers/sprite-library.jsx +++ b/src/containers/sprite-library.jsx @@ -39,6 +39,7 @@ class SpriteLibrary extends React.PureComponent { } startRotatingCostumes () { if (!this.state.activeSprite) return; + this.rotateCostume(); this.intervalId = setInterval(this.rotateCostume, 300); } stopRotatingCostumes () {