diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs index 837065eda3..ea16874955 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs @@ -58,7 +58,8 @@ is-filtered is-expanded dnd-over dnd-over-top dnd-over-bot hide-toggle ;; Callbacks on-select-shape on-context-menu on-pointer-enter on-pointer-leave on-zoom-to-selected - on-toggle-collapse on-enable-drag on-disable-drag on-toggle-visibility on-toggle-blocking]}] + on-toggle-collapse on-enable-drag on-disable-drag on-toggle-visibility on-toggle-blocking + on-tab-press]}] (let [id (:id item) name (:name item) @@ -164,7 +165,8 @@ :variant-properties variant-properties :variant-error variant-error :component-id component-id - :is-hidden hidden?}]] + :is-hidden hidden? + :on-tab-press on-tab-press}]] (when (not ^boolean is-read-only) [:div {:class (stl/css-case :element-actions true @@ -416,7 +418,24 @@ ;; We don't want to change the structure of component copies :draggable? (and ^boolean is-sortable ^boolean (not is-read-only) - ^boolean (not (ctn/has-any-copy-parent? objects item))))] + ^boolean (not (ctn/has-any-copy-parent? objects item)))) + + on-tab-press + (mf/use-fn + (mf/deps id objects) + (fn [shift?] + (let [shape (get objects id) + parent (get objects (:parent-id shape)) + siblings (:shapes parent) + pos (d/index-of siblings id)] + (when (some? pos) + (let [;; Layers render in reverse: Tab (visually down) = dec index, + ;; Shift+Tab (visually up) = inc index + target-id (if shift? + (get siblings (inc pos)) + (get siblings (dec pos)))] + (when (some? target-id) + (st/emit! (dw/start-rename-shape target-id)))))))))] (mf/with-effect [is-selected selected] (let [single? (= (count selected) 1) @@ -531,6 +550,7 @@ :on-disable-drag disable-drag :on-toggle-visibility toggle-visibility :on-toggle-blocking toggle-blocking + :on-tab-press on-tab-press :style style} (when (and ^boolean render-children diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs index 2c8e78fc57..550749ed9a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs @@ -24,11 +24,17 @@ [{:keys [shape-id rename-id shape-name is-shape-touched disabled-double-click on-start-edit on-stop-edit depth parent-size is-selected type-comp type-frame component-id is-hidden is-blocked - variant-id variant-name variant-properties variant-error ref]}] - + variant-id variant-name variant-properties variant-error + on-tab-press ref]}] (let [edition* (mf/use-state false) edition? (deref edition*) + ;; Mutable ref to track editing state inside mf/use-fn callbacks. + ;; mf/use-state wraps React useState, so @edition* inside memoized + ;; callbacks captures a stale State object. A ref is a stable mutable + ;; container that always reflects the latest value. + editing-ref (mf/use-ref false) + local-ref (mf/use-ref) ref (d/nilv ref local-ref) @@ -53,6 +59,7 @@ (when (and (not is-blocked) (not disabled-double-click)) (on-start-edit) + (mf/set-ref-val! editing-ref true) (reset! edition* true) (st/emit! (dw/start-rename-shape shape-id))))) @@ -60,26 +67,42 @@ (mf/use-fn (mf/deps shape-id on-stop-edit component-id variant-id variant-name variant-properties) (fn [] - (let [name-input (mf/ref-val ref) - name (str/trim (dom/get-value name-input))] - (on-stop-edit) - (reset! edition* false) - (st/emit! (dw/rename-shape-or-variant shape-id name))))) + (when (mf/ref-val editing-ref) + (let [name-input (mf/ref-val ref) + name (str/trim (dom/get-value name-input))] + (on-stop-edit) + (mf/set-ref-val! editing-ref false) + (reset! edition* false) + (st/emit! (dw/rename-shape-or-variant shape-id name)))))) cancel-edit (mf/use-fn (mf/deps shape-id on-stop-edit) (fn [] (on-stop-edit) + (mf/set-ref-val! editing-ref false) (reset! edition* false) (st/emit! (dw/end-rename-shape shape-id nil)))) on-key-down (mf/use-fn - (mf/deps accept-edit cancel-edit) + (mf/deps accept-edit cancel-edit on-tab-press shape-id on-stop-edit) (fn [event] (when (kbd/enter? event) (accept-edit)) - (when (kbd/esc? event) (cancel-edit)))) + (when (kbd/esc? event) (cancel-edit)) + (when (kbd/tab? event) + (dom/prevent-default event) + (dom/stop-propagation event) + (when (mf/ref-val editing-ref) + (let [shift? (kbd/shift? event) + name-input (mf/ref-val ref) + name (str/trim (dom/get-value name-input))] + (on-stop-edit) + (mf/set-ref-val! editing-ref false) + (reset! edition* false) + (st/emit! (dw/end-rename-shape shape-id name)) + (when (fn? on-tab-press) + (on-tab-press shift?))))))) parent-size (dm/str (- parent-size space-for-icons) "px")]