-
Notifications
You must be signed in to change notification settings - Fork 255
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
324 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ pom.xml.asc | |
/gh-pages | ||
/node_modules | ||
/_book | ||
figwheel_server.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../modules/reitit-core |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../modules/reitit-frontend |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../../modules/reitit-schema |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
(defproject frontend "0.1.0-SNAPSHOT" | ||
:description "FIXME: write description" | ||
:url "http://example.com/FIXME" | ||
:license {:name "Eclipse Public License" | ||
:url "http://www.eclipse.org/legal/epl-v10.html"} | ||
|
||
:dependencies [[org.clojure/clojure "1.9.0"] | ||
[ring-server "0.5.0"] | ||
[reagent "0.8.1"] | ||
[ring "1.6.3"] | ||
[compojure "1.6.1"] | ||
[hiccup "1.0.5"] | ||
[org.clojure/clojurescript "1.10.238" :scope "provided"] | ||
[metosin/reitit "0.1.3"] | ||
[metosin/reitit-schema "0.1.3"] | ||
[metosin/reitit-frontend "0.1.3"]] | ||
|
||
:plugins [[lein-cljsbuild "1.1.7"]] | ||
|
||
:source-paths [] | ||
:resource-paths ["resources" "target/cljsbuild"] | ||
|
||
:cljsbuild | ||
{:builds | ||
[{:id "app" | ||
:figwheel true | ||
:source-paths ["src"] | ||
:watch-paths ["src" "checkouts/reitit-frontend/src"] | ||
:compiler {:main "frontend.core" | ||
:asset-path "/js/out" | ||
:output-to "target/cljsbuild/public/js/app.js" | ||
:output-dir "target/cljsbuild/public/js/out" | ||
:source-map true | ||
:optimizations :none | ||
:pretty-print true | ||
:preloads [devtools.preload]}} | ||
{:id "min" | ||
:source-paths ["src"] | ||
:compiler {:output-to "target/cljsbuild/public/js/app.js" | ||
:output-dir "target/cljsbuild/public/js" | ||
:source-map "target/cljsbuild/public/js/app.js.map" | ||
:optimizations :advanced | ||
:pretty-print false}}]} | ||
|
||
:figwheel | ||
{:http-server-root "public" | ||
:server-port 3449 | ||
:nrepl-port 7002} | ||
|
||
:profiles {:dev {:dependencies [[binaryage/devtools "0.9.10"]] | ||
:plugins [[lein-figwheel "0.5.16"]]}}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>Reitit frontend example</title> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
<script src="js/app.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
(ns frontend.core | ||
(:require [reagent.core :as r] | ||
[reitit.core :as rc] | ||
[reitit.frontend :as rf] | ||
[reitit.frontend.history :as rfh] | ||
[reitit.coercion.schema :as rs])) | ||
|
||
(def router (atom nil)) | ||
|
||
(defn home-page [] | ||
[:div | ||
[:h2 "Welcome to frontend"] | ||
[:div [:a {:href (rfh/href @router ::about)} "go to about page"]]]) | ||
|
||
(defn about-page [] | ||
[:div | ||
[:h2 "About frontend"] | ||
[:a {:href "http://google.com"} "external link"] | ||
[:div [:a {:href (rfh/href @router ::frontpage)} "go to the home page"]]]) | ||
|
||
(defonce match (r/atom nil)) | ||
|
||
(defn current-page [] | ||
[:div | ||
(if @match | ||
[:div [(:view (:data @match))]]) | ||
(pr-str @match)]) | ||
|
||
(def routes | ||
(rc/router | ||
["" | ||
["" | ||
{:name ::frontpage-root | ||
:view home-page}] | ||
["/" | ||
["" | ||
{:name ::frontpage | ||
:view home-page}] | ||
["about" | ||
{:name ::about | ||
:view about-page}]]])) | ||
|
||
(defn init! [] | ||
(reset! router (rfh/start! routes | ||
(fn [m] (reset! match m)) | ||
{:use-fragment true | ||
:path-prefix "/"})) | ||
(r/render [current-page] (.getElementById js/document "app"))) | ||
|
||
(init!) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,58 @@ | ||
(ns reitit.frontend | ||
"Utilities to implement frontend routing using Reitit. | ||
Controller is way to declare as data the side-effects and optionally | ||
other data related to the route." | ||
"" | ||
(:require [reitit.core :as reitit] | ||
[clojure.string :as str] | ||
goog.Uri | ||
[reitit.coercion :as coercion])) | ||
|
||
;; | ||
;; Utilities | ||
;; | ||
[reitit.coercion :as coercion] | ||
[goog.events :as e] | ||
[goog.dom :as dom]) | ||
(:import goog.Uri)) | ||
|
||
(defn query-params | ||
"Parse query-params from URL into a map." | ||
"Given goog.Uri, read query parameters into Clojure map." | ||
[^goog.Uri uri] | ||
(let [q (.getQueryData uri)] | ||
(->> q | ||
(.getKeys) | ||
(map (juxt keyword #(.get q %))) | ||
(into {})))) | ||
|
||
(defn get-hash | ||
"Given browser hash starting with #, remove the # and | ||
end slashes." | ||
[] | ||
(-> js/location.hash | ||
(subs 1) | ||
(str/replace #"/$" ""))) | ||
|
||
;; | ||
;; Controller implementation | ||
;; | ||
|
||
(defn get-params | ||
"Get controller parameters given match. If controller provides :params | ||
function that will be called with the match. Default is nil." | ||
[controller match] | ||
(if-let [f (:params controller)] | ||
(f match))) | ||
|
||
(defn apply-controller | ||
"Run side-effects (:start or :stop) for controller. | ||
The side-effect function is called with controller params." | ||
[controller method] | ||
(when-let [f (get controller method)] | ||
(f (::params controller)))) | ||
|
||
(defn- pad-same-length [a b] | ||
(concat a (take (- (count b) (count a)) (repeat nil)))) | ||
|
||
(defn apply-controllers | ||
"Applies changes between current controllers and | ||
those previously enabled. Resets controllers whose | ||
parameters have changed." | ||
[old-controllers new-match] | ||
(let [new-controllers (map (fn [controller] | ||
(assoc controller ::params (get-params controller new-match))) | ||
(:controllers (:data new-match))) | ||
changed-controllers (->> (map (fn [old new] | ||
;; different controllers, or params changed | ||
(if (not= old new) | ||
{:old old, :new new})) | ||
(pad-same-length old-controllers new-controllers) | ||
(pad-same-length new-controllers old-controllers)) | ||
(keep identity) | ||
vec)] | ||
(doseq [controller (map :old changed-controllers)] | ||
(apply-controller controller :stop)) | ||
(doseq [controller (map :new changed-controllers)] | ||
(apply-controller controller :start)) | ||
new-controllers)) | ||
|
||
(defn hash-change [router hash] | ||
(let [uri (goog.Uri/parse hash) | ||
match (or (reitit/match-by-path router (.getPath uri)) | ||
{:data {:name :not-found}}) | ||
q (query-params uri) | ||
;; Coerce if coercion enabled | ||
c (if (:result match) | ||
(coercion/coerce-request (:result match) {:query-params q | ||
:path-params (:params match)}) | ||
{:query q | ||
:path (:param match)}) | ||
;; Replace original params with coerced params | ||
match (-> match | ||
(assoc :query (:query c)) | ||
(assoc :params (:path c)))] | ||
match)) | ||
(defn query-string | ||
"Given map, creates " | ||
[m] | ||
(str/join "&" (map (fn [[k v]] | ||
(str (js/encodeURIComponent (name k)) | ||
"=" | ||
;; FIXME: create protocol to handle how types are converted to string | ||
;; FIXME: array to multiple params | ||
(if (coll? v) | ||
(str/join "," (map #(js/encodeURIComponent %) v)) | ||
(js/encodeURIComponent v)))) | ||
m))) | ||
|
||
(defn match-by-path | ||
"Given routing tree and current path, return match with possibly | ||
coerced parameters. Return nil if no match found." | ||
[router path] | ||
(let [uri (.parse Uri path)] | ||
(if-let [match (reitit/match-by-path router (.getPath uri))] | ||
(let [q (query-params uri) | ||
;; Return uncoerced values if coercion is not enabled - so | ||
;; that tha parameters are always accessible from same property. | ||
;; FIXME: coerce! can't be used as it doesn't take query-params | ||
parameters (if (:result match) | ||
(coercion/coerce-request (:result match) {:query-params q | ||
:path-params (:path-params match)}) | ||
{:query q | ||
:path (:param match)})] | ||
(assoc match :parameters parameters))))) | ||
|
||
(defn match-by-name | ||
[router name params] | ||
;; FIXME: move router not initialized to re-frame integration? | ||
(if router | ||
(or (reitit/match-by-name router name params) | ||
;; FIXME: return nil? | ||
(do | ||
(js/console.error "Can't create URL for route " (pr-str name) (pr-str params)) | ||
nil)) | ||
::not-initialized)) |
114 changes: 114 additions & 0 deletions
114
modules/reitit-frontend/src/reitit/frontend/history.cljs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
(ns reitit.frontend.history | ||
"" | ||
(:require [reitit.core :as reitit] | ||
[clojure.string :as string] | ||
[goog.events :as e] | ||
[goog.dom :as dom] | ||
[reitit.frontend :as rf]) | ||
(:import goog.history.Html5History | ||
goog.Uri)) | ||
|
||
;; Token is for Closure HtmlHistory | ||
;; Path is for reitit | ||
|
||
(defn- token->path [history token] | ||
(if (.-useFragment_ history) | ||
token | ||
(str (.getPathPrefix history) token))) | ||
|
||
(defn- path->token [history path] | ||
(subs path (if (.-useFragment_ history) | ||
1 | ||
(count (.getPathPrefix history))))) | ||
|
||
(defn- token->href [history token] | ||
(str (if (.-useFragment_ history) | ||
(str "#")) | ||
(.getPathPrefix history) | ||
token)) | ||
|
||
(def ^:private current-domain (.getDomain (.parse Uri js/location))) | ||
|
||
(defn ignore-anchor-click | ||
"Ignore click events from a elements, if the href points to URL that is part | ||
of the routing tree." | ||
[router history e] | ||
;; Returns the next matching anchestor of event target | ||
(when-let [el (.closest (.-target e) "a")] | ||
(let [uri (.parse Uri (.-href el))] | ||
(when (and (or (and (not (.hasScheme uri)) (not (.hasDomain uri))) | ||
(= current-domain (.getDomain uri))) | ||
(not (.-altKey e)) | ||
(not (.-ctrlKey e)) | ||
(not (.-metaKey e)) | ||
(not (.-shiftKey e)) | ||
(not (contains? #{"_blank" "self"} (.getAttribute el "target"))) | ||
;; Left button | ||
(= 0 (.-button e)) | ||
(reitit/match-by-path router (.getPath uri))) | ||
(.preventDefault e) | ||
(.replaceToken history (path->token history (.getPath uri))))))) | ||
|
||
(defn start! | ||
"Parameters: | ||
- router The reitit routing tree. | ||
- on-navigate Function to be called when route changes. | ||
Options: | ||
- :use-fragment (default true) If true, onhashchange and location hash are used to store the token. | ||
- :path-prefix (default \"/\") If :use-fragment is false, this is prepended to all tokens, and is | ||
removed from start of the token before matching the route." | ||
[router | ||
on-navigate | ||
{:keys [path-prefix use-fragment] | ||
:or {path-prefix "/" | ||
use-fragment true}}] | ||
(let [history | ||
(doto (Html5History.) | ||
(.setEnabled true) | ||
(.setPathPrefix path-prefix) | ||
(.setUseFragment use-fragment)) | ||
|
||
event-key | ||
(e/listen history goog.history.EventType.NAVIGATE | ||
(fn [e] | ||
(on-navigate (rf/match-by-path router (token->path history (.getToken history)))))) | ||
|
||
click-listen-key | ||
(if-not use-fragment | ||
(e/listen js/document e/EventType.CLICK | ||
(partial ignore-anchor-click router history)))] | ||
|
||
;; Trigger navigate event for current route | ||
(on-navigate (rf/match-by-path router (token->path history (.getToken history)))) | ||
|
||
{:router router | ||
:history history | ||
:close-fn (fn [] | ||
(e/unlistenByKey event-key) | ||
(e/unlistenByKey click-listen-key) | ||
(.setEnabled history false))})) | ||
|
||
(defn stop! [{:keys [close-fn]}] | ||
(if close-fn | ||
(close-fn))) | ||
|
||
(defn- match->token [history match k params] | ||
;; FIXME: query string | ||
(if-let [path (:path match)] | ||
(path->token history path))) | ||
|
||
(defn href | ||
([state k] | ||
(href state k nil)) | ||
([state k params] | ||
(href state k params nil)) | ||
([{:keys [router history]} k params query] | ||
(let [match (rf/match-by-name router k params) | ||
token (match->token history match k params)] | ||
(token->href history token)))) | ||
|
||
(defn replace-token [{:keys [router history]} k params] | ||
(let [match (rf/match-by-name router k params) | ||
token (match->token history match k params)] | ||
(.replaceToken history token))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.