reitit/examples/frontend-links/src/frontend/core.cljs
2020-03-05 13:32:48 +02:00

146 lines
4.1 KiB
Clojure

(ns frontend.core
(:require [clojure.string :as string]
[fipp.edn :as fedn]
[reagent.core :as r]
[reitit.coercion.spec :as rss]
[reitit.frontend :as rf]
[reitit.frontend.easy :as rfe]
[spec-tools.data-spec :as ds]))
;; Components similar to react-router `Link`, `NavLink` and `Redirect`
;; with Reitit frontend.
(defn home-page []
[:div
[:h2 "Welcome to frontend"]
[:p "This is home page"]])
(defn about-page []
[:div
[:h2 "About frontend"]
[:p "This is about page"]])
(defn redirect!
"If `push` is truthy, previous page will be left in history."
[{:keys [to path-params query-params push]}]
(if push
(rfe/push-state to path-params query-params)
(rfe/replace-state to path-params query-params)))
(defn Redirect
"Component that only causes a redirect side-effect."
[props]
(r/create-class
{:component-did-mount (fn [this] (redirect! (r/props this)))
:component-did-update (fn [this [_ prev-props]]
(if (not= (r/props this) prev-props)
(redirect! (r/props this))))
:render (fn [this] nil)}))
(defn item-page [match]
(let [{:keys [path query]} (:parameters match)
{:keys [id]} path]
(if (< id 1)
[Redirect {:to ::frontpage}]
[:div
[:h2 "Selected item " id]
(when (:foo query)
[:p "Optional foo query param: " (:foo query)])])))
(def routes
[["/"
{:name ::frontpage
:view home-page}]
["/about"
{:name ::about
:view about-page}]
["/item/:id"
{:name ::item
:view item-page
:parameters
{:path {:id int?}
:query {(ds/opt :foo) keyword?}}}]])
(def router
(rf/router routes {:data {:coercion rss/coercion}}))
(defonce current-match (r/atom nil))
(defn- resolve-href
[to path-params query-params]
(if (keyword? to)
(rfe/href to path-params query-params)
(let [match (rf/match-by-path router to)
route (-> match :data :name)
params (or path-params (:path-params match))
query (or query-params (:query-params match))]
(if match
(rfe/href route params query)
to))))
(defn Link
[{:keys [to path-params query-params active]} & children]
(let [href (resolve-href to path-params query-params)]
(into
[:a {:href href} (when active "> ")] ;; Apply styles or whatever
children)))
(defn- name-matches?
[name path-params match]
(and (= name (-> match :data :name))
(= (not-empty path-params)
(-> match :parameters :path not-empty))))
(defn- url-matches?
[url match]
(= (-> url (string/split #"\?") first)
(:path match)))
(defn NavLink
[{:keys [to path-params] :as props} & children]
(let [active (or (name-matches? to path-params @current-match)
(url-matches? to @current-match))]
[Link (assoc props :active active) children]))
(defn current-page []
[:div
[:h4 "Link"]
[:ul
[:li [Link {:to ::frontpage} "Frontpage"]]
[:li [Link {:to "/about"} "About"]]
[:li [Link {:to ::item :path-params {:id 1}} "Item 1"]]
[:li [Link {:to "/item/2?foo=bar"} "Item 2"]]
[:li [Link {:to "/item/-1"} "Item -1 (redirects to frontpage)"]]
[:li [Link {:to "http://www.google.fi"} "Google"]]]
[:h4 "NavLink"]
[:ul
[:li [NavLink {:to ::frontpage} "Frontpage"]]
[:li [NavLink {:to "/about"} "About"]]
[:li [NavLink {:to ::item :path-params {:id 1}} "Item 1"]]
[:li [NavLink {:to "/item/2?foo=bar"} "Item 2"]]
[:li [NavLink {:to "/item/-1"} "Item -1 (redirects to frontpage)"]]
[:li [NavLink {:to "http://www.google.fi"} "Google"]]]
(if @current-match
(let [view (:view (:data @current-match))]
[view @current-match]))
[:pre (with-out-str (fedn/pprint @current-match))]])
(defn init! []
(rfe/start!
router
(fn [m] (reset! current-match m))
;; set to false to enable HistoryAPI
{:use-fragment true})
(r/render [current-page] (.getElementById js/document "app")))
(init!)
(comment
(rf/match-by-path router "/about?kissa=1&koira=true")
(rf/match-by-path router "/item/2?kissa=1&koira=true"))