mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 06:35:49 +03:00
Drag & Drop Navigation Reordering
Closes #4540 - Implements drag & drop to reorder navigation items - Adds a `sort` property to navigation items - Adds a tiny library to enable touch events for drag & drop. It hooks onto jQuery UI. - Sort nav items before being saved - Adds `settings-view-navigation` to route for body class
This commit is contained in:
parent
f073e10221
commit
68eb6b67b0
@ -599,6 +599,7 @@ var _ = require('lodash'),
|
||||
'bower_components/ember-simple-auth/simple-auth-oauth2.js',
|
||||
'bower_components/google-caja/html-css-sanitizer-bundle.js',
|
||||
'bower_components/nanoscroller/bin/javascripts/jquery.nanoscroller.js',
|
||||
'bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.js',
|
||||
|
||||
'core/shared/lib/showdown/extensions/ghostimagepreview.js',
|
||||
'core/shared/lib/showdown/extensions/ghostgfm.js',
|
||||
@ -635,6 +636,7 @@ var _ = require('lodash'),
|
||||
'bower_components/ember-simple-auth/simple-auth-oauth2.js',
|
||||
'bower_components/google-caja/html-css-sanitizer-bundle.js',
|
||||
'bower_components/nanoscroller/bin/javascripts/jquery.nanoscroller.js',
|
||||
'bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.js',
|
||||
|
||||
'core/shared/lib/showdown/extensions/ghostimagepreview.js',
|
||||
'core/shared/lib/showdown/extensions/ghostgfm.js',
|
||||
|
@ -16,6 +16,7 @@
|
||||
"jquery-file-upload": "9.5.6",
|
||||
"jquery-hammerjs": "1.0.1",
|
||||
"jquery-ui": "1.10.4",
|
||||
"jqueryui-touch-punch": "furf/jquery-ui-touch-punch",
|
||||
"keymaster": "git://github.com/madrobby/keymaster#564ea42e07de40da8113a571f17ceae8802672ff",
|
||||
"loader.js": "git://github.com/stefanpenner/loader.js#1.0.0",
|
||||
"moment": "2.8.3",
|
||||
|
@ -394,18 +394,33 @@
|
||||
display: flex;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@media (min-width: 601px) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
// &.last-navigation-item {
|
||||
&:last-child {
|
||||
padding-left: 27px; // .navigation-item-drag-handle width + horizontal padding
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-item-drag-handle {
|
||||
padding-left: 10px;
|
||||
padding-right: 17px;
|
||||
padding: 6px 17px 0 10px;
|
||||
width: 27px;
|
||||
cursor: move;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 4px;
|
||||
margin-top: -9px;
|
||||
z-index: 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-inputs {
|
||||
@ -449,6 +464,7 @@
|
||||
padding-left: 10px;
|
||||
width: 40px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
||||
button {
|
||||
width: 30px;
|
||||
|
@ -187,6 +187,7 @@ select {
|
||||
width: 100%;
|
||||
border: 1px solid #E0DFD7;
|
||||
border-radius: $border-radius;
|
||||
-webkit-appearance: none;
|
||||
|
||||
font-size: 1.4rem;
|
||||
font-weight: normal;
|
||||
|
@ -1,6 +1,9 @@
|
||||
var NavItemComponent = Ember.Component.extend({
|
||||
classNames: 'navigation-item',
|
||||
|
||||
attributeBindings: ['order:data-order'],
|
||||
order: Ember.computed.readOnly('navItem.order'),
|
||||
|
||||
keyPress: function (event) {
|
||||
// enter key
|
||||
if (event.keyCode === 13) {
|
||||
|
@ -4,6 +4,7 @@ var NavigationController,
|
||||
NavItem = Ember.Object.extend({
|
||||
label: '',
|
||||
url: '',
|
||||
order: '',
|
||||
|
||||
isComplete: Ember.computed('label', 'url', function () {
|
||||
return !(Ember.isBlank(this.get('label')) || Ember.isBlank(this.get('url')));
|
||||
@ -31,6 +32,8 @@ NavigationController = Ember.Controller.extend({
|
||||
return NavItem.create(item);
|
||||
});
|
||||
|
||||
navItems.sortBy('order');
|
||||
|
||||
lastItem = navItems.get('lastObject');
|
||||
if (!lastItem || lastItem.get('isComplete')) {
|
||||
navItems.addObject(NavItem.create());
|
||||
@ -51,13 +54,24 @@ NavigationController = Ember.Controller.extend({
|
||||
});
|
||||
}),
|
||||
|
||||
updateOrder: function (indexes) {
|
||||
var navItems = this.get('navigationItems'),
|
||||
order = 0;
|
||||
|
||||
indexes.forEach(function (index) {
|
||||
navItems[index].set('order', order);
|
||||
order = order + 1; // Increment order order by one
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
addItem: function () {
|
||||
var navItems = this.get('navigationItems'),
|
||||
lastItem = navItems.get('lastObject');
|
||||
|
||||
if (lastItem && lastItem.get('isComplete')) {
|
||||
navItems.addObject(NavItem.create());
|
||||
lastItem.set('order', (navItems.length - 1)); // -1 because order is 0-index, length is 1-index
|
||||
navItems.addObject(NavItem.create()); // Adds new blank navItem
|
||||
}
|
||||
},
|
||||
|
||||
@ -67,6 +81,14 @@ NavigationController = Ember.Controller.extend({
|
||||
}
|
||||
|
||||
this.get('navigationItems').removeObject(item);
|
||||
|
||||
var navItems = this.get('navigationItems'),
|
||||
order = 0;
|
||||
|
||||
navItems.forEach(function (item) {
|
||||
item.set('order', order);
|
||||
order = order + 1; // Increment order order by one
|
||||
});
|
||||
},
|
||||
|
||||
updateUrl: function (url, navItem) {
|
||||
@ -92,7 +114,8 @@ NavigationController = Ember.Controller.extend({
|
||||
|
||||
navSetting = this.get('navigationItems').map(function (item) {
|
||||
var label,
|
||||
url;
|
||||
url,
|
||||
order;
|
||||
|
||||
if (!item || !item.get('isComplete')) {
|
||||
return;
|
||||
@ -100,6 +123,7 @@ NavigationController = Ember.Controller.extend({
|
||||
|
||||
label = item.get('label').trim();
|
||||
url = item.get('url').trim();
|
||||
order = item.get('order');
|
||||
|
||||
match = url.match(blogUrlRegex);
|
||||
|
||||
@ -118,9 +142,14 @@ NavigationController = Ember.Controller.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
return {label: label, url: url};
|
||||
return {label: label, url: url, order: order};
|
||||
}).compact();
|
||||
|
||||
// Sort JSON so nav items are stored in the correct order order
|
||||
navSetting.sort(function (a, b) {
|
||||
return a.order - b.order;
|
||||
});
|
||||
|
||||
this.set('model.navigation', JSON.stringify(navSetting));
|
||||
|
||||
// trigger change event because even if the final JSON is unchanged
|
||||
|
@ -1,10 +1,13 @@
|
||||
import AuthenticatedRoute from 'ghost/routes/authenticated';
|
||||
import CurrentUserSettings from 'ghost/mixins/current-user-settings';
|
||||
import styleBody from 'ghost/mixins/style-body';
|
||||
|
||||
var NavigationRoute = AuthenticatedRoute.extend(CurrentUserSettings, {
|
||||
var NavigationRoute = AuthenticatedRoute.extend(styleBody, CurrentUserSettings, {
|
||||
|
||||
titleToken: 'Navigation',
|
||||
|
||||
classNames: ['settings-view-navigation'],
|
||||
|
||||
beforeModel: function () {
|
||||
if (!this.get('config.navigationUI')) {
|
||||
return this.transitionTo('settings.general');
|
||||
|
@ -1,6 +1,8 @@
|
||||
<button type="button" class="navigation-item-drag-handle icon-grab">
|
||||
<span class="hidden">Reorder</span>
|
||||
</button>
|
||||
{{#unless navItem.last}}
|
||||
<span class="navigation-item-drag-handle icon-grab">
|
||||
<span class="hidden">Reorder</span>
|
||||
</span>
|
||||
{{/unless}}
|
||||
<div class="navigation-inputs">
|
||||
<span class="navigation-item-label">
|
||||
{{gh-trim-focus-input focus=navItem.last placeholder="Label" value=navItem.label}}
|
||||
|
@ -7,7 +7,7 @@
|
||||
</header>
|
||||
|
||||
<section class="content settings-navigation">
|
||||
<form id="settings-navigation" novalidate="novalidate">
|
||||
<form id="settings-navigation" class="js-settings-navigation" novalidate="novalidate">
|
||||
{{#each navItem in navigationItems}}
|
||||
{{gh-navitem navItem=navItem baseUrl=blogUrl addItem="addItem" deleteItem="deleteItem" updateUrl="updateUrl"}}
|
||||
{{/each}}
|
||||
|
@ -1,6 +1,31 @@
|
||||
import BaseView from 'ghost/views/settings/content-base';
|
||||
|
||||
var SettingsNavigationView = BaseView.extend({
|
||||
|
||||
didInsertElement: function () {
|
||||
var controller = this.get('controller'),
|
||||
navContainer = Ember.$('.js-settings-navigation'),
|
||||
navElements = '.navigation-item:not(.navigation-item:last-child)';
|
||||
|
||||
navContainer.sortable({
|
||||
handle: '.navigation-item-drag-handle',
|
||||
items: navElements,
|
||||
|
||||
update: function () {
|
||||
var indexes = [];
|
||||
navContainer.find(navElements).each(function () {
|
||||
var order = Ember.$(this).data('order');
|
||||
indexes.push(order);
|
||||
});
|
||||
controller.updateOrder(indexes);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
willDestroyElement: function () {
|
||||
Ember.$('.js-settings-navigation').sortable('destroy');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
export default SettingsNavigationView;
|
||||
|
@ -72,7 +72,7 @@
|
||||
"defaultValue": "{}"
|
||||
},
|
||||
"navigation": {
|
||||
"defaultValue": "[{\"label\":\"Home\", \"url\":\"/\"}]"
|
||||
"defaultValue": "[{\"label\":\"Home\", \"url\":\"/\", \"order\":0}]"
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
|
Loading…
Reference in New Issue
Block a user