├── .gitignore
├── LICENSE
├── README.md
└── recipes
├── animator
├── .gitignore
├── README.md
├── index.html
├── project.clj
└── src
│ └── animator
│ ├── animator.cljs
│ ├── clock.cljs
│ └── core.cljs
├── auto-complete
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ ├── clj
│ │ │ └── auto_complete
│ │ │ │ └── dev.clj
│ │ └── cljs
│ │ │ └── auto_complete
│ │ │ └── main.cljs
│ ├── prod
│ │ ├── clj
│ │ │ └── auto_complete
│ │ │ │ └── dev.clj
│ │ └── cljs
│ │ │ └── auto_complete
│ │ │ └── main.cljs
│ └── test
│ │ ├── js
│ │ ├── polyfill.js
│ │ └── unit-test.js
│ │ └── unit-test.html
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── auto_complete
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── auto_complete
│ │ └── core.cljs
└── system.properties
├── boot-setup
├── .gitignore
├── README.md
├── boot.properties
├── build.boot
├── resources
│ └── public
│ │ └── index.html
└── src
│ ├── boot_setup
│ └── core.cljs
│ └── public
│ └── main.cljs.edn
├── datascript-query-browser
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── datascript_query_browser
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── datascript_query_browser
│ │ └── prod.cljs
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── datascript_query_browser
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── datascript_query_browser
│ │ └── core.cljs
└── system.properties
├── dev-setup
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── dev_setup
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── dev_setup
│ │ └── prod.cljs
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── dev_setup
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── dev_setup
│ │ └── core.cljs
└── system.properties
├── dimple-bar-chart
├── .gitignore
├── README.md
├── index.html
├── project.clj
└── src
│ └── dimple_bar_chart
│ └── core.cljs
├── example
├── LICENSE
├── README.md
├── project.clj
├── resources
│ └── index.html
└── src
│ ├── clj
│ └── example
│ │ └── server.clj
│ └── cljs
│ └── example
│ └── core.cljs
├── input-validation
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── input_validation
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── input_validation
│ │ └── prod.cljs
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── input_validation
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── input_validation
│ │ └── core.cljs
└── system.properties
├── kioo-blog
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── kioo_blog
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── kioo_blog
│ │ └── prod.cljs
├── project.clj
├── resources
│ ├── blog.html
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── kioo_blog
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── kioo_blog
│ │ └── core.cljs
└── system.properties
├── knob
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── knob
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── knob
│ │ └── prod.cljs
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── knob
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── knob
│ │ └── core.cljs
└── system.properties
├── local-state
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── local_state
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── local_state
│ │ └── prod.cljs
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── local_state
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── local_state
│ │ └── core.cljs
└── system.properties
├── routing-with-secretary
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── routing_with_secretary
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── routing_with_secretary
│ │ └── prod.cljs
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── routing_with_secretary
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── routing_with_secretary
│ │ └── core.cljs
└── system.properties
├── scrollbar
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── scrollbar
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── scrollbar
│ │ └── prod.cljs
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── scrollbar
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── scrollbar
│ │ └── core.cljs
└── system.properties
├── sortable-table
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── sortable_table
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── sortable_table
│ │ └── prod.cljs
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── sortable_table
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── sortable_table
│ │ └── core.cljs
└── system.properties
├── toggle-button
├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ └── cljs
│ │ │ └── toggle_button
│ │ │ └── dev.cljs
│ └── prod
│ │ └── cljs
│ │ └── toggle_button
│ │ └── prod.cljs
├── preview.gif
├── project.clj
├── resources
│ ├── index.html
│ └── public
│ │ └── css
│ │ └── style.css
├── src
│ ├── clj
│ │ └── toggle_button
│ │ │ ├── dev.clj
│ │ │ └── server.clj
│ └── cljs
│ │ └── toggle_button
│ │ └── core.cljs
└── system.properties
└── views
├── .gitignore
├── README.md
├── index.html
├── project.clj
└── src
└── views
└── core.cljs
/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | pom.xml.asc
3 | *jar
4 | /lib/
5 | /classes/
6 | /target/
7 | /checkouts/
8 | .DS_Store
9 | .lein-deps-sum
10 | .lein-repl-history
11 | .lein-plugins/
12 | .lein-failures
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | om-cookbook
2 | ===========
3 |
4 | Home of Om recipes
5 |
6 | ## Contributions
7 |
8 | Contributions welcomed! Please fork, branch, and submit a PR. If you'd
9 | like to help out with maintenance, drop me a message.
10 |
11 | ## Recipes
12 |
13 | 1. [Dev Setup](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/dev-setup)
14 | Basic dev environment setup with chestnut that includes Figwheel, Weasel and
15 | ring server.
16 | 2. [Boot Setup](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/boot-setup)
17 | How to start a new Om project with boot-cljs.
18 | 3. [Dimple Bar Chart](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/dimple-bar-chart)
19 | Simple vertical bar chart created with dimple.js
20 | 4. [Views](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/views)
21 | Multiple views of a single data source (using multimethods).
22 | 5. [Components Using Local State](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/local-state)
23 | Making components with local state.
24 | 6. [Color Coded Input Validation](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/input-validation)
25 | 7. [Input Auto-completion](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/auto-complete)
26 | 8. [Single Attribute Sortable Table](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/sortable-table)
27 | 9. [Secretary Routing with Om Components](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/routing-with-secretary)
28 | 10. [Custom Toggle Buttons](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/toggle-button)
29 | 11. [DataScript Query Browser](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/datascript-query-browser)
30 | 12. [Custom Scrolling Bar](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/scrollbar)
31 | 13. [Circle Rotation Knobs](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/knob)
32 | 14. [Canvas Animation](https://github.com/om-cookbook/om-cookbook/tree/master/recipes/animator)
33 | 15. [Kioo Example - Enlive/Enfocus Style Templates](https://github.com/omcljs/om-cookbook/tree/master/recipes/kioo-blog)
34 |
35 |
--------------------------------------------------------------------------------
/recipes/animator/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | *jar
3 | /lib/
4 | /classes/
5 | /out/
6 | /target/
7 | .lein-deps-sum
8 | .lein-repl-history
9 | .lein-plugins/
10 |
--------------------------------------------------------------------------------
/recipes/animator/README.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | You want to do scripted HTML Canvas animations.
4 |
5 | # Solution
6 |
7 | 1. Create a project structure using Leiningen template
8 | [mies](https://github.com/swannodette/mies).
9 |
10 | ```
11 | $ lein new mies animator
12 | ```
13 |
14 | 2. Add om, sablono, and core.async dependencies to project.clj.
15 |
16 | ```clojure
17 | :dependencies [[org.clojure/clojure "1.6.0"]
18 | [org.clojure/clojurescript "0.0-2371"]
19 |
20 | [om "0.8.0-alpha2"]
21 | [sablono "0.2.20"]
22 | [org.clojure/core.async "0.1.346.0-17112a-alpha"]]
23 | ```
24 |
25 | 3. Add react to index.html, as well as a div to hold our animation.
26 |
27 | ```html
28 |
29 |
30 |
Hello, Animator!
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ```
40 |
41 | 4. Copy animator.cljs and clock.cljs into your project. These
42 | act as a library that you will use, but they are very small, and you might
43 | want to customize them.
44 |
45 | 5. Now we start working with our own code, in core.cljs.
46 | Create a function that draws something in a canvas.
47 | It should take a drawing-context argument and whatever else it needs.
48 |
49 | ```clojure
50 | (defn draw-circle
51 | [context center radius line-width scale {:keys [r g b a]
52 | :or {a 1.0}}]
53 | (let [h (.-height (.-canvas context))
54 | center-x (* scale (:x center))
55 | center-y (- h (* scale (:y center)))
56 | radius (* scale radius)]
57 | (set! (. context -strokeStyle) (str "rgba(" r "," g "," b "," a ")"))
58 | (set! (. context -lineWidth) line-width)
59 | (.beginPath context)
60 | ;; x y radius startAngle endAngle counterClockwise?:
61 | (.arc context center-x center-y radius 0 (* 2 Math/PI) false)
62 | (.stroke context)))
63 | ```
64 |
65 | 6. Create a function that calculates some property based upon the elapsed time.
66 | It should take elapsed-time and whatever else it needs. In this case we are returning
67 | an alpha of 1 (opaque) at the time the animation is to start, and values
68 | linearly decreasing to 0 (transparent) at the time the animation is to end.
69 |
70 | ```clojure
71 | (defn alpha
72 | [elapsed-time delay duration]
73 | (* .001 (- (+ delay duration) elapsed-time)))
74 | ```
75 |
76 | 7. Now create our update function using the two functions we have already defined.
77 | It must take elapsed-time and canvas, followed by a map containing all the parameters.
78 |
79 | ```clojure
80 | (defn fading-circle-update
81 | [elapsed-time canvas {:keys [center radius line-width scale color delay duration] :as opts}]
82 | (let [context (.getContext canvas "2d")
83 | a (alpha elapsed-time delay duration)]
84 | (when (>= elapsed-time delay)
85 | (.clearRect context 0 0 (.-width canvas) (.-height canvas))
86 | (draw-circle context center radius line-width scale (merge color {:a a})))))
87 | ```
88 |
89 | 8. Define a vector of your own animations.
90 | Each animation is a map containing an update function
91 | and all the parameters that the update function needs.
92 | Each animation will run in its own canvas element,
93 | with the later ones in the vector rendering in front of the earlier ones.
94 |
95 | ```clojure
96 | (defonce animations [{:update fading-circle-update
97 | :center {:x 400 :y 200}
98 | :radius 75
99 | :line-width 2
100 | :scale 1
101 | :color {:r 0 :g 255 :b 0}
102 | :delay 0
103 | :duration 2000}
104 |
105 | {:update fading-circle-update
106 | :center {:x 450 :y 250}
107 | :radius 50
108 | :line-width 2
109 | :scale 1
110 | :color {:r 255 :g 0 :b 0}
111 | :delay 250
112 | :duration 1250}
113 |
114 | {:update fading-circle-update
115 | :center {:x 200 :y 340}
116 | :radius 25
117 | :line-width 2
118 | :scale 1
119 | :color {:r 255 :g 255 :b 0}
120 | :delay 750
121 | :duration 1250}
122 |
123 | {:update fading-circle-update
124 | :center {:x 100 :y 100}
125 | :radius 50
126 | :line-width 2
127 | :scale 1
128 | :color {:r 255 :g 0 :b 0}
129 | :delay 1000
130 | :duration 1000}])
131 | ```
132 |
133 | 9. Now let's create a component that builds an animator instance
134 | and passes it the required start-time parameter:
135 |
136 | ```clojure
137 | (defn example
138 | [cursor owner opts]
139 | (reify
140 | om/IRender
141 | (render
142 | [_]
143 | (dom/div #js {:width "800px" :height "500px"}
144 |
145 | (om/build animator
146 | cursor
147 | {;; You must pass a start-time in the state map.
148 | ;; In this case we want the animation timeline
149 | ;; to begin right away, so we use the current time
150 | ;; as reported by the browser:
151 | :state {:start-time (.now (.-performance js/window))}
152 |
153 | ;; You may pass a clock-interval in milliseconds
154 | ;; in the opts map (default is 10):
155 | :opts {:clock-interval 20}})))))
156 |
157 | ```
158 |
159 | 6. That's it. Run ``` lein cljsbuild once ``` in
160 | ```/recipes/animator``` and open ```index.html``` in a
161 | browser of your choice. You should see the animations.
162 |
--------------------------------------------------------------------------------
/recipes/animator/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hello, Animator!
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/recipes/animator/project.clj:
--------------------------------------------------------------------------------
1 | (defproject animator "0.1.0-SNAPSHOT"
2 | :description "FIXME: write this!"
3 | :url "http://example.com/FIXME"
4 |
5 | :dependencies [[org.clojure/clojure "1.6.0"]
6 | [org.clojure/clojurescript "0.0-2371"]
7 |
8 | [om "0.8.0-alpha2"]
9 | [sablono "0.2.20"]
10 | [org.clojure/core.async "0.1.346.0-17112a-alpha"]]
11 |
12 | :plugins [[lein-cljsbuild "1.0.4-SNAPSHOT"]]
13 |
14 | :source-paths ["src"]
15 |
16 | :cljsbuild {
17 | :builds [{:id "animator"
18 | :source-paths ["src"]
19 | :compiler {
20 | :output-to "animator.js"
21 | :output-dir "out"
22 | :optimizations :none
23 | :source-map true}}]})
24 |
--------------------------------------------------------------------------------
/recipes/animator/src/animator/animator.cljs:
--------------------------------------------------------------------------------
1 | (ns animator.animator
2 | (:require [om.core :as om :include-macros true]
3 | [sablono.core :refer-macros [html]]
4 | [cljs.core.async :refer [! out now)))
18 | (condp = ch
19 | stop (recur false)
20 | t (recur running?)
21 | start (recur true))))
22 | [out start stop]))
23 |
--------------------------------------------------------------------------------
/recipes/animator/src/animator/core.cljs:
--------------------------------------------------------------------------------
1 | (ns animator.core
2 | (:require [om.core :as om :include-macros true]
3 | [om.dom :as dom :include-macros true]
4 | [animator.animator :refer [animator]]))
5 |
6 | (enable-console-print!)
7 |
8 |
9 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10 | ;; Define your own update function that draws something
11 | ;; into a canvas based on the elapsed time.
12 | ;; The animator knows nothing of its internals.
13 |
14 |
15 | ;; First, two helper functions:
16 |
17 | (defn draw-circle
18 | [context center radius line-width scale {:keys [r g b a]
19 | :or {a 1.0}}]
20 | (let [h (.-height (.-canvas context))
21 | center-x (* scale (:x center))
22 | center-y (- h (* scale (:y center)))
23 | radius (* scale radius)]
24 | (set! (. context -strokeStyle) (str "rgba(" r "," g "," b "," a ")"))
25 | (set! (. context -lineWidth) line-width)
26 | (.beginPath context)
27 | ;; x y radius startAngle endAngle counterClockwise?:
28 | (.arc context center-x center-y radius 0 (* 2 Math/PI) false)
29 | (.stroke context)))
30 |
31 | (defn alpha
32 | [elapsed-time delay duration]
33 | (* .001 (- (+ delay duration) elapsed-time)))
34 |
35 | ;; Here's the example update function.
36 | ;; elapsed-time and canvas are provided by the animator;
37 | ;; everything in the opts map must come from your animations:
38 |
39 | (defn fading-circle-update
40 | [elapsed-time canvas {:keys [center radius line-width scale color delay duration] :as opts}]
41 | (let [context (.getContext canvas "2d")
42 | a (alpha elapsed-time delay duration)]
43 | (when (>= elapsed-time delay)
44 | (.clearRect context 0 0 (.-width canvas) (.-height canvas))
45 | (draw-circle context center radius line-width scale (merge color {:a a})))))
46 | ;;
47 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
48 |
49 |
50 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
51 | ;; Define a vector of your own animations.
52 | ;; Each animation is a map containing an update function
53 | ;; and all the parameters that the update function needs.
54 | ;; Each animation will run in its own canvas element,
55 | ;; with the later ones in the vector rendering in front of the earlier ones.
56 |
57 | (defonce animations [{:update fading-circle-update
58 | :center {:x 400 :y 200}
59 | :radius 75
60 | :line-width 2
61 | :scale 1
62 | :color {:r 0 :g 255 :b 0}
63 | :delay 0
64 | :duration 2000}
65 |
66 | {:update fading-circle-update
67 | :center {:x 450 :y 250}
68 | :radius 50
69 | :line-width 2
70 | :scale 1
71 | :color {:r 255 :g 0 :b 0}
72 | :delay 250
73 | :duration 1250}
74 |
75 | {:update fading-circle-update
76 | :center {:x 200 :y 340}
77 | :radius 25
78 | :line-width 2
79 | :scale 1
80 | :color {:r 255 :g 255 :b 0}
81 | :delay 750
82 | :duration 1250}
83 |
84 | {:update fading-circle-update
85 | :center {:x 100 :y 100}
86 | :radius 50
87 | :line-width 2
88 | :scale 1
89 | :color {:r 255 :g 0 :b 0}
90 | :delay 1000
91 | :duration 1000}])
92 | ;;
93 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
94 |
95 |
96 | ;; Create the example component.
97 | ;; Its render method will build an animator instance
98 | ;; and pass it the required start-time parameter:
99 |
100 | (defn example
101 | [cursor owner opts]
102 | (reify
103 | om/IRender
104 | (render
105 | [_]
106 | (dom/div #js {:width "800px" :height "500px"}
107 |
108 | (om/build animator
109 | cursor
110 | {;; You must pass a start-time in the state map.
111 | ;; In this case we want the animation timeline
112 | ;; to begin right away, so we use the current time
113 | ;; as reported by the browser:
114 | :state {:start-time (.now (.-performance js/window))}
115 |
116 | ;; You may pass a clock-interval in milliseconds
117 | ;; in the opts map (default is 10):
118 | :opts {:clock-interval 20}})))))
119 |
120 |
121 | ;; I could have passed the animations vector as another member of
122 | ;; the opts map, and this example would work identically.
123 | ;; Passing it as app-state, as I do below,
124 | ;; lets me imagine user interactions modifying the animations
125 | ;; and resetting the start-time to run them again...
126 |
127 | (om/root example
128 | (atom animations)
129 | {:target (. js/document (getElementById "anim"))})
130 |
131 |
--------------------------------------------------------------------------------
/recipes/auto-complete/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/auto-complete/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/auto-complete.jar clojure.main -m auto-complete.server
2 |
--------------------------------------------------------------------------------
/recipes/auto-complete/README.md:
--------------------------------------------------------------------------------
1 | # Input Auto-Complete
2 |
3 | 
4 |
5 | 1) Create a new Om project using Chestnut
6 |
7 | ```bash
8 | lein new chestnut auto-complete -- --om-tools --http-kit
9 | cd auto-complete
10 | ```
11 |
12 | 2) Use your editor of choice to open the file `src/cljs/auto_complete/core.cljs`
13 |
14 |
15 | 3) For this demo keep namespace as is.
16 |
17 |
18 | 4) Replace `app-state` with the following:
19 |
20 | ```clojure
21 | (defonce app-state
22 | (atom {:user-input ""
23 | :things ["Apple"
24 | "ant"
25 | "bird"
26 | "CAR, Audi"
27 | "crayon"
28 | "cooler"
29 | "DVD"
30 | "Dirt"
31 | "DOG, Beagle"]}))
32 | ```
33 |
34 |
35 | 5) Create function for handling auto-complete logic.
36 |
37 | ```clojure
38 | (defn partial-complete
39 | "Takes a needle and looks for it in the haystack.
40 | Return all matches in a set or nil on empty match set.
41 | Empty needle causes empty match set.
42 | The needle will match any part of the string."
43 | [needle haystack]
44 | (when-not (empty? needle)
45 | (let [pattern (js/RegExp. (str ".* " needle ".*|" needle ".*") "i")]
46 | (not-empty (set (keep #(re-matches pattern %) haystack))))))
47 | ```
48 |
49 |
50 | 6) Create a view that will display the results of the completion function.
51 |
52 | ```clojure
53 | (defcomponent partial-complete-view [app _]
54 | (render [_]))
55 | ```
56 |
57 |
58 | 7) Add the completion function to the view.
59 |
60 | ```clojure
61 | (defcomponent partial-complete-view [app _]
62 | (render
63 | [_]
64 | (dom/div
65 | (if-let [completions (partial-complete (:user-input app) (:things app))]
66 | (for [thing completions]
67 | (dom/div {} thing))
68 | "No Completions."))))
69 | ```
70 |
71 |
72 | 8) Add user input field and apply onChange JavaScript event.
73 | This event should update the application state with `om/update!`.
74 |
75 | ```clojure
76 | (defcomponent user-input [app _]
77 | (render
78 | [_]
79 | (dom/input {:value (:user-input app)
80 | :on-change #(om/update! app :user-input (.. % -target -value))})))
81 | ```
82 |
83 |
84 | 9) Make a component view that places the user input view above the partial completion view.
85 |
86 | ```clojure
87 | (defcomponent auto-complete-view [app _]
88 | (render
89 | [_]
90 | (dom/div
91 | (om/build user-input app)
92 | (dom/br {})
93 | (om/build partial-complete-view app))))
94 | ```
95 |
96 |
97 | 10) Replace or alter your main function to display the validation box view.
98 |
99 | ```clojure
100 | (defn main []
101 | (om/root
102 | auto-complete-view
103 | app-state
104 | {:target (. js/document (getElementById "app"))}))
105 | ```
106 |
107 |
108 | 11) Start a REPL with `lein repl`
109 |
110 | ```
111 | nREPL server started on port 52193 on host 127.0.0.1 - nrepl://127.0.0.1:52193
112 | REPL-y 0.3.5, nREPL 0.2.6
113 | Clojure 1.6.0
114 | OpenJDK 64-Bit Server VM 1.8.0_31-b13
115 | Docs: (doc function-name-here)
116 | (find-doc "part-of-name-here")
117 | Source: (source function-name-here)
118 | Javadoc: (javadoc java-object-or-class-here)
119 | Exit: Control+D or (exit) or (quit)
120 | Results: Stored in vars *1, *2, *3, an exception in *e
121 | ```
122 |
123 | 12) Call `run` to start the back end and compile your ClojureScript.
124 |
125 | ```
126 | auto-complete.server=> (run)
127 | Figwheel: Starting server at http://localhost:3449
128 | Figwheel: Serving files from '(resources)/public'
129 | Starting web server on port 10555 .
130 | #
131 | auto-complete.server=> Compiling "resources/public/js/app.js" from ["env/dev/cljs" "src/cljs"]...
132 | WARNING: No such namespace: auto-complete.core at line 1 env/dev/cljs/auto_complete/main.cljs
133 | Successfully compiled "resources/public/js/app.js" in 20.965 seconds.
134 | notifying browser that file changed: /js/app.js
135 | notifying browser that file changed: /js/out/goog/deps.js
136 | notifying browser that file changed: /js/out/auto_complete/core.js
137 | notifying browser that file changed: /js/out/auto_complete/main.js
138 | ```
139 |
140 | 13) Point your browser to http://localhost:port. You can find the port in the REPL message output => `Starting web server on port ...`
141 |
--------------------------------------------------------------------------------
/recipes/auto-complete/env/dev/clj/auto_complete/dev.clj:
--------------------------------------------------------------------------------
1 | (ns auto-complete.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [figwheel-sidecar.auto-builder :as fig-auto]
7 | [figwheel-sidecar.core :as fig]
8 | [clojurescript-build.auto :as auto]
9 | [clojure.java.shell :refer [sh]]))
10 |
11 | (def is-dev? (env :is-dev))
12 |
13 | (def inject-devmode-html
14 | (comp
15 | (set-attr :class "is-dev")
16 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
17 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
18 | (append (html [:script {:type "text/javascript"} "goog.require('auto_complete.main')"]))))
19 |
20 | (defn browser-repl []
21 | (let [repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)]
22 | (piggieback/cljs-repl :repl-env repl-env)
23 | (piggieback/cljs-eval repl-env '(in-ns 'auto-complete.core) {})))
24 |
25 | (defn start-figwheel []
26 | (let [server (fig/start-server { :css-dirs ["resources/public/css"] })
27 | config {:builds [{:id "dev"
28 | :source-paths ["env/dev/cljs" "src/cljs"]
29 | :compiler {:output-to "resources/public/js/app.js"
30 | :output-dir "resources/public/js/out"
31 | :source-map "resources/public/js/out.js.map"
32 | :source-map-timestamp true
33 | :preamble ["react/react.min.js"]}}]
34 | :figwheel-server server}]
35 | (fig-auto/autobuild* config)))
36 |
--------------------------------------------------------------------------------
/recipes/auto-complete/env/dev/cljs/auto_complete/main.cljs:
--------------------------------------------------------------------------------
1 | (ns auto-complete.main
2 | (:require [auto-complete.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn []
12 | (core/main)))
13 |
14 | (weasel/connect "ws://localhost:9001" :verbose true :print #{:repl :console})
15 |
16 | (core/main)
17 |
--------------------------------------------------------------------------------
/recipes/auto-complete/env/prod/clj/auto_complete/dev.clj:
--------------------------------------------------------------------------------
1 | (ns auto-complete.dev
2 | (:require [environ.core :refer [env]]))
3 |
4 | (if (env :is-dev)
5 | (throw (Exception. (str "Production environment code is being loaded while the dev environment is active. "
6 | "You likely have compiled class files lying around from an uberjar build. "
7 | "Remove the target/ directory and try again."))))
8 |
9 | (def is-dev? false)
10 | (def inject-devmode-html identity)
11 | (defn browser-repl []
12 | (throw (Exception. "Browser connected REPL is not available in prod mode")))
13 | (defn start-figwheel []
14 | (throw (Exception. "Figwheel is not available in prod mode")))
15 |
--------------------------------------------------------------------------------
/recipes/auto-complete/env/prod/cljs/auto_complete/main.cljs:
--------------------------------------------------------------------------------
1 | (ns auto-complete.main
2 | (:require [auto-complete.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/auto-complete/env/test/js/polyfill.js:
--------------------------------------------------------------------------------
1 | if (!Function.prototype.bind) {
2 | Function.prototype.bind = function(oThis) {
3 | if (typeof this !== 'function') {
4 | // closest thing possible to the ECMAScript 5
5 | // internal IsCallable function
6 | throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
7 | }
8 |
9 | var aArgs = Array.prototype.slice.call(arguments, 1),
10 | fToBind = this,
11 | fNOP = function() {},
12 | fBound = function() {
13 | return fToBind.apply(this instanceof fNOP && oThis
14 | ? this
15 | : oThis,
16 | aArgs.concat(Array.prototype.slice.call(arguments)));
17 | };
18 |
19 | fNOP.prototype = this.prototype;
20 | fBound.prototype = new fNOP();
21 |
22 | return fBound;
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/recipes/auto-complete/env/test/js/unit-test.js:
--------------------------------------------------------------------------------
1 | var page = require('webpage').create();
2 | var url = phantom.args[0];
3 |
4 | page.onConsoleMessage = function (message) {
5 | console.log(message);
6 | };
7 |
8 | function exit(code) {
9 | setTimeout(function(){ phantom.exit(code); }, 0);
10 | phantom.onError = function(){};
11 | }
12 |
13 | console.log("Loading URL: " + url);
14 |
15 | page.open(url, function (status) {
16 | if (status != "success") {
17 | console.log('Failed to open ' + url);
18 | phantom.exit(1);
19 | }
20 |
21 | console.log("Running test.");
22 |
23 | var result = page.evaluate(function() {
24 | return auto_complete.test_runner.runner();
25 | });
26 |
27 | if (result != 0) {
28 | console.log("*** Test failed! ***");
29 | exit(1);
30 | }
31 | else {
32 | console.log("Test succeeded.");
33 | exit(0);
34 | }
35 |
36 | });
37 |
--------------------------------------------------------------------------------
/recipes/auto-complete/env/test/unit-test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/recipes/auto-complete/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/auto-complete/preview.gif
--------------------------------------------------------------------------------
/recipes/auto-complete/project.clj:
--------------------------------------------------------------------------------
1 | (defproject auto-complete "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj"]
8 | :repl-options {:timeout 200000} ;; Defaults to 30000 (30 seconds)
9 |
10 | :test-paths ["spec/clj"]
11 |
12 | :dependencies [[org.clojure/clojure "1.6.0"]
13 | [org.clojure/clojurescript "0.0-2511" :scope "provided"]
14 | [ring "1.3.2"]
15 | [ring/ring-defaults "0.1.3"]
16 | [compojure "1.3.1"]
17 | [enlive "1.1.5"]
18 | [om "0.8.0-rc1"]
19 | [environ "1.0.0"]
20 | [http-kit "2.1.19"]
21 | [prismatic/om-tools "0.3.10"]]
22 |
23 | :plugins [[lein-cljsbuild "1.0.3"]
24 | [lein-environ "1.0.0"]]
25 |
26 | :min-lein-version "2.5.0"
27 |
28 | :uberjar-name "auto-complete.jar"
29 |
30 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
31 | :compiler {:output-to "resources/public/js/app.js"
32 | :output-dir "resources/public/js/out"
33 | :source-map "resources/public/js/out.js.map"
34 | :preamble ["react/react.min.js"]
35 | :optimizations :none
36 | :pretty-print true}}}}
37 |
38 | :profiles {:dev {:source-paths ["env/dev/clj"]
39 | :test-paths ["test/clj"]
40 |
41 | :dependencies [[figwheel "0.2.1-SNAPSHOT"]
42 | [figwheel-sidecar "0.2.1-SNAPSHOT"]
43 | [com.cemerick/piggieback "0.1.3"]
44 | [weasel "0.4.2"]]
45 |
46 | :repl-options {:init-ns auto-complete.server
47 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
48 |
49 | :plugins [[lein-figwheel "0.2.1-SNAPSHOT"]]
50 |
51 | :figwheel {:http-server-root "public"
52 | :server-port 3449
53 | :css-dirs ["resources/public/css"]}
54 |
55 | :env {:is-dev true}
56 |
57 | :cljsbuild {:test-commands { "test" ["phantomjs" "env/test/js/unit-test.js" "env/test/unit-test.html"] }
58 | :builds {:app {:source-paths ["env/dev/cljs"]}
59 | :test {:source-paths ["src/cljs" "test/cljs"]
60 | :compiler {:output-to "resources/public/js/app_test.js"
61 | :output-dir "resources/public/js/test"
62 | :source-map "resources/public/js/test.js.map"
63 | :preamble ["react/react.min.js"]
64 | :optimizations :whitespace
65 | :pretty-print false}}}}}
66 |
67 | :uberjar {:source-paths ["env/prod/clj"]
68 | :hooks [leiningen.cljsbuild]
69 | :env {:production true}
70 | :omit-source true
71 | :aot :all
72 | :cljsbuild {:builds {:app
73 | {:source-paths ["env/prod/cljs"]
74 | :compiler
75 | {:optimizations :advanced
76 | :pretty-print false}}}}}})
77 |
--------------------------------------------------------------------------------
/recipes/auto-complete/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/auto-complete/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/auto-complete/src/clj/auto_complete/dev.clj:
--------------------------------------------------------------------------------
1 | (ns auto-complete.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('auto_complete.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/auto-complete/src/clj/auto_complete/server.clj:
--------------------------------------------------------------------------------
1 | (ns auto-complete.server
2 | (:require [clojure.java.io :as io]
3 | [auto-complete.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [net.cgrand.enlive-html :refer [deftemplate]]
7 | [net.cgrand.reload :refer [auto-reload]]
8 | [ring.middleware.reload :as reload]
9 | [ring.middleware.defaults :refer [wrap-defaults api-defaults]]
10 | [environ.core :refer [env]]
11 | [org.httpkit.server :refer [run-server]]))
12 |
13 | (deftemplate page (io/resource "index.html") []
14 | [:body] (if is-dev? inject-devmode-html identity))
15 |
16 | (defroutes routes
17 | (resources "/")
18 | (resources "/react" {:root "react"})
19 | (GET "/*" req (page)))
20 |
21 | (def http-handler
22 | (if is-dev?
23 | (reload/wrap-reload (wrap-defaults #'routes api-defaults))
24 | (wrap-defaults routes api-defaults)))
25 |
26 | (defn run-web-server [& [port]]
27 | (let [port (Integer. (or port (env :port) 10555))]
28 | (print "Starting web server on port" port ".\n")
29 | (run-server http-handler {:port port :join? false})))
30 |
31 | (defn run-auto-reload [& [port]]
32 | (auto-reload *ns*)
33 | (start-figwheel))
34 |
35 | (defn run [& [port]]
36 | (when is-dev?
37 | (run-auto-reload))
38 | (run-web-server port))
39 |
40 | (defn -main [& [port]]
41 | (run port))
42 |
--------------------------------------------------------------------------------
/recipes/auto-complete/src/cljs/auto_complete/core.cljs:
--------------------------------------------------------------------------------
1 | (ns auto-complete.core
2 | (:require [om.core :as om :include-macros true]
3 | [om-tools.dom :as dom :include-macros true]
4 | [om-tools.core :refer-macros [defcomponent]]))
5 |
6 | (defonce app-state
7 | (atom {:user-input ""
8 | :things ["Apple"
9 | "ant"
10 | "bird"
11 | "CAR, Audi"
12 | "crayon"
13 | "cooler"
14 | "DVD"
15 | "Dirt"
16 | "DOG, Beagle"]}))
17 |
18 | (defn partial-complete
19 | "Takes a needle and looks for it in the haystack.
20 | Return all matches in a set or nil on empty match set.
21 | Empty needle causes empty match set."
22 | [needle haystack]
23 | (when-not (empty? needle)
24 | (let [pattern (js/RegExp. (str ".* " needle ".*|" needle ".*") "i")]
25 | (not-empty (set (keep #(re-matches pattern %) haystack))))))
26 |
27 | (defcomponent partial-complete-view [app _]
28 | (render
29 | [_]
30 | (dom/div
31 | (if-let [completions (partial-complete (:user-input app) (:things app))]
32 | (for [thing completions]
33 | (dom/div {} thing))
34 | "No Completions."))))
35 |
36 |
37 | (defcomponent user-input [app owner]
38 | (render
39 | [_]
40 | (dom/input {:value (:user-input app)
41 | :on-change #(om/update! app :user-input (.. % -target -value))})))
42 |
43 | (defcomponent auto-complete-view [app _]
44 | (render
45 | [_]
46 | (dom/div
47 | (om/build user-input app)
48 | (dom/br {})
49 | (om/build partial-complete-view app))))
50 |
51 | (defn main []
52 | (om/root
53 | auto-complete-view
54 | app-state
55 | {:target (. js/document (getElementById "app"))}))
56 |
--------------------------------------------------------------------------------
/recipes/auto-complete/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/boot-setup/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /.boot
3 | /classes
4 | /checkouts
5 | pom.xml
6 | pom.xml.asc
7 | *.jar
8 | *.class
9 | /.lein-*
10 | /.nrepl-port
11 | /.nrepl-history
12 | /resources/public/js
13 | /out
14 | /.repl
15 |
--------------------------------------------------------------------------------
/recipes/boot-setup/README.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | You want to start a new om project with boot-cljs but don't know how.
4 |
5 | # Solution
6 |
7 | Read [introductory blog post][blog-post] on boot-cljs.
8 |
9 | ## Prepare
10 |
11 | [Install boot][installboot]. Then, in a terminal:
12 |
13 | ```bash
14 | boot -u
15 | ```
16 |
17 | This will update boot to the latest stable release version.
18 |
19 | ## Use
20 |
21 | To serve the project at http://localhost:3000, watch the sources,
22 | build and reload your project, run:
23 |
24 | ```bash
25 | boot dev
26 | ```
27 |
28 | The `dev` task as well as project dependencies are defined in
29 | [build.boot](./build.boot)-file. The `dev` task uses multiple other boot tasks:
30 |
31 | - [boot-cljs] to compile Cljs sources
32 | - [boot-cljs-repl] to provide nrepl server with Cljs middlewares
33 | - [boot-http] to serve http and js files
34 | - [boot-reload] to reload changed files
35 |
36 | You should read the `build.boot` to see how the tasks are combined. The task
37 | options should be documented on the file.
38 |
39 | Now open http://localhost:3000 and try changing something in core.cljs!
40 | To use cljs-repl in another terminal do:
41 |
42 | ```
43 | boot repl --client
44 | boot.user=> (start-repl)
45 | ; Refresh the page
46 | boot.user=> (js/console.log "Hello!")
47 | boot.user=> (in-ns 'boot-setup.core)
48 | boot-setup.core=> (swap! app-state assoc :cong "Profit!")
49 | boot-setup.core=> (another-root)
50 | boot-setup.core=> (main)
51 | ```
52 |
53 | ## More resources
54 |
55 | - More example projects
56 | - [boot-cljs-example]
57 | - [Saapas][saapas]
58 | - Best places to ask for help are `#hoplon` on Freenode IRC and `#boot` on
59 | [Clojurians Slack][clojurians]
60 |
61 | [installboot]: https://github.com/boot-clj/boot#install
62 | [blog-post]: http://adzerk.com/blog/2014/11/clojurescript-builds-rebooted/
63 | [boot]: https://github.com/boot-clj/boot
64 | [boot-cljs-example]: https://github.com/adzerk/boot-cljs-example
65 | [boot-cljs]: https://github.com/adzerk-oss/boot-cljs
66 | [boot-cljs-repl]: https://github.com/adzerk-oss/boot-cljs-repl
67 | [boot-reload]: https://github.com/adzerk-oss/boot-reload
68 | [boot-http]: https://github.com/pandeiro/boot-http
69 | [saapas]: https://github.com/Deraen/saapas
70 | [clojurians]: https://clojurians.slack.com/
71 |
--------------------------------------------------------------------------------
/recipes/boot-setup/boot.properties:
--------------------------------------------------------------------------------
1 | #https://github.com/boot-clj/boot
2 | #Mon Jul 13 11:46:35 EEST 2015
3 | BOOT_CLOJURE_VERSION=1.7.0
4 | BOOT_VERSION=2.1.2
5 |
--------------------------------------------------------------------------------
/recipes/boot-setup/build.boot:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env boot
2 |
3 | (set-env!
4 | :source-paths #{"src"}
5 | :resource-paths #{"resources"}
6 | :dependencies '[[org.clojure/clojure "1.7.0"]
7 | [org.clojure/clojurescript "0.0-3308"]
8 | [adzerk/boot-cljs "0.0-3308-0" :scope "test"]
9 | [adzerk/boot-cljs-repl "0.1.10-SNAPSHOT" :scope "test"]
10 | [adzerk/boot-reload "0.3.1" :scope "test"]
11 | [pandeiro/boot-http "0.6.2" :scope "test"]
12 | [org.omcljs/om "0.8.8"]])
13 |
14 | (task-options!
15 | pom {:project 'boot-setup
16 | :version "0.1.0-SNAPSHOT"})
17 |
18 | (require '[adzerk.boot-cljs :refer [cljs]]
19 | '[adzerk.boot-cljs-repl :refer [cljs-repl start-repl]]
20 | '[adzerk.boot-reload :refer [reload]]
21 | '[pandeiro.boot-http :refer [serve]])
22 |
23 | (deftask dev
24 | "Development environment"
25 | []
26 | ; Check `boot --help` for more documentation on the task options.
27 | (comp
28 | ; Serve the files directly from classpath. The files to serve located
29 | ; inside public/ prefix. This way e.g. the source code or other things
30 | ; in classpath are not available through http.
31 | (serve :resource-root "public")
32 | (watch)
33 | ; Starts a nrepl server with cljs middlewares.
34 | (cljs-repl)
35 | ; The interesting files in fileset/classpath and inside public prefix.
36 | ; The asset-path option removes given path from urls being reloaded,
37 | ; so when "public/index.html" changes, browser is told to reload
38 | ; "index.html".
39 | (reload :asset-path "public")
40 | ; Compiles the cljs. This searches for *.cljs.edn files and finds
41 | ; src/public/main.cljs.edn, from the relative path of the file inside
42 | ; fileset (public/main.cljs.edn) it sets the output-to and output-path
43 | ; for cljs compiler, and thus the resulting js is written to
44 | ; "public/main.js" and the rest of files to "public/out/".
45 | ; To make main.js refer to other files without public/ prefix, asset-path
46 | ; option is set manually.
47 | (cljs :source-map true :compiler-options {:asset-path "out"})))
48 |
--------------------------------------------------------------------------------
/recipes/boot-setup/resources/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | my first boot-setup
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/recipes/boot-setup/src/boot_setup/core.cljs:
--------------------------------------------------------------------------------
1 | (ns boot-setup.core
2 | (:require [om.core :as om]
3 | [om.dom :as dom]))
4 |
5 | (def app-state (atom {:cong "Very much congratulated!"}))
6 |
7 | (defn main
8 | []
9 | (om/root
10 | (fn [app owner]
11 | (reify
12 | om/IRender
13 | (render [_]
14 | (dom/div nil
15 | (dom/h1 nil "Yay!")
16 | (dom/p nil "I did it!!!")
17 | (dom/p nil (:cong app))))))
18 | app-state
19 | {:target js/document.body}))
20 |
21 | (defn another-root
22 | []
23 | (om/root
24 | (fn [app owner]
25 | (reify
26 | om/IRender
27 | (render [_]
28 | (dom/div nil
29 | (dom/h1 nil "Hello from prototype!")))))
30 | app-state
31 | {:target js/document.body}))
32 |
33 | (set! (.-onload js/window) main)
34 |
--------------------------------------------------------------------------------
/recipes/boot-setup/src/public/main.cljs.edn:
--------------------------------------------------------------------------------
1 | {:require [boot-setup.core]
2 | :init-fns [boot-setup.core/main]}
3 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/datascript-query-browser.jar clojure.main -m datascript-query-browser.server
2 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/README.md:
--------------------------------------------------------------------------------
1 | # DataScript Query Browser
2 |
3 | 
4 |
5 | 1) Create a new Om project using Chestnut
6 |
7 | ```bash
8 | lein new chestnut datascript-query-browser -- --om-tools --http-kit
9 | ```
10 |
11 | 2) Use your editor of choice to open the file `datascript-query-browser/src/cljs/core.cljs`
12 |
13 | 3) Your initial app-state should look like this.
14 |
15 | ```clojure
16 | (ns datascript-query-browser.core
17 | (:require [om.core :as om :include-macros true]
18 | [om-tools.dom :as dom :include-macros true]
19 | [om-tools.core :refer-macros [defcomponent]]
20 | [datascript :as d]))
21 |
22 | (def conn (d/create-conn {}))
23 |
24 | (defonce app-state
25 | (atom
26 | {:viewing-query 0
27 | :queries []
28 | :query-results []}))
29 | ```
30 |
31 | 4) This demo application requires dummy data called fake-people.
32 |
33 | ```clojure
34 | (def fake-people
35 | [{:db/id -1 :name/last "Larson" :name/first "Andrew" :amount 10242.94 }
36 | {:db/id -1 :name/last "Richardson" :name/first "Sarah" :amount 26327.99 }
37 | {:db/id -1 :name/last "Rice" :name/first "Emily" :amount 35799.79 }
38 | {:db/id -1 :name/last "Williams" :name/first "Cheryl" :amount 42043.31 }
39 | {:db/id -1 :name/last "Boyd" :name/first "Alan" :amount 18659.8 }
40 | {:db/id -1 :name/last "Ryan" :name/first "Donna" :amount 24686.99 }
41 | {:db/id -1 :name/last "Hayes" :name/first "Jane" :amount 38395.31 }
42 | {:db/id -1 :name/last "Lynch" :name/first "Kathleen" :amount 40031.52 }
43 | {:db/id -1 :name/last "White" :name/first "Jane" :amount 16953.81 }
44 | {:db/id -1 :name/last "Morgan" :name/first "Joyce" :amount 25542.66 }
45 | {:db/id -1 :name/last "Oliver" :name/first "Susan" :amount 35623.38 }
46 | {:db/id -1 :name/last "Bradley" :name/first "Jacqueline" :amount 4191.39 }
47 | {:db/id -1 :name/last "Gardner" :name/first "Steve" :amount 58599.19 }
48 | {:db/id -1 :name/last "Clark" :name/first "Dorothy" :amount 59395.47 }
49 | {:db/id -1 :name/last "Roberts" :name/first "Terry" :amount 55777.04 }
50 | {:db/id -1 :name/last "Dunn" :name/first "Jeremy" :amount 26784.03 }
51 | {:db/id -1 :name/last "Little" :name/first "Robin" :amount 13619.52 }
52 | {:db/id -1 :name/last "Fowler" :name/first "Harry" :amount 21089.65 }
53 | {:db/id -1 :name/last "Mitchell" :name/first "David" :amount 18071.86 }
54 | {:db/id -1 :name/last "Olson" :name/first "Jonathan" :amount 23951.22 }
55 | {:db/id -1 :name/last "Hunter" :name/first "Nicholas" :amount 35798.73 }
56 | {:db/id -1 :name/last "Lawson" :name/first "Norma" :amount 35146.94 }
57 | {:db/id -1 :name/last "Allen" :name/first "William" :amount 6512.05 }])
58 | ```
59 | 5) Create a function to add the fake people to the db once the main function is loaded.
60 |
61 | ```clojure
62 | (defn add-fake-people! []
63 | (doseq [person fake-people]
64 | (d/transact! conn [person])))
65 | ```
66 |
67 | 6) The same can be done to make dummy queries.
68 |
69 | ```clojure
70 | (defn query-amount-minimum [n]
71 | (into '[:find ?a ?ln ?fn
72 | :where
73 | [?e :amount ?a]
74 | [?e :name/last ?ln]
75 | [?e :name/first ?fn]]
76 | [[(list '<= n '?a)]]))
77 |
78 | (def queries
79 | [{:title "Over 10k" :query (query-amount-minimum 10000)}
80 | {:title "Over 20k" :query (query-amount-minimum 20000)}
81 | {:title "Over 30k" :query (query-amount-minimum 30000)}
82 | {:title "Over 40k" :query (query-amount-minimum 40000)}
83 | {:title "Over 50k" :query (query-amount-minimum 50000)}])
84 |
85 | (defn add-queries! [cursor]
86 | (om/update! cursor [:queries] queries))
87 | ```
88 | 7) Create a function to load both the dummy data and queries.
89 |
90 | ```clojure
91 | (defn add-demo-data! [cursor]
92 | (add-fake-people!)
93 | (add-queries! cursor))
94 | ```
95 |
96 | 8) Create a function that can cycle through all available queries.
97 |
98 | ```clojure
99 | (defn next-query! [cursor]
100 | (let [vqk :viewing-query
101 | qk :queries
102 | state cursor
103 | qc (-> state qk count dec)
104 | vq (-> state vqk inc)
105 | vq (if (> vq qc) 0 vq)]
106 | (om/update! cursor [vqk] vq)))
107 | ```
108 |
109 | 9) Create a function that can fetch the query results.
110 |
111 | ```clojure
112 | (defn query-results! [cursor]
113 | (let [{:keys [queries viewing-query]} cursor
114 | current-query (nth queries viewing-query)
115 | {:keys [query]} current-query
116 | results (d/q query @conn)]
117 | (om/update! cursor [:query-results] results)))
118 | ```
119 |
120 | 10) Create a query view component.
121 |
122 | ```clojure
123 | (defcomponent query-view [cursor _]
124 | (will-mount
125 | [_]
126 | (add-demo-data! cursor)
127 | (query-results! cursor))
128 | (render
129 | [_]
130 | (let [{:keys [queries viewing-query]} cursor
131 | current-query (nth queries viewing-query)
132 | {:keys [title query]} current-query]
133 | (dom/div
134 | (dom/h1 "Query Viewer")
135 | (dom/h3 title)
136 | (dom/div {:style {:font-size "16px"
137 | :background-color "hsl(150,80%,95%)"
138 | :width "400px"}}
139 | (dom/pre {:style {:white-space "pre-wrap"
140 | :padding "20px"}}
141 | (let [r clojure.string/replace]
142 | (-> query
143 | pr-str
144 | (r ":find" "\n:find")
145 | (r ":in" "\n:in")
146 | (r ":where" "\n:where\t")
147 | (r "]" "]\n\t"))))
148 | (dom/button {:on-click (fn [_]
149 | (next-query! cursor)
150 | (query-results! cursor))}
151 | "Next Query"))))))
152 |
153 | ```
154 |
155 | 11) Create a query results component.
156 |
157 | ```clojure
158 | (defcomponent query-results-view [cursor _]
159 | (render
160 | [_]
161 | (dom/ul
162 | (for [qr (:query-results cursor)]
163 | (dom/li
164 | (dom/table
165 | (dom/tr
166 | (for [x qr]
167 | (dom/td {:style {:padding "10px;"}} x)))))))))
168 | ```
169 |
170 | 12) Create a main view component.
171 |
172 | ```clojure
173 | (defcomponent main-view [app _]
174 | (render
175 | [_]
176 | (dom/div
177 | (om/build query-view app)
178 | (om/build query-results-view app))))
179 | ```
180 |
181 | 13) Alter or replace your main function to display the main view component.
182 |
183 | ```clojure
184 | (defn main []
185 | (om/root
186 | main-view
187 | app-state
188 | {:target (. js/document (getElementById "app"))}))
189 | ```
190 |
191 | 14) Start a REPL with `lein repl`
192 |
193 | ```
194 | nREPL server started on port 54879 on host 127.0.0.1 - nrepl://127.0.0.1:54879
195 | REPL-y 0.3.5, nREPL 0.2.6
196 | Clojure 1.6.0
197 | Java HotSpot(TM) 64-Bit Server VM 1.8.0_05-b13
198 | Docs: (doc function-name-here)
199 | (find-doc "part-of-name-here")
200 | Source: (source function-name-here)
201 | Javadoc: (javadoc java-object-or-class-here)
202 | Exit: Control+D or (exit) or (quit)
203 | Results: Stored in vars *1, *2, *3, an exception in *e
204 | ```
205 |
206 | 15) Call `run` to start the back end and compile your ClojureScript.
207 |
208 | ```
209 | datascript-query-browser.server=> (run)
210 | Starting figwheel.
211 | Starting web server on port 10555 .
212 | #
213 | datascript-query-browser.server=> Compiling ClojureScript.
214 | Figwheel: Starting server at http://localhost:3449
215 | Figwheel: Serving files from '(dev-resources|resources)/public'
216 | Compiling "resources/public/js/app.js" from ("src/cljs" "env/dev/cljs")...
217 | Successfully compiled "resources/public/js/app.js" in 18.01 seconds.
218 | notifying browser that file changed: /js/out/local_state/core.js
219 | ```
220 |
221 | 16) Point your browser to http://localhost:port. You can find the port in the REPL message output => `Starting web server on port ...`
222 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/env/dev/cljs/datascript_query_browser/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns datascript-query-browser.dev
2 | (:require [datascript-query-browser.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/env/prod/cljs/datascript_query_browser/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns datascript-query-browser.prod
2 | (:require [datascript-query-browser.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/datascript-query-browser/preview.gif
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/project.clj:
--------------------------------------------------------------------------------
1 | (defproject datascript-query-browser "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [ring "1.3.1"]
12 | [compojure "1.2.0"]
13 | [enlive "1.1.5"]
14 | [om "0.7.3"]
15 | [figwheel "0.1.4-SNAPSHOT"]
16 | [environ "1.0.0"]
17 | [com.cemerick/piggieback "0.1.3"]
18 | [weasel "0.4.0-SNAPSHOT"]
19 | [leiningen "2.5.0"]
20 | [http-kit "2.1.19"]
21 | [prismatic/om-tools "0.3.3"]
22 | [datascript "0.6.0"]]
23 |
24 | :plugins [[lein-cljsbuild "1.0.3"]
25 | [lein-environ "1.0.0"]]
26 |
27 | :min-lein-version "2.5.0"
28 |
29 | :uberjar-name "datascript-query-browser.jar"
30 |
31 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
32 | :compiler {:output-to "resources/public/js/app.js"
33 | :output-dir "resources/public/js/out"
34 | :source-map "resources/public/js/out.js.map"
35 | :preamble ["react/react.min.js"]
36 | :externs ["react/externs/react.js"]
37 | :optimizations :none
38 | :pretty-print true}}}}
39 |
40 | :profiles {:dev {:repl-options {:init-ns datascript-query-browser.server
41 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
42 |
43 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
44 |
45 | :figwheel {:http-server-root "public"
46 | :port 3449
47 | :css-dirs ["resources/public/css"]}
48 |
49 | :env {:is-dev true}
50 |
51 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
52 |
53 | :uberjar {:hooks [leiningen.cljsbuild]
54 | :env {:production true}
55 | :omit-source true
56 | :aot :all
57 | :cljsbuild {:builds {:app
58 | {:source-paths ["env/prod/cljs"]
59 | :compiler
60 | {:optimizations :advanced
61 | :pretty-print false}}}}}})
62 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/src/clj/datascript_query_browser/dev.clj:
--------------------------------------------------------------------------------
1 | (ns datascript-query-browser.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('datascript_query_browser.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/src/clj/datascript_query_browser/server.clj:
--------------------------------------------------------------------------------
1 | (ns datascript-query-browser.server
2 | (:require [clojure.java.io :as io]
3 | [datascript-query-browser.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [org.httpkit.server :refer [run-server]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-server http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/src/cljs/datascript_query_browser/core.cljs:
--------------------------------------------------------------------------------
1 | (ns datascript-query-browser.core
2 | (:require [om.core :as om :include-macros true]
3 | [om-tools.dom :as dom :include-macros true]
4 | [om-tools.core :refer-macros [defcomponent]]
5 | [datascript :as d]))
6 |
7 | (def conn (d/create-conn {}))
8 |
9 | (defonce app-state
10 | (atom
11 | {:viewing-query 0
12 | :queries []
13 | :query-results []}))
14 |
15 | (def fake-people
16 | [{:db/id -1 :name/last "Larson" :name/first "Andrew" :amount 10242.94 }
17 | {:db/id -1 :name/last "Richardson" :name/first "Sarah" :amount 26327.99 }
18 | {:db/id -1 :name/last "Rice" :name/first "Emily" :amount 35799.79 }
19 | {:db/id -1 :name/last "Williams" :name/first "Cheryl" :amount 42043.31 }
20 | {:db/id -1 :name/last "Boyd" :name/first "Alan" :amount 18659.8 }
21 | {:db/id -1 :name/last "Ryan" :name/first "Donna" :amount 24686.99 }
22 | {:db/id -1 :name/last "Hayes" :name/first "Jane" :amount 38395.31 }
23 | {:db/id -1 :name/last "Lynch" :name/first "Kathleen" :amount 40031.52 }
24 | {:db/id -1 :name/last "White" :name/first "Jane" :amount 16953.81 }
25 | {:db/id -1 :name/last "Morgan" :name/first "Joyce" :amount 25542.66 }
26 | {:db/id -1 :name/last "Oliver" :name/first "Susan" :amount 35623.38 }
27 | {:db/id -1 :name/last "Bradley" :name/first "Jacqueline" :amount 4191.39 }
28 | {:db/id -1 :name/last "Gardner" :name/first "Steve" :amount 58599.19 }
29 | {:db/id -1 :name/last "Clark" :name/first "Dorothy" :amount 59395.47 }
30 | {:db/id -1 :name/last "Roberts" :name/first "Terry" :amount 55777.04 }
31 | {:db/id -1 :name/last "Dunn" :name/first "Jeremy" :amount 26784.03 }
32 | {:db/id -1 :name/last "Little" :name/first "Robin" :amount 13619.52 }
33 | {:db/id -1 :name/last "Fowler" :name/first "Harry" :amount 21089.65 }
34 | {:db/id -1 :name/last "Mitchell" :name/first "David" :amount 18071.86 }
35 | {:db/id -1 :name/last "Olson" :name/first "Jonathan" :amount 23951.22 }
36 | {:db/id -1 :name/last "Hunter" :name/first "Nicholas" :amount 35798.73 }
37 | {:db/id -1 :name/last "Lawson" :name/first "Norma" :amount 35146.94 }
38 | {:db/id -1 :name/last "Allen" :name/first "William" :amount 6512.05 }])
39 |
40 | (defn add-fake-people! []
41 | (doseq [person fake-people]
42 | (d/transact! conn [person])))
43 |
44 | (defn query-amount-minimum [n]
45 | (into '[:find ?a ?ln ?fn
46 | :where
47 | [?e :amount ?a]
48 | [?e :name/last ?ln]
49 | [?e :name/first ?fn]]
50 | [[(list '<= n '?a)]]))
51 |
52 | (def queries
53 | [{:title "Over 10k" :query (query-amount-minimum 10000)}
54 | {:title "Over 20k" :query (query-amount-minimum 20000)}
55 | {:title "Over 30k" :query (query-amount-minimum 30000)}
56 | {:title "Over 40k" :query (query-amount-minimum 40000)}
57 | {:title "Over 50k" :query (query-amount-minimum 50000)}])
58 |
59 | (defn add-queries! [cursor]
60 | (om/update! cursor [:queries] queries))
61 |
62 | (defn add-demo-data! [cursor]
63 | (add-fake-people!)
64 | (add-queries! cursor))
65 |
66 | (defn next-query! [cursor]
67 | (let [vqk :viewing-query
68 | qk :queries
69 | state cursor
70 | qc (-> state qk count dec)
71 | vq (-> state vqk inc)
72 | vq (if (> vq qc) 0 vq)]
73 | (om/update! cursor [vqk] vq)))
74 |
75 | (defn query-results! [cursor]
76 | (let [{:keys [queries viewing-query]} cursor
77 | current-query (nth queries viewing-query)
78 | {:keys [query]} current-query
79 | results (d/q query @conn)]
80 | (om/update! cursor [:query-results] results)))
81 |
82 | (defcomponent query-view [cursor _]
83 | (will-mount
84 | [_]
85 | (add-demo-data! cursor)
86 | (query-results! cursor))
87 | (render
88 | [_]
89 | (let [{:keys [queries viewing-query]} cursor
90 | current-query (nth queries viewing-query)
91 | {:keys [title query]} current-query]
92 | (dom/div
93 | (dom/h1 "Query Viewer")
94 | (dom/h3 title)
95 | (dom/div {:style {:font-size "16px"
96 | :background-color "hsl(150,80%,95%)"
97 | :width "400px"}}
98 | (dom/pre {:style {:white-space "pre-wrap"
99 | :padding "20px"}}
100 | (let [r clojure.string/replace]
101 | (-> query
102 | pr-str
103 | (r ":find" "\n:find")
104 | (r ":in" "\n:in")
105 | (r ":where" "\n:where\t")
106 | (r "]" "]\n\t"))))
107 | (dom/button {:on-click (fn [_]
108 | (next-query! cursor)
109 | (query-results! cursor))}
110 | "Next Query"))))))
111 |
112 |
113 | (defcomponent query-results-view [cursor _]
114 | (render
115 | [_]
116 | (dom/ul
117 | (for [qr (:query-results cursor)]
118 | (dom/li
119 | (dom/table
120 | (dom/tr
121 | (for [x qr]
122 | (dom/td {:style {:padding "10px;"}} x)))))))))
123 |
124 |
125 | (defcomponent main-view [app _]
126 | (render
127 | [_]
128 | (dom/div
129 | (om/build query-view app)
130 | (om/build query-results-view app))))
131 |
132 | (defn main []
133 | (om/root
134 | main-view
135 | app-state
136 | {:target (. js/document (getElementById "app"))}))
137 |
--------------------------------------------------------------------------------
/recipes/datascript-query-browser/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/dev-setup/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/dev-setup/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/dev-setup.jar clojure.main -m dev-setup.server
2 |
--------------------------------------------------------------------------------
/recipes/dev-setup/README.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | You'd like to start a new Om project that includes
4 | [Figwheel](https://github.com/bhauman/lein-figwheel) and
5 | REPL-connected browser
6 |
7 | # Solution
8 |
9 | Use [chestnut](https://github.com/plexus/chestnut) template:
10 |
11 | ```clojure
12 | $ lein new chestnut
13 | ```
14 |
15 | This creates project structure, adds needed dependencies to your
16 | `project.clj`. If you open up `README.md` that was created,
17 | you'll see step by step instructions of how to proceed further.
18 |
19 | But the gist is:
20 |
21 | 1. Start the REPL
22 | 2. Do:
23 |
24 | ```clojure
25 | (run)
26 | ```
27 | This will start the webserver at the port 10555 and Figweheel server
28 | at port 3449, as specified in your `project.clj`:
29 |
30 | ```clojure
31 | :figwheel {:http-server-root "public"
32 | :port 3449
33 | :css-dirs ["resources/public/css"]}
34 | ```
35 | Your files will compile to `public` directory, and this is where
36 | you should put your CSS files too. You can modify it as needed.
37 | First time you exectute `run` it will take some time. Wait until
38 | files are compiled and you see `Successfully compiled
39 | "resources/public/app.js"`.
40 | You can open up the index page by going to `http://localhost:10555`
41 | in your browser.
42 |
43 | 3. Run `(browser-repl)` to start the Weasel REPL server
44 | This will take you to ClojureScript REPL. Make sure you have the
45 | page opened in a browser. You can evaluate expressions here and
46 | see the resoluts in your browser.
47 |
48 | 4. Whenever you modify your CSS or cljs files, you'll see your page
49 | being updated instantly.
50 |
51 | 5. Go to [Chestnut documention](http://plexus.github.io/chestnut/) for
52 | more information.
53 |
--------------------------------------------------------------------------------
/recipes/dev-setup/env/dev/cljs/dev_setup/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns dev-setup.dev
2 | (:require [dev-setup.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/dev-setup/env/prod/cljs/dev_setup/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns dev-setup.prod
2 | (:require [dev-setup.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/dev-setup/project.clj:
--------------------------------------------------------------------------------
1 | (defproject dev-setup "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [ring "1.3.1"]
12 | [compojure "1.2.0"]
13 | [enlive "1.1.5"]
14 | [om "0.7.3"]
15 | [figwheel "0.1.4-SNAPSHOT"]
16 | [environ "1.0.0"]
17 | [com.cemerick/piggieback "0.1.3"]
18 | [weasel "0.4.0-SNAPSHOT"]
19 | [leiningen "2.5.0"]]
20 |
21 | :plugins [[lein-cljsbuild "1.0.3"]
22 | [lein-environ "1.0.0"]]
23 |
24 | :min-lein-version "2.5.0"
25 |
26 | :uberjar-name "dev-setup.jar"
27 |
28 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
29 | :compiler {:output-to "resources/public/js/app.js"
30 | :output-dir "resources/public/js/out"
31 | :source-map "resources/public/js/out.js.map"
32 | :preamble ["react/react.min.js"]
33 | :externs ["react/externs/react.js"]
34 | :optimizations :none
35 | :pretty-print true}}}}
36 |
37 | :profiles {:dev {:repl-options {:init-ns dev-setup.server
38 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
39 |
40 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
41 |
42 | :figwheel {:http-server-root "public"
43 | :port 3449
44 | :css-dirs ["resources/public/css"]}
45 |
46 | :env {:is-dev true}
47 |
48 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
49 |
50 | :uberjar {:hooks [leiningen.cljsbuild]
51 | :env {:production true}
52 | :omit-source true
53 | :aot :all
54 | :cljsbuild {:builds {:app
55 | {:source-paths ["env/prod/cljs"]
56 | :compiler
57 | {:optimizations :advanced
58 | :pretty-print false}}}}}})
59 |
--------------------------------------------------------------------------------
/recipes/dev-setup/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/dev-setup/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/dev-setup/src/clj/dev_setup/dev.clj:
--------------------------------------------------------------------------------
1 | (ns dev-setup.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('dev_setup.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/dev-setup/src/clj/dev_setup/server.clj:
--------------------------------------------------------------------------------
1 | (ns dev-setup.server
2 | (:require [clojure.java.io :as io]
3 | [dev-setup.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [ring.adapter.jetty :refer [run-jetty]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-jetty http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/dev-setup/src/cljs/dev_setup/core.cljs:
--------------------------------------------------------------------------------
1 | (ns dev-setup.core
2 | (:require [om.core :as om :include-macros true]
3 | [om.dom :as dom :include-macros true]))
4 |
5 | (defonce app-state (atom {:text "Hello Chestnut!"}))
6 |
7 | (defn main []
8 | (om/root
9 | (fn [app owner]
10 | (reify
11 | om/IRender
12 | (render [_]
13 | (dom/h1 nil (:text app)))))
14 | app-state
15 | {:target (. js/document (getElementById "app"))}))
16 |
--------------------------------------------------------------------------------
/recipes/dev-setup/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/dimple-bar-chart/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | *jar
3 | /lib/
4 | /classes/
5 | /out/
6 | /target/
7 | .lein-deps-sum
8 | .lein-repl-history
9 | .lein-plugins/
10 |
--------------------------------------------------------------------------------
/recipes/dimple-bar-chart/README.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | You want to create a simple bar chart using [dimple.js](http://dimplejs.org/).
4 |
5 | # Solution
6 |
7 | 1. Create a project structure using Leiningen template
8 | [mies](https://github.com/swannodette/mies)
9 |
10 | ```
11 | $ lein new mies dimple-bar-chart
12 | ```
13 |
14 | 2. Add om and [sablono](https://github.com/r0man/sablono) to dependencies
15 |
16 | ```clojure
17 | :dependencies [[org.clojure/clojure "1.6.0"]
18 | [org.clojure/clojurescript "0.0-2371"]
19 |
20 | [om "0.8.0-alpha2"]
21 | ;; Hiccup style templating for React in ClojureScript
22 | [sablono "0.2.20"]]
23 | ```
24 |
25 | 3. Add react, d3 and dimple to index.html
26 |
27 | ```html
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ```
40 | 4. Code to create a vertical bar chart using JavaScript looks like
41 | this:
42 |
43 | ```JavaScript
44 |
45 |
60 |
61 | ```
62 |
63 | This creates a very simple bar chart. We're going to use interop to do
64 | the same in ClojureScript:
65 |
66 | ```Clojure
67 | (defn- draw-chart [data div {:keys [id chart]}]
68 | (let [{:keys [width height]} div
69 | {:keys [bounds plot
70 | x-axis y-axis]} chart
71 | Chart (.-chart js/dimple)
72 | svg (.newSvg js/dimple (str "#" id) width height)
73 | dimple-chart (.setBounds (Chart. svg) (:x bounds) (:y bounds) (:width bounds) (:height bounds))
74 | x (.addCategoryAxis dimple-chart "x" x-axis)
75 | y (.addMeasureAxis dimple-chart "y" y-axis)
76 | s (.addSeries dimple-chart nil plot (clj->js [x y]))]
77 | (aset s "data" (clj->js data))
78 | (.addLegend dimple-chart "5%" "10%" "20%" "10%" "right")
79 | (.draw dimple-chart)))
80 | ```
81 |
82 | We've provided some options around the height, width and margins
83 | making the chart a little bit more robust.
84 |
85 | 5. Now let's create a component that builds this chart:
86 |
87 | We want to generate an empty div that d3/dimple will use for the
88 | SVG. Width and height are stored in our application state:
89 |
90 | ```Clojure
91 | om/IRender
92 | (render [_]
93 | (let [{:keys [width height]} (:div cursor)]
94 | (html
95 | [:div {:id id :width width :height height}])))
96 | ```
97 | Once the component is mounted, we can tell dimple to draw the chart:
98 |
99 | ```clojure
100 | om/IDidMount
101 | (did-mount [_]
102 | (when-let [data (seq (:data cursor))]
103 | (draw-chart data (:div cursor) opts)))
104 | ```
105 |
106 | But we also want to update the chart whenever the data changes, so we
107 | want to do this:
108 |
109 | ```clojure
110 | om/IDidUpdate
111 | (did-update [_ _ _]
112 | (let [{:keys [width height]} (:div cursor)]
113 | (let [n (.getElementById js/document id)]
114 | (while (.hasChildNodes n)
115 | (.removeChild n (.-lastChild n))))
116 | (when-let [data (seq (:data cursor))]
117 | (draw-chart data (:div cursor) opts))))
118 | ```
119 | We need to remove the old chart and let dimple create a new one.
120 |
121 | One last step: when we resize the window, we'd like the chart to
122 | resize as well. This can be done by adding an event listener:
123 |
124 | ```clojure
125 | om/IWillMount
126 | (will-mount [_]
127 | (.addEventListener js/window
128 | "resize" (fn []
129 | (let [{:keys [width height]} (get-div-dimensions id)]
130 | (om/update! cursor :div {:width width :height height})))))
131 | ```
132 | 6. That's it. Run ``` lein cljsbuild once ``` in
133 | ```/recipes/dimple-bar-chart``` and open ```index.html``` in a
134 | browser of your choice. You should see the chart.
135 |
--------------------------------------------------------------------------------
/recipes/dimple-bar-chart/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/recipes/dimple-bar-chart/project.clj:
--------------------------------------------------------------------------------
1 | (defproject dimple-bar-chart "0.1.0-SNAPSHOT"
2 | :description "FIXME: write this!"
3 | :url "http://example.com/FIXME"
4 |
5 | :dependencies [[org.clojure/clojure "1.6.0"]
6 | [org.clojure/clojurescript "0.0-2371"]
7 |
8 | [om "0.8.0-alpha2"]
9 | ;; Hiccup style templating for React in ClojureScript
10 | [sablono "0.2.20"]]
11 |
12 | :plugins [[lein-cljsbuild "1.0.4-SNAPSHOT"]]
13 |
14 | :source-paths ["src"]
15 |
16 | :cljsbuild {
17 | :builds [{:id "dimple-bar-chart"
18 | :source-paths ["src"]
19 | :compiler {
20 | :output-to "dimple_bar_chart.js"
21 | :output-dir "out"
22 | :optimizations :none
23 | :source-map true}}]})
24 |
--------------------------------------------------------------------------------
/recipes/dimple-bar-chart/src/dimple_bar_chart/core.cljs:
--------------------------------------------------------------------------------
1 | (ns dimple-bar-chart.core
2 | (:require [om.core :as om :include-macros true]
3 | [sablono.core :as html :refer-macros [html]]))
4 |
5 | (enable-console-print!)
6 |
7 | (defonce app-state (atom {:chart {:div {:width "100%" :height 600}
8 | :data [{:value 240000 :timestamp "2014-01-01"}
9 | {:value 260000 :timestamp "2014-02-01"}
10 | {:value 290000 :timestamp "2014-03-01"}
11 | {:value 70000 :timestamp "2014-04-01"}
12 | {:value 100000 :timestamp "2014-05-01"}
13 | {:value 120000 :timestamp "2014-06-01"}
14 | {:value 240000 :timestamp "2014-07-01"}
15 | {:value 220000 :timestamp "2014-08-01"}
16 | {:value 360000 :timestamp "2014-09-01"}
17 | {:value 260000 :timestamp "2014-10-01"}
18 | {:value 250000 :timestamp "2014-11-01"}
19 | {:value 190000 :timestamp "2014-12-01"}]}}))
20 |
21 | (defn get-div-dimensions
22 | "Get width and height of a div with a specified id."
23 | [id]
24 | (let [e (.getElementById js/document id)
25 | x (.-clientWidth e)
26 | y (.-clientHeight e)]
27 | {:width x :height y}))
28 |
29 | (defn- draw-chart [data div {:keys [id chart]}]
30 | (let [{:keys [width height]} div
31 | {:keys [bounds plot
32 | x-axis y-axis]} chart
33 | Chart (.-chart js/dimple)
34 | svg (.newSvg js/dimple (str "#" id) width height)
35 | dimple-chart (.setBounds (Chart. svg) (:x bounds) (:y bounds) (:width bounds) (:height bounds))
36 | x (.addCategoryAxis dimple-chart "x" x-axis)
37 | y (.addMeasureAxis dimple-chart "y" y-axis)
38 | s (.addSeries dimple-chart nil plot (clj->js [x y]))]
39 | (aset s "data" (clj->js data))
40 | (.addLegend dimple-chart "5%" "10%" "20%" "10%" "right")
41 | (.draw dimple-chart)))
42 |
43 | (defn bar-chart [cursor owner {:keys [id chart] :as opts}]
44 | (reify
45 | om/IWillMount
46 | (will-mount [_]
47 | ;; Add event listener that will update width & height when window is resized
48 | (.addEventListener js/window
49 | "resize" (fn []
50 | (let [{:keys [width height]} (get-div-dimensions id)]
51 | (om/update! cursor :div {:width width :height height})))))
52 | om/IRender
53 | (render [_]
54 | (let [{:keys [width height]} (:div cursor)]
55 | (html
56 | [:div {:id id :width width :height height}])))
57 | om/IDidMount
58 | (did-mount [_]
59 | (when-let [data (seq (:data cursor))]
60 | (draw-chart data (:div cursor) opts)))
61 | om/IDidUpdate
62 | (did-update [_ _ _]
63 | (let [{:keys [width height]} (:div cursor)]
64 | (let [n (.getElementById js/document id)]
65 | (while (.hasChildNodes n)
66 | (.removeChild n (.-lastChild n))))
67 | (when-let [data (seq (:data cursor))]
68 | (draw-chart data (:div cursor) opts))))))
69 |
70 | (om/root
71 | (fn [app owner]
72 | (reify
73 | om/IRender
74 | (render [_]
75 | (html
76 | [:div
77 | [:h1 "Dimple vertical bar chart"]
78 | (om/build bar-chart (:chart app) {:opts {:id "chart"
79 | :chart {:bounds {:x "5%"
80 | :y "15%"
81 | :width "80%"
82 | :height "60%"}
83 | :plot js/dimple.plot.bar
84 | :x-axis "timestamp"
85 | :y-axis "value"}}})]))))
86 | app-state {:target (. js/document (getElementById "app"))})
87 |
--------------------------------------------------------------------------------
/recipes/example/README.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | Description of the problem.
4 |
5 | # Solution
6 |
7 | Description (walk-through) of the solution.
8 |
--------------------------------------------------------------------------------
/recipes/example/project.clj:
--------------------------------------------------------------------------------
1 | (defproject example "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [om "0.7.3"]
12 | [sablono "0.2.22"]]
13 |
14 | :plugins [[lein-cljsbuild "1.0.3"]
15 | [lein-environ "1.0.0"]]
16 |
17 | :min-lein-version "2.5.0"
18 |
19 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
20 | :compiler {:output-to "resources/public/js/app.js"
21 | :output-dir "resources/public/js/out"
22 | :source-map "resources/public/js/out.js.map"
23 | :preamble ["react/react.min.js"]
24 | :externs ["react/externs/react.js"]
25 | :optimizations :none
26 | :pretty-print true}}}}
27 |
28 | :profiles {:dev {:cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}})
29 |
--------------------------------------------------------------------------------
/recipes/example/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/example/src/clj/example/server.clj:
--------------------------------------------------------------------------------
1 | (ns example.server)
2 |
--------------------------------------------------------------------------------
/recipes/example/src/cljs/example/core.cljs:
--------------------------------------------------------------------------------
1 | (ns example.core)
2 |
--------------------------------------------------------------------------------
/recipes/input-validation/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/input-validation/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/input-validation.jar clojure.main -m input-validation.server
2 |
--------------------------------------------------------------------------------
/recipes/input-validation/README.md:
--------------------------------------------------------------------------------
1 | # Color Coded Input Validation
2 |
3 | 
4 |
5 | 1) Create a new Om project using Chestnut
6 |
7 | ```bash
8 | lein new chestnut input-validation -- --om-tools --http-kit
9 | ```
10 |
11 | 2) Use your editor of choice to open the file `input-validation/src/cljs/core.cljs`
12 |
13 |
14 | 3) Your initial app-state should look like this.
15 |
16 | ```clojure
17 | (defonce app-state (atom {:user-input "some value"}))
18 | ```
19 | 4) Create a user input component
20 |
21 | ```clojure
22 | (defcomponent user-input-view [app owner]
23 | (render [_]))
24 | ```
25 |
26 | 5) Make it render an Input element
27 |
28 | ```clojure
29 | (defcomponent user-input-view [app owner]
30 | (render
31 | [_]
32 | (let [ref "user-input"
33 | k :user-input]
34 | (dom/input {:ref ref
35 | :value (k app)}))))
36 | ```
37 |
38 | 6) Add an onChange JavaScript event handler. This handler should get the Input element's value and update the application state using `om/update!`
39 |
40 | ```clojure
41 | (defcomponent user-input-view [app owner]
42 | (render
43 | [_]
44 | (let [ref "user-input"
45 | k :user-input]
46 | (dom/input {:ref ref
47 | :value (k app)
48 | :on-change (fn [_]
49 | (let [this (om/get-node owner ref)]
50 | (om/update! app [k] (.-value this))))}))))
51 | ```
52 |
53 | 7) Create a new function to validate user input.
54 |
55 | ```clojure
56 | (defn valid-input? [x]
57 | (> 10 (count x)))
58 | ```
59 |
60 | 8) Create a new component that will display a valid or invalid state for the user input.
61 |
62 | ```clojure
63 | (defcomponent validation-box-view [app _]
64 | (render [_]))
65 | ```
66 |
67 | 9) The user input view should be parented by the validation view.
68 |
69 | ```clojure
70 | (defcomponent validation-box-view [app _]
71 | (render
72 | [_]
73 | (dom/div
74 | (om/build user-input-view app))))
75 | ```
76 |
77 | 10) Add validation logic and styles.
78 |
79 | ```clojure
80 | (defcomponent validation-box-view [app _]
81 | (render
82 | [_]
83 | (let [valid-color "green"
84 | invalid-color "red"
85 | color (if (valid-input? (:user-input app))
86 | valid-color
87 | invalid-color)]
88 | (dom/div {:style {:padding "10px;"
89 | :background-color color}}
90 | (om/build user-input-view app)))))
91 | ```
92 |
93 | 11) Replace or alter your main function to display the validation box view.
94 |
95 | ```clojure
96 | (defn main []
97 | (om/root
98 | validation-box-view
99 | app-state
100 | {:target (. js/document (getElementById "app"))}))
101 | ```
102 | 12) Start a REPL with `lein repl`
103 |
104 | ```
105 | nREPL server started on port 54879 on host 127.0.0.1 - nrepl://127.0.0.1:54879
106 | REPL-y 0.3.5, nREPL 0.2.6
107 | Clojure 1.6.0
108 | Java HotSpot(TM) 64-Bit Server VM 1.8.0_05-b13
109 | Docs: (doc function-name-here)
110 | (find-doc "part-of-name-here")
111 | Source: (source function-name-here)
112 | Javadoc: (javadoc java-object-or-class-here)
113 | Exit: Control+D or (exit) or (quit)
114 | Results: Stored in vars *1, *2, *3, an exception in *e
115 | ```
116 |
117 | 13) Call `run` to start the back end and compile your ClojureScript.
118 |
119 | ```
120 | input-validation.server=> (run)
121 | Starting figwheel.
122 | Starting web server on port 10555 .
123 | #
124 | input-validation.server=> Compiling ClojureScript.
125 | Figwheel: Starting server at http://localhost:3449
126 | Figwheel: Serving files from '(dev-resources|resources)/public'
127 | Compiling "resources/public/js/app.js" from ("src/cljs" "env/dev/cljs")...
128 | Successfully compiled "resources/public/js/app.js" in 18.01 seconds.
129 | notifying browser that file changed: /js/out/local_state/core.js
130 | ```
131 |
132 | 14) Point your browser to http://localhost:port. You can find the port in the REPL message output => `Starting web server on port ...`
133 |
134 |
135 |
--------------------------------------------------------------------------------
/recipes/input-validation/env/dev/cljs/input_validation/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns input-validation.dev
2 | (:require [input-validation.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/input-validation/env/prod/cljs/input_validation/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns input-validation.prod
2 | (:require [input-validation.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/input-validation/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/input-validation/preview.gif
--------------------------------------------------------------------------------
/recipes/input-validation/project.clj:
--------------------------------------------------------------------------------
1 | (defproject input-validation "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [ring "1.3.1"]
12 | [compojure "1.2.0"]
13 | [enlive "1.1.5"]
14 | [om "0.7.3"]
15 | [figwheel "0.1.4-SNAPSHOT"]
16 | [environ "1.0.0"]
17 | [com.cemerick/piggieback "0.1.3"]
18 | [weasel "0.4.0-SNAPSHOT"]
19 | [leiningen "2.5.0"]
20 | [http-kit "2.1.19"]
21 | [prismatic/om-tools "0.3.3"]]
22 |
23 | :plugins [[lein-cljsbuild "1.0.3"]
24 | [lein-environ "1.0.0"]]
25 |
26 | :min-lein-version "2.5.0"
27 |
28 | :uberjar-name "input-validation.jar"
29 |
30 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
31 | :compiler {:output-to "resources/public/js/app.js"
32 | :output-dir "resources/public/js/out"
33 | :source-map "resources/public/js/out.js.map"
34 | :preamble ["react/react.min.js"]
35 | :externs ["react/externs/react.js"]
36 | :optimizations :none
37 | :pretty-print true}}}}
38 |
39 | :profiles {:dev {:repl-options {:init-ns input-validation.server
40 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
41 |
42 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
43 |
44 | :figwheel {:http-server-root "public"
45 | :port 3449
46 | :css-dirs ["resources/public/css"]}
47 |
48 | :env {:is-dev true}
49 |
50 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
51 |
52 | :uberjar {:hooks [leiningen.cljsbuild]
53 | :env {:production true}
54 | :omit-source true
55 | :aot :all
56 | :cljsbuild {:builds {:app
57 | {:source-paths ["env/prod/cljs"]
58 | :compiler
59 | {:optimizations :advanced
60 | :pretty-print false}}}}}})
61 |
--------------------------------------------------------------------------------
/recipes/input-validation/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/input-validation/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/input-validation/src/clj/input_validation/dev.clj:
--------------------------------------------------------------------------------
1 | (ns input-validation.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('input_validation.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/input-validation/src/clj/input_validation/server.clj:
--------------------------------------------------------------------------------
1 | (ns input-validation.server
2 | (:require [clojure.java.io :as io]
3 | [input-validation.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [org.httpkit.server :refer [run-server]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-server http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/input-validation/src/cljs/input_validation/core.cljs:
--------------------------------------------------------------------------------
1 | (ns input-validation.core
2 | (:require [om.core :as om :include-macros true]
3 | [om-tools.dom :as dom :include-macros true]
4 | [om-tools.core :refer-macros [defcomponent]]))
5 |
6 | (defonce app-state (atom {:user-input "some value"}))
7 |
8 | (defcomponent user-input-view [app owner]
9 | (render
10 | [_]
11 | (let [ref "user-input"
12 | k :user-input]
13 | (dom/input {:ref ref
14 | :value (k app)
15 | :on-change (fn [_]
16 | (let [this (om/get-node owner ref)]
17 | (om/update! app [k] (.-value this))))}))))
18 |
19 |
20 | (defn valid-input? [x]
21 | (> 10 (count x)))
22 |
23 | (defcomponent validation-box-view [app _]
24 | (render
25 | [_]
26 | (let [valid-color "green"
27 | invalid-color "red"
28 | color (if (valid-input? (:user-input app))
29 | valid-color
30 | invalid-color)]
31 | (dom/div {:style {:padding "10px;"
32 | :background-color color}}
33 | (om/build user-input-view app)))))
34 |
35 | (defn main []
36 | (om/root
37 | validation-box-view
38 | app-state
39 | {:target (. js/document (getElementById "app"))}))
40 |
--------------------------------------------------------------------------------
/recipes/input-validation/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/kioo-blog.jar clojure.main -m kioo-blog.server
2 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/README.md:
--------------------------------------------------------------------------------
1 | # Om with Kioo Templating
2 |
3 | Kioo brings Enlive/Enfocus style templates to React/Om. Instead of building the dom with om/om-tools/sablono, you accept a static html and replace its dom with css-like selectors. Kioo snippets and templates are components that return React/om components, and as such, you'll be working with kioo's api to 'transform' the dom.
4 |
5 | For further info on a list of transformations, checkout [kioo](https://github.com/ckirkendall/kioo).
6 |
7 | 1) Create a new Om project using Chestnut
8 |
9 | ```bash
10 | lein new chestnut kioo-blog
11 | ```
12 |
13 | 2) Use your editor of choice to open the file `kioo-blog/src/cljs/core.cljs`
14 |
15 | 3) Add kioo to your project file
16 |
17 | ```clojure
18 | [kioo "0.4.0" :exclusions [com.facebook/react
19 | om]]
20 | ```
21 |
22 | 4) Add Kioo to your project requirements
23 |
24 | ```clojure
25 | (ns kioo-blog.core
26 | (:require [kioo.om :refer [set-style set-attr do-> substitute listen] :as kio]
27 | [kioo.core :refer [handle-wrapper]]
28 | [om.core :as om :include-macros true]
29 | [om.dom :as dom :include-macros true])
30 | (:require-macros [kioo.om :refer [defsnippet deftemplate]]))
31 | ```
32 |
33 | 6) Add an initial app-state.
34 |
35 | ```clojure
36 | (def app-state (atom {:heading "My programming blog"
37 | :contents [[:100 "Are We There Yet?"]
38 | [:200 "Simple Made Easy."]
39 | [:300 "The Value of Values"]]
40 | :navigation [["home" #(.log js/console %)]
41 | ["blog" #(.log js/console %)]]
42 | :footer "Eclipse Public License (c)"}))
43 | ```
44 |
45 | 7) Initialize a template and replace header/footer
46 |
47 | Study `resources/blog.html`. Let's replace menu and header.
48 |
49 | ```clojure
50 |
51 | (defsnippet menu-view "blog.html" [:.nav-item]
52 | [[caption func]]
53 | {[:a] (do-> (kio/content caption)
54 | (listen :onClick #(func caption)))})
55 |
56 | (defsnippet header-view "blog.html" [:header]
57 | [{:keys [heading navigation]}]
58 | {[:h1] (kio/content heading)
59 | [:ul] (kio/content (map menu-view navigation))})
60 |
61 | (deftemplate blog-tmpl "blog.html"
62 | [data]
63 | {[:header] (substitute (header-view data))})
64 |
65 | ```
66 |
67 | What just happened: `header-snippet` read the header tag of `blog.html`. After that, it transformed inidividual selectors, h1, ul, menu with content passed in (from the app-state cursor). Note how transformations can call other snippets, like `menu-view`.
68 |
69 | 8) Continue transforming content.
70 |
71 | ```clojure
72 | (defsnippet content-view "blog.html" [:section#blog]
73 | [content]
74 | {[:article.content] (kio/content (second content))})
75 |
76 | (defsnippet footer-view "blog.html" [:footer]
77 | [{:keys [footer]}]
78 | {[:h4] (kio/content footer)})
79 |
80 | (deftemplate blog-tmpl "blog.html"
81 | [data]
82 | {[:header] (substitute (header-view data))
83 | [:section#blog] (substitute (map content-view (:contents data)))
84 | [:footer] (substitute (footer-view data))})
85 | ```
86 |
87 | 9) Start a REPL with `lein repl`
88 |
89 | Call `run` to start the back end and compile your ClojureScript. Point your browser to http://localhost:port.
90 |
91 | You can find the port in the REPL message output.
92 |
93 |
94 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/env/dev/cljs/kioo_blog/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns kioo-blog.dev
2 | (:require [kioo-blog.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/env/prod/cljs/kioo_blog/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns kioo-blog.prod
2 | (:require [kioo-blog.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/project.clj:
--------------------------------------------------------------------------------
1 | (defproject kioo-blog "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [ring "1.3.1"]
12 | [compojure "1.2.0"]
13 | [enlive "1.1.5"]
14 | [om "0.7.3"]
15 | [kioo "0.4.0" :exclusions [com.facebook/react
16 | om]]
17 | [figwheel "0.1.4-SNAPSHOT"]
18 | [environ "1.0.0"]
19 | [com.cemerick/piggieback "0.1.3"]
20 | [weasel "0.4.0-SNAPSHOT"]
21 | [leiningen "2.5.0"]]
22 |
23 | :plugins [[lein-cljsbuild "1.0.3"]
24 | [lein-environ "1.0.0"]]
25 |
26 | :min-lein-version "2.5.0"
27 |
28 | :uberjar-name "kioo-blog.jar"
29 |
30 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
31 | :compiler {:output-to "resources/public/js/app.js"
32 | :output-dir "resources/public/js/out"
33 | :source-map "resources/public/js/out.js.map"
34 | :preamble ["react/react.min.js"]
35 | :externs ["react/externs/react.js"]
36 | :optimizations :none
37 | :pretty-print true}}}}
38 |
39 | :profiles {:dev {:repl-options {:init-ns kioo-blog.server
40 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
41 |
42 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
43 |
44 | :figwheel {:http-server-root "public"
45 | :port 3449
46 | :css-dirs ["resources/public/css"]}
47 |
48 | :env {:is-dev true}
49 |
50 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
51 |
52 | :uberjar {:hooks [leiningen.cljsbuild]
53 | :env {:production true}
54 | :omit-source true
55 | :aot :all
56 | :cljsbuild {:builds {:app
57 | {:source-paths ["env/prod/cljs"]
58 | :compiler
59 | {:optimizations :advanced
60 | :pretty-print false}}}}}})
61 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/resources/blog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 | content 1
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/src/clj/kioo_blog/dev.clj:
--------------------------------------------------------------------------------
1 | (ns kioo-blog.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('kioo_blog.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/src/clj/kioo_blog/server.clj:
--------------------------------------------------------------------------------
1 | (ns kioo-blog.server
2 | (:require [clojure.java.io :as io]
3 | [kioo-blog.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [ring.adapter.jetty :refer [run-jetty]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-jetty http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/src/cljs/kioo_blog/core.cljs:
--------------------------------------------------------------------------------
1 | (ns kioo-blog.core
2 | (:require [kioo.om :refer [set-style set-attr do-> substitute listen] :as kio]
3 | [kioo.core :refer [handle-wrapper]]
4 | [om.core :as om :include-macros true]
5 | [om.dom :as dom :include-macros true])
6 | (:require-macros [kioo.om :refer [defsnippet deftemplate]]))
7 |
8 | (def app-state (atom {:heading "My programming blog"
9 | :contents [[:100 "Are We There Yet?"]
10 | [:200 "Simple Made Easy."]
11 | [:300 "The Value of Values"]]
12 | :navigation [["home" #(.log js/console %)]
13 | ["blog" #(.log js/console %)]]
14 | :footer "Eclipse Public License (c)"}))
15 |
16 | (defsnippet menu-view "blog.html" [:.nav-item]
17 | [[caption func]]
18 | {[:a] (do-> (kio/content caption)
19 | (listen :onClick #(func caption)))})
20 |
21 | (defsnippet header-view "blog.html" [:header]
22 | [{:keys [heading navigation]}]
23 | {[:h1] (kio/content heading)
24 | [:ul] (kio/content (map menu-view navigation))})
25 |
26 | (defsnippet content-view "blog.html" [:section#blog]
27 | [content]
28 | {[:article.content] (kio/content (second content))})
29 |
30 | (defsnippet footer-view "blog.html" [:footer]
31 | [{:keys [footer]}]
32 | {[:h4] (kio/content footer)})
33 |
34 | (deftemplate blog-tmpl "blog.html"
35 | [data]
36 | {[:header] (substitute (header-view data))
37 | [:section#blog] (substitute (map content-view (:contents data)))
38 | [:footer] (substitute (footer-view data))})
39 |
40 | (defn template [data] (om/component (blog-tmpl data)))
41 |
42 | (defn main []
43 | (om/root
44 | template
45 | app-state
46 | {:target (.-body js/document)}))
47 |
--------------------------------------------------------------------------------
/recipes/kioo-blog/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/knob/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/knob/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/knob.jar clojure.main -m knob.server
2 |
--------------------------------------------------------------------------------
/recipes/knob/env/dev/cljs/knob/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns knob.dev
2 | (:require [knob.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/knob/env/prod/cljs/knob/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns knob.prod
2 | (:require [knob.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/knob/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/knob/preview.gif
--------------------------------------------------------------------------------
/recipes/knob/project.clj:
--------------------------------------------------------------------------------
1 | (defproject knob "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [org.clojure/core.async "0.1.346.0-17112a-alpha"]
12 | [ring "1.3.1"]
13 | [compojure "1.2.0"]
14 | [enlive "1.1.5"]
15 | [om "0.7.3"]
16 | [figwheel "0.1.4-SNAPSHOT"]
17 | [environ "1.0.0"]
18 | [com.cemerick/piggieback "0.1.3"]
19 | [weasel "0.4.0-SNAPSHOT"]
20 | [leiningen "2.5.0"]
21 | [http-kit "2.1.19"]
22 | [prismatic/om-tools "0.3.3" :exclusions [org.clojure/clojure]]]
23 |
24 | :plugins [[lein-cljsbuild "1.0.3"]
25 | [lein-environ "1.0.0"]]
26 |
27 | :min-lein-version "2.5.0"
28 |
29 | :uberjar-name "knob.jar"
30 |
31 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
32 | :compiler {:output-to "resources/public/js/app.js"
33 | :output-dir "resources/public/js/out"
34 | :source-map "resources/public/js/out.js.map"
35 | :preamble ["react/react.min.js"]
36 | :externs ["react/externs/react.js"]
37 | :optimizations :none
38 | :pretty-print true}}}}
39 |
40 | :profiles {:dev {:repl-options {:init-ns knob.server
41 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
42 |
43 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
44 |
45 | :figwheel {:http-server-root "public"
46 | :port 3449
47 | :css-dirs ["resources/public/css"]}
48 |
49 | :env {:is-dev true}
50 |
51 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
52 |
53 | :uberjar {:hooks [leiningen.cljsbuild]
54 | :env {:production true}
55 | :omit-source true
56 | :aot :all
57 | :cljsbuild {:builds {:app
58 | {:source-paths ["env/prod/cljs"]
59 | :compiler
60 | {:optimizations :advanced
61 | :pretty-print false}}}}}})
62 |
--------------------------------------------------------------------------------
/recipes/knob/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/knob/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/knob/src/clj/knob/dev.clj:
--------------------------------------------------------------------------------
1 | (ns knob.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('knob.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/knob/src/clj/knob/server.clj:
--------------------------------------------------------------------------------
1 | (ns knob.server
2 | (:require [clojure.java.io :as io]
3 | [knob.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [org.httpkit.server :refer [run-server]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-server http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/knob/src/cljs/knob/core.cljs:
--------------------------------------------------------------------------------
1 | (ns knob.core
2 | (:require-macros [cljs.core.async.macros :refer [go go-loop]])
3 | (:require [om.core :as om :include-macros true]
4 | [om-tools.dom :as dom :include-macros true]
5 | [om-tools.core :refer-macros [defcomponent]]))
6 |
7 | (defonce app-state
8 | (atom
9 | {:panel {:bg-color "#22313F"
10 | :font-color "white"}
11 | :knobs [{:id "a" :deg 0 :size 10 :color "#34495E" :border "#22A7F0"}
12 | {:id "b" :deg 80 :size 20 :color "#22313F" :border "#22A7F0"}
13 | {:id "c" :deg 180 :size 30 :color "#4B77BE" :border "#22A7F0"}
14 | {:id "d" :deg 350 :size 40 :color "#446CB3" :border "#22A7F0"}]}))
15 |
16 | (defn quadrant [y x rect]
17 | (let [half #(-> % (* 0.5) int)
18 | top (.-top rect)
19 | bottom (.-bottom rect)
20 | left (.-left rect)
21 | right (.-right rect)
22 | x-half (half (- right left))
23 | x (- x left)
24 | y-half (half (- bottom top))
25 | y (- y top)
26 | y-pos (if (<= y y-half) :up :down)
27 | x-pos (if (>= x x-half) :right :left)]
28 | [y-pos x-pos]))
29 |
30 | (defn direction
31 | [cursor y quadrant]
32 | (let [state @cursor
33 | {:keys [last-quadrant last-y]} state
34 | up? (< y last-y)
35 | direction [up? last-quadrant quadrant]]
36 |
37 | (condp = direction
38 |
39 | ;; increase top left to right
40 | [true [:up :left] [:up :left]] inc
41 | [false [:up :left] [:up :right]] inc
42 | [true [:up :left] [:up :right]] inc
43 | [false [:up :right] [:up :right]] inc
44 | [false [:up :right] [:down :right]] inc
45 |
46 | ;; increase bottom right to left
47 | [false [:down :right] [:down :right]] inc
48 | [false [:down :right] [:down :left]] inc
49 | [true [:down :left] [:down :left]] inc
50 | [false [:down :left] [:down :left]] dec
51 |
52 | ;; decrease bottom left to right
53 | [false [:down :left] [:down :right]] dec
54 | [true [:down :right] [:down :right]] dec
55 | [true [:down :right] [:up :right]] dec
56 |
57 | ;; decrease up right to left
58 | [true [:up :right] [:up :right]] dec
59 | [true [:up :right] [:up :left]] dec
60 | [false [:up :left] [:up :left]] dec
61 |
62 | identity)))
63 |
64 | (defn mouse-move! [cursor evt]
65 | (go-loop [state @cursor]
66 | (when (:mouse-down? state)
67 | (let [dk :deg
68 | yk :last-y
69 | lqk :last-quadrant
70 | rect (-> js/document
71 | (.getElementById (:id state))
72 | .getBoundingClientRect)
73 | y (.-clientY evt)
74 | x (.-clientX evt)
75 | q (quadrant y x rect)
76 | f (direction cursor y q)]
77 | (om/transact! cursor
78 | (fn [state]
79 | (-> state
80 | (update-in [dk]
81 | (fn [deg]
82 | (if (and (<= deg 350)
83 | (>= deg 0))
84 | (f deg))))
85 | (assoc-in [lqk] q)
86 | (assoc-in [yk] y))))))))
87 |
88 | (defn mouse-down! [cursor evt]
89 | (go
90 | (om/update! cursor [:mouse-down?] (-> evt .-button zero?))))
91 |
92 | (defn mouse-up! [cursor evt]
93 | (go
94 | (om/update! cursor [:mouse-down?] false)))
95 |
96 | (defcomponent knob-view [cursor _]
97 | (did-mount
98 | [_]
99 | (let [node (-> js/document
100 | (.getElementById (:id cursor)))]
101 | (-> node .-onmousemove (set! #(mouse-move! cursor %)))
102 | (-> node .-onmousedown (set! #(mouse-down! cursor %)))
103 | (-> node .-onmouseup (set! #(mouse-up! cursor %)))))
104 | (render
105 | [_]
106 | (let [{:keys [id deg size color border]} cursor
107 | rotate (str "rotate(" deg "deg)")
108 | width (* 1.5 size)
109 | height width
110 | padding (* size 0.30)
111 | stick-width (* width 0.35)
112 | stick-margin-top (* padding 2)
113 | stick-margin-left (str "-" (* padding 0.5))
114 | percent (-> (/ deg 350) (* 100) int)]
115 | (dom/div
116 | (dom/h2 (str percent "%"))
117 | (dom/div {:id id
118 | :style {:margin "20px"
119 | :border-color border
120 | :border-style "solid"
121 | :border-width "5px"
122 | :padding padding
123 | :border-radius "50%"
124 | :transform rotate
125 | :width width
126 | :height height
127 | :background-color color}}
128 | (dom/div {:style {:margin-top stick-margin-top
129 | :margin-left stick-margin-left
130 | :padding-right stick-width
131 | :padding-left stick-width
132 | :height "15%"
133 | :border-radius "35%"
134 | :background-color "white"
135 | :border-color "black"
136 | :border-style "solid"
137 | :border-width "1px"
138 | :float "left"}}))))))
139 |
140 | (defcomponent main-view [app owner]
141 | (render
142 | [_]
143 | (dom/div {:style {:background-color (-> app :panel :bg-color)
144 | :color (-> app :panel :font-color)
145 | :padding "10px"}}
146 | (om/build-all knob-view (:knobs app)))))
147 |
148 | (defn main []
149 | (om/root
150 | main-view
151 | app-state
152 | {:target (. js/document (getElementById "app"))}))
153 |
--------------------------------------------------------------------------------
/recipes/knob/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/local-state/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/local-state/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/local-state.jar clojure.main -m local-state.server
2 |
--------------------------------------------------------------------------------
/recipes/local-state/README.md:
--------------------------------------------------------------------------------
1 | # Local State
2 |
3 | 
4 |
5 | 1) Create a new Om project using Chestnut
6 |
7 | ```bash
8 | lein new chestnut local-state -- --om-tools --http-kit
9 | ```
10 |
11 | 2) Use your editor of choice to open the file `local-state/src/cljs/core.cljs`
12 |
13 | 3) You should remove `(defonce app-state ...)` because it's not needed for this example.
14 |
15 | 4) Create an Om component with initial state
16 |
17 | ```clojure
18 | (defcomponent local-state-counter-view [_ owner]
19 | (init-state [_])
20 | (render-state [_ state]))
21 | ```
22 |
23 | 5) Set your initial state values
24 |
25 | ```clojure
26 | (defcomponent local-state-counter-view [_ owner]
27 | (init-state
28 | [_]
29 | {:button-presses 1})
30 | (render-state [_ state]))
31 | ```
32 |
33 | 6) Create a render state so the initial state can be displayed
34 |
35 | ```clojure
36 | (defcomponent local-state-counter-view [_ owner]
37 | (init-state
38 | [_]
39 | {:button-presses 1})
40 | (render-state
41 | [_ state]
42 | (dom/div
43 | (dom/button {} "Click Me")
44 | (dom/br {})
45 | (str "Button Presses: "
46 | (:button-presses state)))))
47 |
48 | ```
49 |
50 | 7) Add an onClick JavaScript event to the "Click Me" button. This event should update the local state using `om/update-state!`.
51 |
52 | ```clojure
53 | (defcomponent local-state-counter-view [_ owner]
54 | (init-state
55 | [_]
56 | {:button-presses 1})
57 | (render-state
58 | [_ state]
59 | (dom/div
60 | (dom/button
61 | {:on-click (fn [_]
62 | (om/update-state! owner
63 | [:button-presses]
64 | inc))}
65 |
66 | "Click Me")
67 | (dom/br {})
68 | (str "Button Presses: "
69 | (:button-presses state)))))
70 |
71 | ```
72 |
73 | 8) Alter or replace the `(defn main [] ...)` function so that the local state component is displayed on the webpage.
74 |
75 | ```clojure
76 | (defn main []
77 | (om/root
78 | local-state-counter-view
79 | {}
80 | {:target (. js/document (getElementById "app"))}))
81 | ```
82 |
83 | 9) Start a REPL with `lein repl`
84 |
85 | ```
86 | nREPL server started on port 54879 on host 127.0.0.1 - nrepl://127.0.0.1:54879
87 | REPL-y 0.3.5, nREPL 0.2.6
88 | Clojure 1.6.0
89 | Java HotSpot(TM) 64-Bit Server VM 1.8.0_05-b13
90 | Docs: (doc function-name-here)
91 | (find-doc "part-of-name-here")
92 | Source: (source function-name-here)
93 | Javadoc: (javadoc java-object-or-class-here)
94 | Exit: Control+D or (exit) or (quit)
95 | Results: Stored in vars *1, *2, *3, an exception in *e
96 | ```
97 |
98 | 10) Call `run` to start the back end and compile your ClojureScript.
99 |
100 | ```
101 | local-state.server=> (run)
102 | Starting figwheel.
103 | Starting web server on port 10555 .
104 | #
105 | local-state.server=> Compiling ClojureScript.
106 | Figwheel: Starting server at http://localhost:3449
107 | Figwheel: Serving files from '(dev-resources|resources)/public'
108 | Compiling "resources/public/js/app.js" from ("src/cljs" "env/dev/cljs")...
109 | Successfully compiled "resources/public/js/app.js" in 18.01 seconds.
110 | notifying browser that file changed: /js/out/local_state/core.js
111 | ```
112 |
113 | 11) Point your browser to http://localhost:port. You can find the port in the REPL message output => `Starting web server on port ...`
114 |
--------------------------------------------------------------------------------
/recipes/local-state/env/dev/cljs/local_state/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns local-state.dev
2 | (:require [local-state.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/local-state/env/prod/cljs/local_state/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns local-state.prod
2 | (:require [local-state.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/local-state/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/local-state/preview.gif
--------------------------------------------------------------------------------
/recipes/local-state/project.clj:
--------------------------------------------------------------------------------
1 | (defproject local-state "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [ring "1.3.1"]
12 | [compojure "1.2.0"]
13 | [enlive "1.1.5"]
14 | [om "0.7.3"]
15 | [figwheel "0.1.4-SNAPSHOT"]
16 | [environ "1.0.0"]
17 | [com.cemerick/piggieback "0.1.3"]
18 | [weasel "0.4.0-SNAPSHOT"]
19 | [leiningen "2.5.0"]
20 | [http-kit "2.1.19"]
21 | [prismatic/om-tools "0.3.3" :exclusions [org.clojure/clojure]]]
22 |
23 | :plugins [[lein-cljsbuild "1.0.3"]
24 | [lein-environ "1.0.0"]]
25 |
26 | :min-lein-version "2.5.0"
27 |
28 | :uberjar-name "local-state.jar"
29 |
30 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
31 | :compiler {:output-to "resources/public/js/app.js"
32 | :output-dir "resources/public/js/out"
33 | :source-map "resources/public/js/out.js.map"
34 | :preamble ["react/react.min.js"]
35 | :externs ["react/externs/react.js"]
36 | :optimizations :none
37 | :pretty-print true}}}}
38 |
39 | :profiles {:dev {:repl-options {:init-ns local-state.server
40 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
41 |
42 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
43 |
44 | :figwheel {:http-server-root "public"
45 | :port 3449
46 | :css-dirs ["resources/public/css"]}
47 |
48 | :env {:is-dev true}
49 |
50 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
51 |
52 | :uberjar {:hooks [leiningen.cljsbuild]
53 | :env {:production true}
54 | :omit-source true
55 | :aot :all
56 | :cljsbuild {:builds {:app
57 | {:source-paths ["env/prod/cljs"]
58 | :compiler
59 | {:optimizations :advanced
60 | :pretty-print false}}}}}})
61 |
--------------------------------------------------------------------------------
/recipes/local-state/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/local-state/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/local-state/src/clj/local_state/dev.clj:
--------------------------------------------------------------------------------
1 | (ns local-state.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('local_state.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/local-state/src/clj/local_state/server.clj:
--------------------------------------------------------------------------------
1 | (ns local-state.server
2 | (:require [clojure.java.io :as io]
3 | [local-state.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [org.httpkit.server :refer [run-server]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-server http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/local-state/src/cljs/local_state/core.cljs:
--------------------------------------------------------------------------------
1 | (ns local-state.core
2 | (:require [om.core :as om :include-macros true]
3 | [om-tools.dom :as dom :include-macros true]
4 | [om-tools.core :refer-macros [defcomponent]]))
5 |
6 | (defcomponent local-state-counter-view [_ owner]
7 | (init-state
8 | [_]
9 | {:button-presses 1})
10 | (render-state
11 | [_ state]
12 | (dom/div
13 | (dom/button
14 | {:on-click (fn [_]
15 | (om/update-state! owner
16 | [:button-presses]
17 | inc))}
18 |
19 | "Click Me")
20 | (dom/br {})
21 | (str "Button Presses: "
22 | (:button-presses state)))))
23 |
24 | (defn main []
25 | (om/root
26 | local-state-counter-view
27 | {}
28 | {:target (. js/document (getElementById "app"))}))
29 |
--------------------------------------------------------------------------------
/recipes/local-state/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/routing-with-secretary.jar clojure.main -m routing-with-secretary.server
2 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/README.md:
--------------------------------------------------------------------------------
1 | # Single Page Routing With Secretary
2 |
3 | 
4 |
5 | 1) Create a new Om project using Chestnut
6 |
7 | ```bash
8 | lein new chestnut routing-with-secretary -- --om-tools --http-kit
9 | ```
10 |
11 | 2) Use your editor of choice to open the file `routing-with-secretary/src/cljs/core.cljs`
12 |
13 | 3) Add Secretary to your project file
14 |
15 | ```clojure
16 | [secretary "1.2.1"]
17 | ```
18 |
19 | 4) Add Secretary to your project requirements
20 |
21 | ```clojure
22 | (ns routing-with-secretary.core
23 | (:require [om.core :as om :include-macros true]
24 | [om-tools.dom :as dom :include-macros true]
25 | [om-tools.core :refer-macros [defcomponent]]
26 | [secretary.core :as sec
27 | :include-macros true]))
28 | ```
29 |
30 | 5) Add Google Closure History related requirements and imports.
31 |
32 | ```clojure
33 | (ns routing-with-secretary.core
34 | (:require [om.core :as om :include-macros true]
35 | [om-tools.dom :as dom :include-macros true]
36 | [om-tools.core :refer-macros [defcomponent]]
37 | [secretary.core :as sec
38 | :include-macros true]
39 | [goog.events :as events]
40 | [goog.history.EventType :as EventType])
41 | (:import goog.History))
42 | ```
43 | 6) Configure Secretary to have a fallback for browsers that don't support HTML5 history.
44 |
45 | ```clojure
46 | (sec/set-config! :prefix "#")
47 | ```
48 |
49 | 7) Enable HTML5 history support.
50 |
51 | ```clojure
52 | (let [history (History.)
53 | navigation EventType/NAVIGATE]
54 | (goog.events/listen history
55 | navigation
56 | #(-> % .-token sec/dispatch!))
57 | (doto history (.setEnabled true)))
58 | ```
59 |
60 | 8) Create a navigation component.
61 |
62 | ```clojure
63 | (defcomponent navigation-view [_ _]
64 | (render
65 | [_]
66 | (let [style {:style {:margin "10px;"}}]
67 | (dom/div style
68 | (dom/a (assoc style :href "#/")
69 | "Home")
70 | (dom/a (assoc style :href "#/something")
71 | "Something")
72 | (dom/a (assoc style :href "#/about")
73 | "About")))))
74 | ```
75 |
76 | 9) Create a view component and a Secretary route for the index page.
77 |
78 | ```clojure
79 | (defcomponent index-page-view [_ _]
80 | (render
81 | [_]
82 | (dom/div
83 | (om/build navigation-view {})
84 | (dom/h1 "Index Page"))))
85 |
86 | (sec/defroute index-page "/" []
87 | (om/root index-page-view
88 | {}
89 | {:target (. js/document (getElementById "app"))}))
90 | ```
91 |
92 | 10) Create a view component and a Secretary route for the something page.
93 |
94 | ```clojure
95 | (defcomponent something-page-view [_ _]
96 | (render
97 | [_]
98 | (dom/div
99 | (om/build navigation-view {})
100 | (dom/h1 "Something Page"))))
101 |
102 | (sec/defroute something-page "/something" []
103 | (om/root something-page-view
104 | {}
105 | {:target (. js/document (getElementById "app"))}))
106 | ```
107 | 11) Create a view component and a Secretary route for the about page.
108 |
109 | ```clojure
110 | (defcomponent about-page-view [_ _]
111 | (render
112 | [_]
113 | (dom/div
114 | (om/build navigation-view {})
115 | (dom/h1 "About Page"))))
116 |
117 | (sec/defroute about-page "/about" []
118 | (om/root about-page-view
119 | {}
120 | {:target (. js/document (getElementById "app"))}))
121 | ```
122 |
123 | 12) Alter or replace your main function to display the index page view when the page is loaded.
124 |
125 | ```clojure
126 | (defn main []
127 | (-> js/document
128 | .-location
129 | (set! "#/")))
130 | ```
131 | 13) Start a REPL with `lein repl`
132 |
133 | ```
134 | nREPL server started on port 54879 on host 127.0.0.1 - nrepl://127.0.0.1:54879
135 | REPL-y 0.3.5, nREPL 0.2.6
136 | Clojure 1.6.0
137 | Java HotSpot(TM) 64-Bit Server VM 1.8.0_05-b13
138 | Docs: (doc function-name-here)
139 | (find-doc "part-of-name-here")
140 | Source: (source function-name-here)
141 | Javadoc: (javadoc java-object-or-class-here)
142 | Exit: Control+D or (exit) or (quit)
143 | Results: Stored in vars *1, *2, *3, an exception in *e
144 | ```
145 |
146 | 14) Call `run` to start the back end and compile your ClojureScript.
147 |
148 | ```
149 | routing-with-secretary.server=> (run)
150 | Starting figwheel.
151 | Starting web server on port 10555 .
152 | #
153 | routing-with-secretary.server=> Compiling ClojureScript.
154 | Figwheel: Starting server at http://localhost:3449
155 | Figwheel: Serving files from '(dev-resources|resources)/public'
156 | Compiling "resources/public/js/app.js" from ("src/cljs" "env/dev/cljs")...
157 | Successfully compiled "resources/public/js/app.js" in 18.01 seconds.
158 | notifying browser that file changed: /js/out/local_state/core.js
159 | ```
160 |
161 | 15) Point your browser to http://localhost:port. You can find the port in the REPL message output => `Starting web server on port ...`
162 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/env/dev/cljs/routing_with_secretary/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns routing-with-secretary.dev
2 | (:require [routing-with-secretary.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/env/prod/cljs/routing_with_secretary/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns routing-with-secretary.prod
2 | (:require [routing-with-secretary.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/routing-with-secretary/preview.gif
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/project.clj:
--------------------------------------------------------------------------------
1 | (defproject routing-with-secretary "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [ring "1.3.1"]
12 | [compojure "1.2.0"]
13 | [enlive "1.1.5"]
14 | [om "0.7.3"]
15 | [figwheel "0.1.4-SNAPSHOT"]
16 | [environ "1.0.0"]
17 | [com.cemerick/piggieback "0.1.3"]
18 | [weasel "0.4.0-SNAPSHOT"]
19 | [leiningen "2.5.0"]
20 | [http-kit "2.1.19"]
21 | [secretary "1.2.1"]
22 | [prismatic/om-tools "0.3.3" :exclusions [org.clojure/clojure]]]
23 |
24 | :plugins [[lein-cljsbuild "1.0.3"]
25 | [lein-environ "1.0.0"]]
26 |
27 | :min-lein-version "2.5.0"
28 |
29 | :uberjar-name "routing-with-secretary.jar"
30 |
31 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
32 | :compiler {:output-to "resources/public/js/app.js"
33 | :output-dir "resources/public/js/out"
34 | :source-map "resources/public/js/out.js.map"
35 | :preamble ["react/react.min.js"]
36 | :externs ["react/externs/react.js"]
37 | :optimizations :none
38 | :pretty-print true}}}}
39 |
40 | :profiles {:dev {:repl-options {:init-ns routing-with-secretary.server
41 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
42 |
43 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
44 |
45 | :figwheel {:http-server-root "public"
46 | :port 3449
47 | :css-dirs ["resources/public/css"]}
48 |
49 | :env {:is-dev true}
50 |
51 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
52 |
53 | :uberjar {:hooks [leiningen.cljsbuild]
54 | :env {:production true}
55 | :omit-source true
56 | :aot :all
57 | :cljsbuild {:builds {:app
58 | {:source-paths ["env/prod/cljs"]
59 | :compiler
60 | {:optimizations :advanced
61 | :pretty-print false}}}}}})
62 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/src/clj/routing_with_secretary/dev.clj:
--------------------------------------------------------------------------------
1 | (ns routing-with-secretary.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('routing_with_secretary.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/src/clj/routing_with_secretary/server.clj:
--------------------------------------------------------------------------------
1 | (ns routing-with-secretary.server
2 | (:require [clojure.java.io :as io]
3 | [routing-with-secretary.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [org.httpkit.server :refer [run-server]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-server http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/src/cljs/routing_with_secretary/core.cljs:
--------------------------------------------------------------------------------
1 | (ns routing-with-secretary.core
2 | (:require [om.core :as om :include-macros true]
3 | [om-tools.dom :as dom :include-macros true]
4 | [om-tools.core :refer-macros [defcomponent]]
5 | [secretary.core :as sec
6 | :include-macros true]
7 | [goog.events :as events]
8 | [goog.history.EventType :as EventType])
9 | (:import goog.History))
10 |
11 | (sec/set-config! :prefix "#")
12 |
13 | (let [history (History.)
14 | navigation EventType/NAVIGATE]
15 | (goog.events/listen history
16 | navigation
17 | #(-> % .-token sec/dispatch!))
18 | (doto history (.setEnabled true)))
19 |
20 | (defcomponent navigation-view [_ _]
21 | (render
22 | [_]
23 | (let [style {:style {:margin "10px;"}}]
24 | (dom/div style
25 | (dom/a (assoc style :href "#/")
26 | "Home")
27 | (dom/a (assoc style :href "#/something")
28 | "Something")
29 | (dom/a (assoc style :href "#/about")
30 | "About")))))
31 |
32 | (defcomponent index-page-view [_ _]
33 | (render
34 | [_]
35 | (dom/div
36 | (om/build navigation-view {})
37 | (dom/h1 "Index Page"))))
38 |
39 | (sec/defroute index-page "/" []
40 | (om/root index-page-view
41 | {}
42 | {:target (. js/document (getElementById "app"))}))
43 |
44 | (defcomponent something-page-view [_ _]
45 | (render
46 | [_]
47 | (dom/div
48 | (om/build navigation-view {})
49 | (dom/h1 "Something Page"))))
50 |
51 | (sec/defroute something-page "/something" []
52 | (om/root something-page-view
53 | {}
54 | {:target (. js/document (getElementById "app"))}))
55 |
56 | (defcomponent about-page-view [_ _]
57 | (render
58 | [_]
59 | (dom/div
60 | (om/build navigation-view {})
61 | (dom/h1 "About Page"))))
62 |
63 | (sec/defroute about-page "/about" []
64 | (om/root about-page-view
65 | {}
66 | {:target (. js/document (getElementById "app"))}))
67 |
68 | (defn main []
69 | (-> js/document
70 | .-location
71 | (set! "#/")))
72 |
--------------------------------------------------------------------------------
/recipes/routing-with-secretary/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/scrollbar/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/scrollbar/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/scrollbar.jar clojure.main -m scrollbar.server
2 |
--------------------------------------------------------------------------------
/recipes/scrollbar/README.md:
--------------------------------------------------------------------------------
1 | # Custom Scrolling Bar
2 |
3 | 
4 |
5 | 1) Create a new Om project using Chestnut
6 |
7 | ```bash
8 | lein new chestnut scrollbar -- --om-tools --http-kit
9 | ```
10 |
11 | 2) Use your editor of choice to open the file `scrollbar/src/cljs/core.cljs`
12 |
13 |
14 | 3) Your initial app-state should look like this.
15 |
16 | ```clojure
17 | (ns scrollbar.core
18 | (:require-macros [cljs.core.async.macros :refer [go go-loop]])
19 | (:require [om.core :as om :include-macros true]
20 | [om-tools.dom :as dom :include-macros true]
21 | [om-tools.core :refer-macros [defcomponent]]))
22 |
23 |
24 | (def app-state
25 | (atom {:scrollbars []}))
26 | ```
27 | 4) Create a function that generates a scrollbar.
28 |
29 | ```clojure
30 | (defn add-sample-scrollbar! [cursor]
31 | (let [id (gensym)
32 | sb {:scroller-id (str "scroller" id)
33 | :slider-track-id (str "slider-track" id)
34 | :scrollbar-id (str "scrollbar" id)
35 | :scrollbar-height (rand-nth (range 40 200))
36 | :scrollbar 100
37 | :scrolling false}]
38 | (om/transact! cursor
39 | [:scrollbars]
40 | (fn [coll]
41 | (conj coll sb)))))
42 | ```
43 | 5) Create mouse up/down event handlers
44 |
45 | ```clojure
46 | (defn scrollbar-mouse-down! [cursor]
47 | (go
48 | (om/update! cursor [:scrolling] true)))
49 |
50 | (defn scrollbar-mouse-up! [cursor]
51 | (go
52 | (om/update! cursor [:scrolling] false)))
53 | ```
54 |
55 | 6) Create a function to change the scroll percent.
56 |
57 | ```clojure
58 | (defn scrollbar-percent! [cursor percent]
59 | (om/update! cursor [:scrollbar] percent))
60 | ```
61 |
62 | 7) The mouse move event handler should update the scroller percent value and positon when `(:scrolling state)`.
63 |
64 | ```clojure
65 | (defn scrollbar-mouse-move! [evt cursor]
66 | (go-loop [state @cursor
67 | scroller-node (-> js/document
68 | (.getElementById (:scroller-id state)))
69 | slider-track (-> js/document
70 | (.getElementById (:slider-track-id state)))
71 | y (- (.-clientY evt) 8)
72 | yp (str y "px")
73 | true-offset (-> slider-track .getBoundingClientRect .-top)
74 | true-y (- y true-offset)
75 | full (:scrollbar-height state)
76 | percent (->> (/ true-y full)
77 | (* 100)
78 | (- 100)
79 | int)]
80 | (when (and (:scrolling state)
81 | (>= 100 percent)
82 | (>= percent 0))
83 | (-> scroller-node
84 | .-style.top
85 | (set! yp))
86 | (scrollbar-percent! cursor percent))))
87 | ```
88 |
89 | 8) Add the event handlers to the scroll bar view
90 |
91 | ```clojure
92 | (defcomponent scrollbar-view [cursor _]
93 | (did-mount
94 | [_]
95 | (let [scroller-node (-> js/document
96 | (.getElementById (:scroller-id cursor)))
97 | scrollbar-node (-> js/document
98 | (.getElementById (:scrollbar-id cursor)))]
99 | ;; set scrolling
100 | (doseq [node [scroller-node
101 | scrollbar-node]]
102 | (-> node
103 | .-onmousedown
104 | (set! (fn [_] (scrollbar-mouse-down! cursor)))))
105 |
106 | ;; set not scrolling
107 | (doseq [node [scroller-node
108 | scrollbar-node]]
109 | (-> node
110 | .-onmouseup
111 | (set! (fn [_] (scrollbar-mouse-up! cursor)))))
112 |
113 | ;; set movement listener
114 | (doseq [node [scroller-node
115 | scrollbar-node]]
116 | (-> node
117 | .-onmousemove
118 | (set! (fn [evt] (scrollbar-mouse-move! evt cursor)))))))
119 | (render
120 | [_]
121 | (let [height-px (str (:scrollbar-height cursor) "px")]
122 | (dom/span
123 | (dom/b (str (:scrollbar cursor) "%"))
124 | (dom/div {:id (:scrollbar-id cursor)
125 | :style {:width "20px"
126 | :height height-px
127 | :padding "8px 2px 8px 2px"
128 | :border-width "1px"
129 | :border-color "#000"
130 | :border-style "solid"
131 | :background-color "hsl(190,15%,90%)"}}
132 | (dom/div {:id (:slider-track-id cursor)
133 | :style {:float "left"
134 | :padding "1px"
135 | :height height-px
136 | :margin-left "9px"
137 | :background-color "#000"}})
138 | (dom/div {:id (:scroller-id cursor)
139 | :style {:width "18px"
140 | :height "10px"
141 | :position "absolute"
142 | :background-color "hsl(195,100%,50%)"
143 | :border-width "1px"
144 | :border-style "solid"
145 | :border-color "#000"}}))))))
146 | ```
147 |
148 | 9) The main view should generate 10 scrollbars with each being of a random height.
149 |
150 | ```clojure
151 | (defcomponent main-view [app owner]
152 | (will-mount
153 | [_]
154 | (dotimes [_ 10]
155 | (add-sample-scrollbar! app)))
156 | (render
157 | [_]
158 | (dom/table
159 | (dom/tr
160 | (for [sb (:scrollbars app)]
161 | (dom/td {:style {:padding 30}}
162 | (om/build scrollbar-view sb)))))))
163 | ```
164 |
165 | 10) Replace or alter your main function to display the main view
166 |
167 | ```clojure
168 | (defn main []
169 | (om/root
170 | main-view
171 | app-state
172 | {:target (. js/document (getElementById "app"))}))
173 | ```
174 | 11) Start a REPL with `lein repl`
175 |
176 | ```
177 | nREPL server started on port 54879 on host 127.0.0.1 - nrepl://127.0.0.1:54879
178 | REPL-y 0.3.5, nREPL 0.2.6
179 | Clojure 1.6.0
180 | Java HotSpot(TM) 64-Bit Server VM 1.8.0_05-b13
181 | Docs: (doc function-name-here)
182 | (find-doc "part-of-name-here")
183 | Source: (source function-name-here)
184 | Javadoc: (javadoc java-object-or-class-here)
185 | Exit: Control+D or (exit) or (quit)
186 | Results: Stored in vars *1, *2, *3, an exception in *e
187 | ```
188 |
189 | 12) Call `run` to start the back end and compile your ClojureScript.
190 |
191 | ```
192 | scrollbar.server=> (run)
193 | Starting figwheel.
194 | Starting web server on port 10555 .
195 | #
196 | scrollbar.server=> Compiling ClojureScript.
197 | Figwheel: Starting server at http://localhost:3449
198 | Figwheel: Serving files from '(dev-resources|resources)/public'
199 | Compiling "resources/public/js/app.js" from ("src/cljs" "env/dev/cljs")...
200 | Successfully compiled "resources/public/js/app.js" in 18.01 seconds.
201 | notifying browser that file changed: /js/out/local_state/core.js
202 | ```
203 |
204 | 13) Point your browser to http://localhost:port. You can find the port in the REPL message output => `Starting web server on port ...`
205 |
--------------------------------------------------------------------------------
/recipes/scrollbar/env/dev/cljs/scrollbar/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns scrollbar.dev
2 | (:require [scrollbar.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/scrollbar/env/prod/cljs/scrollbar/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns scrollbar.prod
2 | (:require [scrollbar.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/scrollbar/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/scrollbar/preview.gif
--------------------------------------------------------------------------------
/recipes/scrollbar/project.clj:
--------------------------------------------------------------------------------
1 | (defproject scrollbar "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [org.clojure/core.async "0.1.346.0-17112a-alpha"]
12 | [ring "1.3.1"]
13 | [compojure "1.2.0"]
14 | [enlive "1.1.5"]
15 | [om "0.7.3"]
16 | [figwheel "0.1.4-SNAPSHOT"]
17 | [environ "1.0.0"]
18 | [com.cemerick/piggieback "0.1.3"]
19 | [weasel "0.4.0-SNAPSHOT"]
20 | [leiningen "2.5.0"]
21 | [http-kit "2.1.19"]
22 | [prismatic/om-tools "0.3.3"
23 | :exclusions [org.clojure/clojure]]]
24 |
25 | :plugins [[lein-cljsbuild "1.0.3"]
26 | [lein-environ "1.0.0"]]
27 |
28 | :min-lein-version "2.5.0"
29 |
30 | :uberjar-name "scrollbar.jar"
31 |
32 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
33 | :compiler {:output-to "resources/public/js/app.js"
34 | :output-dir "resources/public/js/out"
35 | :source-map "resources/public/js/out.js.map"
36 | :preamble ["react/react.min.js"]
37 | :externs ["react/externs/react.js"]
38 | :optimizations :none
39 | :pretty-print true}}}}
40 |
41 | :profiles {:dev {:repl-options {:init-ns scrollbar.server
42 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
43 |
44 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
45 |
46 | :figwheel {:http-server-root "public"
47 | :port 3449
48 | :css-dirs ["resources/public/css"]}
49 |
50 | :env {:is-dev true}
51 |
52 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
53 |
54 | :uberjar {:hooks [leiningen.cljsbuild]
55 | :env {:production true}
56 | :omit-source true
57 | :aot :all
58 | :cljsbuild {:builds {:app
59 | {:source-paths ["env/prod/cljs"]
60 | :compiler
61 | {:optimizations :advanced
62 | :pretty-print false}}}}}})
63 |
--------------------------------------------------------------------------------
/recipes/scrollbar/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/scrollbar/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/scrollbar/src/clj/scrollbar/dev.clj:
--------------------------------------------------------------------------------
1 | (ns scrollbar.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('scrollbar.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/scrollbar/src/clj/scrollbar/server.clj:
--------------------------------------------------------------------------------
1 | (ns scrollbar.server
2 | (:require [clojure.java.io :as io]
3 | [scrollbar.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [org.httpkit.server :refer [run-server]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-server http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/scrollbar/src/cljs/scrollbar/core.cljs:
--------------------------------------------------------------------------------
1 | (ns scrollbar.core
2 | (:require-macros [cljs.core.async.macros :refer [go go-loop]])
3 | (:require [om.core :as om :include-macros true]
4 | [om-tools.dom :as dom :include-macros true]
5 | [om-tools.core :refer-macros [defcomponent]]))
6 |
7 |
8 | (def app-state
9 | (atom {:scrollbars []}))
10 |
11 | (defn add-sample-scrollbar! [cursor]
12 | (let [id (gensym)
13 | sb {:scroller-id (str "scroller" id)
14 | :slider-track-id (str "slider-track" id)
15 | :scrollbar-id (str "scrollbar" id)
16 | :scrollbar-height (rand-nth (range 40 200))
17 | :scrollbar 100
18 | :scrolling false}]
19 | (om/transact! cursor
20 | [:scrollbars]
21 | (fn [coll]
22 | (conj coll sb)))))
23 |
24 | (defn scrollbar-mouse-down! [cursor]
25 | (go
26 | (om/update! cursor [:scrolling] true)))
27 |
28 | (defn scrollbar-mouse-up! [cursor]
29 | (go
30 | (om/update! cursor [:scrolling] false)))
31 |
32 | (defn scrollbar-percent! [cursor percent]
33 | (om/update! cursor [:scrollbar] percent))
34 |
35 | (defn scrollbar-mouse-move! [evt cursor]
36 | (go-loop [state @cursor
37 | scroller-node (-> js/document
38 | (.getElementById (:scroller-id state)))
39 | slider-track (-> js/document
40 | (.getElementById (:slider-track-id state)))
41 | y (- (.-clientY evt) 8)
42 | yp (str y "px")
43 | true-offset (-> slider-track .getBoundingClientRect .-top)
44 | true-y (- y true-offset)
45 | full (:scrollbar-height state)
46 | percent (->> (/ true-y full)
47 | (* 100)
48 | (- 100)
49 | int)]
50 | (when (and (:scrolling state)
51 | (>= 100 percent)
52 | (>= percent 0))
53 | (-> scroller-node
54 | .-style.top
55 | (set! yp))
56 | (scrollbar-percent! cursor percent))))
57 |
58 | (defcomponent scrollbar-view [cursor _]
59 | (did-mount
60 | [_]
61 | (let [scroller-node (-> js/document
62 | (.getElementById (:scroller-id cursor)))
63 | scrollbar-node (-> js/document
64 | (.getElementById (:scrollbar-id cursor)))]
65 | ;; set scrolling
66 | (doseq [node [scroller-node
67 | scrollbar-node]]
68 | (-> node
69 | .-onmousedown
70 | (set! (fn [_] (scrollbar-mouse-down! cursor)))))
71 |
72 | ;; set not scrolling
73 | (doseq [node [scroller-node
74 | scrollbar-node]]
75 | (-> node
76 | .-onmouseup
77 | (set! (fn [_] (scrollbar-mouse-up! cursor)))))
78 |
79 | ;; set movement listener
80 | (doseq [node [scroller-node
81 | scrollbar-node]]
82 | (-> node
83 | .-onmousemove
84 | (set! (fn [evt] (scrollbar-mouse-move! evt cursor)))))))
85 | (render
86 | [_]
87 | (let [height-px (str (:scrollbar-height cursor) "px")]
88 | (dom/span
89 | (dom/b (str (:scrollbar cursor) "%"))
90 | (dom/div {:id (:scrollbar-id cursor)
91 | :style {:width "20px"
92 | :height height-px
93 | :padding "8px 2px 8px 2px"
94 | :border-width "1px"
95 | :border-color "#000"
96 | :border-style "solid"
97 | :background-color "hsl(190,15%,90%)"}}
98 | (dom/div {:id (:slider-track-id cursor)
99 | :style {:float "left"
100 | :padding "1px"
101 | :height height-px
102 | :margin-left "9px"
103 | :background-color "#000"}})
104 | (dom/div {:id (:scroller-id cursor)
105 | :style {:width "18px"
106 | :height "10px"
107 | :position "absolute"
108 | :background-color "hsl(195,100%,50%)"
109 | :border-width "1px"
110 | :border-style "solid"
111 | :border-color "#000"}}))))))
112 |
113 | (defcomponent main-view [app owner]
114 | (will-mount
115 | [_]
116 | (dotimes [_ 10]
117 | (add-sample-scrollbar! app)))
118 | (render
119 | [_]
120 | (dom/table
121 | (dom/tr
122 | (for [sb (:scrollbars app)]
123 | (dom/td {:style {:padding 30}}
124 | (om/build scrollbar-view sb)))))))
125 |
126 | (defn main []
127 | (om/root
128 | main-view
129 | app-state
130 | {:target (. js/document (getElementById "app"))}))
131 |
--------------------------------------------------------------------------------
/recipes/scrollbar/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/sortable-table/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/sortable-table/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/sortable-table.jar clojure.main -m sortable-table.server
2 |
--------------------------------------------------------------------------------
/recipes/sortable-table/README.md:
--------------------------------------------------------------------------------
1 | # Single Attribute Sortable Table
2 |
3 | 
4 |
5 | 1) Create a new Om project using Chestnut
6 |
7 | ```bash
8 | lein new chestnut sortable-table -- --om-tools --http-kit
9 | ```
10 |
11 | 2) Use your editor of choice to open the file `sortable-table/src/cljs/core.cljs`
12 |
13 |
14 | 3) Your initial app-state should look like this.
15 |
16 | ```clojure
17 | (defonce app-state
18 | (atom
19 | {:table [{:gender "M" :name "person1" :age 100}
20 | {:gender "F" :name "person2" :age 50}
21 | {:gender "N/A" :name "person3" :age 10}
22 | {:gender "F" :name "person4" :age 20}
23 | {:gender "F" :name "person5" :age 30}
24 | {:gender "M" :name "person6" :age 80}
25 | {:gender "N/A" :name "person7" :age 80}]}))
26 | ```
27 |
28 | 4) Define state in your local component to store sort settings
29 |
30 | ```clojure
31 | (init-state [_] {:sort :nil
32 | :sort-dir nil})
33 | ```
34 |
35 | 5) Define a way to change the sort order and key
36 |
37 | ``` clojure
38 | (defn change-sorting [owner key]
39 | ;; invert direction on second click
40 | (when (= (om/get-state owner :sort) key)
41 | (om/set-state! owner :sort-dir (not (om/get-state owner :sort-dir))))
42 | (om/set-state! owner :sort key))
43 | ```
44 |
45 | 6) Set up the function to sort the data
46 |
47 | ``` clojure
48 | (defn sorted-data [key direction data]
49 | (if key
50 | ((if direction reverse identity)
51 | (sort-by key data))
52 | data))
53 | ```
54 |
55 | 7) Create a table view header component
56 |
57 | ```clojure
58 | (defcomponent table-view-header [{:keys [header]} _]
59 | (render [_]))
60 | ```
61 |
62 | 8) Add header element with a fn that changes the sort setting
63 |
64 | ```clojure
65 | (defcomponent table-view-header [{:keys [header sort-fn]} _]
66 | (render
67 | [_]
68 | (let [header-name (name header)
69 | header-kw (keyword header-name)
70 | attr {:href "javascript:void(0);"
71 | :style {:margin "5px;"
72 | :font-size "30px;"
73 | :text-decoration "none;"}
74 | :on-click sort-fn}
75 | header-attr {:style {:padding-right "20px;"
76 | :font-size "25px;"}}]
77 | (dom/th header-attr
78 | (clojure.string/capitalize header-name)
79 | ;; UTF-8 Up/Down Arrow
80 | (dom/a attr "⬍")))))
81 | ```
82 |
83 | 8) Create a table row component
84 |
85 | ```clojure
86 | (defcomponent table-row [row owner]
87 | (render
88 | [_]
89 | (dom/tr
90 | (for [value (map val row)]
91 | (dom/td value)))))
92 | ```
93 |
94 | 9) Define a function that gets the table headers and adds the sort key
95 |
96 | ```clojure
97 | (defn header-types [data sort-fn]
98 | (map #(hash-map :header % :sort-fn (partial sort-fn %))
99 | (-> data
100 | first
101 | keys)))
102 | ```
103 |
104 | 10) Create a component that builds the table's header and rows.
105 | This table component holds the local sort state.
106 | The partial is being used to draft a function that is called with the "owern" from this scope and the header key from the header-types scope
107 |
108 | ```clojure
109 | (defcomponent table-view [data owner]
110 | (init-state [_] {:sort :nil
111 | :sort-dir nil})
112 | (render-state
113 | [_ state]
114 | (dom/table
115 | (dom/tr
116 | (om/build-all table-view-header (header-types data (partial change-sorting owner))))
117 | (om/build-all table-row
118 | (sorted-data (:sort state)
119 | (:sort-dir state)
120 | data)))))
121 | ```
122 |
123 | 11) Define the view that holds the table, and hand it the app state cursor
124 |
125 | (defcomponent main-view [app-state]
126 | (render [_]
127 | (om/build table-view (:table app-state))))
128 |
129 | 12) Display the main view
130 |
131 | ```clojure
132 | (defn main []
133 | (om/root
134 | main-view
135 | app-state
136 | {:target (. js/document (getElementById "app"))}))
137 | ```
138 |
139 | 13) Start a REPL with `lein repl`
140 |
141 | ```
142 | nREPL server started on port 54879 on host 127.0.0.1 - nrepl://127.0.0.1:54879
143 | REPL-y 0.3.5, nREPL 0.2.6
144 | Clojure 1.6.0
145 | Java HotSpot(TM) 64-Bit Server VM 1.8.0_05-b13
146 | Docs: (doc function-name-here)
147 | (find-doc "part-of-name-here")
148 | Source: (source function-name-here)
149 | Javadoc: (javadoc java-object-or-class-here)
150 | Exit: Control+D or (exit) or (quit)
151 | Results: Stored in vars *1, *2, *3, an exception in *e
152 | ```
153 |
154 | 14) Call `run` to start the back end and compile your ClojureScript.
155 |
156 | ```
157 | sortable-table.server=> (run)
158 | Starting figwheel.
159 | Starting web server on port 10555 .
160 | #
161 | sortable-table.server=> Compiling ClojureScript.
162 | Figwheel: Starting server at http://localhost:3449
163 | Figwheel: Serving files from '(dev-resources|resources)/public'
164 | Compiling "resources/public/js/app.js" from ("src/cljs" "env/dev/cljs")...
165 | Successfully compiled "resources/public/js/app.js" in 18.01 seconds.
166 | notifying browser that file changed: /js/out/local_state/core.js
167 | ```
168 |
169 | 15) Point your browser to http://localhost:port. You can find the port in the REPL message output => `Starting web server on port ...`
170 |
171 |
--------------------------------------------------------------------------------
/recipes/sortable-table/env/dev/cljs/sortable_table/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns sortable-table.dev
2 | (:require [sortable-table.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/sortable-table/env/prod/cljs/sortable_table/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns sortable-table.prod
2 | (:require [sortable-table.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/sortable-table/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/sortable-table/preview.gif
--------------------------------------------------------------------------------
/recipes/sortable-table/project.clj:
--------------------------------------------------------------------------------
1 | (defproject sortable-table "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [ring "1.3.1"]
12 | [compojure "1.2.0"]
13 | [enlive "1.1.5"]
14 | [om "0.7.3"]
15 | [figwheel "0.1.4-SNAPSHOT"]
16 | [environ "1.0.0"]
17 | [com.cemerick/piggieback "0.1.3"]
18 | [weasel "0.4.0-SNAPSHOT"]
19 | [leiningen "2.5.0"]
20 | [http-kit "2.1.19"]
21 | [prismatic/om-tools "0.3.3"]]
22 |
23 | :plugins [[lein-cljsbuild "1.0.3"]
24 | [lein-environ "1.0.0"]]
25 |
26 | :min-lein-version "2.5.0"
27 |
28 | :uberjar-name "sortable-table.jar"
29 |
30 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
31 | :compiler {:output-to "resources/public/js/app.js"
32 | :output-dir "resources/public/js/out"
33 | :source-map "resources/public/js/out.js.map"
34 | :preamble ["react/react.min.js"]
35 | :externs ["react/externs/react.js"]
36 | :optimizations :none
37 | :pretty-print true}}}}
38 |
39 | :profiles {:dev {:repl-options {:init-ns sortable-table.server
40 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
41 |
42 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
43 |
44 | :figwheel {:http-server-root "public"
45 | :port 3449
46 | :css-dirs ["resources/public/css"]}
47 |
48 | :env {:is-dev true}
49 |
50 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
51 |
52 | :uberjar {:hooks [leiningen.cljsbuild]
53 | :env {:production true}
54 | :omit-source true
55 | :aot :all
56 | :cljsbuild {:builds {:app
57 | {:source-paths ["env/prod/cljs"]
58 | :compiler
59 | {:optimizations :advanced
60 | :pretty-print false}}}}}})
61 |
--------------------------------------------------------------------------------
/recipes/sortable-table/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/sortable-table/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/sortable-table/src/clj/sortable_table/dev.clj:
--------------------------------------------------------------------------------
1 | (ns sortable-table.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('sortable_table.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/sortable-table/src/clj/sortable_table/server.clj:
--------------------------------------------------------------------------------
1 | (ns sortable-table.server
2 | (:require [clojure.java.io :as io]
3 | [sortable-table.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [org.httpkit.server :refer [run-server]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10559))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-server http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/sortable-table/src/cljs/sortable_table/core.cljs:
--------------------------------------------------------------------------------
1 | (ns sortable-table.core
2 | (:require
3 | [om.core :as om :include-macros true]
4 | [om-tools.dom :as dom :include-macros true]
5 | [om-tools.core :refer-macros [defcomponent]]))
6 |
7 | (def app-state
8 | (atom
9 | {:table [{:gender "M" :name "person1" :age 100}
10 | {:gender "F" :name "person2" :age 50}
11 | {:gender "N/A" :name "person3" :age 10}
12 | {:gender "F" :name "person4" :age 20}
13 | {:gender "F" :name "person5" :age 30}
14 | {:gender "M" :name "person6" :age 80}
15 | {:gender "N/A" :name "person7" :age 80}]}))
16 |
17 | (defcomponent table-view-header [{:keys [header sort-fn]} _]
18 | (render
19 | [_]
20 | (let [header-name (name header)
21 | header-kw (keyword header-name)
22 | attr {:href "javascript:void(0);"
23 | :style {:margin "5px;"
24 | :font-size "30px;"
25 | :text-decoration "none;"}
26 | :on-click sort-fn}
27 | header-attr {:style {:padding-right "20px;"
28 | :font-size "25px;"}}]
29 | (dom/th header-attr
30 | (clojure.string/capitalize header-name)
31 | ;; UTF-8 Up/Down Arrow
32 | (dom/a attr "⬍")))))
33 |
34 | (defcomponent table-row [row owner]
35 | (render
36 | [_]
37 | (dom/tr
38 | (for [value (map val row)]
39 | (dom/td value)))))
40 |
41 | (defn sorted-data [key direction data]
42 | (if key
43 | ((if direction reverse identity)
44 | (sort-by key data))
45 | data))
46 |
47 | (defn change-sorting [owner key]
48 | ;; invert direction on second click
49 | (when (= (om/get-state owner :sort) key)
50 | (om/set-state! owner :sort-dir (not (om/get-state owner :sort-dir))))
51 | (om/set-state! owner :sort key))
52 |
53 | (defn header-types [data sort-fn]
54 | (map #(hash-map :header % :sort-fn (partial sort-fn %))
55 | (-> data
56 | first
57 | keys)))
58 |
59 | (defcomponent table-view [data owner]
60 | ;; Our local state stores the sort key and the sort direction.
61 | ;; The model data will never be modified, it contains the truth.
62 | ;; Sorting is just a ephemeral view setting, which is also stored in said view.
63 | (init-state [_] {:sort :nil
64 | :sort-dir nil})
65 | (render-state
66 | [_ state]
67 | (dom/table
68 | (dom/tr
69 | (om/build-all table-view-header (header-types data (partial change-sorting owner))))
70 | (om/build-all table-row
71 | (sorted-data (:sort state)
72 | (:sort-dir state)
73 | data)))))
74 |
75 |
76 | (defcomponent main-view [app-state]
77 | (render [_]
78 | (om/build table-view (:table app-state))))
79 |
80 | (defn main []
81 | (om/root
82 | main-view
83 | app-state
84 | {:target (. js/document (getElementById "app"))}))
85 |
--------------------------------------------------------------------------------
/recipes/sortable-table/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/toggle-button/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | /resources/public/js
11 | /out
12 | /.repl
13 |
--------------------------------------------------------------------------------
/recipes/toggle-button/Procfile:
--------------------------------------------------------------------------------
1 | web: java $JVM_OPTS -cp target/toggle-button.jar clojure.main -m toggle-button.server
2 |
--------------------------------------------------------------------------------
/recipes/toggle-button/README.md:
--------------------------------------------------------------------------------
1 | # Om Toggle Button
2 |
3 | Preview Image:
4 | 
5 |
6 | 1) Create a new Om project using Chestnut
7 |
8 | ```bash
9 | lein new chestnut toggle-button -- --om-tools --http-kit
10 | ```
11 |
12 | 2) Use your editor of choice to open the file `toggle-button/src/cljs/core.cljs`
13 |
14 |
15 | 3) Your initial app-state should look like this.
16 |
17 | ```clojure
18 | (defonce app-state (atom {:toggle true}))
19 | ```
20 |
21 | 4) Create a toggle button component
22 |
23 | ```clojure
24 | (defcomponent toggle-button-view [cursor _]
25 | (render
26 | [_]
27 | (let [on? (:toggle cursor)]
28 | (dom/div
29 | {:class "can-toggle"}
30 | (dom/span "ON")
31 | (dom/a
32 | {:href "javascript:void(0)"}
33 | (dom/span
34 | {:class "can-toggle-switch"}))
35 | (dom/span "OFF")))))
36 | ```
37 |
38 | 5) Add button view styles
39 |
40 | ```clojure
41 | (defcomponent toggle-button-view [cursor _]
42 | (render
43 | [_]
44 | (let [on? (:toggle cursor)
45 | [on off] (if on?
46 | ["inline" "none"]
47 | ["none" "inline"])
48 | padding "2px 20px 2px 20px;"
49 | on-attr {:background-color "hsl(100,90%,60%)"
50 | :color "hsl(0,90%,60%)"
51 | :font-weight "bold"
52 | :font-size "20px"
53 | :padding padding
54 | :display on}
55 | off-attr {:background-color "hsl(0,90%,60%)"
56 | :color "hsl(110,50%,50%)"
57 | :font-weight "bold"
58 | :font-size "20px"
59 | :padding padding
60 | :display off}
61 | toggle-attr {:width "5px;"
62 | :height "5px;"
63 | :padding "5px;"
64 | :border-width "1px;"
65 | :border-style "solid;"
66 | :border-color "black"}]
67 | (dom/div
68 | {:class "can-toggle"
69 | :style {:width "55px;"}}
70 | (dom/span {:style on-attr} "ON")
71 | (dom/a
72 | {:href "javascript:void(0)"}
73 | (dom/span
74 | {:class "can-toggle-switch"
75 | :style toggle-attr}))
76 | (dom/span {:style off-attr} "OFF")))))
77 | ```
78 |
79 | 6) Add click logic to toggle using `om/update!`
80 |
81 | ```clojure
82 | (defcomponent toggle-button-view [cursor _]
83 | (render
84 | [_]
85 | (let [on? (:toggle cursor)
86 | [on off] (if on?
87 | ["inline" "none"]
88 | ["none" "inline"])
89 | padding "2px 20px 2px 20px;"
90 | on-attr {:background-color "hsl(100,90%,60%)"
91 | :color "hsl(0,90%,60%)"
92 | :font-weight "bold"
93 | :font-size "20px"
94 | :padding padding
95 | :display on}
96 | off-attr {:background-color "hsl(0,90%,60%)"
97 | :color "hsl(110,50%,50%)"
98 | :font-weight "bold"
99 | :font-size "20px"
100 | :padding padding
101 | :display off}
102 | toggle-attr {:width "5px;"
103 | :height "5px;"
104 | :padding "5px;"
105 | :border-width "1px;"
106 | :border-style "solid;"
107 | :border-color "black"}]
108 | (dom/div
109 | {:class "can-toggle"
110 | :style {:width "55px;"}}
111 | (dom/span {:style on-attr} "ON")
112 | (dom/a
113 | {:href "javascript:void(0)"
114 | :on-click (fn [_]
115 | (om/update! cursor
116 | [:toggle]
117 | (not on?)))}
118 | (dom/span
119 | {:class "can-toggle-switch"
120 | :style toggle-attr}))
121 | (dom/span {:style off-attr} "OFF")))))
122 | ```
123 | 7) Replace or alter your main function to display the validation box view.
124 |
125 | ```clojure
126 | (defn main []
127 | (om/root
128 | toggle-button-view
129 | app-state
130 | {:target (. js/document (getElementById "app"))}))
131 | ```
132 | 8) Start a REPL with `lein repl`
133 |
134 | ```
135 | nREPL server started on port 54879 on host 127.0.0.1 - nrepl://127.0.0.1:54879
136 | REPL-y 0.3.5, nREPL 0.2.6
137 | Clojure 1.6.0
138 | Java HotSpot(TM) 64-Bit Server VM 1.8.0_05-b13
139 | Docs: (doc function-name-here)
140 | (find-doc "part-of-name-here")
141 | Source: (source function-name-here)
142 | Javadoc: (javadoc java-object-or-class-here)
143 | Exit: Control+D or (exit) or (quit)
144 | Results: Stored in vars *1, *2, *3, an exception in *e
145 | ```
146 |
147 | 9) Call `run` to start the back end and compile your ClojureScript.
148 |
149 | ```
150 | toggle-button.server=> (run)
151 | Starting figwheel.
152 | Starting web server on port 10555 .
153 | #
154 | toggle-button.server=> Compiling ClojureScript.
155 | Figwheel: Starting server at http://localhost:3449
156 | Figwheel: Serving files from '(dev-resources|resources)/public'
157 | Compiling "resources/public/js/app.js" from ("src/cljs" "env/dev/cljs")...
158 | Successfully compiled "resources/public/js/app.js" in 18.01 seconds.
159 | notifying browser that file changed: /js/out/local_state/core.js
160 | ```
161 |
162 | 10) Point your browser to http://localhost:port. You can find the port in the REPL message output => `Starting web server on port ...`
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/recipes/toggle-button/env/dev/cljs/toggle_button/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns toggle-button.dev
2 | (:require [toggle-button.core :as core]
3 | [figwheel.client :as figwheel :include-macros true]
4 | [cljs.core.async :refer [put!]]
5 | [weasel.repl :as weasel]))
6 |
7 | (enable-console-print!)
8 |
9 | (figwheel/watch-and-reload
10 | :websocket-url "ws://localhost:3449/figwheel-ws"
11 | :jsload-callback (fn [] (core/main)))
12 |
13 | (weasel/connect "ws://localhost:9001" :verbose true)
14 |
15 | (core/main)
16 |
--------------------------------------------------------------------------------
/recipes/toggle-button/env/prod/cljs/toggle_button/prod.cljs:
--------------------------------------------------------------------------------
1 | (ns toggle-button.prod
2 | (:require [toggle-button.core :as core]))
3 |
4 | (core/main)
5 |
--------------------------------------------------------------------------------
/recipes/toggle-button/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omcljs/om-cookbook/a44bb8d8a3330bae7bc000830a28bae4567ea830/recipes/toggle-button/preview.gif
--------------------------------------------------------------------------------
/recipes/toggle-button/project.clj:
--------------------------------------------------------------------------------
1 | (defproject toggle-button "0.1.0-SNAPSHOT"
2 | :description "FIXME: write description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :source-paths ["src/clj" "src/cljs"]
8 |
9 | :dependencies [[org.clojure/clojure "1.6.0"]
10 | [org.clojure/clojurescript "0.0-2371" :scope "provided"]
11 | [ring "1.3.1"]
12 | [compojure "1.2.0"]
13 | [enlive "1.1.5"]
14 | [om "0.7.3"]
15 | [figwheel "0.1.4-SNAPSHOT"]
16 | [environ "1.0.0"]
17 | [com.cemerick/piggieback "0.1.3"]
18 | [weasel "0.4.0-SNAPSHOT"]
19 | [leiningen "2.5.0"]
20 | [http-kit "2.1.19"]
21 | [prismatic/om-tools "0.3.3"]]
22 |
23 | :plugins [[lein-cljsbuild "1.0.3"]
24 | [lein-environ "1.0.0"]]
25 |
26 | :min-lein-version "2.5.0"
27 |
28 | :uberjar-name "toggle-button.jar"
29 |
30 | :cljsbuild {:builds {:app {:source-paths ["src/cljs"]
31 | :compiler {:output-to "resources/public/js/app.js"
32 | :output-dir "resources/public/js/out"
33 | :source-map "resources/public/js/out.js.map"
34 | :preamble ["react/react.min.js"]
35 | :externs ["react/externs/react.js"]
36 | :optimizations :none
37 | :pretty-print true}}}}
38 |
39 | :profiles {:dev {:repl-options {:init-ns toggle-button.server
40 | :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
41 |
42 | :plugins [[lein-figwheel "0.1.4-SNAPSHOT"]]
43 |
44 | :figwheel {:http-server-root "public"
45 | :port 3449
46 | :css-dirs ["resources/public/css"]}
47 |
48 | :env {:is-dev true}
49 |
50 | :cljsbuild {:builds {:app {:source-paths ["env/dev/cljs"]}}}}
51 |
52 | :uberjar {:hooks [leiningen.cljsbuild]
53 | :env {:production true}
54 | :omit-source true
55 | :aot :all
56 | :cljsbuild {:builds {:app
57 | {:source-paths ["env/prod/cljs"]
58 | :compiler
59 | {:optimizations :advanced
60 | :pretty-print false}}}}}})
61 |
--------------------------------------------------------------------------------
/recipes/toggle-button/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/recipes/toggle-button/resources/public/css/style.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | text-decoration: underline;
3 | }
4 |
--------------------------------------------------------------------------------
/recipes/toggle-button/src/clj/toggle_button/dev.clj:
--------------------------------------------------------------------------------
1 | (ns toggle-button.dev
2 | (:require [environ.core :refer [env]]
3 | [net.cgrand.enlive-html :refer [set-attr prepend append html]]
4 | [cemerick.piggieback :as piggieback]
5 | [weasel.repl.websocket :as weasel]
6 | [leiningen.core.main :as lein]))
7 |
8 | (def is-dev? (env :is-dev))
9 |
10 | (def inject-devmode-html
11 | (comp
12 | (set-attr :class "is-dev")
13 | (prepend (html [:script {:type "text/javascript" :src "/js/out/goog/base.js"}]))
14 | (prepend (html [:script {:type "text/javascript" :src "/react/react.js"}]))
15 | (append (html [:script {:type "text/javascript"} "goog.require('toggle_button.dev')"]))))
16 |
17 | (defn browser-repl []
18 | (piggieback/cljs-repl :repl-env (weasel/repl-env :ip "0.0.0.0" :port 9001)))
19 |
20 | (defn start-figwheel []
21 | (future
22 | (print "Starting figwheel.\n")
23 | (lein/-main ["figwheel"])))
24 |
--------------------------------------------------------------------------------
/recipes/toggle-button/src/clj/toggle_button/server.clj:
--------------------------------------------------------------------------------
1 | (ns toggle-button.server
2 | (:require [clojure.java.io :as io]
3 | [toggle-button.dev :refer [is-dev? inject-devmode-html browser-repl start-figwheel]]
4 | [compojure.core :refer [GET defroutes]]
5 | [compojure.route :refer [resources]]
6 | [compojure.handler :refer [api]]
7 | [net.cgrand.enlive-html :refer [deftemplate]]
8 | [ring.middleware.reload :as reload]
9 | [environ.core :refer [env]]
10 | [org.httpkit.server :refer [run-server]]))
11 |
12 | (deftemplate page
13 | (io/resource "index.html") [] [:body] (if is-dev? inject-devmode-html identity))
14 |
15 | (defroutes routes
16 | (resources "/")
17 | (resources "/react" {:root "react"})
18 | (GET "/*" req (page)))
19 |
20 | (def http-handler
21 | (if is-dev?
22 | (reload/wrap-reload (api #'routes))
23 | (api routes)))
24 |
25 | (defn run [& [port]]
26 | (defonce ^:private server
27 | (do
28 | (if is-dev? (start-figwheel))
29 | (let [port (Integer. (or port (env :port) 10555))]
30 | (print "Starting web server on port" port ".\n")
31 | (run-server http-handler {:port port
32 | :join? false}))))
33 | server)
34 |
35 | (defn -main [& [port]]
36 | (run port))
37 |
--------------------------------------------------------------------------------
/recipes/toggle-button/src/cljs/toggle_button/core.cljs:
--------------------------------------------------------------------------------
1 | (ns toggle-button.core
2 | (:require [om.core :as om :include-macros true]
3 | [om-tools.dom :as dom :include-macros true]
4 | [om-tools.core :refer-macros [defcomponent]]))
5 |
6 |
7 | (defonce app-state (atom {:toggle true}))
8 |
9 | (defcomponent toggle-button-view [cursor _]
10 | (render
11 | [_]
12 | (let [on? (:toggle cursor)
13 | [on off] (if on?
14 | ["inline" "none"]
15 | ["none" "inline"])
16 | padding "2px 20px 2px 20px;"
17 | on-attr {:background-color "hsl(100,90%,60%)"
18 | :color "hsl(0,90%,60%)"
19 | :font-weight "bold"
20 | :font-size "20px"
21 | :padding padding
22 | :display on}
23 | off-attr {:background-color "hsl(0,90%,60%)"
24 | :color "hsl(110,50%,50%)"
25 | :font-weight "bold"
26 | :font-size "20px"
27 | :padding padding
28 | :display off}
29 | toggle-attr {:width "5px;"
30 | :height "5px;"
31 | :padding "5px;"
32 | :border-width "1px;"
33 | :border-style "solid;"
34 | :border-color "black"}]
35 | (dom/div
36 | {:class "can-toggle"
37 | :style {:width "55px;"}}
38 | (dom/span {:style on-attr} "ON")
39 | (dom/a
40 | {:href "javascript:void(0)"
41 | :on-click (fn [_]
42 | (om/update! cursor
43 | [:toggle]
44 | (not on?)))}
45 | (dom/span
46 | {:class "can-toggle-switch"
47 | :style toggle-attr}))
48 | (dom/span {:style off-attr} "OFF")))))
49 |
50 | (defn main []
51 | (om/root
52 | toggle-button-view
53 | app-state
54 | {:target (. js/document (getElementById "app"))}))
55 |
--------------------------------------------------------------------------------
/recipes/toggle-button/system.properties:
--------------------------------------------------------------------------------
1 | java.runtime.version=1.8
2 |
--------------------------------------------------------------------------------
/recipes/views/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | *jar
3 | /lib/
4 | /classes/
5 | /out/
6 | /target/
7 | .lein-deps-sum
8 | .lein-repl-history
9 | .lein-plugins/
10 |
--------------------------------------------------------------------------------
/recipes/views/README.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | You want create different views for the same data and toggle them
4 | depending on user's selection.
5 |
6 | # Solution
7 |
8 | 1. Create a project structure using Leiningen template
9 | [mies](https://github.com/swannodette/mies)
10 |
11 | ```
12 | $ lein new mies views
13 | ```
14 |
15 | 2. We will use [om-boostrap](http://om-bootstrap.herokuapp.com/) to
16 | create table and panels. Add we'll also add Om and
17 | [sablono](https://github.com/r0man/sablono) to our
18 | dependencies:
19 |
20 | ```clojure
21 | :dependencies [[org.clojure/clojure "1.6.0"]
22 | [org.clojure/clojurescript "0.0-2371"]
23 |
24 | [racehub/om-bootstrap "0.3.1"]
25 | [om "0.8.0-alpha2"]
26 | [sablono "0.2.20"]]
27 | ```
28 |
29 | 3. Let's add React and Boostrap CSS to our html file:
30 |
31 | ```
32 |
33 |
34 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | ```
46 |
47 | 4. We're going to use a toolbar with two buttons as our view toggle:
48 |
49 | ```clojure
50 | (defn toggle [cursor owner opts]
51 | (om/component
52 | (b/toolbar {}
53 | (b/button {:active? (when (= :table (-> cursor :selected)) true)
54 | :on-click (fn [_] (om/update! cursor :selected :table))}
55 | "Table")
56 | (b/button {:active? (when (= :panels (-> cursor :selected)) true)
57 | :on-click (fn [_] (om/update! cursor :selected :panels))}
58 | "Panels"))))
59 | ```
60 |
61 | 5. Depending on the view selected we'll want to build a different
62 | component. We can use a multimethod for that:
63 |
64 | ```clojure
65 | (defmulti view (fn [cursor owner opts] (-> cursor :view :selected)))
66 | ```
67 |
68 | If the view selected is a table, we'll build this:
69 |
70 | ```clojure
71 | (defmethod view :table [cursor owner opts]
72 | (om/component
73 | (table {:striped? true :bordered? true :hover? true}
74 | (d/thead
75 | (d/tr
76 | (d/th "Timestamp")
77 | (d/th "Value")))
78 | (d/tbody
79 | (for [v (:data cursor)]
80 | (d/tr
81 | (d/td (:timestamp v))
82 | (d/td (:value v))))))))
83 | ```
84 |
85 | Or if it's panels, then we'll build this:
86 |
87 | ```clojure
88 | (defmethod view :panels [cursor owner opts]
89 | (om/component
90 | (d/div
91 | (for [v (:data cursor)]
92 | (p/panel {}
93 | (d/p
94 | (d/p (str "Timestamp: " (:timestamp v)))
95 | (d/p (str "Value: " (:value v)))))))))
96 | ```
97 |
98 | 6. That's it. Run `lein cljsbuild once`, open up `index.html` in
99 | your browser and try out selecting different views.
100 |
101 | Multimethods are a great way to build different components based on
102 | some criteria.
103 |
--------------------------------------------------------------------------------
/recipes/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/recipes/views/project.clj:
--------------------------------------------------------------------------------
1 | (defproject views "0.1.0-SNAPSHOT"
2 | :description "FIXME: write this!"
3 | :url "http://example.com/FIXME"
4 |
5 | :dependencies [[org.clojure/clojure "1.6.0"]
6 | [org.clojure/clojurescript "0.0-2371"]
7 |
8 | [racehub/om-bootstrap "0.3.1"]
9 | [om "0.8.0-alpha2"]
10 | [sablono "0.2.20"]]
11 |
12 | :plugins [[lein-cljsbuild "1.0.4-SNAPSHOT"]]
13 |
14 | :source-paths ["src"]
15 |
16 | :cljsbuild {
17 | :builds [{:id "views"
18 | :source-paths ["src"]
19 | :compiler {
20 | :output-to "views.js"
21 | :output-dir "out"
22 | :optimizations :none
23 | :source-map true}}]})
24 |
--------------------------------------------------------------------------------
/recipes/views/src/views/core.cljs:
--------------------------------------------------------------------------------
1 | (ns views.core
2 | (:require [om-bootstrap.button :as b]
3 | [om-bootstrap.table :refer [table]]
4 | [om-bootstrap.panel :as p]
5 | [om-tools.dom :as d :include-macros true]
6 | [om.core :as om :include-macros true]
7 | [sablono.core :as html :refer-macros [html]]))
8 |
9 | (enable-console-print!)
10 |
11 | (defonce app-state (atom {:view {:selected :table}
12 | :data [{:value 240000 :timestamp "2014-01-01"}
13 | {:value 260000 :timestamp "2014-02-01"}
14 | {:value 290000 :timestamp "2014-03-01"}
15 | {:value 70000 :timestamp "2014-04-01"}
16 | {:value 100000 :timestamp "2014-05-01"}
17 | {:value 120000 :timestamp "2014-06-01"}
18 | {:value 240000 :timestamp "2014-07-01"}
19 | {:value 220000 :timestamp "2014-08-01"}
20 | {:value 360000 :timestamp "2014-09-01"}
21 | {:value 260000 :timestamp "2014-10-01"}
22 | {:value 250000 :timestamp "2014-11-01"}
23 | {:value 190000 :timestamp "2014-12-01"}]}))
24 |
25 | (defmulti view (fn [cursor owner opts] (-> cursor :view :selected)))
26 |
27 | (defmethod view :table [cursor owner opts]
28 | (om/component
29 | (table {:striped? true :bordered? true :hover? true}
30 | (d/thead
31 | (d/tr
32 | (d/th "Timestamp")
33 | (d/th "Value")))
34 | (d/tbody
35 | (for [v (:data cursor)]
36 | (d/tr
37 | (d/td (:timestamp v))
38 | (d/td (:value v))))))))
39 |
40 | (defmethod view :panels [cursor owner opts]
41 | (om/component
42 | (d/div
43 | (for [v (:data cursor)]
44 | (p/panel {}
45 | (d/p
46 | (d/p (str "Timestamp: " (:timestamp v)))
47 | (d/p (str "Value: " (:value v)))))))))
48 |
49 | (defn toggle [cursor owner opts]
50 | (om/component
51 | (b/toolbar {}
52 | (b/button {:active? (when (= :table (-> cursor :selected)) true)
53 | :on-click (fn [_] (om/update! cursor :selected :table))}
54 | "Table")
55 | (b/button {:active? (when (= :panels (-> cursor :selected)) true)
56 | :on-click (fn [_] (om/update! cursor :selected :panels))}
57 | "Panels"))))
58 |
59 | (om/root
60 | (fn [app owner]
61 | (reify
62 | om/IRender
63 | (render [_]
64 | (html
65 | [:div.col-md-6
66 | [:h1 "Views"]
67 | [:div
68 | [:div {:style {:padding-bottom "3px"}}
69 | ;; Button to toggle the view
70 | (om/build toggle (:view app))]
71 | ;; View component
72 | (om/build view app)]]))))
73 | app-state {:target (. js/document (getElementById "app"))})
74 |
--------------------------------------------------------------------------------