From 009556b8f7e9f30e5b24095ef2fc5c14590cfe33 Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Wed, 3 Jan 2024 16:47:14 +0100
Subject: [PATCH] :sparkles: Improve grid cell selection

---
 common/src/app/common/types/shape/layout.cljc | 17 ++++++++++
 .../data/workspace/grid_layout/editor.cljs    | 33 +++++++++++++++----
 .../app/main/ui/workspace/context_menu.cljs   |  4 ++-
 .../viewport/grid_layout_editor.cljs          | 15 +++++++--
 4 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc
index 86aff113e..08645a098 100644
--- a/common/src/app/common/types/shape/layout.cljc
+++ b/common/src/app/common/types/shape/layout.cljc
@@ -1397,6 +1397,23 @@
                   (< (inc index) (+ column column-span)))
              (= (inc index) column)))))))
 
+(defn cells-in-area
+  [parent first-row last-row first-column last-column]
+  (->> (:layout-grid-cells parent)
+       (vals)
+       (filter
+        (fn [{:keys [row column row-span column-span] :as cell}]
+          (and
+           (or (<= row first-row (+ row row-span -1))
+               (<= row last-row (+ row row-span -1))
+               (<= first-row row last-row)
+               (<= first-row (+ row row-span -1) last-row))
+
+           (or (<= column first-column (+ column column-span -1))
+               (<= column last-column (+ column column-span -1))
+               (<= first-column column last-column)
+               (<= first-column (+ column column-span -1) last-column)))))))
+
 (defn shapes-by-row
   ([parent index]
    (shapes-by-row parent index true))
diff --git a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs
index dc6e99fa5..9af194119 100644
--- a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs
+++ b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs
@@ -6,6 +6,7 @@
 
 (ns app.main.data.workspace.grid-layout.editor
   (:require
+   [app.common.data.macros :as dm]
    [app.common.geom.rect :as grc]
    [app.common.types.shape.layout :as ctl]
    [app.main.data.workspace.state-helpers :as wsh]
@@ -25,14 +26,34 @@
              (conj hover-set cell-id)
              (disj hover-set cell-id))))))))
 
-(defn select-grid-cell
-  [grid-id cell-id add?]
-  (ptk/reify ::select-grid-cell
+(defn add-to-selection
+  ([grid-id cell-id]
+   (add-to-selection grid-id cell-id false))
+  ([grid-id cell-id shift?]
+   (ptk/reify ::add-to-selection
+     ptk/UpdateEvent
+     (update [_ state]
+       (if shift?
+         (let [objects  (wsh/lookup-page-objects state)
+               grid     (get objects grid-id)
+               selected (or (dm/get-in state [:workspace-grid-edition grid-id :selected]) #{})
+               selected (into selected [cell-id])
+               cells    (->> selected (map #(dm/get-in grid [:layout-grid-cells %])))
+
+               {:keys [first-row last-row first-column last-column]} (ctl/cells-coordinates cells)
+               new-selected
+               (into #{}
+                     (map :id)
+                     (ctl/cells-in-area grid first-row last-row first-column last-column))]
+           (assoc-in state [:workspace-grid-edition grid-id :selected] new-selected))
+         (update-in state [:workspace-grid-edition grid-id :selected] (fnil conj #{}) cell-id))))))
+
+(defn set-selection
+  [grid-id cell-id]
+  (ptk/reify ::set-selection
     ptk/UpdateEvent
     (update [_ state]
-      (if add?
-        (update-in state [:workspace-grid-edition grid-id :selected] (fnil conj #{}) cell-id)
-        (assoc-in state [:workspace-grid-edition grid-id :selected] #{cell-id})))))
+      (assoc-in state [:workspace-grid-edition grid-id :selected] #{cell-id}))))
 
 (defn remove-selection
   [grid-id cell-id]
diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs
index 1e2296454..af84fffa6 100644
--- a/frontend/src/app/main/ui/workspace/context_menu.cljs
+++ b/frontend/src/app/main/ui/workspace/context_menu.cljs
@@ -576,6 +576,7 @@
   (let [{:keys [grid cells]} mdata
 
         single? (= (count cells) 1)
+
         can-merge?
         (mf/use-memo
          (mf/deps cells)
@@ -603,7 +604,8 @@
                        :on-click do-merge-cells}])
 
      [:& menu-entry {:title (tr "workspace.context-menu.grid-cells.create-board")
-                     :on-click do-create-board}]]))
+                     :on-click do-create-board
+                     :disabled (and (not single?) (not can-merge?))}]]))
 
 
 (mf/defc context-menu
diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs
index ddc740d96..7b99e9f61 100644
--- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs
@@ -337,10 +337,19 @@
         (mf/use-callback
          (mf/deps (:id shape) (:id cell) selected?)
          (fn [event]
-           (when (or (dom/left-mouse? event) (not selected?))
-             (if (and (kbd/shift? event) selected?)
+           (when (dom/left-mouse? event)
+             (cond
+               (and selected? (or (kbd/mod? event) (kbd/shift? event)))
                (st/emit! (dwge/remove-selection (:id shape) (:id cell)))
-               (st/emit! (dwge/select-grid-cell (:id shape) (:id cell) (kbd/shift? event)))))))
+
+               (and (not selected?) (kbd/mod? event))
+               (st/emit! (dwge/add-to-selection (:id shape) (:id cell)))
+
+               (and (not selected?) (kbd/shift? event))
+               (st/emit! (dwge/add-to-selection (:id shape) (:id cell) true))
+
+               :else
+               (st/emit! (dwge/set-selection (:id shape) (:id cell)))))))
 
         handle-context-menu
         (mf/use-callback