From 157e8413fbb4e5a652ca0fbcb1fc6cdc728b3818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Wed, 15 Sep 2021 12:49:04 +0200 Subject: [PATCH 1/5] :tada: Allow to position interaction overlays --- common/src/app/common/pages/helpers.cljc | 6 + common/src/app/common/pages/spec.cljc | 22 +-- common/src/app/common/types/interactions.cljc | 187 ++++++++++++++++++ frontend/src/app/main/data/viewer.cljs | 9 +- frontend/src/app/main/data/workspace.cljs | 90 ++++++++- frontend/src/app/main/ui/viewer.cljs | 15 +- frontend/src/app/main/ui/viewer/shapes.cljs | 5 +- .../sidebar/options/menus/interactions.cljs | 19 +- .../ui/workspace/viewport/interactions.cljs | 86 ++++++-- 9 files changed, 372 insertions(+), 67 deletions(-) create mode 100644 common/src/app/common/types/interactions.cljc diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index 6b30e3a9e..0d729275b 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -161,6 +161,12 @@ (when parent-id (lazy-seq (cons parent-id (get-parents parent-id objects)))))) +(defn get-frame + "Get the frame that contains the shape. If the shape is already a frame, get itself." + [shape objects] + (if (= (:type shape) :frame) + shape + (get objects (:frame-id shape)))) (defn clean-loops "Clean a list of ids from circular references." diff --git a/common/src/app/common/pages/spec.cljc b/common/src/app/common/pages/spec.cljc index da4f79ae3..81f5b3335 100644 --- a/common/src/app/common/pages/spec.cljc +++ b/common/src/app/common/pages/spec.cljc @@ -9,6 +9,7 @@ [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.spec :as us] + [app.common.types.interactions :as cti] [app.common.uuid :as uuid] [clojure.set :as set] [clojure.spec.alpha :as s])) @@ -183,25 +184,6 @@ (s/def :internal.page/options (s/keys :opt-un [:internal.page.options/background])) -;; Interactions - -(s/def :internal.shape.interaction/event-type #{:click :hover}) -(s/def :internal.shape.interaction/action-type #{:navigate :open-overlay :close-overlay}) -(s/def :internal.shape.interaction/destination (s/nilable ::uuid)) - -(s/def :internal.shape/interaction - (s/keys :req-un [:internal.shape.interaction/event-type - :internal.shape.interaction/action-type - :internal.shape.interaction/destination])) - -(s/def :internal.shape/interactions - (s/coll-of :internal.shape/interaction :kind vector?)) - -(def default-interaction - {:event-type :click - :action-type :navigate - :destination nil}) - ;; Size constraints (s/def :internal.shape/constraints-h #{:left :right :leftright :center :scale}) @@ -366,7 +348,7 @@ :internal.shape/transform-inverse :internal.shape/width :internal.shape/height - :internal.shape/interactions + ::cti/interactions :internal.shape/masked-group? :internal.shape/shadow :internal.shape/blur])) diff --git a/common/src/app/common/types/interactions.cljc b/common/src/app/common/types/interactions.cljc new file mode 100644 index 000000000..64ae31899 --- /dev/null +++ b/common/src/app/common/types/interactions.cljc @@ -0,0 +1,187 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.common.types.interactions + (:require + [app.common.geom.point :as gpt] + [app.common.spec :as us] + [clojure.spec.alpha :as s])) + +;; TODO: Move this to other place to avoid duplication with common.pages.spec +(s/def ::string string?) +(s/def ::safe-integer ::us/safe-integer) +(s/def ::uuid uuid?) + +(s/def ::point + (s/and (s/keys :req-un [::x ::y]) + gpt/point?)) + +;; -- Options depending on event type + +(s/def ::event-type #{:click + :mouse-over + :mouse-press + :mouse-enter + :mouse-leave + :after-delay}) + +(s/def ::delay ::safe-integer) + +(defmulti event-opts-spec :event-type) + +(defmethod event-opts-spec :after-delay [_] + (s/keys :req-un [::delay])) + +(defmethod event-opts-spec :default [_] + (s/keys :req-un [])) + +(s/def ::event-opts + (s/multi-spec event-opts-spec ::event-type)) + +;; -- Options depending on action type + +(s/def ::action-type #{:navigate + :open-overlay + :close-overlay + :prev-screen + :open-url}) + +(s/def ::destination (s/nilable ::uuid)) +(s/def ::overlay-position ::point) +(s/def ::url ::string) + +(defmulti action-opts-spec :action-type) + +(defmethod action-opts-spec :navigate [_] + (s/keys :req-un [::destination])) + +(defmethod action-opts-spec :open-overlay [_] + (s/keys :req-un [::destination + ::overlay-position])) + +(defmethod action-opts-spec :close-overlay [_] + (s/keys :req-un [::destination])) + +(defmethod action-opts-spec :prev-screen [_] + (s/keys :req-un [])) + +(defmethod action-opts-spec :open-url [_] + (s/keys :req-un [::url])) + +(s/def ::action-opts + (s/multi-spec action-opts-spec ::action-type)) + +;; -- Interaction + +(s/def ::classifier + (s/keys :req-un [::event-type + ::action-type])) + +(s/def ::interaction + (s/merge ::classifier + ::event-opts + ::action-opts)) + +(s/def ::interactions + (s/coll-of ::interaction :kind vector?)) + +(def default-interaction + {:event-type :click + :action-type :navigate + :destination nil}) + +(def default-delay 100) + +;; -- Helpers + +(defn set-event-type + [interaction event-type] + (us/verify ::interaction interaction) + (us/verify ::event-type event-type) + (if (= (:event-type interaction) event-type) + interaction + (case event-type + + :after-delay + (assoc interaction + :event-type event-type + :delay (get interaction :delay default-delay)) + + (assoc interaction + :event-type event-type)))) + + +(defn set-action-type + [interaction action-type] + (us/verify ::interaction interaction) + (us/verify ::action-type action-type) + (if (= (:action-type interaction) action-type) + interaction + (case action-type + + :navigate + (assoc interaction + :action-type action-type + :destination (get interaction :destination)) + + :open-overlay + (assoc interaction + :action-type action-type + :destination (get interaction :destination) + :overlay-position (get interaction :overlay-position (gpt/point 0 0))) + + :close-overlay + (assoc interaction + :action-type action-type + :destination (get interaction :destination)) + + :prev-screen + (assoc interaction + :action-type action-type) + + :open-url + (assoc interaction + :action-type action-type + :url (get interaction :url ""))))) + + +(defn set-destination + [interaction destination shape objects] + (us/verify ::interaction interaction) + (us/verify ::destination destination) + (assert (or (nil? destination) + (some? (get objects destination)))) + (assert #(:navigate :open-overlay :close-overlay) (:action-type interaction)) + (let [calc-overlay-position + (fn [] + (if (nil? destination) + (gpt/point 0 0) + (let [dest-frame (get objects destination) + overlay-size (:selrect dest-frame) + + orig-frame (if (= (:type shape) :frame) + shape + (get objects (:frame-id shape))) + frame-size (:selrect orig-frame) + + x (/ (- (:width frame-size) (:width overlay-size)) 2) + y (/ (- (:height frame-size) (:height overlay-size)) 2)] + (gpt/point x y))))] + + (cond-> interaction + :always + (assoc :destination destination) + + (= (:action-type interaction) :open-overlay) + (assoc :overlay-position (calc-overlay-position))))) + + +(defn set-overlay-position + [interaction overlay-position] + (us/verify ::interaction interaction) + (us/verify ::overlay-position overlay-position) + (assoc interaction :overlay-position overlay-position)) + diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index e438a54f5..9b4ba9af4 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -328,8 +328,9 @@ ;; --- Overlays (defn open-overlay - [frame-id] + [frame-id position] (us/verify ::us/uuid frame-id) + (us/verify ::us/point position) (ptk/reify ::open-overlay ptk/UpdateEvent (update [_ state] @@ -340,7 +341,9 @@ frame (d/seek #(= (:id %) frame-id) frames) overlays (get-in state [:viewer-local :overlays])] (if-not (some #(= % frame) overlays) - (update-in state [:viewer-local :overlays] conj frame) + (update-in state [:viewer-local :overlays] conj + {:frame frame + :position position}) state))))) (defn close-overlay @@ -350,7 +353,7 @@ (update [_ state] (update-in state [:viewer-local :overlays] (fn [overlays] - (remove #(= (:id %) frame-id) overlays)))))) + (remove #(= (:id (:frame %)) frame-id) overlays)))))) ;; --- Objects selection diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 362a461f3..dbff3982c 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -18,6 +18,7 @@ [app.common.pages.spec :as spec] [app.common.spec :as us] [app.common.transit :as t] + [app.common.types.interactions :as cti] [app.common.uuid :as uuid] [app.config :as cfg] [app.main.data.events :as ev] @@ -1826,10 +1827,93 @@ frame)] ;; Update or create interaction (if index - (assoc-in interactions [index :destination] (:id frame)) + (update-in interactions [index] + #(cti/set-destination % (:id frame) shape objects)) (conj (or interactions []) - (assoc spec/default-interaction - :destination (:id frame)))))))))))))))) + (cti/set-destination cti/default-interaction + (:id frame) + shape + objects))))))))))))))) + +(declare move-overlay-pos) +(declare finish-move-overlay-pos) + +(defn start-move-overlay-pos + [index] + (ptk/reify ::start-move-overlay-pos + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:workspace-local :move-overlay-to] nil) + (assoc-in [:workspace-local :move-overlay-index] index))) + + ptk/WatchEvent + (watch [_ state stream] + (let [initial-pos @ms/mouse-position + selected (wsh/lookup-selected state) + stopper (rx/filter ms/mouse-up? stream)] + (when (= 1 (count selected)) + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state page-id) + shape (->> state + wsh/lookup-selected + first + (get objects)) + overlay-pos (-> shape + (get-in [:interactions index]) + :overlay-position) + orig-frame (cph/get-frame shape objects) + frame-pos (gpt/point (:x orig-frame) (:y orig-frame)) + offset (-> initial-pos + (gpt/subtract overlay-pos) + (gpt/subtract frame-pos))] + (rx/concat + (->> ms/mouse-position + (rx/take-until stopper) + (rx/map #(move-overlay-pos % overlay-pos frame-pos offset))) + (rx/of (finish-move-overlay-pos index overlay-pos frame-pos offset))))))))) + +(defn move-overlay-pos + [pos overlay-pos frame-pos offset] + (ptk/reify ::move-overlay-pos + ptk/UpdateEvent + (update [_ state] + (let [pos (-> pos + (gpt/subtract frame-pos) + (gpt/subtract offset))] + (assoc-in state [:workspace-local :move-overlay-to] pos))))) + +(defn finish-move-overlay-pos + [index overlay-pos frame-pos offset] + (ptk/reify ::finish-move-overlay-pos + ptk/UpdateEvent + (update [_ state] + (-> state + (d/dissoc-in [:workspace-local :move-overlay-to]) + (d/dissoc-in [:workspace-local :move-overlay-index]))) + + ptk/WatchEvent + (watch [_ state _] + (let [pos @ms/mouse-position + overlay-pos (-> pos + (gpt/subtract frame-pos) + (gpt/subtract offset)) + + page-id (:current-page-id state) + objects (wsh/lookup-page-objects state page-id) + shape (->> state + wsh/lookup-selected + first + (get objects)) + + interactions (:interactions shape) + + new-interactions + (update interactions index + #(cti/set-overlay-position % overlay-pos))] + + (rx/of (update-shape (:id shape) {:interactions new-interactions})))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CANVAS OPTIONS diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index c98628bb4..b55016671 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -33,11 +33,6 @@ :height (* height zoom) :vbox (str "0 0 " width " " height)})) -(defn- position-overlay - [size size-over] - {:x (/ (- (:width size) (:width size-over)) 2) - :y (/ (- (:height size) (:height size-over)) 2)}) - (mf/defc viewer [{:keys [params data]}] @@ -125,7 +120,6 @@ :section section :local local}] - [:div.viewport-container {:style {:width (:width size) :height (:height size) @@ -147,16 +141,15 @@ :local local}] (for [overlay (:overlays local)] - (let [size-over (calculate-size overlay zoom) - pos-over (position-overlay size size-over)] + (let [size-over (calculate-size (:frame overlay) zoom)] [:div.viewport-container {:style {:width (:width size-over) :height (:height size-over) :position "absolute" - :left (:x pos-over) - :top (:y pos-over)}} + :left (:x (:position overlay)) + :top (:y (:position overlay))}} [:& interactions/viewport - {:frame overlay + {:frame (:frame overlay) :size size-over :page page :file file diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index d4850808c..f743f4c92 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -39,9 +39,10 @@ (st/emit! (dv/go-to-frame frame-id))) :open-overlay - (let [frame-id (:destination interaction)] + (let [frame-id (:destination interaction) + position (:overlay-position interaction)] (dom/stop-propagation event) - (st/emit! (dv/open-overlay frame-id))) + (st/emit! (dv/open-overlay frame-id position))) :close-overlay (let [frame-id (or (:destination interaction) 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 0f9b20400..c8dc7c2e7 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 @@ -8,7 +8,7 @@ (:require [app.common.data :as d] [app.common.pages :as cp] - [app.common.pages.spec :as spec] + [app.common.types.interactions :as cti] [app.common.uuid :as uuid] [app.main.data.workspace :as dw] [app.main.refs :as refs] @@ -21,7 +21,7 @@ (defn- event-type-names [] {:click (tr "workspace.options.interaction-on-click") - :hover (tr "workspace.options.interaction-while-hovering")}) + :mouse-over (tr "workspace.options.interaction-while-hovering")}) (defn- event-type-name [interaction] @@ -56,18 +56,18 @@ change-event-type (fn [event] (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(assoc % :event-type value)))) + (update-interaction index #(cti/set-event-type % value)))) change-action-type (fn [event] (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(assoc % :action-type value)))) + (update-interaction index #(cti/set-action-type % value)))) change-destination (fn [event] (let [value (-> event dom/get-target dom/get-value) value (when (not= value "") (uuid/uuid value))] - (update-interaction index #(assoc % :destination value))))] + (update-interaction index #(cti/set-destination % value shape objects))))] [:* [:div.element-set-options-group @@ -84,21 +84,21 @@ [:div.interactions-element [:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")] [:select.input-select - {:default-value (str (:event-type interaction)) + {:value (str (:event-type interaction)) :on-change change-event-type} (for [[value name] (event-type-names)] [:option {:value (str value)} name])]] [:div.interactions-element [:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")] [:select.input-select - {:default-value (str (:action-type interaction)) + {:value (str (:action-type interaction)) :on-change change-action-type} (for [[value name] (action-type-names)] [:option {:value (str value)} name])]] [:div.interactions-element [:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")] [:select.input-select - {:default-value (str (:destination interaction)) + {:value (str (:destination interaction)) :on-change change-destination} [:option {:value ""} (tr "workspace.options.interaction-none")] (for [frame frames] @@ -112,8 +112,7 @@ add-interaction (fn [_] - (let [new-interactions - (conj interactions (update spec/default-interaction :event-type identity))] + (let [new-interactions (conj interactions cti/default-interaction)] (st/emit! (dw/update-shape (:id shape) {:interactions new-interactions})))) remove-interaction diff --git a/frontend/src/app/main/ui/workspace/viewport/interactions.cljs b/frontend/src/app/main/ui/workspace/viewport/interactions.cljs index aaa267bd5..404431856 100644 --- a/frontend/src/app/main/ui/workspace/viewport/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/interactions.cljs @@ -8,6 +8,7 @@ "Visually show shape interactions in workspace" (:require [app.common.data :as d] + [app.common.pages.helpers :as cph] [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] @@ -86,11 +87,8 @@ :right "M -5 0 l 8 0 l -4 -4 m 4 4 l -4 4" :left "M 5 0 l -8 0 l 4 -4 m -4 4 l 4 4" nil) - :open-overlay (case arrow-dir - ;; TODO: have a different icon for open overlay? - :right "M -5 0 l 8 0 l -4 -4 m 4 4 l -4 4" - :left "M 5 0 l -8 0 l 4 -4 m -4 4 l 4 4" - nil) + + :open-overlay "M-4 -4 h6 v6 h-6 z M2 -2 h2.5 v6.5 h-6.5 v-2.5" :close-overlay "M -4 -4 L 4 4 M -4 4 L 4 -4" @@ -162,6 +160,11 @@ :pointer-events "visible" :stroke-width (/ 2 zoom) :d pdata}] + + (when dest-shape + [:& outline {:shape dest-shape + :color "#31EFB8"}]) + [:& interaction-marker {:index index :x orig-x :y orig-y @@ -173,11 +176,7 @@ :stroke "#31EFB8" :action-type action-type :arrow-dir arrow-dir - :zoom zoom}] - - (when dest-shape - [:& outline {:shape dest-shape - :color "#31EFB8"}])]))) + :zoom zoom}]]))) (mf/defc interaction-handle @@ -194,6 +193,37 @@ :zoom zoom}]])) +(mf/defc overlay-marker + [{:keys [index orig-shape dest-shape position objects zoom] :as props}] + (let [start-move-position + (fn [event] + (st/emit! (dw/start-move-overlay-pos index)))] + + (when dest-shape + (let [orig-frame (cph/get-frame orig-shape objects) + marker-x (+ (:x orig-frame) (:x position)) + marker-y (+ (:y orig-frame) (:y position)) + width (:width dest-shape) + height (:height dest-shape)] + [:g {:on-mouse-down start-move-position} + [:path {:stroke "#31EFB8" + :fill "#000000" + :fill-opacity 0.3 + :stroke-width 1 + :d (str "M" marker-x " " marker-y " " + "h " width " " + "v " height " " + "h -" width " z" + "M" marker-x " " marker-y " " + "l " width " " height " " + "M" marker-x " " (+ marker-y height) " " + "l " width " -" height " ")}] + [:circle {:cx (+ marker-x (/ width 2)) + :cy (+ marker-y (/ height 2)) + :r 8 + :fill "#31EFB8"}] + ])))) + (mf/defc interactions [{:keys [selected] :as props}] (let [local (mf/deref refs/workspace-local) @@ -205,6 +235,8 @@ editing-interaction-index (:editing-interaction-index local) draw-interaction-to (:draw-interaction-to local) draw-interaction-to-frame (:draw-interaction-to-frame local) + move-overlay-to (:move-overlay-to local) + move-overlay-index (:move-overlay-index local) first-selected (first selected-shapes)] [:g.interactions @@ -238,14 +270,32 @@ (for [[index interaction] (d/enumerate (:interactions shape))] (when-not (= index editing-interaction-index) (let [dest-shape (get objects (:destination interaction))] - [:& interaction-path {:key (str (:id shape) "-" index) - :index index - :orig-shape shape - :dest-shape dest-shape - :selected selected - :selected? true - :action-type (:action-type interaction) - :zoom zoom}]))) + [:* + [:& interaction-path {:key (str (:id shape) "-" index) + :index index + :orig-shape shape + :dest-shape dest-shape + :selected selected + :selected? true + :action-type (:action-type interaction) + :zoom zoom}] + (when (= (:action-type interaction) :open-overlay) + (if (and (some? move-overlay-to) + (= move-overlay-index index)) + [:& overlay-marker {:key (str "pos" (:id shape) "-" index) + :index index + :orig-shape shape + :dest-shape dest-shape + :position move-overlay-to + :objects objects + :zoom zoom}] + [:& overlay-marker {:key (str "pos" (:id shape) "-" index) + :index index + :orig-shape shape + :dest-shape dest-shape + :position (:overlay-position interaction) + :objects objects + :zoom zoom}]))]))) (when (not (#{:move :rotate} current-transform)) [:& interaction-handle {:key (:id shape) :index nil From 0516cfa29629b10b3ffd65803b070bcf9a402477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Fri, 17 Sep 2021 16:04:26 +0200 Subject: [PATCH 2/5] :recycle: Small refactor of basic specs --- common/src/app/common/pages/spec.cljc | 91 +++++++++---------- common/src/app/common/types/interactions.cljc | 11 +-- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/common/src/app/common/pages/spec.cljc b/common/src/app/common/pages/spec.cljc index 81f5b3335..a1db691f2 100644 --- a/common/src/app/common/pages/spec.cljc +++ b/common/src/app/common/pages/spec.cljc @@ -31,9 +31,6 @@ (s/def ::component-root? boolean?) (s/def ::shape-ref uuid?) -(s/def ::safe-integer ::us/safe-integer) -(s/def ::safe-number ::us/safe-number) - (s/def :internal.matrix/a ::us/safe-number) (s/def :internal.matrix/b ::us/safe-number) (s/def :internal.matrix/c ::us/safe-number) @@ -62,15 +59,15 @@ ;; GRADIENTS (s/def :internal.gradient.stop/color ::string) -(s/def :internal.gradient.stop/opacity ::safe-number) -(s/def :internal.gradient.stop/offset ::safe-number) +(s/def :internal.gradient.stop/opacity ::us/safe-number) +(s/def :internal.gradient.stop/offset ::us/safe-number) (s/def :internal.gradient/type #{:linear :radial}) -(s/def :internal.gradient/start-x ::safe-number) -(s/def :internal.gradient/start-y ::safe-number) -(s/def :internal.gradient/end-x ::safe-number) -(s/def :internal.gradient/end-y ::safe-number) -(s/def :internal.gradient/width ::safe-number) +(s/def :internal.gradient/start-x ::us/safe-number) +(s/def :internal.gradient/start-y ::us/safe-number) +(s/def :internal.gradient/end-x ::us/safe-number) +(s/def :internal.gradient/end-y ::us/safe-number) +(s/def :internal.gradient/width ::us/safe-number) (s/def :internal.gradient/stop (s/keys :req-un [:internal.gradient.stop/color @@ -96,7 +93,7 @@ (s/def :internal.color/path (s/nilable ::string)) (s/def :internal.color/value (s/nilable ::string)) (s/def :internal.color/color (s/nilable ::string)) -(s/def :internal.color/opacity (s/nilable ::safe-number)) +(s/def :internal.color/opacity (s/nilable ::us/safe-number)) (s/def :internal.color/gradient (s/nilable ::gradient)) (s/def ::color @@ -114,10 +111,10 @@ (s/def :internal.shadow/id uuid?) (s/def :internal.shadow/style #{:drop-shadow :inner-shadow}) (s/def :internal.shadow/color ::color) -(s/def :internal.shadow/offset-x ::safe-number) -(s/def :internal.shadow/offset-y ::safe-number) -(s/def :internal.shadow/blur ::safe-number) -(s/def :internal.shadow/spread ::safe-number) +(s/def :internal.shadow/offset-x ::us/safe-number) +(s/def :internal.shadow/offset-y ::us/safe-number) +(s/def :internal.shadow/blur ::us/safe-number) +(s/def :internal.shadow/spread ::us/safe-number) (s/def :internal.shadow/hidden boolean?) (s/def :internal.shadow/shadow @@ -138,7 +135,7 @@ (s/def :internal.blur/id uuid?) (s/def :internal.blur/type #{:layer-blur}) -(s/def :internal.blur/value ::safe-number) +(s/def :internal.blur/value ::us/safe-number) (s/def :internal.blur/hidden boolean?) (s/def ::blur @@ -149,17 +146,17 @@ ;; Page Options (s/def :internal.page.grid.color/value string?) -(s/def :internal.page.grid.color/opacity ::safe-number) +(s/def :internal.page.grid.color/opacity ::us/safe-number) -(s/def :internal.page.grid/size ::safe-integer) +(s/def :internal.page.grid/size ::us/safe-integer) (s/def :internal.page.grid/color (s/keys :req-un [:internal.page.grid.color/value :internal.page.grid.color/opacity])) (s/def :internal.page.grid/type #{:stretch :left :center :right}) -(s/def :internal.page.grid/item-length (s/nilable ::safe-integer)) -(s/def :internal.page.grid/gutter (s/nilable ::safe-integer)) -(s/def :internal.page.grid/margin (s/nilable ::safe-integer)) +(s/def :internal.page.grid/item-length (s/nilable ::us/safe-integer)) +(s/def :internal.page.grid/gutter (s/nilable ::us/safe-integer)) +(s/def :internal.page.grid/margin (s/nilable ::us/safe-integer)) (s/def :internal.page.grid/square (s/keys :req-un [:internal.page.grid/size @@ -214,33 +211,33 @@ (s/def :internal.shape/content any?) (s/def :internal.shape/fill-color string?) -(s/def :internal.shape/fill-opacity ::safe-number) +(s/def :internal.shape/fill-opacity ::us/safe-number) (s/def :internal.shape/fill-color-gradient (s/nilable ::gradient)) (s/def :internal.shape/fill-color-ref-file (s/nilable uuid?)) (s/def :internal.shape/fill-color-ref-id (s/nilable uuid?)) (s/def :internal.shape/font-family string?) -(s/def :internal.shape/font-size ::safe-integer) +(s/def :internal.shape/font-size ::us/safe-integer) (s/def :internal.shape/font-style string?) (s/def :internal.shape/font-weight string?) (s/def :internal.shape/hidden boolean?) -(s/def :internal.shape/letter-spacing ::safe-number) -(s/def :internal.shape/line-height ::safe-number) +(s/def :internal.shape/letter-spacing ::us/safe-number) +(s/def :internal.shape/line-height ::us/safe-number) (s/def :internal.shape/locked boolean?) (s/def :internal.shape/page-id uuid?) -(s/def :internal.shape/proportion ::safe-number) +(s/def :internal.shape/proportion ::us/safe-number) (s/def :internal.shape/proportion-lock boolean?) -(s/def :internal.shape/rx ::safe-number) -(s/def :internal.shape/ry ::safe-number) -(s/def :internal.shape/r1 ::safe-number) -(s/def :internal.shape/r2 ::safe-number) -(s/def :internal.shape/r3 ::safe-number) -(s/def :internal.shape/r4 ::safe-number) +(s/def :internal.shape/rx ::us/safe-number) +(s/def :internal.shape/ry ::us/safe-number) +(s/def :internal.shape/r1 ::us/safe-number) +(s/def :internal.shape/r2 ::us/safe-number) +(s/def :internal.shape/r3 ::us/safe-number) +(s/def :internal.shape/r4 ::us/safe-number) (s/def :internal.shape/stroke-color string?) (s/def :internal.shape/stroke-color-gradient (s/nilable ::gradient)) (s/def :internal.shape/stroke-color-ref-file (s/nilable uuid?)) (s/def :internal.shape/stroke-color-ref-id (s/nilable uuid?)) -(s/def :internal.shape/stroke-opacity ::safe-number) +(s/def :internal.shape/stroke-opacity ::us/safe-number) (s/def :internal.shape/stroke-style #{:solid :dotted :dashed :mixed :none :svg}) (def stroke-caps-line #{:round :square}) @@ -253,26 +250,26 @@ [shape] (= (:type shape) :path)) -(s/def :internal.shape/stroke-width ::safe-number) +(s/def :internal.shape/stroke-width ::us/safe-number) (s/def :internal.shape/stroke-alignment #{:center :inner :outer}) (s/def :internal.shape/text-align #{"left" "right" "center" "justify"}) -(s/def :internal.shape/x ::safe-number) -(s/def :internal.shape/y ::safe-number) -(s/def :internal.shape/cx ::safe-number) -(s/def :internal.shape/cy ::safe-number) -(s/def :internal.shape/width ::safe-number) -(s/def :internal.shape/height ::safe-number) +(s/def :internal.shape/x ::us/safe-number) +(s/def :internal.shape/y ::us/safe-number) +(s/def :internal.shape/cx ::us/safe-number) +(s/def :internal.shape/cy ::us/safe-number) +(s/def :internal.shape/width ::us/safe-number) +(s/def :internal.shape/height ::us/safe-number) (s/def :internal.shape/index integer?) (s/def :internal.shape/shadow ::shadow) (s/def :internal.shape/blur ::blur) -(s/def :internal.shape/x1 ::safe-number) -(s/def :internal.shape/y1 ::safe-number) -(s/def :internal.shape/x2 ::safe-number) -(s/def :internal.shape/y2 ::safe-number) +(s/def :internal.shape/x1 ::us/safe-number) +(s/def :internal.shape/y1 ::us/safe-number) +(s/def :internal.shape/x2 ::us/safe-number) +(s/def :internal.shape/y2 ::us/safe-number) (s/def :internal.shape.export/suffix string?) -(s/def :internal.shape.export/scale ::safe-number) +(s/def :internal.shape.export/scale ::us/safe-number) (s/def :internal.shape/export (s/keys :req-un [::type :internal.shape.export/suffix @@ -384,8 +381,8 @@ :internal.color/gradient])) (s/def :internal.media-object/name ::string) -(s/def :internal.media-object/width ::safe-integer) -(s/def :internal.media-object/height ::safe-integer) +(s/def :internal.media-object/width ::us/safe-integer) +(s/def :internal.media-object/height ::us/safe-integer) (s/def :internal.media-object/mtype ::string) (s/def ::media-object diff --git a/common/src/app/common/types/interactions.cljc b/common/src/app/common/types/interactions.cljc index 64ae31899..287b4b30d 100644 --- a/common/src/app/common/types/interactions.cljc +++ b/common/src/app/common/types/interactions.cljc @@ -10,11 +10,6 @@ [app.common.spec :as us] [clojure.spec.alpha :as s])) -;; TODO: Move this to other place to avoid duplication with common.pages.spec -(s/def ::string string?) -(s/def ::safe-integer ::us/safe-integer) -(s/def ::uuid uuid?) - (s/def ::point (s/and (s/keys :req-un [::x ::y]) gpt/point?)) @@ -28,7 +23,7 @@ :mouse-leave :after-delay}) -(s/def ::delay ::safe-integer) +(s/def ::delay ::us/safe-integer) (defmulti event-opts-spec :event-type) @@ -49,9 +44,9 @@ :prev-screen :open-url}) -(s/def ::destination (s/nilable ::uuid)) +(s/def ::destination (s/nilable ::us/uuid)) (s/def ::overlay-position ::point) -(s/def ::url ::string) +(s/def ::url ::us/string) (defmulti action-opts-spec :action-type) From f208731746956cfb921520e0dfa3088bc74be4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Fri, 17 Sep 2021 19:48:18 +0200 Subject: [PATCH 3/5] :tada: Overlay positions buttons --- common/src/app/common/types/interactions.cljc | 139 ++++++++++++++---- .../images/icons/position-bottom-center.svg | 6 + .../images/icons/position-bottom-left.svg | 6 + .../images/icons/position-bottom-right.svg | 6 + .../images/icons/position-center.svg | 6 + .../images/icons/position-top-center.svg | 6 + .../images/icons/position-top-left.svg | 7 + .../images/icons/position-top-right.svg | 6 + .../partials/sidebar-element-options.scss | 6 +- .../main/partials/sidebar-interactions.scss | 10 ++ frontend/src/app/main/data/workspace.cljs | 12 +- frontend/src/app/main/ui/icons.cljs | 7 + frontend/src/app/main/ui/viewer.cljs | 4 +- .../sidebar/options/menus/interactions.cljs | 73 ++++++++- .../ui/workspace/viewport/interactions.cljs | 10 +- frontend/translations/en.po | 36 +++++ frontend/translations/es.po | 36 +++++ 17 files changed, 328 insertions(+), 48 deletions(-) create mode 100644 frontend/resources/images/icons/position-bottom-center.svg create mode 100644 frontend/resources/images/icons/position-bottom-left.svg create mode 100644 frontend/resources/images/icons/position-bottom-right.svg create mode 100644 frontend/resources/images/icons/position-center.svg create mode 100644 frontend/resources/images/icons/position-top-center.svg create mode 100644 frontend/resources/images/icons/position-top-left.svg create mode 100644 frontend/resources/images/icons/position-top-right.svg diff --git a/common/src/app/common/types/interactions.cljc b/common/src/app/common/types/interactions.cljc index 287b4b30d..aa28a6425 100644 --- a/common/src/app/common/types/interactions.cljc +++ b/common/src/app/common/types/interactions.cljc @@ -45,6 +45,14 @@ :open-url}) (s/def ::destination (s/nilable ::us/uuid)) +(s/def ::overlay-pos-type #{:manual + :center + :top-left + :top-right + :top-center + :bottom-left + :bottom-right + :bottom-center}) (s/def ::overlay-position ::point) (s/def ::url ::us/string) @@ -55,7 +63,8 @@ (defmethod action-opts-spec :open-overlay [_] (s/keys :req-un [::destination - ::overlay-position])) + ::overlay-position + ::overlay-pos-type])) (defmethod action-opts-spec :close-overlay [_] (s/keys :req-un [::destination])) @@ -92,6 +101,8 @@ ;; -- Helpers +(declare calc-overlay-position) + (defn set-event-type [interaction event-type] (us/verify ::interaction interaction) @@ -110,7 +121,7 @@ (defn set-action-type - [interaction action-type] + [interaction action-type shape objects] (us/verify ::interaction interaction) (us/verify ::action-type action-type) (if (= (:action-type interaction) action-type) @@ -123,10 +134,21 @@ :destination (get interaction :destination)) :open-overlay - (assoc interaction - :action-type action-type - :destination (get interaction :destination) - :overlay-position (get interaction :overlay-position (gpt/point 0 0))) + (let [destination (get interaction :destination) + overlay-pos-type (get interaction :overlay-pos-type :center) + overlay-position (get interaction + :overlay-position + (calc-overlay-position + destination + interaction + shape + objects + overlay-pos-type))] + (assoc interaction + :action-type action-type + :destination destination + :overlay-pos-type overlay-pos-type + :overlay-position overlay-position)) :close-overlay (assoc interaction @@ -142,7 +164,6 @@ :action-type action-type :url (get interaction :url ""))))) - (defn set-destination [interaction destination shape objects] (us/verify ::interaction interaction) @@ -150,33 +171,95 @@ (assert (or (nil? destination) (some? (get objects destination)))) (assert #(:navigate :open-overlay :close-overlay) (:action-type interaction)) - (let [calc-overlay-position - (fn [] - (if (nil? destination) - (gpt/point 0 0) - (let [dest-frame (get objects destination) - overlay-size (:selrect dest-frame) + (cond-> interaction + :always + (assoc :destination destination) - orig-frame (if (= (:type shape) :frame) - shape - (get objects (:frame-id shape))) - frame-size (:selrect orig-frame) + (= (:action-type interaction) :open-overlay) + (assoc :overlay-pos-type :center + :overlay-position (calc-overlay-position destination + interaction + shape + objects + :center)))) - x (/ (- (:width frame-size) (:width overlay-size)) 2) - y (/ (- (:height frame-size) (:height overlay-size)) 2)] - (gpt/point x y))))] - - (cond-> interaction - :always - (assoc :destination destination) - - (= (:action-type interaction) :open-overlay) - (assoc :overlay-position (calc-overlay-position))))) +(defn set-overlay-pos-type + [interaction overlay-pos-type shape objects] + (us/verify ::interaction interaction) + (us/verify ::overlay-pos-type overlay-pos-type) + (assert #(= :open-overlay (:action-type interaction))) + (assoc interaction + :overlay-pos-type overlay-pos-type + :overlay-position (calc-overlay-position (:destination interaction) + interaction + shape + objects + overlay-pos-type))) +(defn toggle-overlay-pos-type + [interaction overlay-pos-type shape objects] + (us/verify ::interaction interaction) + (us/verify ::overlay-pos-type overlay-pos-type) + (assert #(= :open-overlay (:action-type interaction))) + (let [new-pos-type (if (= (:overlay-pos-type interaction) overlay-pos-type) + :manual + overlay-pos-type)] + (assoc interaction + :overlay-pos-type new-pos-type + :overlay-position (calc-overlay-position (:destination interaction) + interaction + shape + objects + new-pos-type)))) (defn set-overlay-position [interaction overlay-position] (us/verify ::interaction interaction) (us/verify ::overlay-position overlay-position) - (assoc interaction :overlay-position overlay-position)) + (assert #(= :open-overlay (:action-type interaction))) + (assoc interaction + :overlay-pos-type :manual + :overlay-position overlay-position)) + +(defn- calc-overlay-position + [destination interaction shape objects overlay-pos-type] + (if (nil? destination) + (gpt/point 0 0) + (let [dest-frame (get objects destination) + overlay-size (:selrect dest-frame) + orig-frame (if (= (:type shape) :frame) + shape + (get objects (:frame-id shape))) + frame-size (:selrect orig-frame)] + (case overlay-pos-type + + :center + (gpt/point (/ (- (:width frame-size) (:width overlay-size)) 2) + (/ (- (:height frame-size) (:height overlay-size)) 2)) + + :top-left + (gpt/point 0 0) + + :top-right + (gpt/point (- (:width frame-size) (:width overlay-size)) + 0) + + :top-center + (gpt/point (/ (- (:width frame-size) (:width overlay-size)) 2) + 0) + + :bottom-left + (gpt/point 0 + (- (:height frame-size) (:height overlay-size))) + + :bottom-right + (gpt/point (- (:width frame-size) (:width overlay-size)) + (- (:height frame-size) (:height overlay-size))) + + :bottom-center + (gpt/point (/ (- (:width frame-size) (:width overlay-size)) 2) + (- (:height frame-size) (:height overlay-size))) + + :manual + (:overlay-position interaction))))) diff --git a/frontend/resources/images/icons/position-bottom-center.svg b/frontend/resources/images/icons/position-bottom-center.svg new file mode 100644 index 000000000..002466ead --- /dev/null +++ b/frontend/resources/images/icons/position-bottom-center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/resources/images/icons/position-bottom-left.svg b/frontend/resources/images/icons/position-bottom-left.svg new file mode 100644 index 000000000..4811b74a9 --- /dev/null +++ b/frontend/resources/images/icons/position-bottom-left.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/resources/images/icons/position-bottom-right.svg b/frontend/resources/images/icons/position-bottom-right.svg new file mode 100644 index 000000000..ebf861dcf --- /dev/null +++ b/frontend/resources/images/icons/position-bottom-right.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/resources/images/icons/position-center.svg b/frontend/resources/images/icons/position-center.svg new file mode 100644 index 000000000..ce6695ba7 --- /dev/null +++ b/frontend/resources/images/icons/position-center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/resources/images/icons/position-top-center.svg b/frontend/resources/images/icons/position-top-center.svg new file mode 100644 index 000000000..5a971d427 --- /dev/null +++ b/frontend/resources/images/icons/position-top-center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/resources/images/icons/position-top-left.svg b/frontend/resources/images/icons/position-top-left.svg new file mode 100644 index 000000000..0285e444e --- /dev/null +++ b/frontend/resources/images/icons/position-top-left.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/resources/images/icons/position-top-right.svg b/frontend/resources/images/icons/position-top-right.svg new file mode 100644 index 000000000..838f63602 --- /dev/null +++ b/frontend/resources/images/icons/position-top-right.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index f1845e3d9..5eee36b8d 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -945,11 +945,15 @@ width: 12px; height: 12px; fill: $color-gray-20; + stroke: $color-gray-20; } - &:hover svg { + &:hover svg, + &.active svg { fill: $color-primary; + stroke: $color-primary; } + &.actions-inside { position: absolute; right: 0; diff --git a/frontend/resources/styles/main/partials/sidebar-interactions.scss b/frontend/resources/styles/main/partials/sidebar-interactions.scss index c8ea87266..d2c9cb1dc 100644 --- a/frontend/resources/styles/main/partials/sidebar-interactions.scss +++ b/frontend/resources/styles/main/partials/sidebar-interactions.scss @@ -47,3 +47,13 @@ width: 64px; } } + +.interactions-pos-buttons { + margin-top: $small; + justify-content: space-around; + + svg { + width: 18px; + height: 18px; + } +} diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index dbff3982c..a53fdcee3 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1827,8 +1827,8 @@ frame)] ;; Update or create interaction (if index - (update-in interactions [index] - #(cti/set-destination % (:id frame) shape objects)) + (update interactions index + #(cti/set-destination % (:id frame) shape objects)) (conj (or interactions []) (cti/set-destination cti/default-interaction (:id frame) @@ -1870,11 +1870,11 @@ (rx/concat (->> ms/mouse-position (rx/take-until stopper) - (rx/map #(move-overlay-pos % overlay-pos frame-pos offset))) - (rx/of (finish-move-overlay-pos index overlay-pos frame-pos offset))))))))) + (rx/map #(move-overlay-pos % frame-pos offset))) + (rx/of (finish-move-overlay-pos index frame-pos offset))))))))) (defn move-overlay-pos - [pos overlay-pos frame-pos offset] + [pos frame-pos offset] (ptk/reify ::move-overlay-pos ptk/UpdateEvent (update [_ state] @@ -1884,7 +1884,7 @@ (assoc-in state [:workspace-local :move-overlay-to] pos))))) (defn finish-move-overlay-pos - [index overlay-pos frame-pos offset] + [index frame-pos offset] (ptk/reify ::finish-move-overlay-pos ptk/UpdateEvent (update [_ state] diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index 47738e45a..abf60ef6e 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -101,6 +101,13 @@ (def play (icon-xref :play)) (def plus (icon-xref :plus)) (def pointer-inner (icon-xref :pointer-inner)) +(def position-bottom-center (icon-xref :position-bottom-center)) +(def position-bottom-left (icon-xref :position-bottom-left)) +(def position-bottom-right (icon-xref :position-bottom-right)) +(def position-center (icon-xref :position-center)) +(def position-top-center (icon-xref :position-top-center)) +(def position-top-left (icon-xref :position-top-left)) +(def position-top-right (icon-xref :position-top-right)) (def radius (icon-xref :radius)) (def radius-1 (icon-xref :radius-1)) (def radius-4 (icon-xref :radius-4)) diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index b55016671..99fa30d33 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -146,8 +146,8 @@ {:style {:width (:width size-over) :height (:height size-over) :position "absolute" - :left (:x (:position overlay)) - :top (:y (:position overlay))}} + :left (* (:x (:position overlay)) zoom) + :top (* (:y (:position overlay)) zoom)}} [:& interactions/viewport {:frame (:frame overlay) :size size-over 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 c8dc7c2e7..3bdd854d1 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 @@ -31,7 +31,8 @@ [] {:navigate (tr "workspace.options.interaction-navigate-to") :open-overlay (tr "workspace.options.interaction-open-overlay") - :close-overlay (tr "workspace.options.interaction-close-overlay")}) + :close-overlay (tr "workspace.options.interaction-close-overlay") + :prev-screen (tr "workspace.options.interaction-prev-screen")}) (defn- action-summary [interaction destination] @@ -44,6 +45,17 @@ (get destination :name (tr "workspace.options.interaction-self"))) "--")) +(defn- overlay-pos-type-names + [] + {:manual (tr "workspace.options.interaction-pos-manual") + :center (tr "workspace.options.interaction-pos-center") + :top-left (tr "workspace.options.interaction-pos-top-left") + :top-right (tr "workspace.options.interaction-pos-top-right") + :top-center (tr "workspace.options.interaction-pos-top-center") + :bottom-left (tr "workspace.options.interaction-pos-bottom-left") + :bottom-right (tr "workspace.options.interaction-pos-bottom-right") + :bottom-center (tr "workspace.options.interaction-pos-bottom-center")}) + (mf/defc interaction-entry [{:keys [index shape interaction update-interaction remove-interaction]}] (let [objects (deref refs/workspace-page-objects) @@ -51,6 +63,9 @@ frames (mf/use-memo (mf/deps objects) #(cp/select-frames objects)) + action-type (:action-type interaction) + overlay-pos-type (:overlay-pos-type interaction) + extended-open? (mf/use-state false) change-event-type @@ -61,13 +76,22 @@ change-action-type (fn [event] (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(cti/set-action-type % value)))) + (update-interaction index #(cti/set-action-type % value shape objects)))) change-destination (fn [event] (let [value (-> event dom/get-target dom/get-value) value (when (not= value "") (uuid/uuid value))] - (update-interaction index #(cti/set-destination % value shape objects))))] + (update-interaction index #(cti/set-destination % value shape objects)))) + + change-overlay-pos-type + (fn [event] + (let [value (-> event dom/get-target dom/get-value d/read-string)] + (update-interaction index #(cti/set-overlay-pos-type % value shape objects)))) + + toggle-overlay-pos-type + (fn [pos-type] + (update-interaction index #(cti/toggle-overlay-pos-type % pos-type shape objects)))] [:* [:div.element-set-options-group @@ -95,7 +119,8 @@ :on-change change-action-type} (for [[value name] (action-type-names)] [:option {:value (str value)} name])]] - [:div.interactions-element + (when (#{:navigate :open-overlay :close-overlay} action-type) + [:div.interactions-element [:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")] [:select.input-select {:value (str (:destination interaction)) @@ -104,7 +129,45 @@ (for [frame frames] (when (and (not= (:id frame) (:id shape)) ; A frame cannot navigate to itself (not= (:id frame) (:frame-id shape))) ; nor a shape to its container frame - [:option {:value (str (:id frame))} (:name frame)]))]]]])])) + [:option {:value (str (:id frame))} (:name frame)]))]]) + (when (= action-type :open-overlay) + [:* + [:div.interactions-element + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-position")] + [:select.input-select + {:value (str (:overlay-pos-type interaction)) + :on-change change-overlay-pos-type} + (for [[value name] (overlay-pos-type-names)] + [:option {:value (str value)} name])]] + [:div.interactions-element.interactions-pos-buttons + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :center)) + :on-click #(toggle-overlay-pos-type :center)} + i/position-center] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :top-left)) + :on-click #(toggle-overlay-pos-type :top-left)} + i/position-top-left] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :top-right)) + :on-click #(toggle-overlay-pos-type :top-right)} + i/position-top-right] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :top-center)) + :on-click #(toggle-overlay-pos-type :top-center)} + i/position-top-center] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :bottom-left)) + :on-click #(toggle-overlay-pos-type :bottom-left)} + i/position-bottom-left] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :bottom-right)) + :on-click #(toggle-overlay-pos-type :bottom-right)} + i/position-bottom-right] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :bottom-center)) + :on-click #(toggle-overlay-pos-type :bottom-center)} + i/position-bottom-center]]])]])])) (mf/defc interactions-menu [{:keys [shape] :as props}] diff --git a/frontend/src/app/main/ui/workspace/viewport/interactions.cljs b/frontend/src/app/main/ui/workspace/viewport/interactions.cljs index 404431856..a5ec3e378 100644 --- a/frontend/src/app/main/ui/workspace/viewport/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/interactions.cljs @@ -194,9 +194,9 @@ (mf/defc overlay-marker - [{:keys [index orig-shape dest-shape position objects zoom] :as props}] + [{:keys [index orig-shape dest-shape position objects] :as props}] (let [start-move-position - (fn [event] + (fn [_] (st/emit! (dw/start-move-overlay-pos index)))] (when dest-shape @@ -287,15 +287,13 @@ :orig-shape shape :dest-shape dest-shape :position move-overlay-to - :objects objects - :zoom zoom}] + :objects objects}] [:& overlay-marker {:key (str "pos" (:id shape) "-" index) :index index :orig-shape shape :dest-shape dest-shape :position (:overlay-position interaction) - :objects objects - :zoom zoom}]))]))) + :objects objects}]))]))) (when (not (#{:move :rotate} current-transform)) [:& interaction-handle {:key (:id shape) :index nil diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 0ed54d467..a49fdf5a1 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2441,6 +2441,42 @@ msgstr "Open overlay" msgid "workspace.options.interaction-open-overlay-dest" msgstr "Open overlay: %s" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-manual" +msgstr "Manual" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-center" +msgstr "Center" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-top-left" +msgstr "Top left" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-top-right" +msgstr "Top right" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-top-center" +msgstr "Top center" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-bottom-left" +msgstr "Bottom left" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-bottom-right" +msgstr "Bottom right" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-bottom-center" +msgstr "Bottom center" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-position" +msgstr "Position" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.interaction-self" msgstr "self" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 83b47aefe..1e48f671a 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -2324,6 +2324,42 @@ msgstr "Open overlay" msgid "workspace.options.interaction-open-overlay-dest" msgstr "Open overlay: %s" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-manual" +msgstr "Manual" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-center" +msgstr "Centro" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-top-left" +msgstr "Arriba izquierda" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-top-right" +msgstr "Arriba derecha" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-top-center" +msgstr "Arriba centro" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-bottom-left" +msgstr "Abajo izquierda" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-bottom-right" +msgstr "Abajo derecha" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-pos-bottom-center" +msgstr "Abajo centro" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-position" +msgstr "Posición" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.interaction-self" msgstr "self" From bbdf1152c1a415b5b3594f7da0e7f13abedf2f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Mon, 20 Sep 2021 14:20:52 +0200 Subject: [PATCH 4/5] :tada: Add close on click and background overlays --- common/src/app/common/types/interactions.cljc | 20 ++++++++- .../resources/styles/main/layouts/viewer.scss | 16 +++++++ .../partials/sidebar-element-options.scss | 25 +++++++++++ frontend/src/app/main/data/viewer.cljs | 8 +++- frontend/src/app/main/ui/viewer.cljs | 45 +++++++++++++------ frontend/src/app/main/ui/viewer/shapes.cljs | 11 +++-- .../sidebar/options/menus/interactions.cljs | 36 +++++++++++++-- frontend/translations/en.po | 8 ++++ frontend/translations/es.po | 8 ++++ 9 files changed, 153 insertions(+), 24 deletions(-) diff --git a/common/src/app/common/types/interactions.cljc b/common/src/app/common/types/interactions.cljc index aa28a6425..f506ea7ea 100644 --- a/common/src/app/common/types/interactions.cljc +++ b/common/src/app/common/types/interactions.cljc @@ -55,6 +55,8 @@ :bottom-center}) (s/def ::overlay-position ::point) (s/def ::url ::us/string) +(s/def ::close-click-outside ::us/boolean) +(s/def ::background-overlay ::us/boolean) (defmulti action-opts-spec :action-type) @@ -64,7 +66,9 @@ (defmethod action-opts-spec :open-overlay [_] (s/keys :req-un [::destination ::overlay-position - ::overlay-pos-type])) + ::overlay-pos-type] + :opt-un [::close-click-outside + ::background-overlay])) (defmethod action-opts-spec :close-overlay [_] (s/keys :req-un [::destination])) @@ -221,6 +225,20 @@ :overlay-pos-type :manual :overlay-position overlay-position)) +(defn set-close-click-outside + [interaction close-click-outside] + (us/verify ::interaction interaction) + (us/verify ::us/boolean close-click-outside) + (assert #(= :open-overlay (:action-type interaction))) + (assoc interaction :close-click-outside close-click-outside)) + +(defn set-background-overlay + [interaction background-overlay] + (us/verify ::interaction interaction) + (us/verify ::us/boolean background-overlay) + (assert #(= :open-overlay (:action-type interaction))) + (assoc interaction :background-overlay background-overlay)) + (defn- calc-overlay-position [destination interaction shape objects overlay-pos-type] (if (nil? destination) diff --git a/frontend/resources/styles/main/layouts/viewer.scss b/frontend/resources/styles/main/layouts/viewer.scss index 7e05a861b..296322332 100644 --- a/frontend/resources/styles/main/layouts/viewer.scss +++ b/frontend/resources/styles/main/layouts/viewer.scss @@ -43,3 +43,19 @@ grid-row: 1 / span 2; } } + +.viewer-overlay { + position: absolute; +} + +.viewer-overlay-background { + position: absolute; + top: 0; + left: 0; + + &.visible { + background-color: rgb(0, 0, 0, 0.2); + } +} + + diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 5eee36b8d..1c38124e5 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -222,6 +222,31 @@ } } + .input-checkbox { + label { + color: $color-gray-20; + } + + label::before { + background-color: transparent; + width: 16px; + height: 16px; + } + + label::after { + width: 16px; + height: 16px; + } + + input:checked + label::before { + border-width: 1px; + } + + input:checked + label::after { + font-size: 0.8rem; + } + } + .element-set-subtitle { color: $color-gray-20; font-size: $fs11; diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index 9b4ba9af4..75063ce04 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -328,9 +328,11 @@ ;; --- Overlays (defn open-overlay - [frame-id position] + [frame-id position close-click-outside background-overlay] (us/verify ::us/uuid frame-id) (us/verify ::us/point position) + (us/verify (s/nilable ::us/boolean) close-click-outside) + (us/verify (s/nilable ::us/boolean) background-overlay) (ptk/reify ::open-overlay ptk/UpdateEvent (update [_ state] @@ -343,7 +345,9 @@ (if-not (some #(= % frame) overlays) (update-in state [:viewer-local :overlays] conj {:frame frame - :position position}) + :position position + :close-click-outside close-click-outside + :background-overlay background-overlay}) state))))) (defn close-overlay diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 99fa30d33..e84fc0361 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -65,7 +65,12 @@ (mf/deps section) (fn [_] (when (= section :comments) - (st/emit! (dcm/close-thread)))))] + (st/emit! (dcm/close-thread))))) + + close-overlay + (mf/use-callback + (fn [frame] + (st/emit! (dv/close-overlay (:id frame)))))] (hooks/use-shortcuts ::viewer sc/shortcuts) @@ -142,19 +147,31 @@ (for [overlay (:overlays local)] (let [size-over (calculate-size (:frame overlay) zoom)] - [:div.viewport-container - {:style {:width (:width size-over) - :height (:height size-over) - :position "absolute" - :left (* (:x (:position overlay)) zoom) - :top (* (:y (:position overlay)) zoom)}} - [:& interactions/viewport - {:frame (:frame overlay) - :size size-over - :page page - :file file - :users users - :local local}]]))]))]]])) + [:* + (when (or (:close-click-outside overlay) + (:background-overlay overlay)) + [:div.viewer-overlay-background + {:class (dom/classnames + :visible (:background-overlay overlay)) + :style {:width (:width frame) + :height (:height frame) + :position "absolute" + :left 0 + :top 0} + :on-click #(when (:close-click-outside overlay) + (close-overlay (:frame overlay)))}]) + [:div.viewport-container.viewer-overlay + {:style {:width (:width size-over) + :height (:height size-over) + :left (* (:x (:position overlay)) zoom) + :top (* (:y (:position overlay)) zoom)}} + [:& interactions/viewport + {:frame (:frame overlay) + :size size-over + :page page + :file file + :users users + :local local}]]]))]))]]])) ;; --- Component: Viewer Page diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index f743f4c92..a39d11894 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -39,10 +39,15 @@ (st/emit! (dv/go-to-frame frame-id))) :open-overlay - (let [frame-id (:destination interaction) - position (:overlay-position interaction)] + (let [frame-id (:destination interaction) + position (:overlay-position interaction) + close-click-outside (:close-click-outside interaction) + background-overlay (:background-overlay interaction)] (dom/stop-propagation event) - (st/emit! (dv/open-overlay frame-id position))) + (st/emit! (dv/open-overlay frame-id + position + close-click-outside + background-overlay))) :close-overlay (let [frame-id (or (:destination interaction) 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 3bdd854d1..7a77359d8 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 @@ -63,8 +63,10 @@ frames (mf/use-memo (mf/deps objects) #(cp/select-frames objects)) - action-type (:action-type interaction) - overlay-pos-type (:overlay-pos-type interaction) + action-type (:action-type interaction) + overlay-pos-type (:overlay-pos-type interaction) + close-click-outside? (:close-click-outside interaction) + background-overlay? (:background-overlay interaction) extended-open? (mf/use-state false) @@ -91,7 +93,17 @@ toggle-overlay-pos-type (fn [pos-type] - (update-interaction index #(cti/toggle-overlay-pos-type % pos-type shape objects)))] + (update-interaction index #(cti/toggle-overlay-pos-type % pos-type shape objects))) + + change-close-click-outside + (fn [event] + (let [value (-> event dom/get-target dom/checked?)] + (update-interaction index #(cti/set-close-click-outside % value)))) + + change-background-overlay + (fn [event] + (let [value (-> event dom/get-target dom/checked?)] + (update-interaction index #(cti/set-background-overlay % value))))] [:* [:div.element-set-options-group @@ -167,7 +179,23 @@ [:div.element-set-actions-button {:class (dom/classnames :active (= overlay-pos-type :bottom-center)) :on-click #(toggle-overlay-pos-type :bottom-center)} - i/position-bottom-center]]])]])])) + i/position-bottom-center]] + [:div.interactions-element + [:div.input-checkbox + [:input {:type "checkbox" + :id (str "close-" index) + :checked close-click-outside? + :on-change change-close-click-outside}] + [:label {:for (str "close-" index)} + (tr "workspace.options.interaction-close-outside")]]] + [:div.interactions-element + [:div.input-checkbox + [:input {:type "checkbox" + :id (str "background-" index) + :checked background-overlay? + :on-change change-background-overlay}] + [:label {:for (str "background-" index)} + (tr "workspace.options.interaction-background")]]]])]])])) (mf/defc interactions-menu [{:keys [shape] :as props}] diff --git a/frontend/translations/en.po b/frontend/translations/en.po index a49fdf5a1..2a28ffd7a 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2405,6 +2405,14 @@ msgstr "Click the + button to add interactions." msgid "workspace.options.interaction-action" msgstr "Action" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-background" +msgstr "Add background overlay" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-close-outside" +msgstr "Close when clicking outside" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.interaction-close-overlay" msgstr "Close overlay" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 1e48f671a..344baeabd 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -2288,6 +2288,14 @@ msgstr "Pulsa el botón + para añadir interacciones." msgid "workspace.options.interaction-action" msgstr "Acción" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-background" +msgstr "Añadir sombreado de fondo" + +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.interaction-close-outside" +msgstr "Cerrar al pulsar fuera" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.interaction-close-overlay" msgstr "Close overlay" From f8d09917a53b54081b848ac422af2a4c826f8435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 21 Sep 2021 15:48:05 +0200 Subject: [PATCH 5/5] :lipstick: Make some design adjustments --- .../partials/sidebar-element-options.scss | 27 ++++++-- .../main/partials/sidebar-interactions.scss | 28 ++++++++- .../sidebar/options/menus/interactions.cljs | 61 ++++++++++--------- .../ui/workspace/viewport/interactions.cljs | 35 ++++++----- 4 files changed, 98 insertions(+), 53 deletions(-) diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 1c38124e5..5a36e748f 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -908,12 +908,31 @@ &.selected { border: 1px solid $color-primary; } + + &:not(:first-child) { + margin-top: 7px; + } + + &.open { + &:hover { + background: unset; + } + } +} + +.interactions-options { + &.element-set { + border-bottom: 0; + } + + .element-set-options-group { + flex-wrap: wrap; + } } .exports-options, -.shadow-options{ +.shadow-options { .element-set-options-group { - justify-content: space-between; .delete-icon { display: flex; min-width: 40px; @@ -927,10 +946,6 @@ fill: $color-gray-20; } } - - &:not(:first-child) { - margin-top: 7px; - } } .download-button { diff --git a/frontend/resources/styles/main/partials/sidebar-interactions.scss b/frontend/resources/styles/main/partials/sidebar-interactions.scss index d2c9cb1dc..c01779e32 100644 --- a/frontend/resources/styles/main/partials/sidebar-interactions.scss +++ b/frontend/resources/styles/main/partials/sidebar-interactions.scss @@ -7,8 +7,14 @@ .interactions-help { font-size: $fs12; - margin: 0 $medium; + padding: 7px $medium; + margin: 0 -7px; text-align: center; + + &.separator { + padding-bottom: $medium; + border-bottom: 1px solid $color-black; + } } .interactions-help-icon { @@ -24,7 +30,9 @@ } .interactions-summary { - width: 100%; + cursor: pointer; + flex-basis: 0; + flex-grow: 1; .trigger-name { font-size: $fs12; @@ -40,17 +48,31 @@ .interactions-element { display: flex; align-items: center; + margin: 0 -7px; + padding: 0 7px; .element-label { color: $color-gray-20; font-size: $fs11; width: 64px; } + + &.separator { + border-top: 1px solid $color-black; + margin-top: $x-small; + } } .interactions-pos-buttons { margin-top: $small; - justify-content: space-around; + padding-top: $small; + padding-bottom: $small; + justify-content: space-between; + + .element-set-actions-button { + min-width: 18px; + min-height: 18px; + } svg { width: 18px; 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 7a77359d8..545eea731 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 @@ -65,8 +65,8 @@ action-type (:action-type interaction) overlay-pos-type (:overlay-pos-type interaction) - close-click-outside? (:close-click-outside interaction) - background-overlay? (:background-overlay interaction) + close-click-outside? (:close-click-outside interaction false) + background-overlay? (:background-overlay interaction false) extended-open? (mf/use-state false) @@ -106,31 +106,31 @@ (update-interaction index #(cti/set-background-overlay % value))))] [:* - [:div.element-set-options-group + [:div.element-set-options-group {:class (dom/classnames + :open @extended-open?)} [:div.element-set-actions-button {:on-click #(swap! extended-open? not)} i/actions] - [:div.interactions-summary + [:div.interactions-summary {:on-click #(swap! extended-open? not)} [:div.trigger-name (event-type-name interaction)] [:div.action-summary (action-summary interaction destination)]] [:div.elemen-set-actions {:on-click #(remove-interaction index)} - [:div.element-set-actions-button i/minus]]] - (when @extended-open? - [:div.element-set + [:div.element-set-actions-button i/minus]] + (when @extended-open? [:div.element-set-content - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")] - [:select.input-select - {:value (str (:event-type interaction)) - :on-change change-event-type} - (for [[value name] (event-type-names)] - [:option {:value (str value)} name])]] - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")] - [:select.input-select - {:value (str (:action-type interaction)) - :on-change change-action-type} - (for [[value name] (action-type-names)] - [:option {:value (str value)} name])]] + [:div.interactions-element.separator + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")] + [:select.input-select + {:value (str (:event-type interaction)) + :on-change change-event-type} + (for [[value name] (event-type-names)] + [:option {:value (str value)} name])]] + [:div.interactions-element.separator + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")] + [:select.input-select + {:value (str (:action-type interaction)) + :on-change change-action-type} + (for [[value name] (action-type-names)] + [:option {:value (str value)} name])]] (when (#{:navigate :open-overlay :close-overlay} action-type) [:div.interactions-element [:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")] @@ -195,7 +195,7 @@ :checked background-overlay? :on-change change-background-overlay}] [:label {:for (str "background-" index)} - (tr "workspace.options.interaction-background")]]]])]])])) + (tr "workspace.options.interaction-background")]]]])])]])) (mf/defc interactions-menu [{:keys [shape] :as props}] @@ -218,7 +218,7 @@ (let [new-interactions (update interactions index update-fn)] (st/emit! (dw/update-shape (:id shape) {:interactions new-interactions})))) ] - [:div.element-set + [:div.element-set.interactions-options (when shape [:div.element-set-title [:span (tr "workspace.options.interactions")] @@ -231,15 +231,16 @@ (when shape [:* [:div.interactions-help-icon i/plus] - [:div.interactions-help (tr "workspace.options.add-interaction")]]) + [:div.interactions-help.separator (tr "workspace.options.add-interaction")]]) [:div.interactions-help-icon i/interaction] [:div.interactions-help (tr "workspace.options.select-a-shape")] [:div.interactions-help-icon i/play] [:div.interactions-help (tr "workspace.options.use-play-button")]])] - (for [[index interaction] (d/enumerate interactions)] - [:& interaction-entry {:index index - :shape shape - :interaction interaction - :update-interaction update-interaction - :remove-interaction remove-interaction}])])) + [:div.groups + (for [[index interaction] (d/enumerate interactions)] + [:& interaction-entry {:index index + :shape shape + :interaction interaction + :update-interaction update-interaction + :remove-interaction remove-interaction}])]])) diff --git a/frontend/src/app/main/ui/workspace/viewport/interactions.cljs b/frontend/src/app/main/ui/workspace/viewport/interactions.cljs index a5ec3e378..e041bd773 100644 --- a/frontend/src/app/main/ui/workspace/viewport/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/interactions.cljs @@ -97,17 +97,15 @@ [:* [:circle {:cx 0 :cy 0 - :r 8 - :stroke stroke - :stroke-width 2 - :fill "#FFFFFF" + :r (if (some? action-type) 8 4) + :fill stroke :transform (str "scale(" inv-zoom ", " inv-zoom ") " "translate(" (* zoom x) ", " (* zoom y) ")")}] (when icon-pdata - [:path {:stroke stroke - :fill "none" - :stroke-width 2 + [:path {:fill stroke + :stroke-width 1 + :stroke "#FFFFFF" :d icon-pdata :transform (str "scale(" inv-zoom ", " inv-zoom ") " @@ -115,7 +113,7 @@ (mf/defc interaction-path - [{:keys [index orig-shape dest-shape dest-point selected? action-type zoom] :as props}] + [{:keys [index level orig-shape dest-shape dest-point selected? action-type zoom] :as props}] (let [[orig-pos orig-x orig-y dest-pos dest-x dest-y] (cond dest-shape @@ -127,7 +125,8 @@ :else (connect-to-point orig-shape {:x (+ (:x2 (:selrect orig-shape)) 100) - :y (- (:y1 (:selrect orig-shape)) 50)})) + :y (+ (- (:y1 (:selrect orig-shape)) 50) + (* level 16))})) orig-dx (if (= orig-pos :right) 100 -100) dest-dx (if (= dest-pos :right) 100 -100) @@ -144,8 +143,7 @@ :pointer-events "visible" :stroke-width (/ 2 zoom) :d pdata}] - (when (and (not dest-shape) - (= action-type :close-overlay)) + (when (not dest-shape) [:& interaction-marker {:index index :x dest-x :y dest-y @@ -237,17 +235,24 @@ draw-interaction-to-frame (:draw-interaction-to-frame local) move-overlay-to (:move-overlay-to local) move-overlay-index (:move-overlay-index local) - first-selected (first selected-shapes)] + first-selected (first selected-shapes) + + calc-level (fn [index interactions] + (->> (subvec interactions 0 index) + (filter #(nil? (:destination %))) + (count)))] [:g.interactions [:g.non-selected (for [shape active-shapes] (for [[index interaction] (d/enumerate (:interactions shape))] (let [dest-shape (get objects (:destination interaction)) - selected? (contains? selected (:id shape))] + selected? (contains? selected (:id shape)) + level (calc-level index (:interactions shape))] (when-not selected? [:& interaction-path {:key (str (:id shape) "-" index) :index index + :level level :orig-shape shape :dest-shape dest-shape :selected selected @@ -269,10 +274,12 @@ (if (seq (:interactions shape)) (for [[index interaction] (d/enumerate (:interactions shape))] (when-not (= index editing-interaction-index) - (let [dest-shape (get objects (:destination interaction))] + (let [dest-shape (get objects (:destination interaction)) + level (calc-level index (:interactions shape))] [:* [:& interaction-path {:key (str (:id shape) "-" index) :index index + :level level :orig-shape shape :dest-shape dest-shape :selected selected