From 28a6534c53dd17b693b4d1574335357cb43319fa Mon Sep 17 00:00:00 2001 From: David Goldfarb Date: Thu, 23 Nov 2017 12:32:35 +0200 Subject: [PATCH] Move much Sodium code to new library, Iron --- README.md | 10 ++++ project.clj | 3 +- src/sodium/chars.cljs | 15 ----- src/sodium/chrome_utils.cljs | 14 ----- src/sodium/core.cljs | 113 ++++++++++------------------------- src/sodium/extensions.cljs | 23 +++---- src/sodium/macros.clj | 16 ++--- src/sodium/re_utils.cljs | 59 ------------------ src/sodium/utils.cljc | 81 +------------------------ 9 files changed, 67 insertions(+), 267 deletions(-) delete mode 100644 src/sodium/chars.cljs delete mode 100644 src/sodium/chrome_utils.cljs delete mode 100644 src/sodium/re_utils.cljs diff --git a/README.md b/README.md index fa33400..6146191 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,15 @@ # Sodium +## ALERT + +Sodium 0.9.0-SNAPSHOT introduces incompatible changes. + +I have divided Sodium into two libraries. Sodium retains the Soda-ash/Semantic-UI +code. But, all the more general utilities have been moved to a new library, +[Iron](https://github.com/deg/iron). There will be teething pains, so I recommend +avoiding these snapshot version unless you want to help me stabilize. I hope to have a +new version up and stable within a few days. + ## Introduction Sodium is a wrapper around [soda-ash](https://github.com/gadfly361/soda-ash) which, diff --git a/project.clj b/project.clj index 988de4a..86af826 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,8 @@ [org.clojure/clojurescript "1.9.946"] [re-frame "0.10.2"] [reagent "0.7.0"] - [soda-ash "0.76.0"]] + [soda-ash "0.76.0"] + [com.degel/iron "0.1.0-SNAPSHOT"]] :plugins [[lein-cljsbuild "1.1.7"] [lein-doo "0.1.8"]] :cljsbuild diff --git a/src/sodium/chars.cljs b/src/sodium/chars.cljs deleted file mode 100644 index 913e295..0000000 --- a/src/sodium/chars.cljs +++ /dev/null @@ -1,15 +0,0 @@ -;;; Author: David Goldfarb (deg@degel.com) -;;; Copyright (c) 2017, David Goldfarb - -(ns sodium.chars) - -;;; Names for commonly-used unicode characters -;;; [TODO] This list is still embarassingly empty. Fill in. - -(def em-dash "\u2014") -(def en-dash "\u2013") -(def figure-dash "\u2012") -(def horizontal-bar "\u2015") -(def euro "\u20AC") -(def gb-pound "\u00A3") -(def new-israeli-shekel "\u20AA") diff --git a/src/sodium/chrome_utils.cljs b/src/sodium/chrome_utils.cljs deleted file mode 100644 index 5ee6110..0000000 --- a/src/sodium/chrome_utils.cljs +++ /dev/null @@ -1,14 +0,0 @@ -;;; Author: David Goldfarb (deg@degel.com) -;;; Copyright (c) 2017, David Goldfarb - -(ns sodium.chrome-utils) - -(defn console-dir - "Inspect an object in the Chrome console, preceded by a text message. - This is useful in general, but especially nice for JavaScript objects - that print opaquely in the REPL." - [msg obj] - (when msg - (js/console.log msg)) - (when obj - (js/console.dir obj))) diff --git a/src/sodium/core.cljs b/src/sodium/core.cljs index 8f405e7..b784ad1 100644 --- a/src/sodium/core.cljs +++ b/src/sodium/core.cljs @@ -8,72 +8,37 @@ [clojure.spec.alpha :as s] [re-frame.loggers :refer [console]] [soda-ash.core :as sa] - [sodium.re-utils :refer [evt]] + [iron.re-utils :refer [evt]] + [iron.utils :refer [validate camelize-map-keys negligible?]] [sodium.utils :as utils])) (s/def :sodium/size #{:tiny :small :medium :large :huge}) -(defn- negligible? - [x] - (if (seqable? x) (empty? x) (not x))) - -(defn >event - "Return a function suitable for an on-* handler, to deliver the value - into into a re-frame event. See also >atom. - The first argument is a re-frame event vector, into which the value - will be conjed as the final element. - It is followed by two optional arguments: a default value that will - be used when the value is empty, and a 'coercer' function to convert - the value into a suitable form for the event. - Note that the default value is _not_ passed through the coercer." - ;; [TODO] The line about the default value is obviously wrong. Check users and fix! - ([event] - (>event event "")) - ([event default] - (>event event default identity)) - ([event default coercer] - #(>evt (let [value (or (.-value %2) (.-checked %2))] - (conj event - (coercer (if (negligible? value) - default - value))))))) - -(defn >events - "Utility function to dispatch multiple events from an on-* hander. - The syntax is a bit opaque, because we have to wrap both the event - parameters and the parameters to >event (default and coercer). - - So, a usage looks like: - - (na/>events [[[:update-age :in-minutes] 42] - [[:set-color] :cyan nearest-color]]) - " - [events] - (fn [dom-event param-map] - (run! (fn [event] - ((apply >event event) dom-event param-map)) - events))) - -(defn >atom - "Return a function suitable for an on-* handler, to deliver the value - into into an atom. This would typically be used to store a result into - a local reagent atom. See also >event. - The first argument is an atom, which the value will be reset! into. - It is followed by two optional arguments: a default value that will - be used when the value is empty, and a 'coercer' function to convert - the value into a suitable form for the event. - Note that the default value is _not_ passed through the coercer." - ;; [TODO] Default wrong here too - ([atom] - (>atom atom "")) - ([atom default] - (>atom atom default identity)) - ([atom default coercer] - #(->> (or (.-value %2) (.-checked %2)) - js->clj - coercer - (reset! atom)))) - +(defn value + "A bit hackish, but I think this is enough to get the useful value + from any Semantic-UI-React element." + [_dom_event data] + (js->clj + (or (.-value data) (.-checked data)))) + +(defn value->event-fn + "Return a function that will collect the value from a + react-semantic-ui dom event and pass it to a re-frame event" + ([event] (value->event-fn event {})) + ([event {:keys [default coercer] :as >evt-params}] + (fn [dom_event data] + (>evt event (value dom_event data) >evt-params)))) + +(defn value->atom-fn + "Return a function that will collect the value from a + react-semantic-ui dom event and pass it into an atom" + ([atom] (value->atom-fn atom {})) + ([atom {:keys [default coercer] :or {coercer identity}}] + (fn [dom_event data] + (let [value (value dom_event data)] + (reset! atom (coercer (if (negligible? value) + default + value))))))) (defn list-option "Convert value and text info format suitable for a React list element @@ -90,18 +55,6 @@ (list-option (value-fn item) (text-fn item))) items)) -(defn params (update :type #(or % "button")) - (utils/camelize-map-keys :exclude [:data-tooltip]))]) + (camelize-map-keys :exclude [:data-tooltip]))]) (defcomponent button [params] {::key-sets [:basic :button] ::keys [data-tooltip] - :pre [(utils/validate (s/nilable ifn?) (:on-click params)) - (utils/validate (s/nilable string?) (:data-tooltip params))]} + :pre [(validate (s/nilable ifn?) (:on-click params)) + (validate (s/nilable string?) (:data-tooltip params))]} [sa/Button (-> params (update :type #(or % "button")) - (utils/camelize-map-keys :exclude [:data-tooltip]))]) + (camelize-map-keys :exclude [:data-tooltip]))]) diff --git a/src/sodium/extensions.cljs b/src/sodium/extensions.cljs index 3022d63..bf173c7 100644 --- a/src/sodium/extensions.cljs +++ b/src/sodium/extensions.cljs @@ -7,8 +7,9 @@ [reagent.core :as reagent] [re-frame.core :as re-frame] [re-frame.loggers :refer [console]] + [iron.re-utils :refer [evt]] + [iron.utils :refer [ci-sort validate]] [sodium.core :as na] - [sodium.re-utils :refer [evt]] [sodium.utils :as utils])) @@ -17,9 +18,9 @@ ;;; Various page and section headers/dividers (defn- header-maker [title size dividing? sub?] - {:pre [(utils/validate (s/or :string string? :event vector?) title) - (utils/validate boolean? dividing?) - (utils/validate :sodium/size size)]} + {:pre [(validate (s/or :string string? :event vector?) title) + (validate boolean? dividing?) + (validate :sodium/size size)]} (na/header {:content (if (vector? title) (str (atom partial-tag-text) + :default-value (or @partial-tag-text "") + :on-change (na/value->atom-fn partial-tag-text) :action (when-not (empty? @partial-tag-text) {:icon "add" :on-click #(let [tag (conj selected-tags @partial-tag-text)] @@ -218,12 +219,12 @@ :or {all-tags-sub [::all-tags] selected-tags-sub [::selected-tags] set-selected-tags-event [::selected-tags]}}] - (let [available-tags (utils/ci-sort (event set-selected-tags-event #{} set) + :on-change (na/value->event-fn set-selected-tags-event {:default #{} :coercer set}) :options (na/dropdown-list available-tags identity identity)}])) diff --git a/src/sodium/macros.clj b/src/sodium/macros.clj index 0c8dd45..d993796 100644 --- a/src/sodium/macros.clj +++ b/src/sodium/macros.clj @@ -4,6 +4,7 @@ (ns sodium.macros (:require [clojure.spec.alpha :as s] + [iron.utils :refer [camelize-map-keys validate vconcat]] [sodium.utils :as utils] [sodium.keys :as keys])) @@ -32,18 +33,19 @@ [name [params-var & other-params] {:keys [pre post :sodium.core/keys :sodium.core/key-sets]} & component-body] - {:pre [(utils/validate symbol? name) - (utils/validate symbol? params-var)]} + {:pre [(validate symbol? name) + (validate symbol? params-var)]} (let [all-keys (merge-keys keys key-sets)] `(defn ~name [{:keys ~all-keys :as ~params-var} ~@other-params] - {:pre ~(utils/vconcat pre - `[(utils/all-keys-valid? (keys ~params-var) - ~(set (map keyword all-keys)))]) + {:pre ~(vconcat pre + `[(utils/all-keys-valid? + (keys ~params-var) + ~(set (map keyword all-keys)))]) :post ~post} ~@component-body))) (defmacro def-simple-component [name parent key-sets] `(defcomponent ~name [params# & body#] {:sodium.core/key-sets ~key-sets} - (utils/vconcat [~parent (utils/camelize-map-keys params#)] - body#))) + (vconcat [~parent (camelize-map-keys params#)] + body#))) diff --git a/src/sodium/re_utils.cljs b/src/sodium/re_utils.cljs deleted file mode 100644 index 75cd85a..0000000 --- a/src/sodium/re_utils.cljs +++ /dev/null @@ -1,59 +0,0 @@ -;;; Author: David Goldfarb (deg@degel.com) -;;; Copyright (c) 2017, David Goldfarb - -(ns sodium.re-utils - (:require - [clojure.spec.alpha :as s] - [clojure.string :as str] - [re-frame.core :as re-frame] - [sodium.utils :as utils])) - - -(defn sub2 - "Shorthand for simple 'layer 2` usage of re-sub" - [key db-path] - (re-frame/reg-sub - key - (fn [db _] (get-in db db-path)))) - - -;; Ideas based on https://lambdaisland.com/blog/11-02-2017-re-frame-form-1-subscriptions - -(def >evt "Shorthand for re-frame dispatch to event." - re-frame/dispatch) - -(defn subscription re-frame/subscribe deref)) - ([subscription default] - (or (fn [vec-or-fn key-fn] - {:pre [(utils/validate (s/nilable :re-frame/vec-or-fn) vec-or-fn)] - :post (fn? %)} - (if (vector? vec-or-fn) - #(key-fn (conj vec-or-fn %)) - vec-or-fn)) - -;; [TODO] -;; - sub->fn should not expect a parameter (right?) -;; - Write doc string for sub->fn -;; - Fix the core >/< functions to use these, rather than duplicating them - - -(defn event->fn - "For contexts that want to pass an argument to a sink function: accept - either a function or a re-frame event vector. - If a vector is received, convert it to a function that dispatches to that - event, with the parameter conj'd on to the end." - [event-or-fn] - (vec->fn event-or-fn >evt)) - -(defn sub->fn [sub-or-fn] - (vec->fn sub-or-fn s count dec)) - s)) - -(defn camelize-str - "Convert a string from ClojureScript to JavaScript conventions. - - Replace hyphens with camelCase - - Remove trailing '?'" - [s] - (let [[first-word & more] (str/split (unpredicate s) "-")] - (if more - (str first-word (str/join (map str/capitalize more))) - first-word))) - -(defn camelize-key - "Convert a keyword from ClojureScript to JavaScript conventions. - - Replace hyphens with camelCase - - Remove trailing '?' - - Preserve namespace" - [k] - (keyword (namespace k) - (camelize-str (name k)))) - -(defn camelize-map-keys - "Convert a map from ClojureScript to JavaScript conventions. Change the map - keys, but leave the values alone. For convenience, you can pass in a seq - of keywords that must be excluded (left unchanged)." - [m & {:keys [exclude]}] - (reduce-kv (fn [m k v] - (assoc m - (if (some #{k} exclude) k (camelize-key k)) - v)) - {} m)) - -(defn err - "Simple helper to show an error message in Clojure or ClojureScript" - [& strings] - (apply #?(:clj (partial println "Error: ") :cljs js/console.error) strings)) - + [iron.utils :refer [err validate]])) (defn all-keys-valid? "Check that each of keys is present in universe.