From fb64db0a284c55e9d7cb8dccc9c7134a2d5837a7 Mon Sep 17 00:00:00 2001 From: Eugen Ciur <eugen@papermerge.com> Date: Tue, 9 Nov 2021 20:29:05 +0100 Subject: [PATCH] pretty good selection + rename functionality --- app/components/button/upload.hbs | 4 +- app/components/commander/action_buttons.hbs | 53 ++++++++++-- app/components/commander/action_buttons.js | 33 +++++++ app/components/commander/index.hbs | 12 ++- app/components/commander/index.js | 36 ++++++++ app/components/document.hbs | 20 +++-- app/components/document.js | 6 ++ app/components/documents.js | 6 -- app/components/folder.hbs | 6 +- app/components/folder.js | 5 +- app/components/modal/rename_node.hbs | 18 ++++ app/components/modal/rename_node.js | 52 +++++++++++ app/components/node.js | 20 +++++ app/styles/node.scss | 96 +++++++++++++++------ 14 files changed, 315 insertions(+), 52 deletions(-) create mode 100644 app/components/commander/action_buttons.js create mode 100644 app/components/document.js delete mode 100644 app/components/documents.js create mode 100644 app/components/modal/rename_node.hbs create mode 100644 app/components/modal/rename_node.js create mode 100644 app/components/node.js diff --git a/app/components/button/upload.hbs b/app/components/button/upload.hbs index 543d2a8..1f1f858 100644 --- a/app/components/button/upload.hbs +++ b/app/components/button/upload.hbs @@ -5,8 +5,8 @@ {{on "change" this.onUploadChange }} /> <button - class="btn btn-bordered btn-light btn-flat" + class="btn btn-success" type="button" {{on "click" this.onClickProxyUpload }}> - <i class="fa fa-upload mr-1 text-success"></i> + <i class="fa fa-upload mr-1"></i> Upload </button> \ No newline at end of file diff --git a/app/components/commander/action_buttons.hbs b/app/components/commander/action_buttons.hbs index dbe6b71..330d1dd 100644 --- a/app/components/commander/action_buttons.hbs +++ b/app/components/commander/action_buttons.hbs @@ -1,10 +1,49 @@ <div> - <Button::Upload @node={{@node}} /> + {{#if this.one_node_selected}} + <button + class="btn btn-success" + type="button" + {{on "click" this.onRename}}> + <i class="fa fa-edit"></i> + Rename + </button> + <button + class="btn btn-success" + type="button"> + <i class="fa fa-download"></i> + Download + </button> + <button + class="btn btn-danger mx-5" + type="button" + {{on "click" this.onDelete}}> + <i class="fa fa-times"></i> + Delete + </button> + {{else if this.multiple_nodes_selected}} + <button + class="btn btn-success" + type="button"> + <i class="fa fa-download"></i> + Download + </button> + <button + class="btn btn-danger mx-5" + type="button" + {{on "click" this.onDelete}}> + <i class="fa fa-times"></i> + Delete + </button> + {{else}} + {{! No nodes are currently selected }} + <Button::Upload + @node={{@node}} /> - <button - class="btn btn-bordered btn-light btn-flat" - type="button" {{on "click" @openNewFolderModal }}> - <i class="fa fa-plus mr-1 text-success"></i> - New Folder - </button> + <button + class="btn btn-success" + type="button" {{on "click" @openNewFolderModal }}> + <i class="fa fa-plus mr-1"></i> + New Folder + </button> + {{/if}} </div> \ No newline at end of file diff --git a/app/components/commander/action_buttons.js b/app/components/commander/action_buttons.js new file mode 100644 index 0000000..bbec4e2 --- /dev/null +++ b/app/components/commander/action_buttons.js @@ -0,0 +1,33 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + + +export default class ActionButtonsComponent extends Component { + /* + Arguments: + `openNewFolderModal` - action invoked when new folder button + is clicked + `node` - current node i.e. current + folder whose content is currenlty being displayed + `selectedNodes` - array of selected nodes + */ + get one_node_selected() { + return this.args.selectedNodes.length === 1; + } + + get multiple_nodes_selected() { + return this.args.selectedNodes.length > 1; + } + + @action + onRename() { + this.args.openRenameModal( + this.args.selectedNodes[0] + ); + } + + @action + onDelete() { + + } +} diff --git a/app/components/commander/index.hbs b/app/components/commander/index.hbs index d95c971..812a1a9 100644 --- a/app/components/commander/index.hbs +++ b/app/components/commander/index.hbs @@ -2,6 +2,8 @@ <div class="d-flex justify-content-between"> <Commander::ActionButtons @openNewFolderModal={{this.openNewFolderModal}} + @openRenameModal={{this.openRenameModal}} + @selectedNodes={{this.selected_nodes}} @node={{@node}} /> <Commander::ActionModes @@ -16,6 +18,12 @@ @onClose={{this.closeNewFolderModal}} {{show-when this.show_new_folder_modal}} /> + <Modal::RenameNode + id="rename-node" + @selectedNodes={{this.selected_nodes}} + @onClose={{this.closeRenameModal}} + {{show-when this.show_rename_node_modal}} /> + <Breadcrumb @node={{@node}} @extranode={{@extranode}} @@ -25,7 +33,9 @@ {{#each @node.children as |node|}} {{#let (component node.nodeType) as |NodeType|}} {{! NodeType is either <Folder /> or <Document />}} - <NodeType @model={{node}}> + <NodeType + @model={{node}} + @onCheckboxChange={{this.onCheckboxChange}} > {{#if (is_equal @hint "left")}} <Commander::LinkToLeft @node={{node}} diff --git a/app/components/commander/index.js b/app/components/commander/index.js index 64f348d..d2de83a 100644 --- a/app/components/commander/index.js +++ b/app/components/commander/index.js @@ -1,19 +1,36 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; +import { A } from '@ember/array'; import { action } from '@ember/object'; export default class CommanderComponent extends Component { // show create new folder modal dialog? @tracked show_new_folder_modal = false; + + // show rename node modal dialog? + @tracked show_rename_node_modal = false; + // nodes are displayed as list or as grid? @tracked view_mode = 'list'; + @tracked selected_nodes = A([]); + @action openNewFolderModal() { this.show_new_folder_modal = true; } + @action + openRenameModal() { + this.show_rename_node_modal = true; + } + + @action + closeRenameModal() { + this.show_rename_node_modal = false; + } + @action closeNewFolderModal() { this.show_new_folder_modal = false; @@ -23,4 +40,23 @@ export default class CommanderComponent extends Component { onViewModeChange(new_view_mode) { this.view_mode = new_view_mode; } + + @action + onCheckboxChange({node, is_selected}) { + /** + Triggered whenever node's checkbox changes. + + `node` is an instance of Node model of whose + selection state changed. + + `is_selected` - new selection state i.e. if user checked + the checkbox then `is_selected` is true; if user unchecked + the checkbox then `is_selected` is false; + */ + if (is_selected) { + this.selected_nodes.pushObject(node); + } else { + this.selected_nodes.removeObject(node); + } + } } diff --git a/app/components/document.hbs b/app/components/document.hbs index 9d3de59..e2ee59e 100644 --- a/app/components/document.hbs +++ b/app/components/document.hbs @@ -1,9 +1,13 @@ -<div class="node document"> - <Input @type="checkbox" /> - <div class="icon document"></div> - <div class="title"> - <LinkTo @route="authenticated.document" @model={{@model}}> - {{@model.title}} - </LinkTo> - </div> +<div class="node document {{if this.is_selected "checked"}}"> + <Input + @type="checkbox" + {{on "change" this.onCheckboxChange}} /> + <div class="icon document"></div> + <div class="title"> + <LinkTo + @route="authenticated.document" + @model={{@model}}> + {{@model.title}} + </LinkTo> + </div> </div> \ No newline at end of file diff --git a/app/components/document.js b/app/components/document.js new file mode 100644 index 0000000..c2c1868 --- /dev/null +++ b/app/components/document.js @@ -0,0 +1,6 @@ +import NodeComponent from "./node"; + + +export default class DocumentComponent extends NodeComponent { + +} diff --git a/app/components/documents.js b/app/components/documents.js deleted file mode 100644 index 386f372..0000000 --- a/app/components/documents.js +++ /dev/null @@ -1,6 +0,0 @@ -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; - -export default class DocumentsComponent extends Component { - @tracked query = ''; -} diff --git a/app/components/folder.hbs b/app/components/folder.hbs index a63727c..efc14be 100644 --- a/app/components/folder.hbs +++ b/app/components/folder.hbs @@ -1,5 +1,7 @@ -<div class="node folder"> - <Input @type="checkbox" /> +<div class="node folder {{if this.is_selected "checked"}}"> + <Input + @type="checkbox" + {{on "change" this.onCheckboxChange}} /> <div class="icon folder"></div> <div class="title"> {{yield}} diff --git a/app/components/folder.js b/app/components/folder.js index e8f8a27..0fd871d 100644 --- a/app/components/folder.js +++ b/app/components/folder.js @@ -1,7 +1,7 @@ -import Component from '@glimmer/component'; +import NodeComponent from "./node"; -export default class FolderComponent extends Component { +export default class FolderComponent extends NodeComponent { get query() { if (this.args.extranode) { @@ -16,4 +16,5 @@ export default class FolderComponent extends Component { get model() { return this.args.model; } + } diff --git a/app/components/modal/rename_node.hbs b/app/components/modal/rename_node.hbs new file mode 100644 index 0000000..a7efb36 --- /dev/null +++ b/app/components/modal/rename_node.hbs @@ -0,0 +1,18 @@ + +<Modal::Base + @title="Rename" + @actionTitle="Rename" + @onClose={{@onClose}} + @onSubmit={{this.onSubmit}} + @onCancel={{this.onCancel}} + ...attributes +> + {{this.title}} + <label for="node-title" class="form-label">New title:</label> + <Input + id="node-title" + class="form-control" + @type="text" + @value={{this.new_title}} + /> +</Modal::Base> \ No newline at end of file diff --git a/app/components/modal/rename_node.js b/app/components/modal/rename_node.js new file mode 100644 index 0000000..0ab2cb0 --- /dev/null +++ b/app/components/modal/rename_node.js @@ -0,0 +1,52 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; + + +export default class RenameNodeComponent extends Component { + @service store; + @service currentUser; + + + get new_title() { + let selected_nodes; + + if (this.title) { + return this.title; + } + + selected_nodes = this.args.selectedNodes; + if (selected_nodes && selected_nodes[0]) { + return selected_nodes[0].title; + } + + return ''; + } + + set new_title(value) { + this.title = value; + } + + get node() { + return this.args.selectedNodes[0]; + } + + @action + onSubmit() { + + this.store.findRecord('node', this.node.id).then( + (node) => { + node.title = this.title; + node.save(); + }); + + this.args.onClose(); + } + + @action + onCancel() { + this.args.onClose(); + this.title = ''; + } + +} diff --git a/app/components/node.js b/app/components/node.js new file mode 100644 index 0000000..144bada --- /dev/null +++ b/app/components/node.js @@ -0,0 +1,20 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; + + +export default class NodeComponent extends Component { + @tracked is_selected = false; + + @action + onCheckboxChange(event) { + let is_checked = event.target.checked; + + this.is_selected = is_checked; + + this.args.onCheckboxChange({ + node: this.args.model, + is_selected: is_checked + }); + } +} diff --git a/app/styles/node.scss b/app/styles/node.scss index 3dc95a8..912d8c5 100644 --- a/app/styles/node.scss +++ b/app/styles/node.scss @@ -1,39 +1,87 @@ .node { + display: flex; + align-items: center; + .icon { + width: 4rem; + height: 3rem; + } + + .icon.folder { + background-image: url("/assets/images/folder.svg"); + background-size: 100% 88%; + } + + .icon.document { + background-image: url("/assets/images/document.svg"); + background-size: 100% 100%; + } + + &.checked { + background-color: #e6f5ff; + outline: 1px solid #A6DAFF; + } + + &:hover { + background-color: #8ed2fe66; + outline: 1px solid #6fc5ff; + //border: 1px solid $result_list_hover_border_color; + } + .title { + a { + text-decoration: none; + } + text-align: center; + opacity: 1; + z-index: 0; + } +} + + +.view-mode-grid { + display: flex; + flex-direction: row; + flex-wrap: wrap; + + .node { + + width: 8rem; + height: 8rem; display: flex; - align-items: center; + flex-direction: column; + + margin: 0.75rem; + padding: 0.5rem; .icon { - width: 4rem; - height: 3rem; + width: 6rem; + height: 5rem; } - .icon.folder { - background-image: url("/assets/images/folder.svg"); - background-size: 100% 88%; + input[type=checkbox] { + align-self: start; + visibility: hidden; } - .icon.document { - background-image: url("/assets/images/document.svg"); - background-size: 100% 100%; + &:hover { + input[type=checkbox] { + visibility: visible; + } } &.checked { - background-color: #e6f5ff; - outline: 1px solid #A6DAFF; + input[type=checkbox] { + visibility: visible; + } } + } +} - &:hover { - background-color: #8ed2fe66; - outline: 1px solid #6fc5ff; - //border: 1px solid $result_list_hover_border_color; - } - .title { - a { - text-decoration: none; - } - text-align: center; - opacity: 1; - z-index: 0; - } +.view-mode-list { + display: flex; + flex-direction: column; + + .node { + padding: 0.25rem; + } } \ No newline at end of file -- GitLab