Skip to content

Commit

Permalink
add in visualizations, cleanup html a bit, and add a bunch of new stu…
Browse files Browse the repository at this point in the history
…ff in the readme

Signed-off-by: Chris Granger <ibdknox@gmail.com>
  • Loading branch information
ibdknox committed Aug 26, 2011
1 parent 845073a commit 59f446f
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 26 deletions.
90 changes: 88 additions & 2 deletions README.md
Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand Down
82 changes: 58 additions & 24 deletions src/pinot/html.cljs
Expand Up @@ -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
;; ********************************************
Expand All @@ -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))))

;; ********************************************
Expand Down Expand Up @@ -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]
Expand All @@ -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))

Expand All @@ -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))

Expand All @@ -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)))
3 changes: 3 additions & 0 deletions src/pinot/util/js.cljs
Expand Up @@ -8,3 +8,6 @@

(defn log [text]
(. js/console (log text)))

(defn as-int [n]
(js/parseInt n))
83 changes: 83 additions & 0 deletions src/pinot/visual.cljs
@@ -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.