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:
parent
0ea07fbe01
commit
bd834ba840
6 changed files with 139 additions and 95 deletions
|
@ -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]
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) " *")])))
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue