mirror of
https://github.com/tonsky/rum.git
synced 2025-08-03 18:13:19 +08:00
0.11.3 updated docstrings
This commit is contained in:
@ -1,3 +1,7 @@
|
||||
## 0.11.3
|
||||
|
||||
- Docstrings for https://cljdoc.org/d/rum/rum
|
||||
|
||||
## 0.11.2
|
||||
|
||||
- Server-render on-* event handlers with string values
|
||||
|
6
doc/cljdoc.edn
Normal file
6
doc/cljdoc.edn
Normal file
@ -0,0 +1,6 @@
|
||||
{:cljdoc.doc/tree [
|
||||
["Readme" {:file "README.md"}]
|
||||
["Changelog" {:file "CHANGELOG.md"}]
|
||||
["Useful mixins" {:file "doc/useful-mixins.md"}]
|
||||
["React interop" {:file "doc/react-interop.md"}]
|
||||
]}
|
109
doc/react-interop.md
Normal file
109
doc/react-interop.md
Normal file
@ -0,0 +1,109 @@
|
||||
# Use different React version
|
||||
|
||||
Add to `project.clj`:
|
||||
|
||||
```
|
||||
:dependencies {
|
||||
[rum "0.11.3" :exclusions [[cljsjs/react] [cljsjs/react-dom]]]
|
||||
[cljsjs/react "16.6.0-0"]
|
||||
[cljsjs/react-dom "16.6.0-0"]
|
||||
}
|
||||
```
|
||||
|
||||
# Including React.js manually
|
||||
|
||||
If you want to include `react.js` yourself, then add this to `project.clj`:
|
||||
|
||||
```
|
||||
:dependencies {
|
||||
[rum "0.11.3" :exclusions [[cljsjs/react] [cljsjs/react-dom]]]
|
||||
}
|
||||
```
|
||||
|
||||
Create two files
|
||||
|
||||
1. `src/cljsjs/react.cljs`:
|
||||
|
||||
```
|
||||
(ns cljsjs.react)
|
||||
```
|
||||
|
||||
2. `src/cljsjs/react/dom.cljs`:
|
||||
|
||||
```
|
||||
(ns cljsjs.react.dom)
|
||||
```
|
||||
|
||||
Add to your HTML the version you want:
|
||||
|
||||
```
|
||||
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
|
||||
```
|
||||
|
||||
# Using React with addons
|
||||
|
||||
```clj
|
||||
[rum "0.11.3" :exclusions [cljsjs/react cljsjs/react-dom]]
|
||||
[cljsjs/react-dom "16.2.0-3" :exclusions [cljsjs/react]]
|
||||
[cljsjs/react-dom-server "16.2.0-3" :exclusions [cljsjs/react]]
|
||||
[cljsjs/react-with-addons "16.2.0-3"]
|
||||
```
|
||||
|
||||
# Profiling with [React perf](https://facebook.github.io/react/docs/perf.html)
|
||||
|
||||
Specify the `react-with-addons` dependency in your `project.clj` (see above ↑)
|
||||
|
||||
Then from within your program run:
|
||||
|
||||
```clj
|
||||
(js/React.addons.Perf.start)
|
||||
;;run your app
|
||||
(js/React.addons.Perf.stop)
|
||||
(js/React.addons.Perf.printWasted)
|
||||
```
|
||||
|
||||
and results will be printed to the developer console.
|
||||
|
||||
# Using 3rd-party React components
|
||||
|
||||
Given e.g. [react-router-transition](https://github.com/maisano/react-router-transition)
|
||||
|
||||
```clj
|
||||
(defn route-transition [pathname children]
|
||||
(js/React.createElement js/RouteTransition
|
||||
#js { :pathname pathname
|
||||
:atEnter #js { :opacity 0 }
|
||||
:atLeave #js { opacity: 0 }
|
||||
:atActive #js { opacity: 1 } }
|
||||
(clj->js children)))
|
||||
```
|
||||
|
||||
Another example [react-component/slider](https://github.com/react-component/slider)
|
||||
|
||||
```clj
|
||||
(defn range-slider [min max]
|
||||
(js/React.createElement js/Slider #js { :min min
|
||||
:max max
|
||||
:range true
|
||||
:defaultValue #js [40 60] }))
|
||||
```
|
||||
|
||||
**Note:** See how `defn` is used here instead of `defc`? Using `defc` would cause two components being created (e.g. `range-slider` and the `Slider` component). Because in many cases you don't need the wrapping component you can just use `defn`.
|
||||
|
||||
# Get displayName of component
|
||||
|
||||
This might be useful for development when you want to know which component this mixin is handling:
|
||||
|
||||
```clojure
|
||||
(ns ...
|
||||
(:require [goog.object :as gobj]))
|
||||
|
||||
(defn display-name
|
||||
"Returns the displayname of the component"
|
||||
[state]
|
||||
(gobj/getValueByKeys
|
||||
(:rum/react-component state)
|
||||
"constructor"
|
||||
"displayName"))
|
||||
```
|
184
doc/useful-mixins.md
Normal file
184
doc/useful-mixins.md
Normal file
@ -0,0 +1,184 @@
|
||||
# Request stuff on mount by AJAX
|
||||
|
||||
Components using this mixin will do an AJAX request on mount and will update themselves when they got a reply. Mixin puts an atom to the state whose value (after deref) is either nil (request pending) or returned value.
|
||||
|
||||
```clojure
|
||||
(defn ajax-mixin [url key]
|
||||
{ :will-mount
|
||||
(fn [state]
|
||||
(let [*data (atom nil)
|
||||
comp (:rum/react-component state)]
|
||||
(ajax
|
||||
url
|
||||
(fn [data]
|
||||
(reset! *data data)
|
||||
(rum/request-render comp)))
|
||||
(assoc state key *data))) })
|
||||
|
||||
|
||||
(rum/defcs user-info < (ajax-mixin "/api/user/info" ::user)
|
||||
[state]
|
||||
(if-let [user @(::user state)]
|
||||
...
|
||||
[:div "Loading..."]))
|
||||
```
|
||||
|
||||
Customize to your taste: dynamic URL generation from component args, AJAX retries, failed state, callback on finish, deserialization.
|
||||
|
||||
# Debouncer
|
||||
|
||||
```clojure
|
||||
(ns ... (:import [goog.async Debouncer]))
|
||||
|
||||
(defn debouncer-mixin
|
||||
"Creates a debouncer in (:debouncer state) which can be called with (.fire).
|
||||
Invokes the callback cb or invokes the first argument passed to fire.
|
||||
Usage:
|
||||
1. (debouncer-mixin 200)
|
||||
(.fire (:debouncer state) #(do-actual-action ...))
|
||||
|
||||
2. (debouncer-mixin 200 #(do-an-action ...))
|
||||
(.fire (:debouncer state))"
|
||||
([ms] (debouncer-mixin ms nil))
|
||||
([ms cb]
|
||||
{:will-mount
|
||||
(fn debouncer-mount [state]
|
||||
(assoc state :debouncer (Debouncer. (if (nil? cb) #(%1) cb) ms)))
|
||||
:will-unmount
|
||||
(fn debouncer-unmount [state]
|
||||
(.dispose (:debouncer state))
|
||||
state)}))
|
||||
```
|
||||
|
||||
# Install CSS styles on mount
|
||||
|
||||
For your root rum component, you can install CSS styles on mount and uninstall them on unmount.
|
||||
This is useful if you have your (garden) styles defined in `cljc` file. In production you generate
|
||||
a css file and include it with a normal `<style>` tag, in development however, you can just use this
|
||||
mixin to have them automatically be applied as soon as you change any style.
|
||||
|
||||
```clojure
|
||||
(ns ...
|
||||
(:require [goog.style :as gstyle]))
|
||||
|
||||
(defn install-styles-mixin
|
||||
"Installes the stylesheet when mounting. Uninstall when unmount.
|
||||
Useful for use at the very root render and use with garden in a cljc environment.
|
||||
Live update of CSS without needing to go trough figwheel :)"
|
||||
[css-str]
|
||||
{:will-mount
|
||||
(fn [st]
|
||||
(assoc st ::stylesheet (gstyle/installStyles css-str)))
|
||||
:will-unmount
|
||||
(fn [st]
|
||||
(gstyle/uninstallStyles (::stylesheet st))
|
||||
st)})
|
||||
|
||||
;; Then use them like so:
|
||||
app < (install-styles-mixin (your-cljc/gen-css))
|
||||
```
|
||||
|
||||
# Measure render
|
||||
|
||||
You can measure how long a component needs to render with this mixin:
|
||||
|
||||
```clojure
|
||||
(defn perf-measure-mixin
|
||||
[desc]
|
||||
"Does performance measurements in development."
|
||||
{:wrap-render
|
||||
(fn wrap-render [render-fn]
|
||||
(fn [state]
|
||||
(profile
|
||||
(str "Render " desc)
|
||||
(render-fn state))))})
|
||||
;; where profile is a macro like this:
|
||||
(defmacro profile [k & body]
|
||||
(if macros/dev?
|
||||
`(let [k# ~k]
|
||||
(.time js/console k#)
|
||||
(let [res# (do ~@body)]
|
||||
(.timeEnd js/console k#)
|
||||
res#))
|
||||
`(do ~@body)))
|
||||
```
|
||||
|
||||
# Keyboard shortcut
|
||||
|
||||
Install a keyboard shortcut that is only valid while the component is mounted:
|
||||
|
||||
```clojure
|
||||
(defn keyboard-mixin
|
||||
"Triggers f when key is pressed while the component is mounted.
|
||||
if target is a function it will be called AFTER the component mounted
|
||||
with state and should return a dom node that is the target of the listener.
|
||||
If no target is given it is defaulted to js/window (global handler)
|
||||
Ex:
|
||||
(keyboard-mixin \"esc\" #(browse-to :home/home))"
|
||||
([key f] (keyboard-mixin key f js/window))
|
||||
([key f target]
|
||||
(let [target-fn (if (fn? target) target (fn [_] target))]
|
||||
{:did-mount
|
||||
(fn [state]
|
||||
(assoc state ::keyboard-listener
|
||||
(keyboard/install-shortcut! key f false (target-fn state))))
|
||||
:will-unmount
|
||||
(fn [state]
|
||||
((::keyboard-listener state))
|
||||
state)})))
|
||||
|
||||
;; where install-shortcut! is:
|
||||
(ns you-tools.keyboard
|
||||
(:require [goog.events :as events]
|
||||
[goog.ui.KeyboardShortcutHandler.EventType :as EventType]
|
||||
[goog.events.KeyCodes :as KeyCodes])
|
||||
(:import [goog.ui KeyboardShortcutHandler]))
|
||||
|
||||
(defn install-shortcut!
|
||||
"Installs a Keyboard Shortcut handler.
|
||||
The key is a string the trigger is a function that will receive the keyboard event as the
|
||||
first argument. If once? is true the keyboard shortcut is only fired once.
|
||||
The unregister handler is returned and can be called to unregister the listener.
|
||||
If target is not given it's attached to window."
|
||||
([key trigger] (install-shortcut! key trigger false js/window))
|
||||
([key trigger once?] (install-shortcut! key trigger once? js/window))
|
||||
([key trigger once? target]
|
||||
(let [handler (new KeyboardShortcutHandler target)]
|
||||
(.registerShortcut handler (str key once?) key)
|
||||
(events/listen
|
||||
handler
|
||||
EventType/SHORTCUT_TRIGGERED
|
||||
(fn [e]
|
||||
(trigger e)
|
||||
(when once?
|
||||
(.unregisterShortcut handler keys))))
|
||||
(fn []
|
||||
(.unregisterShortcut handler key)))))
|
||||
```
|
||||
|
||||
# Remember state
|
||||
|
||||
This mixin can be used to remember the state of a component. For this to work you need to UNIQUELY identify
|
||||
your component somehow. For instance, by generating some unique id from the given args to your component:
|
||||
|
||||
```clojure
|
||||
(defn remember-state-mixin
|
||||
"Remembers the state :rum/local for a given component and swaps it back on mount.
|
||||
The given function is passed the args of the component (with apply).
|
||||
And should return a map key that is used to uniquely identify the component.
|
||||
|
||||
(remember-state-mixin (fn [arg1 arg2] (:db/id arg1)))"
|
||||
[f]
|
||||
(let [store (atom {})
|
||||
key-fn (fn [state] (apply f (:rum/args state)))]
|
||||
{:will-unmount
|
||||
(fn remember-state-unmount [state]
|
||||
(swap! store assoc (key-fn state) @(:rum/local state))
|
||||
state)
|
||||
:will-mount
|
||||
(fn remember-state-mount [state]
|
||||
(let [old-state @store
|
||||
key (key-fn state)]
|
||||
(swap! (:rum/local state) merge (get old-state key {})))
|
||||
state)}))
|
||||
```
|
@ -1,4 +1,4 @@
|
||||
(defproject rum "0.11.2"
|
||||
(defproject rum "0.11.3"
|
||||
:description "ClojureScript wrapper for React"
|
||||
:license { :name "Eclipse"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html" }
|
||||
|
218
src/rum/core.clj
218
src/rum/core.clj
@ -63,7 +63,11 @@
|
||||
|
||||
|
||||
(defmacro defc
|
||||
"Defc does couple of things:
|
||||
"```
|
||||
(defc name doc-string? (< mixins+)? [ params* ] render-body+)
|
||||
```
|
||||
|
||||
Defc does couple of things:
|
||||
|
||||
1. Wraps body into sablono/compile-html
|
||||
2. Generates render function from that
|
||||
@ -71,37 +75,38 @@
|
||||
4. Using that class, generates constructor fn [args]->ReactElement
|
||||
5. Defines top-level var with provided name and assigns ctor to it
|
||||
|
||||
(rum/defc label [t]
|
||||
[:div t])
|
||||
|
||||
;; creates React class
|
||||
;; defines ctor fn (defn label [t] ...) => element
|
||||
|
||||
(label \"text\") ;; => returns React element built with label class
|
||||
|
||||
Usage:
|
||||
|
||||
```
|
||||
(rum/defc label < rum/static [t]
|
||||
[:div t])
|
||||
|
||||
(defc name doc-string? [< mixins+]? [params*] render-body+)"
|
||||
;; creates React class
|
||||
;; adds mixin rum/static
|
||||
;; defines ctor fn (defn label [t] ...) => element
|
||||
|
||||
(label \"text\") ;; => returns React element built with label class
|
||||
```"
|
||||
[& body]
|
||||
(-defc 'rum.core/build-defc (boolean (:ns &env)) body))
|
||||
|
||||
|
||||
(defmacro defcs
|
||||
"Same as defc, but render will take additional first argument: state
|
||||
|
||||
Usage:
|
||||
|
||||
(defcs name doc-string? [< mixins+]? [state params*] render-body+)"
|
||||
"```
|
||||
(defcs name doc-string? (< mixins+)? [ state-arg params* ] render-body+)
|
||||
```
|
||||
|
||||
Same as [[defc]], but render will take additional first argument: component state."
|
||||
[& body]
|
||||
(-defc 'rum.core/build-defcs (boolean (:ns &env)) body))
|
||||
|
||||
|
||||
(defmacro defcc
|
||||
"Same as defc, but render will take additional first argument: react component
|
||||
|
||||
Usage:
|
||||
"```
|
||||
(defcc name doc-string? (< mixins+)? [ comp-arg params* ] render-body+)
|
||||
```
|
||||
|
||||
(defcc name doc-string? [< mixins+]? [comp params*] render-body+)"
|
||||
Same as [[defc]], but render will take additional first argument: react component."
|
||||
[& body]
|
||||
(-defc 'rum.core/build-defcc (boolean (:ns &env)) body))
|
||||
|
||||
@ -127,19 +132,19 @@
|
||||
(or dom [:rum/nothing])))))
|
||||
|
||||
|
||||
(defn build-defc [render-body mixins display-name]
|
||||
(defn ^:no-doc build-defc [render-body mixins display-name]
|
||||
(if (empty? mixins)
|
||||
(fn [& args] (or (apply render-body args) [:rum/nothing]))
|
||||
(let [render (fn [state] [(apply render-body (:rum/args state)) state])]
|
||||
(build-ctor render mixins display-name))))
|
||||
|
||||
|
||||
(defn build-defcs [render-body mixins display-name]
|
||||
(defn ^:no-doc build-defcs [render-body mixins display-name]
|
||||
(let [render (fn [state] [(apply render-body state (:rum/args state)) state])]
|
||||
(build-ctor render mixins display-name)))
|
||||
|
||||
|
||||
(defn build-defcc [render-body mixins display-name]
|
||||
(defn ^:no-doc build-defcc [render-body mixins display-name]
|
||||
(let [render (fn [state] [(apply render-body (:rum/react-component state) (:rum/args state)) state])]
|
||||
(build-ctor render mixins display-name)))
|
||||
|
||||
@ -147,7 +152,17 @@
|
||||
;; rum.core APIs
|
||||
|
||||
|
||||
(defn with-key [element key]
|
||||
(defn with-key
|
||||
"Adds React key to element.
|
||||
|
||||
```
|
||||
(rum/defc label [text] [:div text])
|
||||
|
||||
(-> (label)
|
||||
(rum/with-key \"abc\")
|
||||
(rum/mount js/document.body))
|
||||
```"
|
||||
[element key]
|
||||
(cond
|
||||
(render/nothing? element)
|
||||
element
|
||||
@ -159,43 +174,65 @@
|
||||
(into [(first element) {:key key}] (next element))))
|
||||
|
||||
|
||||
(defn with-ref [element ref]
|
||||
(defn with-ref
|
||||
"Supported, does nothing."
|
||||
[element ref]
|
||||
element)
|
||||
|
||||
|
||||
;; mixins
|
||||
|
||||
|
||||
(def static {})
|
||||
(def static "Supported, does nothing." {})
|
||||
|
||||
|
||||
(defn local
|
||||
"Mixin constructor. Adds an atom to component’s state that can be used to keep stuff during component’s lifecycle. Component will be re-rendered if atom’s value changes. Atom is stored under user-provided key or under `:rum/local` by default.
|
||||
|
||||
```
|
||||
(rum/defcs counter < (rum/local 0 :cnt)
|
||||
[state label]
|
||||
(let [*cnt (:cnt state)]
|
||||
[:div {:on-click (fn [_] (swap! *cnt inc))}
|
||||
label @*cnt]))
|
||||
|
||||
(rum/mount (counter \"Click count: \"))
|
||||
```"
|
||||
([initial] (local initial :rum/local))
|
||||
([initial key]
|
||||
{:will-mount (fn [state]
|
||||
(assoc state key (atom initial)))}))
|
||||
|
||||
|
||||
(def reactive {})
|
||||
(def reactive "Supported, does nothing." {})
|
||||
|
||||
|
||||
(def react deref)
|
||||
(def ^{:arglists '([ref])
|
||||
:doc "Supported as simple deref."}
|
||||
react deref)
|
||||
|
||||
|
||||
(defn cursor-in
|
||||
"Given atom with deep nested value and path inside it, creates an atom-like structure
|
||||
that can be used separately from main atom, but will sync changes both ways:
|
||||
|
||||
(def db (atom { :users { \"Ivan\" { :age 30 }}}))
|
||||
(def ivan (rum/cursor db [:users \"Ivan\"]))
|
||||
\\@ivan ;; => { :age 30 }
|
||||
(swap! ivan update :age inc) ;; => { :age 31 }
|
||||
\\@db ;; => { :users { \"Ivan\" { :age 31 }}}
|
||||
(swap! db update-in [:users \"Ivan\" :age] inc) ;; => { :users { \"Ivan\" { :age 32 }}}
|
||||
\\@ivan ;; => { :age 32 }
|
||||
```
|
||||
(def db (atom { :users { \"Ivan\" { :age 30 }}}))
|
||||
|
||||
(def ivan (rum/cursor db [:users \"Ivan\"]))
|
||||
(deref ivan) ;; => { :age 30 }
|
||||
|
||||
(swap! ivan update :age inc) ;; => { :age 31 }
|
||||
(deref db) ;; => { :users { \"Ivan\" { :age 31 }}}
|
||||
|
||||
(swap! db update-in [:users \"Ivan\" :age] inc)
|
||||
;; => { :users { \"Ivan\" { :age 32 }}}
|
||||
|
||||
(deref ivan) ;; => { :age 32 }
|
||||
```
|
||||
|
||||
Returned value supports deref, swap!, reset!, watches and metadata.
|
||||
The only supported option is `:meta`"
|
||||
Returned value supports `deref`, `swap!`, `reset!`, watches and metadata.
|
||||
|
||||
The only supported option is `:meta`"
|
||||
^rum.cursor.Cursor [ref path & { :as options }]
|
||||
(if (instance? Cursor ref)
|
||||
(cursor/Cursor. (.-ref ^Cursor ref) (into (.-path ^Cursor ref) path) (:meta options) (volatile! {}))
|
||||
@ -203,90 +240,93 @@
|
||||
|
||||
|
||||
(defn cursor
|
||||
"Same as `rum.core/cursor-in` but accepts single key instead of path vector"
|
||||
"Same as [[cursor-in]] but accepts single key instead of path vector."
|
||||
^rum.cursor.Cursor [ref key & options]
|
||||
(apply cursor-in ref [key] options))
|
||||
|
||||
(def ^{:style/indent 2} derived-atom
|
||||
"Use this to create “chains” and acyclic graphs of dependent atoms.
|
||||
`derived-atom` will:
|
||||
- Take N “source” refs
|
||||
- Set up a watch on each of them
|
||||
- Create “sink” atom
|
||||
- When any of source refs changes:
|
||||
- re-run function `f`, passing N dereferenced values of source refs
|
||||
- `reset!` result of `f` to the sink atom
|
||||
- return sink atom
|
||||
|
||||
(def *a (atom 0))
|
||||
(def *b (atom 1))
|
||||
(def *x (derived-atom [*a *b] ::key
|
||||
(fn [a b]
|
||||
(str a \":\" b))))
|
||||
(type *x) ;; => clojure.lang.Atom
|
||||
\\@*x ;; => 0:1
|
||||
(swap! *a inc)
|
||||
\\@*x ;; => 1:1
|
||||
(reset! *b 7)
|
||||
\\@*x ;; => 1:7
|
||||
(def ^{:style/indent 2
|
||||
:arglists '([refs key f] [refs key f opts])
|
||||
:doc "Use this to create “chains” and acyclic graphs of dependent atoms.
|
||||
|
||||
[[derived-atom]] will:
|
||||
|
||||
- Take N “source” refs.
|
||||
- Set up a watch on each of them.
|
||||
- Create “sink” atom.
|
||||
- When any of source refs changes:
|
||||
- re-run function `f`, passing N dereferenced values of source refs.
|
||||
- `reset!` result of `f` to the sink atom.
|
||||
- Return sink atom.
|
||||
|
||||
Arguments:
|
||||
refs - sequence of source refs
|
||||
key - unique key to register watcher, see `clojure.core/add-watch`
|
||||
f - function that must accept N arguments (same as number of source refs)
|
||||
and return a value to be written to the sink ref.
|
||||
Note: `f` will be called with already dereferenced values
|
||||
opts - optional. Map of:
|
||||
:ref - Use this as sink ref. By default creates new atom
|
||||
:check-equals? - Do an equality check on each update: `(= @sink (f new-vals))`.
|
||||
If result of `f` is equal to the old one, do not call `reset!`.
|
||||
Defaults to `true`. Set to false if calling `=` would be expensive"
|
||||
derived-atom/derived-atom)
|
||||
Example:
|
||||
|
||||
```
|
||||
(def *a (atom 0))
|
||||
(def *b (atom 1))
|
||||
(def *x (derived-atom [*a *b] ::key
|
||||
(fn [a b]
|
||||
(str a \":\" b))))
|
||||
|
||||
(type *x) ;; => clojure.lang.Atom
|
||||
(deref *x) ;; => \"0:1\"
|
||||
|
||||
(swap! *a inc)
|
||||
(deref *x) ;; => \"1:1\"
|
||||
|
||||
(reset! *b 7)
|
||||
(deref *x) ;; => \"1:7\"
|
||||
```
|
||||
|
||||
Arguments:
|
||||
|
||||
- `refs` - sequence of source refs,
|
||||
- `key` - unique key to register watcher, same as in `clojure.core/add-watch`,
|
||||
- `f` - function that must accept N arguments (same as number of source refs) and return a value to be written to the sink ref. Note: `f` will be called with already dereferenced values,
|
||||
- `opts` - optional. Map of:
|
||||
- `:ref` - use this as sink ref. By default creates new atom,
|
||||
- `:check-equals?` - Defaults to `true`. If equality check should be run on each source update: `(= @sink (f new-vals))`. When result of recalculating `f` equals to the old value, `reset!` won’t be called. Set to `false` if checking for equality can be expensive."}
|
||||
derived-atom derived-atom/derived-atom)
|
||||
|
||||
|
||||
;;; Server-side rendering
|
||||
|
||||
(def ^{:arglists '([element] [element opts])
|
||||
:doc "Main server-side rendering method. Given component, returns HTML string with static markup of that component. Serve that string to the browser and [[mount]] same Rum component over it. React will be able to reuse already existing DOM and will initialize much faster. No opts are supported at the moment."}
|
||||
render-html render/render-html)
|
||||
|
||||
(def render-html
|
||||
"Main server-side rendering method. Given component, returns HTML string with
|
||||
static markup of that component. Serve that string to the browser and
|
||||
`rum.core/mount` same Rum component over it. React will be able to reuse already
|
||||
existing DOM and will initialize much faster"
|
||||
render/render-html)
|
||||
|
||||
(def render-static-markup
|
||||
"Same as `rum.core/render-html` but returned string has nothing React-specific.
|
||||
This allows Rum to be used as traditional server-side template engine"
|
||||
render/render-static-markup)
|
||||
(def ^{:arglists '([element])
|
||||
:doc "Same as [[render-html]] but returned string has nothing React-specific. This allows Rum to be used as traditional server-side templating engine."}
|
||||
render-static-markup render/render-static-markup)
|
||||
|
||||
|
||||
;; method parity with CLJS version so you can avoid conditional directive
|
||||
;; in e.g. did-mount/will-unmount mixin bodies
|
||||
|
||||
|
||||
(defn state [c]
|
||||
(defn ^:no-doc state [c]
|
||||
(throw (UnsupportedOperationException. "state is only available from ClojureScript")))
|
||||
|
||||
|
||||
(defn dom-node [s]
|
||||
(defn ^:no-doc dom-node [s]
|
||||
(throw (UnsupportedOperationException. "dom-node is only available from ClojureScript")))
|
||||
|
||||
|
||||
(defn ref [s k]
|
||||
(defn ^:no-doc ref [s k]
|
||||
(throw (UnsupportedOperationException. "ref is only available from ClojureScript")))
|
||||
|
||||
|
||||
(defn ref-node [s k]
|
||||
(defn ^:no-doc ref-node [s k]
|
||||
(throw (UnsupportedOperationException. "ref is only available from ClojureScript")))
|
||||
|
||||
|
||||
(defn mount [c n]
|
||||
(defn ^:no-doc mount [c n]
|
||||
(throw (UnsupportedOperationException. "mount is only available from ClojureScript")))
|
||||
|
||||
|
||||
(defn unmount [c]
|
||||
(defn ^:no-doc unmount [c]
|
||||
(throw (UnsupportedOperationException. "unmount is only available from ClojureScript")))
|
||||
|
||||
|
||||
(defn request-render [c]
|
||||
(defn ^:no-doc request-render [c]
|
||||
(throw (UnsupportedOperationException. "request-render is only available from ClojureScript")))
|
||||
|
@ -12,12 +12,12 @@
|
||||
|
||||
|
||||
(defn state
|
||||
"Given React component, returns Rum state associated with it"
|
||||
"Given React component, returns Rum state associated with it."
|
||||
[comp]
|
||||
(gobj/get (.-state comp) ":rum/state"))
|
||||
|
||||
|
||||
(defn extend! [obj props]
|
||||
(defn- extend! [obj props]
|
||||
(doseq [[k v] props
|
||||
:when (some? v)]
|
||||
(gobj/set obj (name k) (clj->js v))))
|
||||
@ -148,7 +148,7 @@
|
||||
(with-meta ctor { :rum/class class })))
|
||||
|
||||
|
||||
(defn build-defc [render-body mixins display-name]
|
||||
(defn ^:no-doc build-defc [render-body mixins display-name]
|
||||
(if (empty? mixins)
|
||||
(let [class (fn [props]
|
||||
(apply render-body (aget props ":rum/args")))
|
||||
@ -160,12 +160,12 @@
|
||||
(build-ctor render mixins display-name))))
|
||||
|
||||
|
||||
(defn build-defcs [render-body mixins display-name]
|
||||
(defn ^:no-doc build-defcs [render-body mixins display-name]
|
||||
(let [render (fn [state] [(apply render-body state (:rum/args state)) state])]
|
||||
(build-ctor render mixins display-name)))
|
||||
|
||||
|
||||
(defn build-defcc [render-body mixins display-name]
|
||||
(defn ^:no-doc build-defcc [render-body mixins display-name]
|
||||
(let [render (fn [state] [(apply render-body (:rum/react-component state) (:rum/args state)) state])]
|
||||
(build-ctor render mixins display-name)))
|
||||
|
||||
@ -204,7 +204,7 @@
|
||||
|
||||
|
||||
(defn request-render
|
||||
"Schedules react component to be rendered on next animation frame"
|
||||
"Schedules react component to be rendered on next animation frame."
|
||||
[component]
|
||||
(when (empty? @render-queue)
|
||||
(schedule render))
|
||||
@ -212,58 +212,74 @@
|
||||
|
||||
|
||||
(defn mount
|
||||
"Add component to the DOM tree. Idempotent. Subsequent mounts will just update component"
|
||||
[component node]
|
||||
(js/ReactDOM.render component node)
|
||||
"Add element to the DOM tree. Idempotent. Subsequent mounts will just update element."
|
||||
[element node]
|
||||
(js/ReactDOM.render element node)
|
||||
nil)
|
||||
|
||||
|
||||
(defn unmount
|
||||
"Removes component from the DOM tree"
|
||||
"Removes component from the DOM tree."
|
||||
[node]
|
||||
(js/ReactDOM.unmountComponentAtNode node))
|
||||
|
||||
|
||||
(defn hydrate
|
||||
"Hydrates server rendered DOM tree with provided component."
|
||||
[component node]
|
||||
(js/ReactDOM.hydrate component node))
|
||||
"Same as [[mount]] but must be called on DOM tree already rendered by a server via [[render-html]]."
|
||||
[element node]
|
||||
(js/ReactDOM.hydrate element node))
|
||||
|
||||
|
||||
(defn portal
|
||||
"Render `component` in a DOM `node` that might be ouside of current DOM hierarchy"
|
||||
[component node]
|
||||
(js/ReactDOM.createPortal component node))
|
||||
"Render `element` in a DOM `node` that is ouside of current DOM hierarchy."
|
||||
[element node]
|
||||
(js/ReactDOM.createPortal element node))
|
||||
|
||||
|
||||
;; initialization
|
||||
|
||||
(defn with-key
|
||||
"Adds React key to component"
|
||||
[component key]
|
||||
(js/React.cloneElement component #js { "key" key } nil))
|
||||
"Adds React key to element.
|
||||
|
||||
```
|
||||
(rum/defc label [text] [:div text])
|
||||
|
||||
(-> (label)
|
||||
(rum/with-key \"abc\")
|
||||
(rum/mount js/document.body))
|
||||
```"
|
||||
[element key]
|
||||
(js/React.cloneElement element #js { "key" key } nil))
|
||||
|
||||
|
||||
(defn with-ref
|
||||
"Adds React ref (string or callback) to component"
|
||||
[component ref]
|
||||
(js/React.cloneElement component #js { "ref" ref } nil))
|
||||
"Adds React ref (string or callback) to element.
|
||||
|
||||
```
|
||||
(rum/defc label [text] [:div text])
|
||||
|
||||
(-> (label)
|
||||
(rum/with-ref \"abc\")
|
||||
(rum/mount js/document.body))
|
||||
```"
|
||||
[element ref]
|
||||
(js/React.cloneElement element #js { "ref" ref } nil))
|
||||
|
||||
|
||||
(defn dom-node
|
||||
"Given state, returns top-level DOM node. Can’t be called during render"
|
||||
"Given state, returns top-level DOM node of component. Call it during lifecycle callbacks. Can’t be called during render."
|
||||
[state]
|
||||
(js/ReactDOM.findDOMNode (:rum/react-component state)))
|
||||
|
||||
|
||||
(defn ref
|
||||
"Given state and ref handle, returns React component"
|
||||
"Given state and ref handle, returns React component."
|
||||
[state key]
|
||||
(-> state :rum/react-component (aget "refs") (aget (name key))))
|
||||
|
||||
|
||||
(defn ref-node
|
||||
"Given state and ref handle, returns DOM node associated with ref"
|
||||
"Given state and ref handle, returns DOM node associated with ref."
|
||||
[state key]
|
||||
(js/ReactDOM.findDOMNode (ref state (name key))))
|
||||
|
||||
@ -271,8 +287,21 @@
|
||||
;; static mixin
|
||||
|
||||
(def static
|
||||
"Mixin. Will avoid re-render if none of component’s arguments have changed.
|
||||
Does equality check (=) on all arguments"
|
||||
"Mixin. Will avoid re-render if none of component’s arguments have changed. Does equality check (`=`) on all arguments.
|
||||
|
||||
```
|
||||
(rum/defc label < rum/static
|
||||
[text]
|
||||
[:div text])
|
||||
|
||||
(rum/mount (label \"abc\") js/document.body)
|
||||
|
||||
;; def != abc, will re-render
|
||||
(rum/mount (label \"def\") js/document.body)
|
||||
|
||||
;; def == def, won’t re-render
|
||||
(rum/mount (label \"def\") js/document.body)
|
||||
```"
|
||||
{ :should-update
|
||||
(fn [old-state new-state]
|
||||
(not= (:rum/args old-state) (:rum/args new-state))) })
|
||||
@ -281,9 +310,17 @@
|
||||
;; local mixin
|
||||
|
||||
(defn local
|
||||
"Mixin constructor. Adds an atom to component’s state that can be used to keep stuff
|
||||
during component’s lifecycle. Component will be re-rendered if atom’s value changes.
|
||||
Atom is stored under user-provided key or under `:rum/local` by default"
|
||||
"Mixin constructor. Adds an atom to component’s state that can be used to keep stuff during component’s lifecycle. Component will be re-rendered if atom’s value changes. Atom is stored under user-provided key or under `:rum/local` by default.
|
||||
|
||||
```
|
||||
(rum/defcs counter < (rum/local 0 :cnt)
|
||||
[state label]
|
||||
(let [*cnt (:cnt state)]
|
||||
[:div {:on-click (fn [_] (swap! *cnt inc))}
|
||||
label @*cnt]))
|
||||
|
||||
(rum/mount (counter \"Click count: \"))
|
||||
```"
|
||||
([initial] (local initial :rum/local))
|
||||
([initial key]
|
||||
{ :will-mount
|
||||
@ -302,7 +339,17 @@
|
||||
|
||||
|
||||
(def reactive
|
||||
"Mixin. Works in conjunction with `rum.core/react`"
|
||||
"Mixin. Works in conjunction with [[react]].
|
||||
|
||||
```
|
||||
(rum/defc comp < rum/reactive
|
||||
[*counter]
|
||||
[:div (rum/react counter)])
|
||||
|
||||
(def *counter (atom 0))
|
||||
(rum/mount (comp *counter) js/document.body)
|
||||
(swap! *counter inc) ;; will force comp to re-render
|
||||
```"
|
||||
{ :init
|
||||
(fn [state props]
|
||||
(assoc state :rum.reactive/key (random-uuid)))
|
||||
@ -333,9 +380,7 @@
|
||||
|
||||
|
||||
(defn react
|
||||
"Works in conjunction with `rum.core/reactive` mixin. Use this function instead of
|
||||
`deref` inside render, and your component will subscribe to changes happening
|
||||
to the derefed atom."
|
||||
"Works in conjunction with [[reactive]] mixin. Use this function instead of `deref` inside render, and your component will subscribe to changes happening to the derefed atom."
|
||||
[ref]
|
||||
(assert *reactions* "rum.core/react is only supported in conjunction with rum.core/reactive")
|
||||
(vswap! *reactions* conj ref)
|
||||
@ -344,41 +389,48 @@
|
||||
|
||||
;; derived-atom
|
||||
|
||||
(def ^{:style/indent 2} derived-atom
|
||||
"Use this to create “chains” and acyclic graphs of dependent atoms.
|
||||
`derived-atom` will:
|
||||
- Take N “source” refs
|
||||
- Set up a watch on each of them
|
||||
- Create “sink” atom
|
||||
- When any of source refs changes:
|
||||
- re-run function `f`, passing N dereferenced values of source refs
|
||||
- `reset!` result of `f` to the sink atom
|
||||
- return sink atom
|
||||
(def ^{:style/indent 2
|
||||
:arglists '([refs key f] [refs key f opts])
|
||||
:doc "Use this to create “chains” and acyclic graphs of dependent atoms.
|
||||
|
||||
[[derived-atom]] will:
|
||||
|
||||
- Take N “source” refs.
|
||||
- Set up a watch on each of them.
|
||||
- Create “sink” atom.
|
||||
- When any of source refs changes:
|
||||
- re-run function `f`, passing N dereferenced values of source refs.
|
||||
- `reset!` result of `f` to the sink atom.
|
||||
- Return sink atom.
|
||||
|
||||
(def *a (atom 0))
|
||||
(def *b (atom 1))
|
||||
(def *x (derived-atom [*a *b] ::key
|
||||
(fn [a b]
|
||||
(str a \":\" b))))
|
||||
(type *x) ;; => clojure.lang.Atom
|
||||
\\@*x ;; => 0:1
|
||||
(swap! *a inc)
|
||||
\\@*x ;; => 1:1
|
||||
(reset! *b 7)
|
||||
\\@*x ;; => 1:7
|
||||
Example:
|
||||
|
||||
Arguments:
|
||||
refs - sequence of source refs
|
||||
key - unique key to register watcher, see `clojure.core/add-watch`
|
||||
f - function that must accept N arguments (same as number of source refs)
|
||||
and return a value to be written to the sink ref.
|
||||
Note: `f` will be called with already dereferenced values
|
||||
opts - optional. Map of:
|
||||
:ref - Use this as sink ref. By default creates new atom
|
||||
:check-equals? - Do an equality check on each update: `(= @sink (f new-vals))`.
|
||||
If result of `f` is equal to the old one, do not call `reset!`.
|
||||
Defaults to `true`. Set to false if calling `=` would be expensive"
|
||||
derived-atom/derived-atom)
|
||||
```
|
||||
(def *a (atom 0))
|
||||
(def *b (atom 1))
|
||||
(def *x (derived-atom [*a *b] ::key
|
||||
(fn [a b]
|
||||
(str a \":\" b))))
|
||||
|
||||
(type *x) ;; => clojure.lang.Atom
|
||||
(deref *x) ;; => \"0:1\"
|
||||
|
||||
(swap! *a inc)
|
||||
(deref *x) ;; => \"1:1\"
|
||||
|
||||
(reset! *b 7)
|
||||
(deref *x) ;; => \"1:7\"
|
||||
```
|
||||
|
||||
Arguments:
|
||||
|
||||
- `refs` - sequence of source refs,
|
||||
- `key` - unique key to register watcher, same as in `clojure.core/add-watch`,
|
||||
- `f` - function that must accept N arguments (same as number of source refs) and return a value to be written to the sink ref. Note: `f` will be called with already dereferenced values,
|
||||
- `opts` - optional. Map of:
|
||||
- `:ref` - use this as sink ref. By default creates new atom,
|
||||
- `:check-equals?` - Defaults to `true`. If equality check should be run on each source update: `(= @sink (f new-vals))`. When result of recalculating `f` equals to the old value, `reset!` won’t be called. Set to `false` if checking for equality can be expensive."}
|
||||
derived-atom derived-atom/derived-atom)
|
||||
|
||||
|
||||
;; cursors
|
||||
@ -387,16 +439,24 @@
|
||||
"Given atom with deep nested value and path inside it, creates an atom-like structure
|
||||
that can be used separately from main atom, but will sync changes both ways:
|
||||
|
||||
(def db (atom { :users { \"Ivan\" { :age 30 }}}))
|
||||
(def ivan (rum/cursor db [:users \"Ivan\"]))
|
||||
\\@ivan ;; => { :age 30 }
|
||||
(swap! ivan update :age inc) ;; => { :age 31 }
|
||||
\\@db ;; => { :users { \"Ivan\" { :age 31 }}}
|
||||
(swap! db update-in [:users \"Ivan\" :age] inc) ;; => { :users { \"Ivan\" { :age 32 }}}
|
||||
\\@ivan ;; => { :age 32 }
|
||||
```
|
||||
(def db (atom { :users { \"Ivan\" { :age 30 }}}))
|
||||
|
||||
(def ivan (rum/cursor db [:users \"Ivan\"]))
|
||||
(deref ivan) ;; => { :age 30 }
|
||||
|
||||
(swap! ivan update :age inc) ;; => { :age 31 }
|
||||
(deref db) ;; => { :users { \"Ivan\" { :age 31 }}}
|
||||
|
||||
(swap! db update-in [:users \"Ivan\" :age] inc)
|
||||
;; => { :users { \"Ivan\" { :age 32 }}}
|
||||
|
||||
(deref ivan) ;; => { :age 32 }
|
||||
```
|
||||
|
||||
Returned value supports deref, swap!, reset!, watches and metadata.
|
||||
The only supported option is `:meta`"
|
||||
Returned value supports `deref`, `swap!`, `reset!`, watches and metadata.
|
||||
|
||||
The only supported option is `:meta`"
|
||||
[ref path & {:as options}]
|
||||
(if (instance? cursor/Cursor ref)
|
||||
(cursor/Cursor. (.-ref ref) (into (.-path ref) path) (:meta options))
|
||||
@ -404,6 +464,6 @@
|
||||
|
||||
|
||||
(defn cursor
|
||||
"Same as `rum.core/cursor-in` but accepts single key instead of path vector"
|
||||
"Same as [[cursor-in]] but accepts single key instead of path vector."
|
||||
[ref key & options]
|
||||
(apply cursor-in ref [key] options))
|
||||
|
@ -1,4 +1,4 @@
|
||||
(ns rum.cursor)
|
||||
(ns ^:no-doc rum.cursor)
|
||||
|
||||
|
||||
(deftype Cursor [ref path ^:volatile-mutable meta watches]
|
||||
|
@ -1,4 +1,4 @@
|
||||
(ns rum.cursor)
|
||||
(ns ^:no-doc rum.cursor)
|
||||
|
||||
|
||||
(deftype Cursor [ref path meta]
|
||||
|
@ -1,4 +1,4 @@
|
||||
(ns rum.derived-atom)
|
||||
(ns ^:no-doc rum.derived-atom)
|
||||
|
||||
|
||||
(defn derived-atom
|
||||
|
@ -1,4 +1,4 @@
|
||||
(ns rum.server-render
|
||||
(ns ^:no-doc rum.server-render
|
||||
(:require
|
||||
[clojure.string :as str])
|
||||
(:import
|
||||
|
@ -1,4 +1,4 @@
|
||||
(ns rum.util)
|
||||
(ns ^:no-doc rum.util)
|
||||
|
||||
|
||||
(defn collect [key mixins]
|
||||
|
@ -360,7 +360,7 @@ e,h,f,k,l,m,n,p,q,w,z);B=C(D);var G=N(D);if(null==G)return a.Ca?a.Ca(b,c,d,e,h,f
|
||||
q,w,z,B,D,G,E);S=C(ia);var Ga=N(ia);if(null==Ga)return a.Ga?a.Ga(b,c,d,e,h,f,k,l,m,n,p,q,w,z,B,D,G,E,S):a.call(a,b,c,d,e,h,f,k,l,m,n,p,q,w,z,B,D,G,E,S);ia=C(Ga);Ga=N(Ga);if(null==Ga)return a.Ha?a.Ha(b,c,d,e,h,f,k,l,m,n,p,q,w,z,B,D,G,E,S,ia):a.call(a,b,c,d,e,h,f,k,l,m,n,p,q,w,z,B,D,G,E,S,ia);b=[b,c,d,e,h,f,k,l,m,n,p,q,w,z,B,D,G,E,S,ia];for(c=Ga;;)if(c)b.push(C(c)),c=N(c);else break;return a.apply(a,b)}
|
||||
function be(a,b){if(a.D){var c=a.F,d=Sd(c+1,b);return d<=c?Wd(a,d,b):a.D(b)}c=I(b);return null==c?a.v?a.v():a.call(a):Xd(a,C(c),N(c))}function ce(a,b,c){if(a.D){b=R(b,c);var d=a.F;c=Sd(d,c)+1;return c<=d?Wd(a,c,b):a.D(b)}return Xd(a,b,I(c))}function de(a,b,c,d){return a.D?(b=R(b,R(c,d)),c=a.F,d=2+Sd(c-1,d),d<=c?Wd(a,d,b):a.D(b)):Yd(a,b,c,I(d))}function ee(a,b,c,d,e){return a.D?(b=R(b,R(c,R(d,e))),c=a.F,e=3+Sd(c-2,e),e<=c?Wd(a,e,b):a.D(b)):Zd(a,b,c,d,I(e))}
|
||||
function ed(a,b,c,d,e,f){return a.D?(f=Td(f),b=R(b,R(c,R(d,R(e,f)))),c=a.F,f=4+Sd(c-3,f),f<=c?Wd(a,f,b):a.D(b)):ae(a,b,c,d,e,Td(f))}
|
||||
function fe(){"undefined"===typeof Ma&&(Ma=function(a){this.mc=a;this.m=393216;this.C=0},Ma.prototype.U=function(a,b){return new Ma(b)},Ma.prototype.R=function(){return this.mc},Ma.prototype.Z=function(){return!1},Ma.prototype.next=function(){return Error("No such element")},Ma.prototype.remove=function(){return Error("Unsupported operation")},Ma.Fc=function(){return new W(null,1,5,X,[ge],null)},Ma.Tb=!0,Ma.wb="cljs.core/t_cljs$core13207",Ma.ic=function(a){return Ub(a,"cljs.core/t_cljs$core13207")});
|
||||
function fe(){"undefined"===typeof Ma&&(Ma=function(a){this.mc=a;this.m=393216;this.C=0},Ma.prototype.U=function(a,b){return new Ma(b)},Ma.prototype.R=function(){return this.mc},Ma.prototype.Z=function(){return!1},Ma.prototype.next=function(){return Error("No such element")},Ma.prototype.remove=function(){return Error("Unsupported operation")},Ma.Fc=function(){return new W(null,1,5,X,[ge],null)},Ma.Tb=!0,Ma.wb="cljs.core/t_cljs$core13215",Ma.ic=function(a){return Ub(a,"cljs.core/t_cljs$core13215")});
|
||||
return new Ma(he)}function ie(a,b){for(;;){if(null==I(b))return!0;var c=L(b);c=a.f?a.f(c):a.call(null,c);if(v(c)){c=a;var d=N(b);a=c;b=d}else return!1}}function je(a,b){for(;;)if(I(b)){var c=L(b);c=a.f?a.f(c):a.call(null,c);if(v(c))return c;c=a;var d=N(b);a=c;b=d}else return null}
|
||||
function ke(a){return function(){function b(b,c){return $a(a.c?a.c(b,c):a.call(null,b,c))}function c(b){return $a(a.f?a.f(b):a.call(null,b))}function d(){return $a(a.v?a.v():a.call(null))}var e=null,f=function(){function b(a,b,d){var e=null;if(2<arguments.length){e=0;for(var f=Array(arguments.length-2);e<f.length;)f[e]=arguments[e+2],++e;e=new J(f,0,null)}return c.call(this,a,b,e)}function c(b,c,d){return $a(de(a,b,c,d))}b.F=2;b.D=function(a){var b=L(a);a=N(a);var d=L(a);a=zc(a);return c(b,d,a)};
|
||||
b.w=c;return b}();e=function(a,e,l){switch(arguments.length){case 0:return d.call(this);case 1:return c.call(this,a);case 2:return b.call(this,a,e);default:var h=null;if(2<arguments.length){h=0;for(var k=Array(arguments.length-2);h<k.length;)k[h]=arguments[h+2],++h;h=new J(k,0,null)}return f.w(a,e,h)}throw Error("Invalid arity: "+(arguments.length-1));};e.F=2;e.D=f.D;e.v=d;e.f=c;e.c=b;e.w=f.w;return e}()}
|
||||
@ -512,12 +512,12 @@ g.O=function(){null==this.A&&(this.A=vc(this.qb));return this.A};function mg(a){
|
||||
function ng(){function a(){return Math.floor(16*Math.random()).toString(16)}var b=(8|3&Math.floor(16*Math.random())).toString(16);return mg([A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),"-",A.f(a()),A.f(a()),A.f(a()),A.f(a()),"-4",A.f(a()),A.f(a()),A.f(a()),"-",A.f(b),A.f(a()),A.f(a()),A.f(a()),"-",A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a()),A.f(a())].join(""))}
|
||||
function og(a,b,c){var d=Error(a);this.message=a;this.data=b;this.Ib=c;this.name=d.name;this.description=d.description;this.number=d.number;this.fileName=d.fileName;this.lineNumber=d.lineNumber;this.columnNumber=d.columnNumber;this.stack=d.stack;return this}og.prototype.__proto__=Error.prototype;og.prototype.W=t;
|
||||
og.prototype.M=function(a,b,c){Ub(b,"#error {:message ");Zf(this.message,b,c);v(this.data)&&(Ub(b,", :data "),Zf(this.data,b,c));v(this.Ib)&&(Ub(b,", :cause "),Zf(this.Ib,b,c));return Ub(b,"}")};og.prototype.toString=function(){return lc(this)};function pg(a,b){return new og(a,b,null)};var qg=new V("rum","react-component","rum/react-component",-1879897248),rg=new V(null,"did-mount","did-mount",918232960),sg=new V(null,"min","min",444991522),tg=new V(null,"will-unmount","will-unmount",-808051550),ug=new V(null,"email","email",1415816706),vg=new V("rum.examples.errors","error","rum.examples.errors/error",1819457764),Va=new V(null,"meta","meta",1499536964),wg=new V(null,"age","age",-604307804),xg=new V(null,"did-remount","did-remount",1362550500),yg=new V(null,"color","color",1011675173),
|
||||
Wa=new V(null,"dup","dup",556298533),ge=new xc(null,"meta13208","meta13208",1259608997,null),zg=new V("rum","class","rum/class",-2030775258),Ag=new V(null,"init","init",-1875481434),Bg=new V("rum.examples.portals","*clicks","rum.examples.portals/*clicks",840774855),Cg=new V(null,"childContextTypes","childContextTypes",578717991),Dg=new V(null,"phone","phone",-763596057),Eg=new V(null,"content","content",15833224),Fg=new V(null,"msgData","msgData",345907944),Gg=new V(null,"did-catch","did-catch",2139522313),
|
||||
Hg=new V(null,"child-context","child-context",-1375270295),Ig=new V(null,"margin-left","margin-left",2015598377),Jg=new V(null,"value","value",305978217),Kg=new V("rum.reactive","key","rum.reactive/key",-803425142),Lg=new V(null,"contextTypes","contextTypes",-2023853910),Mg=new V("rum","args","rum/args",1315791754),Ng=new V(null,"width","width",-384071477),fg=new V(null,"val","val",128701612),Og=new V(null,"cursor","cursor",1011937484),Pg=new V(null,"type","type",1174270348),Qg=new V(null,"timer-static",
|
||||
"timer-static",1373464428),bg=new V(null,"fallback-impl","fallback-impl",-1501286995),Rg=new V(null,"before-render","before-render",71256781),Ta=new V(null,"flush-on-newline","flush-on-newline",-151457939),Sg=new V(null,"e","e",1381269198),Tg=new V(null,"className","className",-1983287057),Ee=new V(null,"style","style",-496642736),Ug=new V(null,"div","div",1057191632),Vg=new V(null,"did-update","did-update",-2143702256),Ua=new V(null,"readably","readably",1129599760),Tf=new V(null,"more-marker","more-marker",
|
||||
-14717935),Wg=new V(null,"key-fn","key-fn",-636154479),Xg=new V(null,"g","g",1738089905),Yg=new V(null,"will-mount","will-mount",-434633071),Zg=new V(null,"c","c",-1763192079),$g=new V(null,"for","for",-1323786319),ah=new V("rum","component-stack","rum/component-stack",2037541138),bh=new V(null,"weight","weight",-1262796205),Xa=new V(null,"print-length","print-length",1931866356),ch=new V(null,"max","max",61366548),dh=new V(null,"id","id",-1388402092),eh=new V(null,"class","class",-2030961996),hh=
|
||||
new V(null,"bmi","bmi",1421979636),ih=new V(null,"will-update","will-update",328062998),jh=new V(null,"on-mouse-move","on-mouse-move",-1386320874),kh=new V(null,"class-properties","class-properties",1351279702),lh=new V("rum","local","rum/local",-1497916586),mh=new V(null,"b","b",1482224470),nh=new V(null,"d","d",1972142424),oh=new V(null,"htmlFor","htmlFor",-1050291720),ph=new V("rum.examples.core","interval","rum.examples.core/interval",-891109255),qh=new V(null,"after-render","after-render",1997533433),
|
||||
rh=new V(null,"static-properties","static-properties",-577838503),sh=new V(null,"tag","tag",-1290361223),th=new V(null,"input","input",556931961),uh=new V(null,"msgMethod","msgMethod",523741434),vh=new V(null,"wrap-render","wrap-render",1782000986),wh=new V(null,"on-change","on-change",-732046149),xh=new V("rum.reactive","refs","rum.reactive/refs",-814076325),ag=new V(null,"alt-impl","alt-impl",670969595),yh=new V(null,"backgroundColor","backgroundColor",1738438491),zh=new V(null,"should-update",
|
||||
Wa=new V(null,"dup","dup",556298533),zg=new V("rum","class","rum/class",-2030775258),Ag=new V(null,"init","init",-1875481434),Bg=new V("rum.examples.portals","*clicks","rum.examples.portals/*clicks",840774855),Cg=new V(null,"childContextTypes","childContextTypes",578717991),Dg=new V(null,"phone","phone",-763596057),Eg=new V(null,"content","content",15833224),Fg=new V(null,"msgData","msgData",345907944),Gg=new V(null,"did-catch","did-catch",2139522313),Hg=new V(null,"child-context","child-context",
|
||||
-1375270295),Ig=new V(null,"margin-left","margin-left",2015598377),Jg=new V(null,"value","value",305978217),Kg=new V("rum.reactive","key","rum.reactive/key",-803425142),Lg=new V(null,"contextTypes","contextTypes",-2023853910),Mg=new V("rum","args","rum/args",1315791754),Ng=new V(null,"width","width",-384071477),fg=new V(null,"val","val",128701612),Og=new V(null,"cursor","cursor",1011937484),Pg=new V(null,"type","type",1174270348),Qg=new V(null,"timer-static","timer-static",1373464428),bg=new V(null,
|
||||
"fallback-impl","fallback-impl",-1501286995),Rg=new V(null,"before-render","before-render",71256781),Ta=new V(null,"flush-on-newline","flush-on-newline",-151457939),Sg=new V(null,"e","e",1381269198),Tg=new V(null,"className","className",-1983287057),Ee=new V(null,"style","style",-496642736),Ug=new V(null,"div","div",1057191632),Vg=new V(null,"did-update","did-update",-2143702256),Ua=new V(null,"readably","readably",1129599760),Tf=new V(null,"more-marker","more-marker",-14717935),Wg=new V(null,"key-fn",
|
||||
"key-fn",-636154479),Xg=new V(null,"g","g",1738089905),Yg=new V(null,"will-mount","will-mount",-434633071),Zg=new V(null,"c","c",-1763192079),ge=new xc(null,"meta13216","meta13216",-501243119,null),$g=new V(null,"for","for",-1323786319),ah=new V("rum","component-stack","rum/component-stack",2037541138),bh=new V(null,"weight","weight",-1262796205),Xa=new V(null,"print-length","print-length",1931866356),ch=new V(null,"max","max",61366548),dh=new V(null,"id","id",-1388402092),eh=new V(null,"class","class",
|
||||
-2030961996),hh=new V(null,"bmi","bmi",1421979636),ih=new V(null,"will-update","will-update",328062998),jh=new V(null,"on-mouse-move","on-mouse-move",-1386320874),kh=new V(null,"class-properties","class-properties",1351279702),lh=new V("rum","local","rum/local",-1497916586),mh=new V(null,"b","b",1482224470),nh=new V(null,"d","d",1972142424),oh=new V(null,"htmlFor","htmlFor",-1050291720),ph=new V("rum.examples.core","interval","rum.examples.core/interval",-891109255),qh=new V(null,"after-render","after-render",
|
||||
1997533433),rh=new V(null,"static-properties","static-properties",-577838503),sh=new V(null,"tag","tag",-1290361223),th=new V(null,"input","input",556931961),uh=new V(null,"msgMethod","msgMethod",523741434),vh=new V(null,"wrap-render","wrap-render",1782000986),wh=new V(null,"on-change","on-change",-732046149),xh=new V("rum.reactive","refs","rum.reactive/refs",-814076325),ag=new V(null,"alt-impl","alt-impl",670969595),yh=new V(null,"backgroundColor","backgroundColor",1738438491),zh=new V(null,"should-update",
|
||||
"should-update",-1292781795),Ah=new V(null,"a","a",-2123407586),Bh=new V(null,"height","height",1025178622);function Ch(a,b,c){this.ref=a;this.path=b;this.meta=c;this.m=2153938944;this.C=114690}g=Ch.prototype;g.equiv=function(a){return this.B(null,a)};g.M=function(a,b,c){Ub(b,"#object [rum.cursor.Cursor ");Zf(new u(null,1,[fg,this.va(null)],null),b,c);return Ub(b,"]")};g.R=function(){return this.meta};g.O=function(){return this[ba]||(this[ba]=++ca)};g.B=function(a,b){return this===b};g.bb=function(a,b){pe.G(this.ref,Be,this.path,b);return b};
|
||||
g.Mb=function(a,b){var c=this;return c.bb(0,function(){var a=c.va(null);return b.f?b.f(a):b.call(null,a)}())};g.Nb=function(a,b,c){var d=this;return d.bb(0,function(){var a=d.va(null);return b.c?b.c(a,c):b.call(null,a,c)}())};g.Ob=function(a,b,c,d){var e=this;return e.bb(0,function(){var a=e.va(null);return b.j?b.j(a,c,d):b.call(null,a,c,d)}())};g.Pb=function(a,b,c,d,e){return this.bb(0,ee(b,this.va(null),c,d,e))};
|
||||
g.Db=function(a,b,c){var d=this;gg(d.ref,kb(kb(M,b),this),function(a){return function(e,h,k,l){e=fb(H,k,d.path);l=fb(H,l,d.path);return Ac.c(e,l)?null:c.G?c.G(b,a,e,l):c.call(null,b,a,e,l)}}(this));return this};g.Eb=function(a,b){var c=kb(kb(M,b),this);Xb(this.ref,c);return this};g.va=function(){var a=F(this.ref);return fb(H,a,this.path)};var Dh;a:{var Eh=aa.navigator;if(Eh){var Fh=Eh.userAgent;if(Fh){Dh=Fh;break a}}Dh=""}function Gh(a){return-1!=Dh.indexOf(a)};function Hh(){return Gh("iPhone")&&!Gh("iPod")&&!Gh("iPad")};var Ih=Gh("Opera"),Jh=Gh("Trident")||Gh("MSIE"),Kh=Gh("Edge"),Lh=Gh("Gecko")&&!(-1!=Dh.toLowerCase().indexOf("webkit")&&!Gh("Edge"))&&!(Gh("Trident")||Gh("MSIE"))&&!Gh("Edge"),Mh=-1!=Dh.toLowerCase().indexOf("webkit")&&!Gh("Edge");Mh&&Gh("Mobile");Gh("Macintosh");Gh("Windows");Gh("Linux")||Gh("CrOS");var Nh=aa.navigator||null;Nh&&(Nh.appVersion||"").indexOf("X11");Gh("Android");Hh();Gh("iPad");Gh("iPod");Hh()||Gh("iPad")||Gh("iPod");function Oh(){var a=aa.document;return a?a.documentMode:void 0}var Ph;
|
||||
|
Reference in New Issue
Block a user