0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-19 11:11:21 -05:00

🎉 Component swap

This commit is contained in:
Pablo Alba 2023-09-28 12:47:01 +02:00 committed by Andrés Moya
parent 917e6425d1
commit fac72a5874
26 changed files with 837 additions and 348 deletions

View file

@ -463,20 +463,22 @@
(d/update-in-when data [:components component-id :objects] reg-objects))))
(defmethod process-change :mov-objects
[data {:keys [parent-id shapes index page-id component-id ignore-touched after-shape]}]
[data {:keys [parent-id shapes index page-id component-id ignore-touched after-shape component-swap]}]
(letfn [(calculate-invalid-targets [objects shape-id]
(let [reduce-fn #(into %1 (calculate-invalid-targets objects %2))]
(->> (get-in objects [shape-id :shapes])
(reduce reduce-fn #{shape-id}))))
;; Avoid placing a shape as a direct or indirect child of itself,
;; or inside its main component if it's in a copy.
;; or inside its main component if it's in a copy,
;; or inside a copy
(is-valid-move? [objects shape-id]
(let [invalid-targets (calculate-invalid-targets objects shape-id)]
(and (contains? objects shape-id)
(not (invalid-targets parent-id))
(not (cph/components-nesting-loop? objects shape-id parent-id))
#_(cph/valid-frame-target? objects parent-id shape-id))))
(or component-swap
(not (ctk/in-component-copy? (get objects parent-id))))))) ;; We don't want to change the structure of component copies
(insert-items [prev-shapes index shapes]
(let [prev-shapes (or prev-shapes [])]

View file

@ -308,9 +308,12 @@
(defn change-parent
([changes parent-id shapes]
(change-parent changes parent-id shapes nil))
(change-parent changes parent-id shapes nil {}))
([changes parent-id shapes index]
(change-parent changes parent-id shapes index {}))
([changes parent-id shapes index options]
(assert-page-id! changes)
(assert-objects! changes)
(let [objects (lookup-objects changes)
@ -323,7 +326,9 @@
:shapes (->> shapes reverse (mapv :id))}
(some? index)
(assoc :index index))
(assoc :index index)
(:component-swap options)
(assoc :component-swap true))
mk-undo-change
(fn [undo-changes shape]

View file

@ -531,6 +531,14 @@
(merge-path other-path item))))
[other-path last-item false]))))
(defn prev-path
"Remove the last item of the path."
[path]
(let [split (split-path path)]
(if (= 1 (count split))
""
(join-path (butlast split)))))
(defn compact-name
"Append the first item of the path and the name."
[path name]

View file

@ -134,12 +134,19 @@
[shape]
(some? (:shape-ref shape)))
(defn in-component-copy-not-head?
"Check if the shape is inside a component non-main instance and
is not the head of a subinstance."
[shape]
(and (some? (:shape-ref shape))
(nil? (:component-id shape))))
(defn in-component-copy-not-root?
"Check if the shape is inside a component non-main instance and
is not the root shape."
[shape]
(and (some? (:shape-ref shape))
(nil? (:component-id shape))))
(nil? (:component-root shape))))
(defn main-instance-of?
"Check if this shape is the root of the main instance of the given component."

View file

@ -344,3 +344,22 @@
(if (= parent-id uuid/zero)
current-top
(get-top-instance objects parent current-top))))
(defn get-first-not-copy-parent
"Go trough the parents until we find a shape that is not a copy of a component."
[objects id]
(let [shape (get objects id)]
(if (ctk/in-component-copy? shape)
(get-first-not-copy-parent objects (:parent-id shape))
shape)))
(defn has-any-copy-parent?
"Check if the shape has any parent that is a copy of a component."
[objects shape]
(let [parent (get objects (:parent-id shape))]
(if (nil? parent)
false
(if (ctk/in-component-copy? parent)
true
(has-any-copy-parent? objects (:parent-id shape))))))

View file

@ -73,6 +73,18 @@
width: 100%;
align-items: center;
justify-content: space-between;
svg {
height: 8px;
width: 8px;
fill: $color-gray-20;
margin-right: 1rem;
transform: rotate(180deg);
}
&.back {
cursor: pointer;
}
}
}
@ -585,6 +597,26 @@
}
}
}
&.copy {
flex-wrap: wrap;
border-radius: 8px;
border: 1px solid $color-gray-60;
padding: 0.5rem;
cursor: pointer;
.component-name {
width: 80%;
color: $color-white;
}
.component-parent-name {
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
padding-left: calc(0.5rem + 16px);
color: $color-gray-40;
}
}
}
.grid-option .custom-select {
@ -2554,3 +2586,125 @@
}
}
}
.component-swap {
.search-block {
margin: 0.7rem 0.5rem 0.2rem 0.2rem;
height: 2.1rem;
width: 100%;
}
svg {
fill: $color-gray-20;
height: 0.7rem;
width: 0.7rem;
cursor: pointer;
}
.search-block {
border: 1px solid $color-gray-30;
margin: 0.6rem 0.5rem 0.2rem 0.2rem;
padding: $size-1 $size-2;
display: flex;
align-items: center;
&:hover {
border-color: $color-gray-20;
}
&:focus-within {
border-color: $color-primary;
}
& .search-input {
background-color: $color-gray-50;
border: none;
color: $color-gray-10;
font-size: $fs12;
margin: 0;
padding: 0;
flex-grow: 1;
&:focus {
color: lighten($color-gray-10, 8%);
outline: none;
}
}
& .search-icon {
display: flex;
align-items: center;
svg {
fill: $color-gray-30;
height: 16px;
width: 16px;
}
&.close {
transform: rotate(45deg);
cursor: pointer;
}
}
}
.component-path {
display: flex;
margin: 0.4rem 0 0 0.4rem;
cursor: pointer;
svg {
height: 8px;
width: 8px;
margin-right: 0.5rem;
transform: rotate(180deg);
}
}
.component-list {
margin: 0.7rem 0.5rem 0.5rem 0.5rem;
}
.component-item {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
cursor: pointer;
svg {
background-color: $color-canvas;
border-radius: $br4;
border: 2px solid transparent;
height: 24px;
width: 24px;
margin-right: $size-2;
}
.selected {
color: $color-primary;
}
&:hover {
color: $color-primary;
}
&.disabled {
cursor: auto;
color: $color-gray-30;
}
}
.component-group {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
justify-content: space-between;
cursor: pointer;
height: 24px;
svg {
height: 8px;
width: 8px;
}
&:hover {
color: $color-primary;
}
}
}

View file

@ -921,7 +921,7 @@
component-shape (ctn/get-component-shape objects shape)
component-shape-parent (ctn/get-component-shape objects parent)
detach? (and (ctk/in-component-copy-not-root? shape)
detach? (and (ctk/in-component-copy-not-head? shape)
(not= (:id component-shape)
(:id component-shape-parent)))
deroot? (and (ctk/instance-root? shape)
@ -1834,6 +1834,11 @@
;; Calculate position for the pasted elements
[frame-id parent-id delta index] (calculate-paste-position state mouse-pos in-viewport?)
;; We don't want to change the structure of component copies
;; If the parent-id or the frame-id are component-copies, we need to get the first not copy parent
parent-id (:id (ctn/get-first-not-copy-parent page-objects parent-id))
frame-id (:id (ctn/get-first-not-copy-parent page-objects frame-id))
process-shape
(fn [_ shape]
(let [parent (get page-objects parent-id)
@ -1841,7 +1846,7 @@
component-shape-parent (ctn/get-component-shape page-objects parent)
;; if foreign instance, or a shape belonging to another component, detach the shape
detach? (or (foreign-instance? shape paste-objects state)
(and (ctk/in-component-copy-not-root? shape)
(and (ctk/in-component-copy-not-head? shape)
(not= (:id component-shape)
(:id component-shape-parent))))
assign-shapes? (and (or (cph/group-shape? shape)

View file

@ -101,7 +101,7 @@
;; Shapes that are in a component, but are not root, must be detached,
;; because they will be now children of a non instance group.
shapes-to-detach (filter ctk/in-component-copy-not-root? shapes)
shapes-to-detach (filter ctk/in-component-copy-not-head? shapes)
;; Look at the `get-empty-groups-after-group-creation`
;; docstring to understand the real purpose of this code

View file

@ -32,6 +32,7 @@
[app.main.data.workspace.notifications :as-alias dwn]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.specialized-panel :as dwsp]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.thumbnails :as dwt]
[app.main.data.workspace.undo :as dwu]
@ -744,6 +745,57 @@
(rx/map #(update-component-sync (:id %) file-id (uuid/next)) (rx/from shapes))
(rx/of (dwu/commit-undo-transaction undo-id)))))))
(defn- find-shape-index
[objects id shape-id]
(let [object (get objects id)]
(when object
(let [shapes (:shapes object)]
(or (->> shapes
(map-indexed (fn [index shape] [shape index]))
(filter #(= shape-id (first %)))
first
second)
0)))))
(defn component-swap
"Swaps a component with another one"
[shape file-id id-new-component]
(dm/assert! (uuid? id-new-component))
(dm/assert! (uuid? file-id))
(ptk/reify ::component-swap
ptk/WatchEvent
(watch [it state _]
(let [page (wsh/lookup-page state)
libraries (wsh/get-libraries state)
objects (:objects page)
index (find-shape-index objects (:parent-id shape) (:id shape))
position (gpt/point (:x shape) (:y shape))
changes (-> (pcb/empty-changes it (:id page))
(pcb/with-objects objects))
[new-shape changes]
(dwlh/generate-instantiate-component changes
objects
file-id
id-new-component
position
page
libraries)
changes (pcb/change-parent changes (:parent-id shape) [new-shape] index {:component-swap true})
undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(ptk/data-event :layout/update [(:id new-shape)])
(dws/select-shapes (d/ordered-set (:id new-shape)))
(dwsh/delete-shapes nil (d/ordered-set (:id shape)) {:component-swap true})
(dwu/commit-undo-transaction undo-id)
(dwsp/open-specialized-panel :component-swap [(assoc new-shape :parent-id (:parent-id shape))]))))))
(def valid-asset-types
#{:colors :components :typographies})

View file

@ -123,7 +123,8 @@
(update [_ state]
(-> state
(update-in [:workspace-local :selected] d/toggle-selection id toggle?)
(assoc-in [:workspace-local :last-selected] id)))
(assoc-in [:workspace-local :last-selected] id)
(dissoc :specialized-panel)))
ptk/WatchEvent
(watch [_ state _]
@ -188,7 +189,8 @@
(update [_ state]
(-> state
(update-in [:workspace-local :selected] disj id)
(update :workspace-local dissoc :last-selected)))))
(update :workspace-local dissoc :last-selected)
(dissoc :specialized-panel)))))
(defn shift-select-shapes
([id]
@ -206,7 +208,8 @@
(-> state
(assoc-in [:workspace-local :selected]
(set/union selection append-to-selection))
(update :workspace-local assoc :last-selected id)))))))
(update :workspace-local assoc :last-selected id)
(dissoc :specialized-panel)))))))
(defn select-shapes
[ids]
@ -223,7 +226,9 @@
ids (if (d/not-empty? focus)
(cpf/filter-not-focus objects focus ids)
ids)]
(assoc-in state [:workspace-local :selected] ids)))
(-> state
(assoc-in [:workspace-local :selected] ids)
(dissoc :specialized-panel))))
ptk/WatchEvent
(watch [_ state _]
@ -277,7 +282,9 @@
(update :workspace-local
#(-> %
(assoc :selected (d/ordered-set))
(dissoc :selected-frame))))))))
(dissoc :selected-frame)))
:allways
(dissoc :specialized-panel))))))
;; --- Select Shapes (By selrect)
@ -631,7 +638,11 @@
(when (or (not move-delta?) (nil? (get-in state [:workspace-local :transform])))
(let [page (wsh/lookup-page state)
objects (:objects page)
selected (wsh/lookup-selected state)]
selected (->> (wsh/lookup-selected state)
(map #(get objects %))
(remove #(ctk/in-component-copy-not-root? %)) ;; We don't want to change the structure of component copies
(map :id)
set)]
(when (seq selected)
(let [obj (get objects (first selected))
delta (if move-delta?

View file

@ -12,7 +12,6 @@
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
[app.common.schema :as sm]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.page :as ctp]
[app.common.types.shape :as cts]
@ -133,8 +132,9 @@
(declare update-shape-flags)
(defn delete-shapes
([ids] (delete-shapes nil ids))
([page-id ids]
([ids] (delete-shapes nil ids {}))
([page-id ids] (delete-shapes page-id ids {}))
([page-id ids options]
(dm/assert! (sm/set-of-uuid? ids))
(ptk/reify ::delete-shapes
ptk/WatchEvent
@ -154,11 +154,11 @@
;; Look for shapes that are inside a component copy, but are
;; not the root. In this case, they must not be deleted,
;; but hidden (to be able to recover them more easily).
(let [shape (get objects shape-id)
component-shape (ctn/get-component-shape objects shape)]
(and (ctk/in-component-copy? shape)
(not= shape component-shape)
(not (ctk/main-instance? component-shape)))))
;; Unless we are doing a component swap, in which case we want
;; to delete the old shape
(let [shape (get objects shape-id)]
(and (ctn/has-any-copy-parent? objects shape)
(not (:component-swap options)))))
[ids-to-delete ids-to-hide]
(if components-v2
@ -347,6 +347,11 @@
frame-id
(:parent-id base))
;; If the parent-id or the frame-id are component-copies, we need to get the first not copy parent
parent-id (:id (ctn/get-first-not-copy-parent objects parent-id)) ;; We don't want to change the structure of component copies
frame-id (:id (ctn/get-first-not-copy-parent objects frame-id))
shape (cts/setup-shape
(-> attrs
(assoc :type type)

View file

@ -68,8 +68,9 @@
:cut {:tooltip (ds/meta "X")
:command (ds/c-mod "x")
:subsections [:edit]
:fn #(emit-when-no-readonly (dw/copy-selected)
(dw/delete-selected))}
:fn #(emit-when-no-readonly
(dw/copy-selected)
(dw/delete-selected))}
:paste {:tooltip (ds/meta "V")
:disabled true

View file

@ -0,0 +1,31 @@
;; 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) KALEIDOS INC
(ns app.main.data.workspace.specialized-panel
(:require
[beicon.core :as rx]
[potok.core :as ptk]))
(defn interrupt? [e] (= e :interrupt))
(def clear-specialized-panel
(ptk/reify ::clear-specialized-panel
ptk/UpdateEvent
(update [_ state]
(dissoc state :specialized-panel))))
(defn open-specialized-panel
[type shapes]
(ptk/reify ::open-specialized-panel
ptk/UpdateEvent
(update [_ state]
(assoc state :specialized-panel {:type type :shapes shapes}))
ptk/WatchEvent
(watch [_ _ stream]
(->> stream
(rx/filter interrupt?)
(rx/take 1)
(rx/map (constantly clear-specialized-panel))))))

View file

@ -513,10 +513,11 @@
:else
[move-vector nil])
nesting-loop? (some #(cph/components-nesting-loop? objects (:id %) target-frame) shapes)]
nesting-loop? (some #(cph/components-nesting-loop? objects (:id %) target-frame) shapes)
is-component-copy? (ctk/in-component-copy? (get objects target-frame))]
(cond-> (dwm/create-modif-tree ids (ctm/move-modifiers move-vector))
(not nesting-loop?)
(and (not nesting-loop?) (not is-component-copy?))
(dwm/build-change-frame-modifiers objects selected target-frame drop-index cell-data)
:always
(dwm/set-modifiers false false {:snap-ignore-axis snap-ignore-axis}))))))
@ -820,7 +821,7 @@
shape-ids-to-detach
(reduce (fn [result shape]
(if (and (some? shape) (ctk/in-component-copy-not-root? shape))
(if (and (some? shape) (ctk/in-component-copy-not-head? shape))
(let [shape-component (ctn/get-component-shape objects shape)]
(if (= (:id frame-component) (:id shape-component))
result

View file

@ -573,3 +573,6 @@
(defn workspace-preview-blend-by-id [id]
(l/derived (l/key id) workspace-preview-blend =))
(def specialized-panel
(l/derived :specialized-panel st/state))

View file

@ -118,7 +118,9 @@
on-drag-start
(fn [event]
(if (or disabled (not draggable?))
(dom/prevent-default event)
(do
(dom/stop-propagation event)
(dom/prevent-default event))
(do
(dom/stop-propagation event)
(dnd/set-data! event data-type data)
@ -178,7 +180,8 @@
on-mount
(fn []
(let [dom (mf/ref-val ref)]
(.setAttribute dom "draggable" draggable?)
(.setAttribute dom "draggable" true) ;; In firefox it needs to be draggable for problems with event handling.
;; It will stop the drag operation in on-drag-start
;; Register all events in the (default) bubble mode, so that they
;; are captured by the most leaf item. The handler will stop

View file

@ -444,7 +444,7 @@
has-component? (some true? (map #(ctk/instance-head? %) shapes))
is-component? (and single? (-> shapes first :component-id some?))
in-copy-not-root? (some true? (map #(ctk/in-component-copy-not-root? %) shapes))
in-copy-not-root? (some true? (map #(ctk/in-component-copy-not-head? %) shapes))
objects (deref refs/workspace-page-objects)
touched? (and single? (cph/component-touched? objects (:id (first shapes))))

View file

@ -10,6 +10,8 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.pages.helpers :as cph]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
@ -145,10 +147,15 @@
(mf/use-fn
(mf/deps id index objects)
(fn [side _data]
(if (= side :center)
(st/emit! (dw/relocate-selected-shapes id 0))
(let [to-index (if (= side :top) (inc index) index)
parent-id (cph/get-parent-id objects id)]
(let [to-index (cond
(= side :center) 0
(= side :top) (inc index)
:else index)
parent-id (if (= side :center)
id
(cph/get-parent-id objects id))
parent (get objects parent-id)]
(when-not (ctk/in-component-copy? parent) ;; We don't want to change the structure of component copies
(st/emit! (dw/relocate-selected-shapes parent-id to-index))))))
on-hold
@ -176,7 +183,10 @@
:data {:id (:id item)
:index index
:name (:name item)}
:draggable? (and sortable? (not read-only?)))
:draggable? (and
sortable?
(not read-only?)
(not (ctn/has-any-copy-parent? objects item)))) ;; We don't want to change the structure of component copies
ref (mf/use-ref)
depth (+ depth 1)
@ -372,7 +382,7 @@
[:div.toggle-element {:class (when ^boolean hidden? "selected")
:title (if (:hidden item)
(tr "workspace.shape.menu.show")
(tr "workspace.shape.menu.hide"))
(tr "workspace.shape.menu.hide"))
:on-click toggle-visibility}
(if ^boolean hidden? i/eye-closed i/eye)]
[:div.block-element {:class (when ^boolean blocked? "selected")

View file

@ -21,6 +21,7 @@
[app.main.ui.viewer.inspect.right-sidebar :as hrs]
[app.main.ui.workspace.sidebar.options.menus.align :refer [align-options]]
[app.main.ui.workspace.sidebar.options.menus.bool :refer [bool-options]]
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu]]
[app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu]]
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
[app.main.ui.workspace.sidebar.options.menus.interactions :refer [interactions-menu]]
@ -67,6 +68,13 @@
:page-id page-id
:file-id file-id}]]))
(mf/defc specialized-panel
{::mf/wrap [mf/memo]}
[{:keys [panel]}]
(when (= (:type panel) :component-swap)
[:& component-menu {:shape (first (:shapes panel)) :swap-opened? true}]))
(mf/defc options-content
{::mf/wrap [mf/memo]}
[{:keys [selected section shapes shapes-with-children page-id file-id on-change-section on-expand]}]
@ -76,6 +84,7 @@
shared-libs (mf/deref refs/workspace-libraries)
edition (mf/deref refs/selected-edition)
grid-edition (mf/deref refs/workspace-grid-edition)
sp-panel (mf/deref refs/specialized-panel)
selected-shapes (into [] (keep (d/getf objects)) selected)
first-selected-shape (first selected-shapes)
@ -116,6 +125,9 @@
{:ids [edition]
:values (get objects edition)}]
(not (nil? sp-panel))
[:& specialized-panel {:panel sp-panel}]
(d/not-empty? drawing)
[:& shape-options
{:shape (:object drawing)
@ -177,6 +189,10 @@
{:ids [edition]
:values (get objects edition)}]
sp-panel
[:& specialized-panel {:panel sp-panel}]
(d/not-empty? drawing)
[:& shape-options
{:shape (:object drawing)

View file

@ -11,10 +11,13 @@
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf]
[app.common.uuid :as uuid]
[app.main.data.modal :as modal]
[app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.specialized-panel :as dwsp]
[app.main.refs :as refs]
[app.main.render :refer [component-svg]]
[app.main.store :as st]
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.components.dropdown :refer [dropdown]]
@ -23,6 +26,7 @@
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
@ -144,9 +148,153 @@
(when (or @editing? creating?)
[:div.counter (str @size "/300")])]])))
(mf/defc component-swap
[{:keys [shapes] :as props}]
(let [shape (first shapes)
new-css-system (mf/use-ctx ctx/new-css-system)
current-file-id (mf/use-ctx ctx/current-file-id)
workspace-file (deref refs/workspace-file)
workspace-data (deref refs/workspace-data)
workspace-libraries (deref refs/workspace-libraries)
objects (deref refs/workspace-page-objects)
libraries (assoc workspace-libraries current-file-id (assoc workspace-file :data workspace-data))
filters* (mf/use-state
{:term ""
:file-id (:component-file shape)
:path (cph/prev-path (:name shape))})
filters (deref filters*)
components (-> (get-in libraries [(:file-id filters) :data :components])
vals)
components (if (str/empty? (:term filters))
components
(filter #(str/includes? (str/lower (:name %)) (str/lower (:term filters))) components))
groups (->> (map :path components)
(filter #(= (cph/prev-path (:path %)) (:path filters)))
(remove str/empty?)
distinct
(map #(hash-map :name %)))
components (filter #(= (:path %) (:path filters)) components)
items (->> (concat groups components)
(sort-by :name))
;; Get the ids of the components and its root-shapes that are parents of the current shape, to avoid loops
get-comps-ids (fn get-comps-ids [shape ids]
(if (uuid/zero? (:id shape))
ids
(let [ids (if (ctk/instance-head? shape)
(conj ids (:id shape) (:component-id shape))
ids)]
(get-comps-ids (get objects (:parent-id shape)) ids))))
parent-components (set (get-comps-ids (get objects (:parent-id shape)) []))
on-library-change
(mf/use-fn
(fn [event]
(let [value (or (-> (dom/get-target event)
(dom/get-value))
(as-> (dom/get-current-target event) $
(dom/get-attribute $ "data-test")))
value (uuid/uuid value)]
(swap! filters* assoc :file-id value :term "" :path ""))))
on-search-term-change
(mf/use-fn
(mf/deps new-css-system)
(fn [event]
;; NOTE: When old-css-system is removed this function will recibe value and event
;; Let won't be necessary any more
(let [value (if ^boolean new-css-system
event
(dom/get-target-val event))]
(swap! filters* assoc :term value))))
on-search-clear-click
(mf/use-fn #(swap! filters* assoc :term ""))
on-go-back
(mf/use-fn
(mf/deps (:path filters))
#(swap! filters* assoc :path (cph/prev-path (:path filters))))
on-enter-group
(mf/use-fn #(swap! filters* assoc :path %))
handle-key-down
(mf/use-fn
(fn [event]
(let [enter? (kbd/enter? event)
esc? (kbd/esc? event)
node (dom/event->target event)]
(when ^boolean enter? (dom/blur! node))
(when ^boolean esc? (dom/blur! node)))))]
[:div.component-swap
[:div.element-set-title
[:span (tr "workspace.options.component.swap")]]
[:div.component-swap-content
[:div.search-block
[:input.search-input
{:placeholder (str (tr "labels.search") " " (get-in libraries [(:file-id filters) :name]))
:type "text"
:value (:term filters)
:on-change on-search-term-change
:on-key-down handle-key-down}]
(if ^boolean (str/empty? (:term filters))
[:div.search-icon
i/search]
[:div.search-icon.close
{:on-click on-search-clear-click}
i/close])]
[:select.input-select {:value (:file-id filters)
:data-mousetrap-dont-stop true
:on-change on-library-change}
(for [library (vals libraries)]
[:option {:key (:id library) :value (:id library)} (:name library)])]
(when-not (str/empty? (:path filters))
[:div.component-path {:on-click on-go-back}
[:span i/arrow-slide]
[:span (-> (cph/split-path (:path filters))
last)]])
[:div.component-list
(for [item items]
(if (:id item)
(let [data (get-in libraries [(:file-id filters) :data])
container (ctf/get-component-page data item)
root-shape (ctf/get-component-root data item)
loop? (or (contains? parent-components (:main-instance-id item))
(contains? parent-components (:id item)))]
[:div.component-item
{:class (stl/css-case :disabled loop?)
:key (:id item)
:on-click #(when-not loop?
(st/emit!
(dwl/component-swap shape (:file-id filters) (:id item))))}
[:& component-svg {:root-shape root-shape
:objects (:objects container)}]
[:span.component-name
{:class (stl/css-case :selected (= (:id item) (:component-id shape)))}
(:name item)]])
[:div.component-group {:key (uuid/next) :on-click #(on-enter-group (:name item))}
[:span (:name item)]
[:span i/arrow-slide]]))]]]))
(mf/defc component-menu
[{:keys [ids values shape] :as props}]
(let [new-css-system (mf/use-ctx ctx/new-css-system)
[{:keys [shape swap-opened?] :as props}]
(let [[ids values] [[(:id shape)] (select-keys shape component-attrs)]
new-css-system (mf/use-ctx ctx/new-css-system)
current-file-id (mf/use-ctx ctx/current-file-id)
components-v2 (mf/use-ctx ctx/components-v2)
@ -171,6 +319,7 @@
main-instance? (if components-v2
(ctk/main-instance? values)
true)
can-swap? (and components-v2 (not main-instance?))
main-component? (:main-instance values)
lacks-annotation? (nil? (:annotation values))
@ -342,13 +491,20 @@
[:& component-annotation {:id id :values values :shape shape :component component}])])]
[:div.element-set
[:div.element-set-title
[:span (tr "workspace.options.component")]
[:div.element-set-title {:class (stl/css-case :back swap-opened?)
:on-click #(when swap-opened? (st/emit! :interrupt))}
[:div
(when swap-opened?
[:span
i/arrow-slide])
[:span (tr "workspace.options.component")]]
[:span (if main-instance?
(tr "workspace.options.component.main")
(tr "workspace.options.component.copy"))]]
[:div.element-set-content
[:div.row-flex.component-row
{:class (stl/css-case :copy can-swap?)
:on-click #(when can-swap? (st/emit! (dwsp/open-specialized-panel :component-swap [shape])))}
(if main-instance?
i/component
i/component-copy)
@ -391,7 +547,14 @@
[(tr "workspace.shape.menu.reset-overrides") do-reset-component])
(when can-update-main?
[(tr "workspace.shape.menu.update-main") do-update-remote-component])
[(tr "workspace.shape.menu.go-main") do-navigate-component-file]])))}]]]
[(tr "workspace.shape.menu.go-main") do-navigate-component-file]])))}]]
(when components-v2
(when can-swap?
[:div.component-parent-name
(cph/merge-path-item (:path component) (:name component))])]
(when swap-opened?
[:& component-swap {:shapes [shape]}])
(when (and (not swap-opened?) components-v2)
[:& component-annotation {:id id :values values :shape shape :component component}])]]))))

View file

@ -12,7 +12,7 @@
[app.main.ui.hooks :as hooks]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]]
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
[app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]]
@ -36,7 +36,6 @@
constraint-values (select-keys shape constraint-attrs)
layout-container-values (select-keys shape layout-container-flex-attrs)
layout-item-values (select-keys shape layout-item-attrs)
[comp-ids comp-values] [[(:id shape)] (select-keys shape component-attrs)]
ids (hooks/use-equal-memo ids)
@ -63,9 +62,7 @@
:values measure-values
:type type
:shape shape}]
[:& component-menu {:ids comp-ids
:values comp-values
:shape shape}]
[:& component-menu {:shape shape}]
(when (or (not is-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids ids
:values constraint-values}])

View file

@ -14,7 +14,7 @@
[app.main.ui.hooks :as hooks]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]]
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]]
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
@ -66,7 +66,6 @@
[stroke-ids stroke-values] (get-attrs [shape] objects :stroke)
[text-ids text-values] (get-attrs [shape] objects :text)
[svg-ids svg-values] [[(:id shape)] (select-keys shape [:svg-attrs])]
[comp-ids comp-values] [[(:id shape)] (select-keys shape component-attrs)]
[layout-item-ids layout-item-values] (get-attrs [shape] objects :layout-item)]
@ -74,7 +73,7 @@
:options true)}
[:& layer-menu {:type type :ids layer-ids :values layer-values}]
[:& measures-menu {:type type :ids measure-ids :values measure-values :shape shape}]
[:& component-menu {:ids comp-ids :values comp-values :shape shape}] ;;remove this in components-v2
[:& component-menu {:shape shape}] ;;remove this in components-v2
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)

View file

@ -18,6 +18,7 @@
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.media :as dwm]
[app.main.data.workspace.path :as dwdp]
[app.main.data.workspace.specialized-panel :as dwsp]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.streams :as ms]
@ -84,7 +85,8 @@
left-click?
(do
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?))
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?)
dwsp/clear-specialized-panel)
(when (and (not= edition id) (or text-editing? grid-editing?))
(st/emit! dw/clear-edition-mode))

View file

@ -90,63 +90,58 @@
(t/deftest test-touched-children-add
(t/async done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"
:fill-color clr/white
:fill-opacity 1})
(thp/make-component :main1 :component1
[(thp/id :shape1)])
(thp/instantiate-component :instance1
(thp/id :component1))
(thp/sample-shape :shape2 :circle
{:name "Circle 1"}))
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"
:fill-color clr/white
:fill-opacity 1})
(thp/make-component :main1 :component1
[(thp/id :shape1)])
(thp/instantiate-component :instance1
(thp/id :component1))
(thp/sample-shape :shape2 :circle
{:name "Circle 1"}))
instance1 (thp/get-shape state :instance1)
shape2 (thp/get-shape state :shape2)
instance1 (thp/get-shape state :instance1)
shape2 (thp/get-shape state :shape2)
store (the/prepare-store state done
(fn [new-state]
store (the/prepare-store state done
(fn [new-state]
;; Expected shape tree:
;;
;; [Page]
;; Root Frame
;; Rect 1
;; Rect 1
;; Rect 1* #--> Rect 1
;; #{:shapes-group}
;; [Page: Page 1]
;; Root Frame
;; {Rect 1}
;; Rect1
;; Rect 1 #--> Rect 1
;; Rect 1 ---> Rect 1
;; Circle 1
;; Rect 1 ---> Rect 1
;;
;; [Rect 1]
;; page1 / Rect 1
;;
(let [[[group shape1 shape2] [c-group c-shape1] _component]
(thl/resolve-instance-and-main-allow-dangling
new-state
(thp/id :instance1))]
;; [Component: Rect 1] core.cljs:200:23
;; --> [Page 1] Rect 1
(t/is (= (:name group) "Rect 1"))
(t/is (= (:touched group) #{:shapes-group}))
(t/is (= (:name shape1) "Circle 1"))
(t/is (= (:touched shape1) nil))
(t/is (= (:shape-ref shape1) nil))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:touched shape2) nil))
(t/is (not= (:shape-ref shape2) nil))
(let [[[group shape1] [c-group c-shape1] _component]
(thl/resolve-instance-and-main-allow-dangling
new-state
(thp/id :instance1))]
(t/is (= (:name c-group) "Rect 1"))
(t/is (= (:touched c-group) nil))
(t/is (= (:shape-ref c-group) nil))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:touched c-shape1) nil))
(t/is (= (:shape-ref c-shape1) nil)))))]
(t/is (= (:name group) "Rect 1"))
(t/is (nil? (:touched group)))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:touched shape1) nil))
(t/is (not= (:shape-ref shape1) nil))
(ptk/emit!
store
(dw/relocate-shapes #{(:id shape2)} (:id instance1) 0)
:the/end))))
(t/is (= (:name c-group) "Rect 1"))
(t/is (= (:touched c-group) nil))
(t/is (= (:shape-ref c-group) nil))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:touched c-shape1) nil))
(t/is (= (:shape-ref c-shape1) nil)))))]
(ptk/emit!
store
(dw/relocate-shapes #{(:id shape2)} (:id instance1) 0) ;; We cant't change the structure of component copies, so this operation will do nothing
:the/end))))
(t/deftest test-touched-children-delete
(t/async done
@ -215,78 +210,78 @@
(t/deftest test-touched-children-move
(t/async done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/sample-shape :shape2 :rect
{:name "Rect 2"})
(thp/sample-shape :shape3 :rect
{:name "Rect 3"})
(thp/make-component :main1 :component1
[(thp/id :shape1)
(thp/id :shape2)
(thp/id :shape3)])
(thp/instantiate-component :instance1
(thp/id :component1)))
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/sample-shape :shape2 :rect
{:name "Rect 2"})
(thp/sample-shape :shape3 :rect
{:name "Rect 3"})
(thp/make-component :main1 :component1
[(thp/id :shape1)
(thp/id :shape2)
(thp/id :shape3)])
(thp/instantiate-component :instance1
(thp/id :component1)))
[group1' shape1']
(thl/resolve-instance state (thp/id :instance1))
[group1' shape1']
(thl/resolve-instance state (thp/id :instance1))
store (the/prepare-store state done
(fn [new-state]
store (the/prepare-store state done
(fn [new-state]
;; Expected shape tree:
;;
;; [Page]
;; Root Frame
;; Component 1
;; Rect 1
;; Rect 2
;; Rect 3
;; Component 1* #--> Component 1
;; #{:shapes-group}
;; Rect 2 ---> Rect 2
;; Rect 1 ---> Rect 1
;; Rect 3 ---> Rect 3
;;
;; [Component 1]
;; page1 / Component 1
;;
(let [[[group shape1 shape2 shape3]
[c-group c-shape1 c-shape2 c-shape3] _component]
(thl/resolve-instance-and-main-allow-dangling
new-state
(thp/id :instance1))]
;; [Page: Page 1]
;; Root Frame
;; {Component 1} #
;; Rect 1
;; Rect 2
;; Rect 3
;; Component 1 #--> Component 1
;; Rect 1 ---> Rect 1
;; Rect 2 ---> Rect 2
;; Rect 3 ---> Rect 3
;;
;; ========= Local library
;;
;; [Component: Component 1]
;; --> [Page 1] Component 1
(t/is (= (:name group) "Component 1"))
(t/is (= (:touched group) #{:shapes-group}))
(t/is (= (:name shape1) "Rect 2"))
(t/is (= (:touched shape1) nil))
(t/is (not= (:shape-ref shape1) nil))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:touched shape2) nil))
(t/is (not= (:shape-ref shape2) nil))
(t/is (= (:name shape3) "Rect 3"))
(t/is (= (:touched shape3) nil))
(t/is (not= (:shape-ref shape3) nil))
(let [[[group shape1 shape2 shape3]
[c-group c-shape1 c-shape2 c-shape3] _component]
(thl/resolve-instance-and-main-allow-dangling
new-state
(thp/id :instance1))]
(t/is (= (:name c-group) "Component 1"))
(t/is (= (:touched c-group) nil))
(t/is (= (:shape-ref c-group) nil))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:touched c-shape1) nil))
(t/is (= (:shape-ref c-shape1) nil))
(t/is (= (:name c-shape2) "Rect 2"))
(t/is (= (:touched c-shape2) nil))
(t/is (= (:shape-ref c-shape2) nil))
(t/is (= (:name c-shape3) "Rect 3"))
(t/is (= (:touched c-shape3) nil))
(t/is (= (:shape-ref c-shape3) nil)))))]
(t/is (= (:name group) "Component 1"))
(t/is (nil? (:touched group)))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:touched shape1) nil))
(t/is (not= (:shape-ref shape1) nil))
(t/is (= (:name shape2) "Rect 2"))
(t/is (= (:touched shape2) nil))
(t/is (not= (:shape-ref shape2) nil))
(t/is (= (:name shape3) "Rect 3"))
(t/is (= (:touched shape3) nil))
(t/is (not= (:shape-ref shape3) nil))
(ptk/emit!
store
(dw/relocate-shapes #{(:id shape1')} (:id group1') 2)
:the/end))))
(t/is (= (:name c-group) "Component 1"))
(t/is (= (:touched c-group) nil))
(t/is (= (:shape-ref c-group) nil))
(t/is (= (:name c-shape1) "Rect 1"))
(t/is (= (:touched c-shape1) nil))
(t/is (= (:shape-ref c-shape1) nil))
(t/is (= (:name c-shape2) "Rect 2"))
(t/is (= (:touched c-shape2) nil))
(t/is (= (:shape-ref c-shape2) nil))
(t/is (= (:name c-shape3) "Rect 3"))
(t/is (= (:touched c-shape3) nil))
(t/is (= (:shape-ref c-shape3) nil)))))]
(ptk/emit!
store
(dw/relocate-shapes #{(:id shape1')} (:id group1') 2) ;; We cant't change the structure of component copies, so this operation will do nothing
:the/end))))
(t/deftest test-touched-from-lib
(t/async
@ -1507,109 +1502,97 @@
(t/deftest test-update-children-add
(t/async done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"
:fill-color clr/white
:fill-opacity 1})
(thp/make-component :main1 :component1
[(thp/id :shape1)])
(thp/instantiate-component :instance1
(thp/id :component1))
(thp/instantiate-component :instance2
(thp/id :component1))
(thp/sample-shape :shape2 :circle
{:name "Circle 1"}))
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"
:fill-color clr/white
:fill-opacity 1})
(thp/make-component :main1 :component1
[(thp/id :shape1)])
(thp/instantiate-component :instance1
(thp/id :component1))
(thp/instantiate-component :instance2
(thp/id :component1))
(thp/sample-shape :shape2 :circle
{:name "Circle 1"}))
file (wsh/get-local-file state)
file (wsh/get-local-file state)
instance1 (thp/get-shape state :instance1)
shape2 (thp/get-shape state :shape2)
instance1 (thp/get-shape state :instance1)
shape2 (thp/get-shape state :shape2)
store (the/prepare-store state done
(fn [new-state]
store (the/prepare-store state done
(fn [new-state]
;; Expected shape tree:
;;
;; [Page]
;; Root Frame
;; Rect 1
;; [Page: Page 1]
;; Root Frame
;; {Rect 1} #
;; Rect 1
;; Rect 1 #--> Rect 1
;; Rect 1 ---> Rect 1
;; Rect 1 #--> Rect 1
;; Rect 1 ---> Rect 1
;; Circle 1
;; Rect 1
;; Rect 1 #--> Rect 1
;; Circle 1 ---> Circle 1
;; Rect 1 ---> Rect 1
;; Rect 1 #--> Rect 1
;; Circle 1 ---> Circle 1
;; Rect 1 ---> Rect 1
;;
;; [Rect 1]
;; page1 / Rect 1
;; ========= Local library
;;
(let [[[main1 shape1 shape2]
[c-main1 c-shape1 c-shape2] component1]
(thl/resolve-instance-and-main
new-state
(thp/id :main1))
;; [Component: Rect 1]
;; --> [Page 1] Rect 1
;;
(let [[[main1 shape1]
[c-main1 c-shape1] component1]
(thl/resolve-instance-and-main
new-state
(thp/id :main1))
[[instance1 shape3 shape4]
[c-instance1 c-shape3 c-shape4] component2]
(thl/resolve-instance-and-main
new-state
(thp/id :instance1))
[[instance1 shape2]
[c-instance1 c-shape2] component2]
(thl/resolve-instance-and-main
new-state
(thp/id :instance1))
[[instance2 shape5 shape6]
[c-instance2 c-shape5 c-shape6] component3]
(thl/resolve-instance-and-main
new-state
(thp/id :instance2))]
[[instance2 shape3]
[c-instance2 c-shape3] component3]
(thl/resolve-instance-and-main
new-state
(thp/id :instance2))]
(t/is (= (:name main1) "Rect 1"))
(t/is (= (:touched main1) nil))
(t/is (= (:shape-ref main1) nil))
(t/is (= (:name shape1) "Circle 1"))
(t/is (= (:touched shape1) nil))
(t/is (= (:shape-ref shape1) nil))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:touched shape2) nil))
(t/is (= (:shape-ref shape2) nil))
(t/is (= (:name main1) "Rect 1"))
(t/is (= (:touched main1) nil))
(t/is (= (:shape-ref main1) nil))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:touched shape1) nil))
(t/is (= (:shape-ref shape1) nil))
(t/is (= (:name instance1) "Rect 1"))
(t/is (= (:touched instance1) nil))
(t/is (= (:shape-ref instance1) (:id c-main1)))
(t/is (= (:name shape3) "Circle 1"))
(t/is (= (:touched shape3) nil))
(t/is (= (:shape-ref shape3) (:id c-shape1)))
(t/is (= (:name shape4) "Rect 1"))
(t/is (= (:touched shape4) nil))
(t/is (= (:shape-ref shape4) (:id c-shape2)))
(t/is (= (:name instance1) "Rect 1"))
(t/is (= (:touched instance1) nil))
(t/is (= (:shape-ref instance1) (:id c-main1)))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:touched shape2) nil))
(t/is (= (:shape-ref shape2) (:id c-shape1)))
(t/is (= (:name instance2) "Rect 1"))
(t/is (= (:touched instance2) nil))
(t/is (= (:shape-ref instance2) (:id c-main1)))
(t/is (= (:name shape5) "Circle 1"))
(t/is (= (:touched shape5) nil))
(t/is (= (:shape-ref shape5) (:id c-shape1)))
(t/is (= (:name shape6) "Rect 1"))
(t/is (= (:touched shape6) nil))
(t/is (= (:shape-ref shape4) (:id c-shape2)))
(t/is (= (:name instance2) "Rect 1"))
(t/is (= (:touched instance2) nil))
(t/is (= (:shape-ref instance2) (:id c-main1)))
(t/is (= (:name shape3) "Rect 1"))
(t/is (= (:touched shape3) nil))
(t/is (= (:shape-ref shape2) (:id c-shape1)))
(t/is (= component1 component2 component3))
(t/is (= c-main1 main1))
(t/is (= c-shape1 shape1))
(t/is (= c-shape2 shape2))
(t/is (= c-instance1 c-main1))
(t/is (= c-shape3 c-shape1))
(t/is (= c-shape4 c-shape2))
(t/is (= c-instance2 c-main1))
(t/is (= c-shape5 c-shape1))
(t/is (= c-shape6 c-shape2)))))]
(t/is (= component1 component2 component3))
(t/is (= c-main1 main1))
(t/is (= c-shape1 shape1))
(t/is (= c-instance1 c-main1))
(t/is (= c-shape2 c-shape1))
(t/is (= c-instance2 c-main1))
(t/is (= c-shape3 c-shape1)))))]
(ptk/emit!
store
(dw/relocate-shapes #{(:id shape2)} (:id instance1) 0)
(dwl/update-component-sync (:id instance1) (:id file))
:the/end))))
(ptk/emit!
store
(dw/relocate-shapes #{(:id shape2)} (:id instance1) 0) ;; We cant't change the structure of component copies, so this operation will do nothing
(dwl/update-component-sync (:id instance1) (:id file))
:the/end))))
(t/deftest test-update-children-delete
(t/async done
@ -1724,126 +1707,126 @@
(t/deftest test-update-children-move
(t/async done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/sample-shape :shape2 :rect
{:name "Rect 2"})
(thp/sample-shape :shape3 :rect
{:name "Rect 3"})
(thp/make-component :main1 :component1
[(thp/id :shape1)
(thp/id :shape2)
(thp/id :shape3)])
(thp/instantiate-component :instance1
(thp/id :component1))
(thp/instantiate-component :instance2
(thp/id :component1)))
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect 1"})
(thp/sample-shape :shape2 :rect
{:name "Rect 2"})
(thp/sample-shape :shape3 :rect
{:name "Rect 3"})
(thp/make-component :main1 :component1
[(thp/id :shape1)
(thp/id :shape2)
(thp/id :shape3)])
(thp/instantiate-component :instance1
(thp/id :component1))
(thp/instantiate-component :instance2
(thp/id :component1)))
file (wsh/get-local-file state)
file (wsh/get-local-file state)
[instance1 shape1' _shape2' _shape3']
(thl/resolve-instance state (thp/id :instance1))
[instance1 shape1' _shape2' _shape3']
(thl/resolve-instance state (thp/id :instance1))
store (the/prepare-store state done
(fn [new-state]
store (the/prepare-store state done
(fn [new-state]
;; Expected shape tree:
;;
;; [Page]
;; Root Frame
;; Component 1
;; Rect 2
;; Rect 1
;; Rect 2
;; Rect 3
;; Component 1 #--> Component 1
;; Rect 2 ---> Rect 2
;; Rect 1 ---> Rect 1
;; Rect 2 ---> Rect 2
;; Rect 3 ---> Rect 3
;; Component 1 #--> Component 1
;; Rect 2 ---> Rect 2
;; Rect 1 ---> Rect 1
;; Rect 2 ---> Rect 2
;; Rect 3 ---> Rect 3
;;
;; [Component 1]
;; page1 / Component 1
;;
(let [[[main1 shape1 shape2 shape3]
[c-main1 c-shape1 c-shape2 c-shape3] component1]
(thl/resolve-instance-and-main
new-state
(thp/id :main1))
(let [[[main1 shape1 shape2 shape3]
[c-main1 c-shape1 c-shape2 c-shape3] component1]
(thl/resolve-instance-and-main
new-state
(thp/id :main1))
[[instance1 shape4 shape5 shape6]
[c-instance1 c-shape4 c-shape5 c-shape6] component2]
(thl/resolve-instance-and-main
new-state
(thp/id :instance1))
[[instance1 shape4 shape5 shape6]
[c-instance1 c-shape4 c-shape5 c-shape6] component2]
(thl/resolve-instance-and-main
new-state
(thp/id :instance1))
[[instance2 shape7 shape8 shape9]
[c-instance2 c-shape7 c-shape8 c-shape9] component3]
(thl/resolve-instance-and-main
new-state
(thp/id :instance2))]
[[instance2 shape7 shape8 shape9]
[c-instance2 c-shape7 c-shape8 c-shape9] component3]
(thl/resolve-instance-and-main
new-state
(thp/id :instance2))]
(t/is (= (:name main1) "Component 1"))
(t/is (= (:touched main1) nil))
(t/is (= (:shape-ref main1) nil))
(t/is (= (:name shape1) "Rect 2"))
(t/is (= (:touched shape1) nil))
(t/is (= (:shape-ref shape1) nil))
(t/is (= (:name shape2) "Rect 1"))
(t/is (= (:touched shape2) nil))
(t/is (= (:shape-ref shape2) nil))
(t/is (= (:name shape3) "Rect 3"))
(t/is (= (:touched shape3) nil))
(t/is (= (:shape-ref shape3) nil))
(t/is (= (:name main1) "Component 1"))
(t/is (= (:touched main1) nil))
(t/is (= (:shape-ref main1) nil))
(t/is (= (:name shape1) "Rect 1"))
(t/is (= (:touched shape1) nil))
(t/is (= (:shape-ref shape1) nil))
(t/is (= (:name shape2) "Rect 2"))
(t/is (= (:touched shape2) nil))
(t/is (= (:shape-ref shape2) nil))
(t/is (= (:name shape3) "Rect 3"))
(t/is (= (:touched shape3) nil))
(t/is (= (:shape-ref shape3) nil))
(t/is (= (:name instance1) "Component 1"))
(t/is (= (:touched instance1) nil))
(t/is (= (:shape-ref instance1) (:id c-main1)))
(t/is (= (:name shape4) "Rect 2"))
(t/is (= (:touched shape4) nil))
(t/is (= (:shape-ref shape4) (:id c-shape1)))
(t/is (= (:name shape5) "Rect 1"))
(t/is (= (:touched shape5) nil))
(t/is (= (:shape-ref shape5) (:id c-shape2)))
(t/is (= (:name shape6) "Rect 3"))
(t/is (= (:touched shape6) nil))
(t/is (= (:shape-ref shape6) (:id c-shape3)))
(t/is (= (:name instance1) "Component 1"))
(t/is (= (:touched instance1) nil))
(t/is (= (:shape-ref instance1) (:id c-main1)))
(t/is (= (:name shape4) "Rect 1"))
(t/is (= (:touched shape4) nil))
(t/is (= (:shape-ref shape4) (:id c-shape1)))
(t/is (= (:name shape5) "Rect 2"))
(t/is (= (:touched shape5) nil))
(t/is (= (:shape-ref shape5) (:id c-shape2)))
(t/is (= (:name shape6) "Rect 3"))
(t/is (= (:touched shape6) nil))
(t/is (= (:shape-ref shape6) (:id c-shape3)))
(t/is (= (:name instance2) "Component 1"))
(t/is (= (:touched instance2) nil))
(t/is (= (:shape-ref instance2) (:id c-main1)))
(t/is (= (:name shape7) "Rect 2"))
(t/is (= (:touched shape7) nil))
(t/is (= (:shape-ref shape7) (:id c-shape1)))
(t/is (= (:name shape8) "Rect 1"))
(t/is (= (:touched shape8) nil))
(t/is (= (:shape-ref shape8) (:id c-shape2)))
(t/is (= (:name shape9) "Rect 3"))
(t/is (= (:touched shape9) nil))
(t/is (= (:shape-ref shape9) (:id c-shape3)))
(t/is (= (:name instance2) "Component 1"))
(t/is (= (:touched instance2) nil))
(t/is (= (:shape-ref instance2) (:id c-main1)))
(t/is (= (:name shape7) "Rect 1"))
(t/is (= (:touched shape7) nil))
(t/is (= (:shape-ref shape7) (:id c-shape1)))
(t/is (= (:name shape8) "Rect 2"))
(t/is (= (:touched shape8) nil))
(t/is (= (:shape-ref shape8) (:id c-shape2)))
(t/is (= (:name shape9) "Rect 3"))
(t/is (= (:touched shape9) nil))
(t/is (= (:shape-ref shape9) (:id c-shape3)))
(t/is (= component1 component2 component3))
(t/is (= c-main1 main1))
(t/is (= c-shape1 shape1))
(t/is (= c-shape2 shape2))
(t/is (= c-shape3 shape3))
(t/is (= c-instance1 c-main1))
(t/is (= c-shape4 c-shape4))
(t/is (= c-shape5 c-shape5))
(t/is (= c-shape6 c-shape6))
(t/is (= c-instance2 c-main1))
(t/is (= c-shape7 c-shape7))
(t/is (= c-shape8 c-shape8))
(t/is (= c-shape9 c-shape9)))))]
(t/is (= component1 component2 component3))
(t/is (= c-main1 main1))
(t/is (= c-shape1 shape1))
(t/is (= c-shape2 shape2))
(t/is (= c-shape3 shape3))
(t/is (= c-instance1 c-main1))
(t/is (= c-shape4 c-shape4))
(t/is (= c-shape5 c-shape5))
(t/is (= c-shape6 c-shape6))
(t/is (= c-instance2 c-main1))
(t/is (= c-shape7 c-shape7))
(t/is (= c-shape8 c-shape8))
(t/is (= c-shape9 c-shape9)))))]
(ptk/emit!
store
(dw/relocate-shapes #{(:id shape1')} (:id instance1) 2)
(dwl/update-component-sync (:id instance1) (:id file))
:the/end))))
(ptk/emit!
store
(dw/relocate-shapes #{(:id shape1')} (:id instance1) 2)
(dwl/update-component-sync (:id instance1) (:id file))
:the/end))))
(t/deftest test-update-from-lib
(t/async done

View file

@ -1673,6 +1673,9 @@ msgstr "(you)"
msgid "labels.your-account"
msgstr "Your account"
msgid "labels.search"
msgstr "Search"
#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs
msgid "media.loading"
msgstr "Loading image…"
@ -3590,6 +3593,9 @@ msgstr "Edit an annotation"
msgid "workspace.options.component.main"
msgstr "Main"
msgid "workspace.options.component.swap"
msgstr "Swap component"
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
msgid "workspace.options.constraints"
msgstr "Constraints"

View file

@ -1725,6 +1725,9 @@ msgstr "(tú)"
msgid "labels.your-account"
msgstr "Tu cuenta"
msgid "labels.search"
msgstr "Buscar"
#: src/app/main/data/workspace/persistence.cljs, src/app/main/data/media.cljs
msgid "media.loading"
msgstr "Cargando imagen…"
@ -3666,6 +3669,9 @@ msgstr "Editar una nota"
msgid "workspace.options.component.main"
msgstr "Principal"
msgid "workspace.options.component.swap"
msgstr "Intercambiar componente"
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
msgid "workspace.options.constraints"
msgstr "Restricciones"