0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-11 07:11:32 -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]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[linked.core :as lks]
[potok.core :as ptk]))
(def default-workspace-local {:zoom 1})
@ -220,7 +219,7 @@
(rx/map (fn [data] (assoc file :data data))))))
(rx/reduce conj [])
(rx/map libraries-fetched)))
(rx/of (workspace-initialized)))
(rx/of (with-meta (workspace-initialized) {:file-id id})))
(rx/take-until stoper))))))
(defn- libraries-fetched
@ -369,7 +368,10 @@
ptk/WatchEvent
(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)
(rx/of (preload-data-uris page-id)
(dwth/watch-state-changes)
@ -593,6 +595,7 @@
(defn start-rename-shape
"Start shape renaming process"
[id]
(dm/assert! (uuid? id))
(ptk/reify ::start-rename-shape
@ -601,14 +604,33 @@
(assoc-in state [:workspace-local :shape-for-rename] id))))
(defn end-rename-shape
[]
(ptk/reify ::end-rename-shape
ptk/UpdateEvent
(update [_ state]
(update state :workspace-local dissoc :shape-for-rename))))
"End the ongoing shape rename process"
([] (end-rename-shape nil))
([name]
(ptk/reify ::end-rename-shape
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
(defn update-selected-shapes
[attrs]
(dm/assert! (cts/shape-attrs? attrs))
@ -1173,27 +1195,53 @@
(update state :workspace-assets dissoc :selected))))))
(defn go-to-main-instance
[page-id shape-id]
(dm/assert! (uuid? page-id))
(dm/assert! (uuid? shape-id))
[file-id component-id]
(dm/assert!
"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/WatchEvent
(watch [_ state stream]
(let [current-page-id (:current-page-id state)]
(if (= page-id current-page-id)
(rx/of (dws/select-shapes (lks/set shape-id))
dwz/zoom-to-selected-shape)
(let [project-id (:current-project-id state)
file-id (:current-file-id state)
pparams {:file-id file-id :project-id project-id}
qparams {:page-id page-id}]
(rx/merge
(rx/of (rt/nav :workspace pparams qparams))
(->> stream
(rx/filter (ptk/type? ::dwv/page-loaded))
(rx/take 1)
(rx/mapcat #(rx/of (dws/select-shapes (lks/set shape-id))
dwz/zoom-to-selected-shape))))))))))
(let [current-file-id (:current-file-id state)
current-page-id (:current-page-id state)
current-project-id (:current-project-id state)
file-id (or file-id current-file-id)
redirect-to
(fn [file-id page-id]
(let [pparams {:file-id file-id :project-id current-project-id}
qparams {:page-id page-id}]
(rx/merge
(rx/of (rt/nav :workspace pparams qparams))
(->> stream
(rx/filter (ptk/type? ::workspace-initialized))
(rx/map meta)
(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
[component-id]

View file

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

View file

@ -104,9 +104,12 @@
(def workspace-drawing
(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?
(l/derived (fn [state]
(and (:workspace-ready? state)
(:workspace-data state)
(:current-file-id state)
(:current-project-id state)))
st/state))

View file

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

View file

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

View file

@ -49,7 +49,7 @@
(defn point->viewport
[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)
(some? @viewport-brect))
(let [vbox (.. ^js @viewport-ref -viewBox -baseVal)