♻️ Refactor state management of shadow menu

This commit is contained in:
Andrey Antukh 2025-01-29 20:26:27 +01:00
parent c2fae0fef2
commit b449074425

View file

@ -27,7 +27,6 @@
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row*]] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row*]]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[okulary.core :as l]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def shadow-attrs [:shadow]) (def shadow-attrs [:shadow])
@ -50,8 +49,8 @@
(filterv (fn [[idx _]] (not= idx index))) (filterv (fn [[idx _]] (not= idx index)))
(mapv second))) (mapv second)))
(mf/defc shadow-entry (mf/defc shadow-entry*
[{:keys [ids index value on-reorder disable-drag? on-blur open-state-ref]}] [{:keys [ids index shadow on-reorder is-open on-toggle-open]}]
(let [basic-offset-x-ref (mf/use-ref nil) (let [basic-offset-x-ref (mf/use-ref nil)
basic-offset-y-ref (mf/use-ref nil) basic-offset-y-ref (mf/use-ref nil)
basic-blur-ref (mf/use-ref nil) basic-blur-ref (mf/use-ref nil)
@ -61,16 +60,12 @@
adv-blur-ref (mf/use-ref nil) adv-blur-ref (mf/use-ref nil)
adv-spread-ref (mf/use-ref nil) adv-spread-ref (mf/use-ref nil)
shadow-style (:style value) shadow-style (:style shadow)
shadow-id (:id shadow)
shadow-id (:id value) hidden? (:hidden shadow)
open-status-ref (mf/with-memo [open-state-ref shadow-id]
(-> (l/key shadow-id)
(l/derived open-state-ref)))
open-shadow (mf/deref open-status-ref)
hidden? (:hidden value)
;; FIXME: move to parent
on-remove-shadow on-remove-shadow
(mf/use-fn (mf/use-fn
(mf/deps ids index) (mf/deps ids index)
@ -87,7 +82,6 @@
(h/use-sortable (h/use-sortable
:data-type "penpot/shadow-entry" :data-type "penpot/shadow-entry"
:on-drop on-drop :on-drop on-drop
:disabled disable-drag?
:detect-center? false :detect-center? false
:data {:id (dm/str "shadow-" index) :data {:id (dm/str "shadow-" index)
:index index :index index
@ -118,13 +112,13 @@
detach-color detach-color
(mf/use-fn (mf/use-fn
(mf/deps ids index value) (mf/deps ids index shadow)
(fn [_color _opacity] (fn [_color _opacity]
(when-not (string? (:color value)) (when-not (string? (:color shadow))
(st/emit! (dwsh/update-shapes (st/emit! (dwsh/update-shapes
ids ids
#(assoc-in % [:shadow index :color] #(assoc-in % [:shadow index :color]
(dissoc (:color value) :id :file-id))))))) (dissoc (:color shadow) :id :file-id)))))))
toggle-visibility toggle-visibility
(mf/use-fn (mf/use-fn
@ -132,9 +126,10 @@
(fn [] (fn []
(st/emit! (dwsh/update-shapes ids #(update-in % [:shadow index :hidden] not))))) (st/emit! (dwsh/update-shapes ids #(update-in % [:shadow index :hidden] not)))))
on-toggle-open-shadow on-toggle-open
(fn [] (mf/use-fn
(swap! open-state-ref update shadow-id not)) (mf/deps shadow-id on-toggle-open)
#(on-toggle-open shadow-id))
on-type-change on-type-change
(mf/use-fn (mf/use-fn
@ -143,12 +138,16 @@
(let [value (keyword event)] (let [value (keyword event)]
(st/emit! (dwsh/update-shapes ids #(assoc-in % [:shadow index :style] value)))))) (st/emit! (dwsh/update-shapes ids #(assoc-in % [:shadow index :style] value))))))
type-options [{:value "drop-shadow" :label (tr "workspace.options.shadow-options.drop-shadow")} type-options
{:value "inner-shadow" :label (tr "workspace.options.shadow-options.inner-shadow")}] (mf/with-memo []
[{:value "drop-shadow" :label (tr "workspace.options.shadow-options.drop-shadow")}
{:value "inner-shadow" :label (tr "workspace.options.shadow-options.inner-shadow")}])
manage-on-open #(st/emit! (dwu/start-undo-transaction :color-row)) manage-on-open
manage-on-close #(st/emit! (dwu/commit-undo-transaction :color-row))] (mf/use-fn #(st/emit! (dwu/start-undo-transaction :color-row)))
manage-on-close
(mf/use-fn #(st/emit! (dwu/commit-undo-transaction :color-row)))]
[:div {:class (stl/css-case :global/shadow-option true [:div {:class (stl/css-case :global/shadow-option true
:shadow-element true :shadow-element true
@ -162,8 +161,8 @@
[:div {:class (stl/css-case :shadow-info true [:div {:class (stl/css-case :shadow-info true
:hidden hidden?)} :hidden hidden?)}
[:button {:class (stl/css-case :more-options true [:button {:class (stl/css-case :more-options true
:selected open-shadow) :selected is-open)
:on-click on-toggle-open-shadow} :on-click on-toggle-open}
i/menu] i/menu]
[:div {:class (stl/css :type-select)} [:div {:class (stl/css :type-select)}
[:& select [:& select
@ -180,10 +179,10 @@
:aria-label (tr "workspace.options.shadow-options.remove-shadow") :aria-label (tr "workspace.options.shadow-options.remove-shadow")
:on-click on-remove-shadow :on-click on-remove-shadow
:icon "remove"}]]] :icon "remove"}]]]
(when open-shadow (when is-open
[:& advanced-options {:class (stl/css :shadow-advanced-options) [:& advanced-options {:class (stl/css :shadow-advanced-options)
:visible? open-shadow :visible? is-open
:on-close on-toggle-open-shadow} :on-close on-toggle-open}
[:div {:class (stl/css :first-row)} [:div {:class (stl/css :first-row)}
[:div {:class (stl/css :offset-x-input) [:div {:class (stl/css :offset-x-input)
@ -195,8 +194,7 @@
:no-validate true :no-validate true
:placeholder "--" :placeholder "--"
:on-change (update-attr index :offset-x basic-offset-x-ref) :on-change (update-attr index :offset-x basic-offset-x-ref)
:on-blur on-blur :value (:offset-x shadow)}]]
:value (:offset-x value)}]]
[:div {:class (stl/css :blur-input) [:div {:class (stl/css :blur-input)
:title (tr "workspace.options.shadow-options.blur")} :title (tr "workspace.options.shadow-options.blur")}
@ -207,9 +205,8 @@
:no-validate true :no-validate true
:placeholder "--" :placeholder "--"
:on-change (update-attr index :blur basic-blur-ref) :on-change (update-attr index :blur basic-blur-ref)
:on-blur on-blur
:min 0 :min 0
:value (:blur value)}]] :value (:blur shadow)}]]
[:div {:class (stl/css :spread-input) [:div {:class (stl/css :spread-input)
:title (tr "workspace.options.shadow-options.spread")} :title (tr "workspace.options.shadow-options.spread")}
@ -220,8 +217,7 @@
:no-validate true :no-validate true
:placeholder "--" :placeholder "--"
:on-change (update-attr index :spread) :on-change (update-attr index :spread)
:on-blur on-blur :value (:spread shadow)}]]]
:value (:spread value)}]]]
[:div {:class (stl/css :second-row)} [:div {:class (stl/css :second-row)}
[:div {:class (stl/css :offset-y-input) [:div {:class (stl/css :offset-y-input)
@ -233,14 +229,9 @@
:no-validate true :no-validate true
:placeholder "--" :placeholder "--"
:on-change (update-attr index :offset-y basic-offset-y-ref) :on-change (update-attr index :offset-y basic-offset-y-ref)
:on-blur on-blur :value (:offset-y shadow)}]]
:value (:offset-y value)}]]
;; FIXME: memoize color [:> color-row* {:color (:color shadow)
[:> color-row* {:color (if (string? (:color value))
;; Support for old format colors
{:color (:color value) :opacity (:opacity value)}
(:color value))
:title (tr "workspace.options.shadow-options.color") :title (tr "workspace.options.shadow-options.color")
:disable-gradient true :disable-gradient true
:disable-image true :disable-image true
@ -249,26 +240,36 @@
:on-open manage-on-open :on-open manage-on-open
:on-close manage-on-close}]]])]])) :on-close manage-on-close}]]])]]))
(def ^:private xf:add-index
(map-indexed (fn [index shadow]
(assoc shadow ::index index))))
(mf/defc shadow-menu (mf/defc shadow-menu
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [{:keys [ids type values]}]
(let [ids (unchecked-get props "ids")
type (unchecked-get props "type")
values (unchecked-get props "values")
shadows (:shadow values []) (let [shadows (get values :shadow [])
open-state-ref (mf/with-memo [] (l/atom {})) shadows (mf/with-memo [shadows]
has-shadows? (or (= :multiple shadows) (some? (seq shadows))) (if (= :multiple shadows)
shadows
(into [] xf:add-index shadows)))
state* (mf/use-state {:show-content true open-state* (mf/use-state {})
:disable-drag false}) open-state (deref open-state*)
state (deref state*) has-shadows? (or (= :multiple shadows)
open? (:show-content state) (some? (seq shadows)))
disable-drag? (:disable-drag state)
show-content* (mf/use-state true)
show-content? (deref show-content*)
toggle-content toggle-content
(mf/use-fn #(swap! state* update :show-content not)) (mf/use-fn #(swap! show-content* not))
on-toggle-open
(mf/use-fn
(fn [shadow-id] (swap! open-state* update shadow-id not)))
on-remove-all on-remove-all
(mf/use-fn (mf/use-fn
@ -282,10 +283,6 @@
(fn [new-index index] (fn [new-index index]
(st/emit! (dc/reorder-shadows ids index new-index)))) (st/emit! (dc/reorder-shadows ids index new-index))))
on-blur
(mf/use-fn
#(swap! state* assoc :disable-drag false))
on-add-shadow on-add-shadow
(mf/use-fn (mf/use-fn
(mf/deps ids) (mf/deps ids)
@ -294,7 +291,7 @@
[:div {:class (stl/css :element-set)} [:div {:class (stl/css :element-set)}
[:div {:class (stl/css :element-title)} [:div {:class (stl/css :element-title)}
[:& title-bar {:collapsable has-shadows? [:& title-bar {:collapsable has-shadows?
:collapsed (not open?) :collapsed (not show-content?)
:on-collapsed toggle-content :on-collapsed toggle-content
:title (case type :title (case type
:multiple (tr "workspace.options.shadow-options.title.multiple") :multiple (tr "workspace.options.shadow-options.title.multiple")
@ -309,7 +306,7 @@
:icon "add" :icon "add"
:data-testid "add-shadow"}])]] :data-testid "add-shadow"}])]]
(when open? (when show-content?
(cond (cond
(= :multiple shadows) (= :multiple shadows)
[:div {:class (stl/css :element-set-content)} [:div {:class (stl/css :element-set-content)}
@ -324,13 +321,12 @@
(seq shadows) (seq shadows)
[:& h/sortable-container {} [:& h/sortable-container {}
[:div {:class (stl/css :element-set-content)} [:div {:class (stl/css :element-set-content)}
(for [[index value] (d/enumerate shadows)] (for [{:keys [::index id] :as shadow} shadows]
[:& shadow-entry [:> shadow-entry*
{:key (dm/str "shadow-" index) {:key (dm/str "shadow-" index)
:ids ids :ids ids
:value value :shadow shadow
:is-open (get open-state id)
:on-reorder handle-reorder :on-reorder handle-reorder
:disable-drag? disable-drag? :on-toggle-open on-toggle-open
:on-blur on-blur :index index}])]]))]))
:index index
:open-state-ref open-state-ref}])]]))]))