From c807baaf7a8ac757bd9cbca3052bb1866842a31c Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Mon, 26 Aug 2024 15:54:44 +0200 Subject: [PATCH] Add custom select with grouped options --- frontend/src/app/main/refs.cljs | 3 + .../app/main/ui/workspace/tokens/sidebar.cljs | 15 +- .../ui/workspace/tokens/theme_select.cljs | 165 ++++++------------ .../ui/workspace/tokens/theme_select.scss | 15 ++ 4 files changed, 73 insertions(+), 125 deletions(-) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index dcf7381f1..349dc2967 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -247,6 +247,9 @@ (def workspace-active-theme-ids (l/derived wtts/get-active-theme-ids st/state)) +(def workspace-temp-theme-id + (l/derived wtts/get-temp-theme-id st/state)) + (def workspace-active-set-ids (l/derived wtts/get-active-set-ids st/state)) diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index 3998e049d..9c1e536dd 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -191,21 +191,12 @@ (mf/defc themes-sidebar [_props] - (let [open? (mf/use-state true) - active-theme-ids (mf/deref refs/workspace-active-theme-ids) - themes (mf/deref refs/workspace-ordered-token-themes)] + (let [ordered-themes (mf/deref refs/workspace-ordered-token-themes)] [:div {:class (stl/css :theme-sidebar)} [:span {:class (stl/css :themes-header)} "Themes"] [:div {:class (stl/css :theme-select-wrapper)} - [:& theme-select - {:default-value (some-> active-theme-ids first) - :class (stl/css :select-format-wrapper) - :options (mapcat (fn [[_ xs]] - (map (fn [{:keys [id name] :as f}] - {:value id :label name}) - xs)) - themes)}] - [:& edit-button {:create? (empty? themes)}]]])) + [:& theme-select] + [:& edit-button {:create? (empty? ordered-themes)}]]])) (mf/defc sets-sidebar [] diff --git a/frontend/src/app/main/ui/workspace/tokens/theme_select.cljs b/frontend/src/app/main/ui/workspace/tokens/theme_select.cljs index f8ac1f403..8977dabc1 100644 --- a/frontend/src/app/main/ui/workspace/tokens/theme_select.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/theme_select.cljs @@ -7,123 +7,62 @@ (ns app.main.ui.workspace.tokens.theme-select (:require-macros [app.main.style :as stl]) (:require - [app.common.data :as d] - [app.common.data.macros :as dm] [app.common.uuid :as uuid] + [app.main.refs :as refs] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.icons :as i] - [app.util.dom :as dom] [rumext.v2 :as mf])) - -(defn- as-key-value - [item] - (if (map? item) - [(:value item) (:label item) (:icon item)] - [item item item])) - -(mf/defc theme-select - [{:keys [default-value options class dropdown-class is-open? on-change on-pointer-enter-option on-pointer-leave-option disabled]}] - (let [label-index (mf/with-memo [options] - (into {} (map as-key-value) options)) - - state* (mf/use-state - {:id (uuid/next) - :is-open? (or is-open? false) - :current-value default-value}) - - state (deref state*) - current-id (get state :id) - current-value (get state :current-value) - current-label (get label-index current-value) - is-open? (:is-open? state) - - dropdown-element* (mf/use-ref nil) - dropdown-direction* (mf/use-state "down") - dropdown-direction-change* (mf/use-ref 0) - - open-dropdown - (mf/use-fn - (mf/deps disabled) - (fn [] - (when-not disabled - (swap! state* assoc :is-open? true)))) - - close-dropdown (mf/use-fn #(swap! state* assoc :is-open? false)) - - select-item - (mf/use-fn - (mf/deps on-change) - (fn [event] - (let [value (-> (dom/get-current-target event) - (dom/get-data "value") - (d/read-string))] - - (swap! state* assoc :current-value value) - (when (fn? on-change) - (on-change value))))) - - highlight-item - (mf/use-fn - (mf/deps on-pointer-enter-option) - (fn [event] - (when (fn? on-pointer-enter-option) - (let [value (-> (dom/get-current-target event) - (dom/get-data "value") - (d/read-string))] - (on-pointer-enter-option value))))) - - unhighlight-item - (mf/use-fn - (mf/deps on-pointer-leave-option) - (fn [event] - (when (fn? on-pointer-leave-option) - (let [value (-> (dom/get-current-target event) - (dom/get-data "value") - (d/read-string))] - (on-pointer-leave-option value)))))] - - (mf/with-effect [default-value] - (swap! state* assoc :current-value default-value)) - - (mf/with-effect [is-open? dropdown-element*] - (let [dropdown-element (mf/ref-val dropdown-element*)] - (when (and (= 0 (mf/ref-val dropdown-direction-change*)) dropdown-element) - (let [is-outside? (dom/is-element-outside? dropdown-element)] - (reset! dropdown-direction* (if is-outside? "up" "down")) - (mf/set-ref-val! dropdown-direction-change* (inc (mf/ref-val dropdown-direction-change*))))))) - - (let [selected-option (first (filter #(= (:value %) default-value) options)) - current-icon (:icon selected-option) - current-icon-ref (i/key->icon current-icon)] - [:div {:on-click open-dropdown - :class (dm/str (stl/css-case :custom-select true - :disabled disabled - :icon (some? current-icon-ref)) - " " class)} - (when (and current-icon current-icon-ref) - [:span {:class (stl/css :current-icon)} current-icon-ref]) - [:span {:class (stl/css :current-label)} current-label] - [:span {:class (stl/css :dropdown-button)} i/arrow] - [:& dropdown {:show is-open? :on-close close-dropdown} - [:ul {:ref dropdown-element* :data-direction @dropdown-direction* - :class (dm/str dropdown-class " " (stl/css :custom-select-dropdown))} - (for [[index item] (d/enumerate options)] - (if (= :separator item) - [:li {:class (dom/classnames (stl/css :separator) true) - :key (dm/str current-id "-" index)}] - (let [[value label icon] (as-key-value item) - icon-ref (i/key->icon icon)] - [:li - {:key (dm/str current-id "-" index) +(mf/defc theme-options + [{:keys []}] + (let [active-theme-ids (mf/deref refs/workspace-active-theme-ids) + ordered-themes (mf/deref refs/workspace-ordered-token-themes)] + [:ul + (for [[group themes] ordered-themes] + [:li {:key group} + (when group + [:span {:class (stl/css :group)} group]) + [:ul + (for [{:keys [id name]} themes + :let [selected? (get active-theme-ids id)]] + [:li {:key id :class (stl/css-case :checked-element true - :disabled (:disabled item) - :is-selected (= value current-value)) - :data-value (pr-str value) - :on-pointer-enter highlight-item - :on-pointer-leave unhighlight-item - :on-click select-item} - (when (and icon icon-ref) [:span {:class (stl/css :icon)} icon-ref]) - [:span {:class (stl/css :label)} label] - [:span {:class (stl/css :check-icon)} i/tick]])))]]]))) + :is-selected selected?)} + [:span {:class (stl/css :label)} name] + [:span {:class (stl/css :check-icon)} i/tick]])]])])) + +(mf/defc theme-select + [{:keys []}] + (let [;; Store + temp-theme-id (mf/deref refs/workspace-temp-theme-id) + active-theme-ids (-> (mf/deref refs/workspace-active-theme-ids) + (disj temp-theme-id)) + active-themes-count (count active-theme-ids) + themes (mf/deref refs/workspace-token-themes) + + ;; Data + current-label (cond + (> active-themes-count 1) (str active-themes-count " themes active") + (pos? active-themes-count) (get-in themes [(first active-theme-ids) :name]) + :else "No theme active") + + ;; State + state* (mf/use-state + {:id (uuid/next) + :is-open? false}) + state (deref state*) + is-open? (:is-open? state) + + ;; Dropdown + dropdown-element* (mf/use-ref nil) + on-close-dropdown (mf/use-fn #(swap! state* assoc :is-open? false)) + on-open-dropdown (mf/use-fn #(swap! state* assoc :is-open? true))] + [:div {:on-click on-open-dropdown + :class (stl/css :custom-select)} + [:span {:class (stl/css :current-label)} current-label] + [:span {:class (stl/css :dropdown-button)} i/arrow] + [:& dropdown {:show is-open? :on-close on-close-dropdown} + [:div {:ref dropdown-element* + :class (stl/css :custom-select-dropdown)} + [:& theme-options]]]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/theme_select.scss b/frontend/src/app/main/ui/workspace/tokens/theme_select.scss index 68af4ae36..0c97b2721 100644 --- a/frontend/src/app/main/ui/workspace/tokens/theme_select.scss +++ b/frontend/src/app/main/ui/workspace/tokens/theme_select.scss @@ -27,6 +27,18 @@ color: var(--text-color); cursor: pointer; + ul { + margin-bottom: 0; + } + + .group { + display: block; + @include headlineSmallTypography; + padding: $s-8; + color: var(--color-foreground-secondary); + font-weight: 600; + } + &.icon { grid-template-columns: auto 1fr auto; } @@ -87,6 +99,9 @@ .checked-element { @extend .dropdown-element-base; + + padding-left: $s-16; + .icon { @include flexCenter; height: $s-24;