2014-01-02 13:04:30 +00:00
|
|
|
(ns demo
|
|
|
|
(:require [cloact.core :as cloact :refer [atom]]
|
2014-01-06 11:46:58 +00:00
|
|
|
[clojure.string :as string]
|
|
|
|
[demoutil :as demoutil])
|
2014-01-02 13:04:30 +00:00
|
|
|
(:require-macros [demoutil :refer [get-source]]
|
|
|
|
[cloact.debug :refer [dbg println]]))
|
|
|
|
|
|
|
|
(defn src-parts [src]
|
2014-01-06 11:46:58 +00:00
|
|
|
(string/split src #"\n(?=[(])"))
|
2014-01-02 13:04:30 +00:00
|
|
|
|
|
|
|
(defn src-defs [parts]
|
2014-01-06 11:46:58 +00:00
|
|
|
(let [ws #"\s+"]
|
|
|
|
(into {} (for [x parts]
|
|
|
|
[(-> x (string/split ws) second keyword) x]))))
|
2014-01-02 13:04:30 +00:00
|
|
|
|
|
|
|
(def srcmap
|
2014-01-06 11:46:58 +00:00
|
|
|
(-> "demo.cljs" get-source src-parts src-defs))
|
2014-01-02 13:04:30 +00:00
|
|
|
|
|
|
|
(def nssrc
|
|
|
|
"(ns example
|
2014-01-05 14:52:51 +00:00
|
|
|
(:require [cloact.core :as cloact :refer [atom]]))
|
2014-01-02 13:04:30 +00:00
|
|
|
")
|
|
|
|
|
|
|
|
(defn src-for-names [names]
|
2014-01-06 11:46:58 +00:00
|
|
|
(string/join "\n" (-> srcmap
|
|
|
|
(assoc :ns nssrc)
|
|
|
|
(select-keys names)
|
|
|
|
vals)))
|
2014-01-02 16:45:31 +00:00
|
|
|
|
2014-01-05 12:29:22 +00:00
|
|
|
(defn src-for [defs]
|
2014-01-06 11:46:58 +00:00
|
|
|
[:pre (-> defs src-for-names demoutil/syntaxify)])
|
2014-01-02 13:04:30 +00:00
|
|
|
|
2014-01-06 18:16:53 +00:00
|
|
|
(defn demo-component [{:keys [comp defs src]}]
|
2014-01-07 11:45:08 +00:00
|
|
|
(let [colored (if src
|
|
|
|
(demoutil/syntaxify src)
|
|
|
|
(src-for defs))
|
|
|
|
showing (atom true)]
|
|
|
|
(fn []
|
|
|
|
[:div
|
|
|
|
(when comp
|
|
|
|
[:div.demo-example
|
|
|
|
[:a.demo-example-hide {:on-click (fn [e]
|
|
|
|
(.preventDefault e)
|
|
|
|
(swap! showing not))}
|
|
|
|
(if @showing "hide" "show")]
|
|
|
|
[:h3.demo-heading "Example "]
|
|
|
|
(when @showing
|
|
|
|
(if defs
|
|
|
|
[:div.simple-demo [comp]]
|
|
|
|
[comp]))])
|
|
|
|
(when @showing
|
|
|
|
[:div.demo-source
|
|
|
|
[:h3.demo-heading "Source"]
|
|
|
|
colored])])))
|
2014-01-02 15:18:21 +00:00
|
|
|
|
2014-01-02 13:04:30 +00:00
|
|
|
(defn simple-component []
|
|
|
|
[:div
|
2014-01-03 09:56:15 +00:00
|
|
|
[:p "I am a component!"]
|
2014-01-02 13:04:30 +00:00
|
|
|
[:p.someclass
|
|
|
|
"I have " [:strong "bold"]
|
2014-01-03 09:56:15 +00:00
|
|
|
[:span {:style {:color "red"}} " and red "] "text."]])
|
2014-01-02 13:04:30 +00:00
|
|
|
|
2014-01-03 09:56:15 +00:00
|
|
|
(defn simple-parent []
|
2014-01-02 13:04:30 +00:00
|
|
|
[:div
|
2014-01-03 09:56:15 +00:00
|
|
|
[:p "I include simple-component."]
|
|
|
|
[simple-component]])
|
|
|
|
|
|
|
|
(defn lister [props]
|
|
|
|
[:ul
|
|
|
|
(for [item (:items props)]
|
2014-01-03 15:52:05 +00:00
|
|
|
[:li {:key item} "Item " item])])
|
2014-01-03 09:56:15 +00:00
|
|
|
|
|
|
|
(defn lister-user []
|
|
|
|
[:div
|
|
|
|
"Here is a list:"
|
|
|
|
[lister {:items (range 3)}]])
|
2014-01-02 15:18:21 +00:00
|
|
|
|
2014-01-03 12:22:41 +00:00
|
|
|
(def click-count (atom 0))
|
|
|
|
|
|
|
|
(defn counting-component []
|
|
|
|
[:div
|
2014-01-03 14:52:18 +00:00
|
|
|
"The atom " [:code "click-count"] " has value: "
|
|
|
|
@click-count ". "
|
2014-01-04 14:03:26 +00:00
|
|
|
[:input {:type "button" :value "Click me!"
|
2014-01-03 12:22:41 +00:00
|
|
|
:on-click #(swap! click-count inc)}]])
|
|
|
|
|
2014-01-04 14:03:26 +00:00
|
|
|
(defn atom-input [{:keys [value]}]
|
|
|
|
[:input {:type "text"
|
|
|
|
:value @value
|
|
|
|
:on-change #(reset! value (-> % .-target .-value))}])
|
|
|
|
|
|
|
|
(defn shared-state []
|
2014-01-03 12:22:41 +00:00
|
|
|
(let [val (atom "foo")]
|
|
|
|
(fn []
|
|
|
|
[:div
|
2014-01-04 09:56:21 +00:00
|
|
|
[:p "The value is now: " @val]
|
2014-01-04 14:03:26 +00:00
|
|
|
[:p "Change it here: "
|
|
|
|
[atom-input {:value val}]]])))
|
2014-01-03 14:52:18 +00:00
|
|
|
|
2014-01-04 09:56:21 +00:00
|
|
|
(defn timer-component []
|
|
|
|
(let [seconds-elapsed (atom 0)]
|
|
|
|
(fn []
|
|
|
|
(js/setTimeout #(swap! seconds-elapsed inc) 1000)
|
|
|
|
[:div
|
|
|
|
"Seconds Elapsed: " @seconds-elapsed])))
|
|
|
|
|
2014-01-03 14:52:18 +00:00
|
|
|
(defn render-simple []
|
|
|
|
(cloact/render-component [simple-component]
|
|
|
|
(.-body js/document)))
|
2014-01-03 12:22:41 +00:00
|
|
|
|
2014-01-07 11:45:08 +00:00
|
|
|
(defn calc-bmi [params to-calc]
|
|
|
|
(let [{:keys [height weight bmi]} params
|
|
|
|
h (/ height 100)]
|
|
|
|
(case to-calc
|
|
|
|
:bmi (assoc params :bmi (/ weight (* h h)))
|
|
|
|
:weight (assoc params :weight (* bmi h h)))))
|
2014-01-02 15:18:21 +00:00
|
|
|
|
2014-01-07 11:45:08 +00:00
|
|
|
(def bmi-data (atom (calc-bmi {:height 180 :weight 80} :bmi)))
|
2014-01-02 15:18:21 +00:00
|
|
|
|
2014-01-07 11:45:08 +00:00
|
|
|
(defn set-bmi [key val]
|
|
|
|
(swap! bmi-data #(calc-bmi (assoc % key val)
|
|
|
|
(case key :bmi :weight :bmi))))
|
2014-01-02 15:18:21 +00:00
|
|
|
|
2014-01-07 11:45:08 +00:00
|
|
|
(defn slider [{:keys [value min max param]}]
|
2014-01-02 15:18:21 +00:00
|
|
|
[:div
|
|
|
|
[:input {:type "range" :min min :max max :value value
|
|
|
|
:style {:width "100%"}
|
2014-01-07 11:45:08 +00:00
|
|
|
:on-change #(set-bmi param (-> % .-target .-value))}]])
|
2014-01-02 15:18:21 +00:00
|
|
|
|
|
|
|
(defn bmi-component []
|
|
|
|
(let [{:keys [weight height bmi]} @bmi-data
|
|
|
|
[color diagnose] (cond
|
|
|
|
(< bmi 18.5) ["orange" "underweight"]
|
|
|
|
(< bmi 25) ["inherit" "normal"]
|
|
|
|
(< bmi 30) ["orange" "overweight"]
|
|
|
|
:else ["red" "obese"])]
|
|
|
|
[:div
|
2014-01-03 14:52:18 +00:00
|
|
|
[:h3 "BMI calculator"]
|
2014-01-02 15:18:21 +00:00
|
|
|
[:div
|
|
|
|
"Height: " (int height) "cm"
|
2014-01-03 14:52:18 +00:00
|
|
|
[slider {:value height :min 100 :max 220 :param :height}]]
|
2014-01-02 15:18:21 +00:00
|
|
|
[:div
|
|
|
|
"Weight: " (int weight) "kg"
|
2014-01-03 14:52:18 +00:00
|
|
|
[slider {:value weight :min 30 :max 150 :param :weight}]]
|
2014-01-02 15:18:21 +00:00
|
|
|
[:div
|
|
|
|
"BMI: " (int bmi) " "
|
|
|
|
[:span {:style {:color color}} diagnose]
|
2014-01-07 11:45:08 +00:00
|
|
|
[slider {:value bmi :min 10 :max 50 :param :bmi}]]]))
|
2014-01-02 15:18:21 +00:00
|
|
|
|
2014-01-03 09:56:15 +00:00
|
|
|
(defn intro []
|
2014-01-07 11:45:08 +00:00
|
|
|
[:div.demo-text
|
|
|
|
|
2014-01-03 12:22:41 +00:00
|
|
|
[:h2 "Introduction to Cloact"]
|
2014-01-03 09:56:15 +00:00
|
|
|
|
2014-01-05 12:29:22 +00:00
|
|
|
[:p [:a {:href "https://github.com/holmsand/cloact"} "Cloact"]
|
|
|
|
" provides a minimalistic interface between "
|
2014-01-04 14:03:26 +00:00
|
|
|
[:a {:href "https://github.com/clojure/clojurescript"} "ClojureScript"]
|
|
|
|
" and " [:a {:href "http://facebook.github.io/react/"} "React"]
|
|
|
|
". It allows you to define React components using nothing but
|
|
|
|
plain ClojureScript functions, that describe your UI using a
|
|
|
|
Hiccup-like syntax."]
|
2014-01-03 09:56:15 +00:00
|
|
|
|
|
|
|
[:p "A very basic component may look something like this: "]
|
|
|
|
[demo-component {:comp simple-component
|
|
|
|
:defs [:simple-component]}]
|
|
|
|
|
|
|
|
[:p "You can build new components using other components as
|
|
|
|
building blocks. That looks like this:"]
|
|
|
|
[demo-component {:comp simple-parent
|
|
|
|
:defs [:simple-parent]}]
|
|
|
|
|
2014-01-07 11:45:08 +00:00
|
|
|
[:p "Data is passed to child components using plain old Clojure
|
2014-01-03 09:56:15 +00:00
|
|
|
maps. For example, here is a component that shows items in a "
|
2014-01-07 11:45:08 +00:00
|
|
|
[:code "seq"] ":" ]
|
2014-01-03 09:56:15 +00:00
|
|
|
|
|
|
|
[demo-component {:comp lister-user
|
2014-01-03 15:52:05 +00:00
|
|
|
:defs [:lister :lister-user]}]
|
|
|
|
|
|
|
|
[:p [:strong "Note: "]
|
|
|
|
"The " [:code "{:key item}"] " part of the " [:code ":li"] " isn't
|
|
|
|
really necessary in this simple example, but passing a unique key
|
|
|
|
for every item in a dynamically generated list of components is
|
|
|
|
good practice, and helps React to improve performance a lot for
|
|
|
|
large lists."]])
|
2014-01-03 12:22:41 +00:00
|
|
|
|
|
|
|
(defn managing-state []
|
2014-01-07 11:45:08 +00:00
|
|
|
[:div.demo-text
|
2014-01-03 12:22:41 +00:00
|
|
|
[:h2 "Managing state in Cloact"]
|
|
|
|
|
|
|
|
[:p "The easiest way to manage state in Cloact is to use Cloact's
|
|
|
|
own version of " [:code "atom"] ". It works exactly like the one in
|
|
|
|
clojure.core, except that it keeps track of every time it is
|
2014-01-03 14:52:18 +00:00
|
|
|
deref'ed. Any component that uses an " [:code "atom"]" is automagically
|
|
|
|
re-rendered when its value changes."]
|
2014-01-03 12:22:41 +00:00
|
|
|
|
|
|
|
[:p "Let's demonstrate that with a simple example:"]
|
|
|
|
[demo-component {:comp counting-component
|
|
|
|
:defs [:ns :click-count :counting-component]}]
|
|
|
|
|
|
|
|
[:p "Sometimes you may want to maintain state locally in a
|
2014-01-03 14:52:18 +00:00
|
|
|
component. That is easy to do with an " [:code "atom"] " as well."]
|
2014-01-03 12:22:41 +00:00
|
|
|
|
2014-01-04 14:03:26 +00:00
|
|
|
[:p "Here is an example of that, where we call setTimeout every
|
|
|
|
time the component is rendered to update a simple clock:"]
|
|
|
|
|
|
|
|
[demo-component {:comp timer-component
|
|
|
|
:defs [:timer-component]}]
|
|
|
|
|
2014-01-04 09:56:21 +00:00
|
|
|
[:p "The previous example also uses another feature of Cloact: a component
|
2014-01-03 12:22:41 +00:00
|
|
|
function can return another function, that is used to do the actual
|
|
|
|
rendering. It is called with the same arguments as any other
|
|
|
|
component function. This allows you to perform some setup of newly
|
|
|
|
created components, without resorting to React's lifecycle
|
2014-01-04 09:56:21 +00:00
|
|
|
events."]
|
|
|
|
|
2014-01-04 14:03:26 +00:00
|
|
|
[:p "By simply passing atoms around you can share state management
|
|
|
|
between components, like this:"]
|
|
|
|
[demo-component {:comp shared-state
|
|
|
|
:defs [:ns :atom-input :shared-state]}]])
|
2014-01-03 09:56:15 +00:00
|
|
|
|
2014-01-03 14:52:18 +00:00
|
|
|
(defn essential-api []
|
2014-01-07 11:45:08 +00:00
|
|
|
[:div.demo-text
|
2014-01-03 14:52:18 +00:00
|
|
|
[:h2 "Essential API"]
|
|
|
|
|
2014-01-03 15:38:28 +00:00
|
|
|
[:p "Cloact supports most of React's API, but there is really only
|
|
|
|
one entry-point that is necessary for most applications: "
|
2014-01-04 14:03:26 +00:00
|
|
|
[:code "cloact.core/render-component"] "."]
|
2014-01-03 14:52:18 +00:00
|
|
|
|
|
|
|
[:p "It takes too arguments: a component, and a DOM node. For
|
|
|
|
example, splashing the very first example all over the page would
|
|
|
|
look like this:"]
|
|
|
|
|
|
|
|
[demo-component {:defs [:ns :simple-component :render-simple]}]])
|
2014-01-03 09:56:15 +00:00
|
|
|
|
2014-01-02 15:18:21 +00:00
|
|
|
(defn bmi-demo []
|
2014-01-07 11:45:08 +00:00
|
|
|
[:div.demo-text
|
2014-01-03 14:52:18 +00:00
|
|
|
[:h2 "Putting it all together"]
|
|
|
|
|
|
|
|
[:p "Here is a slightly less contrived example: a simple BMI
|
|
|
|
calculator."]
|
|
|
|
|
|
|
|
[:p "Data is kept in a single " [:code "cloact.core/atom"] ": a map
|
|
|
|
with height, weight and BMI as keys."]
|
|
|
|
|
2014-01-02 15:18:21 +00:00
|
|
|
[demo-component {:comp bmi-component
|
|
|
|
:defs [:ns :calc-bmi :bmi-data :set-bmi :slider
|
|
|
|
:bmi-component]}]])
|
2014-01-02 13:04:30 +00:00
|
|
|
|
2014-01-06 18:16:53 +00:00
|
|
|
(defn test-results []
|
2014-01-07 11:45:08 +00:00
|
|
|
[:div.demo-text
|
2014-01-06 18:16:53 +00:00
|
|
|
[:h2 "Test results"]
|
|
|
|
[runtests/test-output]])
|
|
|
|
|
|
|
|
(defn complete-simple-demo []
|
2014-01-07 11:45:08 +00:00
|
|
|
[:div.demo-text
|
2014-01-06 18:16:53 +00:00
|
|
|
[:h2 "Another demo"]
|
|
|
|
[demo-component {:comp simpleexample/simple-example
|
|
|
|
:src (get-source "simpleexample.cljs")}]])
|
|
|
|
|
|
|
|
(defn todomvc-demo []
|
2014-01-07 11:45:08 +00:00
|
|
|
[:div.demo-text
|
2014-01-06 18:16:53 +00:00
|
|
|
[:h2 "Todomvc"]
|
|
|
|
[demo-component {:comp todomvc/todo-app
|
|
|
|
:src (get-source "todomvc.cljs")}]])
|
|
|
|
|
2014-01-02 13:04:30 +00:00
|
|
|
(defn demo []
|
2014-01-06 18:16:53 +00:00
|
|
|
[:div
|
|
|
|
[:div.test-output-mini
|
|
|
|
[runtests/test-output-mini]]
|
|
|
|
[:div.cloact-demo
|
|
|
|
[:h1 "This will become a demo"]
|
|
|
|
[intro]
|
|
|
|
[managing-state]
|
|
|
|
[essential-api]
|
|
|
|
[bmi-demo]
|
|
|
|
[test-results]
|
|
|
|
[complete-simple-demo]
|
|
|
|
[todomvc-demo]
|
|
|
|
[:p "WIP"]]])
|
2014-01-05 10:16:01 +00:00
|
|
|
|
|
|
|
(defn ^:export mountdemo []
|
|
|
|
(cloact/render-component [demo] (.-body js/document)))
|
|
|
|
|
|
|
|
(defn ^:export genpage []
|
|
|
|
(cloact/render-component-to-string [demo]))
|