Skip to content

Instantly share code, notes, and snippets.

@mynomoto
Last active November 23, 2023 18:46
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 mynomoto/98c229f651cee2a07c64a8d8dcae052a to your computer and use it in GitHub Desktop.
Save mynomoto/98c229f651cee2a07c64a8d8dcae052a to your computer and use it in GitHub Desktop.
(require '[hoplon.core :as h])
(require '[javelin.core :as j])
(defn random-string
[]
(apply str (repeatedly 20 (fn [] (rand-nth "abcdefghijklmnopqrstuvwxyz0123456789")))))
(j/defc title "From Hoplon")
(def next-title
{"From Hoplon" "Clicked button"
"Clicked button" "From Hoplon"})
(j/defc scroll-to-scroll-here-section false)
(j/defc scroll-to-scroll-to-section false)
(j/defc text-cell "from Hoplon")
(j/defc class-string "a b c")
(def next-class-string
{"a b c" "a c"
"a c" "a b c"})
(j/defc css
{:color "red"
:font-size "20px"})
(j/defc attr {:a "x"
:b "y"})
(j/defc class-map {:a true
:b true
:c true})
(j/defc toggle-show? true)
(j/defc text "some text")
(j/defc html (h/a :href "#" "some link"))
(j/defc dangerous-html (h/a :href "#" "some link"))
(j/defc html-txt "<span>This is a span</span>")
(def next-html-txt
{"<span>This is a span</span>" "<strong onclick=\"console.log('abc')\">This is a strong</strong>"
"<strong onclick=\"console.log('abc')\">This is a strong</strong>" "<span>This is a span</span>"})
(j/defc focus? false)
(j/defc select? false)
(j/defc text-input "")
(j/defc range-input 20)
(j/defc select-input "green")
(j/defc multi-select #{"green"})
(j/defc check-box true)
(j/defc radio-input "b")
(h/defattr :bazz [elem key value]
(elem :attr {:custombazz value}))
(j/defc loop-tpl-input ["a" "b" "c"])
(j/defc for-tpl-input ["x" "y" "z"])
(j/defc if-tpl-if-branch "true")
(j/defc if-tpl-else-branch "false")
(j/defc if-tpl-condition false)
(j/defc when-tpl-condition false)
(j/defc when-tpl-content "when is true")
(j/defc cond-tpl-number 4)
(j/defc= is-4 (= cond-tpl-number 4))
(j/defc route :about)
(def routes [:about :home :contact :broken])
(j/defc set-cell-test "a")
(j/defc i 0)
(j/defc j 1)
(j/defc k 2)
(def formula-of-test (j/formula-of [i j k] (+ i j k)))
(def a (j/cell 42))
(def b (j/cell {:x 100 :y 200}))
(def formulet-test
(j/formulet [v (j/cell= (inc a))
w (+ 1 2)
{:keys [x y]} b]
(+ v w x y)))
(def doseq-vec (j/cell [{:x :a} {:x :b} {:x :c}]))
(j/cell-doseq [{:keys [x]} doseq-vec]
(prn :creating @x)
(add-watch x nil #(prn :updating %3 %4)))
(h/defelem timer [attrs children]
(let [start (or (:start attrs) 0)
seconds (j/cell start)]
(h/with-interval 1000 (swap! seconds inc))
(h/div attrs (j/cell= (str "Seconds Elapsed: " seconds)))))
(h/body
(let [elem (h/div
(h/h1 title)
(h/div "This header is dynamic, if you change the value in the cell it will change in the page.")
(h/button :click #(swap! title (fn [s] (get next-title s)))
"Click to change the header")
(h/h2 :scroll-to scroll-to-scroll-to-section (h/code ":scroll-to"))
(h/div (h/code ":scroll-to") " can be used to scroll to the element.")
(h/button :click #(do (reset! scroll-to-scroll-here-section true)
(h/with-timeout 0 (reset! scroll-to-scroll-here-section false)))
"Go to Scroll Here")
(h/h2 (h/code "h/text"))
(h/div (h/code "h/text") " is a macro that allows users to interpolate text and cells. The text will be updated when cells change")
(h/div (h/text "The cell content is: ~{text-cell}"))
(h/button :click #(reset! text-cell (random-string))
"Click to change the text")
(h/h2 (h/code ":class"))
(h/div "The " (h/code ":class") " attribute allows you to dynamically control classes in a element. Classes with truthy values will be added and classes with falsey values will be removed.")
(h/div :class class-map
"This is the class map: " (j/cell= (str class-map))
(h/br)
"(inspect me to check it out, it should have classes that have truthy values in the class map)")
(h/button :click #(swap! class-map update :a not)
"Click me to toggle a")
(h/h2 (h/code ":smart-class"))
(h/div "The " (h/code ":smart-class") "attribute allows you to dynamically control classes in a element using strings or vectors.
You should not use it more than once per element or there will be trouble.")
(h/div :smart-class class-string
"This is the class string: " class-string
(h/br)
"(inspect me to check it out, it should have classes like the class string is showing)")
(h/button :click #(swap! class-string (fn [s] (get next-class-string s)))
"Click me to toggle b")
(h/h3 "What happens when you use " (h/code ":smart-class") " multiple times")
((h/div :smart-class class-string
"This is the class string: " class-string
(h/br)
"(inspect me to check it out, this will have the class \"x y z\" initially but then will be in sync with the example above after pressing the button)")
:smart-class "x y z")
(h/h2 (h/code ":css"))
(h/div "The " (h/code ":css") " attribute allows you to dynamically control css inline styles in a element.")
(h/div :css css
"Custom css")
(h/button :click #(swap! css assoc :color "red")
"Make the text red")
(h/button :click #(swap! css assoc :color "green")
"Make the text green")
(h/h2 (h/code ":attr"))
(h/div "The " (h/code ":attr") " attribute allows you to dynamically add custom-attributes.")
(h/div :attr attr
"Inpect me to see that I have attributes " (h/code "a=\"x\"") " and " (h/code "b=\"y\""))
(h/h2 (h/code ":toggle"))
(h/div (h/code ":toggle") " can be used to hide/show an element")
(h/p (h/text "show?: ~{toggle-show?}"))
(h/p :toggle toggle-show? "This element will be hidden/shown")
(h/button :click #(swap! toggle-show? not) "Click me to toggle the element")
(h/h2 (h/code ":text"))
(h/div (h/code ":text") " can be used to set the element text")
(h/p :text text)
(h/input :placeholder "type here to change the text"
:value text
:keyup #(reset! text @%))
(h/button :click #(reset! text (apply str (repeatedly 20 (fn [] (rand-nth "abcdefghijklmnopqrstuvwxyz0123456789")))))
"Random text")
(h/h2 (h/code ":html"))
(h/div (h/code ":html") " can be used to set the element html. Text will be treated in a safe way.")
(h/p :html html)
(h/p :html html-txt)
(h/h2 (h/code ":dangerous-html"))
(h/div (h/code ":dangerous-html") " can be used to set the element html. Text will be treated in a dangerous way. The strong is clickable and will output a console.log.")
(h/p :dangerous-html dangerous-html)
(h/p :dangerous-html html-txt)
(h/button :click #(swap! html-txt (fn [s] (get next-html-txt s)))
"Change html text")
(h/h2 :scroll-to scroll-to-scroll-here-section "Scroll Here")
(h/button :click #(do (reset! scroll-to-scroll-to-section true)
(h/with-timeout 0 (reset! scroll-to-scroll-to-section false)))
"Go to " (h/code ":scroll-to") " section")
(h/h1 "Inputs and state in Hoplon")
(h/p
"Each input example has a corresponding Javelin cell.
When you change the input its cell is updated.")
(h/h2 (h/code ":value"))
(h/div "The "(h/code ":value") " attribute is used to update the element value. When set to a cell, it will update the value of the element when tne cell changes.")
(h/h2 (h/code ":focus"))
(h/div "The "(h/code ":focus") " attribute is used to focus on the element.")
(h/h2 (h/code ":select"))
(h/div "The "(h/code ":select") " attribute is used to focus and select the element content.")
(h/h2 (h/code "deref") " or " (h/code "@"))
(h/div (h/code "deref") " an event will return the value of its target.")
(h/h2 "A simple text input")
(h/p "This shows that you can have more than one field pointing to the same
cell. Edit one input and see everything change in sync.")
(h/input
:type "text"
:focus focus?
:placeholder "Type something here"
;; The cell is used as the value of the input.
:value text-input
;; The on-keyup event fires on every keystroke for demo purposes. In
;; practice you should only update on-blur or on-change.
:keyup #(reset! text-input @%)
:blur #(reset! focus? false))
;; On all the examples we are using the event to get the value of the
;; input. So no id is necessary.
;; This second input is just to show that you can point two inputs to the
;; same cell and update them in sync. It's the same as the first input.
(h/input
:type "text"
:select select?
:placeholder "Type something here"
:value text-input
:keyup #(reset! text-input @%)
:blur #(reset! select? false))
(h/p (h/text "Value of text input: ~{text-input}"))
(h/button :click #(swap! focus? not) "Click me to toggle focus on first input")
(h/button :click #(swap! select? not) "Click me to toggle select on second input")
(h/h2 "A range input")
(h/p "Every example gets the value from a cell and updates it when it changes.")
(h/input
:style "width:400px"
:type "range" :min 0 :max 100 :step 1
:value range-input
:input #(reset! range-input @%))
(h/p (h/text "Value of range input: ~{range-input}"))
(h/h2 "A select input")
(h/select
:change #(reset! select-input @%)
:value select-input
(h/option :selected (j/cell= (= select-input "blue")) :value "blue" "blue")
(h/option :selected (j/cell= (= select-input "green")) :value "green" "green")
(h/option :selected (j/cell= (= select-input "gold")) :value "gold" "gold")
(h/option :selected (j/cell= (= select-input "indigo")) :value "indigo" "indigo"))
(h/p (h/text "Value of select input: ~{select-input}"))
(h/h2 "Multiple select input")
(h/select
;; The handler is more complex here because of the parsing of the
;; selectedOptions object. The cell is a set in this case.
:change #(let [options (.. % -target -selectedOptions)
l (.-length options)]
(reset! multi-select
(set
(for [i (range l)]
(.-value (.item options i))))))
:multiple "true"
;; You need to check each option value against the multi-select cell to
;; se if it should be selected.
(h/option :selected (j/cell= (multi-select "blue")) :value "blue" "blue")
(h/option :selected (j/cell= (multi-select "green")) :value "green" "green")
(h/option :selected (j/cell= (multi-select "gold")) :value "gold" "gold")
(h/option :selected (j/cell= (multi-select "indigo")) :value "indigo" "indigo"))
(h/p (h/text "Value of multiple select input: ~{multi-select}"))
(h/h2 "A checkbox")
(h/label
(h/input
:type "checkbox"
;; On checkboxes you need to return true from the handler or the
;; checkbox will not uncheck (at the moment).
:click #(do
(swap! check-box not)
true)
:value check-box)
"Checkbox")
(h/p (h/text "Value of checkbox: ~{check-box}"))
(h/h2 "A radio button")
;; Radio buttons require more code, but work fine with cells too.
(h/label
(h/input
:type "radio"
:click #(reset! radio-input (.getAttribute (.-target %) "data-id"))
;; You need to check each input to see if it is checked.
:checked (j/cell= (= "a" radio-input))
:data-id "a"
:name "radio-group")
"a")
(h/label
(h/input
:type "radio"
:click #(reset! radio-input (.getAttribute (.-target %) "data-id"))
:checked (j/cell= (= "b" radio-input))
:data-id "b"
:name "radio-group")
"b")
(h/p (h/text "Selected radio: ~{radio-input}"))
(h/h2 (h/code "h/defattr"))
(h/div "You can define custom attributes using " (h/code "h/defattr") " to add behavior to your application. "
(h/code ":bazz") " is a special attribute that will add a different attribute when used.")
(h/div :bazz true
"This should have a `custombazz` attribute (inspect me to check it out)")
(h/h1 "Template macros")
(h/h2 (h/code "h/loop-tpl"))
(h/div (h/code "h/loop-tpl") " allows you to efficiently render elements in the dom.")
(h/loop-tpl :bindings [x loop-tpl-input]
(h/div x))
(h/button :click #(swap! loop-tpl-input conj (rand-nth "abcdefghijklmnopqrstuvwxyz0123456789"))
"Add element")
(h/button :click #(swap! loop-tpl-input (fn [i] (vec (butlast i))))
"Remove element")
(h/h2 (h/code "h/for-tpl"))
(h/div (h/code "h/for-tpl") " allows you to efficiently render elements in the dom.")
(h/for-tpl [x for-tpl-input]
(h/div x))
(h/button :click #(swap! for-tpl-input conj (rand-nth "abcdefghijklmnopqrstuvwxyz0123456789"))
"Add element")
(h/button :click #(swap! for-tpl-input (fn [i] (vec (butlast i))))
"Remove element")
(h/h2 (h/code "h/if-tpl"))
(h/div (h/code "h/if-tpl") " allows you to efficiently render conditional elements in the dom.")
(h/if-tpl if-tpl-condition
(h/p :css {:color "green"} if-tpl-if-branch)
(h/p :css {:color "red"} if-tpl-else-branch))
(h/button :click #(swap! if-tpl-condition not)
"Change condition result")
(h/button :click #(reset! if-tpl-if-branch (str "if-branch: "(random-string)))
"Change if branch")
(h/button :click #(reset! if-tpl-else-branch (str "else-branch: "(random-string)))
"Change else branch")
(h/h2 (h/code "h/when-tpl"))
(h/div (h/code "h/when-tpl") " allows you to efficiently render a conditional element in the dom.")
(h/when-tpl when-tpl-condition
(h/div when-tpl-content))
(h/button :click #(swap! when-tpl-condition not)
"Change condition result")
(h/button :click #(reset! when-tpl-content (random-string))
"Change when-tpl content")
(h/h2 (h/code "h/cond-tpl"))
(h/div (h/code "h/cond-tpl") " allows you to efficiently render conditional elements in the dom.")
(h/div
(h/cond-tpl is-4 (h/div "totally random!")
(j/cell= (= cond-tpl-number 0)) (h/div "something went wrong...")
(j/cell= (> cond-tpl-number 1)) (h/div "balloons")
:else (h/div "bananas")))
(h/button :click #(reset! cond-tpl-number (rand-int 5))
"Change condition result")
(h/p (h/text "Debug: the number is: ~{cond-tpl-number}"))
(h/h2 (h/code "h/case-tpl"))
(h/div (h/code "h/case-tpl") " allows you to efficiently render conditional elements in the dom.")
(h/case-tpl route
:home (h/div "Hello world!")
:about (h/div "We are a small team of passionate")
:contact (h/div "hello@example.com")
(h/div "four oh four"))
(h/button :click #(reset! route (rand-nth routes))
"Change route")
(h/h2 (h/code "j/formula-of"))
(h/div (h/code "j/formula-of") " allows you to create a formula-cell passing the input cells explicitly.")
(h/div (h/text "i: ~{i} j: ~{j} k: ~{k}"))
(h/div (h/text "~{i} + ~{j} + ~{k} = ~{formula-of-test}"))
(h/button :click #(swap! i inc) "(inc i)")
(h/button :click #(swap! i dec) "(dec i)")
(h/button :click #(swap! j inc) "(inc j)")
(h/button :click #(swap! j dec) "(dec j)")
(h/button :click #(swap! k inc) "(inc k)")
(h/button :click #(swap! k dec) "(dec k)")
(h/h2 (h/code "j/formulet"))
(h/div (h/code "j/formulet") " allows you to create a formula-cell using dynamic bindings.")
(h/div (h/text "formulet value: ~{formulet-test}"))
(h/button :click #(swap! a inc) "a++")
(h/button :click #(swap! a dec) "a--")
(h/h2 (h/code "j/cell-doseq"))
(h/div (h/code "j/cell-doseq") " allows you to do side-effects based on changes in cell containing a seq of elements.")
(h/div (j/cell= (str "The list is: " doseq-vec)))
(h/button :click #(swap! doseq-vec pop) "Shrink doseq-vec")
(h/button :click #(swap! doseq-vec into [{:x :u} {:x :v}]) "Add to doseq-vec")
(h/h2 (h/code "h/defelem"))
(h/div (h/code "h/defelem") " allows you to create your own custom elements that receive arguments like the built in Hoplon ones.")
(h/div "Below you can see custom timer elements:")
(timer)
(timer :css {:color "green"})
(h/h1 "Advanced")
(h/h2 (h/code "j/set-cell!=") " and " (h/code "j/set-cell!"))
(h/div (h/code "h/set-cell!=") " allows you to change a cell to a formula cell.")
(h/div (h/code "h/set-cell!") " allows you to change a cell to an input cell.")
(h/div (h/text "value: ~{set-cell-test}"))
(h/button :click #(js/console.log {:input? (j/input? set-cell-test)})
"Print is input?")
(h/button :click #(j/set-cell!= set-cell-test (count (str class-string)))
"Change cell to formula")
(h/button :click #(j/set-cell! set-cell-test :a)
"Change cell to input"))]
(h/with-dom elem (js/console.log "Element on dom"))
elem))
(h/with-init! (js/console.log "after init"))
(h/with-animation-frame (js/console.log "animation frame"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment