mirror of
https://github.com/discourse/discourse.git
synced 2025-03-25 22:16:41 +08:00
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:
parent
427f473e1b
commit
931485b7c1
@ -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>
|
||||
}
|
@ -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}}
|
@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
@ -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");
|
||||
});
|
||||
});
|
@ -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 & {
|
||||
|
@ -17,6 +17,7 @@
|
||||
@import "invite-signup";
|
||||
@import "lightbox";
|
||||
@import "menu-panel";
|
||||
@import "list-controls";
|
||||
@import "login-modal";
|
||||
@import "modal";
|
||||
@import "modal-overrides";
|
||||
|
87
app/assets/stylesheets/mobile/list-controls.scss
Normal file
87
app/assets/stylesheets/mobile/list-controls.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user