DEV: replace list control nav dropdown with DMenuMobile (#28324)

Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
Co-authored-by: Renato Atilio <renato@discourse.org>
Co-authored-by: David Taylor <david@taylorhq.com>
This commit is contained in:
chapoi 2024-08-16 01:40:47 +02:00 committed by GitHub
parent 427f473e1b
commit 931485b7c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 232 additions and 285 deletions

View File

@ -0,0 +1,108 @@
import Component from "@glimmer/component";
import { concat, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DropdownMenu from "discourse/components/dropdown-menu";
import NavigationItem from "discourse/components/navigation-item";
import PluginOutlet from "discourse/components/plugin-outlet";
import { filterTypeForMode } from "discourse/lib/filter-mode";
import icon from "discourse-common/helpers/d-icon";
import DMenu from "float-kit/components/d-menu";
export default class NavigationBarComponent extends Component {
@service site;
get filterType() {
return filterTypeForMode(this.args.filterMode);
}
get selectedNavItem() {
const { navItems } = this.args;
let item = navItems.find((i) => i.active === true);
item = item || navItems.find((i) => i.filterType === this.filterType);
return item || navItems[0];
}
@action
onRegisterApi(api) {
this.dMenu = api;
}
<template>
<ul id="navigation-bar" class="nav nav-pills">
{{#if this.site.mobileView}}
<li>
<DMenu
@modalForMobile={{true}}
@autofocus={{true}}
@identifier="list-control-toggle-link"
@onRegisterApi={{this.onRegisterApi}}
>
<:trigger>
<span
class="list-control-toggle-link__text"
>{{this.selectedNavItem.displayName}}</span>
{{icon "discourse-chevron-expand"}}
</:trigger>
<:content>
<DropdownMenu {{on "click" this.dMenu.close}} as |dropdown|>
{{#each @navItems as |navItem|}}
<dropdown.item>
<NavigationItem
@tagName="div"
@content={{navItem}}
@filterMode={{@filterMode}}
@category={{@category}}
class={{concat "nav-item_" navItem.name}}
/>
</dropdown.item>
{{/each}}
<dropdown.item>
<PluginOutlet
@name="extra-nav-item"
@connectorTagName="span"
@outletArgs={{hash
category=@category
tag=@tag
filterMode=@filterMode
}}
/>
</dropdown.item>
</DropdownMenu>
</:content>
</DMenu>
</li>
<li>
<PluginOutlet
@name="inline-extra-nav-item"
@connectorTagName="span"
@outletArgs={{hash category=@category filterMode=@filterMode}}
/>
</li>
{{else}}
{{#each @navItems as |navItem|}}
<NavigationItem
@content={{navItem}}
@filterMode={{@filterMode}}
@category={{@category}}
class={{concat "nav-item_" navItem.name}}
/>
{{/each}}
<PluginOutlet
@name="extra-nav-item"
@connectorTagName="li"
@outletArgs={{hash
category=@category
tag=@tag
filterMode=@filterMode
}}
/>
{{/if}}
</ul>
</template>
}

View File

@ -1,55 +0,0 @@
{{#if this.site.mobileView}}
<li class="navigation-toggle">
<a href {{on "click" this.toggleDrop}} class="toggle-link">
<span
class="toggle-link__text"
>{{this.selectedNavItem.displayName}}</span>
{{d-icon "discourse-chevron-expand"}}
</a>
</li>
{{#if this.expanded}}
<ul class="drop">
{{#each this.navItems as |navItem|}}
<NavigationItem
@content={{navItem}}
@filterMode={{this.filterMode}}
@category={{this.category}}
class={{concat "nav-item_" navItem.name}}
/>
{{/each}}
<PluginOutlet
@name="extra-nav-item"
@connectorTagName="li"
@outletArgs={{hash
category=this.category
tag=this.tag
filterMode=this.filterMode
}}
/>
</ul>
{{/if}}
<PluginOutlet
@name="inline-extra-nav-item"
@connectorTagName="li"
@outletArgs={{hash category=this.category filterMode=this.filterMode}}
/>
{{else}}
{{#each this.navItems as |navItem|}}
<NavigationItem
@content={{navItem}}
@filterMode={{this.filterMode}}
@category={{this.category}}
class={{concat "nav-item_" navItem.name}}
/>
{{/each}}
<PluginOutlet
@name="extra-nav-item"
@connectorTagName="li"
@outletArgs={{hash
category=this.category
tag=this.tag
filterMode=this.filterMode
}}
/>
{{/if}}

View File

@ -1,103 +0,0 @@
import { tracked } from "@glimmer/tracking";
import Component from "@ember/component";
import { action } from "@ember/object";
import { dependentKeyCompat } from "@ember/object/compat";
import { next } from "@ember/runloop";
import $ from "jquery";
import { filterTypeForMode } from "discourse/lib/filter-mode";
import DiscourseURL from "discourse/lib/url";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
export default Component.extend({
tagName: "ul",
classNameBindings: [":nav", ":nav-pills"],
elementId: "navigation-bar",
filterMode: tracked(),
@dependentKeyCompat
get filterType() {
return filterTypeForMode(this.filterMode);
},
init() {
this._super(...arguments);
},
@discourseComputed("filterType", "navItems")
selectedNavItem(filterType, navItems) {
let item = navItems.find((i) => i.active === true);
item = item || navItems.find((i) => i.get("filterType") === filterType);
if (!item) {
let connectors = this.connectors;
let category = this.category;
if (connectors && category) {
connectors.forEach((c) => {
if (
c.connectorClass &&
typeof c.connectorClass.path === "function" &&
typeof c.connectorClass.displayName === "function"
) {
let path = c.connectorClass.path(category);
if (path.indexOf(filterType) > 0) {
item = {
displayName: c.connectorClass.displayName(),
};
}
}
});
}
}
return item || navItems[0];
},
@observes("expanded")
closedNav() {
if (!this.expanded) {
this.ensureDropClosed();
}
},
ensureDropClosed() {
if (!this.element || this.isDestroying || this.isDestroyed) {
return;
}
if (this.expanded) {
this.set("expanded", false);
}
$(window).off("click.navigation-bar");
DiscourseURL.appEvents.off("dom:clean", this, this.ensureDropClosed);
},
@action
toggleDrop(event) {
event?.preventDefault();
this.set("expanded", !this.expanded);
if (this.expanded) {
DiscourseURL.appEvents.on("dom:clean", this, this.ensureDropClosed);
next(() => {
if (!this.expanded) {
return;
}
$(this.element.querySelector(".drop a")).on("click", () => {
this.element.querySelector(".drop").style.display = "none";
next(() => {
this.ensureDropClosed();
});
return true;
});
$(window).on("click.navigation-bar", () => {
this.ensureDropClosed();
return true;
});
});
}
},
});

View File

@ -0,0 +1,35 @@
import EmberObject from "@ember/object";
import { click, render } from "@ember/test-helpers";
import { module, test } from "qunit";
import NavigationBar from "discourse/components/navigation-bar";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
const navItems = [
EmberObject.create({ name: "new", displayName: "New" }),
EmberObject.create({ name: "unread", displayName: "Unread" }),
];
module("Integration | Component | navigation-bar", function (hooks) {
setupRenderingTest(hooks);
test("display navigation bar items", async function (assert) {
await render(<template><NavigationBar @navItems={{navItems}} /></template>);
assert.dom(".nav .nav-item_new").includesText("New");
assert.dom(".nav .nav-item_unread").includesText("Unread");
});
test("display navigation bar items behind a dropdown on mobile", async function (assert) {
this.site.mobileView = true;
await render(<template><NavigationBar @navItems={{navItems}} /></template>);
assert.dom(".nav .nav-item_new").doesNotExist();
assert.dom(".nav .nav-item_unread").doesNotExist();
await click(".list-control-toggle-link-trigger");
assert.dom(".nav .nav-item_new").includesText("New");
assert.dom(".nav .nav-item_unread").includesText("Unread");
});
});

View File

@ -100,7 +100,7 @@ body:not(.archetype-private_message) {
overflow: auto;
align-items: start;
overscroll-behavior: contain;
padding: 1.5em;
padding: 1rem 1.5rem;
width: 100%;
flex-direction: column;
.desktop-view & {

View File

@ -17,6 +17,7 @@
@import "invite-signup";
@import "lightbox";
@import "menu-panel";
@import "list-controls";
@import "login-modal";
@import "modal";
@import "modal-overrides";

View File

@ -0,0 +1,87 @@
.list-controls {
.container {
display: flex;
flex-wrap: wrap;
align-items: center;
#create-topic {
box-sizing: border-box;
display: flex;
align-self: stretch;
align-items: center;
}
}
.navigation-container {
display: flex;
flex-wrap: wrap;
width: 100%;
margin: 0;
}
.nav-pills {
flex: 1 1 auto;
margin: 0 3px 5px 3px;
position: relative;
.list-control-toggle-link-trigger {
font-size: var(--font-up-1-rem);
font-weight: bold;
color: var(--primary-high);
padding: 0;
&:hover,
&:focus {
background: none;
}
.d-icon {
color: inherit;
font-size: var(--font-down-2);
margin-left: 0.5em;
}
}
> li {
margin-right: 0;
}
> li > a {
line-height: var(--line-height-large);
display: flex;
align-items: center;
.d-icon {
margin-left: 5px;
margin-right: 0;
}
}
}
}
.list-container .full-width {
margin-left: 0;
}
.fk-d-menu-modal.list-control-toggle-link-content {
.d-modal {
&__body {
padding-block: 0.75rem;
}
}
.dropdown-menu {
display: flex;
flex-direction: column;
&__item {
a {
display: block;
color: var(--primary);
padding: 0.5em 1rem;
&.active {
background: var(--d-selected);
font-weight: bold;
}
}
}
}
}

View File

@ -2,132 +2,6 @@
// Topic lists
// --------------------------------------------------
// List controls
// --------------------------------------------------
.list-controls {
.container {
display: flex;
flex-wrap: wrap;
align-items: center;
#create-topic {
box-sizing: border-box;
display: flex;
align-self: stretch;
align-items: center;
}
}
.dropdown-select-box-header {
display: flex;
height: 100%;
}
.navigation-container {
display: flex;
flex-wrap: wrap;
width: 100%;
margin: 0;
button {
&.select-kit-header {
display: flex;
height: 100%;
flex: 1 1 auto;
}
}
}
.nav-pills {
display: flex;
flex: 1 1 auto;
margin: 0 3px 5px 3px;
position: relative;
.navigation-toggle {
flex: 0 1 auto;
border: 0;
.toggle-link {
font-size: var(--font-up-1-rem);
font-weight: bold;
color: var(--primary-high);
padding: 0;
&:hover,
&:focus {
background: none;
}
.d-icon {
color: inherit;
font-size: var(--font-down-2);
margin-left: 0.5em;
}
}
}
> li {
margin-right: 0;
font-size: var(--font-down-1);
border: 1px solid var(--primary-medium);
border-radius: var(--d-input-border-radius);
}
> li > a {
line-height: var(--line-height-large);
display: flex;
align-items: center;
border-radius: var(--d-input-border-radius);
.d-icon {
margin-left: 5px;
margin-right: 0;
}
}
.drop {
border: 1px solid var(--primary-low);
position: absolute;
z-index: z("dropdown") - 1;
background-color: var(--secondary);
padding: 0 10px 10px 10px;
width: 150px;
top: 100%;
margin: 0;
left: 0; // iOS6 alignment
li {
list-style-type: none;
margin-left: 0;
margin-top: 5px;
padding-top: 10px;
a {
width: 100%;
display: inline-block;
}
}
}
}
}
.list-container .full-width {
margin-left: 0;
}
.category-breadcrumb {
width: 100%;
gap: 0.5em;
li {
flex: 1 1 33%;
margin: 0;
details {
width: 100%;
}
.name,
.category-name {
@include ellipsis;
}
}
}
// Base list
// --------------------------------------------------
.topic-list {
.right {
margin-left: 55px;