Skip to content

Commit

Permalink
allow dynamic element types, fixes #94
Browse files Browse the repository at this point in the history
  • Loading branch information
roman01la committed Feb 10, 2023
1 parent 0cb6bec commit 14d5b7d
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 9 deletions.
31 changes: 23 additions & 8 deletions core/src/uix/compiler/alpha.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,43 @@
"Reagent element should be Hiccup wrapped with r/as-element, i.e. (r/as-element [" name-str "])")))))
true)

(defn- normalise-args [component-type js-props props-children]
(if (= 2 (.-length ^js props-children))
#js [component-type js-props (aget props-children 1)]
#js [component-type js-props]))

(defn- uix-component-element [component-type ^js props-children children]
(let [props (aget props-children 0)
js-props (if-some [key (:key props)]
#js {:key key :argv (dissoc props :key)}
#js {:argv props})
args (if (= 2 (.-length props-children))
#js [component-type js-props (aget props-children 1)]
#js [component-type js-props])]
args (normalise-args component-type js-props props-children)]
(.apply react/createElement nil (.concat args children))))

(defn- react-component-element [component-type ^js props-children children]
(let [js-props (-> (aget props-children 0)
(attrs/interpret-attrs #js [] true)
(aget 0))
args (if (= 2 (.-length props-children))
#js [component-type js-props (aget props-children 1)]
#js [component-type js-props])]
args (normalise-args component-type js-props props-children)]
(.apply react/createElement nil (.concat args children))))

(defn- dynamic-element [component-type ^js props-children children]
(let [tag-id-class (attrs/parse-tag component-type)
js-props (-> (aget props-children 0)
(attrs/interpret-attrs tag-id-class false)
(aget 0))
tag (aget tag-id-class 0)
args (normalise-args tag js-props props-children)]
(.apply react/createElement nil (.concat args children))))

(defn component-element [^clj component-type props-children children]
(when ^boolean goog.DEBUG
(validate-component component-type))
(if (.-uix-component? component-type)
(cond
(.-uix-component? component-type)
(uix-component-element component-type props-children children)
(react-component-element component-type props-children children)))

(keyword? component-type)
(dynamic-element component-type props-children children)

:else (react-component-element component-type props-children children)))
21 changes: 20 additions & 1 deletion core/src/uix/compiler/attributes.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,26 @@
([a b & rst]
(reduce class-names (class-names a b) rst)))

(defn set-id-class
(def re-tag
"HyperScript tag pattern :div :div#id.class etc."
#"([^\.#]*)(?:#([^\.#]+))?(?:\.([^#]+))?")

(defn parse-tag
"Takes HyperScript tag (:div#id.class) and returns parsed tag, id and class fields,
and boolean indicating if tag name is a custom element (a custom DOM element that has hyphen in the name)"
[tag]
(let [tag-str (name tag)]
(when (and (not (re-matches re-tag tag-str))
(re-find #"[#\.]" tag-str))
;; Throwing NPE here because shadow catches those to bring up error view in a browser
(throw (js/Error. (str "Invalid tag name (found: " tag-str "). Make sure that the name matches the format and ordering is correct `:tag#id.class`"))))
(let [[tag id class-name] (next (re-matches re-tag tag-str))
tag (if (= "" tag) "div" tag)
class-name (when-not (nil? class-name)
(str/replace class-name #"\." " "))]
#js [tag id class-name (some? (re-find #"-" tag))])))

(defn- set-id-class
"Takes attributes map and parsed tag, and returns attributes merged with class names and id"
[props id-class]
(let [props-class (get props :class)
Expand Down
22 changes: 22 additions & 0 deletions core/test/uix/core_test.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,27 @@
(is (= "HELLO! 2" (.-textContent root)))
(react-dom/unmountComponentAtNode root)))

(defui dyn-uix-comp [props]
($ :button props))

(defn dyn-react-comp [^js props]
($ :button
{:title (.-title props)
:children (.-children props)}))

(deftest test-dynamic-element
(testing "dynamic element as a keyword"
(let [as :button#btn.action]
(is (= "<button title=\"hey\" id=\"btn\" class=\"action\">hey</button>"
(t/as-string ($ as {:title "hey"} "hey"))))))
(testing "dynamic element as uix component"
(let [as dyn-uix-comp]
(is (= "<button title=\"hey\">hey</button>"
(t/as-string ($ as {:title "hey"} "hey"))))))
(testing "dynamic element as react component"
(let [as dyn-react-comp]
(is (= "<button title=\"hey\">hey</button>"
(t/as-string ($ as {:title "hey"} "hey")))))))

(defn -main []
(run-tests))

0 comments on commit 14d5b7d

Please sign in to comment.