From efc08d40341bfc63d55c73cd98ba1b96cdb59c37 Mon Sep 17 00:00:00 2001 From: Xavier Julian Date: Fri, 6 Feb 2026 14:04:25 +0100 Subject: [PATCH] :tada: Rename token group - modal form --- common/src/app/common/types/token.cljc | 7 + frontend/src/app/main/ui/forms.cljs | 12 +- .../main/ui/workspace/tokens/management.cljs | 14 +- .../management/forms/rename_node_modal.cljs | 135 +++++++++++++++++- .../management/forms/rename_node_modal.scss | 5 + frontend/src/app/util/dom.cljs | 6 + frontend/translations/en.po | 4 + 7 files changed, 156 insertions(+), 27 deletions(-) diff --git a/common/src/app/common/types/token.cljc b/common/src/app/common/types/token.cljc index 94bf808ae0..148da8c759 100644 --- a/common/src/app/common/types/token.cljc +++ b/common/src/app/common/types/token.cljc @@ -112,6 +112,13 @@ (def token-name-validation-regex #"^[a-zA-Z0-9_-][a-zA-Z0-9$_-]*(\.[a-zA-Z0-9$_-]+)*$") +(def token-node-name-validation-regex + #"^[a-zA-Z0-9_-][a-zA-Z0-9$_-]*$") + +(def node-name-ref + [:re {:title "TokenNodeNameRef" :gen/gen sg/text} + token-node-name-validation-regex]) + (def token-name-ref [:re {:title "TokenNameRef" :gen/gen sg/text} token-name-validation-regex]) diff --git a/frontend/src/app/main/ui/forms.cljs b/frontend/src/app/main/ui/forms.cljs index 0fe34d1f25..cfe1cc4bc1 100644 --- a/frontend/src/app/main/ui/forms.cljs +++ b/frontend/src/app/main/ui/forms.cljs @@ -48,7 +48,7 @@ [:> input* props])) (mf/defc form-submit* - [{:keys [disabled on-submit] :rest props}] + [{:keys [disabled] :rest props}] (let [form (mf/use-ctx context) disabled? (or (and (some? form) @@ -56,17 +56,9 @@ (seq (:async-errors @form)) (seq (:extra-errors @form)))) (true? disabled)) - handle-key-down-save - (mf/use-fn - (mf/deps on-submit form) - (fn [e] - (when (or (k/enter? e) (k/space? e)) - (dom/prevent-default e) - (on-submit form e)))) props (mf/spread-props props {:disabled disabled? - :on-key-down handle-key-down-save :type "submit"})] [:> button* props])) @@ -80,4 +72,4 @@ (when (fn? on-submit) (on-submit form event))))] [:> (mf/provider context) {:value form} - [:form {:class class :on-submit on-submit'} children]])) \ No newline at end of file + [:form {:class class :on-submit on-submit'} children]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/management.cljs b/frontend/src/app/main/ui/workspace/tokens/management.cljs index c235b7012e..f688aa6f3c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management.cljs @@ -165,21 +165,14 @@ (st/emit! (dwtl/toggle-token-path (str (name type) "." path))) (st/emit! (dwtl/toggle-token-path (name type))))))) - ;; node-to-rename (mf/use-state nil) - ;; node-to-rename* (deref node-to-rename) - - ;; rename-node - ;; (fn [node type] - ;; (st/emit! (dwtl/assign-token-node-context-menu nil)) - ;; (st/emit! (modal/show :tokens/rename-node {:node node - ;; :type type}))) - rename-node (mf/use-fn + (mf/deps selected-token-set-tokens) (fn [node type] (prn "Open rename node modal") (modal/show! :tokens/rename-node {:node node - :type type}))) + :type type + :tokens-in-active-set selected-token-set-tokens}))) delete-node (mf/with-memo [selected-token-set-tokens selected-token-set-id] @@ -210,7 +203,6 @@ [:& token-context-menu {:on-delete-token delete-token}] [:> token-node-context-menu* {:on-rename-node rename-node :on-delete-node delete-node}] - ;; [:> rename-node-modal* {}] [:> selected-set-info* {:tokens-lib tokens-lib :selected-token-set-id selected-token-set-id}] diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs index 17dc04c87c..aabfb2d591 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs @@ -1,28 +1,151 @@ (ns app.main.ui.workspace.tokens.management.forms.rename-node-modal (:require-macros [app.main.style :as stl]) (:require + [app.common.data :as d] + [app.common.files.tokens :as cft] + [app.common.schema :as sm] + [app.common.types.token :as cto] + [app.common.types.tokens-lib :as ctob] [app.main.data.modal :as modal] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] + [app.main.ui.forms :as fc] + [app.util.forms :as fm] [app.util.i18n :refer [tr]] + [app.util.keyboard :as kbd] [rumext.v2 :as mf])) +(defn- make-schema + [tokens-tree] + (sm/schema + [:map + [:name + [:and + [:string {:min 1 :max 255 :error/fn #(str (:value %) (tr "workspace.tokens.token-name-length-validation-error"))}] + (sm/update-properties cto/node-name-ref assoc :error/fn #(str (:value %) (tr "workspace.tokens.token-name-validation-error"))) + [:fn {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))} + #(not (cft/token-name-path-exists? % tokens-tree))]]]])) + +(mf/defc rename-node-form* + [{:keys [node type tokens-tree on-close on-submit]}] + (let [schema + (mf/with-memo [tokens-tree] + (make-schema tokens-tree)) + + initial (mf/with-memo [node] + {:name (:name node)}) + + form (fm/use-form :schema schema + :initial initial) + + submit (mf/use-fn + (mf/deps form on-submit node) + (fn [] + (let [name (get-in @form [:clean-data :name]) + old-path (str (d/name type) "." (:name node)) + new-path (str (d/name type) "." name) + _ (prn {:old-path old-path :new-path new-path})] + (prn "Submitting rename node form with name: " name " and path: " new-path) + (if (not= old-path new-path) + (on-submit {:name name}) + (on-close))))) + #_(let [{:keys [clean-data valid extra-errors async-errors]} @form] + (when (and valid + (empty? extra-errors) + (empty? async-errors)) + (on-submit clean-data)))] + + ;; (fn [] + ;; ;; Call shared remapping logic + ;; (let [old-token-name (:old-token-name remap-modal) + ;; new-token-name (:new-token-name remap-modal)] + ;; (st/emit! [:tokens/remap-tokens old-token-name new-token-name])) + ;; (when (fn? on-remap) + ;; (on-remap)))) + + + + ;; remap (mf/use-fn + ;; (mf/deps form on-submit) + ;; (fn [] + ;; (let [name (get-in @form [:clean-data :name]) + ;; path (str (d/name type) "." name)] + ;; (prn "Submitting rename node form with name: " name " and path: " path)) + ;; #_(let [{:keys [clean-data valid extra-errors async-errors]} @form] + ;; (when (and valid + ;; (empty? extra-errors) + ;; (empty? async-errors)) + ;; (on-submit clean-data))))) + + ;; submit (mf/use-fn + ;; (mf/deps form on-submit) + ;; (fn [_ event] + ;; (let [event (dom/event->native-event event) + ;; submitter (dom/get-event-submitter event) + ;; handler (.-name submitter) + ;; handlerKey (keyword handler)] + ;; (if (= handlerKey :rename) + ;; (rename) + ;; (remap)))))] + [:div + [:> heading* {:level 2 + :typography "headline-medium" + :class (stl/css :form-modal-title)} + (tr "workspace.tokens.rename-group")] + [:> fc/form* {:class (stl/css :form-wrapper) + :form form + :on-submit submit} + [:> fc/form-input* {:id (:name node) + :name :name + :label (tr "workspace.tokens.token-name") + :placeholder (tr "workspace.tokens.token-name") + :max-length 255 + :variant "comfortable" + :hint-type "hint" + :hint-message (tr "workspace.tokens.rename-group-name-hint") + :auto-focus true}] + [:div {:class (stl/css :form-actions)} + #_[:> fc/form-submit* {:variant "secondary" + :name "rename"} "rename"] + [:> button* {:variant "secondary" + :name "cancel" + :on-click on-close} (tr "labels.cancel")] + [:> fc/form-submit* {:variant "primary" + :name "rename"} (tr "labels.rename")]]]])) + (mf/defc rename-node-modal* {::mf/register modal/components ::mf/register-as :tokens/rename-node} - [{:keys [node type]}] + [{:keys [node type tokens-in-active-set]}] - (let [on-close + (let [tokens-tree-in-selected-set + (mf/with-memo [tokens-in-active-set node] + (-> (ctob/tokens-tree tokens-in-active-set) + (d/dissoc-in (:name node)))) + + close-modal (mf/use-fn (mf/deps []) (fn [] - (modal/hide!)))] + (modal/hide!))) - [:div {:class (stl/css :modal-overlay)} + on-key-down + (mf/use-fn + (mf/deps [close-modal]) + (fn [event] + (when (kbd/esc? event) + (close-modal))))] + + [:div {:class (stl/css :modal-overlay) :on-key-down on-key-down} [:div {:class (stl/css :modal-dialog)} [:> icon-button* {:class (stl/css :close-btn) - :on-click on-close + :on-click close-modal :aria-label (tr "labels.close") :variant "ghost" :icon i/close}] - (str "Rename node modal for node: " node " of type: " type)]])) + [:> rename-node-form* {:node node + :type type + :tokens-tree tokens-tree-in-selected-set + :on-close close-modal}]]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.scss b/frontend/src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.scss index b21060b960..71e0cda690 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.scss +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.scss @@ -39,3 +39,8 @@ user-select: none; position: relative; } + +.form-modal-title { + @include t.use-typography("headline-medium"); + color: var(--color-foreground-primary); +} diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index ffa2b8f361..c247f87ff0 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -134,6 +134,12 @@ (when (some? event) (.-relatedTarget event))) +(defn get-event-submitter + "Extract the submitter from a form submit event." + [^js event] + (when (some? event) + (.-submitter event))) + (defn select-target "Extract the target from event instance and select it" [^js event] diff --git a/frontend/translations/en.po b/frontend/translations/en.po index fea252fdc4..21a49b5a18 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -8412,6 +8412,10 @@ msgstr "Invalid value: % is not allowed." msgid "workspace.tokens.value-with-units" msgstr "Invalid value: Units are not allowed." +#: src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:16 +msgid "workspace.tokens.rename-group" +msgstr "Rename tokens group" + #: src/app/main/ui/workspace/sidebar.cljs:154, src/app/main/ui/workspace/sidebar.cljs:161 msgid "workspace.toolbar.assets" msgstr "Assets"