mirror of
https://github.com/penpot/penpot.git
synced 2025-05-31 11:56:12 +02:00
🐛 Fix positioning of comment thread draft (#6357)
* 🐛 Fix positioning of comment thread draft * 📚 Update changelog
This commit is contained in:
parent
9c4896d72b
commit
b958dcb891
5 changed files with 99 additions and 49 deletions
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :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 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 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)
|
- Fix position problems cutting-pasting a component [Taiga #10677](https://tree.taiga.io/project/penpot/issue/10677)
|
||||||
|
|
|
@ -759,9 +759,29 @@
|
||||||
[:> comment-form-buttons* {:on-submit on-submit*
|
[:> comment-form-buttons* {:on-submit on-submit*
|
||||||
:on-cancel on-cancel
|
:on-cancel on-cancel
|
||||||
:is-disabled disabled?}]]))
|
: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*
|
(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)
|
(let [profile (mf/deref refs/profile)
|
||||||
|
|
||||||
mentions-s (mf/use-memo #(rx/subject))
|
mentions-s (mf/use-memo #(rx/subject))
|
||||||
|
@ -770,9 +790,14 @@
|
||||||
(some? position-modifier)
|
(some? position-modifier)
|
||||||
(gpt/transform position-modifier))
|
(gpt/transform position-modifier))
|
||||||
content (:content draft)
|
content (:content draft)
|
||||||
|
bubble-margin (gpt/point 0 0)
|
||||||
|
|
||||||
pos-x (* (:x position) zoom)
|
pos (offset-position position viewport zoom bubble-margin)
|
||||||
pos-y (* (:y position) zoom)
|
|
||||||
|
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)
|
disabled? (or (blank-content? content)
|
||||||
(exceeds-length? content))
|
(exceeds-length? content))
|
||||||
|
@ -799,33 +824,39 @@
|
||||||
(on-submit draft)))]
|
(on-submit draft)))]
|
||||||
|
|
||||||
[:> (mf/provider mentions-context) {:value mentions-s}
|
[:> (mf/provider mentions-context) {:value mentions-s}
|
||||||
[:div
|
[:div {:class (stl/css-case :floating-thread-draft-wrapper true
|
||||||
{:class (stl/css :floating-preview-wrapper)
|
:left (= (:h-dir pos) :left)
|
||||||
:data-testid "floating-thread-bubble"
|
:top (= (:v-dir pos) :top))
|
||||||
:style {:top (str pos-y "px")
|
:style {:top (str pos-y "px")
|
||||||
:left (str pos-x "px")}
|
:left (str pos-x "px")}}
|
||||||
:on-click dom/stop-propagation}
|
[:div
|
||||||
[:> comment-avatar* {:class (stl/css :avatar-lg)
|
{:data-testid "floating-thread-bubble"
|
||||||
:image (cfg/resolve-profile-photo-url profile)}]]
|
:style {:top (str pos-y "px")
|
||||||
[:div {:class (stl/css :floating-thread-wrapper :cursor-auto)
|
:left (str pos-x "px")}
|
||||||
:style {:top (str (- pos-y 24) "px")
|
:on-click dom/stop-propagation}
|
||||||
:left (str (+ pos-x 28) "px")}
|
[:> comment-avatar* {:class (stl/css :avatar-lg)
|
||||||
:on-click dom/stop-propagation}
|
:image (cfg/resolve-profile-photo-url profile)}]]
|
||||||
[:div {:class (stl/css :form)}
|
[:div {:class (stl/css :floating-thread-draft-inner-wrapper
|
||||||
[:> comment-input*
|
:cursor-auto)
|
||||||
{:placeholder (tr "labels.write-new-comment")
|
:style {:top (str (- pos-y 24) "px")
|
||||||
:value (or content "")
|
:left (str (+ pos-x 28) "px")}
|
||||||
:autofocus true
|
|
||||||
:on-esc on-esc
|
:on-click dom/stop-propagation}
|
||||||
:on-change on-change
|
[:div {:class (stl/css :form)}
|
||||||
:on-ctrl-enter on-submit*}]
|
[:> comment-input*
|
||||||
(when (exceeds-length? content)
|
{:placeholder (tr "labels.write-new-comment")
|
||||||
[:div {:class (stl/css :error-text)}
|
:value (or content "")
|
||||||
(tr "errors.character-limit-exceeded")])
|
:autofocus true
|
||||||
[:> comment-form-buttons* {:on-submit on-submit*
|
:on-esc on-esc
|
||||||
:on-cancel on-esc
|
:on-change on-change
|
||||||
:is-disabled disabled?}]]
|
:on-ctrl-enter on-submit*}]
|
||||||
[:> mentions-panel*]]]))
|
(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/defc comment-floating-thread-header*
|
||||||
{::mf/private true}
|
{::mf/private true}
|
||||||
|
@ -977,26 +1008,7 @@
|
||||||
[thread-id]
|
[thread-id]
|
||||||
(l/derived (l/in [:comments thread-id]) st/state))
|
(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/defc comment-floating-thread*
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
|
|
|
@ -160,6 +160,40 @@
|
||||||
z-index: initial;
|
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 {
|
.floating-preview-displacement {
|
||||||
margin-left: calc(-1 * ($s-12 + $s-2));
|
margin-left: calc(-1 * ($s-12 + $s-2));
|
||||||
margin-top: calc(-1 * ($s-8 + $s-2));
|
margin-top: calc(-1 * ($s-8 + $s-2));
|
||||||
|
@ -177,6 +211,7 @@
|
||||||
border: $s-2 solid var(--modal-border-color);
|
border: $s-2 solid var(--modal-border-color);
|
||||||
background-color: var(--comment-modal-background-color);
|
background-color: var(--comment-modal-background-color);
|
||||||
max-height: var(--comment-height);
|
max-height: var(--comment-height);
|
||||||
|
|
||||||
--translate-x: 0%;
|
--translate-x: 0%;
|
||||||
--translate-y: 0%;
|
--translate-y: 0%;
|
||||||
transform: translate(var(--translate-x), var(--translate-y));
|
transform: translate(var(--translate-x), var(--translate-y));
|
||||||
|
|
|
@ -231,6 +231,7 @@
|
||||||
:position-modifier modifier1
|
:position-modifier modifier1
|
||||||
:on-cancel on-draft-cancel
|
:on-cancel on-draft-cancel
|
||||||
:on-submit on-draft-submit
|
:on-submit on-draft-submit
|
||||||
|
:viewport nil
|
||||||
:zoom zoom}])]]]))
|
:zoom zoom}])]]]))
|
||||||
|
|
||||||
(mf/defc comments-sidebar*
|
(mf/defc comments-sidebar*
|
||||||
|
|
|
@ -86,4 +86,5 @@
|
||||||
{:draft draft
|
{:draft draft
|
||||||
:on-cancel on-draft-cancel
|
:on-cancel on-draft-cancel
|
||||||
:on-submit on-draft-submit
|
:on-submit on-draft-submit
|
||||||
|
:viewport viewport
|
||||||
:zoom zoom}])]]]))
|
:zoom zoom}])]]]))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue