Merge pull request #183 from samroberton/cljc
Converting to CLJC for JVM testing
This commit is contained in:
commit
a169e1bc5e
15
project.clj
15
project.clj
|
@ -1,4 +1,4 @@
|
|||
(defproject re-frame "0.8.0-alpha2"
|
||||
(defproject re-frame "0.8.0-alpha3-SNAPSHOT"
|
||||
:description "A Clojurescript MVC-like Framework For Writing SPAs Using Reagent."
|
||||
:url "https://github.com/Day8/re-frame.git"
|
||||
:license {:name "MIT"}
|
||||
|
@ -7,12 +7,13 @@
|
|||
[reagent "0.6.0-rc"]]
|
||||
|
||||
:profiles {:debug {:debug true}
|
||||
:dev {:dependencies [[karma-reporter "0.3.0"]
|
||||
[binaryage/devtools "0.7.2"]]
|
||||
:plugins [[lein-cljsbuild "1.1.3"]
|
||||
[lein-npm "0.6.2"]
|
||||
[lein-figwheel "0.5.4-7"]
|
||||
[lein-shell "0.5.0"]]}}
|
||||
:dev {:dependencies [[karma-reporter "0.3.0"]
|
||||
[binaryage/devtools "0.7.2"]
|
||||
[org.clojure/tools.logging "0.3.1"]]
|
||||
:plugins [[lein-cljsbuild "1.1.3"]
|
||||
[lein-npm "0.6.2"]
|
||||
[lein-figwheel "0.5.4-7"]
|
||||
[lein-shell "0.5.0"]]}}
|
||||
|
||||
:clean-targets [:target-path "run/compiled"]
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
(ns re-frame.db
|
||||
(:require [reagent.core :as reagent]))
|
||||
(:require [re-frame.interop :refer [ratom]]))
|
||||
|
||||
|
||||
;; -- Application State --------------------------------------------------------------------------
|
||||
|
@ -7,5 +7,5 @@
|
|||
;; Should not be accessed directly by application code
|
||||
;; Read access goes through subscriptions.
|
||||
;; Updates via event handlers.
|
||||
(def app-db (reagent/atom {}))
|
||||
(def app-db (ratom {}))
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
:stack)]
|
||||
(try
|
||||
(handler-fn app-db event-v)
|
||||
(catch :default e
|
||||
(catch #?(:cljs :default :clj Exception) e
|
||||
(console :warn stack) ;; output a msg to help to track down dispatching point
|
||||
(throw e)))))))))
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
(ns re-frame.fx
|
||||
(:require [reagent.ratom :refer [IReactiveAtom]]
|
||||
[re-frame.router :refer [dispatch]]
|
||||
(:require [re-frame.router :refer [dispatch]]
|
||||
[re-frame.db :refer [app-db]]
|
||||
[re-frame.events]
|
||||
[re-frame.interop :refer [ratom? set-timeout!]]
|
||||
[re-frame.loggers :refer [console]]))
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
|||
:dispatch-later
|
||||
(fn [effect]
|
||||
(doseq [[ms events] effect]
|
||||
(js/setTimeout #(dispatch-helper events) ms))))
|
||||
(set-timeout! #(dispatch-helper events) ms))))
|
||||
|
||||
|
||||
;; Supply either a vector or a list of vectors. For example:
|
||||
|
@ -120,7 +120,7 @@
|
|||
[handler]
|
||||
(fn fx-handler
|
||||
[app-db event-vec]
|
||||
(if-not (satisfies? IReactiveAtom app-db)
|
||||
(if-not (ratom? app-db)
|
||||
(if (map? app-db)
|
||||
(console :warn "re-frame: Did you use \"fx\" middleware with \"def-event\"? Use \"def-event-fx\" instead (and don't directly use \"fx\")")
|
||||
(console :warn "re-frame: \"fx\" middleware not given a Ratom. Got: " app-db)))
|
|
@ -0,0 +1,64 @@
|
|||
(ns re-frame.interop
|
||||
(:import java.util.concurrent.Executors))
|
||||
|
||||
|
||||
;; The purpose of this file is to provide JVM-runnable implementations of the
|
||||
;; CLJS equivalents in interop.cljs.
|
||||
;;
|
||||
;; These implementations are to enable you to bring up a re-frame app on the JVM
|
||||
;; in order to run tests, or to develop at a JVM REPL instead of a CLJS one.
|
||||
;;
|
||||
;; Please note, though, that the purpose here *isn't* to fully replicate all of
|
||||
;; re-frame's behaviour in a real CLJS environment. We don't have Reagent or
|
||||
;; React on the JVM, and we don't try to mimic the stateful lifecycles that they
|
||||
;; embody.
|
||||
;;
|
||||
;; In particular, if you're performing side effects in any code that's triggered
|
||||
;; by a change to a Ratom's value, and not via a call to `dispatch`, then you're
|
||||
;; going to have a hard time getting any accurate tests with this code.
|
||||
;; However, if your subscriptions and Reagent render functions are pure, and
|
||||
;; your side-effects are all managed by event handlers, then hopefully this will
|
||||
;; allow you to write some useful tests that can run on the JVM.
|
||||
|
||||
|
||||
(defonce ^:private executor (Executors/newSingleThreadExecutor))
|
||||
|
||||
(defn next-tick [f]
|
||||
(.execute executor f)
|
||||
nil)
|
||||
|
||||
(def empty-queue clojure.lang.PersistentQueue/EMPTY)
|
||||
|
||||
(def after-render next-tick)
|
||||
|
||||
(def debug-enabled? true)
|
||||
|
||||
(defn ratom [x]
|
||||
(atom x))
|
||||
|
||||
(defn ratom? [x]
|
||||
(instance? clojure.lang.IAtom x))
|
||||
|
||||
(defn make-reaction
|
||||
"On JVM Clojure, return a `deref`-able thing which invokes the given function
|
||||
on every `deref`. That is, `make-reaction` here provides precisely none of the
|
||||
benefits of `reagent.ratom/make-reaction` (which only invokes its function if
|
||||
the reactions that the function derefs have changed value). But so long as `f`
|
||||
only depends on other reactions (which also behave themselves), the only
|
||||
difference is one of efficiency. That is, your tests should see no difference
|
||||
other than that they do redundant work."
|
||||
[f]
|
||||
(reify clojure.lang.IDeref
|
||||
(deref [_] (f))))
|
||||
|
||||
(defn add-on-dispose!
|
||||
"No-op in JVM Clojure, since for testing purposes, we don't care about
|
||||
releasing resources for efficiency purposes."
|
||||
[a-ratom f]
|
||||
nil)
|
||||
|
||||
(defn set-timeout!
|
||||
"Note that we ignore the `ms` value and just invoke the function, because
|
||||
there isn't often much point firing a timed event in a test."
|
||||
[f ms]
|
||||
(next-tick f))
|
|
@ -0,0 +1,27 @@
|
|||
(ns re-frame.interop
|
||||
(:require [goog.async.nextTick]
|
||||
[reagent.core]
|
||||
[reagent.ratom]))
|
||||
|
||||
(def next-tick goog.async.nextTick)
|
||||
|
||||
(def empty-queue #queue [])
|
||||
|
||||
(def after-render reagent.core/after-render)
|
||||
|
||||
(def ^:boolean debug-enabled? js/goog.DEBUG)
|
||||
|
||||
(defn ratom [x]
|
||||
(reagent.core/atom x))
|
||||
|
||||
(defn ratom? [x]
|
||||
(satisfies? reagent.ratom/IReactiveAtom x))
|
||||
|
||||
(defn make-reaction [f]
|
||||
(reagent.ratom/make-reaction f))
|
||||
|
||||
(defn add-on-dispose! [a-ratom f]
|
||||
(reagent.ratom/add-on-dispose! a-ratom f))
|
||||
|
||||
(defn set-timeout! [f ms]
|
||||
(js/setTimeout f ms))
|
|
@ -0,0 +1,43 @@
|
|||
(ns re-frame.loggers
|
||||
(:require
|
||||
[clojure.set :refer [difference]]
|
||||
#?@(:clj [[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]])))
|
||||
|
||||
(defn log [level & args]
|
||||
(log/log level (if (= 1 (count args))
|
||||
(first args)
|
||||
(str/join " " (map pr-str args)))))
|
||||
|
||||
;; "loggers" holds the current set of logging functions.
|
||||
;; By default, re-frame uses the functions provided by js/console.
|
||||
;; Use set-loggers! to change these defaults.
|
||||
|
||||
(def ^:private loggers
|
||||
(atom {:log #?(:cljs (js/console.log.bind js/console)
|
||||
:clj (partial log :info))
|
||||
:warn #?(:cljs (js/console.warn.bind js/console)
|
||||
:clj (partial log :warn))
|
||||
:error #?(:cljs (js/console.error.bind js/console)
|
||||
:clj (partial log :error))
|
||||
:group #?(:cljs (if (.-group js/console) ;; console.group does not exist < IE 11
|
||||
(js/console.group.bind js/console)
|
||||
(js/console.log.bind js/console))
|
||||
:clj (partial log :info))
|
||||
:groupEnd #?(:cljs (if (.-groupEnd js/console) ;; console.groupEnd does not exist < IE 11
|
||||
(js/console.groupEnd.bind js/console)
|
||||
#())
|
||||
:clj #())}))
|
||||
|
||||
(defn console
|
||||
[level & args]
|
||||
(assert (contains? @loggers level) (str "re-frame: log called with unknown level: " level))
|
||||
(apply (level @loggers) args))
|
||||
|
||||
|
||||
(defn set-loggers!
|
||||
"Change the set (or a subset) of logging functions used by re-frame.
|
||||
'new-loggers' should be a map which looks like default-loggers"
|
||||
[new-loggers]
|
||||
(assert (empty? (difference (set (keys new-loggers)) (-> @loggers keys set))) "Unknown keys in new-loggers")
|
||||
(swap! loggers merge new-loggers))
|
|
@ -1,27 +0,0 @@
|
|||
(ns re-frame.loggers
|
||||
(:require
|
||||
[clojure.set :refer [difference]]))
|
||||
|
||||
;; "loggers" holds the current set of logging functions.
|
||||
;; By default, re-frame uses the functions provided by js/console.
|
||||
;; Use set-loggers! to change these defaults.
|
||||
|
||||
(def ^:private loggers
|
||||
(atom {:log (js/console.log.bind js/console)
|
||||
:warn (js/console.warn.bind js/console)
|
||||
:error (js/console.error.bind js/console)
|
||||
:group (if (.-group js/console) (js/console.group.bind js/console) (js/console.log.bind js/console)) ;; console.group does not exist < IE 11
|
||||
:groupEnd (if (.-groupEnd js/console) (js/console.groupEnd.bind js/console) #())})) ;; console.groupEnd does not exist < IE 11
|
||||
|
||||
(defn console
|
||||
[level & args]
|
||||
(assert (contains? @loggers level) (str "re-frame: log called with unknown level: " level))
|
||||
(apply (level @loggers) args))
|
||||
|
||||
|
||||
(defn set-loggers!
|
||||
"Change the set (or a subset) of logging functions used by re-frame.
|
||||
'new-loggers' should be a map which looks like default-loggers"
|
||||
[new-loggers]
|
||||
(assert (empty? (difference (set (keys new-loggers)) (-> @loggers keys set))) "Unknown keys in new-loggers")
|
||||
(swap! loggers merge new-loggers))
|
|
@ -1,6 +1,6 @@
|
|||
(ns re-frame.middleware
|
||||
(:require
|
||||
[reagent.ratom :refer [IReactiveAtom]]
|
||||
[re-frame.interop :refer [ratom?]]
|
||||
[re-frame.loggers :refer [console]]
|
||||
[clojure.data :as data]))
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
|||
[handler]
|
||||
(fn pure-handler
|
||||
[app-db event-vec]
|
||||
(if-not (satisfies? IReactiveAtom app-db)
|
||||
(if-not (ratom? app-db)
|
||||
(do
|
||||
(if (map? app-db)
|
||||
(console :warn "re-frame: Looks like \"pure\" is in the middleware pipeline twice. Ignoring.")
|
|
@ -1,7 +1,6 @@
|
|||
(ns re-frame.router
|
||||
(:require [reagent.core]
|
||||
[re-frame.events :refer [handle]]
|
||||
[goog.async.nextTick]))
|
||||
(:require [re-frame.events :refer [handle]]
|
||||
[re-frame.interop :refer [after-render empty-queue next-tick]]))
|
||||
|
||||
|
||||
;; -- Router Loop ------------------------------------------------------------
|
||||
|
@ -61,8 +60,8 @@
|
|||
;; Events can have metadata which says to pause event processing.
|
||||
;; event metadata -> "run later" functions
|
||||
(def later-fns
|
||||
{:flush-dom (fn [f] ((.-after-render reagent.core) #(goog.async.nextTick f))) ;; one tick after the end of the next annimation frame
|
||||
:yield goog.async.nextTick}) ;; almost immediately
|
||||
{:flush-dom (fn [f] (after-render #(next-tick f))) ;; one tick after the end of the next annimation frame
|
||||
:yield next-tick}) ;; almost immediately
|
||||
|
||||
|
||||
;; Abstract representation of the Event Queue
|
||||
|
@ -88,9 +87,9 @@
|
|||
|
||||
|
||||
;; Concrete implementation of IEventQueue
|
||||
(deftype EventQueue [^:mutable fsm-state
|
||||
^:mutable queue
|
||||
^:mutable post-event-callback-fns]
|
||||
(deftype EventQueue [#?(:cljs ^:mutable fsm-state :clj ^:volatile-mutable fsm-state)
|
||||
#?(:cljs ^:mutable queue :clj ^:volatile-mutable queue)
|
||||
#?(:cljs ^:mutable post-event-callback-fns :clj ^:volatile-mutable post-event-callback-fns)]
|
||||
IEventQueue
|
||||
|
||||
;; -- API ------------------------------------------------------------------
|
||||
|
@ -144,7 +143,8 @@
|
|||
[:paused :add-event] [:paused #(-add-event this arg)]
|
||||
[:paused :resume ] [:running #(-resume this)]
|
||||
|
||||
(throw (js/Error. (str "re-frame: router state transition not found. " fsm-state " " trigger))))]
|
||||
(throw (ex-info (str "re-frame: router state transition not found. " fsm-state " " trigger)
|
||||
{:fsm-state fsm-state, :trigger trigger})))]
|
||||
|
||||
;; The "case" above computed both the new FSM state, and the action. Now, make it happen.
|
||||
(set! fsm-state new-fsm-state)
|
||||
|
@ -161,12 +161,12 @@
|
|||
(handle event-v)
|
||||
(set! queue (pop queue))
|
||||
(-call-post-event-callbacks this event-v)
|
||||
(catch :default ex
|
||||
(catch #?(:cljs :default :clj Exception) ex
|
||||
(-fsm-trigger this :exception ex)))))
|
||||
|
||||
(-run-next-tick
|
||||
[this]
|
||||
(goog.async.nextTick #(-fsm-trigger this :run-queue nil)))
|
||||
(next-tick #(-fsm-trigger this :run-queue nil)))
|
||||
|
||||
;; Process all the events currently in the queue, but not any new ones.
|
||||
;; Be aware that events might have metadata which will pause processing.
|
||||
|
@ -182,7 +182,7 @@
|
|||
|
||||
(-exception
|
||||
[_ ex]
|
||||
(set! queue #queue []) ;; purge the queue
|
||||
(set! queue empty-queue) ;; purge the queue
|
||||
(throw ex))
|
||||
|
||||
(-pause
|
||||
|
@ -206,7 +206,7 @@
|
|||
;; When "dispatch" is called, the event is added into this event queue. Later,
|
||||
;; the queue will "run" and the event will be "handled" by the registered function.
|
||||
;;
|
||||
(def event-queue (->EventQueue :idle #queue [] []))
|
||||
(def event-queue (->EventQueue :idle empty-queue []))
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
@ -224,11 +224,12 @@
|
|||
;; anything goes wrong, we know where it came from.
|
||||
;; To get a source mapped stack, we must get rid of the react frames
|
||||
;; See https://github.com/Day8/re-frame/issues/164#issuecomment-233528154
|
||||
stack (->> (js/Error. (str "Event " (first event-v) " dispatched from here:"))
|
||||
.-stack
|
||||
clojure.string/split-lines
|
||||
(remove #(re-find #"react.inc.js|\(native\)" %))
|
||||
(clojure.string/join "\n"))]
|
||||
stack #?(:cljs (->> (js/Error. (str "Event " (first event-v) " dispatched from here:"))
|
||||
.-stack
|
||||
clojure.string/split-lines
|
||||
(remove #(re-find #"react\.inc\.js|\(native\)" %))
|
||||
(clojure.string/join "\n"))
|
||||
:clj "n/a")]
|
||||
(if (nil? event-v)
|
||||
(throw (ex-info "re-frame: you called \"dispatch\" without an event vector." {}))
|
||||
(push event-queue (with-meta event-v {:stack stack}))))
|
|
@ -1,7 +1,7 @@
|
|||
(ns re-frame.subs
|
||||
(:require
|
||||
[reagent.ratom :as ratom :refer [make-reaction] :refer-macros [reaction]]
|
||||
[re-frame.db :refer [app-db]]
|
||||
[re-frame.interop :refer [add-on-dispose! debug-enabled? make-reaction ratom?]]
|
||||
[re-frame.loggers :refer [console]]
|
||||
[re-frame.utils :refer [first-in-vector]]))
|
||||
|
||||
|
@ -38,8 +38,8 @@
|
|||
[query-v dynv r]
|
||||
(let [cache-key [query-v dynv]]
|
||||
;; when this reaction is nolonger being used, remove it from the cache
|
||||
(ratom/add-on-dispose! r #(do (swap! query->reaction dissoc cache-key)
|
||||
(console :warn "Removing subscription: " cache-key)))
|
||||
(add-on-dispose! r #(do (swap! query->reaction dissoc cache-key)
|
||||
(console :warn "Removing subscription: " cache-key)))
|
||||
|
||||
(console :log "Dispatch site: ")
|
||||
(console :log (:dispatch-site (meta query-v)))
|
||||
|
@ -77,17 +77,17 @@
|
|||
cached)
|
||||
(let [query-id (first-in-vector v)
|
||||
handler-fn (get @qid->fn query-id)]
|
||||
(when ^boolean js/goog.DEBUG
|
||||
(when-let [not-reactive (remove #(implements? reagent.ratom/IReactiveAtom %) dynv)]
|
||||
(when debug-enabled?
|
||||
(when-let [not-reactive (remove ratom? dynv)]
|
||||
(console :warn "re-frame: your subscription's dynamic parameters that don't implement IReactiveAtom: " not-reactive)))
|
||||
(if (nil? handler-fn)
|
||||
(console :error "re-frame: no subscription handler registered for: \"" query-id "\". Returning a nil subscription.")
|
||||
(let [dyn-vals (reaction (mapv deref dynv))
|
||||
sub (reaction (handler-fn app-db v @dyn-vals))]
|
||||
(let [dyn-vals (make-reaction (fn [] (mapv deref dynv)))
|
||||
sub (make-reaction (fn [] (handler-fn app-db v @dyn-vals)))]
|
||||
;; handler-fn returns a reaction which is then wrapped in the sub reaction
|
||||
;; need to double deref it to get to the actual value.
|
||||
(console :warn "Subscription created: " v dynv)
|
||||
(cache-and-return v dynv (reaction @@sub))))))))
|
||||
(cache-and-return v dynv (make-reaction (fn [] @@sub)))))))))
|
||||
|
||||
;; -- Helper code for register-pure -------------------
|
||||
|
||||
|
@ -154,22 +154,37 @@
|
|||
sub-fn ;; first case the user provides a custom sub-fn
|
||||
(register
|
||||
sub-name
|
||||
(fn [db q-vec d-vec]
|
||||
(let [subscriptions (sub-fn q-vec d-vec)] ;; this let needs to be outside the fn
|
||||
(ratom/make-reaction
|
||||
(fn [] (f (multi-deref subscriptions) q-vec d-vec))))))
|
||||
(fn subs-handler-fn ;; multi-arity to match the arities `subscribe` might invoke.
|
||||
([db q-vec]
|
||||
(let [subscriptions (sub-fn q-vec)]
|
||||
(make-reaction
|
||||
(fn [] (f (multi-deref subscriptions) q-vec)))))
|
||||
([db q-vec d-vec]
|
||||
(let [subscriptions (sub-fn q-vec d-vec)]
|
||||
(make-reaction
|
||||
(fn [] (f (multi-deref subscriptions) q-vec d-vec)))))))
|
||||
(seq arrow-args) ;; the user uses the :<- sugar
|
||||
(register
|
||||
sub-name
|
||||
(fn [db q-vec d-vec]
|
||||
(let [subscriptions (map subscribe arrow-subs)
|
||||
subscriptions (if (< 1 (count subscriptions))
|
||||
subscriptions
|
||||
(first subscriptions))] ;; automatically provide a singlton
|
||||
(ratom/make-reaction
|
||||
(fn [] (f (multi-deref subscriptions) q-vec d-vec))))))
|
||||
(letfn [(get-subscriptions []
|
||||
(let [subscriptions (map subscribe arrow-subs)]
|
||||
(if (< 1 (count subscriptions))
|
||||
subscriptions
|
||||
(first subscriptions))))] ;; automatically provide a singleton
|
||||
(fn subs-handler-fn
|
||||
([db q-vec]
|
||||
(let [subscriptions (get-subscriptions)]
|
||||
(make-reaction
|
||||
(fn [] (f (multi-deref subscriptions) q-vec)))))
|
||||
([db q-vec d-vec]
|
||||
(let [subscriptions (get-subscriptions)]
|
||||
(make-reaction
|
||||
(fn [] (f (multi-deref subscriptions) q-vec d-vec))))))))
|
||||
:else
|
||||
(register ;; the simple case with no subs
|
||||
sub-name
|
||||
(fn [db q-vec d-vec]
|
||||
(ratom/make-reaction (fn [] (f @db q-vec d-vec)))))))())
|
||||
(fn subs-handler-fn
|
||||
([db q-vec]
|
||||
(make-reaction (fn [] (f @db q-vec))))
|
||||
([db q-vec d-vec]
|
||||
(make-reaction (fn [] (f @db q-vec d-vec))))))))())
|
|
@ -1,10 +1,9 @@
|
|||
(ns re-frame.undo
|
||||
(:require-macros [reagent.ratom :refer [reaction]])
|
||||
(:require
|
||||
[reagent.core :as reagent]
|
||||
[re-frame.loggers :refer [console]]
|
||||
[re-frame.db :refer [app-db]]
|
||||
[re-frame.events :as handlers]
|
||||
[re-frame.events :as handlers]
|
||||
[re-frame.interop :refer [make-reaction ratom]]
|
||||
[re-frame.subs :as subs]))
|
||||
|
||||
|
||||
|
@ -32,8 +31,8 @@
|
|||
|
||||
;; -- State history ----------------------------------------------------------
|
||||
|
||||
(def ^:private undo-list "A list of history states" (reagent/atom []))
|
||||
(def ^:private redo-list "A list of future states, caused by undoing" (reagent/atom []))
|
||||
(def ^:private undo-list "A list of history states" (ratom []))
|
||||
(def ^:private redo-list "A list of future states, caused by undoing" (ratom []))
|
||||
|
||||
;; -- Explanations -----------------------------------------------------------
|
||||
;;
|
||||
|
@ -41,9 +40,9 @@
|
|||
;;
|
||||
;; Seems really ugly to have mirrored vectors, but ...
|
||||
;; the code kinda falls out when you do. I'm feeling lazy.
|
||||
(def ^:private app-explain "Mirrors app-db" (reagent/atom ""))
|
||||
(def ^:private undo-explain-list "Mirrors undo-list" (reagent/atom []))
|
||||
(def ^:private redo-explain-list "Mirrors redo-list" (reagent/atom []))
|
||||
(def ^:private app-explain "Mirrors app-db" (ratom ""))
|
||||
(def ^:private undo-explain-list "Mirrors undo-list" (ratom []))
|
||||
(def ^:private redo-explain-list "Mirrors redo-list" (ratom []))
|
||||
|
||||
(defn- clear-undos!
|
||||
[]
|
||||
|
@ -101,14 +100,14 @@
|
|||
(fn handler
|
||||
; "returns true if anything is stored in the undo list, otherwise false"
|
||||
[_ _]
|
||||
(reaction (undos?))))
|
||||
(make-reaction undos?)))
|
||||
|
||||
(subs/register
|
||||
:redos?
|
||||
(fn handler
|
||||
; "returns true if anything is stored in the redo list, otherwise false"
|
||||
[_ _]
|
||||
(reaction (redos?))))
|
||||
(make-reaction redos?)))
|
||||
|
||||
|
||||
(subs/register
|
||||
|
@ -116,14 +115,14 @@
|
|||
(fn handler
|
||||
; "returns a vector of string explanations ordered oldest to most recent"
|
||||
[_ _]
|
||||
(reaction (undo-explanations))))
|
||||
(make-reaction undo-explanations)))
|
||||
|
||||
(subs/register
|
||||
:redo-explanations
|
||||
(fn handler
|
||||
; "returns a vector of string explanations ordered from most recent undo onward"
|
||||
[_ _]
|
||||
(reaction (deref redo-explain-list))))
|
||||
(make-reaction #(deref redo-explain-list))))
|
||||
|
||||
;; -- event handlers ----------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue