From 37636210c76296523d479928f7eeb3062ff49333 Mon Sep 17 00:00:00 2001
From: Tsering Paljor <20007514-paljort@users.noreply.gitlab.com>
Date: Tue, 5 Mar 2024 08:16:41 +0000
Subject: [PATCH] Add Keyboard Shortcut Support for Page Elements

---
 .../components/elements/ElementMenu.vue       |  6 +++---
 .../components/elements/ElementPreview.vue    | 20 +++++++++++++++++++
 .../elements/components/ColumnElement.vue     | 10 ++++------
 web-frontend/modules/builder/store/element.js |  7 ++++---
 4 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/web-frontend/modules/builder/components/elements/ElementMenu.vue b/web-frontend/modules/builder/components/elements/ElementMenu.vue
index c41de4410..ab17280a4 100644
--- a/web-frontend/modules/builder/components/elements/ElementMenu.vue
+++ b/web-frontend/modules/builder/components/elements/ElementMenu.vue
@@ -6,7 +6,7 @@
     <a v-else class="element-preview__menu-item" @click="$emit('duplicate')">
       <i class="iconoir-copy"></i>
       <span class="element-preview__menu-item-description">
-        {{ $t('action.duplicate') }}
+        {{ $t('action.duplicate') }} (d)
       </span>
     </a>
     <a
@@ -16,7 +16,7 @@
     >
       <i class="iconoir-scale-frame-enlarge"></i>
       <span class="element-preview__menu-item-description">
-        {{ $t('elementMenu.selectParent') }}
+        {{ $t('elementMenu.selectParent') }} (p)
       </span>
     </a>
     <a
@@ -89,7 +89,7 @@
     <a class="element-preview__menu-item" @click="$emit('delete')">
       <i class="iconoir-bin"></i>
       <span class="element-preview__menu-item-description">
-        {{ $t('action.delete') }}
+        {{ $t('action.delete') }} (del)
       </span>
     </a>
   </div>
diff --git a/web-frontend/modules/builder/components/elements/ElementPreview.vue b/web-frontend/modules/builder/components/elements/ElementPreview.vue
index b23216b50..e12ac057c 100644
--- a/web-frontend/modules/builder/components/elements/ElementPreview.vue
+++ b/web-frontend/modules/builder/components/elements/ElementPreview.vue
@@ -1,5 +1,6 @@
 <template>
   <div
+    :key="element.id"
     class="element-preview"
     :class="{
       'element-preview--active': isSelected,
@@ -7,7 +8,11 @@
       'element-preview--in-error': inError,
       'element-preview--first-element': isFirstElement,
     }"
+    tabindex="0"
     @click="onSelect"
+    @keyup.d.stop="duplicateElement"
+    @keyup.delete.stop="deleteElement"
+    @keyup.p.stop="selectParentElement"
   >
     <InsertElementButton
       v-show="isSelected"
@@ -101,6 +106,16 @@ export default {
       isDuplicating: false,
     }
   },
+  watch: {
+    /**
+     * Focuses the element if the element has been selected.
+     */
+    isSelected(newValue, old) {
+      if (newValue && !old) {
+        this.$el.focus()
+      }
+    },
+  },
   computed: {
     ...mapGetters({
       elementSelected: 'element/getSelected',
@@ -213,6 +228,11 @@ export default {
         notifyIf(error)
       }
     },
+    selectParentElement() {
+      if (this.parentElement) {
+        this.actionSelectElement({ element: this.parentElement })
+      }
+    },
   },
 }
 </script>
diff --git a/web-frontend/modules/builder/components/elements/components/ColumnElement.vue b/web-frontend/modules/builder/components/elements/components/ColumnElement.vue
index fa96976c2..1dede001e 100644
--- a/web-frontend/modules/builder/components/elements/components/ColumnElement.vue
+++ b/web-frontend/modules/builder/components/elements/components/ColumnElement.vue
@@ -15,15 +15,12 @@
       <template v-if="childrenInColumn.length > 0">
         <div
           v-for="(childCurrent, rowIndex) in childrenInColumn"
-          :key="rowIndex"
+          :key="childCurrent.id"
           class="column-element__element"
         >
           <ElementPreview
             v-if="mode === 'editing'"
-            class="element"
             :element="childCurrent"
-            :active="childCurrent.id === elementSelectedId"
-            :index="rowIndex"
             :placements="[
               PLACEMENTS.BEFORE,
               PLACEMENTS.AFTER,
@@ -167,10 +164,11 @@ export default {
       // the click event select the container because the element is removed from the
       // DOM too quickly
       await flushPromises()
+
       if (placement === PLACEMENTS.AFTER || placement === PLACEMENTS.BEFORE) {
-        this.moveVertical(element, rowIndex, columnIndex, placement)
+        await this.moveVertical(element, rowIndex, columnIndex, placement)
       } else {
-        this.moveHorizontal(element, columnIndex, placement)
+        await this.moveHorizontal(element, columnIndex, placement)
       }
     },
     async moveVertical(element, rowIndex, columnIndex, placement) {
diff --git a/web-frontend/modules/builder/store/element.js b/web-frontend/modules/builder/store/element.js
index 416faf6b9..ce7b2e244 100644
--- a/web-frontend/modules/builder/store/element.js
+++ b/web-frontend/modules/builder/store/element.js
@@ -9,6 +9,7 @@ const populateElement = (element) => {
     content: [],
     hasNextPage: false,
     reset: 0,
+    shouldBeFocused: false,
   }
 
   return element
@@ -272,7 +273,7 @@ const actions = {
     return elements
   },
   async move(
-    { dispatch, getters },
+    { commit, dispatch, getters },
     {
       page,
       elementId,
@@ -313,7 +314,7 @@ const actions = {
       throw error
     }
   },
-  async duplicate({ dispatch, getters }, { page, elementId }) {
+  async duplicate({ commit, dispatch, getters }, { page, elementId }) {
     const {
       data: { elements, workflow_actions: workflowActions },
     } = await ElementService(this.$client).duplicate(elementId)
@@ -337,7 +338,7 @@ const actions = {
         parentId === elementToDuplicate.parent_element_id
     )
 
-    dispatch('select', { page, element: elementToSelect })
+    commit('SELECT_ITEM', { element: elementToSelect })
 
     return elements
   },