diff --git a/app/assets/stylesheets/pageflow/editor/base.scss b/app/assets/stylesheets/pageflow/editor/base.scss
index 1071d381a8..33d80bc97a 100644
--- a/app/assets/stylesheets/pageflow/editor/base.scss
+++ b/app/assets/stylesheets/pageflow/editor/base.scss
@@ -61,6 +61,7 @@
@import "./drop_down_button";
@import "./outline";
+ @import "./sortable";
@import "./failures";
@import "./menu";
@import "./inputs";
diff --git a/app/assets/stylesheets/pageflow/editor/drop_down_button.scss b/app/assets/stylesheets/pageflow/editor/drop_down_button.scss
index 3ca5439806..81ee6b55dc 100644
--- a/app/assets/stylesheets/pageflow/editor/drop_down_button.scss
+++ b/app/assets/stylesheets/pageflow/editor/drop_down_button.scss
@@ -12,7 +12,13 @@
> button.ellipsis_icon {
@include fa-ellipsis-v-icon;
- width: 31px;
+ width: var(--drop-down-button-width, 31px);
+ }
+
+ > button.borderless {
+ --ui-button-border-color: transparent;
+ --ui-button-hover-border-color: transparent;
+ --ui-button-focus-ring-color: transparent;
}
&.full_width {
diff --git a/app/assets/stylesheets/pageflow/editor/list.scss b/app/assets/stylesheets/pageflow/editor/list.scss
index d9ed69cf6b..cb0b1d58cf 100644
--- a/app/assets/stylesheets/pageflow/editor/list.scss
+++ b/app/assets/stylesheets/pageflow/editor/list.scss
@@ -1,6 +1,7 @@
.list {
.list_items {
margin-bottom: 10px;
+ user-select: none;
}
.list_blank_slate {
@@ -104,8 +105,4 @@
@include icon-only-button("destructive");
@include trash-icon;
}
-
- .sortable-placeholder {
- height: 35px;
- }
}
diff --git a/app/assets/stylesheets/pageflow/editor/outline.scss b/app/assets/stylesheets/pageflow/editor/outline.scss
index 6eec9705d2..03e2275be1 100644
--- a/app/assets/stylesheets/pageflow/editor/outline.scss
+++ b/app/assets/stylesheets/pageflow/editor/outline.scss
@@ -99,10 +99,6 @@ ul.outline {
&.hide_in_navigation a {
color: var(--ui-on-surface-color-light);
}
-
- &.ui-sortable-helper {
- box-shadow: var(--ui-box-shadow-xl);
- }
}
}
@@ -122,10 +118,6 @@ ul.chapters {
font-weight: bold;
}
}
-
- > .sortable-placeholder {
- border: 0;
- }
}
ul.pages {
@@ -145,8 +137,3 @@ ul.dragged {
margin: 0;
padding: 0;
}
-
-.sortable-placeholder {
- outline: 1px dotted var(--ui-on-surface-color-lighter);
- border-radius: rounded();
-}
diff --git a/app/assets/stylesheets/pageflow/editor/sortable.scss b/app/assets/stylesheets/pageflow/editor/sortable.scss
new file mode 100644
index 0000000000..8228631a59
--- /dev/null
+++ b/app/assets/stylesheets/pageflow/editor/sortable.scss
@@ -0,0 +1,12 @@
+.sortable-placeholder {
+ outline: 1px dotted var(--ui-on-surface-color-lighter);
+
+ // scss-lint:disable ImportantRule
+ border-color: transparent !important;
+ background-color: transparent !important;
+ // scss-lint:enable ImportantRule
+
+ * {
+ visibility: hidden;
+ }
+}
diff --git a/app/assets/stylesheets/pageflow/mixins/buttons.scss b/app/assets/stylesheets/pageflow/mixins/buttons.scss
index 7f5c8af85a..8c5577f10c 100644
--- a/app/assets/stylesheets/pageflow/mixins/buttons.scss
+++ b/app/assets/stylesheets/pageflow/mixins/buttons.scss
@@ -49,8 +49,8 @@
}
} @else {
background-color: transparent;
- border: solid 1px var(--ui-primary-color-light);
- color: var(--ui-primary-color);
+ border: solid 1px var(--ui-button-border-color);
+ color: var(--ui-on-button-color);
@if $type == "destructive" {
&:hover:not(:disabled):not(.disabled) {
@@ -61,7 +61,7 @@
} @else {
&.hover:not(:disabled):not(.disabled),
&:hover:not(:disabled):not(.disabled) {
- border: 1px solid var(--ui-primary-color);
+ border-color: var(--ui-button-hover-border-color);
}
}
@@ -78,6 +78,6 @@
&:active:not(:disabled):not(.disabled),
&:focus:not(:disabled):not(.disabled) {
- box-shadow: 0 0 0 2px var(--ui-primary-color-lighter);
+ box-shadow: 0 0 0 2px var(--ui-button-focus-ring-color);
}
}
diff --git a/app/assets/stylesheets/pageflow/ui/properties.scss b/app/assets/stylesheets/pageflow/ui/properties.scss
index 84117e897e..c598b9f5cb 100644
--- a/app/assets/stylesheets/pageflow/ui/properties.scss
+++ b/app/assets/stylesheets/pageflow/ui/properties.scss
@@ -41,4 +41,9 @@
--ui-box-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--ui-box-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
--ui-box-shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
+
+ --ui-on-button-color: var(--ui-primary-color);
+ --ui-button-border-color: var(--ui-primary-color-light);
+ --ui-button-hover-border-color: var(--ui-primary-color);
+ --ui-button-focus-ring-color: var(--ui-primary-color-lighter);
}
diff --git a/entry_types/scrolled/config/locales/new/reorder_chapters.de.yml b/entry_types/scrolled/config/locales/new/reorder_chapters.de.yml
new file mode 100644
index 0000000000..2243e94b7b
--- /dev/null
+++ b/entry_types/scrolled/config/locales/new/reorder_chapters.de.yml
@@ -0,0 +1,7 @@
+de:
+ pageflow_scrolled:
+ editor:
+ entry_outline:
+ reorder_chapters: "Kapitel umsortieren..."
+ reorder_chapters_header: "Kapitel umsortieren"
+ done: Fertig
diff --git a/entry_types/scrolled/config/locales/new/reorder_chapters.en.yml b/entry_types/scrolled/config/locales/new/reorder_chapters.en.yml
new file mode 100644
index 0000000000..bb11f6fb70
--- /dev/null
+++ b/entry_types/scrolled/config/locales/new/reorder_chapters.en.yml
@@ -0,0 +1,7 @@
+en:
+ pageflow_scrolled:
+ editor:
+ entry_outline:
+ reorder_chapters: "Reorder chapters..."
+ reorder_chapters_header: "Reorder chapters"
+ done: Done
diff --git a/entry_types/scrolled/package/src/editor/views/ChapterItemView.module.css b/entry_types/scrolled/package/src/editor/views/ChapterItemView.module.css
index 7cc075a10e..42bb293090 100644
--- a/entry_types/scrolled/package/src/editor/views/ChapterItemView.module.css
+++ b/entry_types/scrolled/package/src/editor/views/ChapterItemView.module.css
@@ -9,10 +9,6 @@
border-radius: rounded(lg);
}
-.root:global(.ui-sortable-helper) {
- box-shadow: var(--ui-box-shadow-xl);
-}
-
.selectableHover {
background-color: var(--ui-selection-color-lighter);
}
@@ -24,10 +20,9 @@
}
.outlineLink {
- composes: rightOpen from './icons.module.css';
+ composes: chapterLink from './outline.module.css';
composes: link;
position: relative;
- padding-left: 30px;
}
.link::before {
@@ -40,11 +35,7 @@
.dragHandle {
composes: dragHandle from './outline.module.css';
- color: var(--ui-on-surface-color-light);
-}
-
-.link:hover .dragHandle {
- opacity: 1;
+ color: var(--ui-on-surface-color);
}
.number {
@@ -54,6 +45,7 @@
.title {}
.sections {
+ composes: sections from './outline.module.css';
margin: 10px 0 10px 0;
min-height: 20px;
}
@@ -69,4 +61,5 @@
.addSection {
composes: addButton from './buttons.module.css';
+ composes: button from './outline.module.css';
}
diff --git a/entry_types/scrolled/package/src/editor/views/EntryOutlineView.js b/entry_types/scrolled/package/src/editor/views/EntryOutlineView.js
index 51bdcd172e..6a3a1ac054 100644
--- a/entry_types/scrolled/package/src/editor/views/EntryOutlineView.js
+++ b/entry_types/scrolled/package/src/editor/views/EntryOutlineView.js
@@ -1,6 +1,8 @@
import Marionette from 'backbone.marionette';
+import Backbone from 'backbone';
import I18n from 'i18n-js';
import {cssModulesUtils, SortableCollectionView} from 'pageflow/ui';
+import {DropDownButtonView} from 'pageflow/editor';
import {ChapterItemView} from './ChapterItemView';
@@ -11,7 +13,15 @@ export const EntryOutlineView = Marionette.Layout.extend({
className: styles.root,
template: () => `
-
+
+
@@ -19,22 +29,84 @@ export const EntryOutlineView = Marionette.Layout.extend({
`,
- ui: cssModulesUtils.ui(styles, 'chapters'),
+ ui: cssModulesUtils.ui(styles, 'header', 'dropDownButton', 'chapters'),
events: cssModulesUtils.events(styles, {
'click addChapter': function() {
this.options.entry.addChapter();
+ },
+
+ 'click expandChapters': function() {
+ this.toggleCollapsed();
}
}),
+ initialize() {
+ this.collapsed = false;
+ },
+
onRender() {
- this.subview(new SortableCollectionView({
+ const dropDownMenuItems = new Backbone.Collection();
+
+ this.reorderChaptersMenutItem = new MenuItem({
+ label: I18n.t('pageflow_scrolled.editor.entry_outline.reorder_chapters')
+ }, {
+ selected: () =>
+ this.toggleCollapsed()
+ })
+
+ dropDownMenuItems.add(this.reorderChaptersMenutItem);
+
+ this.appendSubview(new DropDownButtonView({
+ items: dropDownMenuItems,
+ alignMenu: 'right',
+ ellipsisIcon: true,
+ borderless: true,
+ openOnClick: true
+ }), {to: this.ui.dropDownButton});
+
+ this.sortableCollectionView = new SortableCollectionView({
el: this.ui.chapters,
collection: this.options.entry.chapters,
itemViewConstructor: ChapterItemView,
itemViewOptions: {
entry: this.options.entry
}
- }));
+ });
+
+ this.subview(this.sortableCollectionView);
+ this.sortableCollectionView.disableSorting();
+ },
+
+ toggleCollapsed() {
+ this.collapsed = !this.collapsed;
+
+ this.$el.toggleClass(styles.collapsed, this.collapsed);
+
+ if (this.collapsed) {
+ this.ui.header.text(
+ I18n.t('pageflow_scrolled.editor.entry_outline.reorder_chapters_header')
+ );
+
+ this.sortableCollectionView.enableSorting();
+ }
+ else {
+ this.ui.header.text(
+ I18n.t('pageflow_scrolled.editor.entry_outline.header')
+ );
+
+ this.sortableCollectionView.disableSorting();
+
+ }
+ }
+});
+
+const MenuItem = Backbone.Model.extend({
+ initialize: function(attributes, options) {
+ this.options = options;
+ },
+
+ selected: function() {
+ this.options.selected();
}
});
diff --git a/entry_types/scrolled/package/src/editor/views/EntryOutlineView.module.css b/entry_types/scrolled/package/src/editor/views/EntryOutlineView.module.css
index cb0e44c7fc..20ebb04018 100644
--- a/entry_types/scrolled/package/src/editor/views/EntryOutlineView.module.css
+++ b/entry_types/scrolled/package/src/editor/views/EntryOutlineView.module.css
@@ -1,13 +1,51 @@
-.root {}
+.root {
+ position: relative;
+ user-select: none;
+ composes: outline from './outline.module.css';
+}
-.chapters {}
+.collapsed {
+ composes: collapsed from './outline.module.css';
+}
+
+.root .header {
+ margin-bottom: 10px;
+}
+
+.toolbar {
+ position: absolute;
+ top: -6px;
+ right: -1px;
+}
+
+.dropDownButton {
+ --drop-down-button-width: 26px;
+ --ui-on-button-color: var(--ui-primary-color-light);
-.chapters > :global(.sortable-placeholder) {
- margin-bottom: 12px;
- padding: 0 10px 10px 10px;
- border-radius: rounded(lg);
+ button.hover,
+ button:hover {
+ --ui-on-button-color: var(--ui-primary-color);
+ }
}
+.collapsed .dropDownButton,
+.expandChapters {
+ display: none !important;
+}
+
+.expandChapters {
+ composes: saveButton from './buttons.module.css';
+ padding-top: 3px !important;
+ padding-bottom: 3px !important;
+}
+
+.collapsed .expandChapters {
+ display: block !important;
+}
+
+.chapters {}
+
.addChapter {
composes: addButton from './buttons.module.css';
+ composes: button from './outline.module.css';
}
diff --git a/entry_types/scrolled/package/src/editor/views/SectionItemView.js b/entry_types/scrolled/package/src/editor/views/SectionItemView.js
index 8211e96a91..cda5f109c0 100644
--- a/entry_types/scrolled/package/src/editor/views/SectionItemView.js
+++ b/entry_types/scrolled/package/src/editor/views/SectionItemView.js
@@ -150,6 +150,7 @@ export const SectionItemView = Marionette.ItemView.extend({
items: dropDownMenuItems,
alignMenu: 'right',
ellipsisIcon: true,
+ borderless: true,
openOnClick: true
}), {to: this.ui.dropDownButton});
},
diff --git a/entry_types/scrolled/package/src/editor/views/SectionItemView.module.css b/entry_types/scrolled/package/src/editor/views/SectionItemView.module.css
index b7d112ad87..35d04cdc69 100644
--- a/entry_types/scrolled/package/src/editor/views/SectionItemView.module.css
+++ b/entry_types/scrolled/package/src/editor/views/SectionItemView.module.css
@@ -72,6 +72,7 @@
.dragHandle {
composes: dragHandle from './outline.module.css';
+ --outline-drag-handle-transition-delay: 0.2s;
color: var(--ui-on-primary-color);
text-shadow: 0 0 2px #000;
}
@@ -93,8 +94,7 @@
}
.dropDownButton button {
- border: 0 !important;
- color: var(--ui-on-primary-color) !important;
+ --ui-on-button-color: var(--ui-on-primary-color);
text-shadow: 0 0 2px #000;
box-shadow: none !important;
opacity: 0.3;
@@ -103,7 +103,7 @@
}
.invert .dropDownButton button {
- color: var(--ui-primary-color) !important;
+ --ui-on-button-color: var(--ui-primary-color);
text-shadow: 0 0 2px #fff;
}
diff --git a/entry_types/scrolled/package/src/editor/views/outline.module.css b/entry_types/scrolled/package/src/editor/views/outline.module.css
index a3a26bae21..1a2fd56e4a 100644
--- a/entry_types/scrolled/package/src/editor/views/outline.module.css
+++ b/entry_types/scrolled/package/src/editor/views/outline.module.css
@@ -1,9 +1,36 @@
@value indicatorIconColor, errorIconColor from './colors.module.css';
+.chapter {
+ padding: 0 10px 10px 10px;
+}
+
+.chapterLink {
+ composes: rightOpen from './icons.module.css';
+}
+
+.collapsed .chapter {
+ padding-bottom: 0;
+ cursor: move;
+}
+
+.collapsed .chapterLink {
+ padding-left: 30px;
+ pointer-events: none;
+}
+
+.collapsed .chapterLink::before {
+ display: none;
+}
+
.chapter:first-child .sectionWithTransition:first-child .transition {
display: none;
}
+.collapsed .sections,
+.collapsed .button {
+ display: none;
+}
+
.indicator {
display: none;
position: absolute;
@@ -43,5 +70,9 @@
height: 100%;
width: 30px;
transition: opacity 0.1s ease;
- transition-delay: 0.2s;
+ transition-delay: var(--outline-drag-handle-transition-delay);
+}
+
+.outline:not(.collapsed) .chapterLink .dragHandle {
+ display: none;
}
diff --git a/package/package.json b/package/package.json
index 48a44589d9..3e6ecacd36 100644
--- a/package/package.json
+++ b/package/package.json
@@ -27,6 +27,7 @@
"backbone-events-standalone": "^0.2.7",
"classlist.js": "^1.1.20150312",
"core-js": "^3.4.1",
+ "sortablejs": "^1.15.3",
"i18n-js": "^3.5.1",
"postcss-functions": "^3.0.0"
}
diff --git a/package/src/editor/views/DropDownButtonView.js b/package/src/editor/views/DropDownButtonView.js
index 6ff4afb6b0..5feeb6f37e 100644
--- a/package/src/editor/views/DropDownButtonView.js
+++ b/package/src/editor/views/DropDownButtonView.js
@@ -76,6 +76,7 @@ export const DropDownButtonView = Marionette.ItemView.extend({
this.ui.button.toggleClass('has_icon_and_text', !!this.options.label);
this.ui.button.toggleClass('has_icon_only', !this.options.label);
this.ui.button.toggleClass('ellipsis_icon', !!this.options.ellipsisIcon);
+ this.ui.button.toggleClass('borderless', !!this.options.borderless);
this.ui.button.text(this.options.label);
this.ui.button.addClass(this.options.buttonClassName);
diff --git a/package/src/ui/views/SortableCollectionView.js b/package/src/ui/views/SortableCollectionView.js
index 3be004e00e..2c31b0ec26 100644
--- a/package/src/ui/views/SortableCollectionView.js
+++ b/package/src/ui/views/SortableCollectionView.js
@@ -1,5 +1,6 @@
import $ from 'jquery';
import _ from 'underscore';
+import Sortable from 'sortablejs';
import {CollectionView} from './CollectionView';
@@ -7,38 +8,52 @@ export const SortableCollectionView = CollectionView.extend({
render: function() {
CollectionView.prototype.render.call(this);
- this.$el.sortable({
- connectWith: this.options.connectWith,
- placeholder: 'sortable-placeholder',
- forcePlaceholderSize: true,
- delay: 200,
+ this.sortable = Sortable.create(this.el, {
+ group: this.options.connectWith,
+ animation: 150,
- update: _.bind(function(event, ui) {
- if (ui.item.parent().is(this.el)) {
+ ghostClass: 'sortable-placeholder',
+
+ onEnd: event => {
+ const item = $(event.item);
+
+ if (item.parent().is(this.el)) {
this.updateOrder();
}
- }, this),
+ },
- receive: _.bind(function(event, ui) {
- var view = ui.item.data('view');
+ onRemove: event => {
+ const view = $(event.item).data('view');
- this.reindexPositions();
+ this.itemViews.remove(view);
+ this.collection.remove(view.model);
+ },
- this.itemViews.add(view);
- this.collection.add(view.model);
- }, this),
+ onSort: event => {
+ if (event.from !== event.to && event.to === this.el) {
+ const view = $(event.item).data('view');
- remove: _.bind(function(event, ui) {
- var view = ui.item.data('view');
+ this.reindexPositions();
- this.itemViews.remove(view);
- this.collection.remove(view.model);
- }, this)
+ this.itemViews.add(view);
+ this.collection.add(view.model);
+
+ this.collection.saveOrder();
+ }
+ }
});
return this;
},
+ disableSorting() {
+ this.sortable.option('disabled', true);
+ },
+
+ enableSorting() {
+ this.sortable.option('disabled', false);
+ },
+
addItem: function(item) {
if (!this.itemViews.findByModel(item)) {
CollectionView.prototype.addItem.call(this, item);
@@ -63,4 +78,4 @@ export const SortableCollectionView = CollectionView.extend({
$(this).data('view').model.set('position', index);
});
}
-});
\ No newline at end of file
+});
diff --git a/yarn.lock b/yarn.lock
index 2d195dffa7..f3d1b113b0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11795,6 +11795,11 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
+sortablejs@^1.15.3:
+ version "1.15.3"
+ resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.3.tgz#033668db5ebfb11167d1249ab88e748f27959e29"
+ integrity sha512-zdK3/kwwAK1cJgy1rwl1YtNTbRmc8qW/+vgXf75A7NHag5of4pyI6uK86ktmQETyWRH7IGaE73uZOOBcGxgqZg==
+
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"