Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/bareforge/render/canvas.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,15 @@
;; Raw-html nodes own their children via :inner-html. Structured
;; text / child slots are ignored on these nodes so innerHTML
;; cannot be clobbered by a stray text node or appendChild pass.
raw-html? (some? (:inner-html node))]
;;
;; Derive from the tag's `:raw-html-slot?` meta (driven by
;; `meta/augment` — `x-icon` is the canonical user). Reading
;; from the *node's current* `:inner-html` would skip the
;; reconciler's `set-inner-html!` call when the value is
;; cleared (nil), leaving stale markup in the DOM — the
;; field has been emptied in the doc but the previous SVG
;; never gets wiped from the live element.
raw-html? (boolean (:raw-html-slot? (registry/get-meta (:tag node))))]
(if existing
(let [old-node (get old-by-id id)]
(when old-node
Expand Down
42 changes: 31 additions & 11 deletions src/bareforge/render/reconcile.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,27 @@
:when (and k v (not= "" v))]
(str k ":" v))))

(def ^:private numeric-string-re
;; Optionally negative integer or decimal — `200`, `-7`, `1.5`. Used
;; by `as-length` to recognise a bare numeric string (typed into an
;; inspector input as text) and apply the same `px` default the
;; numeric branch uses for actual numbers. Without this, a user who
;; types `200` into a width field gets the invalid CSS `width:200;`
;; and the browser silently drops it.
#"-?\d+(?:\.\d+)?")

(defn- as-length
"Render a pixel length from either a number (→ 'Npx') or a string
like '50%' / '10rem' (passed through). Returns nil for nil or
empty input."
"Render a pixel length from either a number (→ 'Npx'), a bare
numeric string (also → 'Npx'), or a CSS-unit string like '50%' /
'10rem' (passed through). Returns nil for nil or empty input."
[v]
(cond
(nil? v) nil
(number? v) (str v "px")
(and (string? v) (not= "" v)) v
:else nil))
(nil? v) nil
(number? v) (str v "px")
(and (string? v) (not= "" v)) (if (re-matches numeric-string-re v)
(str v "px")
v)
:else nil))

(defn layout->css
"Pure: build the inline style string for a node from its `:layout`
Expand Down Expand Up @@ -182,12 +193,21 @@
;; :free owns the width/height axis via :w/:h, so skip the
;; generic :width/:height fields when placement is :free to
;; avoid double-setting. Padding / margin still apply.
;;
;; Width / height go through `as-length` so a user who types
;; bare `200` into the inspector still gets `width:200px;`
;; instead of `width:200;` (which the browser drops as
;; invalid CSS). Padding / margin keep their string-only
;; behaviour because their multi-value forms (`10px 20px`,
;; `1em 2em 3em 4em`) don't fit `as-length`'s single-value
;; contract — if needed, a future variant could split on
;; whitespace.
named (cond-> base
(and (not= :free placement) width (not= "" width))
(conj (str "width:" width))
(and (not= :free placement) (as-length width))
(conj (str "width:" (as-length width)))

(and (not= :free placement) height (not= "" height))
(conj (str "height:" height))
(and (not= :free placement) (as-length height))
(conj (str "height:" (as-length height)))

(and padding (not= "" padding)) (conj (str "padding:" padding))
(and margin (not= "" margin)) (conj (str "margin:" margin)))
Expand Down
37 changes: 37 additions & 0 deletions test/bareforge/render/reconcile_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,43 @@
(is (= "width:200px;"
(r/layout->css {:placement :flow :width "200px"}))))

(deftest layout-css-width-bare-numeric-string-gets-px
(testing "an inspector input that types `200` (no unit) lands in
the doc as the string \"200\" — historically the
reconciler would emit `width:200;` which the browser
silently drops. `as-length` now recognises a bare
numeric string and applies the same `px` default the
number branch uses, so legacy docs and inspector input
both render correctly."
(is (= "width:200px;"
(r/layout->css {:placement :flow :width "200"})))
(is (= "width:1.5px;"
(r/layout->css {:placement :flow :width "1.5"})))
(is (= "width:-10px;"
(r/layout->css {:placement :flow :width "-10"})))))

(deftest layout-css-width-as-actual-number-also-gets-px
(testing "the existing inspector path that stores actual numbers
(free-coord scrubbing, parse-length-value's exact-
representation guard) keeps producing `Npx`"
(is (= "width:200px;"
(r/layout->css {:placement :flow :width 200})))))

(deftest layout-css-width-with-unit-passes-through
(testing "non-numeric / unit'd strings stay verbatim — `as-length`
only adds px to bare numeric strings, so `50%` /
`10rem` / `auto` round-trip unchanged"
(is (= "width:50%;"
(r/layout->css {:placement :flow :width "50%"})))
(is (= "width:10rem;"
(r/layout->css {:placement :flow :width "10rem"})))
(is (= "width:auto;"
(r/layout->css {:placement :flow :width "auto"})))))

(deftest layout-css-height-bare-numeric-string-gets-px
(is (= "height:120px;"
(r/layout->css {:placement :flow :height "120"}))))

(deftest layout-css-padding-and-margin
(is (= "padding:1rem;margin:0 auto;"
(r/layout->css {:placement :flow :padding "1rem" :margin "0 auto"}))))
Expand Down
Loading