(ns re-com.tour (:require-macros [re-com.core :refer [handler-fn]]) (:require [reagent.core :as reagent] [re-com.box :refer [flex-child-style]] [re-com.buttons :refer [button]])) ;;-------------------------------------------------------------------------------------------------- ;; Component: tour ;; ;; Strings together ;;-------------------------------------------------------------------------------------------------- (defn make-tour "Returns a map containing - A reagent atom for each tour step controlling popover show/hide (boolean) - A standard atom holding the current step (integer) - A copy of the steps parameter passed in, to determine the order for prev/next functions It sets the first step atom to true so that it will be initially shown Sample return value: {:steps [:step1 :step2 :step3] :current-step (atom 0) :step1 (reagent/atom true) :step2 (reagent/atom false) :step3 (reagent/atom false)}" [tour-spec] (let [tour-map {:current-step (atom 0) :steps tour-spec}] ;; Only need normal atom (reduce #(assoc %1 %2 (reagent/atom false)) tour-map tour-spec))) ;; Old way: (merge {} (map #(hash-map % (reagent/atom false)) tour-spec)) (defn- initialise-tour "Resets all poover atoms to false" [tour] (doall (for [step (:steps tour)] (reset! (step tour) false)))) (defn start-tour "Sets the first popover atom in the tour to true" [tour] (initialise-tour tour) (reset! (:current-step tour) 0) (reset! ((first (:steps tour)) tour) true)) (defn finish-tour "Closes all tour popovers" [tour] (initialise-tour tour)) (defn- next-tour-step [tour] (let [steps (:steps tour) old-step @(:current-step tour) new-step (inc old-step)] (when (< new-step (count (:steps tour))) (reset! (:current-step tour) new-step) (reset! ((nth steps old-step) tour) false) (reset! ((nth steps new-step) tour) true)))) (defn- prev-tour-step [tour] (let [steps (:steps tour) old-step @(:current-step tour) new-step (dec old-step)] (when (>= new-step 0) (reset! (:current-step tour) new-step) (reset! ((nth steps old-step) tour) false) (reset! ((nth steps new-step) tour) true)))) (defn make-tour-nav "Generate the hr and previous/next buttons markup. If first button in tour, don't generate a Previous button. If last button in tour, change Next button to a Finish button" [tour] (let [on-first-button (= @(:current-step tour) 0) on-last-button (= @(:current-step tour) (dec (count (:steps tour))))] [:div [:hr {:style (merge (flex-child-style "none") {:margin "10px 0px 10px"})}] (when-not on-first-button [button :label "Previous" :on-click (handler-fn (prev-tour-step tour)) :style {:margin-right "15px"} :class "btn-default"]) [button :label (if on-last-button "Finish" "Next") :on-click (handler-fn (if on-last-button (finish-tour tour) (next-tour-step tour))) :class "btn-default"]]))