diff --git a/deps.edn b/deps.edn
index 0131855..806eacd 100644
--- a/deps.edn
+++ b/deps.edn
@@ -6,8 +6,8 @@
cljs-ajax/cljs-ajax {:mvn/version "0.8.1"}
;; react
- reagent/reagent {:mvn/version "1.1.1"}
- reagent-utils/reagent-utils {:mvn/version "0.3.3"}
+ reagent/reagent {:mvn/version "1.2.0"}
+ reagent-utils/reagent-utils {:mvn/version "0.3.6"}
olymk2/cl-org {:git/url "https://gitlab.com/olymk2/cl-org.git"
:sha "528e8125afcac5d7664aac80f7fbba12dd0f5d98"}
;;routing
diff --git a/reagent-reitit-demo/resources/public/documents/reagent-reitit.org b/reagent-reitit-demo/resources/public/documents/reagent-reitit.org
index 06c45ed..e58bacd 100644
--- a/reagent-reitit-demo/resources/public/documents/reagent-reitit.org
+++ b/reagent-reitit-demo/resources/public/documents/reagent-reitit.org
@@ -7,11 +7,89 @@ Reagent is a popular react wrapper in the clojurescript it greatly simplify buil
The basis of any react app is components these are small snippets of html which can be composed to build up a page,
reagent provides 3 main ways to create components dependant on the complexity needed most of the time you will create form 1 and form 2 components.
+#+BEGIN_SRC html :results silent :exports none :tangle resources/public/index.html
+
+
+
+
+ Clojure demos
+
+
+
+
+ App loading here
+
+
+
+
+#+END_SRC
+
+
+#+BEGIN_SRC edn :results silent :tangle deps.edn
+{:paths ["src" "resources"]
+ :deps
+ {org.clojure/clojure {:mvn/version "1.10.0"}
+ org.clojure/clojurescript {:mvn/version "1.11.60"}
+ reagent/reagent {:mvn/version "1.2.0"}
+ thheller/shadow-cljs {:mvn/version "2.24.0"}}
+ }
+#+END_SRC
+
+#+BEGIN_SRC edn :results silent :tangle shadow-cljs.edn
+{:deps {:aliases [:dev]}
+ :dev-http {8080 ["resources/public/" "classpath:public"]}
+ :source ["src" "../../components"]
+ :builds {:app {:output-dir "resources/public/cljs-out/"
+ :asset-path "/cljs-out"
+ :target :browser
+ :compiler-options {:infer-externs :auto
+ :externs ["datascript/externs.js"]
+ :output-feature-set :es6}
+ :modules {:main_bundle {:init-fn clojure-demo.core/startup!}}
+ :devtools {:after-load app.main/reload!}}}}
+#+END_SRC
+
+
+#+BEGIN_SRC json :results silent :tangle package.json
+{
+ "dependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "shadow-cljs": "^2.23.3",
+ "webpack": "^5.74.0",
+ "webpack-cli": "^4.10.0"
+ }
+}
+#+END_SRC
+
+#+BEGIN_SRC text :results silent :tangle readme.org
+#+TITLE: Getting started
+
+* Install the npm requirements.
+
+npx install
+
+* Launch shadow-cljs watch for source code changes
+npx shadow-cljs watch app
+#+END_SRC
+
+
+#+BEGIN_SRC json :tangle src/clojure_demo/core.cljs
+(ns clojure-demo.core
+ (:require
+ ["react-dom/client" :refer [createRoot]]
+ [reagent.core :as reagent]))
+#+END_SRC
+
+
** Form 1 components
Form one components are the most basic simply rendering some html with values that are not going to change.
In the example below the function just returns some hiccup with the parameters inserted, you need to specify =:key= when dynamically repeating the elements and these should be reproducible unique id's where possible not randomly generated or indexed numbers if the data is unordered.
-#+BEGIN_SRC clojure :tangle "src/core.cljs"
+
+#+BEGIN_SRC clojure :tangle src/clojure_demo/core.cljs
(defn navbar-link [{:keys [href title text] :or {text nil title nil}}]
[:a.link.dim.dib.mr3 {:key href :href href :title title} text])
@@ -19,7 +97,7 @@ In the example below the function just returns some hiccup with the parameters i
#+END_SRC
-#+BEGIN_SRC clojure
+#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
(defn product-card
[{:keys [title amount description link]}]
[:article.br2.ba.dark-gray.b--black-10.ma2.w-100.w-50-m.w-25-l.mw5
@@ -46,31 +124,50 @@ In the example below the function just returns some hiccup with the parameters i
** Form 2 components
Form two components are used so we can track local state of a component, this is appropriate any time we need to react to change forms and user click event's being simple examples.
-#+BEGIN_SRC clojurescript
+
+#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
(defn my-component [title starting-value]
(let [local-state (reagent/atom starting-value)]
(fn [title]
[:h1 {:class (when @local-state "hide")
- :on-click (fn [] (swap! local-state inc))}
+ :on-click (fn [e]
+ (prn (-> e .-target))
+ (swap! local-state inc))}
(str title " " @local-state)])))
[my-component "Clickable component" 1]
#+END_SRC
-#+BEGIN_SRC clojurescript
-(defn update-form-data [form-data name value]
- (swap! form-data assoc [name] value))
+#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
+(defn update-form-data [form-data ^js event]
+ (prn "test here")
+ (prn @form-data)
+ ;;(prn (.dir js/console event))
+ (prn (-> event))
+ (prn (-> event .-currentTarget))
+ (prn (-> event .-target ))
+ (prn (-> event .-target .-value))
+ (swap! form-data assoc
+ (keyword (-> event .-target .-name))
+ (-> event .-target .-value)))
(defn my-address-form []
- (let [form-data (r/atom {})]
- (fn []
- [:form
- [:input {:name "address-line-1" :on-change :placeholder "Address Line 1"}]
- [:input {:name "address-line-2" :placeholder "Address Line 2"}]
- [:input {:name "address-line-3" :placeholder "Address Line 3"}]
- [:input {:name "city" :placeholder "City"}]
- [:input {:name "postcode" :placeholder "Postcode / Zipcode"}]
- ]))
+ (let [form-data (reagent/atom {})
+ form-change (partial update-form-data form-data)]
+ (fn []
+ [:div
+ (str @form-data)
+ [:form {:on-submit prn}
+ [:input {:type "text"
+ :default-value (str (:test @form-data))
+ :name "address-line-1"
+ :on-change form-change :placeholder "Address Line 1"}]
+ [:input {:type "text" :name "address-line-2" :on-change form-change :placeholder "Address Line 2"}]
+ [:input {:name "address-line-3" :on-change form-change :placeholder "Address Line 3"}]
+ [:input {:name "city" :on-change form-change :placeholder "City"}]
+ [:input {:name "postcode" :on-change form-change :placeholder "Postcode / Zipcode"}]]])))
+
+[:div [my-address-form]]
#+END_SRC
@@ -96,7 +193,7 @@ usually this form of component is only needed when rendering graphics or things
* Fetching a html element reference
If we wish to capture a node we can use =:ref= and store the result in an atom, we can then de reference the atom and call a method on the node using =aget=.
-#+BEGIN_SRC clojurescript :results output
+#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
(defn example-ref-component [title]
(let [local-ref (reagent/atom nil)]
(fn [title]
@@ -108,6 +205,21 @@ If we wish to capture a node we can use =:ref= and store the result in an atom,
#+END_SRC
+#+BEGIN_SRC json :tangle src/clojure_demo/core.cljs
+
+(defn current-page [])
+
+(defn mount-root-page []
+ ;; this select the main node from the html file and injects your page content
+ (.render
+ (createRoot (.getElementById js/document "app"))
+ (reagent/as-element [err-boundary [current-page]])))
+
+(def launch (do (startup!) true))
+
+#+END_SRC
+
+
* Further reading
https://github.com/reagent-project/reagent
diff --git a/reagent-reitit-demo/src/clojure_demo/core.cljs b/reagent-reitit-demo/src/clojure_demo/core.cljs
index 9481512..cae1d7b 100644
--- a/reagent-reitit-demo/src/clojure_demo/core.cljs
+++ b/reagent-reitit-demo/src/clojure_demo/core.cljs
@@ -25,11 +25,12 @@
[sci.core :as sci]
[spec-tools.data-spec :as ds]))
-(def languages {"clojure" {:mode "clojure"}
- "clojurescript" {:mode "clojure"}
- "yaml" {:mode "yaml"}})
+(def languages
+ {"clojure" {:mode "clojure"}
+ "clojurescript" {:mode "clojure"}
+ "yaml" {:mode "yaml"}})
-(def yaml-mode (LanguageSupport. (.define StreamLanguage yaml) ))
+(def yaml-mode (LanguageSupport. (.define StreamLanguage yaml)))
;; https://github.com/babashka/sci.configs
(def rf-ns (sci/create-ns 'reitit.frontend nil))
@@ -56,22 +57,22 @@
'rfe {'start! (sci/copy-var rfe/start! rfe-ns)
'href (sci/copy-var rfe/href rfe-ns)}}}))
-
;(def sci-ctx (sci/empty-environment))
(sci/alter-var-root sci/print-fn (constantly *print-fn*))
(defn slugify [s]
- (str "toc"
- (-> s
- (clojure.string/lower-case)
- (clojure.string/replace #"[^\w]+" "-")
- (clojure.string/replace #"^-\\|-\\-$" ""))))
+ (when s
+ (str ;;"toc"
+ (-> s
+ (clojure.string/lower-case)
+ (clojure.string/replace #"[^\w]+" "-")
+ (clojure.string/replace #"^-\\|-\\-$" "")))))
(defn fetch-selected-text
"Get the users selected text"
[updated-view transactions]
(reduce (fn [text t]
- (if (= "select.pointer" (str (.annotation t (.-userEvent Transaction) )))
+ (if (= "select.pointer" (str (.annotation t (.-userEvent Transaction))))
(conj text
(.sliceDoc
^EditorState (.-state updated-view)
@@ -101,7 +102,7 @@
(first selected-text)
(.-doc (.-state view-update)))]
(reset! evaled-result (sci/eval-string* sci-ctx
- (.toString eval-code) ))
+ (.toString eval-code)))
(prn "updated delayed"))) 1000)))))
start-state
(.create EditorState
@@ -119,12 +120,12 @@
;:mode "yaml"
:mode "clojure"
:updateListener prn
- :parent @editor })))))
+ :parent @editor})))))
:component-will-unmount (fn [_] (.destroy @view))
:reagent-render (fn []
[:div.mb2
[:div.ba.ma0.f5.b--black-05.pa2.overflow-auto.editor {:ref #(reset! editor %)}]
- (when @evaled-result
+ (when @evaled-result
[:pre.ba.ma0.f6.code.b--black-05.pa2.pl4.overflow-auto
{:style {:white-space "pre-line"}}
;; See org mode :results key
@@ -135,24 +136,21 @@
"silent" ""
@evaled-result)])])})))
-
(defn link-handler [v _]
(prn v)
(prn (conj [:a] v #_(second v)))
(if (-> v second :img)
(conj [:img] (dissoc (rename-keys (second v) {:href :src}) :img))
(conj (conj [:a] v #_(second v)) (or (when-not (clojure.string/blank? (last v)) (last v))
- (:href (second v))
- (:title (second v))))))
-
+ (:href (second v))
+ (:title (second v))))))
(defn link-handler4 [v _]
(if (-> v second :img)
(conj [:img] (dissoc (rename-keys (second v) {:href :src}) :img))
(conj [:a] (second v) #_(or (when-not (clojure.string/blank? (last v)) (last v))
- {:href (:href (second v))}
- (:title (second v))))))
-
+ {:href (:href (second v))}
+ (:title (second v))))))
(def theme
(merge tachyon-theme
@@ -168,15 +166,15 @@
{:HEADER1 (fn [v _] [:li [:a {:href (str "#" (slugify v))}
[:span.f4.fw6.f3-ns.lh-title.mt0.mb2 v]]])
:HEADER2 (fn [v _] [:li.ml2 [:a {:href (str "#" (slugify v))}
- [:span.f4.fw6.f4-ns.lh-title.mt0.mb2 v]]])
+ [:span.f4.fw6.f4-ns.lh-title.mt0.mb2 v]]])
:HEADER3 (fn [v _] [:li.ml4 [:a {:href (str "#" (slugify v))}
- [:span.f5.fw6.f5-ns.lh-title.mt0.mb2 v]]])
+ [:span.f5.fw6.f5-ns.lh-title.mt0.mb2 v]]])
:HEADER4 (fn [v _] [:li.ml6 [:a {:href (str "#" (slugify v))}
- [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])
+ [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])
:HEADER5 (fn [v _] [:li.ml8 [:a {:href (str "#" (slugify v))}
- [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])
+ [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])
:HEADER6 (fn [v _] [:li.ml10 [:a {:href (str "#" (slugify v))}
- [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])}))
+ [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])}))
;; put constant data here
(def site-data
@@ -215,7 +213,7 @@
;; form one component to render article tiles
(defn articles [{:keys [title body articles]}]
- [:section.mw7.center.avenir
+ [:section.mw7.center.avenir
[:h2.baskerville.fw1.ph3.ph0-l title]
(when body [:p body])
(map (fn [{:keys [title author link description img-src img-alt]}]
@@ -245,7 +243,6 @@
(defn circle [{:keys [img alt]}]
[:div.pa4.tc [:img.br-100.ba.h3.w3.dib {:src img :alt alt}]])
-
;; form one component to render a nav link
(defn navbar-link [{:keys [href title text key] :or {text nil title nil}}]
[:a.link.dim.white.dib.mr3 {:key (or key href) :href href :title title} text])
@@ -261,7 +258,6 @@
{:target "_blank" :href "https://matrix.to/#/@oly:matrix.org"}
"Contact me"]])
-
;; form2 component notice the render function takes the same param as the component function
;; this is important, you can hit issues if you forget this in form 2 components.
(defn my-component [title value]
@@ -324,16 +320,17 @@
(defn build-file-tar [code-blocks]
(tarts (clj->js (mapv (fn [block] {:name (str (:tangle (second block)))
:content (str (last block))})
- code-blocks ))))
+ code-blocks))))
(defn build-file-tar-hm [code-blocks]
- (tarts (clj->js code-blocks )))
+ (tarts (clj->js code-blocks)))
(defn build-tarts-map [blocks]
(->> blocks
(group-by (fn [block] (:tangle (second block))))
- (reduce (fn [m [k v]] (conj m {:name (str k)
- :content (str (concat (mapv last v)))})) [])))
+ (reduce (fn [m [k v]]
+ (conj m {:name (str k)
+ :content (str (clojure.string/join "" (mapv last v)))})) [])))
(defn org->split2
"Split out meta and body"
@@ -343,6 +340,34 @@
:code (filter (fn filter-code [tag] (org-code (first tag))) dsl)
:body (filter (fn filter-body [tag] (body (first tag))) dsl)})
+(defn update-form-data [form-data ^js event]
+ (prn "test here")
+ (prn @form-data)
+ ;;(prn (.dir js/console event))
+ (prn (-> event))
+ (prn (-> event .-currentTarget))
+ (prn (-> event .-target))
+ (prn (-> event .-target .-value))
+ (swap! form-data assoc
+ (keyword (-> event .-target .-name))
+ (-> event .-target .-value)))
+
+(defn my-address-form []
+ (let [form-data (reagent/atom {})
+ form-change (partial update-form-data form-data)]
+ (fn []
+ [:div
+ (str @form-data)
+ [:form {:on-submit prn}
+ [:input {:type "text"
+ :default-value (str (:test @form-data))
+ :name "address-line-1"
+ :on-change form-change :placeholder "Address Line 1"}]
+ [:input {:type "text" :name "address-line-2" :on-change form-change :placeholder "Address Line 2"}]
+ [:input {:name "address-line-3" :on-change form-change :placeholder "Address Line 3"}]
+ [:input {:name "city" :on-change form-change :placeholder "City"}]
+ [:input {:name "postcode" :on-change form-change :placeholder "Postcode / Zipcode"}]]])))
+
;; form two component render demo
;;(h/org->split (parse document1))
(defn demo-page [route]
@@ -358,6 +383,7 @@
org->split2
(reset! content)))})
(fn [route]
+<<<<<<< HEAD
[:main.mt4.mw7.center.avenir
[:h1.f3.f2-ns (:content (last (first (:header @content))))]
@@ -375,7 +401,30 @@
"Download Code"]
[:div.mw7.center.avenir
(into [:div] (org->replacements theme (:body @content)))]])))
+=======
+ (if @content
+ [:main.mt4.mw7.center.avenir
+ [my-address-form]
+ (slugify (:content (last (first (:header @content)))))
+ [:h1.f3.f2-ns (:content (last (first (:header @content))))]
+ [:div.mw7.center.avenir
+ (into [:ol] (org->replacements theme-toc (:toc @content)))]
+ [:p "The code in these examples is evaluated when modified, you can highlight a partial expression to evalute the selection, You can also download the code as a tar if you like and use it in your favourite editor."]
+ [:a.f6.link.dim.ph3.pv2.mb2.dib.white.bg-dark-blue
+ {:download (str (slugify (:content (last (first (:header @content))))) ".tar")
+ :title (:content (last (first (:header @content))))
+ :href (.createObjectURL
+ js/URL
+ (js/Blob. #js [(build-file-tar-hm
+ (build-tarts-map
+ (:code @content)))]
+ {:type "application/tar"}))}
+ "Download Code"]
+ [:div.mw7.center.avenir
+ (into [:div] (org->replacements theme (:body @content)))]]
+>>>>>>> a6c9ed1 (Updated reagent info.)
+ [:<>]))))
;; form one render about page component
(defn about-page []
@@ -462,8 +511,7 @@
;; this select the main node from the html file and injects your page content
(.render
(createRoot (.getElementById js/document "app"))
- (reagent/as-element [err-boundary [current-page]])
- ))
+ (reagent/as-element [err-boundary [current-page]])))
(defn ^:after-load render-site []
;; this select the main node from the html file and injects your page content
@@ -478,15 +526,14 @@
:ignore-anchor-click?
(fn [router e el uri]
;; Add additional check on top of the default checks\
- (and
- (ignore-anchor-click? router e el uri)
+ (and
+ (ignore-anchor-click? router e el uri)
(not (let [href (or (.-href el) "")
result (clojure.string/includes? href "#")]
- #_(when result
+ #_(when result
;(.preventDefault e)
- #_(js/console.log "will prevent by href" href))
- result))))
- })
+ #_(js/console.log "will prevent by href" href))
+ result))))})
(render-site))
;; we defonce the startup so that hot reloading does not reinitialize the state of the site