Skip to content

Instantly share code, notes, and snippets.

@agzam agzam/entry.cljs
Created Feb 15, 2018

Embed
What would you like to do?
(ns finops-admin.re-frame.components.common.navbar.entry
""
(:require [cljs.spec.alpha :as s]
[finops-admin.auth :as auth]
[finops-admin.page :as page]
[finops-admin.re-frame.components :as components]
[finops-admin.re-frame.components.common.navbar2.fc :as fc]
[finops-admin.utils :as utils]
[goog.dom :as dom]
[re-frame.core :as rf]
[reagent.core :as reagent]))
;; Every entry must at least include a title
(s/def ::base-entry-config
(s/keys :req-un [::title
::active-for-path]))
(s/def ::title (and string?
not-empty))
(s/def ::active-for-path (s/coll-of simple-keyword? :into []))
(defn- handler->route-path
[handler]
(let [parts (-> handler
namespace
(clojure.string/split "."))]
(mapv keyword parts)))
(defn- route-path-sub
"Accept as a signal the page's current ::page/route. Return a vector indicating the route path.
For example :accounts.payments/something would expand to the path [:accounts :payments]"
[route _]
(let [parts (some-> route
:handler
namespace
(clojure.string/split "."))]
(mapv keyword parts)))
(rf/reg-sub ::route-path :<- [::page/route] route-path-sub)
(defn- visiting-path-sub
""
[db _]
(get db ::visiting-path))
(rf/reg-sub ::visiting-path visiting-path-sub)
(defn- matches-path?
""
[current-path entry-path]
(= (take (count entry-path) current-path)
entry-path))
(defn active-sub
"Return whether or not an entry is currently active.
While visiting a path, match the entry's path against the visited path.
Otherwise match the entry's path against the current route/uri path."
[[route-path visiting-path] [_ entry-path]]
(if visiting-path
(matches-path? visiting-path entry-path)
(matches-path? route-path entry-path)))
;; Return true when the current item is active or being navigated through
(rf/reg-sub ::active?
:<- [::route-path]
:<- [::visiting-path]
active-sub)
(defn- active-signal
"Return a subscription that yields true when the current entry should be
displayed as active."
[[_ config]]
(rf/subscribe [::active? (:active-for-path config)]))
(def active-item-class "menu__item--active")
;; TODO: Split namespaces
;; === Navigable Entry Component ===
;; Specs
(s/def ::navigable-entry-config
(s/merge ::base-entry-config
(s/keys :req-un [::navigates-to])))
(s/def ::navigates-to qualified-keyword?)
(components/reg-spec ::navigable-entry ::navigable-entry-config)
;; Events
(defn- navigate-to [_ [_ handler]]
;; TODO: Set current route and/or clear active-path?
;; TODO: this feels really sluggish in practice
{:dispatch [::visit-path nil]
::page/navigate! handler})
(rf/reg-event-fx ::navigate-to navigate-to)
;; Subscriptions
(defn- navigable-entry-sub
""
[active? [_ {:keys [navigates-to class] :as config} parent-config]]
(let [class' (str class " " (when active? active-item-class))]
{:entry-props {:class class'}
:link-props {:href "#"
:on-click #(rf/dispatch [::navigate-to navigates-to])}}))
(rf/reg-sub ::navigable-entry active-signal navigable-entry-sub)
;; Block
(defn navigable-entry-block
""
[{:keys [entry-props link-props]} [_ config parent-config]]
[:li.menu__item entry-props
[:a link-props (:title config)]])
(components/reg-block ::navigable-entry navigable-entry-block)
;; TODO: Split namespaces
;; === Expandable Entry Component ===
;; Specs
(s/def ::expandable-entry-config
(s/merge ::base-entry-config
(s/keys :req-un [::expands-to])))
;; TODO: How do we specify this is a component?
(s/def ::expands-to any?)
(components/reg-spec ::expandable-entry ::expandable-entry-config)
;; Events
(defn- set-visiting-path
""
[db [_ path]]
(assoc db ::visiting-path path))
(rf/reg-event-db ::visit-path set-visiting-path)
;; Subscriptions
(defn- expandable-entry-sub
""
[active? [_ {:keys [class] :as config} parent-config]]
(let [{:keys [active-for-path expands-to]} config]
;; TODO: duplicates navigable-entry-sub, refactor
{:entry-props {:class class}
:link-props {:href "#"
:class (when active? active-item-class)
:on-click #(do
(.preventDefault %)
(rf/dispatch [::visit-path active-for-path]))}
:active? active?}))
(rf/reg-sub ::expandable-entry active-signal expandable-entry-sub)
;; Block
(defn expandable-entry-block
""
[signals [_ config]]
(let [{:keys [active? entry-props link-props]} signals
{:keys [title expands-to]} config]
[:li.menu__item entry-props
[:a link-props title]
(when active? [expands-to])]))
(components/reg-block ::expandable-entry expandable-entry-block)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.