diff --git a/frontend/resources/images/cursors/create-reclangle.svg b/frontend/resources/images/cursors/create-rectangle.svg similarity index 100% rename from frontend/resources/images/cursors/create-reclangle.svg rename to frontend/resources/images/cursors/create-rectangle.svg diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index 5817481548..e2d3ba7b66 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -130,7 +130,7 @@ grid-template-columns: 20px 1fr; .viewport { - cursor: var(--cursor); + cursor: none; grid-column: 1 / span 2; grid-row: 1 / span 2; overflow: hidden; diff --git a/frontend/src/app/config.cljs b/frontend/src/app/config.cljs index 6f0eb25910..8fe724aeff 100644 --- a/frontend/src/app/config.cljs +++ b/frontend/src/app/config.cljs @@ -22,7 +22,7 @@ ;; --- Auxiliar Functions (s/def ::platform #{:windows :linux :macos :other}) -(s/def ::browser #{:chrome :mozilla :safari :edge :other}) +(s/def ::browser #{:chrome :firefox :safari :edge :other}) (defn- parse-browser [] diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index dc04be45ea..cbfabd0432 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -54,6 +54,23 @@ [id] (l/derived #(contains? % id) selected-shapes)) +(def viewport-data + (l/derived #(select-keys % [:options-mode + :zoom + :vport + :vbox + :edition + :edit-path + :tooltip + :selected + :panning + :picking-color? + :transform + :hover + :modifiers + :selrect]) + workspace-local =)) + (def selected-zoom (l/derived :zoom workspace-local)) diff --git a/frontend/src/app/main/ui/cursors.cljs b/frontend/src/app/main/ui/cursors.cljs index 46a1b5d2e9..4d2412ba55 100644 --- a/frontend/src/app/main/ui/cursors.cljs +++ b/frontend/src/app/main/ui/cursors.cljs @@ -18,7 +18,7 @@ (def create-artboard (cursor-ref :create-artboard)) (def create-ellipse (cursor-ref :create-ellipse)) (def create-polygon (cursor-ref :create-polygon)) -(def create-rectangle (cursor-ref :create-reclangle)) +(def create-rectangle (cursor-ref :create-rectangle)) (def create-shape (cursor-ref :create-shape)) (def duplicate (cursor-ref :duplicate 0 0 0)) (def hand (cursor-ref :hand)) diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs index 4762e71d4b..ad5a6b87d7 100644 --- a/frontend/src/app/main/ui/workspace.cljs +++ b/frontend/src/app/main/ui/workspace.cljs @@ -60,7 +60,8 @@ (mf/defc workspace-content {::mf/wrap-props false} [props] - (let [local (mf/deref refs/workspace-local) + (let [local (mf/deref refs/viewport-data) + {:keys [zoom vbox vport options-mode selected]} local file (obj/get props "file") layout (obj/get props "layout")] [:* @@ -72,9 +73,9 @@ [:section.workspace-content [:section.workspace-viewport (when (contains? layout :rules) - [:& workspace-rules {:zoom (:zoom local) - :vbox (:vbox local) - :vport (:vport local)}]) + [:& workspace-rules {:zoom zoom + :vbox vbox + :vport vport}]) [:& viewport-actions] [:& viewport {:file file @@ -85,8 +86,8 @@ ;; Aside [:& left-sidebar {:layout layout}] - [:& right-sidebar {:section (:options-mode local) - :selected (:selected local)}]])) + [:& right-sidebar {:section options-mode + :selected selected}]])) (def trimmed-page-ref (l/derived :trimmed-page st/state =)) diff --git a/frontend/src/app/main/ui/workspace/snap_points.cljs b/frontend/src/app/main/ui/workspace/snap_points.cljs index 1a24da9321..6404b161a9 100644 --- a/frontend/src/app/main/ui/workspace/snap_points.cljs +++ b/frontend/src/app/main/ui/workspace/snap_points.cljs @@ -56,13 +56,13 @@ :opacity line-opacity}]) (defn get-snap - [coord {:keys [shapes page-id filter-shapes local]}] + [coord {:keys [shapes page-id filter-shapes modifiers]}] (let [shape (if (> (count shapes) 1) (->> shapes (map gsh/transform-shape) gsh/selection-rect (gsh/setup {:type :rect})) (->> shapes (first))) - shape (if (:modifiers local) - (-> shape (assoc :modifiers (:modifiers local)) gsh/transform-shape) + shape (if modifiers + (-> shape (assoc :modifiers modifiers) gsh/transform-shape) shape) frame-id (snap/snap-frame-id shapes)] @@ -108,7 +108,7 @@ (hash-map coord fixedv (flip coord) maxv)])))) (mf/defc snap-feedback - [{:keys [shapes page-id filter-shapes zoom local] :as props}] + [{:keys [shapes page-id filter-shapes zoom modifiers] :as props}] (let [state (mf/use-state []) subject (mf/use-memo #(rx/subject)) @@ -134,7 +134,7 @@ #(rx/dispose! sub)))) (mf/use-effect - (mf/deps shapes local) + (mf/deps shapes modifiers) (fn [] (rx/push! subject props))) @@ -155,7 +155,7 @@ (mf/defc snap-points {::mf/wrap [mf/memo]} - [{:keys [layout zoom selected page-id drawing transform local] :as props}] + [{:keys [layout zoom selected page-id drawing transform modifiers] :as props}] (let [shapes (mf/deref (refs/objects-by-id selected)) filter-shapes (mf/deref refs/selected-shapes-with-children) filter-shapes (fn [id] @@ -164,13 +164,11 @@ (not (contains? layout :snap-grid))) (or (filter-shapes id) (not (contains? layout :dynamic-alignment))))) - ;; current-transform (mf/deref refs/current-transform) - ;; snap-data (mf/deref refs/workspace-snap-data) shapes (if drawing [drawing] shapes)] (when (or drawing transform) [:& snap-feedback {:shapes shapes :page-id page-id :filter-shapes filter-shapes :zoom zoom - :local local}]))) + :modifiers modifiers}]))) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 2787f0d4bb..cbee0ddf98 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -59,6 +59,25 @@ (:import goog.events.EventType goog.events.WheelEvent)) +(defonce css-mouse? + (cfg/check-browser? :firefox)) + +(defn get-cursor [cursor] + (if-not css-mouse? + (name cursor) + + (case cursor + :hand cur/hand + :comments cur/comments + :create-artboard cur/create-artboard + :create-rectangle cur/create-rectangle + :create-ellipse cur/create-ellipse + :pen cur/pen + :pencil cur/pencil + :create-shape cur/create-shape + :duplicate cur/duplicate + cur/pointer-inner))) + ;; --- Coordinates Widget (mf/defc coordinates @@ -128,6 +147,65 @@ (declare remote-user-cursors) +(mf/defc render-cursor + {::mf/wrap-props false} + [props] + (let [cursor (unchecked-get props "cursor") + viewport-ref (unchecked-get props "viewport") + + visible? (mf/use-state true) + in-viewport? (mf/use-state true) + + cursor-ref (mf/use-ref nil) + viewport (mf/ref-val viewport-ref) + + node (mf/ref-val cursor-ref) + + on-mouse-move + (mf/use-callback + (mf/deps node @visible?) + (fn [left top event] + + (let [target (dom/get-target event) + style (.getComputedStyle js/window target) + cursor (.getPropertyValue style "cursor") + + x (- (.-clientX event) left) + y (- (.-clientY event) top)] + + (cond + (and (= cursor "none") (not @visible?)) + (reset! visible? true) + + (and (not= cursor "none") @visible?) + (reset! visible? false)) + + (timers/raf + #(let [style (obj/get node "style")] + (obj/set! style "transform" (str "translate(" x "px, " y "px)")))))))] + + (mf/use-effect + (mf/deps viewport on-mouse-move) + (fn [] + (when viewport + (let [{:keys [left top]} (dom/get-bounding-rect viewport) + keys [(events/listen (dom/get-root) EventType.MOUSEMOVE (partial on-mouse-move left top)) + (events/listen viewport EventType.POINTERENTER #(reset! in-viewport? true)) + (events/listen viewport EventType.POINTERLEAVE #(reset! in-viewport? false))]] + + (fn [] + (doseq [key keys] + (events/unlistenByKey key))))))) + + [:svg {:ref cursor-ref + :width 20 + :height 20 + :viewBox "0 0 16 18" + :style {:position "absolute" + :pointer-events "none" + :will-change "transform" + :display (when-not (and @in-viewport? @visible?) "none")}} + [:use {:xlinkHref (str "#cursor-" cursor)}]])) ;; TODO: revisit the refs usage (vs props) (mf/defc shape-outlines @@ -227,9 +305,10 @@ (mf/defc viewport [{:keys [local layout file] :as props}] - (let [{:keys [options-mode + (let [;; When adding data from workspace-local revisit `app.main.ui.workspace` to check + ;; that the new parameter is sent + {:keys [options-mode zoom - flags vport vbox edition @@ -237,14 +316,18 @@ tooltip selected panning - picking-color?]} local + picking-color? + transform + hover + modifiers + selrect]} local page-id (mf/use-ctx ctx/current-page-id) selected-objects (mf/deref refs/selected-objects) alt? (mf/use-state false) - cursor (mf/use-state cur/pointer-inner) + cursor (mf/use-state (get-cursor :pointer-inner)) viewport-ref (mf/use-ref nil) zoom-view-ref (mf/use-ref nil) last-position (mf/use-var nil) @@ -258,9 +341,9 @@ show-grids? (contains? layout :display-grid) show-snap-points? (and (contains? layout :dynamic-alignment) - (or drawing-obj (:transform local))) + (or drawing-obj transform)) show-snap-distance? (and (contains? layout :dynamic-alignment) - (= (:transform local) :move) + (= transform :move) (not (empty? selected))) on-mouse-down @@ -593,29 +676,24 @@ ;; 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/use-effect (mf/deps @cursor @alt? 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 - @alt? cur/duplicate - :else cur/pointer-inner)] + panning (get-cursor :hand) + (= drawing-tool :comments) (get-cursor :comments) + (= drawing-tool :frame) (get-cursor :create-artboard) + (= drawing-tool :rect) (get-cursor :create-rectangle) + (= drawing-tool :circle) (get-cursor :create-ellipse) + (or (= drawing-tool :path) + drawing-path?) (get-cursor :pen) + (= drawing-tool :curve) (get-cursor :pencil) + drawing-tool (get-cursor :create-shape) + @alt? (get-cursor :duplicate) + :else (get-cursor :pointer-inner))] - ;; Chrome BUG: https://bugs.chromium.org/p/chromium/issues/detail?id=664066 - ;; Right now this is a performance concern but cannot find a better alternative (when (not= @cursor new-cursor) - (timers/raf - #(dom/set-css-property (dom/get-root) "--cursor" new-cursor)) (reset! cursor new-cursor))))) (mf/use-layout-effect (mf/deps layout) on-resize) @@ -630,13 +708,17 @@ :layout layout}]) (when (= drawing-tool :comments) - [:& comments-layer {:vbox (:vbox local) - :vport (:vport local) - :zoom (:zoom local) + [:& comments-layer {:vbox vbox + :vport vport + :zoom zoom :drawing drawing :page-id page-id :file-id (:id file)}]) + (when-not css-mouse? + [:& render-cursor {:viewport viewport-ref + :cursor @cursor}]) + [:svg.viewport {:xmlns "http://www.w3.org/2000/svg" :xmlnsXlink "http://www.w3.org/1999/xlink" @@ -647,7 +729,8 @@ :view-box (format-viewbox vbox) :ref viewport-ref :class (when drawing-tool "drawing") - :style {:background-color (get options :background "#E8E9EA")} + :style {:cursor (when css-mouse? @cursor) + :background-color (get options :background "#E8E9EA")} :on-context-menu on-context-menu :on-click on-click :on-double-click on-double-click @@ -665,19 +748,19 @@ "none" "auto")}} [:& frames {:key page-id - :hover (:hover local) + :hover hover :selected selected :edition edition}] - [:g {:style {:display (when (not= :move (:transform local)) "none")}} - [:& ghost-frames {:modifiers (:modifiers local) + [:g {:style {:display (when (not= :move transform) "none")}} + [:& ghost-frames {:modifiers modifiers :selected selected}]] (when (seq selected) [:& selection-handlers {:selected selected :zoom zoom :edition edition - :show-distances (and (not (:transform local)) @alt?)}]) + :show-distances (and (not transform) @alt?)}]) (when (= (count selected) 1) [:& gradient-handlers {:id (first selected) @@ -687,24 +770,24 @@ [:& draw-area {:shape drawing-obj :zoom zoom :tool drawing-tool - :modifiers (:modifiers local)}]) + :modifiers modifiers}]) (when show-grids? [:& frame-grid {:zoom zoom}]) (when show-snap-points? [:& snap-points {:layout layout - :transform (:transform local) + :transform transform :drawing drawing-obj :zoom zoom :page-id page-id :selected selected - :local local}]) + :modifiers modifiers}]) (when show-snap-distance? [:& snap-distances {:layout layout :zoom zoom - :transform (:transform local) + :transform transform :selected selected :page-id page-id}]) @@ -712,7 +795,8 @@ [:& cursor-tooltip {:zoom zoom :tooltip tooltip}])] [:& presence/active-cursors {:page-id page-id}] - [:& selection-rect {:data (:selrect local)}] + [:& selection-rect {:data selrect}] + (when (= options-mode :prototype) [:& interactions {:selected selected}])]]))