diff --git a/gui/app/components/document/add-section.js b/gui/app/components/document/add-section.js new file mode 100644 index 00000000..c37c5779 --- /dev/null +++ b/gui/app/components/document/add-section.js @@ -0,0 +1,161 @@ +// Copyright 2016 Documize Inc. . All rights reserved. +// +// This software (Documize Community Edition) is licensed under +// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html +// +// You can operate outside the AGPL restrictions by purchasing +// Documize Enterprise Edition and obtaining a commercial license +// by contacting . +// +// https://documize.com + +import $ from 'jquery'; +import { empty } from '@ember/object/computed'; +import { inject as service } from '@ember/service'; +import { computed } from '@ember/object'; +import Tooltips from '../../mixins/tooltip'; +import Notifier from '../../mixins/notifier'; +import Modals from '../../mixins/modal'; +import Component from '@ember/component'; + +export default Component.extend(Tooltips, Notifier, Modals, { + documentService: service('document'), + sectionService: service('section'), + store: service(), + newSectionName: '', + newSectionNameMissing: empty('newSectionName'), + show: false, + modalId: '#add-section-modal', + canEdit: computed('permissions', 'document.protection', function() { + let canEdit = this.get('document.protection') !== this.get('constants').ProtectionType.Lock && this.get('permissions.documentEdit'); + return canEdit; + }), + hasBlocks: computed('blocks', function() { + return this.get('blocks.length') > 0; + }), + + onModalToggle: function () { + let modalId = this.get('modalId'); + + if (this.get('show')) { + this.modalOpen(modalId, {'show': true}, '#new-section-name'); + + let self = this; + $(modalId).one('hidden.bs.modal', function(e) { // eslint-disable-line no-unused-vars + self.set('show', false); + }); + } else { + this.modalClose(modalId); + $(modalId).modal('hide'); + $(modalId).modal('dispose'); + } + }.observes('show'), + + addSection(model) { + this.modalClose(this.get('modalId')); + + let sequence = 0; + let level = 1; + let beforePage = this.get('beforePage'); + let constants = this.get('constants'); + let pages = this.get('pages'); + + // By default, we create page at the end of the document. + if (pages.get('length') > 0 ) { + let p = pages.get('lastObject'); + sequence = p.get('page.sequence') * 2; + level = p.get('page.level'); + } + + // But, if we can work work correct placement, we put new content as best we can. + if (is.object(beforePage)) { + level = beforePage.get('level'); + + // get any page before the beforePage so we can insert this new section between them + let index = _.findIndex(this.get('pages'), function(item) { return item.get('page.id') === beforePage.get('id'); }); + + if (index !== -1) { + let beforeBeforePage = this.get('pages')[index-1]; + + if (is.not.undefined(beforeBeforePage)) { + sequence = (beforePage.get('sequence') + beforeBeforePage.get('page.sequence')) / 2; + } else { + sequence = beforePage.get('sequence') / 2; + } + } + } + + model.page.set('sequence', sequence); + model.page.set('level', level); + + if (this.get('document.protection') === constants.ProtectionType.Review) { + model.page.set('status', model.page.get('relativeId') === '' ? constants.ChangeState.PendingNew : constants.ChangeState.Pending); + } + + return this.get('onInsertSection')(model); + }, + + actions: { + onInsertSection(section) { + let sectionName = this.get('newSectionName'); + if (is.empty(sectionName)) { + $("#new-section-name").focus(); + return; + } + + let page = this.get('store').createRecord('page'); + page.set('documentId', this.get('document.id')); + page.set('title', sectionName); + page.set('contentType', section.get('contentType')); + page.set('pageType', section.get('pageType')); + + let meta = { + documentId: this.get('document.id'), + rawBody: "", + config: "" + }; + + let model = { + page: page, + meta: meta + }; + + const promise = this.addSection(model); + promise.then((id) => { + this.set('toEdit', model.page.pageType === 'section' ? id : ''); + }); + }, + + onInsertBlock(block) { + let sectionName = this.get('newSectionName'); + if (is.empty(sectionName)) { + $("#new-section-name").focus(); + return; + } + + let page = this.get('store').createRecord('page'); + page.set('documentId', this.get('document.id')); + page.set('title', `${block.get('title')}`); + page.set('body', block.get('body')); + page.set('contentType', block.get('contentType')); + page.set('pageType', block.get('pageType')); + page.set('blockId', block.get('id')); + + let meta = { + documentId: this.get('document.id'), + rawBody: block.get('rawBody'), + config: block.get('config'), + externalSource: block.get('externalSource') + }; + + let model = { + page: page, + meta: meta + }; + + const promise = this.addSection(model); + promise.then((id) => { // eslint-disable-line no-unused-vars + }); + } + } +}); diff --git a/gui/app/components/document/page-heading.js b/gui/app/components/document/page-heading.js index f3829f26..2fb9647b 100644 --- a/gui/app/components/document/page-heading.js +++ b/gui/app/components/document/page-heading.js @@ -13,11 +13,12 @@ import $ from 'jquery'; import { computed } from '@ember/object'; import { debounce } from '@ember/runloop'; import { inject as service } from '@ember/service'; +import Tooltips from '../../mixins/tooltip'; import ModalMixin from '../../mixins/modal'; import tocUtil from '../../utils/toc'; import Component from '@ember/component'; -export default Component.extend(ModalMixin, { +export default Component.extend(ModalMixin, Tooltips, { documentService: service('document'), searchService: service('search'), router: service(), @@ -77,6 +78,14 @@ export default Component.extend(ModalMixin, { this.setState(this.get('page.id')); }, + didInsertElement() { + this._super(...arguments); + + if (this.get('session.authenticated')) { + this.renderTooltips(); + } + }, + searchDocs() { let payload = { keywords: this.get('docSearchFilter').trim(), doc: true }; if (payload.keywords.length == 0) return; diff --git a/gui/app/components/document/view-content.js b/gui/app/components/document/view-content.js index c6c30dcd..0b6846cb 100644 --- a/gui/app/components/document/view-content.js +++ b/gui/app/components/document/view-content.js @@ -10,37 +10,33 @@ // https://documize.com import $ from 'jquery'; -import { notEmpty, empty } from '@ember/object/computed'; +import { notEmpty } from '@ember/object/computed'; import { inject as service } from '@ember/service'; import { computed } from '@ember/object'; -import Component from '@ember/component'; import TooltipMixin from '../../mixins/tooltip'; +import Notifier from '../../mixins/notifier'; +import Component from '@ember/component'; -export default Component.extend(TooltipMixin, { +export default Component.extend(TooltipMixin, Notifier, { documentService: service('document'), sectionService: service('section'), store: service(), appMeta: service(), link: service(), hasPages: notEmpty('pages'), - newSectionName: '', - newSectionNameMissing: empty('newSectionName'), + showInsertSectionModal: false, newSectionLocation: '', - beforePage: null, toEdit: '', - showDeleteBlockDialog: false, - deleteBlockId: '', canEdit: computed('permissions', 'document.protection', function() { let canEdit = this.get('document.protection') !== this.get('constants').ProtectionType.Lock && this.get('permissions.documentEdit'); return canEdit; }), - hasBlocks: computed('blocks', function() { - return this.get('blocks.length') > 0; - }), - mousetrap: null, voteThanks: false, showLikes: false, + showDeleteBlockDialog: false, + deleteBlockId: '', + didReceiveAttrs() { this._super(...arguments); this.set('showLikes', this.get('folder.allowLikes') && this.get('document.isLive')); @@ -49,22 +45,12 @@ export default Component.extend(TooltipMixin, { didRender() { this._super(...arguments); this.contentLinkHandler(); - - let mousetrap = this.get('mousetrap'); - let msContainer = document.getElementById('new-section-wizard'); - if (is.null(mousetrap)) mousetrap = new Mousetrap(msContainer); - - mousetrap.bind('esc', () => { - this.send('onHideSectionWizard'); - return false; - }); }, didInsertElement() { this._super(...arguments); if (this.get('session.authenticated')) { - // this.setupAddWizard(); this.renderTooltips(); } @@ -75,14 +61,8 @@ export default Component.extend(TooltipMixin, { this._super(...arguments); if (this.get('session.authenticated')) { - $('.start-section:not(.start-section-empty-state)').off('.hoverIntent'); this.removeTooltips(); } - - let mousetrap = this.get('mousetrap'); - if (is.not.null(mousetrap)) { - mousetrap.unbind('esc'); - } }, contentLinkHandler() { @@ -120,50 +100,6 @@ export default Component.extend(TooltipMixin, { }); }, - addSection(model) { - let sequence = 0; - let level = 1; - let beforePage = this.get('beforePage'); - let constants = this.get('constants'); - - // calculate sequence of page (position in document) - if (is.not.null(beforePage)) { - level = beforePage.get('level'); - - // get any page before the beforePage so we can insert this new section between them - let index = _.findIndex(this.get('pages'), function(item) { return item.get('page.id') === beforePage.get('id'); }); - - if (index !== -1) { - let beforeBeforePage = this.get('pages')[index-1]; - - if (is.not.undefined(beforeBeforePage)) { - sequence = (beforePage.get('sequence') + beforeBeforePage.get('page.sequence')) / 2; - } else { - sequence = beforePage.get('sequence') / 2; - } - - } - } else { - let pages = this.get('pages'); - if (pages.get('length') > 0 ) { - let p = pages.get('lastObject'); - sequence = p.get('page.sequence') * 2; - level = p.get('page.level'); - } - } - - model.page.set('sequence', sequence); - model.page.set('level', level); - - if (this.get('document.protection') === constants.ProtectionType.Review) { - model.page.set('status', model.page.get('relativeId') === '' ? constants.ChangeState.PendingNew : constants.ChangeState.Pending); - } - - this.send('onHideSectionWizard'); - - return this.get('onInsertSection')(model); - }, - jumpToSection() { let cp = this.get('currentPageId'); if (is.not.empty(cp) && is.not.undefined(cp) && is.not.null(cp)) { @@ -209,8 +145,7 @@ export default Component.extend(TooltipMixin, { if (page.get('relativeId') === '' && page.get('status') === constants.ChangeState.PendingNew) { // new page, edits this.set('toEdit', ''); - let cb = this.get('onSavePage'); - cb(page, meta); + this.get('onSavePage')(page, meta); } else if (page.get('relativeId') !== '' && page.get('status') === constants.ChangeState.Published) { // existing page, first edit const promise = this.addSection({ page: page, meta: meta }); @@ -218,120 +153,21 @@ export default Component.extend(TooltipMixin, { } else if (page.get('relativeId') !== '' && page.get('status') === constants.ChangeState.Pending) { // existing page, subsequent edits this.set('toEdit', ''); - let cb = this.get('onSavePage'); - cb(page, meta); + this.get('onSavePage')(page, meta); } break; case constants.ProtectionType.None: // for un-protected documents, edits welcome! this.set('toEdit', ''); - // let cb2 = this.get('onSavePage'); - // cb2(page, meta); - this.attrs.onSavePage(page, meta); // eslint-disable-line ember/no-attrs-in-components + this.get('onSavePage')(page, meta); break; } }, - onShowSectionWizard(page) { - if (is.undefined(page)) { - page = { id: '0' }; - } - - let beforePage = this.get('beforePage'); - if (is.not.null(beforePage) && $("#new-section-wizard").is(':visible') && beforePage.get('id') === page.id) { - this.send('onHideSectionWizard'); - return; - } - - this.set('newSectionLocation', page.id); - - if (page.id === '0') { - // this handles add section at the end of the document - // because we are not before another page - this.set('beforePage', null); - } else { - this.set('beforePage', page); - } - - $("#new-section-wizard").insertAfter(`#add-section-button-${page.id}`); - $("#new-section-wizard").velocity("transition.slideDownIn", { duration: 300, complete: - function() { - $("#new-section-name").focus(); - }}); - }, - - onHideSectionWizard() { - if (this.get('isDestroyed') || this.get('isDestroying')) return; - - this.set('newSectionLocation', ''); - this.set('beforePage', null); - $("#new-section-wizard").insertAfter('#wizard-placeholder'); - $("#new-section-wizard").velocity("transition.slideUpOut", { duration: 300 }); - }, - - onInsertSection(section) { - let sectionName = this.get('newSectionName'); - if (is.empty(sectionName)) { - $("#new-section-name").focus(); - return; - } - - let page = this.get('store').createRecord('page'); - page.set('documentId', this.get('document.id')); - page.set('title', sectionName); - page.set('contentType', section.get('contentType')); - page.set('pageType', section.get('pageType')); - - let meta = { - documentId: this.get('document.id'), - rawBody: "", - config: "" - }; - - let model = { - page: page, - meta: meta - }; - - const promise = this.addSection(model); - promise.then((id) => { - this.set('toEdit', model.page.pageType === 'section' ? id: ''); - // this.setupAddWizard(); - }); - }, - - onInsertBlock(block) { - let sectionName = this.get('newSectionName'); - if (is.empty(sectionName)) { - $("#new-section-name").focus(); - return; - } - - let page = this.get('store').createRecord('page'); - page.set('documentId', this.get('document.id')); - page.set('title', `${block.get('title')}`); - page.set('body', block.get('body')); - page.set('contentType', block.get('contentType')); - page.set('pageType', block.get('pageType')); - page.set('blockId', block.get('id')); - - let meta = { - documentId: this.get('document.id'), - rawBody: block.get('rawBody'), - config: block.get('config'), - externalSource: block.get('externalSource') - }; - - let model = { - page: page, - meta: meta - }; - - const promise = this.addSection(model); - promise.then((id) => { // eslint-disable-line no-unused-vars - // this.setupAddWizard(); - }); + onShowSectionWizard(beforePage) { + this.set('newSectionLocation', beforePage); + this.set('showInsertSectionModal', true) }, onShowDeleteBlockModal(id) { diff --git a/gui/app/components/section/code/type-editor.js b/gui/app/components/section/code/type-editor.js index 0f512113..1c415fdc 100644 --- a/gui/app/components/section/code/type-editor.js +++ b/gui/app/components/section/code/type-editor.js @@ -10,13 +10,12 @@ // https://documize.com import { computed } from '@ember/object'; -import Component from '@ember/component'; import TooltipMixin from '../../../mixins/tooltip'; +import Component from '@ember/component'; export default Component.extend(TooltipMixin, { isDirty: false, pageBody: "", - codeSyntax: null, codeEditor: null, editorId: computed('page', function () { diff --git a/gui/app/styles/app.scss b/gui/app/styles/app.scss index c72dc1d8..138e564f 100644 --- a/gui/app/styles/app.scss +++ b/gui/app/styles/app.scss @@ -25,11 +25,3 @@ @import "news.scss"; @import "section/all.scss"; @import "enterprise/all.scss"; - -// Bootstrap override that removes gutter space on smaller screens -// @media (max-width: 1200px) { -// .container { -// width: 100%; -// max-width: none; -// } -// } diff --git a/gui/app/styles/view/document/add-section.scss b/gui/app/styles/view/document/add-section.scss index 0c9cefd3..f68908ed 100644 --- a/gui/app/styles/view/document/add-section.scss +++ b/gui/app/styles/view/document/add-section.scss @@ -24,12 +24,8 @@ } .new-section-wizard { - display: none; - @include border-radius(2px); - margin: 0 0 60px 0; - padding: 30px; - border: 1px solid $color-stroke; - background-color: $color-primary-light; + margin: 0; + padding: 0; .new-section-caption { margin: 20px 0 10px 0; @@ -39,7 +35,7 @@ } .preset-list { - margin: 20px 0 0 0; + margin: 0; padding: 0; > .item { @@ -47,17 +43,16 @@ @include border-radius(3px); list-style: none; cursor: pointer; - display: inline-block; - border: 1px solid $color-border; - background-color: $color-white; + display: block; margin: 0 20px 20px 0; padding: 12px 0 0 20px; - width: 250px; height: 60px; + border: 1px solid $color-border; + background-color: $color-off-white; &:hover { - @include ease-in(); - border-color: $color-link; + border-color: $color-primary; + background-color: $color-primary-light; } .icon { @@ -76,8 +71,8 @@ > .title { display: inline-block; - font-size: 1rem; - font-weight: normal; + font-size: 1.1rem; + font-weight: 500; color: $color-off-black; letter-spacing: 0.5px; margin-top: 6px; @@ -86,7 +81,7 @@ } .block-list { - margin: 20px 0 0 0; + margin: 0; padding: 0; > .item { @@ -95,21 +90,16 @@ list-style: none; cursor: pointer; display: block; - border: 1px solid $color-border; - background-color: $color-white; margin: 0 20px 20px 0; - padding: 20px; - height: 90px; + padding: 12px 20px; width: 100%; position: relative; + border: 1px solid $color-border; + background-color: $color-off-white; &:hover { - @include ease-in(); - border-color: $color-link; - - > .block-actions { - display: block; - } + border-color: $color-primary; + background-color: $color-primary-light; } > .actions { @@ -122,16 +112,14 @@ > .details { > .title { font-size: 1.1rem; - font-weight: bold; + font-weight: 500; color: $color-off-black; letter-spacing: 0.5px; - margin-top: 0; } > .desc { color: $color-off-black; font-size: 1rem; - margin-top: 5px; } } } diff --git a/gui/app/templates/components/document/add-section.hbs b/gui/app/templates/components/document/add-section.hbs new file mode 100644 index 00000000..d0a6a3f5 --- /dev/null +++ b/gui/app/templates/components/document/add-section.hbs @@ -0,0 +1,64 @@ + diff --git a/gui/app/templates/components/document/view-content.hbs b/gui/app/templates/components/document/view-content.hbs index 7b59c0b1..3891b65c 100644 --- a/gui/app/templates/components/document/view-content.hbs +++ b/gui/app/templates/components/document/view-content.hbs @@ -2,8 +2,7 @@ {{#each pages key="id" as |item index|}} {{#if canEdit}} -
+
+ SECTION
@@ -33,7 +32,7 @@ {{/each}} {{#if canEdit}} -
+
+ SECTION
@@ -58,94 +57,29 @@
{{/if}} -{{/if}} +{{else }} -{{#unless hasPages}} {{#if canEdit}} -
+
+ SECTION
{{/if}} -{{/unless}} + +{{/if}} {{#if canEdit}} -
-
-
-
-
-
- -
-
-
-
-
-
- {{input type="text" id="new-section-name" value=newSectionName class=(if newSectionNameMissing 'mousetrap form-control form-control-lg - is-invalid' 'mousetrap form-control form-control-lg') placeholder="Enter section name" autocomplete="off"}} -
-
-
- -
-
-
-
Insert section type
-
    - {{#each sections as |section|}} -
  • -
    - -
    -
    {{section.title}}
    -
  • - {{/each}} -
-
-
-
- -
-
- {{#if hasBlocks}} -
Insert re-usable content
-
    - {{#each blocks as |block|}} -
  • -
    - {{#if permissions.documentTemplate}} {{#link-to 'document.block' folder.id folder.slug document.id document.slug block.id - class="button-icon-gray button-icon-small align-middle"}} - edit - {{/link-to}} -
    -
    - delete -
    - {{/if}} -
    -
    -
    {{block.title}}
    -
    {{block.excerpt}}
    -
    -
  • - {{/each}} -
- {{else}} -
You have no reusable content — publish any section as a template
- {{/if}} -
-
-
-
-{{/if}} - -{{#if permissions.documentTemplate}} - {{#ui/ui-dialog title="Delete Content Block" confirmCaption="Delete" buttonType="btn-danger" show=showDeleteBlockDialog onAction=(action 'onDeleteBlock')}} -

Are you sure you want to delete this re-usable content block?

- {{/ui/ui-dialog}} -{{/if}} + {{document/add-section + pages=pages + blocks=blocks + folder=folder + toEdit=toEdit + folders=folders + sections=sections + document=document + permissions=permissions + show=showInsertSectionModal + beforePage=newSectionLocation + onInsertSection=(action onInsertSection)}} +{{/if}} \ No newline at end of file