From 25f24f0e8e3d044ea81df42263466c29dc39232f Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Thu, 19 Feb 2026 09:58:29 +0100 Subject: [PATCH] :recycle: Extract token parsing --- .../management/forms/controls/combobox.cljs | 126 +++++------------- .../management/forms/controls/floating.cljs | 38 +++--- .../forms/controls/token_parsing.cljs | 66 +++++++++ 3 files changed, 123 insertions(+), 107 deletions(-) create mode 100644 frontend/src/app/main/ui/workspace/tokens/management/forms/controls/token_parsing.cljs diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs index bbeb7618ba..a1d7f876aa 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs @@ -14,13 +14,13 @@ [app.main.ui.context :as muc] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.controls.input :as ds] - [app.main.ui.ds.controls.select :refer [get-option]] [app.main.ui.ds.controls.shared.options-dropdown :refer [options-dropdown*]] [app.main.ui.ds.controls.utilities.utils :as csu] [app.main.ui.ds.foundations.assets.icon :as i] [app.main.ui.forms :as fc] [app.main.ui.workspace.tokens.management.forms.controls.floating :refer [use-floating-dropdown]] [app.main.ui.workspace.tokens.management.forms.controls.navigation :refer [use-navigation]] + [app.main.ui.workspace.tokens.management.forms.controls.token-parsing :as tp] [app.util.dom :as dom] [app.util.forms :as fm] [app.util.i18n :refer [tr]] @@ -29,61 +29,6 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) -(defn extract-partial-token - [value cursor] - (let [text-before (subs value 0 cursor) - last-open (str/last-index-of text-before "{") - last-close (str/last-index-of text-before "}")] - (when (and last-open (or (nil? last-close) (> last-open last-close))) - {:start last-open - :partial (subs text-before (inc last-open))}))) - -(defn replace-active-token - [value cursor new-name] - - (let [before (subs value 0 cursor) - last-open (str/last-index-of before "{") - last-close (str/last-index-of before "}")] - - (if (and last-open - (or (nil? last-close) - (> last-open last-close))) - - (let [after-start (subs value last-open) - close-pos (str/index-of after-start "}") - end (if close-pos - (+ last-open close-pos 1) - cursor)] - (str (subs value 0 last-open) - "{" new-name "}" - (subs value end))) - (str (subs value 0 cursor) - "{" new-name "}" - (subs value cursor))))) - -(defn active-token [value input-node] - (let [cursor (.-selectionStart input-node)] - (extract-partial-token value cursor))) - -(defn remove-self-token [filtered-options current-token] - (let [group (:type current-token) - current-id (:id current-token) - filtered-options (deref filtered-options)] - (update filtered-options group - (fn [options] - (remove #(= (:id %) current-id) options))))) - -(defn- select-option-by-id - [id options-ref input-node value] - (let [cursor (.-selectionStart input-node) - options (mf/ref-val options-ref) - options (if (delay? options) @options options) - - option (get-option options id) - name (:name option) - final-val (replace-active-token value cursor name)] - final-val)) - (defn- resolve-value [tokens prev-token token-name value] (let [valid-token-name? @@ -150,7 +95,7 @@ visible-options (mf/with-memo [filtered-tokens-by-type token] (if token - (remove-self-token filtered-tokens-by-type token) + (tp/remove-self-token filtered-tokens-by-type token) filtered-tokens-by-type)) dropdown-options @@ -168,12 +113,12 @@ toggle-dropdown (mf/use-fn - (mf/deps) + (mf/deps is-open) (fn [event] (dom/prevent-default event) + (swap! is-open* not) (let [input-node (mf/ref-val ref)] - (dom/focus! input-node)) - (swap! is-open* not))) + (dom/focus! input-node)))) resolve-stream (mf/with-memo [token] @@ -186,7 +131,7 @@ (mf/deps value resolve-stream name) (fn [id] (let [input-node (mf/ref-val ref) - final-val (select-option-by-id id options-ref input-node value)] + final-val (tp/select-option-by-id id options-ref input-node value)] (fm/on-input-change form name final-val true) (rx/push! resolve-stream final-val) (reset! filter-term* "") @@ -207,7 +152,7 @@ (fn [event] (let [node (dom/get-target event) value (dom/get-input-value node) - token (active-token value node)] + token (tp/active-token value node)] (fm/on-input-change form name value) (rx/push! resolve-stream value) @@ -227,7 +172,7 @@ (let [input-node (mf/ref-val ref) node (dom/get-current-target event) id (dom/get-data node "id") - final-val (select-option-by-id id options-ref input-node value)] + final-val (tp/select-option-by-id id options-ref input-node value)] (fm/on-input-change form name final-val true) (rx/push! resolve-stream final-val) @@ -278,7 +223,7 @@ props) - floating (use-floating-dropdown is-open wrapper-ref dropdown-ref)] + {:keys [style ready?]} (use-floating-dropdown is-open wrapper-ref dropdown-ref)] (mf/with-effect [resolve-stream tokens token name token-name] (let [subs (->> resolve-stream @@ -304,41 +249,42 @@ (mf/with-effect [dropdown-options] (mf/set-ref-val! options-ref dropdown-options)) - (mf/with-effect [is-open* ref nodes-ref] + (mf/with-effect [is-open* ref wrapper-ref] (when is-open (let [handler (fn [event] - (let [input-node (mf/ref-val ref) + (let [wrapper-node (mf/ref-val wrapper-ref) dropdown-node (mf/ref-val dropdown-ref) target (dom/get-target event)] - (when (and input-node dropdown-node - (not (dom/child? target input-node)) + (when (and wrapper-node dropdown-node + (not (dom/child? target wrapper-node)) (not (dom/child? target dropdown-node))) (reset! is-open* false))))] (.addEventListener js/document "mousedown" handler) (fn [] - (.removeEventListener js/document "mousedown" handler))))) + (.removeEventListener js/document "mousedown" handler))))) - [:div {:ref wrapper-ref} - [:> ds/input* props] - (when ^boolean is-open - (let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)] - (mf/portal - (mf/html - [:> options-dropdown* {:on-click on-option-click - :class (stl/css :dropdown) - :style {:visibility (if (:ready? floating) "visible" "hidden") - :left (get-in floating [:style :left]) - :top (get-in floating [:style :top]) - :width (get-in floating [:style :width])} - :id listbox-id - :options options - :focused focused-id - :selected nil - :align :right - :empty-to-end empty-to-end - :wrapper-ref dropdown-ref - :ref set-option-ref}]) - (dom/get-body))))])) \ No newline at end of file + [:div {:ref wrapper-ref} + [:> ds/input* props] + (when ^boolean is-open + (let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)] + (mf/portal + (mf/html + [:> options-dropdown* {:on-click on-option-click + :class (stl/css :dropdown) + :style {:visibility (if ready? "visible" "hidden") + :left (:left style) + :top (or (:top style) "unset") + :bottom (or (:bottom style) "unset") + :width (:width style)} + :id listbox-id + :options options + :focused focused-id + :selected nil + :align :right + :empty-to-end empty-to-end + :wrapper-ref dropdown-ref + :ref set-option-ref}]) + (dom/get-body))))])) \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/floating.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/floating.cljs index 0bd3aa1ebe..0195b37cc8 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/floating.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/floating.cljs @@ -10,8 +10,10 @@ [rumext.v2 :as mf])) (defn use-floating-dropdown [is-open wrapper-ref dropdown-ref] - (let [pos* (mf/use-state nil) - ready* (mf/use-state false) + (let [position* (mf/use-state nil) + position (deref position*) + ready* (mf/use-state false) + ready (deref ready*) calculate-position (fn [node] (let [combobox-rect (dom/get-bounding-rect node) @@ -24,22 +26,23 @@ windows-height (-> (dom/get-window-size) (:height)) - space-below (- windows-height (:bottom combobox-rect)) - space-above (:top combobox-rect) + space-below (- windows-height (:bottom combobox-rect)) - place-top? (< space-below dropdown-height) + open-up? (and dropdown-height + (> dropdown-height space-below)) - position (if place-top? - {:top (str (- space-above dropdown-height -14) "px") - :left (str (:left combobox-rect) "px") - :width (str (:width combobox-rect) "px") - :placement :top} - {:top (str (+ (:bottom combobox-rect) 4) "px") - :left (str (:left combobox-rect) "px") - :width (str (:width combobox-rect) "px") - :placement :bottom})] + position (if open-up? + {:bottom (str (- windows-height (:top combobox-rect) 12) "px") + :left (str (:left combobox-rect) "px") + :width (str (:width combobox-rect) "px") + :placement :top} + + {:top (str (+ (:bottom combobox-rect) 4) "px") + :left (str (:left combobox-rect) "px") + :width (str (:width combobox-rect) "px") + :placement :bottom})] (reset! ready* true) - (reset! pos* position)))] + (reset! position* position)))] (mf/with-effect [is-open dropdown-ref wrapper-ref] (when is-open @@ -63,5 +66,6 @@ (.removeEventListener js/window "resize" handler) (.removeEventListener js/window "scroll" handler true))))) - {:style @pos* - :ready? @ready*})) \ No newline at end of file + {:style position + :ready? ready + :recalculate calculate-position})) \ No newline at end of file diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/token_parsing.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/token_parsing.cljs new file mode 100644 index 0000000000..1a89195f67 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/controls/token_parsing.cljs @@ -0,0 +1,66 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.main.ui.workspace.tokens.management.forms.controls.token-parsing + (:require + [app.main.ui.ds.controls.select :refer [get-option]] + [cuerdas.core :as str] + [rumext.v2 :as mf])) + +(defn extract-partial-token + [value cursor] + (let [text-before (subs value 0 cursor) + last-open (str/last-index-of text-before "{") + last-close (str/last-index-of text-before "}")] + (when (and last-open (or (nil? last-close) (> last-open last-close))) + {:start last-open + :partial (subs text-before (inc last-open))}))) + +(defn replace-active-token + [value cursor new-name] + + (let [before (subs value 0 cursor) + last-open (str/last-index-of before "{") + last-close (str/last-index-of before "}")] + + (if (and last-open + (or (nil? last-close) + (> last-open last-close))) + + (let [after-start (subs value last-open) + close-pos (str/index-of after-start "}") + end (if close-pos + (+ last-open close-pos 1) + cursor)] + (str (subs value 0 last-open) + "{" new-name "}" + (subs value end))) + (str (subs value 0 cursor) + "{" new-name "}" + (subs value cursor))))) + +(defn active-token [value input-node] + (let [cursor (.-selectionStart input-node)] + (extract-partial-token value cursor))) + +(defn remove-self-token [filtered-options current-token] + (let [group (:type current-token) + current-id (:id current-token) + filtered-options (deref filtered-options)] + (update filtered-options group + (fn [options] + (remove #(= (:id %) current-id) options))))) + +(defn select-option-by-id + [id options-ref input-node value] + (let [cursor (.-selectionStart input-node) + options (mf/ref-val options-ref) + options (if (delay? options) @options options) + + option (get-option options id) + name (:name option) + final-val (replace-active-token value cursor name)] + final-val)) \ No newline at end of file