Refactor general UI state into a service

no issue
- moves general UI state control such as menu display, autonav, settings menu, etc into a `ui` service for easier use within components
- no longer required to jump through hoops passing state and actions down from application controller into components
- removes indirect "route" actions in favour of calling actions/methods directly on the `ui` service
This commit is contained in:
Kevin Ansfield 2017-08-14 13:30:00 +01:00 committed by Hannah Wolfe
parent b643d47054
commit 267ce40945
20 changed files with 153 additions and 130 deletions

View File

@ -1,15 +1,15 @@
import $ from 'jquery';
import Component from 'ember-component';
import observer from 'ember-metal/observer';
export default Component.extend({
classNames: ['gh-app'],
showSettingsMenu: false,
toggleSettingsMenuBodyClass: observer('showSettingsMenu', function () {
didReceiveAttrs() {
this._super(...arguments);
let showSettingsMenu = this.get('showSettingsMenu');
$('body').toggleClass('settings-menu-expanded', showSettingsMenu);
})
}
});

View File

@ -6,23 +6,25 @@ should be closed when the user clicks elsewhere.
Example:
```
{{gh-content-cover onClick="closeMenus" onMouseEnter="closeAutoNav"}}
{{gh-content-cover}}
```
**/
import Component from 'ember-component';
import {inject as injectService} from '@ember/service';
export default Component.extend({
ui: injectService(),
classNames: ['content-cover'],
onClick: null,
onMouseEnter: null,
click() {
this.sendAction('onClick');
this.get('ui').closeMenus();
},
mouseEnter() {
this.sendAction('onMouseEnter');
this.get('ui').closeAutoNav();
}
});

View File

@ -19,6 +19,10 @@ export default Component.extend({
isMobile: reads('mediaQueries.isMobile'),
maximise: false,
// closure actions
desktopAction() {},
mobileAction() {},
iconClass: computed('maximise', 'isMobile', function () {
if (this.get('maximise') && !this.get('isMobile')) {
return 'icon-maximise';
@ -29,10 +33,10 @@ export default Component.extend({
click() {
if (this.get('isMobile')) {
this.sendAction('mobileAction');
this.mobileAction();
} else {
this.toggleProperty('maximise');
this.sendAction('desktopAction');
this.desktopAction();
}
}
});

View File

@ -1,12 +1,9 @@
import Component from 'ember-component';
import {inject as injectService} from '@ember/service';
export default Component.extend({
tagName: 'nav',
classNames: ['gh-mobile-nav-bar'],
ui: injectService(),
actions: {
openMobileMenu() {
this.sendAction('openMobileMenu');
}
}
tagName: 'nav',
classNames: ['gh-mobile-nav-bar']
});

View File

@ -6,10 +6,11 @@ import {htmlSafe} from 'ember-string';
export default Component.extend({
config: injectService(),
session: injectService(),
ghostPaths: injectService(),
feature: injectService(),
ghostPaths: injectService(),
routing: injectService('-routing'),
session: injectService(),
ui: injectService(),
tagName: 'nav',
classNames: ['gh-nav'],
@ -74,20 +75,8 @@ export default Component.extend({
},
actions: {
toggleAutoNav() {
this.sendAction('toggleMaximise');
},
showMarkdownHelp() {
this.sendAction('showMarkdownHelp');
},
closeMobileMenu() {
this.sendAction('closeMobileMenu');
},
openAutoNav() {
this.sendAction('openAutoNav');
}
}
});

View File

@ -7,7 +7,6 @@ import injectService from 'ember-service/inject';
import moment from 'moment';
import run from 'ember-runloop';
import {guidFor} from 'ember-metal/utils';
import {invokeAction} from 'ember-invoke-action';
import {task, timeout} from 'ember-concurrency';
const PSM_ANIMATION_LENGTH = 400;
@ -23,6 +22,7 @@ export default Component.extend(SettingsMenuMixin, {
slugGenerator: injectService(),
session: injectService(),
settings: injectService(),
ui: injectService(),
model: null,
@ -495,14 +495,6 @@ export default Component.extend(SettingsMenuMixin, {
});
},
closeNavMenu() {
invokeAction(this, 'closeNavMenu');
},
closeMenus() {
invokeAction(this, 'closeMenus');
},
changeAuthor(newAuthor) {
let author = this.get('model.author');
let model = this.get('model');

View File

@ -1,12 +1,9 @@
import Component from 'ember-component';
import {inject as injectService} from '@ember/service';
export default Component.extend({
tagName: 'h2',
classNames: ['view-title'],
ui: injectService(),
actions: {
openMobileMenu() {
this.sendAction('openMobileMenu');
}
}
tagName: 'h2',
classNames: ['view-title']
});

View File

@ -1,11 +1,12 @@
import Controller from 'ember-controller';
import computed from 'ember-computed';
import injectService from 'ember-service/inject';
import Controller from '@ember/controller';
import {computed} from '@ember/object';
import {inject as injectService} from '@ember/service';
export default Controller.extend({
dropdown: injectService(),
session: injectService(),
settings: injectService(),
ui: injectService(),
showNavMenu: computed('currentPath', 'session.isAuthenticated', 'session.user.isFulfilled', function () {
// we need to defer showing the navigation menu until the session.user
@ -19,45 +20,11 @@ export default Controller.extend({
}),
topNotificationCount: 0,
showMobileMenu: false,
showSettingsMenu: false,
showMarkdownHelpModal: false,
autoNav: false,
autoNavOpen: computed('autoNav', {
get() {
return false;
},
set(key, value) {
if (this.get('autoNav')) {
return value;
}
return false;
}
}),
actions: {
topNotificationChange(count) {
this.set('topNotificationCount', count);
},
toggleAutoNav() {
this.toggleProperty('autoNav');
},
openAutoNav() {
this.set('autoNavOpen', true);
},
closeAutoNav() {
if (this.get('autoNavOpen')) {
this.get('dropdown').closeDropdowns();
}
this.set('autoNavOpen', false);
},
closeMobileMenu() {
this.set('showMobileMenu', false);
}
}
});

View File

@ -44,6 +44,7 @@ export default Mixin.create({
notifications: injectService(),
clock: injectService(),
slugGenerator: injectService(),
ui: injectService(),
wordcount: 0,
cards: [], // for apps
@ -587,14 +588,6 @@ export default Mixin.create({
}
},
closeNavMenu() {
this.get('application').send('closeAutoNav');
},
closeMenus() {
this.get('application').send('closeMenus');
},
toggleLeaveEditorModal(transition) {
let leaveTransition = this.get('leaveEditorTransition');
@ -673,10 +666,6 @@ export default Mixin.create({
setWordcount(wordcount) {
this.set('wordcount', wordcount);
},
toggleAutoNav() {
this.get('application').send('toggleAutoNav');
}
}
});

View File

@ -30,12 +30,12 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
config: injectService(),
feature: injectService(),
dropdown: injectService(),
lazyLoader: injectService(),
notifications: injectService(),
settings: injectService(),
upgradeNotification: injectService(),
tour: injectService(),
ui: injectService(),
beforeModel() {
return this.get('config').fetch();
@ -129,20 +129,8 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
},
actions: {
openMobileMenu() {
this.controller.set('showMobileMenu', true);
},
openSettingsMenu() {
this.controller.set('showSettingsMenu', true);
},
closeMenus() {
this.get('dropdown').closeDropdowns();
this.controller.setProperties({
showSettingsMenu: false,
showMobileMenu: false
});
this.get('ui').closeMenus();
},
didTransition() {

View File

@ -0,0 +1,88 @@
import Service from '@ember/service';
import injectService from 'ember-service/inject';
import {computed} from '@ember/object';
export default Service.extend({
dropdown: injectService(),
autoNav: false,
showMobileMenu: false,
showSettingsMenu: false,
autoNavOpen: computed('autoNav', {
get() {
return false;
},
set(key, value) {
if (this.get('autoNav')) {
return value;
}
return false;
}
}),
closeMenus() {
this.get('dropdown').closeDropdowns();
this.setProperties({
showSettingsMenu: false,
showMobileMenu: false
});
},
openAutoNav() {
this.set('autoNavOpen', true);
},
closeAutoNav() {
if (this.get('autoNavOpen')) {
this.get('dropdown').closeDropdowns();
}
this.set('autoNavOpen', false);
},
closeMobileMenu() {
this.set('showMobileMenu', false);
},
openMobileMenu() {
this.set('showMobileMenu', true);
},
openSettingsMenu() {
this.set('showSettingsMenu', true);
},
toggleAutoNav() {
this.toggleProperty('autoNav');
},
actions: {
closeMenus() {
this.closeMenus();
},
openAutoNav() {
this.openAutoNav();
},
closeAutoNav() {
this.closeAutoNav();
},
closeMobileMenu() {
this.closeMobileMenu();
},
openMobileMenu() {
this.openMobileMenu();
},
openSettingsMenu() {
this.openSettingsMenu();
},
toggleAutoNav() {
this.toggleAutoNav();
}
}
});

View File

@ -1,29 +1,26 @@
{{#gh-app showSettingsMenu=showSettingsMenu}}
{{#gh-app showSettingsMenu=ui.showSettingsMenu}}
{{#gh-skip-link anchor=".gh-main"}}Skip to main content{{/gh-skip-link}}
{{gh-alerts notify="topNotificationChange"}}
<div class="gh-viewport {{if autoNav 'gh-autonav'}} {{if showSettingsMenu 'settings-menu-expanded'}} {{if showMobileMenu 'mobile-menu-expanded'}}">
<div class="gh-viewport {{if ui.autoNav 'gh-autonav'}} {{if ui.showSettingsMenu 'settings-menu-expanded'}} {{if ui.showMobileMenu 'mobile-menu-expanded'}}">
{{#if showNavMenu}}
{{gh-nav-menu
open=autoNavOpen
open=ui.autoNavOpen
icon=settings.settledIcon
toggleMaximise="toggleAutoNav"
openAutoNav="openAutoNav"
showMarkdownHelp="toggleMarkdownHelpModal"
closeMobileMenu="closeMobileMenu"}}
showMarkdownHelp="toggleMarkdownHelpModal"}}
{{/if}}
{{#gh-main onMouseEnter="closeAutoNav" data-notification-count=topNotificationCount}}
{{#gh-main onMouseEnter=(action "closeAutoNav" target=ui) data-notification-count=topNotificationCount}}
{{outlet}}
{{/gh-main}}
{{gh-notifications}}
{{gh-content-cover onClick="closeMenus" onMouseEnter="closeAutoNav"}}
{{gh-content-cover}}
{{gh-mobile-nav-bar openMobileMenu="openMobileMenu"}}
{{gh-mobile-nav-bar}}
</div>{{!gh-viewport}}
{{/gh-app}}

View File

@ -5,5 +5,5 @@
{{#link-to "posts" classNames="gh-nav-main-content"}}{{inline-svg "content"}}Content{{/link-to}}
{{/if}}
{{#link-to "team" classNames="gh-nav-main-users"}}{{inline-svg "account-group"}}Team{{/link-to}}
<div class="gh-mobile-nav-bar-more" {{action "openMobileMenu"}}>{{inline-svg "icon" class="icon-gh"}}More</div>
<div class="gh-mobile-nav-bar-more" {{action "openMobileMenu" target=ui}}>{{inline-svg "icon" class="icon-gh"}}More</div>
{{yield}}

View File

@ -1,4 +1,4 @@
{{gh-menu-toggle desktopAction="toggleAutoNav" mobileAction="closeMobileMenu"}}
{{gh-menu-toggle desktopAction=(action "toggleAutoNav" target=ui) mobileAction=(action "closeMobileMenu" target=ui)}}
{{#gh-basic-dropdown horizontalPosition="right" calculatePosition=userDropdownPosition as |dropdown|}}
{{#dropdown.trigger tagName="header" class="gh-nav-menu"}}
<div class="gh-nav-menu-icon" style={{iconStyle}}></div>
@ -91,7 +91,7 @@
<footer class="gh-nav-foot">
<a class="gh-nav-foot-sitelink" href="{{config.blogUrl}}/" target="_blank">View site {{inline-svg "external"}}</a>
</footer>
<div class="gh-autonav-toggle" {{action "openAutoNav" on="mouseEnter"}}></div>
<div class="gh-autonav-toggle" {{action "openAutoNav" on="mouseEnter" target=ui}}></div>
{{gh-tour-item "getting-started"
target=".gh-menu-toggle"

View File

@ -3,7 +3,7 @@
<div class="{{if isViewingSubview 'settings-menu-pane-out-left' 'settings-menu-pane-in'}} settings-menu settings-menu-pane">
<div class="settings-menu-header">
<h4>Post Settings</h4>
<button class="close settings-menu-header-action" {{action "closeMenus"}} data-test-close-settings-menu>
<button class="close settings-menu-header-action" {{action "closeMenus" target=ui}} data-test-close-settings-menu>
{{inline-svg "close"}}<span class="hidden">Close</span>
</button>
</div>

View File

@ -1,2 +1,2 @@
<button {{action "openMobileMenu"}} class="gh-mobilemenu-button" role="presentation">{{inline-svg "icon" class="icon-gh"}}<span class="sr-only">Menu</span></button>
<button {{action "openMobileMenu" target=ui}} class="gh-mobilemenu-button" role="presentation">{{inline-svg "icon" class="icon-gh"}}<span class="sr-only">Menu</span></button>
{{yield}}

View File

@ -25,7 +25,7 @@
onOpen=(action "cancelAutosave")}}
{{/unless}}
<button type="button" class="post-settings" title="Settings" {{action "openSettingsMenu"}} data-test-psm-trigger>
<button type="button" class="post-settings" title="Settings" {{action "openSettingsMenu" target=ui}} data-test-psm-trigger>
{{inline-svg "settings"}}
</button>
</section>
@ -151,9 +151,7 @@
{{#liquid-wormhole}}
{{gh-post-settings-menu
model=model
showSettingsMenu=application.showSettingsMenu
closeNavMenu=(action "closeNavMenu")
closeMenus=(action "closeMenus")
showSettingsMenu=ui.showSettingsMenu
deletePost=(action "toggleDeletePostModal")
updateSlug=updateSlug
}}

View File

@ -1,6 +1,6 @@
<section class="gh-view">
<header class="view-header">
{{#gh-view-title openMobileMenu="openMobileMenu"}}<span>Tags</span>{{/gh-view-title}}
{{#gh-view-title}}<span>Tags</span>{{/gh-view-title}}
<section class="view-actions">
{{#link-to "settings.tags.new" class="gh-btn gh-btn-green"}}<span>New Tag</span>{{/link-to}}
</section>

View File

@ -1,6 +1,6 @@
<section class="gh-view">
<header class="view-header">
{{#gh-view-title openMobileMenu="openMobileMenu"}}<span>Tags</span>{{/gh-view-title}}
{{#gh-view-title}}<span>Tags</span>{{/gh-view-title}}
<section class="view-actions">
{{#link-to "settings.tags.new" class="gh-btn gh-btn-green"}}<span>New Tag</span>{{/link-to}}
</section>

View File

@ -0,0 +1,15 @@
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {setupTest} from 'ember-mocha';
describe('Unit: Service: ui', function() {
setupTest('service:ui', {
needs: ['service:dropdown']
});
// Replace this with your real tests.
it('exists', function() {
let service = this.subject();
expect(service).to.be.ok;
});
});