Last active
November 23, 2023 18:46
-
-
Save mynomoto/98c229f651cee2a07c64a8d8dcae052a to your computer and use it in GitHub Desktop.
This file contains 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
(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