mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 22:22:43 -05:00
Merge pull request #157 from tokens-studio/florian/input-select
Allow token selection with keyboard from right side
This commit is contained in:
commit
e89f03393b
2 changed files with 83 additions and 37 deletions
|
@ -11,14 +11,19 @@
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.data.shortcuts :as dsc]
|
||||||
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
[app.util.globals :as globals]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.timers :as timers]
|
[app.util.timers :as timers]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.v2 :as mf]))
|
[goog.events :as events]
|
||||||
|
[rumext.v2 :as mf])
|
||||||
|
(:import goog.events.EventType))
|
||||||
|
|
||||||
(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!]}]
|
||||||
(let [up? (kbd/up-arrow? event)
|
(let [up? (kbd/up-arrow? event)
|
||||||
|
@ -38,28 +43,67 @@
|
||||||
:else new-value)]
|
:else new-value)]
|
||||||
(set-value! new-value)))))
|
(set-value! new-value)))))
|
||||||
|
|
||||||
|
(defn direction-select
|
||||||
|
"Returns next `n` in `direction` while wrapping around at the last item at the count of `coll`.
|
||||||
|
|
||||||
|
`direction` accepts `:up` or `:down`."
|
||||||
|
[direction n coll]
|
||||||
|
(let [last-n (dec (count coll))
|
||||||
|
next-n (case direction
|
||||||
|
:up (dec n)
|
||||||
|
:down (inc n))
|
||||||
|
wrap-around-n (cond
|
||||||
|
(neg? next-n) last-n
|
||||||
|
(> next-n last-n) 0
|
||||||
|
:else next-n)]
|
||||||
|
wrap-around-n))
|
||||||
|
|
||||||
(mf/defc dropdown-select [{:keys [position on-close element-id element-ref options on-select]}]
|
(mf/defc dropdown-select [{:keys [position on-close element-id element-ref options on-select]}]
|
||||||
|
(let [highlighted* (mf/use-state nil)
|
||||||
|
highlighted (deref highlighted*)
|
||||||
|
on-keyup (fn [event]
|
||||||
|
(cond
|
||||||
|
(and (kbd/enter? event) highlighted) (on-select (nth options highlighted))
|
||||||
|
(kbd/up-arrow? event) (do
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(->> (direction-select :up (or highlighted 0) options)
|
||||||
|
(reset! highlighted*)))
|
||||||
|
(kbd/down-arrow? event) (do
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(->> (direction-select :down (or highlighted -1) options)
|
||||||
|
(reset! highlighted*)))))]
|
||||||
|
(mf/with-effect [highlighted]
|
||||||
|
(let [keys [(events/listen globals/document EventType.KEYUP on-keyup)
|
||||||
|
(events/listen globals/document EventType.KEYDOWN dom/prevent-default)]]
|
||||||
|
(st/emit! (dsc/push-shortcuts :token {}))
|
||||||
|
(fn []
|
||||||
|
(doseq [key keys]
|
||||||
|
(events/unlistenByKey key))
|
||||||
|
(st/emit! (dsc/pop-shortcuts :token)))))
|
||||||
[:& dropdown {:show true
|
[:& dropdown {:show true
|
||||||
:on-close on-close}
|
:on-close on-close}
|
||||||
[:> :div {:class (stl/css-case :custom-select-dropdown true
|
[:> :div {:class (stl/css-case :custom-select-dropdown true
|
||||||
:custom-select-dropdown-right (= position :right)
|
:custom-select-dropdown-right (= position :right)
|
||||||
:custom-select-dropdown-left (= position :left))
|
:custom-select-dropdown-left (= position :left))
|
||||||
|
:on-mouse-enter #(reset! highlighted* nil)
|
||||||
:ref element-ref}
|
:ref element-ref}
|
||||||
[:ul {:class (stl/css :custom-select-dropdown-list)}
|
[:ul {:class (stl/css :custom-select-dropdown-list)}
|
||||||
(for [[index item] (d/enumerate options)]
|
(for [[index item] (d/enumerate options)]
|
||||||
(cond
|
(cond
|
||||||
(= :separator item) [:li {:class (stl/css :separator)
|
(= :separator item) [:li {:class (stl/css :separator)
|
||||||
:key (dm/str element-id "-" index)}]
|
:key (dm/str element-id "-" index)}]
|
||||||
:else (let [{:keys [value label selected?]} item]
|
:else (let [{:keys [value label selected?]} item
|
||||||
|
highlighted? (= highlighted index)]
|
||||||
[:li
|
[:li
|
||||||
{:key (str element-id "-" index)
|
{:key (str element-id "-" index)
|
||||||
:class (stl/css-case :dropdown-element true
|
:class (stl/css-case :dropdown-element true
|
||||||
:is-selected selected?)
|
:is-selected selected?
|
||||||
|
:is-highlighted highlighted?)
|
||||||
:data-label label
|
:data-label label
|
||||||
:on-click on-select}
|
:on-click #(on-select item)}
|
||||||
[:span {:class (stl/css :label)} label]
|
[:span {:class (stl/css :label)} label]
|
||||||
[:span {:class (stl/css :value)} value]
|
[:span {:class (stl/css :value)} value]
|
||||||
[: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 options disabled class on-change placeholder on-blur on-token-remove position input-props] :as params}]
|
[{:keys [value options disabled class on-change placeholder on-blur on-token-remove position input-props] :as params}]
|
||||||
|
@ -118,18 +162,13 @@
|
||||||
select-item
|
select-item
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps on-change on-blur labels-map)
|
(mf/deps on-change on-blur labels-map)
|
||||||
(fn [event]
|
(fn [{:keys [value] :as item}]
|
||||||
(let [label (-> (dom/get-current-target event)
|
|
||||||
(dom/get-data "label")
|
|
||||||
(d/read-string)
|
|
||||||
(str))
|
|
||||||
{:keys [value] :as item} (get labels-map label)]
|
|
||||||
(swap! state* assoc
|
(swap! state* assoc
|
||||||
:current-value value
|
:current-value value
|
||||||
:token-value nil
|
: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))))
|
||||||
|
|
||||||
handle-change-input
|
handle-change-input
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
@ -167,9 +206,6 @@
|
||||||
(handle-change-input event)
|
(handle-change-input event)
|
||||||
(set-token-value! nil))
|
(set-token-value! nil))
|
||||||
:else (set-token-value! value)))
|
:else (set-token-value! value)))
|
||||||
is-open? (let [up? (kbd/up-arrow? event)
|
|
||||||
down? (kbd/down-arrow? event)]
|
|
||||||
(dom/prevent-default event))
|
|
||||||
(= 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
|
||||||
|
|
|
@ -128,6 +128,7 @@
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--menu-background-color-hover);
|
background-color: var(--menu-background-color-hover);
|
||||||
color: var(--menu-foreground-color-hover);
|
color: var(--menu-foreground-color-hover);
|
||||||
|
@ -135,6 +136,15 @@
|
||||||
stroke: var(--menu-foreground-color-hover);
|
stroke: var(--menu-foreground-color-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&.is-highlighted {
|
||||||
|
background-color: var(--button-primary-background-color-rest);
|
||||||
|
span {
|
||||||
|
color: var(--button-primary-foreground-color-rest);
|
||||||
|
}
|
||||||
|
.check-icon svg {
|
||||||
|
stroke: var(--button-primary-foreground-color-rest);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue