Some themeing.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Oliver Marks 2021-02-01 09:11:20 +00:00
parent 2317538328
commit 7a9c2e18d9
12 changed files with 278 additions and 237 deletions

View File

@ -8,14 +8,17 @@
reagent {:mvn/version "0.9.1"} reagent {:mvn/version "0.9.1"}
reagent-utils {:mvn/version "0.3.3"} reagent-utils {:mvn/version "0.3.3"}
olymk2/cl-org {:git/url "https://gitlab.com/olymk2/cl-org.git" olymk2/cl-org {:git/url "https://gitlab.com/olymk2/cl-org.git"
:sha "b5a084e8003298a1eb79ecd65ea454970c16b123"} :sha "2bb6f41b317749ea3467987ea57ee7807b59d921"}
;;routing ;;routing
metosin/reitit {:mvn/version "0.5.10"} metosin/reitit {:mvn/version "0.5.10"}
metosin/reitit-spec {:mvn/version "0.5.10"} metosin/reitit-spec {:mvn/version "0.5.10"}
metosin/reitit-frontend {:mvn/version "0.5.10"} metosin/reitit-frontend {:mvn/version "0.5.10"}
;; interactive code snippets ;; interactive code snippets
viebel/klipse {:mvn/version "7.10.4"} ;;viebel/klipse {:mvn/version "7.10.4"}
viebel/klipse {:mvn/version "7.9.1"}
datascript {:mvn/version "1.0.0"}
com.bhauman/figwheel-main {:mvn/version "0.2.11"}} com.bhauman/figwheel-main {:mvn/version "0.2.11"}}
:paths ["src" "resources"] :paths ["src" "resources"]

View File

@ -5,4 +5,4 @@
:source-map true :source-map true
:source-map-timestamp true :source-map-timestamp true
:devcards true :devcards true
:main demo.core} :main clojure-demo.core}

View File

@ -10,6 +10,12 @@ clojure -m figwheel.main --build dev --repl
#+END_SRC #+END_SRC
* Intro to datalog * Intro to datalog
First import datascript which is a client side version of datalog, see datomic crux datalevin and datahike for other datalog database.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs
(ns core.demo
(:require [datascript.core :as d]))
#+END_SRC
See the extensive comments in the src code for a working example, also watch / read these for a good intro. See the extensive comments in the src code for a working example, also watch / read these for a good intro.
@ -22,17 +28,17 @@ https://tonsky.me/blog/the-web-after-tomorrow/
** Creating a DATABASE ** Creating a DATABASE
Datalog databases can be schema less but a lot of the power comes from creating a schema specifying uniqueness and relations. Datalog databases can be schema less but a lot of the power comes from creating a schema specifying uniqueness and relations on the stored fields.
*** Schema less Database You can create a new database using create-conn as below then empty hash map is simply a blank schema,
In it's simplest for we create a database connection like below.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs #+BEGIN_SRC clojurescript :tangle ./src/test.cljs
(def demo-conn (d/create-conn {})) (def demo-conn (d/create-conn {}))
#+END_SRC #+END_SRC
Using the connection we can just start inserting data, using standard hash maps and lists structures, we always specify the attribute and the value when transacting. Using the connection we can just start inserting data, using standard hash maps and lists structures, we always specify the attribute and the value when transacting.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs #+BEGIN_SRC clojurescript :tangle ./src/test.cljs
(d/transact! demo-conn [{:user/name "Oly" :user/img "me.jpg"} {:user/name "Sam" :user/img "you.jpg"}]) (d/transact! demo-conn [{:user/name "Brooke" :user/img "me.jpg"} {:user/name "Kalvin" :user/img "you.jpg"}])
#+END_SRC #+END_SRC
*** Using a Schema *** Using a Schema
@ -44,18 +50,18 @@ In this example we are saying name is unique and rooms has a many to one relatio
(def demo-conn (d/create-conn schema)) (def demo-conn (d/create-conn schema))
#+END_SRC #+END_SRC
Transacting this data would mean Oly would be inserted once but the image will be updated to =you.jpg= Transacting this data would mean Brooke would be inserted once but the image will be updated to =you.jpg=
#+BEGIN_SRC clojurescript :tangle ./src/test.cljc #+BEGIN_SRC clojurescript :tangle ./src/test.cljc
(d/transact! demo-conn [{:user/name "Oly" :user/img "me.jpg"} {:user/name "Oly" :user/img "you.jpg"}]) (d/transact! demo-conn [{:user/name "Brooke" :user/img "you.jpg"}])
#+END_SRC #+END_SRC
** Querying the databases ** Querying the databases
There are three types of queries in datalog entity lookup's pulling a tree of data or querying with =d/q=. There are three types of queries in datalog entity lookup's pulling a tree of data or querying with =d/q=.
*** Looking up an entity *** Looking up an entity
=d/entity= is used to find the entity id, using any unique piece of data for example the user =Oly= exists once so the entity db/id will be returned which can be used for further queries. =d/entity= is used to find the entity id, using any unique piece of data for example the user =Brooke= exists once so the entity db/id will be returned which can be used for further queries.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljc #+BEGIN_SRC clojurescript :tangle ./src/test.cljc
(d/entity @conn [:user/name "Oly"]) (d/entity @demo-conn [:user/name "Brooke"])
#+END_SRC #+END_SRC
*** Pull a tree of data *** Pull a tree of data

View File

@ -192,19 +192,19 @@ This has some really nice side effects, one being it is very easy to navigate yo
*** Basic value fetching *** Basic value fetching
#+BEGIN_SRC clojure :tangle src/core.cljc #+BEGIN_SRC clojure :tangle src/core.cljc
(def my-hashmap {:top {:first-key 1 :second-key 2}}) (def my-hashmap {:top-lvl-key {:first-key 1 :second-key 2}})
;; you can get the value in a number of ways. ;; you can get the value in a number of ways.
;; using neted get return the result of one get to the next ;; using neted get return the result of one get to the next
(get (get my-hashmap :top) :second-key) (get (get my-hashmap :top-lvl-key) :second-key)
;; much nicer is to use get-in and specify the path ;; much nicer is to use get-in and specify the path
(get-in my-hashmap [:top :second-key]) (get-in my-hashmap [:top-lvl-key :second-key])
;; You can also call the keywords as a function ;; You can also call the keywords as a function
(:second-key (:top my-hashmap)) (:second-key (:top-lvl-key my-hashmap))
;; or using something called a threading macro ;; or using something called a threading macro
;; push the map through the :top function then the result ;; push the map through the :top-lvl-key function then the result
;; into the :second-key function ;; into the :second-key function
(-> my-hashmap :top :second-key) (-> my-hashmap :top-lvl-key :second-key)
#+END_SRC #+END_SRC
@ -219,9 +219,34 @@ Using =:or= we can set default values if the key is missing, the :as keyword can
* Wierd symbol's * Wierd symbol's
There is a good reference on the symbols in clojure below when you encounter one your not sure about. There is a good reference on the symbols in clojure in the link below when you encounter one your not sure about.
https://clojure.org/guides/weird_characters https://clojure.org/guides/weird_characters
Below you will find some simple examples, it is worth noting that some of the wierd symbols are just shorthand for a longer function name calls.
=:= Colon indicates a keyword's
;; :: Double colon makes a namespaced keyword, what ever is defined at the top of your file under (ns)
;; will be pre pended to the keyword
;; , comma is just white space and is used only for readability to the user
;; #( is an annoymous function (fn [param1] (prn param1)) is equivalent to #(prn %) % being param1
;; you can also use %1 %2 %3 etc to refernce other params, longer form is prefered because the params are named
;; but for very short small functions this variant can be handy
;; -> is the threading macro, basically the result of each statement is passed to the next
;; as the first parameter, this works nicely with hashmaps because keywords are functions
;; ->> same as above but the result is the last parameter
;; when working with sequences you tend to use this one most functions Take
;; a sequence as the last parameter
;; '( this is the same as writting (list 1 2 3) the ' denotes we are using the list function
;; #_ this is the comment block it
#+BEGIN_SRC clojure :tangle src/core.cljc #+BEGIN_SRC clojure :tangle src/core.cljc
;; : Colon indicates a keyword's ;; : Colon indicates a keyword's

View File

@ -0,0 +1,86 @@
#+TITLE: Minimal clojurescript project demoing datalog queries
* Getting started
To work with the code interactively jack into the project in your IDE of choice select "figwheel-main" as the build tool and dev as the build.
Alternatively start a repl from the command line with
#+BEGIN_SRC sh
clojure -m figwheel.main --build dev --repl
#+END_SRC
* Intro to datalog
First import datascript which is a client side version of datalog, see datomic crux datalevin and datahike for other datalog database.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs
(ns core.demo
(:require [datascript.core :as d]))
#+END_SRC
See the extensive comments in the src code for a working example, also watch / read these for a good intro.
https://www.youtube.com/watch?v=oo-7mN9WXTw
http://www.learndatalogtoday.org/
https://udayv.com/clojurescript/clojure/2016/04/28/datascript101/
Blog of the dev who makes datascript
https://tonsky.me/blog/the-web-after-tomorrow/
** Creating a DATABASE
Datalog databases can be schema less but a lot of the power comes from creating a schema specifying uniqueness and relations on the stored fields.
You can create a new database using create-conn as below then empty hash map is simply a blank schema,
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs
(def demo-conn (d/create-conn {}))
#+END_SRC
Using the connection we can just start inserting data, using standard hash maps and lists structures, we always specify the attribute and the value when transacting.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs
(d/transact! demo-conn [{:user/name "Brooke" :user/img "me.jpg"} {:user/name "Kalvin" :user/img "you.jpg"}])
#+END_SRC
*** Using a Schema
In this example we are saying name is unique and rooms has a many to one relationship, when we transact data will be inserted even if its not in the schema but rules stop things like duplicates from happening.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljs
(def schema {:user/name {:db/unique :db.unique/identity}
:user/rooms {:db/cardinality :db.cardinality/many
:db/valueType :db.type/ref}})
(def demo-conn (d/create-conn schema))
#+END_SRC
Transacting this data would mean Brooke would be inserted once but the image will be updated to =you.jpg=
#+BEGIN_SRC clojurescript :tangle ./src/test.cljc
(d/transact! demo-conn [{:user/name "Brooke" :user/img "you.jpg"}])
#+END_SRC
** Querying the databases
There are three types of queries in datalog entity lookup's pulling a tree of data or querying with =d/q=.
*** Looking up an entity
=d/entity= is used to find the entity id, using any unique piece of data for example the user =Brooke= exists once so the entity db/id will be returned which can be used for further queries.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljc
(d/entity @demo-conn [:user/name "Brooke"])
#+END_SRC
*** Pull a tree of data
Pull is used with entity id's once you know the entity you can specify what data you want to view ='[*]= being the most common looking up all keys, you can also specify the attributes your interested in looking up including there relations to make a more specific view.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljc
(d/pull @demo-conn '[*] 1)
(d/pull @demo-conn '[:user/name :user/rooms] 1)
#+END_SRC
*** Querying your dataset
Querying in datalog is all about binding variables to your entities attributes and values which you can use in you conditions or to return in the result set.
In this example we return the user-id and user/name in the find clause which we looked up in the where clause by finding all attributes =:user/name= the binding the entity id and username to variables on each match to display in the find clause.
#+BEGIN_SRC clojurescript :tangle ./src/test.cljc
(d/q '[:find ?user-entity ?user-name :where
[?user-entity :user/name ?user-name]] @demo-conn)
#+END_SRC
Write some more as needed better examples in the src code.

View File

@ -0,0 +1,98 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>Clojure demos</title>
<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 rel="stylesheet" type="text/css" href="https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css">
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
<link rel="stylesheet" type="text/css" href="https://raw.githubusercontent.com/FarhadG/code-mirror-themes/master/themes/rdark.css">
<style>
.cm-s-rdark {
font-size: 1em;
line-height: 1.5em;
font-family: inconsolata, monospace;
letter-spacing: 0.3px;
word-spacing: 1px;
background: #1B2426;
color: #B9BDB6;
}
.cm-s-rdark .CodeMirror-lines {
padding: 8px 0;
}
.cm-s-rdark .CodeMirror-gutters {
box-shadow: 1px 0 2px 0 rgba(0, 0, 0, 0.5);
-webkit-box-shadow: 1px 0 2px 0 rgba(0, 0, 0, 0.5);
background-color: #1B2426;
padding-right: 10px;
z-index: 3;
border: none;
}
.cm-s-rdark div.CodeMirror-cursor {
border-left: 3px solid #B9BDB6;
}
.cm-s-rdark .CodeMirror-activeline-background {
background: #00000070;
}
.cm-s-rdark .CodeMirror-selected {
background: #E0E8FF66;
}
.cm-s-rdark .cm-comment {
color: #646763;
}
.cm-s-rdark .cm-string {
color: #5CE638;
}
.cm-s-rdark .cm-number {
color: null;
}
.cm-s-rdark .cm-atom {
color: null;
}
.cm-s-rdark .cm-keyword {
color: #5BA1CF;
}
.cm-s-rdark .cm-variable {
color: #FFAA3E;
}
.cm-s-rdark .cm-def {
color: #FFFFFF;
}
.cm-s-rdark .cm-variable-2 {
color: #FFFFFF;
}
.cm-s-rdark .cm-property {
color: null;
}
.cm-s-rdark .cm-operator {
color: #5BA1CF;
}
.cm-s-rdark .CodeMirror-linenumber {
color: #646763;
}
#notification-container {
position: fixed;
z-index: 999999;
top: 12px;
right: 12px;
</style>
<meta name="robots" content="noindex">
</head>
<body>
<div id="app">
loading here
</div>
<script src="/cljs-out/dev-main.js" type="text/javascript"></script>
</body>
</html>

View File

@ -9,8 +9,8 @@
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> <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 rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<link type="text/css" href="/css/style.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css"> <link rel="stylesheet" type="text/css" href="https://storage.googleapis.com/app.klipse.tech/css/codemirror.css">
<link rel="stylesheet" type="text/css" href="https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css">
<style> <style>
#notification-container { #notification-container {

View File

@ -1,24 +1,54 @@
(ns ^:figwheel-hooks demo.core (ns ^:figwheel-hooks clojure-demo.core
(:require [reagent.core :as reagent] (:require [reagent.core :as reagent]
[ajax.core :refer [GET raw-response-format]] [ajax.core :refer [GET raw-response-format]]
;[demo.org :refer [parse->to-hiccup parse-flat]] ;[demo.org :refer [parse->to-hiccup parse-flat]]
[cl-eorg.parser :as o :refer [parse parse-flat]] [cl-eorg.parser :as o :refer [parse parse-flat]]
[cl-eorg.html :refer [org->replacements]] [cl-eorg.html :refer [org->replacements tachyon-theme]]
[reitit.frontend :as rf] [reitit.frontend :as rf]
[reitit.frontend.easy :as rfe] [reitit.frontend.easy :as rfe]
[reitit.coercion.spec :as rss] [reitit.coercion.spec :as rss]
;; interactive
[klipse.run.plugin.plugin]
[klipse.plugin :as klipse-plugin]
[datascript.core :as d]
[spec-tools.data-spec :as ds])) [spec-tools.data-spec :as ds]))
(defn klipse-wrapper
[{:keys [scripts content settings]}]
(reagent/create-class
{:component-did-mount (fn [_]
(klipse-plugin/init (clj->js settings)))
:reagent-render (fn [{:keys [content]}]
content)}))
(defn klipse-snippet [content]
[klipse-wrapper {:content [:div.cm-s-rdark [:pre.cm-s-rdark.bg-near-black.silver.pa2.hljs.roboto.overflow-auto.klipse content]]
:settings {:selector ".klipse"
:selector_reagent ".klipse"
}}])
(def theme
(-> tachyon-theme
(assoc :U :u)
(assoc :I :i)
(assoc :TITLE :header.f2.fw6.f2-ns.lh-title.mt0.mb2)
(assoc :HEADER1 :h1.f3.fw6.f3-ns.lh-title.mt0.mb2)
(assoc :HEADER2 :h2.f3.fw6.f3-ns.lh-title.mt0.mb2)
(assoc :SRC klipse-snippet)))
;; put constant data here ;; put constant data here
(def site-data (def site-data
{:demos {:dsl-demo {:demos {:dsl-demo
{:file "dsl-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"} {:file "documents/dsl-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
:datalog-demo :datalog-demo
{:file "datalog-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"} {:file "documents/datalog-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
:reagent-demo :reagent-demo
{:file "reagent-reitit.org" :git-link "https://github.com/atomjuice/dsl-demo"} {:file "documents/reagent-reitit.org" :git-link "https://github.com/atomjuice/dsl-demo"}
:clojure-basics :clojure-basics
{:file "clojure-basics.org" :git-link "https://github.com/atomjuice/dsl-demo"}} {:file "documents/clojure-basics.org" :git-link "https://github.com/atomjuice/dsl-demo"}}
:homepage {:intro "Clojure tutorials examples and exploration"} :homepage {:intro "Clojure tutorials examples and exploration"}
:lorem "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."}) :lorem "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."})
@ -118,7 +148,7 @@
:handler (fn [response] :handler (fn [response]
(->> response (->> response
parse parse
org->replacements (org->replacements theme)
(reset! content)))}) (reset! content)))})
(fn [route] (fn [route]
[:main.mt4 [:main.mt4
@ -189,7 +219,9 @@
;; top level component contains nav and adds in the select page into a containing element ;; top level component contains nav and adds in the select page into a containing element
;; we are adding in a style sheet but this will often be done in index.html ;; we are adding in a style sheet but this will often be done in index.html
(defn current-page [] (defn current-page []
[:<> [:link {:rel "stylesheet" :href "https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css"}] [:<>
;;[:link {:rel "stylesheet" :href "https://raw.githubusercontent.com/FarhadG/code-mirror-themes/master/themes/rdark.css"}]
;;[:link {:rel "stylesheet" :href "https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css"}]
[navbar [{:href (rfe/href ::frontpage) :title "title here" :text "home"} [navbar [{:href (rfe/href ::frontpage) :title "title here" :text "home"}
{:href (rfe/href ::about) :text "About"} {:href (rfe/href ::about) :text "About"}
{:href (rfe/href ::i-do-not-exist) :text "missing"}]] {:href (rfe/href ::i-do-not-exist) :text "missing"}]]
@ -213,6 +245,7 @@
(mount-root-page)) (mount-root-page))
(defn startup! [] (defn startup! []
(rfe/start! (rfe/start!
(rf/router routes {:data {:coercion rss/coercion}}) (rf/router routes {:data {:coercion rss/coercion}})
(fn [m] (swap! site-state assoc :current-route m)) (fn [m] (swap! site-state assoc :current-route m))

View File

@ -1,210 +0,0 @@
(ns ^:figwheel-hooks core.demo
(:require [reagent.core :as reagent]
[ajax.core :refer [GET raw-response-format]]
[klipse.run.plugin.plugin]
[klipse.plugin :as klipse-plugin]
;[demo.org :refer [parse->to-hiccup parse-flat]]
[cl-eorg.parser :as o :refer [parse parse-flat]]
[cl-eorg.html :refer [org->replacements]]
[reitit.frontend :as rf]
[reitit.frontend.easy :as rfe]
[reitit.coercion.spec :as rss]
[spec-tools.data-spec :as ds]))
;; put constant data here
(def site-data
{:demos {:dsl-demo
{:file "dsl-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
:datalog-demo
{:file "datalog-demo.org" :git-link "https://github.com/atomjuice/dsl-demo"}
:reagent-demo
{:file "reagent-reitit.org" :git-link "https://github.com/atomjuice/dsl-demo"}}
:lorem "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."})
;; Store site state
(defonce site-state (reagent/atom {}))
;; for one component to render an article
(defn article [{:keys [title description tagline]}]
[:article {:data-name "article-full-bleed-background"}
[:div.cf {:style {:background "url(http://mrmrs.github.io/photos/12.jpg)"
:no-repeat "center center fixed" :background-size "cover"}}
[:div.fl.pa3.pa4-ns.bg-white.black-70.measure-narrow.f3.times
[:header.b--black-70.pv4 {:class (when tagline "bb")}
[:h3.f2.fw7.ttu.tracked.lh-title.mt0.mb3.avenir title]
(when tagline [:h4.f3.fw4.i.lh-title.mt0 tagline])]
[:section.pt5.pb4 [:p.times.lh-copy.measure.f4.mt0 description]]]]])
;; form one component to render article tiles
(defn articles [{:keys [title articles]}]
[:section.mw7.center.avenir {:key title}
[:h2.baskerville.fw1.ph3.ph0-l title]
(map (fn [{:keys [title author link description img-src img-alt]}]
[:article.bt.bb.b--black-10
[:a.db.pv4.ph3.ph0-l.no-underline.black.dim {:href link}
[:div.flex.flex-column.flex-row-ns
(when img-src
[:div.pr3-ns.mb4.mb0-ns.w-100.w-40-ns
[:img.db {:src img-src :alt img-alt}]])
[:div.w-100.w-60-ns.pl3-ns
[:h1.f3.fw1.baskerville.mt0.lh-title title]
[:p.f6.f5-l.lh-copy description]
[:p.f6.lh-copy.mv0 author]]]]])
articles)])
;; form one component to render a product
(defn product-card [{:keys [title amount description link]}]
[:article.br2.ba.dark-gray.b--black-10.mv4.w-100.w-50-m.w-25-l.mw5.center
[:img.db.w-100.br2.br--top {:src link}]
[:div.pa2.ph3-ns.pb3-ns
[:div.dt.w-100.mt1
[:div.dtc [:h1.f5.f4-ns.mv0 title]]
[:div.dtc.tr [:h2.f5.mv0 amount]]]
[:p.f6.lh-copy.measure.mt2.mid-gray description]]])
(defn circle [{:keys [img alt]}]
[:div.pa4.tc [:img.br-100.ba.h3.w3.dib {:src img :alt alt}]])
;; form one component ro render a nav link
(defn navbar-link [{:keys [href title text] :or {text nil title nil}}]
[:a.link.dim.white.dib.mr3 {:key href :href href :title title} text])
;; form one component to render a navbar
(defn navbar [links]
[:header.bg-black-90.w-100.ph3.pv3.pv4-ns.ph4-m.ph5-l
[:nav.f6.fw6.ttu.tracked
(map navbar-link links)]])
(defn my-component [title]
(let [local-state (reagent/atom true)]
(fn []
[:h1 {:class (when @local-state "hid") :on-click (fn [] (swap! local-state not))} title])))
;; form one homepage component
(defn home-page []
[:<>
[:h1 (:lorem @site-state)]
[articles
{:title "Clojure Demos"
:articles [{:title "DSL Demo"
:link (rfe/href ::demo {:page "dsl-demo"})
:img-src "https://miro.medium.com/max/1400/1*CEYFj5R57UFyCXts2nsBqA.png"}
{:title "Datalog Demo"
:link (rfe/href ::demo {:page "datalog-demo"})
:img-src "https://raw.githubusercontent.com/tonsky/datascript/master/extras/logo.svg"}
{:title "Reagent Demo"
:link (rfe/href ::demo {:page "reagent-demo"})
:img-src "https://raw.githubusercontent.com/reagent-project/reagent/master/logo/logo-text.png"}]}]])
;; form one render demo component
(defn demo-page [route]
(let [demo-key (keyword (-> route :parameters :path :page))
content (reagent/atom {})]
(GET (-> site-data :demos demo-key :file)
{:response-format (raw-response-format)
:handler (fn [response]
(->> response
parse
org->replacements
(reset! content))
#_(reset! content (org->replacements (parse response))))})
(fn [route]
;[:div (-> route :parameters :path :page)]
[:main.mt4
[my-component "component 1"]
[my-component "component 2"]
[circle {:alt "test"}]
[:div.mw7.center.avenir @content]
#_[article {:title "Homepage"
:description @content
:tagline "tagline here"}]])))
;; form one render about page component
(defn about-page []
[:div "about"
[product-card {:title "title" :amount "10.59" :description "long description here" :href "test-link"}]])
;; form 3 component wrap rendering to catch errors and render them
;; or just render the rest of the page if all is good
;; this uses =create-class= and a hash map of life cycle functions
(defn err-boundary
[& children]
(let [err-state (reagent/atom nil)]
(reagent/create-class
{:display-name "ErrBoundary"
:component-did-catch (fn [err info]
(reset! err-state [err info]))
:reagent-render (fn [& children]
(if (nil? @err-state)
(into [:<>] children)
(let [[_ info] @err-state]
[:pre [:code (pr-str info)]])))})))
;; define our routes, just nested vectors of the route definition hash maps
(def routes
[["/"
{:name ::frontpage
:my-data "hi"
:view home-page}]
["/about"
{:name ::about
:view about-page}]
["/demo/:page"
{:name ::demo
:view demo-page
:parameters {:path {:page string?}
:query {(ds/opt :foo) keyword?}}}]])
;; top level component contains nav and adds in the select page into a containing element
;; we are adding in a style sheet but this will often be done in index.html
(defn current-page []
[:<> [:link {:rel "stylesheet" :href "https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css"}]
[navbar [{:href (rfe/href ::frontpage) :title "title here" :text "home"}
{:href (rfe/href ::about) :text "About"}
{:href (rfe/href ::i-do-not-exist) :text "missing"}]]
[:main.mt4
(when-let [view (-> @site-state :current-route :data :view)] [view (-> @site-state :current-route)])]])
;; This simply calls reagent render and puts the result in a div with the id of app
;; you can create your own index.html or figwheel provides one with the app id which will replace the default data
;; ^:after-load is meta data its not needed but informs figwheel to run this code after a page load
(defn mount-root-page []
;; this select the main node from the html file and injects your page content
(reagent/render
(fn [] [err-boundary [current-page]])
(.getElementById js/document "app")))
(defn ^:after-load render-site []
;; this select the main node from the html file and injects your page content
(mount-root-page))
(defn startup! []
;; this is used for interactive code only
(klipse-plugin/init #js {:selector ".klipse"
:selector_reagent ".klipse-reagent"})
(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})
(render-site))
;; we defonce the startup so that hot reloading does not reinitialize the state of the site
(def launch (do (startup!) true))
(comment
@site-state
(GET "/test.org" {:handler (fn [response] (swap! site-state assoc :content response))}))