From 5925d2520ffc1f8fdecbe3d82e8b225ba1a6f033 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 8 May 2023 17:25:22 +0200 Subject: [PATCH] :sparkles: Changes to the editor UI --- .../geom/shapes/grid_layout/layout_data.cljc | 3 + .../resources/styles/common/framework.scss | 4 + .../styles/main/partials/workspace.scss | 24 +- .../app/main/data/workspace/drawing/box.cljs | 11 +- .../main/data/workspace/drawing/curve.cljs | 11 +- .../data/workspace/grid_layout/editor.cljs | 20 + .../app/main/data/workspace/shape_layout.cljs | 2 +- .../src/app/main/data/workspace/shapes.cljs | 3 + .../shapes/frame/dynamic_modifiers.cljs | 2 +- .../main/ui/workspace/viewport/actions.cljs | 5 +- .../viewport/grid_layout_editor.cljs | 355 +++++++++++------- .../viewport/grid_layout_editor.scss | 2 +- .../app/main/ui/workspace/viewport/utils.cljs | 15 +- .../main/ui/workspace/viewport/widgets.cljs | 55 ++- frontend/translations/en.po | 3 + frontend/translations/es.po | 3 + 16 files changed, 350 insertions(+), 168 deletions(-) diff --git a/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc index b89b372b4..53663bbef 100644 --- a/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc +++ b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc @@ -349,6 +349,9 @@ column-tracks (add-auto-size column-tracks column-add-auto) row-tracks (add-auto-size row-tracks row-add-auto) + column-total-size (tracks-total-size column-tracks) + row-total-size (tracks-total-size row-tracks) + start-p (cond-> bound-corner (= :end (:layout-align-content parent)) diff --git a/frontend/resources/styles/common/framework.scss b/frontend/resources/styles/common/framework.scss index 257ea2e31..37e877f26 100644 --- a/frontend/resources/styles/common/framework.scss +++ b/frontend/resources/styles/common/framework.scss @@ -123,6 +123,10 @@ padding: $size-1; svg { fill: $color-gray-20; + + &.icon-close { + transform: rotate(45deg); + } } &:hover { background: transparent; diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index b1a04a6d8..595d2d831 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -367,7 +367,8 @@ $height-palette-max: 80px; z-index: 12; pointer-events: none; - .path-actions { + .path-actions, + .grid-actions { pointer-events: initial; display: flex; flex-direction: row; @@ -378,6 +379,27 @@ $height-palette-max: 80px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } + .grid-actions { + padding-left: 1rem; + gap: 12px; + color: var(--color-gray-60); + align-items: center; + font-size: 12px; + + .btn-primary, + .btn-secondary { + height: 24px; + } + + .grid-edit-title { + margin-right: 2rem; + } + + .grid-edit-board-name { + font-weight: 600; + } + } + .viewport-actions-group { display: flex; flex-direction: row; diff --git a/frontend/src/app/main/data/workspace/drawing/box.cljs b/frontend/src/app/main/data/workspace/drawing/box.cljs index 9517047d7..2f5209e5f 100644 --- a/frontend/src/app/main/data/workspace/drawing/box.cljs +++ b/frontend/src/app/main/data/workspace/drawing/box.cljs @@ -8,7 +8,8 @@ (:require [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] - [app.common.geom.shapes.flex-layout :as gsl] + [app.common.geom.shapes.flex-layout :as gslf] + [app.common.geom.shapes.grid-layout :as gslg] [app.common.math :as mth] [app.common.pages.helpers :as cph] [app.common.types.modifiers :as ctm] @@ -84,7 +85,10 @@ fid (ctst/top-nested-frame objects initial) flex-layout? (ctl/flex-layout? objects fid) - drop-index (when flex-layout? (gsl/get-drop-index fid objects initial)) + grid-layout? (ctl/grid-layout? objects fid) + + drop-index (when flex-layout? (gslf/get-drop-index fid objects initial)) + drop-cell (when grid-layout? (gslg/get-drop-cell fid objects initial)) shape (get-in state [:workspace-drawing :object]) shape (-> shape @@ -101,6 +105,9 @@ (cond-> (some? drop-index) (with-meta {:index drop-index})) + (cond-> (some? drop-cell) + (with-meta {:cell drop-cell})) + (assoc :initialized? true) (assoc :click-draw? true))] (rx/concat diff --git a/frontend/src/app/main/data/workspace/drawing/curve.cljs b/frontend/src/app/main/data/workspace/drawing/curve.cljs index 307f0973a..e4c400a04 100644 --- a/frontend/src/app/main/data/workspace/drawing/curve.cljs +++ b/frontend/src/app/main/data/workspace/drawing/curve.cljs @@ -8,7 +8,8 @@ (:require [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] - [app.common.geom.shapes.flex-layout :as gsl] + [app.common.geom.shapes.flex-layout :as gslf] + [app.common.geom.shapes.grid-layout :as gslg] [app.common.geom.shapes.path :as gsp] [app.common.types.shape-tree :as ctst] [app.common.types.shape.layout :as ctl] @@ -53,11 +54,15 @@ position (when start (gpt/point start)) frame-id (ctst/top-nested-frame objects position) flex-layout? (ctl/flex-layout? objects frame-id) - drop-index (when flex-layout? (gsl/get-drop-index frame-id objects position))] + grid-layout? (ctl/grid-layout? objects frame-id) + drop-index (when flex-layout? (gslf/get-drop-index frame-id objects position)) + drop-cell (when grid-layout? (gslg/get-drop-cell frame-id objects position))] (-> state (assoc-in [:workspace-drawing :object :frame-id] frame-id) (cond-> (some? drop-index) - (update-in [:workspace-drawing :object] with-meta {:index drop-index}))))))) + (update-in [:workspace-drawing :object] with-meta {:index drop-index})) + (cond-> (some? drop-cell) + (update-in [:workspace-drawing :object] with-meta {:cell drop-cell}))))))) (defn curve-to-path [{:keys [segments] :as shape}] (let [content (gsp/segments->content segments) diff --git a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs index 2a7710366..045927fdc 100644 --- a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs +++ b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs @@ -6,6 +6,8 @@ (ns app.main.data.workspace.grid-layout.editor (:require + [app.common.geom.shapes :as gsh] + [app.main.data.workspace.state-helpers :as wsh] [potok.core :as ptk])) (defn hover-grid-cell @@ -42,3 +44,21 @@ ptk/UpdateEvent (update [_ state] (update state :workspace-grid-edition dissoc grid-id)))) + +(defn locate-board + [grid-id] + (ptk/reify ::locate-board + ptk/UpdateEvent + (update [_ state] + (let [objects (wsh/lookup-page-objects state) + srect (get-in objects [grid-id :selrect])] + (prn srect) + (-> state + (update :workspace-local + (fn [{:keys [zoom vport] :as local}] + (let [{:keys [x y width height]} srect + x (+ x (/ width 2) (- (/ (:width vport) 2 zoom))) + y (+ y (/ height 2) (- (/ (:height vport) 2 zoom))) + srect (gsh/make-selrect x y width height)] + (-> local + (update :vbox merge (select-keys srect [:x :y :x1 :x2 :y1 :y2]))))))))))) diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index fc1ec6395..7deb5385f 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -1,4 +1,4 @@ -; This Source Code Form is subject to the terms of the Mozilla Public +;; 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/. ;; diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 0ef0f39a2..e8d04ff94 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -82,6 +82,7 @@ selected) index (:index (meta attrs)) + [row column :as cell] (:cell (meta attrs)) changes (-> changes (pcb/with-objects objects) @@ -91,6 +92,8 @@ (pcb/add-object shape)) (cond-> (some? (:parent-id attrs)) (pcb/change-parent (:parent-id attrs) [shape] index)) + (cond-> (some? cell) + (pcb/update-shapes [(:parent-id shape)] #(ctl/push-into-cell % [id] row column))) (cond-> (ctl/grid-layout? objects (:parent-id shape)) (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells)))] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs index caba728b9..f0141037c 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs @@ -151,7 +151,7 @@ (dom/class? node "frame-title") (let [shape (gsh/transform-shape shape modifiers) zoom (get-in @st/state [:workspace-local :zoom] 1) - mtx (vwu/title-transform shape zoom)] + mtx (vwu/title-transform shape zoom false)] (override-transform-att! node "transform" mtx)) (or (= (dom/get-tag-name node) "mask") diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 628c6c8ff..7c1f48565 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -53,9 +53,9 @@ (.setPointerCapture editor (.-pointerId bevent)) (.setPointerCapture target (.-pointerId bevent)))) - (when (or (dom/class? (dom/get-target bevent) "viewport-controls") - (dom/class? (dom/get-target bevent) "viewport-selrect")) + (dom/child? (dom/get-target bevent) (dom/query ".viewport-controls"))) + (dom/stop-propagation bevent) (when-not @z? @@ -79,7 +79,6 @@ (st/emit! (dw/start-zooming pt))) (st/emit! (dw/start-panning)))) - left-click? (do (st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?)) diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs index b446f1b7b..9eb72cb46 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs @@ -38,48 +38,6 @@ :flex (dm/str (fmt/format-number value) "FR") :auto "AUTO")) -(mf/defc track-marker - {::mf/wrap-props false} - [props] - - (let [center (unchecked-get props "center") - value (unchecked-get props "value") - zoom (unchecked-get props "zoom") - - marker-width (/ 24 zoom) - marker-h1 (/ 22 zoom) - marker-h2 (/ 8 zoom) - - marker-half-width (/ marker-width 2) - marker-half-height (/ (+ marker-h1 marker-h2) 2) - - marker-points - (reduce - apply-to-point - [(gpt/subtract center - (gpt/point marker-half-width marker-half-height))] - [#(gpt/add % (gpt/point marker-width 0)) - #(gpt/add % (gpt/point 0 marker-h1)) - #(gpt/add % (gpt/point (- marker-half-width) marker-h2)) - #(gpt/subtract % (gpt/point marker-half-width marker-h2))]) - - text-x (:x center) - text-y (:y center)] - - [:g {:class (css :grid-track-marker)} - [:polygon {:class (css :marker-shape) - :points (->> marker-points - (map #(dm/fmt "%,%" (:x %) (:y %))) - (str/join " "))}] - [:text {:class (css :marker-text) - :x text-x - :y text-y - :width (/ 26.26 zoom) - :height (/ 32 zoom) - :text-anchor "middle" - :dominant-baseline "middle"} - (dm/str value)]])) - (mf/defc grid-editor-frame {::mf/wrap-props false} [props] @@ -282,7 +240,22 @@ cell-bounds (gsg/cell-bounds layout-data cell) cell-origin (gpo/origin cell-bounds) cell-width (gpo/width-points cell-bounds) - cell-height (gpo/height-points cell-bounds)] + cell-height (gpo/height-points cell-bounds) + + handle-pointer-enter + (mf/use-callback + (fn [] + (st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) true)))) + + handle-pointer-leave + (mf/use-callback + (fn [] + (st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) false)))) + + handle-pointer-down + (mf/use-callback + (fn [] + (st/emit! (dwge/select-grid-cell (:id shape) (:id cell)))))] [:g.cell-editor [:rect @@ -294,9 +267,9 @@ :width cell-width :height cell-height - :on-pointer-enter #(st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) true)) - :on-pointer-leave #(st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) false)) - :on-click #(st/emit! (dwge/select-grid-cell (:id shape) (:id cell)))}] + :on-pointer-enter handle-pointer-enter + :on-pointer-leave handle-pointer-leave + :on-pointer-down handle-pointer-down}] (when selected? (let [handlers @@ -318,31 +291,17 @@ :direction dir :layout-data layout-data}])]))])) -(mf/defc resize-track-handler - {::mf/wrap-props false} - [props] +(defn use-resize-track + [type shape index track-before track-after zoom snap-pixel?] - (let [shape (unchecked-get props "shape") - index (unchecked-get props "index") - track-before (unchecked-get props "track-before") - track-after (unchecked-get props "track-after") - - {:keys [column-total-size column-total-gap row-total-size row-total-gap]} (unchecked-get props "layout-data") - start-p (unchecked-get props "start-p") - type (unchecked-get props "type") - zoom (unchecked-get props "zoom") - - [layout-gap-row layout-gap-col] (ctl/gaps shape) - - start-size-before (mf/use-var nil) + (let [start-size-before (mf/use-var nil) start-size-after (mf/use-var nil) - snap-pixel? (mf/deref refs/snap-pixel?) - handle-drag-start (mf/use-callback (mf/deps shape track-before track-after) (fn [] + (prn "???" type (:name shape) index track-before track-after zoom snap-pixel?) (reset! start-size-before (:size track-before)) (reset! start-size-after (:size track-after)) (let [tracks-prop @@ -387,23 +346,42 @@ (fn [] (reset! start-size-before nil) (reset! start-size-after nil) - (st/emit! (dwm/apply-modifiers)))) + (st/emit! (dwm/apply-modifiers))))] + + (use-drag {:on-drag-start handle-drag-start + :on-drag-delta handle-drag-position + :on-drag-end handle-drag-end}))) + +(mf/defc resize-track-handler + {::mf/wrap-props false} + [props] + + (let [shape (unchecked-get props "shape") + index (unchecked-get props "index") + track-before (unchecked-get props "track-before") + track-after (unchecked-get props "track-after") + snap-pixel? (unchecked-get props "snap-pixel?") + + {:keys [column-total-size column-total-gap row-total-size row-total-gap]} (unchecked-get props "layout-data") + start-p (unchecked-get props "start-p") + type (unchecked-get props "type") + zoom (unchecked-get props "zoom") + + [layout-gap-row layout-gap-col] (ctl/gaps shape) {:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]} - (use-drag {:on-drag-start handle-drag-start - :on-drag-delta handle-drag-position - :on-drag-end handle-drag-end}) + (use-resize-track type shape index track-before track-after zoom snap-pixel?) [x y width height] (if (= type :column) - [(- (:x start-p) (max layout-gap-col (/ 8 zoom))) - (- (:y start-p) (/ 40 zoom)) + [(- (:x start-p) layout-gap-col (/ (- (max layout-gap-col (/ 16 zoom)) layout-gap-col) 2)) + (:y start-p) (max layout-gap-col (/ 16 zoom)) - (+ row-total-size row-total-gap (/ 40 zoom))] + (+ row-total-size row-total-gap)] - [(- (:x start-p) (/ 40 zoom)) - (- (:y start-p) (max layout-gap-row (/ 8 zoom))) - (+ column-total-size column-total-gap (/ 40 zoom)) + [(:x start-p) + (- (:y start-p) layout-gap-row (/ (- (max layout-gap-row (/ 16 zoom)) layout-gap-row) 2)) + (+ column-total-size column-total-gap) (max layout-gap-row (/ 16 zoom))])] [:rect.resize-track-handler @@ -419,6 +397,63 @@ :on-pointer-move handle-pointer-move :style {:fill "transparent"}}])) +(mf/defc track-marker + {::mf/wrap-props false} + [props] + + (let [center (unchecked-get props "center") + value (unchecked-get props "value") + zoom (unchecked-get props "zoom") + shape (unchecked-get props "shape") + index (unchecked-get props "index") + type (unchecked-get props "type") + track-before (unchecked-get props "track-before") + track-after (unchecked-get props "track-after") + snap-pixel? (unchecked-get props "snap-pixel?") + + marker-width (/ 24 zoom) + marker-h1 (/ 22 zoom) + marker-h2 (/ 8 zoom) + + marker-half-width (/ marker-width 2) + marker-half-height (/ (+ marker-h1 marker-h2) 2) + + marker-points + (reduce + apply-to-point + [(gpt/subtract center + (gpt/point marker-half-width marker-half-height))] + [#(gpt/add % (gpt/point marker-width 0)) + #(gpt/add % (gpt/point 0 marker-h1)) + #(gpt/add % (gpt/point (- marker-half-width) marker-h2)) + #(gpt/subtract % (gpt/point marker-half-width marker-h2))]) + + text-x (:x center) + text-y (:y center) + + {:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]} + (use-resize-track type shape index track-before track-after zoom snap-pixel?)] + + [:g {:on-pointer-down handle-pointer-down + :on-lost-pointer-capture handle-lost-pointer-capture + :on-pointer-move handle-pointer-move + :class (css :grid-track-marker) + :style {:cursor (if (= type :column) + (cur/resize-ew 0) + (cur/resize-ns 0))}} + [:polygon {:class (css :marker-shape) + :points (->> marker-points + (map #(dm/fmt "%,%" (:x %) (:y %))) + (str/join " "))}] + [:text {:class (css :marker-text) + :x text-x + :y text-y + :width (/ 26.26 zoom) + :height (/ 32 zoom) + :text-anchor "middle" + :dominant-baseline "middle"} + (dm/str value)]])) + (mf/defc track {::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "zoom" "index" "type" "track-data" "layout-data"]))] ::mf/wrap-props false} @@ -427,32 +462,40 @@ zoom (unchecked-get props "zoom") type (unchecked-get props "type") index (unchecked-get props "index") + snap-pixel? (unchecked-get props "snap-pixel?") track-data (unchecked-get props "track-data") layout-data (unchecked-get props "layout-data") track-input-ref (mf/use-ref) [layout-gap-row layout-gap-col] (ctl/gaps shape) bounds (:points shape) + origin (gpo/origin bounds) vv #(gpo/start-vv bounds %) hv #(gpo/start-hv bounds %) start-p (:start-p track-data) + relative (gpt/to-vec origin start-p) + marker-p (if (= type :column) - (-> start-p + (-> origin + (gpt/add (hv (:x relative))) (gpt/subtract (vv (/ 20 zoom))) (cond-> (not= index 0) (gpt/subtract (hv (/ layout-gap-col 2))))) - (-> start-p + (-> origin + (gpt/add (vv (:y relative))) (gpt/subtract (hv (/ 20 zoom))) (cond-> (not= index 0) (gpt/subtract (vv (/ layout-gap-row 2)))))) text-p (if (= type :column) - (-> start-p + (-> origin + (gpt/add (hv (:x relative))) (gpt/subtract (vv (/ 36 zoom)))) - (-> start-p + (-> origin + (gpt/add (vv (:y relative))) (gpt/subtract (hv (/ (:size track-data) 2))) (gpt/subtract (hv (/ 16 zoom))) (gpt/add (vv (/ (:size track-data) 2))) @@ -499,7 +542,9 @@ [text-x text-y text-width text-height] (if (= type :column) [(:x text-p) (:y text-p) (max 0 (- (:size track-data) 4)) (/ 32 zoom)] - [(:x text-p) (:y text-p) (:size track-data) (/ 36 zoom)])] + [(:x text-p) (:y text-p) (:size track-data) (/ 36 zoom)]) + + track-before (get-in layout-data [track-list-prop (dec index)])] (mf/use-effect (mf/deps track-data) @@ -509,6 +554,12 @@ [:g.track [:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))} [:& track-marker {:center marker-p + :index index + :shape shape + :snap-pixel? snap-pixel? + :track-after track-data + :track-before track-before + :type type :value (dm/str (inc index)) :zoom zoom}]] [:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (+ (:x text-p) (/ (:size track-data) 2)) (+ (:y text-p) (/ 36 zoom 2))))} @@ -522,16 +573,17 @@ :on-key-down handle-keydown-track-input :on-blur handle-blur-track-input}]]] - (let [track-before (get-in layout-data [track-list-prop (dec index)])] - [:& resize-track-handler - {:index index - :shape shape - :layout-data layout-data - :start-p start-p - :type type - :track-before track-before - :track-after track-data - :zoom zoom}])])) + + [:& resize-track-handler + {:index index + :layout-data layout-data + :shape shape + :snap-pixel? snap-pixel? + :start-p start-p + :track-after track-data + :track-before track-before + :type type + :zoom zoom}]])) (mf/defc editor {::mf/wrap [mf/memo] @@ -547,6 +599,8 @@ shape-ref (mf/use-memo (mf/deps (:id shape)) #(refs/object-by-id (:id shape))) base-shape (mf/deref shape-ref) + snap-pixel? (mf/deref refs/snap-pixel?) + grid-edition-id-ref (mf/use-memo #(refs/workspace-grid-edition-id (:id shape))) grid-edition (mf/deref grid-edition-id-ref) @@ -565,9 +619,18 @@ height (gpo/height-points bounds) origin (gpo/origin bounds) + [layout-gap-row layout-gap-col] (ctl/gaps shape) + {:keys [row-tracks column-tracks] :as layout-data} (gsg/calc-layout-data shape children bounds) + handle-pointer-down + (mf/use-callback + (fn [event] + (let [left-click? (= 1 (.-which (.-nativeEvent event)))] + (when left-click? + (dom/stop-propagation event))))) + handle-add-column (mf/use-callback (mf/deps (:id shape)) @@ -581,10 +644,11 @@ (st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :row ctl/default-track-value)))))] (mf/use-effect - (fn [] - #(st/emit! (dwge/stop-grid-layout-editing (:id shape))))) + (fn [] + #(st/emit! (dwge/stop-grid-layout-editing (:id shape))))) - [:g.grid-editor {:pointer-events (when view-only "none")} + [:g.grid-editor {:pointer-events (when view-only "none") + :on-pointer-down handle-pointer-down} (when-not view-only [:* [:& grid-editor-frame {:zoom zoom @@ -608,57 +672,76 @@ :type :column :index idx :layout-data layout-data + :snap-pixel? snap-pixel? :track-data column-data}]) ;; Last track resize handler - (let [last-track (last column-tracks) - start-p (:start-p (last column-tracks)) - marker-p (-> start-p - (gpt/subtract (vv (/ 20 zoom))) - (gpt/add (hv (:size last-track))))] - [:g.track - [:& track-marker {:center marker-p - :value (dm/str (inc (count column-tracks))) - :zoom zoom}] - [:& resize-track-handler - {:index (count column-tracks) - :shape shape - :layout-data layout-data - :start-p (-> start-p - (gpt/add (hv (:size last-track))) - (gpt/add (hv (/ 20 zoom)))) - :type :column - :track-before (last column-tracks) - :zoom zoom}]]) + (when-not (empty? column-tracks) + (let [last-track (last column-tracks) + start-p (:start-p (last column-tracks)) + relative (gpt/to-vec origin start-p) + marker-p (-> origin + (gpt/add (hv (:x relative))) + (gpt/subtract (vv (/ 20 zoom))) + (gpt/add (hv (:size last-track))))] + [:g.track + [:& track-marker {:center marker-p + :index (count column-tracks) + :shape shape + :snap-pixel? snap-pixel? + :track-before (last column-tracks) + :type :column + :value (dm/str (inc (count column-tracks))) + :zoom zoom}] + [:& resize-track-handler + {:index (count column-tracks) + :shape shape + :layout-data layout-data + :snap-pixel? snap-pixel? + :start-p (-> start-p (gpt/add (hv (+ layout-gap-col (:size last-track))))) + :type :column + :track-before (last column-tracks) + :zoom zoom}]])) (for [[idx row-data] (d/enumerate row-tracks)] - [:& track {:key (dm/str "row-track-" idx) - :shape shape - :zoom zoom - :type :row - :index idx + [:& track {:index idx + :key (dm/str "row-track-" idx) :layout-data layout-data - :track-data row-data}]) + :shape shape + :snap-pixel? snap-pixel? + :track-data row-data + :type :row + :zoom zoom}]) - (let [last-track (last row-tracks) - start-p (:start-p (last row-tracks)) - marker-p (-> start-p - (gpt/subtract (hv (/ 20 zoom))) - (gpt/add (vv (:size last-track))))] - [:g.track - [:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))} - [:& track-marker {:center marker-p - :value (dm/str (inc (count row-tracks))) - :zoom zoom}]] - [:& resize-track-handler - {:index (count row-tracks) - :shape shape - :layout-data layout-data - :start-p (-> start-p - (gpt/add (vv (:size last-track)))) - :type :row - :track-before (last row-tracks) - :zoom zoom}]]) + (when-not (empty? row-tracks) + (let [last-track (last row-tracks) + start-p (:start-p (last row-tracks)) + relative (gpt/to-vec origin start-p) + marker-p + (-> origin + (gpt/add (vv (:y relative))) + (gpt/subtract (hv (/ 20 zoom))) + (gpt/add (vv (:size last-track))))] + [:g.track + [:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))} + [:& track-marker {:center marker-p + :index (count row-tracks) + :shape shape + :snap-pixel? snap-pixel? + :track-before (last row-tracks) + :type :row + :value (dm/str (inc (count row-tracks))) + :zoom zoom}]] + [:& resize-track-handler + {:index (count row-tracks) + :shape shape + :layout-data layout-data + :start-p (-> start-p + (gpt/add (vv (+ layout-gap-row (:size last-track))))) + :type :row + :track-before (last row-tracks) + :snap-pixel? snap-pixel? + :zoom zoom}]])) (for [[_ cell] (:layout-grid-cells shape)] [:& grid-cell {:key (dm/str "cell-" (:id cell)) diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss index 8a8a13659..1526c3aaa 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss @@ -31,7 +31,7 @@ } .grid-frame { - fill: transparent; + fill: #F6F6F6; stroke: var(--color-distance); stroke-width: calc(1 / var(--zoom)); } diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index 5587022d8..5fdc522a6 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -69,7 +69,8 @@ (> (:x cand) (:x cur)) cand :else cur))) -(defn title-transform [{:keys [points] :as shape} zoom] +(defn title-transform + [{:keys [points] :as shape} zoom grid-edition?] (let [leftmost (->> points (reduce left?)) topmost (->> points (remove #{leftmost}) (reduce top?)) rightmost (->> points (remove #{leftmost topmost}) (reduce right?)) @@ -81,14 +82,18 @@ top-right-angle (gpt/angle top-right) ;; Choose the position that creates the less angle between left-side and top-side - [label-pos angle v-pos] + [label-pos angle h-pos v-pos] (if (< (mth/abs left-top-angle) (mth/abs top-right-angle)) - [leftmost left-top-angle (gpt/perpendicular left-top)] - [topmost top-right-angle (gpt/perpendicular top-right)]) + [leftmost left-top-angle left-top (gpt/perpendicular left-top)] + [topmost top-right-angle top-right (gpt/perpendicular top-right)]) + delta-x (if grid-edition? 40 0) + delta-y (if grid-edition? 50 10) label-pos - (gpt/subtract label-pos (gpt/scale (gpt/unit v-pos) (/ 10 zoom)))] + (-> label-pos + (gpt/subtract (gpt/scale (gpt/unit v-pos) (/ delta-y zoom))) + (gpt/subtract (gpt/scale (gpt/unit h-pos) (/ delta-x zoom))))] (dm/fmt "rotate(% %,%) scale(%, %) translate(%, %)" ;; rotate diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index 7755d080a..9da52f0b2 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -12,8 +12,10 @@ [app.common.pages.helpers :as cph] [app.common.types.container :as ctn] [app.common.types.shape-tree :as ctt] + [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] [app.main.data.workspace :as dw] + [app.main.data.workspace.grid-layout.editor :as dwge] [app.main.data.workspace.interactions :as dwi] [app.main.refs :as refs] [app.main.store :as st] @@ -24,6 +26,7 @@ [app.main.ui.workspace.viewport.path-actions :refer [path-actions]] [app.main.ui.workspace.viewport.utils :as vwu] [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] [app.util.timers :as ts] [debug :refer [debug?]] [rumext.v2 :as mf])) @@ -56,16 +59,34 @@ selected (mf/deref refs/selected-objects) drawing (mf/deref refs/workspace-drawing) drawing-obj (:object drawing) - shape (or drawing-obj (-> selected first))] - (when (or (and (= (count selected) 1) - (= (:id shape) edition) - (and (not (cph/text-shape? shape)) - (not (cph/frame-shape? shape)))) - (and (some? drawing-obj) - (cph/path-shape? drawing-obj) - (not= :curve (:tool drawing)))) + shape (or drawing-obj (-> selected first)) + + single? (= (count selected) 1) + editing? (= (:id shape) edition) + draw-path? (and (some? drawing-obj) + (cph/path-shape? drawing-obj) + (not= :curve (:tool drawing))) + + path-edition? (or (and single? editing? + (and (not (cph/text-shape? shape)) + (not (cph/frame-shape? shape)))) + draw-path?) + + grid-edition? (and single? editing? (ctl/grid-layout? shape))] + + (cond + path-edition? [:div.viewport-actions - [:& path-actions {:shape shape}]]))) + [:& path-actions {:shape shape}]] + + grid-edition? + [:div.viewport-actions + [:div.grid-actions + [:div.grid-edit-title + (tr "workspace.layout_grid.editor.title") " " [:span.grid-edit-board-name (:name shape)]] + [:button.btn-secondary {:on-click #(st/emit! (dwge/locate-board (:id shape)))} "Locate"] + [:button.btn-primary {:on-click #(st/emit! dw/clear-edition-mode)} "Done"] + [:button.btn-icon-basic {:on-click #(st/emit! dw/clear-edition-mode)} i/close]]]))) (mf/defc cursor-tooltip [{:keys [zoom tooltip] :as props}] @@ -97,7 +118,7 @@ (mf/defc frame-title {::mf/wrap [mf/memo #(mf/deferred % ts/raf)]} - [{:keys [frame selected? zoom show-artboard-names? show-id? on-frame-enter on-frame-leave on-frame-select]}] + [{:keys [frame selected? zoom show-artboard-names? show-id? on-frame-enter on-frame-leave on-frame-select grid-edition?]}] (let [workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) ;; Note that we don't use mf/deref to avoid a repaint dependency here @@ -114,8 +135,8 @@ (fn [bevent] (let [event (.-nativeEvent bevent)] (when (= 1 (.-which event)) - (dom/prevent-default event) - (dom/stop-propagation event) + (dom/prevent-default bevent) + (dom/stop-propagation bevent) (on-frame-select event (:id frame)))))) on-double-click @@ -150,7 +171,7 @@ (when (not (:hidden frame)) [:g.frame-title {:id (dm/str "frame-title-" (:id frame)) - :transform (vwu/title-transform frame zoom) + :transform (vwu/title-transform frame zoom grid-edition?) :pointer-events (when (:blocked frame) "none")} (when (:use-for-thumbnail? frame) [:svg {:x 0 @@ -195,7 +216,10 @@ (map (d/getf objects)) selected) shapes) - focus (unchecked-get props "focus")] + focus (unchecked-get props "focus") + + edition (mf/deref refs/selected-edition) + grid-edition? (ctl/grid-layout? objects edition)] [:g.frame-titles (for [{:keys [id parent-id] :as shape} shapes] @@ -211,7 +235,8 @@ :show-id? (debug? :shape-titles) :on-frame-enter on-frame-enter :on-frame-leave on-frame-leave - :on-frame-select on-frame-select}]))])) + :on-frame-select on-frame-select + :grid-edition? (and (= id edition) grid-edition?)}]))])) (mf/defc frame-flow [{:keys [flow frame selected? zoom on-frame-enter on-frame-leave on-frame-select]}] diff --git a/frontend/translations/en.po b/frontend/translations/en.po index bfefc32b1..964aceddf 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -4957,3 +4957,6 @@ msgstr "Marketing" #: src/app/main/ui/onboarding/questions.cljs msgid "questions.student-teacher" msgstr "Student or teacher" + +msgid "workspace.layout_grid.editor.title" +msgstr "Editing grid" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index dcdd6727b..16a40b937 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -5075,3 +5075,6 @@ msgstr "Marketing" #: src/app/main/ui/onboarding/questions.cljs msgid "questions.student-teacher" msgstr "Estudiante o profesorado" + +msgid "workspace.layout_grid.editor.title" +msgstr "Editando rejilla"