0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-17 18:21:23 -05:00

Minor refactor on shapes data events.

Adding more asserts and more specs.
This commit is contained in:
Andrey Antukh 2017-03-02 16:13:42 +01:00
parent fdf3f1b6f0
commit b3a2ae3eb2
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
8 changed files with 281 additions and 205 deletions

View file

@ -49,6 +49,8 @@
(s/def ::hidden boolean?)
(s/def ::blocked boolean?)
(s/def ::locked boolean?)
(s/def ::width number?)
(s/def ::height number?)
(s/def ::x1 number?)
(s/def ::y1 number?)
(s/def ::x2 number?)
@ -103,11 +105,12 @@
ptk/UpdateEvent
(update [_ state]
(let [shape (geom/setup-proportions data)
page (l/focus ul/selected-page state)]
page (get-in state [:workspace :page])]
(impl/assoc-shape-to-page state shape page))))
(defn add-shape
[data]
{:pre [(us/valid? ::shape data)]}
(AddShape. data))
;; --- Delete Shape
@ -125,14 +128,18 @@
{:pre [(uuid? id)]}
(DeleteShape. id))
(defn update-shape
"Just updates in place the shape."
[{:keys [id] :as shape}]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes id] merge shape))))
;; --- Rename Shape
(deftype RenameShape [id name]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes id :name] name)))
(defn rename-shape
[id name]
{:pre [(uuid? id) (string? name)]}
(RenameShape. id name))
;; --- Shape Transformations
@ -142,7 +149,6 @@
(declare apply-temporal-displacement)
(deftype InitialShapeAlign [id]
ptk/WatchEvent
(watch [_ state s]
@ -160,29 +166,40 @@
{:pre [(uuid? id)]}
(InitialShapeAlign. id))
;; --- Update Rotation
(deftype UpdateShapeRotation [id rotation]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes id :rotation] rotation)))
(defn update-rotation
[sid rotation]
{:pre [(number? rotation)
[id rotation]
{:pre [(uuid? id)
(number? rotation)
(>= rotation 0)
(>= 360 rotation)]}
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes sid]
geom/rotate rotation))))
(UpdateShapeRotation. id rotation))
(defn update-size
;; --- Update Dimensions
(deftype UpdateDimensions [id dimensions]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes id] geom/resize-dim dimensions)))
(s/def ::update-dimensions-opts
(s/keys :opt-un [::width ::height]))
(defn update-dimensions
"A helper event just for update the position
of the shape using the width and height attrs
instread final point of coordinates."
[sid opts]
{:pre [(uuid? sid)]}
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes sid] geom/resize-dim opts))))
[id opts]
{:pre [(uuid? id) (us/valid? ::update-dimensions-opts opts)]}
(UpdateDimensions. id opts))
;; --- Apply Temporal Displacement
@ -246,22 +263,32 @@
{:pre [(uuid? id)]}
(ApplyResize. id))
;; --- Update Shape Position
(deftype UpdateShapePosition [id point]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes id] geom/absolute-move point)))
(defn update-position
"Update the start position coordenate of the shape."
[sid {:keys [x y] :as opts}]
(reify
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes sid] geom/absolute-move opts))))
[id point]
{:pre [(uuid? id) (gpt/point? point)]}
(UpdateShapePosition. id point))
;; --- Update Shape Text
(deftype UpdateShapeTextContent [id text]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes id :content] text)))
(defn update-text
[sid {:keys [content]}]
{:pre [(string? content)]}
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes sid :content] content))))
[id text]
{:pre [(uuid? id) (string? text)]}
(UpdateShapeTextContent. id text))
;; --- Update Shape Attrs
@ -283,173 +310,206 @@
;; --- Shape Proportions
(deftype LockShapeProportions [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(let [[width height] (-> (get-in state [:shapes id])
(geom/size)
(keep [:width :height]))
proportion (/ width height)]
(update-in state [:shapes id] assoc
:proportion proportion
:proportion-lock true))))
(defn lock-proportions
"Mark proportions of the shape locked and save the current
proportion as additional precalculated property."
[sid]
{:pre [(uuid? sid)]}
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(let [[width height] (-> (get-in state [:shapes sid])
(geom/size)
(keep [:width :height]))
proportion (/ width height)]
(update-in state [:shapes sid] assoc
:proportion proportion
:proportion-lock true)))))
[id]
{:pre [(uuid? id)]}
(LockShapeProportions. id))
(deftype UnlockShapeProportions [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes id :proportion-lock] true)))
(defn unlock-proportions
[sid]
{:pre [(uuid? sid)]}
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes sid] assoc
:proportion-lock false))))
[id]
{:pre [(uuid? id)]}
(UnlockShapeProportions. id))
;; --- Group Collapsing
(deftype CollapseGroupShape [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes id] assoc :collapsed true)))
(defn collapse-shape
[id]
{:pre [(uuid? id)]}
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes id] assoc :collapsed true))))
(CollapseGroupShape. id))
(deftype UncollapseGroupShape [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes id] assoc :collapsed false)))
(defn uncollapse-shape
[id]
{:pre [(uuid? id)]}
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes id] assoc :collapsed false))))
(UncollapseGroupShape. id))
;; --- Shape Visibility
(defn hide-shape
[sid]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes sid :hidden] true))
(deftype HideShape [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(letfn [(mark-hidden [state id]
(let [shape (get-in state [:shapes id])]
(if (= :group (:type shape))
(as-> state $
(assoc-in $ [:shapes id :hidden] true)
(reduce mark-hidden $ (:items shape)))
(assoc-in state [:shapes id :hidden] true))))]
(mark-hidden state id))))
ptk/WatchEvent
(watch [_ state s]
(let [shape (get-in state [:shapes sid])]
(if-not (= (:type shape) :group)
(rx/empty)
(rx/from-coll
(map hide-shape (:items shape))))))))
(defn hide-shape
[id]
{:pre [(uuid? id)]}
(HideShape. id))
(deftype ShowShape [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(letfn [(mark-visible [state id]
(let [shape (get-in state [:shapes id])]
(if (= :group (:type shape))
(as-> state $
(assoc-in $ [:shapes id :hidden] false)
(reduce mark-visible $ (:items shape)))
(assoc-in state [:shapes id :hidden] false))))]
(mark-visible state id))))
(defn show-shape
[sid]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes sid :hidden] false))
[id]
{:pre [(uuid? id)]}
(ShowShape. id))
ptk/WatchEvent
(watch [_ state s]
(let [shape (get-in state [:shapes sid])]
(if-not (= (:type shape) :group)
(rx/empty)
(rx/from-coll
(map show-shape (:items shape))))))))
;; --- Shape Blocking
(deftype BlockShape [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(letfn [(mark-blocked [state id]
(let [shape (get-in state [:shapes id])]
(if (= :group (:type shape))
(as-> state $
(assoc-in $ [:shapes id :blocked] true)
(reduce mark-blocked $ (:items shape)))
(assoc-in state [:shapes id :blocked] true))))]
(mark-blocked state id))))
(defn block-shape
[sid]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes sid :blocked] true))
[id]
{:pre [(uuid? id)]}
(BlockShape. id))
ptk/WatchEvent
(watch [_ state s]
(let [shape (get-in state [:shapes sid])]
(if-not (= (:type shape) :group)
(rx/empty)
(rx/from-coll
(map block-shape (:items shape))))))))
(deftype UnblockShape [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(letfn [(mark-unblocked [state id]
(let [shape (get-in state [:shapes id])]
(if (= :group (:type shape))
(as-> state $
(assoc-in $ [:shapes id :blocked] false)
(reduce mark-unblocked $ (:items shape)))
(assoc-in state [:shapes id :blocked] false))))]
(mark-unblocked state id))))
(defn unblock-shape
[sid]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes sid :blocked] false))
[id]
{:pre [(uuid? id)]}
(UnblockShape. id))
ptk/WatchEvent
(watch [_ state s]
(let [shape (get-in state [:shapes sid])]
(if-not (= (:type shape) :group)
(rx/empty)
(rx/from-coll
(map unblock-shape (:items shape))))))))
;; --- Shape Locking
(deftype LockShape [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(letfn [(mark-locked [state id]
(let [shape (get-in state [:shapes id])]
(if (= :group (:type shape))
(as-> state $
(assoc-in $ [:shapes id :locked] true)
(reduce mark-locked $ (:items shape)))
(assoc-in state [:shapes id :locked] true))))]
(mark-locked state id))))
(defn lock-shape
[sid]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes sid :locked] true))
[id]
{:pre [(uuid? id)]}
(LockShape. id))
ptk/WatchEvent
(watch [_ state s]
(let [shape (get-in state [:shapes sid])]
(if-not (= (:type shape) :group)
(rx/empty)
(rx/from-coll
(map lock-shape (:items shape))))))))
(deftype UnlockShape [id]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(letfn [(mark-unlocked [state id]
(let [shape (get-in state [:shapes id])]
(if (= :group (:type shape))
(as-> state $
(assoc-in $ [:shapes id :locked] false)
(reduce mark-unlocked $ (:items shape)))
(assoc-in state [:shapes id :locked] false))))]
(mark-unlocked state id))))
(defn unlock-shape
[sid]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes sid :locked] false))
[id]
{:pre [(uuid? id)]}
(UnlockShape. id))
ptk/WatchEvent
(watch [_ state s]
(let [shape (get-in state [:shapes sid])]
(if-not (= (:type shape) :group)
(rx/empty)
(rx/from-coll
(map unlock-shape (:items shape))))))))
;; --- Drop Shape
(deftype DropShape [sid tid loc]
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(impl/drop-shape state sid tid loc)))
(defn drop-shape
"Event used in drag and drop for transfer shape
from one position to an other."
[sid tid loc]
{:pre [(not (nil? tid))
(not (nil? sid))]}
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(impl/drop-shape state sid tid loc))))
{:pre [(uuid? sid)
(uuid? tid)
(keyword? loc)]}
(DropShape. sid tid loc))
;; --- Select First Shape
(deftype SelectFirstShape []
ptk/UpdateEvent
(update [_ state]
(let [page (get-in state [:workspace :page])
id (first (get-in state [:pages page :shapes]))]
(assoc-in state [:workspace :selected] #{id}))))
(defn select-first-shape
"Mark a shape selected for drawing in the canvas."
[]
(reify
ptk/UpdateEvent
(update [_ state]
(let [page (get-in state [:workspace :page])
id (first (get-in state [:pages page :shapes]))]
(assoc-in state [:workspace :selected] #{id})))))
(SelectFirstShape.))
;; --- Mark Shape Selected
(deftype SelectShape [id]
ptk/UpdateEvent
@ -466,7 +526,7 @@
{:pre [(uuid? id)]}
(SelectShape. id))
;; --- Select Shapes
;; --- Select Shapes (By selrect)
(deftype SelectShapesBySelrect [selrect]
ptk/UpdateEvent
@ -578,25 +638,37 @@
[]
(DeselectAll.))
;; --- Group Selected Shapes
(deftype GroupSelectedShapes []
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(let [id (get-in state [:workspace :page])
selected (get-in state [:workspace :selected])]
(assert (not (empty? selected)) "selected set is empty")
(assert (uuid? id) "selected page is not an uuid")
(impl/group-shapes state selected id))))
(defn group-selected
[]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :page])
selected (get-in state [:workspace :selected])]
(impl/group-shapes state selected pid)))))
(GroupSelectedShapes.))
(defn degroup-selected
;; --- Ungroup Selected Shapes
(deftype UngroupSelectedShapes []
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(let [id (get-in state [:workspace :page])
selected (get-in state [:workspace :selected])]
(assert (not (empty? selected)) "selected set is empty")
(assert (uuid? id) "selected page is not an uuid")
(impl/degroup-shapes state selected id))))
(defn ungroup-selected
[]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(let [pid (get-in state [:workspace :page])
selected (get-in state [:workspace :selected])]
(impl/degroup-shapes state selected pid)))))
(UngroupSelectedShapes.))
;; --- Duplicate Selected

View file

@ -108,6 +108,7 @@
;; TODO: maybe we can consider apply the rotation
;; directly to the shape coordinates?
;; FIXME: deprecated, should be removed
(defn rotate
"Apply the rotation to the shape."

View file

@ -121,7 +121,7 @@
props {:x x1 :y y1 :width width :height height}]
(letfn [(on-input [ev]
(let [content (dom/event->inner-text ev)]
(st/emit! (uds/update-text id {:content content}))))]
(st/emit! (uds/update-text id content))))]
[:foreignObject props
[:div {:style style
:ref "container"

View file

@ -28,7 +28,7 @@
(defonce +shortcuts+
{:shift+g #(st/emit! (dw/toggle-flag :grid))
:ctrl+g #(st/emit! (uds/group-selected))
:ctrl+shift+g #(st/emit! (uds/degroup-selected))
:ctrl+shift+g #(st/emit! (uds/ungroup-selected))
:ctrl+shift+m #(st/emit! (dw/toggle-flag :sitemap))
:ctrl+shift+f #(st/emit! (dw/toggle-flag :drawtools))
:ctrl+shift+i #(st/emit! (dw/toggle-flag :icons))

View file

@ -92,14 +92,13 @@
"A generic component that displays the shape name
if it is available and allows inline edition of it."
{:mixins [mx/static (mx/local)]}
[{:keys [rum/local]} shape]
[{:keys [rum/local]} {:keys [id] :as shape}]
(letfn [(on-blur [event]
(let [target (dom/event->target event)
parent (.-parentNode target)
data {:id (:id shape)
:name (dom/get-value target)}]
name (dom/get-value target)]
(set! (.-draggable parent) true)
(st/emit! (uds/update-shape data))
(st/emit! (uds/rename-shape id name))
(swap! local assoc :edition false)))
(on-key-down [event]
(js/console.log event)
@ -295,8 +294,8 @@
groups (into #{} xform selected)]
(= 1 (count groups))))
(defn- allow-degrouping?
"Check if the current situation allows degrouping
(defn- allow-ungrouping?
"Check if the current situation allows ungrouping
of the currently selected shapes."
[selected shapes-map]
(let [xform (comp (map shapes-map)
@ -310,11 +309,11 @@
[selected shapes-map]
(let [duplicate #(st/emit! (uds/duplicate-selected))
group #(st/emit! (uds/group-selected))
degroup #(st/emit! (uds/degroup-selected))
ungroup #(st/emit! (uds/ungroup-selected))
delete #(st/emit! (uds/delete-selected))
allow-grouping? (allow-grouping? selected shapes-map)
allow-degrouping? (allow-degrouping? selected shapes-map)
allow-ungrouping? (allow-ungrouping? selected shapes-map)
allow-duplicate? (= 1 (count selected))
allow-deletion? (pos? (count selected))]
[:div.layers-tools
@ -331,8 +330,8 @@
i/folder]
[:li.degroup-layer.tooltip.tooltip-top
{:alt "Ungroup"
:class (when-not allow-degrouping? "disable")
:on-click degroup}
:class (when-not allow-ungrouping? "disable")
:on-click ungroup}
i/ungroup]
[:li.delete-layer.tooltip.tooltip-top
{:alt "Delete"

View file

@ -19,8 +19,9 @@
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.geom :as geom]
[uxbox.util.dom :as dom]
[uxbox.util.math :refer (precision-or-0)]
[uxbox.util.data :refer (parse-int parse-float read-string)]))
[uxbox.util.geom.point :as gpt]
[uxbox.util.data :refer (parse-int parse-float read-string)]
[uxbox.util.math :refer (precision-or-0)]))
(mx/defc circle-measures-menu
{:mixins [mx/static]}
@ -30,7 +31,7 @@
value (parse-int value 0)
sid (:id shape)
props {attr value}]
(st/emit! (uds/update-size sid props))))
(st/emit! (uds/update-dimensions sid props))))
(on-rotation-change [event]
(let [value (dom/event->value event)
value (parse-int value 0)
@ -40,8 +41,8 @@
(let [value (dom/event->value event)
value (parse-int value nil)
sid (:id shape)
props {attr value}]
(st/emit! (uds/update-position sid props))))
point (gpt/point {attr value})]
(st/emit! (uds/update-position sid point))))
(on-proportion-lock-change [event]
(if (:proportion-lock shape)
(st/emit! (uds/unlock-proportions id))

View file

@ -19,8 +19,9 @@
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.geom :as geom]
[uxbox.util.dom :as dom]
[uxbox.util.math :refer (precision-or-0)]
[uxbox.util.data :refer (parse-int parse-float read-string)]))
[uxbox.util.geom.point :as gpt]
[uxbox.util.data :refer (parse-int parse-float read-string)]
[uxbox.util.math :refer (precision-or-0)]))
(defn- icon-measures-menu-render
[own menu shape]
@ -29,7 +30,7 @@
value (parse-int value 0)
sid (:id shape)
props {attr value}]
(st/emit! (uds/update-size sid props))))
(st/emit! (uds/update-dimensions sid props))))
(on-rotation-change [event]
(let [value (dom/event->value event)
value (parse-int value 0)
@ -39,8 +40,8 @@
(let [value (dom/event->value event)
value (parse-int value nil)
sid (:id shape)
props {attr value}]
(st/emit! (uds/update-position sid props))))
point (gpt/point {attr value})]
(st/emit! (uds/update-position sid point))))
(on-proportion-lock-change [event]
(if (:proportion-lock shape)
(st/emit! (uds/unlock-proportions (:id shape)))

View file

@ -7,7 +7,7 @@
(ns uxbox.main.ui.workspace.sidebar.options.rect-measures
(:require [lentes.core :as l]
[uxbox.util.i18n :refer (tr)]
[uxbox.util.i18n :refer [tr]]
[uxbox.util.router :as r]
[potok.core :as ptk]
[uxbox.main.store :as st]
@ -17,8 +17,9 @@
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.geom :as geom]
[uxbox.util.dom :as dom]
[uxbox.util.math :refer (precision-or-0)]
[uxbox.util.data :refer (parse-int parse-float read-string)]))
[uxbox.util.geom.point :as gpt]
[uxbox.util.data :refer [parse-int parse-float read-string]]
[uxbox.util.math :refer [precision-or-0]]))
(mx/defc rect-measures-menu
{:mixins [mx/static]}
@ -26,15 +27,16 @@
(letfn [(on-size-change [event attr]
(let [value (-> (dom/event->value event)
(parse-int 0))]
(st/emit! (uds/update-size id {attr value}))))
(st/emit! (uds/update-dimensions id {attr value}))))
(on-rotation-change [event]
(let [value (-> (dom/event->value event)
(parse-int 0))]
(st/emit! (uds/update-rotation id value))))
(on-pos-change [event attr]
(let [value (-> (dom/event->value event)
(parse-int nil))]
(st/emit! (uds/update-position id {attr value}))))
(parse-int nil))
point (gpt/point {attr value})]
(st/emit! (uds/update-position id point))))
(on-proportion-lock-change [event]
(if (:proportion-lock shape)
(st/emit! (uds/unlock-proportions id))