From 43e5319db24f2d072750a873f46dc244d9ed5ab4 Mon Sep 17 00:00:00 2001 From: Ilia Ablamonov Date: Fri, 18 Jan 2013 00:32:34 +0400 Subject: [PATCH] Binding stuff. Documentation. --- .gitignore | 2 ++ README.md | 33 +++++++++++++++++++++- project.clj | 2 +- src/widje/core.cljs | 65 ++++++++++++++++++++++++++++++++++++++------ src/widje/macros.clj | 3 +- src/widje/role.cljs | 15 +++------- src/widje/util.cljs | 57 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 154 insertions(+), 23 deletions(-) create mode 100644 src/widje/util.cljs diff --git a/.gitignore b/.gitignore index 51c331a..a41027d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ pom.xml.asc /targets/ /target/ .lein-deps-sum +.idea +*.iml diff --git a/README.md b/README.md index b135760..955c804 100644 --- a/README.md +++ b/README.md @@ -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 @@ -30,6 +30,37 @@ Widget is a function that return DOM node. It combines crate template with suppo => HTMLElement

Do stuff

``` +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 diff --git a/project.clj b/project.clj index 4fde30b..5366b63 100644 --- a/project.clj +++ b/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" diff --git a/src/widje/core.cljs b/src/widje/core.cljs index 0776ec3..48f842b 100644 --- a/src/widje/core.cljs +++ b/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)))))) + diff --git a/src/widje/macros.clj b/src/widje/macros.clj index 190802f..644102c 100644 --- a/src/widje/macros.clj +++ b/src/widje/macros.clj @@ -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))) diff --git a/src/widje/role.cljs b/src/widje/role.cljs index 2eab5bb..b8a2a16 100644 --- a/src/widje/role.cljs +++ b/src/widje/role.cljs @@ -4,25 +4,18 @@ (:myrole ($ \"

test

\")) => HTMLElement

" (: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)))) diff --git a/src/widje/util.cljs b/src/widje/util.cljs new file mode 100644 index 0000000..92d5644 --- /dev/null +++ b/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))))