-
Notifications
You must be signed in to change notification settings - Fork 3
/
css.clj
133 lines (111 loc) · 3.54 KB
/
css.clj
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
(ns shadow.markup.css
(:require [clojure.string :as str]
[shadow.markup.css.impl.gen :as gen]
[shadow.markup.css.impl.server :as server]))
(defn- gen-el-selector [ns name]
(-> (str ns "--" name)
;; FIXME: some more munging might be required?
;; css selectors probably don't allow some chars that are otherwise ok in an ns/name ($!? come to mind)
(str/replace #"\." (constantly "-"))))
(defmacro defstyled [el-name el-type args & body]
{:pre [(symbol? el-name) ;; simple-symbol? 1.9 only
(keyword? el-type)
(vector? args)
(= 1 (count args))]}
(let [el-selector
(gen-el-selector
(or (-> *ns* meta :shadow.markup.css/alias)
(str *ns*))
(or (-> el-name meta :shadow.markup.css/alias)
(name el-name)))]
`(def ~(vary-meta el-name assoc :shadow.markup.css/element true)
(shadow.markup.css/element*
~(name el-type)
~el-selector
(fn ~args
~@body)))))
(defn element*
"use defstyled macro"
[el-type el-selector style-fn]
(server/->StyledElement el-type el-selector style-fn))
(defn root [attrs & rules]
(gen/root* attrs rules))
(defn rule [selector attrs]
(gen/rule selector attrs))
(defn nested-rule [parent attrs]
(gen/nested-rule parent attrs))
;; for server side css generation
(defn- find-elements* [ns-sym]
(if (qualified-symbol? ns-sym)
;; only one var
(let [el-var (resolve ns-sym)]
(when-not el-var
(throw (ex-info (str "cannot find css element by name " ns-sym) {:sym ns-sym})))
[@el-var])
;; symbols without ns yield all elements defined in ns
(->> (ns-publics ns-sym)
(vals)
(filter #(-> % meta :shadow.markup.css/element))
(map deref))
))
(defn require-elements!
[ns-symbols]
(doseq [ns-sym
(->> ns-symbols
(map (fn [ns-sym]
(if (simple-symbol? ns-sym)
ns-sym
(symbol (namespace ns-sym)))))
(distinct))]
(when (nil? (find-ns ns-sym))
(require ns-sym)))
ns-symbols)
(defn find-elements
"given a list of symbols find all css elements
namespaced symbols should resolve to a single element
simple symbols are treated as an ns and yield all defined elements in that ns"
[ns-symbols]
(into [] (mapcat find-elements*) ns-symbols))
(defn generate-css-for-elements
[env elements]
(->> elements
(map #(gen/generate-css env %))
(str/join "\n")))
(comment
(defstyled bar :div
[_]
{:color "bar"})
(defstyled foo :div
[_]
{:color "foo"
bar
{:color "red"}})
;; this could be used to generate .css files to be used via <link .../>
(println
(->> '[shadow.markup.css]
;; (require-elements!)
;; can be skipped if already required in ns form
(find-elements)
(generate-css-for-elements {})
))
;; this could be a strategy to produce only the css used on a given page
(let [el1
(element* "div" "outer" (fn [x] {:border "1px solid red"}))
el2
(element* "div" "inner" (fn [x] {:background-color "green"}))
used
(volatile! #{})
html
(binding [server/*used-elements* used]
(el1 {:data-x "yo" :data-y true}
;; meh on the assumption of hiccup
[:h1 "hello world"]
(el2 {})))]
;; <style>
(println (generate-css-for-elements {} @used))
;; </style>
(println)
;; <html>
(println html)
;; </html>
))