diff --git a/frontend/resources/styles/main/layouts/projects-page.scss b/frontend/resources/styles/main/layouts/projects-page.scss index d2c7d3bcf..c1341fbf0 100644 --- a/frontend/resources/styles/main/layouts/projects-page.scss +++ b/frontend/resources/styles/main/layouts/projects-page.scss @@ -1,3 +1,5 @@ .projects-page { padding: 1rem; + height: 100%; + background-color: $color-white; } diff --git a/frontend/resources/styles/main/layouts/recent-files-page.scss b/frontend/resources/styles/main/layouts/recent-files-page.scss index c9cbb18dd..61c5b426a 100644 --- a/frontend/resources/styles/main/layouts/recent-files-page.scss +++ b/frontend/resources/styles/main/layouts/recent-files-page.scss @@ -1,20 +1,13 @@ .recent-files-page { overflow: scroll; height: 100%; + background-color: $color-white; } .recent-files-row { padding: 1rem; border-top: 1px solid $color-gray-10; - &:hover { - background-color: $color-white; - - .grid-item.project-th { - box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.2); - } - } - &.first { border-top: none; } diff --git a/frontend/resources/styles/main/partials/dashboard-grid.scss b/frontend/resources/styles/main/partials/dashboard-grid.scss index b4bf6d9fb..d20da0d9c 100644 --- a/frontend/resources/styles/main/partials/dashboard-grid.scss +++ b/frontend/resources/styles/main/partials/dashboard-grid.scss @@ -349,11 +349,18 @@ position: relative; width: 100%; + background-color: $color-canvas; + .img-th { height: auto; width: 100%; } + svg { + height: 100%; + width: 100%; + } + } // MULTISELECT OPTIONS BAR diff --git a/frontend/src/uxbox/main.cljs b/frontend/src/uxbox/main.cljs index 75f4f7413..0e18738c8 100644 --- a/frontend/src/uxbox/main.cljs +++ b/frontend/src/uxbox/main.cljs @@ -88,7 +88,3 @@ (when (= "main" (unchecked-get js/window app-sym)) (reinit))) -(defn ^:export toggle-debug - [] - (swap! st/*debug* not)) - diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 3326f591d..ee335cb94 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -1237,6 +1237,9 @@ (us/verify number? index) (ptk/reify ::relocate-shape + IUpdateGroup + (get-ids [_] [id]) + ptk/WatchEvent (watch [_ state stream] (let [page-id (:current-page-id state) diff --git a/frontend/src/uxbox/main/data/workspace/transforms.cljs b/frontend/src/uxbox/main/data/workspace/transforms.cljs index 79346b684..28a500522 100644 --- a/frontend/src/uxbox/main/data/workspace/transforms.cljs +++ b/frontend/src/uxbox/main/data/workspace/transforms.cljs @@ -70,7 +70,6 @@ (let [frame (get objects (:frame-id shape)) {:keys [width height rotation]} shape - center (gpt/center shape) shapev (-> (gpt/point width height)) ;; Vector modifiers depending on the handler @@ -140,7 +139,7 @@ (watch [_ state stream] (let [stoper (rx/filter ms/mouse-up? stream) group (gsh/selection-rect shapes) - group-center (gpt/center group) + group-center (gsh/center group) initial-angle (gpt/angle (apply-zoom @ms/mouse-position) group-center) calculate-angle (fn [pos ctrl?] (let [angle (- (gpt/angle pos group-center) initial-angle) @@ -161,7 +160,7 @@ (rx/with-latest vector ms/mouse-position-ctrl) (rx/map (fn [[pos ctrl?]] (let [delta-angle (calculate-angle pos ctrl?)] - (set-rotation delta-angle shapes)))) + (set-rotation delta-angle shapes group-center)))) (rx/take-until stoper)) (rx/of (apply-modifiers (map :id shapes)))))))) @@ -275,7 +274,7 @@ ;; Set-rotation is custom because applies different modifiers to each shape adjusting their position (defn set-rotation - [delta-rotation shapes] + [delta-rotation shapes center] (ptk/reify ::set-rotation IUpdateGroup (get-ids [_] (map :id shapes)) @@ -293,8 +292,7 @@ (rotate-around-center [state angle center shapes] (reduce #(rotate-shape %1 angle %2 center) state shapes))] - (let [center (-> shapes gsh/selection-rect gpt/center) - objects (get-in state [:workspace-data page-id :objects]) + (let [objects (get-in state [:workspace-data page-id :objects]) id->obj #(get objects %) get-children (fn [shape] (map id->obj (helpers/get-children (:id shape) objects))) shapes (concat shapes (mapcat get-children shapes))] diff --git a/frontend/src/uxbox/main/exports.cljs b/frontend/src/uxbox/main/exports.cljs index 336d37bfa..c2fae66e7 100644 --- a/frontend/src/uxbox/main/exports.cljs +++ b/frontend/src/uxbox/main/exports.cljs @@ -22,13 +22,14 @@ [uxbox.main.ui.shapes.text :as text] [uxbox.main.ui.shapes.group :as group])) +(def ^:private background-color "#E8E9EA") ;; $color-canvas (mf/defc background [] [:rect {:x 0 :y 0 :width "100%" :height "100%" - :fill "#AFB2BF"}]) + :fill background-color}]) (defn- calculate-dimensions [data] @@ -75,7 +76,7 @@ (let [group-wrapper (mf/use-memo (mf/deps objects) #(group-wrapper objects))] (when (and shape (not (:hidden shape))) (let [shape (geom/transform-shape frame shape) - opts #js {:shape shape :frame frame}] + opts #js {:shape shape}] (case (:type shape) :curve [:> path/path-shape opts] :text [:> text/text-shape opts] @@ -84,7 +85,7 @@ :path [:> path/path-shape opts] :image [:> image/image-shape opts] :circle [:> circle/circle-shape opts] - :group [:> group-wrapper opts] + :group [:> group-wrapper {:shape shape :frame frame}] nil)))))) (mf/defc page-svg diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index 9c05d3c0a..4a34220ae 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -57,6 +57,14 @@ (get-in % [:workspace-data page-id])) (l/derived st/state))) +(defn object-by-id + [id] + (letfn [(selector [state] + (let [page-id (get-in state [:workspace-page :id]) + objects (get-in state [:workspace-data page-id :objects])] + (get objects id)))] + (l/derived selector st/state =))) + (defn objects-by-id [ids] (letfn [(selector [state] diff --git a/frontend/src/uxbox/main/store.cljs b/frontend/src/uxbox/main/store.cljs index bc01282de..5512e9fc3 100644 --- a/frontend/src/uxbox/main/store.cljs +++ b/frontend/src/uxbox/main/store.cljs @@ -10,7 +10,8 @@ [okulary.core :as l] [potok.core :as ptk] [uxbox.common.uuid :as uuid] - [uxbox.util.storage :refer [storage]])) + [uxbox.util.storage :refer [storage]] + [uxbox.util.debug :refer [debug? logjs]])) (enable-console-print!) @@ -34,13 +35,11 @@ :else (str "unk: " (pr-str event)))) -(defonce ^:dynamic *debug* (atom false)) - (when *assert* (defonce debug-subscription (as-> stream $ - (rx/filter ptk/event? $) - (rx/filter (fn [s] (deref *debug*)) $) + #_(rx/filter ptk/event? $) + (rx/filter (fn [s] (debug? :events)) $) (rx/subscribe $ (fn [event] (println "[stream]: " (repr-event event))))))) (defn emit! @@ -62,3 +61,10 @@ ([props] (emit! #(merge % initial-state props)) (rx/to-atom store state))) + +(defn ^:export dump-state [] + (logjs "state" @state)) + +(defn ^:export dump-objects [] + (let [page-id (get @state :current-page-id)] + (logjs "state" (get-in @state [:workspace-data page-id :objects])))) diff --git a/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs b/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs index c08d70ec7..807a04a87 100644 --- a/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs @@ -51,11 +51,14 @@ (mf/defc recent-project [{:keys [project files first? locale] :as props}] - (let [project-id (:id project)] + (let [project-id (:id project) + team-id (:team-id project)] [:div.recent-files-row {:class-name (when first? "first")} [:div.recent-files-row-title - [:h2.recent-files-row-title-name (:name project)] + [:h2.recent-files-row-title-name {:on-click #(st/emit! (rt/nav :dashboard-project {:team-id team-id + :project-id project-id})) + :style {:cursor "pointer"}} (:name project)] [:span.recent-files-row-title-info (str (:file-count project) " files")] (let [time (-> (:modified-at project) (dt/timeago {:locale locale}))] diff --git a/frontend/src/uxbox/main/ui/shapes/bounding_box.cljs b/frontend/src/uxbox/main/ui/shapes/bounding_box.cljs index 42b3c4628..35ec32486 100644 --- a/frontend/src/uxbox/main/ui/shapes/bounding_box.cljs +++ b/frontend/src/uxbox/main/ui/shapes/bounding_box.cljs @@ -23,11 +23,10 @@ (when (debug? :bounding-boxes) (let [shape (unchecked-get props "shape") frame (unchecked-get props "frame") - selrect (geom/transform-selrect frame shape) - shape-path (-> shape - (geom/transform-apply-modifiers) - (geom/translate-to-frame frame)) - shape-center (geom/center shape-path)] + selrect (-> shape + (geom/selection-rect-shape) + (geom/translate-to-frame frame)) + shape-center (geom/center selrect)] [:g [:text {:x (:x selrect) :y (- (:y selrect) 5) @@ -37,10 +36,12 @@ :stroke-width 0.1} (str/format "%s - (%s, %s)" (str/slice (str (:id shape)) 0 8) (fix (:x shape)) (fix (:y shape)))] - [:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}] - [:rect {:x (:x selrect) :y (:y selrect) :width (:width selrect) :height (:height selrect) - :style {:stroke "red" :fill "transparent" :stroke-width "1px" :stroke-opacity 0.5}}]]))) + :style {:stroke "red" + :fill "transparent" + :stroke-width "1px" + :stroke-opacity 0.5 + :pointer-events "none"}}]]))) diff --git a/frontend/src/uxbox/main/ui/shapes/circle.cljs b/frontend/src/uxbox/main/ui/shapes/circle.cljs index a93bdaf39..f524bc45b 100644 --- a/frontend/src/uxbox/main/ui/shapes/circle.cljs +++ b/frontend/src/uxbox/main/ui/shapes/circle.cljs @@ -22,7 +22,7 @@ (declare circle-shape) (mf/defc circle-wrapper - [{:keys [shape frame] :as props}] + [{:keys [shape] :as props}] (let [selected (mf/deref refs/selected-shapes) selected? (contains? selected (:id shape)) on-mouse-down #(common/on-mouse-down % shape) @@ -30,8 +30,7 @@ [:g.shape {:class (when selected? "selected") :on-mouse-down on-mouse-down :on-context-menu on-context-menu} - [:& circle-shape {:shape (geom/transform-shape frame shape)}] - [:& bounding-box {:shape shape :frame frame}]])) + [:& circle-shape {:shape shape}]])) ;; --- Circle Shape diff --git a/frontend/src/uxbox/main/ui/shapes/frame.cljs b/frontend/src/uxbox/main/ui/shapes/frame.cljs index 3e034a787..76d372537 100644 --- a/frontend/src/uxbox/main/ui/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/shapes/frame.cljs @@ -75,7 +75,7 @@ inv-zoom (/ 1 zoom) childs (mapv #(get objects %) (:shapes shape)) - ds-modifier (:displacement-modifier shape) + ds-modifier (get-in shape [:modifiers :displacement]) label-pos (cond-> (gpt/point x (- y 10)) (gmt/matrix? ds-modifier) (gpt/transform ds-modifier)) diff --git a/frontend/src/uxbox/main/ui/shapes/group.cljs b/frontend/src/uxbox/main/ui/shapes/group.cljs index 3f6e0f421..28feee6e5 100644 --- a/frontend/src/uxbox/main/ui/shapes/group.cljs +++ b/frontend/src/uxbox/main/ui/shapes/group.cljs @@ -73,11 +73,9 @@ [:& group-shape {:frame frame - :shape (geom/transform-shape frame shape) + :shape shape :children children - :is-child-selected? is-child-selected?}] - (when (not is-child-selected?) - [:& bounding-box {:shape shape :frame frame}])])))) + :is-child-selected? is-child-selected?}]])))) (defn group-shape [shape-wrapper] diff --git a/frontend/src/uxbox/main/ui/shapes/image.cljs b/frontend/src/uxbox/main/ui/shapes/image.cljs index b475ba801..bc35be20a 100644 --- a/frontend/src/uxbox/main/ui/shapes/image.cljs +++ b/frontend/src/uxbox/main/ui/shapes/image.cljs @@ -15,15 +15,14 @@ [uxbox.main.ui.shapes.attrs :as attrs] [uxbox.main.ui.shapes.common :as common] [uxbox.util.interop :as itr] - [uxbox.util.geom.matrix :as gmt] - [uxbox.main.ui.shapes.bounding-box :refer [bounding-box]])) + [uxbox.util.geom.matrix :as gmt])) ;; --- Image Wrapper (declare image-shape) (mf/defc image-wrapper - [{:keys [shape frame] :as props}] + [{:keys [shape] :as props}] (let [selected (mf/deref refs/selected-shapes) selected? (contains? selected (:id shape)) on-mouse-down (mf/use-callback @@ -37,8 +36,7 @@ [:g.shape {:class (when selected? "selected") :on-mouse-down on-mouse-down :on-context-menu on-context-menu} - [:& image-shape {:shape (geom/transform-shape frame shape)}] - [:& bounding-box {:shape shape :frame frame}]])) + [:& image-shape {:shape shape}]])) ;; --- Image Shape diff --git a/frontend/src/uxbox/main/ui/shapes/path.cljs b/frontend/src/uxbox/main/ui/shapes/path.cljs index 20ffba953..be2ce15e8 100644 --- a/frontend/src/uxbox/main/ui/shapes/path.cljs +++ b/frontend/src/uxbox/main/ui/shapes/path.cljs @@ -24,7 +24,7 @@ (declare path-shape) (mf/defc path-wrapper - [{:keys [shape frame] :as props}] + [{:keys [shape] :as props}] (let [selected (mf/deref refs/selected-shapes) selected? (contains? selected (:id shape)) on-mouse-down (mf/use-callback @@ -42,8 +42,7 @@ [:g.shape {:on-double-click on-double-click :on-mouse-down on-mouse-down :on-context-menu on-context-menu} - [:& path-shape {:shape (geom/transform-shape frame shape) :background? true}] - [:& bounding-box {:shape shape :frame frame}]])) + [:& path-shape {:shape shape :background? true}]])) ;; --- Path Shape diff --git a/frontend/src/uxbox/main/ui/shapes/rect.cljs b/frontend/src/uxbox/main/ui/shapes/rect.cljs index 9a6db3f01..299dbc1ae 100644 --- a/frontend/src/uxbox/main/ui/shapes/rect.cljs +++ b/frontend/src/uxbox/main/ui/shapes/rect.cljs @@ -15,7 +15,6 @@ [uxbox.main.ui.shapes.attrs :as attrs] [uxbox.main.ui.shapes.common :as common] [uxbox.util.interop :as itr] - [uxbox.main.ui.shapes.bounding-box :refer [bounding-box]] [uxbox.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]])) ;; --- Rect Wrapper @@ -26,7 +25,6 @@ {::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape") - frame (unchecked-get props "frame") on-mouse-down (mf/use-callback (mf/deps shape) #(common/on-mouse-down % shape)) @@ -35,8 +33,7 @@ #(common/on-context-menu % shape))] [:g.shape {:on-mouse-down on-mouse-down :on-context-menu on-context-menu} - [:& rect-shape {:shape (geom/transform-shape frame shape) }] - [:& bounding-box {:shape shape :frame frame}]])) + [:& rect-shape {:shape shape}]])) ;; --- Rect Shape diff --git a/frontend/src/uxbox/main/ui/shapes/shape.cljs b/frontend/src/uxbox/main/ui/shapes/shape.cljs index 412b610ea..31f50ad06 100644 --- a/frontend/src/uxbox/main/ui/shapes/shape.cljs +++ b/frontend/src/uxbox/main/ui/shapes/shape.cljs @@ -19,6 +19,8 @@ [uxbox.main.ui.shapes.text :as text] [uxbox.main.ui.shapes.group :as group] [uxbox.main.ui.shapes.frame :as frame] + [uxbox.main.ui.shapes.bounding-box :refer [bounding-box]] + [uxbox.util.geom.shapes :as gsh] [uxbox.main.refs :as refs])) (defn- shape-wrapper-memo-equals? @@ -42,21 +44,24 @@ [props] (let [shape (unchecked-get props "shape") frame (unchecked-get props "frame") - opts #js {:shape shape :frame frame}] + opts #js {:shape (->> shape (gsh/transform-shape frame)) + :frame frame}] (when (and shape (not (:hidden shape))) - (case (:type shape) - :group [:> group-wrapper opts] - :curve [:> path/path-wrapper opts] - :text [:> text/text-wrapper opts] - :icon [:> icon/icon-wrapper opts] - :rect [:> rect/rect-wrapper opts] - :path [:> path/path-wrapper opts] - :image [:> image/image-wrapper opts] - :circle [:> circle/circle-wrapper opts] + [:* + (case (:type shape) + :group [:> group-wrapper opts] + :curve [:> path/path-wrapper opts] + :text [:> text/text-wrapper opts] + :icon [:> icon/icon-wrapper opts] + :rect [:> rect/rect-wrapper opts] + :path [:> path/path-wrapper opts] + :image [:> image/image-wrapper opts] + :circle [:> circle/circle-wrapper opts] - ;; Only used when drawing a new frame. - :frame [:> frame-wrapper opts] - nil)))) + ;; Only used when drawing a new frame. + :frame [:> frame-wrapper {:shape shape}] + nil) + [:& bounding-box {:shape shape :frame frame}]]))) (def group-wrapper (group/group-wrapper shape-wrapper)) (def frame-wrapper (frame/frame-wrapper shape-wrapper)) diff --git a/frontend/src/uxbox/main/ui/shapes/text.cljs b/frontend/src/uxbox/main/ui/shapes/text.cljs index ea5ce8596..877d5c481 100644 --- a/frontend/src/uxbox/main/ui/shapes/text.cljs +++ b/frontend/src/uxbox/main/ui/shapes/text.cljs @@ -46,7 +46,7 @@ (declare text-shape) (mf/defc text-wrapper - [{:keys [shape frame] :as props}] + [{:keys [shape] :as props}] (let [{:keys [id x1 y1 content group]} shape selected (mf/deref refs/selected-shapes) edition (mf/deref refs/selected-edition) @@ -67,8 +67,8 @@ :on-mouse-down on-mouse-down :on-context-menu on-context-menu} (if edition? - [:& text-shape-edit {:shape (geom/transform-shape frame shape)}] - [:& text-shape {:shape (geom/transform-shape frame shape) + [:& text-shape-edit {:shape shape}] + [:& text-shape {:shape shape :selected? selected?}])])) ;; --- Text Rendering @@ -284,7 +284,8 @@ (mf/use-effect on-mount) - [:foreignObject {:x x :y y :width width :height height :ref self-ref} + [:foreignObject {:transform (geom/transform-matrix shape) + :x x :y y :width width :height height :ref self-ref} [:> rslate/Slate {:editor editor :value @state :on-change on-change} @@ -305,13 +306,7 @@ (mf/defc text-shape [{:keys [shape selected?] :as props}] - (let [{:keys [id x y width height rotation content]} shape - transform (when (and rotation (pos? rotation)) - (str/format "rotate(%s %s %s)" - rotation - (+ x (/ width 2)) - (+ y (/ height 2)))) - + (let [{:keys [id x y width height content]} shape content (parse-content content) editor (mf/use-memo #(rslate/withReact (slate/createEditor))) @@ -334,7 +329,7 @@ [:foreignObject {:x x :y y - :transform transform + :transform (geom/transform-matrix shape) :id (str id) :width width :height height} diff --git a/frontend/src/uxbox/main/ui/viewer.cljs b/frontend/src/uxbox/main/ui/viewer.cljs index 50bb46b1d..519453992 100644 --- a/frontend/src/uxbox/main/ui/viewer.cljs +++ b/frontend/src/uxbox/main/ui/viewer.cljs @@ -35,7 +35,6 @@ frames (:frames data []) objects (:objects data) frame (get frames index)] - [:section.viewer-preview (cond (empty? frames) diff --git a/frontend/src/uxbox/main/ui/viewer/thumbnails.cljs b/frontend/src/uxbox/main/ui/viewer/thumbnails.cljs index 167da81aa..b18e7b0e6 100644 --- a/frontend/src/uxbox/main/ui/viewer/thumbnails.cljs +++ b/frontend/src/uxbox/main/ui/viewer/thumbnails.cljs @@ -17,6 +17,7 @@ [uxbox.common.data :as d] [uxbox.main.store :as st] [uxbox.main.data.viewer :as dv] + [uxbox.main.data.helpers :as helpers] [uxbox.main.ui.components.dropdown :refer [dropdown']] [uxbox.main.ui.shapes.frame :as frame] [uxbox.main.exports :as exports] @@ -83,7 +84,15 @@ (let [modifier (-> (gpt/point (:x frame) (:y frame)) (gpt/negate) (gmt/translate-matrix)) - frame (assoc frame :displacement-modifier modifier) + + frame-id (:id frame) + modifier-ids (concat [frame-id] (helpers/get-children frame-id objects)) + + update-fn (fn [state shape-id] + (-> state + (assoc-in [shape-id :modifiers :displacement] modifier))) + objects (reduce update-fn objects modifier-ids) + frame (assoc-in frame [:modifiers :displacement] modifier ) width (* (:width frame) zoom) height (* (:height frame) zoom) @@ -96,9 +105,9 @@ :height height :version "1.1" :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns "http://www.w3.org/2000/svg"} + :xmlns "http://www.w3.org/2000/svg"} [:& frame-wrapper {:shape frame - :view-box vbox}]])) + :view-box vbox}]])) (mf/defc thumbnails-summary [{:keys [on-toggle-expand on-close total] :as props}] diff --git a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs index 492c25d63..7c43e52f4 100644 --- a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs +++ b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs @@ -35,15 +35,18 @@ (declare handle-finish-drawing) (declare conditional-align) +(def ^:private default-color "#b1b2b5") ;; $color-gray-20 + (def ^:private minimal-shapes [{:type :rect :name "Rect" - :stroke-color "#000000" + :fill-color default-color :stroke-alignment :center} {:type :image} {:type :icon} {:type :circle - :name "Circle"} + :name "Circle" + :fill-color default-color} {:type :path :name "Path" :stroke-style :solid @@ -305,17 +308,16 @@ (mf/defc generic-draw-area [{:keys [shape zoom]}] - (let [{:keys [x y width height]} (geom/transform-selrect nil shape)] + (let [{:keys [x y width height]} (geom/selection-rect-shape shape)] (when (and x y) [:g [:& shapes/shape-wrapper {:shape shape}] [:rect.main {:x x :y y :width width :height height - :stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom)) - :style {:stroke "#333" + :style {:stroke "#1FDEA7" :fill "transparent" - :stroke-opacity "1"}}]]))) + :stroke-width "1"}}]]))) (mf/defc path-draw-area [{:keys [shape] :as props}] diff --git a/frontend/src/uxbox/main/ui/workspace/selection.cljs b/frontend/src/uxbox/main/ui/workspace/selection.cljs index 1e0452cad..10b9492a1 100644 --- a/frontend/src/uxbox/main/ui/workspace/selection.cljs +++ b/frontend/src/uxbox/main/ui/workspace/selection.cljs @@ -72,6 +72,7 @@ [{:keys [shape zoom on-resize on-rotate] :as props}] (let [{:keys [x y width height rotation] :as shape} (geom/shape->rect-shape shape) radius (if (> (max width height) handler-size-threshold) 4.0 4.0) + transform (geom/transform-matrix shape) resize-handlers {:top [(+ x (/ width 2 )) y] @@ -85,14 +86,12 @@ [:g.controls [:rect.main {:transform transform - :x x :y y - :width width - :height height - ;;:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom)) - :vector-effect "non-scaling-stroke" + :x (- x 1) :y (- y 1) + :width (+ width 2) + :height (+ height 2) :style {:stroke "#1FDEA7" - :fill "transparent" - :stroke-opacity "1"}}] + :stroke-width "1" + :fill "transparent"}}] (for [[position [cx cy]] resize-handlers] (let [tp (gpt/transform (gpt/point cx cy) transform)] @@ -156,12 +155,12 @@ (mf/defc text-edition-selection-handlers [{:keys [shape zoom] :as props}] - (let [{:keys [x y width height] :as shape} shape] + (let [{:keys [x y width height]} shape] [:g.controls [:rect.main {:x x :y y + :transform (geom/transform-matrix shape) :width width :height height - ;; :stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom)) :style {:stroke "#1FDEA7" :stroke-width "0.5" :stroke-opacity "1" @@ -170,16 +169,20 @@ (mf/defc multiple-selection-handlers [{:keys [shapes selected zoom objects] :as props}] (let [shape (geom/selection-rect shapes) + shape-center (geom/center shape) on-resize #(do (dom/stop-propagation %2) (st/emit! (dw/start-resize %1 selected shape objects))) on-rotate #(do (dom/stop-propagation %) (st/emit! (dw/start-rotate shapes)))] - [:& controls {:shape shape - :zoom zoom - :on-resize on-resize - :on-rotate on-rotate}])) + [:* + [:& controls {:shape shape + :zoom zoom + :on-resize on-resize + :on-rotate on-rotate}] + (when (debug? :selection-center) + [:circle {:cx (:x shape-center) :cy (:y shape-center) :r 5 :fill "yellow"}])])) (mf/defc single-selection-handlers [{:keys [shape zoom objects] :as props}] 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 6d2d46028..a6751b7f4 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/measures.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/measures.cljs @@ -15,25 +15,43 @@ [uxbox.main.refs :as refs] [uxbox.common.data :as d] [uxbox.util.dom :as dom] + [uxbox.util.geom.shapes :as gsh] + [uxbox.util.geom.point :as gpt] [uxbox.main.data.workspace :as udw] [uxbox.util.math :as math] [uxbox.util.i18n :refer [t] :as i18n])) + +;; -- User/drawing coords +(defn user-coords-vector [shape] + (let [{sel-x :x sel-y :y :as selrect} + (-> shape + gsh/shape->path + (gsh/center-transform (:transform shape)) + gsh/shape->rect-shape) + {rec-x :x rec-y :y} (-> shape gsh/shape->rect-shape) + + dx (- rec-x sel-x) + dy (- rec-y sel-y)] + (gpt/point dx dy))) + +(defn user->draw [{:keys [x y width height] :as shape}] + (let [dv (user-coords-vector shape)] + (-> shape (gsh/move dv)))) + +(defn draw->user [{:keys [x y width height] :as shape}] + (let [dv (user-coords-vector shape)] + (-> shape (gsh/move (gpt/negate dv))))) + (mf/defc measures-menu [{:keys [shape options] :as props}] (let [options (or options #{:size :position :rotation :radius}) locale (i18n/use-locale) + frame (deref (refs/object-by-id (:frame-id shape))) - data (deref refs/workspace-data) - parent (get-in data [:objects (:frame-id shape)]) - - x (cond - (:x shape) :x - (:cx shape) :cx) - - y (cond - (:y shape) :y - (:cy shape) :cy) + shape (->> shape + (gsh/transform-shape frame) + (draw->user)) on-size-change (fn [event attr] @@ -58,9 +76,12 @@ (fn [event attr] (let [value (-> (dom/get-target event) (dom/get-value) - (d/parse-integer 0) - (+ (attr parent)))] ; Convert back to absolute position before update - (st/emit! (udw/update-position (:id shape) {attr value})))) + (d/parse-integer 0)) + new-shape (-> shape + (assoc attr value) + (gsh/translate-from-frame frame) + (user->draw))] + (st/emit! (udw/update-position (:id shape) (select-keys new-shape [attr]))))) on-rotation-change (fn [event] @@ -89,31 +110,31 @@ ;; WIDTH & HEIGHT (when (options :size) - [:div.row-flex - [:span.element-set-subtitle (t locale "workspace.options.size")] - [:div.lock-size {:class (when (:proportion-lock shape) "selected") - :on-click on-proportion-lock-change} - (if (:proportion-lock shape) - i/lock - i/unlock)] - [:div.input-element.pixels - [:input.input-text {:type "number" - :min "0" - :no-validate true - :on-change on-width-change - :value (str (-> (:width shape) - (d/coalesce 0) - (math/round)))}]] + [:div.row-flex + [:span.element-set-subtitle (t locale "workspace.options.size")] + [:div.lock-size {:class (when (:proportion-lock shape) "selected") + :on-click on-proportion-lock-change} + (if (:proportion-lock shape) + i/lock + i/unlock)] + [:div.input-element.pixels + [:input.input-text {:type "number" + :min "0" + :no-validate true + :on-change on-width-change + :value (str (-> (:width shape) + (d/coalesce 0) + (math/round)))}]] - [:div.input-element.pixels - [:input.input-text {:type "number" - :min "0" - :no-validate true - :on-change on-height-change - :value (str (-> (:height shape) - (d/coalesce 0) - (math/round)))}]]]) + [:div.input-element.pixels + [:input.input-text {:type "number" + :min "0" + :no-validate true + :on-change on-height-change + :value (str (-> (:height shape) + (d/coalesce 0) + (math/round)))}]]]) ;; Circle RX RY (when (options :circle-size) @@ -142,57 +163,61 @@ ;; POSITION (when (options :position) [:div.row-flex - [:span.element-set-subtitle (t locale "workspace.options.position")] - [:div.input-element.pixels - [:input.input-text {:placeholder "x" - :type "number" - :no-validate true - :on-change on-pos-x-change - :value (str (-> (- (x shape) (:x parent)) ; Show to user position relative to frame - (d/coalesce 0) - (math/round)))}]] - [:div.input-element.pixels - [:input.input-text {:placeholder "y" - :type "number" - :no-validate true - :on-change on-pos-y-change - :value (str (-> (- (y shape) (:y parent)) - (d/coalesce 0) - (math/round)))}]]]) + [:span.element-set-subtitle (t locale "workspace.options.position")] + [:div.input-element.pixels + [:input.input-text {:placeholder "x" + :type "number" + :no-validate true + :on-change on-pos-x-change + :value (:x shape) + ;;:value (str (-> (- (x shape) (:x parent)) ; Show to user position relative to frame + ;; (d/coalesce 0) + ;; (math/round))) + }]] + [:div.input-element.pixels + [:input.input-text {:placeholder "y" + :type "number" + :no-validate true + :on-change on-pos-y-change + :value (:y shape) + ;;:value (str (-> (- (y shape) (:y parent)) + ;; (d/coalesce 0) + ;; (math/round))) + }]]]) (when (options :rotation) - [:div.row-flex - [:span.element-set-subtitle (t locale "workspace.options.rotation")] - [:div.input-element.degrees - [:input.input-text - {:placeholder "" - :type "number" - :no-validate true + [:div.row-flex + [:span.element-set-subtitle (t locale "workspace.options.rotation")] + [:div.input-element.degrees + [:input.input-text + {:placeholder "" + :type "number" + :no-validate true + :min "0" + :max "360" + :on-change on-rotation-change + :value (str (-> (:rotation shape) + (d/coalesce 0) + (math/round)))}]] + [:input.slidebar + {:type "range" :min "0" :max "360" + :step "1" + :no-validate true :on-change on-rotation-change :value (str (-> (:rotation shape) - (d/coalesce 0) - (math/round)))}]] - [:input.slidebar - {:type "range" - :min "0" - :max "360" - :step "1" - :no-validate true - :on-change on-rotation-change - :value (str (-> (:rotation shape) - (d/coalesce 0)))}]]) + (d/coalesce 0)))}]]) (when (options :radius) - [:div.row-flex - [:span.element-set-subtitle (t locale "workspace.options.radius")] - [:div.input-element.pixels - [:input.input-text - {:placeholder "rx" - :type "number" - :on-change on-radius-change - :value (str (-> (:rx shape) - (d/coalesce 0) - (math/round)))}]] - [:div.input-element]])]])) + [:div.row-flex + [:span.element-set-subtitle (t locale "workspace.options.radius")] + [:div.input-element.pixels + [:input.input-text + {:placeholder "rx" + :type "number" + :on-change on-radius-change + :value (str (-> (:rx shape) + (d/coalesce 0) + (math/round)))}]] + [:div.input-element]])]])) diff --git a/frontend/src/uxbox/util/debug.cljs b/frontend/src/uxbox/util/debug.cljs index 2c3407788..1d6e6a0a4 100644 --- a/frontend/src/uxbox/util/debug.cljs +++ b/frontend/src/uxbox/util/debug.cljs @@ -1,9 +1,7 @@ (ns uxbox.util.debug - "Debugging utils" - (:require - [uxbox.main.store :as store])) + "Debugging utils") -(def debug-options #{:bounding-boxes :group :events :rotation-handler #_:simple-selection }) +(def debug-options #{:bounding-boxes :group :events :rotation-handler :selection-center #_:simple-selection }) (defonce ^:dynamic *debug* (atom #{})) @@ -13,6 +11,13 @@ (defn -debug! [option] (swap! *debug* disj option)) (defn debug? [option] (@*debug* option)) + +(defn ^:export toggle-debug [name] (let [option (keyword name)] + (if (debug? option) + (-debug! option) + (debug! option)))) +(defn ^:export debug-all [name] (debug-all!)) + (defn tap "Transducer function that can execute a side-effect `effect-fn` per input" [effect-fn] @@ -31,9 +36,4 @@ (js/console.log str (clj->js val)) val)) -(defn dump-state [] - (logjs "state" @store/state)) -(defn dump-objects [] - (let [page-id (get @store/state :current-page-id)] - (logjs "state" (get-in @store/state [:workspace-data page-id :objects])))) diff --git a/frontend/src/uxbox/util/geom/point.cljs b/frontend/src/uxbox/util/geom/point.cljs index 4e9bc7e88..0e2d9b7f3 100644 --- a/frontend/src/uxbox/util/geom/point.cljs +++ b/frontend/src/uxbox/util/geom/point.cljs @@ -40,11 +40,6 @@ (throw (ex-info "Invalid arguments" {:v v})))) ([x y] (Point. x y))) -(defn center - [{:keys [x y width height]}] - (point (+ x (/ width 2)) - (+ y (/ height 2)))) - (defn add "Returns the addition of the supplied value to both coordinates of the point as a new point." diff --git a/frontend/src/uxbox/util/geom/shapes.cljs b/frontend/src/uxbox/util/geom/shapes.cljs index 485177d36..6f08ba976 100644 --- a/frontend/src/uxbox/util/geom/shapes.cljs +++ b/frontend/src/uxbox/util/geom/shapes.cljs @@ -376,6 +376,7 @@ maxx (transduce (map :x) max segments) maxy (transduce (map :y) max segments)] (assoc shape + :type :rect :x1 minx :y1 miny :x2 maxx @@ -495,7 +496,7 @@ (defn translate-from-frame [shape {:keys [x y] :as frame}] - (move shape (gpt/point (+ x) (+ y)))) + (move shape (gpt/point x y))) ;; --- Alignment @@ -736,13 +737,6 @@ (gpt/divide (gpt/point (:width shape-path-temp-rec) (:height shape-path-temp-rec)) (gpt/point (:width shape-path-temp-dim) (:height shape-path-temp-dim))))) -(defn transform-selrect - [frame shape] - (-> shape - (transform-apply-modifiers) - (translate-to-frame frame) - (shape->rect-shape))) - (defn transform-rect-shape [shape] (let [;; Apply modifiers to the rect as a path so we have the end shape expected @@ -788,6 +782,8 @@ new-shape (-> shape (merge rec) + (update :x #(mth/precision % 2)) + (update :y #(mth/precision % 2)) (update :transform #(gmt/multiply (or % (gmt/matrix)) stretch-matrix)) (update :transform-inverse #(gmt/multiply stretch-matrix-inverse (or % (gmt/matrix)))))]