diff --git a/frontend/src/app/main/ui/components/numeric_input.cljs b/frontend/src/app/main/ui/components/numeric_input.cljs index 9b90349798..4218a6b37a 100644 --- a/frontend/src/app/main/ui/components/numeric_input.cljs +++ b/frontend/src/app/main/ui/components/numeric_input.cljs @@ -42,7 +42,7 @@ ;; We need to store the handle-blur ref so we can call it on unmount handle-blur-ref (mf/use-ref nil) - dirty-ref (mf/use-ref false) + dirty-ref (mf/use-ref false) ;; This `value` represents the previous value and is used as ;; initil value for the simple math expression evaluation. @@ -106,10 +106,11 @@ apply-value (mf/use-callback (mf/deps on-change update-input value) - (fn [new-value] + (fn [new-value event] (mf/set-ref-val! dirty-ref false) - (when (and (not= new-value value) (some? on-change)) - (on-change new-value)) + (when (and (not= new-value value) + (fn? on-change)) + (on-change new-value event)) (update-input new-value))) set-delta @@ -146,7 +147,7 @@ :else new-value)] - (apply-value new-value)))))) + (apply-value new-value event)))))) handle-key-down (mf/use-callback @@ -180,12 +181,12 @@ handle-blur (mf/use-callback (mf/deps parse-value apply-value update-input on-blur) - (fn [_] + (fn [event] (let [new-value (or (parse-value) default-val)] (if (or nillable new-value) - (apply-value new-value) + (apply-value new-value event) (update-input new-value))) - (when on-blur (on-blur)))) + (when on-blur (on-blur event)))) on-click (mf/use-callback @@ -236,7 +237,7 @@ (events/listen globals/window EventType.CLICK on-click)]] #(doseq [key keys] (events/unlistenByKey key))))) - + (mf/use-layout-effect (mf/deps handle-mouse-wheel) (fn [] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs index 3a122a53f0..63cace23b2 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs @@ -8,6 +8,8 @@ (:require [app.common.colors :as clr] [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.math :as mth] [app.common.uuid :as uuid] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.colors :as dc] @@ -24,75 +26,85 @@ (def shadow-attrs [:shadow]) -(defn create-shadow [] - (let [id (uuid/next)] - {:id id - :style :drop-shadow - :color {:color clr/black :opacity 0.2} - :offset-x 4 - :offset-y 4 - :blur 4 - :spread 0 - :hidden false})) +(defn- create-shadow + [] + {:id (uuid/next) + :style :drop-shadow + :color {:color clr/black + :opacity 0.2} + :offset-x 4 + :offset-y 4 + :blur 4 + :spread 0 + :hidden false}) -(defn valid-number? [value] - (or (number? value) (not (js/isNaN (js/parseInt value))))) +(defn- remove-shadow-by-index + [values index] + (->> (d/enumerate values) + (filterv (fn [[idx _]] (not= idx index))) + (mapv second))) (mf/defc shadow-entry - [{:keys [ids index value on-reorder select-all disable-drag on-blur]}] - (let [open-shadow (mf/use-state false) + [{:keys [ids index value on-reorder disable-drag? on-blur]}] + (let [open-shadow (mf/use-state false) basic-offset-x-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) - adv-offset-x-ref (mf/use-ref nil) - adv-offset-y-ref (mf/use-ref nil) - adv-blur-ref (mf/use-ref nil) - adv-spread-ref (mf/use-ref nil) + adv-offset-x-ref (mf/use-ref nil) + adv-offset-y-ref (mf/use-ref nil) + adv-blur-ref (mf/use-ref nil) + adv-spread-ref (mf/use-ref nil) - shadow-style (str (:style value)) - - remove-shadow-by-index - (fn [values index] (->> (d/enumerate values) - (filterv (fn [[idx _]] (not= idx index))) - (mapv second))) + shadow-style (dm/str (:style value)) on-remove-shadow - (fn [index] - (fn [] - (st/emit! (dch/update-shapes ids #(update % :shadow remove-shadow-by-index index))))) - - select-text - (fn [ref] (fn [_] (dom/select-text! (mf/ref-val ref)))) + (mf/use-fn + (mf/deps ids) + (fn [event] + (let [index (-> (dom/get-current-target event) + (dom/get-data "index") + (parse-long))] + (st/emit! (dch/update-shapes ids #(update % :shadow remove-shadow-by-index index)))))) on-drop - (fn [_ data] - (on-reorder (:index data))) + (mf/use-fn + (mf/deps on-reorder index) + (fn [_ data] + (on-reorder index (:index data)))) - [dprops dref] (if (some? on-reorder) - (h/use-sortable - :data-type "penpot/shadow-entry" - :on-drop on-drop - :disabled @disable-drag - :detect-center? false - :data {:id (str "shadow-" index) - :index index - :name (str "Border row" index)}) - [nil nil]) + [dprops dref] + (h/use-sortable + :data-type "penpot/shadow-entry" + :on-drop on-drop + :disabled disable-drag? + :detect-center? false + :data {:id (dm/str "shadow-" index) + :index index + :name (dm/str "Border row" index)}) + ;; FIXME: this function causes the numeric-input rerender + ;; ALWAYS, this is causes because numeric-input design makes + ;; imposible implement efficiently any component that uses it; + ;; it should be refactored update-attr (fn update-attr - ([index attr valid?] - (update-attr index attr valid? nil)) - - ([index attr valid? update-ref] + ([index attr] + (update-attr index attr nil)) + ([index attr update-ref] (fn [value] - (when (or (not valid?) (valid? value)) - (do (st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index attr] value))) - (let [update-node (and update-ref (mf/ref-val update-ref))] - (when update-node - (dom/set-value! update-node value)))))))) + (when (mth/finite? value) + (st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index attr] value))) + (when-let [update-node (and update-ref (mf/ref-val update-ref))] + (dom/set-value! update-node value)))))) + + ;; FIXME: the same as previous function, imposible to + ;; implement efficiently because of numeric-input component + ;; and probably this affects all callbacks that that component + ;; receives + select-text + (fn [ref] (fn [_] (dom/select-text! (mf/ref-val ref)))) update-color (fn [index] @@ -115,136 +127,135 @@ (fn [] (st/emit! (dch/update-shapes ids #(update-in % [:shadow index :hidden] not)))))] [:* - [:div.border-data {:class (dom/classnames - :dnd-over-top (= (:over dprops) :top) - :dnd-over-bot (= (:over dprops) :bot)) - :ref dref} - [:div.element-set-options-group {:style {:display (when @open-shadow "none")}} - [:div.element-set-actions-button - {:on-click #(reset! open-shadow true)} - i/actions] + [:div.border-data {:class (dom/classnames + :dnd-over-top (= (:over dprops) :top) + :dnd-over-bot (= (:over dprops) :bot)) + :ref dref} + [:div.element-set-options-group {:style {:display (when @open-shadow "none")}} + [:div.element-set-actions-button + {:on-click #(reset! open-shadow true)} + i/actions] - ;; [:> numeric-input {:ref basic-offset-x-ref - ;; :on-change (update-attr index :offset-x valid-number?) - ;; :on-click (select-text basic-offset-x-ref) - ;; :value (:offset-x value)}] - ;; [:> numeric-input {:ref basic-offset-y-ref - ;; :on-change (update-attr index :offset-y valid-number?) - ;; :on-click (select-text basic-offset-y-ref) - ;; :value (:offset-y value)}] - ;; [:> numeric-input {:ref basic-blur-ref - ;; :on-click (select-text basic-blur-ref) - ;; :on-change (update-attr index :blur valid-number?) - ;; :min 0 - ;; :value (:blur value)}] + [:select.input-select + {:default-value shadow-style + :on-change (fn [event] + (let [value (-> event dom/get-target dom/get-value d/read-string)] + (st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value)))))} + [:option {:value ":drop-shadow" + :selected (when (= shadow-style ":drop-shadow") "selected")} + (tr "workspace.options.shadow-options.drop-shadow")] + [:option {:value ":inner-shadow" + :selected (when (= shadow-style ":inner-shadow") "selected")} + (tr "workspace.options.shadow-options.inner-shadow")]] - [:select.input-select - {:default-value shadow-style - :on-change (fn [event] - (let [value (-> event dom/get-target dom/get-value d/read-string)] - (st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value)))))} - [:option {:value ":drop-shadow" :selected (when (= shadow-style ":drop-shadow") "selected")} (tr "workspace.options.shadow-options.drop-shadow")] - [:option {:value ":inner-shadow" :selected (when (= shadow-style ":inner-shadow") "selected")} (tr "workspace.options.shadow-options.inner-shadow")]] + [:div.element-set-actions + [:div.element-set-actions-button {:on-click (toggle-visibility index)} + (if (:hidden value) i/eye-closed i/eye)] + [:div.element-set-actions-button + {:data-index index + :on-click on-remove-shadow} + i/minus]]] - [:div.element-set-actions - [:div.element-set-actions-button {:on-click (toggle-visibility index)} - (if (:hidden value) i/eye-closed i/eye)] - [:div.element-set-actions-button {:on-click (on-remove-shadow index)} - i/minus]]] + [:& advanced-options {:visible? @open-shadow + :on-close #(reset! open-shadow false)} + [:div.color-data + [:div.element-set-actions-button + {:on-click #(reset! open-shadow false)} + i/actions] + [:select.input-select + {:default-value (str (:style value)) + :on-change (fn [event] + (let [value (-> event dom/get-target dom/get-value d/read-string)] + (st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value)))))} + [:option {:value ":drop-shadow"} (tr "workspace.options.shadow-options.drop-shadow")] + [:option {:value ":inner-shadow"} (tr "workspace.options.shadow-options.inner-shadow")]]] - [:& advanced-options {:visible? @open-shadow - :on-close #(reset! open-shadow false)} - [:div.color-data - [:div.element-set-actions-button - {:on-click #(reset! open-shadow false)} - i/actions] - [:select.input-select - {:default-value (str (:style value)) - :on-change (fn [event] - (let [value (-> event dom/get-target dom/get-value d/read-string)] - (st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value)))))} - [:option {:value ":drop-shadow"} (tr "workspace.options.shadow-options.drop-shadow")] - [:option {:value ":inner-shadow"} (tr "workspace.options.shadow-options.inner-shadow")]]] + [:div.row-grid-2 + [:div.input-element {:title (tr "workspace.options.shadow-options.offsetx")} + [:> numeric-input {:ref adv-offset-x-ref + :no-validate true + :placeholder "--" + :on-focus (select-text adv-offset-x-ref) + :on-change (update-attr index :offset-x basic-offset-x-ref) + :on-blur on-blur + :value (:offset-x value)}] + [:span.after (tr "workspace.options.shadow-options.offsetx")]] - [:div.row-grid-2 - [:div.input-element {:title (tr "workspace.options.shadow-options.offsetx")} - [:> numeric-input {:ref adv-offset-x-ref - :no-validate true - :placeholder "--" - :on-focus (select-text adv-offset-x-ref) - :on-change (update-attr index :offset-x valid-number? basic-offset-x-ref) - :on-blur on-blur - :value (:offset-x value)}] - [:span.after (tr "workspace.options.shadow-options.offsetx")]] + [:div.input-element {:title (tr "workspace.options.shadow-options.offsety")} + [:> numeric-input {:ref adv-offset-y-ref + :no-validate true + :placeholder "--" + :on-focus (select-text adv-offset-y-ref) + :on-change (update-attr index :offset-y basic-offset-y-ref) + :on-blur on-blur + :value (:offset-y value)}] + [:span.after (tr "workspace.options.shadow-options.offsety")]]] - [:div.input-element {:title (tr "workspace.options.shadow-options.offsety")} - [:> numeric-input {:ref adv-offset-y-ref - :no-validate true - :placeholder "--" - :on-focus (select-text adv-offset-y-ref) - :on-change (update-attr index :offset-y valid-number? basic-offset-y-ref) - :on-blur on-blur - :value (:offset-y value)}] - [:span.after (tr "workspace.options.shadow-options.offsety")]]] + [:div.row-grid-2 + [:div.input-element {:title (tr "workspace.options.shadow-options.blur")} + [:> numeric-input {:ref adv-blur-ref + :no-validate true + :placeholder "--" + :on-focus (select-text adv-blur-ref) + :on-change (update-attr index :blur basic-blur-ref) + :on-blur on-blur + :min 0 + :value (:blur value)}] + [:span.after (tr "workspace.options.shadow-options.blur")]] - [:div.row-grid-2 - [:div.input-element {:title (tr "workspace.options.shadow-options.blur")} - [:> numeric-input {:ref adv-blur-ref - :no-validate true - :placeholder "--" - :on-focus (select-text adv-blur-ref) - :on-change (update-attr index :blur valid-number? basic-blur-ref) - :on-blur on-blur - :min 0 - :value (:blur value)}] - [:span.after (tr "workspace.options.shadow-options.blur")]] + [:div.input-element {:title (tr "workspace.options.shadow-options.spread")} + [:> numeric-input {:ref adv-spread-ref + :no-validate true + :placeholder "--" + :on-focus (select-text adv-spread-ref) + :on-change (update-attr index :spread) + :on-blur on-blur + :value (:spread value)}] + [:span.after (tr "workspace.options.shadow-options.spread")]]] - [:div.input-element {:title (tr "workspace.options.shadow-options.spread")} - [:> numeric-input {:ref adv-spread-ref - :no-validate true - :placeholder "--" - :on-focus (select-text adv-spread-ref) - :on-change (update-attr index :spread valid-number?) - :on-blur on-blur - :value (:spread value)}] - [:span.after (tr "workspace.options.shadow-options.spread")]]] - - [:div.color-row-wrap - [:& 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") - :disable-gradient true - :on-change (update-color index) - :on-detach (detach-color index) - :on-open #(st/emit! (dwu/start-undo-transaction :color-row)) - :on-close #(st/emit! (dwu/commit-undo-transaction :color-row))}]]]]])) + [:div.color-row-wrap + [:& 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") + :disable-gradient true + :on-change (update-color index) + :on-detach (detach-color index) + :on-open #(st/emit! (dwu/start-undo-transaction :color-row)) + :on-close #(st/emit! (dwu/commit-undo-transaction :color-row))}]]]]])) (mf/defc shadow-menu - [{:keys [ids type values] :as props}] - (let [on-remove-all-shadows - (fn [_] (st/emit! (dch/update-shapes ids #(dissoc % :shadow)))) + {::mf/wrap-props false} + [props] + (let [ids (unchecked-get props "ids") + type (unchecked-get props "type") + values (unchecked-get props "values") + + shadows (:shadow values []) + + disable-drag* (mf/use-state false) + disable-drag? (deref disable-drag*) + + on-remove-all + (mf/use-fn + (mf/deps ids) + (fn [] + (st/emit! (dch/update-shapes ids #(dissoc % :shadow))))) handle-reorder - (mf/use-callback - (mf/deps ids) - (fn [new-index] - (fn [index] - (st/emit! (dc/reorder-shadows ids index new-index))))) + (mf/use-fn + (mf/deps ids) + (fn [new-index index] + (st/emit! (dc/reorder-shadows ids index new-index)))) - disable-drag (mf/use-state false) - - select-all (fn [event] - (when (not @disable-drag) - (dom/select-text! (dom/get-target event))) - (reset! disable-drag true)) - - on-blur (fn [_] - (reset! disable-drag false)) + on-blur + (mf/use-fn + #(reset! disable-drag* false)) on-add-shadow - (fn [_] - (st/emit! (dc/add-shadow ids (create-shadow))))] + (mf/use-fn + (mf/deps ids) + #(st/emit! (dc/add-shadow ids (create-shadow))))] [:div.element-set.shadow-options [:div.element-set-title @@ -254,27 +265,27 @@ :group (tr "workspace.options.shadow-options.title.group") (tr "workspace.options.shadow-options.title"))] - (when-not (= :multiple (:shadow values)) + (when-not (= :multiple shadows) [:div.add-page {:on-click on-add-shadow} i/close])] (cond - (= :multiple (:shadow values)) + (= :multiple shadows) [:div.element-set-content [:div.element-set-options-group [:div.element-set-label (tr "settings.multiple")] [:div.element-set-actions - [:div.element-set-actions-button {:on-click on-remove-all-shadows} + [:div.element-set-actions-button {:on-click on-remove-all} i/minus]]]] - (seq (:shadow values)) + (seq shadows) [:& h/sortable-container {} [:div.element-set-content - (for [[index value] (d/enumerate (:shadow values []))] - [:& shadow-entry {:key (str "shadow-" index) - :ids ids - :value value - :on-reorder (handle-reorder index) - :select-all select-all - :disable-drag disable-drag - :on-blur on-blur - :index index}])]])])) + (for [[index value] (d/enumerate shadows)] + [:& shadow-entry + {:key (dm/str "shadow-" index) + :ids ids + :value value + :on-reorder handle-reorder + :disable-drag? disable-drag? + :on-blur on-blur + :index index}])]])]))