Skip to content

Instantly share code, notes, and snippets.

@awkay
Created August 12, 2021 20:58
Show Gist options
  • Save awkay/1d8f7e24ba5cf52a5ad20f9852e6cf7a to your computer and use it in GitHub Desktop.
Save awkay/1d8f7e24ba5cf52a5ad20f9852e6cf7a to your computer and use it in GitHub Desktop.
Some functions for working with Fulcro dynamic routers. I'll probably put some version of these once they're refined into Fulcro itself, but for now there are here for reference.
(defn- get-ast-children [query-ast at-depth]
(let [{:keys [children] :as node} query-ast]
(cond
(and (zero? at-depth) (seq children)) children
(zero? at-depth) node
:else (let [sub-children (mapcat :children children)]
(get-ast-children {:type :root
:children sub-children} (dec at-depth))))))
(defn find-router
"Find the router closest to the root of the given query. Logs a warning if there are sibling routers, and returns
the first of those."
[query]
(let [ast (eql/query->ast query)
routers (first
(drop-while empty?
(map
(fn [n]
(sequence
(comp
(keep :component)
(filter dr/router?))
(get-ast-children ast n)))
(range 0 8))))]
(when (> (count routers) 1)
(log/warn "There were sibling routers when trying to find a router in" query
" which can cause problems with route detection"))
(first routers)))
(defn routed*?
"Returns true if the given router (class) has been asked to (and is finished) routing."
[state-map router]
(boolean
(when router
(= :routed
(get-in state-map [::uism/asm-id
(second (comp/get-ident router {}))
::uism/active-state])))))
(defn router-started*?
"Returns true if the given router (class) has been started."
[state-map router]
(boolean
(get-in state-map [::uism/asm-id
(second (comp/get-ident router {}))
::uism/active-state])))
(defn active-router-target
"Returns the target that the given router is pointing to. This is the best you can do in terms of determining what a
given router is actively showing, independent of if that router is properly started or is in transition.
The first input argument can be a state map, a Fulcro application, or an active instance (this).
Returns the component class of the target."
[state-map-or-app-or-this RouterClass]
(let [state-map (or
(some-> (comp/any->app state-map-or-app-or-this) app/current-state)
state-map-or-app-or-this)]
(some->> (some-> RouterClass (comp/get-query state-map) eql/query->ast :children)
(utils/find-first #(= (:key %) ::dr/current-route))
:component)))
(defn current-active-target
"Returns the component class of the ultimate leaf that has been routed to. This function uses the state of the router
dynamic queries. If `parent` is supplied, this function starts the search from that parent; otherwise it requires
you pass an app or this and it starts from the actively mounted root."
([app-or-this]
(current-active-target app-or-this (app/app-root (comp/any->app app-or-this))))
([app-or-this-or-state-map parent]
(let [state-map (or
(some-> app-or-this-or-state-map comp/any->app app/current-state)
app-or-this-or-state-map)
query (comp/get-query parent state-map)
router (find-router query)
current-target (active-router-target state-map router)]
(if (and (routed*? state-map router) current-target)
(current-active-target state-map current-target)
parent))))
(defn target-visible*?
"
Recursively walks the routing tree in state (starting from `parent`) and attempts to determine if the given `target` is
currently being routed to.
Returns true if the given `target` (class) is currently some part of the active route.
* state-map - The current app state
* parent - The class from which to start checking the route (app root is fine)
* target - The class for which you want to know if it is visible (must be a target with a :route-segment)
"
[state-map parent target]
(let [query (comp/get-query parent state-map)
router (find-router query)
routed? (routed*? state-map router)
current-target (active-router-target state-map router)]
(cond
(and routed? (= target current-target)) true
(and routed? current-target) (target-visible*? state-map current-target target)
:else false)))
(defn target-visible?
"Recursively walks the routing tree in state (starting from the mounted app root) and attempts to determine if the given `target` is
currently being routed to.
Returns true if the given `target` (class) is currently some part of the active route.
* app-ish - Fulcro application or component `this`.
* target - The class for which you want to know if it is visible (must be a target with a :route-segment)
"
[app-ish target]
(when-not (comp/component-options target :route-segment)
(log/error "target-visible? called with a target that has no route-segment. There is no way this will EVER return true."
(comp/component-name target)))
(let [Root (app/app-root (comp/any->app app-ish))
state-map (app/current-state app-ish)]
(target-visible*? state-map Root target)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment