Last active
August 3, 2020 22:25
-
-
Save jkxyz/f41c9948dd1bed38ef540de11d26c4dc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns tailwind | |
(:require | |
[clojure.spec.alpha :as s])) | |
(defn- parse-map [m] | |
(reduce | |
(fn [classes [k v]] | |
(cond | |
;; If v is another collection of classes, we prepend k as the variant | |
;; prefix for all of the classes in v, e.g. | |
;; {:lg {:px 1}} => lg:px-1 | |
;; {:lg [:container {:px 1}]} => lg:container lg:px-1 | |
(map? v) | |
(apply conj classes (map #(str (name k) ":" %) (parse-map v))) | |
(coll? v) | |
(apply conj | |
classes | |
(->> v | |
(mapcat #(if (map? %) (parse-map %) [%])) | |
(map #(str (name k) ":" (name %))))) | |
;; If v is a single value, we use k as a shorthand prefix, e.g. | |
;; {:px 1} => px-1 | |
(not (false? v)) | |
(conj classes | |
;; If the value is a negative number then prefix the class with a minus, e.g. | |
;; {:px -1} => -px-1 | |
(str (when (and (number? v) (neg? v)) "-") | |
(name k) | |
(cond | |
(keyword? v) (str "-" (name v)) | |
;; Use the absolute value here as we handle the minus above | |
(number? v) (str "-" (Math/abs v)) | |
(boolean? v) nil | |
:else (str "-" v)))) | |
:else classes)) | |
[] | |
m)) | |
(s/def ::tw-val | |
(s/or :keyword keyword? | |
:string string? | |
:number number? | |
:boolean boolean?)) | |
(s/def ::tw-map | |
(s/map-of keyword? | |
(s/or :val ::tw-val | |
:map (s/map-of keyword? ::tw-val) | |
:vec (s/coll-of (s/or :keyword keyword? | |
:string string? | |
:map ::tw-map))))) | |
(s/def ::tw-args | |
(s/* (s/or :map ::tw-map | |
:keyword keyword? | |
:string string? | |
:vec (s/coll-of (s/or :keyword keyword? :string string?))))) | |
(def ^:private used-classes (atom #{})) | |
(defmacro tw | |
"Takes a collection of class specs and returns a list of class names. | |
Examples: | |
(tw {:px 2 :py 1} :text-white :bg-blue-500) => px-2 py-1 text-white bg-blue-500 | |
(tw {:hover [:text-gray-100 {:pl 0}]}) => hover:text-gray-100 hover:pl-0 | |
(tw {:lg [:container {:ml -2}]}) => container lg:-ml-2" | |
[& classes] | |
(assert (s/valid? ::tw-args classes) (s/explain-data ::tw-args classes)) | |
(let [classes (->> classes | |
(mapcat #(cond (map? %) (parse-map %) (coll? %) % :else [%])) | |
(mapv name))] | |
(swap! used-classes into classes) | |
classes)) | |
(s/fdef tw | |
:args ::tw-args | |
:ret (s/coll-of string?)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment