/
pinkie.cljs
162 lines (133 loc) · 4.89 KB
/
pinkie.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
(ns pinkgorilla.ui.pinkie
(:require
[clojure.string :as str]
[reagent.core :as r :refer [atom]]
[reagent.impl.template :refer [HiccupTag cached-parse]]
; [taoensso.timbre :refer-macros (info)]
[clojure.walk :refer [prewalk postwalk]] ; cljs 1.10 still does not have walk fixed
; [pinkgorilla.ui.walk :refer [prewalk]] ; TODO: replace this as soon as 1.11 cljs is out.
[pinkgorilla.ui.htmltags :refer [html5-tags]]))
(def custom-renderers (atom {}))
(defn renderer-list []
(map #(assoc {} :k (first %) :r (pr-str (last %))) (seq @custom-renderers)))
(defn register-tag [k v]
(swap! custom-renderers assoc k v)
; it would be ideal to let reagent deal with this, but the below line did not work.
;(gobj/set reagent.impl.template/tag-name-cache (name k) v)
)
; mfikes approach would be great, but does not work
; https://github.com/reagent-project/reagent/issues/362
#_(defn register-tag2 [k v]
(gobj/set reagent.impl.template/tag-name-cache k v))
#_(defn register-tag3 [kw c]
(register-tag2 (name kw) (r/as-element c)))
(defn clj->json
[ds]
(.stringify js/JSON (clj->js ds)))
(defn html5-tag? [tag]
(let [; reagent also has :div#main.big which we have to transform to :div
tag-typed (cached-parse tag) ; #js {:name "<>", :id nil, :class nil, :custom false}
;_ (.log js/console "tag typed:" (pr-str tag-typed))
tag-clean (.-tag tag-typed)
tag-clean (if (nil? tag-clean) nil (keyword tag-clean))
; tag-clean (keyword (:name (js->clj tag-typed :keywordize-keys true)))
; _ (.log js/console "tag clean:" tag-clean)
]
(contains? html5-tags tag-clean)))
(def pinkie-namespace (namespace :p/test))
(defn pinkie-tag? [tag]
(let [kw-namespace (namespace tag)]
(= pinkie-namespace kw-namespace)))
(defn pinkie-exclude? [hiccup-vector]
(contains? (meta hiccup-vector) :r))
(defn- hiccup-vector? [hiccup-vector]
(and
(vector? hiccup-vector)
(not (map-entry? hiccup-vector)); ignore maps
(keyword? (first hiccup-vector)); reagent syntax requires first element to be a keyword
))
(defn should-replace? [hiccup-vector]
(if (hiccup-vector? hiccup-vector)
(let [tag (first hiccup-vector)]
(and (not (pinkie-exclude? hiccup-vector))
(pinkie-tag? tag)
#_(not (html5-tag? tag))))
false))
(defn unknown-tag
"ui component for unknown tags - so that we don't need to catch react errors
Currently not yet used (see resolve function)"
[tag]
[:span.unknown-tag {:style {:background-color "red"}}
(str "Unknown Tag: " tag)])
(defn replace-tag-in-hiccup-vector
"input: hiccup vector
if keyword (first position in vector) has been registered via register-tag,
then it gets replaced with the react function,
otherwise keyword remains"
[hiccup-vector]
(let [;_ (.log js/console "pinkie replacing: " (pr-str hiccup-vector))
tag (first hiccup-vector)
render-function (tag @custom-renderers)]
(if (nil? render-function)
(do (.log js/console "pinkie unknown tag: " (name tag))
(unknown-tag tag))
(into [] (assoc hiccup-vector 0 render-function)))))
(defn tag-inject
"resolve function-as symbol to function references in the reagent-hickup-map.
Leaves regular hiccup data unchanged."
[hiccup-vector]
(prewalk
(fn [x]
(if (should-replace? x)
(replace-tag-in-hiccup-vector x)
x))
hiccup-vector))
;; Hiccup accepts Style as string, but Reagent does not.
;; Example: [:rect {:width "100%", :height "100%", :style "stroke: none; fill: #FFFFFF;"}]
(defn to-map-style [s]
(let [style-vec (map #(str/split % #":") (str/split s #";"))]
(into {}
(for [[k v] style-vec]
[(keyword (str/trim k)) (str/trim v)]))))
(defn is-style? [x]
;(println "is-style? " x)
(if (and (vector? x)
(= 2 (count x))
(= (first x) :style)
(string? (second x)))
true
false))
(defn replace-style [x]
(println "pinkie replacing string style: " x)
(into [] (assoc x 1 (to-map-style (last x)))))
(defn convert-style-as-strings-to-map
"resolve function-as symbol to function references in the reagent-hickup-map.
Leaves regular hiccup data unchanged."
[hiccup-vector]
(prewalk
(fn [x]
(if (is-style? x)
(replace-style x)
x))
hiccup-vector))
(comment
(to-map-style "background-color: blue; font-size: 14px")
;=> {:background-color "blue" :font-size "14px"}
)
;; RENDER-AS
(defn render-as? [hiccup-vector]
(contains? (meta hiccup-vector) :p/render-as))
(defn wrap-renderer [x]
(let [renderer (:p/render-as (meta x))]
(println "pinkie wrapping renderer " renderer " to: " x)
^:R [renderer x]))
(defn convert-render-as
"resolve function-as symbol to function references in the reagent-hickup-map.
Leaves regular hiccup data unchanged."
[hiccup-vector]
(postwalk
(fn [x]
(if (render-as? x)
(wrap-renderer x)
x))
hiccup-vector))