mirror of
https://github.com/penpot/penpot.git
synced 2025-01-24 15:39:50 -05:00
💄 Update comment UI with new design
This commit is contained in:
parent
25c60f3e0f
commit
f4323fd1ac
12 changed files with 999 additions and 255 deletions
|
@ -75,6 +75,12 @@
|
|||
stroke: var(--button-primary-foreground-color-active);
|
||||
}
|
||||
}
|
||||
&:global(.disabled) {
|
||||
background-color: var(--button-background-color-disabled);
|
||||
border: $s-1 solid var(--button-border-color-disabled);
|
||||
color: var(--button-foreground-color-disabled);
|
||||
cursor: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
|
@ -118,6 +124,12 @@
|
|||
stroke: var(--button-secondary-foreground-color-active);
|
||||
}
|
||||
}
|
||||
&:global(.disabled) {
|
||||
background-color: var(--button-background-color-disabled);
|
||||
border: $s-1 solid var(--button-border-color-disabled);
|
||||
color: var(--button-foreground-color-disabled);
|
||||
cursor: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.button-tertiary {
|
||||
|
@ -161,6 +173,12 @@
|
|||
stroke: var(--button-tertiary-foreground-color-active);
|
||||
}
|
||||
}
|
||||
&:global(.disabled) {
|
||||
background-color: var(--button-background-color-disabled);
|
||||
border: $s-1 solid var(--button-border-color-disabled);
|
||||
color: var(--button-foreground-color-disabled);
|
||||
cursor: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.button-radio {
|
||||
|
@ -375,6 +393,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
.checkbox-icon {
|
||||
@include flexCenter;
|
||||
width: $s-16;
|
||||
height: $s-16;
|
||||
min-width: $s-16;
|
||||
min-height: $s-16;
|
||||
border-radius: $br-6;
|
||||
background-color: var(--input-background-color);
|
||||
svg {
|
||||
display: none;
|
||||
}
|
||||
&:global(.checked) {
|
||||
background-color: var(--input-border-color-active);
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
stroke: var(--input-details-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -385,20 +423,7 @@
|
|||
gap: $s-6;
|
||||
cursor: pointer;
|
||||
span {
|
||||
@include flexCenter;
|
||||
width: $s-16;
|
||||
height: $s-16;
|
||||
min-width: $s-16;
|
||||
min-height: $s-16;
|
||||
border-radius: $br-6;
|
||||
background-color: var(--input-background-color);
|
||||
&:global(.checked) {
|
||||
background-color: var(--input-border-color-active);
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
stroke: var(--input-details-color);
|
||||
}
|
||||
}
|
||||
@extend .checkbox-icon;
|
||||
}
|
||||
input {
|
||||
margin: 0;
|
||||
|
@ -505,6 +530,28 @@
|
|||
grid-template-columns: $s-92 1fr;
|
||||
}
|
||||
|
||||
.comment-bubbles {
|
||||
@include titleTipography;
|
||||
@include flexCenter;
|
||||
height: $s-32;
|
||||
width: $s-32;
|
||||
border-radius: $br-circle;
|
||||
background-color: var(--comment-bullet-background-color-rest);
|
||||
border: $s-1 solid var(--comment-bullet-border-color-rest);
|
||||
color: var(--comment-bullet-foreground-color-rest);
|
||||
}
|
||||
|
||||
.resolved-comment-bubble {
|
||||
background-color: var(--comment-bullet-background-color-resolved);
|
||||
border: $s-1 solid var(--comment-bullet-border-color-resolved);
|
||||
color: var(--comment-bullet-foreground-color-resolved);
|
||||
}
|
||||
.unread-comment-bubble {
|
||||
background-color: var(--comment-bullet-background-color-unread);
|
||||
border: $s-1 solid var(--comment-bullet-border-color-unread);
|
||||
color: var(--comment-bullet-foreground-color-unread);
|
||||
}
|
||||
|
||||
// SELECTS AND DROPDOWNS
|
||||
.menu-dropdown {
|
||||
@include menuShadow;
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
--button-background-focus: var(--color-background-secondary);
|
||||
--button-foreground-focus: var(--color-foreground-primary);
|
||||
--button-border-focus: var(--color-accent-primary);
|
||||
--button-foreground-color-disabled: var(--color-background-quaternary);
|
||||
--button-foreground-color-disabled: var(--color-foreground-secondary);
|
||||
--button-background-color-disabled: var(--color-background-primary);
|
||||
--button-border-color-disabled: var(--color-background-quaternary);
|
||||
|
||||
--button-primary-background-color-rest: var(--color-accent-primary);
|
||||
--button-primary-border-color-rest: var(--color-accent-primary);
|
||||
|
@ -228,4 +230,18 @@
|
|||
--colorpicker-details-color: var(--color-background-quaternary);
|
||||
--colorpicker-details-color-selected: var(--color-accent-primary);
|
||||
--colorpicker-handlers-color: var(--color-foreground-primary);
|
||||
|
||||
// COMMENTS
|
||||
--comment-title-color: var(--color-foreground-primary);
|
||||
--comment-subtitle-color: var(--color-foreground-secondary);
|
||||
--comment-bullet-background-color-rest: var(--color-background-quaternary);
|
||||
--comment-bullet-foreground-color-rest: var(--color-foreground-primary);
|
||||
--comment-bullet-border-color-rest: var(--color-background-secondary);
|
||||
--comment-bullet-background-color-unread: var(--color-accent-primary);
|
||||
--comment-bullet-foreground-color-unread: var(--color-background-primary);
|
||||
--comment-bullet-border-color-unread: var(--color-background-secondary);
|
||||
--comment-bullet-background-color-resolved: var(--color-background-primary);
|
||||
--comment-bullet-foreground-color-resolved: var(--color-foreground-secondary);
|
||||
--comment-bullet-border-color-resolved: var(--color-background-quaternary);
|
||||
--comment-modal-background-color: var(--color-background-primary);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.comments
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
|
@ -17,6 +18,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
|
@ -69,8 +71,6 @@
|
|||
;; In webkit browsers the mouseup event will be called after the on-focus causing and unselect
|
||||
(.addEventListener target "mouseup" dom/prevent-default #js {:once true})))))]
|
||||
|
||||
|
||||
|
||||
(mf/use-layout-effect
|
||||
nil
|
||||
(fn []
|
||||
|
@ -90,9 +90,13 @@
|
|||
|
||||
(mf/defc reply-form
|
||||
[{:keys [thread] :as props}]
|
||||
(let [show-buttons? (mf/use-state false)
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
show-buttons? (mf/use-state false)
|
||||
content (mf/use-state "")
|
||||
|
||||
disabled? (or (fm/all-spaces? @content)
|
||||
(str/empty-or-nil? @content))
|
||||
|
||||
on-focus
|
||||
(mf/use-fn
|
||||
#(reset! show-buttons? true))
|
||||
|
@ -116,29 +120,52 @@
|
|||
(fn []
|
||||
(st/emit! (dcm/add-comment thread @content))
|
||||
(on-cancel)))]
|
||||
(if new-css-system
|
||||
[:div {:class (stl/css :reply-form)}
|
||||
[:& resizing-textarea {:value @content
|
||||
:placeholder "Reply"
|
||||
:on-blur on-blur
|
||||
:on-focus on-focus
|
||||
:select-on-focus? false
|
||||
:on-change on-change}]
|
||||
(when (or @show-buttons? (seq @content))
|
||||
[:div {:class (stl/css :buttons-wrapper)}
|
||||
[:input.btn-secondary
|
||||
{:type "button"
|
||||
:class (stl/css :cancel-btn)
|
||||
:value "Cancel"
|
||||
:on-click on-cancel}]
|
||||
[:input
|
||||
{:type "button"
|
||||
:class (stl/css-case :post-btn true
|
||||
:global/disabled disabled?)
|
||||
:value "Post"
|
||||
:on-click on-submit
|
||||
:disabled disabled?}]])]
|
||||
|
||||
[:div.reply-form
|
||||
[:& resizing-textarea {:value @content
|
||||
:placeholder "Reply"
|
||||
:on-blur on-blur
|
||||
:on-focus on-focus
|
||||
:on-change on-change}]
|
||||
(when (or @show-buttons? (seq @content))
|
||||
[:div.buttons
|
||||
[:input.btn-primary
|
||||
{:type "button"
|
||||
:value "Post"
|
||||
:on-click on-submit
|
||||
:disabled (or (fm/all-spaces? @content)
|
||||
(str/empty-or-nil? @content))}]
|
||||
[:input.btn-secondary
|
||||
{:type "button"
|
||||
:value "Cancel"
|
||||
:on-click on-cancel}]])]))
|
||||
|
||||
[:div.reply-form
|
||||
[:& resizing-textarea {:value @content
|
||||
:placeholder "Reply"
|
||||
:on-blur on-blur
|
||||
:on-focus on-focus
|
||||
:on-change on-change}]
|
||||
(when (or @show-buttons? (seq @content))
|
||||
[:div.buttons
|
||||
[:input.btn-primary
|
||||
{:type "button"
|
||||
:value "Post"
|
||||
:on-click on-submit
|
||||
:disabled disabled?}]
|
||||
[:input.btn-secondary
|
||||
{:type "button"
|
||||
:value "Cancel"
|
||||
:on-click on-cancel}]])])))
|
||||
|
||||
(mf/defc draft-thread
|
||||
[{:keys [draft zoom on-cancel on-submit position-modifier]}]
|
||||
(let [position (cond-> (:position draft)
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
position (cond-> (:position draft)
|
||||
(some? position-modifier)
|
||||
(gpt/transform position-modifier))
|
||||
content (:content draft)
|
||||
|
@ -146,6 +173,9 @@
|
|||
pos-x (* (:x position) zoom)
|
||||
pos-y (* (:y position) zoom)
|
||||
|
||||
disabled? (or (fm/all-spaces? content)
|
||||
(str/empty-or-nil? content))
|
||||
|
||||
on-esc
|
||||
(mf/use-fn
|
||||
(mf/deps draft)
|
||||
|
@ -165,38 +195,70 @@
|
|||
(mf/use-fn
|
||||
(mf/deps draft)
|
||||
(partial on-submit draft))]
|
||||
(if new-css-system
|
||||
[:*
|
||||
[:div
|
||||
{:class (stl/css :floating-thread-bubble)
|
||||
:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
"?"]
|
||||
[:div {:class (stl/css :thread-content)
|
||||
:style {:top (str (- pos-y 24) "px")
|
||||
:left (str (+ pos-x 28) "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
[:div {:class (stl/css :reply-form)}
|
||||
[:& resizing-textarea {:placeholder (tr "labels.write-new-comment")
|
||||
:value (or content "")
|
||||
:autofocus true
|
||||
:select-on-focus? false
|
||||
:on-esc on-esc
|
||||
:on-change on-change}]
|
||||
[:div {:class (stl/css :buttons-wrapper)}
|
||||
|
||||
[:*
|
||||
[:div.thread-bubble
|
||||
{:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
[:span "?"]]
|
||||
[:div.thread-content
|
||||
{:style {:top (str (- pos-y 14) "px")
|
||||
:left (str (+ pos-x 14) "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
[:div.reply-form
|
||||
[:& resizing-textarea {:placeholder (tr "labels.write-new-comment")
|
||||
:value (or content "")
|
||||
:autofocus true
|
||||
:on-esc on-esc
|
||||
:on-change on-change}]
|
||||
[:div.buttons
|
||||
[:input.btn-primary
|
||||
{:on-click on-submit
|
||||
:type "button"
|
||||
:value "Post"
|
||||
:disabled (or (fm/all-spaces? content)
|
||||
(str/empty-or-nil? content))}]
|
||||
[:input.btn-secondary
|
||||
{:on-click on-esc
|
||||
:type "button"
|
||||
:value "Cancel"}]]]]]))
|
||||
[:input {:on-click on-esc
|
||||
:class (stl/css :cancel-btn)
|
||||
:type "button"
|
||||
:value "Cancel"}]
|
||||
|
||||
[:input {:on-click on-submit
|
||||
:type "button"
|
||||
:value "Post"
|
||||
:class (stl/css-case :post-btn true
|
||||
:global/disabled disabled?)
|
||||
:disabled disabled?}]]]]]
|
||||
|
||||
[:*
|
||||
[:div.thread-bubble
|
||||
{:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
[:span "?"]]
|
||||
[:div.thread-content
|
||||
{:style {:top (str (- pos-y 14) "px")
|
||||
:left (str (+ pos-x 14) "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
[:div.reply-form
|
||||
[:& resizing-textarea {:placeholder (tr "labels.write-new-comment")
|
||||
:value (or content "")
|
||||
:autofocus true
|
||||
:on-esc on-esc
|
||||
:on-change on-change}]
|
||||
[:div.buttons
|
||||
[:input.btn-primary
|
||||
{:on-click on-submit
|
||||
:type "button"
|
||||
:value "Post"
|
||||
:disabled disabled?}]
|
||||
[:input.btn-secondary
|
||||
{:on-click on-esc
|
||||
:type "button"
|
||||
:value "Cancel"}]]]]])))
|
||||
|
||||
(mf/defc edit-form
|
||||
[{:keys [content on-submit on-cancel] :as props}]
|
||||
(let [content (mf/use-state content)
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
content (mf/use-state content)
|
||||
|
||||
on-change
|
||||
(mf/use-fn
|
||||
|
@ -205,34 +267,56 @@
|
|||
on-submit*
|
||||
(mf/use-fn
|
||||
(mf/deps @content)
|
||||
(fn [] (on-submit @content)))]
|
||||
(fn [] (on-submit @content)))
|
||||
|
||||
disabled? (or (fm/all-spaces? @content)
|
||||
(str/empty-or-nil? @content))]
|
||||
|
||||
(if new-css-system
|
||||
[:div {:class (stl/css :edit-form)}
|
||||
[:& resizing-textarea {:value @content
|
||||
:autofocus true
|
||||
:select-on-focus true
|
||||
:select-on-focus? false
|
||||
:on-change on-change}]
|
||||
[:div {:class (stl/css :buttons-wrapper)}
|
||||
[:input {:type "button"
|
||||
:value "Cancel"
|
||||
:class (stl/css :cancel-btn)
|
||||
:on-click on-cancel}]
|
||||
[:input {:type "button"
|
||||
:class (stl/css-case :post-btn true
|
||||
:global/disabled disabled?)
|
||||
:value "Post"
|
||||
:on-click on-submit*
|
||||
:disabled disabled?}]]]
|
||||
|
||||
|
||||
[:div.reply-form.edit-form
|
||||
[:& resizing-textarea {:value @content
|
||||
:autofocus true
|
||||
:select-on-focus true
|
||||
:on-change on-change}]
|
||||
[:div.buttons
|
||||
[:input.btn-primary {:type "button"
|
||||
:value "Post"
|
||||
:on-click on-submit*
|
||||
:disabled (or (fm/all-spaces? @content)
|
||||
(str/empty-or-nil? @content))}]
|
||||
[:input.btn-secondary {:type "button" :value "Cancel" :on-click on-cancel}]]]))
|
||||
[:div.reply-form.edit-form
|
||||
[:& resizing-textarea {:value @content
|
||||
:autofocus true
|
||||
:select-on-focus true
|
||||
:on-change on-change}]
|
||||
[:div.buttons
|
||||
[:input.btn-primary {:type "button"
|
||||
:value "Post"
|
||||
:on-click on-submit*
|
||||
:disabled disabled?}]
|
||||
[:input.btn-secondary {:type "button" :value "Cancel" :on-click on-cancel}]]])))
|
||||
|
||||
(mf/defc comment-item
|
||||
[{:keys [comment thread users origin] :as props}]
|
||||
(let [owner (get users (:owner-id comment))
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
owner (get users (:owner-id comment))
|
||||
profile (mf/deref refs/profile)
|
||||
options (mf/use-state false)
|
||||
edition? (mf/use-state false)
|
||||
|
||||
on-show-options
|
||||
on-toggle-options
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(reset! options true)))
|
||||
(swap! options not)))
|
||||
|
||||
on-hide-options
|
||||
(mf/use-fn
|
||||
|
@ -264,11 +348,11 @@
|
|||
(mf/use-fn
|
||||
(mf/deps thread)
|
||||
#(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.delete-comment-thread.title")
|
||||
:message (tr "modals.delete-comment-thread.message")
|
||||
:accept-label (tr "modals.delete-comment-thread.accept")
|
||||
:on-accept delete-thread})))
|
||||
{:type :confirm
|
||||
:title (tr "modals.delete-comment-thread.title")
|
||||
:message (tr "modals.delete-comment-thread.message")
|
||||
:accept-label (tr "modals.delete-comment-thread.accept")
|
||||
:on-accept delete-thread})))
|
||||
|
||||
on-submit
|
||||
(mf/use-fn
|
||||
|
@ -287,39 +371,82 @@
|
|||
(dom/stop-propagation event)
|
||||
(st/emit! (dcm/update-comment-thread (update thread :is-resolved not)))))]
|
||||
|
||||
[:div.comment-container
|
||||
[:div.comment
|
||||
[:div.author
|
||||
[:div.avatar
|
||||
[:img {:src (cfg/resolve-profile-photo-url owner)}]]
|
||||
[:div.name
|
||||
[:div.fullname (:fullname owner)]
|
||||
[:div.timeago (dt/timeago (:modified-at comment))]]
|
||||
(if new-css-system
|
||||
[:div {:class (stl/css :comment-container)}
|
||||
[:div {:class (stl/css :comment)}
|
||||
[:div {:class (stl/css :author)}
|
||||
[:div {:class (stl/css :avatar)}
|
||||
[:img {:src (cfg/resolve-profile-photo-url owner)}]]
|
||||
[:div {:class (stl/css :name)}
|
||||
[:div {:class (stl/css :fullname)} (:fullname owner)]
|
||||
[:div {:class (stl/css :timeago)} (dt/timeago (:modified-at comment))]]
|
||||
|
||||
(when (some? thread)
|
||||
[:div.options-resolve {:on-click toggle-resolved}
|
||||
(if (:is-resolved thread)
|
||||
[:span i/checkbox-checked]
|
||||
[:span i/checkbox-unchecked])])
|
||||
(when (some? thread)
|
||||
[:div {:class (stl/css :options-resolve-wrapper)
|
||||
:on-click toggle-resolved}
|
||||
[:span {:class (stl/css-case :options-resolve true
|
||||
:global/checked (:is-resolved thread))} i/tick-refactor]])
|
||||
|
||||
(when (= (:id profile) (:id owner))
|
||||
[:div.options
|
||||
[:div.options-icon {:on-click on-show-options} i/actions]])]
|
||||
(when (= (:id profile) (:id owner))
|
||||
[:div {:class (stl/css :options)
|
||||
:on-click on-toggle-options}
|
||||
i/menu-refactor])]
|
||||
|
||||
[:div.content
|
||||
(if @edition?
|
||||
[:& edit-form {:content (:content comment)
|
||||
:on-submit on-submit
|
||||
:on-cancel on-cancel}]
|
||||
[:span.text (:content comment)])]]
|
||||
[:div {:class (stl/css :content)}
|
||||
(if @edition?
|
||||
[:& edit-form {:content (:content comment)
|
||||
:on-submit on-submit
|
||||
:on-cancel on-cancel}]
|
||||
[:span {:class (stl/css :text)} (:content comment)])]]
|
||||
|
||||
[:& dropdown {:show @options
|
||||
:on-close on-hide-options}
|
||||
[:ul.dropdown.comment-options-dropdown
|
||||
[:li {:on-click on-edit-clicked} (tr "labels.edit")]
|
||||
(if thread
|
||||
[:li {:on-click on-delete-thread} (tr "labels.delete-comment-thread")]
|
||||
[:li {:on-click on-delete-comment} (tr "labels.delete-comment")])]]]))
|
||||
[:& dropdown {:show @options
|
||||
:on-close on-hide-options}
|
||||
[:ul {:class (stl/css :comment-options-dropdown)}
|
||||
[:li {:class (stl/css :context-menu-option)
|
||||
:on-click on-edit-clicked}
|
||||
(tr "labels.edit")]
|
||||
(if thread
|
||||
[:li {:class (stl/css :context-menu-option)
|
||||
:on-click on-delete-thread}
|
||||
(tr "labels.delete-comment-thread")]
|
||||
[:li {:class (stl/css :context-menu-option)
|
||||
:on-click on-delete-comment}
|
||||
(tr "labels.delete-comment")])]]]
|
||||
|
||||
|
||||
[:div.comment-container
|
||||
[:div.comment
|
||||
[:div.author
|
||||
[:div.avatar
|
||||
[:img {:src (cfg/resolve-profile-photo-url owner)}]]
|
||||
[:div.name
|
||||
[:div.fullname (:fullname owner)]
|
||||
[:div.timeago (dt/timeago (:modified-at comment))]]
|
||||
|
||||
(when (some? thread)
|
||||
[:div.options-resolve {:on-click toggle-resolved}
|
||||
(if (:is-resolved thread)
|
||||
[:span i/checkbox-checked]
|
||||
[:span i/checkbox-unchecked])])
|
||||
|
||||
(when (= (:id profile) (:id owner))
|
||||
[:div.options
|
||||
[:div.options-icon {:on-click on-toggle-options} i/actions]])]
|
||||
|
||||
[:div.content
|
||||
(if @edition?
|
||||
[:& edit-form {:content (:content comment)
|
||||
:on-submit on-submit
|
||||
:on-cancel on-cancel}]
|
||||
[:span.text (:content comment)])]]
|
||||
|
||||
[:& dropdown {:show @options
|
||||
:on-close on-hide-options}
|
||||
[:ul.dropdown.comment-options-dropdown
|
||||
[:li {:on-click on-edit-clicked} (tr "labels.edit")]
|
||||
(if thread
|
||||
[:li {:on-click on-delete-thread} (tr "labels.delete-comment-thread")]
|
||||
[:li {:on-click on-delete-comment} (tr "labels.delete-comment")])]]])))
|
||||
|
||||
(defn make-comments-ref
|
||||
[thread-id]
|
||||
|
@ -328,7 +455,8 @@
|
|||
(mf/defc thread-comments
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [thread zoom users origin position-modifier]}]
|
||||
(let [ref (mf/use-ref)
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
ref (mf/use-ref)
|
||||
|
||||
|
||||
thread-id (:id thread)
|
||||
|
@ -338,8 +466,13 @@
|
|||
(some? position-modifier)
|
||||
(gpt/transform position-modifier))
|
||||
|
||||
pos-x (+ (* (:x pos) zoom) 14)
|
||||
pos-y (- (* (:y pos) zoom) 14)
|
||||
pos-x (if new-css-system
|
||||
(+ (* (:x pos) zoom) 24)
|
||||
(+ (* (:x pos) zoom) 14))
|
||||
pos-y (if new-css-system
|
||||
(- (* (:y pos) zoom) 28)
|
||||
(- (* (:y pos) zoom) 14))
|
||||
|
||||
|
||||
comments-ref (mf/with-memo [thread-id]
|
||||
(make-comments-ref thread-id))
|
||||
|
@ -360,26 +493,46 @@
|
|||
(mf/with-layout-effect [thread-pos comments-map]
|
||||
(when-let [node (mf/ref-val ref)]
|
||||
(dom/scroll-into-view-if-needed! node)))
|
||||
(if new-css-system
|
||||
(when (some? comment)
|
||||
[:div {:class (stl/css :thread-content)
|
||||
:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
|
||||
(when (some? comment)
|
||||
[:div.thread-content
|
||||
{:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
[:div {:class (stl/css :comments)}
|
||||
[:& comment-item {:comment comment
|
||||
:users users
|
||||
:thread thread
|
||||
:origin origin}]
|
||||
(for [item (rest comments)]
|
||||
[:* {:key (dm/str (:id item))}
|
||||
[:& comment-item {:comment item
|
||||
:users users
|
||||
:origin origin}]])
|
||||
[:div {:ref ref}]]
|
||||
[:& reply-form {:thread thread}]])
|
||||
|
||||
[:div.comments
|
||||
[:& comment-item {:comment comment
|
||||
:users users
|
||||
:thread thread
|
||||
:origin origin}]
|
||||
(for [item (rest comments)]
|
||||
[:* {:key (dm/str (:id item))}
|
||||
[:hr]
|
||||
[:& comment-item {:comment item
|
||||
:users users
|
||||
:origin origin}]])
|
||||
[:div {:ref ref}]]
|
||||
[:& reply-form {:thread thread}]])))
|
||||
|
||||
(when (some? comment)
|
||||
[:div.thread-content
|
||||
{:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-click dom/stop-propagation}
|
||||
|
||||
[:div.comments
|
||||
[:& comment-item {:comment comment
|
||||
:users users
|
||||
:thread thread
|
||||
:origin origin}]
|
||||
(for [item (rest comments)]
|
||||
[:* {:key (dm/str (:id item))}
|
||||
[:hr]
|
||||
[:& comment-item {:comment item
|
||||
:users users
|
||||
:origin origin}]])
|
||||
[:div {:ref ref}]]
|
||||
[:& reply-form {:thread thread}]]))))
|
||||
|
||||
(defn use-buble
|
||||
[zoom {:keys [position frame-id]}]
|
||||
|
@ -438,7 +591,8 @@
|
|||
(mf/defc thread-bubble
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [thread zoom open? on-click origin position-modifier]}]
|
||||
(let [pos (cond-> (:position thread)
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
pos (cond-> (:position thread)
|
||||
(some? position-modifier)
|
||||
(gpt/transform position-modifier))
|
||||
|
||||
|
@ -493,23 +647,37 @@
|
|||
(dom/stop-propagation event)
|
||||
(when (= origin :viewer)
|
||||
(on-click thread))))]
|
||||
(if new-css-system
|
||||
[:div {:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-pointer-down on-pointer-down*
|
||||
:on-pointer-up on-pointer-up*
|
||||
:on-pointer-move on-pointer-move*
|
||||
:on-click on-click*
|
||||
:on-lost-pointer-capture on-lost-pointer-capture
|
||||
:class (stl/css-case
|
||||
:floating-thread-bubble true
|
||||
:resolved (:is-resolved thread)
|
||||
:unread (pos? (:count-unread-comments thread)))}
|
||||
[:span (:seqn thread)]]
|
||||
|
||||
[:div.thread-bubble
|
||||
{:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-pointer-down on-pointer-down*
|
||||
:on-pointer-up on-pointer-up*
|
||||
:on-pointer-move on-pointer-move*
|
||||
:on-click on-click*
|
||||
:on-lost-pointer-capture on-lost-pointer-capture
|
||||
:class (dom/classnames
|
||||
:resolved (:is-resolved thread)
|
||||
:unread (pos? (:count-unread-comments thread)))}
|
||||
[:span (:seqn thread)]]))
|
||||
[:div.thread-bubble
|
||||
{:style {:top (str pos-y "px")
|
||||
:left (str pos-x "px")}
|
||||
:on-pointer-down on-pointer-down*
|
||||
:on-pointer-up on-pointer-up*
|
||||
:on-pointer-move on-pointer-move*
|
||||
:on-click on-click*
|
||||
:on-lost-pointer-capture on-lost-pointer-capture
|
||||
:class (dom/classnames
|
||||
:resolved (:is-resolved thread)
|
||||
:unread (pos? (:count-unread-comments thread)))}
|
||||
[:span (:seqn thread)]])))
|
||||
|
||||
(mf/defc comment-thread
|
||||
[{:keys [item users on-click]}]
|
||||
(let [owner (get users (:owner-id item))
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
owner (get users (:owner-id item))
|
||||
on-click*
|
||||
(mf/use-fn
|
||||
(mf/deps item)
|
||||
|
@ -519,48 +687,99 @@
|
|||
(when (fn? on-click)
|
||||
(on-click item))))]
|
||||
|
||||
[:div.comment {:on-click on-click*}
|
||||
[:div.author
|
||||
[:div.thread-bubble
|
||||
{:class (dom/classnames
|
||||
:resolved (:is-resolved item)
|
||||
:unread (pos? (:count-unread-comments item)))}
|
||||
(:seqn item)]
|
||||
[:div.avatar
|
||||
[:img {:src (cfg/resolve-profile-photo-url owner)}]]
|
||||
[:div.name
|
||||
[:div.fullname (:fullname owner) ", "]
|
||||
[:div.timeago (dt/timeago (:modified-at item))]]]
|
||||
[:div.content
|
||||
[:span.text (:content item)]]
|
||||
[:div.content.replies
|
||||
(let [unread (:count-unread-comments item ::none)
|
||||
total (:count-comments item 1)]
|
||||
[:*
|
||||
(when (> total 1)
|
||||
(if (= total 2)
|
||||
[:span.total-replies "1 reply"]
|
||||
[:span.total-replies (str (dec total) " replies")]))
|
||||
(if new-css-system
|
||||
[:div {:class (stl/css :comment)
|
||||
:on-click on-click*}
|
||||
[:div {:class (stl/css :author)}
|
||||
[:div {:class (stl/css-case :thread-bubble true
|
||||
:resolved (:is-resolved item)
|
||||
:unread (pos? (:count-unread-comments item)))}
|
||||
(:seqn item)]
|
||||
[:div {:class (stl/css :avatar)}
|
||||
[:img {:src (cfg/resolve-profile-photo-url owner)}]]
|
||||
[:div {:class (stl/css :name)}
|
||||
[:div {:class (stl/css :fullname)} (:fullname owner)]
|
||||
[:div {:class (stl/css :timeago)} (dt/timeago (:modified-at item))]]]
|
||||
[:div {:class (stl/css :content)}
|
||||
(:content item)]
|
||||
[:div {:class (stl/css :replies)}
|
||||
(let [unread (:count-unread-comments item ::none)
|
||||
total (:count-comments item 1)]
|
||||
[:*
|
||||
(when (> total 1)
|
||||
(if (= total 2)
|
||||
[:span {:class (stl/css :total-replies)} "1 reply"]
|
||||
[:span {:class (stl/css :total-replies)} (str (dec total) " replies")]))
|
||||
|
||||
(when (and (> total 1) (> unread 0))
|
||||
(if (= unread 1)
|
||||
[:span.new-replies "1 new reply"]
|
||||
[:span.new-replies (str unread " new replies")]))])]]))
|
||||
(when (and (> total 1) (> unread 0))
|
||||
(if (= unread 1)
|
||||
[:span {:class (stl/css :new-replies)} "1 new reply"]
|
||||
[:span {:class (stl/css :new-replies)} (str unread " new replies")]))])]]
|
||||
|
||||
|
||||
[:div.comment {:on-click on-click*}
|
||||
[:div.author
|
||||
[:div.thread-bubble
|
||||
{:class (dom/classnames
|
||||
:resolved (:is-resolved item)
|
||||
:unread (pos? (:count-unread-comments item)))}
|
||||
(:seqn item)]
|
||||
[:div.avatar
|
||||
[:img {:src (cfg/resolve-profile-photo-url owner)}]]
|
||||
[:div.name
|
||||
[:div.fullname (:fullname owner) ", "]
|
||||
[:div.timeago (dt/timeago (:modified-at item))]]]
|
||||
[:div.content
|
||||
[:span.text (:content item)]]
|
||||
[:div.content.replies
|
||||
(let [unread (:count-unread-comments item ::none)
|
||||
total (:count-comments item 1)]
|
||||
[:*
|
||||
(when (> total 1)
|
||||
(if (= total 2)
|
||||
[:span.total-replies "1 reply"]
|
||||
[:span.total-replies (str (dec total) " replies")]))
|
||||
|
||||
(when (and (> total 1) (> unread 0))
|
||||
(if (= unread 1)
|
||||
[:span.new-replies "1 new reply"]
|
||||
[:span.new-replies (str unread " new replies")]))])]])))
|
||||
|
||||
(mf/defc comment-thread-group
|
||||
[{:keys [group users on-thread-click]}]
|
||||
[:div.thread-group
|
||||
(if (:file-name group)
|
||||
[:div.section-title
|
||||
[:span.label.filename (:file-name group) ", "]
|
||||
[:span.label (:page-name group)]]
|
||||
[:div.section-title
|
||||
[:span.icon i/file-html]
|
||||
[:span.label (:page-name group)]])
|
||||
[:div.threads
|
||||
(for [item (:items group)]
|
||||
[:& comment-thread
|
||||
{:item item
|
||||
:on-click on-thread-click
|
||||
:users users
|
||||
:key (:id item)}])]])
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)]
|
||||
(if new-css-system
|
||||
[:div {:class (stl/css :thread-group)}
|
||||
(if (:file-name group)
|
||||
[:div {:class (stl/css :section-title)}
|
||||
[:span {:class (stl/css :file-name)} (:file-name group) ", "]
|
||||
[:span {:class (stl/css :page-name)} (:page-name group)]]
|
||||
|
||||
[:div {:class (stl/css :section-title)}
|
||||
[:span {:class (stl/css :icon)} i/document-refactor]
|
||||
[:span {:class (stl/css :page-name)} (:page-name group)]])
|
||||
|
||||
[:div {:class (stl/css :threads)}
|
||||
(for [item (:items group)]
|
||||
[:& comment-thread
|
||||
{:item item
|
||||
:on-click on-thread-click
|
||||
:users users
|
||||
:key (:id item)}])]]
|
||||
|
||||
|
||||
[:div.thread-group
|
||||
(if (:file-name group)
|
||||
[:div.section-title
|
||||
[:span.label.filename (:file-name group) ", "]
|
||||
[:span.label (:page-name group)]]
|
||||
[:div.section-title
|
||||
[:span.icon i/file-html]
|
||||
[:span.label (:page-name group)]])
|
||||
[:div.threads
|
||||
(for [item (:items group)]
|
||||
[:& comment-thread
|
||||
{:item item
|
||||
:on-click on-thread-click
|
||||
:users users
|
||||
:key (:id item)}])]])))
|
||||
|
|
232
frontend/src/app/main/ui/comments.scss
Normal file
232
frontend/src/app/main/ui/comments.scss
Normal file
|
@ -0,0 +1,232 @@
|
|||
// 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
// Comment-thread-group
|
||||
.thread-group {
|
||||
padding: 0 $s-12;
|
||||
.section-title {
|
||||
@include titleTipography;
|
||||
height: $s-32;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: $s-8;
|
||||
.file-name {
|
||||
color: var(--comment-subtitle-color);
|
||||
}
|
||||
.page-name {
|
||||
color: var(--comment-subtitle-color);
|
||||
}
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 $s-6 0 $s-4;
|
||||
width: $s-24;
|
||||
height: $s-32;
|
||||
margin-left: $s-6;
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
.threads {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-24;
|
||||
}
|
||||
}
|
||||
|
||||
// Comment-thread
|
||||
.comment {
|
||||
@include titleTipography;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-12;
|
||||
.author {
|
||||
display: flex;
|
||||
gap: $s-8;
|
||||
.thread-bubble {
|
||||
@extend .comment-bubbles;
|
||||
&.resolved {
|
||||
@extend .resolved-comment-bubble;
|
||||
}
|
||||
&.unread {
|
||||
@extend .unread-comment-bubble;
|
||||
}
|
||||
}
|
||||
.avatar {
|
||||
height: $s-32;
|
||||
width: $s-32;
|
||||
border-radius: $br-circle;
|
||||
img {
|
||||
border-radius: $br-circle;
|
||||
}
|
||||
}
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
.fullname {
|
||||
@include textEllipsis;
|
||||
color: var(--comment-title-color);
|
||||
}
|
||||
.timeago {
|
||||
@include textEllipsis;
|
||||
color: var(--comment-subtitle-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
@include titleTipography;
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
.replies {
|
||||
display: flex;
|
||||
gap: $s-8;
|
||||
.total-replies {
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
.new-replies {
|
||||
color: var(--color-accent-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Thread-bubble
|
||||
|
||||
.floating-thread-bubble {
|
||||
@extend .comment-bubbles;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
transform: translate(calc(-1 * $s-16), calc(-1 * $s-16));
|
||||
|
||||
&.resolved {
|
||||
@extend .resolved-comment-bubble;
|
||||
}
|
||||
&.unread {
|
||||
@extend .unread-comment-bubble;
|
||||
}
|
||||
}
|
||||
|
||||
// thread-content
|
||||
.thread-content {
|
||||
position: absolute;
|
||||
pointer-events: auto;
|
||||
user-select: text;
|
||||
width: $s-284;
|
||||
padding: $s-12;
|
||||
border-radius: $br-8;
|
||||
background-color: var(--comment-modal-background-color);
|
||||
.comments {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-24;
|
||||
}
|
||||
}
|
||||
|
||||
// comment-item
|
||||
|
||||
.comment-container {
|
||||
position: relative;
|
||||
.comment {
|
||||
@include titleTipography;
|
||||
.author {
|
||||
display: flex;
|
||||
gap: $s-8;
|
||||
.avatar {
|
||||
height: $s-32;
|
||||
width: $s-32;
|
||||
border-radius: $br-circle;
|
||||
img {
|
||||
border-radius: $br-circle;
|
||||
}
|
||||
}
|
||||
.name {
|
||||
flex-grow: 1;
|
||||
.fullname {
|
||||
@include textEllipsis;
|
||||
color: var(--comment-title-color);
|
||||
}
|
||||
.timeago {
|
||||
@include textEllipsis;
|
||||
color: var(--comment-subtitle-color);
|
||||
}
|
||||
}
|
||||
.options-resolve-wrapper {
|
||||
@include flexCenter;
|
||||
width: $s-16;
|
||||
height: $s-32;
|
||||
.options-resolve {
|
||||
@extend .checkbox-icon;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.options {
|
||||
@extend .button-tertiary;
|
||||
height: $s-32;
|
||||
width: $s-28;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content {
|
||||
position: relative;
|
||||
.text {
|
||||
@include titleTipography;
|
||||
}
|
||||
}
|
||||
}
|
||||
.comment-options-dropdown {
|
||||
@extend .dropdown-wrapper;
|
||||
position: absolute;
|
||||
width: $s-120;
|
||||
right: 0;
|
||||
left: unset;
|
||||
.context-menu-option {
|
||||
@extend .dropdown-element-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// edit-form & reply-form
|
||||
|
||||
.edit-form,
|
||||
.reply-form {
|
||||
textarea {
|
||||
@extend .input-element;
|
||||
line-height: 1.45;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: $s-260;
|
||||
min-width: $s-260;
|
||||
margin-bottom: $s-8;
|
||||
padding: $s-8;
|
||||
color: var(--input-foreground-color-active);
|
||||
&:focus {
|
||||
border: $s-1 solid var(--input-border-color-active);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
.buttons-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: $s-4;
|
||||
.post-btn {
|
||||
@extend .button-primary;
|
||||
height: $s-32;
|
||||
width: $s-92;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.cancel-btn {
|
||||
@extend .button-secondary;
|
||||
height: $s-32;
|
||||
width: $s-92;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,19 +44,19 @@
|
|||
&.disabled {
|
||||
cursor: default;
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
stroke: var(--button-background-color-disabled);
|
||||
}
|
||||
.title-name {
|
||||
color: var(--button-foreground-color-disabled);
|
||||
color: var(--button-background-color-disabled);
|
||||
}
|
||||
&:hover {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
stroke: var(--button-background-color-disabled);
|
||||
}
|
||||
.title-name {
|
||||
color: var(--button-foreground-color-disabled);
|
||||
color: var(--button-background-color-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
}
|
||||
&:disabled {
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
stroke: var(--button-background-color-disabled);
|
||||
}
|
||||
&::after {
|
||||
background-image: none;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.workspace.comments
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.events :as ev]
|
||||
|
@ -27,54 +28,104 @@
|
|||
|
||||
(mf/defc sidebar-options
|
||||
[]
|
||||
(let [{cmode :mode cshow :show} (mf/deref refs/comments-local)
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
{cmode :mode cshow :show} (mf/deref refs/comments-local)
|
||||
update-mode
|
||||
(mf/use-fn
|
||||
(fn [mode]
|
||||
(st/emit! (dcm/update-filters {:mode mode}))))
|
||||
(fn [event]
|
||||
(let [mode (-> (dom/get-current-target event)
|
||||
(dom/get-data "value")
|
||||
(keyword))]
|
||||
(st/emit! (dcm/update-filters {:mode mode})))))
|
||||
|
||||
update-show
|
||||
(mf/use-fn
|
||||
(fn [mode]
|
||||
(st/emit! (dcm/update-filters {:show mode}))))]
|
||||
(mf/deps cshow)
|
||||
(fn []
|
||||
(let [mode (if (= :pending cshow) :all :pending)]
|
||||
(st/emit! (dcm/update-filters {:show mode})))))]
|
||||
|
||||
[:ul.dropdown.with-check
|
||||
[:li {:class (dom/classnames :selected (or (= :all cmode) (nil? cmode)))
|
||||
:on-click #(update-mode :all)}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "labels.show-all-comments")]]
|
||||
(if new-css-system
|
||||
[:ul {:class (stl/css :comment-mode-dropdown)}
|
||||
[:li {:class (stl/css-case :dropdown-item true
|
||||
:selected (or (= :all cmode) (nil? cmode)))
|
||||
:data-value "all"
|
||||
:on-click update-mode}
|
||||
|
||||
[:li {:class (dom/classnames :selected (= :yours cmode))
|
||||
:on-click #(update-mode :yours)}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "labels.show-your-comments")]]
|
||||
[:span {:class (stl/css :label)} (tr "labels.show-all-comments")]
|
||||
[:span {:class (stl/css :icon)} i/tick-refactor]]
|
||||
[:li {:class (stl/css-case :dropdown-item true
|
||||
:selected (= :yours cmode))
|
||||
:data-value "yours"
|
||||
:on-click update-mode}
|
||||
[:span {:class (stl/css :label)} (tr "labels.show-your-comments")]
|
||||
[:span {:class (stl/css :icon)} i/tick-refactor]]
|
||||
[:li {:class (stl/css :separator)}]
|
||||
[:li {:class (stl/css-case :dropdown-item true
|
||||
:selected (= :pending cshow))
|
||||
:on-click update-show}
|
||||
[:span {:class (stl/css :label)} (tr "labels.hide-resolved-comments")]
|
||||
[:span {:class (stl/css :icon)} i/tick-refactor]]]
|
||||
|
||||
[:hr]
|
||||
|
||||
[:li {:class (dom/classnames :selected (= :pending cshow))
|
||||
:on-click #(update-show (if (= :pending cshow) :all :pending))}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "labels.hide-resolved-comments")]]]))
|
||||
[:ul.dropdown.with-check
|
||||
[:li {:class (dom/classnames :selected (or (= :all cmode) (nil? cmode)))
|
||||
:data-value "all"
|
||||
:on-click update-mode}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "labels.show-all-comments")]]
|
||||
|
||||
[:li {:class (dom/classnames :selected (= :yours cmode))
|
||||
:data-value "yours"
|
||||
:on-click update-mode}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "labels.show-your-comments")]]
|
||||
|
||||
[:hr]
|
||||
|
||||
[:li {:class (dom/classnames :selected (= :pending cshow))
|
||||
:on-click update-show}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "labels.hide-resolved-comments")]]])
|
||||
|
||||
))
|
||||
|
||||
(mf/defc comments-sidebar
|
||||
[{:keys [users threads page-id]}]
|
||||
(let [threads-map (mf/deref refs/threads-ref)
|
||||
(let [new-css-system (mf/use-ctx ctx/new-css-system)
|
||||
threads-map (mf/deref refs/threads-ref)
|
||||
profile (mf/deref refs/profile)
|
||||
users-refs (mf/deref refs/current-file-comments-users)
|
||||
users (or users users-refs)
|
||||
local (mf/deref refs/comments-local)
|
||||
options? (mf/use-state false)
|
||||
|
||||
state* (mf/use-state false)
|
||||
options? (deref state*)
|
||||
|
||||
threads (if (nil? threads)
|
||||
(->> (vals threads-map)
|
||||
(sort-by :modified-at)
|
||||
(reverse)
|
||||
(dcm/apply-filters local profile))
|
||||
threads)
|
||||
tgroups (->> threads
|
||||
(dcm/group-threads-by-page))
|
||||
|
||||
close-section
|
||||
(mf/use-fn
|
||||
(mf/deps)
|
||||
#(st/emit! :interrupt (dw/deselect-all true)))
|
||||
|
||||
tgroups (->> threads
|
||||
(dcm/group-threads-by-page))
|
||||
|
||||
page-id (or page-id (mf/use-ctx ctx/current-page-id))
|
||||
|
||||
toggle-mode-selector
|
||||
(mf/use-fn
|
||||
(mf/deps)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(swap! state* not)))
|
||||
|
||||
on-thread-click
|
||||
(mf/use-fn
|
||||
(mf/deps page-id)
|
||||
|
@ -88,38 +139,76 @@
|
|||
(dwcm/center-to-comment-thread thread)
|
||||
(-> (dcm/open-thread thread)
|
||||
(with-meta {::ev/origin "workspace"})))))))]
|
||||
(if new-css-system
|
||||
[:div {:class (stl/css :comments-section)}
|
||||
[:div {:class (stl/css :comments-section-title)}
|
||||
[:span (tr "labels.comments")]
|
||||
[:button {:class (stl/css :close-button)
|
||||
:on-click close-section}
|
||||
i/close-refactor]]
|
||||
|
||||
[:div.comments-section.comment-threads-section
|
||||
[:div.workspace-comment-threads-sidebar-header
|
||||
[:div.label (tr "labels.comments")]
|
||||
[:div.options {:on-click
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(reset! options? true))}
|
||||
[:div.label (case (:mode local)
|
||||
(nil :all) (tr "labels.all")
|
||||
:yours (tr "labels.only-yours"))]
|
||||
[:div.icon i/arrow-down]]
|
||||
(when (seq tgroups)
|
||||
[:button {:class (stl/css :mode-dropdown-wrapper)
|
||||
:on-click toggle-mode-selector}
|
||||
|
||||
[:& dropdown {:show @options?
|
||||
:on-close #(reset! options? false)}
|
||||
[:& sidebar-options {:local local}]]]
|
||||
[:span {:class (stl/css :mode-label)} (case (:mode local)
|
||||
(nil :all) (tr "labels.show-all-comments")
|
||||
:yours (tr "labels.show-your-comments"))]
|
||||
[:div {:class (stl/css :icon)} i/arrow-refactor]]
|
||||
|
||||
(if (seq tgroups)
|
||||
[:div.thread-groups
|
||||
[:& cmt/comment-thread-group
|
||||
{:group (first tgroups)
|
||||
:on-thread-click on-thread-click
|
||||
:users users}]
|
||||
(for [tgroup (rest tgroups)]
|
||||
[:*
|
||||
[:hr]
|
||||
[:& dropdown {:show options?
|
||||
:on-close #(reset! state* false)}
|
||||
[:& sidebar-options {:local local}]])
|
||||
|
||||
[:div {:class (stl/css :comments-section-content)}
|
||||
|
||||
(if (seq tgroups)
|
||||
[:div {:class (stl/css :thread-groups)}
|
||||
[:& cmt/comment-thread-group
|
||||
{:group tgroup
|
||||
{:group (first tgroups)
|
||||
:on-thread-click on-thread-click
|
||||
:users users
|
||||
:key (:page-id tgroup)}]])]
|
||||
:users users}]
|
||||
(for [tgroup (rest tgroups)]
|
||||
[:& cmt/comment-thread-group
|
||||
{:group tgroup
|
||||
:on-thread-click on-thread-click
|
||||
:users users
|
||||
:key (:page-id tgroup)}])]
|
||||
|
||||
[:div.thread-groups-placeholder
|
||||
i/chat
|
||||
(tr "labels.no-comments-available")])]))
|
||||
[:div {:class (stl/css :thread-group-placeholder)}
|
||||
[:span {:class (stl/css :placeholder-icon)} i/comments-refactor]
|
||||
[:span {:class (stl/css :placeholder-label)}
|
||||
(tr "labels.no-comments-available")]])]]
|
||||
|
||||
|
||||
[:div.comments-section.comment-threads-section
|
||||
[:div.workspace-comment-threads-sidebar-header
|
||||
[:div.label (tr "labels.comments")]
|
||||
[:div.options {:on-click toggle-mode-selector}
|
||||
[:div.label (case (:mode local)
|
||||
(nil :all) (tr "labels.all")
|
||||
:yours (tr "labels.only-yours"))]
|
||||
[:div.icon i/arrow-down]]
|
||||
|
||||
[:& dropdown {:show options?
|
||||
:on-close #(reset! state* false)}
|
||||
[:& sidebar-options {:local local}]]]
|
||||
|
||||
(if (seq tgroups)
|
||||
[:div.thread-groups
|
||||
[:& cmt/comment-thread-group
|
||||
{:group (first tgroups)
|
||||
:on-thread-click on-thread-click
|
||||
:users users}]
|
||||
(for [tgroup (rest tgroups)]
|
||||
[:*
|
||||
[:hr]
|
||||
[:& cmt/comment-thread-group
|
||||
{:group tgroup
|
||||
:on-thread-click on-thread-click
|
||||
:users users
|
||||
:key (:page-id tgroup)}]])]
|
||||
|
||||
[:div.thread-groups-placeholder
|
||||
i/chat
|
||||
(tr "labels.no-comments-available")])])))
|
||||
|
|
141
frontend/src/app/main/ui/workspace/comments.scss
Normal file
141
frontend/src/app/main/ui/workspace/comments.scss
Normal file
|
@ -0,0 +1,141 @@
|
|||
// 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.comments-section {
|
||||
position: relative;
|
||||
background-color: var(--panel-background-color);
|
||||
.comments-section-title {
|
||||
@include flexCenter;
|
||||
@include tabTitleTipography;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: $s-32;
|
||||
min-height: $s-32;
|
||||
margin: $s-8 $s-8 0 $s-8;
|
||||
border-radius: $br-8;
|
||||
background-color: var(--panel-title-background-color);
|
||||
span {
|
||||
@include flexCenter;
|
||||
flex-grow: 1;
|
||||
color: var(--title-foreground-color-hover);
|
||||
}
|
||||
|
||||
.close-button {
|
||||
@extend .button-tertiary;
|
||||
height: $s-28;
|
||||
width: $s-28;
|
||||
border-radius: $br-6;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
.mode-dropdown-wrapper {
|
||||
@include buttonStyle;
|
||||
@extend .asset-element;
|
||||
background-color: var(--color-background-tertiary);
|
||||
display: flex;
|
||||
width: $s-256;
|
||||
height: $s-32;
|
||||
padding: $s-8;
|
||||
border-radius: $br-8;
|
||||
margin: $s-16 auto 0 auto;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
.mode-label {
|
||||
padding-right: 8px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.icon {
|
||||
@include flexCenter;
|
||||
padding-right: 8px;
|
||||
height: $s-24;
|
||||
width: $s-24;
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
transform: rotate(90deg);
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
.comment-mode-dropdown {
|
||||
@extend .dropdown-wrapper;
|
||||
top: $s-80;
|
||||
left: $s-12;
|
||||
width: $s-256;
|
||||
.dropdown-item {
|
||||
@extend .dropdown-element-base;
|
||||
justify-content: space-between;
|
||||
.icon {
|
||||
@include flexCenter;
|
||||
height: $s-24;
|
||||
width: $s-24;
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
stroke: transparent;
|
||||
}
|
||||
}
|
||||
.label {
|
||||
@include titleTipography;
|
||||
}
|
||||
&:hover {
|
||||
.icon svg {
|
||||
stroke: transparent;
|
||||
}
|
||||
}
|
||||
&.selected {
|
||||
.label {
|
||||
color: var(--menu-foreground-color);
|
||||
}
|
||||
.icon svg {
|
||||
stroke: var(--icon-foreground-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
.separator {
|
||||
height: $s-12;
|
||||
}
|
||||
}
|
||||
.comments-section-content {
|
||||
.thread-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $s-24;
|
||||
}
|
||||
.thread-group-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: $s-4;
|
||||
margin-top: $s-36;
|
||||
.placeholder-icon {
|
||||
@include flexCenter;
|
||||
height: $s-48;
|
||||
width: $s-48;
|
||||
border-radius: $br-circle;
|
||||
background-color: var(--empty-message-background-color);
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
height: $s-28;
|
||||
width: $s-28;
|
||||
stroke: var(--empty-message-foreground-color);
|
||||
}
|
||||
}
|
||||
.placeholder-label {
|
||||
@include titleTipography;
|
||||
text-align: center;
|
||||
width: $s-184;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
position: relative;
|
||||
height: $s-32;
|
||||
min-height: $s-32;
|
||||
margin: $s-4 $s-4 0 $s-4;
|
||||
margin: $s-8 $s-8 0 $s-8;
|
||||
border-radius: $br-8;
|
||||
background-color: var(--panel-title-background-color);
|
||||
|
||||
|
@ -53,7 +53,7 @@
|
|||
@include flexCenter;
|
||||
height: $s-48;
|
||||
width: $s-48;
|
||||
border-radius: 50%;
|
||||
border-radius: $br-circle;
|
||||
background-color: var(--empty-message-background-color);
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
|
|
|
@ -28,12 +28,12 @@
|
|||
&.disabled {
|
||||
cursor: default;
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
stroke: var(--button-background-color-disabled);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--panel-background-color);
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
stroke: var(--button-background-color-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
&.disabled {
|
||||
cursor: default;
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
stroke: var(--button-background-color-disabled);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--panel-background-color);
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
stroke: var(--button-background-color-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
}
|
||||
&:disabled {
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
stroke: var(--button-background-color-disabled);
|
||||
}
|
||||
&::after {
|
||||
background-image: none;
|
||||
|
|
Loading…
Add table
Reference in a new issue