mirror of
https://github.com/penpot/penpot.git
synced 2025-04-01 09:31:26 -05:00
🐛 Fixed issues with text selection and edition
This commit is contained in:
parent
40e54dbbd4
commit
86936a66e0
9 changed files with 164 additions and 109 deletions
|
@ -50,7 +50,7 @@
|
|||
(when (and wrapper
|
||||
(not allow-click-outside)
|
||||
(not (.contains wrapper current))
|
||||
(not (= type (keyword (.getAttribute current "data-allow-click-modal")))))
|
||||
(not (= type (keyword (dom/get-data current "allow-click-modal")))))
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (dm/hide)))))
|
||||
|
|
|
@ -52,21 +52,24 @@
|
|||
drawing? @refs/selected-drawing-tool
|
||||
button (.-which (.-nativeEvent event))
|
||||
shift? (kbd/shift? event)
|
||||
ctrl? (kbd/ctrl? event)]
|
||||
(dom/prevent-default event)
|
||||
(when-not blocked
|
||||
ctrl? (kbd/ctrl? event)
|
||||
|
||||
allow-click? (and (not blocked)
|
||||
(not drawing?)
|
||||
(not ctrl?)
|
||||
(not edition))]
|
||||
|
||||
(when (and (= button 1) allow-click?)
|
||||
(cond
|
||||
(or (not= 1 button) drawing? ctrl? edition)
|
||||
nil
|
||||
|
||||
(= type :frame)
|
||||
(when selected?
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dw/start-move-selected))))
|
||||
|
||||
:else
|
||||
(and (= type :frame) selected?)
|
||||
(do
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dw/start-move-selected)))
|
||||
|
||||
(not= type :frame)
|
||||
(do
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
|
||||
(let [toggle-selected? (and selected? shift?)
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
[app.main.ui.measurements :as msr]
|
||||
[app.main.ui.workspace.shapes.path.editor :refer [path-editor]]))
|
||||
|
||||
(def rotation-handler-size 25)
|
||||
(def rotation-handler-size 20)
|
||||
(def resize-point-radius 4)
|
||||
(def resize-point-circle-radius 10)
|
||||
(def resize-point-rect-size 8)
|
||||
|
@ -43,6 +43,7 @@
|
|||
(def selection-rect-color-component "#00E0FF")
|
||||
(def selection-rect-width 1)
|
||||
(def min-selrect-side 10)
|
||||
(def small-selrect-side 30)
|
||||
|
||||
(mf/defc selection-rect [{:keys [transform rect zoom color]}]
|
||||
(when rect
|
||||
|
@ -57,66 +58,78 @@
|
|||
:stroke-width (/ selection-rect-width zoom)
|
||||
:fill "transparent"}}])))
|
||||
|
||||
(defn- handlers-for-selection [{:keys [x y width height]}]
|
||||
(->>
|
||||
[ ;; TOP-LEFT
|
||||
{:type :rotation
|
||||
:position :top-left
|
||||
:props {:cx x :cy y}}
|
||||
(defn- handlers-for-selection [{:keys [x y width height]} {:keys [type]} zoom]
|
||||
(let [zoom-width (* width zoom)
|
||||
zoom-height (* height zoom)
|
||||
|
||||
(when (and (> width min-selrect-side) (> height min-selrect-side))
|
||||
{:type :resize-point
|
||||
align (when (or (<= zoom-width small-selrect-side)
|
||||
(<= zoom-height small-selrect-side))
|
||||
:outside)
|
||||
show-resize-point? (or (not= type :path)
|
||||
(and
|
||||
(> zoom-width min-selrect-side)
|
||||
(> zoom-height min-selrect-side)))
|
||||
min-side-top? (or (not= type :path) (> zoom-height min-selrect-side))
|
||||
min-side-side? (or (not= type :path) (> zoom-width min-selrect-side))]
|
||||
(->>
|
||||
[ ;; TOP-LEFT
|
||||
{:type :rotation
|
||||
:position :top-left
|
||||
:props {:cx x :cy y}})
|
||||
:props {:cx x :cy y}}
|
||||
|
||||
{:type :rotation
|
||||
:position :top-right
|
||||
:props {:cx (+ x width) :cy y}}
|
||||
(when show-resize-point?
|
||||
{:type :resize-point
|
||||
:position :top-left
|
||||
:props {:cx x :cy y :align align}})
|
||||
|
||||
(when (and (> width min-selrect-side) (> height min-selrect-side))
|
||||
{:type :resize-point
|
||||
{:type :rotation
|
||||
:position :top-right
|
||||
:props {:cx (+ x width) :cy y}})
|
||||
:props {:cx (+ x width) :cy y}}
|
||||
|
||||
{:type :rotation
|
||||
:position :bottom-right
|
||||
:props {:cx (+ x width) :cy (+ y height)}}
|
||||
(when show-resize-point?
|
||||
{:type :resize-point
|
||||
:position :top-right
|
||||
:props {:cx (+ x width) :cy y :align align}})
|
||||
|
||||
(when (and (> width min-selrect-side) (> height min-selrect-side))
|
||||
{:type :resize-point
|
||||
{:type :rotation
|
||||
:position :bottom-right
|
||||
:props {:cx (+ x width) :cy (+ y height)}})
|
||||
:props {:cx (+ x width) :cy (+ y height)}}
|
||||
|
||||
{:type :rotation
|
||||
:position :bottom-left
|
||||
:props {:cx x :cy (+ y height)}}
|
||||
(when show-resize-point?
|
||||
{:type :resize-point
|
||||
:position :bottom-right
|
||||
:props {:cx (+ x width) :cy (+ y height) :align align}})
|
||||
|
||||
(when (and (> width min-selrect-side) (> height min-selrect-side))
|
||||
{:type :resize-point
|
||||
{:type :rotation
|
||||
:position :bottom-left
|
||||
:props {:cx x :cy (+ y height)}})
|
||||
:props {:cx x :cy (+ y height)}}
|
||||
|
||||
(when (> height min-selrect-side)
|
||||
{:type :resize-side
|
||||
:position :top
|
||||
:props {:x x :y y :length width :angle 0 }})
|
||||
(when show-resize-point?
|
||||
{:type :resize-point
|
||||
:position :bottom-left
|
||||
:props {:cx x :cy (+ y height) :align align}})
|
||||
|
||||
(when (> width min-selrect-side)
|
||||
{:type :resize-side
|
||||
:position :right
|
||||
:props {:x (+ x width) :y y :length height :angle 90 }})
|
||||
(when min-side-top?
|
||||
{:type :resize-side
|
||||
:position :top
|
||||
:props {:x x :y y :length width :angle 0 :align align}})
|
||||
|
||||
(when (> height min-selrect-side)
|
||||
{:type :resize-side
|
||||
:position :bottom
|
||||
:props {:x (+ x width) :y (+ y height) :length width :angle 180 }})
|
||||
(when min-side-side?
|
||||
{:type :resize-side
|
||||
:position :right
|
||||
:props {:x (+ x width) :y y :length height :angle 90 :align align}})
|
||||
|
||||
(when (> width min-selrect-side)
|
||||
{:type :resize-side
|
||||
:position :left
|
||||
:props {:x x :y (+ y height) :length height :angle 270 }})]
|
||||
(when min-side-top?
|
||||
{:type :resize-side
|
||||
:position :bottom
|
||||
:props {:x (+ x width) :y (+ y height) :length width :angle 180 :align align}})
|
||||
|
||||
(filterv (comp not nil?))))
|
||||
(when min-side-side?
|
||||
{:type :resize-side
|
||||
:position :left
|
||||
:props {:x x :y (+ y height) :length height :angle 270 :align align}})]
|
||||
|
||||
(filterv (comp not nil?)))))
|
||||
|
||||
(mf/defc rotation-handler [{:keys [cx cy transform position rotation zoom on-rotate]}]
|
||||
(let [size (/ rotation-handler-size zoom)
|
||||
|
@ -137,13 +150,11 @@
|
|||
:on-mouse-down on-rotate}]))
|
||||
|
||||
(mf/defc resize-point-handler
|
||||
[{:keys [cx cy zoom position on-resize transform rotation color overflow-text]}]
|
||||
(let [{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)
|
||||
rot-square (case position
|
||||
:top-left 0
|
||||
:top-right 90
|
||||
:bottom-right 180
|
||||
:bottom-left 270)]
|
||||
[{:keys [cx cy zoom position on-resize transform rotation color overflow-text align]}]
|
||||
(let [cursor (if (#{:top-left :bottom-right} position)
|
||||
(cur/resize-nesw rotation) (cur/resize-nwse rotation))
|
||||
{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)]
|
||||
|
||||
[:g.resize-handler
|
||||
[:circle {:r (/ resize-point-radius zoom)
|
||||
:style {:fillOpacity "1"
|
||||
|
@ -154,24 +165,53 @@
|
|||
:cx cx'
|
||||
:cy cy'}]
|
||||
|
||||
[:circle {:on-mouse-down #(on-resize {:x cx' :y cy'} %)
|
||||
:r (/ resize-point-circle-radius zoom)
|
||||
:fill (if (debug? :resize-handler) "red" "transparent")
|
||||
:cx cx'
|
||||
:cy cy'
|
||||
:style {:cursor (if (#{:top-left :bottom-right} position)
|
||||
(cur/resize-nesw rotation) (cur/resize-nwse rotation))}}]
|
||||
]))
|
||||
(if (= align :outside)
|
||||
(let [resize-point-circle-radius (/ resize-point-circle-radius zoom)
|
||||
offset-x (if (#{:top-right :bottom-right} position) 0 (- resize-point-circle-radius))
|
||||
offset-y (if (#{:bottom-left :bottom-right} position) 0 (- resize-point-circle-radius))
|
||||
cx (+ cx offset-x)
|
||||
cy (+ cy offset-y)
|
||||
{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)]
|
||||
[:rect {:x cx'
|
||||
:y cy'
|
||||
:width resize-point-circle-radius
|
||||
:height resize-point-circle-radius
|
||||
:transform (str/fmt "rotate(%s, %s, %s)" rotation cx' cy')
|
||||
:style {:fill (if (debug? :resize-handler) "red" "transparent")
|
||||
:cursor cursor}
|
||||
:on-mouse-down #(on-resize {:x cx' :y cy'} %)}])
|
||||
|
||||
(mf/defc resize-side-handler [{:keys [x y length angle zoom position rotation transform on-resize]}]
|
||||
(let [rot-square (case position
|
||||
:top-left 0
|
||||
:top-right 90
|
||||
:bottom-right 180
|
||||
:bottom-left 270)]
|
||||
[:circle {:on-mouse-down #(on-resize {:x cx' :y cy'} %)
|
||||
:r (/ resize-point-circle-radius zoom)
|
||||
:cx cx'
|
||||
:cy cy'
|
||||
:style {:fill (if (debug? :resize-handler) "red" "transparent")
|
||||
:cursor cursor}}])
|
||||
)]))
|
||||
|
||||
(mf/defc resize-side-handler
|
||||
"The side handler is always rendered horizontaly and then rotated"
|
||||
[{:keys [x y length align angle zoom position rotation transform on-resize]}]
|
||||
(let [res-point (if (#{:top :bottom} position)
|
||||
{:y y}
|
||||
{:x x})
|
||||
target-length (max 0 (- length (/ (* resize-point-rect-size 2) zoom)))
|
||||
|
||||
width (if (< target-length 6) length target-length)
|
||||
height (/ resize-side-height zoom)]
|
||||
[:rect {:x (+ x (/ (- length width) 2))
|
||||
:y (- y (/ height 2))
|
||||
height (/ resize-side-height zoom)
|
||||
|
||||
offset-x (/ (- length width) 2)
|
||||
offset-y (if (= align :outside) (- height) (- (/ height 2)))
|
||||
|
||||
target-x (+ x offset-x)
|
||||
target-y (+ y offset-y)]
|
||||
[:rect {:x target-x
|
||||
:y target-y
|
||||
:width width
|
||||
:height height
|
||||
:transform (gmt/multiply transform
|
||||
|
@ -217,7 +257,7 @@
|
|||
[:& outline {:shape shape :color color}]
|
||||
|
||||
;; Handlers
|
||||
(for [{:keys [type position props]} (handlers-for-selection selrect)]
|
||||
(for [{:keys [type position props]} (handlers-for-selection selrect shape zoom)]
|
||||
(let [common-props {:key (str (name type) "-" (name position))
|
||||
:zoom zoom
|
||||
:position position
|
||||
|
|
|
@ -43,20 +43,23 @@
|
|||
(recur (first ids) (rest ids))
|
||||
false))))))
|
||||
|
||||
(defn use-select-shape [{:keys [id]}]
|
||||
(defn use-select-shape [{:keys [id]} edition]
|
||||
(mf/use-callback
|
||||
(mf/deps id)
|
||||
(mf/deps id edition)
|
||||
(fn [event]
|
||||
(let [selected @refs/selected-shapes
|
||||
selected? (contains? selected id)]
|
||||
(dom/prevent-default event)
|
||||
(if selected?
|
||||
(when (kbd/shift? event)
|
||||
(st/emit! (dw/select-shape id true)))
|
||||
(do
|
||||
(when-not (or (empty? selected) (kbd/shift? event))
|
||||
(st/emit! (dw/deselect-all)))
|
||||
(st/emit! (dw/select-shape id))))))))
|
||||
(when (not edition)
|
||||
(let [selected @refs/selected-shapes
|
||||
selected? (contains? selected id)
|
||||
shift? (kbd/shift? event)]
|
||||
(cond
|
||||
(and selected? shift?)
|
||||
(st/emit! (dw/select-shape id true))
|
||||
|
||||
(and (not (empty? selected)) (not shift?))
|
||||
(st/emit! (dw/deselect-all) (dw/select-shape id))
|
||||
|
||||
(not selected?)
|
||||
(st/emit! (dw/select-shape id))))))))
|
||||
|
||||
;; Ensure that the label has always the same font
|
||||
;; size, regardless of zoom
|
||||
|
@ -72,8 +75,9 @@
|
|||
[{:keys [frame]}]
|
||||
(let [{:keys [width x y]} frame
|
||||
zoom (mf/deref refs/selected-zoom)
|
||||
edition (mf/deref refs/selected-edition)
|
||||
label-pos (gpt/point x (- y (/ 10 zoom)))
|
||||
handle-click (use-select-shape frame)
|
||||
handle-click (use-select-shape frame edition)
|
||||
handle-pointer-enter (we/use-pointer-enter frame)
|
||||
handle-pointer-leave (we/use-pointer-leave frame)]
|
||||
[:text {:x 0
|
||||
|
@ -123,6 +127,7 @@
|
|||
(let [shape (unchecked-get props "shape")
|
||||
objects (unchecked-get props "objects")
|
||||
ghost? (mf/use-ctx muc/ghost-ctx)
|
||||
edition (mf/deref refs/selected-edition)
|
||||
|
||||
moving-iref (mf/use-memo (mf/deps (:id shape))
|
||||
#(make-is-moving-ref (:id shape)))
|
||||
|
@ -137,7 +142,7 @@
|
|||
ds-modifier (get-in shape [:modifiers :displacement])
|
||||
|
||||
handle-context-menu (we/use-context-menu shape)
|
||||
handle-double-click (use-select-shape shape)
|
||||
handle-double-click (use-select-shape shape edition)
|
||||
handle-mouse-down (we/use-mouse-down shape)
|
||||
|
||||
hide-moving? (and (not ghost?) moving?)]
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
(and (= edit-mode :move) selected?)
|
||||
(st/emit! (drp/deselect-node position))))))
|
||||
|
||||
|
||||
on-mouse-down
|
||||
(fn [event]
|
||||
(when-not last-p?
|
||||
|
@ -60,6 +61,7 @@
|
|||
|
||||
(and (= edit-mode :draw) (not start-path?))
|
||||
(st/emit! (drp/close-path-drag-start position))))))]
|
||||
|
||||
[:g.path-point
|
||||
[:circle.path-point
|
||||
{:cx x
|
||||
|
@ -182,11 +184,18 @@
|
|||
editor-dom (mf/ref-val editor-ref)]
|
||||
(when-not (or (.contains editor-dom current)
|
||||
(dom/class? current "viewport-actions-entry"))
|
||||
(st/emit! (drp/deselect-all)))))]
|
||||
(st/emit! (drp/deselect-all)))))
|
||||
|
||||
handle-double-click-outside
|
||||
(fn [event]
|
||||
(when (= edit-mode :move)
|
||||
(st/emit! :interrupt)))]
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps edit-mode)
|
||||
(fn []
|
||||
(let [keys [(events/listen js/document EventType.CLICK handle-click-outside)]]
|
||||
(let [keys [(events/listen (dom/get-root) EventType.CLICK handle-click-outside)
|
||||
(events/listen (dom/get-root) EventType.DBLCLICK handle-double-click-outside)]]
|
||||
#(doseq [key keys]
|
||||
(events/unlistenByKey key)))))
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@
|
|||
render-editor (mf/use-state false)
|
||||
|
||||
edition? (= edition id)
|
||||
|
||||
embed-resources? (mf/use-ctx muc/embed-ctx)
|
||||
|
||||
handle-mouse-down (we/use-mouse-down shape)
|
||||
|
@ -116,7 +115,7 @@
|
|||
:shape shape
|
||||
:selected? selected?
|
||||
:grow-type (:grow-type shape)}]]
|
||||
(when edition?
|
||||
(when (and (not ghost?) edition?)
|
||||
[:& editor/text-shape-edit {:key (str "editor" (:id shape))
|
||||
:shape shape}])
|
||||
|
||||
|
|
|
@ -167,19 +167,18 @@
|
|||
|
||||
on-click-outside
|
||||
(fn [event]
|
||||
(let [options (dom/get-element-by-class "element-options")
|
||||
(let [target (dom/get-target event)
|
||||
options (dom/get-element-by-class "element-options")
|
||||
assets (dom/get-element-by-class "assets-bar")
|
||||
cpicker (dom/get-element-by-class "colorpicker-tooltip")
|
||||
self (mf/ref-val self-ref)
|
||||
target (dom/get-target event)
|
||||
selecting? (mf/ref-val selecting-ref)]
|
||||
|
||||
(when-not (or (and options (.contains options target))
|
||||
(and assets (.contains assets target))
|
||||
(and self (.contains self target))
|
||||
(and cpicker (.contains cpicker target)))
|
||||
(do
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
|
||||
(if selecting?
|
||||
(mf/set-ref-val! selecting-ref false)
|
||||
|
@ -203,16 +202,16 @@
|
|||
|
||||
on-mount
|
||||
(fn []
|
||||
(let [lkey1 (events/listen js/document EventType.CLICK on-click-outside)
|
||||
lkey2 (events/listen js/document EventType.KEYUP on-key-up)]
|
||||
(let [keys [(events/listen js/document EventType.CLICK on-click-outside)
|
||||
(events/listen js/document EventType.KEYUP on-key-up)]]
|
||||
(st/emit! (dwt/assign-editor id editor)
|
||||
(dwc/start-undo-transaction))
|
||||
|
||||
#(do
|
||||
(st/emit! (dwt/assign-editor id nil)
|
||||
(dwc/commit-undo-transaction))
|
||||
(events/unlistenByKey lkey1)
|
||||
(events/unlistenByKey lkey2))))
|
||||
(doseq [key keys]
|
||||
(events/unlistenByKey key)))))
|
||||
|
||||
on-focus
|
||||
(fn [event]
|
||||
|
|
|
@ -345,10 +345,7 @@
|
|||
(let [ctrl? (kbd/ctrl? event)
|
||||
shift? (kbd/shift? event)
|
||||
alt? (kbd/alt? event)]
|
||||
(st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?))
|
||||
|
||||
(if (not drawing-path?)
|
||||
(st/emit! dw/clear-edition-mode)))))
|
||||
(st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?)))))
|
||||
|
||||
on-key-down
|
||||
(mf/use-callback
|
||||
|
|
|
@ -253,3 +253,6 @@
|
|||
|
||||
(defn active? [node]
|
||||
(= (.-activeElement js/document) node))
|
||||
|
||||
(defn get-data [^js node ^string attr]
|
||||
(.getAttribute node (str "data-" attr)))
|
||||
|
|
Loading…
Add table
Reference in a new issue