diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..cdd1de3 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,13 @@ +/resources/public/js/compiled/** +figwheel_server.log +pom.xml +*jar +/lib/ +/classes/ +/out/ +/target/ +.lein-deps-sum +.lein-repl-history +.lein-plugins/ +.repl +.nrepl-port diff --git a/example/project.clj b/example/project.clj new file mode 100644 index 0000000..0d45551 --- /dev/null +++ b/example/project.clj @@ -0,0 +1,41 @@ +(defproject example "0.1.0-SNAPSHOT" + :description "FIXME: write this!" + :url "http://example.com/FIXME" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + + :min-lein-version "2.7.1" + + :dependencies [[org.clojure/clojure "1.8.0"] + [org.clojure/clojurescript "1.9.671"] + [rum "0.10.8"] + [reagent "0.7.0"] + [org.omcljs/om "1.0.0-beta1"]] + + :plugins [[lein-cljsbuild "1.1.6" :exclusions [[org.clojure/clojure]]]] + + :source-paths ["src"] + + :cljsbuild {:builds + [{:id "dev" + :source-paths ["src" "../src"] + :compiler {:main example.core + :asset-path "js/compiled/out" + :output-to "resources/public/js/compiled/example.js" + :output-dir "resources/public/js/compiled/out" + :source-map-timestamp true + :preloads [devtools.preload]}} + + {:id "min" + :source-paths ["src" "../src"] + :compiler {:output-to "resources/public/js/compiled/example.js" + :main example.core + :optimizations :advanced + :pretty-print false}}]} + + :profiles {:dev {:dependencies [[binaryage/devtools "0.9.2"] + [com.cemerick/piggieback "0.2.1"]] + :source-paths ["src"] + :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]} + :clean-targets ^{:protect false} ["resources/public/js/compiled" + :target-path]}}) diff --git a/example/resources/public/index.html b/example/resources/public/index.html new file mode 100644 index 0000000..8ff12bf --- /dev/null +++ b/example/resources/public/index.html @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/example/src/example/core.cljs b/example/src/example/core.cljs new file mode 100644 index 0000000..1a9e57e --- /dev/null +++ b/example/src/example/core.cljs @@ -0,0 +1,60 @@ +(ns example.core + (:require [rum.core :as rum] + [reagent.core :as r] + [om.dom :as dom] + [om.next :as om :refer [defui]] + [goog.dom :as gdom] + [cljss.core :refer [defstyles]] + [cljss.rum :as rumss] + [cljss.reagent :as rss] + [cljss.om :as omss])) + +(defstyles wrapper [width] + {:padding "8px 16px" + :margin "0 auto" + :text-align "center" + :font "normal 18px sans-serif" + :width width}) + +;; Rum +(rumss/defstyled RumH1 :h1 + {:font-size "32px" + :color "#242424" + :margin-top :v-margin + :margin-bottom :v-margin}) + +(rum/defc RumApp [] + [:div {:class (wrapper "600px")} + (RumH1 {:v-margin "8px"} "Clojure Style Sheets for Rum")]) + +(rum/mount (RumApp) (gdom/getElement "rum-app")) + + +;; Reagent +(rss/defstyled ReagentH1 :h1 + {:font-size "32px" + :color "#242424" + :margin-top :v-margin + :margin-bottom :v-margin}) + +(defn ReagentApp [] + [:div {:class (wrapper "600px")} + (ReagentH1 {:v-margin "8px"} "Clojure Style Sheets for Reagent")]) + +(r/render [ReagentApp] (gdom/getElement "reagent-app")) + + +;; Om +(omss/defstyled OmH1 :h1 + {:font-size "32px" + :color "#242424" + :margin-top :v-margin + :margin-bottom :v-margin}) + +(defui OmApp + Object + (render [this] + (dom/div #js {:className (wrapper "600px")} + (OmH1 {:v-margin "8px"} "Clojure Style Sheets for Om")))) + +(.render js/ReactDOM ((om/factory OmApp)) (gdom/getElement "om-app")) diff --git a/src/cljss/core.clj b/src/cljss/core.clj index 11ae92d..7e44759 100644 --- a/src/cljss/core.clj +++ b/src/cljss/core.clj @@ -1,9 +1,5 @@ (ns cljss.core - (:require [cljs.analyzer :as ana] - [cljss.utils :refer [build-css]])) - -; (let [var (ana/resolve-var &env y (ana/confirm-var-exists-throw))] -; (-> var :tag keyword)) + (:require [cljss.utils :refer [build-css]])) (defn- varid [id idx [rule]] [rule (str "--css-" id "-" idx)]) @@ -16,7 +12,7 @@ (and (re-matches #"&:.*" (name rule)) (map? value))) -(defn- build-styles [cls id idx styles] +(defn- collect-styles [cls id idx styles] (let [dynamic (filterv dynamic? styles) static (filterv (comp not dynamic?) styles) vars (map-indexed #(varid id (+ idx %1) %2) dynamic) @@ -27,23 +23,51 @@ (build-css cls))] [static vals (count vars)])) -(defmacro defstyled [var tag styles] - (let [tag# (name tag) - pseudo (filterv pseudo? styles) +(defn- build-styles [styles] + (let [pseudo (filterv pseudo? styles) styles (filterv (comp not pseudo?) styles) - id# (-> styles hash str) - cls (str "css-" id#) - attrs# (->> styles (map second) (filterv keyword?)) - [static vals idx] (build-styles cls id# 0 styles) + id (-> styles hash str) + cls (str "css-" id) + [static vals idx] (collect-styles cls id 0 styles) pstyles (->> pseudo (map (fn [[rule styles]] - (build-styles (str cls (subs (name rule) 1)) id# idx styles)))) - static# (->> pstyles - (map first) - (apply str) - (str static)) - vals# (->> pstyles - (mapcat second) - (into vals))] - `(def ~var - (cljss.core/styled ~tag# ~id# ~static# ~vals# ~attrs#)))) + (collect-styles (str cls (subs (name rule) 1)) id idx styles)))) + static (->> pstyles + (map first) + (apply str) + (str static)) + vals (->> pstyles + (mapcat second) + (into vals))] + [id static vals])) + +(defmacro defstyles + "Takes var name, a vector of arguments and a hash map of styles definition. + Generates class name, static and dynamic parts of styles. + Returns a function that calls `cljss.core/css` to inject styles at runtime + and return generated class name." + [var args styles] + (let [[id# static# vals#] (build-styles styles)] + `(defn ~var ~args + (cljss.core/css ~id# ~static# ~vals#)))) + +(defn ->styled + "Takes var name, HTML tag name and a hash map of styles definition. + Generates class name, static and dynamic parts of styles. + Returns a var bound to the result of calling `cljss.core/styled`, + which produces Hiccup-style element and injects styles." + [styles] + (let [attrs (->> styles (map second) (filterv keyword?)) + [id static vals] (build-styles styles)] + [id static vals attrs])) + +(defmacro make-styled [] + '(defn styled [cls static vars attrs create-element] + (fn [props & children] + (let [[props children] (if (map? props) [props children] [{} (into [props] children)]) + varClass (->> vars (map (fn [[cls v]] (if (ifn? v) [cls (v props)] [cls v]))) (cljss.core/css cls static)) + className (get props :className) + className (str (when className (str className " ")) varClass) + props (assoc props :className className) + props (apply dissoc props attrs)] + (create-element props children))))) diff --git a/src/cljss/core.cljs b/src/cljss/core.cljs index 1d233f1..f777cda 100644 --- a/src/cljss/core.cljs +++ b/src/cljss/core.cljs @@ -4,7 +4,10 @@ (defonce ^:private sheet (create-sheet)) -(defn- css [static cls vars] +(defn css + "Takes class name, static styles and dynamic styles. + Injects styles and returns a string of generated class names." + [cls static vars] (when-not (empty? static) (insert! sheet static)) (if (seq vars) @@ -12,13 +15,3 @@ (insert! sheet (build-css var-cls vars)) (str "css-" cls " " var-cls)) (str "css-" cls))) - -(defn styled [tag cls static vars attrs] - (fn [props & children] - (let [[props children] (if (map? props) [props children] [{} (into [props] children)]) - varClass (->> vars (map (fn [[cls v]] (if (ifn? v) [cls (v props)] [cls v]))) (css static cls)) - className (get props :className) - className (str (when className (str className " ")) varClass) - props (assoc props :className className) - props (apply dissoc props attrs)] - (apply vector tag props children)))) diff --git a/src/cljss/om.clj b/src/cljss/om.clj new file mode 100644 index 0000000..c07c151 --- /dev/null +++ b/src/cljss/om.clj @@ -0,0 +1,9 @@ +(ns cljss.om + (:require [cljss.core :refer [->styled]])) + +(defmacro defstyled [var tag styles] + (let [tag# (name tag) + [id# static# vals# attrs#] (->styled styles) + create-element# `#(apply js/React.createElement ~tag# (cljs.core/clj->js %1) %2)] + `(def ~var + (cljss.rum/styled ~id# ~static# ~vals# ~attrs# ~create-element#)))) diff --git a/src/cljss/om.cljs b/src/cljss/om.cljs new file mode 100644 index 0000000..2c4d164 --- /dev/null +++ b/src/cljss/om.cljs @@ -0,0 +1,4 @@ +(ns cljss.om + (:require [cljss.core :refer [make-styled]])) + +(make-styled) diff --git a/src/cljss/reagent.clj b/src/cljss/reagent.clj new file mode 100644 index 0000000..c360db0 --- /dev/null +++ b/src/cljss/reagent.clj @@ -0,0 +1,9 @@ +(ns cljss.reagent + (:require [cljss.core :refer [->styled]])) + +(defmacro defstyled [var tag styles] + (let [tag# tag + [id# static# vals# attrs#] (->styled styles) + create-element# `#(apply vector ~tag# %1 %2)] + `(def ~var + (cljss.rum/styled ~id# ~static# ~vals# ~attrs# ~create-element#)))) diff --git a/src/cljss/reagent.cljs b/src/cljss/reagent.cljs new file mode 100644 index 0000000..9c9bf02 --- /dev/null +++ b/src/cljss/reagent.cljs @@ -0,0 +1,4 @@ +(ns cljss.reagent + (:require [cljss.core :refer [make-styled]])) + +(make-styled) diff --git a/src/cljss/rum.clj b/src/cljss/rum.clj new file mode 100644 index 0000000..585126a --- /dev/null +++ b/src/cljss/rum.clj @@ -0,0 +1,9 @@ +(ns cljss.rum + (:require [cljss.core :refer [->styled]])) + +(defmacro defstyled [var tag styles] + (let [tag# (name tag) + [id# static# vals# attrs#] (->styled styles) + create-element# `#(apply js/React.createElement ~tag# (cljs.core/clj->js %1) %2)] + `(def ~var + (cljss.rum/styled ~id# ~static# ~vals# ~attrs# ~create-element#)))) diff --git a/src/cljss/rum.cljs b/src/cljss/rum.cljs new file mode 100644 index 0000000..d6b7fa0 --- /dev/null +++ b/src/cljss/rum.cljs @@ -0,0 +1,4 @@ +(ns cljss.rum + (:require [cljss.core :refer [make-styled]])) + +(make-styled)