mirror of
https://github.com/penpot/penpot.git
synced 2025-05-21 09:06:11 +02:00
Replace funcool/struct with cljs.spec.
As a result, one dependency less.
This commit is contained in:
parent
6bc6ee68b6
commit
1aa236e812
12 changed files with 440 additions and 391 deletions
|
@ -2,173 +2,155 @@
|
|||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.util.forms
|
||||
(:refer-clojure :exclude [keyword uuid vector boolean map set])
|
||||
(:require [struct.core :as f]
|
||||
(:require [cljs.spec :as s :include-macros true]
|
||||
[cuerdas.core :as str]
|
||||
[lentes.core :as l]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.util.mixins :as mx :include-macros true]
|
||||
[uxbox.util.i18n :refer (tr)]))
|
||||
[uxbox.util.i18n :refer [tr]]))
|
||||
|
||||
;; TODO: rewrite form stuff using cljs.spec
|
||||
;; --- Form Validation Api
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Form Validation
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(defn- interpret-problem
|
||||
[acc {:keys [path pred val via in] :as problem}]
|
||||
(cond
|
||||
(and (empty? path)
|
||||
(= (first pred) 'contains?))
|
||||
(let [path (conj path (last pred))]
|
||||
(update-in acc path assoc :missing))
|
||||
|
||||
;; --- Form Validators
|
||||
(and (seq path)
|
||||
(= 1 (count path)))
|
||||
(update-in acc path assoc :invalid)
|
||||
|
||||
(def required
|
||||
(assoc f/required :message "errors.form.required"))
|
||||
|
||||
(def string
|
||||
(assoc f/string :message "errors.form.string"))
|
||||
|
||||
(def number
|
||||
(assoc f/number :message "errors.form.number"))
|
||||
|
||||
(def integer
|
||||
(assoc f/integer :message "errors.form.integer"))
|
||||
|
||||
(def boolean
|
||||
(assoc f/boolean :message "errors.form.bool"))
|
||||
|
||||
(def identical-to
|
||||
(assoc f/identical-to :message "errors.form.identical-to"))
|
||||
|
||||
(def in-range f/in-range)
|
||||
(def uuid f/uuid)
|
||||
(def keyword f/keyword)
|
||||
(def integer-str f/integer-str)
|
||||
(def number-str f/number-str)
|
||||
(def email f/email)
|
||||
(def positive f/positive)
|
||||
|
||||
(def max-len
|
||||
{:message "errors.form.max-len"
|
||||
:optional true
|
||||
:validate (fn [v n]
|
||||
(let [len (count v)]
|
||||
(>= len v)))})
|
||||
|
||||
(def min-len
|
||||
{:message "errors.form.min-len"
|
||||
:optional true
|
||||
:validate (fn [v n]
|
||||
(>= (count v) n))})
|
||||
|
||||
(def color
|
||||
{:message "errors.form.color"
|
||||
:optional true
|
||||
:validate #(not (nil? (re-find #"^#[0-9A-Fa-f]{6}$" %)))})
|
||||
|
||||
;; --- Public Validation Api
|
||||
:else acc))
|
||||
|
||||
(defn validate
|
||||
([data schema]
|
||||
(validate data schema nil))
|
||||
([data schema opts]
|
||||
(f/validate data schema opts)))
|
||||
|
||||
(defn validate!
|
||||
([data schema]
|
||||
(validate! data schema nil))
|
||||
([data schema opts]
|
||||
(let [[errors data] (validate data schema opts)]
|
||||
(if errors
|
||||
(throw (ex-info "Invalid data" errors))
|
||||
data))))
|
||||
[spec data]
|
||||
(when-not (s/valid? spec data)
|
||||
(let [report (s/explain-data spec data)]
|
||||
(reduce interpret-problem {} (::s/problems report)))))
|
||||
|
||||
(defn valid?
|
||||
[data schema]
|
||||
(let [[errors data] (validate data schema)]
|
||||
(not errors)))
|
||||
[spec data]
|
||||
(s/valid? spec data))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Form Events
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; --- Form Specs and Conformers
|
||||
|
||||
;; --- Set Error
|
||||
(def ^:private email-re
|
||||
#"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
|
||||
|
||||
(defrecord SetError [type field error]
|
||||
(def ^:private number-re
|
||||
#"^[-+]?[0-9]*\.?[0-9]+$")
|
||||
|
||||
(def ^:private color-re
|
||||
#"^#[0-9A-Fa-f]{6}$")
|
||||
|
||||
(s/def ::email
|
||||
(s/and string? #(boolean (re-matches email-re %))))
|
||||
|
||||
(s/def ::non-empty-string
|
||||
(s/and string? #(not (str/empty? %))))
|
||||
|
||||
(defn- parse-number
|
||||
[v]
|
||||
(cond
|
||||
(re-matches number-re v) (js/parseFloat v)
|
||||
(number? v) v
|
||||
:else ::s/invalid))
|
||||
|
||||
(s/def ::string-number
|
||||
(s/conformer parse-number str))
|
||||
|
||||
(s/def ::color
|
||||
(s/and string? #(boolean (re-matches color-re %))))
|
||||
|
||||
;; --- Form State Events
|
||||
|
||||
;; --- Assoc Error
|
||||
|
||||
(defrecord AssocError [type field error]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:errors type field] error)))
|
||||
|
||||
(defn set-error
|
||||
(defn assoc-error
|
||||
([type field]
|
||||
(set-error type field nil))
|
||||
(assoc-error type field nil))
|
||||
([type field error]
|
||||
{:pre [(keyword? type)
|
||||
(keyword? field)
|
||||
(any? error)]}
|
||||
(SetError. type field error)))
|
||||
(AssocError. type field error)))
|
||||
|
||||
(defn set-error!
|
||||
[store & args]
|
||||
(ptk/emit! store (apply set-error args)))
|
||||
;; --- Assoc Errors
|
||||
|
||||
;; --- Set Errors
|
||||
|
||||
(defrecord SetErrors [type errors]
|
||||
(defrecord AssocErrors [type errors]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:errors type] errors)))
|
||||
|
||||
(defn set-errors
|
||||
(defn assoc-errors
|
||||
([type]
|
||||
(set-errors type nil))
|
||||
(assoc-errors type nil))
|
||||
([type errors]
|
||||
{:pre [(keyword? type)
|
||||
(or (map? errors)
|
||||
(nil? errors))]}
|
||||
(SetErrors. type errors)))
|
||||
(AssocErrors. type errors)))
|
||||
|
||||
(defn set-errors!
|
||||
[store & args]
|
||||
(ptk/emit! store (apply set-errors args)))
|
||||
;; --- Assoc Value
|
||||
|
||||
;; --- Set Value
|
||||
(declare clear-error)
|
||||
|
||||
(defrecord SetValue [type field value]
|
||||
(defrecord AssocValue [type field value]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [form-path (into [:forms type] (if (coll? field) field [field]))
|
||||
errors-path (into [:errors type] (if (coll? field) field [field]))]
|
||||
(-> state
|
||||
(assoc-in form-path value)
|
||||
(update-in (butlast errors-path) dissoc (last errors-path))))))
|
||||
(let [form-path (into [:forms type] (if (coll? field) field [field]))]
|
||||
(assoc-in state form-path value)))
|
||||
|
||||
(defn set-value
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (clear-error type field))))
|
||||
|
||||
(defn assoc-value
|
||||
[type field value]
|
||||
{:pre [(keyword? type)
|
||||
(keyword? field)
|
||||
(any? value)]}
|
||||
(SetValue. type field value))
|
||||
(AssocValue. type field value))
|
||||
|
||||
(defn set-value!
|
||||
[store type field value]
|
||||
(ptk/emit! store (set-value type field value)))
|
||||
;; --- Clear Values
|
||||
|
||||
;; --- Clear Form
|
||||
|
||||
(defrecord ClearForm [type]
|
||||
(defrecord ClearValues [type]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:forms type] nil)))
|
||||
|
||||
(defn clear-form
|
||||
(defn clear-values
|
||||
[type]
|
||||
{:pre [(keyword? type)]}
|
||||
(ClearForm. type))
|
||||
(ClearValues. type))
|
||||
|
||||
(defn clear-form!
|
||||
[store type]
|
||||
(ptk/emit! store (clear-form type)))
|
||||
;; --- Clear Error
|
||||
|
||||
(deftype ClearError [type field]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [errors (get-in state [:errors type])]
|
||||
(if (map? errors)
|
||||
(assoc-in state [:errors type] (dissoc errors field))
|
||||
(update state :errors dissoc type)))))
|
||||
|
||||
(defn clear-error
|
||||
[type field]
|
||||
{:pre [(keyword? type)
|
||||
(keyword? field)]}
|
||||
(ClearError. type field))
|
||||
|
||||
;; --- Clear Errors
|
||||
|
||||
|
@ -182,17 +164,18 @@
|
|||
{:pre [(keyword? type)]}
|
||||
(ClearErrors. type))
|
||||
|
||||
(defn clear-errors!
|
||||
[store type]
|
||||
(ptk/emit! store (clear-errors type)))
|
||||
;; --- Clear Form
|
||||
|
||||
;; --- Clear
|
||||
(deftype ClearForm [type]
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (clear-values type)
|
||||
(clear-errors type))))
|
||||
|
||||
(defn clear!
|
||||
[store type]
|
||||
(ptk/emit! store
|
||||
(clear-form type)
|
||||
(clear-errors type)))
|
||||
(defn clear-form
|
||||
[type]
|
||||
{:pre [(keyword? type)]}
|
||||
(ClearForm. type))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
|
@ -224,5 +207,5 @@
|
|||
(defn clear-mixin
|
||||
[store type]
|
||||
{:will-unmount (fn [own]
|
||||
(clear! store type)
|
||||
(ptk/emit! store (clear-form type))
|
||||
own)})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue