mirror of
https://github.com/penpot/penpot.git
synced 2025-05-14 19:26:38 +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
|
@ -10,7 +10,7 @@
|
||||||
:profiles {:dev {:source-paths ["dev"]}}
|
:profiles {:dev {:source-paths ["dev"]}}
|
||||||
|
|
||||||
:dependencies [[org.clojure/clojure "1.9.0-alpha14" :scope "provided"]
|
:dependencies [[org.clojure/clojure "1.9.0-alpha14" :scope "provided"]
|
||||||
[org.clojure/clojurescript "1.9.494" :scope "provided"]
|
[org.clojure/clojurescript "1.9.495" :scope "provided"]
|
||||||
|
|
||||||
;; Build
|
;; Build
|
||||||
[figwheel-sidecar "0.5.9" :scope "provided"]
|
[figwheel-sidecar "0.5.9" :scope "provided"]
|
||||||
|
@ -24,12 +24,11 @@
|
||||||
[cljsjs/react-dom "15.4.2-2"]
|
[cljsjs/react-dom "15.4.2-2"]
|
||||||
[cljsjs/react-dom-server "15.4.2-2"]
|
[cljsjs/react-dom-server "15.4.2-2"]
|
||||||
|
|
||||||
[funcool/potok "2.0.0"]
|
|
||||||
[funcool/struct "1.0.0"]
|
|
||||||
[funcool/lentes "1.2.0"]
|
|
||||||
[funcool/beicon "3.1.1"]
|
[funcool/beicon "3.1.1"]
|
||||||
|
[funcool/bide "1.4.0"]
|
||||||
[funcool/cuerdas "2.0.3"]
|
[funcool/cuerdas "2.0.3"]
|
||||||
[funcool/bide "1.4.0"]]
|
[funcool/lentes "1.2.0"]
|
||||||
|
[funcool/potok "2.0.0"]]
|
||||||
:plugins [[lein-ancient "0.6.10"]]
|
:plugins [[lein-ancient "0.6.10"]]
|
||||||
:clean-targets ^{:protect false} ["resources/public/js" "target"]
|
:clean-targets ^{:protect false} ["resources/public/js" "target"]
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,11 +13,6 @@
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer (tr)]
|
||||||
[uxbox.util.messages :as uum]))
|
[uxbox.util.messages :as uum]))
|
||||||
|
|
||||||
(s/def ::fullname string?)
|
|
||||||
(s/def ::email us/email?)
|
|
||||||
(s/def ::username string?)
|
|
||||||
(s/def ::theme string?)
|
|
||||||
|
|
||||||
;; --- Profile Fetched
|
;; --- Profile Fetched
|
||||||
|
|
||||||
(deftype ProfileFetched [data]
|
(deftype ProfileFetched [data]
|
||||||
|
@ -68,48 +63,53 @@
|
||||||
(rx/map profile-updated)
|
(rx/map profile-updated)
|
||||||
(rx/catch rp/client-error? handle-error)))))
|
(rx/catch rp/client-error? handle-error)))))
|
||||||
|
|
||||||
(s/def ::update-profile-event
|
(s/def ::fullname string?)
|
||||||
(s/keys :req-un [::fullname ::email ::username ::theme]))
|
(s/def ::email us/email?)
|
||||||
|
(s/def ::username string?)
|
||||||
|
(s/def ::theme string?)
|
||||||
|
|
||||||
|
(s/def ::update-profile
|
||||||
|
(s/keys :req-un [::fullname
|
||||||
|
::email
|
||||||
|
::username
|
||||||
|
::theme]))
|
||||||
|
|
||||||
(defn update-profile
|
(defn update-profile
|
||||||
[data on-success on-error]
|
[data on-success on-error]
|
||||||
{:pre [(us/valid? ::update-profile-event data)
|
{:pre [(us/valid? ::update-profile data)
|
||||||
(fn? on-error)
|
(fn? on-error)
|
||||||
(fn? on-success)]}
|
(fn? on-success)]}
|
||||||
(UpdateProfile. data on-success on-error))
|
(UpdateProfile. data on-success on-error))
|
||||||
|
|
||||||
;; --- Password Updated
|
|
||||||
|
|
||||||
(deftype PasswordUpdated []
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(rx/of (uum/info (tr "settings.password-saved")))))
|
|
||||||
|
|
||||||
(defn password-updated
|
|
||||||
[]
|
|
||||||
(PasswordUpdated.))
|
|
||||||
|
|
||||||
;; --- Update Password (Form)
|
;; --- Update Password (Form)
|
||||||
|
|
||||||
(deftype UpdatePassword [data]
|
(deftype UpdatePassword [data on-success on-error]
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state s]
|
(watch [_ state s]
|
||||||
(let [params {:old-password (:old-password data)
|
(let [params {:old-password (:password-old data)
|
||||||
:password (:password-1 data)}]
|
:password (:password-1 data)}]
|
||||||
(->> (rp/req :update/profile-password params)
|
(->> (rp/req :update/profile-password params)
|
||||||
(rx/map password-updated)))))
|
(rx/catch rp/client-error? (fn [e]
|
||||||
|
(on-error (:payload e))
|
||||||
|
(rx/empty)))
|
||||||
|
(rx/do on-success)
|
||||||
|
(rx/ignore)))))
|
||||||
|
|
||||||
(s/def ::password-1 string?)
|
(s/def ::password-1 string?)
|
||||||
(s/def ::password-2 string?)
|
(s/def ::password-2 string?)
|
||||||
(s/def ::old-password string?)
|
(s/def ::password-old string?)
|
||||||
|
|
||||||
(s/def ::update-password-event
|
(s/def ::update-password
|
||||||
(s/keys :req-un [::password-1 ::password-2 ::old-password]))
|
(s/keys :req-un [::password-1
|
||||||
|
::password-2
|
||||||
|
::password-old]))
|
||||||
|
|
||||||
(defn update-password
|
(defn update-password
|
||||||
[data]
|
[data & {:keys [on-success on-error]}]
|
||||||
{:pre [(us/valid? ::update-password-event data)]}
|
{:pre [(us/valid? ::update-password data)
|
||||||
(UpdatePassword. data))
|
(fn? on-success)
|
||||||
|
(fn? on-error)]}
|
||||||
|
(UpdatePassword. data on-success on-error))
|
||||||
|
|
||||||
;; --- Update Photo
|
;; --- Update Photo
|
||||||
|
|
||||||
|
@ -123,5 +123,6 @@
|
||||||
(defn update-photo
|
(defn update-photo
|
||||||
([file] (update-photo file (constantly nil)))
|
([file] (update-photo file (constantly nil)))
|
||||||
([file done]
|
([file done]
|
||||||
{:pre [(us/file? file) (fn? done)]}
|
{:pre [(us/file? file)
|
||||||
|
(fn? done)]}
|
||||||
(UpdatePhoto. file done)))
|
(UpdatePhoto. file done)))
|
||||||
|
|
|
@ -6,38 +6,45 @@
|
||||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.login
|
(ns uxbox.main.ui.auth.login
|
||||||
(:require [lentes.core :as l]
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
|
[lentes.core :as l]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]
|
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.data.auth :as da]
|
[uxbox.main.data.auth :as da]
|
||||||
[uxbox.main.ui.messages :refer [messages-widget]]
|
[uxbox.main.ui.messages :refer [messages-widget]]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
[uxbox.main.ui.navigation :as nav]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
|
||||||
[uxbox.util.router :as rt]
|
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.forms :as forms]))
|
[uxbox.util.forms :as fm]
|
||||||
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
|
[uxbox.util.router :as rt]))
|
||||||
|
|
||||||
(def form-data (forms/focus-data :login st/state))
|
(def form-data (fm/focus-data :login st/state))
|
||||||
(def set-value! (partial forms/set-value! st/store :login))
|
(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]))
|
||||||
|
|
||||||
(def +login-form+
|
|
||||||
{:email [forms/required forms/string]
|
|
||||||
:password [forms/required forms/string]})
|
|
||||||
|
|
||||||
(mx/defc login-form
|
(mx/defc login-form
|
||||||
{:mixins [mx/static mx/reactive]}
|
{:mixins [mx/static mx/reactive]}
|
||||||
[]
|
[]
|
||||||
(let [data (mx/react form-data)
|
(let [data (mx/react form-data)
|
||||||
valid? (forms/valid? data +login-form+)]
|
valid? (fm/valid? ::login-form data)]
|
||||||
(letfn [(on-change [event field]
|
(letfn [(on-change [event field]
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(set-value! field value)))
|
(st/emit! (assoc-value field value))))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (da/login {:username (:email data)
|
(st/emit! (da/login {:username (:username data)
|
||||||
:password (:password data)})))]
|
:password (:password data)})))]
|
||||||
[:form {:on-submit on-submit}
|
[:form {:on-submit on-submit}
|
||||||
[:div.login-content
|
[:div.login-content
|
||||||
|
@ -52,8 +59,8 @@
|
||||||
{:name "email"
|
{:name "email"
|
||||||
:tab-index "2"
|
:tab-index "2"
|
||||||
:ref "email"
|
:ref "email"
|
||||||
:value (:email data "")
|
:value (:username data "")
|
||||||
:on-change #(on-change % :email)
|
:on-change #(on-change % :username)
|
||||||
:placeholder "Email or Username"
|
:placeholder "Email or Username"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
|
@ -80,7 +87,7 @@
|
||||||
"Don't have an account?"]]]])))
|
"Don't have an account?"]]]])))
|
||||||
|
|
||||||
(mx/defc login-page
|
(mx/defc login-page
|
||||||
{:mixins [mx/static (forms/clear-mixin st/store :login)]
|
{:mixins [mx/static (fm/clear-mixin st/store :login)]
|
||||||
:will-mount (fn [own]
|
:will-mount (fn [own]
|
||||||
(when @st/auth-ref
|
(when @st/auth-ref
|
||||||
(st/emit! (rt/navigate :dashboard/projects)))
|
(st/emit! (rt/navigate :dashboard/projects)))
|
||||||
|
|
|
@ -6,42 +6,44 @@
|
||||||
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.recovery
|
(ns uxbox.main.ui.auth.recovery
|
||||||
(:require [lentes.core :as l]
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
|
[lentes.core :as l]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]
|
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.data.auth :as uda]
|
[uxbox.main.data.auth :as uda]
|
||||||
[uxbox.main.ui.messages :refer [messages-widget]]
|
[uxbox.main.ui.messages :refer [messages-widget]]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
[uxbox.main.ui.navigation :as nav]
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.forms :as forms]
|
[uxbox.util.forms :as fm]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.router :as rt]))
|
||||||
|
|
||||||
|
(def form-data (fm/focus-data :recovery st/state))
|
||||||
|
(def form-errors (fm/focus-errors :recovery st/state))
|
||||||
|
|
||||||
|
(def assoc-value (partial fm/assoc-value :recovery))
|
||||||
|
(def assoc-errors (partial fm/assoc-errors :recovery))
|
||||||
|
(def clear-form (partial fm/clear-form :recovery))
|
||||||
|
|
||||||
;; --- Recovery Form
|
;; --- Recovery Form
|
||||||
|
|
||||||
(def form-data (forms/focus-data :recovery st/state))
|
(s/def ::password ::fm/non-empty-string)
|
||||||
(def set-value! (partial forms/set-value! st/store :recovery))
|
(s/def ::recovery-form
|
||||||
|
(s/keys :req-un [::password]))
|
||||||
(def +recovery-form+
|
|
||||||
{:password [forms/required forms/string]})
|
|
||||||
|
|
||||||
(mx/defc recovery-form
|
(mx/defc recovery-form
|
||||||
{:mixins [mx/static mx/reactive]}
|
{:mixins [mx/static mx/reactive]}
|
||||||
[token]
|
[token]
|
||||||
(let [data (merge (mx/react form-data)
|
(let [data (merge (mx/react form-data) {:token token})
|
||||||
{:token token})
|
valid? (fm/valid? ::recovery-form data)]
|
||||||
valid? (forms/valid? data +recovery-form+)]
|
|
||||||
(letfn [(on-change [field event]
|
(letfn [(on-change [field event]
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(set-value! field value)))
|
(st/emit! (assoc-value field value))))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (uda/recovery data)
|
(st/emit! (uda/recovery data)
|
||||||
(forms/clear-form :recovery)
|
(clear-form)))]
|
||||||
(forms/clear-errors :recovery)))]
|
|
||||||
[:form {:on-submit on-submit}
|
[:form {:on-submit on-submit}
|
||||||
[:div.login-content
|
[:div.login-content
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
|
@ -68,7 +70,7 @@
|
||||||
own))
|
own))
|
||||||
|
|
||||||
(mx/defc recovery-page
|
(mx/defc recovery-page
|
||||||
{:mixins [mx/static (forms/clear-mixin st/store :recovery)]
|
{:mixins [mx/static (fm/clear-mixin st/store :recovery)]
|
||||||
:will-mount recovery-page-will-mount}
|
:will-mount recovery-page-will-mount}
|
||||||
[token]
|
[token]
|
||||||
[:div.login
|
[:div.login
|
||||||
|
|
|
@ -6,45 +6,47 @@
|
||||||
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.recovery-request
|
(ns uxbox.main.ui.auth.recovery-request
|
||||||
(:require [lentes.core :as l]
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
|
[lentes.core :as l]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.data.auth :as uda]
|
[uxbox.main.data.auth :as uda]
|
||||||
[uxbox.builtins.icons :as i]
|
|
||||||
[uxbox.main.ui.messages :refer [messages-widget]]
|
[uxbox.main.ui.messages :refer [messages-widget]]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
[uxbox.main.ui.navigation :as nav]
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.forms :as forms]
|
[uxbox.util.forms :as fm]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.router :as rt]))
|
||||||
|
|
||||||
|
(def form-data (fm/focus-data :recovery-request st/state))
|
||||||
|
(def form-errors (fm/focus-errors :recovery-request st/state))
|
||||||
|
|
||||||
(def form-data (forms/focus-data :recovery-request st/state))
|
(def assoc-value (partial fm/assoc-value :profile-password))
|
||||||
(def set-value! (partial forms/set-value! st/store :recovery-request))
|
(def assoc-errors (partial fm/assoc-errors :profile-password))
|
||||||
|
(def clear-form (partial fm/clear-form :profile-password))
|
||||||
|
|
||||||
(def +recovery-request-form+
|
(s/def ::username ::fm/non-empty-string)
|
||||||
{:username [forms/required forms/string]})
|
(s/def ::recovery-request-form (s/keys :req-un [::username]))
|
||||||
|
|
||||||
(mx/defc recovery-request-form
|
(mx/defc recovery-request-form
|
||||||
{:mixins [mx/static mx/reactive]}
|
{:mixins [mx/static mx/reactive]}
|
||||||
[]
|
[]
|
||||||
(let [data (mx/react form-data)
|
(let [data (mx/react form-data)
|
||||||
valid? (forms/valid? data +recovery-request-form+)]
|
valid? (fm/valid? ::recovery-request-form data)]
|
||||||
(letfn [(on-change [field event]
|
(letfn [(on-change [event]
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(set-value! field value)))
|
(st/emit! (assoc-value :username value))))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (uda/recovery-request data)
|
(st/emit! (uda/recovery-request data)
|
||||||
(forms/clear-form :recovery-request)
|
(clear-form)))]
|
||||||
(forms/clear-errors :recovery-request)))]
|
|
||||||
[:form {:on-submit on-submit}
|
[:form {:on-submit on-submit}
|
||||||
[:div.login-content
|
[:div.login-content
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "username"
|
{:name "username"
|
||||||
:value (:username data "")
|
:value (:username data "")
|
||||||
:on-change (partial on-change :username)
|
:on-change on-change
|
||||||
:placeholder "username or email address"
|
:placeholder "username or email address"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
|
@ -59,7 +61,7 @@
|
||||||
;; --- Recovery Request Page
|
;; --- Recovery Request Page
|
||||||
|
|
||||||
(mx/defc recovery-request-page
|
(mx/defc recovery-request-page
|
||||||
{:mixins [mx/static (forms/clear-mixin st/store :recovery-request)]}
|
{:mixins [mx/static (fm/clear-mixin st/store :recovery-request)]}
|
||||||
[]
|
[]
|
||||||
[:div.login
|
[:div.login
|
||||||
[:div.login-body
|
[:div.login-body
|
||||||
|
|
|
@ -2,52 +2,59 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.register
|
(ns uxbox.main.ui.auth.register
|
||||||
(:require [lentes.core :as l]
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
|
[lentes.core :as l]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.data.auth :as uda]
|
[uxbox.main.data.auth :as uda]
|
||||||
[uxbox.builtins.icons :as i]
|
|
||||||
[uxbox.main.ui.messages :refer [messages-widget]]
|
[uxbox.main.ui.messages :refer [messages-widget]]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
[uxbox.main.ui.navigation :as nav]
|
||||||
[uxbox.util.router :as rt]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.forms :as forms]
|
[uxbox.util.forms :as fm]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.router :as rt]))
|
||||||
|
|
||||||
;; --- Register Form
|
(def form-data (fm/focus-data :register st/state))
|
||||||
|
(def form-errors (fm/focus-errors :register st/state))
|
||||||
|
|
||||||
(def form-data (forms/focus-data :register st/state))
|
(def assoc-value (partial fm/assoc-value :register))
|
||||||
(def form-errors (forms/focus-errors :register st/state))
|
(def assoc-error (partial fm/assoc-error :register))
|
||||||
(def set-value! (partial forms/set-value! st/store :register))
|
(def clear-form (partial fm/clear-form :register))
|
||||||
(def set-error! (partial forms/set-error! st/store :register))
|
|
||||||
|
|
||||||
(def +register-form+
|
;; TODO: add better password validation
|
||||||
{:username [forms/required forms/string]
|
|
||||||
:fullname [forms/required forms/string]
|
(s/def ::username ::fm/non-empty-string)
|
||||||
:email [forms/required forms/email]
|
(s/def ::fullname ::fm/non-empty-string)
|
||||||
:password [forms/required forms/string]})
|
(s/def ::password ::fm/non-empty-string)
|
||||||
|
(s/def ::email ::fm/email)
|
||||||
|
|
||||||
|
(s/def ::register-form
|
||||||
|
(s/keys :req-un [::username
|
||||||
|
::fullname
|
||||||
|
::email
|
||||||
|
::password]))
|
||||||
|
|
||||||
(mx/defc register-form
|
(mx/defc register-form
|
||||||
{:mixins [mx/static mx/reactive
|
{:mixins [mx/static mx/reactive
|
||||||
(forms/clear-mixin st/store :register)]}
|
(fm/clear-mixin st/store :register)]}
|
||||||
[]
|
[]
|
||||||
(let [data (mx/react form-data)
|
(let [data (mx/react form-data)
|
||||||
errors (mx/react form-errors)
|
errors (mx/react form-errors)
|
||||||
valid? (forms/valid? data +register-form+)]
|
valid? (fm/valid? ::register-form data)]
|
||||||
(letfn [(on-change [field event]
|
(letfn [(on-change [field event]
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(set-value! field value)))
|
(st/emit! (assoc-value field value))))
|
||||||
(on-error [{:keys [type code] :as payload}]
|
(on-error [{:keys [type code] :as payload}]
|
||||||
(case code
|
(case code
|
||||||
:uxbox.services.users/email-already-exists
|
:uxbox.services.users/email-already-exists
|
||||||
(set-error! :email "Email already exists")
|
(st/emit! (assoc-error :email "Email already exists"))
|
||||||
:uxbox.services.users/username-already-exists
|
:uxbox.services.users/username-already-exists
|
||||||
(set-error! :username "Username already exists")))
|
(st/emit! (assoc-error :username "Username already exists"))))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (uda/register data on-error)))]
|
(st/emit! (uda/register data on-error)))]
|
||||||
|
@ -60,7 +67,7 @@
|
||||||
:on-change (partial on-change :fullname)
|
:on-change (partial on-change :fullname)
|
||||||
:placeholder "Full Name"
|
:placeholder "Full Name"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
(forms/input-error errors :fullname)
|
(fm/input-error errors :fullname)
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "username"
|
{:name "username"
|
||||||
|
@ -69,7 +76,7 @@
|
||||||
:on-change (partial on-change :username)
|
:on-change (partial on-change :username)
|
||||||
:placeholder "Username"
|
:placeholder "Username"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
(forms/input-error errors :username)
|
(fm/input-error errors :username)
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "email"
|
{:name "email"
|
||||||
|
@ -79,7 +86,7 @@
|
||||||
:on-change (partial on-change :email)
|
:on-change (partial on-change :email)
|
||||||
:placeholder "Email"
|
:placeholder "Email"
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
(forms/input-error errors :email)
|
(fm/input-error errors :email)
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "password"
|
{:name "password"
|
||||||
|
@ -89,7 +96,7 @@
|
||||||
:on-change (partial on-change :password)
|
:on-change (partial on-change :password)
|
||||||
:placeholder "Password"
|
:placeholder "Password"
|
||||||
:type "password"}]
|
:type "password"}]
|
||||||
(forms/input-error errors :password)
|
(fm/input-error errors :password)
|
||||||
|
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:name "login"
|
{:name "login"
|
||||||
|
|
|
@ -209,7 +209,7 @@
|
||||||
(sort-projects-by ordering))]
|
(sort-projects-by ordering))]
|
||||||
(letfn [(on-click [e]
|
(letfn [(on-click [e]
|
||||||
(dom/prevent-default e)
|
(dom/prevent-default e)
|
||||||
(udl/open! :new-project))]
|
(udl/open! :create-project))]
|
||||||
[:section.dashboard-grid
|
[:section.dashboard-grid
|
||||||
[:h2 "Your projects"]
|
[:h2 "Your projects"]
|
||||||
[:div.dashboard-grid-content
|
[:div.dashboard-grid-content
|
||||||
|
|
|
@ -6,42 +6,44 @@
|
||||||
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.dashboard.projects-createform
|
(ns uxbox.main.ui.dashboard.projects-createform
|
||||||
(:require [lentes.core :as l]
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
|
[lentes.core :as l]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.exports :as exports]
|
|
||||||
[uxbox.main.data.projects :as udp]
|
[uxbox.main.data.projects :as udp]
|
||||||
[uxbox.main.data.lightbox :as udl]
|
[uxbox.main.data.lightbox :as udl]
|
||||||
[uxbox.builtins.icons :as i]
|
|
||||||
[uxbox.main.ui.dashboard.header :refer [header]]
|
|
||||||
[uxbox.main.ui.lightbox :as lbx]
|
[uxbox.main.ui.lightbox :as lbx]
|
||||||
[uxbox.main.ui.keyboard :as kbd]
|
[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.i18n :as t :refer [tr]]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.util.forms :as forms]
|
|
||||||
[uxbox.util.data :refer [read-string]]
|
|
||||||
[uxbox.util.dom :as dom]
|
|
||||||
[uxbox.util.blob :as blob]
|
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.time :as dt]))
|
[uxbox.util.time :as dt]))
|
||||||
|
|
||||||
(def form-data (forms/focus-data :create-project st/state))
|
(def form-data (fm/focus-data :create-project st/state))
|
||||||
(def form-errors (forms/focus-errors :create-project st/state))
|
(def form-errors (fm/focus-errors :create-project st/state))
|
||||||
(def set-value! (partial forms/set-value! st/store :create-project))
|
|
||||||
(def set-error! (partial forms/set-error! st/store :create-project))
|
|
||||||
(def clear! (partial forms/clear! st/store :create-project))
|
|
||||||
|
|
||||||
(def ^:private create-project-form
|
(def assoc-value (partial fm/assoc-value :create-project))
|
||||||
{:name [forms/required forms/string]
|
(def clear-form (partial fm/clear-form :create-project))
|
||||||
:width [forms/required forms/integer]
|
|
||||||
:height [forms/required forms/integer]
|
|
||||||
:layout [forms/required forms/string]})
|
|
||||||
|
|
||||||
;; --- Lightbox: Layout input
|
(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
|
(mx/defc layout-input
|
||||||
|
{:mixins [mx/static]}
|
||||||
[data layout-id]
|
[data layout-id]
|
||||||
(let [layout (get c/page-layouts layout-id)]
|
(let [layout (get c/page-layouts layout-id)]
|
||||||
[:div
|
[:div
|
||||||
|
@ -51,17 +53,15 @@
|
||||||
:name "project-layout"
|
:name "project-layout"
|
||||||
:value (:name layout)
|
:value (:name layout)
|
||||||
:checked (when (= layout-id (:layout data)) "checked")
|
:checked (when (= layout-id (:layout data)) "checked")
|
||||||
:on-change #(do
|
:on-change #(st/emit! (assoc-value :layout layout-id)
|
||||||
(set-value! :layout layout-id)
|
(assoc-value :width (:width layout))
|
||||||
(set-value! :width (:width layout))
|
(assoc-value :height (:height layout)))}]
|
||||||
(set-value! :height (:height layout)))}]
|
|
||||||
[:label {:value (:name layout)
|
[:label {:value (:name layout)
|
||||||
:for layout-id}
|
:for layout-id}
|
||||||
(:name layout)]]))
|
(:name layout)]]))
|
||||||
|
|
||||||
;; --- Lightbox: Layout selector
|
|
||||||
|
|
||||||
(mx/defc layout-selector
|
(mx/defc layout-selector
|
||||||
|
{:mixins [mx/static]}
|
||||||
[data]
|
[data]
|
||||||
[:div.input-radio.radio-primary
|
[:div.input-radio.radio-primary
|
||||||
(layout-input data "mobile")
|
(layout-input data "mobile")
|
||||||
|
@ -69,70 +69,84 @@
|
||||||
(layout-input data "notebook")
|
(layout-input data "notebook")
|
||||||
(layout-input data "desktop")])
|
(layout-input data "desktop")])
|
||||||
|
|
||||||
;; -- New Project Lightbox
|
(mx/defc create-project-form
|
||||||
|
{:mixins [mx/reactive mx/static]}
|
||||||
(mx/defcs new-project-lightbox
|
[]
|
||||||
{:mixins [mx/static mx/reactive
|
|
||||||
(forms/clear-mixin st/store :create-project)]}
|
|
||||||
[own]
|
|
||||||
(let [data (merge c/project-defaults (mx/react form-data))
|
(let [data (merge c/project-defaults (mx/react form-data))
|
||||||
errors (mx/react form-errors)
|
errors (mx/react form-errors)
|
||||||
valid? (forms/valid? data create-project-form)]
|
valid? (fm/valid? ::project-form data)]
|
||||||
|
(println data)
|
||||||
|
(println valid?)
|
||||||
(letfn [(on-submit [event]
|
(letfn [(on-submit [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(when valid?
|
(when valid?
|
||||||
(st/emit! (udp/create-project data))
|
(st/emit! (udp/create-project data))
|
||||||
(udl/close!)))
|
(udl/close!)))
|
||||||
(set-value [event attr]
|
|
||||||
(set-value! attr (dom/event->value event)))
|
(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 []
|
(swap-size []
|
||||||
(set-value! :width (:height data))
|
(st/emit! (assoc-value :width (:height data))
|
||||||
(set-value! :height (:width data)))
|
(assoc-value :height (:width data))))]
|
||||||
(close []
|
[:form {:on-submit on-submit}
|
||||||
(udl/close!)
|
[:input#project-name.input-text
|
||||||
(clear!))]
|
{:placeholder "New project name"
|
||||||
[:div.lightbox-body
|
:type "text"
|
||||||
[:h3 "New project"]
|
:value (:name data)
|
||||||
[:form {:on-submit on-submit}
|
:auto-focus true
|
||||||
[:input#project-name.input-text
|
:on-change update-name}]
|
||||||
{:placeholder "New project name"
|
[:div.project-size
|
||||||
:type "text"
|
[:div.input-element.pixels
|
||||||
:value (:name data)
|
[:span "Width"]
|
||||||
:auto-focus true
|
[:input#project-witdh.input-text
|
||||||
:on-change #(set-value % :name)}]
|
{:placeholder "Width"
|
||||||
[:div.project-size
|
:type "number"
|
||||||
[:div.input-element.pixels
|
:min 0 ;;TODO check this value
|
||||||
[:span "Width"]
|
:max 666666 ;;TODO check this value
|
||||||
[:input#project-witdh.input-text
|
:value (:width data)
|
||||||
{:placeholder "Width"
|
:on-change (partial update-size :width)}]]
|
||||||
:type "number"
|
[:a.toggle-layout {:on-click swap-size} i/toggle]
|
||||||
:min 0 ;;TODO check this value
|
[:div.input-element.pixels
|
||||||
:max 666666 ;;TODO check this value
|
[:span "Height"]
|
||||||
:value (:width data)
|
[:input#project-height.input-text
|
||||||
:on-change #(set-value % :width)}]]
|
{:placeholder "Height"
|
||||||
[:a.toggle-layout {:on-click swap-size} i/toggle]
|
:type "number"
|
||||||
[:div.input-element.pixels
|
:min 0 ;;TODO check this value
|
||||||
[:span "Height"]
|
:max 666666 ;;TODO check this value
|
||||||
[:input#project-height.input-text
|
:value (:height data)
|
||||||
{:placeholder "Height"
|
:on-change (partial update-size :height)}]]]
|
||||||
:type "number"
|
|
||||||
:min 0 ;;TODO check this value
|
|
||||||
:max 666666 ;;TODO check this value
|
|
||||||
:value (:height data)
|
|
||||||
:on-change #(set-value % :height)}]]]
|
|
||||||
|
|
||||||
;; Layout selector
|
;; Layout selector
|
||||||
(layout-selector data)
|
(layout-selector data)
|
||||||
|
|
||||||
;; Submit
|
;; Submit
|
||||||
[:input#project-btn.btn-primary
|
[:input#project-btn.btn-primary
|
||||||
{:value "Go go go!"
|
{:value "Go go go!"
|
||||||
:class (when-not valid? "btn-disabled")
|
:class (when-not valid? "btn-disabled")
|
||||||
:disabled (not valid?)
|
:disabled (not valid?)
|
||||||
:type "submit"}]]
|
:type "submit"}]])))
|
||||||
[:a.close {:on-click #(udl/close!)} i/close]])))
|
|
||||||
|
|
||||||
(defmethod lbx/render-lightbox :new-project
|
;; --- Create Project Lightbox
|
||||||
|
|
||||||
|
(mx/defcs create-project-lightbox
|
||||||
|
{:mixins [mx/static mx/reactive
|
||||||
|
(fm/clear-mixin st/store :create-project)]}
|
||||||
|
[own]
|
||||||
|
(letfn [(close []
|
||||||
|
(udl/close!)
|
||||||
|
(st/emit! (clear-form)))]
|
||||||
|
[:div.lightbox-body
|
||||||
|
[:h3 "New project"]
|
||||||
|
(create-project-form)
|
||||||
|
[:a.close {:on-click #(udl/close!)} i/close]]))
|
||||||
|
|
||||||
|
(defmethod lbx/render-lightbox :create-project
|
||||||
[_]
|
[_]
|
||||||
(new-project-lightbox))
|
(create-project-lightbox))
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2016-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.settings.password
|
(ns uxbox.main.ui.settings.password
|
||||||
(:require [lentes.core :as l]
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
|
[lentes.core :as l]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
|
@ -14,57 +15,75 @@
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.ui.messages :refer [messages-widget]]
|
[uxbox.main.ui.messages :refer [messages-widget]]
|
||||||
[uxbox.main.ui.settings.header :refer [header]]
|
[uxbox.main.ui.settings.header :refer [header]]
|
||||||
[uxbox.util.forms :as forms]
|
[uxbox.util.i18n :refer [tr]]
|
||||||
|
[uxbox.util.forms :as fm]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
|
[uxbox.util.messages :as um]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]))
|
[uxbox.util.mixins :as mx :include-macros true]))
|
||||||
|
|
||||||
|
(def form-data (fm/focus-data :profile-password st/state))
|
||||||
|
(def form-errors (fm/focus-errors :profile-password st/state))
|
||||||
|
|
||||||
(def form-data (forms/focus-data :profile-password st/state))
|
(def assoc-value (partial fm/assoc-value :profile-password))
|
||||||
(def form-errors (forms/focus-errors :profile-password st/state))
|
(def assoc-error (partial fm/assoc-error :profile-password))
|
||||||
(def set-value! (partial forms/set-value! st/store :profile-password))
|
(def clear-form (partial fm/clear-form :profile-password))
|
||||||
(def set-errors! (partial forms/set-errors! st/store :profile-password))
|
|
||||||
|
|
||||||
(def +password-form+
|
;; TODO: add better password validation
|
||||||
[[:password-1 forms/required forms/string [forms/min-len 6]]
|
|
||||||
[:password-2 forms/required forms/string
|
(s/def ::password-1 ::fm/non-empty-string)
|
||||||
[forms/identical-to :password-1 :message "errors.form.password-not-match"]]
|
(s/def ::password-2 ::fm/non-empty-string)
|
||||||
[:old-password forms/required forms/string]])
|
(s/def ::password-old ::fm/non-empty-string)
|
||||||
|
|
||||||
|
(s/def ::password-form
|
||||||
|
(s/keys :req-un [::password-1
|
||||||
|
::password-2
|
||||||
|
::password-old]))
|
||||||
|
|
||||||
(mx/defc password-form
|
(mx/defc password-form
|
||||||
{:mixins [mx/reactive mx/static]}
|
{:mixins [mx/reactive mx/static]}
|
||||||
[]
|
[]
|
||||||
(let [data (mx/react form-data)
|
(let [data (mx/react form-data)
|
||||||
errors (mx/react form-errors)
|
errors (mx/react form-errors)
|
||||||
valid? (forms/valid? data +password-form+)]
|
valid? (fm/valid? ::password-form data)]
|
||||||
(letfn [(on-change [field event]
|
(letfn [(on-change [field event]
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(set-value! field value)))
|
(st/emit! (assoc-value field value))))
|
||||||
|
(on-success []
|
||||||
|
(st/emit! (um/info (tr "settings.password-saved"))))
|
||||||
|
(on-error [{:keys [code] :as payload}]
|
||||||
|
(case code
|
||||||
|
:uxbox.services.users/old-password-not-match
|
||||||
|
(st/emit! (assoc-error :password-old "Wrong old password"))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(throw (ex-info "unexpected" {:error payload}))))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(println "on-submit" data)
|
(st/emit! (udu/update-password data
|
||||||
#_(st/emit! (udu/update-password form)))]
|
:on-success on-success
|
||||||
|
:on-error on-error)))]
|
||||||
[:form.password-form
|
[:form.password-form
|
||||||
[:span.user-settings-label "Change password"]
|
[:span.user-settings-label "Change password"]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:class (forms/error-class errors :old-password)
|
:class (fm/error-class errors :password-old)
|
||||||
:value (:old-password data "")
|
:value (:password-old data "")
|
||||||
:on-change (partial on-change :old-password)
|
:on-change (partial on-change :password-old)
|
||||||
:placeholder "Old password"}]
|
:placeholder "Old password"}]
|
||||||
(forms/input-error errors :old-password)
|
(fm/input-error errors :password-old)
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:class (forms/error-class errors :password-1)
|
:class (fm/error-class errors :password-1)
|
||||||
:value (:password-1 data "")
|
:value (:password-1 data "")
|
||||||
:on-change (partial on-change :password-1)
|
:on-change (partial on-change :password-1)
|
||||||
:placeholder "New password"}]
|
:placeholder "New password"}]
|
||||||
(forms/input-error errors :password-1)
|
(fm/input-error errors :password-1)
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "password"
|
{:type "password"
|
||||||
:class (forms/error-class errors :password-2)
|
:class (fm/error-class errors :password-2)
|
||||||
:value (:password-2 data "")
|
:value (:password-2 data "")
|
||||||
:on-change (partial on-change :password-2)
|
:on-change (partial on-change :password-2)
|
||||||
:placeholder "Confirm password"}]
|
:placeholder "Confirm password"}]
|
||||||
(forms/input-error errors :password-2)
|
(fm/input-error errors :password-2)
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:type "button"
|
{:type "button"
|
||||||
:class (when-not valid? "btn-disabled")
|
:class (when-not valid? "btn-disabled")
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
;; Copyright (c) 2016-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2016-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.settings.profile
|
(ns uxbox.main.ui.settings.profile
|
||||||
(:require [cuerdas.core :as str]
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
|
@ -14,52 +15,58 @@
|
||||||
[uxbox.main.ui.settings.header :refer [header]]
|
[uxbox.main.ui.settings.header :refer [header]]
|
||||||
[uxbox.main.ui.messages :refer [messages-widget]]
|
[uxbox.main.ui.messages :refer [messages-widget]]
|
||||||
[uxbox.main.data.users :as udu]
|
[uxbox.main.data.users :as udu]
|
||||||
[uxbox.util.forms :as forms]
|
[uxbox.util.forms :as fm]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.util.interop :refer [iterable->seq]]
|
[uxbox.util.interop :refer [iterable->seq]]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
|
||||||
|
|
||||||
(def form-data (forms/focus-data :profile st/state))
|
(def form-data (fm/focus-data :profile st/state))
|
||||||
(def form-errors (forms/focus-errors :profile st/state))
|
(def form-errors (fm/focus-errors :profile st/state))
|
||||||
(def set-value! (partial forms/set-value! st/store :profile))
|
|
||||||
(def set-error! (partial forms/set-error! st/store :profile))
|
(def assoc-value (partial fm/assoc-value :profile))
|
||||||
(def clear! (partial forms/clear! st/store :profile))
|
(def assoc-error (partial fm/assoc-error :profile))
|
||||||
|
(def clear-form (partial fm/clear-form :profile))
|
||||||
|
|
||||||
(def profile-ref
|
(def profile-ref
|
||||||
(-> (l/key :profile)
|
(-> (l/key :profile)
|
||||||
(l/derive st/state)))
|
(l/derive st/state)))
|
||||||
|
|
||||||
(def +profile-form+
|
(s/def ::fullname ::fm/non-empty-string)
|
||||||
{:fullname [forms/required forms/string]
|
(s/def ::username ::fm/non-empty-string)
|
||||||
:email [forms/required forms/email]
|
(s/def ::email ::fm/email)
|
||||||
:username [forms/required forms/string]})
|
|
||||||
|
(s/def ::profile-form
|
||||||
|
(s/keys :req-un [::fullname
|
||||||
|
::username
|
||||||
|
::email]))
|
||||||
|
|
||||||
;; --- Profile Form
|
;; --- Profile Form
|
||||||
|
|
||||||
(mx/defc profile-form
|
(mx/defc profile-form
|
||||||
{:mixins [mx/static mx/reactive
|
{:mixins [mx/static mx/reactive
|
||||||
(forms/clear-mixin st/store :profile)]}
|
(fm/clear-mixin st/store :profile)]}
|
||||||
[]
|
[]
|
||||||
;; TODO: properly persist theme
|
|
||||||
(let [data (merge {:theme "light"}
|
(let [data (merge {:theme "light"}
|
||||||
(mx/react profile-ref)
|
(mx/react profile-ref)
|
||||||
(mx/react form-data))
|
(mx/react form-data))
|
||||||
errors (mx/react form-errors)
|
errors (mx/react form-errors)
|
||||||
valid? (forms/valid? data +profile-form+)
|
valid? (fm/valid? ::profile-form data)
|
||||||
theme (:theme data)]
|
theme (:theme data)]
|
||||||
(letfn [(on-change [field event]
|
(letfn [(on-change [field event]
|
||||||
(let [value (dom/event->value event)]
|
(let [value (dom/event->value event)]
|
||||||
(set-value! field value)))
|
(st/emit! (assoc-value field value))))
|
||||||
(on-error [{:keys [code] :as payload}]
|
(on-error [{:keys [code] :as payload}]
|
||||||
(case code
|
(case code
|
||||||
:uxbox.services.users/email-already-exists
|
:uxbox.services.users/email-already-exists
|
||||||
(set-error! :email "Email already exists")
|
(st/emit! (assoc-error :email "Email already exists"))
|
||||||
:uxbox.services.users/username-already-exists
|
:uxbox.services.users/username-already-exists
|
||||||
(set-error! :username "Username already exists")))
|
(st/emit! (assoc-error :username "Username already exists"))))
|
||||||
|
(on-success [_]
|
||||||
|
(st/emit! (clear-form)))
|
||||||
(on-submit [event]
|
(on-submit [event]
|
||||||
(st/emit! (udu/update-profile data clear! on-error)))]
|
(st/emit! (udu/update-profile data on-success on-error)))]
|
||||||
[:form.profile-form
|
[:form.profile-form
|
||||||
[:span.user-settings-label "Name, username and email"]
|
[:span.user-settings-label "Name, username and email"]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
|
@ -72,14 +79,14 @@
|
||||||
:on-change (partial on-change :username)
|
:on-change (partial on-change :username)
|
||||||
:value (:username data "")
|
:value (:username data "")
|
||||||
:placeholder "Your username"}]
|
:placeholder "Your username"}]
|
||||||
(forms/input-error errors :username)
|
(fm/input-error errors :username)
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:type "email"
|
{:type "email"
|
||||||
:on-change (partial on-change :email)
|
:on-change (partial on-change :email)
|
||||||
:value (:email data "")
|
:value (:email data "")
|
||||||
:placeholder "Your email"}]
|
:placeholder "Your email"}]
|
||||||
(forms/input-error errors :email)
|
(fm/input-error errors :email)
|
||||||
|
|
||||||
#_[:span.user-settings-label "Choose a color theme"]
|
#_[:span.user-settings-label "Choose a color theme"]
|
||||||
#_[:div.input-radio.radio-primary
|
#_[:div.input-radio.radio-primary
|
||||||
|
|
|
@ -6,41 +6,49 @@
|
||||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
(ns uxbox.main.ui.workspace.sidebar.sitemap-pageform
|
(ns uxbox.main.ui.workspace.sidebar.sitemap-pageform
|
||||||
(:require [lentes.core :as l]
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
[cuerdas.core :as str]
|
[lentes.core :as l]
|
||||||
[potok.core :as ptk]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.data.pages :as udp]
|
[uxbox.main.data.pages :as udp]
|
||||||
[uxbox.main.data.workspace :as dw]
|
|
||||||
[uxbox.main.data.lightbox :as udl]
|
[uxbox.main.data.lightbox :as udl]
|
||||||
[uxbox.builtins.icons :as i]
|
|
||||||
[uxbox.main.ui.lightbox :as lbx]
|
[uxbox.main.ui.lightbox :as lbx]
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[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]
|
[uxbox.util.router :as r]
|
||||||
[uxbox.util.forms :as forms]
|
[uxbox.util.mixins :as mx :include-macros true]))
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
|
||||||
[uxbox.util.data :refer (deep-merge parse-int)]
|
|
||||||
[uxbox.util.dom :as dom]))
|
|
||||||
|
|
||||||
(def form-data (forms/focus-data :workspace-page-form st/state))
|
|
||||||
(def set-value! (partial forms/set-value! st/store :workspace-page-form))
|
(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
|
;; --- Lightbox
|
||||||
|
|
||||||
(def +page-form+
|
(s/def ::name ::fm/non-empty-string)
|
||||||
{:name [forms/required forms/string]
|
(s/def ::layout ::fm/non-empty-string)
|
||||||
:width [forms/required forms/number]
|
(s/def ::width number?)
|
||||||
:height [forms/required forms/number]
|
(s/def ::height number?)
|
||||||
:layout [forms/required forms/string]})
|
|
||||||
|
(s/def ::page-form
|
||||||
|
(s/keys :req-un [::name
|
||||||
|
::width
|
||||||
|
::height
|
||||||
|
::layout]))
|
||||||
|
|
||||||
(mx/defc layout-input
|
(mx/defc layout-input
|
||||||
[data id]
|
[data id]
|
||||||
(let [{:keys [id name width height]} (get c/page-layouts id)]
|
(let [{:keys [id name width height]} (get c/page-layouts id)]
|
||||||
(letfn [(on-change [event]
|
(letfn [(on-change [event]
|
||||||
(set-value! :layout id)
|
(st/emit! (assoc-value :layout id)
|
||||||
(set-value! :width width)
|
(assoc-value :width width)
|
||||||
(set-value! :height height))]
|
(assoc-value :height height)))]
|
||||||
[:div
|
[:div
|
||||||
[:input {:type "radio"
|
[:input {:type "radio"
|
||||||
:id id
|
:id id
|
||||||
|
@ -57,18 +65,18 @@
|
||||||
(select-keys page [:name :id :project])
|
(select-keys page [:name :id :project])
|
||||||
(select-keys metadata [:width :height :layout])
|
(select-keys metadata [:width :height :layout])
|
||||||
(mx/react form-data))
|
(mx/react form-data))
|
||||||
valid? (forms/valid? data +page-form+)]
|
valid? (fm/valid? ::page-form data)]
|
||||||
(letfn [(update-size [field e]
|
(letfn [(update-size [field e]
|
||||||
(let [value (dom/event->value e)
|
(let [value (dom/event->value e)
|
||||||
value (parse-int value)]
|
value (parse-int value)]
|
||||||
(set-value! field value)))
|
(st/emit! (assoc-value field value))))
|
||||||
(update-name [e]
|
(update-name [e]
|
||||||
(let [value (dom/event->value e)]
|
(let [value (dom/event->value e)]
|
||||||
(set-value! :name value)))
|
(st/emit! (assoc-value :name value))))
|
||||||
(toggle-sizes []
|
(toggle-sizes []
|
||||||
(let [{:keys [width height]} data]
|
(let [{:keys [width height]} data]
|
||||||
(set-value! :width height)
|
(st/emit! (assoc-value :width width)
|
||||||
(set-value! :height width)))
|
(assoc-value :height height))))
|
||||||
(on-cancel [e]
|
(on-cancel [e]
|
||||||
(dom/prevent-default e)
|
(dom/prevent-default e)
|
||||||
(udl/close!))
|
(udl/close!))
|
||||||
|
@ -119,7 +127,7 @@
|
||||||
:type "button"}]])))
|
:type "button"}]])))
|
||||||
|
|
||||||
(mx/defc page-form-lightbox
|
(mx/defc page-form-lightbox
|
||||||
{:mixins [mx/static (forms/clear-mixin st/store :workspace-page-form)]}
|
{:mixins [mx/static (fm/clear-mixin st/store :workspace-page-form)]}
|
||||||
[{:keys [id] :as page}]
|
[{:keys [id] :as page}]
|
||||||
(letfn [(on-cancel [event]
|
(letfn [(on-cancel [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
|
|
|
@ -2,173 +2,155 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
|
||||||
|
|
||||||
(ns uxbox.util.forms
|
(ns uxbox.util.forms
|
||||||
(:refer-clojure :exclude [keyword uuid vector boolean map set])
|
(:require [cljs.spec :as s :include-macros true]
|
||||||
(:require [struct.core :as f]
|
[cuerdas.core :as str]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[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
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
(defn- interpret-problem
|
||||||
;; Form Validation
|
[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
|
:else acc))
|
||||||
(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
|
|
||||||
|
|
||||||
(defn validate
|
(defn validate
|
||||||
([data schema]
|
[spec data]
|
||||||
(validate data schema nil))
|
(when-not (s/valid? spec data)
|
||||||
([data schema opts]
|
(let [report (s/explain-data spec data)]
|
||||||
(f/validate data schema opts)))
|
(reduce interpret-problem {} (::s/problems report)))))
|
||||||
|
|
||||||
(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))))
|
|
||||||
|
|
||||||
(defn valid?
|
(defn valid?
|
||||||
[data schema]
|
[spec data]
|
||||||
(let [[errors data] (validate data schema)]
|
(s/valid? spec data))
|
||||||
(not errors)))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;; --- Form Specs and Conformers
|
||||||
;; Form Events
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
;; --- 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
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:errors type field] error)))
|
(assoc-in state [:errors type field] error)))
|
||||||
|
|
||||||
(defn set-error
|
(defn assoc-error
|
||||||
([type field]
|
([type field]
|
||||||
(set-error type field nil))
|
(assoc-error type field nil))
|
||||||
([type field error]
|
([type field error]
|
||||||
{:pre [(keyword? type)
|
{:pre [(keyword? type)
|
||||||
(keyword? field)
|
(keyword? field)
|
||||||
(any? error)]}
|
(any? error)]}
|
||||||
(SetError. type field error)))
|
(AssocError. type field error)))
|
||||||
|
|
||||||
(defn set-error!
|
;; --- Assoc Errors
|
||||||
[store & args]
|
|
||||||
(ptk/emit! store (apply set-error args)))
|
|
||||||
|
|
||||||
;; --- Set Errors
|
(defrecord AssocErrors [type errors]
|
||||||
|
|
||||||
(defrecord SetErrors [type errors]
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:errors type] errors)))
|
(assoc-in state [:errors type] errors)))
|
||||||
|
|
||||||
(defn set-errors
|
(defn assoc-errors
|
||||||
([type]
|
([type]
|
||||||
(set-errors type nil))
|
(assoc-errors type nil))
|
||||||
([type errors]
|
([type errors]
|
||||||
{:pre [(keyword? type)
|
{:pre [(keyword? type)
|
||||||
(or (map? errors)
|
(or (map? errors)
|
||||||
(nil? errors))]}
|
(nil? errors))]}
|
||||||
(SetErrors. type errors)))
|
(AssocErrors. type errors)))
|
||||||
|
|
||||||
(defn set-errors!
|
;; --- Assoc Value
|
||||||
[store & args]
|
|
||||||
(ptk/emit! store (apply set-errors args)))
|
|
||||||
|
|
||||||
;; --- Set Value
|
(declare clear-error)
|
||||||
|
|
||||||
(defrecord SetValue [type field value]
|
(defrecord AssocValue [type field value]
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [form-path (into [:forms type] (if (coll? field) field [field]))
|
(let [form-path (into [:forms type] (if (coll? field) field [field]))]
|
||||||
errors-path (into [:errors type] (if (coll? field) field [field]))]
|
(assoc-in state form-path value)))
|
||||||
(-> state
|
|
||||||
(assoc-in form-path value)
|
|
||||||
(update-in (butlast errors-path) dissoc (last errors-path))))))
|
|
||||||
|
|
||||||
(defn set-value
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(rx/of (clear-error type field))))
|
||||||
|
|
||||||
|
(defn assoc-value
|
||||||
[type field value]
|
[type field value]
|
||||||
{:pre [(keyword? type)
|
{:pre [(keyword? type)
|
||||||
(keyword? field)
|
(keyword? field)
|
||||||
(any? value)]}
|
(any? value)]}
|
||||||
(SetValue. type field value))
|
(AssocValue. type field value))
|
||||||
|
|
||||||
(defn set-value!
|
;; --- Clear Values
|
||||||
[store type field value]
|
|
||||||
(ptk/emit! store (set-value type field value)))
|
|
||||||
|
|
||||||
;; --- Clear Form
|
(defrecord ClearValues [type]
|
||||||
|
|
||||||
(defrecord ClearForm [type]
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:forms type] nil)))
|
(assoc-in state [:forms type] nil)))
|
||||||
|
|
||||||
(defn clear-form
|
(defn clear-values
|
||||||
[type]
|
[type]
|
||||||
{:pre [(keyword? type)]}
|
{:pre [(keyword? type)]}
|
||||||
(ClearForm. type))
|
(ClearValues. type))
|
||||||
|
|
||||||
(defn clear-form!
|
;; --- Clear Error
|
||||||
[store type]
|
|
||||||
(ptk/emit! store (clear-form type)))
|
(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
|
;; --- Clear Errors
|
||||||
|
|
||||||
|
@ -182,17 +164,18 @@
|
||||||
{:pre [(keyword? type)]}
|
{:pre [(keyword? type)]}
|
||||||
(ClearErrors. type))
|
(ClearErrors. type))
|
||||||
|
|
||||||
(defn clear-errors!
|
;; --- Clear Form
|
||||||
[store type]
|
|
||||||
(ptk/emit! store (clear-errors type)))
|
|
||||||
|
|
||||||
;; --- Clear
|
(deftype ClearForm [type]
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(rx/of (clear-values type)
|
||||||
|
(clear-errors type))))
|
||||||
|
|
||||||
(defn clear!
|
(defn clear-form
|
||||||
[store type]
|
[type]
|
||||||
(ptk/emit! store
|
{:pre [(keyword? type)]}
|
||||||
(clear-form type)
|
(ClearForm. type))
|
||||||
(clear-errors type)))
|
|
||||||
|
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
|
||||||
|
@ -224,5 +207,5 @@
|
||||||
(defn clear-mixin
|
(defn clear-mixin
|
||||||
[store type]
|
[store type]
|
||||||
{:will-unmount (fn [own]
|
{:will-unmount (fn [own]
|
||||||
(clear! store type)
|
(ptk/emit! store (clear-form type))
|
||||||
own)})
|
own)})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue