Changes to the editor UI

This commit is contained in:
alonso.torres 2023-05-08 17:25:22 +02:00
parent 3c8934e847
commit 5925d2520f
16 changed files with 350 additions and 168 deletions

View file

@ -349,6 +349,9 @@
column-tracks (add-auto-size column-tracks column-add-auto) column-tracks (add-auto-size column-tracks column-add-auto)
row-tracks (add-auto-size row-tracks row-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 start-p
(cond-> bound-corner (cond-> bound-corner
(= :end (:layout-align-content parent)) (= :end (:layout-align-content parent))

View file

@ -123,6 +123,10 @@
padding: $size-1; padding: $size-1;
svg { svg {
fill: $color-gray-20; fill: $color-gray-20;
&.icon-close {
transform: rotate(45deg);
}
} }
&:hover { &:hover {
background: transparent; background: transparent;

View file

@ -367,7 +367,8 @@ $height-palette-max: 80px;
z-index: 12; z-index: 12;
pointer-events: none; pointer-events: none;
.path-actions { .path-actions,
.grid-actions {
pointer-events: initial; pointer-events: initial;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -378,6 +379,27 @@ $height-palette-max: 80px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); 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 { .viewport-actions-group {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View file

@ -8,7 +8,8 @@
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [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.math :as mth]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.modifiers :as ctm] [app.common.types.modifiers :as ctm]
@ -84,7 +85,10 @@
fid (ctst/top-nested-frame objects initial) fid (ctst/top-nested-frame objects initial)
flex-layout? (ctl/flex-layout? objects fid) 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 (get-in state [:workspace-drawing :object])
shape (-> shape shape (-> shape
@ -101,6 +105,9 @@
(cond-> (some? drop-index) (cond-> (some? drop-index)
(with-meta {:index drop-index})) (with-meta {:index drop-index}))
(cond-> (some? drop-cell)
(with-meta {:cell drop-cell}))
(assoc :initialized? true) (assoc :initialized? true)
(assoc :click-draw? true))] (assoc :click-draw? true))]
(rx/concat (rx/concat

View file

@ -8,7 +8,8 @@
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [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.geom.shapes.path :as gsp]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
@ -53,11 +54,15 @@
position (when start (gpt/point start)) position (when start (gpt/point start))
frame-id (ctst/top-nested-frame objects position) frame-id (ctst/top-nested-frame objects position)
flex-layout? (ctl/flex-layout? objects frame-id) 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 (-> state
(assoc-in [:workspace-drawing :object :frame-id] frame-id) (assoc-in [:workspace-drawing :object :frame-id] frame-id)
(cond-> (some? drop-index) (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}] (defn curve-to-path [{:keys [segments] :as shape}]
(let [content (gsp/segments->content segments) (let [content (gsp/segments->content segments)

View file

@ -6,6 +6,8 @@
(ns app.main.data.workspace.grid-layout.editor (ns app.main.data.workspace.grid-layout.editor
(:require (:require
[app.common.geom.shapes :as gsh]
[app.main.data.workspace.state-helpers :as wsh]
[potok.core :as ptk])) [potok.core :as ptk]))
(defn hover-grid-cell (defn hover-grid-cell
@ -42,3 +44,21 @@
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(update state :workspace-grid-edition dissoc grid-id)))) (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])))))))))))

View file

@ -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 ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;

View file

@ -82,6 +82,7 @@
selected) selected)
index (:index (meta attrs)) index (:index (meta attrs))
[row column :as cell] (:cell (meta attrs))
changes (-> changes changes (-> changes
(pcb/with-objects objects) (pcb/with-objects objects)
@ -91,6 +92,8 @@
(pcb/add-object shape)) (pcb/add-object shape))
(cond-> (some? (:parent-id attrs)) (cond-> (some? (:parent-id attrs))
(pcb/change-parent (:parent-id attrs) [shape] index)) (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)) (cond-> (ctl/grid-layout? objects (:parent-id shape))
(pcb/update-shapes [(:parent-id shape)] ctl/assign-cells)))] (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells)))]

View file

@ -151,7 +151,7 @@
(dom/class? node "frame-title") (dom/class? node "frame-title")
(let [shape (gsh/transform-shape shape modifiers) (let [shape (gsh/transform-shape shape modifiers)
zoom (get-in @st/state [:workspace-local :zoom] 1) 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)) (override-transform-att! node "transform" mtx))
(or (= (dom/get-tag-name node) "mask") (or (= (dom/get-tag-name node) "mask")

View file

@ -53,9 +53,9 @@
(.setPointerCapture editor (.-pointerId bevent)) (.setPointerCapture editor (.-pointerId bevent))
(.setPointerCapture target (.-pointerId bevent)))) (.setPointerCapture target (.-pointerId bevent))))
(when (or (dom/class? (dom/get-target bevent) "viewport-controls") (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) (dom/stop-propagation bevent)
(when-not @z? (when-not @z?
@ -79,7 +79,6 @@
(st/emit! (dw/start-zooming pt))) (st/emit! (dw/start-zooming pt)))
(st/emit! (dw/start-panning)))) (st/emit! (dw/start-panning))))
left-click? left-click?
(do (do
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?)) (st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?))

View file

@ -38,48 +38,6 @@
:flex (dm/str (fmt/format-number value) "FR") :flex (dm/str (fmt/format-number value) "FR")
:auto "AUTO")) :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/defc grid-editor-frame
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]
@ -282,7 +240,22 @@
cell-bounds (gsg/cell-bounds layout-data cell) cell-bounds (gsg/cell-bounds layout-data cell)
cell-origin (gpo/origin cell-bounds) cell-origin (gpo/origin cell-bounds)
cell-width (gpo/width-points 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 [:g.cell-editor
[:rect [:rect
@ -294,9 +267,9 @@
:width cell-width :width cell-width
:height cell-height :height cell-height
:on-pointer-enter #(st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) true)) :on-pointer-enter handle-pointer-enter
:on-pointer-leave #(st/emit! (dwge/hover-grid-cell (:id shape) (:id cell) false)) :on-pointer-leave handle-pointer-leave
:on-click #(st/emit! (dwge/select-grid-cell (:id shape) (:id cell)))}] :on-pointer-down handle-pointer-down}]
(when selected? (when selected?
(let [handlers (let [handlers
@ -318,31 +291,17 @@
:direction dir :direction dir
:layout-data layout-data}])]))])) :layout-data layout-data}])]))]))
(mf/defc resize-track-handler (defn use-resize-track
{::mf/wrap-props false} [type shape index track-before track-after zoom snap-pixel?]
[props]
(let [shape (unchecked-get props "shape") (let [start-size-before (mf/use-var nil)
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)
start-size-after (mf/use-var nil) start-size-after (mf/use-var nil)
snap-pixel? (mf/deref refs/snap-pixel?)
handle-drag-start handle-drag-start
(mf/use-callback (mf/use-callback
(mf/deps shape track-before track-after) (mf/deps shape track-before track-after)
(fn [] (fn []
(prn "???" type (:name shape) index track-before track-after zoom snap-pixel?)
(reset! start-size-before (:size track-before)) (reset! start-size-before (:size track-before))
(reset! start-size-after (:size track-after)) (reset! start-size-after (:size track-after))
(let [tracks-prop (let [tracks-prop
@ -387,23 +346,42 @@
(fn [] (fn []
(reset! start-size-before nil) (reset! start-size-before nil)
(reset! start-size-after 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]} {:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]}
(use-drag {:on-drag-start handle-drag-start (use-resize-track type shape index track-before track-after zoom snap-pixel?)
:on-drag-delta handle-drag-position
:on-drag-end handle-drag-end})
[x y width height] [x y width height]
(if (= type :column) (if (= type :column)
[(- (:x start-p) (max layout-gap-col (/ 8 zoom))) [(- (:x start-p) layout-gap-col (/ (- (max layout-gap-col (/ 16 zoom)) layout-gap-col) 2))
(- (:y start-p) (/ 40 zoom)) (:y start-p)
(max layout-gap-col (/ 16 zoom)) (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)) [(:x start-p)
(- (:y start-p) (max layout-gap-row (/ 8 zoom))) (- (:y start-p) layout-gap-row (/ (- (max layout-gap-row (/ 16 zoom)) layout-gap-row) 2))
(+ column-total-size column-total-gap (/ 40 zoom)) (+ column-total-size column-total-gap)
(max layout-gap-row (/ 16 zoom))])] (max layout-gap-row (/ 16 zoom))])]
[:rect.resize-track-handler [:rect.resize-track-handler
@ -419,6 +397,63 @@
:on-pointer-move handle-pointer-move :on-pointer-move handle-pointer-move
:style {:fill "transparent"}}])) :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/defc track
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "zoom" "index" "type" "track-data" "layout-data"]))] {::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "zoom" "index" "type" "track-data" "layout-data"]))]
::mf/wrap-props false} ::mf/wrap-props false}
@ -427,32 +462,40 @@
zoom (unchecked-get props "zoom") zoom (unchecked-get props "zoom")
type (unchecked-get props "type") type (unchecked-get props "type")
index (unchecked-get props "index") index (unchecked-get props "index")
snap-pixel? (unchecked-get props "snap-pixel?")
track-data (unchecked-get props "track-data") track-data (unchecked-get props "track-data")
layout-data (unchecked-get props "layout-data") layout-data (unchecked-get props "layout-data")
track-input-ref (mf/use-ref) track-input-ref (mf/use-ref)
[layout-gap-row layout-gap-col] (ctl/gaps shape) [layout-gap-row layout-gap-col] (ctl/gaps shape)
bounds (:points shape) bounds (:points shape)
origin (gpo/origin bounds)
vv #(gpo/start-vv bounds %) vv #(gpo/start-vv bounds %)
hv #(gpo/start-hv bounds %) hv #(gpo/start-hv bounds %)
start-p (:start-p track-data) start-p (:start-p track-data)
relative (gpt/to-vec origin start-p)
marker-p marker-p
(if (= type :column) (if (= type :column)
(-> start-p (-> origin
(gpt/add (hv (:x relative)))
(gpt/subtract (vv (/ 20 zoom))) (gpt/subtract (vv (/ 20 zoom)))
(cond-> (not= index 0) (cond-> (not= index 0)
(gpt/subtract (hv (/ layout-gap-col 2))))) (gpt/subtract (hv (/ layout-gap-col 2)))))
(-> start-p (-> origin
(gpt/add (vv (:y relative)))
(gpt/subtract (hv (/ 20 zoom))) (gpt/subtract (hv (/ 20 zoom)))
(cond-> (not= index 0) (cond-> (not= index 0)
(gpt/subtract (vv (/ layout-gap-row 2)))))) (gpt/subtract (vv (/ layout-gap-row 2))))))
text-p text-p
(if (= type :column) (if (= type :column)
(-> start-p (-> origin
(gpt/add (hv (:x relative)))
(gpt/subtract (vv (/ 36 zoom)))) (gpt/subtract (vv (/ 36 zoom))))
(-> start-p (-> origin
(gpt/add (vv (:y relative)))
(gpt/subtract (hv (/ (:size track-data) 2))) (gpt/subtract (hv (/ (:size track-data) 2)))
(gpt/subtract (hv (/ 16 zoom))) (gpt/subtract (hv (/ 16 zoom)))
(gpt/add (vv (/ (:size track-data) 2))) (gpt/add (vv (/ (:size track-data) 2)))
@ -499,7 +542,9 @@
[text-x text-y text-width text-height] [text-x text-y text-width text-height]
(if (= type :column) (if (= type :column)
[(:x text-p) (:y text-p) (max 0 (- (:size track-data) 4)) (/ 32 zoom)] [(: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/use-effect
(mf/deps track-data) (mf/deps track-data)
@ -509,6 +554,12 @@
[:g.track [:g.track
[:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))} [:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))}
[:& track-marker {:center 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)) :value (dm/str (inc index))
:zoom zoom}]] :zoom zoom}]]
[:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (+ (:x text-p) (/ (:size track-data) 2)) (+ (:y text-p) (/ 36 zoom 2))))} [: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-key-down handle-keydown-track-input
:on-blur handle-blur-track-input}]]] :on-blur handle-blur-track-input}]]]
(let [track-before (get-in layout-data [track-list-prop (dec index)])]
[:& resize-track-handler [:& resize-track-handler
{:index index {:index index
:shape shape :layout-data layout-data
:layout-data layout-data :shape shape
:start-p start-p :snap-pixel? snap-pixel?
:type type :start-p start-p
:track-before track-before :track-after track-data
:track-after track-data :track-before track-before
:zoom zoom}])])) :type type
:zoom zoom}]]))
(mf/defc editor (mf/defc editor
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
@ -547,6 +599,8 @@
shape-ref (mf/use-memo (mf/deps (:id shape)) #(refs/object-by-id (:id shape))) shape-ref (mf/use-memo (mf/deps (:id shape)) #(refs/object-by-id (:id shape)))
base-shape (mf/deref shape-ref) 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-id-ref (mf/use-memo #(refs/workspace-grid-edition-id (:id shape)))
grid-edition (mf/deref grid-edition-id-ref) grid-edition (mf/deref grid-edition-id-ref)
@ -565,9 +619,18 @@
height (gpo/height-points bounds) height (gpo/height-points bounds)
origin (gpo/origin bounds) origin (gpo/origin bounds)
[layout-gap-row layout-gap-col] (ctl/gaps shape)
{:keys [row-tracks column-tracks] :as layout-data} {:keys [row-tracks column-tracks] :as layout-data}
(gsg/calc-layout-data shape children bounds) (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 handle-add-column
(mf/use-callback (mf/use-callback
(mf/deps (:id shape)) (mf/deps (:id shape))
@ -581,10 +644,11 @@
(st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :row ctl/default-track-value)))))] (st/emit! (st/emit! (dwsl/add-layout-track [(:id shape)] :row ctl/default-track-value)))))]
(mf/use-effect (mf/use-effect
(fn [] (fn []
#(st/emit! (dwge/stop-grid-layout-editing (:id shape))))) #(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 (when-not view-only
[:* [:*
[:& grid-editor-frame {:zoom zoom [:& grid-editor-frame {:zoom zoom
@ -608,57 +672,76 @@
:type :column :type :column
:index idx :index idx
:layout-data layout-data :layout-data layout-data
:snap-pixel? snap-pixel?
:track-data column-data}]) :track-data column-data}])
;; Last track resize handler ;; Last track resize handler
(let [last-track (last column-tracks) (when-not (empty? column-tracks)
start-p (:start-p (last column-tracks)) (let [last-track (last column-tracks)
marker-p (-> start-p start-p (:start-p (last column-tracks))
(gpt/subtract (vv (/ 20 zoom))) relative (gpt/to-vec origin start-p)
(gpt/add (hv (:size last-track))))] marker-p (-> origin
[:g.track (gpt/add (hv (:x relative)))
[:& track-marker {:center marker-p (gpt/subtract (vv (/ 20 zoom)))
:value (dm/str (inc (count column-tracks))) (gpt/add (hv (:size last-track))))]
:zoom zoom}] [:g.track
[:& resize-track-handler [:& track-marker {:center marker-p
{:index (count column-tracks) :index (count column-tracks)
:shape shape :shape shape
:layout-data layout-data :snap-pixel? snap-pixel?
:start-p (-> start-p :track-before (last column-tracks)
(gpt/add (hv (:size last-track))) :type :column
(gpt/add (hv (/ 20 zoom)))) :value (dm/str (inc (count column-tracks)))
:type :column :zoom zoom}]
:track-before (last column-tracks) [:& resize-track-handler
:zoom zoom}]]) {: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)] (for [[idx row-data] (d/enumerate row-tracks)]
[:& track {:key (dm/str "row-track-" idx) [:& track {:index idx
:shape shape :key (dm/str "row-track-" idx)
:zoom zoom
:type :row
:index idx
:layout-data layout-data :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) (when-not (empty? row-tracks)
start-p (:start-p (last row-tracks)) (let [last-track (last row-tracks)
marker-p (-> start-p start-p (:start-p (last row-tracks))
(gpt/subtract (hv (/ 20 zoom))) relative (gpt/to-vec origin start-p)
(gpt/add (vv (:size last-track))))] marker-p
[:g.track (-> origin
[:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))} (gpt/add (vv (:y relative)))
[:& track-marker {:center marker-p (gpt/subtract (hv (/ 20 zoom)))
:value (dm/str (inc (count row-tracks))) (gpt/add (vv (:size last-track))))]
:zoom zoom}]] [:g.track
[:& resize-track-handler [:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))}
{:index (count row-tracks) [:& track-marker {:center marker-p
:shape shape :index (count row-tracks)
:layout-data layout-data :shape shape
:start-p (-> start-p :snap-pixel? snap-pixel?
(gpt/add (vv (:size last-track)))) :track-before (last row-tracks)
:type :row :type :row
:track-before (last row-tracks) :value (dm/str (inc (count row-tracks)))
:zoom zoom}]]) :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)] (for [[_ cell] (:layout-grid-cells shape)]
[:& grid-cell {:key (dm/str "cell-" (:id cell)) [:& grid-cell {:key (dm/str "cell-" (:id cell))

View file

@ -31,7 +31,7 @@
} }
.grid-frame { .grid-frame {
fill: transparent; fill: #F6F6F6;
stroke: var(--color-distance); stroke: var(--color-distance);
stroke-width: calc(1 / var(--zoom)); stroke-width: calc(1 / var(--zoom));
} }

View file

@ -69,7 +69,8 @@
(> (:x cand) (:x cur)) cand (> (:x cand) (:x cur)) cand
:else cur))) :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?)) (let [leftmost (->> points (reduce left?))
topmost (->> points (remove #{leftmost}) (reduce top?)) topmost (->> points (remove #{leftmost}) (reduce top?))
rightmost (->> points (remove #{leftmost topmost}) (reduce right?)) rightmost (->> points (remove #{leftmost topmost}) (reduce right?))
@ -81,14 +82,18 @@
top-right-angle (gpt/angle top-right) top-right-angle (gpt/angle top-right)
;; Choose the position that creates the less angle between left-side and top-side ;; 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)) (if (< (mth/abs left-top-angle) (mth/abs top-right-angle))
[leftmost left-top-angle (gpt/perpendicular left-top)] [leftmost left-top-angle left-top (gpt/perpendicular left-top)]
[topmost top-right-angle (gpt/perpendicular top-right)]) [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 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(%, %)" (dm/fmt "rotate(% %,%) scale(%, %) translate(%, %)"
;; rotate ;; rotate

View file

@ -12,8 +12,10 @@
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.shape-tree :as ctt] [app.common.types.shape-tree :as ctt]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.grid-layout.editor :as dwge]
[app.main.data.workspace.interactions :as dwi] [app.main.data.workspace.interactions :as dwi]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
@ -24,6 +26,7 @@
[app.main.ui.workspace.viewport.path-actions :refer [path-actions]] [app.main.ui.workspace.viewport.path-actions :refer [path-actions]]
[app.main.ui.workspace.viewport.utils :as vwu] [app.main.ui.workspace.viewport.utils :as vwu]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.timers :as ts] [app.util.timers :as ts]
[debug :refer [debug?]] [debug :refer [debug?]]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -56,16 +59,34 @@
selected (mf/deref refs/selected-objects) selected (mf/deref refs/selected-objects)
drawing (mf/deref refs/workspace-drawing) drawing (mf/deref refs/workspace-drawing)
drawing-obj (:object drawing) drawing-obj (:object drawing)
shape (or drawing-obj (-> selected first))] shape (or drawing-obj (-> selected first))
(when (or (and (= (count selected) 1)
(= (:id shape) edition) single? (= (count selected) 1)
(and (not (cph/text-shape? shape)) editing? (= (:id shape) edition)
(not (cph/frame-shape? shape)))) draw-path? (and (some? drawing-obj)
(and (some? drawing-obj) (cph/path-shape? drawing-obj)
(cph/path-shape? drawing-obj) (not= :curve (:tool drawing)))
(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 [: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 (mf/defc cursor-tooltip
[{:keys [zoom tooltip] :as props}] [{:keys [zoom tooltip] :as props}]
@ -97,7 +118,7 @@
(mf/defc frame-title (mf/defc frame-title
{::mf/wrap [mf/memo {::mf/wrap [mf/memo
#(mf/deferred % ts/raf)]} #(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?) (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 ;; Note that we don't use mf/deref to avoid a repaint dependency here
@ -114,8 +135,8 @@
(fn [bevent] (fn [bevent]
(let [event (.-nativeEvent bevent)] (let [event (.-nativeEvent bevent)]
(when (= 1 (.-which event)) (when (= 1 (.-which event))
(dom/prevent-default event) (dom/prevent-default bevent)
(dom/stop-propagation event) (dom/stop-propagation bevent)
(on-frame-select event (:id frame)))))) (on-frame-select event (:id frame))))))
on-double-click on-double-click
@ -150,7 +171,7 @@
(when (not (:hidden frame)) (when (not (:hidden frame))
[:g.frame-title {:id (dm/str "frame-title-" (:id 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")} :pointer-events (when (:blocked frame) "none")}
(when (:use-for-thumbnail? frame) (when (:use-for-thumbnail? frame)
[:svg {:x 0 [:svg {:x 0
@ -195,7 +216,10 @@
(map (d/getf objects)) (map (d/getf objects))
selected) selected)
shapes) 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 [:g.frame-titles
(for [{:keys [id parent-id] :as shape} shapes] (for [{:keys [id parent-id] :as shape} shapes]
@ -211,7 +235,8 @@
:show-id? (debug? :shape-titles) :show-id? (debug? :shape-titles)
:on-frame-enter on-frame-enter :on-frame-enter on-frame-enter
:on-frame-leave on-frame-leave :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 (mf/defc frame-flow
[{:keys [flow frame selected? zoom on-frame-enter on-frame-leave on-frame-select]}] [{:keys [flow frame selected? zoom on-frame-enter on-frame-leave on-frame-select]}]

View file

@ -4957,3 +4957,6 @@ msgstr "Marketing"
#: src/app/main/ui/onboarding/questions.cljs #: src/app/main/ui/onboarding/questions.cljs
msgid "questions.student-teacher" msgid "questions.student-teacher"
msgstr "Student or teacher" msgstr "Student or teacher"
msgid "workspace.layout_grid.editor.title"
msgstr "Editing grid"

View file

@ -5075,3 +5075,6 @@ msgstr "Marketing"
#: src/app/main/ui/onboarding/questions.cljs #: src/app/main/ui/onboarding/questions.cljs
msgid "questions.student-teacher" msgid "questions.student-teacher"
msgstr "Estudiante o profesorado" msgstr "Estudiante o profesorado"
msgid "workspace.layout_grid.editor.title"
msgstr "Editando rejilla"