Created: 2020-01-08 Wed 08:09
The web is built on this language so we need two programs in this language:
C++
Two c++ programs:
To target a browser:
mkdir app
cd app
npm init -y
npm i react create-react-class react-dom shadow-cljs
Alternatively, can use a starter:
npx create-cljs-app my-app
cd my-app
npm start
This can be a bit heavier. It has some opinionated testing out of the box and installs a heavy headless browser in node-modules. The former options is usually better for me.
{:builds {:app {:asset-path "/js"
:modules {:main {:init-fn app.main/init}}
:output-dir "public/js"
:target :browser}}
:dependencies [[reagent "0.8.1"]]
:dev-http {3000 "public"}
:source-paths ["src"]}
tree public
public
└── index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>app</title>
</head>
<body>
<div id="app"></div>
<script src="/js/main.js" type="text/javascript"></script>
</body>
</html>
Datastructures describing html
<div>
<h1>Title</h1>
<a href="https://www.google.com">Link text</a>
<ul>
<li>Thing 1</li>
<li>Thing 2</li>
</ul>
</div>
[:div
[:h1 "Title"]
[:a {:href "www.google.com"
:style {:color "blue"
:font-size "24px"}}
"Link Text"]
[:ul
[:li "Thing 1"]
[:li "Thing 2"]]]
tree src
src
└── app
└── main.cljs
(ns app.main
(:require [reagent.core :as r]))
(defn app
[]
[:div {:style {:margin "auto"
:margin-top "100px"
:width "600px"}}
[:h1 "hi"]])
(defn ^:dev/after-load start []
(r/render [app]
(.getElementById js/document "app")))
(defn ^:export init
[]
(start))
;; src/app/main.cljs
(ns app.main
(:require [reagent.core :as r]))
(defn app
[]
[:div {:style {:margin "auto"
:margin-top "100px"
:width "600px"}}
[:h1 "hi"]])
(defn ^:dev/after-load start []
(r/render [app]
(.getElementById js/document "app")))
(defn ^:export init
[]
(start))
dan@pop-os:~/projects/clojure/cljs-play/app$ npx shadow-cljs watch app
shadow-cljs - config: /home/dan/projects/clojure/cljs-play/app/shadow-cljs.edn cli version: 2.8.83 node: v12.13.1
shadow-cljs - updating dependencies
shadow-cljs - dependencies updated
shadow-cljs - HTTP server available at http://localhost:3000
shadow-cljs - server version: 2.8.83 running at http://localhost:9630
shadow-cljs - nREPL server started on port 44793
shadow-cljs - watching build :app
[:app] Configuring build.
[:app] Compiling ...
[:app] Build completed. (162 files, 161 compiled, 0 warnings, 32.62s)
And visit localhost:3000 as configured in shadow-cljs.edn
:dev-http {3000 "public"}
How to connect to a repl
dan@pop-os:~/projects/clojure/cljs-play/app$ npx shadow-cljs cljs-repl app
shadow-cljs - config: /home/dan/projects/clojure/cljs-play/app/shadow-cljs.edn cli version: 2.8.83 node: v12.13.1
shadow-cljs - connected to server
cljs.user=> (require 'app.main)
No application has connected to the REPL server. Make sure your JS environment has loaded your compiled ClojureScript code.
> (require 'app.main)
nil
cljs.user=> (in-ns 'app.main)
nil
app.main=> (app)
[:div {:style {:margin "auto", :margin-top "100px", :width "600px"}} [:h1 "hi"]]
app.main=>
Above is a common mistake: "no application has connected…". You need to visit localhost:3000 so that Shadow CLJS is connected to the js runtime to evaluate your code.
Also, note that `(app)` returns just a vector. No React objects. Note that the code in the repo is a bit more advanced so will actually return a function though.
While server is running you can just install
npm i react-modal
(ns app.main
(:require [reagent.core :as r]
["react-modal" :as Modal]))
(.setAppElement Modal "#app")
(defn app
[]
(let [modal-state (r/atom false)]
(fn []
[:div {:style {:margin "auto"
:margin-top "100px"
:width "600px"}}
[:h1 "hi"]
[:button {:on-click #(swap! modal-state not)}
"Button"]
[:> Modal {:isOpen @modal-state
:onRequestClose #(reset! modal-state false)
:contentLabel "Example Modal"
:shouldCloseOnOverlayClick true
:style {:content {:top "50%"
:left "50%"
:right "auto"
:bottom "auto"
:marginRight "-50%"
:transform "translate(-50%, -50%)"}}}
[:div
"This is a modal"
[:ul
[:li "With content"]
[:li "And lists"]]]]])))
Netlify can build!