mirror of
https://github.com/penpot/penpot.git
synced 2026-05-30 04:08:08 +00:00
♻️ Migrate fo-text and html-text renderers to modern component syntax (#9385)
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 <luislee3108@gmail.com>
This commit is contained in:
parent
defeeab054
commit
fa06efa84d
@ -43,4 +43,4 @@
|
|||||||
;; will give a tainted canvas error because the `foreignObject` cannot be
|
;; will give a tainted canvas error because the `foreignObject` cannot be
|
||||||
;; rendered.
|
;; rendered.
|
||||||
(and (nil? position-data) (or is-component? is-render?))
|
(and (nil? position-data) (or is-component? is-render?))
|
||||||
[:> fo/text-shape props])))
|
[:> fo/text-shape* props])))
|
||||||
|
|||||||
@ -11,73 +11,54 @@
|
|||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.types.color :as cc]
|
[app.common.types.color :as cc]
|
||||||
[app.main.ui.shapes.text.styles :as sts]
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
[app.util.object :as obj]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(mf/defc render-text
|
(mf/defc render-text*
|
||||||
{::mf/wrap-props false}
|
[{:keys [node parent shape]}]
|
||||||
[props]
|
(let [text (:text node)
|
||||||
(let [node (obj/get props "node")
|
style (if (= text "")
|
||||||
parent (obj/get props "parent")
|
(sts/generate-text-styles shape parent)
|
||||||
shape (obj/get props "shape")
|
(sts/generate-text-styles shape node))]
|
||||||
text (:text node)
|
|
||||||
style (if (= text "")
|
|
||||||
(sts/generate-text-styles shape parent)
|
|
||||||
(sts/generate-text-styles shape node))]
|
|
||||||
[:span.text-node {:style style}
|
[:span.text-node {:style style}
|
||||||
(if (= text "") "\u00A0" text)]))
|
(if (= text "") "\u00A0" text)]))
|
||||||
|
|
||||||
(mf/defc render-root
|
(mf/defc render-root*
|
||||||
{::mf/wrap-props false}
|
[{:keys [node children shape]}]
|
||||||
[props]
|
(let [style (sts/generate-root-styles shape node)]
|
||||||
(let [node (obj/get props "node")
|
|
||||||
children (obj/get props "children")
|
|
||||||
shape (obj/get props "shape")
|
|
||||||
style (sts/generate-root-styles shape node)]
|
|
||||||
[:div.root.rich-text
|
[:div.root.rich-text
|
||||||
{:style style
|
{:style style
|
||||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||||
children]))
|
children]))
|
||||||
|
|
||||||
(mf/defc render-paragraph-set
|
(mf/defc render-paragraph-set*
|
||||||
{::mf/wrap-props false}
|
[{:keys [children shape]}]
|
||||||
[props]
|
(let [style (sts/generate-paragraph-set-styles shape)]
|
||||||
(let [children (obj/get props "children")
|
|
||||||
shape (obj/get props "shape")
|
|
||||||
style (sts/generate-paragraph-set-styles shape)]
|
|
||||||
[:div.paragraph-set {:style style} children]))
|
[:div.paragraph-set {:style style} children]))
|
||||||
|
|
||||||
(mf/defc render-paragraph
|
(mf/defc render-paragraph*
|
||||||
{::mf/wrap-props false}
|
[{:keys [node children shape]}]
|
||||||
[props]
|
(let [style (sts/generate-paragraph-styles shape node)
|
||||||
(let [node (obj/get props "node")
|
dir (:text-direction node "auto")]
|
||||||
shape (obj/get props "shape")
|
|
||||||
children (obj/get props "children")
|
|
||||||
style (sts/generate-paragraph-styles shape node)
|
|
||||||
dir (:text-direction node "auto")]
|
|
||||||
[:p.paragraph {:style style :dir dir} children]))
|
[:p.paragraph {:style style :dir dir} children]))
|
||||||
|
|
||||||
;; -- Text nodes
|
;; -- Text nodes
|
||||||
(mf/defc render-node
|
(mf/defc render-node*
|
||||||
{::mf/wrap-props false}
|
{::mf/props :obj}
|
||||||
[props]
|
[{:keys [node] :as props}]
|
||||||
(let [{:keys [type text children]} (obj/get props "node")]
|
(let [{:keys [type text children]} node]
|
||||||
(if (string? text)
|
(if (string? text)
|
||||||
[:> render-text props]
|
[:> render-text* props]
|
||||||
(let [component (case type
|
(let [component (case type
|
||||||
"root" render-root
|
"root" render-root*
|
||||||
"paragraph-set" render-paragraph-set
|
"paragraph-set" render-paragraph-set*
|
||||||
"paragraph" render-paragraph
|
"paragraph" render-paragraph*
|
||||||
nil)]
|
nil)]
|
||||||
(when component
|
(when component
|
||||||
[:> component props
|
[:> component props
|
||||||
(for [[index node] (d/enumerate children)]
|
(for [[index child-node] (d/enumerate children)]
|
||||||
(let [props (-> (obj/clone props)
|
[:> render-node*
|
||||||
(obj/set! "node" node)
|
(mf/spread-props props {:node child-node :index index :key index})])])))))
|
||||||
(obj/set! "index" index)
|
|
||||||
(obj/set! "key" index))]
|
|
||||||
[:> render-node props]))])))))
|
|
||||||
|
|
||||||
(defn- next-color
|
(defn- next-color
|
||||||
"Given a set of colors try to get a color not yet used"
|
"Given a set of colors try to get a color not yet used"
|
||||||
@ -168,9 +149,8 @@
|
|||||||
|
|
||||||
[colors color-mapping color-mapping-inverse]))
|
[colors color-mapping color-mapping-inverse]))
|
||||||
|
|
||||||
(mf/defc text-shape
|
(mf/defc text-shape*
|
||||||
{::mf/props :obj
|
{::mf/forward-ref true}
|
||||||
::mf/forward-ref true}
|
|
||||||
[{:keys [shape grow-type]} ref]
|
[{:keys [shape grow-type]} ref]
|
||||||
(let [transform (gsh/transform-str shape)
|
(let [transform (gsh/transform-str shape)
|
||||||
id (dm/get-prop shape :id)
|
id (dm/get-prop shape :id)
|
||||||
@ -196,6 +176,6 @@
|
|||||||
;; `background-clip`
|
;; `background-clip`
|
||||||
[:style ".text-node { background-clip: text;
|
[:style ".text-node { background-clip: text;
|
||||||
-webkit-background-clip: text; }"]
|
-webkit-background-clip: text; }"]
|
||||||
[:& render-node {:index 0
|
[:> render-node* {:index 0
|
||||||
:shape shape
|
:shape shape
|
||||||
:node content}]]))
|
:node content}]]))
|
||||||
|
|||||||
@ -10,99 +10,73 @@
|
|||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.text :as legacy.txt]
|
[app.common.text :as legacy.txt]
|
||||||
[app.main.ui.shapes.text.styles :as sts]
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
[app.util.object :as obj]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(mf/defc render-text
|
(mf/defc render-text*
|
||||||
{::mf/wrap-props false}
|
[{:keys [node parent shape is-code]}]
|
||||||
[props]
|
(let [text (:text node)
|
||||||
(let [node (obj/get props "node")
|
style (if (= text "")
|
||||||
parent (obj/get props "parent")
|
(sts/generate-text-styles shape parent)
|
||||||
shape (obj/get props "shape")
|
(sts/generate-text-styles shape node))
|
||||||
code? (obj/get props "code?")
|
class (when is-code (:$id node))]
|
||||||
text (:text node)
|
|
||||||
style (if (= text "")
|
|
||||||
(sts/generate-text-styles shape parent)
|
|
||||||
(sts/generate-text-styles shape node))
|
|
||||||
class (when code? (:$id node))]
|
|
||||||
[:span.text-node {:style style :class class}
|
[:span.text-node {:style style :class class}
|
||||||
(if (= text "") "\u00A0" text)]))
|
(if (= text "") "\u00A0" text)]))
|
||||||
|
|
||||||
(mf/defc render-root
|
(mf/defc render-root*
|
||||||
{::mf/wrap-props false}
|
[{:keys [node children shape is-code]}]
|
||||||
[props]
|
(let [style (sts/generate-root-styles shape node is-code)
|
||||||
(let [node (obj/get props "node")
|
class (when is-code (:$id 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))]
|
|
||||||
[:div.root.rich-text
|
[:div.root.rich-text
|
||||||
{:style style
|
{:style style
|
||||||
:class class
|
:class class
|
||||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||||
children]))
|
children]))
|
||||||
|
|
||||||
(mf/defc render-paragraph-set
|
(mf/defc render-paragraph-set*
|
||||||
{::mf/wrap-props false}
|
[{:keys [node children shape is-code]}]
|
||||||
[props]
|
(let [style (when-not is-code (sts/generate-paragraph-set-styles shape))
|
||||||
(let [node (obj/get props "node")
|
class (when is-code (:$id 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))]
|
|
||||||
[:div.paragraph-set {:style style :class class} children]))
|
[:div.paragraph-set {:style style :class class} children]))
|
||||||
|
|
||||||
(mf/defc render-paragraph
|
(mf/defc render-paragraph*
|
||||||
{::mf/wrap-props false}
|
[{:keys [node children shape is-code]}]
|
||||||
[props]
|
(let [style (when-not is-code (sts/generate-paragraph-styles shape node))
|
||||||
(let [node (obj/get props "node")
|
class (when is-code (:$id node))
|
||||||
shape (obj/get props "shape")
|
dir (:text-direction node "auto")]
|
||||||
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")]
|
|
||||||
[:p.paragraph {:style style :dir dir :class class} children]))
|
[:p.paragraph {:style style :dir dir :class class} children]))
|
||||||
|
|
||||||
;; -- Text nodes
|
;; -- Text nodes
|
||||||
(mf/defc render-node
|
(mf/defc render-node*
|
||||||
{::mf/wrap-props false}
|
{::mf/props :obj}
|
||||||
[props]
|
[{:keys [node is-code] :as props}]
|
||||||
(let [{:keys [type text children] :as parent} (obj/get props "node")
|
(let [{:keys [type text children] :as parent} node]
|
||||||
code? (obj/get props "code?")]
|
|
||||||
(if (string? text)
|
(if (string? text)
|
||||||
[:> render-text props]
|
[:> render-text* props]
|
||||||
(let [component (case type
|
(let [component (case type
|
||||||
"root" render-root
|
"root" render-root*
|
||||||
"paragraph-set" render-paragraph-set
|
"paragraph-set" render-paragraph-set*
|
||||||
"paragraph" render-paragraph
|
"paragraph" render-paragraph*
|
||||||
nil)]
|
nil)]
|
||||||
(when component
|
(when component
|
||||||
[:> component props
|
[:> component props
|
||||||
(for [[index node] (d/enumerate children)]
|
(for [[index child-node] (d/enumerate children)]
|
||||||
(let [props (-> (obj/clone props)
|
[:> render-node*
|
||||||
(obj/set! "node" node)
|
(mf/spread-props props
|
||||||
(obj/set! "parent" parent)
|
{:node child-node
|
||||||
(obj/set! "index" index)
|
:parent parent
|
||||||
(obj/set! "key" index)
|
:index index
|
||||||
(obj/set! "code?" code?))]
|
:key index
|
||||||
[:> render-node props]))])))))
|
:is-code is-code})])])))))
|
||||||
|
|
||||||
(mf/defc text-shape
|
(mf/defc text-shape*
|
||||||
{::mf/wrap-props false
|
{::mf/forward-ref true}
|
||||||
::mf/forward-ref true}
|
[{:keys [shape grow-type is-code]} ref]
|
||||||
[props ref]
|
(let [{:keys [id x y width height content]} shape
|
||||||
(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
|
|
||||||
|
|
||||||
content (if code? (legacy.txt/index-content content) content)
|
content (if is-code (legacy.txt/index-content content) content)
|
||||||
|
|
||||||
style
|
style
|
||||||
(when-not code?
|
(when-not is-code
|
||||||
#js {:position "fixed"
|
#js {:position "fixed"
|
||||||
:left 0
|
:left 0
|
||||||
:top 0
|
:top 0
|
||||||
@ -118,10 +92,10 @@
|
|||||||
:style style}
|
:style style}
|
||||||
;; We use a class here because react has a bug that won't use the appropriate selector for
|
;; We use a class here because react has a bug that won't use the appropriate selector for
|
||||||
;; `background-clip`
|
;; `background-clip`
|
||||||
(when (not code?)
|
(when (not is-code)
|
||||||
[:style ".text-node { background-clip: text;
|
[:style ".text-node { background-clip: text;
|
||||||
-webkit-background-clip: text; }"])
|
-webkit-background-clip: text; }"])
|
||||||
[:& render-node {:index 0
|
[:> render-node* {:index 0
|
||||||
:shape shape
|
:shape shape
|
||||||
:node content
|
:node content
|
||||||
:code? code?}]]))
|
:is-code is-code}]]))
|
||||||
|
|||||||
@ -130,10 +130,10 @@
|
|||||||
(when (some? node)
|
(when (some? node)
|
||||||
(on-update shape node))))]
|
(on-update shape node))))]
|
||||||
|
|
||||||
[:& html/text-shape {:key (str "shape-" (:id shape))
|
[:> html/text-shape* {:key (str "shape-" (:id shape))
|
||||||
:ref handle-update
|
:ref handle-update
|
||||||
:shape shape
|
:shape shape
|
||||||
:grow-type (:grow-type shape)}]))
|
:grow-type (:grow-type shape)}]))
|
||||||
|
|
||||||
(defn text-properties-equal?
|
(defn text-properties-equal?
|
||||||
[shape other]
|
[shape other]
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
indent))
|
indent))
|
||||||
|
|
||||||
(cfh/text-shape? shape)
|
(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*[\"'][^\"']*[\"']" "")]
|
text-shape-html (str/replace text-shape-html #"style\s*=\s*[\"'][^\"']*[\"']" "")]
|
||||||
(dm/fmt "%<div class=\"%\">\n%\n%</div>"
|
(dm/fmt "%<div class=\"%\">\n%\n%</div>"
|
||||||
indent
|
indent
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user