mirror of
https://github.com/penpot/penpot.git
synced 2025-02-13 10:38:13 -05:00
✨ Support rotated UI
This commit is contained in:
parent
b83c35b0dd
commit
03c64303f5
3 changed files with 102 additions and 82 deletions
|
@ -242,7 +242,6 @@
|
|||
children (->> (cph/get-immediate-children objects (:id frame))
|
||||
(remove :hidden)
|
||||
(map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %)))
|
||||
layout-data (ld/calc-layout-data frame children (:points frame))
|
||||
position (gmt/transform-point-center position (gco/center-shape frame) (:transform-inverse frame))]
|
||||
layout-data (ld/calc-layout-data frame children (:points frame))]
|
||||
|
||||
(get-position-grid-coord layout-data position)))
|
||||
|
|
|
@ -160,6 +160,7 @@
|
|||
(fn [event]
|
||||
(when (and (nil? selrect)
|
||||
(or (dom/class? (dom/get-target event) "viewport-controls")
|
||||
(dom/child? (dom/get-target event) (dom/query ".viewport-controls"))
|
||||
(dom/class? (dom/get-target event) "viewport-selrect")))
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
|
@ -233,11 +234,11 @@
|
|||
(if workspace-read-only?
|
||||
(dom/prevent-default event)
|
||||
(when (or (dom/class? (dom/get-target event) "viewport-controls")
|
||||
(dom/class? (dom/get-target event) "viewport-selrect"))
|
||||
(dom/prevent-default event)
|
||||
|
||||
(dom/child? (dom/get-target event) (dom/query ".viewport-controls"))
|
||||
(dom/class? (dom/get-target event) "viewport-selrect")
|
||||
workspace-read-only?)
|
||||
(let [position (dom/get-client-position event)]
|
||||
;; Delayed callback because we need to wait to the previous context menu to be closed
|
||||
;; Delayed callback because we need to wait to the previous context menu to be closed
|
||||
(timers/schedule
|
||||
#(st/emit!
|
||||
(if (some? @hover)
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.grid-layout :as gsg]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.math :as mth]
|
||||
|
@ -170,7 +172,10 @@
|
|||
|
||||
direction (unchecked-get props "direction")
|
||||
layout-data (unchecked-get props "layout-data")
|
||||
cursor (if (= direction :row) (cur/scale-ns 0) (cur/scale-ew 0))
|
||||
cursor
|
||||
(if (= direction :row)
|
||||
(cur/scale-ns (:rotation shape))
|
||||
(cur/scale-ew (:rotation shape)))
|
||||
|
||||
handle-drag-position
|
||||
(mf/use-callback
|
||||
|
@ -277,6 +282,8 @@
|
|||
cell-origin (gpo/origin cell-bounds)
|
||||
cell-width (gpo/width-points cell-bounds)
|
||||
cell-height (gpo/height-points cell-bounds)
|
||||
cell-center (gsh/center-points cell-bounds)
|
||||
cell-origin (gpt/transform cell-origin (gmt/transform-in cell-center (:transform-inverse shape)))
|
||||
|
||||
handle-pointer-enter
|
||||
(mf/use-callback
|
||||
|
@ -298,7 +305,8 @@
|
|||
|
||||
[:g.cell-editor
|
||||
[:rect
|
||||
{:class (dom/classnames (css :grid-cell-outline) true
|
||||
{:transform (dm/str (gmt/transform-in cell-center (:transform shape)))
|
||||
:class (dom/classnames (css :grid-cell-outline) true
|
||||
(css :hover) hover?
|
||||
(css :selected) selected?)
|
||||
:x (:x cell-origin)
|
||||
|
@ -323,7 +331,7 @@
|
|||
[:right (+ (:x cell-origin) cell-width (/ -10 zoom)) (:y cell-origin) (/ 20 zoom) cell-height :column]
|
||||
[:bottom (:x cell-origin) (+ (:y cell-origin) cell-height (/ -10 zoom)) cell-width (/ 20 zoom) :row]
|
||||
[:left (+ (:x cell-origin) (/ -10 zoom)) (:y cell-origin) (/ 20 zoom) cell-height :column]]]
|
||||
[:*
|
||||
[:g {:transform (dm/str (gmt/transform-in cell-center (:transform shape)))}
|
||||
(for [[handler x y width height dir] handlers]
|
||||
[:& resize-cell-handler {:key (dm/str "resize-" (d/name handler) "-" (:id cell))
|
||||
:shape shape
|
||||
|
@ -402,6 +410,7 @@
|
|||
|
||||
(let [shape (unchecked-get props "shape")
|
||||
index (unchecked-get props "index")
|
||||
last? (unchecked-get props "last?")
|
||||
track-before (unchecked-get props "track-before")
|
||||
track-after (unchecked-get props "track-after")
|
||||
snap-pixel? (unchecked-get props "snap-pixel?")
|
||||
|
@ -411,35 +420,54 @@
|
|||
type (unchecked-get props "type")
|
||||
zoom (unchecked-get props "zoom")
|
||||
|
||||
bounds (:points shape)
|
||||
hv #(gpo/start-hv bounds %)
|
||||
vv #(gpo/start-vv bounds %)
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps shape)
|
||||
|
||||
{:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]}
|
||||
(use-resize-track type shape index track-before track-after zoom snap-pixel?)
|
||||
|
||||
[x y width height]
|
||||
[width height]
|
||||
(if (= type :column)
|
||||
[(- (: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))
|
||||
[(max layout-gap-col (/ 16 zoom))
|
||||
(+ row-total-size row-total-gap)]
|
||||
|
||||
[(: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))])]
|
||||
[(+ column-total-size column-total-gap)
|
||||
(max layout-gap-row (/ 16 zoom))])
|
||||
|
||||
start-p
|
||||
(cond-> start-p
|
||||
(and (= type :column) (= index 0))
|
||||
(gpt/subtract (hv width))
|
||||
|
||||
(and (= type :row) (= index 0))
|
||||
(gpt/subtract (vv height))
|
||||
|
||||
(and (= type :column) (not= index 0) (not last?))
|
||||
(-> (gpt/subtract (hv (/ layout-gap-col 2)))
|
||||
(gpt/subtract (hv (/ width 2))))
|
||||
|
||||
(and (= type :row) (not= index 0) (not last?))
|
||||
(-> (gpt/subtract (vv (/ layout-gap-row 2)))
|
||||
(gpt/subtract (vv (/ height 2)))))]
|
||||
|
||||
[:rect.resize-track-handler
|
||||
{:x x
|
||||
:y y
|
||||
:class (if (= type :column)
|
||||
"resize-ew-0"
|
||||
"resize-ns-0")
|
||||
{:x (:x start-p)
|
||||
:y (:y start-p)
|
||||
:height height
|
||||
:width width
|
||||
:on-pointer-down handle-pointer-down
|
||||
:on-lost-pointer-capture handle-lost-pointer-capture
|
||||
:on-pointer-move handle-pointer-move
|
||||
:style {:fill "transparent"}}]))
|
||||
:transform (dm/str (gmt/transform-in start-p (:transform shape)))
|
||||
:style {:fill "transparent"
|
||||
:opacity 0.5
|
||||
:stroke-width 0
|
||||
:cursor (if (= type :column)
|
||||
(cur/resize-ew (:rotation shape))
|
||||
(cur/resize-ns (:rotation shape)))}}]))
|
||||
|
||||
(mf/defc track-marker
|
||||
{::mf/wrap-props false}
|
||||
|
@ -482,9 +510,11 @@
|
|||
:on-lost-pointer-capture handle-lost-pointer-capture
|
||||
:on-pointer-move handle-pointer-move
|
||||
:class (css :grid-track-marker)
|
||||
:transform (dm/str (gmt/transform-in center (:transform shape)))
|
||||
:style {:cursor (if (= type :column)
|
||||
(cur/resize-ew 0)
|
||||
(cur/resize-ns 0))}}
|
||||
(cur/resize-ew (:rotation shape))
|
||||
(cur/resize-ns (:rotation shape)))}}
|
||||
|
||||
[:polygon {:class (css :marker-shape)
|
||||
:points (->> marker-points
|
||||
(map #(dm/fmt "%,%" (:x %) (:y %)))
|
||||
|
@ -493,7 +523,7 @@
|
|||
:x text-x
|
||||
:y text-y
|
||||
:width (/ 26.26 zoom)
|
||||
:height (/ 32 zoom)
|
||||
:height (/ 36 zoom)
|
||||
:text-anchor "middle"
|
||||
:dominant-baseline "middle"}
|
||||
(dm/str value)]]))
|
||||
|
@ -512,38 +542,28 @@
|
|||
|
||||
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)
|
||||
|
||||
hpt (gpo/project-point bounds :h start-p)
|
||||
vpt (gpo/project-point bounds :v start-p)
|
||||
|
||||
marker-p
|
||||
(if (= type :column)
|
||||
(-> origin
|
||||
(gpt/add (hv (:x relative)))
|
||||
(-> hpt
|
||||
(gpt/subtract (vv (/ 20 zoom)))
|
||||
(cond-> (not= index 0)
|
||||
(gpt/subtract (hv (/ layout-gap-col 2)))))
|
||||
(-> origin
|
||||
(gpt/add (vv (:y relative)))
|
||||
(-> vpt
|
||||
(gpt/subtract (hv (/ 20 zoom)))
|
||||
(cond-> (not= index 0)
|
||||
(gpt/subtract (vv (/ layout-gap-row 2))))))
|
||||
|
||||
text-p
|
||||
(if (= type :column)
|
||||
(-> origin
|
||||
(gpt/add (hv (:x relative)))
|
||||
(gpt/subtract (vv (/ 36 zoom))))
|
||||
(-> 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)))
|
||||
(gpt/subtract (vv (/ 18 zoom)))))
|
||||
text-p (if (= type :column) hpt vpt)
|
||||
|
||||
handle-blur-track-input
|
||||
(mf/use-callback
|
||||
|
@ -585,8 +605,8 @@
|
|||
track-list-prop (if (= type :column) :column-tracks :row-tracks)
|
||||
[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) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)]
|
||||
[(- (:x text-p) (max 0 (:size track-data))) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)])
|
||||
|
||||
track-before (get-in layout-data [track-list-prop (dec index)])]
|
||||
|
||||
|
@ -596,17 +616,9 @@
|
|||
(dom/set-value! (mf/ref-val track-input-ref) (format-size track-data))))
|
||||
|
||||
[: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))))}
|
||||
[:g {:transform (if (= type :column)
|
||||
(dm/str (gmt/transform-in text-p (:transform shape)))
|
||||
(dm/str (gmt/transform-in text-p (gmt/rotate (:transform shape) -90))))}
|
||||
[:foreignObject {:x text-x :y text-y :width text-width :height text-height}
|
||||
[:input
|
||||
{:ref track-input-ref
|
||||
|
@ -617,6 +629,18 @@
|
|||
:on-key-down handle-keydown-track-input
|
||||
:on-blur handle-blur-track-input}]]]
|
||||
|
||||
[: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}]]
|
||||
|
||||
[:& resize-track-handler
|
||||
{:index index
|
||||
:layout-data layout-data
|
||||
|
@ -668,8 +692,6 @@
|
|||
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}
|
||||
(mf/use-memo
|
||||
(mf/deps shape children)
|
||||
|
@ -705,16 +727,18 @@
|
|||
[:& grid-editor-frame {:zoom zoom
|
||||
:bounds bounds}]
|
||||
(let [start-p (-> origin (gpt/add (hv width)))]
|
||||
[:& plus-btn {:start-p start-p
|
||||
:zoom zoom
|
||||
:type :column
|
||||
:on-click handle-add-column}])
|
||||
[:g {:transform (dm/str (gmt/transform-in start-p (:transform shape)))}
|
||||
[:& plus-btn {:start-p start-p
|
||||
:zoom zoom
|
||||
:type :column
|
||||
:on-click handle-add-column}]])
|
||||
|
||||
(let [start-p (-> origin (gpt/add (vv height)))]
|
||||
[:& plus-btn {:start-p start-p
|
||||
:zoom zoom
|
||||
:type :row
|
||||
:on-click handle-add-row}])
|
||||
[:g {:transform (dm/str (gmt/transform-in start-p (:transform shape)))}
|
||||
[:& plus-btn {:start-p start-p
|
||||
:zoom zoom
|
||||
:type :row
|
||||
:on-click handle-add-row}]])
|
||||
|
||||
(for [[idx column-data] (d/enumerate column-tracks)]
|
||||
[:& track {:key (dm/str "column-track-" idx)
|
||||
|
@ -729,12 +753,10 @@
|
|||
;; Last track resize handler
|
||||
(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))))]
|
||||
start-p (:start-p last-track)
|
||||
end-p (gpt/add start-p (hv (:size last-track)))
|
||||
marker-p (-> (gpo/project-point bounds :h end-p)
|
||||
(gpt/subtract (vv (/ 20 zoom))))]
|
||||
[:g.track
|
||||
[:& track-marker {:center marker-p
|
||||
:index (count column-tracks)
|
||||
|
@ -746,10 +768,11 @@
|
|||
:zoom zoom}]
|
||||
[:& resize-track-handler
|
||||
{:index (count column-tracks)
|
||||
:last? true
|
||||
:shape shape
|
||||
:layout-data layout-data
|
||||
:snap-pixel? snap-pixel?
|
||||
:start-p (-> start-p (gpt/add (hv (+ layout-gap-col (:size last-track)))))
|
||||
:start-p end-p
|
||||
:type :column
|
||||
:track-before (last column-tracks)
|
||||
:zoom zoom}]]))
|
||||
|
@ -766,13 +789,10 @@
|
|||
|
||||
(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))))]
|
||||
start-p (:start-p last-track)
|
||||
end-p (gpt/add start-p (vv (:size last-track)))
|
||||
marker-p (-> (gpo/project-point bounds :v end-p)
|
||||
(gpt/subtract (hv (/ 20 zoom))))]
|
||||
[:g.track
|
||||
[:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))}
|
||||
[:& track-marker {:center marker-p
|
||||
|
@ -785,10 +805,10 @@
|
|||
:zoom zoom}]]
|
||||
[:& resize-track-handler
|
||||
{:index (count row-tracks)
|
||||
:last? true
|
||||
:shape shape
|
||||
:layout-data layout-data
|
||||
:start-p (-> start-p
|
||||
(gpt/add (vv (+ layout-gap-row (:size last-track)))))
|
||||
:start-p end-p
|
||||
:type :row
|
||||
:track-before (last row-tracks)
|
||||
:snap-pixel? snap-pixel?
|
||||
|
|
Loading…
Add table
Reference in a new issue