Skip to content

Instantly share code, notes, and snippets.

@jjttjj
Last active November 13, 2019 22:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jjttjj/9d29a3d93f5598068eb2d5195265e614 to your computer and use it in GitHub Desktop.
Save jjttjj/9d29a3d93f5598068eb2d5195265e614 to your computer and use it in GitHub Desktop.
(ns app.index
(:require [cljs.core.async :as a]
[lib.dom :as d :refer-macros [tpl-let tpl-with tpl=]]))
;;EXAMPLE USAGE
(enable-console-print!)
(def todos-db (atom {}))
(defn todo-input []
(let [val (atom "")]
(d/input
{:onkeyup #(reset! val (.-target.value %))
:onkeydown #(case (.-which %)
13 (do (swap! todos-db
assoc
(random-uuid)
{:text @val
:done? false})
(js/console.log %)
(set! (.-target.value %) "")
)
nil)})))
(defn todo-list []
(d/div
((d/tpl
(fn [todos]
(d/ul
(for [[id {:keys [text done?]}] todos]
(d/li (d/label {:for id} text)
(d/input {:id id
:type "checkbox"
:checked done?
:onchange #(swap! todos-db
assoc-in
[id
:done?]
(.-target.checked %))}))))))
todos-db)
;;derefs given atoms, binds to same name
(d/tpl-with todos-db
(d/div "count:" (count todos-db)))
;;more magical version, auto-walks all watchables
(d/tpl=
(d/div (pr-str todos-db)))))
(defn ui []
(d/div
(todo-input)
(todo-list)))
(defn init []
(doto (d/$ "#app")
d/clear
(d/append (ui))))
(init)
(ns lib.dom
(:require [javelin.core :as j]))
(def to-list #(into '() (reverse %)))
(defmacro tpl= [expr]
(let [[f args] (j/hoist expr &env)]
(to-list `((tpl ~f) ~@args))))
;;todo:consider destructuring
(defmacro tpl-let [bindings & body]
`(let ~bindings ;;(destructure bindings)
((tpl
(fn ~(vec (take-nth 2 bindings)) ~@body))
~@(take-nth 2 bindings))))
(defmacro tpl-with [& args]
(let [tpl-args (butlast args)
body (last args)]
`((tpl (fn ~(vec tpl-args) ~body))
~@tpl-args)))
(ns lib.dom
(:require [goog.dom :as gdom]
[goog.object :as gobj]
[goog.array :as garr]
[goog.style :as gstyle]
["set-dom" :as set-dom])
(:require-macros [lib.dom :refer [tpl-let tpl-with tpl=]]))
(defn $ [selector]
(js/document.querySelector selector))
(defn append [node & children]
(doto node (gdom/append (garr/flatten (clj->js children)))))
(defn frag [& kids]
(doto (js/document.createDocumentFragment) (append kids)))
(defn clear [node]
(gdom/removeChildren node)
node)
(defn set-styles [node kvs]
(doseq [[k v] kvs]
(gobj/set (.-style node) (name k) (str v)))
node)
(defn elem-fn
"Creates a function which creates a dom node of type tagname. The
created function takes an optional attribute map as a first argument
followed by any number of child nodes. Nodes are created via
`goog.dom/createDom`, so attributes are created via
`goog.dom/setProperties`."
[tag-name]
(fn [& attrs-kids]
(let [maybe-attrs (first attrs-kids)
is-attr? (map? maybe-attrs)
attrs (if is-attr? maybe-attrs {})
styled? (and is-attr? (:style attrs))
kids (if is-attr? (rest attrs-kids) attrs-kids)
elem (->> (map #(if (number? %) (str %) %) kids)
clj->js
garr/flatten
(gdom/createDom tag-name (clj->js attrs)))]
(cond-> elem ;;note: this slows things down
styled? (set-styles (:style attrs))))))
(defn reconciliate! [old-el new-el]
(set-dom old-el new-el))
(defn deref*
"If x is derefable, deref it, otherwise returns x."
[x]
(if (satisfies? cljs.core/IDeref x) @x x))
(defn tpl
"Takes a dom-element-returning-function of any number of arguments
that may implement IWatchable. Returns the element. When any
watchable arguments change, re-runs the function and reconciliates
the old element with the new one"
[f]
(fn [& args]
(let [e (apply f (map deref* args))]
(doseq [arg (filter #(satisfies? cljs.core/IWatchable %) args)]
(add-watch
arg (gensym)
(fn [k ref o new]
(->> (map #(if (= % ref) new (deref* %)) args)
(apply f)
(reconciliate! e)))))
e)))
(def a (elem-fn "a"))
(def abbr (elem-fn "abbr"))
(def address (elem-fn "address"))
(def area (elem-fn "area"))
(def article (elem-fn "article"))
(def aside (elem-fn "aside"))
(def audio (elem-fn "audio"))
(def b (elem-fn "b"))
(def base (elem-fn "base"))
(def bdi (elem-fn "bdi"))
(def bdo (elem-fn "bdo"))
(def blockquote (elem-fn "blockquote"))
(def br (elem-fn "br"))
(def button (elem-fn "button"))
(def canvas (elem-fn "canvas"))
(def caption (elem-fn "caption"))
(def cite (elem-fn "cite"))
(def code (elem-fn "code"))
(def col (elem-fn "col"))
(def colgroup (elem-fn "colgroup"))
(def data (elem-fn "data"))
(def datalist (elem-fn "datalist"))
(def dd (elem-fn "dd"))
(def del (elem-fn "del"))
(def details (elem-fn "details"))
(def dfn (elem-fn "dfn"))
(def dialog (elem-fn "dialog"))
(def div (elem-fn "div"))
(def dl (elem-fn "dl"))
(def dt (elem-fn "dt"))
(def em (elem-fn "em"))
(def embed (elem-fn "embed"))
(def fieldset (elem-fn "fieldset"))
(def figcaption (elem-fn "figcaption"))
(def figure (elem-fn "figure"))
(def footer (elem-fn "footer"))
(def form (elem-fn "form"))
(def h1 (elem-fn "h1"))
(def h2 (elem-fn "h2"))
(def h3 (elem-fn "h3"))
(def h4 (elem-fn "h4"))
(def h5 (elem-fn "h5"))
(def h6 (elem-fn "h6"))
(def header (elem-fn "header"))
(def hgroup (elem-fn "hgroup"))
(def hr (elem-fn "hr"))
(def i (elem-fn "i"))
(def iframe (elem-fn "iframe"))
(def img (elem-fn "img"))
(def input (elem-fn "input"))
(def ins (elem-fn "ins"))
(def kbd (elem-fn "kbd"))
(def keygen (elem-fn "keygen"))
(def label (elem-fn "label"))
(def legend (elem-fn "legend"))
(def li (elem-fn "li"))
(def link (elem-fn "link"))
(def main (elem-fn "main"))
(def html-map (elem-fn "map"))
(def mark (elem-fn "mark"))
(def menu (elem-fn "menu"))
(def menuitem (elem-fn "menuitem"))
(def html-meta (elem-fn "meta"))
(def meter (elem-fn "meter"))
(def multicol (elem-fn "multicol"))
(def nav (elem-fn "nav"))
(def noframes (elem-fn "noframes"))
(def noscript (elem-fn "noscript"))
(def html-object (elem-fn "object"))
(def ol (elem-fn "ol"))
(def optgroup (elem-fn "optgroup"))
(def option (elem-fn "option"))
(def output (elem-fn "output"))
(def p (elem-fn "p"))
(def param (elem-fn "param"))
(def picture (elem-fn "picture"))
(def pre (elem-fn "pre"))
(def progress (elem-fn "progress"))
(def q (elem-fn "q"))
(def rp (elem-fn "rp"))
(def rt (elem-fn "rt"))
(def rtc (elem-fn "rtc"))
(def ruby (elem-fn "ruby"))
(def s (elem-fn "s"))
(def samp (elem-fn "samp"))
(def script (elem-fn "script"))
(def section (elem-fn "section"))
(def select (elem-fn "select"))
(def shadow (elem-fn "shadow"))
(def small (elem-fn "small"))
(def source (elem-fn "source"))
(def span (elem-fn "span"))
(def strong (elem-fn "strong"))
(def style (elem-fn "style"))
(def sub (elem-fn "sub"))
(def summary (elem-fn "summary"))
(def sup (elem-fn "sup"))
(def table (elem-fn "table"))
(def tbody (elem-fn "tbody"))
(def td (elem-fn "td"))
(def template (elem-fn "template"))
(def textarea (elem-fn "textarea"))
(def tfoot (elem-fn "tfoot"))
(def th (elem-fn "th"))
(def thead (elem-fn "thead"))
(def html-time (elem-fn "time"))
(def title (elem-fn "title"))
(def tr (elem-fn "tr"))
(def track (elem-fn "track"))
(def u (elem-fn "u"))
(def ul (elem-fn "ul"))
(def html-var (elem-fn "var"))
(def video (elem-fn "video"))
(def wbr (elem-fn "wbr"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment