From 91271b9e41eb20d10032f29b2ab6192430096875 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 07:57:55 +0200 Subject: [PATCH 01/13] Custom editable-select --- .../sidebar/options/menus/measures.cljs | 2 +- .../ui/workspace/tokens/editable_select.cljs | 202 ++++++++++++++++++ .../ui/workspace/tokens/editable_select.scss | 75 +++++++ 3 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 frontend/src/app/main/ui/workspace/tokens/editable_select.cljs create mode 100644 frontend/src/app/main/ui/workspace/tokens/editable_select.scss diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index d457c3d7a..37f89db6c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -20,12 +20,12 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] - [app.main.ui.components.editable-select :refer [editable-select]] [app.main.ui.components.numeric-input :refer [numeric-input*]] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] [app.main.ui.hooks :as hooks] [app.main.ui.icons :as i] [app.main.ui.workspace.tokens.core :as wtc] + [app.main.ui.workspace.tokens.editable-select :refer [editable-select]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [clojure.set :refer [rename-keys union]] diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs new file mode 100644 index 000000000..15c122779 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs @@ -0,0 +1,202 @@ +;; 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) KALEIDOS INC + +(ns app.main.ui.workspace.tokens.editable-select + (:require-macros [app.main.style :as stl]) + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.math :as mth] + [app.common.uuid :as uuid] + [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.components.numeric-input :refer [numeric-input*]] + [app.main.ui.icons :as i] + [app.util.dom :as dom] + [app.util.keyboard :as kbd] + [app.util.timers :as timers] + [rumext.v2 :as mf])) + +(mf/defc editable-select + [{:keys [value type options class on-change placeholder on-blur input-class] :as params}] + (let [state* (mf/use-state {:id (uuid/next) + :is-open? false + :current-value value + :top nil + :left nil + :bottom nil}) + state (deref state*) + is-open? (:is-open? state) + current-value (:current-value state) + element-id (:id state) + + min-val (get params :min) + max-val (get params :max) + + emit-blur? (mf/use-ref nil) + font-size-wrapper-ref (mf/use-ref) + + toggle-dropdown + (mf/use-fn + (mf/deps state) + #(swap! state* update :is-open? not)) + + close-dropdown + (fn [event] + (dom/stop-propagation event) + (swap! state* assoc :is-open? false)) + + select-item + (mf/use-fn + (mf/deps on-change on-blur) + (fn [event] + (let [value (-> (dom/get-current-target event) + (dom/get-data "value") + (d/read-string))] + (swap! state* assoc :current-value value) + (when on-change (on-change value)) + (when on-blur (on-blur))))) + + as-key-value (fn [item] (if (map? item) [(:value item) (:label item)] [item item])) + labels-map (into {} (map as-key-value) options) + value->label (fn [value] (get labels-map value value)) + + set-value + (fn [value] + (swap! state* assoc :current-value value) + (when on-change (on-change value))) + + ;; TODO: why this method supposes that all editable select + ;; works with numbers? + + handle-change-input + (fn [event] + (let [value (-> event dom/get-target dom/get-value) + value (or (d/parse-double 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 + (mf/use-fn + (mf/deps set-value) + (fn [event] + (when (= type "number") + (let [up? (kbd/up-arrow? event) + down? (kbd/down-arrow? event)] + (when (or up? down?) + (dom/prevent-default event) + (let [value (-> event dom/get-target dom/get-value) + value (or (d/parse-double value) value) + + increment (cond + (kbd/shift? event) + (if up? 10 -10) + + (kbd/alt? event) + (if up? 0.1 -0.1) + + :else + (if up? 1 -1)) + + new-value (+ value increment) + + new-value (cond + (and (d/num? min-val) (< new-value min-val)) min-val + (and (d/num? max-val) (> new-value max-val)) max-val + :else new-value)] + + (set-value new-value))))))) + + handle-focus + (mf/use-fn + (fn [] + (mf/set-ref-val! emit-blur? false))) + + handle-blur + (mf/use-fn + (fn [] + (mf/set-ref-val! emit-blur? true) + (timers/schedule + 200 + (fn [] + (when (and on-blur (mf/ref-val emit-blur?)) (on-blur))))))] + + (mf/use-effect + (mf/deps value current-value) + #(when (not= (str value) current-value) + (reset! state* {:current-value value}))) + + (mf/with-effect [is-open?] + (let [wrapper-node (mf/ref-val font-size-wrapper-ref) + node (dom/get-element-by-class "checked-element is-selected" wrapper-node) + nodes (dom/get-elements-by-class "checked-element-value" wrapper-node) + closest (fn [a b] (first (sort-by #(mth/abs (- % b)) a))) + closest-value (str (closest options value))] + (when is-open? + (if (some? node) + (dom/scroll-into-view-if-needed! node) + (some->> nodes + (d/seek #(= closest-value (dom/get-inner-text %))) + (dom/scroll-into-view-if-needed!))))) + + (mf/set-ref-val! emit-blur? (not is-open?))) + + + [:div {:class (dm/str class " " (stl/css :editable-select)) + :ref on-node-load} + (if (= type "number") + [:> numeric-input* {:value (or (some-> current-value value->label) "") + :className input-class + :on-change set-value + :on-focus handle-focus + :on-blur handle-blur + :placeholder placeholder}] + [:input {:value (or (some-> current-value value->label) "") + :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}]) + + [:span {:class (stl/css :dropdown-button) + :on-click toggle-dropdown} + i/arrow] + + [:& dropdown {:show (or is-open? false) + :on-close close-dropdown} + [:ul {:class (stl/css :custom-select-dropdown) + :ref font-size-wrapper-ref} + (for [[index item] (map-indexed vector options)] + (if (= :separator item) + [:li {:class (stl/css :separator) + :key (dm/str element-id "-" index)}] + (let [[value label] (as-key-value item)] + [:li + {:key (str element-id "-" index) + :class (stl/css-case :dropdown-element true + :is-selected (= (dm/str value) current-value)) + :data-value value + :on-click select-item} + [:span {:class (stl/css :label)} label] + [:span {:class (stl/css :check-icon)} + i/tick]])))]]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss new file mode 100644 index 000000000..0fbe4abea --- /dev/null +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss @@ -0,0 +1,75 @@ +// 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) KALEIDOS INC + +@import "refactor/common-refactor.scss"; + +.editable-select { + @extend .asset-element; + margin: 0; + border: $s-1 solid var(--input-border-color); + position: relative; + display: flex; + height: $s-32; + width: 100%; + padding: $s-8; + border-radius: $br-8; + cursor: pointer; + + .dropdown-button { + @include flexCenter; + margin-right: -$s-8; + padding-left: 0; + aspect-ratio: 0.8 / 1; + width: auto; + + svg { + @extend .button-icon-small; + transform: rotate(90deg); + stroke: var(--icon-foreground); + } + } + + .custom-select-dropdown { + @extend .dropdown-wrapper; + max-height: $s-320; + .separator { + margin: 0; + height: $s-12; + } + .dropdown-element { + @extend .dropdown-element-base; + color: var(--menu-foreground-color-rest); + .label { + flex-grow: 1; + width: 100%; + } + + .check-icon { + @include flexCenter; + svg { + @extend .button-icon-small; + visibility: hidden; + stroke: var(--icon-foreground); + } + } + + &.is-selected { + color: var(--menu-foreground-color); + .check-icon svg { + stroke: var(--menu-foreground-color); + visibility: visible; + } + } + &:hover { + background-color: var(--menu-background-color-hover); + color: var(--menu-foreground-color-hover); + .check-icon svg { + stroke: var(--menu-foreground-color-hover); + } + } + } + } +} From d2107e7f69a8be042cf1f59275d1568558d6f5e5 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 08:02:06 +0200 Subject: [PATCH 02/13] Fix width of drop down --- .../src/app/main/ui/workspace/tokens/editable_select.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss index 0fbe4abea..a8fc021ef 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss @@ -35,6 +35,11 @@ .custom-select-dropdown { @extend .dropdown-wrapper; max-height: $s-320; + width: auto; + margin-top: $s-4; + right: 0; + left: auto; + .separator { margin: 0; height: $s-12; From 6a8887d9ccf862beeac087b0096fc9323512a814 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 08:04:23 +0200 Subject: [PATCH 03/13] Remove text transform --- frontend/src/app/main/ui/workspace/tokens/editable_select.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss index a8fc021ef..2dcd74776 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss @@ -50,6 +50,7 @@ .label { flex-grow: 1; width: 100%; + text-transform: unset; } .check-icon { From b0dcbae3ac1c86473183ab76be1121e31783dd95 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 08:13:55 +0200 Subject: [PATCH 04/13] Fix border clipping --- frontend/src/app/main/ui/workspace/tokens/editable_select.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss index 2dcd74776..698ed839e 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss @@ -9,10 +9,9 @@ .editable-select { @extend .asset-element; margin: 0; - border: $s-1 solid var(--input-border-color); position: relative; display: flex; - height: $s-32; + height: calc($s-32 - 2px); // Fixes border being clipped by the input field width: 100%; padding: $s-8; border-radius: $br-8; From 165e222117faa746108b4a1fff0962527aed950c Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 08:17:51 +0200 Subject: [PATCH 05/13] Only show dropdown when options contain items --- .../src/app/main/ui/workspace/tokens/editable_select.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs index 15c122779..357d88fa2 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs @@ -178,9 +178,10 @@ :placeholder placeholder :type type}]) - [:span {:class (stl/css :dropdown-button) - :on-click toggle-dropdown} - i/arrow] + (when (seq options) + [:span {:class (stl/css :dropdown-button) + :on-click toggle-dropdown} + i/arrow]) [:& dropdown {:show (or is-open? false) :on-close close-dropdown} From cb980ace44d97c2b11f2ba0f39d23d744558e0ad Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 09:04:35 +0200 Subject: [PATCH 06/13] Use regular map for options --- .../main/ui/workspace/sidebar/options/menus/measures.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 37f89db6c..4bb1d09b5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -100,7 +100,10 @@ tokens (mf/deref refs/workspace-tokens) border-radius-tokens (mf/use-memo (mf/deps tokens) #(wtc/tokens-name-map-for-type :border-radius tokens)) - border-radius-options (mf/use-memo (mf/deps border-radius-tokens) #(map (comp :name val) border-radius-tokens)) + border-radius-options (mf/use-memo (mf/deps border-radius-tokens) + #(map (fn [[_k {:keys [name] :as item}]] + (assoc item :label name)) + border-radius-tokens)) flex-child? (->> selection-parents (some ctl/flex-layout?)) absolute? (ctl/item-absolute? shape) From 8dd2ba7d7800fbec75d7930d797c9c13192e472a Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 09:04:50 +0200 Subject: [PATCH 07/13] Fix naming --- .../src/app/main/ui/workspace/tokens/editable_select.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs index 357d88fa2..f2d374b87 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs @@ -36,7 +36,7 @@ max-val (get params :max) emit-blur? (mf/use-ref nil) - font-size-wrapper-ref (mf/use-ref) + select-wrapper-ref (mf/use-ref) toggle-dropdown (mf/use-fn @@ -145,7 +145,7 @@ (reset! state* {:current-value value}))) (mf/with-effect [is-open?] - (let [wrapper-node (mf/ref-val font-size-wrapper-ref) + (let [wrapper-node (mf/ref-val select-wrapper-ref) node (dom/get-element-by-class "checked-element is-selected" wrapper-node) nodes (dom/get-elements-by-class "checked-element-value" wrapper-node) closest (fn [a b] (first (sort-by #(mth/abs (- % b)) a))) From e69bfb8c544dd41713b3e1ba131f0aea72d93fad Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 09:05:18 +0200 Subject: [PATCH 08/13] Style select --- .../ui/workspace/tokens/editable_select.cljs | 34 ++++++++++--------- .../ui/workspace/tokens/editable_select.scss | 25 ++++++++++++-- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs index f2d374b87..70f625cff 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs @@ -185,19 +185,21 @@ [:& dropdown {:show (or is-open? false) :on-close close-dropdown} - [:ul {:class (stl/css :custom-select-dropdown) - :ref font-size-wrapper-ref} - (for [[index item] (map-indexed vector options)] - (if (= :separator item) - [:li {:class (stl/css :separator) - :key (dm/str element-id "-" index)}] - (let [[value label] (as-key-value item)] - [:li - {:key (str element-id "-" index) - :class (stl/css-case :dropdown-element true - :is-selected (= (dm/str value) current-value)) - :data-value value - :on-click select-item} - [:span {:class (stl/css :label)} label] - [:span {:class (stl/css :check-icon)} - i/tick]])))]]])) + [:div {:class (stl/css :custom-select-dropdown) + :ref select-wrapper-ref} + [:ul {:class (stl/css :custom-select-dropdown-list)} + (for [[index item] (map-indexed vector options)] + (if (= :separator item) + [:li {:class (stl/css :separator) + :key (dm/str element-id "-" index)}] + (let [[value label] (as-key-value item)] + [:li + {:key (str element-id "-" index) + :class (stl/css-case :dropdown-element true + :is-selected (= (dm/str value) current-value)) + :data-value value + :on-click select-item} + [:span {:class (stl/css :label)} label] + [:span {:class (stl/css :value)} value] + [:span {:class (stl/css :check-icon)} + i/tick]])))]]]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss index 698ed839e..6d40a3b00 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss @@ -31,25 +31,44 @@ } } + .custom-select-dropdown-list { + width: 100%; + max-width: 180px; + margin-bottom: 0; + } + .custom-select-dropdown { @extend .dropdown-wrapper; max-height: $s-320; width: auto; margin-top: $s-4; right: 0; - left: auto; + left: unset; .separator { margin: 0; height: $s-12; } + .dropdown-element { @extend .dropdown-element-base; color: var(--menu-foreground-color-rest); + padding: 0; + display: flex; + + .label, + .value { + width: fit-content; + } + .label { - flex-grow: 1; - width: 100%; text-transform: unset; + flex: 1; + } + + .value { + flex: 0.2; + text-align: right; } .check-icon { From ced325e0090aa1b4b18177c8e94ca1e844ad57f2 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 10:15:22 +0200 Subject: [PATCH 09/13] Return selected item map instead of value [*] [*] Multiple tokens could have the same value --- .../sidebar/options/menus/measures.cljs | 3 +- .../ui/workspace/tokens/editable_select.cljs | 68 ++++++++++--------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 4bb1d09b5..6674298c1 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -294,8 +294,7 @@ (mf/use-fn (mf/deps ids change-radius border-radius-tokens) (fn [value] - (let [token (when (symbol? value) - (get border-radius-tokens (str value))) + (let [token (when (map? value) value) token-value (some-> token wtc/resolve-token-value)] (st/emit! (change-radius (fn [shape] diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs index 70f625cff..e89f0d9f6 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs @@ -48,28 +48,30 @@ (dom/stop-propagation event) (swap! state* assoc :is-open? false)) - select-item - (mf/use-fn - (mf/deps on-change on-blur) - (fn [event] - (let [value (-> (dom/get-current-target event) - (dom/get-data "value") - (d/read-string))] - (swap! state* assoc :current-value value) - (when on-change (on-change value)) - (when on-blur (on-blur))))) - - as-key-value (fn [item] (if (map? item) [(:value item) (:label item)] [item item])) - labels-map (into {} (map as-key-value) options) - value->label (fn [value] (get labels-map value value)) + labels-map (->> (map (fn [{:keys [label] :as item}] + [label item]) + options) + (into {})) set-value (fn [value] (swap! state* assoc :current-value value) (when on-change (on-change value))) - ;; TODO: why this method supposes that all editable select - ;; works with numbers? + select-item + (mf/use-fn + (mf/deps on-change on-blur labels-map) + (fn [event] + (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 + :current-value value + :current-item item) + (when on-change (on-change item)) + (when on-blur (on-blur))))) handle-change-input (fn [event] @@ -163,13 +165,13 @@ [:div {:class (dm/str class " " (stl/css :editable-select)) :ref on-node-load} (if (= type "number") - [:> numeric-input* {:value (or (some-> current-value value->label) "") + [:> numeric-input* {:value (or current-value "") :className input-class :on-change set-value :on-focus handle-focus :on-blur handle-blur :placeholder placeholder}] - [:input {:value (or (some-> current-value value->label) "") + [:input {:value (or current-value "") :class input-class :on-change handle-change-input :on-key-down handle-key-down @@ -188,18 +190,18 @@ [:div {:class (stl/css :custom-select-dropdown) :ref select-wrapper-ref} [:ul {:class (stl/css :custom-select-dropdown-list)} - (for [[index item] (map-indexed vector options)] - (if (= :separator item) - [:li {:class (stl/css :separator) - :key (dm/str element-id "-" index)}] - (let [[value label] (as-key-value item)] - [:li - {:key (str element-id "-" index) - :class (stl/css-case :dropdown-element true - :is-selected (= (dm/str value) current-value)) - :data-value value - :on-click select-item} - [:span {:class (stl/css :label)} label] - [:span {:class (stl/css :value)} value] - [:span {:class (stl/css :check-icon)} - i/tick]])))]]]])) + (for [[index item] (d/enumerate options)] + (cond + (= :separator item) [:li {:class (stl/css :separator) + :key (dm/str element-id "-" index)}] + :else (let [{:keys [value label]} item] + [:li + {:key (str element-id "-" index) + :class (stl/css-case :dropdown-element true + :is-selected (= (dm/str value) current-value)) + :data-label label + :on-click select-item} + [:span {:class (stl/css :label)} label] + [:span {:class (stl/css :value)} value] + [:span {:class (stl/css :check-icon)} + i/tick]])))]]]])) From b61a59d3757558c1140a1d97840d32942f04b33d Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 10:16:01 +0200 Subject: [PATCH 10/13] Extract key down handler --- .../ui/workspace/tokens/editable_select.cljs | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs index e89f0d9f6..e67195983 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs @@ -19,11 +19,30 @@ [app.util.timers :as timers] [rumext.v2 :as mf])) +(defn on-number-input-key-down [{:keys [event min-val max-val set-value!]}] + (let [up? (kbd/up-arrow? event) + down? (kbd/down-arrow? event)] + (when (or up? down?) + (dom/prevent-default event) + (let [value (-> event dom/get-target dom/get-value) + value (or (d/parse-double value) value) + increment (cond + (kbd/shift? event) (if up? 10 -10) + (kbd/alt? event) (if up? 0.1 -0.1) + :else (if up? 1 -1)) + new-value (+ value increment) + new-value (cond + (and (d/num? min-val) (< new-value min-val)) min-val + (and (d/num? max-val) (> new-value max-val)) max-val + :else new-value)] + (set-value! new-value))))) + (mf/defc editable-select [{:keys [value type options class on-change placeholder on-blur input-class] :as params}] (let [state* (mf/use-state {:id (uuid/next) :is-open? false :current-value value + :current-item nil :top nil :left nil :bottom nil}) @@ -98,34 +117,17 @@ handle-key-down (mf/use-fn - (mf/deps set-value) + (mf/deps set-value is-open?) (fn [event] - (when (= type "number") - (let [up? (kbd/up-arrow? event) - down? (kbd/down-arrow? event)] - (when (or up? down?) - (dom/prevent-default event) - (let [value (-> event dom/get-target dom/get-value) - value (or (d/parse-double value) value) - - increment (cond - (kbd/shift? event) - (if up? 10 -10) - - (kbd/alt? event) - (if up? 0.1 -0.1) - - :else - (if up? 1 -1)) - - new-value (+ value increment) - - new-value (cond - (and (d/num? min-val) (< new-value min-val)) min-val - (and (d/num? max-val) (> new-value max-val)) max-val - :else new-value)] - - (set-value new-value))))))) + (cond + is-open? (let [up? (kbd/up-arrow? event) + down? (kbd/down-arrow? event)] + (dom/prevent-default event) + (js/console.log "up? down?" up? down?)) + (= type "number") (on-number-input-key-down {:event event + :min-val min-val + :max-val max-val + :set-value! set-value})))) handle-focus (mf/use-fn From 85a40d19ed2fe0d85059ebd46889f0773badb60f Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 10:26:32 +0200 Subject: [PATCH 11/13] Extract component --- .../ui/workspace/tokens/editable_select.cljs | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs index e67195983..afdb1810a 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs @@ -37,6 +37,27 @@ :else new-value)] (set-value! new-value))))) +(mf/defc dropdown-select [{:keys [on-close element-id element-ref options on-select]}] + [:& dropdown {:show true + :on-close on-close} + [:div {:class (stl/css :custom-select-dropdown) + :ref element-ref} + [:ul {:class (stl/css :custom-select-dropdown-list)} + (for [[index item] (d/enumerate options)] + (cond + (= :separator item) [:li {:class (stl/css :separator) + :key (dm/str element-id "-" index)}] + :else (let [{:keys [value label]} item] + [:li + {:key (str element-id "-" index) + :class (stl/css-case :dropdown-element true + :is-selected false #_(= (dm/str value) current-value)) + :data-label label + :on-click on-select} + [:span {:class (stl/css :label)} label] + [:span {:class (stl/css :value)} value] + [:span {:class (stl/css :check-icon)} i/tick]])))]]]) + (mf/defc editable-select [{:keys [value type options class on-change placeholder on-blur input-class] :as params}] (let [state* (mf/use-state {:id (uuid/next) @@ -187,23 +208,9 @@ :on-click toggle-dropdown} i/arrow]) - [:& dropdown {:show (or is-open? false) - :on-close close-dropdown} - [:div {:class (stl/css :custom-select-dropdown) - :ref select-wrapper-ref} - [:ul {:class (stl/css :custom-select-dropdown-list)} - (for [[index item] (d/enumerate options)] - (cond - (= :separator item) [:li {:class (stl/css :separator) - :key (dm/str element-id "-" index)}] - :else (let [{:keys [value label]} item] - [:li - {:key (str element-id "-" index) - :class (stl/css-case :dropdown-element true - :is-selected (= (dm/str value) current-value)) - :data-label label - :on-click select-item} - [:span {:class (stl/css :label)} label] - [:span {:class (stl/css :value)} value] - [:span {:class (stl/css :check-icon)} - i/tick]])))]]]])) + (when (and is-open? (seq options)) + [:& dropdown-select {:on-close close-dropdown + :element-id element-id + :element-ref select-wrapper-ref + :options options + :on-select select-item}])])) From ad26d9e2d36b9c262377c8bc88e5cdad7672c483 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 10:55:24 +0200 Subject: [PATCH 12/13] More styling --- .../src/app/main/ui/workspace/tokens/editable_select.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss index 6d40a3b00..650968571 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss @@ -32,8 +32,9 @@ } .custom-select-dropdown-list { + min-width: 150px; width: 100%; - max-width: 180px; + max-width: 200px; margin-bottom: 0; } @@ -67,8 +68,8 @@ } .value { - flex: 0.2; text-align: right; + flex: 0.6; } .check-icon { From 3caa9d780ae132dc9abc6fb0b09597bfe6c76c95 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Fri, 17 May 2024 11:37:36 +0200 Subject: [PATCH 13/13] Show checkmark icon for applied tokens --- .../main/ui/workspace/sidebar/options/menus/measures.cljs | 5 +++-- .../src/app/main/ui/workspace/tokens/editable_select.cljs | 4 ++-- .../src/app/main/ui/workspace/tokens/editable_select.scss | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 6674298c1..1b862b22d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -100,9 +100,10 @@ tokens (mf/deref refs/workspace-tokens) border-radius-tokens (mf/use-memo (mf/deps tokens) #(wtc/tokens-name-map-for-type :border-radius tokens)) - border-radius-options (mf/use-memo (mf/deps border-radius-tokens) + border-radius-options (mf/use-memo (mf/deps shape border-radius-tokens) #(map (fn [[_k {:keys [name] :as item}]] - (assoc item :label name)) + (cond-> (assoc item :label name) + (wtc/token-applied? item shape (wtc/token-attributes :border-radius)) (assoc :selected? true))) border-radius-tokens)) flex-child? (->> selection-parents (some ctl/flex-layout?)) diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs index afdb1810a..65264f703 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.cljs @@ -47,11 +47,11 @@ (cond (= :separator item) [:li {:class (stl/css :separator) :key (dm/str element-id "-" index)}] - :else (let [{:keys [value label]} item] + :else (let [{:keys [value label selected?]} item] [:li {:key (str element-id "-" index) :class (stl/css-case :dropdown-element true - :is-selected false #_(= (dm/str value) current-value)) + :is-selected selected?) :data-label label :on-click on-select} [:span {:class (stl/css :label)} label] diff --git a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss index 650968571..bfc2a0c9e 100644 --- a/frontend/src/app/main/ui/workspace/tokens/editable_select.scss +++ b/frontend/src/app/main/ui/workspace/tokens/editable_select.scss @@ -74,6 +74,7 @@ .check-icon { @include flexCenter; + translate: -$s-4 0; svg { @extend .button-icon-small; visibility: hidden;