diff --git a/CHANGES.md b/CHANGES.md index 5b28dd8bc..3b24f0b8c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ ### :bug: Bugs fixed +- Fix positioning of comment drafts when near the right / bottom edges of viewport [Taiga #10534](https://tree.taiga.io/project/penpot/issue/10534) - Fix path having a wrong selrect [Taiga #10257](https://tree.taiga.io/project/penpot/issue/10257) - Fix SVG `stroke-linecap` property when importing SVGs [Taiga #9489](https://tree.taiga.io/project/penpot/issue/9489) - Fix position problems cutting-pasting a component [Taiga #10677](https://tree.taiga.io/project/penpot/issue/10677) diff --git a/frontend/src/app/main/ui/comments.cljs b/frontend/src/app/main/ui/comments.cljs index 5b384d8c5..479c810cd 100644 --- a/frontend/src/app/main/ui/comments.cljs +++ b/frontend/src/app/main/ui/comments.cljs @@ -759,9 +759,29 @@ [:> comment-form-buttons* {:on-submit on-submit* :on-cancel on-cancel :is-disabled disabled?}]])) +(defn- offset-position [position viewport zoom bubble-margin] + (let [viewport (or viewport {:offset-x 0 :offset-y 0 :width 0 :height 0}) + base-x (+ (* (:x position) zoom) (:offset-x viewport)) + base-y (+ (* (:y position) zoom) (:offset-y viewport)) + + x (:x position) + y (:y position) + + w (:width viewport) + h (:height viewport) + + comment-width 284 ;; TODO: this is the width set via CSS in an outer container… + ;; We should probably do this in a different way. + + orientation-left? (>= (+ base-x comment-width (:x bubble-margin)) w) + orientation-top? (>= base-y (/ h 2)) + + h-dir (if orientation-left? :left :right) + v-dir (if orientation-top? :top :bottom)] + {:x x :y y :h-dir h-dir :v-dir v-dir})) (mf/defc comment-floating-thread-draft* - [{:keys [draft zoom on-cancel on-submit position-modifier]}] + [{:keys [draft zoom on-cancel on-submit position-modifier viewport]}] (let [profile (mf/deref refs/profile) mentions-s (mf/use-memo #(rx/subject)) @@ -770,9 +790,14 @@ (some? position-modifier) (gpt/transform position-modifier)) content (:content draft) + bubble-margin (gpt/point 0 0) - pos-x (* (:x position) zoom) - pos-y (* (:y position) zoom) + pos (offset-position position viewport zoom bubble-margin) + + margin-x (* (:x bubble-margin) (if (= (:h-dir pos) :left) -1 1)) + margin-y (* (:y bubble-margin) (if (= (:v-dir pos) :top) -1 1)) + pos-x (+ (* (:x pos) zoom) margin-x) + pos-y (- (* (:y pos) zoom) margin-y) disabled? (or (blank-content? content) (exceeds-length? content)) @@ -799,33 +824,39 @@ (on-submit draft)))] [:> (mf/provider mentions-context) {:value mentions-s} - [:div - {:class (stl/css :floating-preview-wrapper) - :data-testid "floating-thread-bubble" - :style {:top (str pos-y "px") - :left (str pos-x "px")} - :on-click dom/stop-propagation} - [:> comment-avatar* {:class (stl/css :avatar-lg) - :image (cfg/resolve-profile-photo-url profile)}]] - [:div {:class (stl/css :floating-thread-wrapper :cursor-auto) - :style {:top (str (- pos-y 24) "px") - :left (str (+ pos-x 28) "px")} - :on-click dom/stop-propagation} - [:div {:class (stl/css :form)} - [:> comment-input* - {:placeholder (tr "labels.write-new-comment") - :value (or content "") - :autofocus true - :on-esc on-esc - :on-change on-change - :on-ctrl-enter on-submit*}] - (when (exceeds-length? content) - [:div {:class (stl/css :error-text)} - (tr "errors.character-limit-exceeded")]) - [:> comment-form-buttons* {:on-submit on-submit* - :on-cancel on-esc - :is-disabled disabled?}]] - [:> mentions-panel*]]])) + [:div {:class (stl/css-case :floating-thread-draft-wrapper true + :left (= (:h-dir pos) :left) + :top (= (:v-dir pos) :top)) + :style {:top (str pos-y "px") + :left (str pos-x "px")}} + [:div + {:data-testid "floating-thread-bubble" + :style {:top (str pos-y "px") + :left (str pos-x "px")} + :on-click dom/stop-propagation} + [:> comment-avatar* {:class (stl/css :avatar-lg) + :image (cfg/resolve-profile-photo-url profile)}]] + [:div {:class (stl/css :floating-thread-draft-inner-wrapper + :cursor-auto) + :style {:top (str (- pos-y 24) "px") + :left (str (+ pos-x 28) "px")} + + :on-click dom/stop-propagation} + [:div {:class (stl/css :form)} + [:> comment-input* + {:placeholder (tr "labels.write-new-comment") + :value (or content "") + :autofocus true + :on-esc on-esc + :on-change on-change + :on-ctrl-enter on-submit*}] + (when (exceeds-length? content) + [:div {:class (stl/css :error-text)} + (tr "errors.character-limit-exceeded")]) + [:> comment-form-buttons* {:on-submit on-submit* + :on-cancel on-esc + :is-disabled disabled?}]] + [:> mentions-panel*]]]])) (mf/defc comment-floating-thread-header* {::mf/private true} @@ -977,26 +1008,7 @@ [thread-id] (l/derived (l/in [:comments thread-id]) st/state)) -(defn- offset-position [position viewport zoom bubble-margin] - (let [viewport (or viewport {:offset-x 0 :offset-y 0 :width 0 :height 0}) - base-x (+ (* (:x position) zoom) (:offset-x viewport)) - base-y (+ (* (:y position) zoom) (:offset-y viewport)) - x (:x position) - y (:y position) - - w (:width viewport) - h (:height viewport) - - comment-width 284 ;; TODO: this is the width set via CSS in an outer container… - ;; We should probably do this in a different way. - - orientation-left? (>= (+ base-x comment-width (:x bubble-margin)) w) - orientation-top? (>= base-y (/ h 2)) - - h-dir (if orientation-left? :left :right) - v-dir (if orientation-top? :top :bottom)] - {:x x :y y :h-dir h-dir :v-dir v-dir})) (mf/defc comment-floating-thread* {::mf/wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/comments.scss b/frontend/src/app/main/ui/comments.scss index 4e4eb4345..8ca32f61b 100644 --- a/frontend/src/app/main/ui/comments.scss +++ b/frontend/src/app/main/ui/comments.scss @@ -160,6 +160,40 @@ z-index: initial; } +.floating-thread-draft-wrapper { + position: absolute; + display: flex; + flex-direction: row; + column-gap: $s-12; + + --translate-x: 0%; + --translate-y: 0%; + transform: translate(var(--translate-x), var(--translate-y)); + + &.left { + --translate-x: -100%; + flex-direction: row-reverse; + } + + &.top { + --translate-y: -100%; + align-items: flex-end; + } +} + +.floating-thread-draft-inner-wrapper { + display: flex; + flex-direction: column; + gap: $s-12; + width: $s-284; + padding: $s-8 $s-12 $s-8 $s-12; + pointer-events: auto; + border-radius: $br-8; + border: $s-2 solid var(--modal-border-color); + background-color: var(--comment-modal-background-color); + max-height: var(--comment-height); +} + .floating-preview-displacement { margin-left: calc(-1 * ($s-12 + $s-2)); margin-top: calc(-1 * ($s-8 + $s-2)); @@ -177,6 +211,7 @@ border: $s-2 solid var(--modal-border-color); background-color: var(--comment-modal-background-color); max-height: var(--comment-height); + --translate-x: 0%; --translate-y: 0%; transform: translate(var(--translate-x), var(--translate-y)); diff --git a/frontend/src/app/main/ui/viewer/comments.cljs b/frontend/src/app/main/ui/viewer/comments.cljs index 96c8d5fef..eb112cb29 100644 --- a/frontend/src/app/main/ui/viewer/comments.cljs +++ b/frontend/src/app/main/ui/viewer/comments.cljs @@ -231,6 +231,7 @@ :position-modifier modifier1 :on-cancel on-draft-cancel :on-submit on-draft-submit + :viewport nil :zoom zoom}])]]])) (mf/defc comments-sidebar* diff --git a/frontend/src/app/main/ui/workspace/viewport/comments.cljs b/frontend/src/app/main/ui/workspace/viewport/comments.cljs index 2b67b9b6a..e9e0a4d0d 100644 --- a/frontend/src/app/main/ui/workspace/viewport/comments.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/comments.cljs @@ -86,4 +86,5 @@ {:draft draft :on-cancel on-draft-cancel :on-submit on-draft-submit + :viewport viewport :zoom zoom}])]]]))