diff --git a/CHANGELOG.md b/CHANGELOG.md index e97b8ee..c6537d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ Unreleased changes are available via `io.github.borkdude/html {:git/sha "..."}` ## Unreleased - Fix shorthand classes being dropped when the attribute map has no `:class`: `[:div.card {:id "a"}]` rendered `
`. The same path interpolated a symbol's _name_ when `:class` was bound to an expression, so `(let [x "big"] [:div.card {:class x}])` rendered `class="card x"`. +- Omit attributes whose value is `nil`, or `false` in html mode: `[:input {:disabled false}]` rendered `disabled="false"`, which disables the input. This also applies to non-boolean attributes: `[:option {:value false}]` now renders ``. Matches hiccup ([#19](https://github.com/borkdude/html/pull/19), [@telekid](https://github.com/telekid)) +- Render `true` and `false` as `"true"` and `"false"` for `aria-*`, `data-*`, `contenteditable`, `draggable`, `focusable` and `spellcheck`. Names match in any case, so `:spellCheck` works as well +- Fix `[:div {}]` rendering `
` ## 0.2.6 diff --git a/src/borkdude/html.cljc b/src/borkdude/html.cljc index ae41c26..3009219 100644 --- a/src/borkdude/html.cljc +++ b/src/borkdude/html.cljc @@ -32,20 +32,30 @@ (str (name k) ": " (name v) ";")) m))) +(defn- string-boolean-attr? [k] + (let [k (str/lower-case k)] + (or (str/starts-with? k "aria-") + (str/starts-with? k "data-") + (contains? #{"contenteditable" "draggable" "focusable" "spellcheck"} k)))) + (defn ->attrs "Implementation, do not use" ([opts m] (let [xml? (= :xml (:mode opts))] - (str/join " " - (map (fn [[k v]] - (if (and (true? v) (not xml?)) - (name k) - (str (name k) + (apply str + (keep (fn [[k v]] + (let [k (name k)] + (cond + (nil? v) nil + (and (boolean? v) (not xml?) (not (string-boolean-attr? k))) + (when v (str " " k)) + :else + (str " " k "=" (cond (string? v) (pr-str (escape-html v)) (keyword? v) (pr-str (name v)) - (and (map? v) (= "style" (name k))) (pr-str (escape-html (->css v))) - :else (pr-str (escape-html (str v))))))) - m)))) + (and (map? v) (= "style" k)) (pr-str (escape-html (->css v))) + :else (pr-str (escape-html (str v)))))))) + m)))) ([opts m base-map] (let [m (merge base-map m)] (->attrs opts m)))) @@ -126,10 +136,7 @@ omit-tag? (= "<>" tag) unsafe? (= "$" tag) attrs (if ?attrs - (let [a (compile-attrs opts ?attrs)] - (if (string? a) - (str " " a) - a)) + (compile-attrs opts ?attrs) "")] (if unsafe? `(->Html (str ~(first children))) @@ -137,7 +144,7 @@ nil (if (string? attrs) [(str "<" tag attrs ">")] - ["<" tag " " attrs ">"])) + ["<" tag attrs ">"])) ~@(map #(list (if xml? `xml `html) %) children) diff --git a/test/borkdude/html_test.cljc b/test/borkdude/html_test.cljc index b93bdca..27c33b6 100644 --- a/test/borkdude/html_test.cljc +++ b/test/borkdude/html_test.cljc @@ -81,6 +81,61 @@ "" (xml [:input {:checked true}]) + "" + (html [:input {:disabled false}]) + + "" + (html [:input {:disabled nil}]) + + "" + (let [d false] + (html [:input {:type "text" :disabled d}])) + + "" + (xml [:input {:disabled false}]) + + "" + (xml [:input {:disabled nil}]) + + "" + (html [:button {:aria-expanded true}]) + + "" + (let [b false] + (html [:button {:aria-expanded b}])) + + "" + (html [:span {:aria-label nil}]) + + "
" + (html [:div {:spellcheck false}]) + + "
" + (html [:div {:draggable true}]) + + "
" + (html [:div {:spellCheck false}]) + + "" + (html [:svg {:focusable false}]) + + "
" + (html [:div {:data-active false}]) + + "
" + (let [a true] + (html [:div {:data-active a}])) + + "" + (html [:option {:value false}]) + + "
" + (html [:div {}]) + + "
" + (let [m {:disabled false}] + (html [:div {:& m}])) + "" (html [:a {:href (str "http://" "dude")}])