mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
🎉 Add the ability to create variants from a selection (#7045)
* 🎉 Add the ability to create variants from a selection * 📎 Add PR feedback changes * 💄 Add minor cosmetic changes --------- Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
@@ -17,7 +17,6 @@
|
||||
[app.common.geom.proportions :as gpp]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.logging :as log]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.transit :as t]
|
||||
[app.common.types.component :as ctc]
|
||||
[app.common.types.fills :as types.fills]
|
||||
@@ -39,7 +38,6 @@
|
||||
[app.main.data.project :as dpj]
|
||||
[app.main.data.workspace.bool :as dwb]
|
||||
[app.main.data.workspace.clipboard :as dwcp]
|
||||
[app.main.data.workspace.collapse :as dwco]
|
||||
[app.main.data.workspace.colors :as dwcl]
|
||||
[app.main.data.workspace.comments :as dwcm]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
@@ -683,52 +681,13 @@
|
||||
|
||||
;; --- Change Shape Order (D&D Ordering)
|
||||
|
||||
(defn relocate-shapes
|
||||
[ids parent-id to-index & [ignore-parents?]]
|
||||
(dm/assert! (every? uuid? ids))
|
||||
(dm/assert! (set? ids))
|
||||
(dm/assert! (uuid? parent-id))
|
||||
(dm/assert! (number? to-index))
|
||||
|
||||
(ptk/reify ::relocate-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
data (dsh/lookup-file-data state)
|
||||
|
||||
;; Ignore any shape whose parent is also intended to be moved
|
||||
ids (cfh/clean-loops objects ids)
|
||||
|
||||
;; If we try to move a parent into a child we remove it
|
||||
ids (filter #(not (cfh/is-parent? objects parent-id %)) ids)
|
||||
|
||||
all-parents (into #{parent-id} (map #(cfh/get-parent-id objects %)) ids)
|
||||
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/with-page-id page-id)
|
||||
(pcb/with-objects objects)
|
||||
(pcb/with-library-data data)
|
||||
(cls/generate-relocate
|
||||
parent-id
|
||||
to-index
|
||||
ids
|
||||
:ignore-parents? ignore-parents?))
|
||||
undo-id (js/Symbol)]
|
||||
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/commit-changes changes)
|
||||
(dwco/expand-collapse parent-id)
|
||||
(ptk/data-event :layout/update {:ids (concat all-parents ids)})
|
||||
(dwu/commit-undo-transaction undo-id))))))
|
||||
|
||||
(defn relocate-selected-shapes
|
||||
[parent-id to-index]
|
||||
(ptk/reify ::relocate-selected-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [selected (dsh/lookup-selected state)]
|
||||
(rx/of (relocate-shapes selected parent-id to-index))))))
|
||||
(rx/of (dwsh/relocate-shapes selected parent-id to-index))))))
|
||||
|
||||
(defn start-editing-selected
|
||||
[]
|
||||
@@ -1169,7 +1128,7 @@
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [orphans (set (into [] (keys (find-orphan-shapes state))))]
|
||||
(rx/of (relocate-shapes orphans uuid/zero 0 true))))))
|
||||
(rx/of (dwsh/relocate-shapes orphans uuid/zero 0 true))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Sitemap
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
[app.main.data.comments :as dc]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.workspace.collapse :as dwco]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
@@ -249,6 +250,44 @@
|
||||
;; Artboard
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn create-artboard-from-shapes
|
||||
([shapes id parent-id index name delta]
|
||||
(create-artboard-from-shapes shapes id parent-id index name delta true))
|
||||
([shapes id parent-id index name delta layout-update?]
|
||||
(ptk/reify ::create-artboard-from-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects objects))
|
||||
|
||||
[frame-shape changes]
|
||||
(cfsh/prepare-create-artboard-from-selection changes
|
||||
id
|
||||
parent-id
|
||||
objects
|
||||
shapes
|
||||
index
|
||||
name
|
||||
false
|
||||
nil
|
||||
delta)
|
||||
|
||||
undo-id (js/Symbol)]
|
||||
|
||||
(when changes
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dch/commit-changes changes)
|
||||
(dws/select-shapes (d/ordered-set (:id frame-shape)))
|
||||
(when layout-update? (ptk/data-event :layout/update {:ids [(:id frame-shape)]}))
|
||||
(ev/event {::ev/name "create-board"
|
||||
:converted-from (cfh/get-selected-type objects shapes)
|
||||
:parent-type (cfh/get-shape-type objects (:parent-id frame-shape))})
|
||||
(dwu/commit-undo-transaction undo-id))))))))
|
||||
|
||||
(defn create-artboard-from-selection
|
||||
([]
|
||||
(create-artboard-from-selection nil))
|
||||
@@ -263,7 +302,7 @@
|
||||
([id parent-id index name delta]
|
||||
(ptk/reify ::create-artboard-from-selection
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(watch [_ state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
selected (->> (dsh/lookup-selected state)
|
||||
@@ -271,35 +310,10 @@
|
||||
(remove #(ctn/has-any-copy-parent? objects (get objects %)))
|
||||
(remove #(->> %
|
||||
(get objects)
|
||||
(ctc/is-variant?))))
|
||||
(ctc/is-variant?))))]
|
||||
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects objects))
|
||||
|
||||
[frame-shape changes]
|
||||
(cfsh/prepare-create-artboard-from-selection changes
|
||||
id
|
||||
parent-id
|
||||
objects
|
||||
selected
|
||||
index
|
||||
name
|
||||
false
|
||||
nil
|
||||
delta)
|
||||
|
||||
undo-id (js/Symbol)]
|
||||
|
||||
(when changes
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dch/commit-changes changes)
|
||||
(dws/select-shapes (d/ordered-set (:id frame-shape)))
|
||||
(ptk/data-event :layout/update {:ids [(:id frame-shape)]})
|
||||
(ev/event {::ev/name "create-board"
|
||||
:converted-from (cfh/get-selected-type objects selected)
|
||||
:parent-type (cfh/get-shape-type objects (:parent-id frame-shape))})
|
||||
(dwu/commit-undo-transaction undo-id))))))))
|
||||
(rx/of (create-artboard-from-shapes selected id parent-id index name delta)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Shape Flags
|
||||
@@ -379,3 +393,45 @@
|
||||
;; And finally: toggle the flag value on all the selected shapes
|
||||
(rx/of (update-shapes selected #(update % :use-for-thumbnail not))
|
||||
(dwu/commit-undo-transaction undo-id)))))))
|
||||
|
||||
|
||||
;; --- Change Shape Order (D&D Ordering)
|
||||
|
||||
(defn relocate-shapes
|
||||
[ids parent-id to-index & [ignore-parents?]]
|
||||
(dm/assert! (every? uuid? ids))
|
||||
(dm/assert! (set? ids))
|
||||
(dm/assert! (uuid? parent-id))
|
||||
(dm/assert! (number? to-index))
|
||||
|
||||
(ptk/reify ::relocate-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
data (dsh/lookup-file-data state)
|
||||
|
||||
;; Ignore any shape whose parent is also intended to be moved
|
||||
ids (cfh/clean-loops objects ids)
|
||||
|
||||
;; If we try to move a parent into a child we remove it
|
||||
ids (filter #(not (cfh/is-parent? objects parent-id %)) ids)
|
||||
|
||||
all-parents (into #{parent-id} (map #(cfh/get-parent-id objects %)) ids)
|
||||
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/with-page-id page-id)
|
||||
(pcb/with-objects objects)
|
||||
(pcb/with-library-data data)
|
||||
(cls/generate-relocate
|
||||
parent-id
|
||||
to-index
|
||||
ids
|
||||
:ignore-parents? ignore-parents?))
|
||||
undo-id (js/Symbol)]
|
||||
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/commit-changes changes)
|
||||
(dwco/expand-collapse parent-id)
|
||||
(ptk/data-event :layout/update {:ids (concat all-parents ids)})
|
||||
(dwu/commit-undo-transaction undo-id))))))
|
||||
|
||||
@@ -20,9 +20,11 @@
|
||||
[app.common.types.variant :as ctv]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.workspace.colors :as cl]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.pages :as dwpg]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
@@ -338,83 +340,101 @@
|
||||
(defn transform-in-variant
|
||||
"Given the id of a main shape of a component, creates a variant structure for
|
||||
that component"
|
||||
[main-instance-id]
|
||||
(ptk/reify ::transform-in-variant
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [variant-id (uuid/next)
|
||||
variant-vec [variant-id]
|
||||
file-id (:current-file-id state)
|
||||
page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state file-id page-id)
|
||||
main (get objects main-instance-id)
|
||||
parent (get objects (:parent-id main))
|
||||
component-id (:component-id main)
|
||||
cpath (cfh/split-path (:name main))
|
||||
name (first cpath)
|
||||
num-props (max 1 (dec (count cpath)))
|
||||
cont-props {:layout-item-h-sizing :auto
|
||||
:layout-item-v-sizing :auto
|
||||
:layout-padding {:p1 30 :p2 30 :p3 30 :p4 30}
|
||||
:layout-gap {:row-gap 0 :column-gap 20}
|
||||
:name name
|
||||
:r1 20
|
||||
:r2 20
|
||||
:r3 20
|
||||
:r4 20
|
||||
:is-variant-container true}
|
||||
main-props {:layout-item-h-sizing :fix
|
||||
:layout-item-v-sizing :fix
|
||||
:variant-id variant-id
|
||||
:name name}
|
||||
stroke-props {:stroke-alignment :inner
|
||||
:stroke-style :solid
|
||||
:stroke-color "#bb97d8" ;; todo use color var?
|
||||
:stroke-opacity 1
|
||||
:stroke-width 2}
|
||||
([main-instance-id]
|
||||
(transform-in-variant main-instance-id nil nil [] true true))
|
||||
([main-instance-id variant-id delta prefix duplicate? flex?]
|
||||
(ptk/reify ::transform-in-variant
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [variant-id (or variant-id (uuid/next))
|
||||
variant-vec [variant-id]
|
||||
file-id (:current-file-id state)
|
||||
page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state file-id page-id)
|
||||
main (get objects main-instance-id)
|
||||
parent (get objects (:parent-id main))
|
||||
component-id (:component-id main)
|
||||
;; If there is a prefix, set is as first item of path
|
||||
cpath (-> (:name main)
|
||||
cfh/split-path
|
||||
(cond->
|
||||
(seq prefix)
|
||||
(->> (drop (count prefix))
|
||||
(cons (cfh/join-path prefix))
|
||||
vec)))
|
||||
|
||||
name (first cpath)
|
||||
num-props (max 1 (dec (count cpath)))
|
||||
base-props {:is-variant-container true
|
||||
:name name
|
||||
:r1 20
|
||||
:r2 20
|
||||
:r3 20
|
||||
:r4 20}
|
||||
flex-props {:layout-item-h-sizing :auto
|
||||
:layout-item-v-sizing :auto
|
||||
:layout-padding {:p1 30 :p2 30 :p3 30 :p4 30}
|
||||
:layout-gap {:row-gap 0 :column-gap 20}}
|
||||
cont-props (if flex?
|
||||
(into base-props flex-props)
|
||||
base-props)
|
||||
m-base-props {:name name
|
||||
:variant-id variant-id}
|
||||
m-flex-props {:layout-item-h-sizing :fix
|
||||
:layout-item-v-sizing :fix}
|
||||
main-props (if flex?
|
||||
(into m-base-props m-flex-props)
|
||||
m-base-props)
|
||||
stroke-props {:stroke-alignment :inner
|
||||
:stroke-style :solid
|
||||
:stroke-color "#bb97d8" ;; todo use color var?
|
||||
:stroke-opacity 1
|
||||
:stroke-width 2}
|
||||
|
||||
;; Move the position of the variant container so the main shape doesn't
|
||||
;; change its position
|
||||
delta (if (ctsl/any-layout? parent)
|
||||
(gpt/point 0 0)
|
||||
(gpt/point -30 -30))
|
||||
undo-id (js/Symbol)]
|
||||
delta (or delta
|
||||
(if (ctsl/any-layout? parent)
|
||||
(gpt/point 0 0)
|
||||
(gpt/point -30 -30)))
|
||||
undo-id (js/Symbol)]
|
||||
|
||||
|
||||
;;TODO Refactor all called methods in order to be able to
|
||||
;;generate changes instead of call the events
|
||||
(rx/concat
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(rx/concat
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
|
||||
(when (not= name (:name main))
|
||||
(dwl/rename-component component-id name))
|
||||
(when (not= name (:name main))
|
||||
(dwl/rename-component component-id name))
|
||||
|
||||
;; Create variant container
|
||||
(dwsh/create-artboard-from-selection variant-id nil nil nil delta)
|
||||
(cl/remove-all-fills variant-vec {:color clr/black :opacity 1})
|
||||
(dwsl/create-layout-from-id variant-id :flex)
|
||||
(dwsh/update-shapes variant-vec #(merge % cont-props))
|
||||
(dwsh/update-shapes [main-instance-id] #(merge % main-props))
|
||||
(cl/add-stroke variant-vec stroke-props)
|
||||
(set-variant-id component-id variant-id))
|
||||
(dwsh/create-artboard-from-shapes [main-instance-id] variant-id nil nil nil delta flex?)
|
||||
(cl/remove-all-fills variant-vec {:color clr/black :opacity 1})
|
||||
(when flex? (dwsl/create-layout-from-id variant-id :flex))
|
||||
(dwsh/update-shapes variant-vec #(merge % cont-props))
|
||||
(dwsh/update-shapes [main-instance-id] #(merge % main-props))
|
||||
(cl/add-stroke variant-vec stroke-props)
|
||||
(set-variant-id component-id variant-id))
|
||||
|
||||
;; Add the necessary number of new properties, with default values
|
||||
(rx/from
|
||||
(repeatedly num-props
|
||||
#(add-new-property variant-id {:fill-values? true})))
|
||||
(rx/from
|
||||
(repeatedly num-props
|
||||
#(add-new-property variant-id {:fill-values? true})))
|
||||
|
||||
;; When the component has path, set the path items as properties values
|
||||
(when (> (count cpath) 1)
|
||||
(rx/from
|
||||
(map
|
||||
#(update-property-value component-id % (nth cpath (inc %)))
|
||||
(range num-props))))
|
||||
(when (> (count cpath) 1)
|
||||
(rx/from
|
||||
(map
|
||||
#(update-property-value component-id % (nth cpath (inc %)))
|
||||
(range num-props))))
|
||||
|
||||
(rx/of
|
||||
(add-new-variant main-instance-id)
|
||||
(dwu/commit-undo-transaction undo-id)
|
||||
(ptk/data-event :layout/update {:ids [variant-id]})))))))
|
||||
(rx/of
|
||||
(when duplicate? (add-new-variant main-instance-id))
|
||||
(dwu/commit-undo-transaction undo-id)
|
||||
(when flex?
|
||||
(ptk/data-event :layout/update {:ids [variant-id]})))))))))
|
||||
|
||||
(defn add-component-or-variant
|
||||
"Manage the shared shortcut, and do the pertinent action"
|
||||
@@ -509,3 +529,91 @@
|
||||
(rx/of (rename-variant (:variant-id component) name))
|
||||
(rx/of (dwl/rename-component-and-main-instance component-id name)))))))
|
||||
|
||||
|
||||
(defn- bounding-rect
|
||||
"Receives a list of frames (with X, y, width and height) and
|
||||
calculates a rect that contains them all"
|
||||
[frames]
|
||||
(let [xs (map :x frames)
|
||||
ys (map :y frames)
|
||||
x2s (map #(+ (:x %) (:width %)) frames)
|
||||
y2s (map #(+ (:y %) (:height %)) frames)
|
||||
min-x (apply min xs)
|
||||
min-y (apply min ys)
|
||||
max-x (apply max x2s)
|
||||
max-y (apply max y2s)]
|
||||
{:x min-x
|
||||
:y min-y
|
||||
:width (- max-x min-x)
|
||||
:height (- max-y min-y)}))
|
||||
|
||||
(defn- common-prefix
|
||||
[paths]
|
||||
(->> (apply map vector paths)
|
||||
(take-while #(apply = %))
|
||||
(map first)
|
||||
vec))
|
||||
|
||||
(defn combine-as-variants
|
||||
([]
|
||||
(combine-as-variants nil {}))
|
||||
([selected {:keys [page-id]}]
|
||||
(ptk/reify ::combine-as-variants
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [current-page (:current-page-id state)
|
||||
|
||||
combine
|
||||
(fn [current-page]
|
||||
(let [objects (dsh/lookup-page-objects state current-page)
|
||||
selected (or selected
|
||||
(->> (dsh/lookup-selected state)
|
||||
(cfh/clean-loops objects)
|
||||
(remove (fn [id]
|
||||
(let [shape (get objects id)]
|
||||
(or (not (ctc/main-instance? shape))
|
||||
(ctc/is-variant? shape)))))))
|
||||
shapes (mapv #(get objects %) selected)
|
||||
rect (bounding-rect shapes)
|
||||
prefix (->> shapes
|
||||
(mapv #(cfh/split-path (:name %)))
|
||||
(common-prefix))
|
||||
first-shape (first shapes)
|
||||
delta (gpt/point (- (:x rect) (:x first-shape) 30)
|
||||
(- (:y rect) (:y first-shape) 30))
|
||||
common-parent (->> selected
|
||||
(mapv #(-> (cfh/get-parent-ids objects %) reverse))
|
||||
common-prefix
|
||||
last)
|
||||
index (-> (get objects common-parent)
|
||||
:shapes
|
||||
count
|
||||
inc)
|
||||
variant-id (uuid/next)
|
||||
undo-id (js/Symbol)]
|
||||
(rx/concat
|
||||
(rx/of
|
||||
(when (and page-id (not= current-page page-id))
|
||||
(dcm/go-to-workspace :page-id page-id))
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(transform-in-variant (first selected) variant-id delta prefix false false)
|
||||
(dwsh/relocate-shapes (into #{} (-> selected rest reverse)) variant-id 0)
|
||||
(dwt/update-dimensions [variant-id] :width (+ (:width rect) 60))
|
||||
(dwt/update-dimensions [variant-id] :height (+ (:height rect) 60))
|
||||
(dwsh/relocate-shapes #{variant-id} common-parent index)
|
||||
(dwu/commit-undo-transaction undo-id)))))
|
||||
|
||||
redirect-to-page
|
||||
(fn [page-id]
|
||||
(rx/merge
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwpg/initialize-page))
|
||||
(rx/take 1)
|
||||
(rx/observe-on :async)
|
||||
(rx/mapcat (fn [_] (combine page-id))))
|
||||
(rx/of (dcm/go-to-workspace :page-id page-id))))]
|
||||
|
||||
(if (and page-id (not= page-id current-page))
|
||||
(redirect-to-page page-id)
|
||||
(combine current-page)))))))
|
||||
|
||||
|
||||
@@ -42,6 +42,8 @@
|
||||
[:map
|
||||
[:name :string]
|
||||
[:id :string]
|
||||
[:title {:optional true} [:maybe :string]]
|
||||
[:disabled {:optional true} [:maybe :boolean]]
|
||||
[:handler {:optional true} fn?]
|
||||
[:options {:optional true}
|
||||
[:sequential [:ref ::option]]]]
|
||||
@@ -258,7 +260,9 @@
|
||||
(let [name (:name option)
|
||||
id (:id option)
|
||||
sub-options (:options option)
|
||||
handler (:handler option)]
|
||||
handler (:handler option)
|
||||
title (:title option)
|
||||
disabled (:disabled option)]
|
||||
(when name
|
||||
(if (= name :separator)
|
||||
[:li {:key (dm/str "context-item-" index)
|
||||
@@ -273,10 +277,12 @@
|
||||
:role "menuitem"
|
||||
:on-key-down dom/prevent-default}
|
||||
(if-not sub-options
|
||||
[:a {:class (stl/css :context-menu-action)
|
||||
[:a {:class (stl/css-case :context-menu-action true :context-menu-action-disabled disabled)
|
||||
:title title
|
||||
:on-click #(do (dom/stop-propagation %)
|
||||
(on-close %)
|
||||
(handler %))
|
||||
(when-not disabled
|
||||
(on-close %)
|
||||
(handler %)))
|
||||
:data-testid id}
|
||||
(if (and in-dashboard? (= name "Default"))
|
||||
(tr "dashboard.default-team-name")
|
||||
|
||||
@@ -105,6 +105,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-action-disabled,
|
||||
&:hover .context-menu-action-disabled {
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -570,9 +570,14 @@
|
||||
heads (filter ctk/instance-head? shapes)
|
||||
components-menu-entries (cmm/generate-components-menu-entries heads)
|
||||
variant-container? (and single? (ctk/is-variant-container? (first shapes)))
|
||||
do-add-component #(st/emit! (dwl/add-component))
|
||||
do-add-multiple-components #(st/emit! (dwl/add-multiple-components))
|
||||
do-add-variant #(st/emit! (dwv/add-new-variant (:id (first shapes))))]
|
||||
all-main? (every? ctk/main-instance? shapes)
|
||||
any-variant? (some ctk/is-variant? shapes)
|
||||
do-add-component (mf/use-fn #(st/emit! (dwl/add-component)))
|
||||
do-add-multiple-components (mf/use-fn #(st/emit! (dwl/add-multiple-components)))
|
||||
do-combine-as-variants (mf/use-fn #(st/emit! (dwv/combine-as-variants)))
|
||||
do-add-variant (mf/use-fn
|
||||
(mf/deps shapes)
|
||||
#(st/emit! (dwv/add-new-variant (:id (first shapes)))))]
|
||||
[:*
|
||||
(when can-make-component ;; We don't want to change the structure of component copies
|
||||
[:*
|
||||
@@ -596,10 +601,17 @@
|
||||
:on-click (:action entry)}])])
|
||||
|
||||
(when variant-container?
|
||||
[:> menu-separator*]
|
||||
[:> menu-entry* {:title (tr "workspace.shape.menu.add-variant")
|
||||
:shortcut (sc/get-tooltip :create-component)
|
||||
:on-click do-add-variant}])]))
|
||||
[:*
|
||||
[:> menu-separator*]
|
||||
[:> menu-entry* {:title (tr "workspace.shape.menu.add-variant")
|
||||
:shortcut (sc/get-tooltip :create-component)
|
||||
:on-click do-add-variant}]])
|
||||
|
||||
(when (and (not single?) all-main? (not any-variant?))
|
||||
[:*
|
||||
[:> menu-separator*]
|
||||
[:> menu-entry* {:title (tr "workspace.shape.menu.combine-as-variants")
|
||||
:on-click do-combine-as-variants}]])]))
|
||||
|
||||
(mf/defc context-menu-delete*
|
||||
{::mf/props :obj
|
||||
|
||||
@@ -317,13 +317,21 @@
|
||||
(seq (:colors selected))
|
||||
(seq (:typographies selected)))
|
||||
|
||||
any-variant? (mf/with-memo [selected components current-component-id]
|
||||
(let [selected-and-current (-> (d/nilv selected [])
|
||||
(conj current-component-id)
|
||||
set)]
|
||||
(->> components
|
||||
(filter #(contains? selected-and-current (:id %)))
|
||||
(some ctc/is-variant?))))
|
||||
selected-and-current (mf/with-memo [selected components current-component-id]
|
||||
(-> (d/nilv selected [])
|
||||
(conj current-component-id)
|
||||
set))
|
||||
|
||||
selected-and-current-full (mf/with-memo [selected-and-current]
|
||||
(->> components
|
||||
(filter #(contains? selected-and-current (:id %)))))
|
||||
|
||||
any-variant? (mf/with-memo [selected-and-current]
|
||||
(some ctc/is-variant? selected-and-current-full))
|
||||
|
||||
all-same-page? (mf/with-memo [selected-and-current]
|
||||
(let [page (:main-instance-page (first selected-and-current-full))]
|
||||
(every? #(= page (:main-instance-page %)) selected-and-current-full)))
|
||||
|
||||
groups (mf/with-memo [components reverse-sort?]
|
||||
(grp/group-assets components reverse-sort?))
|
||||
@@ -497,7 +505,17 @@
|
||||
(st/emit! (dwl/go-to-component-file file-id component))))))
|
||||
|
||||
on-asset-click
|
||||
(mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))]
|
||||
(mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))
|
||||
|
||||
on-combine-as-variants
|
||||
(mf/use-fn
|
||||
(mf/deps selected-and-current-full)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [page-id (->> selected-and-current-full first :main-instance-page)
|
||||
ids (into #{} (map :main-instance-id selected-full))]
|
||||
|
||||
(st/emit! (dwv/combine-as-variants ids {:page-id page-id})))))]
|
||||
|
||||
[:& cmm/asset-section {:file-id file-id
|
||||
:title (tr "workspace.assets.components")
|
||||
@@ -575,4 +593,10 @@
|
||||
(when (not multi-assets?)
|
||||
{:name (tr "workspace.shape.menu.show-main")
|
||||
:id "assets-show-main-component"
|
||||
:handler on-show-main})]}]]]))
|
||||
:handler on-show-main})
|
||||
(when (and is-local multi-components? (not any-variant?))
|
||||
{:name (tr "workspace.shape.menu.combine-as-variants")
|
||||
:id "assets-combine-as-variants"
|
||||
:title (when-not all-same-page? (tr "workspace.shape.menu.combine-as-variants-error"))
|
||||
:disabled (not all-same-page?)
|
||||
:handler on-combine-as-variants})]}]]]))
|
||||
|
||||
@@ -778,6 +778,9 @@
|
||||
copies (filter ctk/in-component-copy? shapes)
|
||||
can-swap? (boolean (seq copies))
|
||||
|
||||
all-main? (every? ctk/main-instance? shapes)
|
||||
any-variant? (some ctk/is-variant? shapes)
|
||||
|
||||
;; For when it's only one shape
|
||||
shape (first shapes)
|
||||
id (:id shape)
|
||||
@@ -790,6 +793,7 @@
|
||||
data (dm/get-in libraries [(:component-file shape) :data])
|
||||
variants? (features/use-feature "variants/v1")
|
||||
is-variant? (when variants? (ctk/is-variant? component))
|
||||
|
||||
main-instance? (ctk/main-instance? shape)
|
||||
|
||||
components (mapv #(ctf/resolve-component %
|
||||
@@ -830,6 +834,9 @@
|
||||
(when can-swap? (st/emit! (dwsp/open-specialized-panel :component-swap)))
|
||||
(tm/schedule-on-idle #(dom/focus! (dom/get-element search-id))))))
|
||||
|
||||
on-combine-as-variants
|
||||
#(st/emit! (dwv/combine-as-variants))
|
||||
|
||||
;; NOTE: function needed for force rerender from the bottom
|
||||
;; components. This is because `component-annotation`
|
||||
;; component changes the component but that has no direct
|
||||
@@ -937,6 +944,11 @@
|
||||
:shapes shapes
|
||||
:data data}])
|
||||
|
||||
(when (and multi all-main? (not any-variant?))
|
||||
[:button {:class (stl/css :combine-variant-button)
|
||||
:on-click on-combine-as-variants}
|
||||
[:span (tr "workspace.shape.menu.combine-as-variants")]])
|
||||
|
||||
(when (dbg/enabled? :display-touched)
|
||||
[:div ":touched " (str (:touched shape))])])])))
|
||||
|
||||
|
||||
@@ -808,3 +808,22 @@
|
||||
right: $s-2;
|
||||
top: $s-2;
|
||||
}
|
||||
|
||||
.combine-variant-button {
|
||||
@include buttonStyle;
|
||||
@include uppercaseTitleTipography;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: $s-8;
|
||||
border-radius: $br-8;
|
||||
background-color: var(--assets-item-background-color);
|
||||
color: var(--color-foreground-secondary);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--assets-item-background-color-hover);
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,7 +933,7 @@
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")]
|
||||
(st/emit! (dw/relocate-shapes #{child-id} id 0))))))
|
||||
(st/emit! (dwsh/relocate-shapes #{child-id} id 0))))))
|
||||
|
||||
:insertChild
|
||||
(fn [index child]
|
||||
@@ -953,7 +953,7 @@
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")]
|
||||
(st/emit! (dw/relocate-shapes #{child-id} id index))))))
|
||||
(st/emit! (dwsh/relocate-shapes #{child-id} id index))))))
|
||||
|
||||
;; Only for frames
|
||||
:addFlexLayout
|
||||
|
||||
@@ -6872,6 +6872,13 @@ msgstr "Create annotation"
|
||||
msgid "workspace.shape.menu.create-artboard-from-selection"
|
||||
msgstr "Selection to board"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:385
|
||||
msgid "workspace.shape.menu.combine-as-variants"
|
||||
msgstr "Combine as variants"
|
||||
|
||||
msgid "workspace.shape.menu.combine-as-variants-error"
|
||||
msgstr "Components need to be in the same page"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:573
|
||||
msgid "workspace.shape.menu.create-component"
|
||||
msgstr "Create component"
|
||||
|
||||
@@ -6850,6 +6850,13 @@ msgstr "Crear una nota"
|
||||
msgid "workspace.shape.menu.create-artboard-from-selection"
|
||||
msgstr "Tablero de selección"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:385
|
||||
msgid "workspace.shape.menu.combine-as-variants"
|
||||
msgstr "Combinar como variantes"
|
||||
|
||||
msgid "workspace.shape.menu.combine-as-variants-error"
|
||||
msgstr "Los componentes tienen que estar en la misma página"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:573
|
||||
msgid "workspace.shape.menu.create-component"
|
||||
msgstr "Crear componente"
|
||||
|
||||
Reference in New Issue
Block a user