From f7ca1ff8ffaeea9c7692e97b66f7c3bbc9d768b1 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Mon, 31 Dec 2018 12:49:37 +0200 Subject: [PATCH] Add class-names utility function --- CHANGELOG.md | 2 ++ src/reagent/core.cljs | 17 +++++++++- src/reagent/impl/template.cljs | 15 +++------ src/reagent/impl/util.cljs | 56 +++++++++++++++++++------------- test/reagent/impl/util_test.cljs | 31 ++++++++++++++++-- 5 files changed, 85 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9cb8e..897ba01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Create React Component without `create-react-class` ([#416](https://github.com/reagent-project/reagent/issues/416)) - Allow any number of arguments for `reagent.core/merge-props` and ensure `:class` is merged correctly when it is defined as collection. ([#412](https://github.com/reagent-project/reagent/issues/412)) +- Add `reagent.core/class-names` utility functions which can be used +to normalize and combine `:class` values (similar to `classnames` JS library) ## 0.8.1 (2018-05-15) diff --git a/src/reagent/core.cljs b/src/reagent/core.cljs index d5c687b..bd7c048 100644 --- a/src/reagent/core.cljs +++ b/src/reagent/core.cljs @@ -194,8 +194,23 @@ [this] (dom/dom-node this)) +(defn class-names + "Function which normalizes and combines class values to a string + + Reagent allows classes to be defined as: + - Strings + - Named objects (Symbols or Keywords) + - Collections of previous types" + ([]) + ([class] (util/class-names class)) + ([class1 class2] (util/class-names class1 class2)) + ([class1 class2 & others] (apply util/class-names class1 class2 others))) + (defn merge-props - "Utility function that merges some maps, handling :class and :style" + "Utility function that merges some maps, handling `:class` and `:style`. + + The :class value is always normalized (using `class-names`) even if no + merging is done." ([] (util/merge-props)) ([defaults] (util/merge-props defaults)) ([defaults props] (util/merge-props defaults props)) diff --git a/src/reagent/impl/template.cljs b/src/reagent/impl/template.cljs index 2fc6a21..d40e1ab 100644 --- a/src/reagent/impl/template.cljs +++ b/src/reagent/impl/template.cljs @@ -117,19 +117,12 @@ ;; Merge classes class - (assoc :class (let [old-class (:class props)] - (if (nil? old-class) class (str class " " (if (named? old-class) - (name old-class) - old-class)))))))) - -(defn stringify-class [{:keys [class] :as props}] - (if (coll? class) - (assoc props :class (util/stringify-class class)) - props)) + (assoc :class (util/class-names class (:class props)))))) (defn convert-props [props id-class] - (let [props (-> props - util/stringify-class + (let [class (:class props) + props (-> props + (cond-> class (assoc :class (util/class-names class))) (set-id-class id-class))] (if ($ id-class :custom) (convert-custom-prop-value props) diff --git a/src/reagent/impl/util.cljs b/src/reagent/impl/util.cljs index e9f7a8a..3b685cf 100644 --- a/src/reagent/impl/util.cljs +++ b/src/reagent/impl/util.cljs @@ -111,28 +111,34 @@ (or (keyword? x) (symbol? x))) -(defn stringify-class [props] - (let [class (:class props)] - (if (coll? class) - (->> class - (keep (fn [c] - (if c - (if (named? c) - (name c) - c)))) - (string/join " ") - (assoc props :class)) - props))) +(defn class-names + ([]) + ([class] + (if (coll? class) + (let [classes (keep (fn [c] + (if c + (if (named? c) + (name c) + c))) + class)] + (if (seq classes) + (string/join " " classes))) + (if (named? class) + (name class) + class))) + ([a b] + (if a + (if b + (str (class-names a) " " (class-names b)) + (class-names a)) + (class-names b))) + ([a b & rst] + (reduce class-names + (class-names a b) + rst))) (defn- merge-class [p1 p2] - (let [p1 (stringify-class p1) - p2 (stringify-class p2) - class (when-let [c1 (:class p1)] - (when-let [c2 (:class p2)] - (str c1 " " c2)))] - (if (nil? class) - p2 - (assoc p2 :class class)))) + (assoc p2 :class (class-names (:class p1) (:class p2)))) (defn- merge-style [p1 p2] (let [style (when-let [s1 (:style p1)] @@ -144,10 +150,16 @@ (defn merge-props ([] nil) - ([p] (stringify-class p)) + ;; Normalize :class even if there are no merging + ([p] + (if-let [c (:class p)] + (assoc p :class (class-names c)) + p)) ([p1 p2] (if (nil? p1) - (stringify-class p2) + (if-let [c (:class p2)] + (assoc p2 :class (class-names c)) + p2) (do (assert (map? p1) (str "Property must be a map, not " (pr-str p1))) diff --git a/test/reagent/impl/util_test.cljs b/test/reagent/impl/util_test.cljs index f6a433d..90cd785 100644 --- a/test/reagent/impl/util_test.cljs +++ b/test/reagent/impl/util_test.cljs @@ -2,6 +2,33 @@ (:require [clojure.test :refer [deftest is testing]] [reagent.impl.util :as util])) +(deftest class-names-test + (is (= nil + (util/class-names) + (util/class-names nil) + (util/class-names []) + (util/class-names nil []))) + (is (= "a b" + (util/class-names ["a" "b"]) + (util/class-names "a" "b"))) + (is (= "a" + (util/class-names :a) + (util/class-names [:a]) + (util/class-names nil "a") + (util/class-names [] nil "a"))) + (is (= "a b c d" + (util/class-names "a" "b" nil ["c" "d"])))) + +; (simple-benchmark [] +; (do (util/class-names "a" "b") +; (util/class-names nil "a") +; (util/class-names "a" nil)) +; 10000) + +; (simple-benchmark [] +; (util/class-names "a" "b" nil "c" "d") +; 10000) + (deftest merge-props-test (testing "no arguments" (is (nil? (util/merge-props)))) @@ -36,9 +63,9 @@ :class "quux"} nil)))) - (testing ":class collection" + (testing ":class" (is (= {:class "foo bar baz quux"} (util/merge-props {:class "foo bar"} {:class ["baz" "quux"]}) - (util/merge-props {} {:class ["foo" "bar" "baz" "quux"]}) + (util/merge-props nil {:class ["foo" "bar" "baz" "quux"]}) (util/merge-props {:class ["foo" "bar" "baz" "quux"]})))))