From 0e49625ebff9b424f0b17251c1e3277a62c88f5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Fri, 30 Sep 2022 10:42:12 +0200 Subject: [PATCH] :sparkles: Improve interactions with nested boards --- CHANGES.md | 1 + .../app/common/types/shape/interactions.cljc | 18 +++++ .../app/main/data/workspace/interactions.cljs | 67 ++++++++++++++----- .../app/main/data/workspace/transforms.cljs | 15 ----- .../sidebar/options/menus/interactions.cljs | 2 +- .../sidebar/options/menus/measures.cljs | 13 +++- 6 files changed, 81 insertions(+), 35 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 077c99deb..512dc1f0c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,7 @@ ### :sparkles: New features +- Improve interactions with nested boards [Taiga #4054](https://tree.taiga.io/project/penpot/us/4054) - Add team hero in projects dashboard [Taiga #3863](https://tree.taiga.io/project/penpot/us/3863) - Add zoom style to shared link [Taiga #3874](https://tree.taiga.io/project/penpot/us/3874) - Add dashboard creation button as placeholder [Taiga #3861](https://tree.taiga.io/project/penpot/us/3861) diff --git a/common/src/app/common/types/shape/interactions.cljc b/common/src/app/common/types/shape/interactions.cljc index c28f40d89..db55d26db 100644 --- a/common/src/app/common/types/shape/interactions.cljc +++ b/common/src/app/common/types/shape/interactions.cljc @@ -512,6 +512,18 @@ (assert (has-offset-effect? interaction)) (update interaction :animation assoc :offset-effect offset-effect)) +(defn dest-to? + "Check if the interaction has the given frame as destination." + [interaction frame-id] + (and (has-destination interaction) + (= (:destination interaction) frame-id))) + +(defn navs-to? + "Check if the interaction is a navigation to the given frame." + [interaction frame-id] + (and (= (:action-type interaction) :navigate) + (= (:destination interaction) frame-id))) + ;; -- Helpers for interactions (defn add-interaction @@ -543,6 +555,12 @@ (d/update-when interaction :destination #(get ids-map % %)))))] (into [] xform interactions)))) +(defn remove-interactions + "Remove all interactions that the fn returns true." + [f interactions] + (-> (d/removev f interactions) + not-empty)) + (defn actionable? "Check if there is any interaction that is clickable by the user" [interactions] diff --git a/frontend/src/app/main/data/workspace/interactions.cljs b/frontend/src/app/main/data/workspace/interactions.cljs index 49a199a66..c26d6ee96 100644 --- a/frontend/src/app/main/data/workspace/interactions.cljs +++ b/frontend/src/app/main/data/workspace/interactions.cljs @@ -17,6 +17,7 @@ [app.common.uuid :as uuid] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.state-helpers :as wsh] + [app.main.data.workspace.undo :as dwu] [app.main.streams :as ms] [beicon.core :as rx] [potok.core :as ptk])) @@ -149,6 +150,29 @@ (update shape :interactions ctsi/update-interaction index update-fn))))))) +(defn remove-all-interactions-nav-to + "Remove all interactions that navigate to the given frame." + [frame-id] + (ptk/reify ::remove-all-interactions-nav-to + ptk/WatchEvent + (watch [_ state _] + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state page-id) + + remove-interactions-shape + (fn [shape] + (let [interactions (:interactions shape) + new-interactions (ctsi/remove-interactions #(ctsi/navs-to? % frame-id) + interactions)] + (when (not= (count interactions) (count new-interactions)) + (dch/update-shapes [(:id shape)] + (fn [shape] + (assoc shape :interactions new-interactions))))))] + + (rx/from (->> (vals objects) + (map remove-interactions-shape) + (d/vec-without-nils))))))) + (declare move-edit-interaction) (declare finish-edit-interaction) @@ -171,8 +195,7 @@ (rx/map #(move-edit-interaction initial-pos %))) (rx/of (finish-edit-interaction index initial-pos)))))))) - -(defn get-target-frame +(defn- get-target-frame [state position] (let [objects (wsh/lookup-page-objects state) @@ -185,8 +208,7 @@ target-frame (ctst/frame-by-position objects position)] (when (and (not= (:id target-frame) uuid/zero) - (not= (:id target-frame) from-frame-id) - (not (:hide-in-viewer target-frame))) + (not= (:id target-frame) from-frame-id)) target-frame))) (defn move-edit-interaction @@ -225,25 +247,34 @@ :always (ctsi/set-destination (:id target-frame))))] - (cond - (or (nil? shape) + (rx/of + (dwu/start-undo-transaction) - ;; Didn't changed the position for the interaction - (= position initial-pos) + (when (:hide-in-viewer target-frame) + ; If the target frame is hidden, we need to unhide it so + ; users can navigate to it. + (dch/update-shapes [(:id target-frame)] + #(dissoc % :hide-in-viewer))) - ;; New interaction but invalid target - (and (nil? index) (nil? target-frame))) - nil + (cond + (or (nil? shape) + ;; Didn't changed the position for the interaction + (= position initial-pos) + ;; New interaction but invalid target + (and (nil? index) (nil? target-frame))) + nil - ;; Dropped interaction in an invalid target. We remove it - (and (some? index) (nil? target-frame)) - (rx/of (remove-interaction shape index)) + ;; Dropped interaction in an invalid target. We remove it + (and (some? index) (nil? target-frame)) + (remove-interaction shape index) - (nil? index) - (rx/of (add-new-interaction shape (:id target-frame))) + (nil? index) + (add-new-interaction shape (:id target-frame)) - :else - (rx/of (update-interaction shape index change-interaction))))))) + :else + (update-interaction shape index change-interaction)) + + (dwu/commit-undo-transaction)))))) ;; --- Overlays diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index a2c7d003e..8b9a549bd 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -17,7 +17,6 @@ [app.common.pages.helpers :as cph] [app.common.spec :as us] [app.common.types.shape-tree :as ctst] - [app.common.uuid :as uuid] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.collapse :as dwc] [app.main.data.workspace.comments :as-alias dwcm] @@ -758,23 +757,9 @@ (keep lookup) (remove #(= (:frame-id %) frame-id))) - moving-frames - (filter #(cph/frame-shape? (lookup %)) ids) - changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) - (pcb/update-shapes moving-frames - (fn [shape] - ;; Hide in viwer must be enabled just when a board is moved - ;; inside another artboard an nested to it, we have to avoid - ;; situations like: 1. Moving inside the same frame; 2. Moving - ;; outside the frame - (cond-> shape - (and (not= frame-id (:id shape)) - (not= frame-id (:frame-id shape)) - (not= frame-id uuid/zero)) - (assoc :hide-in-viewer true)))) (pcb/change-parent frame-id moving-shapes))] (when-not (empty? changes) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs index 3f84469fd..eac18cdb1 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs @@ -183,7 +183,7 @@ (let [objects (deref refs/workspace-page-objects) destination (get objects (:destination interaction)) - frames (mf/with-memo [objects] (ctt/get-viewer-frames objects {:all-frames? (not= :navigate (:action-type interaction))})) + frames (mf/with-memo [objects] (ctt/get-viewer-frames objects {:all-frames? true})) overlay-pos-type (:overlay-pos-type interaction) close-click-outside? (:close-click-outside interaction false) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 9a531fe2e..e69157fcd 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -12,6 +12,8 @@ [app.main.constants :refer [size-presets]] [app.main.data.workspace :as udw] [app.main.data.workspace.changes :as dch] + [app.main.data.workspace.interactions :as dwi] + [app.main.data.workspace.undo :as dwu] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] @@ -236,7 +238,16 @@ (mf/deps ids) (fn [event] (let [value (-> event dom/get-target dom/checked?)] - (st/emit! (dch/update-shapes ids (fn [shape] (assoc shape :hide-in-viewer (not value)))))))) + (do + (st/emit! (dwu/start-undo-transaction) + (dch/update-shapes ids (fn [shape] (assoc shape :hide-in-viewer (not value))))) + + (when-not value + ;; when a frame is no longer shown in view mode, cannot have + ;; interactions that navigate to it. + (apply st/emit! (map #(dwi/remove-all-interactions-nav-to %) ids))) + + (st/emit! (dwu/commit-undo-transaction)))))) select-all #(-> % (dom/get-target) (.select))]