diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 65def23a5..c3ce7b831 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -221,48 +221,62 @@ (defn not-changed? [old-dim new-dim] (> (mth/abs (- old-dim new-dim)) 0.1)) - (defn resize-text-batch [changes] (ptk/reify ::resize-text-batch ptk/WatchEvent (watch [_ state stream] - (let [objects (dwc/lookup-page-objects state) + (let [page-id (:current-page-id state) + + objects0 (get-in state [:workspace-file :data :pages-index page-id :objects]) + objects1 (get-in state [:workspace-data :pages-index page-id :objects]) + change-text-shape - (fn [events [id [new-width new-height]]] + (fn [objects [id [new-width new-height]]] + (let [shape (get objects id) {:keys [selrect grow-type overflow-text]} (gsh/transform-shape shape) - {shape-width :width shape-height :height} selrect] + {shape-width :width shape-height :height} selrect - (cond-> events - (and overflow-text (not= :fixed grow-type)) - (conj (update-overflow-text id false)) + modifier-width (gsh/resize-modifiers shape :width new-width) + modifier-height (gsh/resize-modifiers shape :height new-height) - (and (= :fixed grow-type) (not overflow-text) (> new-height shape-height)) - (conj (update-overflow-text id true)) + shape (cond-> shape + (and overflow-text (not= :fixed grow-type)) + (assoc :overflow-text false) - (and (= :fixed grow-type) overflow-text (<= new-height shape-height)) - (conj (update-overflow-text id false)) + (and (= :fixed grow-type) (not overflow-text) (> new-height shape-height)) + (assoc :overflow-text true) - (and (or (not-changed? shape-width new-width) (not-changed? shape-height new-height)) - (= grow-type :auto-width)) - (conj (dwt/update-dimensions [id] :width new-width) - (dwt/update-dimensions [id] :height new-height)) + (and (= :fixed grow-type) overflow-text (<= new-height shape-height)) + (assoc :overflow-text true) - (and (not-changed? shape-height new-height) - (= grow-type :auto-height)) - (conj (dwt/update-dimensions [id] :height new-height))))) + (and (not-changed? shape-width new-width) (= grow-type :auto-width)) + (-> (assoc :modifiers modifier-width) + (gsh/transform-shape)) + + (and (not-changed? shape-height new-height) + (or (= grow-type :auto-height) (= grow-type :auto-width))) + (-> (assoc :modifiers modifier-height) + (gsh/transform-shape)))] + (assoc objects id shape))) undo-transaction (get-in state [:workspace-undo :transaction]) - events (->> changes (reduce change-text-shape []))] + objects2 (->> changes (reduce change-text-shape objects1)) - (if (seq events) + regchg {:type :reg-objects + :page-id page-id + :shapes (vec (keys changes))} + + rchanges (dwc/generate-changes page-id objects1 objects2) + uchanges (dwc/generate-changes page-id objects2 objects0)] + + (if (seq rchanges) (rx/concat - (when (not undo-transaction) + (when-not undo-transaction (rx/of (dwc/start-undo-transaction))) - (rx/from events) - (when (not undo-transaction) - (rx/of (dwc/discard-undo-transaction)))) - (rx/empty)))))) + (rx/of (dwc/commit-changes (conj rchanges regchg) (conj uchanges regchg) {:commit-local? true})) + (when-not undo-transaction + (rx/of (dwc/discard-undo-transaction))))))))) ;; When a resize-event arrives we start "buffering" for a time ;; after that time we invoke `resize-text-batch` with all the changes diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 635eb826f..dc04be45e 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -159,51 +159,45 @@ ids)) workspace-page-objects =)) +(def selected-data + (l/derived #(let [selected (get-in % [:workspace-local :selected]) + page-id (:current-page-id %) + objects (get-in % [:workspace-data :pages-index page-id :objects])] + (hash-map :selected selected + :page-id page-id + :objects objects)) + st/state =)) + (defn is-child-selected? [id] - (letfn [(selector [state] - (let [page-id (:current-page-id state) - objects (get-in state [:workspace-data :pages-index page-id :objects]) - selected (get-in state [:workspace-local :selected]) - children (cp/get-children id objects)] + (letfn [(selector [{:keys [selected page-id objects]}] + (let [children (cp/get-children id objects)] (some #(contains? selected %) children)))] - (l/derived selector st/state))) + (l/derived selector selected-data =))) - -;; TODO: can be replaced by objects-by-id (def selected-objects - (letfn [(selector [state] - (let [selected (get-in state [:workspace-local :selected]) - page-id (:current-page-id state) - objects (get-in state [:workspace-data :pages-index page-id :objects])] - (->> selected - (map #(get objects %)) - (filterv (comp not nil?)))))] - (l/derived selector st/state =))) + (letfn [(selector [{:keys [selected page-id objects]}] + (->> selected + (map #(get objects %)) + (filterv (comp not nil?))))] + (l/derived selector selected-data =))) (def selected-shapes-with-children - (letfn [(selector [state] - (let [selected (get-in state [:workspace-local :selected]) - page-id (:current-page-id state) - objects (get-in state [:workspace-data :pages-index page-id :objects]) - children (->> selected + (letfn [(selector [{:keys [selected page-id objects]}] + (let [children (->> selected (mapcat #(cp/get-children % objects)) (filterv (comp not nil?)))] (into selected children)))] - (l/derived selector st/state =))) - + (l/derived selector selected-data =))) (def selected-objects-with-children - (letfn [(selector [state] - (let [selected (get-in state [:workspace-local :selected]) - page-id (:current-page-id state) - objects (get-in state [:workspace-data :pages-index page-id :objects]) - children (->> selected + (letfn [(selector [{:keys [selected page-id objects]}] + (let [children (->> selected (mapcat #(cp/get-children % objects)) (filterv (comp not nil?))) shapes (into selected children)] (mapv #(get objects %) shapes)))] - (l/derived selector st/state =))) + (l/derived selector selected-data =))) ;; ---- Viewer refs diff --git a/frontend/src/app/main/ui/shapes/path.cljs b/frontend/src/app/main/ui/shapes/path.cljs index da40a1ef7..295cceb7f 100644 --- a/frontend/src/app/main/ui/shapes/path.cljs +++ b/frontend/src/app/main/ui/shapes/path.cljs @@ -24,7 +24,8 @@ (let [shape (unchecked-get props "shape") background? (unchecked-get props "background?") {:keys [id x y width height]} (:selrect shape) - pdata (ugp/content->path (:content shape)) + content (:content shape) + pdata (mf/use-memo (mf/deps content) #(ugp/content->path content)) props (-> (attrs/extract-style-attrs shape) (obj/merge! #js {:d pdata}))] diff --git a/frontend/src/app/main/ui/workspace/selection.cljs b/frontend/src/app/main/ui/workspace/selection.cljs index 76f57dbdc..f2b916974 100644 --- a/frontend/src/app/main/ui/workspace/selection.cljs +++ b/frontend/src/app/main/ui/workspace/selection.cljs @@ -359,6 +359,7 @@ :zoom zoom}])])) (mf/defc selection-handlers + {::mf/wrap [mf/memo]} [{:keys [selected edition zoom show-distances] :as props}] (let [;; We need remove posible nil values because on shape ;; deletion many shape will reamin selected and deleted diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/multiple.cljs index 9ac5e9693..319321a7c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/multiple.cljs @@ -153,8 +153,15 @@ result))] (reduce extract-attrs [] shapes))) +;; TODO: Remove when added to rumext +(defn check-props + ([props] (check-props props =)) + ([props eqfn?] + (fn [np op] + (every? #(eqfn? (unchecked-get np %) (unchecked-get op %)) props)))) + (mf/defc options - {::mf/wrap [mf/memo] + {::mf/wrap [#(mf/memo' % (check-props ["shape" "shapes-with-children"]))] ::mf/wrap-props false} [props] (let [shapes (unchecked-get props "shapes") diff --git a/frontend/src/app/main/ui/workspace/snap_distances.cljs b/frontend/src/app/main/ui/workspace/snap_distances.cljs index 41d36fba0..966c5f388 100644 --- a/frontend/src/app/main/ui/workspace/snap_distances.cljs +++ b/frontend/src/app/main/ui/workspace/snap_distances.cljs @@ -267,11 +267,9 @@ (mf/defc snap-distances {::mf/wrap-props false} [props] - (let [layout (unchecked-get props "layout") - page-id (unchecked-get props "page-id") + (let [page-id (unchecked-get props "page-id") zoom (unchecked-get props "zoom") selected (unchecked-get props "selected") - transform (unchecked-get props "transform") selected-shapes (mf/deref (refs/objects-by-id selected)) frame-id (-> selected-shapes first :frame-id) frame (mf/deref (refs/object-by-id frame-id)) @@ -280,23 +278,20 @@ update-shape (fn [shape] (-> shape (update :modifiers merge (:modifiers local)) gsh/transform-shape))] - (when (and (contains? layout :dynamic-alignment) - (= transform :move) - (not (empty? selected))) - (let [selrect (->> selected-shapes (map update-shape) gsh/selection-rect) - key (->> selected (map str) (str/join "-"))] - [:g.distance - [:& shape-distance - {:selrect selrect - :page-id page-id - :frame frame - :zoom zoom - :coord :x - :selected selected}] - [:& shape-distance - {:selrect selrect - :page-id page-id - :frame frame - :zoom zoom - :coord :y - :selected selected}]])))) + (let [selrect (->> selected-shapes (map update-shape) gsh/selection-rect) + key (->> selected (map str) (str/join "-"))] + [:g.distance + [:& shape-distance + {:selrect selrect + :page-id page-id + :frame frame + :zoom zoom + :coord :x + :selected selected}] + [:& shape-distance + {:selrect selrect + :page-id page-id + :frame frame + :zoom zoom + :coord :y + :selected selected}]]))) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 32d630f6d..8d75e685c 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -188,7 +188,8 @@ :edition edition}]])) (mf/defc ghost-frames - {::mf/wrap-props false} + {::mf/wrap [mf/memo] + ::mf/wrap-props false} [props] (let [modifiers (obj/get props "modifiers") selected (obj/get props "selected") @@ -249,6 +250,7 @@ (gsh/selection-rect)) alt? (mf/use-state false) + cursor (mf/use-state cur/pointer-inner) viewport-ref (mf/use-ref nil) zoom-view-ref (mf/use-ref nil) last-position (mf/use-var nil) @@ -260,6 +262,13 @@ drawing-path? (and edition (= :draw (get-in edit-path [edition :edit-mode]))) zoom (or zoom 1) + show-grids? (contains? layout :display-grid) + show-snap-points? (and (contains? layout :dynamic-alignment) + (or drawing-obj (:transform local))) + show-snap-distance? (and (contains? layout :dynamic-alignment) + (= (:transform local) :move) + (not (empty? selected))) + on-mouse-down (mf/use-callback (mf/deps drawing-tool edition) @@ -590,6 +599,27 @@ ;; We schedule the event so it fires after `initialize-page` event (timers/schedule #(st/emit! (dw/initialize-viewport size)))))) + ;; This change is in an effect to minimize the sideffects of the cursor chaning + ;; Changing a cursor will produce a "reflow" so we defer it until the component is rendered + (mf/use-layout-effect + (mf/deps @cursor panning drawing-tool drawing-path?) + (fn [] + (let [new-cursor + (cond + panning cur/hand + (= drawing-tool :comments) cur/comments + (= drawing-tool :frame) cur/create-artboard + (= drawing-tool :rect) cur/create-rectangle + (= drawing-tool :circle) cur/create-ellipse + (or (= drawing-tool :path) drawing-path?) cur/pen + (= drawing-tool :curve) cur/pencil + drawing-tool cur/create-shape + :else cur/pointer-inner)] + + (when (not= @cursor new-cursor) + (timers/raf + #(reset! cursor new-cursor)))))) + (mf/use-layout-effect (mf/deps layout) on-resize) (hooks/use-stream ms/keyboard-alt #(reset! alt? %)) @@ -619,16 +649,7 @@ :view-box (format-viewbox vbox) :ref viewport-ref :class (when drawing-tool "drawing") - :style {:cursor (cond - panning cur/hand - (= drawing-tool :comments) cur/comments - (= drawing-tool :frame) cur/create-artboard - (= drawing-tool :rect) cur/create-rectangle - (= drawing-tool :circle) cur/create-ellipse - (or (= drawing-tool :path) drawing-path?) cur/pen - (= drawing-tool :curve) cur/pencil - drawing-tool cur/create-shape - :else cur/pointer-inner) + :style {:cursor @cursor :background-color (get options :background "#E8E9EA")} :on-context-menu on-context-menu :on-click on-click @@ -671,22 +692,24 @@ :tool drawing-tool :modifiers (:modifiers local)}]) - (when (contains? layout :display-grid) + (when show-grids? [:& frame-grid {:zoom zoom}]) - [:& snap-points {:layout layout - :transform (:transform local) - :drawing drawing-obj - :zoom zoom - :page-id page-id - :selected selected - :local local}] + (when show-snap-points? + [:& snap-points {:layout layout + :transform (:transform local) + :drawing drawing-obj + :zoom zoom + :page-id page-id + :selected selected + :local local}]) - [:& snap-distances {:layout layout - :zoom zoom - :transform (:transform local) - :selected selected - :page-id page-id}] + (when show-snap-distance? + [:& snap-distances {:layout layout + :zoom zoom + :transform (:transform local) + :selected selected + :page-id page-id}]) (when tooltip [:& cursor-tooltip {:zoom zoom :tooltip tooltip}])] @@ -697,7 +720,9 @@ [:& interactions {:selected selected}])]])) -(mf/defc viewport-actions [] +(mf/defc viewport-actions + {::mf/wrap [mf/memo]} + [] (let [edition (mf/deref refs/selected-edition) selected (mf/deref refs/selected-objects) shape (-> selected first)]