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

Improve component renaming process on workspace

This commit is contained in:
Andrey Antukh 2023-05-24 19:08:53 +02:00 committed by Alejandro Alonso
parent 0ea07fbe01
commit bd834ba840
6 changed files with 139 additions and 95 deletions

View file

@ -78,7 +78,6 @@
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[cuerdas.core :as str] [cuerdas.core :as str]
[linked.core :as lks]
[potok.core :as ptk])) [potok.core :as ptk]))
(def default-workspace-local {:zoom 1}) (def default-workspace-local {:zoom 1})
@ -220,7 +219,7 @@
(rx/map (fn [data] (assoc file :data data)))))) (rx/map (fn [data] (assoc file :data data))))))
(rx/reduce conj []) (rx/reduce conj [])
(rx/map libraries-fetched))) (rx/map libraries-fetched)))
(rx/of (workspace-initialized))) (rx/of (with-meta (workspace-initialized) {:file-id id})))
(rx/take-until stoper)))))) (rx/take-until stoper))))))
(defn- libraries-fetched (defn- libraries-fetched
@ -369,7 +368,10 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [pindex (-> state :workspace-data :pages-index)] ;; NOTE: there are cases between files navigation when this
;; event is emmited but the page-index is still not loaded, so
;; we only need to proceed when page-index is properly loaded
(when-let [pindex (-> state :workspace-data :pages-index)]
(if (contains? pindex page-id) (if (contains? pindex page-id)
(rx/of (preload-data-uris page-id) (rx/of (preload-data-uris page-id)
(dwth/watch-state-changes) (dwth/watch-state-changes)
@ -593,6 +595,7 @@
(defn start-rename-shape (defn start-rename-shape
"Start shape renaming process"
[id] [id]
(dm/assert! (uuid? id)) (dm/assert! (uuid? id))
(ptk/reify ::start-rename-shape (ptk/reify ::start-rename-shape
@ -601,14 +604,33 @@
(assoc-in state [:workspace-local :shape-for-rename] id)))) (assoc-in state [:workspace-local :shape-for-rename] id))))
(defn end-rename-shape (defn end-rename-shape
[] "End the ongoing shape rename process"
(ptk/reify ::end-rename-shape ([] (end-rename-shape nil))
ptk/UpdateEvent ([name]
(update [_ state] (ptk/reify ::end-rename-shape
(update state :workspace-local dissoc :shape-for-rename)))) ptk/WatchEvent
(watch [_ state _]
(prn "end-rename-shape" name (string? name) (not (str/blank? name)))
(when-let [shape-id (dm/get-in state [:workspace-local :shape-for-rename])]
(prn "end-rename-shape" shape-id)
(let [shape (wsh/lookup-shape state shape-id)]
(rx/concat
;; Remove rename state from workspace local state
(rx/of #(update % :workspace-local dissoc :shape-for-rename))
;; Rename the shape if string is not empty/blank
(when (and (string? name) (not (str/blank? name)))
(rx/of (update-shape shape-id {:name name})))
;; Update the component in case if shape is a main instance
(when (:main-instance? shape)
(when-let [component-id (:component-id shape)]
(rx/of (dwl/rename-component component-id name)))))))))))
;; --- Update Selected Shapes attrs ;; --- Update Selected Shapes attrs
(defn update-selected-shapes (defn update-selected-shapes
[attrs] [attrs]
(dm/assert! (cts/shape-attrs? attrs)) (dm/assert! (cts/shape-attrs? attrs))
@ -1173,27 +1195,53 @@
(update state :workspace-assets dissoc :selected)))))) (update state :workspace-assets dissoc :selected))))))
(defn go-to-main-instance (defn go-to-main-instance
[page-id shape-id] [file-id component-id]
(dm/assert! (uuid? page-id)) (dm/assert!
(dm/assert! (uuid? shape-id)) "expected uuid type for `file-id` parameter (nilable)"
(or (nil? file-id)
(uuid? file-id)))
(dm/assert!
"expected uuid type for `component-id` parameter"
(uuid? component-id))
(ptk/reify ::go-to-main-instance (ptk/reify ::go-to-main-instance
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [current-page-id (:current-page-id state)] (let [current-file-id (:current-file-id state)
(if (= page-id current-page-id) current-page-id (:current-page-id state)
(rx/of (dws/select-shapes (lks/set shape-id)) current-project-id (:current-project-id state)
dwz/zoom-to-selected-shape) file-id (or file-id current-file-id)
(let [project-id (:current-project-id state)
file-id (:current-file-id state) redirect-to
pparams {:file-id file-id :project-id project-id} (fn [file-id page-id]
qparams {:page-id page-id}] (let [pparams {:file-id file-id :project-id current-project-id}
(rx/merge qparams {:page-id page-id}]
(rx/of (rt/nav :workspace pparams qparams)) (rx/merge
(->> stream (rx/of (rt/nav :workspace pparams qparams))
(rx/filter (ptk/type? ::dwv/page-loaded)) (->> stream
(rx/take 1) (rx/filter (ptk/type? ::workspace-initialized))
(rx/mapcat #(rx/of (dws/select-shapes (lks/set shape-id)) (rx/map meta)
dwz/zoom-to-selected-shape)))))))))) (rx/filter #(= file-id (:file-id %)))
(rx/take 1)
(rx/observe-on :async)
(rx/map #(go-to-main-instance file-id component-id))))))]
(if (= file-id current-file-id)
(let [component (dm/get-in state [:workspace-data :components component-id])
page-id (:main-instance-page component)]
(when (some? page-id)
(if (= page-id current-page-id)
(let [shape-id (:main-instance-id component)]
(rx/of (dws/select-shapes (d/ordered-set shape-id))
dwz/zoom-to-selected-shape))
(redirect-to current-page-id page-id))))
(let [component (dm/get-in state [:workspace-libraries file-id :data :components component-id])]
(some->> (:main-instance-page component)
(redirect-to file-id))))))))
(defn go-to-component (defn go-to-component
[component-id] [component-id]

View file

@ -335,6 +335,8 @@
;; NOTE: we need to ensure the component exists, ;; NOTE: we need to ensure the component exists,
;; because there are small possibilities of race ;; because there are small possibilities of race
;; conditions with component deletion. ;; conditions with component deletion.
;;
;; FIXME: this race conditon should be handled in pcb/update-component
(when component (when component
(cond-> component (cond-> component
:always :always
@ -343,7 +345,7 @@
(not components-v2) (not components-v2)
(update :objects (update :objects
;; Give the same name to the root shape ;; Give the same name to the root shape
#(assoc-in % [id :name] name))))) #(assoc-in % [id :name] name)))))
changes (-> (pcb/empty-changes it) changes (-> (pcb/empty-changes it)
@ -353,9 +355,19 @@
(rx/of (dch/commit-changes changes))))))) (rx/of (dch/commit-changes changes)))))))
(defn rename-component-and-main-instance (defn rename-component-and-main-instance
[component-id shape-id name page-id] [component-id name]
(st/emit! (rename-component component-id name) (ptk/reify ::rename-component-and-main-instance
(dch/update-shapes [shape-id] #(merge % {:name name}) {:page-id page-id :stack-undo? true}))) ptk/WatchEvent
(watch [_ state _]
(when-let [component (dm/get-in state [:workspace-data :components component-id])]
(let [shape-id (:main-instance-id component)
page-id (:main-instance-page component)]
(rx/concat
(rx/of (rename-component component-id name))
;; NOTE: only when components-v2 is enabled
(when (and shape-id page-id)
(rx/of (dch/update-shapes [shape-id] #(assoc % :name name) {:page-id page-id :stack-undo? true})))))))))
(defn duplicate-component (defn duplicate-component
"Create a new component copied from the one with the given id." "Create a new component copied from the one with the given id."

View file

@ -104,9 +104,12 @@
(def workspace-drawing (def workspace-drawing
(l/derived :workspace-drawing st/state)) (l/derived :workspace-drawing st/state))
;; FIXME: define it as function, because in some situations this
;; current check is not enought for true readiness
(def workspace-ready? (def workspace-ready?
(l/derived (fn [state] (l/derived (fn [state]
(and (:workspace-ready? state) (and (:workspace-ready? state)
(:workspace-data state)
(:current-file-id state) (:current-file-id state)
(:current-project-id state))) (:current-project-id state)))
st/state)) st/state))

View file

@ -415,13 +415,10 @@
on-component-double-click on-component-double-click
(mf/use-fn (mf/use-fn
(mf/deps component selected) (mf/deps file-id component-id)
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(let [main-instance-id (:main-instance-id component) (st/emit! (dw/go-to-main-instance file-id component-id))))
main-instance-page (:main-instance-page component)]
(when (and main-instance-id main-instance-page) ;; Only when :components-v2 is enabled
(st/emit! (dw/go-to-main-instance main-instance-page main-instance-id))))))
on-drop on-drop
(mf/use-fn (mf/use-fn
@ -605,8 +602,12 @@
on-asset-click on-assets-delete on-clear-selection open-status-ref]}] on-asset-click on-assets-delete on-clear-selection open-status-ref]}]
(let [input-ref (mf/use-ref nil) (let [input-ref (mf/use-ref nil)
state (mf/use-state {:renaming nil
:component-id nil}) state* (mf/use-state {})
state (deref state*)
current-component-id (:component-id state)
renaming? (:renaming state)
open-groups-ref (mf/with-memo [open-status-ref] open-groups-ref (mf/with-memo [open-status-ref]
(-> (l/in [:groups :components]) (-> (l/in [:groups :components])
@ -647,50 +648,43 @@
on-duplicate on-duplicate
(mf/use-fn (mf/use-fn
(mf/deps @state) (mf/deps current-component-id selected)
(fn [] (fn []
(let [undo-id (js/Symbol)] (if (empty? selected)
(if (empty? selected) (st/emit! (dwl/duplicate-component file-id current-component-id))
(st/emit! (dwl/duplicate-component file-id (:component-id @state))) (let [undo-id (js/Symbol)]
(do (st/emit! (dwu/start-undo-transaction undo-id))
(st/emit! (dwu/start-undo-transaction undo-id)) (run! st/emit! (map (partial dwl/duplicate-component file-id) selected))
(apply st/emit! (map (partial dwl/duplicate-component file-id) selected)) (st/emit! (dwu/commit-undo-transaction undo-id))))))
(st/emit! (dwu/commit-undo-transaction undo-id)))))))
on-delete on-delete
(mf/use-fn (mf/use-fn
(mf/deps @state file-id multi-components? multi-assets?) (mf/deps current-component-id file-id multi-components? multi-assets? on-assets-delete)
(fn [] (fn []
(let [undo-id (js/Symbol)] (let [undo-id (js/Symbol)]
(if (or multi-components? multi-assets?) (if (or multi-components? multi-assets?)
(on-assets-delete) (on-assets-delete)
(st/emit! (dwu/start-undo-transaction undo-id) (st/emit! (dwu/start-undo-transaction undo-id)
(dwl/delete-component {:id (:component-id @state)}) (dwl/delete-component {:id current-component-id})
(dwl/sync-file file-id file-id :components (:component-id @state)) (dwl/sync-file file-id file-id :components current-component-id)
(dwu/commit-undo-transaction undo-id)))))) (dwu/commit-undo-transaction undo-id))))))
on-close-menu
(mf/use-fn #(swap! menu-state close-context-menu))
on-rename on-rename
(mf/use-fn (mf/use-fn #(swap! state* assoc :renaming true))
(fn []
(swap! state (fn [state] cancel-rename
(assoc state :renaming (:component-id state)))))) (mf/use-fn #(swap! state* dissoc :renaming))
do-rename do-rename
(mf/use-fn (mf/use-fn
(mf/deps @state) (mf/deps current-component-id)
(fn [new-name] (fn [new-name]
(let [component-id (:renaming @state) (swap! state* dissoc :renaming)
component (dm/get-in file [:components component-id]) (st/emit!
main-instance-id (:main-instance-id component) (dwl/rename-component-and-main-instance current-component-id new-name))))
main-instance-page (:main-instance-page component)]
(dwl/rename-component-and-main-instance component-id main-instance-id new-name main-instance-page)
(swap! state assoc :renaming nil))))
cancel-rename
(mf/use-fn
(fn []
(swap! state assoc :renaming nil)))
on-context-menu on-context-menu
(mf/use-fn (mf/use-fn
@ -701,17 +695,13 @@
(when (and local? (not read-only?)) (when (and local? (not read-only?))
(when-not (contains? selected component-id) (when-not (contains? selected component-id)
(on-clear-selection)) (on-clear-selection))
(swap! state assoc :component-id component-id)
(swap! menu-state open-context-menu pos)))))
on-close-menu (swap! state* assoc :component-id component-id)
(mf/use-fn (swap! menu-state open-context-menu pos)))))
(fn []
(swap! menu-state close-context-menu)))
create-group create-group
(mf/use-fn (mf/use-fn
(mf/deps components selected on-clear-selection) (mf/deps current-component-id components selected on-clear-selection)
(fn [group-name] (fn [group-name]
(on-clear-selection) (on-clear-selection)
(let [undo-id (js/Symbol)] (let [undo-id (js/Symbol)]
@ -720,7 +710,7 @@
(->> components (->> components
(filter #(if multi-components? (filter #(if multi-components?
(contains? selected (:id %)) (contains? selected (:id %))
(= (:component-id @state) (:id %)))) (= current-component-id (:id %))))
(map #(dwl/rename-component (map #(dwl/rename-component
(:id %) (:id %)
(add-group % group-name))))) (add-group % group-name)))))
@ -780,17 +770,10 @@
on-show-main on-show-main
(mf/use-fn (mf/use-fn
(mf/deps @state components) (mf/deps current-component-id file-id)
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(let [component-id (:component-id @state) (st/emit! (dw/go-to-main-instance file-id current-component-id))))
component (->> components
(filter #(= (:id %) component-id))
first)
main-instance-id (:main-instance-id component)
main-instance-page (:main-instance-page component)]
(when (and main-instance-id main-instance-page) ;; Only when :components-v2 is enabled
(st/emit! (dw/go-to-main-instance main-instance-page main-instance-id))))))
on-asset-click 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))]
@ -815,7 +798,7 @@
:prefix "" :prefix ""
:groups groups :groups groups
:open-groups open-groups :open-groups open-groups
:renaming (:renaming @state) :renaming (when ^boolean renaming? current-component-id)
:listing-thumbs? listing-thumbs? :listing-thumbs? listing-thumbs?
:selected selected :selected selected
:on-asset-click on-asset-click :on-asset-click on-asset-click

View file

@ -8,7 +8,6 @@
(:require-macros [app.main.style :refer [css]]) (:require-macros [app.main.style :refer [css]])
(:require (:require
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.context :as ctx] [app.main.ui.context :as ctx]
[app.util.dom :as dom] [app.util.dom :as dom]
@ -19,6 +18,7 @@
(def shape-for-rename-ref (def shape-for-rename-ref
(l/derived (l/in [:workspace-local :shape-for-rename]) st/state)) (l/derived (l/in [:workspace-local :shape-for-rename]) st/state))
(mf/defc layer-name (mf/defc layer-name
[{:keys [shape on-start-edit disabled-double-click on-stop-edit name-ref depth parent-size selected? type-comp type-frame hidden] :as props}] [{:keys [shape on-start-edit disabled-double-click on-stop-edit name-ref depth parent-size selected? type-comp type-frame hidden] :as props}]
(let [local (mf/use-state {}) (let [local (mf/use-state {})
@ -28,27 +28,25 @@
start-edit (fn [] start-edit (fn []
(when (not disabled-double-click) (when (not disabled-double-click)
(on-start-edit) (on-start-edit)
(swap! local assoc :edition true))) (swap! local assoc :edition true)
(st/emit! (dw/start-rename-shape (:id shape)))))
accept-edit (fn [] accept-edit (fn []
(let [name-input (mf/ref-val name-ref) (let [name-input (mf/ref-val name-ref)
name (str/trim (dom/get-value name-input)) name (str/trim (dom/get-value name-input))]
main-instance? (:main-instance? shape)]
(on-stop-edit) (on-stop-edit)
(swap! local assoc :edition false) (swap! local assoc :edition false)
(st/emit! (dw/end-rename-shape)) (st/emit! (dw/end-rename-shape name))))
(when-not (str/empty? name)
(if main-instance?
(dwl/rename-component-and-main-instance (:component-id shape) (:id shape) name nil)
(st/emit! (dw/update-shape (:id shape) {:name name}))))))
cancel-edit (fn [] cancel-edit (fn []
(on-stop-edit) (on-stop-edit)
(swap! local assoc :edition false) (swap! local assoc :edition false)
(st/emit! (dw/end-rename-shape))) (st/emit! (dw/end-rename-shape nil)))
on-key-down (fn [event] on-key-down (fn [event]
(when (kbd/enter? event) (accept-edit)) (when (kbd/enter? event) (accept-edit))
(when (kbd/esc? event) (cancel-edit))) (when (kbd/esc? event) (cancel-edit)))
space-for-icons 110 space-for-icons 110
parent-size (str (- parent-size space-for-icons) "px")] parent-size (str (- parent-size space-for-icons) "px")]
@ -87,4 +85,4 @@
:ref name-ref :ref name-ref
:on-double-click start-edit} :on-double-click start-edit}
(:name shape "") (:name shape "")
(when (seq (:touched shape)) " *")]))) (when (seq (:touched shape)) " *")])))

View file

@ -49,7 +49,7 @@
(defn point->viewport (defn point->viewport
[pt] [pt]
(let [zoom (dm/get-in @st/state [:workspace-local :zoom])] (let [zoom (dm/get-in @st/state [:workspace-local :zoom] 1)]
(when (and (some? @viewport-ref) (when (and (some? @viewport-ref)
(some? @viewport-brect)) (some? @viewport-brect))
(let [vbox (.. ^js @viewport-ref -viewBox -baseVal) (let [vbox (.. ^js @viewport-ref -viewBox -baseVal)