diff --git a/frontend/css/entity-pages.less b/frontend/css/entity-pages.less index 215badc0cb..6a76c00901 100644 --- a/frontend/css/entity-pages.less +++ b/frontend/css/entity-pages.less @@ -117,19 +117,21 @@ min-height: 150px; min-width: 0; .artist-page& { - max-height: 520px; + max-height: 500px; overflow-y: hidden; border-bottom-style: inset; position: relative; - &::after { - content: ""; - background-image: @white-gradient; - position: absolute; - bottom: 0; - height: 8em; - width: 100%; + &.expanded { + max-height: initial; + padding-bottom: 4em; } } + + .read-more { + bottom: 0; + padding: 1em 0; + } + .album-page& .listen-card .track-position { flex: 0 3em; align-self: center; diff --git a/frontend/css/release-card.less b/frontend/css/release-card.less index d04b32945f..0efd6a39af 100644 --- a/frontend/css/release-card.less +++ b/frontend/css/release-card.less @@ -116,7 +116,7 @@ } .release-coverart.hide-image { - display: none; + display: none !important; height: 0; } diff --git a/frontend/css/scroll-container.less b/frontend/css/scroll-container.less index 5bc2337e91..f69031d091 100644 --- a/frontend/css/scroll-container.less +++ b/frontend/css/scroll-container.less @@ -1,10 +1,16 @@ .horizontal-scroll-container { position: relative; + .horizontal-scroll-container.dragging .horizontal-scroll * { + // Prevent text selection and interactions when the user is actively dragging to scroll + pointer-events: none; + user-select: none; + } + .horizontal-scroll { + overflow-x: auto; .small-scrollbar(); &.no-scrollbar { - overflow-x: scroll; /* Hide the scrollbar: */ -ms-overflow-style: none; /* Internet Explorer and Edge */ scrollbar-width: none; /* Firefox */ @@ -23,17 +29,17 @@ border: none; font-size: 2em; color: @light-grey; - z-index: 1; + z-index: 2; opacity: 1; transition: opacity 300ms linear; &.backward { background: linear-gradient(to right, @white 10%, transparent); text-align: left; - left: 0; + left: -1px; } &.forward { background: linear-gradient(to left, @white 10%, transparent); - right: 0; + right: -1px; text-align: right; } } @@ -42,6 +48,10 @@ opacity: 0; pointer-events: none; } + // Hide arrow buttons when the container does not overflow (class added with javascript) + &.no-scroll .nav-button { + display: none; + } } .dragscroll { diff --git a/frontend/js/src/artist/ArtistPage.tsx b/frontend/js/src/artist/ArtistPage.tsx index b2f68f8701..d6bc22e76c 100644 --- a/frontend/js/src/artist/ArtistPage.tsx +++ b/frontend/js/src/artist/ArtistPage.tsx @@ -40,6 +40,7 @@ import { useBrainzPlayerDispatch } from "../common/brainzplayer/BrainzPlayerCont import SimilarArtistComponent from "../explore/music-neighborhood/components/SimilarArtist"; import CBReviewModal from "../cb-review/CBReviewModal"; import Pill from "../components/Pill"; +import HorizontalScrollContainer from "../components/HorizontalScrollContainer"; function SortingButtons({ sort, @@ -121,6 +122,10 @@ export default function ArtistPage(): JSX.Element { "release_date" ); + const [expandPopularTracks, setExpandPopularTracks] = React.useState( + false + ); + const rgGroups = groupBy( releaseGroups, (rg) => @@ -374,7 +379,7 @@ export default function ArtistPage(): JSX.Element { />
-
+

Popular tracks @@ -419,11 +424,15 @@ export default function ArtistPage(): JSX.Element { /> ); })} - {/*
- -
*/} +

@@ -474,18 +483,18 @@ export default function ArtistPage(): JSX.Element { )}
{Object.entries(groupedReleaseGroups).map(([type, rgGroup]) => ( -
+

{type}

-
{rgGroup.map(getReleaseCard)} -
+
))}
diff --git a/frontend/js/src/components/HorizontalScrollContainer.tsx b/frontend/js/src/components/HorizontalScrollContainer.tsx index daa2541a86..d42e430497 100644 --- a/frontend/js/src/components/HorizontalScrollContainer.tsx +++ b/frontend/js/src/components/HorizontalScrollContainer.tsx @@ -13,6 +13,9 @@ type HorizontalScrollContainerProps = { className?: string; }; +// How many pixels do the arrow buttons scroll? +const MANUAL_SCROLL_AMOUNT = 500; + export default function HorizontalScrollContainer({ showScrollbar = true, enableDragScroll = true, @@ -24,68 +27,100 @@ export default function HorizontalScrollContainer({ const { events } = useDraggable(scrollContainerRef, { applyRubberBandEffect: true, }); + const { onMouseDown: draggableOnMouseDown } = events; - const onMouseDown: React.MouseEventHandler = (event) => { - // Call the dragScrolluse-draggable-safe hook event - events.onMouseDown(event); - // Set our own class to allow for snap-scroll - (event.target as HTMLDivElement)?.parentElement?.classList.add("dragging"); - }; - const onMouseUp: React.MouseEventHandler = (event) => { - (event.target as HTMLDivElement)?.parentElement?.classList.remove( - "dragging" - ); - }; + const onMouseDown: React.MouseEventHandler = React.useCallback( + (event) => { + // Call the use-draggable-scroll-safe hook event + draggableOnMouseDown(event); + // Set our own class to allow for snap-scroll + (event.target as HTMLElement)?.parentElement?.classList.add("dragging"); + }, + [draggableOnMouseDown] + ); + + const onMouseUp: React.MouseEventHandler = React.useCallback( + (event) => { + (event.target as HTMLElement)?.parentElement?.classList.remove( + "dragging" + ); + }, + [] + ); - const onScroll: React.ReactEventHandler = (event) => { - const element = event.target as HTMLDivElement; - const parent = element.parentElement; + const onScroll = React.useCallback(() => { + const element = scrollContainerRef?.current; + const parent = element?.parentElement; if (!element || !parent) { return; } - // calculate horizontal scroll percentage - const scrollPercentage = - (100 * element.scrollLeft) / (element.scrollWidth - element.clientWidth); + // Don't expect so big a scroll before showing nav arrows on smaller screen sizes + const requiredMinimumScrollAmount = Math.min( + MANUAL_SCROLL_AMOUNT / 2, + element.clientWidth / 2 + ); - if (scrollPercentage > 95) { - parent.classList.add("scroll-end"); - parent.classList.remove("scroll-start"); - } else if (scrollPercentage < 5) { - parent.classList.add("scroll-start"); - parent.classList.remove("scroll-end"); - } else { - parent.classList.remove("scroll-end"); - parent.classList.remove("scroll-start"); + // Set up appropriate CSS classes to show or hide nav buttons + if (element.scrollWidth <= element.clientWidth) { + parent.classList.add("no-scroll"); } - }; + parent.classList.remove("scroll-end"); + parent.classList.remove("scroll-start"); - const manualScroll: React.ReactEventHandler = (event) => { - if (!scrollContainerRef?.current) { - return; - } - if (event?.currentTarget.classList.contains("forward")) { - scrollContainerRef.current.scrollBy({ - left: 300, - top: 0, - behavior: "smooth", - }); - } else { - scrollContainerRef.current.scrollBy({ - left: -300, - top: 0, - behavior: "smooth", - }); + if (element.scrollLeft < requiredMinimumScrollAmount) { + // We are at the beginning of the container and haven't scrolled more than requiredMinimumScrollAmount + parent.classList.add("scroll-start"); + } else if ( + // We have scrolled to the end of the container, i.e. there is less than requiredMinimumScrollAmount before the end of the scroll + // (with a 2px adjustement) + element.scrollWidth - element.scrollLeft - element.clientWidth <= + requiredMinimumScrollAmount - 2 + ) { + parent.classList.add("scroll-end"); } - }; + }, []); + + const throttledOnScroll = React.useMemo( + () => throttle(onScroll, 400, { leading: true }), + [onScroll] + ); + + const onManualScroll: React.ReactEventHandler = React.useCallback( + (event) => { + if (!scrollContainerRef?.current) { + return; + } + if (event?.currentTarget.classList.contains("forward")) { + scrollContainerRef.current.scrollBy({ + left: MANUAL_SCROLL_AMOUNT, + top: 0, + behavior: "smooth", + }); + } else { + scrollContainerRef.current.scrollBy({ + left: -MANUAL_SCROLL_AMOUNT, + top: 0, + behavior: "smooth", + }); + } + // Also call the onScroll (throttled) event to ensure + // the expected CSS classes are applied to the container + throttledOnScroll(); + }, + [throttledOnScroll] + ); - const throttledOnScroll = throttle(onScroll, 400, { leading: true }); + React.useEffect(() => { + // Run once on startup to set up expected CSS classes applied to the container + onScroll(); + }, []); return ( -
+