Last active
June 23, 2022 02:45
-
-
Save rgkirch/ece8292e103c9a75c7ce0110253ea56d to your computer and use it in GitHub Desktop.
Button text carousel.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns app.renderer.ui09 | |
(:require | |
[clojure.string :as string] | |
[meander.epsilon :as m] | |
[cljs.pprint :refer [pprint]] | |
[goog.dom :as gdom] | |
[goog.Timer :as gtimer] | |
[missionary.core :as mi] | |
[cljs.spec.alpha :as s]) | |
(:require-macros [clojure.core.strint :refer [<<]])) | |
(enable-console-print!) | |
(def re-tag #"([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?") | |
(defn conform | |
[spec x] | |
(let [r (s/conform spec x)] | |
(when (= r ::s/invalid) (s/explain spec x)) | |
r)) | |
(s/def ::hiccup-leader | |
(s/and keyword? | |
(s/conformer #(if (namespace %) | |
::s/invalid | |
(let [[match tag id class] (re-matches re-tag (name %))] | |
(if-not match | |
::s/invalid | |
(into {} | |
[(when (seq tag) [:tag tag]) | |
(when (seq id) [:id id]) | |
(when (seq class) | |
[:class (string/replace class #"\." " ")])])))) | |
#(let [{:keys [tag id class]} %] | |
(keyword | |
(str tag | |
(when (seq id) (str "#" id)) | |
(when (seq class) (str "." (string/replace class #" " "."))))))))) | |
(s/def ::hiccup-attributes | |
(s/map-of (s/or :keyword keyword? :string string?) | |
(s/or :fn fn? :string string? :number number? :boolean boolean?) | |
:conform-keys true)) | |
(s/def ::hiccup | |
(s/or :text string? | |
:number number? | |
:flow fn? | |
:hiccup (s/cat :leader ::hiccup-leader | |
:attributes (s/? ::hiccup-attributes) | |
:children (s/* ::hiccup)))) | |
(defn set-property! | |
[node k v] | |
(if (and *in-reactor* (fn? v)) | |
(mi/stream! | |
(mi/ap | |
(gdom/setProperties node (clj->js {k (mi/?< v)})))) | |
(do | |
(println "setting " {k v}) | |
(gdom/setProperties node (clj->js {k v}))))) | |
(defn help-attributes | |
[node attributes] | |
(doseq [[k v] (s/unform ::hiccup-attributes attributes)] | |
(let [k (if (keyword? k) (name k) k)] | |
(when (or (= "click" k) (= "onclick" k)) (println (<< "Stetting _attribute_ '~{k}'. Did you mean 'on-click'?"))) | |
(if (= "on-" (subs k 0 3)) | |
(when v (.addEventListener node (subs k 3) v)) | |
(set-property! node k v))))) | |
(defn as-element | |
[form] | |
(when-not (s/valid? ::hiccup form) | |
(throw (ex-info (s/explain ::hiccup form) (s/explain-data ::hiccup form)))) | |
(let [conformed (s/conform ::hiccup form)] | |
(m/match conformed | |
[:text ?text] | |
(gdom/createTextNode ?text) | |
[:number ?number] | |
(gdom/createTextNode (str ?number)) | |
(m/and (m/guard *in-reactor*) [:flow ?flow]) | |
#_(let [old (volatile! nil)] | |
(mi/stream! (mi/ap (let [elem (as-element (mi/?< ?flow))] | |
(do (when @old | |
(.replaceChild (.-parentNode @old) elem @old)) | |
(vreset! old elem))))) | |
@old) | |
(let [old (volatile! nil)] | |
(mi/stream! (mi/ap (let [elem (as-element (mi/?< ?flow))] | |
(do (when @old | |
(gdom/replaceNode elem @old)) | |
(vreset! old elem))))) | |
@old) | |
[:hiccup {:leader {:tag ?tag | |
:id ?id | |
:class ?class} | |
:attributes ?attributes | |
:children (m/seqable (m/cata !children) ...)}] | |
(doto (gdom/createDom ?tag) | |
(as-> node (do | |
(when ?id (set-property! node "id" ?id)) | |
(when ?class (set-property! node "class" ?class)) | |
(help-attributes node ?attributes) | |
(doseq [child !children] | |
(.appendChild node child))))) | |
?x (throw (ex-info "non exhaustive pattern match" {:value ?x}))))) | |
(def ^:dynamic *in-reactor* false) | |
(defn observe | |
[a] | |
(with-meta | |
(mi/observe (fn mount [send!] | |
(let [id (random-uuid)] | |
(add-watch a id | |
(fn [k r o n] | |
(send! n))) | |
(send! @a) | |
(fn unmount [] | |
(remove-watch a id))))) | |
{:type :flow})) | |
(defn rot | |
[s] | |
(let [[c & cs] s] | |
(apply str (concat cs [c])))) | |
(defn sg1 | |
[] | |
(let [count (atom 0)] | |
(as-element [:div | |
[:input {:type "text" | |
:readOnly true | |
:value (observe count)} | |
(observe count)] | |
[:button {:on-click #(swap! count inc)} "Count"]]))) | |
(defn sg2 | |
[] | |
(let [celsius (atom 0) | |
c-to-f (fn [c] (+ 32 (* c (/ 9 5)))) | |
f-to-c (fn [f] (* (- f 32) (/ 5 9))) | |
<celsius (observe celsius) | |
<fahrenheit (mi/ap (c-to-f (mi/?< (observe celsius)))) | |
store-f (fn [f] (reset! celsius (f-to-c f))) | |
store-c (fn [c] (reset! celsius c))] | |
(as-element [:div#app-container | |
[:input {:type "number" | |
:on-input #(store-c (.. % -target -value)) | |
:value <celsius}] | |
" Celsius = " | |
[:input {:type "number" | |
:on-input #(store-f (.. % -target -value)) | |
:value <fahrenheit}] | |
" Fahrenheit"]))) | |
(binding [*in-reactor* true] | |
((mi/reactor | |
(gdom/replaceNode | |
(sg2) | |
(gdom/getElement "app-container"))) | |
prn prn)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment