mirror of
https://github.com/penpot/penpot.git
synced 2025-06-15 09:12:45 +02:00
Merge pull request #128 from tokens-studio/119-higlight-applied-token-in-the-editing-field
Show token value inside shapes panel (border-radius)
This commit is contained in:
commit
2818d097ab
5 changed files with 110 additions and 42 deletions
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.main.data.tokens
|
(ns app.main.data.tokens
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.changes-builder :as pcb]
|
[app.common.files.changes-builder :as pcb]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
|
@ -57,6 +58,9 @@
|
||||||
(->> (map (fn [attr] [attr token-id]) attributes)
|
(->> (map (fn [attr] [attr token-id]) attributes)
|
||||||
(into {})))
|
(into {})))
|
||||||
|
|
||||||
|
(defn unapply-token-id [shape attributes]
|
||||||
|
(update shape :applied-tokens d/without-keys attributes))
|
||||||
|
|
||||||
(defn apply-token-id-to-attributes [{:keys [shape token-id attributes]}]
|
(defn apply-token-id-to-attributes [{:keys [shape token-id attributes]}]
|
||||||
(let [token (token-from-attributes token-id attributes)]
|
(let [token (token-from-attributes token-id attributes)]
|
||||||
(toggle-or-apply-token shape token)))
|
(toggle-or-apply-token shape token)))
|
||||||
|
|
|
@ -291,6 +291,16 @@
|
||||||
(on-switch-to-radius-4)
|
(on-switch-to-radius-4)
|
||||||
(on-switch-to-radius-1))))
|
(on-switch-to-radius-1))))
|
||||||
|
|
||||||
|
on-border-radius-token-unapply
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps ids change-radius border-radius-tokens)
|
||||||
|
(fn [token]
|
||||||
|
(let [token-value (some-> token wtc/resolve-token-value)]
|
||||||
|
(st/emit!
|
||||||
|
(change-radius (fn [shape]
|
||||||
|
(-> (dt/unapply-token-id shape (wtc/token-attributes :border-radius))
|
||||||
|
(ctsr/set-radius-1 token-value))))))))
|
||||||
|
|
||||||
on-radius-1-change
|
on-radius-1-change
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps ids change-radius border-radius-tokens)
|
(mf/deps ids change-radius border-radius-tokens)
|
||||||
|
@ -488,6 +498,7 @@
|
||||||
[:span {:class (stl/css :icon)} i/corner-radius]
|
[:span {:class (stl/css :icon)} i/corner-radius]
|
||||||
[:& editable-select
|
[:& editable-select
|
||||||
{:placeholder (if (= :multiple (:rx values)) (tr "settings.multiple") "--")
|
{:placeholder (if (= :multiple (:rx values)) (tr "settings.multiple") "--")
|
||||||
|
:on-token-remove on-border-radius-token-unapply
|
||||||
:class (stl/css :token-select)
|
:class (stl/css :token-select)
|
||||||
:type "number"
|
:type "number"
|
||||||
:min 0
|
:min 0
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.data.workspace.shape-layout :as dwsl]
|
[app.main.data.workspace.shape-layout :as dwsl]
|
||||||
[app.main.data.workspace.state-helpers :as wsh]
|
|
||||||
[app.main.data.workspace.transforms :as dwt]
|
[app.main.data.workspace.transforms :as dwt]
|
||||||
[app.main.store :as st]))
|
[app.main.store :as st]))
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.timers :as timers]
|
[app.util.timers :as timers]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn on-number-input-key-down [{:keys [event min-val max-val set-value!]}]
|
(defn on-number-input-key-down [{:keys [event min-val max-val set-value!]}]
|
||||||
|
@ -59,22 +60,28 @@
|
||||||
[:span {:class (stl/css :check-icon)} i/tick]])))]]])
|
[:span {:class (stl/css :check-icon)} i/tick]])))]]])
|
||||||
|
|
||||||
(mf/defc editable-select
|
(mf/defc editable-select
|
||||||
[{:keys [value type options class on-change placeholder on-blur input-class] :as params}]
|
[{:keys [value type options class on-change placeholder on-blur input-class on-token-remove] :as params}]
|
||||||
(let [state* (mf/use-state {:id (uuid/next)
|
(let [state* (mf/use-state {:id (uuid/next)
|
||||||
:is-open? false
|
:is-open? false
|
||||||
:current-value value
|
:current-value value
|
||||||
|
:token-value nil
|
||||||
:current-item nil
|
:current-item nil
|
||||||
:top nil
|
:top nil
|
||||||
:left nil
|
:left nil
|
||||||
:bottom nil})
|
:bottom nil})
|
||||||
state (deref state*)
|
state (deref state*)
|
||||||
is-open? (:is-open? state)
|
is-open? (:is-open? state)
|
||||||
|
refocus? (:refocus? state)
|
||||||
current-value (:current-value state)
|
current-value (:current-value state)
|
||||||
element-id (:id state)
|
element-id (:id state)
|
||||||
|
|
||||||
min-val (get params :min)
|
min-val (get params :min)
|
||||||
max-val (get params :max)
|
max-val (get params :max)
|
||||||
|
|
||||||
|
multiple? (= :multiple value)
|
||||||
|
token (when-not multiple?
|
||||||
|
(-> (filter :selected? options) (first)))
|
||||||
|
|
||||||
emit-blur? (mf/use-ref nil)
|
emit-blur? (mf/use-ref nil)
|
||||||
select-wrapper-ref (mf/use-ref)
|
select-wrapper-ref (mf/use-ref)
|
||||||
|
|
||||||
|
@ -93,9 +100,15 @@
|
||||||
options)
|
options)
|
||||||
(into {}))
|
(into {}))
|
||||||
|
|
||||||
|
set-token-value!
|
||||||
|
(fn [value]
|
||||||
|
(swap! state* assoc :token-value value))
|
||||||
|
|
||||||
set-value
|
set-value
|
||||||
(fn [value]
|
(fn [value]
|
||||||
(swap! state* assoc :current-value value)
|
(swap! state* assoc
|
||||||
|
:current-value value
|
||||||
|
:token-value value)
|
||||||
(when on-change (on-change value)))
|
(when on-change (on-change value)))
|
||||||
|
|
||||||
select-item
|
select-item
|
||||||
|
@ -109,6 +122,7 @@
|
||||||
{:keys [value] :as item} (get labels-map label)]
|
{:keys [value] :as item} (get labels-map label)]
|
||||||
(swap! state* assoc
|
(swap! state* assoc
|
||||||
:current-value value
|
:current-value value
|
||||||
|
:token-value nil
|
||||||
:current-item item)
|
:current-item item)
|
||||||
(when on-change (on-change item))
|
(when on-change (on-change item))
|
||||||
(when on-blur (on-blur)))))
|
(when on-blur (on-blur)))))
|
||||||
|
@ -119,32 +133,33 @@
|
||||||
value (or (d/parse-double value) value)]
|
value (or (d/parse-double value) value)]
|
||||||
(set-value value)))
|
(set-value value)))
|
||||||
|
|
||||||
on-node-load
|
|
||||||
(fn [node]
|
|
||||||
;; There is a problem when changing the state in this callback that
|
|
||||||
;; produces the dropdown to close in the same event
|
|
||||||
(when node
|
|
||||||
(timers/schedule
|
|
||||||
#(when-let [bounds (when node (dom/get-bounding-rect node))]
|
|
||||||
(let [{window-height :height} (dom/get-window-size)
|
|
||||||
{:keys [left top height]} bounds
|
|
||||||
bottom (when (< (- window-height top) 300) (- window-height top))
|
|
||||||
top (when (>= (- window-height top) 300) (+ top height))]
|
|
||||||
(swap! state*
|
|
||||||
assoc
|
|
||||||
:left left
|
|
||||||
:top top
|
|
||||||
:bottom bottom))))))
|
|
||||||
|
|
||||||
handle-key-down
|
handle-key-down
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps set-value is-open?)
|
(mf/deps set-value is-open? token)
|
||||||
(fn [event]
|
(fn [^js event]
|
||||||
(cond
|
(cond
|
||||||
|
token (let [backspace? (kbd/backspace? event)
|
||||||
|
enter? (kbd/enter? event)
|
||||||
|
value (-> event dom/get-target dom/get-value)
|
||||||
|
caret-at-beginning? (nil? (.. event -target -selectionStart))
|
||||||
|
no-text-selected? (str/empty? (.toString (js/document.getSelection)))
|
||||||
|
delete-token? (and backspace? caret-at-beginning? no-text-selected?)
|
||||||
|
replace-token-with-value? (and enter? (seq (str/trim value)))]
|
||||||
|
(cond
|
||||||
|
delete-token? (do
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(on-token-remove token)
|
||||||
|
;; Re-focus the input value of the newly rendered input element
|
||||||
|
(swap! state* assoc :refocus? true))
|
||||||
|
replace-token-with-value? (do
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(on-token-remove token)
|
||||||
|
(handle-change-input event)
|
||||||
|
(set-token-value! nil))
|
||||||
|
:else (set-token-value! value)))
|
||||||
is-open? (let [up? (kbd/up-arrow? event)
|
is-open? (let [up? (kbd/up-arrow? event)
|
||||||
down? (kbd/down-arrow? event)]
|
down? (kbd/down-arrow? event)]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event))
|
||||||
(js/console.log "up? down?" up? down?))
|
|
||||||
(= type "number") (on-number-input-key-down {:event event
|
(= type "number") (on-number-input-key-down {:event event
|
||||||
:min-val min-val
|
:min-val min-val
|
||||||
:max-val max-val
|
:max-val max-val
|
||||||
|
@ -152,13 +167,17 @@
|
||||||
|
|
||||||
handle-focus
|
handle-focus
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
(mf/deps refocus?)
|
||||||
(fn []
|
(fn []
|
||||||
|
(when refocus?
|
||||||
|
(swap! state* dissoc :refocus?))
|
||||||
(mf/set-ref-val! emit-blur? false)))
|
(mf/set-ref-val! emit-blur? false)))
|
||||||
|
|
||||||
handle-blur
|
handle-blur
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn []
|
(fn []
|
||||||
(mf/set-ref-val! emit-blur? true)
|
(mf/set-ref-val! emit-blur? true)
|
||||||
|
(swap! state* assoc :token-value nil)
|
||||||
(timers/schedule
|
(timers/schedule
|
||||||
200
|
200
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -167,7 +186,7 @@
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps value current-value)
|
(mf/deps value current-value)
|
||||||
#(when (not= (str value) current-value)
|
#(when (not= (str value) current-value)
|
||||||
(reset! state* {:current-value value})))
|
(swap! state* assoc :current-value value)))
|
||||||
|
|
||||||
(mf/with-effect [is-open?]
|
(mf/with-effect [is-open?]
|
||||||
(let [wrapper-node (mf/ref-val select-wrapper-ref)
|
(let [wrapper-node (mf/ref-val select-wrapper-ref)
|
||||||
|
@ -185,23 +204,34 @@
|
||||||
(mf/set-ref-val! emit-blur? (not is-open?)))
|
(mf/set-ref-val! emit-blur? (not is-open?)))
|
||||||
|
|
||||||
|
|
||||||
[:div {:class (dm/str class " " (stl/css :editable-select))
|
[:div {:class (dm/str class " " (stl/css :editable-select))}
|
||||||
:ref on-node-load}
|
(when-let [{:keys [label value]} token]
|
||||||
(if (= type "number")
|
[:div {:title (str label ": " value)
|
||||||
[:> numeric-input* {:value (or current-value "")
|
:class (stl/css :token-pill)}
|
||||||
:className input-class
|
value])
|
||||||
:on-change set-value
|
(cond
|
||||||
:on-focus handle-focus
|
token [:input {:value (or (:token-value state) "")
|
||||||
:on-blur handle-blur
|
:class input-class
|
||||||
:placeholder placeholder}]
|
:on-change handle-change-input
|
||||||
[:input {:value (or current-value "")
|
:on-key-down handle-key-down
|
||||||
:class input-class
|
:on-focus handle-focus
|
||||||
:on-change handle-change-input
|
:on-blur handle-blur
|
||||||
:on-key-down handle-key-down
|
:type type}]
|
||||||
:on-focus handle-focus
|
(= type "number") [:> numeric-input* {:autoFocus refocus?
|
||||||
:on-blur handle-blur
|
:value (or current-value "")
|
||||||
:placeholder placeholder
|
:className input-class
|
||||||
:type type}])
|
:on-change set-value
|
||||||
|
:on-focus handle-focus
|
||||||
|
:on-blur handle-blur
|
||||||
|
:placeholder placeholder}]
|
||||||
|
:else [:input {:value (or current-value "")
|
||||||
|
:class input-class
|
||||||
|
:on-change handle-change-input
|
||||||
|
:on-key-down handle-key-down
|
||||||
|
:on-focus handle-focus
|
||||||
|
:on-blur handle-blur
|
||||||
|
:placeholder placeholder
|
||||||
|
:type type}])
|
||||||
|
|
||||||
(when (seq options)
|
(when (seq options)
|
||||||
[:span {:class (stl/css :dropdown-button)
|
[:span {:class (stl/css :dropdown-button)
|
||||||
|
|
|
@ -17,6 +17,17 @@
|
||||||
border-radius: $br-8;
|
border-radius: $br-8;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
background: transparent;
|
||||||
|
&:hover {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
&:focus-within {
|
||||||
|
.token-pill {
|
||||||
|
background-color: var(--button-primary-background-color-rest);
|
||||||
|
color: var(--button-primary-foreground-color-rest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-button {
|
.dropdown-button {
|
||||||
@include flexCenter;
|
@include flexCenter;
|
||||||
margin-right: -$s-8;
|
margin-right: -$s-8;
|
||||||
|
@ -38,6 +49,19 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.token-pill {
|
||||||
|
background-color: rgb(94 107 120 / 25%);
|
||||||
|
border-radius: $br-4;
|
||||||
|
padding: $s-2 $s-6;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-pill + input {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.custom-select-dropdown {
|
.custom-select-dropdown {
|
||||||
@extend .dropdown-wrapper;
|
@extend .dropdown-wrapper;
|
||||||
max-height: $s-320;
|
max-height: $s-320;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue