Permalink
Browse files

add in visualizations, cleanup html a bit, and add a bunch of new stu…

…ff in the readme

Signed-off-by: Chris Granger <ibdknox@gmail.com>
  • Loading branch information...
1 parent 845073a commit 59f446ff028744e995563961ff6de275abe14238 @ibdknox committed Aug 26, 2011
Showing with 232 additions and 26 deletions.
  1. +88 −2 README.md
  2. +58 −24 src/pinot/html.cljs
  3. +3 −0 src/pinot/util/js.cljs
  4. +83 −0 src/pinot/visual.cljs
View
@@ -17,7 +17,57 @@ Then make sure you have your lib folder on your classpath.
If you are in a noir project, make sure to get [noir-cljs](https://github.com/ibdknox/noir-cljs) so you have your normal "make a change, refresh" workflow.
-###remotes
+###Dom manipulation
+
+Pinot includes a basic set of dom interaction pieces, including an implementation of hiccup:
+
+```clojure
+(ns client.test
+ (:require [pinot.html :as html])
+ (:require-macros [pinot.macros :as pm]))
+
+(def x (html/html [:p [:em "hey"]]))
+(html/css x {:color :blue})
+(html/attr x {:class "para"})
+(html/val (html/dom-find "input"))
+(html/append-to (html/dom-find "#content div.body") x)
+```
+
+Pinot also includes `(defpartial)` like in Noir, however Pinot derives even greater advantage from it:
+
+```clojure
+(mac/defpartial todo [{:keys [done? text]}]
+ [:li
+ [:h2 t]
+ [:span {:class (when done? "done")}]])
+
+;;You can pass the partial function to dom-find to find all the todos.
+(def all-todos (html/dom-find todo))
+```
+
+###Events
+
+Events can also take advantage of partials
+
+```clojure
+(ns playground.client.test
+ (:require [pinot.html :as html]
+ [pinot.events :as pe]))
+
+(events/on (html/dom-find "li") :click
+ (fn [me e]
+ (html/css me {:background :blue})
+ (events/prevent e)))
+
+;; Partials can also be passed here, allowing you to add an event to every
+;; element created through that partial.
+(events/on todo :click
+ (fn [me e]
+ (html/css me {:background :blue})
+ (events/prevent e)))
+```
+
+###Remotes
Remotes let you make calls to a noir server without having to think about XHR. On the client-side you simply have code that looks like this:
@@ -58,10 +108,46 @@ The noir side of things is just as simple. All you do is declare a remote using
(server/start 8080)
```
+###Visualization
+
+```Clojure
+(ns playground.client.test
+ (:require [pinot.html :as html]
+ [pinot.visual :as vis])
+ (:require-macros [pinot.macros :as mac]))
+
+(def items (range 0 10))
+
+;;For SVG we have to namespace our elements
+(mac/defpartial canvas []
+ [:svg:svg {:width 800 :height 400}])
+
+(mac/defpartial item [x]
+ [:svg:circle {:r (* 2 x)}])
+
+(html/append-to (html/dom-find "#wrapper")
+ (canvas))
+
+(-> (vis/visual items)
+ (vis/elem item)
+ (vis/attr :stroke "#333")
+ (vis/attr :fill "#777")
+ (vis/attr :cx #(+ 20 (rand-int 800)))
+ (vis/attr :cy #(+ 80 (* 10 (mod % 4))))
+ (vis/enter (partial html/append-to (html/dom-find "svg"))))
+
+(-> (vis/select item)
+ (vis/transition 500)
+ (vis/data items)
+ (vis/attr :cx #(* 50 %))
+ (vis/attr :cy #(+ 30 (* 20 (mod % 3))))
+ (vis/start))
+```
+
## Docs
* Coming soon...
-Checkout src/todos/core.cljs as an example.
+Checkout examples/todo.cljs as an example.
## Roadmap
View
@@ -8,6 +8,9 @@
[pinot.util.clj :as pclj]
[pinot.util.js :as pjs]))
+(def xmlns {:xhtml "http://www.w3.org/1999/xhtml"
+ :svg "http://www.w3.org/2000/svg"})
+
;; ********************************************
;; Attribute manipulation
;; ********************************************
@@ -18,23 +21,35 @@
(css elem prop value))
(nil? v) (gstyle/getStyle elem (name k))
:else (doseq [el (pclj/->coll elem)]
- (gstyle/setStyle el (name k) (name v)))))
-
-(defn attr [elem attrs]
- (if-not (map? attrs)
- (. elem (getAttribute (name attrs)))
- (do
- (doseq [el (pclj/->coll elem)
- [k v] attrs]
- (. el (setAttribute (name k) v)))
- elem)))
+ (gstyle/setStyle el (name k) (name v))))
+ elem)
+
+(defn attr
+ ([elem attrs]
+ (if-not (map? attrs)
+ (. elem (getAttribute (name attrs)))
+ (do
+ (doseq [[k v] attrs]
+ (attr elem k v))
+ elem)))
+ ([elem k v]
+ (doseq [el (pclj/->coll elem)]
+ (. el (setAttribute (name k) v)))
+ elem))
+
+(defn text [elem v]
+ (doseq [el (pclj/->coll elem)]
+ (dom/setTextContent el v))
+ elem)
(defn val [elem & [v]]
(let [elem (if (seq elem)
(first elem)
elem)]
(if v
- (forms/setValue elem v)
+ (do
+ (forms/setValue elem v)
+ elem)
(forms/getValue elem))))
;; ********************************************
@@ -69,12 +84,19 @@
(when (not (or (keyword? tag) (symbol? tag) (string? tag)))
(throw (str tag " is not a valid tag name.")))
(let [[_ tag id class] (re-matches re-tag (name tag))
- tag-attrs {:id (or id nil)
- :class (if class (string/replace class #"\." " "))}
+ [nsp tag] (let [[nsp t] (string/split tag #":")
+ ns-xmlns (xmlns (keyword nsp))]
+ (if t
+ [(or ns-xmlns nsp) t]
+ [(:xhtml xmlns) t]))
+ tag-attrs (into {}
+ (filter #(not (nil? (second %)))
+ {:id (or id nil)
+ :class (if class (string/replace class #"\." " "))}))
map-attrs (first content)]
(if (map? map-attrs)
- [tag (merge tag-attrs map-attrs) (next content)]
- [tag tag-attrs content])))
+ [nsp tag (merge tag-attrs map-attrs) (next content)]
+ [nsp tag tag-attrs content])))
(defn parse-content [elem content]
@@ -85,10 +107,13 @@
(rest content))
content)))
+(defn create-elem [nsp tag]
+ (. js/document (createElementNS nsp tag)))
+
(defn elem-factory [tag-def]
- (let [[tag attrs content] (normalize-element tag-def)
- elem (dom/createElement tag (pjs/map->js attrs))]
- (attr elem {:pinotId (swap! elem-id inc)})
+ (let [[nsp tag attrs content] (normalize-element tag-def)
+ elem (create-elem nsp tag)]
+ (attr elem (merge attrs {:pinotId (swap! elem-id inc)}))
(as-content elem content)
elem))
@@ -99,6 +124,11 @@
;; Dom interaction functions
;; ********************************************
+(defn pinot-group [func]
+ (when (fn? func)
+ (let [elem (func)]
+ (attr (first elem) :pinotGroup))))
+
(defn parent [elem]
(.parentNode elem))
@@ -124,12 +154,16 @@
(doseq [elem (pclj/->coll elem)]
(dom/removeNode elem)))
-
-(defn dom-find [q]
- (let [results (dom/query q)
- len (.length results)]
+(defn nodelist->coll [nl]
;; The results are a nodelist, which looks like an array, but
;; isn't one. We have to turn it into a collection that we can
;; work with.
- (for [x (range 0 len)]
- (aget results x))))
+ (for [x (range 0 (.length nl))]
+ (aget nl x)))
+
+(defn dom-find [query]
+ (let [q (if (fn? query)
+ (str "[pinotGroup$=" (pinot-group query) "]")
+ query)
+ results (dom/query q)]
+ (nodelist->coll results)))
View
@@ -8,3 +8,6 @@
(defn log [text]
(. js/console (log text)))
+
+(defn as-int [n]
+ (js/parseInt n))
View
@@ -0,0 +1,83 @@
+(ns pinot.visual
+ (:require [pinot.html :as html]
+ [pinot.util.js :as pjs]))
+
+(defn visual [data]
+ {:data data
+ :attr {}})
+
+(defn attr [vis k v]
+ (assoc-in vis [:attr k] v))
+
+(defn elem [vis el]
+ (assoc vis :elem el))
+
+(defn apply-attr [elem attr d idx]
+ (doseq [[k v] attr]
+ (let [v (if (fn? v)
+ (v d idx)
+ v)]
+ (html/attr elem k v))))
+
+(defn create-elem [elem d idx]
+ (cond
+ (fn? elem) (elem d idx)
+ (vector? elem) (html/html elem)
+ (keyword? elem) (html/html [elem])
+ :else elem))
+
+(defn enter [{:keys [data attr elem]} method]
+ (doseq [[idx d] (map-indexed vector data)]
+ (let [cur (create-elem elem d idx)]
+ (apply-attr cur attr d idx)
+ (method cur))))
+
+(defn select [query]
+ (html/dom-find query))
+
+(defn data [vis d]
+ (assoc vis :data d))
+
+(defn transition [elems dur]
+ {:elems elems
+ :dur dur})
+
+(def animation-frame
+ (or (.requestAnimationFrame js/window)
+ (.webkitRequestAnimationFrame js/window)
+ (.mozRequestAnimationFrame js/window)
+ (.oRequestAnimationFrame js/window)
+ (.msRequestAnimationFrame js/window)
+ (fn [callback] (js/setTimeout callback 17))))
+
+(defn do-animation [{:keys [start-time dur tween] :as anim}]
+ (let [now (. js/Date (now))
+ elapsed (min (/ (- now start-time) dur) 1)]
+ (tween elapsed)
+ (when-not (= elapsed 1)
+ (animation-frame (partial do-animation anim)))))
+
+(defn end-states [elems attr data]
+ (for [[el d] (map list elems data)]
+ (for [[k v] attr]
+ (let [v (if (fn? v) (v d) v)
+ init (pjs/as-int (html/attr el k))
+ delta (- v init)]
+ [k init delta]))))
+
+(defn tween [{:keys [elems attr data]}]
+ (let [final (end-states elems attr data)
+ tweens (map list elems final)]
+ (fn [elapsed-perc]
+ (doseq [[el attrs] tweens
+ [k init delta] attrs]
+ (let [elapsed-delta (* elapsed-perc delta)
+ change (+ init elapsed-delta)]
+ (html/attr el k change))))))
+
+(defn start [{:keys [dur] :as transition}]
+ (let [now (. js/Date (now))]
+ (do-animation {:start-time now
+ :dur dur
+ :tween (tween transition)})))
+

0 comments on commit 59f446f

Please sign in to comment.