mirror of
https://github.com/penpot/penpot.git
synced 2025-04-01 09:31:26 -05:00
✨ Better overlays interactions on boards inside boards
This commit is contained in:
parent
58dbe21544
commit
1affb53a26
9 changed files with 595 additions and 314 deletions
|
@ -4,6 +4,7 @@
|
|||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
### :sparkles: New features
|
||||
- Better overlays interactions on boards inside boards [Taiga #4386](https://tree.taiga.io/project/penpot/us/4386)
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Add title to color bullets [Taiga #4218](https://tree.taiga.io/project/penpot/task/4218)
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
:close-overlay
|
||||
:prev-screen
|
||||
:open-url})
|
||||
|
||||
(s/def ::position-relative-to (s/nilable ::us/uuid))
|
||||
(s/def ::overlay-pos-type
|
||||
#{:manual
|
||||
:center
|
||||
|
@ -110,7 +110,7 @@
|
|||
(defmethod action-opts-spec :navigate [_]
|
||||
(s/keys :opt-un [::destination
|
||||
::preserve-scroll
|
||||
::animation]))
|
||||
::animation]))
|
||||
|
||||
(defmethod action-opts-spec :open-overlay [_]
|
||||
(s/keys :req-un [::overlay-position
|
||||
|
@ -118,7 +118,8 @@
|
|||
:opt-un [::destination
|
||||
::close-click-outside
|
||||
::background-overlay
|
||||
::animation]))
|
||||
::animation
|
||||
::position-relative-to]))
|
||||
|
||||
(defmethod action-opts-spec :toggle-overlay [_]
|
||||
(s/keys :req-un [::overlay-position
|
||||
|
@ -126,11 +127,13 @@
|
|||
:opt-un [::destination
|
||||
::close-click-outside
|
||||
::background-overlay
|
||||
::animation]))
|
||||
::animation
|
||||
::position-relative-to]))
|
||||
|
||||
(defmethod action-opts-spec :close-overlay [_]
|
||||
(s/keys :opt-un [::destination
|
||||
::animation]))
|
||||
::animation
|
||||
::position-relative-to]))
|
||||
|
||||
(defmethod action-opts-spec :prev-screen [_]
|
||||
(s/keys :req-un []))
|
||||
|
@ -159,6 +162,7 @@
|
|||
{:event-type :click
|
||||
:action-type :navigate
|
||||
:destination nil
|
||||
:position-relative-to nil
|
||||
:preserve-scroll false})
|
||||
|
||||
(def default-delay 600)
|
||||
|
@ -336,6 +340,13 @@
|
|||
(assert (has-overlay-opts interaction))
|
||||
(assoc interaction :background-overlay background-overlay))
|
||||
|
||||
(defn set-position-relative-to
|
||||
[interaction position-relative-to]
|
||||
(us/verify ::interaction interaction)
|
||||
(us/verify ::position-relative-to position-relative-to)
|
||||
(assert (has-overlay-opts interaction))
|
||||
(assoc interaction :position-relative-to position-relative-to))
|
||||
|
||||
(defn- calc-overlay-pos-initial
|
||||
[destination shape objects overlay-pos-type]
|
||||
(if (and (= overlay-pos-type :manual) (some? destination))
|
||||
|
@ -350,43 +361,58 @@
|
|||
(gpt/point 0 0)))
|
||||
|
||||
(defn calc-overlay-position
|
||||
[interaction base-frame dest-frame frame-offset]
|
||||
[interaction ;; interaction data
|
||||
relative-to-shape ;; the interaction position is realtive to this sape
|
||||
base-frame ;; the base frame of the current interaction
|
||||
dest-frame ;; the frame to display with this interaction
|
||||
frame-offset] ;; if this interaction starts in a frame opened on another interaction, this is the position of that frame
|
||||
|
||||
(us/verify ::interaction interaction)
|
||||
(assert (has-overlay-opts interaction))
|
||||
(if (nil? dest-frame)
|
||||
(gpt/point 0 0)
|
||||
(let [overlay-size (:selrect dest-frame)
|
||||
base-frame-size (:selrect base-frame)]
|
||||
(let [overlay-position (:overlay-position interaction)
|
||||
overlay-size (:selrect dest-frame)
|
||||
relative-to-shape-size (:selrect relative-to-shape)
|
||||
base-frame-size (:selrect base-frame)
|
||||
relative-to-is-auto? (and (nil? (:position-relative-to interaction)) (not= :manual (:overlay-pos-type interaction)))
|
||||
base-position (if relative-to-is-auto?
|
||||
{:x 0 :y 0}
|
||||
{:x (+ (:x frame-offset)
|
||||
(- (:x relative-to-shape-size) (:x base-frame-size)))
|
||||
:y (+ (:y frame-offset)
|
||||
(- (:y relative-to-shape-size) (:y base-frame-size)))})]
|
||||
(case (:overlay-pos-type interaction)
|
||||
:center
|
||||
(gpt/point (/ (- (:width base-frame-size) (:width overlay-size)) 2)
|
||||
(/ (- (:height base-frame-size) (:height overlay-size)) 2))
|
||||
(gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2))
|
||||
(+ (:y base-position) (/ (- (:height relative-to-shape-size) (:height overlay-size)) 2)))
|
||||
|
||||
:top-left
|
||||
(gpt/point 0 0)
|
||||
(gpt/point (:x base-position) (:y base-position))
|
||||
|
||||
:top-right
|
||||
(gpt/point (- (:width base-frame-size) (:width overlay-size))
|
||||
0)
|
||||
(gpt/point (+ (:x base-position) (- (:width relative-to-shape-size) (:width overlay-size)))
|
||||
(:y base-position))
|
||||
|
||||
:top-center
|
||||
(gpt/point (/ (- (:width base-frame-size) (:width overlay-size)) 2)
|
||||
0)
|
||||
(gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2))
|
||||
(:y base-position))
|
||||
|
||||
:bottom-left
|
||||
(gpt/point 0
|
||||
(- (:height base-frame-size) (:height overlay-size)))
|
||||
(gpt/point (:x base-position)
|
||||
(+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size))))
|
||||
|
||||
:bottom-right
|
||||
(gpt/point (- (:width base-frame-size) (:width overlay-size))
|
||||
(- (:height base-frame-size) (:height overlay-size)))
|
||||
(gpt/point (+ (:x base-position) (- (:width relative-to-shape-size) (:width overlay-size)))
|
||||
(+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size))))
|
||||
|
||||
:bottom-center
|
||||
(gpt/point (/ (- (:width base-frame-size) (:width overlay-size)) 2)
|
||||
(- (:height base-frame-size) (:height overlay-size)))
|
||||
(gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2))
|
||||
(+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size))))
|
||||
|
||||
:manual
|
||||
(gpt/add (:overlay-position interaction) frame-offset)))))
|
||||
(gpt/point (+ (:x base-position) (:x overlay-position))
|
||||
(+ (:y base-position) (:y overlay-position)))))))
|
||||
|
||||
(defn has-animation?
|
||||
[interaction]
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
(t/testing "Set event type changed"
|
||||
(let [new-interaction
|
||||
(ctsi/set-event-type interaction :mouse-press shape)]
|
||||
(t/is (= :mouse-press (:event-type new-interaction)))))
|
||||
(t/is (= :mouse-press (:event-type new-interaction)))))
|
||||
|
||||
(t/testing "Set after delay on non-frame"
|
||||
(let [result (ex/try
|
||||
|
@ -91,7 +91,7 @@
|
|||
|
||||
(t/testing "Set action type open-overlay with previous data"
|
||||
(let [interaction (assoc interaction :overlay-pos-type :top-left
|
||||
:overlay-position (gpt/point 100 200))
|
||||
:overlay-position (gpt/point 100 200))
|
||||
new-interaction
|
||||
(ctsi/set-action-type interaction :open-overlay)]
|
||||
(t/is (= :open-overlay (:action-type new-interaction)))
|
||||
|
@ -107,7 +107,7 @@
|
|||
|
||||
(t/testing "Set action type toggle-overlay with previous data"
|
||||
(let [interaction (assoc interaction :overlay-pos-type :top-left
|
||||
:overlay-position (gpt/point 100 200))
|
||||
:overlay-position (gpt/point 100 200))
|
||||
new-interaction
|
||||
(ctsi/set-action-type interaction :toggle-overlay)]
|
||||
(t/is (= :toggle-overlay (:action-type new-interaction)))
|
||||
|
@ -146,19 +146,18 @@
|
|||
(t/is (= :open-url (:action-type new-interaction)))
|
||||
(t/is (= "https://example.com" (:url new-interaction)))))))
|
||||
|
||||
|
||||
(t/deftest option-delay
|
||||
(let [frame (cts/make-minimal-shape :frame)
|
||||
i1 ctsi/default-interaction
|
||||
i2 (ctsi/set-event-type i1 :after-delay frame)]
|
||||
|
||||
(t/testing "Has delay"
|
||||
(t/is (not (ctsi/has-delay i1)))
|
||||
(t/is (ctsi/has-delay i2)))
|
||||
(t/testing "Has delay"
|
||||
(t/is (not (ctsi/has-delay i1)))
|
||||
(t/is (ctsi/has-delay i2)))
|
||||
|
||||
(t/testing "Set delay"
|
||||
(let [new-interaction (ctsi/set-delay i2 1000)]
|
||||
(t/is (= 1000 (:delay new-interaction)))))))
|
||||
(t/testing "Set delay"
|
||||
(let [new-interaction (ctsi/set-delay i2 1000)]
|
||||
(t/is (= 1000 (:delay new-interaction)))))))
|
||||
|
||||
|
||||
(t/deftest option-destination
|
||||
|
@ -167,47 +166,47 @@
|
|||
i2 (ctsi/set-action-type i1 :prev-screen)
|
||||
i3 (ctsi/set-action-type i1 :open-overlay)]
|
||||
|
||||
(t/testing "Has destination"
|
||||
(t/is (ctsi/has-destination i1))
|
||||
(t/is (not (ctsi/has-destination i2))))
|
||||
(t/testing "Has destination"
|
||||
(t/is (ctsi/has-destination i1))
|
||||
(t/is (not (ctsi/has-destination i2))))
|
||||
|
||||
(t/testing "Set destination"
|
||||
(let [new-interaction (ctsi/set-destination i1 destination)]
|
||||
(t/is (= destination (:destination new-interaction)))
|
||||
(t/is (nil? (:overlay-pos-type new-interaction)))
|
||||
(t/is (nil? (:overlay-position new-interaction)))))
|
||||
(t/testing "Set destination"
|
||||
(let [new-interaction (ctsi/set-destination i1 destination)]
|
||||
(t/is (= destination (:destination new-interaction)))
|
||||
(t/is (nil? (:overlay-pos-type new-interaction)))
|
||||
(t/is (nil? (:overlay-position new-interaction)))))
|
||||
|
||||
(t/testing "Set destination of overlay"
|
||||
(let [new-interaction (ctsi/set-destination i3 destination)]
|
||||
(t/is (= destination (:destination new-interaction)))
|
||||
(t/is (= :center (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction)))))))
|
||||
(t/testing "Set destination of overlay"
|
||||
(let [new-interaction (ctsi/set-destination i3 destination)]
|
||||
(t/is (= destination (:destination new-interaction)))
|
||||
(t/is (= :center (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction)))))))
|
||||
|
||||
|
||||
(t/deftest option-preserve-scroll
|
||||
(let [i1 ctsi/default-interaction
|
||||
i2 (ctsi/set-action-type i1 :prev-screen)]
|
||||
|
||||
(t/testing "Has preserve-scroll"
|
||||
(t/is (ctsi/has-preserve-scroll i1))
|
||||
(t/is (not (ctsi/has-preserve-scroll i2))))
|
||||
(t/testing "Has preserve-scroll"
|
||||
(t/is (ctsi/has-preserve-scroll i1))
|
||||
(t/is (not (ctsi/has-preserve-scroll i2))))
|
||||
|
||||
(t/testing "Set preserve-scroll"
|
||||
(let [new-interaction (ctsi/set-preserve-scroll i1 true)]
|
||||
(t/is (= true (:preserve-scroll new-interaction)))))))
|
||||
(t/testing "Set preserve-scroll"
|
||||
(let [new-interaction (ctsi/set-preserve-scroll i1 true)]
|
||||
(t/is (= true (:preserve-scroll new-interaction)))))))
|
||||
|
||||
|
||||
(t/deftest option-url
|
||||
(let [i1 ctsi/default-interaction
|
||||
i2 (ctsi/set-action-type i1 :open-url)]
|
||||
|
||||
(t/testing "Has url"
|
||||
(t/is (not (ctsi/has-url i1)))
|
||||
(t/is (ctsi/has-url i2)))
|
||||
(t/testing "Has url"
|
||||
(t/is (not (ctsi/has-url i1)))
|
||||
(t/is (ctsi/has-url i2)))
|
||||
|
||||
(t/testing "Set url"
|
||||
(let [new-interaction (ctsi/set-url i2 "https://example.com")]
|
||||
(t/is (= "https://example.com" (:url new-interaction)))))))
|
||||
(t/testing "Set url"
|
||||
(let [new-interaction (ctsi/set-url i2 "https://example.com")]
|
||||
(t/is (= "https://example.com" (:url new-interaction)))))))
|
||||
|
||||
|
||||
(t/deftest option-overlay-opts
|
||||
|
@ -226,50 +225,237 @@
|
|||
(ctsi/set-action-type :open-overlay)
|
||||
(ctsi/set-destination (:id overlay-frame)))]
|
||||
|
||||
(t/testing "Has overlay options"
|
||||
(t/is (not (ctsi/has-overlay-opts i1)))
|
||||
(t/is (ctsi/has-overlay-opts i2)))
|
||||
(t/testing "Has overlay options"
|
||||
(t/is (not (ctsi/has-overlay-opts i1)))
|
||||
(t/is (ctsi/has-overlay-opts i2)))
|
||||
|
||||
(t/testing "Set overlay-pos-type without destination"
|
||||
(let [new-interaction (ctsi/set-overlay-pos-type i2 :top-right base-frame objects)]
|
||||
(t/is (= :top-right (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction)))))
|
||||
(t/testing "Set overlay-pos-type without destination"
|
||||
(let [new-interaction (ctsi/set-overlay-pos-type i2 :top-right base-frame objects)]
|
||||
(t/is (= :top-right (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction)))))
|
||||
|
||||
(t/testing "Set overlay-pos-type with destination and auto"
|
||||
(let [new-interaction (ctsi/set-overlay-pos-type i3 :bottom-right base-frame objects)]
|
||||
(t/is (= :bottom-right (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction)))))
|
||||
(t/testing "Set overlay-pos-type with destination and auto"
|
||||
(let [new-interaction (ctsi/set-overlay-pos-type i3 :bottom-right base-frame objects)]
|
||||
(t/is (= :bottom-right (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction)))))
|
||||
|
||||
(t/testing "Set overlay-pos-type with destination and manual"
|
||||
(let [new-interaction (ctsi/set-overlay-pos-type i3 :manual base-frame objects)]
|
||||
(t/is (= :manual (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 35 40) (:overlay-position new-interaction)))))
|
||||
(t/testing "Set overlay-pos-type with destination and manual"
|
||||
(let [new-interaction (ctsi/set-overlay-pos-type i3 :manual base-frame objects)]
|
||||
(t/is (= :manual (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 35 40) (:overlay-position new-interaction)))))
|
||||
|
||||
(t/testing "Toggle overlay-pos-type"
|
||||
(let [new-interaction (ctsi/toggle-overlay-pos-type i3 :center base-frame objects)
|
||||
new-interaction-2 (ctsi/toggle-overlay-pos-type new-interaction :center base-frame objects)
|
||||
new-interaction-3 (ctsi/toggle-overlay-pos-type new-interaction-2 :top-right base-frame objects)]
|
||||
(t/is (= :manual (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 35 40) (:overlay-position new-interaction)))
|
||||
(t/is (= :center (:overlay-pos-type new-interaction-2)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction-2)))
|
||||
(t/is (= :top-right (:overlay-pos-type new-interaction-3)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction-3)))))
|
||||
(t/testing "Toggle overlay-pos-type"
|
||||
(let [new-interaction (ctsi/toggle-overlay-pos-type i3 :center base-frame objects)
|
||||
new-interaction-2 (ctsi/toggle-overlay-pos-type new-interaction :center base-frame objects)
|
||||
new-interaction-3 (ctsi/toggle-overlay-pos-type new-interaction-2 :top-right base-frame objects)]
|
||||
(t/is (= :manual (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 35 40) (:overlay-position new-interaction)))
|
||||
(t/is (= :center (:overlay-pos-type new-interaction-2)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction-2)))
|
||||
(t/is (= :top-right (:overlay-pos-type new-interaction-3)))
|
||||
(t/is (= (gpt/point 0 0) (:overlay-position new-interaction-3)))))
|
||||
|
||||
(t/testing "Set overlay-position"
|
||||
(let [new-interaction (ctsi/set-overlay-position i3 (gpt/point 50 60))]
|
||||
(t/is (= :manual (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 50 60) (:overlay-position new-interaction)))))
|
||||
(t/testing "Set overlay-position"
|
||||
(let [new-interaction (ctsi/set-overlay-position i3 (gpt/point 50 60))]
|
||||
(t/is (= :manual (:overlay-pos-type new-interaction)))
|
||||
(t/is (= (gpt/point 50 60) (:overlay-position new-interaction)))))
|
||||
|
||||
(t/testing "Set close-click-outside"
|
||||
(let [new-interaction (ctsi/set-close-click-outside i3 true)]
|
||||
(t/is (not (:close-click-outside i3)))
|
||||
(t/is (:close-click-outside new-interaction))))
|
||||
(t/testing "Set close-click-outside"
|
||||
(let [new-interaction (ctsi/set-close-click-outside i3 true)]
|
||||
(t/is (not (:close-click-outside i3)))
|
||||
(t/is (:close-click-outside new-interaction))))
|
||||
|
||||
(t/testing "Set background-overlay"
|
||||
(let [new-interaction (ctsi/set-background-overlay i3 true)]
|
||||
(t/is (not (:background-overlay i3)))
|
||||
(t/is (:background-overlay new-interaction))))))
|
||||
(t/testing "Set background-overlay"
|
||||
(let [new-interaction (ctsi/set-background-overlay i3 true)]
|
||||
(t/is (not (:background-overlay i3)))
|
||||
(t/is (:background-overlay new-interaction))))
|
||||
|
||||
(t/testing "Set relative-to"
|
||||
(let [relative-to-id (uuid/random)
|
||||
new-interaction (ctsi/set-position-relative-to i3 relative-to-id)]
|
||||
(t/is (= relative-to-id (:position-relative-to new-interaction)))))))
|
||||
|
||||
|
||||
(t/deftest calc-overlay-position
|
||||
(let [base-frame (-> (cts/make-minimal-shape :frame)
|
||||
(assoc-in [:selrect :width] 100)
|
||||
(assoc-in [:selrect :height] 100))
|
||||
popup (-> (cts/make-minimal-shape :frame)
|
||||
(assoc-in [:selrect :width] 50)
|
||||
(assoc-in [:selrect :height] 50)
|
||||
(assoc-in [:selrect :x] 10)
|
||||
(assoc-in [:selrect :y] 10))
|
||||
overlay-frame (-> (cts/make-minimal-shape :frame)
|
||||
(assoc-in [:selrect :width] 30)
|
||||
(assoc-in [:selrect :height] 20))
|
||||
|
||||
objects {(:id base-frame) base-frame
|
||||
(:id popup) popup
|
||||
(:id overlay-frame) overlay-frame}
|
||||
|
||||
frame-offset (gpt/point 5 5)
|
||||
|
||||
interaction (-> ctsi/default-interaction
|
||||
(ctsi/set-action-type :open-overlay)
|
||||
(ctsi/set-destination (:id overlay-frame)))
|
||||
interaction-auto (ctsi/set-position-relative-to interaction nil)
|
||||
interaction-base-frame (ctsi/set-position-relative-to interaction (:id base-frame))
|
||||
interaction-popup (ctsi/set-position-relative-to interaction (:id popup))]
|
||||
(t/testing "Overlay top-left relative to auto"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-auto :top-left base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 0))
|
||||
(t/is (= (:y overlay-pos) 0))))
|
||||
|
||||
(t/testing "Overlay top-center relative to auto"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-auto :top-center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 35))
|
||||
(t/is (= (:y overlay-pos) 0))))
|
||||
|
||||
(t/testing "Overlay top-right relative to auto"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-auto :top-right base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 70))
|
||||
(t/is (= (:y overlay-pos) 0))))
|
||||
|
||||
(t/testing "Overlay bottom-left relative to auto"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-left base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 0))
|
||||
(t/is (= (:y overlay-pos) 80))))
|
||||
|
||||
(t/testing "Overlay bottom-center relative to auto"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 35))
|
||||
(t/is (= (:y overlay-pos) 80))))
|
||||
|
||||
(t/testing "Overlay bottom-right relative to auto"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-right base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 70))
|
||||
(t/is (= (:y overlay-pos) 80))))
|
||||
|
||||
(t/testing "Overlay center relative to auto"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-auto :center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 35))
|
||||
(t/is (= (:y overlay-pos) 40))))
|
||||
|
||||
(t/testing "Overlay manual relative to auto"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-auto :center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 35))
|
||||
(t/is (= (:y overlay-pos) 40))))
|
||||
|
||||
(t/testing "Overlay manual relative to auto"
|
||||
(let [i2 (-> interaction-auto
|
||||
(ctsi/set-overlay-pos-type :manual base-frame objects)
|
||||
(ctsi/set-overlay-position (gpt/point 12 62)))
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 17))
|
||||
(t/is (= (:y overlay-pos) 67))))
|
||||
|
||||
(t/testing "Overlay top-left relative to base-frame"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-left base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 5))
|
||||
(t/is (= (:y overlay-pos) 5))))
|
||||
|
||||
(t/testing "Overlay top-center relative to base-frame"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 40))
|
||||
(t/is (= (:y overlay-pos) 5))))
|
||||
|
||||
(t/testing "Overlay top-right relative to base-frame"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-right base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 75))
|
||||
(t/is (= (:y overlay-pos) 5))))
|
||||
|
||||
(t/testing "Overlay bottom-left relative to base-frame"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-left base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 5))
|
||||
(t/is (= (:y overlay-pos) 85))))
|
||||
|
||||
(t/testing "Overlay bottom-center relative to base-frame"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 40))
|
||||
(t/is (= (:y overlay-pos) 85))))
|
||||
|
||||
(t/testing "Overlay bottom-right relative to base-frame"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-right base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 75))
|
||||
(t/is (= (:y overlay-pos) 85))))
|
||||
|
||||
(t/testing "Overlay center relative to base-frame"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 40))
|
||||
(t/is (= (:y overlay-pos) 45))))
|
||||
|
||||
(t/testing "Overlay manual relative to base-frame"
|
||||
(let [i2 (-> interaction-base-frame
|
||||
(ctsi/set-overlay-pos-type :manual base-frame objects)
|
||||
(ctsi/set-overlay-position (gpt/point 12 62)))
|
||||
overlay-pos (ctsi/calc-overlay-position i2 base-frame base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 17))
|
||||
(t/is (= (:y overlay-pos) 67))))
|
||||
|
||||
(t/testing "Overlay top-left relative to popup"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-left base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 popup base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 15))
|
||||
(t/is (= (:y overlay-pos) 15))))
|
||||
|
||||
(t/testing "Overlay top-center relative to popup"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 popup base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 25))
|
||||
(t/is (= (:y overlay-pos) 15))))
|
||||
|
||||
(t/testing "Overlay top-right relative to popup"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-right base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 popup base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 35))
|
||||
(t/is (= (:y overlay-pos) 15))))
|
||||
|
||||
(t/testing "Overlay bottom-left relative to popup"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-left base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 popup base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 15))
|
||||
(t/is (= (:y overlay-pos) 45))))
|
||||
|
||||
(t/testing "Overlay bottom-center relative to popup"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 popup base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 25))
|
||||
(t/is (= (:y overlay-pos) 45))))
|
||||
|
||||
(t/testing "Overlay bottom-right relative to popup"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-right base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 popup base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 35))
|
||||
(t/is (= (:y overlay-pos) 45))))
|
||||
|
||||
(t/testing "Overlay center relative to popup"
|
||||
(let [i2 (ctsi/set-overlay-pos-type interaction-popup :center base-frame objects)
|
||||
overlay-pos (ctsi/calc-overlay-position i2 popup base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 25))
|
||||
(t/is (= (:y overlay-pos) 30))))
|
||||
|
||||
(t/testing "Overlay manual relative to popup"
|
||||
(let [i2 (-> interaction-popup
|
||||
(ctsi/set-overlay-pos-type :manual base-frame objects)
|
||||
(ctsi/set-overlay-position (gpt/point 12 62)))
|
||||
overlay-pos (ctsi/calc-overlay-position i2 popup base-frame overlay-frame frame-offset)]
|
||||
(t/is (= (:x overlay-pos) 27))
|
||||
(t/is (= (:y overlay-pos) 77))))))
|
||||
|
||||
|
||||
(t/deftest animation-checks
|
||||
|
@ -409,11 +595,11 @@
|
|||
:easing :ease-out
|
||||
:direction :left})]
|
||||
(t/is (not (ctsi/allowed-animation? (:action-type bad-interaction-1)
|
||||
(-> bad-interaction-1 :animation :animation-type))))
|
||||
(-> bad-interaction-1 :animation :animation-type))))
|
||||
(t/is (not (ctsi/allowed-animation? (:action-type bad-interaction-2)
|
||||
(-> bad-interaction-1 :animation :animation-type))))
|
||||
(-> bad-interaction-1 :animation :animation-type))))
|
||||
(t/is (not (ctsi/allowed-animation? (:action-type bad-interaction-3)
|
||||
(-> bad-interaction-1 :animation :animation-type))))))
|
||||
(-> bad-interaction-1 :animation :animation-type))))))
|
||||
|
||||
(t/testing "Remove animation if moving to an forbidden state"
|
||||
(let [interaction (ctsi/set-animation-type ctsi/default-interaction :push)
|
||||
|
@ -425,26 +611,26 @@
|
|||
(let [i1 ctsi/default-interaction
|
||||
i2 (ctsi/set-animation-type ctsi/default-interaction :dissolve)]
|
||||
|
||||
(t/testing "Has duration?"
|
||||
(t/is (not (ctsi/has-duration? i1)))
|
||||
(t/is (ctsi/has-duration? i2)))
|
||||
(t/testing "Has duration?"
|
||||
(t/is (not (ctsi/has-duration? i1)))
|
||||
(t/is (ctsi/has-duration? i2)))
|
||||
|
||||
(t/testing "Set duration"
|
||||
(let [new-interaction (ctsi/set-duration i2 1000)]
|
||||
(t/is (= 1000 (-> new-interaction :animation :duration)))))))
|
||||
(t/testing "Set duration"
|
||||
(let [new-interaction (ctsi/set-duration i2 1000)]
|
||||
(t/is (= 1000 (-> new-interaction :animation :duration)))))))
|
||||
|
||||
|
||||
(t/deftest option-easing
|
||||
(let [i1 ctsi/default-interaction
|
||||
i2 (ctsi/set-animation-type ctsi/default-interaction :dissolve)]
|
||||
|
||||
(t/testing "Has easing?"
|
||||
(t/is (not (ctsi/has-easing? i1)))
|
||||
(t/is (ctsi/has-easing? i2)))
|
||||
(t/testing "Has easing?"
|
||||
(t/is (not (ctsi/has-easing? i1)))
|
||||
(t/is (ctsi/has-easing? i2)))
|
||||
|
||||
(t/testing "Set easing"
|
||||
(let [new-interaction (ctsi/set-easing i2 :ease-in)]
|
||||
(t/is (= :ease-in (-> new-interaction :animation :easing)))))))
|
||||
(t/testing "Set easing"
|
||||
(let [new-interaction (ctsi/set-easing i2 :ease-in)]
|
||||
(t/is (= :ease-in (-> new-interaction :animation :easing)))))))
|
||||
|
||||
|
||||
(t/deftest option-way
|
||||
|
@ -452,15 +638,15 @@
|
|||
i2 (ctsi/set-animation-type ctsi/default-interaction :slide)
|
||||
i3 (ctsi/set-action-type i2 :open-overlay)]
|
||||
|
||||
(t/testing "Has way?"
|
||||
(t/is (not (ctsi/has-way? i1)))
|
||||
(t/is (ctsi/has-way? i2))
|
||||
(t/is (not (ctsi/has-way? i3)))
|
||||
(t/is (some? (-> i3 :animation :way)))) ; <- it exists but is ignored
|
||||
(t/testing "Has way?"
|
||||
(t/is (not (ctsi/has-way? i1)))
|
||||
(t/is (ctsi/has-way? i2))
|
||||
(t/is (not (ctsi/has-way? i3)))
|
||||
(t/is (some? (-> i3 :animation :way)))) ; <- it exists but is ignored
|
||||
|
||||
(t/testing "Set way"
|
||||
(let [new-interaction (ctsi/set-way i2 :out)]
|
||||
(t/is (= :out (-> new-interaction :animation :way)))))))
|
||||
(t/testing "Set way"
|
||||
(let [new-interaction (ctsi/set-way i2 :out)]
|
||||
(t/is (= :out (-> new-interaction :animation :way)))))))
|
||||
|
||||
|
||||
(t/deftest option-direction
|
||||
|
@ -468,49 +654,49 @@
|
|||
i2 (ctsi/set-animation-type ctsi/default-interaction :push)
|
||||
i3 (ctsi/set-animation-type ctsi/default-interaction :dissolve)]
|
||||
|
||||
(t/testing "Has direction?"
|
||||
(t/is (not (ctsi/has-direction? i1)))
|
||||
(t/is (ctsi/has-direction? i2)))
|
||||
(t/testing "Has direction?"
|
||||
(t/is (not (ctsi/has-direction? i1)))
|
||||
(t/is (ctsi/has-direction? i2)))
|
||||
|
||||
(t/testing "Set direction"
|
||||
(let [new-interaction (ctsi/set-direction i2 :left)]
|
||||
(t/is (= :left (-> new-interaction :animation :direction)))))
|
||||
(t/testing "Set direction"
|
||||
(let [new-interaction (ctsi/set-direction i2 :left)]
|
||||
(t/is (= :left (-> new-interaction :animation :direction)))))
|
||||
|
||||
(t/testing "Invert direction"
|
||||
(let [a-none (:animation i3)
|
||||
a-right (:animation i2)
|
||||
a-left (assoc a-right :direction :left)
|
||||
a-up (assoc a-right :direction :up)
|
||||
a-down (assoc a-right :direction :down)
|
||||
(t/testing "Invert direction"
|
||||
(let [a-none (:animation i3)
|
||||
a-right (:animation i2)
|
||||
a-left (assoc a-right :direction :left)
|
||||
a-up (assoc a-right :direction :up)
|
||||
a-down (assoc a-right :direction :down)
|
||||
|
||||
a-nil' (ctsi/invert-direction nil)
|
||||
a-none' (ctsi/invert-direction a-none)
|
||||
a-right' (ctsi/invert-direction a-right)
|
||||
a-left' (ctsi/invert-direction a-left)
|
||||
a-up' (ctsi/invert-direction a-up)
|
||||
a-down' (ctsi/invert-direction a-down)]
|
||||
a-nil' (ctsi/invert-direction nil)
|
||||
a-none' (ctsi/invert-direction a-none)
|
||||
a-right' (ctsi/invert-direction a-right)
|
||||
a-left' (ctsi/invert-direction a-left)
|
||||
a-up' (ctsi/invert-direction a-up)
|
||||
a-down' (ctsi/invert-direction a-down)]
|
||||
|
||||
(t/is (nil? a-nil'))
|
||||
(t/is (nil? (:direction a-none')))
|
||||
(t/is (= :left (:direction a-right')))
|
||||
(t/is (= :right (:direction a-left')))
|
||||
(t/is (= :down (:direction a-up')))
|
||||
(t/is (= :up (:direction a-down')))))))
|
||||
(t/is (nil? a-nil'))
|
||||
(t/is (nil? (:direction a-none')))
|
||||
(t/is (= :left (:direction a-right')))
|
||||
(t/is (= :right (:direction a-left')))
|
||||
(t/is (= :down (:direction a-up')))
|
||||
(t/is (= :up (:direction a-down')))))))
|
||||
|
||||
(t/deftest option-offset-effect
|
||||
(let [i1 ctsi/default-interaction
|
||||
i2 (ctsi/set-animation-type ctsi/default-interaction :slide)
|
||||
i3 (ctsi/set-action-type i2 :open-overlay)]
|
||||
|
||||
(t/testing "Has offset-effect"
|
||||
(t/is (not (ctsi/has-offset-effect? i1)))
|
||||
(t/is (ctsi/has-offset-effect? i2))
|
||||
(t/is (not (ctsi/has-offset-effect? i3)))
|
||||
(t/is (some? (-> i3 :animation :offset-effect)))) ; <- it exists but is ignored
|
||||
(t/testing "Has offset-effect"
|
||||
(t/is (not (ctsi/has-offset-effect? i1)))
|
||||
(t/is (ctsi/has-offset-effect? i2))
|
||||
(t/is (not (ctsi/has-offset-effect? i3)))
|
||||
(t/is (some? (-> i3 :animation :offset-effect)))) ; <- it exists but is ignored
|
||||
|
||||
(t/testing "Set offset-effect"
|
||||
(let [new-interaction (ctsi/set-offset-effect i2 true)]
|
||||
(t/is (= true (-> new-interaction :animation :offset-effect)))))))
|
||||
(t/testing "Set offset-effect"
|
||||
(let [new-interaction (ctsi/set-offset-effect i2 true)]
|
||||
(t/is (= true (-> new-interaction :animation :offset-effect)))))))
|
||||
|
||||
|
||||
(t/deftest modify-interactions
|
||||
|
|
|
@ -121,9 +121,9 @@
|
|||
(rx/concat
|
||||
(rx/of (dch/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(let [new-interaction (ctsi/set-destination
|
||||
ctsi/default-interaction
|
||||
destination)]
|
||||
(let [new-interaction (-> ctsi/default-interaction
|
||||
(ctsi/set-destination destination)
|
||||
(assoc :position-relative-to (:id shape)))]
|
||||
(update shape :interactions
|
||||
ctsi/add-interaction new-interaction)))))
|
||||
(when (and (not (connected-frame? objects (:id frame)))
|
||||
|
|
|
@ -388,6 +388,9 @@
|
|||
(def viewer-local
|
||||
(l/derived :viewer-local st/state))
|
||||
|
||||
(def viewer-overlays
|
||||
(l/derived :viewer-overlays st/state))
|
||||
|
||||
(def comment-threads
|
||||
(l/derived :comment-threads st/state))
|
||||
|
||||
|
|
|
@ -36,8 +36,16 @@
|
|||
(def viewer-interactions-show?
|
||||
(l/derived :interactions-show? refs/viewer-local))
|
||||
|
||||
(defn- find-relative-to-base-frame
|
||||
[shape objects overlays-ids base-frame]
|
||||
(if (or (empty? overlays-ids) (nil? shape) (cph/root? shape))
|
||||
base-frame
|
||||
(if (contains? overlays-ids (:id shape))
|
||||
shape
|
||||
(find-relative-to-base-frame (cph/get-parent objects (:id shape)) objects overlays-ids base-frame))))
|
||||
|
||||
(defn- activate-interaction
|
||||
[interaction shape base-frame frame-offset objects]
|
||||
[interaction shape base-frame frame-offset objects overlays]
|
||||
(case (:action-type interaction)
|
||||
:navigate
|
||||
(when-let [frame-id (:destination interaction)]
|
||||
|
@ -49,15 +57,21 @@
|
|||
(dv/go-to-frame frame-id (:animation interaction)))))
|
||||
|
||||
:open-overlay
|
||||
(let [dest-frame-id (:destination interaction)
|
||||
close-click-outside (:close-click-outside interaction)
|
||||
background-overlay (:background-overlay interaction)
|
||||
|
||||
dest-frame (get objects dest-frame-id)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
base-frame
|
||||
dest-frame
|
||||
frame-offset)]
|
||||
(let [dest-frame-id (:destination interaction)
|
||||
dest-frame (get objects dest-frame-id)
|
||||
relative-to-id (if (= :manual (:overlay-pos-type interaction))
|
||||
(:id shape) ;; manual interactions are allways from "self"
|
||||
(:position-relative-to interaction))
|
||||
relative-to-shape (or (get objects relative-to-id) base-frame)
|
||||
close-click-outside (:close-click-outside interaction)
|
||||
background-overlay (:background-overlay interaction)
|
||||
overlays-ids (set (map :id overlays))
|
||||
relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
relative-to-shape
|
||||
relative-to-base-frame
|
||||
dest-frame
|
||||
frame-offset)]
|
||||
(when dest-frame-id
|
||||
(st/emit! (dv/open-overlay dest-frame-id
|
||||
position
|
||||
|
@ -66,14 +80,22 @@
|
|||
(:animation interaction)))))
|
||||
|
||||
:toggle-overlay
|
||||
(let [frame-id (:destination interaction)
|
||||
dest-frame (get objects frame-id)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
base-frame
|
||||
dest-frame
|
||||
frame-offset)
|
||||
close-click-outside (:close-click-outside interaction)
|
||||
background-overlay (:background-overlay interaction)]
|
||||
(let [frame-id (:destination interaction)
|
||||
dest-frame (get objects frame-id)
|
||||
relative-to-id (if (= :manual (:overlay-pos-type interaction))
|
||||
(:id shape) ;; manual interactions are allways from "self"
|
||||
(:position-relative-to interaction))
|
||||
relative-to-shape (or (get objects relative-to-id) base-frame)
|
||||
overlays-ids (set (map :id overlays))
|
||||
relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
relative-to-shape
|
||||
relative-to-base-frame
|
||||
dest-frame
|
||||
frame-offset)
|
||||
|
||||
close-click-outside (:close-click-outside interaction)
|
||||
background-overlay (:background-overlay interaction)]
|
||||
(when frame-id
|
||||
(st/emit! (dv/toggle-overlay frame-id
|
||||
position
|
||||
|
@ -98,7 +120,7 @@
|
|||
|
||||
;; Perform the opposite action of an interaction, if possible
|
||||
(defn- deactivate-interaction
|
||||
[interaction shape base-frame frame-offset objects]
|
||||
[interaction shape base-frame frame-offset objects overlays]
|
||||
(case (:action-type interaction)
|
||||
:open-overlay
|
||||
(let [frame-id (or (:destination interaction)
|
||||
|
@ -120,15 +142,21 @@
|
|||
(:animation interaction)))))
|
||||
|
||||
:close-overlay
|
||||
(let [dest-frame-id (:destination interaction)
|
||||
close-click-outside (:close-click-outside interaction)
|
||||
background-overlay (:background-overlay interaction)
|
||||
|
||||
dest-frame (get objects dest-frame-id)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
base-frame
|
||||
dest-frame
|
||||
frame-offset)]
|
||||
(let [dest-frame-id (:destination interaction)
|
||||
dest-frame (get objects dest-frame-id)
|
||||
relative-to-id (if (= :manual (:overlay-pos-type interaction))
|
||||
(:id shape) ;; manual interactions are allways from "self"
|
||||
(:position-relative-to interaction))
|
||||
relative-to-shape (or (get objects relative-to-id) base-frame)
|
||||
close-click-outside (:close-click-outside interaction)
|
||||
background-overlay (:background-overlay interaction)
|
||||
overlays-ids (set (map :id overlays))
|
||||
relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame)
|
||||
position (ctsi/calc-overlay-position interaction
|
||||
relative-to-shape
|
||||
relative-to-base-frame
|
||||
dest-frame
|
||||
frame-offset)]
|
||||
(when dest-frame-id
|
||||
(st/emit! (dv/open-overlay dest-frame-id
|
||||
position
|
||||
|
@ -138,36 +166,36 @@
|
|||
nil))
|
||||
|
||||
(defn- on-mouse-down
|
||||
[event shape base-frame frame-offset objects]
|
||||
[event shape base-frame frame-offset objects overlays]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(or (= (:event-type %) :click)
|
||||
(= (:event-type %) :mouse-press))))]
|
||||
(when (seq interactions)
|
||||
(dom/stop-propagation event)
|
||||
(doseq [interaction interactions]
|
||||
(activate-interaction interaction shape base-frame frame-offset objects)))))
|
||||
(activate-interaction interaction shape base-frame frame-offset objects overlays)))))
|
||||
|
||||
(defn- on-mouse-up
|
||||
[event shape base-frame frame-offset objects]
|
||||
[event shape base-frame frame-offset objects overlays]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(= (:event-type %) :mouse-press)))]
|
||||
(when (seq interactions)
|
||||
(dom/stop-propagation event)
|
||||
(doseq [interaction interactions]
|
||||
(deactivate-interaction interaction shape base-frame frame-offset objects)))))
|
||||
(deactivate-interaction interaction shape base-frame frame-offset objects overlays)))))
|
||||
|
||||
(defn- on-mouse-enter
|
||||
[event shape base-frame frame-offset objects]
|
||||
[event shape base-frame frame-offset objects overlays]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(or (= (:event-type %) :mouse-enter)
|
||||
(= (:event-type %) :mouse-over))))]
|
||||
(when (seq interactions)
|
||||
(dom/stop-propagation event)
|
||||
(doseq [interaction interactions]
|
||||
(activate-interaction interaction shape base-frame frame-offset objects)))))
|
||||
(activate-interaction interaction shape base-frame frame-offset objects overlays)))))
|
||||
|
||||
(defn- on-mouse-leave
|
||||
[event shape base-frame frame-offset objects]
|
||||
[event shape base-frame frame-offset objects overlays]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(= (:event-type %) :mouse-leave)))
|
||||
interactions-inv (->> (:interactions shape)
|
||||
|
@ -175,19 +203,19 @@
|
|||
(when (or (seq interactions) (seq interactions-inv))
|
||||
(dom/stop-propagation event)
|
||||
(doseq [interaction interactions]
|
||||
(activate-interaction interaction shape base-frame frame-offset objects))
|
||||
(activate-interaction interaction shape base-frame frame-offset objects overlays))
|
||||
(doseq [interaction interactions-inv]
|
||||
(deactivate-interaction interaction shape base-frame frame-offset objects)))))
|
||||
(deactivate-interaction interaction shape base-frame frame-offset objects overlays)))))
|
||||
|
||||
(defn- on-load
|
||||
[shape base-frame frame-offset objects]
|
||||
[shape base-frame frame-offset objects overlays]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(= (:event-type %) :after-delay)))]
|
||||
(loop [interactions (seq interactions)
|
||||
sems []]
|
||||
(if-let [interaction (first interactions)]
|
||||
(let [sem (tm/schedule (:delay interaction)
|
||||
#(activate-interaction interaction shape base-frame frame-offset objects))]
|
||||
#(activate-interaction interaction shape base-frame frame-offset objects overlays))]
|
||||
(recur (next interactions)
|
||||
(conj sems sem)))
|
||||
sems))))
|
||||
|
@ -209,73 +237,71 @@
|
|||
:transform (gsh/transform-str shape)}])))
|
||||
|
||||
|
||||
|
||||
;; TODO: use-memo use-fn
|
||||
|
||||
(defn generic-wrapper-factory
|
||||
"Wrap some svg shape and add interaction controls"
|
||||
[component]
|
||||
(mf/fnc generic-wrapper
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (unchecked-get props "childs")
|
||||
frame (unchecked-get props "frame")
|
||||
objects (unchecked-get props "objects")
|
||||
base-frame (mf/use-ctx base-frame-ctx)
|
||||
frame-offset (mf/use-ctx frame-offset-ctx)
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (unchecked-get props "childs")
|
||||
frame (unchecked-get props "frame")
|
||||
objects (unchecked-get props "objects")
|
||||
base-frame (mf/use-ctx base-frame-ctx)
|
||||
frame-offset (mf/use-ctx frame-offset-ctx)
|
||||
interactions-show? (mf/deref viewer-interactions-show?)
|
||||
overlays (mf/deref refs/viewer-overlays)
|
||||
interactions (:interactions shape)
|
||||
svg-element? (and (= :svg-raw (:type shape))
|
||||
(not= :svg (get-in shape [:content :tag])))
|
||||
|
||||
interactions-show? (mf/deref viewer-interactions-show?)
|
||||
on-mouse-down
|
||||
(mf/use-fn (mf/deps shape base-frame frame-offset objects)
|
||||
#(on-mouse-down % shape base-frame frame-offset objects overlays))
|
||||
|
||||
interactions (:interactions shape)
|
||||
on-mouse-up
|
||||
(mf/use-fn (mf/deps shape base-frame frame-offset objects)
|
||||
#(on-mouse-up % shape base-frame frame-offset objects overlays))
|
||||
|
||||
svg-element? (and (= :svg-raw (:type shape))
|
||||
(not= :svg (get-in shape [:content :tag])))
|
||||
on-mouse-enter
|
||||
(mf/use-fn (mf/deps shape base-frame frame-offset objects)
|
||||
#(on-mouse-enter % shape base-frame frame-offset objects overlays))
|
||||
|
||||
on-mouse-leave
|
||||
(mf/use-fn (mf/deps shape base-frame frame-offset objects)
|
||||
#(on-mouse-leave % shape base-frame frame-offset objects overlays))]
|
||||
|
||||
|
||||
on-mouse-down
|
||||
(mf/use-fn (mf/deps shape base-frame frame-offset objects)
|
||||
#(on-mouse-down % shape base-frame frame-offset objects))
|
||||
(mf/with-effect []
|
||||
(let [sems (on-load shape base-frame frame-offset objects overlays)]
|
||||
(partial run! tm/dispose! sems)))
|
||||
|
||||
on-mouse-up
|
||||
(mf/use-fn (mf/deps shape base-frame frame-offset objects)
|
||||
#(on-mouse-up % shape base-frame frame-offset objects))
|
||||
(if-not svg-element?
|
||||
[:> shape-container {:shape shape
|
||||
:cursor (when (ctsi/actionable? interactions) "pointer")
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-mouse-up on-mouse-up
|
||||
:on-mouse-enter on-mouse-enter
|
||||
:on-mouse-leave on-mouse-leave}
|
||||
|
||||
on-mouse-enter
|
||||
(mf/use-fn (mf/deps shape base-frame frame-offset objects)
|
||||
#(on-mouse-enter % shape base-frame frame-offset objects))
|
||||
[:& component {:shape shape
|
||||
:frame frame
|
||||
:childs childs
|
||||
:is-child-selected? true
|
||||
:objects objects}]
|
||||
|
||||
on-mouse-leave
|
||||
(mf/use-fn (mf/deps shape base-frame frame-offset objects)
|
||||
#(on-mouse-leave % shape base-frame frame-offset objects))]
|
||||
|
||||
|
||||
(mf/with-effect []
|
||||
(let [sems (on-load shape base-frame frame-offset objects)]
|
||||
(partial run! tm/dispose! sems)))
|
||||
|
||||
(if-not svg-element?
|
||||
[:> shape-container {:shape shape
|
||||
:cursor (when (ctsi/actionable? interactions) "pointer")
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-mouse-up on-mouse-up
|
||||
:on-mouse-enter on-mouse-enter
|
||||
:on-mouse-leave on-mouse-leave}
|
||||
|
||||
[:& component {:shape shape
|
||||
:frame frame
|
||||
:childs childs
|
||||
:is-child-selected? true
|
||||
:objects objects}]
|
||||
|
||||
[:& interaction {:shape shape
|
||||
:interactions interactions
|
||||
:interactions-show? interactions-show?}]]
|
||||
[:& interaction {:shape shape
|
||||
:interactions interactions
|
||||
:interactions-show? interactions-show?}]]
|
||||
|
||||
;; Don't wrap svg elements inside a <g> otherwise some can break
|
||||
[:& component {:shape shape
|
||||
:frame frame
|
||||
:childs childs
|
||||
:objects objects}]))))
|
||||
[:& component {:shape shape
|
||||
:frame frame
|
||||
:childs childs
|
||||
:objects objects}]))))
|
||||
|
||||
(defn frame-wrapper
|
||||
[shape-container]
|
||||
|
@ -320,58 +346,58 @@
|
|||
(let [shape-container (shape-container-factory objects)
|
||||
frame-wrapper (frame-wrapper shape-container)]
|
||||
(mf/fnc frame-container
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (obj/get props "shape")
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
shape (gsh/transform-shape shape)
|
||||
props (obj/merge! #js {} props
|
||||
#js {:shape shape
|
||||
:childs childs
|
||||
:objects objects})]
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (obj/get props "shape")
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
shape (gsh/transform-shape shape)
|
||||
props (obj/merge! #js {} props
|
||||
#js {:shape shape
|
||||
:childs childs
|
||||
:objects objects})]
|
||||
|
||||
[:> frame-wrapper props]))))
|
||||
[:> frame-wrapper props]))))
|
||||
|
||||
(defn group-container-factory
|
||||
[objects]
|
||||
(let [shape-container (shape-container-factory objects)
|
||||
group-wrapper (group-wrapper shape-container)]
|
||||
(mf/fnc group-container
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [childs (mapv #(get objects %) (:shapes (unchecked-get props "shape")))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
(when (not-empty childs)
|
||||
[:> group-wrapper props])))))
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [childs (mapv #(get objects %) (:shapes (unchecked-get props "shape")))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
(when (not-empty childs)
|
||||
[:> group-wrapper props])))))
|
||||
|
||||
(defn bool-container-factory
|
||||
[objects]
|
||||
(let [shape-container (shape-container-factory objects)
|
||||
bool-wrapper (bool-wrapper shape-container)]
|
||||
(mf/fnc bool-container
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [childs (->> (cph/get-children-ids objects (:id (unchecked-get props "shape")))
|
||||
(select-keys objects))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
[:> bool-wrapper props]))))
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [childs (->> (cph/get-children-ids objects (:id (unchecked-get props "shape")))
|
||||
(select-keys objects))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
[:> bool-wrapper props]))))
|
||||
|
||||
(defn svg-raw-container-factory
|
||||
[objects]
|
||||
(let [shape-container (shape-container-factory objects)
|
||||
svg-raw-wrapper (svg-raw-wrapper shape-container)]
|
||||
(mf/fnc svg-raw-container
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [childs (mapv #(get objects %) (:shapes (unchecked-get props "shape")))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
[:> svg-raw-wrapper props]))))
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [childs (mapv #(get objects %) (:shapes (unchecked-get props "shape")))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
[:> svg-raw-wrapper props]))))
|
||||
|
||||
(defn shape-container-factory
|
||||
[objects]
|
||||
|
@ -381,42 +407,40 @@
|
|||
image-wrapper (image-wrapper)
|
||||
circle-wrapper (circle-wrapper)]
|
||||
(mf/fnc shape-container
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")
|
||||
|
||||
group-container
|
||||
(mf/with-memo [objects]
|
||||
(group-container-factory objects))
|
||||
group-container
|
||||
(mf/with-memo [objects]
|
||||
(group-container-factory objects))
|
||||
|
||||
frame-container
|
||||
(mf/with-memo [objects]
|
||||
(frame-container-factory objects))
|
||||
frame-container
|
||||
(mf/with-memo [objects]
|
||||
(frame-container-factory objects))
|
||||
|
||||
bool-container
|
||||
(mf/with-memo [objects]
|
||||
(bool-container-factory objects))
|
||||
bool-container
|
||||
(mf/with-memo [objects]
|
||||
(bool-container-factory objects))
|
||||
|
||||
svg-raw-container
|
||||
(mf/with-memo [objects]
|
||||
(svg-raw-container-factory objects))
|
||||
svg-raw-container
|
||||
(mf/with-memo [objects]
|
||||
(svg-raw-container-factory objects))]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(let [shape (-> (gsh/transform-shape shape)
|
||||
(gsh/translate-to-frame frame))
|
||||
|
||||
]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(let [shape (-> (gsh/transform-shape shape)
|
||||
(gsh/translate-to-frame frame))
|
||||
|
||||
opts #js {:shape shape
|
||||
:objects objects}]
|
||||
(case (:type shape)
|
||||
:frame [:> frame-container opts]
|
||||
:text [:> text-wrapper opts]
|
||||
:rect [:> rect-wrapper opts]
|
||||
:path [:> path-wrapper opts]
|
||||
:image [:> image-wrapper opts]
|
||||
:circle [:> circle-wrapper opts]
|
||||
:group [:> group-container {:shape shape :frame frame :objects objects}]
|
||||
:bool [:> bool-container {:shape shape :frame frame :objects objects}]
|
||||
:svg-raw [:> svg-raw-container {:shape shape :frame frame :objects objects}])))))))
|
||||
opts #js {:shape shape
|
||||
:objects objects}]
|
||||
(case (:type shape)
|
||||
:frame [:> frame-container opts]
|
||||
:text [:> text-wrapper opts]
|
||||
:rect [:> rect-wrapper opts]
|
||||
:path [:> path-wrapper opts]
|
||||
:image [:> image-wrapper opts]
|
||||
:circle [:> circle-wrapper opts]
|
||||
:group [:> group-container {:shape shape :frame frame :objects objects}]
|
||||
:bool [:> bool-container {:shape shape :frame frame :objects objects}]
|
||||
:svg-raw [:> svg-raw-container {:shape shape :frame frame :objects objects}])))))))
|
||||
|
|
|
@ -184,6 +184,8 @@
|
|||
destination (get objects (:destination interaction))
|
||||
|
||||
frames (mf/with-memo [objects] (ctt/get-viewer-frames objects {:all-frames? true}))
|
||||
shape-parent-ids (mf/with-memo [objects] (cph/get-parent-ids objects (:id shape)))
|
||||
shape-parents (mf/with-memo [frames shape] (filter (comp (set shape-parent-ids) :id) frames))
|
||||
|
||||
overlay-pos-type (:overlay-pos-type interaction)
|
||||
close-click-outside? (:close-click-outside interaction false)
|
||||
|
@ -220,6 +222,14 @@
|
|||
value (when (not= value "") (uuid/uuid value))]
|
||||
(update-interaction index #(ctsi/set-destination % value))))
|
||||
|
||||
change-position-relative-to
|
||||
(fn [event]
|
||||
(let [value (-> event
|
||||
dom/get-target
|
||||
dom/get-value
|
||||
uuid/uuid)]
|
||||
(update-interaction index #(ctsi/set-position-relative-to % value))))
|
||||
|
||||
change-preserve-scroll
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/checked?)]
|
||||
|
@ -243,9 +253,12 @@
|
|||
(dom/add-class! target "error"))))
|
||||
|
||||
change-overlay-pos-type
|
||||
(fn [event]
|
||||
(fn [shape-id event]
|
||||
(let [value (-> event dom/get-target dom/get-value d/read-string)]
|
||||
(update-interaction index #(ctsi/set-overlay-pos-type % value shape objects))))
|
||||
(update-interaction index #(ctsi/set-overlay-pos-type % value shape objects))
|
||||
(when (= value :manual)
|
||||
(update-interaction index #(ctsi/set-position-relative-to % shape-id)))))
|
||||
|
||||
|
||||
toggle-overlay-pos-type
|
||||
(fn [pos-type]
|
||||
|
@ -287,8 +300,7 @@
|
|||
change-offset-effect
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/checked?)]
|
||||
(update-interaction index #(ctsi/set-offset-effect % value))))
|
||||
]
|
||||
(update-interaction index #(ctsi/set-offset-effect % value))))]
|
||||
|
||||
[:*
|
||||
[:div.element-set-options-group {:class (dom/classnames
|
||||
|
@ -378,12 +390,27 @@
|
|||
|
||||
(when (ctsi/has-overlay-opts interaction)
|
||||
[:*
|
||||
; Overlay position relative-to (select)
|
||||
[:div.interactions-element
|
||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-relative-to")]
|
||||
[:select.input-select
|
||||
{:value (str (:position-relative-to interaction))
|
||||
:on-change change-position-relative-to}
|
||||
(when (not= (:overlay-pos-type interaction) :manual)
|
||||
[:*
|
||||
[:option {:value ""} (tr "workspace.options.interaction-auto")]
|
||||
(for [frame shape-parents]
|
||||
[:option {:key (dm/str "position-relative-to-" (:id frame))
|
||||
:value (str (:id frame))} (:name frame)])])
|
||||
[:option {:key (dm/str "position-relative-to-" (:id shape))
|
||||
:value (str (:id shape))} (:name shape) " (" (tr "workspace.options.interaction-self") ")"]]]
|
||||
|
||||
; Overlay position (select)
|
||||
[: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}
|
||||
:on-change (partial change-overlay-pos-type (:id shape))}
|
||||
(for [[value name] (overlay-pos-type-names)]
|
||||
[:option {:value (str value)} name])]]
|
||||
|
||||
|
|
|
@ -3222,6 +3222,9 @@ msgstr "Push"
|
|||
msgid "workspace.options.interaction-animation-slide"
|
||||
msgstr "Slide"
|
||||
|
||||
msgid "workspace.options.interaction-auto"
|
||||
msgstr "auto"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||
msgid "workspace.options.interaction-background"
|
||||
msgstr "Add background overlay"
|
||||
|
@ -3362,6 +3365,10 @@ msgstr "Top right"
|
|||
msgid "workspace.options.interaction-position"
|
||||
msgstr "Position"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||
msgid "workspace.options.interaction-relative-to"
|
||||
msgstr "Relative to"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||
msgid "workspace.options.interaction-preserve-scroll"
|
||||
msgstr "Preserve scroll position"
|
||||
|
|
|
@ -3621,6 +3621,9 @@ msgstr "Empujar"
|
|||
msgid "workspace.options.interaction-animation-slide"
|
||||
msgstr "Deslizar"
|
||||
|
||||
msgid "workspace.options.interaction-auto"
|
||||
msgstr "automático"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||
msgid "workspace.options.interaction-background"
|
||||
msgstr "Añadir sombreado de fondo"
|
||||
|
@ -3761,6 +3764,10 @@ msgstr "Arriba derecha"
|
|||
msgid "workspace.options.interaction-position"
|
||||
msgstr "Posición"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||
msgid "workspace.options.interaction-relative-to"
|
||||
msgstr "Relativo a"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||
msgid "workspace.options.interaction-preserve-scroll"
|
||||
msgstr "Conservar posición de desplazamiento"
|
||||
|
|
Loading…
Add table
Reference in a new issue