🎉 Allow to create a nested component in one step

This commit is contained in:
Andrés Moya 2022-08-16 15:15:02 +02:00
parent 4ad27c3fca
commit 9725dd5fff
6 changed files with 143 additions and 59 deletions

View file

@ -6,6 +6,10 @@
(ns app.common.types.component) (ns app.common.types.component)
(defn instance-root?
[shape]
(some? (:component-id shape)))
(defn instance-of? (defn instance-of?
[shape file-id component-id] [shape file-id component-id]
(and (some? (:component-id shape)) (and (some? (:component-id shape))

View file

@ -59,22 +59,28 @@
;; ---- Components and instances creation ---- ;; ---- Components and instances creation ----
(defn generate-add-component (defn generate-add-component
"If there is exactly one id, and it's a group, use it as root. Otherwise, "If there is exactly one id, and it's a group, and not already a component, use
create a group that contains all ids. Then, make a component with it, it as root. Otherwise, create a group that contains all ids. Then, make a
and link all shapes to their corresponding one in the component." component with it, and link all shapes to their corresponding one in the component."
[it shapes objects page-id file-id components-v2] [it shapes objects page-id file-id components-v2]
(let [[group changes]
(if (and (= (count shapes) 1) (if (and (= (count shapes) 1)
(:component-id (first shapes))) (= (:type (first shapes)) :group)
[(first shapes) (pcb/empty-changes it)] (not (ctk/instance-root? (first shapes))))
(let [name (if (= 1 (count shapes)) (:name (first shapes)) "Component-1")
[path name] (cph/parse-path-name name)
[group changes]
(if (and (= (count shapes) 1)
(= (:type (first shapes)) :group))
[(first shapes) (-> (pcb/empty-changes it page-id) [(first shapes) (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects))] (pcb/with-objects objects))]
(dwg/prepare-create-group it objects page-id shapes name true)) (let [group-name (if (= 1 (count shapes))
(:name (first shapes))
"Component-1")]
(dwg/prepare-create-group it
objects
page-id
shapes
group-name
(not (ctk/instance-root? (first shapes))))))
name (:name group)
[path name] (cph/parse-path-name name)
[new-shape new-shapes updated-shapes] [new-shape new-shapes updated-shapes]
(ctn/make-component-shape group objects file-id components-v2) (ctn/make-component-shape group objects file-id components-v2)
@ -87,7 +93,7 @@
updated-shapes updated-shapes
(:id group) (:id group)
page-id))] page-id))]
[group new-shape changes]))) [group new-shape changes]))
(defn duplicate-component (defn duplicate-component
"Clone the root shape of the component and all children. Generate new "Clone the root shape of the component and all children. Generate new

View file

@ -407,20 +407,19 @@
:accept-style :primary :accept-style :primary
:on-accept do-update-component-in-bulk}))] :on-accept do-update-component-in-bulk}))]
[:* [:*
(when (and (not has-frame?) (not is-component?)) (when (not has-frame?)
[:* [:*
[:& menu-separator] [:& menu-separator]
[:& menu-entry {:title (tr "workspace.shape.menu.create-component") [:& menu-entry {:title (tr "workspace.shape.menu.create-component")
:shortcut (sc/get-tooltip :create-component) :shortcut (sc/get-tooltip :create-component)
:on-click do-add-component}] :on-click do-add-component}]
(when has-component? (when (and has-component? (not single?))
[:* [:*
[:& menu-entry {:title (tr "workspace.shape.menu.detach-instances-in-bulk") [:& menu-entry {:title (tr "workspace.shape.menu.detach-instances-in-bulk")
:shortcut (sc/get-tooltip :detach-component) :shortcut (sc/get-tooltip :detach-component)
:on-click do-detach-component-in-bulk}] :on-click do-detach-component-in-bulk}]
(when (not single?)
[:& menu-entry {:title (tr "workspace.shape.menu.update-components-in-bulk") [:& menu-entry {:title (tr "workspace.shape.menu.update-components-in-bulk")
:on-click do-update-in-bulk}])])]) :on-click do-update-in-bulk}]])])
(when is-component? (when is-component?
;; WARNING: this menu is the same as the context menu at the sidebar. ;; WARNING: this menu is the same as the context menu at the sidebar.

View file

@ -207,7 +207,7 @@
(dump-selected' @st/state)) (dump-selected' @st/state))
(defn dump-tree' (defn dump-tree'
([state ] (dump-tree' state false false)) ([state] (dump-tree' state false false))
([state show-ids] (dump-tree' state show-ids false)) ([state show-ids] (dump-tree' state show-ids false))
([state show-ids show-touched] ([state show-ids show-touched]
(let [page-id (get state :current-page-id) (let [page-id (get state :current-page-id)

View file

@ -4,6 +4,7 @@
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.groups :as dwg] [app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.libraries :as dwl]
@ -33,6 +34,11 @@
store (the/prepare-store state done store (the/prepare-store state done
(fn [new-state] (fn [new-state]
;; Uncomment to debug
;; (ctf/dump-tree (get new-state :workspace-data)
;; (get new-state :current-page-id)
;; (get new-state :workspace-libraries))
; Expected shape tree: ; Expected shape tree:
; ;
; [Page] ; [Page]
@ -40,7 +46,7 @@
; Rect-2 #--> Rect-2 ; Rect-2 #--> Rect-2
; Rect-1 ---> Rect-1 ; Rect-1 ---> Rect-1
; ;
; [Rect-1] ; [Rect-2]
; Rect-2 ; Rect-2
; Rect-1 ; Rect-1
; ;
@ -55,7 +61,7 @@
(t/is (= (:name shape1) "Rect-1")) (t/is (= (:name shape1) "Rect-1"))
(t/is (= (:name group) "Rect-2")) (t/is (= (:name group) "Rect-2"))
(t/is (= (:name component) "Rect-1")) (t/is (= (:name component) "Rect-2"))
(t/is (= (:name c-shape1) "Rect-1")) (t/is (= (:name c-shape1) "Rect-1"))
(t/is (= (:name c-group) "Rect-2")) (t/is (= (:name c-group) "Rect-2"))
@ -207,6 +213,70 @@
(dwl/add-component) (dwl/add-component)
:the/end)))) :the/end))))
(t/deftest test-add-component-from-component
(t/async
done
(let [state (-> thp/initial-state
(thp/sample-page)
(thp/sample-shape :shape1 :rect
{:name "Rect-1"})
(thp/make-component :instance1 :component1
[(thp/id :shape1)]))
store (the/prepare-store state done
(fn [new-state]
; Expected shape tree:
;
; [Page]
; Root Frame
; Rect-3 #--> Rect-3
; Rect-2 @--> Rect-2
; Rect-1 ---> Rect-1
;
; [Rect-2]
; Rect-2
; Rect-1
;
; [Rect-2]
; Rect-3
; Rect-2 @--> Rect-2
; Rect-1 ---> Rect-1
;
(let [[[instance1 shape1]
[c-instance1 c-shape1]
component1]
(thl/resolve-instance-and-main
new-state
(thp/id :instance1)
true)
[[instance2 instance1' shape1']
[c-instance2 c-instance1' c-shape1']
component2]
(thl/resolve-instance-and-main
new-state
(:parent-id instance1))]
(t/is (= (:name shape1) "Rect-1"))
(t/is (= (:name instance1) "Rect-2"))
(t/is (= (:name component1) "Rect-2"))
(t/is (= (:name c-shape1) "Rect-1"))
(t/is (= (:name c-instance1) "Rect-2"))
(t/is (= (:name shape1') "Rect-1"))
(t/is (= (:name instance1') "Rect-2"))
(t/is (= (:name instance2) "Rect-3"))
(t/is (= (:name component2) "Rect-3"))
(t/is (= (:name c-shape1') "Rect-1"))
(t/is (= (:name c-instance1') "Rect-2"))
(t/is (= (:name c-instance2) "Rect-3")))))]
(ptk/emit!
store
(dw/select-shape (thp/id :instance1))
(dwl/add-component)
:the/end))))
(t/deftest test-rename-component (t/deftest test-rename-component
(t/async (t/async
done done
@ -269,7 +339,7 @@
; Rect-2 ; Rect-2
; Rect-1 ; Rect-1
; ;
; [Rect-2] ; [Rect-3]
; Rect-2 ; Rect-2
; Rect-1 ; Rect-1
; ;
@ -293,7 +363,7 @@
new-state new-state
new-component-id)] new-component-id)]
(t/is (= (:name component2) "Rect-2")))))] (t/is (= (:name component2) "Rect-3")))))]
(ptk/emit! (ptk/emit!
store store

View file

@ -77,7 +77,10 @@
(defn resolve-instance-and-main (defn resolve-instance-and-main
"Get the shape with the given id and all its children, and also "Get the shape with the given id and all its children, and also
the main component and all its shapes." the main component and all its shapes."
[state root-inst-id] ([state root-inst-id]
(resolve-instance-and-main state root-inst-id false))
([state root-inst-id subinstance?]
(let [page (thp/current-page state) (let [page (thp/current-page state)
root-inst (ctn/get-shape page root-inst-id) root-inst (ctn/get-shape page root-inst-id)
@ -102,14 +105,16 @@
(t/is (some? main-shape))))] (t/is (some? main-shape))))]
;; Validate that the instance tree is well constructed ;; Validate that the instance tree is well constructed
(is-instance-root (first shapes-inst)) (if subinstance?
(is-instance-subroot (first shapes-inst))
(is-instance-root (first shapes-inst)))
(run! is-instance-inner (rest shapes-inst)) (run! is-instance-inner (rest shapes-inst))
(t/is (= (count shapes-inst) (t/is (= (count shapes-inst)
(count shapes-main) (count shapes-main)
(count unique-refs))) (count unique-refs)))
(run! main-exists? shapes-inst) (run! main-exists? shapes-inst)
[shapes-inst shapes-main component])) [shapes-inst shapes-main component])))
(defn resolve-instance-and-main-allow-dangling (defn resolve-instance-and-main-allow-dangling
"Get the shape with the given id and all its children, and also "Get the shape with the given id and all its children, and also