reagent/demo/sitetools/core.cljs

190 lines
6.3 KiB
Plaintext
Raw Normal View History

(ns sitetools.core
(:require [clojure.string :as string]
[goog.events :as evt]
[reagent.core :as r]
[reagent.dom.server :as server]
[reagent.debug :refer-macros [dbg log dev?]]
[reagent.interop :as i :refer-macros [$ $!]])
2015-09-07 22:41:04 +00:00
(:import goog.History
2015-09-16 15:55:45 +00:00
[goog.history Html5History EventType]))
2015-09-16 15:55:45 +00:00
(enable-console-print!)
;;; Configuration
2015-09-09 10:11:30 +00:00
(declare main-content)
2015-09-07 22:29:06 +00:00
2015-09-09 10:11:30 +00:00
(defonce config (r/atom {:body [#'main-content]
2015-09-15 10:27:07 +00:00
:pages {"/index.html" {:content [:div]
:title ""}}
:site-dir "outsite/public"
:css-infiles ["site/public/css/main.css"]
:css-file "css/built.css"
:js-file "js/main.js"
:main-div "main-content"
2015-09-08 11:08:09 +00:00
:default-title ""}))
2015-09-07 20:22:26 +00:00
(defonce history nil)
2015-09-15 11:36:02 +00:00
(defn demo-handler [state [id x :as event]]
2015-09-07 20:22:26 +00:00
(case id
2015-09-15 11:36:02 +00:00
:set-content (let [page x
title (:title page)
title (if title
(str (:title-prefix state) title)
2015-09-16 15:55:45 +00:00
(str (:default-title state)))]
2015-09-08 16:07:29 +00:00
(when r/is-client
2015-09-09 09:46:41 +00:00
(set! js/document.title title))
2015-09-16 15:55:45 +00:00
(assoc state :current-page page :title title))
2015-09-15 11:36:02 +00:00
:set-page (let [path x
_ (assert (string? path))
ps (:pages state)
p (get ps path (get ps "/index.html"))]
(recur state [:set-content p]))
:goto-page (let [path x
_ (assert (string? path))]
2015-09-15 09:57:12 +00:00
(when-some [h history]
2016-05-02 10:44:02 +00:00
(r/after-render (fn []
(.setToken h x)
(js/scrollTo 0 0)))
2015-09-15 09:57:12 +00:00
state)
(recur state [:set-page x]))))
2015-09-07 20:22:26 +00:00
(defn emit [event]
2015-09-08 11:08:09 +00:00
;; (dbg event)
(r/rswap! config demo-handler event))
2015-09-07 20:22:26 +00:00
2015-09-08 18:18:27 +00:00
(defn register-page [url comp title]
{:pre [(re-matches #"/.*[.]html" url)
(vector? comp)]}
2015-09-15 10:27:07 +00:00
(swap! config update-in [:pages]
assoc url {:content comp :title title}))
2015-09-08 18:18:27 +00:00
2015-09-08 11:08:09 +00:00
;;; History
2015-09-09 08:51:37 +00:00
(defn init-history [page]
2015-09-07 20:22:26 +00:00
(when-not history
2015-09-09 08:51:37 +00:00
(let [html5 (and page
2015-09-09 14:00:21 +00:00
(Html5History.isSupported)
2015-09-08 11:08:09 +00:00
(#{"http:" "https:"} js/location.protocol))]
2015-09-08 07:35:55 +00:00
(doto (set! history
(if html5
(doto (Html5History.)
(.setUseFragment false)
2015-09-08 11:08:09 +00:00
(.setPathPrefix (-> js/location.pathname
(string/replace
(re-pattern (str page "$")) "")
(string/replace #"/*$" ""))))
2015-09-08 07:35:55 +00:00
(History.)))
2015-09-16 15:55:45 +00:00
(evt/listen EventType.NAVIGATE #(when (.-isNavigation %)
2016-05-02 10:44:02 +00:00
(emit [:set-page (.-token %)])
(r/flush)))
2015-09-08 07:35:55 +00:00
(.setEnabled true))
2015-09-16 15:55:45 +00:00
(let [token (.getToken history)
p (if (and page (not html5) (empty? token))
2015-09-15 09:57:12 +00:00
page
2015-09-16 15:55:45 +00:00
token)]
(emit [:set-page p])))))
2015-09-07 20:22:26 +00:00
2015-09-08 16:00:44 +00:00
(defn to-relative [f]
2015-09-08 11:08:09 +00:00
(string/replace f #"^/" ""))
;;; Components
2015-09-07 20:22:26 +00:00
(defn link [props child]
[:a (assoc props
2015-09-08 16:00:44 +00:00
:href (-> props :href to-relative)
2015-09-07 20:22:26 +00:00
:on-click #(do (.preventDefault %)
(emit [:goto-page (:href props)])))
2015-09-07 20:22:26 +00:00
child])
2015-09-09 10:11:30 +00:00
(defn main-content []
2015-09-16 15:55:45 +00:00
(get-in @config [:current-page :content]))
2015-09-08 11:08:09 +00:00
;;; Static site generation
2015-09-16 15:55:45 +00:00
(defn base [page]
(let [depth (->> page to-relative (re-seq #"/") count)]
(->> "../" (repeat depth) (apply str))))
(defn danger [t s]
[t {:dangerouslySetInnerHTML {:__html s}}])
2015-09-08 15:50:50 +00:00
(defn html-template [{:keys [title body-html timestamp page-conf
js-file css-file main-div]}]
(let [main (str js-file timestamp)]
(server/render-to-static-markup
[:html
[:head
2015-09-09 20:05:14 +00:00
[:meta {:charset 'utf-8}]
[:meta {:name 'viewport
:content "width=device-width, initial-scale=1.0"}]
2015-09-16 15:55:45 +00:00
[:base {:href (-> page-conf :page-path base)}]
[:link {:href (str css-file timestamp) :rel 'stylesheet}]
[:title title]]
[:body
2015-09-08 15:50:50 +00:00
[:div {:id main-div} (danger :div body-html)]
2015-09-08 11:18:21 +00:00
(danger :script (str "var pageConfig = "
2015-09-16 15:55:45 +00:00
(-> page-conf clj->js js/JSON.stringify)))
[:script {:src main :type "text/javascript"}]]])))
2015-09-15 10:27:07 +00:00
(defn gen-page [page-path conf]
(emit [:set-page page-path])
2015-09-15 11:36:02 +00:00
(let [conf (merge conf @config)
b (:body conf)
bhtml (server/render-to-string b)]
2015-09-09 20:05:14 +00:00
(str "<!doctype html>\n"
2015-09-08 15:50:50 +00:00
(html-template (assoc conf
2015-09-15 10:27:07 +00:00
:page-conf {:page-path page-path}
2015-09-08 15:50:50 +00:00
:body-html bhtml)))))
2015-09-09 08:51:37 +00:00
(defn fs [] (js/require "fs"))
(defn path [] (js/require "path"))
(defn mkdirs [f]
2015-09-09 10:36:04 +00:00
(doseq [d (reductions #(str %1 "/" %2)
(-> ($ (path) normalize f)
2015-09-09 10:36:04 +00:00
(string/split #"/")))]
(when-not ($ (fs) existsSync d)
($ (fs) mkdirSync d))))
(defn write-file [f content]
(mkdirs ($ (path) dirname f))
($ (fs) writeFileSync f content))
(defn path-join [& paths]
(apply ($ (path) :join) paths))
2015-09-08 16:00:44 +00:00
(defn write-resources [dir {:keys [css-file css-infiles]}]
(write-file (path-join dir css-file)
2015-09-09 10:36:04 +00:00
(->> css-infiles
(map #($ (fs) readFileSync %))
2015-09-09 10:36:04 +00:00
(string/join "\n"))))
;;; Main entry points
(defn ^:export genpages [opts]
(log "Generating site")
2015-09-09 08:51:37 +00:00
(let [conf (swap! config merge (js->clj opts :keywordize-keys true))
conf (assoc conf :timestamp (str "?" (js/Date.now)))
{:keys [site-dir pages]} conf]
2015-09-15 10:27:07 +00:00
(doseq [f (keys pages)]
2015-09-08 16:00:44 +00:00
(write-file (->> f to-relative (path-join site-dir))
2015-09-08 15:50:50 +00:00
(gen-page f conf)))
(write-resources site-dir conf))
(log "Wrote site"))
(defn start! [site-config]
(swap! config merge site-config)
(when r/is-client
2015-09-09 08:51:37 +00:00
(let [page-conf (when (exists? js/pageConfig)
(js->clj js/pageConfig :keywordize-keys true))
2015-09-09 09:46:41 +00:00
conf (swap! config merge page-conf)
2015-09-15 10:27:07 +00:00
{:keys [page-path body main-div]} conf]
(init-history page-path)
2015-09-09 09:46:41 +00:00
(r/render-component body (js/document.getElementById main-div)))))