♻️ Move the ghost rendering to separate component.

This commit is contained in:
Andrey Antukh 2020-12-21 12:12:45 +01:00 committed by Alonso Torres
parent dbb1e6a890
commit f12ade3b67
2 changed files with 148 additions and 122 deletions

View file

@ -190,7 +190,6 @@
(l/derived selector st/state =))) (l/derived selector st/state =)))
;; TODO: looks very inneficient access method, revisit the usage of this ref
(def selected-objects-with-children (def selected-objects-with-children
(letfn [(selector [state] (letfn [(selector [state]
(let [selected (get-in state [:workspace-local :selected]) (let [selected (get-in state [:workspace-local :selected])

View file

@ -184,6 +184,33 @@
:selected selected :selected selected
:hover hover}])])) :hover hover}])]))
(mf/defc ghost-frames
{::mf/wrap-props false}
[props]
(let [modifiers (obj/get props "modifiers")
selected (obj/get props "selected")
sobjects (mf/deref refs/selected-objects)
selrect-orig (gsh/selection-rect sobjects)
xf (comp
(map #(assoc % :modifiers modifiers))
(map gsh/transform-shape))
selrect (->> (into [] xf sobjects)
(gsh/selection-rect))]
[:svg.ghost
{:x (:x selrect)
:y (:y selrect)
:width (:width selrect)
:height (:height selrect)
:style {:pointer-events "none"}}
[:g {:transform (str/fmt "translate(%s,%s)" (- (:x selrect-orig)) (- (:y selrect-orig)))}
[:& frames
{:ids selected
:ghost? true}]]]))
(defn format-viewbox [vbox] (defn format-viewbox [vbox]
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0)) (str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
(:y vbox 0) (:y vbox 0)
@ -337,40 +364,42 @@
(st/emit! (ms/->KeyboardEvent :up key ctrl? shift? alt?))))) (st/emit! (ms/->KeyboardEvent :up key ctrl? shift? alt?)))))
translate-point-to-viewport translate-point-to-viewport
(fn [pt] (mf/use-callback
(let [viewport (mf/ref-val viewport-ref) (fn [pt]
vbox (.. ^js viewport -viewBox -baseVal) (let [viewport (mf/ref-val viewport-ref)
brect (.getBoundingClientRect viewport) vbox (.. ^js viewport -viewBox -baseVal)
brect (gpt/point (d/parse-integer (.-left brect)) brect (.getBoundingClientRect viewport)
(d/parse-integer (.-top brect))) brect (gpt/point (d/parse-integer (.-left brect))
box (gpt/point (.-x vbox) (d/parse-integer (.-top brect)))
(.-y vbox)) box (gpt/point (.-x vbox)
] (.-y vbox))
(-> (gpt/subtract pt brect) ]
(gpt/divide (gpt/point @refs/selected-zoom)) (-> (gpt/subtract pt brect)
(gpt/add box) (gpt/divide (gpt/point @refs/selected-zoom))
(gpt/round 0)))) (gpt/add box)
(gpt/round 0)))))
on-mouse-move on-mouse-move
(fn [event] (mf/use-callback
(let [event (.getBrowserEvent ^js event) (fn [event]
raw-pt (dom/get-client-position ^js event) (let [event (.getBrowserEvent ^js event)
pt (translate-point-to-viewport raw-pt) raw-pt (dom/get-client-position event)
pt (translate-point-to-viewport raw-pt)
;; We calculate the delta because Safari's MouseEvent.movementX/Y drop ;; We calculate the delta because Safari's MouseEvent.movementX/Y drop
;; events ;; events
delta (if @last-position delta (if @last-position
(gpt/subtract raw-pt @last-position) (gpt/subtract raw-pt @last-position)
(gpt/point 0 0))] (gpt/point 0 0))]
(reset! last-position raw-pt) (reset! last-position raw-pt)
(st/emit! (ms/->PointerEvent :delta delta (st/emit! (ms/->PointerEvent :delta delta
(kbd/ctrl? event) (kbd/ctrl? event)
(kbd/shift? event) (kbd/shift? event)
(kbd/alt? event))) (kbd/alt? event)))
(st/emit! (ms/->PointerEvent :viewport pt (st/emit! (ms/->PointerEvent :viewport pt
(kbd/ctrl? event) (kbd/ctrl? event)
(kbd/shift? event) (kbd/shift? event)
(kbd/alt? event))))) (kbd/alt? event))))))
on-mouse-wheel on-mouse-wheel
(mf/use-callback (mf/use-callback
@ -398,99 +427,105 @@
(st/emit! (dw/update-viewport-position {:y #(+ % delta)})))))))) (st/emit! (dw/update-viewport-position {:y #(+ % delta)}))))))))
on-drag-enter on-drag-enter
(fn [e] (mf/use-callback
(when (or (dnd/has-type? e "app/shape") (fn [e]
(dnd/has-type? e "app/component") (when (or (dnd/has-type? e "app/shape")
(dnd/has-type? e "Files") (dnd/has-type? e "app/component")
(dnd/has-type? e "text/uri-list")) (dnd/has-type? e "Files")
(dom/prevent-default e))) (dnd/has-type? e "text/uri-list"))
(dom/prevent-default e))))
on-drag-over on-drag-over
(fn [e] (mf/use-callback
(when (or (dnd/has-type? e "app/shape") (fn [e]
(dnd/has-type? e "app/component") (when (or (dnd/has-type? e "app/shape")
(dnd/has-type? e "Files") (dnd/has-type? e "app/component")
(dnd/has-type? e "text/uri-list")) (dnd/has-type? e "Files")
(dom/prevent-default e))) (dnd/has-type? e "text/uri-list"))
(dom/prevent-default e))))
;; TODO: seems duplicated callback is the same as one located ;; TODO: seems duplicated callback is the same as one located
;; in left_toolbar ;; in left_toolbar
on-uploaded on-uploaded
(fn [{:keys [id name] :as image} {:keys [x y]}] (mf/use-callback
(let [shape {:name name (fn [{:keys [id name] :as image} {:keys [x y]}]
:width (:width image) (let [shape {:name name
:height (:height image) :width (:width image)
:x (- x (/ (:width image) 2)) :height (:height image)
:y (- y (/ (:height image) 2)) :x (- x (/ (:width image) 2))
:metadata {:width (:width image) :y (- y (/ (:height image) 2))
:height (:height image) :metadata {:width (:width image)
:id (:id image) :height (:height image)
:path (:path image)}} :id (:id image)
aspect-ratio (/ (:width image) (:height image))] :path (:path image)}}
(st/emit! (dw/create-and-add-shape :image x y shape)))) aspect-ratio (/ (:width image) (:height image))]
(st/emit! (dw/create-and-add-shape :image x y shape)))))
on-drop on-drop
(fn [event] (mf/use-callback
(dom/prevent-default event) (fn [event]
(let [point (gpt/point (.-clientX event) (.-clientY event)) (dom/prevent-default event)
viewport-coord (translate-point-to-viewport point)] (let [point (gpt/point (.-clientX event) (.-clientY event))
(cond viewport-coord (translate-point-to-viewport point)]
(dnd/has-type? event "app/shape") (cond
(let [shape (dnd/get-data event "app/shape") (dnd/has-type? event "app/shape")
final-x (- (:x viewport-coord) (/ (:width shape) 2)) (let [shape (dnd/get-data event "app/shape")
final-y (- (:y viewport-coord) (/ (:height shape) 2))] final-x (- (:x viewport-coord) (/ (:width shape) 2))
(st/emit! (dw/add-shape (-> shape final-y (- (:y viewport-coord) (/ (:height shape) 2))]
(assoc :id (uuid/next)) (st/emit! (dw/add-shape (-> shape
(assoc :x final-x) (assoc :id (uuid/next))
(assoc :y final-y))))) (assoc :x final-x)
(assoc :y final-y)))))
(dnd/has-type? event "app/component") (dnd/has-type? event "app/component")
(let [{:keys [component file-id]} (dnd/get-data event "app/component") (let [{:keys [component file-id]} (dnd/get-data event "app/component")
shape (get-in component [:objects (:id component)]) shape (get-in component [:objects (:id component)])
final-x (- (:x viewport-coord) (/ (:width shape) 2)) final-x (- (:x viewport-coord) (/ (:width shape) 2))
final-y (- (:y viewport-coord) (/ (:height shape) 2))] final-y (- (:y viewport-coord) (/ (:height shape) 2))]
(st/emit! (dwl/instantiate-component file-id (st/emit! (dwl/instantiate-component file-id
(:id component) (:id component)
(gpt/point final-x final-y)))) (gpt/point final-x final-y))))
(dnd/has-type? event "text/uri-list") (dnd/has-type? event "text/uri-list")
(let [data (dnd/get-data event "text/uri-list") (let [data (dnd/get-data event "text/uri-list")
name (dnd/get-data event "text/asset-name") name (dnd/get-data event "text/asset-name")
lines (str/lines data) lines (str/lines data)
urls (filter #(and (not (str/blank? %)) urls (filter #(and (not (str/blank? %))
(not (str/starts-with? % "#"))) (not (str/starts-with? % "#")))
lines)] lines)]
(->> urls (->> urls
(map (fn [uri] (map (fn [uri]
(with-meta {:file-id (:id file) (with-meta {:file-id (:id file)
:local? true :local? true
:uri uri :uri uri
:name name} :name name}
{:on-success #(on-uploaded % viewport-coord)}))) {:on-success #(on-uploaded % viewport-coord)})))
(map dw/upload-media-objects) (map dw/upload-media-objects)
(apply st/emit!))) (apply st/emit!)))
:else :else
(let [js-files (dnd/get-files event) (let [js-files (dnd/get-files event)
params {:file-id (:id file) params {:file-id (:id file)
:local? true :local? true
:js-files js-files :js-files js-files
}] }]
(st/emit! (dw/upload-media-objects (st/emit! (dw/upload-media-objects
(with-meta params (with-meta params
{:on-success #(on-uploaded % viewport-coord)}))))))) {:on-success #(on-uploaded % viewport-coord)}))))))))
on-paste on-paste
(fn [event] (mf/use-callback
(st/emit! (dw/paste-from-event event))) (fn [event]
(st/emit! (dw/paste-from-event event))))
on-resize on-resize
(fn [event] (mf/use-callback
(let [node (mf/ref-val viewport-ref) (fn [event]
prnt (dom/get-parent node) (let [node (mf/ref-val viewport-ref)
size (dom/get-client-size prnt)] prnt (dom/get-parent node)
;; We schedule the event so it fires after `initialize-page` event size (dom/get-client-size prnt)]
(timers/schedule #(st/emit! (dw/update-viewport-size size))))) ;; We schedule the event so it fires after `initialize-page` event
(timers/schedule #(st/emit! (dw/update-viewport-size size))))))
options (mf/deref refs/workspace-page-options)] options (mf/deref refs/workspace-page-options)]
@ -575,19 +610,11 @@
"auto")}} "auto")}}
[:& frames {:key page-id [:& frames {:key page-id
:hover (:hover local) :hover (:hover local)
:selected (:selected selected)}] :selected selected}]
(when (= :move (:transform local)) (when (= :move (:transform local))
[:svg.ghost [:& ghost-frames {:modifiers (:modifiers local)
{:x (:x selrect) :selected selected}])
:y (:y selrect)
:width (:width selrect)
:height (:height selrect)
:style {:pointer-events "none"}}
[:g {:transform (str/fmt "translate(%s,%s)" (- (:x selrect-orig)) (- (:y selrect-orig)))}
[:& frames {:ids selected
:ghost? true}]]])
(when (seq selected) (when (seq selected)
[:& selection-handlers {:selected selected [:& selection-handlers {:selected selected