UX: drag new user menus, scroll primary user nav (#18690)

This commit is contained in:
Kris 2022-10-24 19:22:02 -04:00 committed by GitHub
parent cde2719ea1
commit b6f5b236b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 97 deletions

View File

@ -12,10 +12,8 @@ export default class HorizontalOverflowNav extends Component {
scrollInterval;
@bind
scrollToActive() {
const activeElement = document.querySelector(
".user-navigation-secondary a.active"
);
scrollToActive(element) {
const activeElement = element.querySelector("a.active");
activeElement?.scrollIntoView({
block: "nearest",
@ -24,14 +22,14 @@ export default class HorizontalOverflowNav extends Component {
}
@bind
checkScroll(element) {
checkScroll(event) {
if (this.site.mobileView) {
return;
}
this.watchScroll(element);
this.watchScroll(event);
return (this.hasScroll =
element.target.scrollWidth > element.target.offsetWidth);
event.target.scrollWidth > event.target.offsetWidth);
}
@bind
@ -40,14 +38,14 @@ export default class HorizontalOverflowNav extends Component {
}
@bind
watchScroll(element) {
watchScroll(event) {
if (this.site.mobileView) {
return;
}
if (
element.target.offsetWidth + element.target.scrollLeft ===
element.target.scrollWidth
event.target.offsetWidth + event.target.scrollLeft ===
event.target.scrollWidth
) {
this.hideRightScroll = true;
clearInterval(this.scrollInterval);
@ -55,7 +53,7 @@ export default class HorizontalOverflowNav extends Component {
this.hideRightScroll = false;
}
if (element.target.scrollLeft === 0) {
if (event.target.scrollLeft === 0) {
this.hideLeftScroll = true;
clearInterval(this.scrollInterval);
} else {
@ -63,21 +61,57 @@ export default class HorizontalOverflowNav extends Component {
}
}
@action
horizScroll(element) {
let scrollSpeed = 100;
let siblingTarget = element.target.previousElementSibling;
@bind
scrollDrag(event) {
if (this.site.mobileView) {
return;
}
if (element.target.dataset.direction === "left") {
event.preventDefault();
const navPills = event.target.closest(".nav-pills");
const position = {
left: navPills.scrollLeft, // current scroll
x: event.clientX, // mouse position
};
const mouseDragScroll = function (e) {
let mouseChange = e.clientX - position.x;
navPills.scrollLeft = position.left - mouseChange;
navPills.querySelectorAll("a").forEach((a) => {
a.style.cursor = "grabbing";
});
};
const removeDragScroll = function () {
document.removeEventListener("mousemove", mouseDragScroll);
navPills.querySelectorAll("a").forEach((a) => {
a.style.cursor = "pointer";
});
};
document.addEventListener("mousemove", mouseDragScroll);
document.addEventListener("mouseup", removeDragScroll);
}
@action
horizScroll(event) {
let scrollSpeed = 175;
let siblingTarget = event.target.previousElementSibling;
if (event.target.dataset.direction === "left") {
scrollSpeed = scrollSpeed * -1;
siblingTarget = element.target.nextElementSibling;
siblingTarget = event.target.nextElementSibling;
}
this.scrollInterval = setInterval(function () {
siblingTarget.scrollLeft += scrollSpeed;
}, 50);
element.target.addEventListener("mouseup", this.stopScroll);
element.target.addEventListener("mouseleave", this.stopScroll);
event.target.addEventListener("mouseup", this.stopScroll);
event.target.addEventListener("mouseleave", this.stopScroll);
}
}

View File

@ -1,5 +1,5 @@
<section class="user-navigation user-navigation-primary">
<ul class="main-nav nav nav-pills user-nav">
<HorizontalOverflowNav @className="main-nav nav user-nav">
{{#unless @user.profile_hidden}}
<li class="summary">
<LinkTo @route="user.summary">
@ -74,5 +74,5 @@
</a>
</li>
{{/if}}
</ul>
</HorizontalOverflowNav>
</section>

View File

@ -1,4 +1,5 @@
{{!-- template-lint-disable no-down-event-binding --}}
{{!-- template-lint-disable no-invalid-interactive --}}
<div class="horizontal-overflow-nav {{if this.hasScroll "has-scroll"}}">
{{#if this.hasScroll}}
@ -6,7 +7,7 @@
role="button"
{{on "mousedown" this.horizScroll}}
data-direction="left"
class="nav-overflow__scroll-left btn-flat {{if this.hideLeftScroll "transparent"}}"
class="horizontal-overflow-nav__scroll-left {{if this.hideLeftScroll "disabled"}}"
>
{{d-icon "chevron-left"}}
</a>
@ -16,7 +17,8 @@
{{on-resize this.checkScroll}}
{{on "scroll" this.watchScroll}}
{{did-insert this.scrollToActive}}
class="nav-pills action-list"
{{on "mousedown" this.scrollDrag}}
class="nav-pills action-list {{@className}}"
>
{{yield}}
</ul>
@ -25,7 +27,7 @@
<a
role="button"
{{on "mousedown" this.horizScroll}}
class="nav-overflow__scroll-right btn-flat {{if this.hideRightScroll "transparent"}}"
class="horizontal-overflow-nav__scroll-right {{if this.hideRightScroll "disabled"}}"
>
{{d-icon "chevron-right"}}
</a>

View File

@ -2,9 +2,8 @@
margin-top: -15px; // temp, can remove margin from sibling element after nav finalized
.user-navigation {
--user-navigation__border-width: 4px;
&.user-navigation-primary {
border-bottom: 1px solid var(--primary-low);
}
border-bottom: 1px solid var(--primary-low);
.nav-pills {
width: 100%;
margin: 0;
@ -37,7 +36,6 @@
li {
flex: 1 1 auto;
margin: 0;
overflow: hidden;
display: flex;
a {
@ -80,6 +78,15 @@
}
}
.user-navigation-primary {
[class*="horizontal-overflow-nav__scroll"] {
font-size: var(--font-up-1);
.d-icon {
margin-top: 0.15em; // minor alignment
}
}
}
.user-navigation-secondary {
--user-navigation__border-width: 2px;
position: relative;
@ -89,12 +96,6 @@
gap: 0 0.5em;
border-bottom: 1px solid var(--primary-low);
.horizontal-overflow-nav {
position: relative;
min-width: 0;
width: 100%;
}
.select-kit .select-kit-header {
height: 100%;
padding: 0.5em 1em;
@ -116,73 +117,10 @@
}
}
.nav-overflow__scroll-right,
.nav-overflow__scroll-left {
--fade-width: 20px;
opacity: 1;
position: absolute;
z-index: 2;
background-color: var(--secondary);
top: 0;
bottom: 0;
display: flex;
align-items: center;
transition: opacity 0.25s;
.d-icon {
pointer-events: none;
margin-bottom: 0.2em;
color: var(--quaternary);
}
&.transparent {
// hiding with opacity so we can transition visibility
opacity: 0;
pointer-events: none;
}
}
.nav-overflow__scroll-right {
right: 0;
&:before {
content: "";
margin-left: -1.5em;
height: 100%;
width: 1.5em;
background: linear-gradient(
to left,
rgba(var(--secondary-rgb), 1),
rgba(var(--secondary-rgb), 0)
);
}
}
.nav-overflow__scroll-left {
left: 0;
&:after {
content: "";
margin-right: -1.5em;
height: 100%;
width: 1.5em;
background: linear-gradient(
to right,
rgba(var(--secondary-rgb), 1),
rgba(var(--secondary-rgb), 0)
);
}
}
.nav-pills {
flex: 1 1 auto;
font-size: var(--font-down-1);
justify-content: flex-start;
overflow: auto;
position: relative;
scroll-behavior: smooth;
// hides scrollbars, but allows mouse scrolling
scrollbar-width: none;
&::-webkit-scrollbar {
height: 0;
}
li {
flex: 1 0 auto;

View File

@ -15,6 +15,7 @@
@import "group-member-dropdown";
@import "groups-form-membership-fields";
@import "hashtag";
@import "horizontal-overflow-nav";
@import "iframed-html";
@import "ignored-user-list";
@import "keyboard_shortcuts";

View File

@ -0,0 +1,78 @@
.horizontal-overflow-nav {
position: relative;
min-width: 0;
width: 100%;
.nav-pills {
overflow: auto;
min-width: 0;
position: relative;
// avoids auto-scroll on initial load if active nav item is overflowed
scroll-behavior: auto;
// hides scrollbars, but allows mouse scrolling
scrollbar-width: none;
&::-webkit-scrollbar {
height: 0;
}
}
&.has-scroll {
.nav-pills {
scroll-behavior: smooth; // smooth scrolling on user interaction
}
}
}
.horizontal-overflow-nav__scroll-right,
.horizontal-overflow-nav__scroll-left {
--fade-width: 1.5em;
opacity: 1;
position: absolute;
z-index: 2;
background-color: var(--secondary);
top: 0;
bottom: 0;
display: flex;
align-items: center;
transition: opacity 0.25s;
.d-icon {
pointer-events: none;
margin-bottom: 0.2em;
color: var(--quaternary);
}
&.disabled {
// hiding with opacity so we can transition visibility
opacity: 0;
pointer-events: none;
}
}
.horizontal-overflow-nav__scroll-right {
right: 0;
&:before {
content: "";
margin-left: calc(var(--fade-width) * -1);
height: 100%;
width: var(--fade-width);
background: linear-gradient(
to left,
rgba(var(--secondary-rgb), 1),
rgba(var(--secondary-rgb), 0)
);
}
}
.horizontal-overflow-nav__scroll-left {
left: 0;
&:after {
content: "";
margin-right: calc(var(--fade-width) * -1);
height: 100%;
width: var(--fade-width);
background: linear-gradient(
to right,
rgba(var(--secondary-rgb), 1),
rgba(var(--secondary-rgb), 0)
);
}
}