From f8a3f544a45a4ff1581c59c1e4eeb88e51d4959c Mon Sep 17 00:00:00 2001 From: Oly Date: Fri, 16 Jun 2023 14:14:00 +0100 Subject: [PATCH] Updated reagent info. --- .dir-locals.el | 11 +++++ deps.edn | 3 +- dev/user.clj | 12 +++++ .../resources/public/documents/ci-demo.org | 22 +++++----- .../public/documents/reagent-reitit.org | 35 +++++++++++---- .../public/documents/reitit-demo.org | 44 +++++++++++++++++++ .../src/clojure_demo/core.cljs | 26 ++++++----- 7 files changed, 120 insertions(+), 33 deletions(-) create mode 100644 .dir-locals.el create mode 100644 dev/user.clj create mode 100644 reagent-reitit-demo/resources/public/documents/reitit-demo.org diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..856af19 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,11 @@ +((clojurescript-mode . + (cider-clojure-cli-aliases . "-M:dev") + (cider-preferred-build-tool . clojure-cli) + (cider-default-cljs-repl . custom) + (cider-custom-cljs-repl-init-form . "(do (user/cljs-repl))") + ;(cider-shadow-default-options . "") + (cider-shadow-watched-builds . (":app")) + (eval . (progn + (make-variable-buffer-local 'cider-jack-in-nrepl-middlewares) + (add-to-list 'cider-jack-in-nrepl-middlewares "shadow.cljs.devtools.server.nrepl/middleware"))) +)) diff --git a/deps.edn b/deps.edn index 369dd71..0131855 100644 --- a/deps.edn +++ b/deps.edn @@ -27,4 +27,5 @@ datascript/datascript {:mvn/version "1.3.15"} com.bhauman/figwheel-main {:mvn/version "0.2.18"}} - :paths ["src" "resources"]} + :paths ["src" "resources"] + :aliases {:dev [:extra-paths "dev"]}} diff --git a/dev/user.clj b/dev/user.clj new file mode 100644 index 0000000..6d4ac31 --- /dev/null +++ b/dev/user.clj @@ -0,0 +1,12 @@ +(ns user + (:require [shadow.cljs.devtools.api :as shadow] + [shadow.cljs.devtools.server :as server])) + +(defn cljs-repl + "Connects to a given build-id. Defaults to `:app`." + ([] + (cljs-repl :app)) + ([build-id] + (server/start!) + (shadow/watch build-id) + (shadow/nrepl-select build-id))) diff --git a/reagent-reitit-demo/resources/public/documents/ci-demo.org b/reagent-reitit-demo/resources/public/documents/ci-demo.org index 5a700d2..f5d0415 100644 --- a/reagent-reitit-demo/resources/public/documents/ci-demo.org +++ b/reagent-reitit-demo/resources/public/documents/ci-demo.org @@ -1,6 +1,6 @@ #+TITLE: Clojure(script) CI -* CI integration +* Ci integration Building a polylith based clojure project, these steps are similar to most clojure projects with a few additions for the poly tool. @@ -8,17 +8,16 @@ Building a polylith based clojure project, these steps are similar to most cloju ** Deploy rules Name our action and run it only when code is pushed into master -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml name: Staging Deploy on: push: branches: [master] #+END_SRC - ** Checkout Checkout repository, fetch depth is required if you needs tags available -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml # Checkout the code - uses: actions/checkout@v2 with: @@ -27,7 +26,7 @@ Checkout repository, fetch depth is required if you needs tags available ** Cache Cache the downloaded libraries so we don't do each each time. -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml # Cache project dependencies - name: Cache deps uses: actions/cache@v2 @@ -45,7 +44,7 @@ Cache the downloaded libraries so we don't do each each time. ** Set env / project checks Create a var called CHANGED_PROJECTS we can use in conditionals in later build steps, we also check our project comply's to the polylith rules before moving onto linting, testing, building and deploying. -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml # Check the project and set enc for later steps - name: Poly Check / Set Env run: |- @@ -57,7 +56,7 @@ Create a var called CHANGED_PROJECTS we can use in conditionals in later build s ** Linting Lint code with clj-kondo this example lints src and workspace folders. -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml # Lint the code with kondo - name: Code Linting uses: DeLaGuardo/clojure-lint-action@master @@ -69,7 +68,7 @@ Lint code with clj-kondo this example lints src and workspace folders. ** Setup Java Install a version on java using the java action. -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml # Install java - name: Set up JDK & publish to maven uses: actions/setup-java@v1 @@ -79,7 +78,7 @@ Install a version on java using the java action. ** Setup clojure Install clojure from the clojure action. -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml # Install clojure - name: Install clojure tools uses: DeLaGuardo/setup-clojure@3.5 @@ -89,7 +88,7 @@ Install clojure from the clojure action. ** Testing changes since last release Run test but only test since the last tagged release -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml # Run the tests - name: Poly Test run: |- @@ -97,11 +96,10 @@ Run test but only test since the last tagged release clojure -M:poly test since:previous-release #+END_SRC - ** Building Finally create your uberjar, this example uses a condition so it only builds if the projects or one of its components has changed. We need to set the env in a previous step to make it available to future steps. -#+BEGIN_SRC yaml :tangle src/core.cljc +#+BEGIN_SRC yaml :tangle .github/workflows/example.yaml # Build the uberjar - name: Build my example api if: contains('${{ env.CHANGED_PROJECTS }}', 'my-example-api') diff --git a/reagent-reitit-demo/resources/public/documents/reagent-reitit.org b/reagent-reitit-demo/resources/public/documents/reagent-reitit.org index 9d7a583..06c45ed 100644 --- a/reagent-reitit-demo/resources/public/documents/reagent-reitit.org +++ b/reagent-reitit-demo/resources/public/documents/reagent-reitit.org @@ -1,14 +1,16 @@ #+TITLE: Minimal clojurescript reagent example +* Introduction +Reagent is a popular react wrapper in the clojurescript it greatly simplify build react SPA Applications this is usually a good starting point when learning, but there are lots of other options that are worth considering. + * Components -Reagent allow multiple ways to create components with increasing complexity. -There is some good info in this article. -https://purelyfunctional.tv/guide/reagent/ +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. ** Form 1 components -Form one components are for simply rendering some html with values that are not going to change. +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. +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" (defn navbar-link [{:keys [href title text] :or {text nil title nil}}] [:a.link.dim.dib.mr3 {:key href :href href :title title} text]) @@ -28,7 +30,7 @@ In the example below the function just returns some hiccup with the parameters i [:div.dtc.tr [:h2.f5.mv0 amount]]] [:p.f6.lh-copy.measure.mt2.mid-gray description]]]) -[:div.flex +[:div.flex [product-card {:title "Cat 01" :amount "£54.59" @@ -42,9 +44,9 @@ In the example below the function just returns some hiccup with the parameters i #+END_SRC ** Form 2 components -In form two we can track local state inside a component a click counter being a basic example. +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 clojure +#+BEGIN_SRC clojurescript (defn my-component [title starting-value] (let [local-state (reagent/atom starting-value)] (fn [title] @@ -55,6 +57,23 @@ In form two we can track local state inside a component a click counter being a [my-component "Clickable component" 1] #+END_SRC +#+BEGIN_SRC clojurescript +(defn update-form-data [form-data name value] + (swap! form-data assoc [name] 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"}] + ])) +#+END_SRC + + ** Form 3 components This form of component give's you full access to the react life cycle methods, so render did-mount did-unmount etc usually this form of component is only needed when rendering graphics or things like graphs, it's also useful for capturing errors and handling them as in the example below, which renders your components but if =component-did-catch= is trigger the error is caught and displayed instead. diff --git a/reagent-reitit-demo/resources/public/documents/reitit-demo.org b/reagent-reitit-demo/resources/public/documents/reitit-demo.org new file mode 100644 index 0000000..5871d01 --- /dev/null +++ b/reagent-reitit-demo/resources/public/documents/reitit-demo.org @@ -0,0 +1,44 @@ + + + +* Routing +Routing with reitit is all about data, you store your routes as nested vectors of hash maps. +the hash map should take a name and view param at least but you can add in params and validate the data. + +Reitit works as a backend and frontend routing library so you can share routes between the two. + +These are a few simple routes, the last takes parameters and does validation checking against the values. +#+BEGIN_SRC clojurescript +(def routes + [["/" + {:name ::frontpage + :view 'home-page-function}] + + ["/about" + {:name ::about + :view 'about-page-function}] + + ["/item/:id" + {:name ::item + :view 'item-page-function + :parameters {:path {:id int?} + :query {:foo keyword?}}}]]) +#+END_SRC + +You need to connect your routes data structure to =ref/start!= this function take's your own function where you can handle what should happen on route change, in this example an atom is updated causing react to render the new page. + +#+BEGIN_SRC clojurescript +(def site-state (reagent/atom nil)) + + (rfe/start! + (rf/router routes {:data {:coercion rss/coercion}}) + (fn [m] (swap! site-state assoc :current-route m)) + ;; set to false to enable HistoryAPI + {:use-fragment true}) +#+END_SRC + + +To create a link to a route, you can use the =rfe/href= function which takes a lookup key which you specified in your routes, in this instance the key is name spaced to the current namespace. +#+BEGIN_SRC clojurescript +[:a {:href (rfe/href ::frontpage)} "example link"] +#+END_SRC diff --git a/reagent-reitit-demo/src/clojure_demo/core.cljs b/reagent-reitit-demo/src/clojure_demo/core.cljs index bdd8da2..9481512 100644 --- a/reagent-reitit-demo/src/clojure_demo/core.cljs +++ b/reagent-reitit-demo/src/clojure_demo/core.cljs @@ -160,22 +160,23 @@ :HEADER1 (fn [v _] [:h1.f2.fw6.f3-ns.lh-title.mt0.mb2 {:id (slugify v)} [:a {:name (slugify v)} v]]) :HEADER2 (fn [v _] [:h2.f3.fw6.f3-ns.lh-title.mt0.mb2 {:id (slugify v)} [:a {:name (slugify v)} v]]) :LINE :p.f5.f5-ns.lh-copy.mt0 + :BULLETS :ul.mt0 :LINK :a #_link-handler})) (def theme-toc (merge tachyon-theme {:HEADER1 (fn [v _] [:li [:a {:href (str "#" (slugify v))} - [:span.f2.fw6.f3-ns.lh-title.mt0.mb2 v]]]) - :HEADER2 (fn [v _] [:li.ml2 [:a {:href (str "#" (slugify v))} - [:span.f2.fw6.f3-ns.lh-title.mt0.mb2 v]]]) - :HEADER3 (fn [v _] [:li.ml4 [:a {:href (str "#" (slugify v))} - [:span.f3.fw6.f3-ns.lh-title.mt0.mb2 v]]]) - :HEADER4 (fn [v _] [:li.ml6 [: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]]]) + :HEADER3 (fn [v _] [:li.ml4 [:a {:href (str "#" (slugify 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]]]) :HEADER5 (fn [v _] [:li.ml8 [:a {:href (str "#" (slugify v))} - [:span.f5.fw6.f3-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.f3-ns.lh-title.mt0.mb2 v]]])})) + [:span.f6.fw6.f5-ns.lh-title.mt0.mb2 v]]])})) ;; put constant data here (def site-data @@ -359,7 +360,10 @@ (fn [route] [:main.mt4.mw7.center.avenir - [:h1 (: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 "test.tar" :href (.createObjectURL @@ -369,8 +373,6 @@ (:code @content)))] {:type "application/tar"}))} "Download Code"] - [:div.mw7.center.avenir - (into [:ol] (org->replacements theme-toc (:toc @content)))] [:div.mw7.center.avenir (into [:div] (org->replacements theme (:body @content)))]]))) @@ -448,7 +450,7 @@ {:href (rfe/href ::dsl) :text "DSL's" :key "dsl"} {:href (rfe/href ::about) :text "About" :key "about"} #_{:href (rfe/href ::i-do-not-exist) :text "missing"}]] - [:main.mt4 + [:main.ma4 (when-let [view (-> @site-state :current-route :data :view)] [view (-> @site-state :current-route)])] [footer]])