0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-16 01:31:22 -05:00

🎉 Update master component

This commit is contained in:
Andrés Moya 2020-09-09 11:19:29 +02:00
parent 917643489f
commit f837bad894
10 changed files with 337 additions and 166 deletions

View file

@ -182,6 +182,20 @@
(assoc m key (apply f found args))
m)))
(defn assoc-in-when
[m key-seq v]
(let [found (get-in m key-seq sentinel)]
(if-not (identical? sentinel found)
(assoc-in m key-seq v)
m)))
(defn assoc-when
[m key v]
(let [found (get m key sentinel)]
(if-not (identical? sentinel found)
(assoc m key v)
m)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Parsing / Conversion
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -362,8 +362,8 @@
(defmethod change-spec :del-component [_]
(s/keys :req-un [::id]))
(defmethod change-spec :sync-library [_]
(s/keys :req-un [::id]))
(defmethod change-spec :update-component [_]
(s/keys :req-un [::id ::shapes]))
(s/def ::change (s/multi-spec change-spec :type))
(s/def ::changes (s/coll-of ::change))
@ -777,80 +777,50 @@
[data {:keys [id]}]
(d/dissoc-in data [:components id]))
(declare sync-page)
(declare sync-shape-and-children)
(declare sync-shape)
(declare sync-component-shape)
(defmethod process-change :sync-library
[data id]
(cph/walk-pages (sync-page (:components data)) data))
(defmethod process-change :update-component
[data {:keys [id shapes]}]
(let [sync-component
(fn [component]
(update component :objects
#(d/mapm (partial sync-component-shape shapes) %)))]
(defn- sync-page
[components]
(fn [page-id page]
(let [linked-shapes
(cph/select-objects #(some? (:component-id %)) page)
(update-in data [:components id] sync-component)))
updated-shapes
(reduce
(fn [updated-shapes linked-shape]
(let [component-id (:component-id linked-shape)
component (get components component-id)]
(into updated-shapes
(sync-shape-and-children linked-shape
component
(:objects page)))))
[]
linked-shapes)]
(cph/update-object-list page updated-shapes))))
(defn- sync-shape-and-children
[linked-shape component objects]
(let [children (cph/get-children-objects (:id linked-shape) objects)
all-shapes (conj children linked-shape)]
(if (nil? component)
(map #(dissoc % :component-id :shape-ref) all-shapes)
(map #(sync-shape % (:objects component)) all-shapes))))
(defn- sync-shape
[shape component-objs]
(let [component-shape (get component-objs (:shape-ref shape))]
(if (nil? component-shape)
(assoc shape :shape-ref nil)
(-> shape
(d/update-when :content :content component-shape)
(d/update-when :fill-color :fill-color component-shape)
(d/update-when :fill-color-ref-file :fill-color-ref-file component-shape)
(d/update-when :fill-color-ref-id :fill-color-ref-id component-shape)
(d/update-when :fill-opacity :fill-opacity component-shape)
(d/update-when :font-family :font-family component-shape)
(d/update-when :font-size :font-size component-shape)
(d/update-when :font-style :font-style component-shape)
(d/update-when :font-weight :font-weight component-shape)
(d/update-when :letter-spacing :letter-spacing component-shape)
(d/update-when :line-height :line-height component-shape)
(d/update-when :proportion :proportion component-shape)
(d/update-when :rx :rx component-shape)
(d/update-when :ry :ry component-shape)
(d/update-when :cx :cx component-shape)
(d/update-when :cy :cy component-shape)
(d/update-when :x :x component-shape)
(d/update-when :y :y component-shape)
(d/update-when :exports :exports component-shape)
(d/update-when :stroke-color :stroke-color component-shape)
(d/update-when :stroke-color-ref-file :stroke-color-ref-file component-shape)
(d/update-when :stroke-color-ref-id :stroke-color-ref-id component-shape)
(d/update-when :stroke-opacity :stroke-opacity component-shape)
(d/update-when :stroke-style :stroke-style component-shape)
(d/update-when :stroke-width :stroke-width component-shape)
(d/update-when :stroke-alignment :stroke-alignment component-shape)
(d/update-when :text-align :text-align component-shape)
(d/update-when :width :width component-shape)
(d/update-when :height :height component-shape)
(d/update-when :interactions :interactions component-shape)
(d/update-when :selrect :selrect component-shape)
(d/update-when :points :points component-shape)))))
(defn- sync-component-shape
[new-shapes _ component-shape]
(let [shape (d/seek #(= (:shape-ref %) (:id component-shape)) new-shapes)]
(if (nil? shape)
component-shape
(-> component-shape
(d/assoc-when :content (:content shape))
(d/assoc-when :fill-color (:fill-color shape))
(d/assoc-when :fill-color-ref-file (:fill-color-ref-file shape))
(d/assoc-when :fill-color-ref-id (:fill-color-ref-id shape))
(d/assoc-when :fill-opacity (:fill-opacity shape))
(d/assoc-when :font-family (:font-family shape))
(d/assoc-when :font-size (:font-size shape))
(d/assoc-when :font-style (:font-style shape))
(d/assoc-when :font-weight (:font-weight shape))
(d/assoc-when :letter-spacing (:letter-spacing shape))
(d/assoc-when :line-height (:line-height shape))
(d/assoc-when :proportion (:proportion shape))
(d/assoc-when :rx (:rx shape))
(d/assoc-when :ry (:ry shape))
(d/assoc-when :stroke-color (:stroke-color shape))
(d/assoc-when :stroke-color-ref-file (:stroke-color-ref-file shape))
(d/assoc-when :stroke-color-ref-id (:stroke-color-ref-id shape))
(d/assoc-when :stroke-opacity (:stroke-opacity shape))
(d/assoc-when :stroke-style (:stroke-style shape))
(d/assoc-when :stroke-width (:stroke-width shape))
(d/assoc-when :stroke-alignment (:stroke-alignment shape))
(d/assoc-when :text-align (:text-align shape))
(d/assoc-when :width (:width shape))
(d/assoc-when :height (:height shape))
(d/assoc-when :interactions (:interactions shape))
(d/assoc-when :selrect (:selrect shape))
(d/assoc-when :points (:points shape))))))
(defmethod process-operation :set
[shape op]

View file

@ -30,6 +30,16 @@
(update page :objects
#(into % (d/index-by :id objects-list))))
(defn get-root-component
"Get the root shape linked to the component for this shape, if any"
[id objects]
(let [obj (get objects id)]
(if-let [component-id (:component-id obj)]
id
(if-let [parent-id (:parent-id obj)]
(get-root-component parent-id obj)
nil))))
(defn get-children
"Retrieve all children ids recursively for a given object"
[id objects]
@ -43,6 +53,26 @@
[id objects]
(map #(get objects %) (get-children id objects)))
(defn get-object-with-children
"Retrieve a list with an object and all of its children"
[id objects]
(map #(get objects %) (concat [id] (get-children id objects))))
(defn walk-children
"Go through an object and all the children tree, and apply a
function to each one. Return the list of changed objects."
[id f objects]
(let [obj (get objects id)]
(if (nil? (:shapes obj))
[(apply f obj)]
(loop [children (map #(get objects %) (:shapes obj))
updated-children []]
(if (empty? children)
updated-children
(let [child (first children)]
(recur (rest children)
(concat [(apply f child)] updated-children))))))))
(defn is-shape-grouped
"Checks if a shape is inside a group"
[shape-id objects]
@ -136,3 +166,54 @@
(lazy-seq (loopfn (rest ids))))))]
(loopfn (:shapes root))))
(defn clone-object
"Gets a copy of the object and all its children, with new ids
and with the parent-children links correctly set. Admits functions
to make more transformations to the cloned objects and the
original ones.
Returns the cloned object, the list of all new objects (including
the cloned one), and possibly a list of original objects modified."
([object parent-id objects xf-new-object]
(clone-object object parent-id objects xf-new-object identity))
([object parent-id objects xf-new-object xf-original-object]
(let [new-id (uuid/next)]
(loop [child-ids (seq (:shapes object))
new-direct-children []
new-children []
updated-children []]
(if (empty? child-ids)
(let [new-object (cond-> object
true
(assoc :id new-id
:parent-id parent-id)
(some? (:shapes object))
(assoc :shapes (map :id new-direct-children)))
new-object (xf-new-object new-object object)
new-objects (concat [new-object] new-children)
updated-object (xf-original-object object new-object)
updated-objects (if (= object updated-object)
updated-children
(concat [updated-object] updated-children))]
[new-object new-objects updated-objects])
(let [child-id (first child-ids)
child (get objects child-id)
[new-child new-child-objects updated-child-objects]
(clone-object child new-id objects xf-new-object xf-original-object)]
(recur
(next child-ids)
(concat new-direct-children [new-child])
(concat new-children new-child-objects)
(concat updated-children updated-child-objects))))))))

View file

@ -48,10 +48,6 @@
(s/def ::set-of-string
(s/every string? :kind set?))
;; --- Expose inner functions
(defn interrupt? [e] (= e :interrupt))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Workspace Initialization
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -956,7 +952,7 @@
ptk/WatchEvent
(watch [_ state stream]
(->> stream
(rx/filter interrupt?)
(rx/filter dwc/interrupt?)
(rx/take 1)
(rx/map (constantly clear-edition-mode))))))
@ -985,7 +981,7 @@
ptk/WatchEvent
(watch [_ state stream]
(let [cancel-event? (fn [event]
(interrupt? event))
(dwc/interrupt? event))
stoper (rx/filter (ptk/type? ::clear-drawing) stream)]
(->> (rx/filter cancel-event? stream)
(rx/take 1)

View file

@ -44,6 +44,11 @@
([state page-id]
(get-in state [:workspace-data :pages-index page-id :options])))
(defn interrupt? [e] (= e :interrupt))
(defn lookup-component-objects
([state component-id]
(get-in state [:workspace-data :components component-id :objects])))
;; --- Changes Handling
@ -454,3 +459,4 @@
objects (lookup-page-objects state page-id)
[rchanges uchanges] (impl-gen-changes objects page-id (seq ids))]
(rx/of (commit-changes rchanges uchanges {:commit-local? true})))))))

View file

@ -12,11 +12,15 @@
[app.common.data :as d]
[app.common.spec :as us]
[app.common.uuid :as uuid]
[app.common.pages-helpers :as cph]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.selection :as dws]
[app.common.pages :as cp]
[app.main.repo :as rp]
[app.main.store :as st]
[app.main.streams :as ms]
[app.util.color :as color]
[app.util.i18n :refer [tr]]
[beicon.core :as rx]
@ -107,7 +111,7 @@
:object prev}]
(rx/of (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))
(declare clone-shape)
(declare make-component-shape)
(def add-component
(ptk/reify ::add-component
@ -127,7 +131,7 @@
(dws/prepare-create-group page-id shapes "Component-" true))
[new-shape new-shapes updated-shapes]
(clone-shape group nil objects)
(make-component-shape group nil objects)
rchanges (conj rchanges
{:type :add-component
@ -168,59 +172,23 @@
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dws/select-shapes (d/ordered-set (:id group))))))))))
(defn- clone-shape
(defn- make-component-shape
"Clone the shape and all children. Generate new ids and detach
from parent and frame. Update the original shapes to have links
to the new ones."
[shape parent-id objects]
(let [new-id (uuid/next)]
(if (nil? (:shapes shape))
(let [xf-new-shape (fn [new-shape original-shape]
(assoc new-shape :frame-id nil))
; TODO: unify this case with the empty child-ids case.
(let [new-shape (assoc shape
:id new-id
:parent-id parent-id
:frame-id nil)
xf-original-shape (fn [original-shape new-shape]
(cond-> original-shape
true
(assoc :shape-ref (:id new-shape))
new-shapes [new-shape]
(nil? (:parent-id new-shape))
(assoc :component-id (:id new-shape))))]
updated-shapes [(cond-> shape
true (assoc :shape-ref (:id new-shape))
(nil? parent-id) (assoc :component-id (:id new-shape)))]]
[new-shape new-shapes updated-shapes])
(loop [child-ids (seq (:shapes shape))
new-children []
updated-children []]
(if (empty? child-ids)
(let [new-shape (assoc shape
:id new-id
:parent-id parent-id
:frame-id nil
:shapes (map :id new-children))
new-shapes (conj new-children new-shape)
updated-shapes
(conj updated-children
(cond-> shape
true (assoc :shape-ref (:id new-shape))
(nil? parent-id) (assoc :component-id (:id new-shape))))]
[new-shape new-shapes updated-shapes])
(let [child-id (first child-ids)
child (get objects child-id)
[new-child new-child-shapes updated-child-shapes]
(clone-shape child new-id objects)]
(recur
(next child-ids)
(into new-children new-child-shapes)
(into updated-children updated-child-shapes))))))))
(cph/clone-object shape parent-id objects xf-new-shape xf-original-shape)))
(defn delete-component
[{:keys [id] :as params}]
@ -241,3 +209,145 @@
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
(defn instantiate-component
[id]
(us/assert ::us/uuid id)
(ptk/reify ::instantiate-component
ptk/WatchEvent
(watch [_ state stream]
(let [component (get-in state [:workspace-data :components id])
component-shape (get-in component [:objects (:id component)])
orig-pos (gpt/point (:x component-shape) (:y component-shape))
mouse-pos @ms/mouse-position
delta (gpt/subtract mouse-pos orig-pos)
_ (js/console.log "orig-pos" (clj->js orig-pos))
_ (js/console.log "mouse-pos" (clj->js mouse-pos))
_ (js/console.log "delta" (clj->js delta))
page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
unames (dwc/retrieve-used-names objects)
all-frames (cph/select-frames objects)
xf-new-shape
(fn [new-shape original-shape]
(let [new-name ;; TODO: ojoooooooooo
(dwc/generate-unique-name unames (:name new-shape))]
(cond-> new-shape
true
(as-> $
(assoc $ :name new-name)
(geom/move $ delta)
(assoc $ :frame-id
(dwc/calculate-frame-overlap all-frames $))
(assoc $ :parent-id
(or (:parent-id $) (:frame-id $)))
(assoc $ :shape-ref (:id original-shape)))
(nil? (:parent-id original-shape))
(assoc :component-id (:id original-shape)))))
[new-shape new-shapes _]
(cph/clone-object component-shape
nil
(get component :objects)
xf-new-shape)
rchanges (map (fn [obj]
{:type :add-obj
:id (:id obj)
:page-id page-id
:frame-id (:frame-id obj)
:parent-id (:parent-id obj)
:obj obj})
new-shapes)
uchanges (map (fn [obj]
{:type :del-obj
:id (:id obj)
:page-id page-id})
new-shapes)]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dws/select-shapes (d/ordered-set (:id new-shape))))))))
(defn detach-component
[id]
(us/assert ::us/uuid id)
(ptk/reify ::detach-component
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
root-id (cph/get-root-component id objects)
shapes (cph/get-object-with-children root-id objects)
rchanges (map (fn [obj]
{:type :mod-obj
:page-id page-id
:id (:id obj)
:operations [{:type :set
:attr :component-id
:val nil}
{:type :set
:attr :shape-ref
:val nil}]})
shapes)
uchanges (map (fn [obj]
{:type :mod-obj
:page-id page-id
:id (:id obj)
:operations [{:type :set
:attr :component-id
:val (:component-id obj)}
{:type :set
:attr :shape-ref
:val (:shape-ref obj)}]})
shapes)]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
(defn reset-component
[id]
[id]
(us/assert ::us/uuid id)
(ptk/reify ::reset-component
ptk/WatchEvent
(watch [_ state stream]
)))
(defn update-component
[id]
[id]
(us/assert ::us/uuid id)
(ptk/reify ::update-component
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
root-id (cph/get-root-component id objects)
root-shape (get objects id)
component-id (get root-shape :component-id)
component-objs (dwc/lookup-component-objects state component-id)
shapes (cph/get-object-with-children root-id objects)
rchanges [{:type :update-component
:id component-id
:shapes shapes}
{:type :sync-library
:id (get-in state [:workspace-file :id])}]
uchanges [{:type :update-component
:id component-id
:shapes (vals component-objs)}]]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))

View file

@ -33,33 +33,6 @@
(s/def ::set-of-string
(s/every string? :kind set?))
;; Duplicate from workspace.
;; FIXME: Move these functions to a common place
(defn interrupt? [e] (= e :interrupt))
(defn- retrieve-used-names
[objects]
(into #{} (map :name) (vals objects)))
(defn- extract-numeric-suffix
[basename]
(if-let [[match p1 p2] (re-find #"(.*)-([0-9]+)$" basename)]
[p1 (+ 1 (d/parse-integer p2))]
[basename 1]))
(defn- generate-unique-name
"A unique name generator"
[used basename]
(s/assert ::set-of-string used)
(s/assert ::us/string basename)
(let [[prefix initial] (extract-numeric-suffix basename)]
(loop [counter initial]
(let [candidate (str prefix "-" counter)]
(if (contains? used candidate)
(recur (inc counter))
candidate)))))
;; --- Selection Rect
(declare select-shapes-by-current-selrect)
@ -88,7 +61,7 @@
(ptk/reify ::handle-selection
ptk/WatchEvent
(watch [_ state stream]
(let [stoper (rx/filter #(or (interrupt? %)
(let [stoper (rx/filter #(or (dwc/interrupt? %)
(ms/mouse-up? %))
stream)]
(rx/concat
@ -198,7 +171,9 @@
(let [selrect (geom/selection-rect shapes)
frame-id (-> shapes first :frame-id)
parent-id (-> shapes first :parent-id)
group-name (if (and keep-name (= (count shapes) 1))
group-name (if (and keep-name
(= (count shapes) 1)
(= (:type (first shapes)) :group))
(:name (first shapes))
(name (gensym prefix)))]
(-> (cp/make-minimal-group frame-id selrect group-name)
@ -298,7 +273,7 @@
(defn- prepare-duplicate-shape-change
[objects page-id names obj delta frame-id parent-id]
(let [id (uuid/next)
name (generate-unique-name names (:name obj))
name (dwc/generate-unique-name names (:name obj))
renamed-obj (assoc obj :id id :name name)
moved-obj (geom/move renamed-obj delta)
frames (cph/select-frames objects)
@ -338,7 +313,7 @@
(defn- prepare-duplicate-frame-change
[objects page-id names obj delta]
(let [frame-id (uuid/next)
frame-name (generate-unique-name names (:name obj))
frame-name (dwc/generate-unique-name names (:name obj))
sch (->> (map #(get objects %) (:shapes obj))
(mapcat #(prepare-duplicate-shape-change objects page-id names % delta frame-id frame-id)))
@ -367,7 +342,7 @@
selected (get-in state [:workspace-local :selected])
delta (gpt/point 0 0)
unames (retrieve-used-names objects)
unames (dwc/retrieve-used-names objects)
rchanges (prepare-duplicate-changes objects page-id unames selected delta)
uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %))

View file

@ -61,7 +61,10 @@
do-unlock-shape #(st/emit! (dw/update-shape-flags id {:blocked false}))
do-create-group #(st/emit! dw/group-selected)
do-remove-group #(st/emit! dw/ungroup-selected)
do-add-component #(st/emit! dwl/add-component)]
do-add-component #(st/emit! dwl/add-component)
do-detach-component #(st/emit! (dwl/detach-component id))
do-reset-component #(st/emit! (dwl/reset-component id))
do-update-component #(st/emit! (dwl/update-component id))]
[:*
[:& menu-entry {:title "Copy"
:shortcut "Ctrl + c"
@ -110,9 +113,18 @@
:on-click do-lock-shape}])
[:& menu-separator]
[:& menu-entry {:title "Create component"
:shortcut "Ctrl + K"
:on-click do-add-component}]
(if (nil? (:shape-ref shape))
[:& menu-entry {:title "Create component"
:shortcut "Ctrl + K"
:on-click do-add-component}]
[:*
[:& menu-entry {:title "Detach instance"
:on-click do-detach-component}]
[:& menu-entry {:title "Reset overrides"
:on-click do-reset-component}]
[:& menu-entry {:title "Update master component"
:on-click do-update-component}]])
[:& menu-separator]
[:& menu-entry {:title "Delete"

View file

@ -69,8 +69,8 @@
on-drag-start
(mf/use-callback
(fn [path event]
(dnd/set-data! event "text/uri-list" (cfg/resolve-media-path path))
(fn [component-id event]
(dnd/set-data! event "app/component" component-id)
(dnd/set-allowed-effect! event "move")))]
[:div.asset-group
@ -82,7 +82,7 @@
[:div.grid-cell {:key (:id component)
:draggable true
:on-context-menu (on-context-menu (:id component))
:on-drag-start (partial on-drag-start (:path component))}
:on-drag-start (partial on-drag-start (:id component))}
[:& exports/component-svg {:group (get-in component [:objects (:id component)])
:objects (:objects component)}]
[:div.cell-name (:name component)]])

View file

@ -22,6 +22,7 @@
[app.common.data :as d]
[app.main.constants :as c]
[app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.drawing :as dd]
[app.main.data.colors :as dwc]
[app.main.data.fetch :as mdf]
@ -454,6 +455,7 @@
on-drag-enter
(fn [e]
(when (or (dnd/has-type? e "app/shape")
(dnd/has-type? e "app/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list"))
(dom/prevent-default e)))
@ -461,6 +463,7 @@
on-drag-over
(fn [e]
(when (or (dnd/has-type? e "app/shape")
(dnd/has-type? e "app/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list"))
(dom/prevent-default e)))
@ -491,6 +494,10 @@
(assoc :x final-x)
(assoc :y final-y)))))
(dnd/has-type? event "app/component")
(let [component-id (dnd/get-data event "app/component")]
(st/emit! (dwl/instantiate-component component-id)))
(dnd/has-type? event "text/uri-list")
(let [data (dnd/get-data event "text/uri-list")
lines (str/lines data)