diff --git a/frontend/resources/styles/main/partials/workspace-header.scss b/frontend/resources/styles/main/partials/workspace-header.scss index 16bd5a8dac..27cbab6632 100644 --- a/frontend/resources/styles/main/partials/workspace-header.scss +++ b/frontend/resources/styles/main/partials/workspace-header.scss @@ -71,7 +71,7 @@ .zoom-dropdown { top: 45px; - left: -30px; + left: -40px; } } diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index b3f12c849a..ea0e7be7bd 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -93,10 +93,10 @@ .coordinates { background-color: $color-dark-bg; border-radius: $br-small; - bottom: 0px; + bottom: -10px; padding-left: 5px; position: fixed; - right: 240px; + right: 248px; text-align: center; width: 100px; padding-bottom: 2px; diff --git a/frontend/resources/styles/main/partials/zoom-widget.scss b/frontend/resources/styles/main/partials/zoom-widget.scss index 95bd8788b7..8bd3ebdb2e 100644 --- a/frontend/resources/styles/main/partials/zoom-widget.scss +++ b/frontend/resources/styles/main/partials/zoom-widget.scss @@ -17,7 +17,7 @@ .zoom-dropdown { position: absolute; z-index: 12; - width: 150px; + width: 160px; background-color: $color-white; border-radius: $br-small; diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 24fc879c47..d5d7ab4fd2 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -23,8 +23,8 @@ [uxbox.main.data.workspace.common :as dwc] [uxbox.main.data.workspace.notifications :as dwn] [uxbox.main.data.workspace.persistence :as dwp] - [uxbox.main.data.workspace.transforms :as dwt] [uxbox.main.data.workspace.texts :as dwtxt] + [uxbox.main.data.workspace.transforms :as dwt] [uxbox.main.repo :as rp] [uxbox.main.store :as st] [uxbox.main.streams :as ms] @@ -224,8 +224,8 @@ (update-in state [:workspace-local :vbox] (fn [vbox] (-> vbox - (update :x (comp mth/round x)) - (update :y (comp mth/round y)))))))) + (update :x x) + (update :y y))))))) ;; TODO: add spec @@ -328,15 +328,16 @@ ;; --- Zoom Management (defn- impl-update-zoom - [{:keys [vbox vport] :as local} zoom] - (let [zoom (if (fn? zoom) - (zoom (:zoom local)) - zoom) - width (/ (:width vport) zoom) - height (/ (:height vport) zoom)] + [{:keys [vbox vport] :as local} center zoom] + (let [new-zoom (if (fn? zoom) (zoom (:zoom local)) zoom) + old-zoom (:zoom local) + center (if center center (geom/center vbox)) + scale (/ old-zoom new-zoom) + mtx (gmt/scale-matrix (gpt/point scale) center) + vbox' (geom/transform vbox mtx)] (-> local - (assoc :zoom zoom) - (update :vbox assoc :width width :height height)))) + (assoc :zoom new-zoom) + (update :vbox merge (select-keys vbox' [:x :y :width :height]))))) (defn increase-zoom [center] @@ -344,7 +345,7 @@ ptk/UpdateEvent (update [_ state] (update state :workspace-local - #(impl-update-zoom % (fn [z] (* z 1.1))))))) + #(impl-update-zoom % center (fn [z] (min (* z 1.1) 200))))))) (defn decrease-zoom [center] @@ -352,28 +353,14 @@ ptk/UpdateEvent (update [_ state] (update state :workspace-local - #(impl-update-zoom % (fn [z] (* z 0.9))))))) + #(impl-update-zoom % center (fn [z] (max (* z 0.9) 0.01))))))) (def reset-zoom (ptk/reify ::reset-zoom ptk/UpdateEvent (update [_ state] (update state :workspace-local - #(impl-update-zoom % 1))))) - -(def zoom-to-50 - (ptk/reify ::zoom-to-50 - ptk/UpdateEvent - (update [_ state] - (update state :workspace-local - #(impl-update-zoom % 0.5))))) - -(def zoom-to-200 - (ptk/reify ::zoom-to-200 - ptk/UpdateEvent - (update [_ state] - (update state :workspace-local - #(impl-update-zoom % 2))))) + #(impl-update-zoom % nil 1))))) (def zoom-to-fit-all (ptk/reify ::zoom-to-fit-all @@ -1514,10 +1501,8 @@ "-" #(st/emit! decrease-zoom) "ctrl+g" #(st/emit! create-group) "ctrl+shift+g" #(st/emit! remove-group) - "shift+0" #(st/emit! zoom-to-50) - ;; "shift+1" #(st/emit! reset-zoom) + "shift+0" #(st/emit! reset-zoom) "shift+1" #(st/emit! zoom-to-fit-all) - ;; "shift+2" #(st/emit! zoom-to-200) "shift+2" #(st/emit! zoom-to-selected-shape) "ctrl+d" #(st/emit! duplicate-selected) "ctrl+z" #(st/emit! dwc/undo) diff --git a/frontend/src/uxbox/main/ui/shapes/attrs.cljs b/frontend/src/uxbox/main/ui/shapes/attrs.cljs index 62b5f4bc99..71dea3b538 100644 --- a/frontend/src/uxbox/main/ui/shapes/attrs.cljs +++ b/frontend/src/uxbox/main/ui/shapes/attrs.cljs @@ -28,7 +28,7 @@ (when (not= stroke-style :none) (obj/merge! attrs #js {:stroke (:stroke-color shape nil) - :strokeWidth (:stroke-width shape nil) + :strokeWidth (:stroke-width shape 1) :strokeOpacity (:stroke-opacity shape nil) :strokeDasharray (stroke-type->dasharray stroke-style)})) attrs)) diff --git a/frontend/src/uxbox/main/ui/shapes/custom_stroke.cljs b/frontend/src/uxbox/main/ui/shapes/custom_stroke.cljs index 3b8755b921..ce854ae2c0 100644 --- a/frontend/src/uxbox/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/uxbox/main/ui/shapes/custom_stroke.cljs @@ -24,7 +24,6 @@ {:keys [id x y width height]} (geom/shape->rect-shape shape) stroke-style (:stroke-style shape :none) stroke-position (:stroke-alignment shape :center)] - (cond ;; Center alignment (or no stroke): the default in SVG (or (= stroke-style :none) (= stroke-position :center)) @@ -43,7 +42,7 @@ :fill "white" :fillOpacity 1})) - stroke-width (.-strokeWidth base-props) + stroke-width (obj/get base-props "strokeWidth") shape-props (-> (obj/merge! #js {} base-props) (obj/merge! #js {:strokeWidth (* stroke-width 2) :clipPath (str "url('#" clip-id "')")}))] diff --git a/frontend/src/uxbox/main/ui/viewer/header.cljs b/frontend/src/uxbox/main/ui/viewer/header.cljs index 4e683714f7..d957ddbd79 100644 --- a/frontend/src/uxbox/main/ui/viewer/header.cljs +++ b/frontend/src/uxbox/main/ui/viewer/header.cljs @@ -16,14 +16,41 @@ [uxbox.main.refs :as refs] [uxbox.main.store :as st] [uxbox.main.ui.components.dropdown :refer [dropdown]] - [uxbox.main.ui.workspace.header :refer [zoom-widget]] [uxbox.util.data :refer [classnames]] [uxbox.util.dom :as dom] [uxbox.util.i18n :as i18n :refer [t]] [uxbox.util.router :as rt] + [uxbox.util.math :as mth] [uxbox.common.uuid :as uuid] [uxbox.util.webapi :as wapi])) +(mf/defc zoom-widget + {:wrap [mf/memo]} + [{:keys [zoom + on-increase + on-decrease + on-zoom-to-50 + on-zoom-to-100 + on-zoom-to-200] + :as props}] + (let [show-dropdown? (mf/use-state false)] + [:div.zoom-widget {:on-click #(reset! show-dropdown? true)} + [:span {} (str (mth/round (* 100 zoom)) "%")] + [:span.dropdown-button i/arrow-down] + [:& dropdown {:show @show-dropdown? + :on-close #(reset! show-dropdown? false)} + [:ul.zoom-dropdown + [:li {:on-click on-increase} + "Zoom in" [:span "+"]] + [:li {:on-click on-decrease} + "Zoom out" [:span "-"]] + [:li {:on-click on-zoom-to-50} + "Zoom to 50%" [:span "Shift + 0"]] + [:li {:on-click on-zoom-to-100} + "Zoom to 100%" [:span "Shift + 1"]] + [:li {:on-click on-zoom-to-200} + "Zoom to 200%" [:span "Shift + 2"]]]]])) + (mf/defc interactions-menu [{:keys [interactions-mode] :as props}] (let [show-dropdown? (mf/use-state false) diff --git a/frontend/src/uxbox/main/ui/workspace.cljs b/frontend/src/uxbox/main/ui/workspace.cljs index e5306bf380..189360cec8 100644 --- a/frontend/src/uxbox/main/ui/workspace.cljs +++ b/frontend/src/uxbox/main/ui/workspace.cljs @@ -23,7 +23,7 @@ [uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.hooks :as hooks] [uxbox.main.ui.messages :refer [messages]] - [uxbox.main.ui.workspace.viewport :refer [viewport]] + [uxbox.main.ui.workspace.viewport :refer [viewport coordinates]] [uxbox.main.ui.workspace.colorpalette :refer [colorpalette]] [uxbox.main.ui.workspace.context-menu :refer [context-menu]] [uxbox.main.ui.workspace.header :refer [header]] @@ -65,7 +65,9 @@ :vport (:vport local)}] [:& vertical-rule {:zoom (:zoom local 1) :vbox (:vbox local) - :vport (:vport local)}]]) + :vport (:vport local)}] + [:& coordinates]]) + [:& viewport {:page page :key (:id page) diff --git a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs index a55f17bd52..75a7a36c6a 100644 --- a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs +++ b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs @@ -322,14 +322,14 @@ (declare path-draw-area) (mf/defc draw-area - [{:keys [shape] :as props}] + [{:keys [shape zoom] :as props}] (when (:id shape) (case (:type shape) (:path :curve) [:& path-draw-area {:shape shape}] - [:& generic-draw-area {:shape shape}]))) + [:& generic-draw-area {:shape shape :zoom zoom}]))) (mf/defc generic-draw-area - [{:keys [shape]}] + [{:keys [shape zoom]}] (let [{:keys [x y width height]} (geom/selection-rect-shape shape)] (when (and x y) [:g @@ -339,7 +339,7 @@ :height height :style {:stroke "#1FDEA7" :fill "transparent" - :stroke-width "1"}}]]))) + :stroke-width (/ 1 zoom)}}]]))) (mf/defc path-draw-area [{:keys [shape] :as props}] diff --git a/frontend/src/uxbox/main/ui/workspace/header.cljs b/frontend/src/uxbox/main/ui/workspace/header.cljs index d0f750b776..a3352891d4 100644 --- a/frontend/src/uxbox/main/ui/workspace/header.cljs +++ b/frontend/src/uxbox/main/ui/workspace/header.cljs @@ -33,9 +33,9 @@ [{:keys [zoom on-increase on-decrease - on-zoom-to-50 - on-zoom-to-100 - on-zoom-to-200] + on-zoom-reset + on-zoom-fit + on-zoom-selected] :as props}] (let [show-dropdown? (mf/use-state false)] [:div.zoom-widget {:on-click #(reset! show-dropdown? true)} @@ -48,12 +48,12 @@ "Zoom in" [:span "+"]] [:li {:on-click on-decrease} "Zoom out" [:span "-"]] - [:li {:on-click on-zoom-to-50} - "Zoom to 50%" [:span "Shift + 0"]] - [:li {:on-click on-zoom-to-100} - "Zoom to 100%" [:span "Shift + 1"]] - [:li {:on-click on-zoom-to-200} - "Zoom to 200%" [:span "Shift + 2"]]]]])) + [:li {:on-click on-zoom-reset} + "Zoom to 100%" [:span "Shift + 0"]] + [:li {:on-click on-zoom-fit} + "Zoom to fit all" [:span "Shift + 1"]] + [:li {:on-click on-zoom-selected} + "Zoom to selected" [:span "Shift + 2"]]]]])) ;; --- Header Users @@ -145,9 +145,9 @@ {:zoom zoom :on-increase #(st/emit! dw/increase-zoom) :on-decrease #(st/emit! dw/decrease-zoom) - :on-zoom-to-50 #(st/emit! dw/zoom-to-50) - :on-zoom-to-100 #(st/emit! dw/reset-zoom) - :on-zoom-to-200 #(st/emit! dw/zoom-to-200)}] + :on-zoom-reset #(st/emit! dw/reset-zoom) + :on-zoom-fit #(st/emit! dw/zoom-to-fit-all) + :on-zoom-selected #(st/emit! dw/zoom-to-selected-shape)}] [:a.btn-icon-dark.btn-small {;; :target "__blank" diff --git a/frontend/src/uxbox/main/ui/workspace/rules.cljs b/frontend/src/uxbox/main/ui/workspace/rules.cljs index 9831223f42..850e26f7a1 100644 --- a/frontend/src/uxbox/main/ui/workspace/rules.cljs +++ b/frontend/src/uxbox/main/ui/workspace/rules.cljs @@ -23,7 +23,7 @@ (< 0.07 zoom 0.2) 500 (< 0.2 zoom 0.5) 250 (< 0.5 zoom 1) 100 - (< 1 zoom 2) 50 + (<= 1 zoom 2) 50 (< 2 zoom 4) 25 (< 4 zoom 6) 10 (< 6 zoom 15) 5 diff --git a/frontend/src/uxbox/main/ui/workspace/selection.cljs b/frontend/src/uxbox/main/ui/workspace/selection.cljs index 495ee38bdc..e6012cbd81 100644 --- a/frontend/src/uxbox/main/ui/workspace/selection.cljs +++ b/frontend/src/uxbox/main/ui/workspace/selection.cljs @@ -113,13 +113,14 @@ [:g.controls (when (not (#{:move :rotate :resize} current-transform)) - [:rect.main {:transform transform - :x (- x 1) :y (- y 1) - :width (+ width 2) - :height (+ height 2) - :style {:stroke "#1FDEA7" - :stroke-width (/ 1 zoom) - :fill "transparent"}}]) + [:rect.main + {:transform transform + :x x :y y + :width width + :height height + :style {:stroke "#1FDEA7" + :stroke-width (/ 1 zoom) + :fill "transparent"}}]) (when (not (#{:move :rotate} current-transform)) (for [[position [cx cy]] resize-handlers] diff --git a/frontend/src/uxbox/main/ui/workspace/viewport.cljs b/frontend/src/uxbox/main/ui/workspace/viewport.cljs index 7c62bd7fa6..798826420f 100644 --- a/frontend/src/uxbox/main/ui/workspace/viewport.cljs +++ b/frontend/src/uxbox/main/ui/workspace/viewport.cljs @@ -41,7 +41,7 @@ ;; --- Coordinates Widget (mf/defc coordinates - [{:keys [zoom] :as props}] + [] (let [coords (some-> (hooks/use-rxsub ms/mouse-position) (gpt/round 0))] [:ul.coordinates @@ -268,21 +268,27 @@ on-mouse-wheel (mf/use-callback (fn [event] - (dom/prevent-default event) - (dom/stop-propagation event) + (let [node (mf/ref-val viewport-ref) + target (dom/get-target event)] + (cond + (kbd/ctrl? event) + (let [event (.getBrowserEvent event) + pos @ms/mouse-position] + (dom/prevent-default event) + (dom/stop-propagation event) + (if (pos? (.-deltaY event)) + (st/emit! (dw/decrease-zoom pos)) + (st/emit! (dw/increase-zoom pos)))) - (if (kbd/ctrl? event) - (let [event (.getBrowserEvent event) - pos @ms/mouse-position] - (if (pos? (.-deltaY event)) - (st/emit! (dw/decrease-zoom pos)) - (st/emit! (dw/increase-zoom pos)))) - (let [event (.getBrowserEvent event) - delta (.-deltaY ^js event) - delta (/ delta @refs/selected-zoom)] - (if (kbd/shift? event) - (st/emit! (dw/update-viewport-position {:x #(+ % delta)})) - (st/emit! (dw/update-viewport-position {:y #(+ % delta)}))))))) + (.contains ^js node target) + (let [event (.getBrowserEvent event) + delta (.-deltaY ^js event) + delta (/ delta @refs/selected-zoom)] + (dom/prevent-default event) + (dom/stop-propagation event) + (if (kbd/shift? event) + (st/emit! (dw/update-viewport-position {:x #(+ % delta)})) + (st/emit! (dw/update-viewport-position {:y #(+ % delta)})))))))) on-drag-over ;; Should prevent only events that we'll handle on-drop @@ -331,52 +337,50 @@ ] (mf/use-effect on-mount) - [:* - [:& coordinates {:zoom zoom}] - [:svg.viewport - {:preserveAspectRatio "xMidYMid meet" - :width (:width vport 0) - :height (:height vport 0) - :view-box (str/join " " [(:x vbox 0) - (:y vbox 0) - (:width vbox 0 ) - (:height vbox 0)]) - :ref viewport-ref - :class (when drawing-tool "drawing") - :on-context-menu on-context-menu - :on-click on-click - :on-double-click on-double-click - :on-mouse-down on-mouse-down - :on-mouse-up on-mouse-up - :on-drag-over on-drag-over - :on-drop on-drop} - [:g - [:& frames {:key (:id page)}] + [:svg.viewport + {:preserveAspectRatio "xMidYMid meet" + :width (:width vport 0) + :height (:height vport 0) + :view-box (str/join " " [(:x vbox 0) + (:y vbox 0) + (:width vbox 0 ) + (:height vbox 0)]) + :ref viewport-ref + :class (when drawing-tool "drawing") + :on-context-menu on-context-menu + :on-click on-click + :on-double-click on-double-click + :on-mouse-down on-mouse-down + :on-mouse-up on-mouse-up + :on-drag-over on-drag-over + :on-drop on-drop} + [:g + [:& frames {:key (:id page)}] - (when (seq selected) - [:& selection-handlers {:selected selected - :zoom zoom - :edition edition}]) + (when (seq selected) + [:& selection-handlers {:selected selected + :zoom zoom + :edition edition}]) - (when-let [drawing-shape (:drawing local)] - [:& draw-area {:shape drawing-shape - :zoom zoom - :modifiers (:modifiers local)}]) + (when-let [drawing-shape (:drawing local)] + [:& draw-area {:shape drawing-shape + :zoom zoom + :modifiers (:modifiers local)}]) - [:& snap-feedback] + [:& snap-feedback] - (when (contains? flags :grid) - [:& grid])] + (when (contains? flags :grid) + [:& grid])] - (when tooltip - [:& cursor-tooltip {:zoom zoom :tooltip tooltip}]) + (when tooltip + [:& cursor-tooltip {:zoom zoom :tooltip tooltip}]) - (when (contains? flags :ruler) - [:& ruler {:zoom zoom :ruler (:ruler local)}]) + (when (contains? flags :ruler) + [:& ruler {:zoom zoom :ruler (:ruler local)}]) - [:& presence/active-cursors {:page page}] - [:& selection-rect {:data (:selrect local)}] - (when (= options-mode :prototype) - [:& interactions {:selected selected}])]])) + [:& presence/active-cursors {:page page}] + [:& selection-rect {:data (:selrect local)}] + (when (= options-mode :prototype) + [:& interactions {:selected selected}])])) diff --git a/frontend/src/uxbox/util/geom/shapes.cljs b/frontend/src/uxbox/util/geom/shapes.cljs index a8236876a1..d130fe8cd6 100644 --- a/frontend/src/uxbox/util/geom/shapes.cljs +++ b/frontend/src/uxbox/util/geom/shapes.cljs @@ -43,8 +43,8 @@ for rect-like shapes." [shape {dx :x dy :y}] (assoc shape - :x (mth/round (+ (_chk (:x shape)) (_chk dx))) - :y (mth/round (+ (_chk (:y shape)) (_chk dy))))) + :x (+ (_chk (:x shape)) (_chk dx)) + :y (+ (_chk (:y shape)) (_chk dy)))) (defn- move-path "A specialized function for relative movement