mirror of
https://github.com/penpot/penpot.git
synced 2025-06-07 12:31:38 +02:00
🎉 Allow to rename a component in the library
This commit is contained in:
parent
fd1f42dc94
commit
1823ecda40
5 changed files with 104 additions and 23 deletions
|
@ -523,7 +523,8 @@
|
||||||
(s/keys :req-un [::id ::name :internal.changes.add-component/shapes]))
|
(s/keys :req-un [::id ::name :internal.changes.add-component/shapes]))
|
||||||
|
|
||||||
(defmethod change-spec :mod-component [_]
|
(defmethod change-spec :mod-component [_]
|
||||||
(s/keys :req-un [::id ::name :internal.changes.add-component/shapes]))
|
(s/keys :req-un [::id]
|
||||||
|
:opt-un [::name :internal.changes.add-component/shapes]))
|
||||||
|
|
||||||
(defmethod change-spec :del-component [_]
|
(defmethod change-spec :del-component [_]
|
||||||
(s/keys :req-un [::id]))
|
(s/keys :req-un [::id]))
|
||||||
|
@ -964,9 +965,12 @@
|
||||||
(defmethod process-change :mod-component
|
(defmethod process-change :mod-component
|
||||||
[data {:keys [id name shapes]}]
|
[data {:keys [id name shapes]}]
|
||||||
(update-in data [:components id]
|
(update-in data [:components id]
|
||||||
#(assoc %
|
#(cond-> %
|
||||||
:name name
|
(some? name)
|
||||||
:objects (d/index-by :id shapes))))
|
(assoc :name name)
|
||||||
|
|
||||||
|
(some? shapes)
|
||||||
|
(assoc :objects (d/index-by :id shapes)))))
|
||||||
|
|
||||||
(defmethod process-change :del-component
|
(defmethod process-change :del-component
|
||||||
[data {:keys [id]}]
|
[data {:keys [id]}]
|
||||||
|
|
|
@ -213,9 +213,28 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: 3px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
&.editing {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editable-label-input {
|
||||||
|
border: 1px solid $color-gray-20;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: $fs11;
|
||||||
|
padding: 2px;
|
||||||
|
margin: 0;
|
||||||
|
height: unset;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editable-label-close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-cell:hover {
|
.grid-cell:hover {
|
||||||
|
|
|
@ -253,6 +253,25 @@
|
||||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||||
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
(dws/select-shapes (d/ordered-set (:id group))))))))))
|
||||||
|
|
||||||
|
(defn rename-component
|
||||||
|
[id new-name]
|
||||||
|
(us/assert ::us/uuid id)
|
||||||
|
(us/assert ::us/string new-name)
|
||||||
|
(ptk/reify ::rename-component
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [component (get-in state [:workspace-data :components id])
|
||||||
|
|
||||||
|
rchanges [{:type :mod-component
|
||||||
|
:id id
|
||||||
|
:name new-name}]
|
||||||
|
|
||||||
|
uchanges [{:type :mod-component
|
||||||
|
:id id
|
||||||
|
:name (:name component)}]]
|
||||||
|
|
||||||
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? 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."
|
||||||
[{:keys [id] :as params}]
|
[{:keys [id] :as params}]
|
||||||
|
@ -279,7 +298,7 @@
|
||||||
:id (:id new-shape)}]]
|
:id (:id new-shape)}]]
|
||||||
|
|
||||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||||
|
|
||||||
(defn delete-component
|
(defn delete-component
|
||||||
"Delete the component with the given id, from the current file library."
|
"Delete the component with the given id, from the current file library."
|
||||||
[{:keys [id] :as params}]
|
[{:keys [id] :as params}]
|
||||||
|
|
|
@ -17,35 +17,43 @@
|
||||||
[app.util.data :refer [classnames]]))
|
[app.util.data :refer [classnames]]))
|
||||||
|
|
||||||
(mf/defc editable-label
|
(mf/defc editable-label
|
||||||
[{:keys [ value on-change on-cancel edit readonly class-name]}]
|
[{:keys [value on-change on-cancel editing? disable-dbl-click? class-name]}]
|
||||||
(let [input (mf/use-ref nil)
|
(let [input (mf/use-ref nil)
|
||||||
state (mf/use-state (:editing false))
|
state (mf/use-state (:editing false))
|
||||||
is-editing (or edit (:editing @state))
|
is-editing (:editing @state)
|
||||||
start-editing (fn []
|
start-editing (fn []
|
||||||
(swap! state assoc :editing true)
|
(swap! state assoc :editing true)
|
||||||
(timers/schedule 100 #(dom/focus! (mf/ref-val input))))
|
(timers/schedule 100 #(dom/focus! (mf/ref-val input))))
|
||||||
stop-editing (fn [] (swap! state assoc :editing false))
|
stop-editing (fn [] (swap! state assoc :editing false))
|
||||||
|
accept-editing (fn []
|
||||||
|
(when (:editing @state)
|
||||||
|
(let [value (-> (mf/ref-val input) dom/get-value)]
|
||||||
|
(on-change value)
|
||||||
|
(stop-editing))))
|
||||||
cancel-editing (fn []
|
cancel-editing (fn []
|
||||||
(stop-editing)
|
(stop-editing)
|
||||||
(when on-cancel (on-cancel)))
|
(when on-cancel (on-cancel)))
|
||||||
on-dbl-click (fn [e] (when (not readonly) (start-editing)))
|
on-dbl-click (fn [e] (when (not disable-dbl-click?) (start-editing)))
|
||||||
on-key-up (fn [e]
|
on-key-up (fn [e]
|
||||||
(cond
|
(cond
|
||||||
(kbd/esc? e)
|
(kbd/esc? e)
|
||||||
(cancel-editing)
|
(cancel-editing)
|
||||||
|
|
||||||
(kbd/enter? e)
|
(kbd/enter? e)
|
||||||
(let [value (-> e dom/get-target dom/get-value)]
|
(accept-editing)))]
|
||||||
(on-change value)
|
|
||||||
(stop-editing))))
|
(mf/use-effect
|
||||||
]
|
(mf/deps editing?)
|
||||||
|
(fn []
|
||||||
|
(when (and editing? (not (:editing @state)))
|
||||||
|
(start-editing))))
|
||||||
|
|
||||||
(if is-editing
|
(if is-editing
|
||||||
[:div.editable-label {:class class-name}
|
[:div.editable-label {:class class-name}
|
||||||
[:input.editable-label-input {:ref input
|
[:input.editable-label-input {:ref input
|
||||||
:default-value value
|
:default-value value
|
||||||
:on-key-down on-key-up}]
|
:on-key-up on-key-up
|
||||||
|
:on-blur cancel-editing}]
|
||||||
[:span.editable-label-close {:on-click cancel-editing} i/close]]
|
[:span.editable-label-close {:on-click cancel-editing} i/close]]
|
||||||
[:span.editable-label {:class class-name
|
[:span.editable-label {:class class-name
|
||||||
:on-double-click on-dbl-click} value]
|
:on-double-click on-dbl-click} value])))
|
||||||
)))
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
[app.main.ui.components.context-menu :refer [context-menu]]
|
[app.main.ui.components.context-menu :refer [context-menu]]
|
||||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||||
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||||
|
[app.main.ui.components.editable-label :refer [editable-label]]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.keyboard :as kbd]
|
[app.main.ui.keyboard :as kbd]
|
||||||
|
@ -47,6 +48,7 @@
|
||||||
(mf/defc components-box
|
(mf/defc components-box
|
||||||
[{:keys [file-id local? components open? on-open on-close] :as props}]
|
[{:keys [file-id local? components open? on-open on-close] :as props}]
|
||||||
(let [state (mf/use-state {:menu-open false
|
(let [state (mf/use-state {:menu-open false
|
||||||
|
:renaming nil
|
||||||
:top nil
|
:top nil
|
||||||
:left nil
|
:left nil
|
||||||
:component-id nil})
|
:component-id nil})
|
||||||
|
@ -62,6 +64,25 @@
|
||||||
(st/emit! (dwl/delete-component {:id (:component-id @state)}))
|
(st/emit! (dwl/delete-component {:id (:component-id @state)}))
|
||||||
(st/emit! (dwl/sync-file nil))))
|
(st/emit! (dwl/sync-file nil))))
|
||||||
|
|
||||||
|
on-rename
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps state)
|
||||||
|
(fn []
|
||||||
|
(swap! state assoc :renaming (:component-id @state))))
|
||||||
|
|
||||||
|
do-rename
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps state)
|
||||||
|
(fn [new-name]
|
||||||
|
(st/emit! (dwl/rename-component (:renaming @state) new-name))
|
||||||
|
(swap! state assoc :renaming nil)))
|
||||||
|
|
||||||
|
cancel-rename
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps state)
|
||||||
|
(fn []
|
||||||
|
(swap! state assoc :renaming nil)))
|
||||||
|
|
||||||
on-context-menu
|
on-context-menu
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(fn [component-id]
|
(fn [component-id]
|
||||||
|
@ -90,13 +111,22 @@
|
||||||
(when open?
|
(when open?
|
||||||
[:div.group-grid.big
|
[:div.group-grid.big
|
||||||
(for [component components]
|
(for [component components]
|
||||||
[:div.grid-cell {:key (:id component)
|
(let [renaming? (= (:renaming @state)(:id component))]
|
||||||
:draggable true
|
[:div.grid-cell {:key (:id component)
|
||||||
:on-context-menu (on-context-menu (:id component))
|
:draggable true
|
||||||
:on-drag-start (partial on-drag-start component)}
|
:on-context-menu (on-context-menu (:id component))
|
||||||
[:& exports/component-svg {:group (get-in component [:objects (:id component)])
|
:on-drag-start (partial on-drag-start component)}
|
||||||
:objects (:objects component)}]
|
[:& exports/component-svg {:group (get-in component [:objects (:id component)])
|
||||||
[:div.cell-name (:name component)]])])
|
:objects (:objects component)}]
|
||||||
|
[:& editable-label
|
||||||
|
{:class-name (dom/classnames
|
||||||
|
:cell-name true
|
||||||
|
:editing renaming?)
|
||||||
|
:value (:name component)
|
||||||
|
:editing? renaming?
|
||||||
|
:disable-dbl-click? true
|
||||||
|
:on-change do-rename
|
||||||
|
:on-cancel cancel-rename}]]))])
|
||||||
|
|
||||||
(when local?
|
(when local?
|
||||||
[:& context-menu
|
[:& context-menu
|
||||||
|
@ -105,7 +135,8 @@
|
||||||
:on-close #(swap! state assoc :menu-open false)
|
:on-close #(swap! state assoc :menu-open false)
|
||||||
:top (:top @state)
|
:top (:top @state)
|
||||||
:left (:left @state)
|
:left (:left @state)
|
||||||
:options [[(tr "workspace.assets.duplicate") on-duplicate]
|
:options [[(tr "workspace.assets.rename") on-rename]
|
||||||
|
[(tr "workspace.assets.duplicate") on-duplicate]
|
||||||
[(tr "workspace.assets.delete") on-delete]]}])]))
|
[(tr "workspace.assets.delete") on-delete]]}])]))
|
||||||
|
|
||||||
(mf/defc graphics-box
|
(mf/defc graphics-box
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue