mirror of
https://github.com/penpot/penpot.git
synced 2025-05-18 06:16:11 +02:00
🚧 More work on data and forms validation.
This commit is contained in:
parent
2477b289e2
commit
a009961a58
15 changed files with 464 additions and 314 deletions
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns uxbox.main.data.auth
|
||||
(:require
|
||||
[struct.alpha :as st]
|
||||
[cljs.spec.alpha :as s]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.main.repo :as rp]
|
||||
|
@ -14,9 +14,15 @@
|
|||
[uxbox.main.data.users :as du]
|
||||
[uxbox.util.messages :as um]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.storage :refer [storage]]))
|
||||
|
||||
(s/def ::username string?)
|
||||
(s/def ::password string?)
|
||||
(s/def ::fullname string?)
|
||||
(s/def ::email ::us/email)
|
||||
|
||||
;; --- Logged In
|
||||
|
||||
;; TODO: add spec
|
||||
|
@ -43,13 +49,12 @@
|
|||
|
||||
;; --- Login
|
||||
|
||||
(st/defs ::login
|
||||
(st/dict :username ::st/string
|
||||
:password ::st/string))
|
||||
(s/def ::login-params
|
||||
(s/keys :req-un [::username ::password]))
|
||||
|
||||
(defn login
|
||||
[{:keys [username password] :as data}]
|
||||
(assert (st/valid? ::login data))
|
||||
(s/assert ::login-params data)
|
||||
(reify
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
|
@ -93,17 +98,17 @@
|
|||
|
||||
;; --- Register
|
||||
|
||||
(st/defs ::register
|
||||
(st/dict :fullname ::st/string
|
||||
:username ::st/string
|
||||
:password ::st/string
|
||||
:email ::st/email))
|
||||
(s/def ::register-params
|
||||
(s/keys :req-un [::fullname
|
||||
::username
|
||||
::password
|
||||
::email]))
|
||||
|
||||
(defn register
|
||||
"Create a register event instance."
|
||||
[data on-error]
|
||||
(assert (st/valid? ::register data))
|
||||
(assert (fn? on-error))
|
||||
(s/assert ::register-params data)
|
||||
(s/assert ::us/fn on-error)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
|
@ -122,12 +127,12 @@
|
|||
|
||||
;; --- Recovery Request
|
||||
|
||||
(st/defs ::recovery-request
|
||||
(st/dict :username ::st/string))
|
||||
(s/def ::recovery-request-params
|
||||
(s/keys :req-un [::username]))
|
||||
|
||||
(defn recovery-request
|
||||
[data]
|
||||
(assert (st/valid? ::recovery-request data))
|
||||
(s/assert ::recovery-request-params data)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
|
@ -164,13 +169,13 @@
|
|||
|
||||
;; --- Recovery (Password)
|
||||
|
||||
(st/defs ::recovery
|
||||
(st/dict :username ::st/string
|
||||
:token ::st/string))
|
||||
(s/def ::token string?)
|
||||
(s/def ::recovery-params
|
||||
(s/keys :req-un [::username ::token]))
|
||||
|
||||
(defn recovery
|
||||
[{:keys [token password] :as data}]
|
||||
(assert (st/valid? ::recovery data))
|
||||
(s/assert ::recovery-params data)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
(ns uxbox.main.data.pages
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]
|
||||
[struct.alpha :as st]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.util.data :refer [index-by-id]]
|
||||
[uxbox.util.spec :as us]
|
||||
|
@ -18,54 +18,65 @@
|
|||
|
||||
;; --- Struct
|
||||
|
||||
(st/defs ::inst inst?)
|
||||
(st/defs ::width (st/&& ::st/number ::st/positive))
|
||||
(st/defs ::height (st/&& ::st/number ::st/positive))
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::name ::us/string)
|
||||
(s/def ::inst ::us/inst)
|
||||
(s/def ::type ::us/keyword)
|
||||
(s/def ::project ::us/uuid)
|
||||
(s/def ::created-at ::us/inst)
|
||||
(s/def ::modified-at ::us/inst)
|
||||
(s/def ::version ::us/number)
|
||||
(s/def ::width (s/and ::us/number ::us/positive))
|
||||
(s/def ::height (s/and ::us/number ::us/positive))
|
||||
(s/def ::grid-x-axis ::us/number)
|
||||
(s/def ::grid-y-axis ::us/number)
|
||||
(s/def ::grid-color ::us/string)
|
||||
(s/def ::order ::us/number)
|
||||
(s/def ::background ::us/string)
|
||||
(s/def ::background-opacity ::us/number)
|
||||
(s/def ::user ::us/uuid)
|
||||
|
||||
(st/defs ::metadata
|
||||
(st/dict :width ::width
|
||||
:height ::height
|
||||
:grid-y-axis (st/opt ::st/number)
|
||||
:grid-x-axis (st/opt ::st/number)
|
||||
:grid-color (st/opt ::st/string)
|
||||
:order (st/opt ::st/number)
|
||||
:background (st/opt ::st/string)
|
||||
:background-opacity (st/opt ::st/number)))
|
||||
(s/def ::metadata
|
||||
(s/keys :req-un [::width ::height]
|
||||
:opt-un [::grid-y-axis
|
||||
::grid-x-axis
|
||||
::grid-color
|
||||
::order
|
||||
::background
|
||||
::background-opacity]))
|
||||
|
||||
(st/defs ::shapes-list
|
||||
(st/coll-of ::st/uuid))
|
||||
(s/def ::shapes
|
||||
(s/coll-of ::us/uuid :kind vector? :into []))
|
||||
|
||||
(st/defs ::page-entity
|
||||
(st/dict :id ::st/uuid
|
||||
:name ::st/string
|
||||
:project ::st/uuid
|
||||
:created-at ::inst
|
||||
:modified-at ::inst
|
||||
:user ::st/uuid
|
||||
:metadata ::metadata
|
||||
:shapes ::shapes-list))
|
||||
(s/def ::page-entity
|
||||
(s/keys :req-un [::id
|
||||
::name
|
||||
::project
|
||||
::created-at
|
||||
::modified-at
|
||||
::user
|
||||
::metadata
|
||||
::shapes]))
|
||||
|
||||
(st/defs ::minimal-shape
|
||||
(st/dict :id ::st/uuid
|
||||
:type ::st/keyword
|
||||
:name ::st/string))
|
||||
(s/def ::minimal-shape
|
||||
(s/keys :req-un [::type ::name]
|
||||
:opt-un [::id]))
|
||||
|
||||
(st/defs ::server-page-data-sapes
|
||||
(st/coll-of ::minimal-shape))
|
||||
(s/def :uxbox.backend/shapes
|
||||
(s/coll-of ::minimal-shape :kind vector?))
|
||||
|
||||
(st/defs ::server-page-data
|
||||
(st/dict :shapes ::server-page-data-sapes))
|
||||
(s/def :uxbox.backend/data
|
||||
(s/keys :req-un [:uxbox.backend/shapes]))
|
||||
|
||||
(st/defs ::server-page
|
||||
(st/dict :id ::st/uuid
|
||||
:name ::st/string
|
||||
:project ::st/uuid
|
||||
:version ::st/integer
|
||||
:created-at ::inst
|
||||
:modified-at ::inst
|
||||
:user ::st/uuid
|
||||
:metadata ::metadata
|
||||
:data ::server-page-data))
|
||||
(s/def ::server-page
|
||||
(s/keys :req-un [::id ::name
|
||||
::project
|
||||
::version
|
||||
::created-at
|
||||
::modified-at
|
||||
::user
|
||||
::metadata
|
||||
:uxbox.backend/data]))
|
||||
|
||||
;; --- Protocols
|
||||
|
||||
|
@ -173,15 +184,12 @@
|
|||
|
||||
(declare rehash-pages)
|
||||
|
||||
(st/defs ::page-created
|
||||
(st/dict :id ::st/uuid
|
||||
:name ::st/string
|
||||
:project ::st/uuid
|
||||
:metadata ::metadata))
|
||||
(s/def ::page-created-params
|
||||
(s/keys :req-un [::id ::name ::project ::metadata]))
|
||||
|
||||
(defn page-created
|
||||
[data]
|
||||
(assert (st/valid? ::page-created data) "invalid parameters")
|
||||
(s/assert ::page-created-event data)
|
||||
(reify
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
|
@ -197,15 +205,12 @@
|
|||
|
||||
;; --- Create Page
|
||||
|
||||
(st/defs ::create-page
|
||||
(st/dict :name ::st/string
|
||||
:project ::st/uuid
|
||||
:width ::width
|
||||
:height ::height))
|
||||
(s/def ::created-page-params
|
||||
(s/keys :req-un [::name ::project ::width ::height]))
|
||||
|
||||
(defn create-page
|
||||
[{:keys [name project width height layout] :as data}]
|
||||
(assert (st/valid? ::create-page data))
|
||||
(s/assert ::created-page-params data)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [this state s]
|
||||
|
@ -231,7 +236,7 @@
|
|||
|
||||
(defn page-persisted
|
||||
[data]
|
||||
(assert (st/valid? ::server-page data))
|
||||
(s/assert ::server-page data)
|
||||
(reify
|
||||
cljs.core/IDeref
|
||||
(-deref [_] data)
|
||||
|
@ -278,24 +283,24 @@
|
|||
|
||||
;; --- Page Metadata Persisted
|
||||
|
||||
(deftype MetadataPersisted [id data]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:pages id :version] (:version data))))
|
||||
|
||||
(st/defs ::version integer?)
|
||||
(st/defs ::metadata-persisted-event
|
||||
(st/dict :id ::st/uuid
|
||||
:version ::version))
|
||||
|
||||
(defn metadata-persisted?
|
||||
[v]
|
||||
(instance? MetadataPersisted v))
|
||||
(s/def ::metadata-persisted-params
|
||||
(s/keys :req-un [::id ::version]))
|
||||
|
||||
(defn metadata-persisted
|
||||
[{:keys [id] :as data}]
|
||||
{:pre [(st/valid? ::metadata-persisted-event data)]}
|
||||
(MetadataPersisted. id data))
|
||||
(s/assert ::metadata-persisted-params data)
|
||||
(reify
|
||||
ptk/EventType
|
||||
(type [_] ::metadata-persisted)
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:pages id :version] (:version data)))))
|
||||
|
||||
(defn metadata-persisted?
|
||||
[v]
|
||||
(= ::metadata-persisted (ptk/type v)))
|
||||
|
||||
|
||||
;; --- Persist Page Metadata
|
||||
|
||||
|
@ -316,29 +321,28 @@
|
|||
|
||||
;; --- Update Page
|
||||
|
||||
(deftype UpdatePage [id data]
|
||||
IPageUpdate
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:pages id] merge (dissoc data :id :version))))
|
||||
|
||||
(defn update-page
|
||||
[id data]
|
||||
{:pre [(uuid? id) (st/valid? ::page-entity data)]}
|
||||
(UpdatePage. id data))
|
||||
(s/assert ::page-entity data)
|
||||
(s/assert ::id id)
|
||||
(reify
|
||||
IPageUpdate
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:pages id] merge (dissoc data :id :version)))))
|
||||
|
||||
;; --- Update Page Metadata
|
||||
|
||||
(deftype UpdateMetadata [id metadata]
|
||||
IMetadataUpdate
|
||||
ptk/UpdateEvent
|
||||
(update [this state]
|
||||
(assoc-in state [:pages id :metadata] metadata)))
|
||||
|
||||
(defn update-metadata
|
||||
[id metadata]
|
||||
{:pre [(uuid? id) (st/valid? ::metadata metadata)]}
|
||||
(UpdateMetadata. id metadata))
|
||||
(s/assert ::id id)
|
||||
(s/assert ::metadata metadata)
|
||||
(reify
|
||||
IMetadataUpdate
|
||||
ptk/UpdateEvent
|
||||
(update [this state]
|
||||
(assoc-in state [:pages id :metadata] metadata))))
|
||||
|
||||
|
||||
;; --- Rehash Pages
|
||||
;;
|
||||
|
@ -382,24 +386,21 @@
|
|||
;; A specialized event for persist data
|
||||
;; from the update page form.
|
||||
|
||||
(st/defs ::persist-page-update-form
|
||||
(st/dict :id ::st/uuid
|
||||
:name ::st/string
|
||||
:width ::width
|
||||
:height ::height))
|
||||
(s/def ::persist-page-update-form-params
|
||||
(s/keys :req-un [::id ::name ::width ::height]))
|
||||
|
||||
(defn persist-page-update-form
|
||||
[{:keys [id name width height] :as data}]
|
||||
(assert (st/valid? ::persist-page-update-form data))
|
||||
(s/assert ::persist-page-update-form-params data)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page (-> (get-in state [:pages id])
|
||||
(assoc-in [:name] name)
|
||||
(assoc-in [:metadata :width] width)
|
||||
(assoc-in [:metadata :height] height))]
|
||||
(rx/of (update-page id page))))))
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:pages id]
|
||||
(fn [page]
|
||||
(-> (assoc page :name name)
|
||||
(assoc-in [:name] name)
|
||||
(assoc-in [:metadata :width] width)
|
||||
(assoc-in [:metadata :height] height)))))))
|
||||
|
||||
;; --- Delete Page (by id)
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
[cuerdas.core :as str]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]
|
||||
[struct.core :as st]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.main.data.pages :as udp]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
|
@ -35,21 +34,12 @@
|
|||
::created-at
|
||||
::modified-at]))
|
||||
|
||||
(st/defs project-spec
|
||||
{:id [st/required st/uuid]
|
||||
:name [st/required st/string]
|
||||
:version [st/required st/integer]
|
||||
:user [st/required st/uuid]
|
||||
:created-at [st/required inst?]
|
||||
:modified-at [st/required inst?]})
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn assoc-project
|
||||
"A reduce function for assoc the project to the state map."
|
||||
[state {:keys [id] :as project}]
|
||||
(assert (st/valid? project-spec project)
|
||||
"invalid project instance")
|
||||
(s/assert ::project-entity project)
|
||||
(update-in state [:projects id] merge project))
|
||||
|
||||
(defn dissoc-project
|
||||
|
@ -170,15 +160,12 @@
|
|||
|
||||
;; --- Create Project
|
||||
|
||||
(st/defs create-project-spec
|
||||
{:name [st/required st/string]
|
||||
:width [st/required st/number st/positive]
|
||||
:height [st/required st/number st/positive]})
|
||||
(s/def ::create-project-params
|
||||
(s/keys :req-un [::name ::width ::height]))
|
||||
|
||||
(defn create-project
|
||||
[{:keys [name] :as params}]
|
||||
(assert (st/valid? create-project-spec params)
|
||||
"invalid params for create project event")
|
||||
(s/assert ::create-project-params params)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [this state stream]
|
||||
|
|
|
@ -5,14 +5,17 @@
|
|||
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.main.data.shapes
|
||||
(:require [cljs.spec.alpha :as s]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.util.data :refer [index-of]]))
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.util.data :refer [index-of]]
|
||||
[uxbox.util.geom.matrix :as gmt]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.util.uuid :as uuid]))
|
||||
|
||||
;; --- Specs
|
||||
|
||||
(s/def ::id ::us/uuid)
|
||||
(s/def ::blocked boolean?)
|
||||
(s/def ::collapsed boolean?)
|
||||
(s/def ::content string?)
|
||||
|
@ -49,7 +52,7 @@
|
|||
(s/def ::attributes
|
||||
(s/keys :opt-un [::blocked
|
||||
::collapsed
|
||||
::conent
|
||||
::content
|
||||
::fill-color
|
||||
::fill-opacity
|
||||
::font-family
|
||||
|
@ -72,10 +75,10 @@
|
|||
::y1 ::y2]))
|
||||
|
||||
(s/def ::minimal-shape
|
||||
(s/keys ::req-un [::id ::page ::type ::name]))
|
||||
(s/keys :req-un [::id ::page ::type ::name]))
|
||||
|
||||
(s/def ::shape
|
||||
(s/merge ::minimal-shape ::attributes))
|
||||
(s/and ::minimal-shape ::attributes))
|
||||
|
||||
(s/def ::rect-like-shape
|
||||
(s/keys :req-un [::x1 ::y1 ::x2 ::y2 ::type]))
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
(ns uxbox.main.data.users
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[beicon.core :as rx]
|
||||
[struct.core :as s]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.main.repo :as rp]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
|
@ -58,17 +58,22 @@
|
|||
|
||||
;; --- Update Profile
|
||||
|
||||
(s/defs update-profile-spec
|
||||
{:fullname [s/required s/string]
|
||||
:email [s/required s/email]
|
||||
:username [s/required s/string]
|
||||
:language [s/required s/string]})
|
||||
(s/def ::fullname string?)
|
||||
(s/def ::email ::us/email)
|
||||
(s/def ::password string?)
|
||||
(s/def ::language string?)
|
||||
|
||||
(s/def ::update-profile-params
|
||||
(s/keys :req-un [::fullname
|
||||
::email
|
||||
::username
|
||||
::language]))
|
||||
|
||||
(defn update-profile
|
||||
[data {:keys [on-success on-error]}]
|
||||
{:pre [(s/valid? update-profile-spec data)
|
||||
(fn? on-error)
|
||||
(fn? on-success)]}
|
||||
(s/assert ::update-profile-params data)
|
||||
(s/assert ::us/fn on-error)
|
||||
(s/assert ::us/fn on-success)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
|
@ -89,16 +94,20 @@
|
|||
|
||||
;; --- Update Password (Form)
|
||||
|
||||
(s/defs update-password-spec
|
||||
{:password-1 [s/required s/string]
|
||||
:password-2 [s/required s/string [s/identical-to :password-1]]
|
||||
:password-old [s/required s/string]})
|
||||
(s/def ::password-1 string?)
|
||||
(s/def ::password-2 string?)
|
||||
(s/def ::password-old string?)
|
||||
|
||||
(s/def ::update-password-params
|
||||
(s/keys :req-un [::password-1
|
||||
::password-2
|
||||
::password-old]))
|
||||
|
||||
(defn update-password
|
||||
[data {:keys [on-success on-error]}]
|
||||
{:pre [(s/valid? update-password-spec data)
|
||||
(fn? on-success)
|
||||
(fn? on-error)]}
|
||||
(s/assert ::update-password-params data)
|
||||
(s/assert ::us/fn on-success)
|
||||
(s/assert ::us/fn on-error)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
(ns uxbox.main.data.workspace
|
||||
(:require
|
||||
[beicon.core :as rx]
|
||||
;; [uxbox.main.data.workspace.ruler :as wruler]
|
||||
[cljs.spec.alpha :as s]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.config :as cfg]
|
||||
|
@ -342,7 +341,6 @@
|
|||
|
||||
(defn add-shape
|
||||
[data]
|
||||
{:pre [(us/valid? ::ds/shape data)]}
|
||||
(reify
|
||||
udp/IPageUpdate
|
||||
ptk/UpdateEvent
|
||||
|
@ -426,32 +424,27 @@
|
|||
|
||||
;; --- Update Shape Attrs
|
||||
|
||||
(deftype UpdateShapeAttrs [id attrs]
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:shapes id] merge attrs)))
|
||||
|
||||
(defn update-shape-attrs
|
||||
[id attrs]
|
||||
{:pre [(uuid? id) (us/valid? ::ds/attributes attrs)]}
|
||||
(let [atts (us/extract attrs ::ds/attributes)]
|
||||
(UpdateShapeAttrs. id attrs)))
|
||||
(s/assert ::us/uuid id)
|
||||
(s/assert ::ds/attributes attrs)
|
||||
(let [atts (s/conform ::ds/attributes attrs)]
|
||||
(reify
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:shapes id] merge attrs)))))
|
||||
|
||||
;; --- Update Selected Shapes attrs
|
||||
|
||||
|
||||
(deftype UpdateSelectedShapesAttrs [attrs]
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [pid (get-in state [:workspace :current])
|
||||
selected (get-in state [:workspace pid :selected])]
|
||||
(rx/from-coll (map #(update-shape-attrs % attrs) selected)))))
|
||||
|
||||
(defn update-selected-shapes-attrs
|
||||
[attrs]
|
||||
{:pre [(us/valid? ::ds/attributes attrs)]}
|
||||
(UpdateSelectedShapesAttrs. attrs))
|
||||
|
||||
(s/assert ::ds/attributes attrs)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [pid (get-in state [:workspace :current])
|
||||
selected (get-in state [:workspace pid :selected])]
|
||||
(rx/from-coll (map #(update-shape-attrs % attrs) selected))))))
|
||||
|
||||
;; --- Move Selected
|
||||
|
||||
|
@ -485,48 +478,44 @@
|
|||
(declare materialize-current-modifier)
|
||||
(declare apply-temporal-displacement)
|
||||
|
||||
(defrecord MoveSelected [direction speed]
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (get-in state [:workspace :current])
|
||||
workspace (get-in state [:workspace page-id])
|
||||
selected (:selected workspace)
|
||||
flags (:flags workspace)
|
||||
align? (refs/alignment-activated? flags)
|
||||
metadata (merge c/page-metadata (get-in state [:pages page-id :metadata]))
|
||||
distance (get-displacement-distance metadata align?)
|
||||
displacement (get-displacement direction speed distance)]
|
||||
(rx/concat
|
||||
(when align?
|
||||
(rx/concat
|
||||
(rx/from-coll (map initial-shape-align selected))
|
||||
(rx/from-coll (map apply-displacement selected))))
|
||||
(rx/from-coll (map #(apply-temporal-displacement % displacement) selected))
|
||||
(rx/from-coll (map materialize-current-modifier selected))))))
|
||||
|
||||
(s/def ::direction #{:up :down :right :left})
|
||||
(s/def ::speed #{:std :fast})
|
||||
|
||||
(defn move-selected
|
||||
[direction speed]
|
||||
{:pre [(us/valid? ::direction direction)
|
||||
(us/valid? ::speed speed)]}
|
||||
(MoveSelected. direction speed))
|
||||
(s/assert ::direction direction)
|
||||
(s/assert ::speed speed)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (get-in state [:workspace :current])
|
||||
workspace (get-in state [:workspace page-id])
|
||||
selected (:selected workspace)
|
||||
flags (:flags workspace)
|
||||
align? (refs/alignment-activated? flags)
|
||||
metadata (merge c/page-metadata (get-in state [:pages page-id :metadata]))
|
||||
distance (get-displacement-distance metadata align?)
|
||||
displacement (get-displacement direction speed distance)]
|
||||
(rx/concat
|
||||
(when align?
|
||||
(rx/concat
|
||||
(rx/from-coll (map initial-shape-align selected))
|
||||
(rx/from-coll (map apply-displacement selected))))
|
||||
(rx/from-coll (map #(apply-temporal-displacement % displacement) selected))
|
||||
(rx/from-coll (map materialize-current-modifier selected)))))))
|
||||
|
||||
;; --- Move Selected Layer
|
||||
|
||||
(defrecord MoveSelectedLayer [loc]
|
||||
udp/IPageUpdate
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace :current])
|
||||
selected (get-in state [:workspace id :selected])]
|
||||
(ds/move-layer state selected loc))))
|
||||
|
||||
(defn move-selected-layer
|
||||
[loc]
|
||||
{:pre [(us/valid? ::direction loc)]}
|
||||
(MoveSelectedLayer. loc))
|
||||
(assert (s/valid? ::direction loc))
|
||||
(reify
|
||||
udp/IPageUpdate
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace :current])
|
||||
selected (get-in state [:workspace id :selected])]
|
||||
(ds/move-layer state selected loc)))))
|
||||
|
||||
;; --- Update Shape Position
|
||||
|
||||
|
@ -708,22 +697,24 @@
|
|||
|
||||
;; --- Update Dimensions
|
||||
|
||||
(deftype UpdateDimensions [id dimensions]
|
||||
udp/IPageUpdate
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:shapes id] geom/resize-dim dimensions)))
|
||||
(s/def ::width (s/and ::us/number ::us/positive))
|
||||
(s/def ::height (s/and ::us/number ::us/positive))
|
||||
|
||||
(s/def ::update-dimensions-opts
|
||||
(s/def ::update-dimensions
|
||||
(s/keys :opt-un [::width ::height]))
|
||||
|
||||
(defn update-dimensions
|
||||
"A helper event just for update the position
|
||||
of the shape using the width and height attrs
|
||||
instread final point of coordinates."
|
||||
[id opts]
|
||||
{:pre [(uuid? id) (us/valid? ::update-dimensions-opts opts)]}
|
||||
(UpdateDimensions. id opts))
|
||||
[id dimensions]
|
||||
(s/assert ::us/uuid id)
|
||||
(s/assert ::update-dimensions dimensions)
|
||||
(reify
|
||||
udp/IPageUpdate
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:shapes id] geom/resize-dim dimensions))))
|
||||
|
||||
;; --- Update Interaction
|
||||
|
||||
|
@ -983,16 +974,15 @@
|
|||
|
||||
;; Is a workspace aware wrapper over uxbox.data.pages/UpdateMetadata event.
|
||||
|
||||
(defrecord UpdateMetadata [id metadata]
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(rx/of (udp/update-metadata id metadata)
|
||||
(initialize-alignment id))))
|
||||
|
||||
(defn update-metadata
|
||||
[id metadata]
|
||||
{:pre [(uuid? id) (us/valid? ::udp/metadata metadata)]}
|
||||
(UpdateMetadata. id metadata))
|
||||
(s/assert ::us/uuid id)
|
||||
(s/assert ::udp/metadata metadata)
|
||||
(reify
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(rx/of (udp/update-metadata id metadata)
|
||||
(initialize-alignment id)))))
|
||||
|
||||
(defrecord OpenView [page-id]
|
||||
ptk/WatchEvent
|
||||
|
|
|
@ -8,20 +8,22 @@
|
|||
(ns uxbox.main.ui.auth.login
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[struct.alpha :as s]
|
||||
[cljs.spec.alpha :as s]
|
||||
[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.util.dom :as dom]
|
||||
[uxbox.util.forms :as fm]
|
||||
[uxbox.util.forms2 :as fm2]
|
||||
[uxbox.util.i18n :refer [tr]]
|
||||
[uxbox.util.router :as rt]))
|
||||
|
||||
(s/defs ::login-form
|
||||
(s/dict :username (s/&& ::s/string ::fm/not-empty-string)
|
||||
:password (s/&& ::s/string ::fm/not-empty-string)))
|
||||
(s/def ::username ::fm2/not-empty-string)
|
||||
(s/def ::password ::fm2/not-empty-string)
|
||||
|
||||
(s/def ::login-form
|
||||
(s/keys :req-un [::username ::password]))
|
||||
|
||||
(defn- on-submit
|
||||
[event form]
|
||||
|
@ -42,7 +44,8 @@
|
|||
|
||||
(mf/defc login-form
|
||||
[]
|
||||
(let [{:keys [data] :as form} (fm/use-form ::login-form {})]
|
||||
(let [{:keys [data] :as form} (fm2/use-form ::login-form {})]
|
||||
(prn "login-form" form)
|
||||
[:form {:on-submit #(on-submit % form)}
|
||||
[:div.login-content
|
||||
(when cfg/isdemo
|
||||
|
@ -52,16 +55,18 @@
|
|||
{:name "username"
|
||||
:tab-index "2"
|
||||
:value (:username data "")
|
||||
:on-blur (fm/on-input-blur form :username)
|
||||
:on-change (fm/on-input-change form :username)
|
||||
:class (fm2/error-class form :username)
|
||||
:on-blur (fm2/on-input-blur form :username)
|
||||
:on-change (fm2/on-input-change form :username)
|
||||
:placeholder (tr "auth.email-or-username")
|
||||
:type "text"}]
|
||||
[:input.input-text
|
||||
{:name "password"
|
||||
:tab-index "3"
|
||||
:value (:password data "")
|
||||
:on-blur (fm/on-input-blur form :password)
|
||||
:on-change (fm/on-input-change form :password)
|
||||
:class (fm2/error-class form :password)
|
||||
:on-blur (fm2/on-input-blur form :password)
|
||||
:on-change (fm2/on-input-change form :password)
|
||||
:placeholder (tr "auth.password")
|
||||
:type "password"}]
|
||||
[:input.btn-primary
|
||||
|
|
|
@ -53,7 +53,6 @@
|
|||
(mf/defc register-form
|
||||
[props]
|
||||
(let [{:keys [data] :as form} (fm/use-form ::register-form {})]
|
||||
(prn "register-form" form)
|
||||
[:form {:on-submit #(on-submit % form)}
|
||||
[:div.login-content
|
||||
[:input.input-text
|
||||
|
|
|
@ -8,38 +8,40 @@
|
|||
(ns uxbox.main.ui.settings.password
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[struct.alpha :as s]
|
||||
[cljs.spec.alpha :as s]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.users :as udu]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.forms :as fm]
|
||||
[uxbox.util.forms2 :as fm]
|
||||
[uxbox.util.i18n :refer [tr]]
|
||||
[uxbox.util.messages :as um]))
|
||||
|
||||
(defn- on-error
|
||||
[form error]
|
||||
(case (:code error)
|
||||
:uxbox.services.users/old-password-not-match
|
||||
(swap! form assoc-in [:errors :password-old]
|
||||
{:type ::api :message "settings.password.wrong-old-password"})
|
||||
|
||||
:else (throw (ex-info "unexpected" {:error error}))))
|
||||
|
||||
(defn- on-submit
|
||||
[event form]
|
||||
(letfn [(on-error [error]
|
||||
(case (:code error)
|
||||
:uxbox.services.users/old-password-not-match
|
||||
(swap! form assoc-in [:errors :password-old]
|
||||
{:type ::api :message "settings.password.wrong-old-password"})
|
||||
(dom/prevent-default event)
|
||||
(let [data (:clean-data form)
|
||||
opts {:on-success #(st/emit! (um/info (tr "settings.password.password-saved")))
|
||||
:on-error #(on-error form %)}]
|
||||
(st/emit! (udu/update-password data opts))))
|
||||
|
||||
:else (throw (ex-info "unexpected" {:error error}))))
|
||||
(s/def ::password-1 ::fm/not-empty-string)
|
||||
(s/def ::password-2 ::fm/not-empty-string)
|
||||
(s/def ::password-old ::fm/not-empty-string)
|
||||
|
||||
(on-success [_]
|
||||
(st/emit! (um/info (tr "settings.password.password-saved"))))]
|
||||
|
||||
(dom/prevent-default event)
|
||||
(let [data (:clean-data form)
|
||||
opts {:on-success on-success
|
||||
:on-error on-error}]
|
||||
(st/emit! (udu/update-password data opts)))))
|
||||
|
||||
(s/defs ::password-form
|
||||
(s/dict :password-1 (s/&& ::s/string ::fm/not-empty-string)
|
||||
:password-2 (s/&& ::s/string ::fm/not-empty-string)
|
||||
:password-old (s/&& ::s/string ::fm/not-empty-string)))
|
||||
(s/def ::password-form
|
||||
(s/keys :req-un [::password-1
|
||||
::password-2
|
||||
::password-old]))
|
||||
|
||||
(mf/defc password-form
|
||||
[props]
|
||||
|
@ -54,7 +56,8 @@
|
|||
:on-blur (fm/on-input-blur form :password-old)
|
||||
:on-change (fm/on-input-change form :password-old)
|
||||
:placeholder (tr "settings.password.old-password")}]
|
||||
[:& fm/field-error {:form form :field :password-old}]
|
||||
|
||||
[:& fm/field-error {:form form :field :password-old :type ::api}]
|
||||
|
||||
[:input.input-text
|
||||
{:type "password"
|
||||
|
@ -64,7 +67,7 @@
|
|||
:on-blur (fm/on-input-blur form :password-1)
|
||||
:on-change (fm/on-input-change form :password-1)
|
||||
:placeholder (tr "settings.password.new-password")}]
|
||||
[:& fm/field-error {:form form :field :password-1}]
|
||||
;; [:& fm/field-error {:form form :field :password-1}]
|
||||
|
||||
[:input.input-text
|
||||
{:type "password"
|
||||
|
@ -74,7 +77,7 @@
|
|||
:on-blur (fm/on-input-blur form :password-2)
|
||||
:on-change (fm/on-input-change form :password-2)
|
||||
:placeholder (tr "settings.password.confirm-password")}]
|
||||
[:& fm/field-error {:form form :field :password-2}]
|
||||
;; [:& fm/field-error {:form form :field :password-2}]
|
||||
|
||||
[:input.btn-primary
|
||||
{:type "submit"
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
|
||||
(ns uxbox.main.ui.settings.profile
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[lentes.core :as l]
|
||||
[rumext.alpha :as mf]
|
||||
[struct.alpha :as s]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.main.data.users :as udu]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.util.data :refer [read-string]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.forms :as fm]
|
||||
[uxbox.util.forms2 :as fm]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.interop :refer [iterable->seq]]
|
||||
[uxbox.util.messages :as um]))
|
||||
|
@ -32,11 +32,16 @@
|
|||
(-> (l/key :profile)
|
||||
(l/derive st/state)))
|
||||
|
||||
(s/defs ::profile-form
|
||||
(s/dict :fullname (s/&& ::s/string ::fm/not-empty-string)
|
||||
:username (s/&& ::s/string ::fm/not-empty-string)
|
||||
:language (s/&& ::s/string ::fm/not-empty-string)
|
||||
:email ::s/email))
|
||||
(s/def ::fullname ::fm/not-empty-string)
|
||||
(s/def ::username ::fm/not-empty-string)
|
||||
(s/def ::language ::fm/not-empty-string)
|
||||
(s/def ::email ::fm/email)
|
||||
|
||||
(s/def ::profile-form
|
||||
(s/keys :req-un [::fullname
|
||||
::username
|
||||
::language
|
||||
::email]))
|
||||
|
||||
(defn- on-error
|
||||
[error form]
|
||||
|
@ -58,7 +63,6 @@
|
|||
|
||||
(defn- on-submit
|
||||
[event form]
|
||||
(prn "on-submit" form)
|
||||
(dom/prevent-default event)
|
||||
(let [data (:clean-data form)
|
||||
on-success #(st/emit! (um/info (tr "settings.profile.profile-saved")))
|
||||
|
|
|
@ -41,14 +41,12 @@
|
|||
(watch [_ state stream]
|
||||
(let [pid (get-in state [:workspace :current])
|
||||
selected (get-in state [:workspace pid :selected])]
|
||||
(prn "start-move-selected" selected)
|
||||
(rx/from-coll (map start-move selected))))))
|
||||
|
||||
(defn on-mouse-down
|
||||
[event {:keys [id type] :as shape} selected]
|
||||
(let [selected? (contains? selected id)
|
||||
drawing? @refs/selected-drawing-tool]
|
||||
(prn "on-mouse-down" id type selected? (= type :canvas))
|
||||
(when-not (:blocked shape)
|
||||
(cond
|
||||
drawing?
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
(common/on-mouse-down event shape selected))
|
||||
(on-double-click [event]
|
||||
(when selected?
|
||||
(prn "path-component$on-double-click")
|
||||
(st/emit! (dw/start-edition-mode (:id shape)))))]
|
||||
[:g.shape {:class (when selected? "selected")
|
||||
:on-double-click on-double-click
|
||||
|
|
145
frontend/src/uxbox/util/forms2.cljs
Normal file
145
frontend/src/uxbox/util/forms2.cljs
Normal file
|
@ -0,0 +1,145 @@
|
|||
;; 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>
|
||||
|
||||
(ns uxbox.util.forms2
|
||||
(:refer-clojure :exclude [uuid])
|
||||
(:require
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[lentes.core :as l]
|
||||
[potok.core :as ptk]
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.i18n :refer [tr]]))
|
||||
|
||||
;; --- 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- translate-error-type
|
||||
[name]
|
||||
"errors.undefined-error")
|
||||
|
||||
(defn- interpret-problem
|
||||
[acc {:keys [path pred val via in] :as problem}]
|
||||
;; (prn "interpret-problem" problem)
|
||||
(cond
|
||||
(and (empty? path)
|
||||
(list? pred)
|
||||
(= (first (last pred)) 'cljs.core/contains?))
|
||||
(let [path (conj path (last (last pred)))]
|
||||
(assoc-in acc path {:name ::missing :type :builtin}))
|
||||
|
||||
(and (not (empty? path))
|
||||
(not (empty? via)))
|
||||
(assoc-in acc path {:name (last via) :type :builtin})
|
||||
|
||||
:else acc))
|
||||
|
||||
(defn use-form
|
||||
[spec initial]
|
||||
(let [[state update-state] (mf/useState {:data (if (fn? initial) (initial) initial)
|
||||
:errors {}
|
||||
:touched {}})
|
||||
clean-data (s/conform spec (:data state))
|
||||
problems (when (= ::s/invalid clean-data)
|
||||
(::s/problems (s/explain-data spec (:data state))))
|
||||
|
||||
|
||||
errors (merge (reduce interpret-problem {} problems)
|
||||
(:errors state))]
|
||||
(-> (assoc state
|
||||
:errors errors
|
||||
:clean-data (when (not= clean-data ::s/invalid) clean-data)
|
||||
:valid (and (empty? errors)
|
||||
(not= clean-data ::s/invalid)))
|
||||
(impl-mutator update-state))))
|
||||
|
||||
(defn on-input-change
|
||||
[{:keys [data] :as form} field]
|
||||
(fn [event]
|
||||
(let [target (dom/get-target event)
|
||||
value (dom/get-value target)]
|
||||
(swap! form (fn [state]
|
||||
(-> state
|
||||
(assoc-in [:data field] value)
|
||||
(update :errors dissoc field)))))))
|
||||
|
||||
(defn on-input-blur
|
||||
[{:keys [touched] :as form} field]
|
||||
(fn [event]
|
||||
(let [target (dom/get-target event)]
|
||||
(when-not (get touched field)
|
||||
(swap! form assoc-in [:touched field] true)))))
|
||||
|
||||
;; --- Helper Components
|
||||
|
||||
(mf/defc field-error
|
||||
[{:keys [form field type]
|
||||
:or {only (constantly true)}
|
||||
:as props}]
|
||||
(let [touched? (get-in form [:touched field])
|
||||
{:keys [message code] :as error} (get-in form [:errors field])]
|
||||
(when (and touched? error
|
||||
(cond
|
||||
(nil? type) true
|
||||
(keyword? type) (= (:type error) type)
|
||||
(ifn? type) (type (:type error))
|
||||
:else false))
|
||||
(prn "field-error" error)
|
||||
[:ul.form-errors
|
||||
[:li {:key code} (tr message)]])))
|
||||
|
||||
(defn error-class
|
||||
[form field]
|
||||
(when (and (get-in form [:errors field])
|
||||
(get-in form [:touched field]))
|
||||
"invalid"))
|
||||
|
||||
;; --- Form Validation Api
|
||||
|
||||
;; --- Form Specs and Conformers
|
||||
|
||||
(def ^:private email-re
|
||||
#"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$")
|
||||
|
||||
(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 ::not-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 %))))
|
|
@ -25,11 +25,34 @@
|
|||
|
||||
(def +animation-timeout+ 600)
|
||||
|
||||
;; --- Message Event
|
||||
;; --- Main API
|
||||
|
||||
(declare hide)
|
||||
(declare show)
|
||||
(declare show?)
|
||||
|
||||
(defn error
|
||||
[message & {:keys [timeout] :or {timeout 3000}}]
|
||||
(show {:content message
|
||||
:type :error
|
||||
:timeout timeout}))
|
||||
|
||||
(defn info
|
||||
[message & {:keys [timeout] :or {timeout 3000}}]
|
||||
(show {:content message
|
||||
:type :info
|
||||
:timeout timeout}))
|
||||
|
||||
(defn dialog
|
||||
[message & {:keys [on-accept on-cancel]}]
|
||||
(show {:content message
|
||||
:on-accept on-accept
|
||||
:on-cancel on-cancel
|
||||
:timeout js/Number.MAX_SAFE_INTEGER
|
||||
:type :dialog}))
|
||||
|
||||
;; --- Show Event
|
||||
|
||||
(defn show
|
||||
[data]
|
||||
(reify
|
||||
|
@ -53,47 +76,19 @@
|
|||
[v]
|
||||
(= ::show (ptk/type v)))
|
||||
|
||||
(defn error
|
||||
[message & {:keys [timeout] :or {timeout 3000}}]
|
||||
(show {:content message
|
||||
:type :error
|
||||
:timeout timeout}))
|
||||
|
||||
(defn info
|
||||
[message & {:keys [timeout] :or {timeout 3000}}]
|
||||
(show {:content message
|
||||
:type :info
|
||||
:timeout timeout}))
|
||||
|
||||
(defn dialog
|
||||
[message & {:keys [on-accept on-cancel]}]
|
||||
(show {:content message
|
||||
:on-accept on-accept
|
||||
:on-cancel on-cancel
|
||||
:timeout js/Number.MAX_SAFE_INTEGER
|
||||
:type :dialog}))
|
||||
|
||||
;; --- Hide Message
|
||||
;; --- Hide Event
|
||||
|
||||
(defn hide
|
||||
[]
|
||||
(let [canceled? (volatile! {})]
|
||||
(reify
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :message
|
||||
(fn [v]
|
||||
(if (nil? v)
|
||||
(do (vreset! canceled? true) nil)
|
||||
(assoc v :state :hide)))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(if @canceled?
|
||||
(rx/empty)
|
||||
(->> (rx/of #(dissoc % :message))
|
||||
(rx/delay +animation-timeout+)))))))
|
||||
(reify
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :message assoc :state :hide))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rx/of #(dissoc % :message))
|
||||
(rx/delay +animation-timeout+)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; UI Components
|
||||
|
@ -145,6 +140,7 @@
|
|||
|
||||
(mf/defc messages-widget
|
||||
[{:keys [message] :as props}]
|
||||
(prn "messages-widget" props)
|
||||
(case (:type message)
|
||||
:error (mf/element notification-box props)
|
||||
:info (mf/element notification-box props)
|
||||
|
|
|
@ -42,6 +42,12 @@
|
|||
(s/def ::uuid uuid?)
|
||||
(s/def ::email email?)
|
||||
(s/def ::color color?)
|
||||
(s/def ::string string?)
|
||||
(s/def ::number number?)
|
||||
(s/def ::positive pos?)
|
||||
(s/def ::inst inst?)
|
||||
(s/def ::keyword keyword?)
|
||||
(s/def ::fn fn?)
|
||||
|
||||
;; --- Public Api
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue