mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 06:02:32 -05:00
♻️ Review create and edit modal
This commit is contained in:
parent
31b5f5cefa
commit
03ea5414be
12 changed files with 310 additions and 147 deletions
|
@ -279,7 +279,6 @@
|
|||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [data (get state :workspace-data)
|
||||
_ (prn "paso por aquí")
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/with-library-data data)
|
||||
(pcb/delete-token set-name token-name))]
|
||||
|
|
|
@ -130,3 +130,21 @@
|
|||
box-shadow: inset 0 0 #{px2rem(10)} #{px2rem(2)} rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
%base-button-action {
|
||||
--button-bg-color: transparent;
|
||||
--button-fg-color: var(--color-foreground-secondary);
|
||||
|
||||
--button-hover-bg-color: transparent;
|
||||
--button-hover-fg-color: var(--color-accent-primary);
|
||||
|
||||
--button-active-bg-color: var(--color-background-quaternary);
|
||||
|
||||
--button-disabled-bg-color: transparent;
|
||||
--button-disabled-fg-color: var(--color-accent-primary-muted);
|
||||
|
||||
--button-focus-bg-color: transparent;
|
||||
--button-focus-fg-color: var(--color-accent-primary);
|
||||
--button-focus-inner-ring-color: transparent;
|
||||
--button-focus-outer-ring-color: var(--color-accent-primary);
|
||||
}
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
[app.main.ui.ds.foundations.assets.icon :refer [icon* icon-list]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def button-variants (set '("primary" "secondary" "ghost" "destructive")))
|
||||
|
||||
|
||||
(def ^:private schema:icon-button
|
||||
[:map
|
||||
[:class {:optional true} :string]
|
||||
|
@ -22,7 +19,7 @@
|
|||
[:and :string [:fn #(contains? icon-list %)]]]
|
||||
[:aria-label :string]
|
||||
[:variant {:optional true}
|
||||
[:maybe [:enum "primary" "secondary" "ghost" "destructive"]]]])
|
||||
[:maybe [:enum "primary" "secondary" "ghost" "destructive" "action"]]]])
|
||||
|
||||
(mf/defc icon-button*
|
||||
{::mf/props :obj
|
||||
|
@ -33,6 +30,7 @@
|
|||
:icon-button-primary (= variant "primary")
|
||||
:icon-button-secondary (= variant "secondary")
|
||||
:icon-button-ghost (= variant "ghost")
|
||||
:icon-button-action (= variant "action")
|
||||
:icon-button-destructive (= variant "destructive")))
|
||||
props (mf/spread-props props {:class class :title aria-label})]
|
||||
[:> "button" props [:> icon* {:id icon :aria-label aria-label}]]))
|
|
@ -31,3 +31,7 @@
|
|||
.icon-button-destructive {
|
||||
@extend %base-button-destructive;
|
||||
}
|
||||
|
||||
.icon-button-action {
|
||||
@extend %base-button-action;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ export default {
|
|||
},
|
||||
disabled: { control: "boolean" },
|
||||
variant: {
|
||||
options: ["primary", "secondary", "ghost", "destructive"],
|
||||
options: ["primary", "secondary", "ghost", "destructive", "action"],
|
||||
control: { type: "select" },
|
||||
},
|
||||
},
|
||||
|
@ -59,6 +59,12 @@ export const Ghost = {
|
|||
},
|
||||
};
|
||||
|
||||
export const Action = {
|
||||
args: {
|
||||
variant: "action",
|
||||
},
|
||||
};
|
||||
|
||||
export const Destructive = {
|
||||
args: {
|
||||
variant: "destructive",
|
||||
|
|
|
@ -10,12 +10,16 @@
|
|||
["lodash.debounce" :as debounce]
|
||||
[app.common.colors :as c]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[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.components.color-bullet :refer [color-bullet]]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.workspace.colorpicker :as colorpicker]
|
||||
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]]
|
||||
[app.main.ui.workspace.tokens.common :as tokens.common]
|
||||
|
@ -25,6 +29,7 @@
|
|||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[app.main.ui.workspace.tokens.update :as wtu]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[malli.core :as m]
|
||||
[malli.error :as me]
|
||||
|
@ -124,7 +129,7 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
[name-ref token tokens callback & {:keys [timeout] :or {timeout 160}}]
|
||||
(let [timeout-id-ref (mf/use-ref nil)
|
||||
debounced-resolver-callback
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(mf/deps token callback tokens)
|
||||
(fn [value]
|
||||
(let [timeout-id (js/Symbol)
|
||||
|
@ -178,19 +183,22 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
[{:keys [result-or-errors]}]
|
||||
(let [{:keys [errors]} result-or-errors
|
||||
empty-message? (or (nil? result-or-errors)
|
||||
(wte/has-error-code? :error/empty-input errors))]
|
||||
[:div {:class (stl/css-case :resolved-value true
|
||||
:resolved-value-placeholder empty-message?
|
||||
:resolved-value-error (seq errors))}
|
||||
(cond
|
||||
empty-message? "Enter token value"
|
||||
errors (->> (wte/humanize-errors errors)
|
||||
(str/join "\n"))
|
||||
:else [:p result-or-errors])]))
|
||||
(wte/has-error-code? :error/empty-input errors))
|
||||
message (cond
|
||||
empty-message? (dm/str (tr "workspace.token.resolved-value") "-")
|
||||
errors (->> (wte/humanize-errors errors)
|
||||
(str/join "\n"))
|
||||
:else (dm/str (tr "workspace.token.resolved-value") result-or-errors))]
|
||||
[:> text* {:as "p"
|
||||
:typography "body-small"
|
||||
:class (stl/css-case :resolved-value true
|
||||
:resolved-value-placeholder empty-message?
|
||||
:resolved-value-error (seq errors))}
|
||||
message]))
|
||||
|
||||
(mf/defc form
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [token token-type]}]
|
||||
[{:keys [token token-type action selected-token-set-id]}]
|
||||
(let [validate-name? (mf/use-state (not (:id token)))
|
||||
token (or token {:type token-type})
|
||||
color? (wtt/color-token? token)
|
||||
|
@ -212,25 +220,31 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
;; Name
|
||||
name-ref (mf/use-var (:name token))
|
||||
name-errors (mf/use-state nil)
|
||||
validate-name (mf/use-callback
|
||||
(mf/deps selected-set-tokens-tree)
|
||||
(fn [value]
|
||||
(let [schema (token-name-schema {:token token
|
||||
:tokens-tree selected-set-tokens-tree})]
|
||||
(m/explain schema (finalize-name value)))))
|
||||
on-update-name-debounced (mf/use-callback
|
||||
(debounce (fn [e]
|
||||
(let [value (dom/get-target-val e)
|
||||
errors (validate-name value)]
|
||||
validate-name
|
||||
(mf/use-fn
|
||||
(mf/deps selected-set-tokens-tree)
|
||||
(fn [value]
|
||||
(let [schema (token-name-schema {:token token
|
||||
:tokens-tree selected-set-tokens-tree})]
|
||||
(m/explain schema (finalize-name value)))))
|
||||
|
||||
on-update-name-debounced
|
||||
(mf/use-fn
|
||||
(debounce (fn [e]
|
||||
(let [value (dom/get-target-val e)
|
||||
errors (validate-name value)]
|
||||
;; Prevent showing error when just going to another field on a new token
|
||||
(when-not (and validate-name? (str/empty? value))
|
||||
(reset! validate-name? false)
|
||||
(reset! name-errors errors))))))
|
||||
on-update-name (mf/use-callback
|
||||
(mf/deps on-update-name-debounced)
|
||||
(fn [e]
|
||||
(reset! name-ref (dom/get-target-val e))
|
||||
(on-update-name-debounced e)))
|
||||
(when-not (and validate-name? (str/empty? value))
|
||||
(reset! validate-name? false)
|
||||
(reset! name-errors errors))))))
|
||||
|
||||
on-update-name
|
||||
(mf/use-fn
|
||||
(mf/deps on-update-name-debounced)
|
||||
(fn [e]
|
||||
(reset! name-ref (dom/get-target-val e))
|
||||
(on-update-name-debounced e)))
|
||||
|
||||
valid-name-field? (and
|
||||
(not @name-errors)
|
||||
(valid-name? @name-ref))
|
||||
|
@ -241,27 +255,29 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
value-input-ref (mf/use-ref nil)
|
||||
value-ref (mf/use-var (:value token))
|
||||
token-resolve-result (mf/use-state (get-in resolved-tokens [(wtt/token-identifier token) :resolved-value]))
|
||||
set-resolve-value (mf/use-callback
|
||||
(fn [token-or-err]
|
||||
(let [error? (:errors token-or-err)
|
||||
v (if error?
|
||||
token-or-err
|
||||
(:resolved-value token-or-err))]
|
||||
(when color? (reset! color (if error? nil v)))
|
||||
(reset! token-resolve-result v))))
|
||||
set-resolve-value
|
||||
(mf/use-fn
|
||||
(fn [token-or-err]
|
||||
(let [error? (:errors token-or-err)
|
||||
v (if error?
|
||||
token-or-err
|
||||
(:resolved-value token-or-err))]
|
||||
(when color? (reset! color (if error? nil v)))
|
||||
(reset! token-resolve-result v))))
|
||||
on-update-value-debounced (use-debonced-resolve-callback name-ref token active-theme-tokens set-resolve-value)
|
||||
on-update-value (mf/use-callback
|
||||
on-update-value (mf/use-fn
|
||||
(mf/deps on-update-value-debounced)
|
||||
(fn [e]
|
||||
(let [value (dom/get-target-val e)]
|
||||
(reset! value-ref value)
|
||||
(on-update-value-debounced value))))
|
||||
on-update-color (mf/use-callback
|
||||
on-update-color (mf/use-fn
|
||||
(mf/deps on-update-value-debounced)
|
||||
(fn [hex-value]
|
||||
(reset! value-ref hex-value)
|
||||
(set! (.-value (mf/ref-val value-input-ref)) hex-value)
|
||||
(on-update-value-debounced hex-value)))
|
||||
|
||||
value-error? (seq (:errors @token-resolve-result))
|
||||
valid-value-field? (and
|
||||
(not value-error?)
|
||||
|
@ -270,17 +286,18 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
;; Description
|
||||
description-ref (mf/use-var (:description token))
|
||||
description-errors (mf/use-state nil)
|
||||
validate-descripion (mf/use-callback #(m/explain token-description-schema %))
|
||||
on-update-description-debounced (mf/use-callback
|
||||
validate-descripion (mf/use-fn #(m/explain token-description-schema %))
|
||||
on-update-description-debounced (mf/use-fn
|
||||
(debounce (fn [e]
|
||||
(let [value (dom/get-target-val e)
|
||||
errors (validate-descripion value)]
|
||||
(reset! description-errors errors)))))
|
||||
on-update-description (mf/use-callback
|
||||
(mf/deps on-update-description-debounced)
|
||||
(fn [e]
|
||||
(reset! description-ref (dom/get-target-val e))
|
||||
(on-update-description-debounced e)))
|
||||
on-update-description
|
||||
(mf/use-fn
|
||||
(mf/deps on-update-description-debounced)
|
||||
(fn [e]
|
||||
(reset! description-ref (dom/get-target-val e))
|
||||
(on-update-description-debounced e)))
|
||||
valid-description-field? (not @description-errors)
|
||||
|
||||
;; Form
|
||||
|
@ -288,41 +305,62 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
(not valid-value-field?)
|
||||
(not valid-description-field?))
|
||||
|
||||
on-submit (mf/use-callback
|
||||
(mf/deps validate-name validate-descripion token resolved-tokens)
|
||||
(fn [e]
|
||||
(dom/prevent-default e)
|
||||
on-submit
|
||||
(mf/use-fn
|
||||
(mf/deps validate-name validate-descripion token resolved-tokens)
|
||||
(fn [e]
|
||||
(dom/prevent-default e)
|
||||
;; We have to re-validate the current form values before submitting
|
||||
;; because the validation is asynchronous/debounced
|
||||
;; and the user might have edited a valid form to make it invalid,
|
||||
;; and press enter before the next validations could return.
|
||||
(let [final-name (finalize-name @name-ref)
|
||||
valid-name?+ (-> (validate-name final-name) schema-validation->promise)
|
||||
final-value (finalize-value @value-ref)
|
||||
final-description @description-ref
|
||||
valid-description?+ (some-> final-description validate-descripion schema-validation->promise)]
|
||||
(-> (p/all [valid-name?+
|
||||
valid-description?+
|
||||
(validate-token-value+ {:value final-value
|
||||
:name-value final-name
|
||||
:token token
|
||||
:tokens resolved-tokens})])
|
||||
(p/finally (fn [result err]
|
||||
(let [final-name (finalize-name @name-ref)
|
||||
valid-name?+ (-> (validate-name final-name) schema-validation->promise)
|
||||
final-value (finalize-value @value-ref)
|
||||
final-description @description-ref
|
||||
valid-description?+ (some-> final-description validate-descripion schema-validation->promise)]
|
||||
(-> (p/all [valid-name?+
|
||||
valid-description?+
|
||||
(validate-token-value+ {:value final-value
|
||||
:name-value final-name
|
||||
:token token
|
||||
:tokens resolved-tokens})])
|
||||
(p/finally (fn [result err]
|
||||
;; The result should be a vector of all resolved validations
|
||||
;; We do not handle the error case as it will be handled by the components validations
|
||||
(when (and (seq result) (not err))
|
||||
(st/emit! (dt/update-create-token {:token (ctob/make-token :name final-name
|
||||
:type (or (:type token) token-type)
|
||||
:value final-value
|
||||
:description final-description)
|
||||
:prev-token-name (:name token)}))
|
||||
(st/emit! (wtu/update-workspace-tokens))
|
||||
(modal/hide!))))))))]
|
||||
[:form
|
||||
{:class (stl/css :form-wrapper)
|
||||
:on-submit on-submit}
|
||||
(when (and (seq result) (not err))
|
||||
(st/emit! (dt/update-create-token {:token (ctob/make-token :name final-name
|
||||
:type (or (:type token) token-type)
|
||||
:value final-value
|
||||
:description final-description)
|
||||
:prev-token-name (:name token)}))
|
||||
(st/emit! (wtu/update-workspace-tokens))
|
||||
(modal/hide!))))))))
|
||||
on-delete-token
|
||||
(mf/use-fn
|
||||
(mf/deps selected-token-set-id)
|
||||
(fn [e]
|
||||
(dom/prevent-default e)
|
||||
(modal/hide!)
|
||||
(st/emit! (dt/delete-token selected-token-set-id (:name token)))))
|
||||
|
||||
on-cancel
|
||||
(mf/use-fn
|
||||
(fn [e]
|
||||
(dom/prevent-default e)
|
||||
(modal/hide!)))]
|
||||
|
||||
[:form {:class (stl/css :form-wrapper)
|
||||
:on-submit on-submit}
|
||||
[:div {:class (stl/css :token-rows)}
|
||||
[:div
|
||||
[:> text* {:as "span" :typography "headline-medium"}
|
||||
(if (= action "edit")
|
||||
(tr "workspace.token.edit-token")
|
||||
(tr "workspace.token.create-token" token-type))]
|
||||
|
||||
[:div {:class (stl/css :input-row)}
|
||||
;; This should be remove when labeled-imput is modified
|
||||
[:span "Name"]
|
||||
[:& tokens.common/labeled-input {:label "Name"
|
||||
:error? @name-errors
|
||||
:input-props {:default-value @name-ref
|
||||
|
@ -332,37 +370,59 @@ Token names should only contain letters and digits separated by . characters.")}
|
|||
(for [error (->> (:errors @name-errors)
|
||||
(map #(-> (assoc @name-errors :errors [%])
|
||||
(me/humanize))))]
|
||||
[:p {:key error
|
||||
:class (stl/css :error)}
|
||||
[:> text* {:as "p"
|
||||
:key error
|
||||
:typography "body-small"
|
||||
:class (stl/css :error)}
|
||||
error])]
|
||||
[:& tokens.common/labeled-input {:label "Value"
|
||||
:input-props {:default-value @value-ref
|
||||
:on-blur on-update-value
|
||||
:on-change on-update-value
|
||||
:ref value-input-ref}
|
||||
:render-right (when color?
|
||||
(mf/fnc []
|
||||
[:div {:class (stl/css :color-bullet)
|
||||
:on-click #(swap! color-ramp-open? not)}
|
||||
(if-let [hex (some-> @color tinycolor/valid-color tinycolor/->hex)]
|
||||
[:& color-bullet {:color hex
|
||||
:mini? true}]
|
||||
[:div {:class (stl/css :color-bullet-placeholder)}])]))}]
|
||||
(when @color-ramp-open?
|
||||
[:& ramp {:color (some-> (or @token-resolve-result (:value token))
|
||||
(tinycolor/valid-color))
|
||||
:on-change on-update-color}])
|
||||
[:& token-value-or-errors {:result-or-errors @token-resolve-result}]
|
||||
|
||||
[:div
|
||||
[:div {:class (stl/css :input-row)}
|
||||
;; This should be remove when labeled-imput is modified
|
||||
[:span "value"]
|
||||
[:& tokens.common/labeled-input {:label "Value"
|
||||
:input-props {:default-value @value-ref
|
||||
:on-blur on-update-value
|
||||
:on-change on-update-value
|
||||
:ref value-input-ref}
|
||||
:render-right (when color?
|
||||
(mf/fnc []
|
||||
[:div {:class (stl/css :color-bullet)
|
||||
:on-click #(swap! color-ramp-open? not)}
|
||||
(if-let [hex (some-> @color tinycolor/valid-color tinycolor/->hex)]
|
||||
[:& color-bullet {:color hex
|
||||
:mini? true}]
|
||||
[:div {:class (stl/css :color-bullet-placeholder)}])]))}]
|
||||
(when @color-ramp-open?
|
||||
[:& ramp {:color (some-> (or @token-resolve-result (:value token))
|
||||
(tinycolor/valid-color))
|
||||
:on-change on-update-color}])
|
||||
[:& token-value-or-errors {:result-or-errors @token-resolve-result}]]
|
||||
|
||||
|
||||
[:div {:class (stl/css :input-row)}
|
||||
;; This should be remove when labeled-imput is modified
|
||||
[:span "Description"]
|
||||
[:& tokens.common/labeled-input {:label "Description"
|
||||
:input-props {:default-value @description-ref
|
||||
:on-change on-update-description}}]
|
||||
(when @description-errors
|
||||
[:p {:class (stl/css :error)}
|
||||
[:> text* {:as "p"
|
||||
:typography "body-small"
|
||||
:class (stl/css :error)}
|
||||
(me/humanize @description-errors)])]
|
||||
[:div {:class (stl/css :button-row)}
|
||||
[:button {:class (stl/css :button)
|
||||
:type "submit"
|
||||
:disabled disabled?}
|
||||
"Save"]]]]))
|
||||
|
||||
[:div {:class (stl/css-case :button-row true
|
||||
:with-delete (= action "edit"))}
|
||||
(when (= action "edit")
|
||||
[:> button* {:on-click on-delete-token
|
||||
:class (stl/css :delete-btn)
|
||||
:icon i/delete
|
||||
:variant "secondary"}
|
||||
(tr "labels.delete")])
|
||||
[:> button* {:on-click on-cancel
|
||||
:variant "secondary"}
|
||||
(tr "labels.cancel")]
|
||||
[:> button* {:type "submit"
|
||||
:variant "primary"
|
||||
:disabled disabled?}
|
||||
(tr "labels.save")]]]]))
|
||||
|
|
|
@ -8,49 +8,56 @@
|
|||
@import "./common.scss";
|
||||
|
||||
.form-wrapper {
|
||||
width: $s-260;
|
||||
width: $s-384;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: $s-16;
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
justify-content: end;
|
||||
gap: $s-12;
|
||||
padding-block-start: $s-8;
|
||||
}
|
||||
|
||||
.with-delete {
|
||||
grid-template-columns: 1fr auto auto;
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.token-rows {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-8;
|
||||
gap: $s-16;
|
||||
}
|
||||
|
||||
.input-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-4;
|
||||
}
|
||||
|
||||
.error {
|
||||
@include bodySmallTypography;
|
||||
margin-top: $s-6;
|
||||
padding: $s-4 $s-6;
|
||||
margin-bottom: 0;
|
||||
color: var(--status-color-error-500);
|
||||
}
|
||||
|
||||
.resolved-value {
|
||||
@include bodySmallTypography;
|
||||
--input-hint-color: var(--color-foreground-primary);
|
||||
margin-bottom: 0;
|
||||
padding: $s-4 $s-6;
|
||||
font-weight: medium;
|
||||
min-height: 1lh;
|
||||
|
||||
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;
|
||||
}
|
||||
color: var(--input-hint-color);
|
||||
}
|
||||
|
||||
.resolved-value-placeholder {
|
||||
color: var(--color-foreground-secondary);
|
||||
--input-hint-color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.resolved-value-error {
|
||||
color: var(--status-color-error-500);
|
||||
--input-hint-color: var(--status-color-error-500);
|
||||
}
|
||||
|
||||
.color-bullet {
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.ui.workspace.tokens.modals.themes :as wtmt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.workspace.tokens.form :refer [form]]
|
||||
[app.main.ui.workspace.tokens.modals.themes :as wtmt]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
@ -40,12 +43,21 @@
|
|||
|
||||
(mf/defc token-update-create-modal
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [x y position token token-type] :as _args}]
|
||||
(let [wrapper-style (use-viewport-position-style x y position)]
|
||||
[:div
|
||||
{:class (stl/css :shadow)
|
||||
:style wrapper-style}
|
||||
[{:keys [x y position token token-type action selected-token-set-id] :as _args}]
|
||||
(let [wrapper-style (use-viewport-position-style x y position)
|
||||
close-modal (mf/use-fn
|
||||
(fn []
|
||||
(modal/hide!)))]
|
||||
[:div {:class (stl/css :token-modal-wrapper)
|
||||
:style wrapper-style}
|
||||
[:> icon-button* {:on-click close-modal
|
||||
:class (stl/css :close-btn)
|
||||
:icon i/close
|
||||
:variant "action"
|
||||
:aria-label (tr "labels.close")}]
|
||||
[:& form {:token token
|
||||
:action action
|
||||
:selected-token-set-id selected-token-set-id
|
||||
:token-type token-type}]]))
|
||||
|
||||
(mf/defc token-themes-modal
|
||||
|
|
|
@ -6,14 +6,19 @@
|
|||
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.shadow {
|
||||
.token-modal-wrapper {
|
||||
@extend .modal-container-base;
|
||||
width: auto;
|
||||
max-width: auto;
|
||||
min-width: auto;
|
||||
@include menuShadow;
|
||||
position: absolute;
|
||||
width: auto;
|
||||
min-width: auto;
|
||||
z-index: 11;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: $s-6;
|
||||
right: $s-6;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.transit :as t]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.tokens :as dt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -33,6 +33,7 @@
|
|||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[app.main.ui.workspace.tokens.token-types :as wtty]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -60,8 +61,8 @@
|
|||
:title (cond
|
||||
errors? (sd/humanize-errors token)
|
||||
:else (->> [(str "Token: " name)
|
||||
(str "Original value: " value)
|
||||
(str "Resolved value: " resolved-value)]
|
||||
(str (tr "workspace.token.original-value") value)
|
||||
(str (tr "workspace.token.resolved-value") resolved-value)]
|
||||
(str/join "\n")))
|
||||
:on-click on-click
|
||||
:on-context-menu on-context-menu
|
||||
|
@ -111,6 +112,7 @@
|
|||
#(st/emit! (dt/set-token-type-section-open type (not open?))))
|
||||
on-popover-open-click (mf/use-fn
|
||||
(fn [event]
|
||||
(mf/deps type title)
|
||||
(let [{:keys [key fields]} modal]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dt/set-token-type-section-open type true))
|
||||
|
@ -118,6 +120,8 @@
|
|||
:y (.-clientY ^js event)
|
||||
:position :right
|
||||
:fields fields
|
||||
:title title
|
||||
:action "create"
|
||||
:token-type type}))))
|
||||
|
||||
on-token-pill-click (mf/use-fn
|
||||
|
@ -175,16 +179,16 @@
|
|||
:on-click (fn [e]
|
||||
(dom/stop-propagation e)
|
||||
(modal/show! :tokens/themes {}))}
|
||||
(if create? "Create" "Edit")])
|
||||
(if create? (tr "labels.create") (tr "labels.edit"))])
|
||||
|
||||
(mf/defc themes-header
|
||||
[_props]
|
||||
(let [ordered-themes (mf/deref refs/workspace-token-themes-no-hidden)]
|
||||
[:div {:class (stl/css :themes-wrapper)}
|
||||
[:span {:class (stl/css :themes-header)} "Themes"]
|
||||
[:div {:class (stl/css :theme-select-wrapper)}
|
||||
[:& theme-select]
|
||||
[:& edit-button {:create? (empty? ordered-themes)}]]]))
|
||||
[:div {:class (stl/css :themes-wrapper)}
|
||||
[:span {:class (stl/css :themes-header)} (tr "labels.themes")]
|
||||
[:div {:class (stl/css :theme-select-wrapper)}
|
||||
[:& theme-select]
|
||||
[:& edit-button {:create? (empty? ordered-themes)}]]]))
|
||||
|
||||
(mf/defc add-set-button
|
||||
[{:keys [on-open]}]
|
||||
|
@ -207,7 +211,7 @@
|
|||
[:& title-bar {:collapsable true
|
||||
:collapsed (not @open?)
|
||||
:all-clickable true
|
||||
:title "SETS"
|
||||
:title (tr "labels.sets")
|
||||
:on-collapsed #(swap! open? not)}
|
||||
[:& add-set-button {:on-open on-open}]]]
|
||||
(when @open?
|
||||
|
|
|
@ -1993,6 +1993,14 @@ msgstr "Team Leader"
|
|||
msgid "labels.team-member"
|
||||
msgstr "Team member"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "labels.themes"
|
||||
msgstr "Themes"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "labels.sets"
|
||||
msgstr "Sets"
|
||||
|
||||
#: src/app/main/ui/dashboard/sidebar.cljs:992, src/app/main/ui/workspace/main_menu.cljs:118
|
||||
msgid "labels.tutorials"
|
||||
msgstr "Tutorials"
|
||||
|
@ -6139,3 +6147,20 @@ msgstr "Update"
|
|||
#, unused
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Click to close the path"
|
||||
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
msgid "workspace.token.create-token"
|
||||
msgstr "Create new %s token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
msgid "workspace.token.edit-token"
|
||||
msgstr "Edit token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
msgid "workspace.token.resolved-value"
|
||||
msgstr "Resolved value: "
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "workspace.token.original-value"
|
||||
msgstr "Original value: "
|
|
@ -1991,6 +1991,14 @@ msgstr "Líder de equipo"
|
|||
msgid "labels.team-member"
|
||||
msgstr "Miembro de equipo"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "labels.themes"
|
||||
msgstr "Temas"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "labels.sets"
|
||||
msgstr "Sets"
|
||||
|
||||
#: src/app/main/ui/dashboard/sidebar.cljs:992, src/app/main/ui/workspace/main_menu.cljs:118
|
||||
msgid "labels.tutorials"
|
||||
msgstr "Tutoriales"
|
||||
|
@ -6126,3 +6134,20 @@ msgstr "Pulsar para cerrar la ruta"
|
|||
|
||||
msgid "errors.maximum-invitations-by-request-reached"
|
||||
msgstr "Se ha alcanzado el número máximo (%s) de correos electrónicos que se pueden invitar en una sola solicitud"
|
||||
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
msgid "workspace.token.create-token"
|
||||
msgstr "Crear un token de %s"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
msgid "workspace.token.edit-token"
|
||||
msgstr "Editar token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/form.cljs
|
||||
msgid "workspace.token.resolved-value"
|
||||
msgstr "Valor resuelto: "
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs
|
||||
msgid "workspace.token.original-value"
|
||||
msgstr "Valor original: "
|
Loading…
Add table
Reference in a new issue