Skip to content

Commit

Permalink
Binding stuff. Documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Flamefork committed Jan 17, 2013
1 parent 48aa46a commit 43e5319
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 23 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -6,3 +6,5 @@ pom.xml.asc
/targets/
/target/
.lein-deps-sum
.idea
*.iml
33 changes: 32 additions & 1 deletion README.md
Expand Up @@ -6,7 +6,7 @@ Widje is html templating library for ClojureScript based on the awesome [crate](

Widget is a function that return DOM node. It combines crate template with supporting logic (event handling etc) into single logical unit. Also widget keep track of each instance it creates and provide easy way to search them by first argument.

## Usage
## Widget usage

```clojure
(ns myapp
Expand All @@ -30,6 +30,37 @@ Widget is a function that return DOM node. It combines crate template with suppo
=> HTMLElement <div class="todo"><p class="-text">Do stuff</p></div>
```

There are also `widget` (= `fn`) and `letwidget` (= `letfn`) macros.

## Binding usage

This is actually a documentation for the awesome `crate` binding feature.

```clojure
(defwidget item [item-atm]
[:li
(bound item-atm :name)])

(defwidget items [items-coll-atm]
[:ul
(bound-coll items-coll-atm {:as item})])
```

Widje adds `(bound* [atoms*] func?)` to crate features for binding to multiple atoms.

## Utils

See `widje.util` for documentation for:

- `evt->key`
- `bound-checkbox`
- `checkbox-checked?`
- `check!`

`macros/listen` macro is an easy way to bind to events (see widget usage example).

`core/render` is an easy way to render widget to some container.

## License

Copyright (C) 2013 Ilia Ablamonov
Expand Down
2 changes: 1 addition & 1 deletion project.clj
@@ -1,4 +1,4 @@
(defproject widje "0.1.2"
(defproject widje "0.1.3"
:description "Templating System for ClojureScript"
:url "https://github.com/Flamefork/widje"
:license {:name "Eclipse Public License - v 1.0"
Expand Down
65 changes: 56 additions & 9 deletions src/widje/core.cljs
@@ -1,20 +1,67 @@
(ns widje.core
"See widje.macros for documenation."
(:require [jayq.core :as jq]))
"See widje.macros for widget-related stuff."
(:require [jayq.core :as jq]
[crate.binding :as binding]
[widje.role :as role]))

(defn instances [widget]
;; General

(defn render
"Renders widget, inserts it into container and notifies listeners."
[container widget & params]
(let [node (apply widget params)]
(jq/inner (jq/$ container) node)
(jq/trigger (:notify node) :render)))

;; Instances

(defn instances
"Returns all instances (root DOM elements) created by widget.
Result may be map (for parametrized widgets) or vector."
[widget]
@(.-_instances widget))

(defn find-instance [widget param]
(defn find-instance
"Returns instance (root DOM element) created by widget with
param passed as first argument. Works only for widgets with
at least one argument."
[widget param]
((instances widget) param))

(defn render [container widget & params]
"Renders widget, inserts it into container and notifies listeners."
(let [node (apply widget params)]
(jq/inner (jq/$ container) node)
(jq/trigger (:notify node) :render)))
;; Binding

(declare atoms-binding)

(def bound
^{:doc "Returns value of func (or just value) bound to atm.
func is a function of one argument: current value of atm."
:arglists '([atm func?])}
binding/bound)

(defn bound*
^{:doc "Returns value of func (or just value) bound to all provided atoms.
func is a function of (count atm*) arguments: current values of atm(s)."
:arglists '([[atm*] func?])}
[atoms & [func]]
(let [func (or func identity)]
(atoms-binding. atoms func)))

(def bound-coll
^{:doc "Returns value of func (:as opts) bound to each element of collection atom."
:arglists '(atm path? opts?)}
binding/bound-coll)

(def map-bound
^{:doc "Returns value of as func bound to each element of map atom."
:arglists '(as atm opts?)}
binding/map-bound)

;; Internals

(deftype atoms-binding [atoms value-func]
crate.binding/bindable
(-value [this] (apply value-func (map deref atoms)))
(-on-change [this func]
(doseq [atm atoms]
(add-watch atm (gensym "atom-binding") #(func (crate.binding/-value this))))))

3 changes: 2 additions & 1 deletion src/widje/macros.clj
Expand Up @@ -44,8 +44,9 @@
([name params tags bindings & body]
`(def ~name (widje.macros/widget ~params ~tags ~bindings ~@body))))

(defmacro letwidget [widgetspecs & body]
(defmacro letwidget
"Similar to 'letfn'"
[widgetspecs & body]
`(let ~(vec (interleave
(map first widgetspecs)
(map #(cons 'widje.macros/widget (rest %)) widgetspecs)))
Expand Down
15 changes: 4 additions & 11 deletions src/widje/role.cljs
Expand Up @@ -4,25 +4,18 @@
(:myrole ($ \"<div><p class=\"-myrole\">test</p></div>\")) => HTMLElement<p \"test\">"
(:require [jayq.core :as jq]))

(defn $zero? [$obj]
"Returns true if zero elements matched by $obj, else false."
(zero? (.-length $obj)))

(defn $-with-context [selector context]
"Same as $, but matches also against context root."
(defn $-with-context
"Same as $, but matches against context root node too."
[selector context]
(let [found-nodes (jq/$ selector context)]
(if (.is (jq/$ context) selector)
(.add found-nodes context)
found-nodes)))

(defn role-selector [role]
"Returns .-role for :role"
(str ".-" (name role)))

(extend-type js/Node
ILookup
(-lookup
([this k]
($-with-context (role-selector k) this))
($-with-context (str ".-" (name k)) this))
([this k _]
(-lookup this k))))
57 changes: 57 additions & 0 deletions src/widje/util.cljs
@@ -0,0 +1,57 @@
(ns widje.util
(:require [jayq.core :as jq])
(:use-macros [widje.macros :only [defwidget]]))

;; Events

(defn evt->key
"Maps event to readable key names"
[e]
(get {
08 :backspace
09 :tab
13 :enter
16 :shift
17 :ctrl
18 :alt
19 :pause
20 :capslock
27 :esc
32 :space
33 :page-up
34 :page-down
35 :end
36 :home
37 :left-arrow
38 :up-arrow
39 :right-arrow
40 :down-arrow
45 :insert
46 :delete
} (.-keyCode e)))

;; Checkbox

;; sidenote: checkbox state querying and manipulation is
;; definitely not well-designed in html/dom

(defn checkbox-checked?
"Returns boolean state of checkbox"
[checkbox]
(jq/is ($ checkbox) ":checked"))

(defn check! [checkbox value]
"Sets boolean state of checkbox"
(if value
(jq/attr ($ checkbox) "checked" true)
(jq/remove-attr ($ checkbox) "checked")))

; Widget: checkbox bound to atom via val-fn
(defwidget bound-checkbox [id classes atm val-fn]
[:input.-checkbox {:id id
:class (str classes " -checkbox")
:type "checkbox"}]
[checkbox]
(check! checkbox (val-fn @atm))
(add-watch atm (gensym "bound-checkbox")
#(check! checkbox (val-fn %4))))

0 comments on commit 43e5319

Please sign in to comment.