mirror of
https://github.com/penpot/penpot.git
synced 2025-03-12 07:41:43 -05:00
🎉 Add interaction flows
This commit is contained in:
parent
f3bb5c55f5
commit
0159eea526
22 changed files with 856 additions and 257 deletions
|
@ -159,6 +159,11 @@
|
||||||
([mfn coll]
|
([mfn coll]
|
||||||
(into {} (mapm mfn) coll)))
|
(into {} (mapm mfn) coll)))
|
||||||
|
|
||||||
|
(defn removev
|
||||||
|
"Returns a vector of the items in coll for which (fn item) returns logical false"
|
||||||
|
[fn coll]
|
||||||
|
(filterv (comp not fn) coll))
|
||||||
|
|
||||||
(defn filterm
|
(defn filterm
|
||||||
"Filter values of a map that satisfy a predicate"
|
"Filter values of a map that satisfy a predicate"
|
||||||
[pred coll]
|
[pred coll]
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.types.interactions :as cti]
|
[app.common.types.interactions :as cti]
|
||||||
|
[app.common.types.page-options :as cto]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[clojure.spec.alpha :as s]))
|
[clojure.spec.alpha :as s]))
|
||||||
|
@ -144,43 +145,6 @@
|
||||||
:internal.blur/value
|
:internal.blur/value
|
||||||
:internal.blur/hidden]))
|
:internal.blur/hidden]))
|
||||||
|
|
||||||
;; Page Options
|
|
||||||
(s/def :internal.page.grid.color/value string?)
|
|
||||||
(s/def :internal.page.grid.color/opacity ::us/safe-number)
|
|
||||||
|
|
||||||
(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 ::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
|
|
||||||
:internal.page.grid/color]))
|
|
||||||
|
|
||||||
(s/def :internal.page.grid/column
|
|
||||||
(s/keys :req-un [:internal.page.grid/size
|
|
||||||
:internal.page.grid/color
|
|
||||||
:internal.page.grid/type
|
|
||||||
:internal.page.grid/item-length
|
|
||||||
:internal.page.grid/gutter
|
|
||||||
:internal.page.grid/margin]))
|
|
||||||
|
|
||||||
(s/def :internal.page.grid/row :internal.page.grid/column)
|
|
||||||
|
|
||||||
(s/def :internal.page.options/background string?)
|
|
||||||
(s/def :internal.page.options/saved-grids
|
|
||||||
(s/keys :req-un [:internal.page.grid/square
|
|
||||||
:internal.page.grid/row
|
|
||||||
:internal.page.grid/column]))
|
|
||||||
|
|
||||||
(s/def :internal.page/options
|
|
||||||
(s/keys :opt-un [:internal.page.options/background]))
|
|
||||||
|
|
||||||
;; Size constraints
|
;; Size constraints
|
||||||
|
|
||||||
(s/def :internal.shape/constraints-h #{:left :right :leftright :center :scale})
|
(s/def :internal.shape/constraints-h #{:left :right :leftright :center :scale})
|
||||||
|
@ -370,7 +334,7 @@
|
||||||
(s/def ::page
|
(s/def ::page
|
||||||
(s/keys :req-un [::id
|
(s/keys :req-un [::id
|
||||||
::name
|
::name
|
||||||
:internal.page/options
|
::cto/options
|
||||||
:internal.page/objects]))
|
:internal.page/objects]))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,14 @@
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[clojure.spec.alpha :as s]))
|
[clojure.spec.alpha :as s]))
|
||||||
|
|
||||||
(s/def ::point
|
;; WARNING: options are not deleted when changing event or action type, so it can be
|
||||||
(s/and (s/keys :req-un [::x ::y])
|
;; restored if the user changes it back later.
|
||||||
gpt/point?))
|
;;
|
||||||
|
;; But that means that an interaction may have for example a delay or
|
||||||
|
;; destination, even if its type does not require it (but a previous type did).
|
||||||
|
;;
|
||||||
|
;; So make sure to use has-delay/has-destination... functions, or similar,
|
||||||
|
;; before reading them.
|
||||||
|
|
||||||
;; -- Options depending on event type
|
;; -- Options depending on event type
|
||||||
|
|
||||||
|
@ -54,7 +59,7 @@
|
||||||
:bottom-left
|
:bottom-left
|
||||||
:bottom-right
|
:bottom-right
|
||||||
:bottom-center})
|
:bottom-center})
|
||||||
(s/def ::overlay-position ::point)
|
(s/def ::overlay-position ::us/point)
|
||||||
(s/def ::url ::us/string)
|
(s/def ::url ::us/string)
|
||||||
(s/def ::close-click-outside ::us/boolean)
|
(s/def ::close-click-outside ::us/boolean)
|
||||||
(s/def ::background-overlay ::us/boolean)
|
(s/def ::background-overlay ::us/boolean)
|
||||||
|
|
94
common/src/app/common/types/page_options.cljc
Normal file
94
common/src/app/common/types/page_options.cljc
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
;; 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.page-options
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.spec :as us]
|
||||||
|
[clojure.spec.alpha :as s]))
|
||||||
|
|
||||||
|
;; --- Grid options
|
||||||
|
|
||||||
|
(s/def :artboard-grid.color/value ::us/string)
|
||||||
|
(s/def :artboard-grid.color/opacity ::us/safe-number)
|
||||||
|
|
||||||
|
(s/def :artboard-grid/size ::us/safe-integer)
|
||||||
|
(s/def :artboard-grid/color (s/keys :req-un [:artboard-grid.color/value
|
||||||
|
:artboard-grid.color/opacity]))
|
||||||
|
(s/def :artboard-grid/type #{:stretch :left :center :right})
|
||||||
|
(s/def :artboard-grid/item-length (s/nilable ::us/safe-integer))
|
||||||
|
(s/def :artboard-grid/gutter (s/nilable ::us/safe-integer))
|
||||||
|
(s/def :artboard-grid/margin (s/nilable ::us/safe-integer))
|
||||||
|
|
||||||
|
(s/def :artboard-grid/square
|
||||||
|
(s/keys :req-un [:artboard-grid/size
|
||||||
|
:artboard-grid/color]))
|
||||||
|
|
||||||
|
(s/def :artboard-grid/column
|
||||||
|
(s/keys :req-un [:artboard-grid/size
|
||||||
|
:artboard-grid/color
|
||||||
|
:artboard-grid/type
|
||||||
|
:artboard-grid/item-length
|
||||||
|
:artboard-grid/gutter
|
||||||
|
:artboard-grid/margin]))
|
||||||
|
|
||||||
|
(s/def :artboard-grid/row :artboard-grid/column)
|
||||||
|
|
||||||
|
(s/def ::saved-grids
|
||||||
|
(s/keys :req-un [:artboard-grid/square
|
||||||
|
:artboard-grid/row
|
||||||
|
:artboard-grid/column]))
|
||||||
|
|
||||||
|
;; --- Background options
|
||||||
|
|
||||||
|
(s/def ::background string?)
|
||||||
|
|
||||||
|
;; --- Flow options
|
||||||
|
|
||||||
|
(s/def :interactions-flow/id ::us/uuid)
|
||||||
|
(s/def :interactions-flow/name ::us/string)
|
||||||
|
(s/def :interactions-flow/starting-frame ::us/uuid)
|
||||||
|
|
||||||
|
(s/def ::flow
|
||||||
|
(s/keys :req-un [:interactions-flow/id
|
||||||
|
:interactions-flow/name
|
||||||
|
:interactions-flow/starting-frame]))
|
||||||
|
|
||||||
|
(s/def ::flows
|
||||||
|
(s/coll-of ::flow :kind vector?))
|
||||||
|
|
||||||
|
;; --- Options
|
||||||
|
|
||||||
|
(s/def ::options
|
||||||
|
(s/keys :opt-un [::background
|
||||||
|
::saved-grids
|
||||||
|
::flows]))
|
||||||
|
|
||||||
|
;; --- Helpers for flow
|
||||||
|
|
||||||
|
(defn rename-flow
|
||||||
|
[flow name]
|
||||||
|
(assoc flow :name name))
|
||||||
|
|
||||||
|
;; --- Helpers for flows
|
||||||
|
|
||||||
|
(defn add-flow
|
||||||
|
[flows flow]
|
||||||
|
(conj flows flow))
|
||||||
|
|
||||||
|
(defn remove-flow
|
||||||
|
[flows flow-id]
|
||||||
|
(vec (remove #(= (:id %) flow-id) flows)))
|
||||||
|
|
||||||
|
(defn update-flow
|
||||||
|
[flows flow-id update-fn]
|
||||||
|
(let [index (d/index-of-pred flows #(= (:id %) flow-id))]
|
||||||
|
(update flows index update-fn)))
|
||||||
|
|
||||||
|
(defn get-frame-flow
|
||||||
|
[flows frame-id]
|
||||||
|
(d/seek #(= (:starting-frame %) frame-id) flows))
|
||||||
|
|
|
@ -937,6 +937,10 @@
|
||||||
.element-set-options-group {
|
.element-set-options-group {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-top: 1px solid $color-gray-60;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.exports-options,
|
.exports-options,
|
||||||
|
|
|
@ -75,7 +75,84 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flow-element {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: $size-1;
|
||||||
|
|
||||||
|
.element-label {
|
||||||
|
font-size: $fs11;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-name {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
& input.element-name {
|
||||||
|
background: transparent;
|
||||||
|
border-color: $color-primary;
|
||||||
|
color: $color-white;
|
||||||
|
font-size: $fs11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-button {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: $size-2;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
fill: $color-gray-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover svg {
|
||||||
|
fill: $color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-badge {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& .content {
|
||||||
|
align-items: center;
|
||||||
|
background-color: $color-gray-50;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
height: 12px;
|
||||||
|
margin: 0 $size-2;
|
||||||
|
width: 12px;
|
||||||
|
fill: $color-gray-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
color: $color-gray-20;
|
||||||
|
font-size: $fs12;
|
||||||
|
margin-right: $size-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected .content {
|
||||||
|
background-color: $color-primary;
|
||||||
|
|
||||||
|
& svg {
|
||||||
|
fill: $color-gray-60;
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
color: $color-gray-60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 90px;
|
position: relative;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
color: $color-gray-10;
|
color: $color-gray-10;
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown {
|
.dropdown {
|
||||||
min-width: 260px;
|
min-width: 295px;
|
||||||
top: 45px;
|
top: 45px;
|
||||||
left: -25px;
|
left: -25px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,10 +238,6 @@
|
||||||
font-size: $fs12;
|
font-size: $fs12;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selected .workspace-frame-label {
|
|
||||||
fill: $color-primary-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
.multiuser-cursor {
|
.multiuser-cursor {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -111,6 +111,7 @@
|
||||||
(rx/of (df/fonts-fetched fonts)
|
(rx/of (df/fonts-fetched fonts)
|
||||||
(bundle-fetched (merge bundle params))))))))))
|
(bundle-fetched (merge bundle params))))))))))
|
||||||
|
|
||||||
|
(declare go-to-frame-auto)
|
||||||
|
|
||||||
(defn bundle-fetched
|
(defn bundle-fetched
|
||||||
[{:keys [project file share-links libraries users permissions] :as bundle}]
|
[{:keys [project file share-links libraries users permissions] :as bundle}]
|
||||||
|
@ -130,7 +131,15 @@
|
||||||
:permissions permissions
|
:permissions permissions
|
||||||
:project project
|
:project project
|
||||||
:pages pages
|
:pages pages
|
||||||
:file file}))))))
|
:file file})))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(let [route (:route state)
|
||||||
|
qparams (:query-params route)
|
||||||
|
index (:index qparams)]
|
||||||
|
(when (nil? index)
|
||||||
|
(rx/of (go-to-frame-auto))))))))
|
||||||
|
|
||||||
(defn fetch-comment-threads
|
(defn fetch-comment-threads
|
||||||
[{:keys [file-id page-id] :as params}]
|
[{:keys [file-id page-id] :as params}]
|
||||||
|
@ -329,6 +338,20 @@
|
||||||
(when index
|
(when index
|
||||||
(rx/of (go-to-frame-by-index index)))))))
|
(rx/of (go-to-frame-by-index index)))))))
|
||||||
|
|
||||||
|
(defn go-to-frame-auto
|
||||||
|
[]
|
||||||
|
(ptk/reify ::go-to-frame-auto
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(let [route (:route state)
|
||||||
|
qparams (:query-params route)
|
||||||
|
page-id (:page-id qparams)
|
||||||
|
flows (get-in state [:viewer :pages page-id :options :flows])]
|
||||||
|
(if (seq flows)
|
||||||
|
(let [frame-id (:starting-frame (first flows))]
|
||||||
|
(rx/of (go-to-frame frame-id)))
|
||||||
|
(rx/of (go-to-frame-by-index 0)))))))
|
||||||
|
|
||||||
(defn go-to-section
|
(defn go-to-section
|
||||||
[section]
|
[section]
|
||||||
(ptk/reify ::go-to-section
|
(ptk/reify ::go-to-section
|
||||||
|
@ -391,7 +414,7 @@
|
||||||
:background-overlay background-overlay})
|
:background-overlay background-overlay})
|
||||||
(update-in state [:viewer-local :overlays]
|
(update-in state [:viewer-local :overlays]
|
||||||
(fn [overlays]
|
(fn [overlays]
|
||||||
(remove #(= (:id (:frame %)) frame-id) overlays))))))))
|
(d/removev #(= (:id (:frame %)) frame-id) overlays))))))))
|
||||||
|
|
||||||
(defn close-overlay
|
(defn close-overlay
|
||||||
[frame-id]
|
[frame-id]
|
||||||
|
@ -400,7 +423,7 @@
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update-in state [:viewer-local :overlays]
|
(update-in state [:viewer-local :overlays]
|
||||||
(fn [overlays]
|
(fn [overlays]
|
||||||
(remove #(= (:id (:frame %)) frame-id) overlays))))))
|
(d/removev #(= (:id (:frame %)) frame-id) overlays))))))
|
||||||
|
|
||||||
;; --- Objects selection
|
;; --- Objects selection
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
[app.common.pages.spec :as spec]
|
[app.common.pages.spec :as spec]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.transit :as t]
|
[app.common.transit :as t]
|
||||||
[app.common.types.interactions :as cti]
|
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
|
@ -28,6 +27,7 @@
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.drawing :as dwd]
|
[app.main.data.workspace.drawing :as dwd]
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
|
[app.main.data.workspace.interactions :as dwi]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.notifications :as dwn]
|
[app.main.data.workspace.notifications :as dwn]
|
||||||
[app.main.data.workspace.path :as dwdp]
|
[app.main.data.workspace.path :as dwdp]
|
||||||
|
@ -1285,8 +1285,7 @@
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [{:keys [current-file-id current-page-id]} state
|
(let [{:keys [current-file-id current-page-id]} state
|
||||||
pparams {:file-id (or file-id current-file-id)}
|
pparams {:file-id (or file-id current-file-id)}
|
||||||
qparams {:page-id (or page-id current-page-id)
|
qparams {:page-id (or page-id current-page-id)}]
|
||||||
:index 0}]
|
|
||||||
(rx/of ::dwp/force-persist
|
(rx/of ::dwp/force-persist
|
||||||
(rt/nav-new-window* {:rname :viewer
|
(rt/nav-new-window* {:rname :viewer
|
||||||
:path-params pparams
|
:path-params pparams
|
||||||
|
@ -1754,166 +1753,12 @@
|
||||||
;; Interactions
|
;; Interactions
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(declare move-edit-interaction)
|
(d/export dwi/start-edit-interaction)
|
||||||
(declare finish-edit-interaction)
|
(d/export dwi/move-edit-interaction)
|
||||||
|
(d/export dwi/finish-edit-interaction)
|
||||||
(defn start-edit-interaction
|
(d/export dwi/start-move-overlay-pos)
|
||||||
[index]
|
(d/export dwi/move-overlay-pos)
|
||||||
(ptk/reify ::start-edit-interaction
|
(d/export dwi/finish-move-overlay-pos)
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(assoc-in state [:workspace-local :editing-interaction-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))
|
|
||||||
(rx/concat
|
|
||||||
(->> ms/mouse-position
|
|
||||||
(rx/take-until stopper)
|
|
||||||
(rx/map #(move-edit-interaction initial-pos %)))
|
|
||||||
(rx/of (finish-edit-interaction index initial-pos))))))))
|
|
||||||
|
|
||||||
(defn move-edit-interaction
|
|
||||||
[initial-pos position]
|
|
||||||
(ptk/reify ::move-edit-interaction
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
selected-shape-id (-> state wsh/lookup-selected first)
|
|
||||||
selected-shape (get objects selected-shape-id)
|
|
||||||
selected-shape-frame-id (:frame-id selected-shape)
|
|
||||||
start-frame (get objects selected-shape-frame-id)
|
|
||||||
end-frame (dwc/get-frame-at-point objects position)]
|
|
||||||
(cond-> state
|
|
||||||
(not= position initial-pos) (assoc-in [:workspace-local :draw-interaction-to] position)
|
|
||||||
(not= start-frame end-frame) (assoc-in [:workspace-local :draw-interaction-to-frame] end-frame))))))
|
|
||||||
|
|
||||||
(defn finish-edit-interaction
|
|
||||||
[index initial-pos]
|
|
||||||
(ptk/reify ::finish-edit-interaction
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(-> state
|
|
||||||
(assoc-in [:workspace-local :editing-interaction-index] nil)
|
|
||||||
(assoc-in [:workspace-local :draw-interaction-to] nil)
|
|
||||||
(assoc-in [:workspace-local :draw-interaction-to-frame] nil)))
|
|
||||||
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state _]
|
|
||||||
(let [position @ms/mouse-position
|
|
||||||
page-id (:current-page-id state)
|
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
frame (dwc/get-frame-at-point objects position)
|
|
||||||
|
|
||||||
shape-id (-> state wsh/lookup-selected first)
|
|
||||||
shape (get objects shape-id)]
|
|
||||||
|
|
||||||
(when (and shape (not (= position initial-pos)))
|
|
||||||
(rx/of (dch/update-shapes [shape-id]
|
|
||||||
(fn [shape]
|
|
||||||
(update shape :interactions
|
|
||||||
(fn [interactions]
|
|
||||||
(if-not frame
|
|
||||||
;; Drop in an empty space -> remove interaction
|
|
||||||
(if index
|
|
||||||
(into (subvec interactions 0 index)
|
|
||||||
(subvec interactions (inc index)))
|
|
||||||
interactions)
|
|
||||||
(let [frame (if (or (= (:id frame) (:id shape))
|
|
||||||
(= (:id frame) (:frame-id shape)))
|
|
||||||
nil ;; Drop onto self frame -> set destination to none
|
|
||||||
frame)]
|
|
||||||
;; Update or create interaction
|
|
||||||
(if (and index (cti/has-destination (get interactions index)))
|
|
||||||
(update interactions index
|
|
||||||
#(cti/set-destination % (:id frame)))
|
|
||||||
(conj (or interactions [])
|
|
||||||
(cti/set-destination cti/default-interaction
|
|
||||||
(:id frame))))))))))))))))
|
|
||||||
|
|
||||||
(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 % frame-pos offset)))
|
|
||||||
(rx/of (finish-move-overlay-pos index frame-pos offset)))))))))
|
|
||||||
|
|
||||||
(defn move-overlay-pos
|
|
||||||
[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 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
|
;; CANVAS OPTIONS
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
[app.common.types.interactions :as cti]
|
||||||
|
[app.common.types.page-options :as cto]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
@ -379,8 +381,10 @@
|
||||||
(watch [it state _]
|
(watch [it state _]
|
||||||
(let [page-id (:current-page-id state)
|
(let [page-id (:current-page-id state)
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
options (wsh/lookup-page-options state page-id)
|
||||||
|
|
||||||
ids (cp/clean-loops objects ids)
|
ids (cp/clean-loops objects ids)
|
||||||
|
flows (:flows options)
|
||||||
|
|
||||||
groups-to-unmask
|
groups-to-unmask
|
||||||
(reduce (fn [group-ids id]
|
(reduce (fn [group-ids id]
|
||||||
|
@ -399,9 +403,14 @@
|
||||||
interacting-shapes
|
interacting-shapes
|
||||||
(filter (fn [shape]
|
(filter (fn [shape]
|
||||||
(let [interactions (:interactions shape)]
|
(let [interactions (:interactions shape)]
|
||||||
(some ids (map :destination interactions))))
|
(some #(and (cti/has-destination %)
|
||||||
|
(contains? ids (:destination %)))
|
||||||
|
interactions)))
|
||||||
(vals objects))
|
(vals objects))
|
||||||
|
|
||||||
|
starting-flows
|
||||||
|
(filter #(contains? ids (:starting-frame %)) flows)
|
||||||
|
|
||||||
empty-parents-xform
|
empty-parents-xform
|
||||||
(comp
|
(comp
|
||||||
(map (fn [id] (get objects id)))
|
(map (fn [id] (get objects id)))
|
||||||
|
@ -467,7 +476,8 @@
|
||||||
:operations [{:type :set
|
:operations [{:type :set
|
||||||
:attr :interactions
|
:attr :interactions
|
||||||
:val (vec (remove (fn [interaction]
|
:val (vec (remove (fn [interaction]
|
||||||
(contains? ids (:destination interaction)))
|
(and (cti/has-destination interaction)
|
||||||
|
(contains? ids (:destination interaction))))
|
||||||
(:interactions obj)))}]})))
|
(:interactions obj)))}]})))
|
||||||
mk-mod-int-add-xf
|
mk-mod-int-add-xf
|
||||||
(comp (filter some?)
|
(comp (filter some?)
|
||||||
|
@ -479,6 +489,22 @@
|
||||||
:attr :interactions
|
:attr :interactions
|
||||||
:val (:interactions obj)}]})))
|
:val (:interactions obj)}]})))
|
||||||
|
|
||||||
|
mk-mod-del-flow-xf
|
||||||
|
(comp (filter some?)
|
||||||
|
(map (fn [flow]
|
||||||
|
{:type :set-option
|
||||||
|
:page-id page-id
|
||||||
|
:option :flows
|
||||||
|
:value (cto/remove-flow flows (:id flow))})))
|
||||||
|
|
||||||
|
mk-mod-add-flow-xf
|
||||||
|
(comp (filter some?)
|
||||||
|
(map (fn [_]
|
||||||
|
{:type :set-option
|
||||||
|
:page-id page-id
|
||||||
|
:option :flows
|
||||||
|
:value flows})))
|
||||||
|
|
||||||
mk-mod-unmask-xf
|
mk-mod-unmask-xf
|
||||||
(comp (filter (partial contains? objects))
|
(comp (filter (partial contains? objects))
|
||||||
(map (fn [id]
|
(map (fn [id]
|
||||||
|
@ -508,7 +534,8 @@
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:shapes (vec all-parents)})
|
:shapes (vec all-parents)})
|
||||||
(into mk-mod-unmask-xf groups-to-unmask)
|
(into mk-mod-unmask-xf groups-to-unmask)
|
||||||
(into mk-mod-int-del-xf interacting-shapes))
|
(into mk-mod-int-del-xf interacting-shapes)
|
||||||
|
(into mk-mod-del-flow-xf starting-flows))
|
||||||
|
|
||||||
uchanges
|
uchanges
|
||||||
(-> []
|
(-> []
|
||||||
|
@ -520,8 +547,8 @@
|
||||||
:shapes (vec all-parents)})
|
:shapes (vec all-parents)})
|
||||||
(into mk-mod-touched-xf (reverse all-parents))
|
(into mk-mod-touched-xf (reverse all-parents))
|
||||||
(into mk-mod-mask-xf groups-to-unmask)
|
(into mk-mod-mask-xf groups-to-unmask)
|
||||||
(into mk-mod-int-add-xf interacting-shapes))
|
(into mk-mod-int-add-xf interacting-shapes)
|
||||||
]
|
(into mk-mod-add-flow-xf starting-flows))]
|
||||||
|
|
||||||
;; (println "================ rchanges")
|
;; (println "================ rchanges")
|
||||||
;; (cljs.pprint/pprint rchanges)
|
;; (cljs.pprint/pprint rchanges)
|
||||||
|
|
284
frontend/src/app/main/data/workspace/interactions.cljs
Normal file
284
frontend/src/app/main/data/workspace/interactions.cljs
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
;; 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.main.data.workspace.interactions
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.spec :as us]
|
||||||
|
[app.common.types.interactions :as cti]
|
||||||
|
[app.common.types.page-options :as cto]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.data.workspace.changes :as dch]
|
||||||
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
|
[app.main.streams :as ms]
|
||||||
|
[beicon.core :as rx]
|
||||||
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
|
;; --- Flows
|
||||||
|
|
||||||
|
(defn add-flow-selected-frame
|
||||||
|
[]
|
||||||
|
(ptk/reify ::add-flow-selected-frame
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
flows (get-in state [:workspace-data
|
||||||
|
:pages-index
|
||||||
|
page-id
|
||||||
|
:options
|
||||||
|
:flows] [])
|
||||||
|
|
||||||
|
selected (wsh/lookup-selected state)
|
||||||
|
|
||||||
|
unames (into #{} (map :name flows))
|
||||||
|
name (dwc/generate-unique-name unames "Flow-1")
|
||||||
|
|
||||||
|
new-flow {:id (uuid/next)
|
||||||
|
:name name
|
||||||
|
:starting-frame (first selected)}]
|
||||||
|
|
||||||
|
(rx/of (dch/commit-changes
|
||||||
|
{:redo-changes [{:type :set-option
|
||||||
|
:page-id page-id
|
||||||
|
:option :flows
|
||||||
|
:value (cto/add-flow flows new-flow)}]
|
||||||
|
:undo-changes [{:type :set-option
|
||||||
|
:page-id page-id
|
||||||
|
:option :flows
|
||||||
|
:value flows}]
|
||||||
|
:origin it}))))))
|
||||||
|
|
||||||
|
(defn remove-flow
|
||||||
|
[flow-id]
|
||||||
|
(us/verify ::us/uuid flow-id)
|
||||||
|
(ptk/reify ::remove-flow
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
flows (get-in state [:workspace-data
|
||||||
|
:pages-index
|
||||||
|
page-id
|
||||||
|
:options
|
||||||
|
:flows] [])]
|
||||||
|
(rx/of (dch/commit-changes
|
||||||
|
{:redo-changes [{:type :set-option
|
||||||
|
:page-id page-id
|
||||||
|
:option :flows
|
||||||
|
:value (cto/remove-flow flows flow-id)}]
|
||||||
|
:undo-changes [{:type :set-option
|
||||||
|
:page-id page-id
|
||||||
|
:option :flows
|
||||||
|
:value flows}]
|
||||||
|
:origin it}))))))
|
||||||
|
|
||||||
|
(defn rename-flow
|
||||||
|
[flow-id name]
|
||||||
|
(us/verify ::us/uuid flow-id)
|
||||||
|
(us/verify ::us/string name)
|
||||||
|
(ptk/reify ::rename-flow
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [it state _]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
flows (get-in state [:workspace-data
|
||||||
|
:pages-index
|
||||||
|
page-id
|
||||||
|
:options
|
||||||
|
:flows] [])]
|
||||||
|
(rx/of (dch/commit-changes
|
||||||
|
{:redo-changes [{:type :set-option
|
||||||
|
:page-id page-id
|
||||||
|
:option :flows
|
||||||
|
:value (cto/update-flow flows flow-id
|
||||||
|
#(cto/rename-flow % name))}]
|
||||||
|
:undo-changes [{:type :set-option
|
||||||
|
:page-id page-id
|
||||||
|
:option :flows
|
||||||
|
:value flows}]
|
||||||
|
:origin it}))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn start-rename-flow
|
||||||
|
[id]
|
||||||
|
(us/verify ::us/uuid id)
|
||||||
|
(ptk/reify ::start-rename-flow
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc-in state [:workspace-local :flow-for-rename] id))))
|
||||||
|
|
||||||
|
(defn end-rename-flow
|
||||||
|
[]
|
||||||
|
(ptk/reify ::end-rename-flow
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update state :workspace-local dissoc :flow-for-rename))))
|
||||||
|
|
||||||
|
;; --- Interactions
|
||||||
|
|
||||||
|
(declare move-edit-interaction)
|
||||||
|
(declare finish-edit-interaction)
|
||||||
|
|
||||||
|
(defn start-edit-interaction
|
||||||
|
[index]
|
||||||
|
(ptk/reify ::start-edit-interaction
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc-in state [:workspace-local :editing-interaction-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))
|
||||||
|
(rx/concat
|
||||||
|
(->> ms/mouse-position
|
||||||
|
(rx/take-until stopper)
|
||||||
|
(rx/map #(move-edit-interaction initial-pos %)))
|
||||||
|
(rx/of (finish-edit-interaction index initial-pos))))))))
|
||||||
|
|
||||||
|
(defn move-edit-interaction
|
||||||
|
[initial-pos position]
|
||||||
|
(ptk/reify ::move-edit-interaction
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
selected-shape-id (-> state wsh/lookup-selected first)
|
||||||
|
selected-shape (get objects selected-shape-id)
|
||||||
|
selected-shape-frame-id (:frame-id selected-shape)
|
||||||
|
start-frame (get objects selected-shape-frame-id)
|
||||||
|
end-frame (dwc/get-frame-at-point objects position)]
|
||||||
|
(cond-> state
|
||||||
|
(not= position initial-pos) (assoc-in [:workspace-local :draw-interaction-to] position)
|
||||||
|
(not= start-frame end-frame) (assoc-in [:workspace-local :draw-interaction-to-frame] end-frame))))))
|
||||||
|
|
||||||
|
(defn finish-edit-interaction
|
||||||
|
[index initial-pos]
|
||||||
|
(ptk/reify ::finish-edit-interaction
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:workspace-local :editing-interaction-index] nil)
|
||||||
|
(assoc-in [:workspace-local :draw-interaction-to] nil)
|
||||||
|
(assoc-in [:workspace-local :draw-interaction-to-frame] nil)))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(let [position @ms/mouse-position
|
||||||
|
page-id (:current-page-id state)
|
||||||
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
frame (dwc/get-frame-at-point objects position)
|
||||||
|
|
||||||
|
shape-id (-> state wsh/lookup-selected first)
|
||||||
|
shape (get objects shape-id)]
|
||||||
|
|
||||||
|
(when (and shape (not (= position initial-pos)))
|
||||||
|
(rx/of (dch/update-shapes [shape-id]
|
||||||
|
(fn [shape]
|
||||||
|
(update shape :interactions
|
||||||
|
(fn [interactions]
|
||||||
|
(if-not frame
|
||||||
|
;; Drop in an empty space -> remove interaction
|
||||||
|
(if index
|
||||||
|
(into (subvec interactions 0 index)
|
||||||
|
(subvec interactions (inc index)))
|
||||||
|
interactions)
|
||||||
|
(let [frame (if (or (= (:id frame) (:id shape))
|
||||||
|
(= (:id frame) (:frame-id shape)))
|
||||||
|
nil ;; Drop onto self frame -> set destination to none
|
||||||
|
frame)]
|
||||||
|
;; Update or create interaction
|
||||||
|
(if (and index (cti/has-destination (get interactions index)))
|
||||||
|
(update interactions index
|
||||||
|
#(cti/set-destination % (:id frame)))
|
||||||
|
(conj (or interactions [])
|
||||||
|
(cti/set-destination cti/default-interaction
|
||||||
|
(:id frame))))))))))))))))
|
||||||
|
|
||||||
|
;; --- Overlays
|
||||||
|
|
||||||
|
(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 % frame-pos offset)))
|
||||||
|
(rx/of (finish-move-overlay-pos index frame-pos offset)))))))))
|
||||||
|
|
||||||
|
(defn move-overlay-pos
|
||||||
|
[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 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 (dch/update-shapes [(:id shape)] #(merge % {:interactions new-interactions})))))))
|
||||||
|
|
|
@ -41,8 +41,7 @@
|
||||||
(s/keys :req-un [::file-id]))
|
(s/keys :req-un [::file-id]))
|
||||||
|
|
||||||
(s/def ::viewer-query-params
|
(s/def ::viewer-query-params
|
||||||
(s/keys :req-un [::index]
|
(s/keys :opt-un [::index ::share-id ::section ::page-id]))
|
||||||
:opt-un [::share-id ::section ::page-id]))
|
|
||||||
|
|
||||||
(def routes
|
(def routes
|
||||||
[["/auth"
|
[["/auth"
|
||||||
|
|
|
@ -13,14 +13,14 @@
|
||||||
[app.main.ui.components.fullscreen :as fs]
|
[app.main.ui.components.fullscreen :as fs]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.viewer.comments :refer [comments-menu]]
|
[app.main.ui.viewer.comments :refer [comments-menu]]
|
||||||
[app.main.ui.viewer.interactions :refer [interactions-menu]]
|
[app.main.ui.viewer.interactions :refer [flows-menu interactions-menu]]
|
||||||
[app.main.ui.workspace.header :refer [zoom-widget]]
|
[app.main.ui.workspace.header :refer [zoom-widget]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(mf/defc header-options
|
(mf/defc header-options
|
||||||
[{:keys [section zoom page file permissions]}]
|
[{:keys [section zoom page file index permissions]}]
|
||||||
(let [fullscreen (mf/use-ctx fs/fullscreen-context)
|
(let [fullscreen (mf/use-ctx fs/fullscreen-context)
|
||||||
|
|
||||||
toggle-fullscreen
|
toggle-fullscreen
|
||||||
|
@ -43,7 +43,9 @@
|
||||||
|
|
||||||
[:div.options-zone
|
[:div.options-zone
|
||||||
(case section
|
(case section
|
||||||
:interactions [:& interactions-menu]
|
:interactions [:*
|
||||||
|
[:& flows-menu {:page page :index index}]
|
||||||
|
[:& interactions-menu]]
|
||||||
:comments [:& comments-menu]
|
:comments [:& comments-menu]
|
||||||
|
|
||||||
[:div.view-options])
|
[:div.view-options])
|
||||||
|
@ -133,7 +135,6 @@
|
||||||
(fn [section]
|
(fn [section]
|
||||||
(st/emit! (dv/go-to-section section)))]
|
(st/emit! (dv/go-to-section section)))]
|
||||||
|
|
||||||
|
|
||||||
[:header.viewer-header
|
[:header.viewer-header
|
||||||
[:div.main-icon
|
[:div.main-icon
|
||||||
[:a {:on-click go-to-dashboard
|
[:a {:on-click go-to-dashboard
|
||||||
|
@ -167,5 +168,6 @@
|
||||||
:permissions permissions
|
:permissions permissions
|
||||||
:page page
|
:page page
|
||||||
:file file
|
:file file
|
||||||
|
:index index
|
||||||
:zoom zoom}]]))
|
:zoom zoom}]]))
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
|
[app.common.types.page-options :as cto]
|
||||||
[app.main.data.comments :as dcm]
|
[app.main.data.comments :as dcm]
|
||||||
[app.main.data.viewer :as dv]
|
[app.main.data.viewer :as dv]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
@ -99,6 +100,38 @@
|
||||||
:view-box (:vbox size)}]]]]))
|
:view-box (:vbox size)}]]]]))
|
||||||
|
|
||||||
|
|
||||||
|
(mf/defc flows-menu
|
||||||
|
{::mf/wrap [mf/memo]}
|
||||||
|
[{:keys [page index]}]
|
||||||
|
(let [flows (get-in page [:options :flows])
|
||||||
|
frames (:frames page)
|
||||||
|
frame (get frames index)
|
||||||
|
current-flow (cto/get-frame-flow flows (:id frame))
|
||||||
|
|
||||||
|
show-dropdown? (mf/use-state false)
|
||||||
|
toggle-dropdown (mf/use-fn #(swap! show-dropdown? not))
|
||||||
|
hide-dropdown (mf/use-fn #(reset! show-dropdown? false))
|
||||||
|
|
||||||
|
select-flow
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [flow]
|
||||||
|
(st/emit! (dv/go-to-frame (:starting-frame flow)))))]
|
||||||
|
|
||||||
|
(when (seq flows)
|
||||||
|
[:div.view-options {:on-click toggle-dropdown}
|
||||||
|
[:span.icon i/play]
|
||||||
|
[:span.label (:name current-flow)]
|
||||||
|
[:span.icon i/arrow-down]
|
||||||
|
[:& dropdown {:show @show-dropdown?
|
||||||
|
:on-close hide-dropdown}
|
||||||
|
[:ul.dropdown.with-check
|
||||||
|
(for [flow flows]
|
||||||
|
[:li {:class (dom/classnames :selected (= (:id flow) (:id current-flow)))
|
||||||
|
:on-click #(select-flow flow)}
|
||||||
|
[:span.icon i/tick]
|
||||||
|
[:span.label (:name flow)]])]]])))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc interactions-menu
|
(mf/defc interactions-menu
|
||||||
[]
|
[]
|
||||||
(let [local (mf/deref refs/viewer-local)
|
(let [local (mf/deref refs/viewer-local)
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
(ns app.main.ui.workspace.context-menu
|
(ns app.main.ui.workspace.context-menu
|
||||||
"A workspace specific context menu (mouse right click)."
|
"A workspace specific context menu (mouse right click)."
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.types.page-options :as cto]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.interactions :as dwi]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.shortcuts :as sc]
|
[app.main.data.workspace.shortcuts :as sc]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
|
@ -95,6 +97,11 @@
|
||||||
is-group? (and (some? shape) (= :group (:type shape)))
|
is-group? (and (some? shape) (= :group (:type shape)))
|
||||||
is-bool? (and (some? shape) (= :bool (:type shape)))
|
is-bool? (and (some? shape) (= :bool (:type shape)))
|
||||||
|
|
||||||
|
options (mf/deref refs/workspace-page-options)
|
||||||
|
flows (:flows options)
|
||||||
|
|
||||||
|
options-mode (mf/deref refs/options-mode)
|
||||||
|
|
||||||
set-bool
|
set-bool
|
||||||
(fn [bool-type]
|
(fn [bool-type]
|
||||||
#(cond
|
#(cond
|
||||||
|
@ -122,6 +129,8 @@
|
||||||
do-hide-shape (st/emitf (dw/update-shape-flags id {:hidden true}))
|
do-hide-shape (st/emitf (dw/update-shape-flags id {:hidden true}))
|
||||||
do-lock-shape (st/emitf (dw/update-shape-flags id {:blocked true}))
|
do-lock-shape (st/emitf (dw/update-shape-flags id {:blocked true}))
|
||||||
do-unlock-shape (st/emitf (dw/update-shape-flags id {:blocked false}))
|
do-unlock-shape (st/emitf (dw/update-shape-flags id {:blocked false}))
|
||||||
|
do-add-flow (st/emitf (dwi/add-flow-selected-frame))
|
||||||
|
do-remove-flow #(st/emitf (dwi/remove-flow (:id %)))
|
||||||
do-create-group (st/emitf dw/group-selected)
|
do-create-group (st/emitf dw/group-selected)
|
||||||
do-remove-group (st/emitf dw/ungroup-selected)
|
do-remove-group (st/emitf dw/ungroup-selected)
|
||||||
do-mask-group (st/emitf dw/mask-group)
|
do-mask-group (st/emitf dw/mask-group)
|
||||||
|
@ -262,6 +271,14 @@
|
||||||
[:& menu-entry {:title (tr "workspace.shape.menu.lock")
|
[:& menu-entry {:title (tr "workspace.shape.menu.lock")
|
||||||
:on-click do-lock-shape}])
|
:on-click do-lock-shape}])
|
||||||
|
|
||||||
|
(when (and (= options-mode :prototype) (= (:type shape) :frame))
|
||||||
|
(let [flow (cto/get-frame-flow flows (:id shape))]
|
||||||
|
(if (nil? flow)
|
||||||
|
[:& menu-entry {:title (tr "workspace.shape.menu.flow-start")
|
||||||
|
:on-click do-add-flow}]
|
||||||
|
[:& menu-entry {:title (tr "workspace.shape.menu.delete-flow-start")
|
||||||
|
:on-click (do-remove-flow flow)}])))
|
||||||
|
|
||||||
(when (and (or (nil? (:shape-ref shape))
|
(when (and (or (nil? (:shape-ref shape))
|
||||||
(> (count selected) 1))
|
(> (count selected) 1))
|
||||||
(not= (:type shape) :frame))
|
(not= (:type shape) :frame))
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.util.timers :as ts]
|
[app.util.timers :as ts]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
@ -68,7 +69,8 @@
|
||||||
(on-stop-edit)
|
(on-stop-edit)
|
||||||
(swap! local assoc :edition false)
|
(swap! local assoc :edition false)
|
||||||
(st/emit! (dw/end-rename-shape)
|
(st/emit! (dw/end-rename-shape)
|
||||||
(dw/update-shape (:id shape) {:name name}))))
|
(when-not (str/empty? name)
|
||||||
|
(dw/update-shape (:id shape) {:name name})))))
|
||||||
|
|
||||||
cancel-edit (fn []
|
cancel-edit (fn []
|
||||||
(on-stop-edit)
|
(on-stop-edit)
|
||||||
|
|
|
@ -9,14 +9,19 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.types.interactions :as cti]
|
[app.common.types.interactions :as cti]
|
||||||
|
[app.common.types.page-options :as cto]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.interactions :as dwi]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
[app.util.keyboard :as kbd]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[okulary.core :as l]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(defn- event-type-names
|
(defn- event-type-names
|
||||||
|
@ -68,6 +73,92 @@
|
||||||
:bottom-right (tr "workspace.options.interaction-pos-bottom-right")
|
:bottom-right (tr "workspace.options.interaction-pos-bottom-right")
|
||||||
:bottom-center (tr "workspace.options.interaction-pos-bottom-center")})
|
:bottom-center (tr "workspace.options.interaction-pos-bottom-center")})
|
||||||
|
|
||||||
|
(def flow-for-rename-ref
|
||||||
|
(l/derived (l/in [:workspace-local :flow-for-rename]) st/state))
|
||||||
|
|
||||||
|
(mf/defc flow-item
|
||||||
|
[{:keys [flow]}]
|
||||||
|
(let [editing? (mf/use-state false)
|
||||||
|
flow-for-rename (mf/deref flow-for-rename-ref)
|
||||||
|
name-ref (mf/use-ref)
|
||||||
|
|
||||||
|
start-edit (fn []
|
||||||
|
(reset! editing? true))
|
||||||
|
|
||||||
|
accept-edit (fn []
|
||||||
|
(let [name-input (mf/ref-val name-ref)
|
||||||
|
name (dom/get-value name-input)]
|
||||||
|
(reset! editing? false)
|
||||||
|
(st/emit! (dwi/end-rename-flow)
|
||||||
|
(when-not (str/empty? name)
|
||||||
|
(dwi/rename-flow (:id flow) name)))))
|
||||||
|
|
||||||
|
cancel-edit (fn []
|
||||||
|
(reset! editing? false)
|
||||||
|
(st/emit! (dwi/end-rename-flow)))
|
||||||
|
|
||||||
|
on-key-down (fn [event]
|
||||||
|
(when (kbd/enter? event) (accept-edit))
|
||||||
|
(when (kbd/esc? event) (cancel-edit)))]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
#(when editing?
|
||||||
|
(cancel-edit))))
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps flow-for-rename)
|
||||||
|
#(when (and (= flow-for-rename (:id flow))
|
||||||
|
(not @editing?))
|
||||||
|
(start-edit)))
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps @editing?)
|
||||||
|
#(when @editing?
|
||||||
|
(let [name-input (mf/ref-val name-ref)]
|
||||||
|
(dom/select-text! name-input))
|
||||||
|
nil))
|
||||||
|
|
||||||
|
[:div.flow-element
|
||||||
|
[:div.flow-button {:on-click (st/emitf (dw/select-shape (:starting-frame flow)))}
|
||||||
|
i/play]
|
||||||
|
(if @editing?
|
||||||
|
[:input.element-name
|
||||||
|
{:type "text"
|
||||||
|
:ref name-ref
|
||||||
|
:on-blur accept-edit
|
||||||
|
:on-key-down on-key-down
|
||||||
|
:auto-focus true
|
||||||
|
:default-value (:name flow "")}]
|
||||||
|
[:span.element-label.flow-name
|
||||||
|
{:on-double-click (st/emitf (dwi/start-rename-flow (:id flow)))}
|
||||||
|
(:name flow)])
|
||||||
|
[:div.add-page {:on-click (st/emitf (dwi/remove-flow (:id flow)))}
|
||||||
|
i/minus]]))
|
||||||
|
|
||||||
|
(mf/defc page-flows
|
||||||
|
[{:keys [flows]}]
|
||||||
|
(when (seq flows)
|
||||||
|
[:div.element-set.interactions-options
|
||||||
|
[:div.element-set-title
|
||||||
|
[:span (tr "workspace.options.flows.flow-starts")]]
|
||||||
|
(for [flow flows]
|
||||||
|
[:& flow-item {:flow flow :key (str (:id flow))}])]))
|
||||||
|
|
||||||
|
(mf/defc shape-flows
|
||||||
|
[{:keys [flows shape]}]
|
||||||
|
(when (= (:type shape) :frame)
|
||||||
|
(let [flow (cto/get-frame-flow flows (:id shape))]
|
||||||
|
[:div.element-set.interactions-options
|
||||||
|
[:div.element-set-title
|
||||||
|
[:span (tr "workspace.options.flows.flow-start")]]
|
||||||
|
(if (nil? flow)
|
||||||
|
[:div.flow-element
|
||||||
|
[:span.element-label (tr "workspace.options.flows.add-flow-start")]
|
||||||
|
[:div.add-page {:on-click (st/emitf (dwi/add-flow-selected-frame))}
|
||||||
|
i/plus]]
|
||||||
|
[:& flow-item {:flow flow :key (str (:id flow))}])])))
|
||||||
|
|
||||||
(mf/defc interaction-entry
|
(mf/defc interaction-entry
|
||||||
[{:keys [index shape interaction update-interaction remove-interaction]}]
|
[{:keys [index shape interaction update-interaction remove-interaction]}]
|
||||||
(let [objects (deref refs/workspace-page-objects)
|
(let [objects (deref refs/workspace-page-objects)
|
||||||
|
@ -184,7 +275,9 @@
|
||||||
[:select.input-select
|
[:select.input-select
|
||||||
{:value (str (:destination interaction))
|
{:value (str (:destination interaction))
|
||||||
:on-change change-destination}
|
:on-change change-destination}
|
||||||
[:option {:value ""} (tr "workspace.options.interaction-none")]
|
(if (= (:action-type interaction) :close-overlay)
|
||||||
|
[:option {:value ""} (tr "workspace.options.interaction-self")]
|
||||||
|
[:option {:value ""} (tr "workspace.options.interaction-none")])
|
||||||
(for [frame frames]
|
(for [frame frames]
|
||||||
(when (and (not= (:id frame) (:id shape)) ; A frame cannot navigate to itself
|
(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
|
(not= (:id frame) (:frame-id shape))) ; nor a shape to its container frame
|
||||||
|
@ -263,6 +356,9 @@
|
||||||
[{:keys [shape] :as props}]
|
[{:keys [shape] :as props}]
|
||||||
(let [interactions (get shape :interactions [])
|
(let [interactions (get shape :interactions [])
|
||||||
|
|
||||||
|
options (mf/deref refs/workspace-page-options)
|
||||||
|
flows (:flows options)
|
||||||
|
|
||||||
add-interaction
|
add-interaction
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(let [new-interactions (conj interactions cti/default-interaction)]
|
(let [new-interactions (conj interactions cti/default-interaction)]
|
||||||
|
@ -280,13 +376,18 @@
|
||||||
(let [new-interactions (update interactions index update-fn)]
|
(let [new-interactions (update interactions index update-fn)]
|
||||||
(st/emit! (dw/update-shape (:id shape) {:interactions new-interactions})))) ]
|
(st/emit! (dw/update-shape (:id shape) {:interactions new-interactions})))) ]
|
||||||
|
|
||||||
|
[:*
|
||||||
|
(if shape
|
||||||
|
[:& shape-flows {:flows flows
|
||||||
|
:shape shape}]
|
||||||
|
[:& page-flows {:flows flows}])
|
||||||
|
|
||||||
[:div.element-set.interactions-options
|
[:div.element-set.interactions-options
|
||||||
(when shape
|
(when shape
|
||||||
[:div.element-set-title
|
[:div.element-set-title
|
||||||
[:span (tr "workspace.options.interactions")]
|
[:span (tr "workspace.options.interactions")]
|
||||||
[:div.add-page {:on-click add-interaction}
|
[:div.add-page {:on-click add-interaction}
|
||||||
i/plus]])
|
i/plus]])
|
||||||
|
|
||||||
[:div.element-set-content
|
[:div.element-set-content
|
||||||
(when (= (count interactions) 0)
|
(when (= (count interactions) 0)
|
||||||
[:*
|
[:*
|
||||||
|
@ -304,5 +405,5 @@
|
||||||
:shape shape
|
:shape shape
|
||||||
:interaction interaction
|
:interaction interaction
|
||||||
:update-interaction update-interaction
|
:update-interaction update-interaction
|
||||||
:remove-interaction remove-interaction}])]]))
|
:remove-interaction remove-interaction}])]]]))
|
||||||
|
|
||||||
|
|
|
@ -159,13 +159,9 @@
|
||||||
(hooks/setup-shortcuts node-editing? drawing-path?)
|
(hooks/setup-shortcuts node-editing? drawing-path?)
|
||||||
(hooks/setup-active-frames objects vbox hover active-frames)
|
(hooks/setup-active-frames objects vbox hover active-frames)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[:div.viewport
|
[:div.viewport
|
||||||
[:div.viewport-overlays
|
[:div.viewport-overlays
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[:& wtr/frame-renderer {:objects objects
|
[:& wtr/frame-renderer {:objects objects
|
||||||
:background background}]
|
:background background}]
|
||||||
|
|
||||||
|
@ -273,6 +269,17 @@
|
||||||
:on-frame-leave on-frame-leave
|
:on-frame-leave on-frame-leave
|
||||||
:on-frame-select on-frame-select}]
|
:on-frame-select on-frame-select}]
|
||||||
|
|
||||||
|
(when show-prototypes?
|
||||||
|
[:& widgets/frame-flows
|
||||||
|
{:flows (:flows options)
|
||||||
|
:objects objects
|
||||||
|
:selected selected
|
||||||
|
:zoom zoom
|
||||||
|
:modifiers modifiers
|
||||||
|
:on-frame-enter on-frame-enter
|
||||||
|
:on-frame-leave on-frame-leave
|
||||||
|
:on-frame-select on-frame-select}])
|
||||||
|
|
||||||
(when show-gradient-handlers?
|
(when show-gradient-handlers?
|
||||||
[:& gradients/gradient-handlers
|
[:& gradients/gradient-handlers
|
||||||
{:id (first selected)
|
{:id (first selected)
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.interactions :as dwi]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
[app.main.ui.hooks :as hooks]
|
[app.main.ui.hooks :as hooks]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.viewport.path-actions :refer [path-actions]]
|
[app.main.ui.workspace.viewport.path-actions :refer [path-actions]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
@ -156,3 +158,75 @@
|
||||||
:on-frame-enter on-frame-enter
|
:on-frame-enter on-frame-enter
|
||||||
:on-frame-leave on-frame-leave
|
:on-frame-leave on-frame-leave
|
||||||
:on-frame-select on-frame-select}])]))
|
:on-frame-select on-frame-select}])]))
|
||||||
|
|
||||||
|
(mf/defc frame-flow
|
||||||
|
[{:keys [flow frame modifiers selected? zoom on-frame-enter on-frame-leave on-frame-select]}]
|
||||||
|
(let [{:keys [x y]} (gsh/transform-shape frame)
|
||||||
|
flow-pos (gpt/point x (- y (/ 35 zoom)))
|
||||||
|
|
||||||
|
on-mouse-down
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps (:id frame) on-frame-select)
|
||||||
|
(fn [bevent]
|
||||||
|
(let [event (.-nativeEvent bevent)]
|
||||||
|
(when (= 1 (.-which event))
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(on-frame-select event (:id frame))))))
|
||||||
|
|
||||||
|
on-double-click
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps (:id frame))
|
||||||
|
(st/emitf (dwi/start-rename-flow (:id flow))))
|
||||||
|
|
||||||
|
on-pointer-enter
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps (:id frame) on-frame-enter)
|
||||||
|
(fn [_]
|
||||||
|
(on-frame-enter (:id frame))))
|
||||||
|
|
||||||
|
on-pointer-leave
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps (:id frame) on-frame-leave)
|
||||||
|
(fn [_]
|
||||||
|
(on-frame-leave (:id frame))))]
|
||||||
|
|
||||||
|
[:foreignObject {:x 0
|
||||||
|
:y -15
|
||||||
|
:width 100000
|
||||||
|
:height 24
|
||||||
|
:transform (str (when (and selected? modifiers)
|
||||||
|
(str (:displacement modifiers) " " ))
|
||||||
|
(text-transform flow-pos zoom))}
|
||||||
|
[:div.flow-badge {:class (dom/classnames :selected selected?)}
|
||||||
|
[:div.content {:on-mouse-down on-mouse-down
|
||||||
|
:on-double-click on-double-click
|
||||||
|
:on-pointer-enter on-pointer-enter
|
||||||
|
:on-pointer-leave on-pointer-leave}
|
||||||
|
i/play
|
||||||
|
[:span (:name flow)]]]]))
|
||||||
|
|
||||||
|
(mf/defc frame-flows
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [flows (unchecked-get props "flows")
|
||||||
|
objects (unchecked-get props "objects")
|
||||||
|
zoom (unchecked-get props "zoom")
|
||||||
|
modifiers (unchecked-get props "modifiers")
|
||||||
|
selected (or (unchecked-get props "selected") #{})
|
||||||
|
|
||||||
|
on-frame-enter (unchecked-get props "on-frame-enter")
|
||||||
|
on-frame-leave (unchecked-get props "on-frame-leave")
|
||||||
|
on-frame-select (unchecked-get props "on-frame-select")]
|
||||||
|
[:g.frame-flows
|
||||||
|
(for [flow flows]
|
||||||
|
(let [frame (get objects (:starting-frame flow))]
|
||||||
|
[:& frame-flow {:flow flow
|
||||||
|
:frame frame
|
||||||
|
:selected? (contains? selected (:id frame))
|
||||||
|
:zoom zoom
|
||||||
|
:modifiers modifiers
|
||||||
|
:on-frame-enter on-frame-enter
|
||||||
|
:on-frame-leave on-frame-leave
|
||||||
|
:on-frame-select on-frame-select}]))]))
|
||||||
|
|
||||||
|
|
|
@ -2422,6 +2422,18 @@ msgstr "Rotation"
|
||||||
msgid "workspace.options.add-interaction"
|
msgid "workspace.options.add-interaction"
|
||||||
msgstr "Click the + button to add interactions."
|
msgstr "Click the + button to add interactions."
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||||
|
msgid "workspace.options.flows.add-flow-start"
|
||||||
|
msgstr "Add flow start"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||||
|
msgid "workspace.options.flows.flow-start"
|
||||||
|
msgstr "Flow start"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||||
|
msgid "workspace.options.flows.flow-starts"
|
||||||
|
msgstr "Flow starts"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||||
msgid "workspace.options.interaction-action"
|
msgid "workspace.options.interaction-action"
|
||||||
msgstr "Action"
|
msgstr "Action"
|
||||||
|
@ -2857,6 +2869,10 @@ msgstr "Cut"
|
||||||
msgid "workspace.shape.menu.delete"
|
msgid "workspace.shape.menu.delete"
|
||||||
msgstr "Delete"
|
msgstr "Delete"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/context_menu.cljs
|
||||||
|
msgid "workspace.shape.menu.delete-flow-start"
|
||||||
|
msgstr "Delete flow start"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs
|
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs
|
||||||
msgid "workspace.shape.menu.detach-instance"
|
msgid "workspace.shape.menu.detach-instance"
|
||||||
msgstr "Detach instance"
|
msgstr "Detach instance"
|
||||||
|
@ -2877,6 +2893,10 @@ msgstr "Flip horizontal"
|
||||||
msgid "workspace.shape.menu.flip-vertical"
|
msgid "workspace.shape.menu.flip-vertical"
|
||||||
msgstr "Flip vertical"
|
msgstr "Flip vertical"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/context_menu.cljs
|
||||||
|
msgid "workspace.shape.menu.flow-start"
|
||||||
|
msgstr "Flow start"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/context_menu.cljs
|
#: src/app/main/ui/workspace/context_menu.cljs
|
||||||
msgid "workspace.shape.menu.forward"
|
msgid "workspace.shape.menu.forward"
|
||||||
msgstr "Bring forward"
|
msgstr "Bring forward"
|
||||||
|
|
|
@ -2308,6 +2308,18 @@ msgstr "Rotación"
|
||||||
msgid "workspace.options.add-interaction"
|
msgid "workspace.options.add-interaction"
|
||||||
msgstr "Pulsa el botón + para añadir interacciones."
|
msgstr "Pulsa el botón + para añadir interacciones."
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||||
|
msgid "workspace.options.flows.add-flow-start"
|
||||||
|
msgstr "Añadir inicio de flujo"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||||
|
msgid "workspace.options.flows.flow-start"
|
||||||
|
msgstr "Inicio de flujo"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||||
|
msgid "workspace.options.flows.flow-starts"
|
||||||
|
msgstr "Inicios de flujo"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs
|
||||||
msgid "workspace.options.interaction-action"
|
msgid "workspace.options.interaction-action"
|
||||||
msgstr "Acción"
|
msgstr "Acción"
|
||||||
|
@ -2745,6 +2757,10 @@ msgstr "Cortar"
|
||||||
msgid "workspace.shape.menu.delete"
|
msgid "workspace.shape.menu.delete"
|
||||||
msgstr "Eliminar"
|
msgstr "Eliminar"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/context_menu.cljs
|
||||||
|
msgid "workspace.shape.menu.delete-flow-start"
|
||||||
|
msgstr "Eliminar inicio de flujo"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs
|
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/sidebar/options/menus/component.cljs, src/app/main/ui/workspace/context_menu.cljs, src/app/main/ui/workspace/context_menu.cljs
|
||||||
msgid "workspace.shape.menu.detach-instance"
|
msgid "workspace.shape.menu.detach-instance"
|
||||||
msgstr "Desacoplar instancia"
|
msgstr "Desacoplar instancia"
|
||||||
|
@ -2765,6 +2781,10 @@ msgstr "Voltear horizontal"
|
||||||
msgid "workspace.shape.menu.flip-vertical"
|
msgid "workspace.shape.menu.flip-vertical"
|
||||||
msgstr "Voltear vertical"
|
msgstr "Voltear vertical"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/context_menu.cljs
|
||||||
|
msgid "workspace.shape.menu.flow-start"
|
||||||
|
msgstr "Inicio de flujo"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/context_menu.cljs
|
#: src/app/main/ui/workspace/context_menu.cljs
|
||||||
msgid "workspace.shape.menu.forward"
|
msgid "workspace.shape.menu.forward"
|
||||||
msgstr "Mover hacia delante"
|
msgstr "Mover hacia delante"
|
||||||
|
|
Loading…
Add table
Reference in a new issue