mirror of
https://github.com/TryGhost/Ghost-Admin.git
synced 2023-12-14 02:33:04 +01:00
bring keyboard navigation back to the content screen
refs https://github.com/TryGhost/Ghost/issues/7860 - restores the previous up/down/enter/cmd+backspace functionality - modifies the `delete-post` modal to accept a hash with an `onSuccess` action
This commit is contained in:
parent
ffd055c422
commit
6d26685b65
6 changed files with 101 additions and 14 deletions
|
@ -14,9 +14,10 @@ const ObjectPromiseProxy = ObjectProxy.extend(PromiseProxyMixin);
|
|||
export default Component.extend({
|
||||
tagName: 'li',
|
||||
classNames: ['gh-posts-list-item'],
|
||||
classNameBindings: ['active'],
|
||||
|
||||
post: null,
|
||||
previewIsHidden: false,
|
||||
active: false,
|
||||
|
||||
isFeatured: alias('post.featured'),
|
||||
isPage: alias('post.page'),
|
||||
|
@ -63,11 +64,40 @@ export default Component.extend({
|
|||
return htmlSafe(`${text.slice(0, 80)}…`);
|
||||
}),
|
||||
|
||||
didReceiveAttrs() {
|
||||
if (this.get('active')) {
|
||||
this.scrollIntoView();
|
||||
}
|
||||
},
|
||||
|
||||
click() {
|
||||
this.sendAction('onClick', this.get('post'));
|
||||
},
|
||||
|
||||
doubleClick() {
|
||||
this.sendAction('onDoubleClick', this.get('post'));
|
||||
},
|
||||
|
||||
scrollIntoView() {
|
||||
let element = this.$();
|
||||
let offset = element.offset().top;
|
||||
let elementHeight = element.height();
|
||||
let container = $('.content-list');
|
||||
let containerHeight = container.height();
|
||||
let currentScroll = container.scrollTop();
|
||||
let isBelowTop, isAboveBottom, isOnScreen;
|
||||
|
||||
isAboveBottom = offset < containerHeight;
|
||||
isBelowTop = offset > elementHeight;
|
||||
|
||||
isOnScreen = isBelowTop && isAboveBottom;
|
||||
|
||||
if (!isOnScreen) {
|
||||
// Scroll so that element is centered in container
|
||||
// 40 is the amount of padding on the container
|
||||
container.clearQueue().animate({
|
||||
scrollTop: currentScroll + offset - 40 - containerHeight / 2
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -5,7 +5,8 @@ import {task} from 'ember-concurrency';
|
|||
|
||||
export default ModalComponent.extend({
|
||||
|
||||
post: alias('model'),
|
||||
post: alias('model.post'),
|
||||
onSuccess: alias('model.onSuccess'),
|
||||
|
||||
notifications: injectService(),
|
||||
routing: injectService('-routing'),
|
||||
|
@ -24,8 +25,10 @@ export default ModalComponent.extend({
|
|||
// clear any previous error messages
|
||||
this.get('notifications').closeAlerts('post.delete');
|
||||
|
||||
// redirect to content screen
|
||||
this.get('routing').transitionTo('posts');
|
||||
// trigger the success action
|
||||
if (this.get('onSuccess')) {
|
||||
this.get('onSuccess')();
|
||||
}
|
||||
},
|
||||
|
||||
_failure(error) {
|
||||
|
|
|
@ -49,6 +49,10 @@ export default AuthenticatedRoute.extend(base, {
|
|||
actions: {
|
||||
authorizationFailed() {
|
||||
this.get('controller').send('toggleReAuthenticateModal');
|
||||
},
|
||||
|
||||
redirectToContentScreen() {
|
||||
this.transitionTo('posts');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ export default AuthenticatedRoute.extend(InfinityRoute, ShortcutsRoute, {
|
|||
totalPagesParam: 'meta.pagination.pages',
|
||||
|
||||
_type: null,
|
||||
_selectedPostIndex: null,
|
||||
|
||||
model(params) {
|
||||
this.set('_type', params.type);
|
||||
|
@ -60,10 +61,18 @@ export default AuthenticatedRoute.extend(InfinityRoute, ShortcutsRoute, {
|
|||
}),
|
||||
|
||||
stepThroughPosts(step) {
|
||||
let currentPost = this.get('controller.currentPost');
|
||||
let posts = this.get('controller.sortedPosts');
|
||||
let currentPost = this.get('controller.selectedPost');
|
||||
let posts = this.get('controller.model');
|
||||
let length = posts.get('length');
|
||||
let newPosition = posts.indexOf(currentPost) + step;
|
||||
let newPosition;
|
||||
|
||||
// when the currentPost is deleted we won't be able to use indexOf.
|
||||
// we keep track of the index locally so we can select next after deletion
|
||||
if (this._selectedPostIndex !== null && length) {
|
||||
newPosition = this._selectedPostIndex + step;
|
||||
} else {
|
||||
newPosition = posts.indexOf(currentPost) + step;
|
||||
}
|
||||
|
||||
// if we are on the first or last item
|
||||
// just do nothing (desired behavior is to not
|
||||
|
@ -74,20 +83,40 @@ export default AuthenticatedRoute.extend(InfinityRoute, ShortcutsRoute, {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO: highlight post
|
||||
// this.transitionTo('posts.post', posts.objectAt(newPosition));
|
||||
this._selectedPostIndex = newPosition;
|
||||
this.set('controller.selectedPost', posts.objectAt(newPosition));
|
||||
},
|
||||
|
||||
shortcuts: {
|
||||
'up, k': 'moveUp',
|
||||
'down, j': 'moveDown',
|
||||
c: 'newPost'
|
||||
'enter': 'editPost',
|
||||
'c': 'newPost',
|
||||
'command+backspace, ctrl+backspace': 'deletePost'
|
||||
},
|
||||
|
||||
resetController() {
|
||||
this.set('controller.selectedPost', null);
|
||||
this.set('controller.showDeletePostModal', false);
|
||||
},
|
||||
|
||||
actions: {
|
||||
willTransition() {
|
||||
this._selectedPostIndex = null;
|
||||
|
||||
if (this.get('controller')) {
|
||||
this.resetController();
|
||||
}
|
||||
},
|
||||
|
||||
queryParamsDidChange() {
|
||||
this.refresh();
|
||||
// reset the scroll position
|
||||
// on direct page load controller won't exist so we want to
|
||||
// avoid a double transition
|
||||
if (this.get('controller')) {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
// scroll back to the top
|
||||
$('.content-list').scrollTop(0);
|
||||
},
|
||||
|
||||
|
@ -101,6 +130,23 @@ export default AuthenticatedRoute.extend(InfinityRoute, ShortcutsRoute, {
|
|||
|
||||
moveDown() {
|
||||
this.stepThroughPosts(1);
|
||||
},
|
||||
|
||||
editPost() {
|
||||
let selectedPost = this.get('controller.selectedPost');
|
||||
|
||||
if (selectedPost) {
|
||||
this.transitionTo('editor.edit', selectedPost.get('id'));
|
||||
}
|
||||
},
|
||||
|
||||
deletePost() {
|
||||
this.get('controller').send('toggleDeletePostModal');
|
||||
},
|
||||
|
||||
onPostDeletion() {
|
||||
// select next post (re-select the current index)
|
||||
this.stepThroughPosts(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
{{#if showDeletePostModal}}
|
||||
{{gh-fullscreen-modal "delete-post"
|
||||
model=model
|
||||
model=(hash post=model onSuccess=(route-action 'redirectToContentScreen'))
|
||||
close=(action "toggleDeletePostModal")
|
||||
modifier="action wide"}}
|
||||
{{/if}}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
{{#each model as |post|}}
|
||||
{{gh-posts-list-item
|
||||
post=post
|
||||
active=(eq post selectedPost)
|
||||
onDoubleClick="openEditor"
|
||||
data-test-posts-list-item-id=post.id}}
|
||||
{{else}}
|
||||
|
@ -28,7 +29,10 @@
|
|||
|
||||
{{#if showDeletePostModal}}
|
||||
{{gh-fullscreen-modal "delete-post"
|
||||
model=currentPost
|
||||
model=(hash
|
||||
post=selectedPost
|
||||
onSuccess=(route-action 'onPostDeletion')
|
||||
)
|
||||
close=(action "toggleDeletePostModal")
|
||||
modifier="action wide"}}
|
||||
{{/if}}
|
||||
|
|
Loading…
Reference in a new issue