-
Notifications
You must be signed in to change notification settings - Fork 10
/
dom.cljs
152 lines (131 loc) · 6.24 KB
/
dom.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
(ns stylefy.impl.dom
(:require [dommy.core :as dommy]
[reagent.core :as r]
[garden.core :refer [css]]
[stylefy.impl.cache :as cache]
[stylefy.impl.utils :as utils]
[stylefy.impl.conversion :as conversion]
[garden.stylesheet :refer [at-media at-keyframes at-font-face]]
[clojure.set :as set])
(:require-macros [reagent.ratom :refer [run!]]))
(def stylefy-initialised? (r/atom false))
(def styles-in-use (r/atom {})) ;; style hash -> props
(def keyframes-in-use (r/atom []))
(def font-faces-in-use (r/atom []))
(def custom-tags-in-use (r/atom []))
(def custom-classes-in-use (r/atom []))
(def ^:private stylefy-node-id :#_stylefy-styles_)
(def ^:private stylefy-constant-node-id :#_stylefy-constant-styles_)
(def ^:private dom-needs-update? (atom false))
(defn- style-by-hash [style-hash]
(when style-hash
(get @styles-in-use style-hash)))
(defn- update-style-tags!
[node node-constant]
(let [styles-in-css (map (fn [style-hash]
(::css (style-by-hash style-hash)))
(keys @styles-in-use))
;; TODO Keyframes, font-faces, custom classes are not going to change once defined.
;; Now we re-convert those to CSS every single time this function is called.
;; We should use the converted CSS when it has been created.
keyframes-in-css (map (fn [keyframes]
(css keyframes))
@keyframes-in-use)
font-faces-in-use (map (fn [properties]
(css properties))
@font-faces-in-use)
custom-tags-in-use (map (fn [tag-definition]
(conversion/style->css
{:props (::tag-properties tag-definition)
:custom-selector (::tag-name tag-definition)}))
@custom-tags-in-use)
custom-classes-in-use (map (fn [class-definition]
(conversion/style->css
{:props (::class-properties class-definition)
:custom-selector (conversion/class-selector
(::class-name class-definition))}))
@custom-classes-in-use)]
(dommy/set-text! node-constant (apply str (concat font-faces-in-use
keyframes-in-css
custom-tags-in-use
custom-classes-in-use)))
(dommy/set-text! node (apply str styles-in-css))))
(defn- mark-styles-added-in-dom! []
(reset! styles-in-use (apply merge (map
#(-> {% (assoc (get @styles-in-use %) ::in-dom? true)})
(keys @styles-in-use)))))
(declare continuously-update-styles-in-dom!)
(defn- request-dom-update []
(.requestAnimationFrame js/window continuously-update-styles-in-dom!))
(defn- update-styles-in-dom!
"Updates style tag if needed."
[]
(when @dom-needs-update?
(let [node (dommy/sel1 stylefy-node-id)
node-constant (dommy/sel1 stylefy-constant-node-id)]
(if (and node node-constant)
(do (update-style-tags! node node-constant)
(reset! dom-needs-update? false)
(try
(cache/cache-styles (apply merge
(map
#(-> {% (dissoc (get @styles-in-use %) ::in-dom?)})
(keys @styles-in-use))))
(catch :default e
(.warn js/console (str "Unable to cache styles, error: " e))
e))
(mark-styles-added-in-dom!))
(.error js/console "stylefy is unable to find the required <style> tags!")))))
(defn- continuously-update-styles-in-dom!
"Updates style tag if needed."
[]
(when @dom-needs-update?
(update-styles-in-dom!))
(request-dom-update))
(defn init-dom-update []
(continuously-update-styles-in-dom!)
(reset! stylefy-initialised? true))
(defn check-stylefy-initialisation []
(when-not @stylefy-initialised?
(.warn js/console (str "stylefy has not been initialised correctly. Call stylefy/init once when your application starts."))))
(defn init-styles-in-use [options]
(when (:use-caching? options)
(cache/use-caching! (:cache-options options))
(when-let [cached-styles (cache/read-cache-value
cache/cache-key-styles)]
(reset! styles-in-use (or (cache/read-cache-value
cache/cache-key-styles)
{}))
(reset! dom-needs-update? true)
(update-styles-in-dom!))))
(defn- save-style!
"Stores the style in an atom. The style is going to be added into the DOM soon."
[{:keys [props hash] :as style}]
(assert props "Unable to save empty style!")
(assert hash "Unable to save style without hash!")
(let [style-css (conversion/style->css style)
style-to-be-saved (assoc props ::css style-css)]
(swap! styles-in-use assoc hash style-to-be-saved)
(reset! dom-needs-update? true)))
(defn style-in-dom? [style-hash]
(boolean (::in-dom? (style-by-hash style-hash))))
(defn add-keyframes [identifier & frames]
(let [garden-definition (apply at-keyframes identifier frames)]
(swap! keyframes-in-use conj garden-definition)
(reset! dom-needs-update? true)
garden-definition))
(defn add-font-face [properties]
(let [garden-definition (at-font-face properties)]
(swap! font-faces-in-use conj garden-definition)
(reset! dom-needs-update? true)
garden-definition))
(defn add-tag [name properties]
(let [custom-tag-definition {::tag-name name ::tag-properties properties}]
(swap! custom-tags-in-use conj custom-tag-definition)
(reset! dom-needs-update? true)
custom-tag-definition))
(defn add-class [name properties]
(let [custom-class-definition {::class-name name ::class-properties properties}]
(swap! custom-classes-in-use conj custom-class-definition)
(reset! dom-needs-update? true)
custom-class-definition))