FEATURE: Replace hamburger dropdown with Sidebar when undock (#17600)

When the experimental Sidebar is enabled, the hamburger drop down is replaced by a sidebar drop down. A user is given the ability to dock and undock the sidebar depending on their personal preference.

Do also note that the experimental sidebar is well, considered experimental at this point so I do not intend for the features here to be perfect. What I aim to do here is to ship the changes fast so that it can be used internally by the team to provide feedback. Custom links added by plugins and dark mode toggle has not been implemented as part of this commit as I aim to tackle it in another commit.

Co-authored-by: awesomerobot <kris.aubuchon@discourse.org>
This commit is contained in:
Alan Guo Xiang Tan 2022-07-22 13:06:47 +08:00 committed by GitHub
parent 71eb8d2e8e
commit 99073338de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 477 additions and 172 deletions

View File

@ -1,3 +1,4 @@
import { inject as service } from "@ember/service";
import { empty, equal, notEmpty } from "@ember/object/computed";
import Component from "@ember/component";
import DiscourseURL from "discourse/lib/url";
@ -22,6 +23,7 @@ export default Component.extend({
forwardEvent: false,
preventFocus: false,
onKeyDown: null,
router: service(),
isLoading: computed({
set(key, value) {
@ -149,6 +151,10 @@ export default Component.extend({
}
}
if (this.route) {
this.router.transitionTo(this.route);
}
if (this.href && this.href.length) {
DiscourseURL.routeTo(this.href);
}

View File

@ -10,10 +10,15 @@ export default class SidebarSection extends GlimmerComponent {
constructor() {
super(...arguments);
this.displaySection =
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) === undefined
? true
: false;
if (this.args.collapsable) {
this.displaySection =
this.keyValueStore.getItem(this.collapsedSidebarSectionKey) ===
undefined
? true
: false;
} else {
this.displaySection = true;
}
}
willDestroy() {

View File

@ -231,10 +231,22 @@ const SiteHeaderComponent = MountWidget.extend(
this.appEvents.on("header:show-topic", this, "setTopic");
this.appEvents.on("header:hide-topic", this, "setTopic");
if (this.currentUser?.redesigned_user_menu_enabled) {
this.appEvents.on("user-menu:rendered", this, "_animateMenu");
}
if (
this.currentUser?.experimental_sidebar_enabled &&
!this.site.mobileView
) {
this.appEvents.on(
"sidebar:docked-state-updated",
this,
"queueRerender"
);
}
this.dispatch("notifications:changed", "user-notifications");
this.dispatch("header:keyboard-trigger", "header");
this.dispatch("user-menu:navigation", "user-menu");
@ -351,6 +363,17 @@ const SiteHeaderComponent = MountWidget.extend(
this.appEvents.off("user-menu:rendered", this, "_animateMenu");
}
if (
this.currentUser?.experimental_sidebar_enabled &&
!this.site.mobileView
) {
this.appEvents.off(
"sidebar:docked-state-updated",
this,
"queueRerender"
);
}
if (this.currentUser) {
this.currentUser.off("status-changed", this, "queueRerender");
}
@ -367,6 +390,7 @@ const SiteHeaderComponent = MountWidget.extend(
return {
topic: this._topic,
canSignUp: this.canSignUp,
sidebarDocked: this.sidebarDocked,
};
},

View File

@ -50,6 +50,7 @@ export default Controller.extend({
discourseDebounce(this, this._mainOutletAnimate, 250);
this.toggleProperty("showSidebar");
this.appEvents.trigger("sidebar:docked-state-updated");
if (!this.site.mobileView) {
if (this.showSidebar) {

View File

@ -12,6 +12,7 @@ export default {
const site = container.lookup("site:main");
site.set("mobileView", Mobile.mobileView);
site.set("desktopView", !Mobile.mobileView);
site.set("isMobileDevice", Mobile.isMobileDevice);
setResolverOption("mobileView", Mobile.mobileView);

View File

@ -51,6 +51,10 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
mobile.toggleMobileView();
},
toggleSidebar() {
this.controllerFor("application").send("toggleSidebar");
},
logout: unlessReadOnly(
"_handleLogout",
I18n.t("read_only_mode.logout_disabled")

View File

@ -2,7 +2,18 @@
<a href="#main-container" id="skip-link">{{i18n "skip_to_main_content"}}</a>
<DDocument />
<PluginOutlet @name="above-site-header" @connectorTagName="div" />
<SiteHeader @canSignUp={{this.canSignUp}} @showCreateAccount={{route-action "showCreateAccount"}} @showLogin={{route-action "showLogin"}} @showKeyboard={{route-action "showKeyboardShortcutsHelp"}} @toggleMobileView={{route-action "toggleMobileView"}} @toggleAnonymous={{route-action "toggleAnonymous"}} @logout={{route-action "logout"}} @toggleSidebar={{action "toggleSidebar"}} />
<SiteHeader
@canSignUp={{this.canSignUp}}
@showCreateAccount={{route-action "showCreateAccount"}}
@showLogin={{route-action "showLogin"}}
@showKeyboard={{route-action "showKeyboardShortcutsHelp"}}
@toggleMobileView={{route-action "toggleMobileView"}}
@toggleAnonymous={{route-action "toggleAnonymous"}}
@logout={{route-action "logout"}}
@sidebarDocked={{and this.currentUser.experimental_sidebar_enabled this.showSidebar this.site.desktopView}}
@toggleSidebar={{action "toggleSidebar"}} />
<SoftwareUpdatePrompt />
<PluginOutlet @name="below-site-header" @connectorTagName="div" @args={{hash currentPath=this.router._router.currentPath}} />

View File

@ -1,5 +1,6 @@
<DSection @pageClass="has-sidebar" @class="sidebar-container" @scrollTop={{false}}>
<div class="sidebar-scroll-wrap">
<Sidebar::Sections />
<Sidebar::Sections @collapsableSections={{true}}/>
<Sidebar::Footer @toggleSidebar={{@toggleSidebar}} @tagName=""/>
</div>
</DSection>

View File

@ -4,7 +4,8 @@
@headerLinkText={{i18n "sidebar.sections.categories.header_link_text"}}
@headerLinkTitle={{i18n "sidebar.sections.categories.header_link_title"}}
@headerActions={{array (hash action=this.editTracked title=(i18n "sidebar.sections.categories.header_action_title"))}}
@headerActionsIcon="pencil-alt" >
@headerActionsIcon="pencil-alt"
@collapsable={{@collapsable}} >
{{#if (gt this.sectionLinks.length 0)}}
{{#each this.sectionLinks as |sectionLink|}}

View File

@ -0,0 +1,30 @@
<div class="sidebar-footer-wrapper">
<div class="sidebar-footer-container">
<div class="sidebar-footer-links">
<LinkTo @route="about" title={{i18n "about.simple_title"}} class="sidebar-footer-link sidebar-footer-link-about">
{{i18n "about.simple_title"}}
</LinkTo>
{{#if this.currentUser.admin}}
<LinkTo @route="adminSiteSettings" title={{i18n "admin.site_settings.title"}} class="sidebar-footer-link sidebar-footer-link-site-settings">
{{i18n "admin.site_settings.title"}}
</LinkTo>
{{/if}}
</div>
<div class="sidebar-footer-actions">
<DButton
@action={{route-action "showKeyboardShortcutsHelp"}}
@title={{i18n "keyboard_shortcuts_help.title"}}
@icon="keyboard"
@class="sidebar-footer-actions-button sidebar-footer-actions-keyboard-shortcuts" />
{{#if this.site.desktopView}}
<DButton
@action={{@toggleSidebar}}
@icon="thumbtack"
@class="sidebar-footer-actions-button sidebar-footer-actions-dock-toggle"/>
{{/if}}
</div>
</div>
</div>

View File

@ -0,0 +1,12 @@
<div class="hamburger-panel">
<div class="hamburger-menu revamped menu-panel drop-down" data-max-width="320">
<div class="panel-body">
<div class="panel-body-content">
<div class="sidebar-hamburger-dropdown">
<Sidebar::Sections @collapsableSections={{false}}/>
<Sidebar::Footer @toggleSidebar={{route-action "toggleSidebar"}} @tagName="" />
</div>
</div>
</div>
</div>
</div>

View File

@ -6,7 +6,8 @@
@headerActions={{array (hash action=(fn (route-action "composePrivateMessage") null null))}}
@headerActionsIcon="plus"
@headerLinkText={{i18n "sidebar.sections.messages.header_link_text"}}
@headerLinkTitle={{i18n "sidebar.sections.messages.header_link_title"}} >
@headerLinkTitle={{i18n "sidebar.sections.messages.header_link_title"}}
@collapsable={{@collapsable}}>
{{#each this.personalMessagesSectionLinks as |personalMessageSectionLink|}}
{{#if personalMessageSectionLink.shouldDisplay}}
@ -21,8 +22,6 @@
{{/each}}
{{#if (gt this.groupMessagesSectionLinks.length 0)}}
<hr>
{{#each this.groupMessagesSectionLinks as |groupMessageSectionLink|}}
{{#if groupMessageSectionLink.shouldDisplay}}
<Sidebar::SectionLink

View File

@ -1,13 +1,15 @@
<div class={{concat "sidebar-section-wrapper sidebar-section-" @sectionName}}>
<div class="sidebar-section-header">
<button
type="button"
class="sidebar-section-header-caret"
title="toggle section"
{{on "click" this.toggleSectionDisplay}}
>
{{d-icon this.headerCaretIcon}}
</button>
{{#if @collapsable}}
<button
type="button"
class="sidebar-section-header-caret"
title="toggle section"
{{on "click" this.toggleSectionDisplay}}
>
{{d-icon this.headerCaretIcon}}
</button>
{{/if}}
{{#if @headerRoute}}
<LinkTo

View File

@ -1,13 +1,13 @@
<div class="sidebar-sections">
<Sidebar::TopicsSection />
<Sidebar::CategoriesSection />
<Sidebar::TopicsSection @collapsable={{@collapsableSections}}/>
<Sidebar::CategoriesSection @collapsable={{@collapsableSections}}/>
{{#if this.siteSettings.tagging_enabled}}
<Sidebar::TagsSection />
<Sidebar::TagsSection @collapsable={{@collapsableSections}}/>
{{/if}}
{{#if this.siteSettings.enable_personal_messages}}
<Sidebar::MessagesSection />
<Sidebar::MessagesSection @collapsable={{@collapsableSections}}/>
{{/if}}
{{#each this.customSections as |customSection|}}
@ -18,7 +18,8 @@
@headerLinkTitle={{customSection.title}}
@headerActionsIcon={{customSection.actionsIcon}}
@headerActions={{customSection.actions}}
@willDestroy={{customSection.willDestroy}}>
@willDestroy={{customSection.willDestroy}}
@collapsable={{@collapsableSections}}>
{{#each customSection.links as |link|}}
<Sidebar::SectionLink

View File

@ -4,7 +4,8 @@
@headerLinkText={{i18n "sidebar.sections.tags.header_link_text"}}
@headerLinkTitle={{i18n "sidebar.sections.tags.header_link_title"}}
@headerActions={{array (hash action=this.editTracked title=(i18n "sidebar.sections.tags.header_action_title"))}}
@headerActionsIcon="pencil-alt" >
@headerActionsIcon="pencil-alt"
@collapsable={{@collapsable}}>
{{#if (gt this.sectionLinks.length 0)}}
{{#each this.sectionLinks as |sectionLink|}}

View File

@ -5,7 +5,8 @@
@headerLinkText={{i18n "sidebar.sections.topics.header_link_text"}}
@headerLinkTitle={{i18n "sidebar.sections.topics.header_link_title"}}
@headerActionsIcon="plus"
@headerActions={{array (hash action=this.composeTopic title=(i18n "sidebar.sections.topics.header_action_title"))}}>
@headerActions={{array (hash action=this.composeTopic title=(i18n "sidebar.sections.topics.header_action_title"))}}
@collapsable={{@collapsable}} >
{{#each this.sectionLinks as |sectionLink|}}
<Sidebar::SectionLink

View File

@ -4,9 +4,6 @@ import hbs from "discourse/widgets/hbs-compiler";
createWidget("header-contents", {
tagName: "div.contents.clearfix",
template: hbs`
{{#if attrs.sidebarEnabled}}
{{sidebar-toggle attrs=attrs}}
{{/if}}
{{home-logo attrs=attrs}}
{{#if attrs.topic}}
{{header-topic-info attrs=attrs}}

View File

@ -244,32 +244,34 @@ createWidget("header-icons", {
icons.push(search);
const hamburger = this.attach("header-dropdown", {
title: "hamburger_menu",
icon: "bars",
iconId: "toggle-hamburger-menu",
active: attrs.hamburgerVisible,
action: "toggleHamburger",
href: "",
classNames: ["hamburger-dropdown"],
if (!attrs.sidebarDocked) {
const hamburger = this.attach("header-dropdown", {
title: "hamburger_menu",
icon: "bars",
iconId: "toggle-hamburger-menu",
active: attrs.hamburgerVisible,
action: "toggleHamburger",
href: "",
classNames: ["hamburger-dropdown"],
contents() {
let { currentUser } = this;
if (currentUser && currentUser.reviewable_count) {
return h(
"div.badge-notification.reviewables",
{
attributes: {
title: I18n.t("notifications.reviewable_items"),
contents() {
let { currentUser } = this;
if (currentUser && currentUser.reviewable_count) {
return h(
"div.badge-notification.reviewables",
{
attributes: {
title: I18n.t("notifications.reviewable_items"),
},
},
},
this.currentUser.reviewable_count
);
}
},
});
this.currentUser.reviewable_count
);
}
},
});
icons.push(hamburger);
icons.push(hamburger);
}
if (attrs.user) {
icons.push(
@ -332,6 +334,26 @@ export function attachAdditionalPanel(name, toggle, transformAttrs) {
additionalPanels.push({ name, toggle, transformAttrs });
}
createWidget("revamped-hamburger-menu-wrapper", {
buildAttributes() {
return { "data-click-outside": true };
},
html() {
return [
new RenderGlimmer(
this,
"div.widget-component-connector",
hbs`<Sidebar::HamburgerDropdown />`
),
];
},
clickOutside() {
this.sendWidgetAction("toggleHamburger");
},
});
createWidget("revamped-user-menu-wrapper", {
buildAttributes() {
return { "data-click-outside": true };
@ -380,6 +402,10 @@ export default createWidget("header", {
inTopicRoute = this.router.currentRouteName.startsWith("topic.");
}
if (attrs.sidebarDocked) {
state.hamburgerVisible = false;
}
let contents = () => {
const headerIcons = this.attach("header-icons", {
hamburgerVisible: state.hamburgerVisible,
@ -387,6 +413,7 @@ export default createWidget("header", {
searchVisible: state.searchVisible,
ringBackdrop: state.ringBackdrop,
flagCount: attrs.flagCount,
sidebarDocked: attrs.sidebarDocked,
user: this.currentUser,
});
@ -403,7 +430,11 @@ export default createWidget("header", {
})
);
} else if (state.hamburgerVisible) {
panels.push(this.attach("hamburger-menu"));
if (this.currentUser?.experimental_sidebar_enabled) {
panels.push(this.attach("revamped-hamburger-menu-wrapper", {}));
} else {
panels.push(this.attach("hamburger-menu"));
}
} else if (state.userVisible) {
if (this.currentUser.redesigned_user_menu_enabled) {
panels.push(this.attach("revamped-user-menu-wrapper", {}));
@ -433,7 +464,6 @@ export default createWidget("header", {
const contentsAttrs = {
contents,
minimized: !!attrs.topic,
sidebarEnabled: this.currentUser?.experimental_sidebar_enabled,
};
return h(
@ -516,13 +546,20 @@ export default createWidget("header", {
},
toggleHamburger() {
this.state.hamburgerVisible = !this.state.hamburgerVisible;
this.toggleBodyScrolling(this.state.hamburgerVisible);
if (
this.currentUser?.experimental_sidebar_enabled &&
this.site.mobileView
) {
this.sendWidgetAction("toggleSidebar");
} else {
this.state.hamburgerVisible = !this.state.hamburgerVisible;
this.toggleBodyScrolling(this.state.hamburgerVisible);
// auto focus on first link in dropdown
schedule("afterRender", () => {
document.querySelector(".hamburger-panel .menu-links a")?.focus();
});
// auto focus on first link in dropdown
schedule("afterRender", () => {
document.querySelector(".hamburger-panel .menu-links a")?.focus();
});
}
},
toggleBodyScrolling(bool) {

View File

@ -1,16 +0,0 @@
import { createWidget } from "discourse/widgets/widget";
export default createWidget("sidebar-toggle", {
tagName: "span.header-sidebar-toggle",
html() {
return [
this.attach("button", {
title: "",
icon: "bars",
action: "toggleSidebar",
className: "btn btn-flat btn-sidebar-toggle",
}),
];
},
});

View File

@ -416,9 +416,11 @@ export default class Widget {
if (this.clickOutside) {
properties["widget-click-outside"] = new WidgetClickOutsideHook(this);
}
if (this.click) {
properties["widget-click"] = new WidgetClickHook(this);
}
if (this.doubleClick) {
properties["widget-double-click"] = new WidgetDoubleClickHook(this);
}

View File

@ -9,6 +9,9 @@ import {
query,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
import Site from "discourse/models/site";
import discoveryFixture from "discourse/tests/fixtures/discovery-fixtures";
import categoryFixture from "discourse/tests/fixtures/category-fixtures";
@ -387,7 +390,7 @@ acceptance("Sidebar - Categories Section", function (needs) {
);
});
test("clean up topic tracking state state changed callbacks when section is destroyed", async function (assert) {
test("clean up topic tracking state state changed callbacks when Sidebar is collapsed", async function (assert) {
setupUserSidebarCategories();
await visit("/");
@ -400,12 +403,11 @@ acceptance("Sidebar - Categories Section", function (needs) {
topicTrackingState.stateChangeCallbacks
).length;
await click(".header-sidebar-toggle .btn");
await click(".header-sidebar-toggle .btn");
await undockSidebar();
assert.strictEqual(
Object.keys(topicTrackingState.stateChangeCallbacks).length,
initialCallbackCount
assert.ok(
Object.keys(topicTrackingState.stateChangeCallbacks).length <
initialCallbackCount
);
});
});

View File

@ -15,7 +15,12 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
test("clicking outside sidebar collapses it", async function (assert) {
await visit("/");
await click(".btn-sidebar-toggle");
await click(".hamburger-dropdown");
assert.notOk(
exists(".sidebar-footer-actions-dock-toggle"),
"button to dock sidebar is not displayed"
);
assert.ok(exists(".sidebar-container"), "sidebar is displayed");
@ -27,7 +32,7 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
test("clicking on a link or button in sidebar collapses it", async function (assert) {
await visit("/");
await click(".btn-sidebar-toggle");
await click(".hamburger-dropdown");
await click(".sidebar-section-link-tracked");
assert.ok(
@ -35,7 +40,7 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
"sidebar is collapsed when a button in sidebar is clicked"
);
await click(".btn-sidebar-toggle");
await click(".hamburger-dropdown");
await click(".sidebar-section-header-link");
assert.ok(
@ -47,7 +52,7 @@ acceptance("Sidebar - Mobile - User with sidebar enabled", function (needs) {
test("collapsing sidebar sections does not collapse sidebar", async function (assert) {
await visit("/");
await click(".btn-sidebar-toggle");
await click(".hamburger-dropdown");
await click(".sidebar-section-header-caret");
assert.ok(

View File

@ -10,6 +10,7 @@ import {
import { withPluginApi } from "discourse/lib/plugin-api";
import { resetSidebarSection } from "discourse/lib/sidebar/custom-sections";
import { bind } from "discourse-common/utils/decorators";
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
acceptance("Sidebar - Plugin API", function (needs) {
needs.user({ experimental_sidebar_enabled: true });
@ -338,7 +339,7 @@ acceptance("Sidebar - Plugin API", function (needs) {
"displays hover button with correct title"
);
await click(".header-sidebar-toggle button");
await undockSidebar();
assert.strictEqual(
linkDestroy,

View File

@ -9,6 +9,9 @@ import {
query,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
import discoveryFixture from "discourse/tests/fixtures/discovery-fixtures";
import { cloneJSON } from "discourse-common/lib/object";
@ -327,12 +330,11 @@ acceptance("Sidebar - Tags section", function (needs) {
topicTrackingState.stateChangeCallbacks
).length;
await click(".header-sidebar-toggle .btn");
await click(".header-sidebar-toggle .btn");
await undockSidebar();
assert.strictEqual(
Object.keys(topicTrackingState.stateChangeCallbacks).length,
initialCallbackCount
assert.ok(
Object.keys(topicTrackingState.stateChangeCallbacks).length <
initialCallbackCount
);
});
});

View File

@ -1,6 +1,11 @@
import { test } from "qunit";
import { click, visit } from "@ember/test-helpers";
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
import { click, currentRouteName, visit } from "@ember/test-helpers";
import {
acceptance,
exists,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
acceptance("Sidebar - Anon User", function () {
// Don't show sidebar for anon user until we know what we want to display
@ -34,7 +39,39 @@ acceptance("Sidebar - User with sidebar disabled", function (needs) {
acceptance("Sidebar - User with sidebar enabled", function (needs) {
needs.user({ experimental_sidebar_enabled: true });
test("hiding and displaying sidebar", async function (assert) {
test("navigating to about route using sidebar", async function (assert) {
await visit("/");
await click(".sidebar-footer-link-about");
assert.strictEqual(currentRouteName(), "about");
});
test("viewing keyboard shortcuts using sidebar", async function (assert) {
await visit("/");
await click(".sidebar-footer-actions-keyboard-shortcuts");
assert.ok(
exists("#keyboard-shortcuts-help"),
"keyboard shortcuts help is displayed"
);
});
test("navigating to site setting route using sidebar", async function (assert) {
await visit("/");
await click(".sidebar-footer-link-site-settings");
assert.strictEqual(currentRouteName(), "adminSiteSettingsCategory");
});
test("site setting link is not shown in sidebar for non-admin user", async function (assert) {
updateCurrentUser({ admin: false });
await visit("/");
assert.notOk(exists(".sidebar-footer-link-site-settings"));
});
test("undocking and docking sidebar", async function (assert) {
await visit("/");
assert.ok(
@ -44,17 +81,27 @@ acceptance("Sidebar - User with sidebar enabled", function (needs) {
assert.ok(exists(".sidebar-container"), "displays the sidebar by default");
await click(".header-sidebar-toggle .btn");
await undockSidebar();
assert.ok(
!document.body.classList.contains("has-sidebar-page"),
"removes sidebar utility class to body"
"removes sidebar utility class from body"
);
assert.ok(!exists(".sidebar-container"), "hides the sidebar");
await click(".header-sidebar-toggle .btn");
await click(".hamburger-dropdown");
assert.ok(exists(".sidebar-container"), "displays the sidebar");
assert.ok(
exists(".sidebar-hamburger-dropdown"),
"displays the sidebar in hamburger dropdown"
);
await click("button.sidebar-footer-actions-dock-toggle");
assert.ok(
exists(".sidebar-container"),
"displays the sidebar after docking"
);
});
});

View File

@ -10,6 +10,7 @@ import {
publishToMessageBus,
query,
} from "discourse/tests/helpers/qunit-helpers";
import { undockSidebar } from "discourse/tests/helpers/sidebar-helpers";
import topicFixtures from "discourse/tests/fixtures/discovery-fixtures";
import { cloneJSON } from "discourse-common/lib/object";
import { withPluginApi } from "discourse/lib/plugin-api";
@ -725,12 +726,11 @@ acceptance("Sidebar - Topics Section", function (needs) {
topicTrackingState.stateChangeCallbacks
).length;
await click(".header-sidebar-toggle .btn");
await click(".header-sidebar-toggle .btn");
await undockSidebar();
assert.strictEqual(
Object.keys(topicTrackingState.stateChangeCallbacks).length,
initialCallbackCount
assert.ok(
Object.keys(topicTrackingState.stateChangeCallbacks).length <
initialCallbackCount
);
});
});

View File

@ -0,0 +1,5 @@
import { click } from "@ember/test-helpers";
export async function undockSidebar() {
await click("button.sidebar-footer-actions-dock-toggle");
}

View File

@ -18,6 +18,7 @@
--always-black-rgb: 0, 0, 0;
--primary-rgb: #{hexToRGB($primary)};
--primary-low-rgb: #{hexToRGB($primary-low)};
--primary-very-low-rgb: #{hexToRGB($primary-very-low)};
--secondary-rgb: #{hexToRGB($secondary)};
--header_background-rgb: #{hexToRGB($header_background)};
--tertiary-rgb: #{hexToRGB($tertiary)};

View File

@ -45,6 +45,7 @@
@import "share_link";
@import "shared-drafts";
@import "sidebar";
@import "sidebar-footer";
@import "sidebar-section";
@import "sidebar-section-link";
@import "tagging";

View File

@ -17,7 +17,10 @@
box-sizing: border-box;
width: 100%;
height: 100%;
@media (prefers-reduced-motion: no-preference) {
transition: padding-left var(--d-sidebar-animation-time)
var(--d-sidebar-animation-ease) !important; // only works with an important... :/
}
.contents {
display: flex;
align-items: center;

View File

@ -568,3 +568,33 @@ div.menu-links-header {
color: var(--primary-med-or-secondary-med);
}
}
// Sidebar-hamburger hybrid
.hamburger-menu.revamped {
.sidebar-section-wrapper {
.sidebar-section-header-button {
opacity: 1;
}
.sidebar-section-link.active {
font-weight: normal;
color: var(--primary-high);
background: var(--tertiary-low);
}
.sidebar-section-header-link,
.sidebar-section-header-button,
.sidebar-section-link {
&:hover {
background: var(--highlight-medium);
}
}
}
.sidebar-footer-actions-button.btn {
&:hover {
background: var(--highlight-medium);
}
}
}

View File

@ -0,0 +1,101 @@
.sidebar-wrapper {
.sidebar-footer-wrapper {
.sidebar-footer-container {
margin-left: 1.5em;
margin-right: 0.15em;
}
}
}
.sidebar-footer-wrapper {
border-top: 1.5px solid var(--primary-low);
padding: 0.5em 0 0.5em 0.33em;
.sidebar-footer-container {
display: flex;
}
.sidebar-footer-link {
display: inline-block;
font-size: var(--font-down-1);
color: var(--primary-high);
padding: 0.25em 0;
height: 100%;
&:not(:first-child):before {
content: "";
padding: 0 0.25em;
}
}
.sidebar-footer-actions {
margin-left: auto;
}
.sidebar-footer-actions-button.btn {
background: transparent;
border: none;
padding: 0.25em 0.4em;
.d-icon {
font-size: var(--font-down-1);
color: var(--primary-medium);
}
&:hover {
background: var(--primary-low);
}
}
}
.has-sidebar-page {
.sidebar-footer-actions-dock-toggle {
.d-icon {
transform: rotate(180deg);
top: -0.1em; // optical alignment
}
}
}
.sidebar-footer-wrapper {
background: var(--primary-very-low);
.desktop-view & {
position: sticky;
bottom: 0;
.sidebar-footer-container {
position: relative;
&:before {
// fade to make scroll more apparent
position: absolute;
content: "";
display: block;
height: 1.5em;
top: calc(-2em - 1px);
left: -0.5em;
right: -0.5em;
pointer-events: none;
background: linear-gradient(
to bottom,
transparent 0%,
rgba(var(--primary-very-low-rgb), 100%)
);
}
}
}
}
.hamburger-menu.revamped {
.sidebar-footer-wrapper {
background: var(--secondary);
.sidebar-footer-container {
&:before {
// fade to make scroll more apparent
background: linear-gradient(
to bottom,
transparent 0%,
rgba(var(--secondary-rgb), 100%)
);
}
}
}
}

View File

@ -3,6 +3,7 @@
align-items: center;
.sidebar-section-link {
box-sizing: border-box;
display: inline-flex;
width: 100%;
align-items: center;

View File

@ -49,9 +49,12 @@
background: none;
border: none;
padding: 0.25em 0.5em;
opacity: 0;
transition: opacity 0.25s;
transition-delay: 0.5s;
.d-icon {
font-size: $font-down-1;
font-size: var(--font-down-1);
color: var(--primary-medium);
}
@ -92,6 +95,9 @@
opacity: 0;
transition: opacity 0.25s;
transition-delay: 0.5s;
.d-icon {
top: -0.1em; // visual alignment
}
.discourse-no-touch & {
&:hover {
svg {

View File

@ -4,46 +4,6 @@
--d-sidebar-animation-ease: ease-in-out;
}
.header-sidebar-toggle {
--toggle-padding: 0.5em;
margin-right: 0.75em;
// extending the toggle beyond the page when space allows
// for better logo alignment with content
@media screen and (min-width: 1380px) {
margin-left: -3.5em;
}
// align on icon, because button is transparent
@media screen and (max-width: 1480px) {
margin-left: calc(var(--toggle-padding) * -1.3);
}
// prevents toggle overflow on smaller screens
@media screen and (max-width: 1379px) {
:not(.mobile-view) .has-sidebar-page & {
margin-left: initial;
}
}
transition: margin var(--d-sidebar-animation-speed)
var(--d-sidebar-animation-ease);
button {
position: relative;
font-size: var(--font-up-2);
padding: var(--toggle-padding);
.discourse-no-touch & {
&:hover {
background: var(--primary-low);
.d-icon {
color: var(--primary-medium);
}
}
}
}
}
#main-outlet-wrapper {
.sidebar-wrapper {
grid-area: sidebar;
@ -53,8 +13,8 @@
align-self: start;
overflow-y: auto;
background-color: var(--primary-very-low);
.discourse-touch &,
&:hover {
.sidebar-section-header-button,
.sidebar-section-header-caret {
opacity: 1;
transition-delay: 0s;
@ -63,10 +23,12 @@
}
.sidebar-container {
display: flex;
flex-direction: column;
box-sizing: border-box;
height: 100%;
width: var(--d-sidebar-width);
padding: 1em 0;
padding: 1em 0 0;
overflow-x: hidden;
overflow-y: auto;
@ -99,10 +61,13 @@
.sidebar-scroll-wrap {
// limit the wrapper width, so when the scrollbar is added the content doesn't shift
max-width: calc(var(--d-sidebar-width) - var(--scrollbarWidth));
box-sizing: border-box;
flex: 1;
display: flex;
flex-direction: column;
}
.sidebar-toggle {
display: flex;
justify-content: flex-end;
.sidebar-sections {
flex: 1;
}
}

View File

@ -193,7 +193,7 @@ body.has-sidebar-page {
max-width: calc(var(--d-sidebar-width) + var(--d-max-width));
}
.d-header .wrap {
padding-left: 0;
padding-left: 1.85em;
}
#main-outlet-wrapper {
grid-template-columns: var(--d-sidebar-width) minmax(0, 1fr);
@ -202,16 +202,18 @@ body.has-sidebar-page {
}
}
body.sidebar-animate {
#main-outlet-wrapper {
// grid-template-columns transition supported in Firefox, Chrome support coming summer 2022
transition-property: grid-template-columns, max-width;
transition-timing-function: var(--d-sidebar-animation-ease);
transition-duration: var(--d-sidebar-animation-time);
}
@media (prefers-reduced-motion: no-preference) {
body.sidebar-animate {
#main-outlet-wrapper {
// grid-template-columns transition supported in Firefox, Chrome support coming summer 2022
transition-property: grid-template-columns, max-width;
transition-timing-function: var(--d-sidebar-animation-ease);
transition-duration: var(--d-sidebar-animation-time);
}
.d-header-wrap .wrap {
transition: max-width var(--d-sidebar-animation-time)
var(--d-sidebar-animation-ease);
.d-header-wrap .wrap {
transition: max-width var(--d-sidebar-animation-time)
var(--d-sidebar-animation-ease);
}
}
}

View File

@ -143,15 +143,19 @@ blockquote {
// Sidebar styles
.sidebar-wrapper {
width: 0;
transition: width 0.2s ease-in-out;
z-index: z("modal", "content");
}
#main-outlet-wrapper {
grid-template-columns: 0 minmax(0, 100vw);
grid-template-columns: minmax(0, 100vw);
grid-template-areas: "content";
gap: 0;
.sidebar-wrapper {
width: 0;
transition: width 0.2s ease-in-out;
z-index: z("modal", "content");
grid-area: content;
justify-self: end;
}
.sidebar-container {
padding-bottom: 6.6em; // extra space to watch out for navbar
}
@ -166,10 +170,11 @@ body.has-sidebar-page {
z-index: z("modal", "content") + 1;
}
.sidebar-wrapper {
width: var(--d-sidebar-width);
grid-area: content;
margin-left: -10px; // compensate for main-outlet-wrapper padding
#main-outlet-wrapper {
.sidebar-wrapper {
width: var(--d-sidebar-width);
margin-right: -10px; // compensate for main-outlet-wrapper padding
}
}
#main-outlet {

View File

@ -10,3 +10,9 @@
font-size: var(--font-0);
}
}
.sidebar-wrapper .sidebar-footer-wrapper .sidebar-footer-container {
margin-top: 0.5em;
margin-right: 0;
padding-left: 0.25em;
}

View File

@ -3735,6 +3735,7 @@ en:
shortcut_delimiter_slash: "%{shortcut1}/%{shortcut2}"
shortcut_delimiter_space: "%{shortcut1} %{shortcut2}"
title: "Keyboard Shortcuts"
short_title: "Shortcuts"
jump_to:
title: "Jump To"
home: "%{shortcut} Home"

View File

@ -141,6 +141,7 @@ module SvgSprite
"info-circle",
"italic",
"key",
"keyboard",
"layer-group",
"link",
"list",