From fa06efa84ddd5fa7561245cf52e1baa781c8e3cf Mon Sep 17 00:00:00 2001 From: FairyPiggyDev Date: Thu, 7 May 2026 09:03:51 -0400 Subject: [PATCH] :recycle: Migrate fo-text and html-text renderers to modern component syntax (#9385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step toward issue #9260 (incremental migration of legacy UI components to the modern `*`-suffixed syntax, removing the per-render JS-to-Clojure props conversion overhead). Twin namespaces with parallel structure: each defines six components that drive a recursive text rendering pass over the editor's content tree (root -> paragraph-set -> paragraph -> node -> text). Both files were uniformly legacy: every component carried `::mf/wrap-props false` and read its props with `(obj/get props "key")`. None had `::mf/register`, `unchecked-get` or `obj/merge!`, so they qualify as clean Case-A migrations. frontend/src/app/main/ui/shapes/text/fo_text.cljs (6 components) ---------------------------------------------------------------- - `render-text` -> `render-text*` - `render-root` -> `render-root*` - `render-paragraph-set` -> `render-paragraph-set*` - `render-paragraph` -> `render-paragraph*` - `render-node` -> `render-node*` (forward-props case, see below) - `text-shape` -> `text-shape*` (`::mf/forward-ref` preserved) The four leaf components switch from `[props]` + per-key `(obj/get props "key")` to standard destructuring. `text-shape` already used destructuring under `::mf/props :obj`; that legacy metadata is dropped because the modern `*` form handles props automatically. Its single `::mf/forward-ref true` is kept per the prompt's "preserve forward-ref" rule. `render-node` is the recursive driver. It needs to forward all of its incoming props to the matched paragraph-* / text component and then to a child `render-node*` after overriding `:node`, `:index` and `:key`. The migrated form uses `::mf/props :obj` together with `{:keys [node] :as props}` to keep the JS-object props symbol available, and `(mf/spread-props props {…})` replaces the previous `obj/clone` + `obj/set!` chain. `app.util.object` is no longer required by this namespace and the `(:require ... [app.util.object :as obj] ...)` line is removed. frontend/src/app/main/ui/shapes/text/html_text.cljs (6 components) ----------------------------------------------------------------- Identical six-component shape as `fo_text.cljs`, plus a `code?` flag threaded through every component to switch the rendering path between regular shapes and code-style shapes. - `render-text` -> `render-text*` - `render-root` -> `render-root*` - `render-paragraph-set` -> `render-paragraph-set*` - `render-paragraph` -> `render-paragraph*` - `render-node` -> `render-node*` (same forward-props treatment as above, plus `is-code` in the spread) - `text-shape` -> `text-shape*` (`::mf/forward-ref` preserved) The `code?` boolean prop is renamed to `is-code` per the migration prompt's "?-suffixed boolean -> `is-` prefix" rule. The rename is applied at every read site (5 components) and at the `text-shape*` internal call to `render-node*`, so the prop is consistent inside the namespace. `app.util.object` is no longer required by this namespace either and the corresponding `:require` line is dropped. External call sites (3 files, 4 sites) -------------------------------------- - `frontend/src/app/main/ui/shapes/text.cljs` - the legacy text-shape wrapper (intentionally kept legacy in this PR because it dispatches to `svg/text-shape`, which is still being touched by the in-flight PR #9016) now calls `[:> fo/text-shape* props]`. The `props` symbol is the wrapper's incoming JS-object; modern destructured components accept JS-object props at the call site via `[:>` so this works unchanged. - `frontend/src/app/util/code_gen/markup_html.cljs` - `(mf/element text/text-shape #js {:shape shape :code? true})` becomes `(mf/element text/text-shape* #js {:shape shape :is-code true})` (component renamed and the `code?` JS key updated to match the renamed prop). - `frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs` - `[:& html/text-shape {…}]` -> `[:> html/text-shape* {…}]`. Behavior preserved verbatim --------------------------- Same render output, same forward-ref forwarding semantics, same recursive children-by-index keying, same default `:dir "auto"` on `render-paragraph*`. The visible-prop changes are only the `code?` -> `is-code` rename, all driven from this namespace and its single caller in `markup_html.cljs`. Github #9260 Signed-off-by: FairyPigDev --- frontend/src/app/main/ui/shapes/text.cljs | 2 +- .../src/app/main/ui/shapes/text/fo_text.cljs | 84 +++++------- .../app/main/ui/shapes/text/html_text.cljs | 120 +++++++----------- .../shapes/text/viewport_texts_html.cljs | 8 +- .../src/app/util/code_gen/markup_html.cljs | 2 +- 5 files changed, 85 insertions(+), 131 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/text.cljs b/frontend/src/app/main/ui/shapes/text.cljs index 3610d52fe0..24cdf2ed08 100644 --- a/frontend/src/app/main/ui/shapes/text.cljs +++ b/frontend/src/app/main/ui/shapes/text.cljs @@ -43,4 +43,4 @@ ;; will give a tainted canvas error because the `foreignObject` cannot be ;; rendered. (and (nil? position-data) (or is-component? is-render?)) - [:> fo/text-shape props]))) + [:> fo/text-shape* props]))) diff --git a/frontend/src/app/main/ui/shapes/text/fo_text.cljs b/frontend/src/app/main/ui/shapes/text/fo_text.cljs index aed6e6e0b9..f5f5dcb963 100644 --- a/frontend/src/app/main/ui/shapes/text/fo_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/fo_text.cljs @@ -11,73 +11,54 @@ [app.common.geom.shapes :as gsh] [app.common.types.color :as cc] [app.main.ui.shapes.text.styles :as sts] - [app.util.object :as obj] [cuerdas.core :as str] [rumext.v2 :as mf])) -(mf/defc render-text - {::mf/wrap-props false} - [props] - (let [node (obj/get props "node") - parent (obj/get props "parent") - shape (obj/get props "shape") - text (:text node) - style (if (= text "") - (sts/generate-text-styles shape parent) - (sts/generate-text-styles shape node))] +(mf/defc render-text* + [{:keys [node parent shape]}] + (let [text (:text node) + style (if (= text "") + (sts/generate-text-styles shape parent) + (sts/generate-text-styles shape node))] [:span.text-node {:style style} (if (= text "") "\u00A0" text)])) -(mf/defc render-root - {::mf/wrap-props false} - [props] - (let [node (obj/get props "node") - children (obj/get props "children") - shape (obj/get props "shape") - style (sts/generate-root-styles shape node)] +(mf/defc render-root* + [{:keys [node children shape]}] + (let [style (sts/generate-root-styles shape node)] [:div.root.rich-text {:style style :xmlns "http://www.w3.org/1999/xhtml"} children])) -(mf/defc render-paragraph-set - {::mf/wrap-props false} - [props] - (let [children (obj/get props "children") - shape (obj/get props "shape") - style (sts/generate-paragraph-set-styles shape)] +(mf/defc render-paragraph-set* + [{:keys [children shape]}] + (let [style (sts/generate-paragraph-set-styles shape)] [:div.paragraph-set {:style style} children])) -(mf/defc render-paragraph - {::mf/wrap-props false} - [props] - (let [node (obj/get props "node") - shape (obj/get props "shape") - children (obj/get props "children") - style (sts/generate-paragraph-styles shape node) - dir (:text-direction node "auto")] +(mf/defc render-paragraph* + [{:keys [node children shape]}] + (let [style (sts/generate-paragraph-styles shape node) + dir (:text-direction node "auto")] [:p.paragraph {:style style :dir dir} children])) ;; -- Text nodes -(mf/defc render-node - {::mf/wrap-props false} - [props] - (let [{:keys [type text children]} (obj/get props "node")] +(mf/defc render-node* + {::mf/props :obj} + [{:keys [node] :as props}] + (let [{:keys [type text children]} node] (if (string? text) - [:> render-text props] + [:> render-text* props] (let [component (case type - "root" render-root - "paragraph-set" render-paragraph-set - "paragraph" render-paragraph + "root" render-root* + "paragraph-set" render-paragraph-set* + "paragraph" render-paragraph* nil)] (when component [:> component props - (for [[index node] (d/enumerate children)] - (let [props (-> (obj/clone props) - (obj/set! "node" node) - (obj/set! "index" index) - (obj/set! "key" index))] - [:> render-node props]))]))))) + (for [[index child-node] (d/enumerate children)] + [:> render-node* + (mf/spread-props props {:node child-node :index index :key index})])]))))) (defn- next-color "Given a set of colors try to get a color not yet used" @@ -168,9 +149,8 @@ [colors color-mapping color-mapping-inverse])) -(mf/defc text-shape - {::mf/props :obj - ::mf/forward-ref true} +(mf/defc text-shape* + {::mf/forward-ref true} [{:keys [shape grow-type]} ref] (let [transform (gsh/transform-str shape) id (dm/get-prop shape :id) @@ -196,6 +176,6 @@ ;; `background-clip` [:style ".text-node { background-clip: text; -webkit-background-clip: text; }"] - [:& render-node {:index 0 - :shape shape - :node content}]])) + [:> render-node* {:index 0 + :shape shape + :node content}]])) diff --git a/frontend/src/app/main/ui/shapes/text/html_text.cljs b/frontend/src/app/main/ui/shapes/text/html_text.cljs index ff86df2e8e..a2fa9b678a 100644 --- a/frontend/src/app/main/ui/shapes/text/html_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/html_text.cljs @@ -10,99 +10,73 @@ [app.common.data.macros :as dm] [app.common.text :as legacy.txt] [app.main.ui.shapes.text.styles :as sts] - [app.util.object :as obj] [rumext.v2 :as mf])) -(mf/defc render-text - {::mf/wrap-props false} - [props] - (let [node (obj/get props "node") - parent (obj/get props "parent") - shape (obj/get props "shape") - code? (obj/get props "code?") - text (:text node) - style (if (= text "") - (sts/generate-text-styles shape parent) - (sts/generate-text-styles shape node)) - class (when code? (:$id node))] +(mf/defc render-text* + [{:keys [node parent shape is-code]}] + (let [text (:text node) + style (if (= text "") + (sts/generate-text-styles shape parent) + (sts/generate-text-styles shape node)) + class (when is-code (:$id node))] [:span.text-node {:style style :class class} (if (= text "") "\u00A0" text)])) -(mf/defc render-root - {::mf/wrap-props false} - [props] - (let [node (obj/get props "node") - children (obj/get props "children") - shape (obj/get props "shape") - code? (obj/get props "code?") - style (sts/generate-root-styles shape node code?) - class (when code? (:$id node))] +(mf/defc render-root* + [{:keys [node children shape is-code]}] + (let [style (sts/generate-root-styles shape node is-code) + class (when is-code (:$id node))] [:div.root.rich-text {:style style :class class :xmlns "http://www.w3.org/1999/xhtml"} children])) -(mf/defc render-paragraph-set - {::mf/wrap-props false} - [props] - (let [node (obj/get props "node") - children (obj/get props "children") - shape (obj/get props "shape") - code? (obj/get props "code?") - style (when-not code? (sts/generate-paragraph-set-styles shape)) - class (when code? (:$id node))] +(mf/defc render-paragraph-set* + [{:keys [node children shape is-code]}] + (let [style (when-not is-code (sts/generate-paragraph-set-styles shape)) + class (when is-code (:$id node))] [:div.paragraph-set {:style style :class class} children])) -(mf/defc render-paragraph - {::mf/wrap-props false} - [props] - (let [node (obj/get props "node") - shape (obj/get props "shape") - children (obj/get props "children") - code? (obj/get props "code?") - style (when-not code? (sts/generate-paragraph-styles shape node)) - class (when code? (:$id node)) - dir (:text-direction node "auto")] +(mf/defc render-paragraph* + [{:keys [node children shape is-code]}] + (let [style (when-not is-code (sts/generate-paragraph-styles shape node)) + class (when is-code (:$id node)) + dir (:text-direction node "auto")] [:p.paragraph {:style style :dir dir :class class} children])) ;; -- Text nodes -(mf/defc render-node - {::mf/wrap-props false} - [props] - (let [{:keys [type text children] :as parent} (obj/get props "node") - code? (obj/get props "code?")] +(mf/defc render-node* + {::mf/props :obj} + [{:keys [node is-code] :as props}] + (let [{:keys [type text children] :as parent} node] (if (string? text) - [:> render-text props] + [:> render-text* props] (let [component (case type - "root" render-root - "paragraph-set" render-paragraph-set - "paragraph" render-paragraph + "root" render-root* + "paragraph-set" render-paragraph-set* + "paragraph" render-paragraph* nil)] (when component [:> component props - (for [[index node] (d/enumerate children)] - (let [props (-> (obj/clone props) - (obj/set! "node" node) - (obj/set! "parent" parent) - (obj/set! "index" index) - (obj/set! "key" index) - (obj/set! "code?" code?))] - [:> render-node props]))]))))) + (for [[index child-node] (d/enumerate children)] + [:> render-node* + (mf/spread-props props + {:node child-node + :parent parent + :index index + :key index + :is-code is-code})])]))))) -(mf/defc text-shape - {::mf/wrap-props false - ::mf/forward-ref true} - [props ref] - (let [shape (obj/get props "shape") - grow-type (obj/get props "grow-type") - code? (obj/get props "code?") - {:keys [id x y width height content]} shape +(mf/defc text-shape* + {::mf/forward-ref true} + [{:keys [shape grow-type is-code]} ref] + (let [{:keys [id x y width height content]} shape - content (if code? (legacy.txt/index-content content) content) + content (if is-code (legacy.txt/index-content content) content) style - (when-not code? + (when-not is-code #js {:position "fixed" :left 0 :top 0 @@ -118,10 +92,10 @@ :style style} ;; We use a class here because react has a bug that won't use the appropriate selector for ;; `background-clip` - (when (not code?) + (when (not is-code) [:style ".text-node { background-clip: text; -webkit-background-clip: text; }"]) - [:& render-node {:index 0 - :shape shape - :node content - :code? code?}]])) + [:> render-node* {:index 0 + :shape shape + :node content + :is-code is-code}]])) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs index fda9e4e911..0fefa347a8 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts_html.cljs @@ -130,10 +130,10 @@ (when (some? node) (on-update shape node))))] - [:& html/text-shape {:key (str "shape-" (:id shape)) - :ref handle-update - :shape shape - :grow-type (:grow-type shape)}])) + [:> html/text-shape* {:key (str "shape-" (:id shape)) + :ref handle-update + :shape shape + :grow-type (:grow-type shape)}])) (defn text-properties-equal? [shape other] diff --git a/frontend/src/app/util/code_gen/markup_html.cljs b/frontend/src/app/util/code_gen/markup_html.cljs index b34b4d026b..01f65c3331 100644 --- a/frontend/src/app/util/code_gen/markup_html.cljs +++ b/frontend/src/app/util/code_gen/markup_html.cljs @@ -38,7 +38,7 @@ indent)) (cfh/text-shape? shape) - (let [text-shape-html (rds/renderToStaticMarkup (mf/element text/text-shape #js {:shape shape :code? true})) + (let [text-shape-html (rds/renderToStaticMarkup (mf/element text/text-shape* #js {:shape shape :is-code true})) text-shape-html (str/replace text-shape-html #"style\s*=\s*[\"'][^\"']*[\"']" "")] (dm/fmt "%
\n%\n%
" indent