From 855492ab594f1ab1049ac10f5af2ca079269b9ef Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 20 Oct 2021 16:54:41 +0530 Subject: [PATCH 001/101] fix: Added new icons for each wspace blocks --- frappe/public/icons/timeless/symbol-defs.svg | 68 +++++++++++++++++++ .../js/frappe/views/workspace/blocks/card.js | 2 +- .../js/frappe/views/workspace/blocks/chart.js | 2 +- .../frappe/views/workspace/blocks/header.js | 2 +- .../views/workspace/blocks/onboarding.js | 2 +- .../views/workspace/blocks/paragraph.js | 4 +- .../frappe/views/workspace/blocks/shortcut.js | 2 +- .../frappe/views/workspace/blocks/spacer.js | 2 +- frappe/public/scss/desk/desktop.scss | 14 ++-- 9 files changed, 86 insertions(+), 12 deletions(-) diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index b878f713e9..8bfb40a0ad 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -712,4 +712,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frappe/public/js/frappe/views/workspace/blocks/card.js b/frappe/public/js/frappe/views/workspace/blocks/card.js index 9b4a2ed14f..f51f5803a9 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/card.js +++ b/frappe/public/js/frappe/views/workspace/blocks/card.js @@ -3,7 +3,7 @@ export default class Card extends Block { static get toolbox() { return { title: 'Card', - icon: '' + icon: frappe.utils.icon('card', 'md') }; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/chart.js b/frappe/public/js/frappe/views/workspace/blocks/chart.js index 02e6a66e6f..d4f7f1fd9e 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/chart.js +++ b/frappe/public/js/frappe/views/workspace/blocks/chart.js @@ -3,7 +3,7 @@ export default class Chart extends Block { static get toolbox() { return { title: 'Chart', - icon: '' + icon: frappe.utils.icon('chart', 'md') }; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index d88bc42af9..e1d24aee76 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -332,8 +332,8 @@ export default class Header extends Block { static get toolbox() { return { - icon: '', title: 'Heading', + icon: frappe.utils.icon('header', 'md') }; } } \ No newline at end of file diff --git a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js index 7176b7726d..5a1c78d5c1 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js +++ b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js @@ -4,7 +4,7 @@ export default class Onboarding extends Block { static get toolbox() { return { title: 'Onboarding', - icon: '' + icon: frappe.utils.icon('onboarding', 'md') }; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index 9e5dfb68ff..96a283e9b2 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -188,8 +188,8 @@ export default class Paragraph extends Block { static get toolbox() { return { - icon: '', - title: 'Text' + title: 'Text', + icon: frappe.utils.icon('text', 'md') }; } } \ No newline at end of file diff --git a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js index 96b8f47484..0d75b2e42d 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js +++ b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js @@ -3,7 +3,7 @@ export default class Shortcut extends Block { static get toolbox() { return { title: 'Shortcut', - icon: '' + icon: frappe.utils.icon('shortcut', 'md') }; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/spacer.js b/frappe/public/js/frappe/views/workspace/blocks/spacer.js index 3309cad4a4..6e1b47f1d3 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/spacer.js +++ b/frappe/public/js/frappe/views/workspace/blocks/spacer.js @@ -3,7 +3,7 @@ export default class Spacer extends Block { static get toolbox() { return { title: 'Spacer', - icon: '' + icon: frappe.utils.icon('spacer', 'md') }; } diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 6ab01a744c..2947d5e677 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -793,8 +793,8 @@ body { } .block-menu-item-icon svg{ - width: 12px; - height: 12px; + width: 18px; + height: 18px; margin-right: 5px; } @@ -984,8 +984,10 @@ body { .icon { stroke: none; - width: fit-content; - height: fit-content; + + &.icon--plus { + width: 14px; + } } .ce-settings { @@ -993,6 +995,10 @@ body { .ce-settings__button, .cdx-settings-button { color: #707684; + + .icon { + width: 14px; + } } .cdx-settings-button--active { From 4884b360e7b285a0a8eca9dfbf4e5f5d63d3ac70 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 29 Nov 2021 12:04:26 +0530 Subject: [PATCH 002/101] feat: Drag to resize blocks --- .../js/frappe/views/workspace/blocks/block.js | 113 ++++++++++++++++++ .../js/frappe/views/workspace/blocks/card.js | 4 +- .../js/frappe/views/workspace/blocks/chart.js | 4 +- .../frappe/views/workspace/blocks/header.js | 6 +- .../views/workspace/blocks/onboarding.js | 5 +- .../views/workspace/blocks/paragraph.js | 4 +- .../frappe/views/workspace/blocks/shortcut.js | 4 +- frappe/public/scss/desk/desktop.scss | 19 +++ 8 files changed, 146 insertions(+), 13 deletions(-) diff --git a/frappe/public/js/frappe/views/workspace/blocks/block.js b/frappe/public/js/frappe/views/workspace/blocks/block.js index aed3c2f727..04a8f15f86 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/block.js +++ b/frappe/public/js/frappe/views/workspace/blocks/block.js @@ -29,10 +29,123 @@ export default class Block { } rendered() { + !this.readOnly && this.resizer(); var e = this.wrapper.closest('.ce-block'); e.classList.add("col-" + this.get_col()); } + resizer(wrapper) { + if (wrapper) this.wrapper = wrapper; + this.wrapper.className = this.wrapper.className + ' resizable'; + var resizer = document.createElement('div'); + resizer.className = 'resizer'; + this.wrapper.parentElement.appendChild(resizer); + resizer.addEventListener('mousedown', init_drag, false); + let me = this; + var startX, startWidth; + + function init_drag(e) { + startX = e.clientX; + startWidth = this.parentElement.offsetWidth; + document.documentElement.addEventListener('mousemove', do_drag, false); + document.documentElement.addEventListener('mouseup', stop_drag, false); + } + + function do_drag(e) { + $(this).css("cursor", "col-resize"); + $('.widget').css("pointer-events", "none"); + un_focus(); + if ((startWidth + e.clientX - startX) - startWidth > 60) { + startX = e.clientX; + me.increase_width(); + } else if ((startWidth + e.clientX - startX) - startWidth < -60) { + startX = e.clientX; + me.decrease_width(); + } + } + + // disable text selection on mousedown (on drag) + function un_focus() { + if (document.selection) { + document.selection.empty() + } else { + window.getSelection().removeAllRanges() + } + } + + function stop_drag(e) { + $(this).css("cursor", "default"); + $('.widget').css("pointer-events", "auto"); + + document.documentElement.removeEventListener('mousemove', do_drag, false); + document.documentElement.removeEventListener('mouseup', stop_drag, false); + } + } + + decrease_width() { + const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); + + if (currentBlockIndex < 0) { + return; + } + + let currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex); + if (!currentBlock) { + return; + } + + let currentBlockElement = currentBlock.holder; + + let className = 'col-12'; + let colClass = new RegExp(/\bcol-.+?\b/, 'g'); + if (currentBlockElement.className.match(colClass)) { + currentBlockElement.classList.forEach( cn => { + if (cn.match(colClass)) { + className = cn; + } + }); + let parts = className.split('-'); + let width = parseInt(parts[1]); + if (width >= 4) { + currentBlockElement.classList.remove('col-'+width); + width = width - 1; + currentBlockElement.classList.add('col-'+width); + } + } + } + + increase_width() { + const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); + + if (currentBlockIndex < 0) { + return; + } + + const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex); + if (!currentBlock) { + return; + } + + const currentBlockElement = currentBlock.holder; + + let className = 'col-12'; + const colClass = new RegExp(/\bcol-.+?\b/, 'g'); + if (currentBlockElement.className.match(colClass)) { + currentBlockElement.classList.forEach( cn => { + if (cn.match(colClass)) { + className = cn; + } + }); + let parts = className.split('-'); + let width = parseInt(parts[1]); + if (width <= 11) { + currentBlockElement.classList.remove('col-'+width); + width = width + 1; + currentBlockElement.classList.add('col-'+width); + } + } + } + new(block, widget_type = block) { const dialog_class = get_dialog_constructor(widget_type); let block_name = block+'_name'; diff --git a/frappe/public/js/frappe/views/workspace/blocks/card.js b/frappe/public/js/frappe/views/workspace/blocks/card.js index f51f5803a9..1dc237507b 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/card.js +++ b/frappe/public/js/frappe/views/workspace/blocks/card.js @@ -49,9 +49,9 @@ export default class Card extends Block { return true; } - save(blockContent) { + save() { return { - card_name: blockContent.getAttribute('card_name'), + card_name: this.wrapper.getAttribute('card_name'), col: this.get_col(), new: this.new_block_widget }; diff --git a/frappe/public/js/frappe/views/workspace/blocks/chart.js b/frappe/public/js/frappe/views/workspace/blocks/chart.js index d4f7f1fd9e..c544cf3af2 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/chart.js +++ b/frappe/public/js/frappe/views/workspace/blocks/chart.js @@ -49,9 +49,9 @@ export default class Chart extends Block { return true; } - save(blockContent) { + save() { return { - chart_name: blockContent.getAttribute('chart_name'), + chart_name: this.wrapper.getAttribute('chart_name'), col: this.get_col(), new: this.new_block_widget }; diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index e1d24aee76..4ee9a0c7dc 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -36,7 +36,6 @@ export default class Header extends Block { render() { this.wrapper = document.createElement('div'); - this.wrapper.contentEditable = this.readOnly ? 'false' : 'true'; if (!this.readOnly) { let $widget_head = $(`
`); let $widget_control = $(`
`); @@ -146,16 +145,17 @@ export default class Header extends Block { return blockData.text.trim() !== ''; } - save(toolsContent) { + save() { this.wrapper = this._element; return { - text: toolsContent.innerText, + text: this.wrapper.innerText, level: this.currentLevel.number, col: this.get_col() }; } rendered() { + !this.readOnly && this.resizer(this._element); var e = this._element.closest('.ce-block'); e.classList.add("col-" + this.get_col()); } diff --git a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js index 5a1c78d5c1..70287ab088 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js +++ b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js @@ -30,6 +30,7 @@ export default class Onboarding extends Block { if (this.readOnly && !$(this.wrapper).find('.onboarding-widget-box').is(':visible')) { $(e).hide(); } + !this.readOnly && this.resizer(); e.classList.add("col-" + this.get_col()); } @@ -119,9 +120,9 @@ export default class Onboarding extends Block { return true; } - save(blockContent) { + save() { return { - onboarding_name: blockContent.getAttribute('onboarding_name'), + onboarding_name: this.wrapper.getAttribute('onboarding_name'), col: this.get_col(), new: this.new_block_widget }; diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index 96a283e9b2..4f9a325fb7 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -54,7 +54,6 @@ export default class Paragraph extends Block { render() { this.wrapper = document.createElement('div'); - this.wrapper.contentEditable = this.readOnly ? 'false' : 'true'; if (!this.readOnly) { let $para_control = $(`
`); @@ -62,7 +61,7 @@ export default class Paragraph extends Block { this._element.classList.remove('widget'); $para_control.appendTo(this.wrapper); - this.wrapper.classList.add('widget'); + this.wrapper.classList.add('widget', 'paragraph'); frappe.utils.add_custom_button( frappe.utils.icon('dot-horizontal', 'xs'), @@ -132,6 +131,7 @@ export default class Paragraph extends Block { } rendered() { + !this.readOnly && this.resizer(this._element); var e = this._element.closest('.ce-block'); e.classList.add("col-" + this.get_col()); } diff --git a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js index 0d75b2e42d..6e4f187d1b 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js +++ b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js @@ -47,9 +47,9 @@ export default class Shortcut extends Block { return true; } - save(blockContent) { + save() { return { - shortcut_name: blockContent.getAttribute('shortcut_name'), + shortcut_name: this.wrapper.getAttribute('shortcut_name'), col: this.get_col(), new: this.new_block_widget }; diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 2947d5e677..2ccdd9fd31 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -143,6 +143,7 @@ body { font-weight: 500; line-height: 1.3em; color: var(--heading-color); + cursor: default; svg { flex: none; @@ -923,10 +924,23 @@ body { pointer-events: none; } + .resizer { + width: 10px; + height: 100%; + position:absolute; + right: 0; + bottom: 0; + cursor: col-resize; + } + .ce-header { padding: 0 !important; margin-bottom: 0 !important; flex: 1; + + &:focus { + outline: none; + } } .widget{ @@ -940,6 +954,7 @@ body { box-shadow: none; background-color: var(--control-bg); color: var(--text-muted); + cursor: text; } &:focus { @@ -954,6 +969,10 @@ body { display: block; } + &.paragraph { + cursor: text; + } + .paragraph-control { display: flex; flex-direction: row-reverse; From 77dd5951cd3f2cbeb00208adb0ab72bb6fb32366 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 21 Oct 2021 14:15:18 +0530 Subject: [PATCH 003/101] fix: Rearranged and fixed icons --- frappe/public/icons/timeless/symbol-defs.svg | 978 +++++++++++-------- frappe/public/scss/desk/desktop.scss | 4 +- 2 files changed, 546 insertions(+), 436 deletions(-) diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index 8bfb40a0ad..cb9437321f 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -1,217 +1,262 @@ diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 2ccdd9fd31..daedbe1877 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -632,8 +632,8 @@ body { width: 18px; .icon-xs { - width: 8px; - height: 7px; + width: 10px; + height: 10px; } } From 1769b86352c32cd7fb2106c80b8f39fd0456d7bd Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 29 Nov 2021 12:07:12 +0530 Subject: [PATCH 004/101] fix: Header Size Inline Tool --- frappe/public/icons/timeless/symbol-defs.svg | 54 +++- .../js/frappe/views/workspace/blocks/block.js | 214 +++++++++++----- .../js/frappe/views/workspace/blocks/card.js | 2 +- .../js/frappe/views/workspace/blocks/chart.js | 2 +- .../frappe/views/workspace/blocks/header.js | 234 ++---------------- .../views/workspace/blocks/header_size.js | 117 +++++++++ .../js/frappe/views/workspace/blocks/index.js | 4 +- .../views/workspace/blocks/onboarding.js | 4 +- .../views/workspace/blocks/paragraph.js | 72 +++--- .../frappe/views/workspace/blocks/shortcut.js | 2 +- .../frappe/views/workspace/blocks/spacer.js | 55 ++-- .../js/frappe/views/workspace/workspace.js | 11 +- .../public/js/frappe/widgets/base_widget.js | 18 +- frappe/public/scss/desk/desktop.scss | 66 +++++ 14 files changed, 484 insertions(+), 371 deletions(-) create mode 100644 frappe/public/js/frappe/views/workspace/blocks/header_size.js diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index cb9437321f..1b3a945ecb 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -265,6 +265,14 @@ + + + + + + + + @@ -765,14 +773,6 @@ - - - - - - - - @@ -814,6 +814,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { - if (cn.match(colClass)) { - className = cn; - } - }); - let parts = className.split('-'); - let width = parseInt(parts[1]); - if (width >= 4) { - currentBlockElement.classList.remove('col-'+width); - width = width - 1; - currentBlockElement.classList.add('col-'+width); - } - } - } - - increase_width() { - const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); - - if (currentBlockIndex < 0) { - return; - } - - const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex); - if (!currentBlock) { - return; - } - - const currentBlockElement = currentBlock.holder; - - let className = 'col-12'; - const colClass = new RegExp(/\bcol-.+?\b/, 'g'); - if (currentBlockElement.className.match(colClass)) { - currentBlockElement.classList.forEach( cn => { - if (cn.match(colClass)) { - className = cn; - } - }); - let parts = className.split('-'); - let width = parseInt(parts[1]); - if (width <= 11) { - currentBlockElement.classList.remove('col-'+width); - width = width + 1; - currentBlockElement.classList.add('col-'+width); - } - } - } - new(block, widget_type = block) { const dialog_class = get_dialog_constructor(widget_type); let block_name = block+'_name'; @@ -167,7 +103,7 @@ export default class Block { this.block_widget.customize(this.options); this.wrapper.setAttribute(block_name, this.block_widget.label); this.new_block_widget = this.block_widget.get_config(); - this.add_tune_button(); + this.add_settings_button(); }, }); @@ -187,6 +123,84 @@ export default class Block { this.new_block_widget = block_obj.get_config(); } + add_settings_button() { + this.dropdown_list = [ + { + label: 'Delete', + title: 'Delete Block', + icon: frappe.utils.icon('delete-active', 'sm'), + action: () => this.api.blocks.delete() + }, + { + label: 'Move Up', + title: 'Move Up', + icon: frappe.utils.icon('up-arrow', 'sm'), + action: () => this.move_block('up') + }, + { + label: 'Move Down', + title: 'Move Down', + icon: frappe.utils.icon('down-arrow', 'sm'), + action: () => this.move_block('down') + }, + { + label: 'Expand', + title: 'Expand Block', + icon: frappe.utils.icon('expand-alt', 'sm'), + action: () => this.increase_width() + }, + { + label: 'Shrink', + title: 'Shrink Block', + icon: frappe.utils.icon('shrink', 'sm'), + action: () => this.decrease_width() + } + ] + + let $widget_control = $(this.wrapper).find('.widget-control'); + + let $button = $(` + + `); + + + let dropdown_item = function(label, title, icon, action) { + let html = $(` + + `); + + html.click(event => { + event.stopPropagation(); + action && action(); + }); + + return html; + } + + $button.click(event => { + event.stopPropagation(); + $button.find('.dropdown-list').toggleClass('hidden'); + }); + + $(document).click(event => { + $button.find('.dropdown-list').addClass('hidden'); + }) + + $widget_control.prepend($button); + + this.dropdown_list.forEach((item) => { + $button.find('.dropdown-list').append(dropdown_item(item.label, item.title, item.icon, item.action)); + }) + } + add_tune_button() { let $widget_control = $(this.wrapper).find('.widget-control'); frappe.utils.add_custom_button( @@ -225,4 +239,74 @@ export default class Block { } return col; } + + decrease_width() { + const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); + + if (currentBlockIndex < 0) { + return; + } + + let currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex); + if (!currentBlock) { + return; + } + + let currentBlockElement = currentBlock.holder; + + let className = 'col-12'; + let colClass = new RegExp(/\bcol-.+?\b/, 'g'); + if (currentBlockElement.className.match(colClass)) { + currentBlockElement.classList.forEach( cn => { + if (cn.match(colClass)) { + className = cn; + } + }); + let parts = className.split('-'); + let width = parseInt(parts[1]); + if (width >= 4) { + currentBlockElement.classList.remove('col-'+width); + width = width - 1; + currentBlockElement.classList.add('col-'+width); + } + } + } + + increase_width() { + const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); + + if (currentBlockIndex < 0) { + return; + } + + const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex); + if (!currentBlock) { + return; + } + + const currentBlockElement = currentBlock.holder; + + let className = 'col-12'; + const colClass = new RegExp(/\bcol-.+?\b/, 'g'); + if (currentBlockElement.className.match(colClass)) { + currentBlockElement.classList.forEach( cn => { + if (cn.match(colClass)) { + className = cn; + } + }); + let parts = className.split('-'); + let width = parseInt(parts[1]); + if (width <= 11) { + currentBlockElement.classList.remove('col-'+width); + width = width + 1; + currentBlockElement.classList.add('col-'+width); + } + } + } + + move_block(direction) { + let current_index = this.api.blocks.getCurrentBlockIndex(); + let new_index = current_index + (direction == 'down' ? 1 : -1); + this.api.blocks.move(new_index, current_index); + } } \ No newline at end of file diff --git a/frappe/public/js/frappe/views/workspace/blocks/card.js b/frappe/public/js/frappe/views/workspace/blocks/card.js index 1dc237507b..7e67c3fb0c 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/card.js +++ b/frappe/public/js/frappe/views/workspace/blocks/card.js @@ -35,7 +35,7 @@ export default class Card extends Block { } if (!this.readOnly) { - this.add_tune_button(); + this.add_settings_button(); } return this.wrapper; diff --git a/frappe/public/js/frappe/views/workspace/blocks/chart.js b/frappe/public/js/frappe/views/workspace/blocks/chart.js index c544cf3af2..8d9bb89825 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/chart.js +++ b/frappe/public/js/frappe/views/workspace/blocks/chart.js @@ -35,7 +35,7 @@ export default class Chart extends Block { } if (!this.readOnly) { - this.add_tune_button(); + this.add_settings_button(); } return this.wrapper; diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index 4ee9a0c7dc..dacd3e6a7d 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -4,16 +4,8 @@ export default class Header extends Block { constructor({ data, config, api, readOnly }) { super({ config, api, readOnly }); - this._CSS = { - block: this.api.styles.block, - settingsButton: this.api.styles.settingsButton, - settingsButtonActive: this.api.styles.settingsButtonActive, - wrapper: 'ce-header', - }; - this._settings = this.config; this._data = this.normalizeData(data); - this.settingsButtons = []; this._element = this.getTag(); this.data = data; @@ -27,8 +19,7 @@ export default class Header extends Block { data = {}; } - newData.text = (data.text && __(data.text.replace(/(\n|\t)/gm, ""))) || ''; - newData.level = parseInt(data.level) || this.defaultLevel.number; + newData.text = data.text || ''; newData.col = parseInt(data.col) || 12; return newData; @@ -46,25 +37,7 @@ export default class Header extends Block { this.wrapper.classList.add('widget', 'header'); - frappe.utils.add_custom_button( - frappe.utils.icon('dot-horizontal', 'xs'), - (event) => { - let evn = event; - !$('.ce-settings.ce-settings--opened').length && - setTimeout(() => { - this.api.toolbar.toggleBlockSettings(); - var position = $(evn.target).offset(); - $('.ce-settings.ce-settings--opened').offset({ - top: position.top + 25, - left: position.left - 77 - }); - }, 50); - }, - "tune-btn", - `${__('Tune')}`, - null, - $widget_control - ); + this.add_settings_button(); frappe.utils.add_custom_button( frappe.utils.icon('drag', 'xs'), @@ -75,67 +48,14 @@ export default class Header extends Block { $widget_control ); - frappe.utils.add_custom_button( - frappe.utils.icon('delete', 'xs'), - () => this.api.blocks.delete(), - "delete-header", - `${__('Delete')}`, - null, - $widget_control - ); - return this.wrapper; } return this._element; } - renderSettings() { - const holder = document.createElement('DIV'); - - if (this.levels.length <= 1) { - return holder; - } - - this.levels.forEach(level => { - const selectTypeButton = document.createElement('SPAN'); - - selectTypeButton.classList.add(this._CSS.settingsButton); - - if (this.currentLevel.number === level.number) { - selectTypeButton.classList.add(this._CSS.settingsButtonActive); - } - - selectTypeButton.innerHTML = level.svg; - - selectTypeButton.dataset.level = level.number; - - selectTypeButton.addEventListener('click', () => { - this.setLevel(level.number); - }); - - holder.appendChild(selectTypeButton); - - this.settingsButtons.push(selectTypeButton); - }); - - return holder; - } - - setLevel(level) { - this.data = { - level: level, - text: this.data.text, - }; - - this.settingsButtons.forEach(button => { - button.classList.toggle(this._CSS.settingsButtonActive, parseInt(button.dataset.level) === level); - }); - } - merge(data) { const newData = { - text: this.data.text + data.text, - level: this.data.level, + text: this.data.text + data.text }; this.data = newData; @@ -148,8 +68,7 @@ export default class Header extends Block { save() { this.wrapper = this._element; return { - text: this.wrapper.innerText, - level: this.currentLevel.number, + text: this.wrapper.innerHTML.replace(/ /gi,''), col: this.get_col() }; } @@ -160,17 +79,16 @@ export default class Header extends Block { e.classList.add("col-" + this.get_col()); } - static get conversionConfig() { - return { - export: 'text', // use 'text' property for other blocks - import: 'text', // fill 'text' property from other block's export string - }; - } - static get sanitize() { return { level: false, - text: {}, + text: { + br: true, + b: true, + i: true, + a: true, + span: true + }, }; } @@ -180,7 +98,6 @@ export default class Header extends Block { get data() { this._data.text = this._element.innerHTML; - this._data.level = this.currentLevel.number; return this._data; } @@ -188,15 +105,11 @@ export default class Header extends Block { set data(data) { this._data = this.normalizeData(data); - if (data.level !== undefined && this._element.parentNode) { - const newHeader = this.getTag(); - newHeader.innerHTML = this._element.innerHTML; - this._element.parentNode.replaceChild(newHeader, this._element); - this._element = newHeader; - } - if (data.text !== undefined) { - this._element.innerHTML = this._data.text || ''; + let text = this._data.text || ''; + const contains_html_tag = /<[a-z][\s\S]*>/i.test(text); + this._element.innerHTML = contains_html_tag ? + text : `${text}`; } if (!this.readOnly && this.wrapper) { @@ -205,11 +118,12 @@ export default class Header extends Block { } getTag() { - const tag = document.createElement(this.currentLevel.tag); + const tag = document.createElement('DIV'); - tag.innerHTML = this._data.text || ''; + let text = this._data.text || ' '; + tag.innerHTML = `${text}`; - tag.classList.add(this._CSS.wrapper); + tag.classList.add('ce-header'); if (!this.readOnly) { tag.contentEditable = true; @@ -220,116 +134,6 @@ export default class Header extends Block { return tag; } - get currentLevel() { - let level = this.levels.find(levelItem => levelItem.number === this._data.level); - - if (!level) { - level = this.defaultLevel; - } - - return level; - } - - get defaultLevel() { - if (this._settings.defaultLevel) { - const userSpecified = this.levels.find(levelItem => { - return levelItem.number === this._settings.defaultLevel; - }); - - if (userSpecified) { - return userSpecified; - } else { - // console.warn('(ง\'̀-\'́)ง Heading Tool: the default level specified was not found in available levels'); - } - } - - return this.levels[1]; - } - - get levels() { - const availableLevels = [ - { - number: 1, - tag: 'H1', - svg: '', - }, - { - number: 2, - tag: 'H2', - svg: '', - }, - { - number: 3, - tag: 'H3', - svg: '', - }, - { - number: 4, - tag: 'H4', - svg: '', - }, - { - number: 5, - tag: 'H5', - svg: '', - }, - { - number: 6, - tag: 'H6', - svg: '', - }, - ]; - - return this._settings.levels ? availableLevels.filter( - l => this._settings.levels.includes(l.number) - ) : availableLevels; - } - - onPaste(event) { - const content = event.detail.data; - - let level = this.defaultLevel.number; - - switch (content.tagName) { - case 'H1': - level = 1; - break; - case 'H2': - level = 2; - break; - case 'H3': - level = 3; - break; - case 'H4': - level = 4; - break; - case 'H5': - level = 5; - break; - case 'H6': - level = 6; - break; - } - - if (this._settings.levels) { - // Fallback to nearest level when specified not available - level = this._settings.levels.reduce((prevLevel, currLevel) => { - return Math.abs(currLevel - level) < Math.abs(prevLevel - level) ? currLevel : prevLevel; - }); - } - - this.data = { - level, - text: content.innerHTML, - }; - } - - static get pasteConfig() { - return { - tags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'], - }; - } - static get toolbox() { return { title: 'Heading', diff --git a/frappe/public/js/frappe/views/workspace/blocks/header_size.js b/frappe/public/js/frappe/views/workspace/blocks/header_size.js new file mode 100644 index 0000000000..3d5612c046 --- /dev/null +++ b/frappe/public/js/frappe/views/workspace/blocks/header_size.js @@ -0,0 +1,117 @@ +export default class HeaderSize { + + static get isInline() { + return true; + } + + get state() { + return this._state; + } + + set state(state) { + this._state = state; + } + + get title() { + return 'Header Size'; + } + + constructor({api}) { + this.api = api; + this.button = null; + this._state = true; + this.selectedText = null; + this.range = null; + this.headerLevels = []; + } + + render() { + this.button = document.createElement('button'); + this.button.type = 'button'; + this.button.innerHTML = `${frappe.utils.icon('header', 'sm')}${frappe.utils.icon('small-down', 'xs')}`; + this.button.classList = 'header-inline-tool'; + + return this.button; + } + + checkState(selection) { + let termWrapper = this.api.selection.findParentTag('SPAN'); + + for (const h of ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']) { + if (termWrapper && termWrapper.classList.contains(h)) { + let num = h.match(/\d+/)[0]; + $('.header-inline-tool svg:first-child').replaceWith(frappe.utils.icon(`header-${num}`, 'md')); + } + } + + const text = selection.anchorNode; + if (!text) return; + } + + change_size(range, size) { + if (!range) return + + let span = document.createElement('SPAN'); + + span.classList.add(`h${size}`); + span.innerText = range.toString(); + + this.remove_parent_tag(range, range.commonAncestorContainer, span); + + range.extractContents(); + range.insertNode(span); + this.api.inlineToolbar.close(); + } + + remove_parent_tag(range, parent_node, span) { + let diff = range.startContainer.data; + let selected_text = span.innerText; + let parent_tag = parent_node.parentElement; + + if(diff !== selected_text) { + parent_tag = parent_node; + } + + if(parent_tag.innerText == selected_text) { + if (!parent_tag.classList.contains('ce-header') && !parent_tag.classList.contains('ce-paragraph')) { + this.remove_parent_tag(range, parent_node.parentElement, span); + parent_tag.remove(); + } + } + } + + surround(range) { + this.selectedText = range.cloneContents(); + this.actions.hidden = !this.actions.hidden; + this.range = !this.actions.hidden ? range : null; + this.state = !this.actions.hidden; + } + + renderActions() { + this.actions = document.createElement('div'); + this.actions.classList = 'header-level-select'; + + this.headerLevels = new Array(6).fill().map((_, idx) => { + const $header_level = document.createElement('div'); + $header_level.classList.add(`h${idx+1}`, 'header-level'); + $header_level.innerText = `Header ${idx+1}`; + return $header_level; + }); + + for (const [i, headerLevel] of this.headerLevels.entries()) { + this.actions.appendChild(headerLevel); + this.api.listeners.on(headerLevel, 'click', () => { + this.change_size(this.range, i+1); + }); + } + + this.actions.hidden = true; + return this.actions; + } + + destroy() { + for (const headerLevel of this.headerLevels) { + this.api.listeners.off(headerLevel, 'click'); + } + } +} \ No newline at end of file diff --git a/frappe/public/js/frappe/views/workspace/blocks/index.js b/frappe/public/js/frappe/views/workspace/blocks/index.js index 00a9b8c83a..4a9604840f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/index.js +++ b/frappe/public/js/frappe/views/workspace/blocks/index.js @@ -9,6 +9,7 @@ import Onboarding from "./onboarding"; // import tunes import SpacingTune from "./spacing_tune"; +import HeaderSize from "./header_size"; frappe.provide("frappe.wspace_block"); @@ -23,5 +24,6 @@ frappe.wspace_block.blocks = { }; frappe.wspace_block.tunes = { - spacing_tune: SpacingTune + spacing_tune: SpacingTune, + header_size: HeaderSize, }; \ No newline at end of file diff --git a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js index 70287ab088..dd4abd7a5f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js +++ b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js @@ -56,7 +56,7 @@ export default class Onboarding extends Block { this.block_widget.customize(this.options); this.wrapper.setAttribute(block_name, this.block_widget.label || this.block_widget.onboarding_name); this.new_block_widget = this.block_widget.get_config(); - this.add_tune_button(); + this.add_settings_button(); }, }); @@ -106,7 +106,7 @@ export default class Onboarding extends Block { } if (!this.readOnly) { - this.add_tune_button(); + // this.add_tune_button(); } $(this.wrapper).css("padding-bottom", "20px"); return this.wrapper; diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index 4f9a325fb7..4b143e6e88 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -55,7 +55,7 @@ export default class Paragraph extends Block { render() { this.wrapper = document.createElement('div'); if (!this.readOnly) { - let $para_control = $(`
`); + let $para_control = $(`
`); this.wrapper.appendChild(this._element); this._element.classList.remove('widget'); @@ -63,25 +63,26 @@ export default class Paragraph extends Block { this.wrapper.classList.add('widget', 'paragraph'); - frappe.utils.add_custom_button( - frappe.utils.icon('dot-horizontal', 'xs'), - (event) => { - let evn = event; - !$('.ce-settings.ce-settings--opened').length && - setTimeout(() => { - this.api.toolbar.toggleBlockSettings(); - var position = $(evn.target).offset(); - $('.ce-settings.ce-settings--opened').offset({ - top: position.top + 25, - left: position.left - 77 - }); - }, 50); - }, - "tune-btn", - `${__('Tune')}`, - null, - $para_control - ); + this.add_settings_button(); + // frappe.utils.add_custom_button( + // frappe.utils.icon('dot-horizontal', 'xs'), + // (event) => { + // let evn = event; + // !$('.ce-settings.ce-settings--opened').length && + // setTimeout(() => { + // this.api.toolbar.toggleBlockSettings(); + // var position = $(evn.target).offset(); + // $('.ce-settings.ce-settings--opened').offset({ + // top: position.top + 25, + // left: position.left - 77 + // }); + // }, 50); + // }, + // "tune-btn", + // `${__('Tune')}`, + // null, + // $para_control + // ); frappe.utils.add_custom_button( frappe.utils.icon('drag', 'xs'), @@ -92,14 +93,14 @@ export default class Paragraph extends Block { $para_control ); - frappe.utils.add_custom_button( - frappe.utils.icon('delete', 'xs'), - () => this.api.blocks.delete(), - "delete-paragraph", - `${__('Delete')}`, - null, - $para_control - ); + // frappe.utils.add_custom_button( + // frappe.utils.icon('delete-active', 'xs'), + // () => this.api.blocks.delete(), + // "delete-paragraph", + // `${__('Delete')}`, + // null, + // $para_control + // ); return this.wrapper; } @@ -144,12 +145,12 @@ export default class Paragraph extends Block { this.data = data; } - static get conversionConfig() { - return { - export: 'text', // to convert Paragraph to other block, use 'text' property of saved data - import: 'text' // to covert other block's exported string to Paragraph, fill 'text' property of tool data - }; - } + // static get conversionConfig() { + // return { + // export: 'text', // to convert Paragraph to other block, use 'text' property of saved data + // import: 'text' // to covert other block's exported string to Paragraph, fill 'text' property of tool data + // }; + // } static get sanitize() { return { @@ -157,7 +158,8 @@ export default class Paragraph extends Block { br: true, b: true, i: true, - a: true + a: true, + span: true } }; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js index 6e4f187d1b..4dbc194fdf 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js +++ b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js @@ -34,7 +34,7 @@ export default class Shortcut extends Block { } if (!this.readOnly) { - this.add_tune_button(); + this.add_settings_button(); } return this.wrapper; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/spacer.js b/frappe/public/js/frappe/views/workspace/blocks/spacer.js index 6e1b47f1d3..940f62ffdd 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/spacer.js +++ b/frappe/public/js/frappe/views/workspace/blocks/spacer.js @@ -33,25 +33,26 @@ export default class Spacer extends Block { let $widget_control = $spacer.find('.widget-control'); - frappe.utils.add_custom_button( - frappe.utils.icon('dot-horizontal', 'xs'), - (event) => { - let evn = event; - !$('.ce-settings.ce-settings--opened').length && - setTimeout(() => { - this.api.toolbar.toggleBlockSettings(); - var position = $(evn.target).offset(); - $('.ce-settings.ce-settings--opened').offset({ - top: position.top + 25, - left: position.left - 77 - }); - }, 50); - }, - "tune-btn", - `${__('Tune')}`, - null, - $widget_control - ); + this.add_settings_button(); + // frappe.utils.add_custom_button( + // frappe.utils.icon('dot-horizontal', 'xs'), + // (event) => { + // let evn = event; + // !$('.ce-settings.ce-settings--opened').length && + // setTimeout(() => { + // this.api.toolbar.toggleBlockSettings(); + // var position = $(evn.target).offset(); + // $('.ce-settings.ce-settings--opened').offset({ + // top: position.top + 25, + // left: position.left - 77 + // }); + // }, 50); + // }, + // "tune-btn", + // `${__('Tune')}`, + // null, + // $widget_control + // ); frappe.utils.add_custom_button( frappe.utils.icon('drag', 'xs'), @@ -62,14 +63,14 @@ export default class Spacer extends Block { $widget_control ); - frappe.utils.add_custom_button( - frappe.utils.icon('delete', 'xs'), - () => this.api.blocks.delete(), - "delete-spacer", - `${__('Delete')}`, - null, - $widget_control - ); + // frappe.utils.add_custom_button( + // frappe.utils.icon('delete-active', 'xs'), + // () => this.api.blocks.delete(), + // "delete-spacer", + // `${__('Delete')}`, + // null, + // $widget_control + // ); } return this.wrapper; } diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index e6248f66cf..72430f8754 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -433,7 +433,7 @@ frappe.views.Workspace = class Workspace { sidebar_control ); frappe.utils.add_custom_button( - frappe.utils.icon('delete', 'xs'), + frappe.utils.icon('delete-active', 'xs'), () => this.delete_page(item), "delete-page", `${__('Delete')}`, @@ -643,14 +643,14 @@ frappe.views.Workspace = class Workspace { this.tools = { header: { class: this.blocks['header'], - inlineToolbar: true, + inlineToolbar: ['HeaderSize', 'bold', 'italic', 'link'], config: { - defaultLevel: 4 + default_size: 4 } }, paragraph: { class: this.blocks['paragraph'], - inlineToolbar: true + inlineToolbar: ['HeaderSize', 'bold', 'italic', 'link'] }, chart: { class: this.blocks['chart'], @@ -677,7 +677,7 @@ frappe.views.Workspace = class Workspace { } }, spacer: this.blocks['spacer'], - spacingTune: frappe.wspace_block.tunes['spacing_tune'], + HeaderSize: frappe.wspace_block.tunes['header_size'], }; this.editor = new EditorJS({ data: { @@ -685,7 +685,6 @@ frappe.views.Workspace = class Workspace { }, tools: this.tools, autofocus: false, - tunes: ['spacingTune'], readOnly: true, logLevel: 'ERROR' }); diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js index e6ae64d9dc..4950ea21b5 100644 --- a/frappe/public/js/frappe/widgets/base_widget.js +++ b/frappe/public/js/frappe/widgets/base_widget.js @@ -34,15 +34,15 @@ export default class Widget { this.action_area ); - options.allow_delete && - frappe.utils.add_custom_button( - frappe.utils.icon('delete', 'xs'), - () => this.delete(), - "", - `${__('Delete')}`, - null, - this.action_area - ); + // options.allow_delete && + // frappe.utils.add_custom_button( + // frappe.utils.icon('delete-active', 'xs'), + // () => this.delete(), + // "", + // `${__('Delete')}`, + // null, + // this.action_area + // ); if (options.allow_hiding) { if (this.hidden) { diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index daedbe1877..7da24ce031 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -905,6 +905,10 @@ body { padding-left: 0; padding-right: 0; + b span { + font-weight: 700 !important; + } + &.ce-block--selected { .ce-block__content { background-color: inherit; @@ -1049,6 +1053,10 @@ body { .icon { fill: currentColor; } + + svg { + stroke: none; + } } @media (min-width: 1199px) { @@ -1083,4 +1091,62 @@ body { } } + + .cdx-marker { + background: rgba(245,235,111,0.29); + padding: 3px 0; + } + + .header-inline-tool { + border: none; + background-color: transparent; + margin-bottom: 2px; + } + + .header-level-select { + display: flex; + flex-direction: column; + padding: 6px; + } + + .header-level-select .header-level { + border: none; + background-color: transparent; + border-radius: var(--border-radius-sm); + padding: 6px; + margin: 2px 0px; + + &:hover { + background-color: var(--fg-hover-color); + } + } + + .dropdown-btn { + position: relative; + } + + .dropdown-list { + position: absolute; + background-color: white; + box-shadow: var(--shadow-base); + border-radius: var(--border-radius-sm); + padding: 6px; + top: 30px; + right: 0; + width: 150px; + z-index: 1; + } + + .dropdown-list .dropdown-item { + cursor: pointer; + padding: 6px 10px; + font-size: small; + border-radius: var(--border-radius-sm); + margin: 1px 0px; + } + + .dropdown-item-icon { + margin-right: 5px; + } + } From 20aac01874fc952220d9f7a4f8c2d643dc385e09 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 29 Nov 2021 11:58:25 +0530 Subject: [PATCH 005/101] feat: Add new block new design --- frappe/public/icons/timeless/symbol-defs.svg | 13 +++ .../js/frappe/views/workspace/blocks/block.js | 41 ++++++--- .../js/frappe/views/workspace/blocks/card.js | 3 +- .../js/frappe/views/workspace/blocks/chart.js | 3 +- .../frappe/views/workspace/blocks/header.js | 3 +- .../views/workspace/blocks/onboarding.js | 5 +- .../views/workspace/blocks/paragraph.js | 86 ++++++++++-------- .../frappe/views/workspace/blocks/shortcut.js | 3 +- .../frappe/views/workspace/blocks/spacer.js | 33 +------ .../js/frappe/views/workspace/workspace.js | 17 +--- .../public/js/frappe/widgets/base_widget.js | 2 +- frappe/public/scss/desk/desktop.scss | 88 ++++++++++++++++++- 12 files changed, 195 insertions(+), 102 deletions(-) diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index 1b3a945ecb..f2977e3016 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -858,6 +858,18 @@ fill="#70818F" stroke="none">
+ + + + + + + + + + + + @@ -928,4 +940,5 @@ + diff --git a/frappe/public/js/frappe/views/workspace/blocks/block.js b/frappe/public/js/frappe/views/workspace/blocks/block.js index c0f0243224..e2424eb1fb 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/block.js +++ b/frappe/public/js/frappe/views/workspace/blocks/block.js @@ -54,6 +54,7 @@ export default class Block { function do_drag(e) { $(this).css("cursor", "col-resize"); $('.widget').css("pointer-events", "none"); + $(me.wrapper.parentElement).find('.resizer').css("border-right", "3px solid lightgray"); un_focus(); if ((startWidth + e.clientX - startX) - startWidth > 60) { startX = e.clientX; @@ -76,6 +77,7 @@ export default class Block { function stop_drag(e) { $(this).css("cursor", "default"); $('.widget').css("pointer-events", "auto"); + $(me.wrapper.parentElement).find('.resizer').css("border-right", "0px solid transparent"); document.documentElement.removeEventListener('mousemove', do_drag, false); document.documentElement.removeEventListener('mouseup', stop_drag, false); @@ -123,6 +125,21 @@ export default class Block { this.new_block_widget = block_obj.get_config(); } + add_new_block_button() { + let $new_button = $(` +
${frappe.utils.icon('add-round', 'lg')}
+ `); + + $new_button.appendTo(this.wrapper); + + $new_button.click(event => { + event.stopPropagation(); + let index = this.api.blocks.getCurrentBlockIndex(); + this.api.blocks.insert('paragraph', {}, {}, index); + this.api.caret.setToBlock(index); + }); + } + add_settings_button() { this.dropdown_list = [ { @@ -131,18 +148,6 @@ export default class Block { icon: frappe.utils.icon('delete-active', 'sm'), action: () => this.api.blocks.delete() }, - { - label: 'Move Up', - title: 'Move Up', - icon: frappe.utils.icon('up-arrow', 'sm'), - action: () => this.move_block('up') - }, - { - label: 'Move Down', - title: 'Move Down', - icon: frappe.utils.icon('down-arrow', 'sm'), - action: () => this.move_block('down') - }, { label: 'Expand', title: 'Expand Block', @@ -154,6 +159,18 @@ export default class Block { title: 'Shrink Block', icon: frappe.utils.icon('shrink', 'sm'), action: () => this.decrease_width() + }, + { + label: 'Move Up', + title: 'Move Up', + icon: frappe.utils.icon('up-arrow', 'sm'), + action: () => this.move_block('up') + }, + { + label: 'Move Down', + title: 'Move Down', + icon: frappe.utils.icon('down-arrow', 'sm'), + action: () => this.move_block('down') } ] diff --git a/frappe/public/js/frappe/views/workspace/blocks/card.js b/frappe/public/js/frappe/views/workspace/blocks/card.js index 7e67c3fb0c..d179eb42e6 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/card.js +++ b/frappe/public/js/frappe/views/workspace/blocks/card.js @@ -3,7 +3,7 @@ export default class Card extends Block { static get toolbox() { return { title: 'Card', - icon: frappe.utils.icon('card', 'md') + icon: frappe.utils.icon('card', 'sm') }; } @@ -36,6 +36,7 @@ export default class Card extends Block { if (!this.readOnly) { this.add_settings_button(); + this.add_new_block_button(); } return this.wrapper; diff --git a/frappe/public/js/frappe/views/workspace/blocks/chart.js b/frappe/public/js/frappe/views/workspace/blocks/chart.js index 8d9bb89825..956020fc62 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/chart.js +++ b/frappe/public/js/frappe/views/workspace/blocks/chart.js @@ -3,7 +3,7 @@ export default class Chart extends Block { static get toolbox() { return { title: 'Chart', - icon: frappe.utils.icon('chart', 'md') + icon: frappe.utils.icon('chart', 'sm') }; } @@ -36,6 +36,7 @@ export default class Chart extends Block { if (!this.readOnly) { this.add_settings_button(); + this.add_new_block_button(); } return this.wrapper; diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index dacd3e6a7d..69f5eea0a2 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -38,6 +38,7 @@ export default class Header extends Block { this.wrapper.classList.add('widget', 'header'); this.add_settings_button(); + this.add_new_block_button(); frappe.utils.add_custom_button( frappe.utils.icon('drag', 'xs'), @@ -137,7 +138,7 @@ export default class Header extends Block { static get toolbox() { return { title: 'Heading', - icon: frappe.utils.icon('header', 'md') + icon: frappe.utils.icon('header', 'sm') }; } } \ No newline at end of file diff --git a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js index dd4abd7a5f..7a9c6b7752 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js +++ b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js @@ -4,7 +4,7 @@ export default class Onboarding extends Block { static get toolbox() { return { title: 'Onboarding', - icon: frappe.utils.icon('onboarding', 'md') + icon: frappe.utils.icon('onboarding', 'sm') }; } @@ -106,7 +106,8 @@ export default class Onboarding extends Block { } if (!this.readOnly) { - // this.add_tune_button(); + this.add_settings_button(); + this.add_new_block_button(); } $(this.wrapper).css("padding-bottom", "20px"); return this.wrapper; diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index 4b143e6e88..d7fe4c5023 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -27,6 +27,7 @@ export default class Paragraph extends Block { } onKeyUp(e) { + $(this.wrapper.parentElement).find('.block-list-container.dropdown-list').hide(); if (e.code !== 'Backspace' && e.code !== 'Delete') { return; } @@ -34,6 +35,7 @@ export default class Paragraph extends Block { const {textContent} = this._element; if (textContent === '') { + $(this.wrapper.parentElement).find('.block-list-container.dropdown-list').show(); this._element.innerHTML = ''; } } @@ -43,15 +45,58 @@ export default class Paragraph extends Block { div.classList.add(this._CSS.wrapper, this._CSS.block, 'widget'); div.contentEditable = false; - div.dataset.placeholder = this.api.i18n.t(this._placeholder); if (!this.readOnly) { div.contentEditable = true; + div.addEventListener('focus', () => { + const {textContent} = this._element; + if (textContent !== '') return; + let $wrapper = $(this.wrapper).hasClass('ce-paragraph') ? $(this.wrapper.parentElement) : $(this.wrapper); + let $block_list_container = $wrapper.find('.block-list-container.dropdown-list'); + $block_list_container.show(); + }); + div.addEventListener('blur', () => { + let $block_list_container = $(this.wrapper.parentElement).find('.block-list-container.dropdown-list'); + setTimeout(() => $block_list_container.hide(), 1); + }); + div.dataset.placeholder = this.api.i18n.t(this._placeholder); div.addEventListener('keyup', this.onKeyUp); } return div; } + open_block_list() { + let dropdown_title = 'Templates'; + let $block_list_container = $(` + + `); + + let all_blocks = frappe.wspace_block.blocks; + Object.keys(all_blocks).forEach(key => { + let $block_list_item = $(` + + `); + + $block_list_item.click(event => { + event.stopPropagation(); + const index = this.api.blocks.getCurrentBlockIndex(); + this.api.blocks.delete(); + this.api.blocks.insert(key, {}, {}, index); + this.api.caret.setToBlock(index); + }); + + $block_list_container.append($block_list_item); + }); + + $block_list_container.hide(); + $block_list_container.appendTo(this.wrapper); + } + render() { this.wrapper = document.createElement('div'); if (!this.readOnly) { @@ -63,26 +108,9 @@ export default class Paragraph extends Block { this.wrapper.classList.add('widget', 'paragraph'); + this.open_block_list(); + this.add_new_block_button(); this.add_settings_button(); - // frappe.utils.add_custom_button( - // frappe.utils.icon('dot-horizontal', 'xs'), - // (event) => { - // let evn = event; - // !$('.ce-settings.ce-settings--opened').length && - // setTimeout(() => { - // this.api.toolbar.toggleBlockSettings(); - // var position = $(evn.target).offset(); - // $('.ce-settings.ce-settings--opened').offset({ - // top: position.top + 25, - // left: position.left - 77 - // }); - // }, 50); - // }, - // "tune-btn", - // `${__('Tune')}`, - // null, - // $para_control - // ); frappe.utils.add_custom_button( frappe.utils.icon('drag', 'xs'), @@ -93,15 +121,6 @@ export default class Paragraph extends Block { $para_control ); - // frappe.utils.add_custom_button( - // frappe.utils.icon('delete-active', 'xs'), - // () => this.api.blocks.delete(), - // "delete-paragraph", - // `${__('Delete')}`, - // null, - // $para_control - // ); - return this.wrapper; } return this._element; @@ -145,13 +164,6 @@ export default class Paragraph extends Block { this.data = data; } - // static get conversionConfig() { - // return { - // export: 'text', // to convert Paragraph to other block, use 'text' property of saved data - // import: 'text' // to covert other block's exported string to Paragraph, fill 'text' property of tool data - // }; - // } - static get sanitize() { return { text: { @@ -191,7 +203,7 @@ export default class Paragraph extends Block { static get toolbox() { return { title: 'Text', - icon: frappe.utils.icon('text', 'md') + icon: frappe.utils.icon('text', 'sm') }; } } \ No newline at end of file diff --git a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js index 4dbc194fdf..8b1a059eed 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js +++ b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js @@ -3,7 +3,7 @@ export default class Shortcut extends Block { static get toolbox() { return { title: 'Shortcut', - icon: frappe.utils.icon('shortcut', 'md') + icon: frappe.utils.icon('shortcut', 'sm') }; } @@ -35,6 +35,7 @@ export default class Shortcut extends Block { if (!this.readOnly) { this.add_settings_button(); + this.add_new_block_button(); } return this.wrapper; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/spacer.js b/frappe/public/js/frappe/views/workspace/blocks/spacer.js index 940f62ffdd..4eff42080f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/spacer.js +++ b/frappe/public/js/frappe/views/workspace/blocks/spacer.js @@ -3,7 +3,7 @@ export default class Spacer extends Block { static get toolbox() { return { title: 'Spacer', - icon: frappe.utils.icon('spacer', 'md') + icon: frappe.utils.icon('spacer', 'sm') }; } @@ -21,7 +21,7 @@ export default class Spacer extends Block { if (!this.readOnly) { let $spacer = $(`
-
+
Spacer
@@ -34,25 +34,7 @@ export default class Spacer extends Block { let $widget_control = $spacer.find('.widget-control'); this.add_settings_button(); - // frappe.utils.add_custom_button( - // frappe.utils.icon('dot-horizontal', 'xs'), - // (event) => { - // let evn = event; - // !$('.ce-settings.ce-settings--opened').length && - // setTimeout(() => { - // this.api.toolbar.toggleBlockSettings(); - // var position = $(evn.target).offset(); - // $('.ce-settings.ce-settings--opened').offset({ - // top: position.top + 25, - // left: position.left - 77 - // }); - // }, 50); - // }, - // "tune-btn", - // `${__('Tune')}`, - // null, - // $widget_control - // ); + this.add_new_block_button(); frappe.utils.add_custom_button( frappe.utils.icon('drag', 'xs'), @@ -62,15 +44,6 @@ export default class Spacer extends Block { null, $widget_control ); - - // frappe.utils.add_custom_button( - // frappe.utils.icon('delete-active', 'xs'), - // () => this.api.blocks.delete(), - // "delete-spacer", - // `${__('Delete')}`, - // null, - // $widget_control - // ); } return this.wrapper; } diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 72430f8754..e439ad5e24 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -395,18 +395,6 @@ frappe.views.Workspace = class Workspace { page.name && this.page.add_inner_button(__("Settings"), () => { frappe.set_route(`workspace/${page.name}`); }); - - Object.keys(this.blocks).forEach(key => { - this.page.add_inner_button(` - ${this.blocks[key].toolbox.icon} - ${__(this.blocks[key].toolbox.title)} - `, function() { - const index = me.editor.blocks.getBlocksCount() + 1; - me.editor.blocks.insert(key, {}, {}, index, true); - me.editor.caret.setToLastBlock('start', 0); - $('.ce-block:last-child')[0].scrollIntoView(); - }, __('Add Block')); - }); } show_sidebar_actions() { @@ -650,7 +638,10 @@ frappe.views.Workspace = class Workspace { }, paragraph: { class: this.blocks['paragraph'], - inlineToolbar: ['HeaderSize', 'bold', 'italic', 'link'] + inlineToolbar: ['HeaderSize', 'bold', 'italic', 'link'], + config: { + placeholder: 'Choose a block or continue typing' + } }, chart: { class: this.blocks['chart'], diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js index 4950ea21b5..3fc4dac935 100644 --- a/frappe/public/js/frappe/widgets/base_widget.js +++ b/frappe/public/js/frappe/widgets/base_widget.js @@ -71,7 +71,7 @@ export default class Widget { frappe.utils.add_custom_button( frappe.utils.icon("edit", "xs"), () => this.edit(), - null, + "edit-button", `${__('Edit')}`, null, this.action_area diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 7da24ce031..ee32b88822 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -783,8 +783,8 @@ body { } } - .layout-side-section { - margin-right: 20px; + .layout-main-section-wrapper { + padding-left: 35px; } .desk-sidebar { @@ -909,6 +909,36 @@ body { font-weight: 700 !important; } + .new-block-button { + position: absolute; + top: 7px; + left: -22px; + cursor: pointer; + visibility: hidden; + opacity: 0; + transition: visibility 0s, opacity 0.5s ease-in-out; + } + + .widget-control > *, .paragraph-control > * { + visibility: hidden; + opacity: 0; + transition: visibility 0s, opacity 0.5s ease-in-out; + } + + &:hover { + .widget-control > *, .new-block-button { + visibility: visible; + opacity: 1; + } + } + + &.ce-block--focused { + .widget-control > * { + visibility: visible; + opacity: 1; + } + } + &.ce-block--selected { .ce-block__content { background-color: inherit; @@ -934,7 +964,13 @@ body { position:absolute; right: 0; bottom: 0; - cursor: col-resize; + cursor: col-resize; + border-color: transparent; + transition: border-color 0.3s ease-in-out; + + &:hover { + border-right: 3px solid lightgray !important; + } } .ce-header { @@ -947,6 +983,22 @@ body { } } + .block-list-container { + left: 20px; + top: 55px !important; + width: 200px !important; + } + + .dropdown-title { + padding: 6px 10px; + font-size: smaller; + cursor: default; + } + + .ce-paragraph[data-placeholder]:empty::before { + opacity: 1; + } + .widget{ &.header { display: flex; @@ -967,6 +1019,10 @@ body { &.new-widget { align-items: inherit; + + .spacer-left { + min-width: 74px; + } } &.ce-paragraph { @@ -1001,6 +1057,11 @@ body { } .ce-toolbar { + + &.ce-toolbar--opened { + display: none; + } + svg { fill: currentColor; } @@ -1077,6 +1138,27 @@ body { } } + @media (max-width: 995px) { + .ce-block.col-3 { + flex: 0 0 100%; + max-width: 33.33%; + } + } + + @media (max-width: 750px) { + .ce-block.col-3 { + flex: 0 0 50%; + max-width: 50%; + } + } + + @media (max-width: 550px) { + .ce-block.col-3 { + flex: 0 0 100%; + max-width: 100%; + } + } + @media (max-width: 750px) { .ce-block.col-4 { flex: 0 0 100%; From 279d30b3212b24837971d3921465dd0d32647bbd Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 30 Nov 2021 12:01:58 +0530 Subject: [PATCH 006/101] feat: single card design --- .../js/frappe/views/workspace/blocks/block.js | 3 +- .../js/frappe/views/workspace/blocks/card.js | 1 + .../js/frappe/views/workspace/blocks/chart.js | 1 + .../frappe/views/workspace/blocks/header.js | 2 +- .../views/workspace/blocks/onboarding.js | 2 + .../views/workspace/blocks/paragraph.js | 2 +- .../frappe/views/workspace/blocks/shortcut.js | 8 +- .../frappe/views/workspace/blocks/spacer.js | 4 +- .../public/js/frappe/widgets/base_widget.js | 10 +- frappe/public/scss/desk/desktop.scss | 155 ++++++++++++++---- 10 files changed, 143 insertions(+), 45 deletions(-) diff --git a/frappe/public/js/frappe/views/workspace/blocks/block.js b/frappe/public/js/frappe/views/workspace/blocks/block.js index e2424eb1fb..e7a6240340 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/block.js +++ b/frappe/public/js/frappe/views/workspace/blocks/block.js @@ -104,6 +104,7 @@ export default class Block { }); this.block_widget.customize(this.options); this.wrapper.setAttribute(block_name, this.block_widget.label); + $(this.wrapper).find('.widget').addClass(`${widget_type} edit-mode`); this.new_block_widget = this.block_widget.get_config(); this.add_settings_button(); }, @@ -134,7 +135,7 @@ export default class Block { $new_button.click(event => { event.stopPropagation(); - let index = this.api.blocks.getCurrentBlockIndex(); + let index = this.api.blocks.getCurrentBlockIndex() + 1; this.api.blocks.insert('paragraph', {}, {}, index); this.api.caret.setToBlock(index); }); diff --git a/frappe/public/js/frappe/views/workspace/blocks/card.js b/frappe/public/js/frappe/views/workspace/blocks/card.js index d179eb42e6..cfcf530fc3 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/card.js +++ b/frappe/public/js/frappe/views/workspace/blocks/card.js @@ -35,6 +35,7 @@ export default class Card extends Block { } if (!this.readOnly) { + $(this.wrapper).find('.widget').addClass('links edit-mode'); this.add_settings_button(); this.add_new_block_button(); } diff --git a/frappe/public/js/frappe/views/workspace/blocks/chart.js b/frappe/public/js/frappe/views/workspace/blocks/chart.js index 956020fc62..c132b7e5ec 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/chart.js +++ b/frappe/public/js/frappe/views/workspace/blocks/chart.js @@ -35,6 +35,7 @@ export default class Chart extends Block { } if (!this.readOnly) { + $(this.wrapper).find('.widget').addClass('chart edit-mode'); this.add_settings_button(); this.add_new_block_button(); } diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index 69f5eea0a2..634ab2f45c 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -35,7 +35,7 @@ export default class Header extends Block { $widget_control.appendTo($widget_head); $widget_head.appendTo(this.wrapper); - this.wrapper.classList.add('widget', 'header'); + this.wrapper.classList.add('widget', 'header', 'edit-mode'); this.add_settings_button(); this.add_new_block_button(); diff --git a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js index 7a9c6b7752..54b6421048 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/onboarding.js +++ b/frappe/public/js/frappe/views/workspace/blocks/onboarding.js @@ -55,6 +55,7 @@ export default class Onboarding extends Block { }); this.block_widget.customize(this.options); this.wrapper.setAttribute(block_name, this.block_widget.label || this.block_widget.onboarding_name); + $(this.wrapper).find('.widget').addClass(`${widget_type} edit-mode`); this.new_block_widget = this.block_widget.get_config(); this.add_settings_button(); }, @@ -106,6 +107,7 @@ export default class Onboarding extends Block { } if (!this.readOnly) { + $(this.wrapper).find('.widget').addClass('onboarding edit-mode'); this.add_settings_button(); this.add_new_block_button(); } diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index d7fe4c5023..be6ba94c24 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -106,7 +106,7 @@ export default class Paragraph extends Block { this._element.classList.remove('widget'); $para_control.appendTo(this.wrapper); - this.wrapper.classList.add('widget', 'paragraph'); + this.wrapper.classList.add('widget', 'paragraph', 'edit-mode'); this.open_block_list(); this.add_new_block_button(); diff --git a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js index 8b1a059eed..ff2b99d8f0 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js +++ b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js @@ -13,7 +13,7 @@ export default class Shortcut extends Block { constructor({ data, api, config, readOnly, block }) { super({ data, api, config, readOnly, block }); - this.col = this.data.col ? this.data.col : "4"; + this.col = this.data.col ? this.data.col : "3"; this.allow_customization = !this.readOnly; this.options = { allow_sorting: this.allow_customization, @@ -34,8 +34,14 @@ export default class Shortcut extends Block { } if (!this.readOnly) { + $(this.wrapper).find('.widget').addClass('shortcut edit-mode'); this.add_settings_button(); this.add_new_block_button(); + } else { + let $shortcut_icon = frappe.utils.icon('arrow-up-right', 'sm', '', 'stroke: grey'); + $(this.wrapper).find('.widget .widget-title').append($shortcut_icon); + + $(this.wrapper).append($(`
`)); } return this.wrapper; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/spacer.js b/frappe/public/js/frappe/views/workspace/blocks/spacer.js index 4eff42080f..7f4fa2e5b5 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/spacer.js +++ b/frappe/public/js/frappe/views/workspace/blocks/spacer.js @@ -28,8 +28,8 @@ export default class Spacer extends Block { `); $spacer.appendTo(this.wrapper); - this.wrapper.classList.add('widget', 'new-widget'); - this.wrapper.style.minHeight = 50 + 'px'; + this.wrapper.classList.add('widget', 'spacer', 'edit-mode'); + this.wrapper.style.minHeight = 40 + 'px'; let $widget_control = $spacer.find('.widget-control'); diff --git a/frappe/public/js/frappe/widgets/base_widget.js b/frappe/public/js/frappe/widgets/base_widget.js index 3fc4dac935..7376ecb89b 100644 --- a/frappe/public/js/frappe/widgets/base_widget.js +++ b/frappe/public/js/frappe/widgets/base_widget.js @@ -100,9 +100,7 @@ export default class Widget { } make_widget() { - this.widget = $(`
+ this.widget = $(`
@@ -110,10 +108,8 @@ export default class Widget {
-
-
- +
+
`); this.title_field = this.widget.find(".widget-title"); diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index ee32b88822..57c0a56a59 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -107,13 +107,20 @@ body { } } +.divider { + height: 30%; + position: absolute; + top: 18px; + right: 0; + border-right: 1px solid lightgrey; +} + .widget { @include flex(flex, null, null, column); min-height: 1px; - padding: 15px; + padding: 7px; border-radius: var(--border-radius-md); height: 100%; - box-shadow: var(--card-shadow); background-color: var(--card-bg); .btn { @@ -333,6 +340,26 @@ body { margin-top: var(--margin-xs); margin-bottom: var(--margin-2xl); padding: var(--padding-lg); + background-color: var(--bg-light-gray); + + &.edit-mode:hover { + background-color: white; + + .onboarding-step { + &.active, + &:hover { + background-color: var(--bg-light-gray); + + .step-index.step-pending { + background-color: var(--fg-color); + } + } + + .step-index { + background-color: var(--bg-light-gray); + } + } + } .widget-head { display: flex; @@ -391,12 +418,6 @@ body { .step-index.step-pending { display: flex; } - - &.active { - .step-index.step-pending { - background-color: var(--fg-color); - } - } } &.complete { @@ -419,7 +440,11 @@ body { &.active, &:hover { - background-color: var(--bg-light-gray); + background-color: white; + + .step-index { + background-color: var(--bg-light-gray); + } .step-skip { visibility: visible; @@ -435,7 +460,7 @@ body { height: 20px; width: 20px; color: var(--text-on-light-gray); - background-color: var(--bg-light-gray); + background-color: white; margin-right: var(--margin-sm); border-radius: var(--border-radius-full); @@ -514,9 +539,18 @@ body { &.shortcut-widget-box { cursor: pointer; - .widget-head { - margin-top: var(--margin-xs); - margin-bottom: 5px; + &:hover { + .widget-title { + color: var(--blue-500) !important; + } + + svg.icon-sm { + stroke: var(--blue-500) !important; + } + } + + .widget-title { + gap: 10px; } .indicator-pill { @@ -765,7 +799,6 @@ body { .layout-side-section, .layout-main-section-wrapper { height: 100%; overflow-y: auto; - padding-right: 25px; scrollbar-color: var(--gray-200) transparent; [data-theme="dark"] & { scrollbar-color: var(--gray-800) transparent; @@ -783,8 +816,11 @@ body { } } - .layout-main-section-wrapper { - padding-left: 35px; + .layout-main-section { + background-color: white; + border-radius: var(--border-radius-md); + padding: var(--padding-sm) var(--padding-md); + margin-bottom: var(--margin-sm); } .desk-sidebar { @@ -911,7 +947,7 @@ body { .new-block-button { position: absolute; - top: 7px; + top: 16px; left: -22px; cursor: pointer; visibility: hidden; @@ -919,23 +955,55 @@ body { transition: visibility 0s, opacity 0.5s ease-in-out; } - .widget-control > *, .paragraph-control > * { - visibility: hidden; - opacity: 0; - transition: visibility 0s, opacity 0.5s ease-in-out; + .edit-mode { + .widget-control > *, .paragraph-control > * { + width: 0px; + visibility: hidden; + opacity: 0; + transition: visibility 0s, opacity 0.5s ease-in-out; + } } &:hover { .widget-control > *, .new-block-button { + width: auto; visibility: visible; opacity: 1; } } &.ce-block--focused { - .widget-control > * { - visibility: visible; - opacity: 1; + .widget { + box-shadow: var(--card-shadow) !important; + + .widget-control > * { + width: auto; + visibility: visible; + opacity: 1; + } + + &.shortcut, &.header { + background-color: var(--fg-color) !important; + } + + &.onboarding { + background-color: white; + + .onboarding-step { + &.active, + &:hover { + background-color: var(--bg-light-gray); + + .step-index.step-pending { + background-color: var(--fg-color); + } + } + + .step-index { + background-color: var(--bg-light-gray); + } + } + } } } @@ -974,7 +1042,7 @@ body { } .ce-header { - padding: 0 !important; + padding-left: 7px !important; margin-bottom: 0 !important; flex: 1; @@ -1000,31 +1068,54 @@ body { } .widget{ + + &.edit-mode:hover { + box-shadow: var(--shadow-base); + } + &.header { display: flex; justify-content: center; flex: 1; - padding-left: 15px !important; - padding-right: 15px !important; - min-height: 50px; + padding-left: 0px !important; + min-height: 40px; box-shadow: none; background-color: var(--control-bg); color: var(--text-muted); cursor: text; - } - &:focus { - outline: none; + &.edit-mode:hover { + background-color: var(--fg-color); + } } - &.new-widget { + &.spacer { align-items: inherit; + color: var(--text-muted); + border: 1px dashed var(--gray-400); + cursor: pointer; + + .widget-control > * { + width: auto; + } .spacer-left { min-width: 74px; } } + &.shortcut { + background-color: var(--control-bg); + + &:hover { + background-color: var(--fg-color); + } + } + + &:focus { + outline: none; + } + &.ce-paragraph { display: block; } From 2f19c572c4fd5d87d71f08026ab563919d71c3a1 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 30 Nov 2021 16:00:38 +0530 Subject: [PATCH 007/101] fix: removed unused code from container.js --- frappe/public/js/frappe/views/container.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/frappe/public/js/frappe/views/container.js b/frappe/public/js/frappe/views/container.js index cf1d6c9466..4a40452b35 100644 --- a/frappe/public/js/frappe/views/container.js +++ b/frappe/public/js/frappe/views/container.js @@ -39,12 +39,6 @@ frappe.views.Container = class Container { return page; } change_to(label) { - cur_page = this; - if(this.page && this.page.label === label) { - $(this.page).trigger('show'); - } - - var me = this; if(label.tagName) { // if sent the div, get the table var page = label; From 6c364e937c8c2caccce11ba3c3fc155a54b765f8 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 30 Nov 2021 16:01:58 +0530 Subject: [PATCH 008/101] fix: validate_link error while updating shortcut --- frappe/public/js/frappe/widgets/widget_dialog.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/widgets/widget_dialog.js b/frappe/public/js/frappe/widgets/widget_dialog.js index 9262627f02..53ab1b97d8 100644 --- a/frappe/public/js/frappe/widgets/widget_dialog.js +++ b/frappe/public/js/frappe/widgets/widget_dialog.js @@ -9,6 +9,7 @@ class WidgetDialog { this.setup_dialog_events(); this.dialog.show(); + window.cur_dialog = this.dialog; this.editing && this.set_default_values(); } From 171c93078cb2391aa52a66c2939d0e23b8a98f86 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 30 Nov 2021 16:03:18 +0530 Subject: [PATCH 009/101] fix: added title on card links --- frappe/public/js/frappe/widgets/links_widget.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/widgets/links_widget.js b/frappe/public/js/frappe/widgets/links_widget.js index cc771b96b5..3320e88bfb 100644 --- a/frappe/public/js/frappe/widgets/links_widget.js +++ b/frappe/public/js/frappe/widgets/links_widget.js @@ -80,7 +80,9 @@ export default class LinksWidget extends Widget { return $(` + } ${disabled_dependent(item)}" type="${item.type}" title="${ + item.label ? item.label : item.name + }"> ${get_link_for_item(item)} `); From 75fc42db6bdc75dd121fa3160ec8a659de076bb6 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 30 Nov 2021 16:04:14 +0530 Subject: [PATCH 010/101] fix: small fixes --- .../frappe/views/workspace/blocks/header.js | 2 +- .../frappe/views/workspace/blocks/shortcut.js | 2 +- .../frappe/views/workspace/blocks/spacer.js | 3 +- frappe/public/scss/desk/desktop.scss | 42 ++++++++++++------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index 634ab2f45c..6c11a0bb28 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -122,7 +122,7 @@ export default class Header extends Block { const tag = document.createElement('DIV'); let text = this._data.text || ' '; - tag.innerHTML = `${text}`; + tag.innerHTML = `${text}`; tag.classList.add('ce-header'); diff --git a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js index ff2b99d8f0..6a6704152f 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/shortcut.js +++ b/frappe/public/js/frappe/views/workspace/blocks/shortcut.js @@ -38,7 +38,7 @@ export default class Shortcut extends Block { this.add_settings_button(); this.add_new_block_button(); } else { - let $shortcut_icon = frappe.utils.icon('arrow-up-right', 'sm', '', 'stroke: grey'); + let $shortcut_icon = frappe.utils.icon('arrow-up-right', 'xs', '', 'stroke: grey', 'ml-2'); $(this.wrapper).find('.widget .widget-title').append($shortcut_icon); $(this.wrapper).append($(`
`)); diff --git a/frappe/public/js/frappe/views/workspace/blocks/spacer.js b/frappe/public/js/frappe/views/workspace/blocks/spacer.js index 7f4fa2e5b5..a2cc144885 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/spacer.js +++ b/frappe/public/js/frappe/views/workspace/blocks/spacer.js @@ -18,6 +18,7 @@ export default class Spacer extends Block { render() { this.wrapper = document.createElement('div'); + this.wrapper.classList.add('widget','spacer'); if (!this.readOnly) { let $spacer = $(`
@@ -28,7 +29,7 @@ export default class Spacer extends Block { `); $spacer.appendTo(this.wrapper); - this.wrapper.classList.add('widget', 'spacer', 'edit-mode'); + this.wrapper.classList.add('edit-mode'); this.wrapper.style.minHeight = 40 + 'px'; let $widget_control = $spacer.find('.widget-control'); diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 57c0a56a59..3d39b647cf 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -544,13 +544,13 @@ body { color: var(--blue-500) !important; } - svg.icon-sm { + svg.icon-xs { stroke: var(--blue-500) !important; } } .widget-title { - gap: 10px; + cursor: pointer !important; } .indicator-pill { @@ -816,6 +816,10 @@ body { } } + .layout-side-section { + padding-right: 15px; + } + .layout-main-section { background-color: white; border-radius: var(--border-radius-md); @@ -941,13 +945,13 @@ body { padding-left: 0; padding-right: 0; - b span { - font-weight: 700 !important; + .ce-header b { + font-weight: 600 !important; } .new-block-button { position: absolute; - top: 16px; + top: 14px; left: -22px; cursor: pointer; visibility: hidden; @@ -1090,17 +1094,19 @@ body { } &.spacer { - align-items: inherit; - color: var(--text-muted); - border: 1px dashed var(--gray-400); - cursor: pointer; - - .widget-control > * { - width: auto; - } + &.edit-mode { + align-items: inherit; + color: var(--text-muted); + border: 1px dashed var(--gray-400); + cursor: pointer; + + .widget-control > * { + width: auto; + } - .spacer-left { - min-width: 74px; + .spacer-left { + min-width: 74px; + } } } @@ -1122,6 +1128,12 @@ body { &.paragraph { cursor: text; + + &.edit-mode { + .ce-paragraph { + padding: 2px; + } + } } .paragraph-control { From 6c0234dcad363f574ba0fa1e279221853081f9a1 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 30 Nov 2021 17:49:33 +0530 Subject: [PATCH 011/101] fix: Handled dark mode --- .../js/frappe/views/workspace/blocks/block.js | 2 +- frappe/public/scss/common/buttons.scss | 2 +- frappe/public/scss/desk/dark.scss | 2 + frappe/public/scss/desk/desktop.scss | 112 +++++++++--------- 4 files changed, 58 insertions(+), 60 deletions(-) diff --git a/frappe/public/js/frappe/views/workspace/blocks/block.js b/frappe/public/js/frappe/views/workspace/blocks/block.js index e7a6240340..6c1a0307cc 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/block.js +++ b/frappe/public/js/frappe/views/workspace/blocks/block.js @@ -54,7 +54,7 @@ export default class Block { function do_drag(e) { $(this).css("cursor", "col-resize"); $('.widget').css("pointer-events", "none"); - $(me.wrapper.parentElement).find('.resizer').css("border-right", "3px solid lightgray"); + $(me.wrapper.parentElement).find('.resizer').css("border-right", "3px solid var(--gray-400)"); un_focus(); if ((startWidth + e.clientX - startX) - startWidth > 60) { startX = e.clientX; diff --git a/frappe/public/scss/common/buttons.scss b/frappe/public/scss/common/buttons.scss index de3a4cfc20..ca9a872496 100644 --- a/frappe/public/scss/common/buttons.scss +++ b/frappe/public/scss/common/buttons.scss @@ -62,7 +62,7 @@ background-color: var(--control-bg); color: var(--text-color); &:hover, &:active { - background-color: var(--gray-300); + background-color: var(--gray-500); color: var(--text-color); } } diff --git a/frappe/public/scss/desk/dark.scss b/frappe/public/scss/desk/dark.scss index f894704ca2..36b1d2e45b 100644 --- a/frappe/public/scss/desk/dark.scss +++ b/frappe/public/scss/desk/dark.scss @@ -77,6 +77,8 @@ --highlight-shadow: 1px 1px 10px var(--blue-900), 0px 0px 4px var(--blue-500); + --shadow-base: 0px 4px 8px rgba(114, 176, 233, 0.06), 0px 0px 4px rgba(112, 172, 228, 0.12); + // input --input-disabled-bg: none; diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 3d39b647cf..50f387ac0b 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -112,7 +112,7 @@ body { position: absolute; top: 18px; right: 0; - border-right: 1px solid lightgrey; + border-right: 1px solid var(--gray-400); } .widget { @@ -343,7 +343,7 @@ body { background-color: var(--bg-light-gray); &.edit-mode:hover { - background-color: white; + background-color: var(--fg-color); .onboarding-step { &.active, @@ -440,7 +440,7 @@ body { &.active, &:hover { - background-color: white; + background-color: var(--fg-color); .step-index { background-color: var(--bg-light-gray); @@ -460,7 +460,7 @@ body { height: 20px; width: 20px; color: var(--text-on-light-gray); - background-color: white; + background-color: var(--fg-color); margin-right: var(--margin-sm); border-radius: var(--border-radius-full); @@ -473,7 +473,7 @@ body { display: none; background-color: var(--primary); .icon use { - stroke: var(--white); + stroke: var(--var(--fg-color)); } } @@ -821,7 +821,7 @@ body { } .layout-main-section { - background-color: white; + background-color: var(--fg-color); border-radius: var(--border-radius-md); padding: var(--padding-sm) var(--padding-md); margin-bottom: var(--margin-sm); @@ -978,7 +978,7 @@ body { &.ce-block--focused { .widget { - box-shadow: var(--card-shadow) !important; + box-shadow: var(--shadow-base) !important; .widget-control > * { width: auto; @@ -991,7 +991,7 @@ body { } &.onboarding { - background-color: white; + background-color: var(--fg-color); .onboarding-step { &.active, @@ -1041,7 +1041,7 @@ body { transition: border-color 0.3s ease-in-out; &:hover { - border-right: 3px solid lightgray !important; + border-right: 3px solid var(--gray-400) !important; } } @@ -1071,57 +1071,31 @@ body { opacity: 1; } - .widget{ - - &.edit-mode:hover { - box-shadow: var(--shadow-base); - } - - &.header { - display: flex; - justify-content: center; - flex: 1; - padding-left: 0px !important; - min-height: 40px; - box-shadow: none; - background-color: var(--control-bg); - color: var(--text-muted); - cursor: text; + .widget { + &.edit-mode { + padding: 7px 12px; - &.edit-mode:hover { + &:hover { + box-shadow: var(--shadow-base); background-color: var(--fg-color); } - } - &.spacer { - &.edit-mode { + &.spacer { align-items: inherit; color: var(--text-muted); border: 1px dashed var(--gray-400); cursor: pointer; - + .widget-control > * { width: auto; } - + .spacer-left { min-width: 74px; } } } - &.shortcut { - background-color: var(--control-bg); - - &:hover { - background-color: var(--fg-color); - } - } - - &:focus { - outline: none; - } - &.ce-paragraph { display: block; } @@ -1129,27 +1103,49 @@ body { &.paragraph { cursor: text; - &.edit-mode { - .ce-paragraph { - padding: 2px; + .ce-paragraph { + padding: 2px; + } + + .paragraph-control { + display: flex; + flex-direction: row-reverse; + position: absolute; + right: 20px; + gap: 5px; + background-color: var(--card-bg); + padding-left: 5px; + + .drag-handle { + cursor: all-scroll; + cursor: grabbing; } } } - .paragraph-control { + &.header { display: flex; - flex-direction: row-reverse; - position: absolute; - right: 20px; - gap: 5px; - background-color: var(--card-bg); - padding-left: 5px; - - .drag-handle { - cursor: all-scroll; - cursor: grabbing; + justify-content: center; + flex: 1; + padding-left: 0px !important; + min-height: 40px; + box-shadow: none; + background-color: var(--control-bg); + color: var(--text-muted); + cursor: text; + + .ce-header { + padding-left: 14px !important; } } + + &.shortcut { + background-color: var(--control-bg); + } + + &:focus { + outline: none; + } } } } @@ -1312,7 +1308,7 @@ body { .dropdown-list { position: absolute; - background-color: white; + background-color: var(--fg-color); box-shadow: var(--shadow-base); border-radius: var(--border-radius-sm); padding: 6px; From 973de86879fd72efbe3df7674f97a44200b21fb2 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 14 Dec 2021 20:53:21 +0530 Subject: [PATCH 012/101] feat: Edit page title, icon, etc from sidebar --- frappe/desk/doctype/workspace/workspace.json | 3 +- frappe/desk/doctype/workspace/workspace.py | 61 ++- frappe/public/js/desk.bundle.js | 1 + .../workspace_sidebar_loading_skeleton.html | 22 + .../js/frappe/views/workspace/workspace.js | 484 ++++++++++++++---- frappe/public/scss/common/buttons.scss | 2 +- frappe/public/scss/desk/desktop.scss | 57 ++- 7 files changed, 513 insertions(+), 117 deletions(-) create mode 100644 frappe/public/js/frappe/ui/workspace_sidebar_loading_skeleton.html diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index 04975c69e3..c90db0c87d 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_rename": 1, "autoname": "field:label", "beta": 1, "creation": "2020-01-23 13:45:59.470592", @@ -158,7 +159,7 @@ ], "in_create": 1, "links": [], - "modified": "2021-09-16 12:01:06.450622", + "modified": "2021-11-30 21:16:46.160413", "modified_by": "Administrator", "module": "Desk", "name": "Workspace", diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 94114e3918..9f91ef63d4 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -6,6 +6,7 @@ import frappe from frappe import _ from frappe.modules.export_file import export_to_files from frappe.model.document import Document +from frappe.model.rename_doc import rename_doc from frappe.desk.desktop import save_new_widget from frappe.desk.utils import validate_route_conflict @@ -123,7 +124,7 @@ def get_report_type(report): @frappe.whitelist() -def save_page(title, icon, parent, public, sb_public_items, sb_private_items, deleted_pages, new_widgets, blocks, save): +def save_page(title, icon, parent, public, sb_public_items, sb_private_items, new_widgets, blocks, save): save = frappe.parse_json(save) public = frappe.parse_json(public) if save: @@ -164,20 +165,58 @@ def save_page(title, icon, parent, public, sb_public_items, sb_private_items, de if loads(sb_public_items) or loads(sb_private_items): sort_pages(loads(sb_public_items), loads(sb_private_items)) - if loads(deleted_pages): - return delete_pages(loads(deleted_pages)) - return {"name": title, "public": public, "label": doc.label} -def delete_pages(deleted_pages): - for page in deleted_pages: - if page.get("public") and not is_workspace_manager(): - return {"name": page.get("title"), "public": 1, "label": page.get("label")} +@frappe.whitelist() +def update_page(name, title, icon, parent, public): + public = frappe.parse_json(public) + + doc = frappe.get_doc("Workspace", name) + + filters = { + 'parent_page': doc.title, + 'public': doc.public + } + child_docs = frappe.get_list("Workspace", filters=filters) + + if doc: + doc.title = title + doc.icon = icon + doc.parent_page = parent + if doc.public != public: + doc.sequence_id = frappe.db.count('Workspace', {'public':public}, cache=True) + doc.public = public + doc.for_user = '' if public else doc.for_user or frappe.session.user + doc.label = '{0}-{1}'.format(title, doc.for_user) if doc.for_user else title + doc.save(ignore_permissions=True) + + if name != doc.label: + rename_doc("Workspace", name, doc.label, force=True, ignore_permissions=True) + + # update new name and public in child pages + if child_docs: + for child in child_docs: + child_doc = frappe.get_doc("Workspace", child.name) + child_doc.parent_page = doc.title + child_doc.public = doc.public + child_doc.save(ignore_permissions=True) + + return {"name": doc.title, "public": doc.public, "label": doc.label} + +@frappe.whitelist() +def delete_page(page): + if not loads(page): + return + + page = loads(page) + + if page.get("public") and not is_workspace_manager(): + return - if frappe.db.exists("Workspace", page.get("name")): - frappe.get_doc("Workspace", page.get("name")).delete(ignore_permissions=True) + if frappe.db.exists("Workspace", page.get("name")): + frappe.get_doc("Workspace", page.get("name")).delete(ignore_permissions=True) - return {"name": "Home", "public": 1, "label": "Home"} + return {"name": page.get("name"), "public": page.get("public"), "title": page.get("title")} def sort_pages(sb_public_items, sb_private_items): wspace_public_pages = get_page_list(['name', 'title'], {'public': 1}) diff --git a/frappe/public/js/desk.bundle.js b/frappe/public/js/desk.bundle.js index 677b012efe..6333618977 100644 --- a/frappe/public/js/desk.bundle.js +++ b/frappe/public/js/desk.bundle.js @@ -95,6 +95,7 @@ import "./frappe/ui/sort_selector.js"; import "./frappe/change_log.html"; import "./frappe/ui/workspace_loading_skeleton.html"; +import "./frappe/ui/workspace_sidebar_loading_skeleton.html"; import "./frappe/desk.js"; import "./frappe/query_string.js"; diff --git a/frappe/public/js/frappe/ui/workspace_sidebar_loading_skeleton.html b/frappe/public/js/frappe/ui/workspace_sidebar_loading_skeleton.html new file mode 100644 index 0000000000..4f20e3c21c --- /dev/null +++ b/frappe/public/js/frappe/ui/workspace_sidebar_loading_skeleton.html @@ -0,0 +1,22 @@ +
+
+ + + + +
+
+ + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index e439ad5e24..4ea43b8374 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -26,7 +26,6 @@ frappe.views.Workspace = class Workspace { this.pages = {}; this.sorted_public_items = []; this.sorted_private_items = []; - this.deleted_sidebar_items = []; this.current_page = {}; this.sidebar_items = { 'public': {}, @@ -52,6 +51,8 @@ frappe.views.Workspace = class Workspace { } async setup_pages(reload) { + this.create_page_skeleton(); + this.create_sidebar_skeleton(); this.sidebar_pages = !this.discard ? await this.get_pages() : this.sidebar_pages; this.all_pages = this.sidebar_pages.pages; this.has_access = this.sidebar_pages.has_access; @@ -70,13 +71,16 @@ frappe.views.Workspace = class Workspace { } if (this.new_page && this.new_page.name) { if (!frappe.workspaces[frappe.router.slug(this.new_page.label)]) { - this.new_page = { name: this.all_pages[0].title, public: this.all_pages[0].public }; - } - if (this.new_page.public) { - frappe.set_route(`${frappe.router.slug(this.new_page.name)}`); - } else { - frappe.set_route(`private/${frappe.router.slug(this.new_page.name)}`); + this.new_page = { + name: this.all_pages[0].title, + public: this.all_pages[0].public + }; } + + let pre_url = this.new_page.public ? '' : 'private/'; + let route = pre_url + frappe.router.slug(this.new_page.name); + frappe.set_route(route); + this.new_page = null; } this.make_sidebar(); @@ -101,6 +105,7 @@ frappe.views.Workspace = class Workspace {
+
`); } @@ -119,8 +124,10 @@ frappe.views.Workspace = class Workspace { }); // Scroll sidebar to selected page if it is not in viewport. - !frappe.dom.is_element_in_viewport(this.sidebar.find('.selected')) + this.sidebar.find('.selected').length && !frappe.dom.is_element_in_viewport(this.sidebar.find('.selected')) && this.sidebar.find('.selected')[0].scrollIntoView(); + + this.remove_sidebar_skeleton(); } build_sidebar_section(title, root_pages) { @@ -164,7 +171,8 @@ frappe.views.Workspace = class Workspace { let child_items = pages.filter(page => page.parent_page == item.title); if (child_items.length > 0) { - let child_container = $(``); + let child_container = $item_container.find('.sidebar-child-item'); + child_container.addClass('hidden'); this.prepare_sidebar(child_items, child_container, $item_container); } @@ -179,8 +187,13 @@ frappe.views.Workspace = class Workspace { } add_drop_icon(item, sidebar_control, item_container) { + let drop_icon = 'small-down'; + if (item_container.find(`[item-name="${this.current_page.name}"]`).length) { + drop_icon = 'small-up'; + } + let $child_item_section = item_container.find('.sidebar-child-item'); - let $drop_icon = $(``) + let $drop_icon = $(``) .appendTo(sidebar_control); let pages = item.public ? this.public_pages : this.private_pages; if (pages.some(e => e.parent_page == item.title)) { @@ -203,9 +216,37 @@ frappe.views.Workspace = class Workspace { let page = this.get_page_to_show(); this.page.set_title(`${__(page.name)}`); + this.update_selected_sidebar(this.current_page, false); //remove selected from old page + this.update_selected_sidebar(page, true); //add selected on new page + this.show_page(page); } + update_selected_sidebar(page, add) { + let section = page.public ? 'public' : 'private'; + if (this.sidebar && this.sidebar_items[section] && this.sidebar_items[section][page.name]) { + let $sidebar = this.sidebar_items[section][page.name]; + let pages = page.public ? this.public_pages : this.private_pages; + let sidebar_page = pages.find(p => p.title == page.name); + + if (add) { + $sidebar[0].firstElementChild.classList.add("selected"); + if (sidebar_page) sidebar_page.selected = true; + + // open child sidebar section if closed + $sidebar.parent().hasClass('hidden') && + $sidebar.parent().removeClass('hidden'); + + this.current_page = { name: page.name, public: page.public }; + localStorage.current_page = page.name; + localStorage.is_current_page_public = page.public; + } else { + $sidebar[0].firstElementChild.classList.remove("selected"); + if (sidebar_page) sidebar_page.selected = false; + } + } + } + get_data(page) { return frappe.xcall("frappe.desk.desktop.get_desktop_page", { page: page @@ -249,49 +290,32 @@ frappe.views.Workspace = class Workspace { } async show_page(page) { - let section = this.current_page.public ? 'public' : 'private'; - if (this.sidebar_items && this.sidebar_items[section] && this.sidebar_items[section][this.current_page.name]) { - this.sidebar_items[section][this.current_page.name][0].firstElementChild.classList.remove("selected"); - this.sidebar_items[page.public ? 'public':'private'][page.name][0].firstElementChild.classList.add("selected"); - - if (this.sidebar_items[page.public ? 'public':'private'][page.name].parents('.sidebar-item-container')[0]) { - this.sidebar_items[page.public ? 'public':'private'][page.name] - .parents('.sidebar-item-container') - .find('.drop-icon use') - .attr("href", "#icon-small-up"); - } - } - - this.current_page = { name: page.name, public: page.public }; - localStorage.current_page = page.name; - localStorage.is_current_page_public = page.public; - if (!this.body.find('#editorjs')[0]) { this.$page = $(`
`).appendTo(this.body); } - this.create_skeleton(); if (this.all_pages) { + this.create_page_skeleton(); let pages = page.public ? this.public_pages : this.private_pages; - let this_page = pages.filter(p => p.title == page.name)[0]; + let current_page = pages.filter(p => p.title == page.name)[0]; this.setup_actions(page); - this.content = this_page && JSON.parse(this_page.content); + this.content = current_page && JSON.parse(current_page.content); this.add_custom_cards_in_content(); $('.item-anchor').addClass('disable-click'); - if (this.pages && this.pages[this_page.name]) { - this.page_data = this.pages[this_page.name]; + if (this.pages && this.pages[current_page.name]) { + this.page_data = this.pages[current_page.name]; } else { - await this.get_data(this_page); + await this.get_data(current_page); } this.prepare_editorjs(); $('.item-anchor').removeClass('disable-click'); - this.remove_skeleton(); + this.remove_page_skeleton(); } } @@ -329,9 +353,7 @@ frappe.views.Workspace = class Workspace { return; } - this.page.clear_primary_action(); - this.page.clear_secondary_action(); - this.page.clear_inner_toolbar(); + this.clear_page_actions(); current_page.is_editable && this.page.set_secondary_action(__("Edit"), async () => { if (!this.editor || !this.editor.readOnly) return; @@ -357,18 +379,19 @@ frappe.views.Workspace = class Workspace { this.undo.readOnly = false; } - setup_customization_buttons(page) { - let me = this; + clear_page_actions() { this.page.clear_primary_action(); this.page.clear_secondary_action(); this.page.clear_inner_toolbar(); + } + + setup_customization_buttons(page) { + this.clear_page_actions(); page.is_editable && this.page.set_primary_action( __("Save Customizations"), () => { - this.page.clear_primary_action(); - this.page.clear_secondary_action(); - this.page.clear_inner_toolbar(); + this.clear_page_actions(); this.undo.readOnly = true; this.save_page(); this.editor.readOnly.toggle(); @@ -382,9 +405,7 @@ frappe.views.Workspace = class Workspace { __("Discard"), async () => { this.discard = true; - this.page.clear_primary_action(); - this.page.clear_secondary_action(); - this.page.clear_inner_toolbar(); + this.clear_page_actions(); await this.editor.readOnly.toggle(); this.is_read_only = true; this.reload(); @@ -420,21 +441,277 @@ frappe.views.Workspace = class Workspace { null, sidebar_control ); - frappe.utils.add_custom_button( - frappe.utils.icon('delete-active', 'xs'), - () => this.delete_page(item), - "delete-page", - `${__('Delete')}`, - null, - sidebar_control - ); + + this.add_settings_button(item, sidebar_control); + } + } + + edit_page(item) { + this.public_parent_pages = ['', ...this.public_pages.filter(page => !page.parent_page).map(page => page.title)]; + this.private_parent_pages = ['', ...this.private_pages.filter(page => !page.parent_page).map(page => page.title)]; + var me = this; + let old_item = item; + const d = new frappe.ui.Dialog({ + title: __('Update Details'), + fields: [ + { + label: __('Title'), + fieldtype: 'Data', + fieldname: 'title', + reqd: 1, + default: item.title + }, + { + label: __('Parent'), + fieldtype: 'Select', + fieldname: 'parent', + options: item.public ? this.public_parent_pages : this.private_parent_pages, + default: item.parent_page + }, + { + label: __('Public'), + fieldtype: 'Check', + fieldname: 'is_public', + depends_on: `eval:${this.has_access}`, + default: item.public, + onchange: function() { + d.set_df_property('parent', 'options', + this.get_value() ? me.public_parent_pages : me.private_parent_pages); + } + }, + { + fieldtype: 'Column Break' + }, + { + label: __('Icon'), + fieldtype: 'Icon', + fieldname: 'icon', + default: item.icon + }, + ], + primary_action_label: __('Update'), + primary_action: (values) => { + let is_title_changed = values.title != old_item.title; + let is_section_changed = values.is_public != old_item.public; + if ((is_title_changed || is_section_changed) && !this.validate_page(values, old_item)) return; + d.hide(); + + frappe.call({ + method: "frappe.desk.doctype.workspace.workspace.update_page", + args: { + name: old_item.name, + title: values.title, + icon: values.icon || '', + parent: values.parent || '', + public: values.is_public || 0, + } + }); + + this.update_sidebar(old_item, values); + + if (this.make_page_selected) { + let pre_url = values.is_public ? '' : 'private/'; + let route = pre_url + frappe.router.slug(values.title); + frappe.set_route(route); + + this.make_page_selected = false; + } + + this.make_sidebar(); + this.show_sidebar_actions(); + } + }); + d.show(); + } + + update_sidebar(old_item, new_item) { + let is_section_changed = old_item.public != (new_item.is_public || 0); + let is_title_changed = old_item.title != new_item.title; + let new_updated_item = {...old_item}; + + let pages = old_item.public ? this.public_pages : this.private_pages; + + let child_items = pages.filter(page => page.parent_page == old_item.title); + + this.make_page_selected = old_item.selected; + + new_updated_item.title = new_item.title; + new_updated_item.icon = new_item.icon; + new_updated_item.parent_page = new_item.parent || ""; + new_updated_item.public = new_item.is_public; + + if (is_title_changed || is_section_changed){ + if (new_item.is_public) { + new_updated_item.name = new_item.title; + new_updated_item.label = new_item.title; + new_updated_item.for_user = ""; + } else { + let user = frappe.session.user; + new_updated_item.name = `${new_item.title}-${user}`; + new_updated_item.label = `${new_item.title}-${user}`; + new_updated_item.for_user = user; + } + + this.update_cached_values(old_item, new_updated_item); + } + + if (child_items.length) { + child_items.forEach(child => { + child.parent_page = new_item.title; + is_section_changed && this.update_child_sidebar(child, new_item); + }); + } + } + + update_child_sidebar(child, new_item) { + let old_child = {...child}; + this.make_page_selected = child.selected; + + child.public = new_item.is_public; + if (new_item.is_public) { + child.name = child.title; + child.label = child.title; + child.for_user = ""; + } else { + let user = frappe.session.user; + child.name = `${child.title}-${user}`; + child.label = `${child.title}-${user}`; + child.for_user = user; + } + + this.update_cached_values(old_child, child); + } + + update_cached_values(old_item, new_item) { + let [from_pages, to_pages] = old_item.public ? + [this.public_pages, this.private_pages] : [this.private_pages, this.public_pages]; + + let old_item_index = from_pages.findIndex(page => page.title == old_item.title); + + // update frappe.workspaces + if (frappe.workspaces[frappe.router.slug(old_item.name)]) { + delete frappe.workspaces[frappe.router.slug(old_item.name)]; + if (new_item) { + frappe.workspaces[frappe.router.slug(new_item.name)] = {'title': new_item.title}; + } + } + + // update page block data + if (this.pages && this.pages[old_item.name]) { + if (new_item) { + this.pages[new_item.name] = this.pages[old_item.name]; + } + delete this.pages[old_item.name]; + } + + // update public and private pages + if (new_item) { + let is_section_changed = old_item.public != (new_item.is_public || new_item.public || 0); + + if (is_section_changed) { + from_pages.splice(old_item_index, 1); + to_pages.push(new_item); + } else { + from_pages.splice(old_item_index, 1, new_item); + } + } else { + from_pages.splice(old_item_index, 1); + } + + this.sidebar_pages.pages = [...this.public_pages, ...this.private_pages]; + } + + add_settings_button(item, sidebar_control) { + this.dropdown_list = [ + { + label: 'Edit', + title: 'Edit Workspace', + icon: frappe.utils.icon('edit', 'sm'), + action: () => this.edit_page(item) + }, + { + label: 'Delete', + title: 'Delete Workspace', + icon: frappe.utils.icon('delete-active', 'sm'), + action: () => this.delete_page(item) + }, + { + label: 'Duplicate', + title: 'Duplicate Workspace', + icon: frappe.utils.icon('duplicate', 'sm'), + action: () => this.duplicate_page(item) + }, + { + label: 'Settings', + title: 'Settings', + icon: frappe.utils.icon('setting-gear', 'sm'), + action: () => frappe.set_route(`workspace/${item.name}`) + }, + ] + + let $button = $(` + + + `); + + let dropdown_item = function(label, title, icon, action) { + let html = $(` + + `); + + html.click(event => { + event.stopPropagation(); + action && action(); + }); + + return html; } + + $button.filter('.dropdown-btn').click(event => { + event.stopPropagation(); + if ($button.filter('.dropdown-list.hidden').length) { + $('.dropdown-list:not(.hidden)').addClass('hidden'); + } + $button.filter('.dropdown-list').toggleClass('hidden'); + }); + + $(document).click(event => { + event.stopPropagation(); + $('.dropdown-list:not(.hidden)').addClass('hidden'); + }) + + sidebar_control.append($button); + + this.dropdown_list.forEach((i) => { + $button.filter('.dropdown-list').append(dropdown_item(i.label, i.title, i.icon, i.action)); + }) } - delete_page(item) { - frappe.confirm(__("Are you sure you want to delete page {0}?", [item.title]), () => { - this.deleted_sidebar_items.push(item); - this.sidebar.find(`.standard-sidebar-section [item-name="${item.title}"][item-public="${item.public}"]`).addClass('hidden'); + delete_page(page) { + frappe.confirm(__("Are you sure you want to delete page {0}?", [page.title]), () => { + let me = this; + this.sidebar + .find(`.standard-sidebar-section [item-name="${page.title}"][item-public="${page.public}"]`) + .addClass('hidden'); + + frappe.call({ + method: "frappe.desk.doctype.workspace.workspace.delete_page", + args: { + page: page + }, + callback: function(res) { + res.message && me.update_cached_values(res.message); + } + }); + + if (this.current_page.name == page.title && this.current_page.public == page.public) { + frappe.set_route('/'); + } }); } @@ -500,7 +777,7 @@ frappe.views.Workspace = class Workspace { this.private_parent_pages = ['', ...this.private_pages.filter(page => !page.parent_page).map(page => page.title)]; var me = this; const d = new frappe.ui.Dialog({ - title: __('Set Title'), + title: __('New Workspace'), fields: [ { label: __('Title'), @@ -544,15 +821,10 @@ frappe.views.Workspace = class Workspace { this.parent = values.parent; this.public = values.is_public; this.editor.render({ - blocks: [ - { - type: "header", - data: { - text: this.title, - level: 4 - } - } - ] + blocks: [{ + type: "header", + data: { text: this.title } + }] }).then(async () => { if (this.editor.configuration.readOnly) { this.is_read_only = false; @@ -563,24 +835,43 @@ frappe.views.Workspace = class Workspace { this.make_sidebar_sortable(); this.make_blocks_sortable(); this.prepare_sorted_sidebar(values.is_public); + + this.update_selected_sidebar(this.current_page, false); //remove selected from old page }); } }); d.show(); } - validate_page(values) { + validate_page(new_page, old_page) { let message = ""; - let pages = values.is_public ? this.public_pages : this.private_pages; + let [from_pages, to_pages] = new_page.is_public ? + [this.private_pages, this.public_pages] : [this.public_pages, this.private_pages]; - if (pages && pages.filter(p => p.title == values.title)[0]) { - message = "Page with title '{0}' already exist."; - } else if (frappe.router.doctype_route_exist(frappe.router.slug(values.title))) { + let section = this.sidebar_categories[new_page.is_public]; + + if (to_pages && to_pages.filter(p => p.title == new_page.title)[0]) { + message = `Page with title ${new_page.title} already exist.`; + } + + if (frappe.router.doctype_route_exist(frappe.router.slug(new_page.title))) { message = "Doctype with same route already exist. Please choose different title."; } + let child_pages = old_page && from_pages.filter(p => p.parent_page == old_page.title); + if (child_pages) { + child_pages.every(child_page => { + if(to_pages && to_pages.find(p => p.title == child_page.title)) { + message = `One of the child page with name ${child_page.title} already exist in ${section} Section. Please update the name of the child page first before moving`; + cur_dialog.hide(); + return false; + } + return true; + }); + } + if (message) { - frappe.throw(__(message, [__(values.title)])); + frappe.throw(__(message)); return false; } return true; @@ -592,7 +883,8 @@ frappe.views.Workspace = class Workspace { title: title, icon: icon, parent_page: parent, - public: is_public + public: is_public, + selected: true }; let $sidebar_item = this.sidebar_item_container(item); $sidebar_item.addClass('is-draggable'); @@ -605,6 +897,7 @@ frappe.views.Workspace = class Workspace { null, $sidebar_item.find('.sidebar-item-control') ); + $sidebar_item.find('.sidebar-item-control .drag-handle').css('margin-right', '8px'); let $sidebar_section = is_public ? $sidebar[1] : $sidebar[0]; @@ -683,19 +976,21 @@ frappe.views.Workspace = class Workspace { save_page() { frappe.dom.freeze(); - this.create_skeleton(); + this.create_page_skeleton(); + let me = this; let save = true; + if (!this.title && this.current_page) { - let pages = this.current_page.public ? this.public_pages : this.private_pages; this.title = this.current_page.name; - this.public = pages.filter(p => p.title == this.title)[0].public; + this.public = this.current_page.public; save = false; } else { this.current_page = { name: this.title, public: this.public }; } - let me = this; + this.editor.save().then((outputData) => { let new_widgets = {}; + outputData.blocks.forEach(item => { if (item.data.new) { if (!new_widgets[item.type]) { @@ -708,8 +1003,8 @@ frappe.views.Workspace = class Workspace { let blocks = outputData.blocks.filter( item => item.type != 'card' || - (item.data.card_name !== 'Custom Documents' && - item.data.card_name !== 'Custom Reports') + (item.data.card_name !== 'Custom Documents' && + item.data.card_name !== 'Custom Reports') ); frappe.call({ @@ -721,7 +1016,6 @@ frappe.views.Workspace = class Workspace { public: me.public || 0, sb_public_items: me.sorted_public_items, sb_private_items: me.sorted_private_items, - deleted_pages: me.deleted_sidebar_items, new_widgets: new_widgets, blocks: JSON.stringify(blocks), save: save @@ -749,20 +1043,32 @@ frappe.views.Workspace = class Workspace { this.public = false; this.sorted_public_items = []; this.sorted_private_items = []; - this.deleted_sidebar_items = []; - this.create_skeleton(); this.setup_pages(true); this.discard = false; this.undo.readOnly = true; } - create_skeleton() { - this.$page.prepend(frappe.render_template('workspace_loading_skeleton')); - this.$page.find('.codex-editor').addClass('hidden'); + create_page_skeleton() { + if ($('.layout-main-section').find('.workspace-skeleton').length) return; + + $('.layout-main-section').prepend(frappe.render_template('workspace_loading_skeleton')); + $('.layout-main-section').find('.codex-editor').addClass('hidden'); + } + + remove_page_skeleton() { + $('.layout-main-section').find('.codex-editor').removeClass('hidden'); + $('.layout-main-section').find('.workspace-skeleton').remove(); + } + + create_sidebar_skeleton() { + if ($('.list-sidebar').find('.workspace-sidebar-skeleton').length) return; + + $('.list-sidebar').prepend(frappe.render_template('workspace_sidebar_loading_skeleton')); + $('.desk-sidebar').addClass('hidden'); } - remove_skeleton() { - this.$page.find('.codex-editor').removeClass('hidden'); - this.$page.find('.workspace-skeleton').remove(); + remove_sidebar_skeleton() { + $('.desk-sidebar').removeClass('hidden'); + $('.list-sidebar').find('.workspace-sidebar-skeleton').remove(); } }; diff --git a/frappe/public/scss/common/buttons.scss b/frappe/public/scss/common/buttons.scss index ca9a872496..62479e7a7a 100644 --- a/frappe/public/scss/common/buttons.scss +++ b/frappe/public/scss/common/buttons.scss @@ -62,7 +62,7 @@ background-color: var(--control-bg); color: var(--text-color); &:hover, &:active { - background-color: var(--gray-500); + background-color: var(--gray-400); color: var(--text-color); } } diff --git a/frappe/public/scss/desk/desktop.scss b/frappe/public/scss/desk/desktop.scss index 50f387ac0b..878d51d790 100644 --- a/frappe/public/scss/desk/desktop.scss +++ b/frappe/public/scss/desk/desktop.scss @@ -337,10 +337,9 @@ body { } &.onboarding-widget-box { - margin-top: var(--margin-xs); margin-bottom: var(--margin-2xl); - padding: var(--padding-lg); - background-color: var(--bg-light-gray); + padding: var(--padding-lg) !important; + background-color: var(--bg-color); &.edit-mode:hover { background-color: var(--fg-color); @@ -348,7 +347,7 @@ body { .onboarding-step { &.active, &:hover { - background-color: var(--bg-light-gray); + background-color: var(--bg-color); .step-index.step-pending { background-color: var(--fg-color); @@ -356,7 +355,7 @@ body { } .step-index { - background-color: var(--bg-light-gray); + background-color: var(--bg-color); } } } @@ -443,7 +442,7 @@ body { background-color: var(--fg-color); .step-index { - background-color: var(--bg-light-gray); + background-color: var(--bg-color); } .step-skip { @@ -551,6 +550,7 @@ body { .widget-title { cursor: pointer !important; + font-size: var(--text-base) !important; } .indicator-pill { @@ -792,6 +792,25 @@ body { } } +.workspace-sidebar-skeleton { + transition: ease; + .sidebar-box { + height: 40px; + margin-bottom: 10px; + margin-left: 10px; + background-color: var(--skeleton-bg); + + &.child { + margin-left: 30px; + } + + &.section { + height: 25px; + margin-left: 0px; + } + } +} + [data-page-route="Workspaces"] { @media (min-width: map-get($grid-breakpoints, "lg")) { .layout-main { @@ -822,8 +841,8 @@ body { .layout-main-section { background-color: var(--fg-color); - border-radius: var(--border-radius-md); - padding: var(--padding-sm) var(--padding-md); + border-radius: var(--border-radius-lg); + padding: var(--padding-md); margin-bottom: var(--margin-sm); } @@ -844,7 +863,6 @@ body { padding: 0px; .sidebar-item-control { - > * { align-self: center; margin-left: 3px; @@ -857,7 +875,7 @@ body { display: none; } - .delete-page { + .setting-btn { display: none; } @@ -872,6 +890,10 @@ body { svg { margin-right: 0; } + + .dropdown-list { + top: 42px; + } } .sidebar-item-label { @@ -887,6 +909,7 @@ body { } .sidebar-item-container { + position: relative; .sidebar-item-container{ margin-left: 10px; @@ -904,7 +927,7 @@ body { display: inline-block; } - .delete-page { + .setting-btn { display: inline-block; margin-right: 8px; } @@ -916,7 +939,7 @@ body { .drop-icon { padding: 10px 8px 10px 2px; - margin-left: -4px; + margin-left: -8px; } } @@ -996,7 +1019,7 @@ body { .onboarding-step { &.active, &:hover { - background-color: var(--bg-light-gray); + background-color: var(--bg-color); .step-index.step-pending { background-color: var(--fg-color); @@ -1004,7 +1027,7 @@ body { } .step-index { - background-color: var(--bg-light-gray); + background-color: var(--bg-color); } } } @@ -1096,6 +1119,10 @@ body { } } + &.spacer { + height: 18px !important; + } + &.ce-paragraph { display: block; } @@ -1309,7 +1336,7 @@ body { .dropdown-list { position: absolute; background-color: var(--fg-color); - box-shadow: var(--shadow-base); + box-shadow: var(--shadow-base) !important; border-radius: var(--border-radius-sm); padding: 6px; top: 30px; From 706ee4c3c545ef76665263718b9c19a6af226837 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 15 Dec 2021 20:03:52 +0530 Subject: [PATCH 013/101] feat: Duplicate Page from sidebar --- frappe/desk/doctype/workspace/workspace.json | 4 +- frappe/desk/doctype/workspace/workspace.py | 46 ++++- .../js/frappe/views/workspace/workspace.js | 188 ++++++++++++------ 3 files changed, 167 insertions(+), 71 deletions(-) diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index c90db0c87d..211029dfcf 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -142,7 +142,7 @@ }, { "fieldname": "sequence_id", - "fieldtype": "Int", + "fieldtype": "Float", "label": "Sequence Id" }, { @@ -159,7 +159,7 @@ ], "in_create": 1, "links": [], - "modified": "2021-11-30 21:16:46.160413", + "modified": "2021-12-15 19:33:00.805265", "modified_by": "Administrator", "module": "Desk", "name": "Workspace", diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 9f91ef63d4..a46b91ec0c 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -133,21 +133,20 @@ def save_page(title, icon, parent, public, sb_public_items, sb_private_items, ne doc.icon = icon doc.content = blocks doc.parent_page = parent - - if public: - doc.label = title - doc.public = 1 - else: + doc.label = title + doc.for_user = '' + doc.public = 1 + if not public: doc.label = title + "-" + frappe.session.user doc.for_user = frappe.session.user + doc.public = 0 doc.save(ignore_permissions=True) else: - if public: - filters = { - 'public': public, - 'label': title - } - else: + filters = { + 'public': public, + 'label': title + } + if not public: filters = { 'for_user': frappe.session.user, 'label': title + "-" + frappe.session.user @@ -203,6 +202,31 @@ def update_page(name, title, icon, parent, public): return {"name": doc.title, "public": doc.public, "label": doc.label} +@frappe.whitelist() +def duplicate_page(page_name, new_page): + if not loads(new_page): + return + + new_page = loads(new_page) + + if new_page.get("is_public") and not is_workspace_manager(): + return + + old_doc = frappe.get_doc("Workspace", page_name) + doc = frappe.copy_doc(old_doc) + doc.title = new_page.get('title') + doc.icon = new_page.get('icon') + doc.parent_page = new_page.get('parent') or '' + doc.public = new_page.get('is_public') + doc.for_user = '' + doc.label = doc.title + if not doc.public: + doc.for_user = doc.for_user or frappe.session.user + doc.label = '{0}-{1}'.format(doc.title, doc.for_user) + doc.name = doc.label + doc.sequence_id += 0.1 + doc.insert(ignore_permissions=True) + @frappe.whitelist() def delete_page(page): if not loads(page): diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 4ea43b8374..c85da6f386 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -198,12 +198,12 @@ frappe.views.Workspace = class Workspace { let pages = item.public ? this.public_pages : this.private_pages; if (pages.some(e => e.parent_page == item.title)) { $drop_icon.removeClass('hidden'); - $drop_icon.on('click', () => { - let icon = $drop_icon.find("use").attr("href")==="#icon-small-down" ? "#icon-small-up" : "#icon-small-down"; - $drop_icon.find("use").attr("href", icon); - $child_item_section.toggleClass("hidden"); - }); } + $drop_icon.on('click', () => { + let icon = $drop_icon.find("use").attr("href")==="#icon-small-down" ? "#icon-small-up" : "#icon-small-down"; + $drop_icon.find("use").attr("href", icon); + $child_item_section.toggleClass("hidden"); + }); } show() { @@ -248,17 +248,16 @@ frappe.views.Workspace = class Workspace { } get_data(page) { - return frappe.xcall("frappe.desk.desktop.get_desktop_page", { + return frappe.call("frappe.desk.desktop.get_desktop_page", { page: page }).then(data => { - this.page_data = data; + this.page_data = data.message; // caching page data this.pages[page.name] && delete this.pages[page.name]; - this.pages[page.name] = data; + this.pages[page.name] = data.message; if (!this.page_data || Object.keys(this.page_data).length === 0) return; - if (this.page_data.charts && this.page_data.charts.items.length === 0) return; return frappe.dashboard_utils.get_dashboard_settings().then(settings => { @@ -298,11 +297,12 @@ frappe.views.Workspace = class Workspace { if (this.all_pages) { this.create_page_skeleton(); + let pages = page.public ? this.public_pages : this.private_pages; let current_page = pages.filter(p => p.title == page.name)[0]; - this.setup_actions(page); this.content = current_page && JSON.parse(current_page.content); + this.setup_actions(page); this.add_custom_cards_in_content(); $('.item-anchor').addClass('disable-click'); @@ -310,11 +310,12 @@ frappe.views.Workspace = class Workspace { if (this.pages && this.pages[current_page.name]) { this.page_data = this.pages[current_page.name]; } else { - await this.get_data(current_page); + await frappe.after_ajax(() => this.get_data(current_page)); } this.prepare_editorjs(); $('.item-anchor').removeClass('disable-click'); + this.remove_page_skeleton(); } } @@ -363,7 +364,6 @@ frappe.views.Workspace = class Workspace { this.initialize_editorjs_undo(); this.setup_customization_buttons(current_page); this.show_sidebar_actions(); - this.make_sidebar_sortable(); this.make_blocks_sortable(); }); }); @@ -420,9 +420,10 @@ frappe.views.Workspace = class Workspace { show_sidebar_actions() { this.sidebar.find('.standard-sidebar-section').addClass('show-control'); + this.make_sidebar_sortable(); } - add_sidebar_actions(item, sidebar_control) { + add_sidebar_actions(item, sidebar_control, is_new) { if (!item.is_editable) { $(`${frappe.utils.icon("lock", "sm")}`) .appendTo(sidebar_control); @@ -442,15 +443,19 @@ frappe.views.Workspace = class Workspace { sidebar_control ); - this.add_settings_button(item, sidebar_control); + !is_new && this.add_settings_button(item, sidebar_control); } } - edit_page(item) { + get_parent_pages() { this.public_parent_pages = ['', ...this.public_pages.filter(page => !page.parent_page).map(page => page.title)]; this.private_parent_pages = ['', ...this.private_pages.filter(page => !page.parent_page).map(page => page.title)]; + } + + edit_page(item) { var me = this; let old_item = item; + this.get_parent_pages(); const d = new frappe.ui.Dialog({ title: __('Update Details'), fields: [ @@ -551,9 +556,8 @@ frappe.views.Workspace = class Workspace { new_updated_item.label = `${new_item.title}-${user}`; new_updated_item.for_user = user; } - - this.update_cached_values(old_item, new_updated_item); } + this.update_cached_values(old_item, new_updated_item); if (child_items.length) { child_items.forEach(child => { @@ -582,15 +586,16 @@ frappe.views.Workspace = class Workspace { this.update_cached_values(old_child, child); } - update_cached_values(old_item, new_item) { + update_cached_values(old_item, new_item, duplicate) { let [from_pages, to_pages] = old_item.public ? [this.public_pages, this.private_pages] : [this.private_pages, this.public_pages]; let old_item_index = from_pages.findIndex(page => page.title == old_item.title); + duplicate && old_item_index++; // update frappe.workspaces if (frappe.workspaces[frappe.router.slug(old_item.name)]) { - delete frappe.workspaces[frappe.router.slug(old_item.name)]; + !duplicate && delete frappe.workspaces[frappe.router.slug(old_item.name)]; if (new_item) { frappe.workspaces[frappe.router.slug(new_item.name)] = {'title': new_item.title}; } @@ -601,7 +606,7 @@ frappe.views.Workspace = class Workspace { if (new_item) { this.pages[new_item.name] = this.pages[old_item.name]; } - delete this.pages[old_item.name]; + !duplicate && delete this.pages[old_item.name]; } // update public and private pages @@ -609,10 +614,10 @@ frappe.views.Workspace = class Workspace { let is_section_changed = old_item.public != (new_item.is_public || new_item.public || 0); if (is_section_changed) { - from_pages.splice(old_item_index, 1); + !duplicate && from_pages.splice(old_item_index, 1); to_pages.push(new_item); } else { - from_pages.splice(old_item_index, 1, new_item); + from_pages.splice(old_item_index, duplicate ? 0 : 1, new_item); } } else { from_pages.splice(old_item_index, 1); @@ -694,25 +699,98 @@ frappe.views.Workspace = class Workspace { delete_page(page) { frappe.confirm(__("Are you sure you want to delete page {0}?", [page.title]), () => { - let me = this; - this.sidebar - .find(`.standard-sidebar-section [item-name="${page.title}"][item-public="${page.public}"]`) - .addClass('hidden'); - frappe.call({ method: "frappe.desk.doctype.workspace.workspace.delete_page", - args: { - page: page - }, - callback: function(res) { - res.message && me.update_cached_values(res.message); - } + args: { page: page } }); + this.update_cached_values(page); + if (this.current_page.name == page.title && this.current_page.public == page.public) { frappe.set_route('/'); } + + this.make_sidebar(); + this.show_sidebar_actions(); + }); + } + + duplicate_page(page) { + var me = this; + this.get_parent_pages(); + const d = new frappe.ui.Dialog({ + title: __('Create Duplicate'), + fields: [ + { + label: __('Title'), + fieldtype: 'Data', + fieldname: 'title', + reqd: 1 + }, + { + label: __('Parent'), + fieldtype: 'Select', + fieldname: 'parent', + options: page.public ? this.public_parent_pages : this.private_parent_pages, + default: page.parent_page + }, + { + label: __('Public'), + fieldtype: 'Check', + fieldname: 'is_public', + depends_on: `eval:${this.has_access}`, + default: page.public, + onchange: function() { + d.set_df_property('parent', 'options', + this.get_value() ? me.public_parent_pages : me.private_parent_pages); + } + }, + { + fieldtype: 'Column Break' + }, + { + label: __('Icon'), + fieldtype: 'Icon', + fieldname: 'icon', + default: page.icon + }, + ], + primary_action_label: __('Duplicate'), + primary_action: (values) => { + if (!this.validate_page(values)) return; + d.hide(); + frappe.call({ + method: "frappe.desk.doctype.workspace.workspace.duplicate_page", + args: { + page_name: page.name, + new_page: values + } + }); + + let new_page = {...page}; + + new_page.title = values.title; + new_page.name = values.title; + new_page.public = values.is_public; + new_page.parent_page = values.parent || ''; + new_page.for_user = ''; + if (!values.is_public) { + new_page.name += '-' + frappe.session.user; + new_page.for_user = frappe.session.user; + } + new_page.label = new_page.name; + + this.update_cached_values(page, new_page, true); + + let pre_url = values.is_public ? '' : 'private/'; + let route = pre_url + frappe.router.slug(values.title); + frappe.set_route(route); + + me.make_sidebar(); + me.show_sidebar_actions(); + } }); + d.show(); } make_sidebar_sortable() { @@ -773,9 +851,8 @@ frappe.views.Workspace = class Workspace { } initialize_new_page() { - this.public_parent_pages = ['', ...this.public_pages.filter(page => !page.parent_page).map(page => page.title)]; - this.private_parent_pages = ['', ...this.private_pages.filter(page => !page.parent_page).map(page => page.title)]; var me = this; + this.get_parent_pages(); const d = new frappe.ui.Dialog({ title: __('New Workspace'), fields: [ @@ -832,7 +909,6 @@ frappe.views.Workspace = class Workspace { } this.add_page_to_sidebar(values); this.show_sidebar_actions(); - this.make_sidebar_sortable(); this.make_blocks_sortable(); this.prepare_sorted_sidebar(values.is_public); @@ -877,36 +953,26 @@ frappe.views.Workspace = class Workspace { return true; } - add_page_to_sidebar({title, icon, parent, is_public}) { + add_page_to_sidebar(page) { let $sidebar = $('.standard-sidebar-section'); - let item = { - title: title, - icon: icon, - parent_page: parent, - public: is_public, - selected: true - }; + let item = {...page}; + + item.selected = true; + item.is_editable = true; + let $sidebar_item = this.sidebar_item_container(item); - $sidebar_item.addClass('is-draggable'); - frappe.utils.add_custom_button( - frappe.utils.icon('drag', 'xs'), - null, - "drag-handle", - `${__('Drag')}`, - null, - $sidebar_item.find('.sidebar-item-control') - ); + this.add_sidebar_actions(item, $sidebar_item.find('.sidebar-item-control'), true); $sidebar_item.find('.sidebar-item-control .drag-handle').css('margin-right', '8px'); - let $sidebar_section = is_public ? $sidebar[1] : $sidebar[0]; + let sidebar_section = item.is_public ? $sidebar[1] : $sidebar[0]; - if (!parent) { - !is_public && $sidebar.first().removeClass('hidden'); - $sidebar_item.appendTo($sidebar_section); + if (!item.parent) { + !item.is_public && $sidebar.first().removeClass('hidden'); + $sidebar_item.appendTo(sidebar_section); } else { - let $item_container = $($sidebar_section).find(`[item-name="${parent}"]`); + let $item_container = $(sidebar_section).find(`[item-name="${item.parent}"]`); let $child_section = $item_container.find('.sidebar-child-item'); let $drop_icon = $item_container.find('.drop-icon'); if (!$child_section[0]) { @@ -916,8 +982,14 @@ frappe.views.Workspace = class Workspace { } $sidebar_item.appendTo($child_section); $child_section.removeClass('hidden'); + $item_container.find('.drop-icon.hidden').removeClass('hidden'); $item_container.find('.drop-icon use').attr("href", "#icon-small-up"); } + + let section = item.is_public ? 'public' : 'private'; + if (this.sidebar_items && this.sidebar_items[section] && !this.sidebar_items[section][item.title]) { + this.sidebar_items[section][item.title] = $sidebar_item; + } } initialize_editorjs(blocks) { From 0063c917a932ea1bc4c5c831cf05c76f627a247c Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 15 Dec 2021 20:55:43 +0530 Subject: [PATCH 014/101] fix: minor paragraph block fix --- .../js/frappe/views/workspace/blocks/paragraph.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js index be6ba94c24..c2f3aee6ad 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/paragraph.js +++ b/frappe/public/js/frappe/views/workspace/blocks/paragraph.js @@ -27,7 +27,10 @@ export default class Paragraph extends Block { } onKeyUp(e) { - $(this.wrapper.parentElement).find('.block-list-container.dropdown-list').hide(); + if (!this.wrapper) return; + let $block_list_container = $(this.wrapper.parentElement).find('.block-list-container.dropdown-list'); + + $block_list_container.addClass('hidden'); if (e.code !== 'Backspace' && e.code !== 'Delete') { return; } @@ -35,7 +38,7 @@ export default class Paragraph extends Block { const {textContent} = this._element; if (textContent === '') { - $(this.wrapper.parentElement).find('.block-list-container.dropdown-list').show(); + $block_list_container .removeClass('hidden'); this._element.innerHTML = ''; } } @@ -53,11 +56,11 @@ export default class Paragraph extends Block { if (textContent !== '') return; let $wrapper = $(this.wrapper).hasClass('ce-paragraph') ? $(this.wrapper.parentElement) : $(this.wrapper); let $block_list_container = $wrapper.find('.block-list-container.dropdown-list'); - $block_list_container.show(); + $block_list_container.removeClass('hidden'); }); div.addEventListener('blur', () => { let $block_list_container = $(this.wrapper.parentElement).find('.block-list-container.dropdown-list'); - setTimeout(() => $block_list_container.hide(), 1); + setTimeout(() => $block_list_container.addClass('hidden'), 1); }); div.dataset.placeholder = this.api.i18n.t(this._placeholder); div.addEventListener('keyup', this.onKeyUp); @@ -93,7 +96,7 @@ export default class Paragraph extends Block { $block_list_container.append($block_list_item); }); - $block_list_container.hide(); + $block_list_container.addClass('hidden'); $block_list_container.appendTo(this.wrapper); } From eb37ac45b797ae953db521916a286d4b9ce298f1 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 16 Dec 2021 13:05:23 +0530 Subject: [PATCH 015/101] fix: sider fix --- .../js/frappe/views/workspace/blocks/block.js | 16 ++++++++-------- .../js/frappe/views/workspace/blocks/header.js | 2 +- .../frappe/views/workspace/blocks/header_size.js | 6 +++--- .../js/frappe/views/workspace/blocks/spacer.js | 2 +- .../js/frappe/views/workspace/workspace.js | 12 ++++++------ frappe/public/js/frappe/widgets/base_widget.js | 10 ---------- 6 files changed, 19 insertions(+), 29 deletions(-) diff --git a/frappe/public/js/frappe/views/workspace/blocks/block.js b/frappe/public/js/frappe/views/workspace/blocks/block.js index 6c1a0307cc..b85b75a625 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/block.js +++ b/frappe/public/js/frappe/views/workspace/blocks/block.js @@ -68,13 +68,13 @@ export default class Block { // disable text selection on mousedown (on drag) function un_focus() { if (document.selection) { - document.selection.empty() + document.selection.empty(); } else { - window.getSelection().removeAllRanges() + window.getSelection().removeAllRanges(); } } - function stop_drag(e) { + function stop_drag() { $(this).css("cursor", "default"); $('.widget').css("pointer-events", "auto"); $(me.wrapper.parentElement).find('.resizer').css("border-right", "0px solid transparent"); @@ -173,7 +173,7 @@ export default class Block { icon: frappe.utils.icon('down-arrow', 'sm'), action: () => this.move_block('down') } - ] + ]; let $widget_control = $(this.wrapper).find('.widget-control'); @@ -201,22 +201,22 @@ export default class Block { }); return html; - } + }; $button.click(event => { event.stopPropagation(); $button.find('.dropdown-list').toggleClass('hidden'); }); - $(document).click(event => { + $(document).click(() => { $button.find('.dropdown-list').addClass('hidden'); - }) + }); $widget_control.prepend($button); this.dropdown_list.forEach((item) => { $button.find('.dropdown-list').append(dropdown_item(item.label, item.title, item.icon, item.action)); - }) + }); } add_tune_button() { diff --git a/frappe/public/js/frappe/views/workspace/blocks/header.js b/frappe/public/js/frappe/views/workspace/blocks/header.js index 6c11a0bb28..d47b1f5cff 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header.js @@ -69,7 +69,7 @@ export default class Header extends Block { save() { this.wrapper = this._element; return { - text: this.wrapper.innerHTML.replace(/ /gi,''), + text: this.wrapper.innerHTML.replace(/ /gi, ''), col: this.get_col() }; } diff --git a/frappe/public/js/frappe/views/workspace/blocks/header_size.js b/frappe/public/js/frappe/views/workspace/blocks/header_size.js index 3d5612c046..3694b8799b 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/header_size.js +++ b/frappe/public/js/frappe/views/workspace/blocks/header_size.js @@ -49,7 +49,7 @@ export default class HeaderSize { } change_size(range, size) { - if (!range) return + if (!range) return; let span = document.createElement('SPAN'); @@ -68,11 +68,11 @@ export default class HeaderSize { let selected_text = span.innerText; let parent_tag = parent_node.parentElement; - if(diff !== selected_text) { + if (diff !== selected_text) { parent_tag = parent_node; } - if(parent_tag.innerText == selected_text) { + if (parent_tag.innerText == selected_text) { if (!parent_tag.classList.contains('ce-header') && !parent_tag.classList.contains('ce-paragraph')) { this.remove_parent_tag(range, parent_node.parentElement, span); parent_tag.remove(); diff --git a/frappe/public/js/frappe/views/workspace/blocks/spacer.js b/frappe/public/js/frappe/views/workspace/blocks/spacer.js index a2cc144885..bb75cea873 100644 --- a/frappe/public/js/frappe/views/workspace/blocks/spacer.js +++ b/frappe/public/js/frappe/views/workspace/blocks/spacer.js @@ -18,7 +18,7 @@ export default class Spacer extends Block { render() { this.wrapper = document.createElement('div'); - this.wrapper.classList.add('widget','spacer'); + this.wrapper.classList.add('widget', 'spacer'); if (!this.readOnly) { let $spacer = $(`
diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index c85da6f386..aa5ba148b5 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -545,7 +545,7 @@ frappe.views.Workspace = class Workspace { new_updated_item.parent_page = new_item.parent || ""; new_updated_item.public = new_item.is_public; - if (is_title_changed || is_section_changed){ + if (is_title_changed || is_section_changed) { if (new_item.is_public) { new_updated_item.name = new_item.title; new_updated_item.label = new_item.title; @@ -652,7 +652,7 @@ frappe.views.Workspace = class Workspace { icon: frappe.utils.icon('setting-gear', 'sm'), action: () => frappe.set_route(`workspace/${item.name}`) }, - ] + ]; let $button = $(` `); - let all_blocks = frappe.wspace_block.blocks; + let all_blocks = frappe.workspace_block.blocks; Object.keys(all_blocks).forEach(key => { let $block_list_item = $(`