This repository has been archived by the owner on Oct 11, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
core.cljc
193 lines (180 loc) · 6.67 KB
/
core.cljc
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
(ns herb.core
(:require [herb.impl :as impl]
[herb.spec]
[clojure.spec.alpha :as s]
[clojure.string :as str]
[herb.runtime :as runtime])
#?(:clj
(:import garden.types.CSSAtRule)))
(defn init!
"Initialize herb, takes a map of options:
:vendors - a vector of vendor prefixes, ie [:webkit :moz]
:auto-prefix - A set of CSS properties to auto prefix, ie #{:transition :border-radius} "
[options]
(let [parsed (s/conform :herb.spec/options options)]
(if (= parsed ::s/invalid)
(throw (ex-info "Invalid input" (s/explain-data :herb.spec/options options)))
(reset! runtime/options {:vendors (-> (mapv (fn [[k v]] v) (:vendors parsed))
(impl/convert-vendors))
:auto-prefix (:auto-prefix options)}))))
(defn join
"Joins multiple classes together, filtering out nils:
```
(join (<class fn-1) (<class fn-2))
```"
[& classes]
(if (s/valid? :herb.spec/classes classes)
(->> classes
(filter identity)
(str/join " "))
(throw (ex-info "join takes one or more strings as arguments" (s/explain-data :herb.spec/classes classes)))))
#?(:clj
(defmacro defkeyframes
"Define a CSS @keyframes animation:
```
(defkeyframes my-animation
[:from
{:background \"red\"}]
[:to
{:background \"yellow\"}])
```
CLJS: the keyframes CSS gets injected into head under data-herb=\"keyframes\"
CLJ: Use `<keyframes` macro with the defined keyframes returns a CSS string
containing the animation"
[sym & frames]
(let [value {:identifier `(str '~sym)
:frames `(list ~@frames)}
s# `'~sym
n# (name (ns-name *ns*))
obj `(CSSAtRule. :keyframes ~value)]
`(do
(runtime/inject-obj! (str ~n# "/" ~s#) :keyframes ~obj)
(def ~sym ~obj)))))
#?(:clj
(defmacro defglobal
"Define global CSS:
```
(defglobal some-global-style
[:body {:box-sizing \"border-box\"
:font-size (px 14)
[:button {:border \"none\"}])
```
The CSS output of garden style vectors gets appended to head under
data-herb=\"global\"
"
[sym & styles]
(let [styles# `(list ~@styles)
s# `'~sym
n# (name (ns-name *ns*))]
`(do
(runtime/inject-obj! (str ~n# "/" ~s# ) :global ~styles#)
(def ~sym ~styles#)))))
#?(:clj
(defmacro <keyframes
"Returns a CSS string from defined keyframes using the defkeyframes macro.
Intended to be used from clojure
```
(defkeyframes pulse
[:from {:opacity 1}]
[:to {:opacity 0}])
user=> (<keyframes pulse)
@keyframes anime {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
```"
[sym]
(let [s# `'~sym
n# (name (ns-name *ns*))]
`(-> @runtime/injected-keyframes
(get (str ~n# "/" ~s#))
:css))))
#?(:clj
(defmacro defgroup
"Define a style group, everything defined in a group is grouped in the same
style element, It takes a name and a map of styles in the form:
```
(defgroup my-group
{:a-component {:color \"red\"}})
```
To use a group, use one of `<class` or `<id` macro, where the first argument is
the key for whatever component stylesheet you want:
```
[:div {:class (<class my-group :a-component)}]
```"
[n c]
`(defn ~n [~'component & ~'args]
(if-let [style# (get ~c ~'component)]
(vary-meta
style#
assoc
:key ~'component
:group true)
(throw (str "Herb error: failed to get component: " ~'component " in stylegroup: " '~n))))))
#?(:clj
(defmacro <style
"Takes a function `style-fn` that returns a map. Arguments `args` can be passed
along with the function as additional arguments to <style i.e
`(<style some-fn arg1 arg2)`.
Returns a CSS string that is the result of calling passed function"
[style-fn & args]
(let [f `'~style-fn
n (name (ns-name *ns*))]
`(cond
(not (fn? ~style-fn))
(throw (ex-info (str "herb error in ns \"" ~n "\" the first argument to <style needs to be a function."))
{:function ~f
:namespace ~n
:return-value (~style-fn ~@args)})
(not (map? (~style-fn ~@args)))
(throw (ex-info (str "herb error: style function \"" ~n "/" ~f "\" needs to return a map.")
{:function ~f
:namespace ~n
:return-value (~style-fn ~@args)}))
:else (herb.impl/with-style! {:style? true} ~f ~n ~style-fn ~@args)))))
#?(:clj
(defmacro <id
"Takes a function `style-fn` that returns a map. Arguments `args` can be passed
along with the function as additional arguments to <id i.e
`(<id some-fn arg1 arg2)`.
Returns a unique id based on the fully qualified name of the passed function "
[style-fn & args]
(let [f `'~style-fn
n (name (ns-name *ns*))]
`(cond
(not (fn? ~style-fn))
(throw (ex-info (str "herb error in ns \"" ~n "\" the first argument to <id needs to be a function.")
{:function ~f
:namespace ~n
:return-value (~style-fn ~@args)}))
(not (map? (~style-fn ~@args)))
(throw (ex-info (str "herb error: style function \"" ~n "/" ~f "\" needs to return a map.")
{:function ~f
:namespace ~n
:return-value (~style-fn ~@args)}))
:else (herb.impl/with-style! {:id? true} ~f ~n ~style-fn ~@args)))))
#?(:clj
(defmacro <class
"Takes a function `style-fn` that returns a map. Arguments `args` can be passed
along with the function as additional arguments to <class i.e
`(<class some-fn arg1 arg2)`.
Returns a unique class based on the fully qualified name of the passed function"
[style-fn & args]
(let [f `'~style-fn
n (name (ns-name *ns*))]
`(cond
(not (fn? ~style-fn))
(throw (ex-info (str "herb error in ns \"" ~n "\" the first argument to <class needs to be a function.")
{:function ~f
:namespace ~n
:return-value (~style-fn ~@args)}))
(not (map? (~style-fn ~@args)))
(throw (ex-info (str "herb error: style function \"" ~n "/" ~f "\" needs to return a map.")
{:function ~f
:namespace ~n
:return-value (~style-fn ~@args)}))
:else (herb.impl/with-style! {} ~f ~n ~style-fn ~@args)))))