From 6f1b40c74b6525507915a41e69dc0e2a9e994202 Mon Sep 17 00:00:00 2001
From: Eugen Ciur <eugen@papermerge.com>
Date: Thu, 21 Apr 2022 20:46:20 +0200
Subject: [PATCH] Add tag input component

---
 app/components/commander/action_buttons.hbs   | 72 +++++++++++++++----
 .../commander/context_menu/index.hbs          |  8 +++
 .../commander/context_menu/index.js           |  7 ++
 app/components/commander/index.hbs            |  7 ++
 app/components/commander/index.js             | 16 +++++
 app/components/modal/tags.hbs                 | 11 +++
 app/components/modal/tags.js                  | 22 ++++++
 app/components/tag_input/index.hbs            | 19 +++++
 app/components/tag_input/index.js             | 32 +++++++++
 app/components/tag_input/item.hbs             |  5 ++
 app/components/tag_input/item.js              | 11 +++
 app/styles/app.scss                           |  1 +
 app/styles/components/tag_input.scss          | 13 ++++
 13 files changed, 209 insertions(+), 15 deletions(-)
 create mode 100644 app/components/modal/tags.hbs
 create mode 100644 app/components/modal/tags.js
 create mode 100644 app/components/tag_input/index.hbs
 create mode 100644 app/components/tag_input/index.js
 create mode 100644 app/components/tag_input/item.hbs
 create mode 100644 app/components/tag_input/item.js
 create mode 100644 app/styles/components/tag_input.scss

diff --git a/app/components/commander/action_buttons.hbs b/app/components/commander/action_buttons.hbs
index 7a48b35..d1d78d4 100644
--- a/app/components/commander/action_buttons.hbs
+++ b/app/components/commander/action_buttons.hbs
@@ -1,33 +1,75 @@
 <div>
   {{#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::Download
       @inProgress={{this.download_in_progress}}
       @onDownloadNodes={{this.onDownloadNodes}} />
+
+    <button class="btn btn-light"
+      {{on "click" this.onRename}}
+      {{tooltip title='Rename' placement='bottom'}}>
+        <i class="fa fa-edit"></i>
+    </button>
+
     <button
-      class="btn btn-danger mx-5"
       type="button"
-      {{on "click" @openConfirmDeletionModal}}>
-        <i class="fa fa-times"></i>
-      Delete
+      data-bs-toggle="dropdown"
+      class="btn btn-light dropdown-toggle mx-1"
+      {{tooltip title='More' placement='right'}}>
+      ...
     </button>
+    <ul class="dropdown-menu"
+      aria-expand="false"
+      aria-labelledby="display-mode">
+      <li>
+        <a class="dropdown-item"
+            role='button'
+            {{on "click" @openTagsModal}}>
+          <i class="bi bi-tag"></i>
+          Tags
+        </a>
+      </li>
+      <li><hr class="dropdown-divider"></li>
+      <li>
+        <a class="dropdown-item"
+            role='button'
+            {{on "click" @openConfirmDeletionModal}}>
+          <i class="bi bi-trash"></i>
+          Delete
+        </a>
+      </li>
+    </ul>
   {{else if this.multiple_nodes_selected}}
     <Button::Download
       @inProgress={{this.download_in_progress}}
       @onDownloadNodes={{this.onDownloadNodes}} />
+
     <button
-      class="btn btn-danger mx-5"
       type="button"
-       {{on "click" @openConfirmDeletionModal}}>
-        <i class="fa fa-times"></i>
-      Delete
+      data-bs-toggle="dropdown"
+      class="btn btn-light dropdown-toggle mx-1"
+      {{tooltip title='More ...' placement='right'}}>
+      ...
     </button>
+    <ul class="dropdown-menu"
+      aria-expand="false"
+      aria-labelledby="display-mode">
+      <li>
+        <a class="dropdown-item"
+            role='button'
+            {{on "click" @openTagsModal}}>
+          <i class="bi bi-tag"></i>
+          Tags
+        </a>
+      </li>
+      <li>
+        <a class="dropdown-item"
+            role='button'
+            {{on "click" @openConfirmDeletionModal}}>
+          <i class="bi bi-trash"></i>
+          Delete
+        </a>
+      </li>
+    </ul>
   {{else}}
     {{! No nodes are currently selected }}
     <Button::Upload
diff --git a/app/components/commander/context_menu/index.hbs b/app/components/commander/context_menu/index.hbs
index c98e015..f7ea66f 100644
--- a/app/components/commander/context_menu/index.hbs
+++ b/app/components/commander/context_menu/index.hbs
@@ -1,3 +1,4 @@
+
 <ul class="dropdown-menu context-menu">
   <li>
     <a class="dropdown-item"
@@ -36,6 +37,13 @@
     </li>
   {{/if}}
   {{#if this.one_or_multiple_nodes_selected}}
+    <li>
+      <a class="dropdown-item"
+        role='button'
+        {{on "click" this.onTags }}>
+        <i class="bi bi-tag mx-2"></i>Tags
+      </a>
+    </li>
     <li>
       <a class="dropdown-item"
         role='button'
diff --git a/app/components/commander/context_menu/index.js b/app/components/commander/context_menu/index.js
index ff6e912..8c4c793 100644
--- a/app/components/commander/context_menu/index.js
+++ b/app/components/commander/context_menu/index.js
@@ -79,6 +79,13 @@ export default class ContextMenuComponent extends Component {
     );
   }
 
+  @action
+  onTags() {
+    this.args.openTagsModal(
+      this.args.selectedNodes
+    );
+  }
+
   @action
   async onDownloadNodes() {
     this.download_in_progress = true;
diff --git a/app/components/commander/index.hbs b/app/components/commander/index.hbs
index 0b62aaa..998fc2c 100644
--- a/app/components/commander/index.hbs
+++ b/app/components/commander/index.hbs
@@ -16,6 +16,7 @@
   <Commander::ContextMenu
     @openNewFolderModal={{this.openNewFolderModal}}
     @openConfirmDeletionModal={{this.openConfirmDeletionModal}}
+    @openTagsModal={{this.openTagsModal}}
     @onSelectionChanged={{this.onSelectionChanged}}
     @nodes={{this.children}}
     @selectedNodes={{this.selected_nodes}}
@@ -27,6 +28,7 @@
       @openNewFolderModal={{this.openNewFolderModal}}
       @openRenameModal={{this.openRenameModal}}
       @openConfirmDeletionModal={{this.openConfirmDeletionModal}}
+      @openTagsModal={{this.openTagsModal}}
       @onCreateDocumentModel={{this.onCreateDocumentModel}}
       @onDownloadNodes={{this.onDownloadNodes}}
       @selectedNodes={{this.selected_nodes}}
@@ -67,6 +69,11 @@
     @onSubmit={{this.onSubmitMovePages}}
     {{show-when this.show_confirm_move_pages_modal}} />
 
+  <Modal::Tags
+    @onSubmit={{this.onSubmitTagsModal}}
+    @onCancel={{this.onCancelTagsModal}}
+    {{show-when this.show_tags_modal}}/>
+
   <Breadcrumb
     @node={{@node}}
     @extranode={{@extranode}}
diff --git a/app/components/commander/index.js b/app/components/commander/index.js
index 40b05e4..822693b 100644
--- a/app/components/commander/index.js
+++ b/app/components/commander/index.js
@@ -33,6 +33,7 @@ export default class CommanderComponent extends Component {
   @tracked show_rename_node_modal = false;
 
   @tracked show_confirm_deletion_modal = false;
+  @tracked show_tags_modal = false;
 
   // localStorage is tracked
   @localStorage left_view_mode = 'list';
@@ -130,6 +131,11 @@ export default class CommanderComponent extends Component {
     this.__deleted_records = deleted_records;
   }
 
+  @action
+  openTagsModal() {
+    this.show_tags_modal = true;
+  }
+
   @action
   onCreateDocumentModel(new_record) {
     /*
@@ -328,6 +334,16 @@ export default class CommanderComponent extends Component {
     this.router.refresh();
   }
 
+  @action
+  async onSubmitTagsModal(tags) {
+    this.show_tags_modal = false;
+  }
+
+  @action
+  onCancelTagsModal() {
+    this.show_tags_modal = false;
+  }
+
   @action
   onDragEnter({event, element}) {
     event.preventDefault();
diff --git a/app/components/modal/tags.hbs b/app/components/modal/tags.hbs
new file mode 100644
index 0000000..318fce1
--- /dev/null
+++ b/app/components/modal/tags.hbs
@@ -0,0 +1,11 @@
+<Modal::Base
+  @title="Tags"
+  @actionTitle="Submit"
+  @onClose={{@onClose}}
+  @onSubmit={{this.onSubmit}}
+  @onCancel={{this.onCancel}}
+  ...attributes
+>
+  <label for="folder-title" class="form-label">Tags editor:</label>
+  <TagInput />
+</Modal::Base>
\ No newline at end of file
diff --git a/app/components/modal/tags.js b/app/components/modal/tags.js
new file mode 100644
index 0000000..21a788b
--- /dev/null
+++ b/app/components/modal/tags.js
@@ -0,0 +1,22 @@
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { action } from '@ember/object';
+import { service } from '@ember/service';
+
+
+export default class TagsModalComponent extends Component {
+  @tracked tags = '';
+  @service store;
+  @service currentUser;
+
+  @action
+  async onSubmit() {
+    this.args.onSubmit(this.tags);
+  }
+
+  @action
+  onCancel() {
+    this.args.onCancel();
+    this.title = '';
+  }
+}
diff --git a/app/components/tag_input/index.hbs b/app/components/tag_input/index.hbs
new file mode 100644
index 0000000..dfe00c9
--- /dev/null
+++ b/app/components/tag_input/index.hbs
@@ -0,0 +1,19 @@
+<div class="tag-input d-flex  align-items-center">
+  {{#each this.tags as |tag index|}}
+    <TagInput::Item
+      @tag={{tag}}
+      @index={{index}}
+      @onRemoveTagItem={{this.onRemoveTagItem}} />
+  {{/each}}
+
+  <li class="tag-input-new">
+    <Input
+      disabled={{this.readOnly}}
+      class={{concat 'tag-input-new-item' (if this.readOnly ' is-disabled')}}
+      placeholder={{this.placeholder}}
+      {{on-key "Space" this.onAddTag }}
+      {{on-key "Comma" this.onAddTag }}
+      {{on-key "Enter" this.onAddTag }}
+      @value={{this.new_tag_value}} />
+  </li>
+</div>
diff --git a/app/components/tag_input/index.js b/app/components/tag_input/index.js
new file mode 100644
index 0000000..8a29f63
--- /dev/null
+++ b/app/components/tag_input/index.js
@@ -0,0 +1,32 @@
+import Component from '@glimmer/component';
+import { action } from '@ember/object';
+import { tracked } from 'tracked-built-ins';
+import { TrackedArray } from 'tracked-built-ins';
+
+
+export default class TagInputComponent extends Component {
+
+  @tracked tags = new TrackedArray([]);
+  @tracked new_tag_value = '';
+
+  @action
+  onAddTag() {
+    this.tags.push({
+      name: this.new_tag_value
+    });
+    this.new_tag_value = '';
+  }
+
+  @action
+  onRemoveTagItem(index) {
+    this.tags.splice(index, 1);
+  }
+
+  get placeholder() {
+    return "Add a tag..."
+  }
+
+  get readOnly() {
+    return false;
+  }
+}
diff --git a/app/components/tag_input/item.hbs b/app/components/tag_input/item.hbs
new file mode 100644
index 0000000..1013056
--- /dev/null
+++ b/app/components/tag_input/item.hbs
@@ -0,0 +1,5 @@
+<div class="tag">
+  <span>{{@tag.name}}</span>
+  <i class="bi bi-x" role='button'
+    {{on "click" this.onRemoveTagItem}}></i>
+</div>
\ No newline at end of file
diff --git a/app/components/tag_input/item.js b/app/components/tag_input/item.js
new file mode 100644
index 0000000..ff30ee7
--- /dev/null
+++ b/app/components/tag_input/item.js
@@ -0,0 +1,11 @@
+import Component from '@glimmer/component';
+import { action } from '@ember/object';
+
+
+export default class TagInputItemComponent extends Component {
+
+  @action
+  onRemoveTagItem() {
+    this.args.onRemoveTagItem(this.args.index);
+  }
+}
\ No newline at end of file
diff --git a/app/styles/app.scss b/app/styles/app.scss
index 3bd27af..775220f 100644
--- a/app/styles/app.scss
+++ b/app/styles/app.scss
@@ -11,6 +11,7 @@
 @import "./ui_select.scss";
 @import "./context_menu.scss";
 @import "./zoom.scss";
+@import "./components/tag_input.scss";
 
 
 body {
diff --git a/app/styles/components/tag_input.scss b/app/styles/components/tag_input.scss
new file mode 100644
index 0000000..313cfe7
--- /dev/null
+++ b/app/styles/components/tag_input.scss
@@ -0,0 +1,13 @@
+.tag-input {
+  border: 1px solid #888;
+  padding: 0.5rem;
+
+  li.tag-input-new {
+    list-style: none;
+
+    input {
+      border: none;
+      outline: none;
+    }
+  }
+}
\ No newline at end of file
-- 
GitLab