Add example of rendering google maps.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Oly 2023-08-07 16:58:12 +01:00
parent da51aadd6a
commit 608ea2f9e8
7 changed files with 358 additions and 75 deletions

View File

@ -1,4 +1,4 @@
((clojurescript-mode .
((clojurescript-mode .
(cider-clojure-cli-aliases . "-M:dev")
(cider-preferred-build-tool . clojure-cli)
(cider-default-cljs-repl . custom)

View File

@ -1,5 +1,7 @@
{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.11.60"}
org.clojure/core.async {:mvn/version "1.6.673"}
funcool/promesa {:mvn/version "9.0.494"}
;;ajax requests
cljs-ajax/cljs-ajax {:mvn/version "0.8.1"}

View File

@ -19,20 +19,10 @@ The code below can be evaluated live inside the page you can select parts of the
#+END_SRC
#+BEGIN_SRC text :export none :results silent :tangle readme.org
#+BEGIN_SRC text :export none :results silent :tangle readme.org
#+TITLE: Getting started
#+END_SRC
* Install the npm requirements.
npx install
* Launch shadow-cljs watch for source code changes
npx shadow-cljs watch app
#+END_SRC
** Clojure comments
There a few ways to comment code in this language.

View File

@ -0,0 +1,211 @@
#+TITLE: Embedding maps in your apps
#+DESCRIPTION: Example's of embeding maps into your frontend application.
* Introduction
Install npm dependencies
npm install
Run the project
npx shadow-cljs server app
* Google Maps
Below is an example of using google maps, it pulls in the library directly you could alternatively use an npm dependency.
You will need to provide your own api key, the examples are blank and render a warning so paste in your google api key to make this work.
#+BEGIN_SRC html :results silent :exports none :tangle resources/public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Clojure demos</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div id="app">
App loading here
</div>
<script src="/cljs-out/main_bundle.js" type="application/javascript"></script>
</body>
</html>
#+END_SRC
#+BEGIN_SRC edn :results silent :exports none :tangle deps.edn
{:paths ["src" "resources"]
:deps
{org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.11.60"}
funcool/promesa {:mvn/version "11.0.671"}
reagent/reagent {:mvn/version "1.2.0"}
thheller/shadow-cljs {:mvn/version "2.24.0"}}}
#+END_SRC
#+BEGIN_SRC edn :results silent :exports none :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 :exports none :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 clojurescript :exports none :tangle src/clojure_demo/core.cljs
(ns clojure-demo.core
(:require
["react-dom/client" :refer [createRoot]]
[cljs.core.async :as async]
[cljs.core.async.interop :as async-in]
[promesa.core :as promesa]
[shadow.cljs.modern :refer [js-await] :as shadow]
[reagent.core :as reagent]))
#+END_SRC
** Load in the google maps script by adding it to the dom
#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
(defn load-google-maps-script [api-key]
(let [script (.createElement js/document "script")]
;; copied from googles recommended way of loading google maps
(set! (.-innerHTML script) (str "(g=>{var h,a,k,p=\"The Google Maps JavaScript API\",c=\"google\",l=\"importLibrary\",q=\"__ib__\",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement(\"script\"));e.set(\"libraries\",[...r]+\"\");for(k in g)e.set(k.replace(/[A-Z]/g,t=>\"_\"+t[0].toLowerCase()),g[k]);e.set(\"callback\",c+\".maps.\"+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+\" could not load.\"));a.nonce=m.querySelector(\"script[nonce]\")?.nonce||\"\";m.head.append(a)}));d[l]?console.warn(p+\" only loads once. Ignoring:\",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: \"" api-key "\", v: \"weekly\"});"))
(.appendChild (.-head js/document) script)))
#+END_SRC
** Create a map using google and core async
In this example we render google maps using core async, async go blocks drop type hints so we need to pull out any hinting to functions, this is a shame as this version reduces the amount of nesting.
#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
(defn google-map-core-async []
(let [map-element (reagent/atom nil)
;; pulled out of the go block to satisfy the infer warnings, the go block removes hints
get-map-obj (fn [obj] (.-Map ^js/google.maps.Map obj))
get-marker-obj (fn [obj] (.-Marker ^js/google.maps.Map obj))]
(load-google-maps-script "")
;; go blocks loose hints so may cause infer warnings
(async/go (let [gMap (get-map-obj (async-in/<p! (js/google.maps.importLibrary "maps")))
gMarker (get-marker-obj (async-in/<p! (js/google.maps.importLibrary "marker")))
map (gMap. @map-element
(clj->js {:center {:lng 131.031 :lat -25.344} :zoom 4}))]
(gMarker. (clj->js {:map map
:title "test marker 1"
:position {:lng 131.031 :lat -24.344}}) "marker 1")
(gMarker. (clj->js {:map map
:title "test marker 2"
:position {:lng 131.031 :lat -25.344}}) "marker 2")))
(fn []
[:div#google-map-async.m-4
{:style {:width "400px" :height "400px"} :ref #(reset! map-element %)}
"core async map here"])))
#+END_SRC
** Create a map using google and shadow js-await
#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
(defn google-map-js-await []
(let [map-element (reagent/atom nil)]
(load-google-maps-script "")
(shadow/js-await
[js-map (js/google.maps.importLibrary "maps")]
(shadow/js-await
[js-marker (js/google.maps.importLibrary "marker")]
(when @map-element
(let [gMap (.-Map ^js/google.maps.Map js-map)
gMarker (.-Marker ^js/google.maps.Marker js-marker)
map (gMap. @map-element
(clj->js {:center {:lng 131.031 :lat -25.344} :zoom 4}))]
(gMarker. (clj->js {:map map
:title "test marker 1"
:position {:lng 131.031 :lat -24.344}}) "marker 1")
(gMarker. (clj->js {:map map
:title "test marker 2"
:position {:lng 131.031 :lat -25.344}}) "marker 2")
nil))))
(fn []
[:div#google-map-js-await.m-4
{:style {:width "400px" :height "400px"} :ref #(reset! map-element %)}
"shadow js-await map here"])))
[google-map-js-await]
#+END_SRC
** Create a map using google and promesa
An example using promesa to render a google map.
#+BEGIN_SRC clojurescript :results output :tangle src/clojure_demo/core.cljs
(defn google-map-promesa []
(let [map-element (reagent/atom nil)]
(load-google-maps-script "")
(promesa/let
[js-map (js/google.maps.importLibrary "maps")
js-marker (js/google.maps.importLibrary "marker")]
(let
[gMap (.-Map ^js/google.maps.Map js-map)
gMarker (.-Marker ^js/google.maps.Marker js-marker)
map (gMap. @map-element
(clj->js {:center {:lng 131.031 :lat -25.344} :zoom 4}))]
(gMarker. (clj->js {:map map
:title "test"
:position {:lng 131.031 :lat -24.344}}) "marker 1")
(gMarker. (clj->js {:map map
:title "test"
:position {:lng 131.031 :lat -25.344}}) "marker 1")))
(fn []
[:div#google-map-promesa.m-4
{:style {:width "400px" :height "400px"} :ref #(reset! map-element %)}
"promesa map here"])))
[google-map-promesa]
#+END_SRC
#+BEGIN_SRC clojure :exports none :tangle src/clojure_demo/core.cljs
(defn current-page []
[:div
[google-map-core-async]
[google-map-promesa]
[google-map-js-await]])
(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 [current-page])))
(def startup! (mount-root-page))
#+END_SRC

View File

@ -12,6 +12,13 @@
name="description"
content="Examples & Guides on using various libraries and technologies with in the clojure eco system's"
/>
<link rel="canonical" href="https://clojure-demos.digitaloctave.com/" />
<meta property="og:type" content="article" />
<meta property="og:title" content="TITLE OF YOUR POST OR PAGE" />
<meta property="og:description" content="DESCRIPTION OF PAGE CONTENT" />
<meta property="og:image" content="LINK TO THE IMAGE FILE" />
<meta property="og:url" content="PERMALINK" />
<meta property="og:site_name" content="SITE NAME" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link

View File

@ -8,6 +8,9 @@
["@nextjournal/lang-clojure" :refer [clojure]]
["react-dom/client" :refer [createRoot]]
["tarts" :as tarts]
[cljs.core.async :as async]
[cljs.core.async.interop :as async-in]
[shadow.cljs.modern :refer [js-await]]
[ajax.core :refer [GET raw-response-format]]
[cl-eorg.html :as h :refer [body headers org->replacements]]
[cl-eorg.parser :as o :refer [parse]]
@ -22,6 +25,7 @@
[reitit.frontend.history :refer [ignore-anchor-click?]]
[sci.configs.reagent.reagent :as sci-reagent]
[sci.configs.tonsky.datascript :as sci-datascript]
[sci.configs.funcool.promesa :as sci-promesa]
[sci.core :as sci]
[clojure-demo.tailwind-theme :refer [tailwind-theme]]
[spec-tools.data-spec :as ds]))
@ -35,18 +39,45 @@
(def yaml-mode (LanguageSupport. (.define StreamLanguage yaml)))
(defn ^:sci/macro my-js-await [_ _ [name thenable] & body]
(let [last-expr (last body)
[body catch]
(if (and (seq? last-expr) (= 'catch (first last-expr)))
[(butlast body) last-expr]
[body nil])]
;; FIXME: -> here will always return a promise so shouldn't be necessary to add js hint?
`(-> ~thenable
~@(when (seq body)
[`(.then (fn [~name] ~@body))])
~@(when catch
(let [[name & body] catch]
[`(.catch (fn [~name] ~@body))]
)))))
;; https://github.com/babashka/sci.configs
(def rf-ns (sci/create-ns 'reitit.frontend nil))
(def rfe-ns (sci/create-ns 'reitit.frontend.easy nil))
(def rss-ns (sci/create-ns 'reitit.coercion.spec nil))
(def sql-ns (sci/create-ns 'honey.sql.core nil))
(def sqlh-ns (sci/create-ns 'honey.sql.helpers nil))
(def shadow-ns (sci/create-ns 'shadow.cljs.modern nil))
;; core async does not work with sci
(def async-ns (sci/create-ns 'cljs.core.async nil))
(def async-in-ns (sci/create-ns 'cljs.core.async.interop nil))
(def sci-ctx
(sci/init
{:classes {'js js/globalThis :allow :all}
:namespaces
{'reagent sci-reagent/reagent-namespace
'promesa sci-promesa/promesa-namespace
;'h {'html (sci/copy-var h/html hc-ns)}
;'async {'go (sci/copy-var async/go async-ns)}
;'shadow {'js-await (sci/copy-var js-await shadow-ns)}
'shadow {'js-await (sci/copy-var my-js-await shadow-ns)}
;'async-in {'<p! (sci/copy-var async-in/<p! async-in-ns)}
'd sci-datascript/core-namespace
'sql {'format (sci/copy-var sql/format sql-ns)
#_#_'raw (sci/copy-var sql/raw sql-ns)}
@ -108,7 +139,7 @@
;(into [:<>] children)
))})))
(defn code-editor
[{:keys [exports class results]} content]
[{:keys [export class results]} content]
(let [language class
editor (atom nil)
evaled-result (reagent/atom nil)
@ -146,13 +177,13 @@
:parent @editor})))))
:component-will-unmount (fn [_] (.destroy @view))
:reagent-render (fn []
(if exports
(if export
nil
[:div.mb2
[:div.ba.ma0.f5.b--black-05.pa2.overflow-auto.editor {:ref #(reset! editor %)}]
[:div.mb-8
[:div.ba.ma-0.f5.b--black-05.pa2.overflow-auto.editor {:ref #(reset! editor %)}]
(when @evaled-result
[err-boundary
[:pre.ba.ma0.f6.code.b--black-05.pa2.pl4.overflow-auto
[:pre.border-2.pa-4.pl-6.text-xl
{:style {:white-space "pre-line"}}
;; See org mode :results key
(case results
@ -165,9 +196,9 @@
(def theme-tachyon
(merge tachyon-theme
{:SRC code-editor
: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]])
:HEADER3 (fn [v _] [:h2.f4.fw6.f3-ns.lh-title.mt0.mb2 {:id (slugify v)} [:a {:name (slugify v)} v]])
:HEADER1 (fn [v _] [:h1 {:id (slugify v)} [:a {:name (slugify v)} v]])
:HEADER2 (fn [v _] [:h2 {:id (slugify v)} [:a {:name (slugify v)} v]])
:HEADER3 (fn [v _] [:h2 {:id (slugify v)} [:a {:name (slugify v)} v]])
:LINE :p.f5.f5-ns.lh-copy.mt0
:BULLETS :ul.mt0
:LINK :a #_link-handler}))
@ -175,14 +206,14 @@
(def theme
(merge tailwind-theme
{:SRC code-editor
:HEADER1 (fn [v _] [:h1.text-2xl.fw6.lh-title.mt0.mb2 {:id (slugify v)} [:a {:name (slugify v)} v]])
:HEADER2 (fn [v _] [:h2.text-xl.fw6.lh-title.mt0.mb2 {:id (slugify v)} [:a {:name (slugify v)} v]])
:HEADER3 (fn [v _] [:h2.text-base.lh-title.mt0.mb2 {:id (slugify v)} [:a {:name (slugify v)} v]])
:HEADER1 (fn [v _] [:h1.text-2xl.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} [:a {:name (slugify v)} v]])
:HEADER2 (fn [v _] [:h2.text-xl.fw6.lh-title.mt-0.mb-2 {:id (slugify v)} [:a {:name (slugify v)} v]])
:HEADER3 (fn [v _] [:h2.text-base.lh-title.mt-0.mb-2 {: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-tachyon
#_(def theme-toc-tachyon
(merge tachyon-theme
{:HEADER1 (fn [v _] [:li [:a {:href (str "#" (slugify v))}
[:span.f4.fw6.f3-ns.lh-title.mt0.mb2 v]]])
@ -199,17 +230,17 @@
(def theme-toc
(merge tailwind-theme
{:HEADER1 (fn [v _] [:li [:a {:href (str "#" (slugify v))}
{:HEADER1 (fn [v _] [:li.list-decimal [:a.text-blue-800 {:class "hover:text-blue-500" :href (str "#" (slugify v))}
[:span.text-xl.fw6.lh-title.mt0.mb2 v]]])
:HEADER2 (fn [v _] [:li.ml-4 [:a {:href (str "#" (slugify v))}
:HEADER2 (fn [v _] [:li.list-decimal.ml-4 [:a.text-blue-800 {:href (str "#" (slugify v))}
[:span.text-lg.fw6.lh-title.mt0.mb2 v]]])
:HEADER3 (fn [v _] [:li.ml-8 [:a {:href (str "#" (slugify v))}
:HEADER3 (fn [v _] [:li.list-decimal.ml-8 [:a.text-blue-800 {:class "hover:text-blue-500" :href (str "#" (slugify v))}
[:span.text-base.fw6.lh-title.mt0.mb2 v]]])
:HEADER4 (fn [v _] [:li.ml-12 [:a {:href (str "#" (slugify v))}
:HEADER4 (fn [v _] [:li.list-decimal.ml-12 [:a.text-blue-800 {:href (str "#" (slugify v))}
[:span.text-sm.fw6.lh-title.mt0.mb2 v]]])
:HEADER5 (fn [v _] [:li.ml-26 [:a {:href (str "#" (slugify v))}
:HEADER5 (fn [v _] [:li.list-decimal.ml-26 [:a.text-blue-800 {:href (str "#" (slugify v))}
[:span.text-xs.fw6.f5-ns.lh-title.mt0.mb2 v]]])
:HEADER6 (fn [v _] [:li.ml-30 [:a {:href (str "#" (slugify v))}
:HEADER6 (fn [v _] [:li.list-decimal.ml-30 [:a.hover:decoration-blue-400 {:href (str "#" (slugify v))}
[:span.text-xs.fw6.f5-ns.lh-title.mt0.mb2 v]]])}))
;; put constant data here
@ -264,7 +295,11 @@
:icon-image "https://avatars.githubusercontent.com/u/2181346?s=200&v=4"}]}
:examples {:title "Examples"
:intro "Some example applications"
:key ::examples}
:key ::examples
:demos [{:title "Maps"
:description ""
:page "example-maps"
:icon-image "https://avatars.githubusercontent.com/u/2181346?s=200&v=4"}]}
:terminology {:title "Terminology"
:intro ""
:key ::terminology}
@ -282,6 +317,8 @@
{:file "documents/clojure-basics.org" :git-link "https://github.com/atomjuice/dsl-demo"}
:ci-demo
{:file "documents/ci-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
:example-maps
{:file "documents/examples-maps.org" :git-link "https://github.com/atomjuice/dsl-demo"}
:containers
{:file "documents/containers.org" :git-link "https://github.com/atomjuice/containers"}}})
@ -290,7 +327,7 @@
;; form one component to render an article
(defn article [{:keys [title description tagline]}]
[:article {:data-name "article-full-bleed-background"}
[:article.prose {:data-name "article-full-bleed-background"}
[:div.cf {:style {:background "url(http://placekitten.com/g/600/300)"
:no-repeat "center center fixed" :background-size "cover"}}
[:div.fl.pa3.pa4-ns.bg-white.black-70.measure-narrow.f3.times
@ -303,15 +340,13 @@
(defn articles [{:keys [title body articles]}]
[:section.mw7.center.avenir
[:h2.text-4xl.mt-4.mb-4 title]
(when body [:p.mb-8 body])
(map (fn [{:keys [title author link description img-src img-alt]}]
[:article.bt.bb.b--black-10 {:key title}
(map (fn [{:keys [title author link description img-src img-alt] :as article}]
[:article.bt.bb.b--black-10.border-b-2 {:key title}
[:a {:href link}
[:div.flex.flex-column.flex-row-ns.p-4
(when img-src
[:div.flex-none #_{:class "w-1/3"}
[:img.w-48 #_h-24.block {:src img-src :alt img-alt}]])
[:div.flex-none.w-32
(when img-src
[:img.w-32 #_h-24.block {:src img-src :alt img-alt}])]
[:div.ml-4.flex-grow #_{:class "w-2/3"}
[:h1.text-2xl title]
[:p description]
@ -341,39 +376,6 @@
{:target "_blank" :href "https://matrix.to/#/@oly:matrix.org"}
"Contact me"]]])
(defn home-page []
[:<>
[articles
{:title "Clojure Demos"
:body (-> site-data :homepage :intro)
:articles
[{:title "Clojure Basics"
:description "Getting started with clojure syntax datatype's sequences conditions"
:link (rfe/href ::demo {:page "clojure-basics"})
:img-src "https://clojure.org/images/clojure-logo-120b.png"}
{:title "Reagent Demo"
:description "React application using reagent"
:link (rfe/href ::demo {:page "reagent-demo"})
:img-src "https://raw.githubusercontent.com/reagent-project/reagent/master/logo/logo-text.png"}]}]])
(defn grouped-page [route]
(let [group (keyword (name (:name (:data route))))]
[:<>
[articles
{:title (-> site-data :pages group :title)
:body (-> site-data :pages group :intro)
:articles
(mapv (fn fmt-map [demo]
{:title (:title demo)
:description (:description demo)
:link (rfe/href ::demo {:page (:page demo)})
:img-src (:icon-image demo)})
(-> site-data :pages group :demos))}]]))
(defn dialects-page [route]
[:<>
[grouped-page route #_(keyword (name (:name (:data route))))]])
(def toc (partial contains? (into #{} (map keyword [:HEADER1 :HEADER2 :HEADER3 :HEADER4 :HEADER5 :HEADER6]))))
(def org-code (partial contains? (into #{} (map keyword [:SRC]))))
@ -400,6 +402,71 @@
:code (filter (fn filter-code [tag] (org-code (first tag))) dsl)
:body (filter (fn filter-body [tag] (body (first tag))) dsl)})
(defn home-page []
[:<>
[articles
{:title "Clojure Demos"
:body (-> site-data :homepage :intro)
:articles
[{:title "Clojure Basics"
:description "Getting started with clojure syntax datatype's sequences conditions"
:link (rfe/href ::page {:page "clojure-basics"})
:img-src "https://clojure.org/images/clojure-logo-120b.png"}
{:title "Reagent Demo"
:description "React application using reagent"
:link (rfe/href ::page {:page "reagent-demo"})
:img-src "https://raw.githubusercontent.com/reagent-project/reagent/master/logo/logo-text.png"}]}]])
(defn grouped-page [route]
(let [group (keyword (name (:name (:data route))))]
[:<>
[articles
{:title (-> site-data :pages group :title)
:body (-> site-data :pages group :intro)
:articles
(mapv (fn fmt-map [demo]
{:title (:title demo)
:description (:description demo)
:link (rfe/href ::page {:page (:page demo)})
:img-src (:icon-image demo)})
(-> site-data :pages group :demos))}]]))
(defn default-page [route]
(let [demo-key (keyword (-> route :parameters :path :page))
content (reagent/atom {})]
(GET (str "/" (-> site-data :demos demo-key :file))
{:response-format (raw-response-format)
:handler (fn [response]
;;(prn (org->replacements theme (parse response) ))
;;[:LINE "" [:LINK {:href "https://github.com/weavejester/hiccup"} "https://github.com/weavejester/hiccup"]]
(->> response
parse
org->split2
(reset! content)))})
(fn [route]
(if @content
[:main
[:h1.mt-8.mb-8.text-4xl (:content (last (first (:header @content))))]
[:div.mw7.center.avenir
(into [:ol.list-inside.m-6.font-semibold] (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.bg-sky-500.text-white.m-2.p-2.pl-4.pr-4.inline-block
{: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
(into [:div] (org->replacements theme (:body @content)))]]
[:<>]))))
;; form one render about page component
(defn about-page []
[:div
@ -417,6 +484,12 @@
:my-data "hi"
:view home-page}]
["/page/:page"
{:name ::page
:view default-page
:parameters {:path {:page string?}
:query {(ds/opt :foo) keyword?}}}]
["/terminology/"
{:name ::terminology
:view grouped-page}]

View File

@ -33,7 +33,7 @@
:LANGUAGE :class
:SRC :pre.bg-near-black.silver.pa2.hljs.roboto.overflow-auto.klipse
:RESULTS :pre.bg-near-black.silver.pa2.hljs.roboto.overflow-auto
:EXAMPLE :pre.bg-near-black.silver.pa2.hljs.roboto.overflow-auto
:EXAMPLE :pre.text-slate-700.bg-white.rounded-xl
:COMMENT nil
:CAPTION :span
:I :i
@ -42,8 +42,8 @@
;;:LINK link-handler
:IMG :img
;; probably remove these 2
:A :a
:P :p
:A :a.text-blue-800
:P :p.mb-2
:BR :br
:LINE :p.f5.f4-ns.lh-copy.mt0
:BULLETS-ORDERED :ol