0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-24 15:39:50 -05:00

:constructor: Initial work on forms refactor.

This commit is contained in:
Andrey Antukh 2019-08-26 21:41:13 +02:00
parent d147099e92
commit 263da4cc35
11 changed files with 327 additions and 355 deletions

View file

@ -7,10 +7,11 @@
environ/environ {:mvn/version "1.1.0"}
metosin/reitit-core {:mvn/version "0.3.9"}
funcool/struct {:mvn/version "1.4.0"}
funcool/beicon {:mvn/version "5.1.0"}
funcool/cuerdas {:mvn/version "2.2.0"}
funcool/lentes {:mvn/version "1.3.0-SNAPSHOT"}
funcool/potok {:mvn/version "2.4.0"}
funcool/potok {:mvn/version "2.5.0"}
funcool/promesa {:mvn/version "3.0.0-SNAPSHOT"}
funcool/rumext {:mvn/version "2.0.0-SNAPSHOT"}
}

View file

@ -9,13 +9,13 @@
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[lentes.core :as l]
[potok.core :as ptk]
[uxbox.main.repo :as rp]
[uxbox.main.store :as st]
[uxbox.util.data :refer [index-by-id]]
[uxbox.util.spec :as us]
[uxbox.util.timers :as ts]
[uxbox.util.data :refer [index-by-id]]))
[uxbox.util.uuid :as uuid]))
;; --- Specs

View file

@ -12,6 +12,7 @@
[uxbox.main.store :as st]
[uxbox.main.repo :as rp]
[uxbox.main.data.pages :as udp]
[uxbox.util.uuid :as uuid]
[uxbox.util.spec :as us]
[uxbox.util.time :as dt]
[uxbox.util.router :as rt]))

View file

@ -7,45 +7,27 @@
(ns uxbox.main.ui.auth.login
(:require
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[lentes.core :as l]
[rumext.core :as mx]
[rumext.alpha :as mf]
[uxbox.builtins.icons :as i]
[uxbox.config :as cfg]
[uxbox.main.data.auth :as da]
[uxbox.main.store :as st]
[uxbox.main.ui.messages :refer [messages-widget]]
[uxbox.main.ui.navigation :as nav]
[uxbox.util.dom :as dom]
[uxbox.util.forms :as fm]
[uxbox.util.i18n :refer (tr)]
[uxbox.util.i18n :refer [tr]]
[uxbox.util.router :as rt]))
(def form-data (fm/focus-data :login st/state))
(def form-errors (fm/focus-errors :login st/state))
(def assoc-value (partial fm/assoc-value :login))
(def assoc-errors (partial fm/assoc-errors :login))
(def clear-form (partial fm/clear-form :login))
(s/def ::username ::fm/non-empty-string)
(s/def ::password ::fm/non-empty-string)
(s/def ::login-form
(s/keys :req-un [::username ::password]))
(defn- on-change
[event field]
(let [value (dom/event->value event)]
(st/emit! (assoc-value field value))))
(def login-form-spec
{:username [fm/required fm/string fm/non-empty-string]
:password [fm/required fm/string fm/non-empty-string]})
(defn- on-submit
[event data]
[event form]
(dom/prevent-default event)
(st/emit! (da/login {:username (:username data)
:password (:password data)})))
(let [{:keys [username password]} (:clean-data form)]
(st/emit! (da/login {:username username
:password password}))))
(mf/defc demo-warning
[_]
@ -56,33 +38,37 @@
[:strong "DO NOT USE"] " for real work, " [:br]
" the projects will be periodicaly wiped."]])
(mf/defc login-form
[]
(let [data (mf/deref form-data)
valid? (fm/valid? ::login-form data)]
[:form {:on-submit #(on-submit % data)}
(let [{:keys [data] :as form} (fm/use-form {:initial {}
:spec login-form-spec})]
[:form {:on-submit #(on-submit % form)}
[:div.login-content
(when cfg/isdemo
[:& demo-warning])
[:input.input-text
{:name "email"
{:name "username"
:tab-index "2"
:value (:username data "")
:on-change #(on-change % :username)
:on-blur (fm/on-input-blur form)
:on-change (fm/on-input-change form)
:placeholder (tr "auth.email-or-username")
:type "text"}]
[:input.input-text
{:name "password"
:tab-index "3"
:value (:password data "")
:on-change #(on-change % :password)
:on-blur (fm/on-input-blur form)
:on-change (fm/on-input-change form)
:placeholder (tr "auth.password")
:type "password"}]
[:input.btn-primary
{:name "login"
:tab-index "4"
:class (when-not valid? "btn-disabled")
:disabled (not valid?)
:class (when (:errors form) "btn-disabled")
:disabled (boolean (:errors form))
:value (tr "auth.signin")
:type "submit"}]
[:div.login-links
@ -93,12 +79,8 @@
:tab-index "6"}
(tr "auth.no-account")]]]]))
;; {:mixins [mx/static (fm/clear-mixin st/store :login)]}
(mf/defc login-page
[]
(mf/use-effect (constantly #(st/emit! (fm/clear-form :login))))
[:div.login
[:div.login-body
(messages-widget)

View file

@ -17,6 +17,7 @@
[uxbox.main.ui.modal :as modal]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.confirm :refer [confirm-dialog]]
[uxbox.main.ui.dashboard.projects-forms :refer [create-project-dialog]]
[uxbox.util.data :refer [read-string]]
[uxbox.util.dom :as dom]
[uxbox.util.i18n :as t :refer [tr]]
@ -179,6 +180,7 @@
(sort-projects-by order))
on-click #(do
(dom/prevent-default %)
(modal/show! create-project-dialog {})
#_(udl/open! :create-project))]
[:section.dashboard-grid
[:h2 (tr "ds.project-title")]

View file

@ -1,151 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; 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-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.dashboard.projects-createform
(:require
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[lentes.core :as l]
[rumext.core :as mx]
[rumext.alpha :as mf]
[uxbox.builtins.icons :as i]
[uxbox.main.constants :as c]
[uxbox.main.data.lightbox :as udl]
[uxbox.main.data.projects :as udp]
[uxbox.main.store :as st]
[uxbox.main.ui.lightbox :as lbx]
[uxbox.util.data :refer [read-string parse-int]]
[uxbox.util.dom :as dom]
[uxbox.util.forms :as fm]
[uxbox.util.i18n :as t :refer [tr]]
[uxbox.util.router :as r]
[uxbox.util.time :as dt]))
(def form-data (fm/focus-data :create-project st/state))
(def form-errors (fm/focus-errors :create-project st/state))
(def assoc-value (partial fm/assoc-value :create-project))
(def clear-form (partial fm/clear-form :create-project))
(s/def ::name ::fm/non-empty-string)
(s/def ::layout ::fm/non-empty-string)
(s/def ::width number?)
(s/def ::height number?)
(s/def ::project-form
(s/keys :req-un [::name
::width
::height
::layout]))
;; --- Create Project Form
(mx/defc layout-input
[{:keys [::layout-id] :as data}]
(let [layout (get c/page-layouts layout-id)]
[:div
[:input {:type "radio"
:key layout-id
:id layout-id
:name "project-layout"
:value (:name layout)
:checked (= layout-id (:layout data))
:on-change #(st/emit! (assoc-value :layout layout-id)
(assoc-value :width (:width layout))
(assoc-value :height (:height layout)))}]
[:label {:value (:name layout)
:for layout-id}
(:name layout)]]))
(mx/defc layout-selector
[props]
[:div.input-radio.radio-primary
(layout-input (assoc props ::layout-id "mobile"))
(layout-input (assoc props ::layout-id "tablet"))
(layout-input (assoc props ::layout-id "notebook"))
(layout-input (assoc props ::layout-id "desktop"))])
(mf/def create-project-form
:mixins #{mf/reactive}
:render
(fn [own props]
(let [data (merge c/project-defaults (mf/react form-data))
valid? (fm/valid? ::project-form data)]
(letfn [(on-submit [event]
(dom/prevent-default event)
(when valid?
(st/emit! (udp/create-project data)
(udl/close-lightbox))))
(update-size [field e]
(let [value (dom/event->value e)
value (parse-int value)]
(st/emit! (assoc-value field value))))
(update-name [e]
(let [value (dom/event->value e)]
(st/emit! (assoc-value :name value))))
(swap-size []
(st/emit! (assoc-value :width (:height data))
(assoc-value :height (:width data))))]
[:form {:on-submit on-submit}
[:input#project-name.input-text
{:placeholder "New project name"
:type "text"
:value (:name data)
:auto-focus true
:on-change update-name}]
[:div.project-size
[:div.input-element.pixels
[:span "Width"]
[:input#project-witdh.input-text
{:placeholder "Width"
:type "number"
:min 0 ;;TODO check this value
:max 666666 ;;TODO check this value
:value (str (:width data))
:on-change (partial update-size :width)}]]
[:a.toggle-layout {:on-click swap-size} i/toggle]
[:div.input-element.pixels
[:span "Height"]
[:input#project-height.input-text
{:placeholder "Height"
:type "number"
:min 0 ;;TODO check this value
:max 666666 ;;TODO check this value
:value (str (:height data))
:on-change (partial update-size :height)}]]]
;; Layout selector
(layout-selector data)
;; Submit
[:input#project-btn.btn-primary
{:value "Go go go!"
:class (when-not valid? "btn-disabled")
:disabled (not valid?)
:type "submit"}]]))))
;; --- Create Project Lightbox
(mf/def create-project-lightbox
:will-unmount
(fn [own]
(st/emit! (fm/clear-form :create-project))
own)
:render
(fn [own]
[:div.lightbox-body
[:h3 "New project"]
(create-project-form)
[:a.close {:on-click #(st/emit! (udl/close-lightbox))}
i/close]]))
(defmethod lbx/render-lightbox :create-project
[_]
(create-project-lightbox))

View file

@ -0,0 +1,97 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; 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-2019 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2019 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.dashboard.projects-forms
(:require
[cljs.spec.alpha :as s]
[rumext.alpha :as mf]
[uxbox.builtins.icons :as i]
[uxbox.main.data.projects :as udp]
[uxbox.main.store :as st]
[uxbox.main.ui.modal :as modal]
[uxbox.util.dom :as dom]
[uxbox.util.forms :as fm]
[uxbox.util.i18n :as t :refer [tr]]))
(def project-form-spec
{:name [fm/required fm/string fm/non-empty-string]
:width [fm/required fm/number-str]
:height [fm/required fm/number-str]})
(def defaults
{:name ""
:width "1366"
:height "768"})
;; --- Create Project Form
(defn- on-submit
[event form]
(dom/prevent-default event)
(let [data (:clean-data form)]
(st/emit! (udp/create-project data))
(modal/hide!)))
(defn- swap-size
[event {:keys [data] :as form}]
(swap! data assoc
:width (:height data)
:height (:width data)))
(mf/defc create-project-form
[props]
(let [{:keys [data errors] :as form} (fm/use-form {:initial defaults :spec project-form-spec})]
[:form {:on-submit #(on-submit % form)}
[:input.input-text
{:placeholder "New project name"
:type "text"
:name "name"
:value (:name data)
:on-blur (fm/on-input-blur form)
:on-change (fm/on-input-change form)
:auto-focus true}]
[:div.project-size
[:div.input-element.pixels
[:span "Width"]
[:input#project-witdh.input-text
{:placeholder "Width"
:name "width"
:type "number"
:min 0
:max 5000
:on-blur (fm/on-input-blur form)
:on-change (fm/on-input-change form)
:value (:width data)}]]
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
[:div.input-element.pixels
[:span "Height"]
[:input#project-height.input-text
{:placeholder "Height"
:type "number"
:name "height"
:min 0
:max 5000
:on-blur (fm/on-input-blur form)
:on-change (fm/on-input-change form)
:value (:height data)}]]]
;; Submit
[:input#project-btn.btn-primary
{:value "Go go go!"
:class (when-not (:valid form) "btn-disabled")
:disabled (not (:valid form))
:type "submit"}]]))
;; --- Create Project Lightbox
(mf/defc create-project-dialog
[props]
[:div.lightbox-body
[:h3 "New project"]
[:& create-project-form]
[:a.close {:on-click modal/hide!} i/close]])

View file

@ -7,8 +7,8 @@
(ns uxbox.main.ui.workspace.sidebar.sitemap
(:require
;; [uxbox.main.data.lightbox :as udl]
;; [uxbox.main.ui.workspace.sidebar.sitemap-pageform]
[potok.core :as ptk]
[beicon.core :as rx]
[cuerdas.core :as str]
[lentes.core :as l]
[rumext.alpha :as mf]
@ -19,6 +19,7 @@
[uxbox.main.store :as st]
[uxbox.main.ui.confirm :refer [confirm-dialog]]
[uxbox.main.ui.modal :as modal]
[uxbox.main.ui.workspace.sidebar.sitemap-forms :refer [page-form-dialog]]
[uxbox.main.ui.workspace.sortable :refer [use-sortable]]
[uxbox.util.data :refer [classnames]]
[uxbox.util.dom :as dom]
@ -30,17 +31,15 @@
(mf/defc page-item
[{:keys [page index deletable? selected?] :as props}]
(letfn [(on-edit [event]
#_(udl/open! :page-form {:page page}))
(modal/show! page-form-dialog {:page page}))
(delete []
(let [next #(st/emit! (dp/go-to (:project page)))]
(st/emit! (udp/delete-page (:id page) next))))
(st/emit! (dw/delete-page (:id page))))
(on-delete [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(modal/show! confirm-dialog {:on-accept delete}))
(on-drop [item monitor]
(st/emit! (udp/reorder-pages (:project page))))
(st/emit! (udp/rehash-pages (:project page))))
(on-hover [item monitor]
(st/emit! (udp/move-page {:project-id (:project-id item)
:page-id (:page-id item)
@ -99,7 +98,7 @@
:fn #(-> (l/in [:projects project-id])
(l/derive st/state))})
project (mf/deref project-iref)
;; create #(udl/open! :page-form {:page {:project project-id}})
create #(modal/show! page-form-dialog {:page {:project project-id}})
close #(st/emit! (dw/toggle-flag :sitemap))]
[:div.sitemap.tool-window
[:div.tool-window-bar
@ -109,6 +108,6 @@
[:div.tool-window-content
[:div.project-title
[:span (:name project)]
[:div.add-page #_{:on-click create} i/close]]
[:div.add-page {:on-click create} i/close]]
[:& pages-list {:project project
:current-page-id current-page-id}]]]))

View file

@ -0,0 +1,103 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; 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>
(ns uxbox.main.ui.workspace.sidebar.sitemap-forms
(:require
[rumext.alpha :as mf]
[uxbox.builtins.icons :as i]
[uxbox.main.constants :as c]
[uxbox.main.data.pages :as udp]
[uxbox.main.store :as st]
[uxbox.main.ui.modal :as modal]
[uxbox.util.dom :as dom]
[uxbox.util.forms :as fm]
[uxbox.util.i18n :refer [tr]]))
(def page-form-spec
{:id [fm/uuid]
:project [fm/uuid]
:name [fm/required fm/string fm/non-empty-string]
:width [fm/required fm/number-str]
:height [fm/required fm/number-str]})
(def defaults
{:name ""
:width "1366"
:height "768"})
(defn- on-submit
[event form]
(dom/prevent-default event)
(modal/hide!)
(let [data (:clean-data form)]
(if (nil? (:id data))
(st/emit! (udp/create-page data))
(st/emit! (udp/persist-page-update-form data)))))
(defn- swap-size
[event {:keys [data] :as form}]
(swap! data assoc
:width (:height data)
:height (:width data)))
(defn- initial-data
[page]
(merge {:name "" :width "1366" :height "768"}
(select-keys page [:name :id :project])
(select-keys (:metadata page) [:width :height])))
(mf/defc page-form
[{:keys [page] :as props}]
(let [{:keys [data errors] :as form} (fm/use-form {:initial #(initial-data page)
:spec page-form-spec})]
[:form {:on-submit #(on-submit % form)}
[:input.input-text
{:placeholder "Page name"
:type "text"
:name "name"
:on-blur (fm/on-input-blur form)
:on-change (fm/on-input-change form)
:value (:name data)
:auto-focus true}]
[:div.project-size
[:div.input-element.pixels
[:span "Width"]
[:input#project-witdh.input-text
{:placeholder "Width"
:name "width"
:type "number"
:min 0
:max 5000
:on-blur (fm/on-input-blur form)
:on-change (fm/on-input-change form)
:value (:width data)}]]
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
[:div.input-element.pixels
[:span "Height"]
[:input#project-height.input-text
{:placeholder "Height"
:name "height"
:type "number"
:min 0
:max 5000
:on-blur (fm/on-input-blur form)
:on-change (fm/on-input-change form)
:value (:height data)}]]]
[:input.btn-primary
{:value "Go go go!"
:type "submit"
:disabled (not (:valid form))}]]))
(mf/defc page-form-dialog
[{:keys [page] :as props}]
[:div.lightbox-body
(if (nil? (:id page))
[:h3 "New page"]
[:h3 "Edit page"])
[:& page-form {:page page}]
[:a.close {:on-click modal/hide!} i/close]])

View file

@ -1,145 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; 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>
(ns uxbox.main.ui.workspace.sidebar.sitemap-pageform
(:require [cljs.spec.alpha :as s :include-macros true]
[lentes.core :as l]
[uxbox.builtins.icons :as i]
[uxbox.main.store :as st]
[uxbox.main.constants :as c]
[uxbox.main.data.pages :as udp]
[uxbox.main.data.lightbox :as udl]
[uxbox.main.ui.lightbox :as lbx]
[uxbox.util.data :refer [parse-int]]
[uxbox.util.dom :as dom]
[uxbox.util.forms :as fm]
[uxbox.util.i18n :refer [tr]]
[uxbox.util.router :as r]
[rumext.core :as mx :include-macros true]))
(def form-data (fm/focus-data :workspace-page-form st/state))
(def form-errors (fm/focus-errors :workspace-page-form st/state))
(def assoc-value (partial fm/assoc-value :workspace-page-form))
(def assoc-error (partial fm/assoc-error :workspace-page-form))
(def clear-form (partial fm/clear-form :workspace-page-form))
;; --- Lightbox
(s/def ::name ::fm/non-empty-string)
(s/def ::layout ::fm/non-empty-string)
(s/def ::width number?)
(s/def ::height number?)
(s/def ::page-form
(s/keys :req-un [::name
::width
::height
::layout]))
(mx/defc layout-input
[data id]
(let [{:keys [id name width height]} (get c/page-layouts id)]
(letfn [(on-change [event]
(st/emit! (assoc-value :layout id)
(assoc-value :width width)
(assoc-value :height height)))]
[:div
[:input {:type "radio"
:id id
:name "project-layout"
:value id
:checked (when (= id (:layout data)) "checked")
:on-change on-change}]
[:label {:value id :for id} name]])))
(mx/defc page-form
{:mixins [mx/static mx/reactive]}
[{:keys [metadata id] :as page}]
(let [data (merge c/page-defaults
(select-keys page [:name :id :project])
(select-keys metadata [:width :height :layout])
(mx/react form-data))
valid? (fm/valid? ::page-form data)]
(letfn [(update-size [field e]
(let [value (dom/event->value e)
value (parse-int value)]
(st/emit! (assoc-value field value))))
(update-name [e]
(let [value (dom/event->value e)]
(st/emit! (assoc-value :name value))))
(toggle-sizes []
(let [{:keys [width height]} data]
(st/emit! (assoc-value :width width)
(assoc-value :height height))))
(on-cancel [e]
(dom/prevent-default e)
(udl/close!))
(on-save [e]
(dom/prevent-default e)
(udl/close!)
(if (nil? id)
(st/emit! (udp/create-page data))
(st/emit! (udp/persist-page-update-form id data))))]
[:form
[:input#project-name.input-text
{:placeholder "Page name"
:type "text"
:value (:name data "")
:auto-focus true
:on-change update-name}]
[:div.project-size
[:div.input-element.pixels
[:span "Width"]
[:input#project-witdh.input-text
{:placeholder "Width"
:type "number"
:min 0
:max 4000
:value (:width data)
:on-change #(update-size :width %)}]]
[:a.toggle-layout {:on-click toggle-sizes} i/toggle]
[:div.input-element.pixels
[:span "Height"]
[:input#project-height.input-text
{:placeholder "Height"
:type "number"
:min 0
:max 4000
:value (:height data)
:on-change #(update-size :height %)}]]]
[:div.input-radio.radio-primary
(layout-input data "mobile")
(layout-input data "tablet")
(layout-input data "notebook")
(layout-input data "desktop")]
[:input#project-btn.btn-primary
{:value "Go go go!"
:disabled (not valid?)
:on-click on-save
:type "button"}]])))
(mx/defc page-form-lightbox
{:mixins [mx/static (fm/clear-mixin st/store :workspace-page-form)]}
[{:keys [id] :as page}]
(letfn [(on-cancel [event]
(dom/prevent-default event)
(udl/close!))]
(let [creation? (nil? id)]
[:div.lightbox-body
(if creation?
[:h3 "New page"]
[:h3 "Edit page"])
(page-form page)
[:a.close {:on-click on-cancel} i/close]])))
(defmethod lbx/render-lightbox :page-form
[{:keys [page]}]
(page-form-lightbox page))

View file

@ -5,15 +5,97 @@
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.util.forms
(:refer-clojure :exclude [uuid])
(:require
[beicon.core :as rx]
[cljs.spec.alpha :as s :include-macros true]
[cuerdas.core :as str]
[lentes.core :as l]
[potok.core :as ptk]
[rumext.core :as mx :include-macros true]
[rumext.alpha :as mf]
[rumext.core :as mx]
[struct.core :as stt]
[uxbox.util.dom :as dom]
[uxbox.util.i18n :refer [tr]]))
;; --- Main Api
(defn validate
[data spec]
(stt/validate data spec))
(defn valid?
[data spec]
(stt/valid? data spec))
;; --- Handlers Helpers
(defn- impl-mutator
[v update-fn]
(specify v
IReset
(-reset! [_ new-value]
(update-fn new-value))
ISwap
(-swap!
([self f] (update-fn f))
([self f x] (update-fn #(f % x)))
([self f x y] (update-fn #(f % x y)))
([self f x y more] (update-fn #(apply f % x y more))))))
(defn use-form
[{:keys [initial spec] :as opts}]
(let [[data update-data] (mf/useState initial)
[errors update-errors] (mf/useState nil)
[touched update-touched] (mf/useState {})
[errors' clean-data] (validate data spec)
data (impl-mutator data update-data)
errors (-> (merge {} errors' errors)
(impl-mutator update-errors))
touched (impl-mutator touched update-touched)]
{:clean-data clean-data
:touched touched
:data data
:errors errors
:valid (not (seq errors))}))
(defn on-input-change
[{:keys [data] :as form}]
(fn [event]
(let [target (dom/get-target event)
field (keyword (.-name target))
value (dom/get-value target)]
(swap! data assoc field value))))
(defn on-input-blur
[{:keys [touched] :as form}]
(fn [event]
(let [target (dom/get-target event)
field (keyword (.-name target))]
(when-not (get touched field)
(swap! touched assoc field true)))))
;; --- Additional Validators
(def non-empty-string
{:message "errors.empty-string"
:optional true
:validate #(not (str/empty? %))})
(def string (assoc stt/string :message "errors.should-be-string"))
(def number (assoc stt/number :message "errors.should-be-number"))
(def number-str (assoc stt/number-str :message "errors.should-be-number"))
(def integer (assoc stt/integer :message "errors.should-be-integer"))
(def integer-str (assoc stt/integer-str :message "errors.should-be-integer"))
(def required (assoc stt/required :message "errors.required"))
(def email (assoc stt/email :message "errors.should-be-valid-email"))
(def uuid (assoc stt/uuid :message "errors.should-be-uuid"))
(def uuid-str (assoc stt/uuid-str :message "errors.should-be-valid-uuid"))
;; DEPRECATED
;; --- Form Validation Api
(defn- interpret-problem
@ -30,15 +112,16 @@
:else acc))
(defn validate
[spec data]
(when-not (s/valid? spec data)
(let [report (s/explain-data spec data)]
(reduce interpret-problem {} (::s/problems report)))))
;; (defn validate
;; [spec data]
;; (when-not (s/valid? spec data)
;; (let [report (s/explain-data spec data)]
;; (reduce interpret-problem {} (::s/problems report)))))
;; (defn valid?
;; [spec data]
;; (s/valid? spec data))
(defn valid?
[spec data]
(s/valid? spec data))
;; --- Form Specs and Conformers