diff --git a/frontend/src/uxbox/main/ui/components/defer.cljs b/frontend/src/uxbox/main/ui/components/defer.cljs new file mode 100644 index 000000000..df75ebaca --- /dev/null +++ b/frontend/src/uxbox/main/ui/components/defer.cljs @@ -0,0 +1,47 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns uxbox.main.ui.components.defer + (:require + [rumext.alpha :as mf] + [uxbox.common.uuid :as uuid] + [uxbox.util.dom :as dom] + [uxbox.util.timers :as ts] + [goog.events :as events] + [goog.functions :as gf] + [goog.object :as gobj]) + (:import goog.events.EventType + goog.events.KeyCodes)) + +(defn deferred + ([component] (deferred component ts/raf)) + ([component schedule] + (mf/fnc deferred + {::mf/wrap-props false} + [props] + (let [[render? set-render!] (mf/useState false)] + (mf/use-effect + (fn [] (schedule #(set-render! true)))) + (when render? + (mf/create-element component props)))))) + +(defn throttle + [component ms] + (mf/fnc throttle + {::mf/wrap-props false} + [props] + (let [[state set-state] (mf/useState props) + set-state* (mf/use-memo #(gf/throttle set-state ms))] + + (mf/use-effect + nil + (fn [] + (set-state* props))) + + (mf/create-element component state)))) diff --git a/frontend/src/uxbox/main/ui/shapes/frame.cljs b/frontend/src/uxbox/main/ui/shapes/frame.cljs index 76d372537..711e7c09e 100644 --- a/frontend/src/uxbox/main/ui/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/shapes/frame.cljs @@ -17,9 +17,11 @@ [uxbox.util.geom.shapes :as geom] [uxbox.main.refs :as refs] [uxbox.main.store :as st] + [uxbox.main.ui.components.defer :refer [deferred]] [uxbox.main.ui.shapes.attrs :as attrs] [uxbox.main.ui.shapes.common :as common] [uxbox.util.dom :as dom] + [uxbox.util.timers :as ts] [uxbox.util.interop :as itr] [uxbox.util.geom.matrix :as gmt] [uxbox.util.geom.point :as gpt])) @@ -53,7 +55,8 @@ [shape-wrapper] (let [frame-shape (frame-shape shape-wrapper)] (mf/fnc frame-wrapper - {::mf/wrap [#(mf/memo' % frame-wrapper-memo-equals?)] + {::mf/wrap [#(deferred % ts/schedule-on-idle) + #(mf/memo' % frame-wrapper-memo-equals?)] ::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape") diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs index 77ded54a7..316ae3d03 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs @@ -10,21 +10,23 @@ (ns uxbox.main.ui.workspace.sidebar.layers (:require - [rumext.alpha :as mf] [okulary.core :as l] - [uxbox.common.data :as d] + [rumext.alpha :as mf] [uxbox.builtins.icons :as i] - [uxbox.main.data.workspace :as dw] + [uxbox.common.data :as d] + [uxbox.common.uuid :as uuid] [uxbox.main.data.helpers :as dh] + [uxbox.main.data.workspace :as dw] [uxbox.main.refs :as refs] [uxbox.main.store :as st] + [uxbox.main.ui.components.defer :refer [deferred]] [uxbox.main.ui.hooks :as hooks] [uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.shapes.icon :as icon] [uxbox.util.dom :as dom] - [uxbox.util.perf :as perf] - [uxbox.common.uuid :as uuid] - [uxbox.util.i18n :as i18n :refer [t]])) + [uxbox.util.timers :as ts] + [uxbox.util.i18n :as i18n :refer [t]] + [uxbox.util.perf :as perf])) ;; --- Helpers @@ -230,7 +232,8 @@ (mf/defc frame-wrapper {::mf/wrap-props false - ::mf/wrap [#(mf/memo' % frame-wrapper-memo-equals?)]} + ::mf/wrap [#(deferred % ts/idle-then-raf) + #(mf/memo' % frame-wrapper-memo-equals?)]} [props] [:> layer-item props]) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs index ddcebcced..826a5f92c 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options.cljs @@ -15,6 +15,7 @@ [uxbox.main.data.workspace :as udw] [uxbox.main.store :as st] [uxbox.main.refs :as refs] + [uxbox.main.ui.components.defer :refer [throttle]] [uxbox.main.ui.workspace.sidebar.align :refer [align-options]] [uxbox.main.ui.workspace.sidebar.options.frame :as frame] [uxbox.main.ui.workspace.sidebar.options.group :as group] @@ -30,6 +31,7 @@ ;; --- Options (mf/defc shape-options + {::mf/wrap [#(throttle % 60)]} [{:keys [shape] :as props}] [:div (case (:type shape) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/fill.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/fill.cljs index 4fbc63399..f33ac1ae2 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/fill.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/fill.cljs @@ -17,10 +17,21 @@ [uxbox.main.ui.modal :as modal] [uxbox.main.ui.workspace.colorpicker :refer [colorpicker-modal]] [uxbox.util.dom :as dom] + [uxbox.util.object :as obj] [uxbox.util.math :as math] [uxbox.util.i18n :as i18n :refer [tr t]])) +(defn- fill-menu-memo-equals? + [np op] + (let [new-shape (obj/get np "shape") + old-shape (obj/get op "shape")] + (and (identical? (:fill-color new-shape) + (:fill-color old-shape)) + (identical? (:fill-opacity new-shape) + (:fill-opacity old-shape))))) + (mf/defc fill-menu + {::mf/wrap [#(mf/memo' % fill-menu-memo-equals?)]} [{:keys [shape] :as props}] (let [locale (i18n/use-locale) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/measures.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/measures.cljs index af16028ae..4f3a881c2 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/measures.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/measures.cljs @@ -21,9 +21,10 @@ [uxbox.util.math :as math] [uxbox.util.i18n :refer [t] :as i18n])) - ;; -- User/drawing coords -(defn user-coords-vector [shape] + +(defn user-coords-vector + [shape] (let [{sel-x :x sel-y :y :as selrect} (-> shape gsh/shape->path @@ -35,11 +36,13 @@ dy (- rec-y sel-y)] (gpt/point dx dy))) -(defn user->draw [{:keys [x y width height] :as shape}] +(defn user->draw + [{:keys [x y width height] :as shape}] (let [dv (user-coords-vector shape)] - (-> shape (gsh/move dv)))) + (-> shape (gsh/move dv)))) -(defn draw->user [{:keys [x y width height] :as shape}] +(defn draw->user + [{:keys [x y width height] :as shape}] (let [dv (user-coords-vector shape)] (-> shape (gsh/move (gpt/negate dv))))) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rect.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rect.cljs index 5f5fcf7b1..b13418bd2 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/rect.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/rect.cljs @@ -15,6 +15,7 @@ [uxbox.main.ui.workspace.sidebar.options.stroke :refer [stroke-menu]])) (mf/defc options + {::mf/wrap [mf/memo]} [{:keys [shape] :as props}] [:div [:& measures-menu {:shape shape}] diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs index ec2a2a096..5c98aa6a6 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs @@ -17,10 +17,27 @@ [uxbox.main.ui.modal :as modal] [uxbox.main.ui.workspace.colorpicker :refer [colorpicker-modal]] [uxbox.util.dom :as dom] + [uxbox.util.object :as obj] [uxbox.util.i18n :as i18n :refer [tr t]] [uxbox.util.math :as math])) +(defn- stroke-menu-memo-equals? + [np op] + (let [new-shape (obj/get np "shape") + old-shape (obj/get op "shape")] + (and (identical? (:stroke-style new-shape) + (:stroke-style old-shape)) + (identical? (:stroke-alignment new-shape) + (:stroke-alignment old-shape)) + (identical? (:stroke-width new-shape) + (:stroke-width old-shape)) + (identical? (:stroke-color new-shape) + (:stroke-color old-shape)) + (identical? (:stroke-opacity new-shape) + (:stroke-opacity old-shape))))) + (mf/defc stroke-menu + {::mf/wrap [#(mf/memo' % stroke-menu-memo-equals?)]} [{:keys [shape] :as props}] (let [locale (i18n/use-locale) show-options (not= (:stroke-style shape) :none) diff --git a/frontend/src/uxbox/main/ui/workspace/viewport.cljs b/frontend/src/uxbox/main/ui/workspace/viewport.cljs index 122e571cf..4ca78dab8 100644 --- a/frontend/src/uxbox/main/ui/workspace/viewport.cljs +++ b/frontend/src/uxbox/main/ui/workspace/viewport.cljs @@ -23,6 +23,7 @@ [uxbox.main.streams :as ms] [uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.hooks :as hooks] + [uxbox.main.ui.components.defer :refer [deferred]] [uxbox.main.ui.shapes :refer [shape-wrapper frame-wrapper]] [uxbox.main.ui.workspace.drawarea :refer [draw-area start-drawing]] [uxbox.main.ui.workspace.grid :refer [grid]] @@ -102,6 +103,7 @@ (-> (rx/take-until stoper ms/mouse-position) (rx/subscribe #(on-point dom reference %)))))))) + ;; --- Viewport (declare remote-user-cursors) diff --git a/frontend/src/uxbox/util/timers.cljs b/frontend/src/uxbox/util/timers.cljs index 57531058c..ae866918f 100644 --- a/frontend/src/uxbox/util/timers.cljs +++ b/frontend/src/uxbox/util/timers.cljs @@ -32,3 +32,12 @@ (reify rx/IDisposable (-dispose [_] (js/cancelIdleCallback sem))))) + +(defn raf + [f] + (js/window.requestAnimationFrame f)) + +(defn idle-then-raf + [f] + (schedule-on-idle #(raf f))) +