mirror of
https://github.com/penpot/penpot.git
synced 2025-05-11 21:46:38 +02:00
:constructor: Initial work on forms refactor.
This commit is contained in:
parent
d147099e92
commit
263da4cc35
11 changed files with 327 additions and 355 deletions
|
@ -7,10 +7,11 @@
|
||||||
environ/environ {:mvn/version "1.1.0"}
|
environ/environ {:mvn/version "1.1.0"}
|
||||||
metosin/reitit-core {:mvn/version "0.3.9"}
|
metosin/reitit-core {:mvn/version "0.3.9"}
|
||||||
|
|
||||||
|
funcool/struct {:mvn/version "1.4.0"}
|
||||||
funcool/beicon {:mvn/version "5.1.0"}
|
funcool/beicon {:mvn/version "5.1.0"}
|
||||||
funcool/cuerdas {:mvn/version "2.2.0"}
|
funcool/cuerdas {:mvn/version "2.2.0"}
|
||||||
funcool/lentes {:mvn/version "1.3.0-SNAPSHOT"}
|
funcool/lentes {:mvn/version "1.3.0-SNAPSHOT"}
|
||||||
funcool/potok {:mvn/version "2.4.0"}
|
funcool/potok {:mvn/version "2.5.0"}
|
||||||
funcool/promesa {:mvn/version "3.0.0-SNAPSHOT"}
|
funcool/promesa {:mvn/version "3.0.0-SNAPSHOT"}
|
||||||
funcool/rumext {:mvn/version "2.0.0-SNAPSHOT"}
|
funcool/rumext {:mvn/version "2.0.0-SNAPSHOT"}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[lentes.core :as l]
|
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.main.repo :as rp]
|
[uxbox.main.repo :as rp]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
|
[uxbox.util.data :refer [index-by-id]]
|
||||||
[uxbox.util.spec :as us]
|
[uxbox.util.spec :as us]
|
||||||
[uxbox.util.timers :as ts]
|
[uxbox.util.timers :as ts]
|
||||||
[uxbox.util.data :refer [index-by-id]]))
|
[uxbox.util.uuid :as uuid]))
|
||||||
|
|
||||||
;; --- Specs
|
;; --- Specs
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.repo :as rp]
|
[uxbox.main.repo :as rp]
|
||||||
[uxbox.main.data.pages :as udp]
|
[uxbox.main.data.pages :as udp]
|
||||||
|
[uxbox.util.uuid :as uuid]
|
||||||
[uxbox.util.spec :as us]
|
[uxbox.util.spec :as us]
|
||||||
[uxbox.util.time :as dt]
|
[uxbox.util.time :as dt]
|
||||||
[uxbox.util.router :as rt]))
|
[uxbox.util.router :as rt]))
|
||||||
|
|
|
@ -7,45 +7,27 @@
|
||||||
|
|
||||||
(ns uxbox.main.ui.auth.login
|
(ns uxbox.main.ui.auth.login
|
||||||
(:require
|
(:require
|
||||||
[cljs.spec.alpha :as s]
|
|
||||||
[cuerdas.core :as str]
|
|
||||||
[lentes.core :as l]
|
|
||||||
[rumext.core :as mx]
|
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[uxbox.builtins.icons :as i]
|
[uxbox.builtins.icons :as i]
|
||||||
[uxbox.config :as cfg]
|
[uxbox.config :as cfg]
|
||||||
[uxbox.main.data.auth :as da]
|
[uxbox.main.data.auth :as da]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.ui.messages :refer [messages-widget]]
|
[uxbox.main.ui.messages :refer [messages-widget]]
|
||||||
[uxbox.main.ui.navigation :as nav]
|
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.forms :as fm]
|
[uxbox.util.forms :as fm]
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer [tr]]
|
||||||
[uxbox.util.router :as rt]))
|
[uxbox.util.router :as rt]))
|
||||||
|
|
||||||
(def form-data (fm/focus-data :login st/state))
|
(def login-form-spec
|
||||||
(def form-errors (fm/focus-errors :login st/state))
|
{:username [fm/required fm/string fm/non-empty-string]
|
||||||
|
:password [fm/required fm/string fm/non-empty-string]})
|
||||||
(def assoc-value (partial fm/assoc-value :login))
|
|
||||||
(def assoc-errors (partial fm/assoc-errors :login))
|
|
||||||
(def clear-form (partial fm/clear-form :login))
|
|
||||||
|
|
||||||
(s/def ::username ::fm/non-empty-string)
|
|
||||||
(s/def ::password ::fm/non-empty-string)
|
|
||||||
|
|
||||||
(s/def ::login-form
|
|
||||||
(s/keys :req-un [::username ::password]))
|
|
||||||
|
|
||||||
(defn- on-change
|
|
||||||
[event field]
|
|
||||||
(let [value (dom/event->value event)]
|
|
||||||
(st/emit! (assoc-value field value))))
|
|
||||||
|
|
||||||
(defn- on-submit
|
(defn- on-submit
|
||||||
[event data]
|
[event form]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (da/login {:username (:username data)
|
(let [{:keys [username password]} (:clean-data form)]
|
||||||
:password (:password data)})))
|
(st/emit! (da/login {:username username
|
||||||
|
:password password}))))
|
||||||
|
|
||||||
(mf/defc demo-warning
|
(mf/defc demo-warning
|
||||||
[_]
|
[_]
|
||||||
|
@ -56,33 +38,37 @@
|
||||||
[:strong "DO NOT USE"] " for real work, " [:br]
|
[:strong "DO NOT USE"] " for real work, " [:br]
|
||||||
" the projects will be periodicaly wiped."]])
|
" the projects will be periodicaly wiped."]])
|
||||||
|
|
||||||
|
|
||||||
(mf/defc login-form
|
(mf/defc login-form
|
||||||
[]
|
[]
|
||||||
(let [data (mf/deref form-data)
|
(let [{:keys [data] :as form} (fm/use-form {:initial {}
|
||||||
valid? (fm/valid? ::login-form data)]
|
:spec login-form-spec})]
|
||||||
[:form {:on-submit #(on-submit % data)}
|
[:form {:on-submit #(on-submit % form)}
|
||||||
[:div.login-content
|
[:div.login-content
|
||||||
(when cfg/isdemo
|
(when cfg/isdemo
|
||||||
[:& demo-warning])
|
[:& demo-warning])
|
||||||
|
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "email"
|
{:name "username"
|
||||||
:tab-index "2"
|
:tab-index "2"
|
||||||
:value (:username data "")
|
:value (:username data "")
|
||||||
:on-change #(on-change % :username)
|
:on-blur (fm/on-input-blur form)
|
||||||
|
:on-change (fm/on-input-change form)
|
||||||
:placeholder (tr "auth.email-or-username")
|
:placeholder (tr "auth.email-or-username")
|
||||||
:type "text"}]
|
:type "text"}]
|
||||||
[:input.input-text
|
[:input.input-text
|
||||||
{:name "password"
|
{:name "password"
|
||||||
:tab-index "3"
|
:tab-index "3"
|
||||||
:value (:password data "")
|
:value (:password data "")
|
||||||
:on-change #(on-change % :password)
|
:on-blur (fm/on-input-blur form)
|
||||||
|
:on-change (fm/on-input-change form)
|
||||||
:placeholder (tr "auth.password")
|
:placeholder (tr "auth.password")
|
||||||
:type "password"}]
|
:type "password"}]
|
||||||
[:input.btn-primary
|
[:input.btn-primary
|
||||||
{:name "login"
|
{:name "login"
|
||||||
:tab-index "4"
|
:tab-index "4"
|
||||||
:class (when-not valid? "btn-disabled")
|
:class (when (:errors form) "btn-disabled")
|
||||||
:disabled (not valid?)
|
:disabled (boolean (:errors form))
|
||||||
:value (tr "auth.signin")
|
:value (tr "auth.signin")
|
||||||
:type "submit"}]
|
:type "submit"}]
|
||||||
[:div.login-links
|
[:div.login-links
|
||||||
|
@ -93,12 +79,8 @@
|
||||||
:tab-index "6"}
|
:tab-index "6"}
|
||||||
(tr "auth.no-account")]]]]))
|
(tr "auth.no-account")]]]]))
|
||||||
|
|
||||||
|
|
||||||
;; {:mixins [mx/static (fm/clear-mixin st/store :login)]}
|
|
||||||
|
|
||||||
(mf/defc login-page
|
(mf/defc login-page
|
||||||
[]
|
[]
|
||||||
(mf/use-effect (constantly #(st/emit! (fm/clear-form :login))))
|
|
||||||
[:div.login
|
[:div.login
|
||||||
[:div.login-body
|
[:div.login-body
|
||||||
(messages-widget)
|
(messages-widget)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
[uxbox.main.ui.modal :as modal]
|
[uxbox.main.ui.modal :as modal]
|
||||||
[uxbox.main.ui.keyboard :as kbd]
|
[uxbox.main.ui.keyboard :as kbd]
|
||||||
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
||||||
|
[uxbox.main.ui.dashboard.projects-forms :refer [create-project-dialog]]
|
||||||
[uxbox.util.data :refer [read-string]]
|
[uxbox.util.data :refer [read-string]]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.i18n :as t :refer [tr]]
|
[uxbox.util.i18n :as t :refer [tr]]
|
||||||
|
@ -179,6 +180,7 @@
|
||||||
(sort-projects-by order))
|
(sort-projects-by order))
|
||||||
on-click #(do
|
on-click #(do
|
||||||
(dom/prevent-default %)
|
(dom/prevent-default %)
|
||||||
|
(modal/show! create-project-dialog {})
|
||||||
#_(udl/open! :create-project))]
|
#_(udl/open! :create-project))]
|
||||||
[:section.dashboard-grid
|
[:section.dashboard-grid
|
||||||
[:h2 (tr "ds.project-title")]
|
[:h2 (tr "ds.project-title")]
|
||||||
|
|
|
@ -1,151 +0,0 @@
|
||||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
;;
|
|
||||||
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
|
||||||
;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
|
||||||
|
|
||||||
(ns uxbox.main.ui.dashboard.projects-createform
|
|
||||||
(:require
|
|
||||||
[cljs.spec.alpha :as s]
|
|
||||||
[cuerdas.core :as str]
|
|
||||||
[lentes.core :as l]
|
|
||||||
[rumext.core :as mx]
|
|
||||||
[rumext.alpha :as mf]
|
|
||||||
[uxbox.builtins.icons :as i]
|
|
||||||
[uxbox.main.constants :as c]
|
|
||||||
[uxbox.main.data.lightbox :as udl]
|
|
||||||
[uxbox.main.data.projects :as udp]
|
|
||||||
[uxbox.main.store :as st]
|
|
||||||
[uxbox.main.ui.lightbox :as lbx]
|
|
||||||
[uxbox.util.data :refer [read-string parse-int]]
|
|
||||||
[uxbox.util.dom :as dom]
|
|
||||||
[uxbox.util.forms :as fm]
|
|
||||||
[uxbox.util.i18n :as t :refer [tr]]
|
|
||||||
[uxbox.util.router :as r]
|
|
||||||
[uxbox.util.time :as dt]))
|
|
||||||
|
|
||||||
(def form-data (fm/focus-data :create-project st/state))
|
|
||||||
(def form-errors (fm/focus-errors :create-project st/state))
|
|
||||||
(def assoc-value (partial fm/assoc-value :create-project))
|
|
||||||
(def clear-form (partial fm/clear-form :create-project))
|
|
||||||
|
|
||||||
(s/def ::name ::fm/non-empty-string)
|
|
||||||
(s/def ::layout ::fm/non-empty-string)
|
|
||||||
(s/def ::width number?)
|
|
||||||
(s/def ::height number?)
|
|
||||||
|
|
||||||
(s/def ::project-form
|
|
||||||
(s/keys :req-un [::name
|
|
||||||
::width
|
|
||||||
::height
|
|
||||||
::layout]))
|
|
||||||
|
|
||||||
;; --- Create Project Form
|
|
||||||
|
|
||||||
(mx/defc layout-input
|
|
||||||
[{:keys [::layout-id] :as data}]
|
|
||||||
(let [layout (get c/page-layouts layout-id)]
|
|
||||||
[:div
|
|
||||||
[:input {:type "radio"
|
|
||||||
:key layout-id
|
|
||||||
:id layout-id
|
|
||||||
:name "project-layout"
|
|
||||||
:value (:name layout)
|
|
||||||
:checked (= layout-id (:layout data))
|
|
||||||
:on-change #(st/emit! (assoc-value :layout layout-id)
|
|
||||||
(assoc-value :width (:width layout))
|
|
||||||
(assoc-value :height (:height layout)))}]
|
|
||||||
[:label {:value (:name layout)
|
|
||||||
:for layout-id}
|
|
||||||
(:name layout)]]))
|
|
||||||
|
|
||||||
(mx/defc layout-selector
|
|
||||||
[props]
|
|
||||||
[:div.input-radio.radio-primary
|
|
||||||
(layout-input (assoc props ::layout-id "mobile"))
|
|
||||||
(layout-input (assoc props ::layout-id "tablet"))
|
|
||||||
(layout-input (assoc props ::layout-id "notebook"))
|
|
||||||
(layout-input (assoc props ::layout-id "desktop"))])
|
|
||||||
|
|
||||||
(mf/def create-project-form
|
|
||||||
:mixins #{mf/reactive}
|
|
||||||
:render
|
|
||||||
(fn [own props]
|
|
||||||
(let [data (merge c/project-defaults (mf/react form-data))
|
|
||||||
valid? (fm/valid? ::project-form data)]
|
|
||||||
(letfn [(on-submit [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(when valid?
|
|
||||||
(st/emit! (udp/create-project data)
|
|
||||||
(udl/close-lightbox))))
|
|
||||||
|
|
||||||
(update-size [field e]
|
|
||||||
(let [value (dom/event->value e)
|
|
||||||
value (parse-int value)]
|
|
||||||
(st/emit! (assoc-value field value))))
|
|
||||||
|
|
||||||
(update-name [e]
|
|
||||||
(let [value (dom/event->value e)]
|
|
||||||
(st/emit! (assoc-value :name value))))
|
|
||||||
(swap-size []
|
|
||||||
(st/emit! (assoc-value :width (:height data))
|
|
||||||
(assoc-value :height (:width data))))]
|
|
||||||
[:form {:on-submit on-submit}
|
|
||||||
[:input#project-name.input-text
|
|
||||||
{:placeholder "New project name"
|
|
||||||
:type "text"
|
|
||||||
:value (:name data)
|
|
||||||
:auto-focus true
|
|
||||||
:on-change update-name}]
|
|
||||||
[:div.project-size
|
|
||||||
[:div.input-element.pixels
|
|
||||||
[:span "Width"]
|
|
||||||
[:input#project-witdh.input-text
|
|
||||||
{:placeholder "Width"
|
|
||||||
:type "number"
|
|
||||||
:min 0 ;;TODO check this value
|
|
||||||
:max 666666 ;;TODO check this value
|
|
||||||
:value (str (:width data))
|
|
||||||
:on-change (partial update-size :width)}]]
|
|
||||||
[:a.toggle-layout {:on-click swap-size} i/toggle]
|
|
||||||
[:div.input-element.pixels
|
|
||||||
[:span "Height"]
|
|
||||||
[:input#project-height.input-text
|
|
||||||
{:placeholder "Height"
|
|
||||||
:type "number"
|
|
||||||
:min 0 ;;TODO check this value
|
|
||||||
:max 666666 ;;TODO check this value
|
|
||||||
:value (str (:height data))
|
|
||||||
:on-change (partial update-size :height)}]]]
|
|
||||||
|
|
||||||
;; Layout selector
|
|
||||||
(layout-selector data)
|
|
||||||
|
|
||||||
;; Submit
|
|
||||||
[:input#project-btn.btn-primary
|
|
||||||
{:value "Go go go!"
|
|
||||||
:class (when-not valid? "btn-disabled")
|
|
||||||
:disabled (not valid?)
|
|
||||||
:type "submit"}]]))))
|
|
||||||
|
|
||||||
;; --- Create Project Lightbox
|
|
||||||
|
|
||||||
(mf/def create-project-lightbox
|
|
||||||
:will-unmount
|
|
||||||
(fn [own]
|
|
||||||
(st/emit! (fm/clear-form :create-project))
|
|
||||||
own)
|
|
||||||
|
|
||||||
:render
|
|
||||||
(fn [own]
|
|
||||||
[:div.lightbox-body
|
|
||||||
[:h3 "New project"]
|
|
||||||
(create-project-form)
|
|
||||||
[:a.close {:on-click #(st/emit! (udl/close-lightbox))}
|
|
||||||
i/close]]))
|
|
||||||
|
|
||||||
(defmethod lbx/render-lightbox :create-project
|
|
||||||
[_]
|
|
||||||
(create-project-lightbox))
|
|
||||||
|
|
97
frontend/src/uxbox/main/ui/dashboard/projects_forms.cljs
Normal file
97
frontend/src/uxbox/main/ui/dashboard/projects_forms.cljs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2015-2019 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
;; Copyright (c) 2015-2019 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
|
(ns uxbox.main.ui.dashboard.projects-forms
|
||||||
|
(:require
|
||||||
|
[cljs.spec.alpha :as s]
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[uxbox.builtins.icons :as i]
|
||||||
|
[uxbox.main.data.projects :as udp]
|
||||||
|
[uxbox.main.store :as st]
|
||||||
|
[uxbox.main.ui.modal :as modal]
|
||||||
|
[uxbox.util.dom :as dom]
|
||||||
|
[uxbox.util.forms :as fm]
|
||||||
|
[uxbox.util.i18n :as t :refer [tr]]))
|
||||||
|
|
||||||
|
(def project-form-spec
|
||||||
|
{:name [fm/required fm/string fm/non-empty-string]
|
||||||
|
:width [fm/required fm/number-str]
|
||||||
|
:height [fm/required fm/number-str]})
|
||||||
|
|
||||||
|
(def defaults
|
||||||
|
{:name ""
|
||||||
|
:width "1366"
|
||||||
|
:height "768"})
|
||||||
|
|
||||||
|
;; --- Create Project Form
|
||||||
|
|
||||||
|
(defn- on-submit
|
||||||
|
[event form]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(let [data (:clean-data form)]
|
||||||
|
(st/emit! (udp/create-project data))
|
||||||
|
(modal/hide!)))
|
||||||
|
|
||||||
|
(defn- swap-size
|
||||||
|
[event {:keys [data] :as form}]
|
||||||
|
(swap! data assoc
|
||||||
|
:width (:height data)
|
||||||
|
:height (:width data)))
|
||||||
|
|
||||||
|
(mf/defc create-project-form
|
||||||
|
[props]
|
||||||
|
(let [{:keys [data errors] :as form} (fm/use-form {:initial defaults :spec project-form-spec})]
|
||||||
|
[:form {:on-submit #(on-submit % form)}
|
||||||
|
[:input.input-text
|
||||||
|
{:placeholder "New project name"
|
||||||
|
:type "text"
|
||||||
|
:name "name"
|
||||||
|
:value (:name data)
|
||||||
|
:on-blur (fm/on-input-blur form)
|
||||||
|
:on-change (fm/on-input-change form)
|
||||||
|
:auto-focus true}]
|
||||||
|
[:div.project-size
|
||||||
|
[:div.input-element.pixels
|
||||||
|
[:span "Width"]
|
||||||
|
[:input#project-witdh.input-text
|
||||||
|
{:placeholder "Width"
|
||||||
|
:name "width"
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 5000
|
||||||
|
:on-blur (fm/on-input-blur form)
|
||||||
|
:on-change (fm/on-input-change form)
|
||||||
|
:value (:width data)}]]
|
||||||
|
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
|
||||||
|
[:div.input-element.pixels
|
||||||
|
[:span "Height"]
|
||||||
|
[:input#project-height.input-text
|
||||||
|
{:placeholder "Height"
|
||||||
|
:type "number"
|
||||||
|
:name "height"
|
||||||
|
:min 0
|
||||||
|
:max 5000
|
||||||
|
:on-blur (fm/on-input-blur form)
|
||||||
|
:on-change (fm/on-input-change form)
|
||||||
|
:value (:height data)}]]]
|
||||||
|
|
||||||
|
;; Submit
|
||||||
|
[:input#project-btn.btn-primary
|
||||||
|
{:value "Go go go!"
|
||||||
|
:class (when-not (:valid form) "btn-disabled")
|
||||||
|
:disabled (not (:valid form))
|
||||||
|
:type "submit"}]]))
|
||||||
|
|
||||||
|
;; --- Create Project Lightbox
|
||||||
|
|
||||||
|
(mf/defc create-project-dialog
|
||||||
|
[props]
|
||||||
|
[:div.lightbox-body
|
||||||
|
[:h3 "New project"]
|
||||||
|
[:& create-project-form]
|
||||||
|
[:a.close {:on-click modal/hide!} i/close]])
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
(ns uxbox.main.ui.workspace.sidebar.sitemap
|
(ns uxbox.main.ui.workspace.sidebar.sitemap
|
||||||
(:require
|
(:require
|
||||||
;; [uxbox.main.data.lightbox :as udl]
|
[potok.core :as ptk]
|
||||||
;; [uxbox.main.ui.workspace.sidebar.sitemap-pageform]
|
[beicon.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
@ -19,6 +19,7 @@
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
[uxbox.main.ui.confirm :refer [confirm-dialog]]
|
||||||
[uxbox.main.ui.modal :as modal]
|
[uxbox.main.ui.modal :as modal]
|
||||||
|
[uxbox.main.ui.workspace.sidebar.sitemap-forms :refer [page-form-dialog]]
|
||||||
[uxbox.main.ui.workspace.sortable :refer [use-sortable]]
|
[uxbox.main.ui.workspace.sortable :refer [use-sortable]]
|
||||||
[uxbox.util.data :refer [classnames]]
|
[uxbox.util.data :refer [classnames]]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
|
@ -30,17 +31,15 @@
|
||||||
(mf/defc page-item
|
(mf/defc page-item
|
||||||
[{:keys [page index deletable? selected?] :as props}]
|
[{:keys [page index deletable? selected?] :as props}]
|
||||||
(letfn [(on-edit [event]
|
(letfn [(on-edit [event]
|
||||||
#_(udl/open! :page-form {:page page}))
|
(modal/show! page-form-dialog {:page page}))
|
||||||
(delete []
|
(delete []
|
||||||
(let [next #(st/emit! (dp/go-to (:project page)))]
|
(st/emit! (dw/delete-page (:id page))))
|
||||||
(st/emit! (udp/delete-page (:id page) next))))
|
|
||||||
|
|
||||||
(on-delete [event]
|
(on-delete [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(modal/show! confirm-dialog {:on-accept delete}))
|
(modal/show! confirm-dialog {:on-accept delete}))
|
||||||
(on-drop [item monitor]
|
(on-drop [item monitor]
|
||||||
(st/emit! (udp/reorder-pages (:project page))))
|
(st/emit! (udp/rehash-pages (:project page))))
|
||||||
(on-hover [item monitor]
|
(on-hover [item monitor]
|
||||||
(st/emit! (udp/move-page {:project-id (:project-id item)
|
(st/emit! (udp/move-page {:project-id (:project-id item)
|
||||||
:page-id (:page-id item)
|
:page-id (:page-id item)
|
||||||
|
@ -99,7 +98,7 @@
|
||||||
:fn #(-> (l/in [:projects project-id])
|
:fn #(-> (l/in [:projects project-id])
|
||||||
(l/derive st/state))})
|
(l/derive st/state))})
|
||||||
project (mf/deref project-iref)
|
project (mf/deref project-iref)
|
||||||
;; create #(udl/open! :page-form {:page {:project project-id}})
|
create #(modal/show! page-form-dialog {:page {:project project-id}})
|
||||||
close #(st/emit! (dw/toggle-flag :sitemap))]
|
close #(st/emit! (dw/toggle-flag :sitemap))]
|
||||||
[:div.sitemap.tool-window
|
[:div.sitemap.tool-window
|
||||||
[:div.tool-window-bar
|
[:div.tool-window-bar
|
||||||
|
@ -109,6 +108,6 @@
|
||||||
[:div.tool-window-content
|
[:div.tool-window-content
|
||||||
[:div.project-title
|
[:div.project-title
|
||||||
[:span (:name project)]
|
[:span (:name project)]
|
||||||
[:div.add-page #_{:on-click create} i/close]]
|
[:div.add-page {:on-click create} i/close]]
|
||||||
[:& pages-list {:project project
|
[:& pages-list {:project project
|
||||||
:current-page-id current-page-id}]]]))
|
:current-page-id current-page-id}]]]))
|
||||||
|
|
103
frontend/src/uxbox/main/ui/workspace/sidebar/sitemap_forms.cljs
Normal file
103
frontend/src/uxbox/main/ui/workspace/sidebar/sitemap_forms.cljs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
|
(ns uxbox.main.ui.workspace.sidebar.sitemap-forms
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[uxbox.builtins.icons :as i]
|
||||||
|
[uxbox.main.constants :as c]
|
||||||
|
[uxbox.main.data.pages :as udp]
|
||||||
|
[uxbox.main.store :as st]
|
||||||
|
[uxbox.main.ui.modal :as modal]
|
||||||
|
[uxbox.util.dom :as dom]
|
||||||
|
[uxbox.util.forms :as fm]
|
||||||
|
[uxbox.util.i18n :refer [tr]]))
|
||||||
|
|
||||||
|
(def page-form-spec
|
||||||
|
{:id [fm/uuid]
|
||||||
|
:project [fm/uuid]
|
||||||
|
:name [fm/required fm/string fm/non-empty-string]
|
||||||
|
:width [fm/required fm/number-str]
|
||||||
|
:height [fm/required fm/number-str]})
|
||||||
|
|
||||||
|
(def defaults
|
||||||
|
{:name ""
|
||||||
|
:width "1366"
|
||||||
|
:height "768"})
|
||||||
|
|
||||||
|
(defn- on-submit
|
||||||
|
[event form]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(modal/hide!)
|
||||||
|
(let [data (:clean-data form)]
|
||||||
|
(if (nil? (:id data))
|
||||||
|
(st/emit! (udp/create-page data))
|
||||||
|
(st/emit! (udp/persist-page-update-form data)))))
|
||||||
|
|
||||||
|
(defn- swap-size
|
||||||
|
[event {:keys [data] :as form}]
|
||||||
|
(swap! data assoc
|
||||||
|
:width (:height data)
|
||||||
|
:height (:width data)))
|
||||||
|
|
||||||
|
(defn- initial-data
|
||||||
|
[page]
|
||||||
|
(merge {:name "" :width "1366" :height "768"}
|
||||||
|
(select-keys page [:name :id :project])
|
||||||
|
(select-keys (:metadata page) [:width :height])))
|
||||||
|
|
||||||
|
(mf/defc page-form
|
||||||
|
[{:keys [page] :as props}]
|
||||||
|
(let [{:keys [data errors] :as form} (fm/use-form {:initial #(initial-data page)
|
||||||
|
:spec page-form-spec})]
|
||||||
|
[:form {:on-submit #(on-submit % form)}
|
||||||
|
[:input.input-text
|
||||||
|
{:placeholder "Page name"
|
||||||
|
:type "text"
|
||||||
|
:name "name"
|
||||||
|
:on-blur (fm/on-input-blur form)
|
||||||
|
:on-change (fm/on-input-change form)
|
||||||
|
:value (:name data)
|
||||||
|
:auto-focus true}]
|
||||||
|
[:div.project-size
|
||||||
|
[:div.input-element.pixels
|
||||||
|
[:span "Width"]
|
||||||
|
[:input#project-witdh.input-text
|
||||||
|
{:placeholder "Width"
|
||||||
|
:name "width"
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 5000
|
||||||
|
:on-blur (fm/on-input-blur form)
|
||||||
|
:on-change (fm/on-input-change form)
|
||||||
|
:value (:width data)}]]
|
||||||
|
[:a.toggle-layout {:on-click #(swap-size % form)} i/toggle]
|
||||||
|
[:div.input-element.pixels
|
||||||
|
[:span "Height"]
|
||||||
|
[:input#project-height.input-text
|
||||||
|
{:placeholder "Height"
|
||||||
|
:name "height"
|
||||||
|
:type "number"
|
||||||
|
:min 0
|
||||||
|
:max 5000
|
||||||
|
:on-blur (fm/on-input-blur form)
|
||||||
|
:on-change (fm/on-input-change form)
|
||||||
|
:value (:height data)}]]]
|
||||||
|
[:input.btn-primary
|
||||||
|
{:value "Go go go!"
|
||||||
|
:type "submit"
|
||||||
|
:disabled (not (:valid form))}]]))
|
||||||
|
|
||||||
|
(mf/defc page-form-dialog
|
||||||
|
[{:keys [page] :as props}]
|
||||||
|
[:div.lightbox-body
|
||||||
|
(if (nil? (:id page))
|
||||||
|
[:h3 "New page"]
|
||||||
|
[:h3 "Edit page"])
|
||||||
|
[:& page-form {:page page}]
|
||||||
|
[:a.close {:on-click modal/hide!} i/close]])
|
||||||
|
|
|
@ -1,145 +0,0 @@
|
||||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
;;
|
|
||||||
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
|
||||||
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
|
||||||
|
|
||||||
(ns uxbox.main.ui.workspace.sidebar.sitemap-pageform
|
|
||||||
(:require [cljs.spec.alpha :as s :include-macros true]
|
|
||||||
[lentes.core :as l]
|
|
||||||
[uxbox.builtins.icons :as i]
|
|
||||||
[uxbox.main.store :as st]
|
|
||||||
[uxbox.main.constants :as c]
|
|
||||||
[uxbox.main.data.pages :as udp]
|
|
||||||
[uxbox.main.data.lightbox :as udl]
|
|
||||||
[uxbox.main.ui.lightbox :as lbx]
|
|
||||||
[uxbox.util.data :refer [parse-int]]
|
|
||||||
[uxbox.util.dom :as dom]
|
|
||||||
[uxbox.util.forms :as fm]
|
|
||||||
[uxbox.util.i18n :refer [tr]]
|
|
||||||
[uxbox.util.router :as r]
|
|
||||||
[rumext.core :as mx :include-macros true]))
|
|
||||||
|
|
||||||
|
|
||||||
(def form-data (fm/focus-data :workspace-page-form st/state))
|
|
||||||
(def form-errors (fm/focus-errors :workspace-page-form st/state))
|
|
||||||
|
|
||||||
(def assoc-value (partial fm/assoc-value :workspace-page-form))
|
|
||||||
(def assoc-error (partial fm/assoc-error :workspace-page-form))
|
|
||||||
(def clear-form (partial fm/clear-form :workspace-page-form))
|
|
||||||
|
|
||||||
;; --- Lightbox
|
|
||||||
|
|
||||||
(s/def ::name ::fm/non-empty-string)
|
|
||||||
(s/def ::layout ::fm/non-empty-string)
|
|
||||||
(s/def ::width number?)
|
|
||||||
(s/def ::height number?)
|
|
||||||
|
|
||||||
(s/def ::page-form
|
|
||||||
(s/keys :req-un [::name
|
|
||||||
::width
|
|
||||||
::height
|
|
||||||
::layout]))
|
|
||||||
|
|
||||||
(mx/defc layout-input
|
|
||||||
[data id]
|
|
||||||
(let [{:keys [id name width height]} (get c/page-layouts id)]
|
|
||||||
(letfn [(on-change [event]
|
|
||||||
(st/emit! (assoc-value :layout id)
|
|
||||||
(assoc-value :width width)
|
|
||||||
(assoc-value :height height)))]
|
|
||||||
[:div
|
|
||||||
[:input {:type "radio"
|
|
||||||
:id id
|
|
||||||
:name "project-layout"
|
|
||||||
:value id
|
|
||||||
:checked (when (= id (:layout data)) "checked")
|
|
||||||
:on-change on-change}]
|
|
||||||
[:label {:value id :for id} name]])))
|
|
||||||
|
|
||||||
(mx/defc page-form
|
|
||||||
{:mixins [mx/static mx/reactive]}
|
|
||||||
[{:keys [metadata id] :as page}]
|
|
||||||
(let [data (merge c/page-defaults
|
|
||||||
(select-keys page [:name :id :project])
|
|
||||||
(select-keys metadata [:width :height :layout])
|
|
||||||
(mx/react form-data))
|
|
||||||
valid? (fm/valid? ::page-form data)]
|
|
||||||
(letfn [(update-size [field e]
|
|
||||||
(let [value (dom/event->value e)
|
|
||||||
value (parse-int value)]
|
|
||||||
(st/emit! (assoc-value field value))))
|
|
||||||
(update-name [e]
|
|
||||||
(let [value (dom/event->value e)]
|
|
||||||
(st/emit! (assoc-value :name value))))
|
|
||||||
(toggle-sizes []
|
|
||||||
(let [{:keys [width height]} data]
|
|
||||||
(st/emit! (assoc-value :width width)
|
|
||||||
(assoc-value :height height))))
|
|
||||||
(on-cancel [e]
|
|
||||||
(dom/prevent-default e)
|
|
||||||
(udl/close!))
|
|
||||||
(on-save [e]
|
|
||||||
(dom/prevent-default e)
|
|
||||||
(udl/close!)
|
|
||||||
(if (nil? id)
|
|
||||||
(st/emit! (udp/create-page data))
|
|
||||||
(st/emit! (udp/persist-page-update-form id data))))]
|
|
||||||
[:form
|
|
||||||
[:input#project-name.input-text
|
|
||||||
{:placeholder "Page name"
|
|
||||||
:type "text"
|
|
||||||
:value (:name data "")
|
|
||||||
:auto-focus true
|
|
||||||
:on-change update-name}]
|
|
||||||
[:div.project-size
|
|
||||||
[:div.input-element.pixels
|
|
||||||
[:span "Width"]
|
|
||||||
[:input#project-witdh.input-text
|
|
||||||
{:placeholder "Width"
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 4000
|
|
||||||
:value (:width data)
|
|
||||||
:on-change #(update-size :width %)}]]
|
|
||||||
[:a.toggle-layout {:on-click toggle-sizes} i/toggle]
|
|
||||||
[:div.input-element.pixels
|
|
||||||
[:span "Height"]
|
|
||||||
[:input#project-height.input-text
|
|
||||||
{:placeholder "Height"
|
|
||||||
:type "number"
|
|
||||||
:min 0
|
|
||||||
:max 4000
|
|
||||||
:value (:height data)
|
|
||||||
:on-change #(update-size :height %)}]]]
|
|
||||||
|
|
||||||
[:div.input-radio.radio-primary
|
|
||||||
(layout-input data "mobile")
|
|
||||||
(layout-input data "tablet")
|
|
||||||
(layout-input data "notebook")
|
|
||||||
(layout-input data "desktop")]
|
|
||||||
|
|
||||||
[:input#project-btn.btn-primary
|
|
||||||
{:value "Go go go!"
|
|
||||||
:disabled (not valid?)
|
|
||||||
:on-click on-save
|
|
||||||
:type "button"}]])))
|
|
||||||
|
|
||||||
(mx/defc page-form-lightbox
|
|
||||||
{:mixins [mx/static (fm/clear-mixin st/store :workspace-page-form)]}
|
|
||||||
[{:keys [id] :as page}]
|
|
||||||
(letfn [(on-cancel [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(udl/close!))]
|
|
||||||
(let [creation? (nil? id)]
|
|
||||||
[:div.lightbox-body
|
|
||||||
(if creation?
|
|
||||||
[:h3 "New page"]
|
|
||||||
[:h3 "Edit page"])
|
|
||||||
(page-form page)
|
|
||||||
[:a.close {:on-click on-cancel} i/close]])))
|
|
||||||
|
|
||||||
(defmethod lbx/render-lightbox :page-form
|
|
||||||
[{:keys [page]}]
|
|
||||||
(page-form-lightbox page))
|
|
|
@ -5,15 +5,97 @@
|
||||||
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
|
||||||
(ns uxbox.util.forms
|
(ns uxbox.util.forms
|
||||||
|
(:refer-clojure :exclude [uuid])
|
||||||
(:require
|
(:require
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cljs.spec.alpha :as s :include-macros true]
|
[cljs.spec.alpha :as s :include-macros true]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[lentes.core :as l]
|
[lentes.core :as l]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[rumext.core :as mx :include-macros true]
|
[rumext.alpha :as mf]
|
||||||
|
[rumext.core :as mx]
|
||||||
|
[struct.core :as stt]
|
||||||
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.i18n :refer [tr]]))
|
[uxbox.util.i18n :refer [tr]]))
|
||||||
|
|
||||||
|
;; --- Main Api
|
||||||
|
|
||||||
|
(defn validate
|
||||||
|
[data spec]
|
||||||
|
(stt/validate data spec))
|
||||||
|
|
||||||
|
(defn valid?
|
||||||
|
[data spec]
|
||||||
|
(stt/valid? data spec))
|
||||||
|
|
||||||
|
;; --- Handlers Helpers
|
||||||
|
|
||||||
|
(defn- impl-mutator
|
||||||
|
[v update-fn]
|
||||||
|
(specify v
|
||||||
|
IReset
|
||||||
|
(-reset! [_ new-value]
|
||||||
|
(update-fn new-value))
|
||||||
|
|
||||||
|
ISwap
|
||||||
|
(-swap!
|
||||||
|
([self f] (update-fn f))
|
||||||
|
([self f x] (update-fn #(f % x)))
|
||||||
|
([self f x y] (update-fn #(f % x y)))
|
||||||
|
([self f x y more] (update-fn #(apply f % x y more))))))
|
||||||
|
|
||||||
|
(defn use-form
|
||||||
|
[{:keys [initial spec] :as opts}]
|
||||||
|
(let [[data update-data] (mf/useState initial)
|
||||||
|
[errors update-errors] (mf/useState nil)
|
||||||
|
[touched update-touched] (mf/useState {})
|
||||||
|
[errors' clean-data] (validate data spec)
|
||||||
|
|
||||||
|
data (impl-mutator data update-data)
|
||||||
|
errors (-> (merge {} errors' errors)
|
||||||
|
(impl-mutator update-errors))
|
||||||
|
touched (impl-mutator touched update-touched)]
|
||||||
|
{:clean-data clean-data
|
||||||
|
:touched touched
|
||||||
|
:data data
|
||||||
|
:errors errors
|
||||||
|
:valid (not (seq errors))}))
|
||||||
|
|
||||||
|
(defn on-input-change
|
||||||
|
[{:keys [data] :as form}]
|
||||||
|
(fn [event]
|
||||||
|
(let [target (dom/get-target event)
|
||||||
|
field (keyword (.-name target))
|
||||||
|
value (dom/get-value target)]
|
||||||
|
(swap! data assoc field value))))
|
||||||
|
|
||||||
|
(defn on-input-blur
|
||||||
|
[{:keys [touched] :as form}]
|
||||||
|
(fn [event]
|
||||||
|
(let [target (dom/get-target event)
|
||||||
|
field (keyword (.-name target))]
|
||||||
|
(when-not (get touched field)
|
||||||
|
(swap! touched assoc field true)))))
|
||||||
|
|
||||||
|
;; --- Additional Validators
|
||||||
|
|
||||||
|
(def non-empty-string
|
||||||
|
{:message "errors.empty-string"
|
||||||
|
:optional true
|
||||||
|
:validate #(not (str/empty? %))})
|
||||||
|
|
||||||
|
(def string (assoc stt/string :message "errors.should-be-string"))
|
||||||
|
(def number (assoc stt/number :message "errors.should-be-number"))
|
||||||
|
(def number-str (assoc stt/number-str :message "errors.should-be-number"))
|
||||||
|
(def integer (assoc stt/integer :message "errors.should-be-integer"))
|
||||||
|
(def integer-str (assoc stt/integer-str :message "errors.should-be-integer"))
|
||||||
|
(def required (assoc stt/required :message "errors.required"))
|
||||||
|
(def email (assoc stt/email :message "errors.should-be-valid-email"))
|
||||||
|
(def uuid (assoc stt/uuid :message "errors.should-be-uuid"))
|
||||||
|
(def uuid-str (assoc stt/uuid-str :message "errors.should-be-valid-uuid"))
|
||||||
|
|
||||||
|
;; DEPRECATED
|
||||||
|
|
||||||
;; --- Form Validation Api
|
;; --- Form Validation Api
|
||||||
|
|
||||||
(defn- interpret-problem
|
(defn- interpret-problem
|
||||||
|
@ -30,15 +112,16 @@
|
||||||
|
|
||||||
:else acc))
|
:else acc))
|
||||||
|
|
||||||
(defn validate
|
;; (defn validate
|
||||||
[spec data]
|
;; [spec data]
|
||||||
(when-not (s/valid? spec data)
|
;; (when-not (s/valid? spec data)
|
||||||
(let [report (s/explain-data spec data)]
|
;; (let [report (s/explain-data spec data)]
|
||||||
(reduce interpret-problem {} (::s/problems report)))))
|
;; (reduce interpret-problem {} (::s/problems report)))))
|
||||||
|
|
||||||
|
;; (defn valid?
|
||||||
|
;; [spec data]
|
||||||
|
;; (s/valid? spec data))
|
||||||
|
|
||||||
(defn valid?
|
|
||||||
[spec data]
|
|
||||||
(s/valid? spec data))
|
|
||||||
|
|
||||||
;; --- Form Specs and Conformers
|
;; --- Form Specs and Conformers
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue