mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-14 09:52:09 +03:00
Merge pull request #5996 from kevinansfield/routable-tags-mobile
Mobile fixes for tag management UI
This commit is contained in:
commit
237c552735
60
core/client/app/components/gh-tags-management-container.js
Normal file
60
core/client/app/components/gh-tags-management-container.js
Normal file
@ -0,0 +1,60 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {isBlank} = Ember;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['view-container'],
|
||||
classNameBindings: ['isMobile'],
|
||||
|
||||
mobileWidth: 600,
|
||||
tags: null,
|
||||
selectedTag: null,
|
||||
|
||||
isMobile: false,
|
||||
isEmpty: Ember.computed.equal('tags.length', 0),
|
||||
|
||||
resizeService: Ember.inject.service('resize-service'),
|
||||
|
||||
_resizeListener: null,
|
||||
|
||||
displaySettingsPane: Ember.computed('isEmpty', 'selectedTag', 'isMobile', function () {
|
||||
const isEmpty = this.get('isEmpty'),
|
||||
selectedTag = this.get('selectedTag'),
|
||||
isMobile = this.get('isMobile');
|
||||
|
||||
// always display settings pane for blank-slate on mobile
|
||||
if (isMobile && isEmpty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// display list if no tag is selected on mobile
|
||||
if (isMobile && isBlank(selectedTag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// default to displaying settings pane
|
||||
return true;
|
||||
}),
|
||||
|
||||
toggleMobile: function () {
|
||||
let width = Ember.$(window).width();
|
||||
|
||||
if (width < this.get('mobileWidth')) {
|
||||
this.set('isMobile', true);
|
||||
this.sendAction('enteredMobile');
|
||||
} else {
|
||||
this.set('isMobile', false);
|
||||
this.sendAction('leftMobile');
|
||||
}
|
||||
},
|
||||
|
||||
didInitAttrs: function () {
|
||||
this._resizeListener = Ember.run.bind(this, this.toggleMobile);
|
||||
this.get('resizeService').on('debouncedDidResize', this._resizeListener);
|
||||
this.toggleMobile();
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
this.get('resizeService').off('debouncedDidResize', this._resizeListener);
|
||||
}
|
||||
});
|
@ -1,11 +1,23 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {computed, inject} = Ember,
|
||||
{alias, equal, sort} = computed;
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
tagListFocused: Ember.computed.equal('keyboardFocus', 'tagList'),
|
||||
tagContentFocused: Ember.computed.equal('keyboardFocus', 'tagContent'),
|
||||
tagController: inject.controller('settings.tags.tag'),
|
||||
|
||||
tags: Ember.computed.sort('model', function (a, b) {
|
||||
// set at controller level because it's shared by routes and components
|
||||
mobileWidth: 600,
|
||||
|
||||
isMobile: false,
|
||||
selectedTag: alias('tagController.tag'),
|
||||
|
||||
tagListFocused: equal('keyboardFocus', 'tagList'),
|
||||
tagContentFocused: equal('keyboardFocus', 'tagContent'),
|
||||
|
||||
// TODO: replace with ordering by page count once supported by the API
|
||||
tags: sort('model', function (a, b) {
|
||||
const idA = +a.get('id'),
|
||||
idB = +b.get('id');
|
||||
|
||||
@ -16,6 +28,22 @@ export default Ember.Controller.extend({
|
||||
}
|
||||
|
||||
return 0;
|
||||
})
|
||||
}),
|
||||
|
||||
actions: {
|
||||
enteredMobile: function () {
|
||||
this.set('isMobile', true);
|
||||
},
|
||||
|
||||
leftMobile: function () {
|
||||
this.set('isMobile', false);
|
||||
|
||||
// redirect to first tag if possible so that you're not left with
|
||||
// tag settings blank slate when switching from portrait to landscape
|
||||
if (this.get('tags.length') && !this.get('tagController.tag')) {
|
||||
this.transitionToRoute('settings.tags.tag', this.get('tags.firstObject'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -1,11 +1,14 @@
|
||||
import Ember from 'ember';
|
||||
|
||||
const {computed} = Ember,
|
||||
const {computed, inject} = Ember,
|
||||
{alias} = computed;
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
tag: alias('model'),
|
||||
isMobile: alias('tagsController.isMobile'),
|
||||
|
||||
tagsController: inject.controller('settings.tags'),
|
||||
|
||||
saveTagProperty: function (propKey, newValue) {
|
||||
const tag = this.get('tag'),
|
||||
|
@ -1,11 +1,15 @@
|
||||
import Ember from 'ember';
|
||||
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
||||
|
||||
export default AuthenticatedRoute.extend({
|
||||
|
||||
// HACK: ugly way of changing behaviour when on mobile
|
||||
beforeModel: function () {
|
||||
const firstTag = this.modelFor('settings.tags').get('firstObject');
|
||||
const firstTag = this.modelFor('settings.tags').get('firstObject'),
|
||||
mobileWidth = this.controllerFor('settings.tags').get('mobileWidth'),
|
||||
viewportWidth = Ember.$(window).width();
|
||||
|
||||
if (firstTag) {
|
||||
if (firstTag && viewportWidth > mobileWidth) {
|
||||
this.transitionTo('settings.tags.tag', firstTag);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,11 @@ export default AuthenticatedRoute.extend({
|
||||
|
||||
renderTemplate: function () {
|
||||
this.render('settings.tags.tag');
|
||||
},
|
||||
|
||||
// reset the model so that mobile screens react to an empty selectedTag
|
||||
deactivate: function () {
|
||||
this.set('controller.model', null);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -4,6 +4,11 @@ export default AuthenticatedRoute.extend({
|
||||
|
||||
model: function (params) {
|
||||
return this.store.findRecord('tag', params.tag_id);
|
||||
},
|
||||
|
||||
// reset the model so that mobile screens react to an empty selectedTag
|
||||
deactivate: function () {
|
||||
this.set('controller.model', null);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -6,7 +6,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.gh-flow-head {
|
||||
|
@ -1,12 +1,21 @@
|
||||
/* Global Layout
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
Ember's app container, set height so that .gh-app and .gh-viewport
|
||||
don't need to use 100vh where bottom of screen gets covered by iOS menus
|
||||
http://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile-browsers.html
|
||||
*/
|
||||
body > .ember-view {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Main viewport, contains main content, and alerts */
|
||||
.gh-app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Content viewport, contains everything else */
|
||||
@ -14,7 +23,7 @@
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
max-height: 100vh;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.gh-main {
|
||||
|
@ -76,6 +76,17 @@
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tag-list {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-tag .tag-edit-button.active {
|
||||
border-left: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tag Settings (Right pane)
|
||||
/* ---------------------------------------------------------- */
|
||||
|
||||
@ -94,6 +105,29 @@
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.tag-settings .no-posts {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.tag-settings .no-posts h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tag-settings .settings-menu-pane {
|
||||
transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tag-settings {
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1);
|
||||
transform: translate3d(100%, 0px, 0px);
|
||||
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
.tag-settings-in {
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
<div class="{{if isViewingSubview 'settings-menu-pane-out-left' 'settings-menu-pane-in'}} settings-menu settings-menu-pane tag-settings-pane">
|
||||
<div class="settings-menu-header">
|
||||
<h4>{{title}}</h4>
|
||||
<div class="settings-menu-header {{if isMobile 'subview'}}">
|
||||
{{#if isMobile}}
|
||||
{{#link-to 'settings.tags' class="back icon-arrow-left settings-menu-header-action"}}<span class="hidden">Back</span>{{/link-to}}
|
||||
<h4>{{title}}</h4>
|
||||
<div style="width:23px;">{{!flexbox space-between}}</div>
|
||||
{{else}}
|
||||
<h4>{{title}}</h4>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="settings-menu-content">
|
||||
{{gh-uploader uploaded="setCoverImage" canceled="clearCoverImage" description="Add tag image" image=tag.image initUploader="setUploaderReference" tagName="section"}}
|
||||
|
@ -0,0 +1 @@
|
||||
{{yield this}}
|
@ -7,7 +7,7 @@
|
||||
</section>
|
||||
</header>
|
||||
|
||||
<div class="view-container">
|
||||
{{#gh-tags-management-container mobileWidth=mobileWidth tags=tags selectedTag=selectedTag enteredMobile="enteredMobile" leftMobile="leftMobile" as |container|}}
|
||||
{{#gh-infinite-scroll
|
||||
fetch="loadNextPage"
|
||||
isLoading=isLoading
|
||||
@ -26,8 +26,8 @@
|
||||
{{/each}}
|
||||
</section>
|
||||
{{/gh-infinite-scroll}}
|
||||
<section class="settings-menu-container tag-settings {{if tagContentFocused 'keyboard-focused'}}">
|
||||
<section class="settings-menu-container tag-settings {{if tagContentFocused 'keyboard-focused'}} {{if container.displaySettingsPane 'tag-settings-in'}}">
|
||||
{{outlet}}
|
||||
</section>
|
||||
</div>
|
||||
{{/gh-tags-management-container}}
|
||||
</section>
|
||||
|
@ -1 +1 @@
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action "setProperty") openModal="openModal"}}
|
||||
{{gh-tag-settings-form tag=tag setProperty=(action "setProperty") openModal="openModal" isMobile=isMobile}}
|
||||
|
@ -0,0 +1,43 @@
|
||||
/* jshint expr:true */
|
||||
import { expect } from 'chai';
|
||||
import {
|
||||
describeComponent,
|
||||
it
|
||||
} from 'ember-mocha';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import Ember from 'ember';
|
||||
|
||||
const resizeStub = Ember.Service.extend(Ember.Evented, {
|
||||
|
||||
});
|
||||
|
||||
describeComponent(
|
||||
'gh-tags-management-container',
|
||||
'Integration: Component: gh-tags-management-container',
|
||||
{
|
||||
integration: true
|
||||
},
|
||||
function () {
|
||||
beforeEach(function () {
|
||||
this.register('service:resize-service', resizeStub);
|
||||
this.inject.service('resize-service', {as: 'resize-service'});
|
||||
});
|
||||
|
||||
it('renders', function () {
|
||||
this.set('mobileWidth', 600);
|
||||
this.set('tags', []);
|
||||
this.set('selectedTag', null);
|
||||
this.on('enteredMobile', function () {
|
||||
// noop
|
||||
});
|
||||
this.on('leftMobile', function () {
|
||||
// noop
|
||||
});
|
||||
|
||||
this.render(hbs`
|
||||
{{#gh-tags-management-container mobileWidth=mobileWidth tags=tags selectedTag=selectedTag enteredMobile="enteredMobile" leftMobile="leftMobile"}}{{/gh-tags-management-container}}
|
||||
`);
|
||||
expect(this.$()).to.have.length(1);
|
||||
});
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue
Block a user