/
context.cljc
61 lines (55 loc) · 2.58 KB
/
context.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
(ns com.fulcrologic.fulcro.rendering.context
#?(:cljs (:require-macros com.fulcrologic.fulcro.rendering.context))
(:require
[taoensso.timbre :as log]
#?@(:cljs
[["react" :as react]
[goog.object :as gobj]]))
#?(:clj (:import (cljs.tagged_literals JSValue))))
(defn- force-children
"Utility function that will force a lazy sequence of children (recursively) into realized
vectors (React cannot deal with lazy seqs in production mode)"
[x]
(cond->> x
(seq? x) (into [] (map force-children))))
#?(:cljs
(defn- create-element ; circ ref on dom, so just copied here
([tag]
(create-element tag nil))
([tag opts]
(react/createElement tag opts))
([tag opts & children]
(apply react/createElement tag opts children))))
#?(:cljs
(defonce rendering-context (react/createContext nil))
:clj
(defonce ^:dynamic rendering-context {}))
(defonce Provider #?(:cljs (.-Provider rendering-context) :clj nil))
(defonce Consumer #?(:cljs (.-Consumer rendering-context) :clj nil))
(defonce root-provider-context-object (memoize (fn [app-id] #?(:cljs (js/Object.)))))
(defn- gset [obj k v] #?(:cljs (gobj/set obj k v)))
#?(:clj
(defmacro ui-provider
([app child] `(ui-provider ~app ~child false))
([app child force-render?]
(let [cmap `{:app ~app
:shared (some-> ~app :com.fulcrologic.fulcro.application/runtime-atom deref :com.fulcrologic.fulcro.application/shared-props)
:force-render? ~force-render?
:query-state (some-> ~app :com.fulcrologic.fulcro.application/state-atom deref)}]
(if (boolean (:ns &env))
`(let [cobj# (root-provider-context-object (:com.fulcrologic.fulcro.application/id ~app))
arg# ~(JSValue. {})]
(gset cobj# "context" ~cmap)
(gset arg# "value" cobj#)
(com.fulcrologic.fulcro.rendering.context/create-element Provider arg# ~child))
`(binding [rendering-context ~cmap] ~child))))))
(letfn [(extract [f] (fn [js-obj] #?(:cljs (f (gobj/get js-obj "context")))))]
(defn in-context
"Call `f` to render children, where `f` will receive the current rendering context as a parameter. Use `context-props`
to pass props to the React/Consumer element (most importantly a key, if in a list)."
([context-props f]
#?(:clj (f rendering-context)
:cljs (create-element Consumer (clj->js context-props) (extract f))))
([f]
#?(:clj (f rendering-context)
:cljs (create-element Consumer nil (extract f))))))