From c55e8c5ef5a964c4dd23399bedcbc5096bfc22b6 Mon Sep 17 00:00:00 2001 From: Daniel Leong Date: Wed, 28 Aug 2019 17:10:37 -0400 Subject: [PATCH] Add parameters to defkeyframes (#5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make defkeyframes accept a params vector This is a breaking change! But it makes the API much more logically consistent—why shouldn't you be able to parameterize keyframes? This change also includes some heavy refactoring to simplify code generation for the other cases, as well as an optimization that skips the call to build-style-name if the function does not accept parameters. * Update README to reflect params support on defkeyframes --- README.md | 2 +- dev/spade/demo.cljs | 8 +++- src/spade/core.cljc | 96 +++++++++++++++++++++++++-------------- test/spade/core_test.cljs | 12 ++++- 4 files changed, 79 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 299075f..06c2cbf 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ Spade supports `@media` queries in the exact same way you see them in the [garde Spade even supports generating `@keyframes` just like you'd expect: ```clojure -(defkeyframes anim-frames +(defkeyframes anim-frames [] ["0%" {:opacity 0}] ["100%" {:opacity 1}]) ``` diff --git a/dev/spade/demo.cljs b/dev/spade/demo.cljs index 00b2fda..eb0fe82 100644 --- a/dev/spade/demo.cljs +++ b/dev/spade/demo.cljs @@ -3,10 +3,14 @@ [reagent.core :as r] [spade.core :refer [defclass defattrs defglobal defkeyframes]])) -(defkeyframes anim-frames +(defkeyframes anim-frames [] ["0%" {:opacity 0}] ["100%" {:opacity 1}]) +(defkeyframes parameterized-anim-frames [start end] + ["0%" {:opacity start}] + ["100%" {:opacity end}]) + (defglobal background [:body {:background "#333"}]) @@ -19,7 +23,7 @@ {:padding "8px"} [:.title {:font-size "22pt" - :animation [[(anim-frames) "560ms" 'ease-in-out]]}]) + :animation [[(parameterized-anim-frames 0 0.5) "560ms" 'ease-in-out]]}]) (defclass colorized-with-key [color] ^{:key (str/upper-case color)} diff --git a/src/spade/core.cljc b/src/spade/core.cljc index a864dff..0d2eaf3 100644 --- a/src/spade/core.cljc +++ b/src/spade/core.cljc @@ -51,54 +51,82 @@ (assoc base :composes composition) base))) -(defn- transform-named-style [style style-name-var params-var] +(defn- build-style-naming-let + [style params original-style-name-var params-var] (let [has-key-meta? (find-key-meta style) static-key (extract-key style) - [composition style] (extract-composes style) - name-var (gensym "name") - style-var (gensym "style")] - (if (or static-key - (not has-key-meta?)) + name-var (gensym "name")] + (cond + ; easiest case: no params? no need to call build-style-name + (nil? (seq params)) + [nil original-style-name-var nil] + + (or static-key + (not has-key-meta?)) ; if we can extract the key statically, that's better - (let [name-creator `(#'build-style-name - ~style-name-var - ~static-key - ~params-var)] - `(let [~name-var ~name-creator - ~style-var ~(into [`(str "." ~name-var)] style)] - ~(with-composition composition name-var style-var))) - - `(let [base-style# ~(vec style) - key# (:key (meta (first base-style#))) - ~name-var (#'build-style-name - ~style-name-var - key# - ~params-var) - ~style-var (into [(str "." ~name-var)] base-style#)] - ~(with-composition composition name-var style-var))))) - -(defn- transform-style [mode style style-name-var params-var] + [nil name-var `[~name-var (#'build-style-name + ~original-style-name-var + ~static-key + ~params-var)]] + + :else + (let [base-style-var (gensym "base-style")] + [base-style-var name-var `[~base-style-var ~(vec style) + key# (:key (meta (first ~base-style-var))) + ~name-var (#'build-style-name + ~original-style-name-var + key# + ~params-var)]])))) + +(defn- transform-named-style [style params style-name-var params-var] + (let [[composition style] (extract-composes style) + style-var (gensym "style") + [base-style-var name-var name-let] (build-style-naming-let + style params style-name-var + params-var) + style-decl (if base-style-var + `(into [(str "." ~name-var)] ~base-style-var) + (into [`(str "." ~name-var)] style))] + `(let ~(vec (concat name-let + [style-var style-decl])) + ~(with-composition composition name-var style-var)))) + +(defn- transform-keyframes-style [style params style-name-var params-var] + (let [[style-var name-var style-naming-let] (build-style-naming-let + style params style-name-var + params-var) + info-map `{:css (spade.runtime/compile-css + (garden.stylesheet/at-keyframes + ~name-var + ~(or style-var + (vec style)))) + :name ~name-var}] + + ; this (let) might get compiled out in advanced mode anyway, but + ; let's just generate simpler code instead of having a redundant + ; (let) if the keyframes take no params + (if style-naming-let + `(let ~style-naming-let ~info-map) + info-map))) + +(defn- transform-style [mode style params style-name-var params-var] (let [style (replace-at-forms style)] (cond (#{:global} mode) `{:css (spade.runtime/compile-css ~(vec style)) :name ~style-name-var} + ; keyframes are a bit of a special case (#{:keyframes} mode) - `{:css (spade.runtime/compile-css - (garden.stylesheet/at-keyframes - ~style-name-var - ~(vec style))) - :name ~style-name-var} + (transform-keyframes-style style params style-name-var params-var) :else - (transform-named-style style style-name-var params-var)))) + (transform-named-style style params style-name-var params-var)))) (defmulti ^:private declare-style (fn [mode _class-name params _factory-name-var _factory-fn-name] (case mode :global :static - :keyframes :no-args (cond (some #{'&} params) :variadic (every? symbol? params) :default @@ -166,7 +194,7 @@ factory-name-var (gensym "factory-name")] `(do (defn ~factory-fn-name ~factory-params - ~(transform-style mode style style-name-var params-var)) + ~(transform-style mode style params style-name-var params-var)) (let [~factory-name-var (factory->name ~factory-fn-name)] ~(declare-style mode class-name params factory-name-var factory-fn-name))))) @@ -180,5 +208,5 @@ (defmacro defglobal [group-name & style] (declare-style-fns :global group-name nil style)) -(defmacro defkeyframes [keyframes-name & style] - (declare-style-fns :keyframes keyframes-name nil style)) +(defmacro defkeyframes [keyframes-name params & style] + (declare-style-fns :keyframes keyframes-name params style)) diff --git a/test/spade/core_test.cljs b/test/spade/core_test.cljs index 404d18e..2a4351f 100644 --- a/test/spade/core_test.cljs +++ b/test/spade/core_test.cljs @@ -57,14 +57,22 @@ "@media (min-width: 42px) {")))) -(defkeyframes key-frames +(defkeyframes key-frames [] [:from {:opacity 0}]) +(defkeyframes parameterized-key-frames [from] + [:from {:opacity from}]) + (deftest defkeyframes-test (testing "Return keyframes name from defkeyframes" (is (fn? key-frames)) (is (= "spade-core-test-key-frames" - (key-frames))))) + (key-frames)))) + + (testing "Return dynamic keyframes name from parameterized defkeyframes" + (is (fn? key-frames)) + (is (= (str "spade-core-test-parameterized-key-frames_" (hash [0])) + (parameterized-key-frames 0))))) (defclass composed [color] ^{:key color}