mirror of
https://github.com/penpot/penpot.git
synced 2025-01-06 14:50:20 -05:00
Validation in modal
This commit is contained in:
parent
168a5d57d4
commit
0a73c3aa95
2 changed files with 67 additions and 5 deletions
|
@ -7,14 +7,17 @@
|
|||
(ns app.main.ui.workspace.tokens.modal
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
["lodash.debounce" :as debounce]
|
||||
[app.common.data :as d]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.tokens :as dt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.workspace.tokens.common :as tokens.common]
|
||||
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
||||
[app.util.dom :as dom]
|
||||
[okulary.core :as l]
|
||||
[promesa.core :as p]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn calculate-position
|
||||
|
@ -41,12 +44,47 @@
|
|||
[key (:value field)]) fields)
|
||||
(into {})))
|
||||
|
||||
(defn fields-to-token
|
||||
"Converts field to token value that will be stored and processed.
|
||||
Handles a simple token token type for now."
|
||||
[token-type fields]
|
||||
(case token-type
|
||||
(first fields)))
|
||||
|
||||
;; https://dev.to/haseeb1009/the-useevent-hook-1c8l
|
||||
(defn use-event-callback
|
||||
[f]
|
||||
(let [ref (mf/use-ref)]
|
||||
(mf/use-layout-effect
|
||||
(fn []
|
||||
(reset! ref f)
|
||||
js/undefined))
|
||||
(mf/use-callback (fn [& args] (some-> @ref (apply args))) [])))
|
||||
|
||||
(defn use-promise-debounce [fn+ on-success on-err]
|
||||
(let [debounce-promise (mf/use-ref nil)
|
||||
callback-fn (fn []
|
||||
(let [id (random-uuid)]
|
||||
(mf/set-ref-val! debounce-promise id)
|
||||
(-> (fn+)
|
||||
(p/then (fn [result]
|
||||
(js/console.log "@debounce-promise id" @debounce-promise id)
|
||||
(when (= @debounce-promise id)
|
||||
(js/console.log "update" result)
|
||||
(on-success result))))
|
||||
(p/catch on-err))))
|
||||
debounced-fn (debounce callback-fn)]
|
||||
debounced-fn))
|
||||
|
||||
(mf/defc tokens-properties-form
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [token-type x y position fields token]}]
|
||||
(let [vport (mf/deref viewport)
|
||||
[{:keys [token-type x y position fields token] :as args}]
|
||||
(let [tokens (sd/use-resolved-workspace-tokens {:debug? true})
|
||||
vport (mf/deref viewport)
|
||||
style (calculate-position vport position x y)
|
||||
|
||||
resolved-value (mf/use-state (get-in tokens [(:id token) :value]))
|
||||
|
||||
name (mf/use-var (or (:name token) ""))
|
||||
on-update-name #(reset! name (dom/get-target-val %))
|
||||
|
||||
|
@ -60,8 +98,15 @@
|
|||
fields)
|
||||
state (mf/use-state initial-fields)
|
||||
|
||||
debounced-update (use-promise-debounce sd/resolve-tokens+
|
||||
(fn [tokens]
|
||||
(let [value (get-in tokens [(:id token) :value])]
|
||||
(reset! resolved-value value)))
|
||||
#(reset! resolved-value nil))
|
||||
|
||||
on-update-state-field (fn [idx e]
|
||||
(let [value (dom/get-target-val e)]
|
||||
(debounced-update)
|
||||
(swap! state assoc-in [idx :value] value)))
|
||||
|
||||
on-submit (fn [e]
|
||||
|
@ -76,7 +121,6 @@
|
|||
(:id token) (assoc :id (:id token)))]
|
||||
(st/emit! (dt/add-token token))
|
||||
(modal/hide!)))]
|
||||
|
||||
[:form
|
||||
{:class (stl/css :shadow)
|
||||
:style (clj->js style)
|
||||
|
@ -86,13 +130,17 @@
|
|||
:input-props {:default-value @name
|
||||
:auto-focus true
|
||||
:on-change on-update-name}}]
|
||||
(for [[idx {:keys [type label]}] (d/enumerate @state)]
|
||||
(for [[idx {:keys [label type]}] (d/enumerate @state)]
|
||||
[:* {:key (str "form-field-" idx)}
|
||||
(case type
|
||||
:box-shadow [:p "TODO BOX SHADOW"]
|
||||
[:& tokens.common/labeled-input {:label label
|
||||
[:& tokens.common/labeled-input {:label "Value"
|
||||
:input-props {:default-value @token-value
|
||||
:on-change #(on-update-state-field idx %)}}])])
|
||||
(when (and @resolved-value
|
||||
(not= @resolved-value (:value (first @state))))
|
||||
[:div {:class (stl/css :resolved-value)}
|
||||
[:p @resolved-value]])
|
||||
[:& tokens.common/labeled-input {:label "Description"
|
||||
:input-props {:default-value @description
|
||||
:on-change #(on-update-description %)}}]
|
||||
|
|
|
@ -19,6 +19,20 @@
|
|||
gap: $s-8;
|
||||
}
|
||||
|
||||
.resolved-value {
|
||||
@include bodySmallTypography;
|
||||
padding: $s-4 $s-6;
|
||||
font-weight: medium;
|
||||
|
||||
color: var(--color-foreground-primary);
|
||||
border: 1px solid color-mix(in hsl, var(--color-foreground-secondary) 30%, transparent);
|
||||
|
||||
p {
|
||||
font-size: $fs-12;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.shadow {
|
||||
@extend .modal-container-base;
|
||||
@include menuShadow;
|
||||
|
|
Loading…
Reference in a new issue