diff --git a/public/icons/delete.svg b/public/icons/delete.svg index 931cbe95..362d8665 100644 --- a/public/icons/delete.svg +++ b/public/icons/delete.svg @@ -1,3 +1,3 @@ - + diff --git a/public/icons/reply.svg b/public/icons/reply.svg index 8dc91097..2349addc 100644 --- a/public/icons/reply.svg +++ b/public/icons/reply.svg @@ -1,3 +1,4 @@ - + diff --git a/src/components/Article/Comment.module.scss b/src/components/Article/Comment.module.scss new file mode 100644 index 00000000..3b53b70e --- /dev/null +++ b/src/components/Article/Comment.module.scss @@ -0,0 +1,204 @@ +.comment { + background-color: #fff; + margin: 0 -2.4rem 1.5em; + padding: 0.8rem 2.4rem; + transition: background-color 0.3s; + + &:hover { + background-color: #f6f6f6; + + .commentControlShare, + .commentControlDelete, + .commentControlEdit, + .commentControlComplain { + opacity: 1; + } + } + + .shout-body { + @include font-size(1.5rem); + + margin-bottom: 1em; + + *:last-child { + margin-bottom: 0; + } + } + + .author { + align-items: center; + margin-bottom: 1.4rem; + } +} + +.commentLevel1 { + margin-left: 3.2rem; +} + +.commentLevel2 { + margin-left: 6.4rem; +} + +.commentLevel3 { + margin-left: 9.6rem; +} + +.commentLevel4 { + margin-left: 12.8rem; +} + +.commentLevel5 { + margin-left: 16rem; +} + +.commentControls { + @include font-size(1.2rem); + margin-bottom: 0.5em; +} + +.commentControlShare, +.commentControlDelete, +.commentControlEdit, +.commentControlComplain { + @include media-breakpoint-up(md) { + opacity: 0; + transition: opacity 0.3s; + } +} + +.commentControlShare, +.commentControlDelete, +.commentControlEdit { + .icon { + line-height: 1.2; + } +} + +.commentControlShare { + .icon { + height: 1.2rem; + width: 1.2rem; + } +} + +.commentControl { + border: none; + color: #696969; + cursor: pointer; + display: inline-flex; + line-height: 1.2; + margin-right: 0.8rem; + padding: 0.2em 0.3em; + transition: opacity 0.2s, color 0.3s, background-color 0.3s; + vertical-align: top; + + &:hover { + background: #000; + color: #fff; + + .icon { + filter: invert(1); + opacity: 1; + } + } + + .icon { + filter: invert(0); + margin-right: 0.3em; + opacity: 0.6; + transition: filter 0.3s, opacity 0.2s; + + img { + margin-bottom: -0.1em; + } + } +} + +.commentControlReply { + .icon { + height: 1.2em; + width: 1.2em; + } +} + +.commentBody { + @include font-size(1.5rem); + line-height: 1.47; +} + +.commentAuthor, +.commentDate, +.commentRating { + @include font-size(1.2rem); +} + +.commentDate { + color: rgb(0 0 0 / 30%); + flex: 1; + + @include media-breakpoint-down(md) { + margin-left: 1rem; + } +} + +.commentDetails { + display: flex; + margin-bottom: 1.2rem; +} + +.commentRating { + align-items: center; + display: flex; + font-weight: bold; +} + +.commentRatingValue { + padding: 0 0.3em; +} + +.commentRatingPositive { + color: #2bb452; +} + +.commentRatingNegative { + color: #d00820; +} + +.commentRatingControl { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + height: 0; + width: 0; +} + +.commentRatingControlUp { + border-bottom: 8px solid rgb(0 0 0 / 40%); +} + +.commentRatingControlDown { + border-top: 8px solid rgb(0 0 0 / 40%); +} + +.replyForm { + background: #fff; + border: 2px solid rgb(38 56 217 / 50%); + border-radius: 0.8rem; + margin-left: 2.4rem; + position: relative; + + textarea { + border: none; + border-radius: 0.8rem; + padding-top: 1.2rem; + } +} + +.replyFormControls { + padding: 0.5rem 1.6rem 1.6rem; + text-align: right; + + button { + @include font-size(1.6rem); + margin-left: 1.2rem; + } +} diff --git a/src/components/Article/Comment.scss b/src/components/Article/Comment.scss deleted file mode 100644 index b9bda801..00000000 --- a/src/components/Article/Comment.scss +++ /dev/null @@ -1,119 +0,0 @@ -.comment { - background-color: #fff; - margin: 0 -2.4rem 1.5em; - padding: 0.8rem 2.4rem; - transition: background-color 0.3s; - - &:hover { - background-color: #f6f6f6; - - .comment-control--share, - .comment-control--delete, - .comment-control--edit, - .comment-control--complain { - opacity: 1; - } - } - - .shout-body { - @include font-size(1.5rem); - - margin-bottom: 1em; - - *:last-child { - margin-bottom: 0; - } - } - - .circlewrap { - position: absolute; - } - - .author { - align-items: center; - margin-bottom: 1.4rem; - } - - .author__name { - font-weight: bold; - @include font-size(1.2rem); - - margin-bottom: 0; - } - - .author__details { - margin-left: 4rem; - } - - .shout-date { - @include font-size(1.2rem); - - flex: 1; - color: rgb(0 0 0 / 30%); - } -} - -.comment--level-1 { - margin-left: 2.4rem; -} - -.comment--level-2 { - margin-left: 4.8rem; -} - -.comment--level-3 { - margin-left: 7.2rem; -} - -.comment--level-4 { - margin-left: 9.6rem; -} - -.comment--level-5 { - margin-left: 12rem; -} - -.shout-controls { - align-items: baseline; - display: flex; - justify-content: space-between; - padding-top: 0.8rem; -} - -.comment-controls { - margin-bottom: 0.5em; -} - -.comment-control--share, -.comment-control--delete, -.comment-control--edit, -.comment-control--complain { - opacity: 0; - transition: opacity 0.3s; -} - -.comment-control { - background: rgb(0 0 0 / 5%); - border: none; - cursor: pointer; - display: inline-flex; - line-height: 1.2; - margin-right: 0.8rem; - padding: 0.2em 0.3em; - vertical-align: top; - - .icon { - margin-right: 0.3em; - - img { - margin-bottom: -0.1em; - } - } -} - -.comment-control--reply { - .icon { - height: 1.2em; - width: 1.2em; - } -} diff --git a/src/components/Article/Comment.tsx b/src/components/Article/Comment.tsx index 870dc134..a9771fc0 100644 --- a/src/components/Article/Comment.tsx +++ b/src/components/Article/Comment.tsx @@ -1,13 +1,16 @@ -import './Comment.scss' +import styles from './Comment.module.scss' import { Icon } from '../_shared/Icon' import { AuthorCard } from '../Author/Card' -import { Show, createMemo } from 'solid-js' +import { Show, createMemo, createSignal } from 'solid-js' import { clsx } from 'clsx' import type { Author, Reaction as Point } from '../../graphql/types.gen' import { t } from '../../utils/intl' // import { createReaction, updateReaction, deleteReaction } from '../../stores/zine/reactions' import MD from './MD' import { deleteReaction } from '../../stores/zine/reactions' +import { formatDate } from '../../utils' +import { SharePopup } from './SharePopup' +import stylesHeader from '../Nav/Header.module.scss' export default (props: { level?: number @@ -15,6 +18,9 @@ export default (props: { canEdit?: boolean compact?: boolean }) => { + const [isSharePopupVisible, setIsSharePopupVisible] = createSignal(false) + const [isReplyVisible, setIsReplyVisible] = createSignal(false) + const comment = createMemo(() => props.comment) const body = createMemo(() => (comment().body || '').trim()) const remove = () => { @@ -23,19 +29,22 @@ export default (props: { deleteReaction(comment().id) } } + const formattedDate = createMemo(() => + formatDate(new Date(comment()?.createdAt), { hour: 'numeric', minute: 'numeric' }) + ) return ( -
+
-
+
+
@{(comment()?.createdBy || { name: 'anonymous' }).name} -
+ } > -
-
+
+
-
{comment()?.createdAt}
-
{comment().stat?.rating || 0}
+
{formattedDate()}
+
0, + [styles.commentRatingNegative]: comment().stat?.rating < 0 + }} + > + +
{comment().stat?.rating || 0}
+ +
-
+
-
- {/*FIXME implement edit comment modal*/} {/* showModal('editComment')}*/} {/*>*/} - {/* */} + {/* */} {/* {t('Edit')}*/} {/**/} - - {/*FIXME implement modals */} + { + setIsSharePopupVisible(isVisible) + }} + containerCssClass={stylesHeader.control} + trigger={ + + } + /> + {/*
+ + +
+ +
+ + +
+
+
diff --git a/src/components/Article/CommentsTree.tsx b/src/components/Article/CommentsTree.tsx index 9f1a2f31..f240110c 100644 --- a/src/components/Article/CommentsTree.tsx +++ b/src/components/Article/CommentsTree.tsx @@ -7,6 +7,7 @@ import styles from '../../styles/Article.module.scss' import { useReactionsStore } from '../../stores/zine/reactions' import { createEffect, createMemo, createSignal, onMount, Suspense } from 'solid-js' import type { Reaction } from '../../graphql/types.gen' +import { clsx } from 'clsx' const ARTICLE_COMMENTS_PAGE_SIZE = 50 const MAX_COMMENT_LEVEL = 6 @@ -44,9 +45,27 @@ export const CommentsTree = (props: { shout: string; reactions?: Reaction[] }) = return ( <> -

- {t('Comments')} {reactions().length.toString() || ''} -

+
+ +
+
+ + +
+
{(reaction: Reaction) => ( diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index e954f770..f5b91f0b 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -1,4 +1,4 @@ -import { capitalize } from '../../utils' +import { capitalize, formatDate } from '../../utils' import './Full.scss' import { Icon } from '../_shared/Icon' import { AuthorCard } from '../Author/Card' @@ -16,16 +16,6 @@ interface ArticleProps { article: Shout } -const formatDate = (date: Date) => { - return date - .toLocaleDateString('ru', { - month: 'long', - day: 'numeric', - year: 'numeric' - }) - .replace(' г.', '') -} - export const FullArticle = (props: ArticleProps) => { const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt))) const [isSharePopupVisible, setIsSharePopupVisible] = createSignal(false) @@ -90,7 +80,7 @@ export const FullArticle = (props: ArticleProps) => {
- +
@@ -139,6 +129,11 @@ export const FullArticle = (props: ArticleProps) => {
+
+ + +
+
{(topic) => ( @@ -155,7 +150,7 @@ export const FullArticle = (props: ArticleProps) => { {(a: Author) => ( -
+
)} diff --git a/src/components/Author/Card.module.scss b/src/components/Author/Card.module.scss index fb4d76d2..e1e24152 100644 --- a/src/components/Author/Card.module.scss +++ b/src/components/Author/Card.module.scss @@ -49,7 +49,10 @@ .authorSubscribe { align-items: center; display: flex; - padding: 0 0 0 42px; + + @include media-breakpoint-up(sm) { + padding: 0 0 0 42px; + } a { background: #f7f7f7; @@ -256,3 +259,14 @@ display: block; } } + +.authorComments { + .authorName { + @include font-size(1.2rem); + margin-bottom: 0; + } + + .circlewrap { + margin-top: -0.6em; + } +} diff --git a/src/components/Author/Card.tsx b/src/components/Author/Card.tsx index 6440827e..e11264e5 100644 --- a/src/components/Author/Card.tsx +++ b/src/components/Author/Card.tsx @@ -23,6 +23,7 @@ interface AuthorCardProps { isAuthorsList?: boolean truncateBio?: boolean liteButtons?: boolean + isComments?: boolean } export const AuthorCard = (props: AuthorCardProps) => { @@ -47,6 +48,7 @@ export const AuthorCard = (props: AuthorCardProps) => { class={clsx(styles.author)} classList={{ [styles.authorPage]: props.isAuthorPage, + [styles.authorComments]: props.isComments, [styles.authorsListItem]: props.isAuthorsList }} > @@ -55,6 +57,7 @@ export const AuthorCard = (props: AuthorCardProps) => { hasLink={props.hasLink} isBig={props.isAuthorPage} isAuthorsList={props.isAuthorsList} + class={styles.circlewrap} />
diff --git a/src/styles/Article.module.scss b/src/styles/Article.module.scss index 683c38ea..fb2d0689 100644 --- a/src/styles/Article.module.scss +++ b/src/styles/Article.module.scss @@ -77,7 +77,9 @@ img { } .shoutAuthorsList { - margin-top: 2em; + border-bottom: 1px solid #e8e8e8; + margin: 2em 0; + padding-bottom: 2em; h4 { color: #696969; @@ -118,15 +120,18 @@ img { } .shoutStats { - border-bottom: 1px solid #e8e8e8; border-top: 4px solid #000; display: flex; justify-content: flex-start; - padding: 3.2rem 0; + padding: 3rem 0 0; + + @include media-breakpoint-down(sm) { + flex-wrap: wrap; + } } .shoutStatsItem { - @include font-size(1.7rem); + @include font-size(1.5rem); font-weight: 500; display: inline-block; @@ -179,18 +184,32 @@ img { .icon { opacity: 0.4; } + + @include media-breakpoint-down(sm) { + flex: 1 100%; + } } .shoutStatsItemAdditionalDataItem { + font-weight: normal; display: inline-block; margin-left: 2rem; + margin-right: 0; + + @include media-breakpoint-down(sm) { + &:first-child { + margin-left: 0; + } + } } .topicsList { @include font-size(1.2rem); + border-bottom: 1px solid #e8e8e8; letter-spacing: 0.08em; - margin: 1.6rem 0; + margin-top: 1.6rem; + padding-bottom: 1.6rem; .shoutTopic { display: inline-block; @@ -210,3 +229,45 @@ img { } } } + +.commentsHeaderWrapper { + display: flex; + justify-content: space-between; +} + +.commentsHeader { + @include font-size(2.4rem); + margin-bottom: 1em; +} + +.ratingControl { + button { + font-size: 2.225rem; + } +} + +.commentForm { + margin-bottom: 2.4rem; + + input, + textarea { + border-radius: 0.8rem !important; + } +} + +.commentsViewSwitcher { + margin-top: 0; +} + +.help { + border-bottom: 1px solid #e8e8e8; + margin-bottom: 1.6rem; + padding-bottom: 3.2rem; + + button { + @include font-size(1.5rem); + border-radius: 0.8rem; + margin-right: 1.2rem; + padding: 1.1rem 1.2rem 0.9rem; + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 52fb86b7..5d884c66 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -67,3 +67,17 @@ export const snake2camel = (s: string) => .split(/(?=[A-Z])/) .join('-') .toLowerCase() + +export const formatDate = (date: Date, options: Intl.DateTimeFormatOptions = {}) => { + const opts = Object.assign( + {}, + { + month: 'long', + day: 'numeric', + year: 'numeric' + }, + options + ) + + return date.toLocaleDateString('ru', opts).replace(' г.', '') +}