Improve navigation sub-menu and tabs effects (#2971)

* Improve nav border and colors on active and non-active

* Remove margin-top effect on active nav

* Use opacity / bold instead of color change on nav

* Remove dropdown items label in sub-menu and add a class active

* Position sub-menu to fixed

* Autoclose dropdown sub-menu only on outside click

* Remove open dropdown on hover in sub-menu

* Show reusable h1 for dropdown item in sub-menu

* Put reusable sub-menu h1 styles to mixins

* Add icons to sub-menu dropdown-item h1

* Make all the sub-menu accessible with focus

Co-authored-by: kimsible <kimsible@users.noreply.github.com>
pull/2988/head
Kim 2020-07-23 15:09:15 +02:00 committed by GitHub
parent 345b4a22a8
commit ed5bb51726
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 170 additions and 98 deletions

View File

@ -1,3 +1,8 @@
@import '_variables';
@import '_mixins';
my-top-menu-dropdown {
flex-grow: 1;
}
@include sub-menu-h1;

View File

@ -1,3 +1,8 @@
<h1>
<my-global-icon iconName="follower" aria-hidden="true"></my-global-icon>
<ng-container i18n>Instances following you</ng-container>
</h1>
<p-table
[value]="followers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)"

View File

@ -1,3 +1,8 @@
<h1>
<my-global-icon iconName="following" aria-hidden="true"></my-global-icon>
<ng-container i18n>Instances you follow</ng-container>
</h1>
<p-table
[value]="following" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)"

View File

@ -1,3 +1,8 @@
<h1>
<my-global-icon iconName="videos" aria-hidden="true"></my-global-icon>
<ng-container i18n>Videos redundancies</ng-container>
</h1>
<div class="admin-sub-header">
<div class="select-filter-block">
<label for="displayType" i18n>Display</label>

View File

@ -1,3 +1,8 @@
<h1>
<my-global-icon iconName="flag" aria-hidden="true"></my-global-icon>
<ng-container i18n>Reports</ng-container>
</h1>
<p-table
[value]="abuses" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id" [resizableColumns]="true" [lazyLoadOnInit]="false"

View File

@ -1,3 +1,8 @@
<h1>
<my-global-icon iconName="cross" aria-hidden="true"></my-global-icon>
<ng-container i18n>Video blocks</ng-container>
</h1>
<p-table
[value]="blocklist" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"

View File

@ -1,6 +1,4 @@
<div class="admin-sub-header">
<h1 i18n class="form-sub-title">Plugins/Themes</h1>
<div class="admin-sub-nav">
<a i18n routerLink="list-installed" routerLinkActive="active">Installed</a>

View File

@ -1,11 +1,6 @@
@import '_variables';
@import '_mixins';
.form-sub-title {
flex-grow: 0;
margin-right: 30px;
}
@media screen and (max-width: $small-view) {
::ng-deep .plugins .plugin .first-row {
flex-wrap: wrap;

View File

@ -1,6 +1,4 @@
<div class="admin-sub-header">
<h1 i18n class="form-sub-title">System</h1>
<div class="admin-sub-nav">
<a *ngIf="hasJobsRight()" i18n routerLink="jobs" routerLinkActive="active">Jobs</a>

View File

@ -1,4 +1,8 @@
<h1 class="sr-only" i18n>My channels</h1>
<h1>
<my-global-icon iconName="channel" aria-hidden="true"></my-global-icon>
<ng-container i18n>My channels</ng-container>
</h1>
<div class="video-channels-header">
<a class="create-button" routerLink="create">
<my-global-icon iconName="add" aria-hidden="true"></my-global-icon>

View File

@ -1,4 +1,8 @@
<h1 class="sr-only" i18n>History</h1>
<h1>
<my-global-icon iconName="history" aria-hidden="true"></my-global-icon>
<ng-container i18n>My history</ng-container>
</h1>
<div class="top-buttons">
<div class="history-switch">
<p-inputSwitch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></p-inputSwitch>

View File

@ -1,4 +1,8 @@
<h1 class="sr-only" i18n>Ownership changes</h1>
<h1>
<my-global-icon iconName="download" aria-hidden="true"></my-global-icon>
<ng-container i18n>My ownership changes</ng-container>
</h1>
<p-table
[value]="videoChangeOwnerships"
[lazy]="true"

View File

@ -1,4 +1,8 @@
<h1 class="sr-only" i18n>Subscriptions</h1>
<h1>
<my-global-icon iconName="subscriptions" aria-hidden="true"></my-global-icon>
<ng-container i18n>My subscriptions</ng-container>
</h1>
<div class="no-results" i18n *ngIf="pagination.totalItems === 0">You don't have any subscriptions yet.</div>
<div class="video-channels" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">

View File

@ -1,4 +1,8 @@
<h1 class="sr-only" i18n>Imports</h1>
<h1>
<my-global-icon iconName="cloud-download" aria-hidden="true"></my-global-icon>
<ng-container i18n>My imports</ng-container>
</h1>
<p-table
[value]="videoImports" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" dataKey="id"

View File

@ -1,6 +1,10 @@
<div class="video-playlists-header">
<h1 i18n>Playlists <span class="badge badge-secondary">{{ pagination.totalItems }}</span></h1>
<h1>
<my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon>
<ng-container i18n>My playlists</ng-container> <span class="badge badge-secondary">{{ pagination.totalItems }}</span>
</h1>
<div class="video-playlists-header">
<input type="text" placeholder="Search your playlists" i18n-placeholder [(ngModel)]="videoPlaylistsSearch" (ngModelChange)="onVideoPlaylistSearchChanged()" />
<a class="create-button" routerLink="create">

View File

@ -41,10 +41,6 @@
input[type=text] {
@include peertube-input-text(300px);
}
h1 {
font-size: 1.5rem;
}
}
@media screen and (max-width: $small-view) {

View File

@ -1,9 +1,10 @@
<h1>
<my-global-icon iconName="videos" aria-hidden="true"></my-global-icon>
<ng-container i18n>My videos</ng-container><span class="badge badge-secondary"> {{ pagination.totalItems }}</span>
</h1>
<div class="videos-header">
<h1 i18n>Videos <span class="badge badge-secondary">{{ pagination.totalItems }}</span></h1>
<input type="text" placeholder="Search your videos" i18n-placeholder [(ngModel)]="videosSearch" (ngModelChange)="onVideosSearchChanged()" />
<div class="fake-element"></div>
</div>
<my-videos-selection

View File

@ -6,12 +6,6 @@
justify-content: space-between;
margin: 20px 0 50px;
h1,
.fake-element {
flex: 1;
font-size: 1.5rem;
}
input[type=text] {
@include peertube-input-text(300px);
}

View File

@ -1,3 +1,6 @@
@import '_variables';
@import '_mixins';
.row {
flex-direction: column;
width: 100%;
@ -5,4 +8,6 @@
& > my-top-menu-dropdown:nth-child(1) {
flex-grow: 1;
}
@include sub-menu-h1;
}

View File

@ -30,16 +30,25 @@ $nav-link-height: 40px;
padding: 0 30px !important;
font-size: 15px;
border: $border-width $border-type transparent;
span {
border-bottom: 2px solid transparent;
}
&.active {
border: $border-width $border-type $border-color;
border-bottom: none;
border-color: $border-color;
border-bottom-color: transparent;
background-color: pvar(--submenuColor) !important;
span {
border-bottom: 2px solid pvar(--mainColor);
font-weight: $font-bold;
border-bottom-color: pvar(--mainColor);
}
}
&:hover:not(.active) {
border-color: transparent;
}
}
}
@ -71,7 +80,7 @@ $nav-link-height: 40px;
/* Hide active tab style to not have a moving tab effect */
a.nav-link.active {
border: none;
border-color: transparent;
background-color: pvar(--mainBackgroundColor) !important;
}
}

View File

@ -4,26 +4,28 @@
<a *ngIf="menuEntry.routerLink" [routerLink]="menuEntry.routerLink" routerLinkActive="active" class="title-page title-page-settings">{{ menuEntry.label }}</a>
<div *ngIf="!menuEntry.routerLink" ngbDropdown class="parent-entry"
#dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)">
#dropdown="ngbDropdown" autoClose="outside">
<span
*ngIf="isInSmallView"
tabindex=0
[ngClass]="{ active: !!suffixLabels[menuEntry.label] }"
(click)="openModal(id)" role="button" class="title-page title-page-settings">
(click)="openModal(id)" (keydown.enter)="openModal(id)"
role="button" class="title-page title-page-settings">
<ng-container i18n>{{ menuEntry.label }}</ng-container>
<ng-container *ngIf="!!suffixLabels[menuEntry.label]"> - {{ suffixLabels[menuEntry.label] }}</ng-container>
</span>
<span
*ngIf="!isInSmallView"
(mouseenter)="openDropdownOnHover(dropdown)" [ngClass]="{ active: !!suffixLabels[menuEntry.label] }" ngbDropdownAnchor
(click)="dropdownAnchorClicked(dropdown)" role="button" class="title-page title-page-settings"
tabindex=0
[ngClass]="{ active: !!suffixLabels[menuEntry.label] }" ngbDropdownAnchor
(click)="dropdownAnchorClicked(dropdown)" (keydown.enter)="dropdownAnchorClicked(dropdown)"
role="button" class="title-page title-page-settings"
>
<ng-container i18n>{{ menuEntry.label }}</ng-container>
<ng-container *ngIf="!!suffixLabels[menuEntry.label]"> - {{ suffixLabels[menuEntry.label] }}</ng-container>
</span>
<div ngbDropdownMenu>
<a *ngFor="let menuChild of menuEntry.children" class="dropdown-item" [ngClass]="{ icon: hasIcons }" [routerLink]="menuChild.routerLink">
<a *ngFor="let menuChild of menuEntry.children" class="dropdown-item" [ngClass]="{ icon: hasIcons, active: suffixLabels[menuEntry.label] === menuChild.label }" [routerLink]="menuChild.routerLink">
<my-global-icon *ngIf="menuChild.iconName" [iconName]="menuChild.iconName" aria-hidden="true"></my-global-icon>
{{ menuChild.label }}

View File

@ -1,6 +1,12 @@
@import '_variables';
@import '_mixins';
:host {
display: block;
height: $sub-menu-height;
margin-bottom: $sub-menu-margin-bottom;
}
.parent-entry {
span[role=button] {
cursor: pointer;

View File

@ -33,7 +33,6 @@ export class TopMenuDropdownComponent implements OnInit, OnDestroy {
isModalOpened = false
currentMenuEntryIndex: number
private openedOnHover = false
private routeSub: Subscription
constructor (
@ -68,32 +67,10 @@ export class TopMenuDropdownComponent implements OnInit, OnDestroy {
if (this.routeSub) this.routeSub.unsubscribe()
}
openDropdownOnHover (dropdown: NgbDropdown) {
this.openedOnHover = true
dropdown.open()
// Menu was closed
dropdown.openChange
.pipe(take(1))
.subscribe(() => this.openedOnHover = false)
}
dropdownAnchorClicked (dropdown: NgbDropdown) {
if (this.openedOnHover) {
this.openedOnHover = false
return
}
return dropdown.toggle()
}
closeDropdownIfHovered (dropdown: NgbDropdown) {
if (this.openedOnHover === false) return
dropdown.close()
this.openedOnHover = false
}
openModal (index: number) {
this.currentMenuEntryIndex = index
this.isModalOpened = true

View File

@ -1,3 +1,8 @@
<h1>
<my-global-icon iconName="user" aria-hidden="true"></my-global-icon>
<ng-container i18n>Muted accounts</ng-container>
</h1>
<p-table
[value]="blockedAccounts" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)"

View File

@ -1,3 +1,8 @@
<h1>
<my-global-icon iconName="server" aria-hidden="true"></my-global-icon>
<ng-container i18n>Muted servers</ng-container>
</h1>
<p-table
[value]="blockedServers" [lazy]="true" [paginator]="totalRecords > 0" [totalRecords]="totalRecords" [rows]="rowsPerPage" [rowsPerPageOptions]="rowsPerPageOptions"
[sortField]="sort.field" [sortOrder]="sort.order" (onLazyLoad)="loadLazy($event)" (onPage)="onPage($event)"

View File

@ -139,12 +139,13 @@ label {
.sub-menu {
background-color: pvar(--submenuColor);
width: 100%;
height: 81px;
margin-bottom: $sub-menu-margin-bottom;
height: $sub-menu-height;
display: flex;
align-items: center;
padding-left: $not-expanded-horizontal-margins;
padding-right: $not-expanded-horizontal-margins;
position: fixed;
z-index: 1;
}
// Override some properties if the main content is expanded (no menu on the left)
@ -175,6 +176,7 @@ label {
}
.title-page {
opacity: 0.6;
color: pvar(--mainForegroundColor);
font-size: 16px;
display: inline-block;
@ -182,14 +184,15 @@ label {
font-weight: $font-semibold;
@include disable-default-a-behaviour;
&.active, &.title-page-single {
border-bottom: 2px solid transparent;
&.title-page-single {
margin-top: 30px;
margin-bottom: 25px;
}
&.active {
font-weight: $font-bold;
border-bottom: 2px solid pvar(--mainColor);
border-bottom-color: pvar(--mainColor);
}
&.title-page-single {
@ -200,6 +203,11 @@ label {
color: pvar(--mainForegroundColor);
}
&.active, &:hover, &:active, &:focus, &.title-page-single {
opacity: 1;
outline: 0px hidden !important;
}
@media screen and (max-width: $mobile-view) {
margin-right: 15px;
}
@ -209,11 +217,6 @@ label {
.title-page-settings {
white-space: nowrap;
font-size: 115%;
font-weight: $font-regular;
&.active {
font-weight: $font-semibold;
}
}
.admin-sub-header {
@ -232,11 +235,15 @@ label {
color: pvar(--mainForegroundColor);
padding: 5px 15px;
border-radius: 0.25rem;
font-weight: $font-semibold;
opacity: 0.6;
&.active {
font-weight: $font-semibold;
background-color: #f0f0f0;
color: #000;
}
&.active, &:hover, &:active, &:focus {
opacity: 1;
}
}
}

View File

@ -161,9 +161,14 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
.nav.nav-pills {
font-size: 16px !important;
font-weight: $font-semibold !important;
.nav-link.active {
font-weight: $font-semibold !important;
.nav-link {
opacity: 0.6 !important;
&.active, &:hover, &:active, &:focus {
opacity: 1 !important;
}
}
a {
@ -173,29 +178,23 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/';
}
}
.nav-tabs {
.nav-link {
@include disable-default-a-behaviour;
color: pvar(--mainForegroundColor) !important;
}
}
.nav-tabs .nav-link {
&:not(.active) {
border-bottom: 3px solid transparent;
}
@include disable-default-a-behaviour;
color: pvar(--mainForegroundColor);
font-weight: $font-semibold;
border: none;
border-bottom: 2px solid transparent;
opacity: 0.6;
&.active {
font-weight: $font-semibold;
color: pvar(--mainForegroundColor);
background-color: pvar(--mainBackgroundColor) !important;
border: none;
border-bottom: 2px solid pvar(--mainColor);
border-bottom-color: pvar(--mainColor);
}
&:hover {
border-top-color: transparent;
border-left-color: transparent;
border-right-color: transparent;
&.active, &:hover, &:active, &:focus {
opacity: 1;
}
}

View File

@ -929,3 +929,23 @@
@content;
}
}
@mixin sub-menu-h1 {
::ng-deep h1 {
font-size: 1.3rem;
border-bottom: 2px solid $grey-background-color;
padding-bottom: 15px;
margin-bottom: $sub-menu-margin-bottom;
my-global-icon {
margin-right: 10px;
vertical-align: bottom;
width: 24px;
height: 24px;
}
.badge {
margin-left: 7px;
}
}
}

View File

@ -49,6 +49,7 @@ $menu-width: 240px;
$menu-lateral-padding: 26px;
$sub-menu-color: #F7F7F7;
$sub-menu-height: 81px;
$footer-height: 30px;
$footer-margin: 30px;