DEV: Mobile layout support for experimental user nav (#18308)

This commit is contained in:
Alan Guo Xiang Tan 2022-09-22 09:45:50 +08:00 committed by GitHub
parent 54b4e56103
commit b066955838
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 265 additions and 140 deletions

View File

@ -1,100 +1,111 @@
<section class="user-primary-navigation"> {{#if @shouldDisplay}}
<ul class="main-nav nav nav-pills user-nav"> <section class="user-primary-navigation" {{did-insert this.registerClickListener}} {{will-destroy this.unregisterClickListener}}>
{{#unless @user.profile_hidden}} <ul class="main-nav nav nav-pills user-nav">
<li class="summary"> {{#unless @user.profile_hidden}}
<LinkTo @route="user.summary"> <li class="summary">
{{d-icon "user"}} <LinkTo @route="user.summary">
{{i18n "user.summary.title"}} {{d-icon "user"}}
</LinkTo> {{i18n "user.summary.title"}}
</li> </LinkTo>
</li>
<UserNav::DropdownList <UserNav::DropdownList
@icon="stream" @icon="stream"
@text={{i18n "user.activity_stream"}} @text={{i18n "user.activity_stream"}}
@isActive={{eq @currentParentRoute "userActivity"}} @isActive={{eq @currentParentRoute "userActivity"}}
@class="user-activity" > @class="user-activity" >
<:submenu> <:submenu>
<DNavigationItem @route="userActivity.index">{{i18n "user.filters.all"}}</DNavigationItem> <DNavigationItem @route="userActivity.index">{{i18n "user.filters.all"}}</DNavigationItem>
<DNavigationItem @route="userActivity.topics">{{i18n "user_action_groups.4"}}</DNavigationItem> <DNavigationItem @route="userActivity.topics">{{i18n "user_action_groups.4"}}</DNavigationItem>
<DNavigationItem @route="userActivity.replies">{{i18n "user_action_groups.5"}}</DNavigationItem> <DNavigationItem @route="userActivity.replies">{{i18n "user_action_groups.5"}}</DNavigationItem>
{{#if @showRead}} {{#if @showRead}}
<DNavigationItem @route="userActivity.read" @title={{i18n "user.read_help"}}> <DNavigationItem @route="userActivity.read" @title={{i18n "user.read_help"}}>
{{i18n "user.read"}} {{i18n "user.read"}}
</DNavigationItem> </DNavigationItem>
{{/if}} {{/if}}
{{#if @showDrafts}} {{#if @showDrafts}}
<DNavigationItem @route="userActivity.drafts"> <DNavigationItem @route="userActivity.drafts">
{{this.draftLabel}} {{this.draftLabel}}
</DNavigationItem> </DNavigationItem>
{{/if}} {{/if}}
{{#if (gt @user.pending_posts_count 0)}} {{#if (gt @user.pending_posts_count 0)}}
<DNavigationItem @route="userActivity.pending"> <DNavigationItem @route="userActivity.pending">
{{this.pendingLabel}} {{this.pendingLabel}}
</DNavigationItem> </DNavigationItem>
{{/if}} {{/if}}
<DNavigationItem @route="userActivity.likesGiven">{{i18n "user_action_groups.1"}}</DNavigationItem> <DNavigationItem @route="userActivity.likesGiven">{{i18n "user_action_groups.1"}}</DNavigationItem>
{{#if @showBookmarks}} {{#if @showBookmarks}}
<DNavigationItem @route="userActivity.bookmarks">{{i18n "user_action_groups.3"}}</DNavigationItem> <DNavigationItem @route="userActivity.bookmarks">{{i18n "user_action_groups.3"}}</DNavigationItem>
{{/if}} {{/if}}
<PluginOutlet @name="user-activity-bottom" @tagName="span" @connectorTagName="li" @args={{hash model=@user}} /> <PluginOutlet @name="user-activity-bottom" @tagName="span" @connectorTagName="li" @args={{hash model=@user}} />
</:submenu> </:submenu>
</UserNav::DropdownList> </UserNav::DropdownList>
{{/unless}} {{/unless}}
{{#if @showNotificationsTab}} {{#if @showNotificationsTab}}
<li class="user-notifications"> <li class="user-notifications">
<LinkTo @route="userNotifications"> <LinkTo @route="userNotifications">
{{d-icon "comment" class="glyph"}}{{i18n "user.notifications"}} {{d-icon "comment" class="glyph"}}{{i18n "user.notifications"}}
</LinkTo> </LinkTo>
</li> </li>
{{/if}} {{/if}}
{{#if @showPrivateMessages}} {{#if @showPrivateMessages}}
<li class="private-messages"> <li class="private-messages">
<LinkTo @route="userPrivateMessages"> <LinkTo @route="userPrivateMessages">
{{d-icon "far-envelope"}} {{d-icon "far-envelope"}}
{{i18n "user.private_messages"}} {{i18n "user.private_messages"}}
</LinkTo> </LinkTo>
</li> </li>
{{/if}} {{/if}}
{{#if @canInviteToForum}} {{#if @canInviteToForum}}
<li class="invited"> <li class="invited">
<LinkTo @route="userInvited"> <LinkTo @route="userInvited">
{{d-icon "user-plus"}} {{d-icon "user-plus"}}
{{i18n "user.invited.title"}} {{i18n "user.invited.title"}}
</LinkTo> </LinkTo>
</li> </li>
{{/if}} {{/if}}
{{#if @showBadges}} {{#if @showBadges}}
<li class="badges"> <li class="badges">
<LinkTo @route="user.badges"> <LinkTo @route="user.badges">
{{d-icon "certificate"}} {{d-icon "certificate"}}
{{i18n "badges.title"}} {{i18n "badges.title"}}
</LinkTo> </LinkTo>
</li> </li>
{{/if}} {{/if}}
<PluginOutlet <PluginOutlet
@name="user-main-nav" @name="user-main-nav"
@connectorTagName="li" @connectorTagName="li"
@args={{hash model=@user}} /> @args={{hash model=@user}} />
{{#if @user.can_edit}} {{#if @user.can_edit}}
<li class="preferences"> <li class="preferences">
<LinkTo @route="preferences"> <LinkTo @route="preferences">
{{d-icon "cog"}} {{d-icon "cog"}}
{{i18n "user.preferences"}} {{i18n "user.preferences"}}
</LinkTo> </LinkTo>
</li> </li>
{{/if}} {{/if}}
</ul>
</section> {{#if (and this.site.mobileView this.currentUser.staff)}}
<li class="admin">
<a href={{@user.adminPath}} >
{{d-icon "wrench"}}
{{i18n "admin.user.manage_user"}}
</a>
</li>
{{/if}}
</ul>
</section>
{{/if}}

View File

@ -2,6 +2,11 @@ import I18n from "I18n";
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { action } from "@ember/object";
import { next } from "@ember/runloop";
import { bind } from "discourse-common/utils/decorators";
import { DROPDOWN_BUTTON_CSS_CLASS } from "discourse/components/user-nav/dropdown-list";
export default class UserNav extends Component { export default class UserNav extends Component {
@service currentUser; @service currentUser;
@ -15,4 +20,27 @@ export default class UserNav extends Component {
? I18n.t("drafts.label_with_count", { count }) ? I18n.t("drafts.label_with_count", { count })
: I18n.t("drafts.label"); : I18n.t("drafts.label");
} }
@bind
_handleClickEvent(event) {
if (!event.target.closest(`.${DROPDOWN_BUTTON_CSS_CLASS}`)) {
next(() => {
this.args.toggleUserNav();
});
}
}
@action
registerClickListener(element) {
if (this.site.mobileView) {
element.addEventListener("click", this._handleClickEvent);
}
}
@action
unregisterClickListener(element) {
if (this.site.mobileView) {
element.removeEventListener("click", this._handleClickEvent);
}
}
} }

View File

@ -1,17 +1,21 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import { bind } from "discourse-common/utils/decorators"; import { bind } from "discourse-common/utils/decorators";
export const DROPDOWN_BUTTON_CSS_CLASS = "user-nav-dropdown-button";
export default class UserNavDropdownList extends Component { export default class UserNavDropdownList extends Component {
@tracked displayList = false; @service site;
@tracked displayList = this.site.mobileView && this.args.isActive;
get chevron() { get chevron() {
return this.displayList ? "chevron-up" : "chevron-down"; return this.displayList ? "chevron-up" : "chevron-down";
} }
get defaultButtonClass() { get defaultButtonClass() {
return "user-nav-dropdown-button"; return DROPDOWN_BUTTON_CSS_CLASS;
} }
get buttonClass() { get buttonClass() {

View File

@ -1,5 +1,5 @@
import Controller, { inject as controller } from "@ember/controller"; import Controller, { inject as controller } from "@ember/controller";
import EmberObject, { computed, set } from "@ember/object"; import EmberObject, { action, computed, set } from "@ember/object";
import { and, equal, gt, not, or, readOnly } from "@ember/object/computed"; import { and, equal, gt, not, or, readOnly } from "@ember/object/computed";
import CanCheckEmails from "discourse/mixins/can-check-emails"; import CanCheckEmails from "discourse/mixins/can-check-emails";
import User from "discourse/models/user"; import User from "discourse/models/user";
@ -17,6 +17,13 @@ export default Controller.extend(CanCheckEmails, {
dialog: service(), dialog: service(),
userNotifications: controller("user-notifications"), userNotifications: controller("user-notifications"),
adminTools: optionalService(), adminTools: optionalService(),
displayUserNav: false,
init() {
this._super(...arguments);
this.displayUserNav = this.site.desktopView;
},
@discourseComputed("model.username") @discourseComputed("model.username")
viewingSelf(username) { viewingSelf(username) {
@ -186,6 +193,11 @@ export default Controller.extend(CanCheckEmails, {
} }
), ),
@action
toggleUserNav() {
this.toggleProperty("displayUserNav");
},
actions: { actions: {
collapseProfile() { collapseProfile() {
this.set("forceExpand", false); this.set("forceExpand", false);

View File

@ -59,14 +59,17 @@
<DButton @class="btn-primary compose-pm" @action={{route-action "composePrivateMessage" this.model}} @icon="envelope" @label="user.private_message" /> <DButton @class="btn-primary compose-pm" @action={{route-action "composePrivateMessage" this.model}} @icon="envelope" @label="user.private_message" />
</li> </li>
{{/if}} {{/if}}
{{#if this.canMuteOrIgnoreUser}} {{#if this.canMuteOrIgnoreUser}}
<li> <li>
<UserNotificationsDropdown @user={{this.model}} @value={{this.userNotificationLevel}} @updateNotificationLevel={{action "updateNotificationLevel"}} /> <UserNotificationsDropdown @user={{this.model}} @value={{this.userNotificationLevel}} @updateNotificationLevel={{action "updateNotificationLevel"}} />
</li> </li>
{{/if}} {{/if}}
{{#if this.currentUser.staff}}
{{#if (and this.site.desktopView this.currentUser.staff)}}
<li><a href={{this.model.adminPath}} class="btn btn-default">{{d-icon "wrench"}}<span class="d-button-label">{{i18n "admin.user.show_admin_profile"}}</span></a></li> <li><a href={{this.model.adminPath}} class="btn btn-default">{{d-icon "wrench"}}<span class="d-button-label">{{i18n "admin.user.show_admin_profile"}}</span></a></li>
{{/if}} {{/if}}
<PluginOutlet @name="user-profile-controls" @connectorTagName="li" @args={{hash model=this.model}} /> <PluginOutlet @name="user-profile-controls" @connectorTagName="li" @args={{hash model=this.model}} />
{{#if this.canExpandProfile}} {{#if this.canExpandProfile}}
@ -74,6 +77,12 @@
<DButton @ariaExpanded={{this.collapsedInfoState.isExpanded}} @ariaLabel={{this.collapsedInfoState.ariaLabel}} @ariaControls="collapsed-info-panel" @class="btn-default" @label={{concat "user." this.collapsedInfoState.label}} @icon={{this.collapsedInfoState.icon}} @action={{action this.collapsedInfoState.action}} /> <DButton @ariaExpanded={{this.collapsedInfoState.isExpanded}} @ariaLabel={{this.collapsedInfoState.ariaLabel}} @ariaControls="collapsed-info-panel" @class="btn-default" @label={{concat "user." this.collapsedInfoState.label}} @icon={{this.collapsedInfoState.icon}} @action={{action this.collapsedInfoState.action}} />
</li> </li>
{{/if}} {{/if}}
{{#if this.site.mobileView}}
<li>
<DButton @class="btn-default toggle-mobile-user-menu" @action={{this.toggleUserNav}} @icon="bars" @label="hamburger_menu" />
</li>
{{/if}}
</ul> </ul>
</section> </section>
@ -228,6 +237,8 @@
{{#if this.currentUser.redesigned_user_page_nav_enabled}} {{#if this.currentUser.redesigned_user_page_nav_enabled}}
<div class="new-user-wrapper"> <div class="new-user-wrapper">
<UserNav <UserNav
@shouldDisplay={{this.displayUserNav}}
@toggleUserNav={{this.toggleUserNav}}
@user={{this.model}} @user={{this.model}}
@showNotificationsTab={{this.showNotificationsTab}} @showNotificationsTab={{this.showNotificationsTab}}
@showPrivateMessages={{this.showPrivateMessages}} @showPrivateMessages={{this.showPrivateMessages}}
@ -238,9 +249,11 @@
@showDrafts={{this.showDrafts}} @showDrafts={{this.showDrafts}}
@showBookmarks={{this.showBookmarks}} /> @showBookmarks={{this.showBookmarks}} />
<div class="new-user-content-wrapper"> {{#if (or this.site.desktopView (not this.displayUserNav))}}
{{outlet}} <div class="new-user-content-wrapper">
</div> {{outlet}}
</div>
{{/if}}
</div> </div>
{{else}} {{else}}
<div class='user-content-wrapper'> <div class='user-content-wrapper'>

View File

@ -1,55 +1,8 @@
.new-user-wrapper { .new-user-wrapper {
.new-user-content-wrapper {
// Grid layout
width: 100%;
display: grid;
grid-template-columns: 1fr 5fr;
grid-template-rows: auto 1fr;
grid-gap: 20px;
.user-secondary-navigation {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 1;
grid-row-end: 2;
}
.user-content {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 3;
}
.user-additional-controls {
align-self: start;
justify-self: start;
grid-row-start: 2;
}
.user-secondary-navigation ~ .user-content {
grid-column-start: 2;
grid-column-end: 3;
}
}
.user-nav-dropdown-list-item {
position: relative;
}
.user-nav-dropdown-button { .user-nav-dropdown-button {
background: transparent; background: transparent;
} }
.user-nav-dropdown-submenu-wrapper {
position: absolute;
top: 2em;
min-width: 10em;
padding: 0;
box-shadow: shadow("dropdown");
z-index: z("dropdown");
}
.user-nav-dropdown-submenu { .user-nav-dropdown-submenu {
background: var(--secondary); background: var(--secondary);
list-style-type: none; list-style-type: none;
@ -58,6 +11,7 @@
li a { li a {
padding: 0.5em 1em; padding: 0.5em 1em;
color: var(--primary); color: var(--primary);
.discourse-no-touch & { .discourse-no-touch & {
&:hover { &:hover {
background: var(--highlight-medium); background: var(--highlight-medium);

View File

@ -11,6 +11,7 @@
@import "login"; @import "login";
@import "menu-panel"; @import "menu-panel";
@import "modal"; @import "modal";
@import "new-user";
@import "sidebar-section"; @import "sidebar-section";
@import "topic-list"; @import "topic-list";
@import "topic-post"; @import "topic-post";

View File

@ -0,0 +1,48 @@
.new-user-wrapper {
.new-user-content-wrapper {
// Grid layout
width: 100%;
display: grid;
grid-template-columns: 1fr 5fr;
grid-template-rows: auto 1fr;
grid-gap: 20px;
.user-secondary-navigation {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 1;
grid-row-end: 2;
}
.user-content {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 3;
}
.user-additional-controls {
align-self: start;
justify-self: start;
grid-row-start: 2;
}
.user-secondary-navigation ~ .user-content {
grid-column-start: 2;
grid-column-end: 3;
}
}
.user-nav-dropdown-list-item {
position: relative;
}
.user-nav-dropdown-submenu-wrapper {
position: absolute;
top: 2em;
min-width: 10em;
padding: 0;
box-shadow: shadow("dropdown");
z-index: z("dropdown");
}
}

View File

@ -19,6 +19,7 @@
@import "login"; @import "login";
@import "menu-panel"; @import "menu-panel";
@import "modal"; @import "modal";
@import "new-user";
@import "personal-message"; @import "personal-message";
@import "push-notifications-mobile"; @import "push-notifications-mobile";
@import "reviewables"; @import "reviewables";

View File

@ -0,0 +1,52 @@
.new-user-wrapper {
.user-nav {
flex-direction: column;
> li {
width: 100%;
.d-icon {
margin-right: 0.5em;
}
&:not(:first-of-type) {
border-top: 1px solid var(--primary-low);
}
> a,
button {
padding: 1em 0.75em;
width: 100%;
}
}
}
.user-nav-dropdown-list-item {
flex-direction: column;
}
.user-nav-dropdown-chevron {
margin-left: auto;
}
.user-nav-dropdown-button {
width: 100%;
}
.user-nav-dropdown-submenu {
box-sizing: border-box;
position: relative;
top: 0;
box-shadow: none;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(50%, 1fr));
padding: 0.5em 0 1em 1.65em;
li a {
box-sizing: border-box;
padding: 0.75em 1em;
width: 100%;
@include ellipsis;
}
}
}

View File

@ -5331,6 +5331,7 @@ en:
suspended: "Suspended?" suspended: "Suspended?"
staged: "Staged?" staged: "Staged?"
show_admin_profile: "Admin" show_admin_profile: "Admin"
manage_user: "Manage user"
show_public_profile: "Show Public Profile" show_public_profile: "Show Public Profile"
impersonate: "Impersonate" impersonate: "Impersonate"
action_logs: "Action Logs" action_logs: "Action Logs"