From a842f237991f50e016e452f7aa9e299fbe4b41b8 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Thu, 27 Jul 2017 01:55:00 +0100 Subject: [PATCH 1/7] discover page horizontal scroll initial progress --- ui/js/page/discover/view.jsx | 138 ++++++++++++++++++++++++++++------- ui/scss/component/_card.scss | 52 +++++++++++-- 2 files changed, 157 insertions(+), 33 deletions(-) diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx index fc57a5ba8..7464e7aee 100644 --- a/ui/js/page/discover/view.jsx +++ b/ui/js/page/discover/view.jsx @@ -1,38 +1,122 @@ import React from "react"; +import ReactDOM from "react-dom"; import lbryio from "lbryio.js"; import lbryuri from "lbryuri"; import FileCard from "component/fileCard"; -import { BusyMessage } from "component/common.js"; +import { Icon, BusyMessage } from "component/common.js"; import ToolTip from "component/tooltip.js"; -const FeaturedCategory = props => { - const { category, names } = props; +class FeaturedCategory extends React.PureComponent { + componentWillMount() { + this.setState({ + numItems: this.props.names.length, + canScrollPrevious: false, + canScrollNext: true, + }); + } - return ( -
-

- {category} - {category && - category.match(/^community/i) && - 0) { + cardRow.scrollLeft = 0; + this.setState({ canScrollPrevious: false, canScrollNext: true }); + } + } + + handleScrollNext() { + const cardRow = ReactDOM.findDOMNode(this.refs.rowitems); + + // check the visible cards + const cards = cardRow.getElementsByTagName("section"); + var lastVisibleCard = null; + var lastVisibleIdx = -1; + for (var i = 0; i < cards.length; i++) { + if (this.isCardVisible(cards[i], cardRow)) { + lastVisibleCard = cards[i]; + lastVisibleIdx = i; + } + } + + if (lastVisibleCard) { + cardRow.scrollLeft = Math.min( + lastVisibleCard.offsetLeft + 16, + cardRow.scrollWidth - cardRow.clientWidth + ); + this.setState({ canScrollPrevious: true }); + } + if (lastVisibleIdx === cards.length - 1) { + this.setState({ canScrollNext: false }); + } + } + + isCardVisible(section, cardRow) { + var cardRowLeft = cardRow.scrollLeft; + var cardRowEnd = cardRow.offsetWidth; + var sectionLeft = section.offsetLeft; + var sectionEnd = sectionLeft + section.offsetWidth; + + return sectionEnd <= cardRowEnd && sectionLeft >= cardRowLeft; + } + + componentWillUnmount() { + const prevIcon = ReactDOM.findDOMNode(this.refs.scrollPrevious); + const nextIcon = ReactDOM.findDOMNode(this.refs.scrollNext); + prevIcon.removeEventListener("click", this.scrollPreviousHandler); + nextIcon.removeEventListener("click", this.scrollNextHandler); + } + + render() { + const { category, names } = this.props; + const leftNavClassName = + "card-row__nav left-nav" + + (this.state.canScrollPrevious ? " can-scroll" : ""); + const rightNavClassName = + "card-row__nav right-nav" + + (this.state.canScrollNext ? " can-scroll" : ""); + + return ( +
+
+ +
+
+ +
+

+ {category} + {category && + category.match(/^community/i) && + } +

+
+ {names && + names.map(name => + )} - className="tooltip--header" - />} -

- {names && - names.map(name => - - )} -
- ); -}; + + + ); + } +} class DiscoverPage extends React.PureComponent { componentWillMount() { diff --git a/ui/scss/component/_card.scss b/ui/scss/component/_card.scss index d8416d226..20fcb8ed8 100644 --- a/ui/scss/component/_card.scss +++ b/ui/scss/component/_card.scss @@ -92,7 +92,7 @@ $card-link-scaling: 1.1; } .card--link:hover { position: relative; - z-index: 1; + z-index: 2; box-shadow: $box-shadow-focus; transform: scale($card-link-scaling); transform-origin: 50% 50%; @@ -183,26 +183,66 @@ $height-card-small: $spacing-vertical * 15; } .card-row { + + .card-row { + margin-top: $spacing-vertical * 1/3; + } +} +.card-row__items { > .card { vertical-align: top; display: inline-block; margin-right: $spacing-vertical / 3; } - + .card-row { - margin-top: $spacing-vertical * 1/3; + > .card:last-child { + margin-right: 0 } } .card-row--small { - overflow-x: auto; - overflow-y: hidden; + overflow: hidden; white-space: nowrap; + position: relative; /*hacky way to give space for hover */ padding-left: 20px; - margin-left: -20px; + margin-left: -20px +} +.card-row__items { + width: 100%; + overflow-x: hidden; + /*hacky way to give space for hover */ + padding-top: 20px; + margin-top: -20px; padding-right: 20px; margin-right: -20px; } .card-row__header { margin-bottom: $spacing-vertical / 3; +} + +.card-row__nav { + display: none; + position: absolute; + padding: 0 $spacing-vertical / 1.5; + top: ($font-size * 1.4 * $font-line-height) + ($spacing-vertical / 3); + z-index: 1; + height: ($width-card-small * 9 / 16) + ($font-size * 0.82 * $font-line-height * 4) + ($spacing-vertical * 4/3) +} +.card-row__nav .icon { + background: $color-bg; + color: $color-help; + box-shadow: $box-shadow-layer; + padding: $spacing-vertical $spacing-vertical / 2; + position: absolute; + cursor: pointer; + left: 0; + top: 36% +} +.card-row__nav.left-nav { + left: 20px; /** using 20px left padding **/ +} +.card-row__nav.right-nav { + right: 0; +} +.card-row__nav.can-scroll { + display: block } \ No newline at end of file -- 2.45.3 From 57e68b009a3263dd257ed240a923dbb20fa8bfc6 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Thu, 27 Jul 2017 05:56:13 +0100 Subject: [PATCH 2/7] added translate3d transform for card hover and sibling cards --- ui/js/page/discover/view.jsx | 67 ++++++++++++++++++++++++++++++------ ui/scss/component/_card.scss | 17 +++++---- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx index 7464e7aee..d9652c312 100644 --- a/ui/js/page/discover/view.jsx +++ b/ui/js/page/discover/view.jsx @@ -27,8 +27,25 @@ class FeaturedCategory extends React.PureComponent { handleScrollPrevious() { const cardRow = ReactDOM.findDOMNode(this.refs.rowitems); if (cardRow.scrollLeft > 0) { - cardRow.scrollLeft = 0; - this.setState({ canScrollPrevious: false, canScrollNext: true }); + // check the visible cards + const cards = cardRow.getElementsByTagName("section"); + var firstVisibleCard = null; + var firstVisibleIdx = -1; + for (var i = 0; i < cards.length; i++) { + if (this.isCardVisible(cards[i], cardRow, false)) { + firstVisibleCard = cards[i]; + firstVisibleIdx = i; + break; + } + } + + const numDisplayed = this.numDisplayedCards(cardRow); + const scrollToIdx = firstVisibleIdx - numDisplayed; + cardRow.scrollLeft = scrollToIdx < 0 ? 0 : cards[scrollToIdx].offsetLeft; + this.setState({ + canScrollPrevious: cardRow.scrollLeft !== 0, + canScrollNext: true, + }); } } @@ -40,31 +57,61 @@ class FeaturedCategory extends React.PureComponent { var lastVisibleCard = null; var lastVisibleIdx = -1; for (var i = 0; i < cards.length; i++) { - if (this.isCardVisible(cards[i], cardRow)) { + if (this.isCardVisible(cards[i], cardRow, true)) { lastVisibleCard = cards[i]; lastVisibleIdx = i; } } if (lastVisibleCard) { + const numDisplayed = this.numDisplayedCards(cardRow); cardRow.scrollLeft = Math.min( - lastVisibleCard.offsetLeft + 16, + lastVisibleCard.offsetLeft, cardRow.scrollWidth - cardRow.clientWidth ); + + // update last visible index after scroll + lastVisibleIdx += numDisplayed; + if (lastVisibleIdx > cards.length - 1) { + lastVisibleIdx = cards.length - 1; + } + this.setState({ canScrollPrevious: true }); } + if (lastVisibleIdx === cards.length - 1) { this.setState({ canScrollNext: false }); } } - isCardVisible(section, cardRow) { - var cardRowLeft = cardRow.scrollLeft; - var cardRowEnd = cardRow.offsetWidth; - var sectionLeft = section.offsetLeft; - var sectionEnd = sectionLeft + section.offsetWidth; + isCardVisible(section, cardRow, partialVisibility) { + // check if a card is fully or partialy visible in its parent + const cardRowWidth = cardRow.offsetWidth; + const cardRowLeft = cardRow.scrollLeft; + const cardRowEnd = cardRowLeft + cardRow.offsetWidth; + const sectionLeft = section.offsetLeft - cardRowLeft; + const sectionEnd = sectionLeft + section.offsetWidth; - return sectionEnd <= cardRowEnd && sectionLeft >= cardRowLeft; + return ( + (sectionLeft >= 0 && sectionEnd <= cardRowWidth) || + (((sectionLeft < 0 && sectionEnd > 0) || + (sectionLeft > 0 && sectionLeft <= cardRowWidth)) && + partialVisibility) + ); + } + + numDisplayedCards(cardRow) { + const cards = cardRow.getElementsByTagName("section"); + const cardRowWidth = cardRow.offsetWidth; + // get the width of the first card and then calculate + const cardWidth = cards.length > 0 ? cards[0].offsetWidth : 0; + + if (cardWidth > 0) { + return Math.ceil(cardRowWidth / cardWidth); + } + + // return a default value of 1 card displayed if the card width couldn't be determined + return 1; } componentWillUnmount() { diff --git a/ui/scss/component/_card.scss b/ui/scss/component/_card.scss index 20fcb8ed8..74b38d968 100644 --- a/ui/scss/component/_card.scss +++ b/ui/scss/component/_card.scss @@ -1,6 +1,7 @@ @import "../global"; $padding-card-horizontal: $spacing-vertical * 2/3; +$translate-card-hover: 10px; .card { margin-left: auto; @@ -94,10 +95,13 @@ $card-link-scaling: 1.1; position: relative; z-index: 2; box-shadow: $box-shadow-focus; - transform: scale($card-link-scaling); + transform: scale($card-link-scaling) translate3d($translate-card-hover, 0, 0); transform-origin: 50% 50%; overflow-x: visible; - overflow-y: visible; + overflow-y: visible +} +.card--link:hover ~ .card--link { + transform: translate3d($translate-card-hover * 2, 0, 0); } .card__media { @@ -201,14 +205,13 @@ $height-card-small: $spacing-vertical * 15; overflow: hidden; white-space: nowrap; position: relative; - /*hacky way to give space for hover */ - padding-left: 20px; - margin-left: -20px + padding-right: 20px; + margin-right: -20px; } .card-row__items { width: 100%; - overflow-x: hidden; + overflow: hidden; /*hacky way to give space for hover */ padding-top: 20px; margin-top: -20px; @@ -238,7 +241,7 @@ $height-card-small: $spacing-vertical * 15; top: 36% } .card-row__nav.left-nav { - left: 20px; /** using 20px left padding **/ + left: 0; } .card-row__nav.right-nav { right: 0; -- 2.45.3 From be4875e5fc5ec372bc7342ed634485fedb7d413a Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Thu, 27 Jul 2017 06:39:46 +0100 Subject: [PATCH 3/7] implemented simple horizontal scroll animation --- ui/js/page/discover/view.jsx | 81 ++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx index d9652c312..16e41fdb6 100644 --- a/ui/js/page/discover/view.jsx +++ b/ui/js/page/discover/view.jsx @@ -41,11 +41,18 @@ class FeaturedCategory extends React.PureComponent { const numDisplayed = this.numDisplayedCards(cardRow); const scrollToIdx = firstVisibleIdx - numDisplayed; - cardRow.scrollLeft = scrollToIdx < 0 ? 0 : cards[scrollToIdx].offsetLeft; - this.setState({ - canScrollPrevious: cardRow.scrollLeft !== 0, - canScrollNext: true, - }); + const animationCallback = () => { + this.setState({ + canScrollPrevious: cardRow.scrollLeft !== 0, + canScrollNext: true, + }); + }; + this.scrollCardItemsLeftAnimated( + cardRow, + scrollToIdx < 0 ? 0 : cards[scrollToIdx].offsetLeft, + 100, + animationCallback + ); } } @@ -65,23 +72,63 @@ class FeaturedCategory extends React.PureComponent { if (lastVisibleCard) { const numDisplayed = this.numDisplayedCards(cardRow); - cardRow.scrollLeft = Math.min( - lastVisibleCard.offsetLeft, - cardRow.scrollWidth - cardRow.clientWidth + const animationCallback = () => { + // update last visible index after scroll + for (var i = 0; i < cards.length; i++) { + if (this.isCardVisible(cards[i], cardRow, true)) { + lastVisibleIdx = i; + } + } + + if (lastVisibleIdx > cards.length - 1) { + lastVisibleIdx = cards.length - 1; + } + + this.setState({ canScrollPrevious: true }); + if (lastVisibleIdx === cards.length - 1) { + this.setState({ canScrollNext: false }); + } + }; + + this.scrollCardItemsLeftAnimated( + cardRow, + Math.min( + lastVisibleCard.offsetLeft, + cardRow.scrollWidth - cardRow.clientWidth + ), + 100, + animationCallback ); + } + } - // update last visible index after scroll - lastVisibleIdx += numDisplayed; - if (lastVisibleIdx > cards.length - 1) { - lastVisibleIdx = cards.length - 1; + scrollCardItemsLeftAnimated(cardRow, target, duration, callback) { + if (!duration || duration <= diff) { + cardRow.scrollLeft = target; + if (callback) { + callback(); } - - this.setState({ canScrollPrevious: true }); + return; } - if (lastVisibleIdx === cards.length - 1) { - this.setState({ canScrollNext: false }); - } + const component = this; + var diff = target - cardRow.scrollLeft; + var tick = diff / duration * 10; + setTimeout(() => { + cardRow.scrollLeft = cardRow.scrollLeft + tick; + if (cardRow.scrollLeft === target) { + if (callback) { + callback(); + } + return; + } + component.scrollCardItemsLeftAnimated( + cardRow, + target, + duration - 10, + callback + ); + }, 10); } isCardVisible(section, cardRow, partialVisibility) { -- 2.45.3 From 03f5c8004ecc10a01d212d93c811a0cdfd6d792f Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Thu, 27 Jul 2017 06:46:46 +0100 Subject: [PATCH 4/7] updated header z-index --- ui/scss/component/_header.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/scss/component/_header.scss b/ui/scss/component/_header.scss index 1343ddf86..5cc51541e 100644 --- a/ui/scss/component/_header.scss +++ b/ui/scss/component/_header.scss @@ -13,7 +13,7 @@ $color-header-active: darken($color-header, 20%); top: 0; left: 0; width: 100%; - z-index: 2; + z-index: 3; padding: $spacing-vertical / 2; box-sizing: border-box; } -- 2.45.3 From 2b24176335a14e7005a8d4d9be89fc0cdfd831da Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Thu, 27 Jul 2017 10:28:28 +0100 Subject: [PATCH 5/7] switched z-index of cards on hover with horizontal left/right nav buttons --- ui/scss/component/_card.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/scss/component/_card.scss b/ui/scss/component/_card.scss index 74b38d968..3501c2748 100644 --- a/ui/scss/component/_card.scss +++ b/ui/scss/component/_card.scss @@ -93,7 +93,7 @@ $card-link-scaling: 1.1; } .card--link:hover { position: relative; - z-index: 2; + z-index: 1; box-shadow: $box-shadow-focus; transform: scale($card-link-scaling) translate3d($translate-card-hover, 0, 0); transform-origin: 50% 50%; @@ -227,7 +227,6 @@ $height-card-small: $spacing-vertical * 15; position: absolute; padding: 0 $spacing-vertical / 1.5; top: ($font-size * 1.4 * $font-line-height) + ($spacing-vertical / 3); - z-index: 1; height: ($width-card-small * 9 / 16) + ($font-size * 0.82 * $font-line-height * 4) + ($spacing-vertical * 4/3) } .card-row__nav .icon { @@ -238,7 +237,8 @@ $height-card-small: $spacing-vertical * 15; position: absolute; cursor: pointer; left: 0; - top: 36% + top: 36%; + z-index: 2; } .card-row__nav.left-nav { left: 0; -- 2.45.3 From 3c93d6553add2a686bbe47c489d2bffdcac6347e Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Thu, 27 Jul 2017 10:40:43 +0100 Subject: [PATCH 6/7] removed unnecessary code --- ui/js/page/discover/view.jsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx index 16e41fdb6..3a4e2f6c7 100644 --- a/ui/js/page/discover/view.jsx +++ b/ui/js/page/discover/view.jsx @@ -80,10 +80,6 @@ class FeaturedCategory extends React.PureComponent { } } - if (lastVisibleIdx > cards.length - 1) { - lastVisibleIdx = cards.length - 1; - } - this.setState({ canScrollPrevious: true }); if (lastVisibleIdx === cards.length - 1) { this.setState({ canScrollNext: false }); -- 2.45.3 From a5ced91030f5a2e8ae9bf0fcba569702aca88092 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Fri, 28 Jul 2017 03:42:49 +0100 Subject: [PATCH 7/7] added transition and states for horizontal scroll arrows, and some css tweaks and fixes --- ui/js/page/discover/view.jsx | 42 ++++++++++++++++-------------------- ui/scss/component/_card.scss | 20 +++++++++++------ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx index 3a4e2f6c7..2817fb946 100644 --- a/ui/js/page/discover/view.jsx +++ b/ui/js/page/discover/view.jsx @@ -15,22 +15,13 @@ class FeaturedCategory extends React.PureComponent { }); } - componentDidMount() { - const prevIcon = ReactDOM.findDOMNode(this.refs.scrollPrevious); - const nextIcon = ReactDOM.findDOMNode(this.refs.scrollNext); - this.scrollPreviousHandler = this.handleScrollPrevious.bind(this); - this.scrollNextHandler = this.handleScrollNext.bind(this); - prevIcon.addEventListener("click", this.scrollPreviousHandler); - nextIcon.addEventListener("click", this.scrollNextHandler); - } - handleScrollPrevious() { const cardRow = ReactDOM.findDOMNode(this.refs.rowitems); if (cardRow.scrollLeft > 0) { // check the visible cards const cards = cardRow.getElementsByTagName("section"); - var firstVisibleCard = null; - var firstVisibleIdx = -1; + let firstVisibleCard = null; + let firstVisibleIdx = -1; for (var i = 0; i < cards.length; i++) { if (this.isCardVisible(cards[i], cardRow, false)) { firstVisibleCard = cards[i]; @@ -61,8 +52,8 @@ class FeaturedCategory extends React.PureComponent { // check the visible cards const cards = cardRow.getElementsByTagName("section"); - var lastVisibleCard = null; - var lastVisibleIdx = -1; + let lastVisibleCard = null; + let lastVisibleIdx = -1; for (var i = 0; i < cards.length; i++) { if (this.isCardVisible(cards[i], cardRow, true)) { lastVisibleCard = cards[i]; @@ -108,8 +99,8 @@ class FeaturedCategory extends React.PureComponent { } const component = this; - var diff = target - cardRow.scrollLeft; - var tick = diff / duration * 10; + const diff = target - cardRow.scrollLeft; + const tick = diff / duration * 10; setTimeout(() => { cardRow.scrollLeft = cardRow.scrollLeft + tick; if (cardRow.scrollLeft === target) { @@ -157,13 +148,6 @@ class FeaturedCategory extends React.PureComponent { return 1; } - componentWillUnmount() { - const prevIcon = ReactDOM.findDOMNode(this.refs.scrollPrevious); - const nextIcon = ReactDOM.findDOMNode(this.refs.scrollNext); - prevIcon.removeEventListener("click", this.scrollPreviousHandler); - nextIcon.removeEventListener("click", this.scrollNextHandler); - } - render() { const { category, names } = this.props; const leftNavClassName = @@ -176,10 +160,20 @@ class FeaturedCategory extends React.PureComponent { return (
- + + +
- + + +

{category} diff --git a/ui/scss/component/_card.scss b/ui/scss/component/_card.scss index 3501c2748..a81470bfb 100644 --- a/ui/scss/component/_card.scss +++ b/ui/scss/component/_card.scss @@ -205,18 +205,19 @@ $height-card-small: $spacing-vertical * 15; overflow: hidden; white-space: nowrap; position: relative; + /*hacky way to give space for hover */ - padding-right: 20px; - margin-right: -20px; + padding-right: 30px; } .card-row__items { width: 100%; overflow: hidden; + /*hacky way to give space for hover */ padding-top: 20px; margin-top: -20px; - padding-right: 20px; - margin-right: -20px; + padding-right: 30px; + margin-right: -30px; } .card-row__header { margin-bottom: $spacing-vertical / 3; @@ -229,7 +230,7 @@ $height-card-small: $spacing-vertical * 15; top: ($font-size * 1.4 * $font-line-height) + ($spacing-vertical / 3); height: ($width-card-small * 9 / 16) + ($font-size * 0.82 * $font-line-height * 4) + ($spacing-vertical * 4/3) } -.card-row__nav .icon { +.card-row__nav .card-row__scroll-button { background: $color-bg; color: $color-help; box-shadow: $box-shadow-layer; @@ -239,6 +240,13 @@ $height-card-small: $spacing-vertical * 15; left: 0; top: 36%; z-index: 2; + opacity: 0.5; + transition: transform 60ms ease-in-out; + + &:hover { + opacity: 1.0; + transform: scale($card-link-scaling * 1.1) + } } .card-row__nav.left-nav { left: 0; @@ -248,4 +256,4 @@ $height-card-small: $spacing-vertical * 15; } .card-row__nav.can-scroll { display: block -} \ No newline at end of file +} -- 2.45.3