diff --git a/.gitignore b/.gitignore index 73f41df4..c65c7d42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/target +target/ /classes /checkouts pom.xml diff --git a/modules/reitit-frontend/project.clj b/modules/reitit-frontend/project.clj new file mode 100644 index 00000000..38c9557d --- /dev/null +++ b/modules/reitit-frontend/project.clj @@ -0,0 +1,9 @@ +(defproject metosin/reitit-frontend "0.1.3" + :description "Reitit: Clojurescript frontend routing core" + :url "https://github.com/metosin/reitit" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :plugins [[lein-parent "0.3.2"]] + :parent-project {:path "../../project.clj" + :inherit [:deploy-repositories :managed-dependencies]} + :dependencies [[metosin/reitit-core]]) diff --git a/modules/reitit-frontend/src/reitit/frontend.cljs b/modules/reitit-frontend/src/reitit/frontend.cljs new file mode 100644 index 00000000..a50c4e5f --- /dev/null +++ b/modules/reitit-frontend/src/reitit/frontend.cljs @@ -0,0 +1,90 @@ +(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 +;; + +(defn query-params + "Parse query-params from URL into a 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)) diff --git a/modules/reitit/project.clj b/modules/reitit/project.clj index 391b1dd2..1bca731f 100644 --- a/modules/reitit/project.clj +++ b/modules/reitit/project.clj @@ -11,4 +11,6 @@ [metosin/reitit-spec] [metosin/reitit-schema] [metosin/reitit-swagger] - [metosin/reitit-swagger-ui]]) + [metosin/reitit-swagger-ui] + [metosin/reitit-frontend] + [metosin/reitit-re-frame]]) diff --git a/project.clj b/project.clj index 905bd954..ab2f6e5a 100644 --- a/project.clj +++ b/project.clj @@ -16,6 +16,7 @@ [metosin/reitit-schema "0.1.3"] [metosin/reitit-swagger "0.1.3"] [metosin/reitit-swagger-ui "0.1.3"] + [metosin/reitit-frontend "0.1.3"] [meta-merge "1.0.0"] [ring/ring-core "1.6.3"] @@ -40,7 +41,8 @@ "modules/reitit-spec/src" "modules/reitit-schema/src" "modules/reitit-swagger/src" - "modules/reitit-swagger-ui/src"] + "modules/reitit-swagger-ui/src" + "modules/reitit-frontend/src"] :dependencies [[org.clojure/clojure "1.9.0"] [org.clojure/clojurescript "1.10.238"] diff --git a/scripts/lein-modules b/scripts/lein-modules index daf815fe..fc3870d8 100755 --- a/scripts/lein-modules +++ b/scripts/lein-modules @@ -3,6 +3,6 @@ set -e # Modules -for ext in reitit-core reitit-ring reitit-spec reitit-schema reitit-swagger reitit-swagger-ui reitit; do +for ext in reitit-core reitit-ring reitit-spec reitit-schema reitit-swagger reitit-swagger-ui reitit-frontend reitit; do cd modules/$ext; lein "$@"; cd ../..; done