WIP Comments
This commit is contained in:
parent
f6ec3558d6
commit
57f1d026da
|
@ -1,3 +1,3 @@
|
||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M1 1L6 6M6 6L1 11M6 6L11 1M6 6L11 11" stroke="#696969" stroke-width="2"/>
|
<path d="M1 1L6 6M6 6L1 11M6 6L11 1M6 6L11 11" stroke="#000" stroke-width="2"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 186 B After Width: | Height: | Size: 183 B |
|
@ -1,3 +1,4 @@
|
||||||
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M6 0C2.69432 0 0 2.6826 0 5.97389C0 9.26518 2.69432 11.9478 6 11.9478V14L7.02604 13.3453C8.5166 12.3934 11.2347 10.3384 11.8659 7.22363C11.9523 6.82188 12 6.40385 12 5.97389C12 2.6826 9.30568 0 6 0Z" fill="#696969"/>
|
<path
|
||||||
|
d="M6 0C2.69432 0 0 2.6826 0 5.97389C0 9.26518 2.69432 11.9478 6 11.9478V14L7.02604 13.3453C8.5166 12.3934 11.2347 10.3384 11.8659 7.22363C11.9523 6.82188 12 6.40385 12 5.97389C12 2.6826 9.30568 0 6 0Z" fill="#000"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 328 B |
204
src/components/Article/Comment.module.scss
Normal file
204
src/components/Article/Comment.module.scss
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,16 @@
|
||||||
import './Comment.scss'
|
import styles from './Comment.module.scss'
|
||||||
import { Icon } from '../_shared/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
import { Show, createMemo } from 'solid-js'
|
import { Show, createMemo, createSignal } from 'solid-js'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import type { Author, Reaction as Point } from '../../graphql/types.gen'
|
import type { Author, Reaction as Point } from '../../graphql/types.gen'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
// import { createReaction, updateReaction, deleteReaction } from '../../stores/zine/reactions'
|
// import { createReaction, updateReaction, deleteReaction } from '../../stores/zine/reactions'
|
||||||
import MD from './MD'
|
import MD from './MD'
|
||||||
import { deleteReaction } from '../../stores/zine/reactions'
|
import { deleteReaction } from '../../stores/zine/reactions'
|
||||||
|
import { formatDate } from '../../utils'
|
||||||
|
import { SharePopup } from './SharePopup'
|
||||||
|
import stylesHeader from '../Nav/Header.module.scss'
|
||||||
|
|
||||||
export default (props: {
|
export default (props: {
|
||||||
level?: number
|
level?: number
|
||||||
|
@ -15,6 +18,9 @@ export default (props: {
|
||||||
canEdit?: boolean
|
canEdit?: boolean
|
||||||
compact?: boolean
|
compact?: boolean
|
||||||
}) => {
|
}) => {
|
||||||
|
const [isSharePopupVisible, setIsSharePopupVisible] = createSignal(false)
|
||||||
|
const [isReplyVisible, setIsReplyVisible] = createSignal(false)
|
||||||
|
|
||||||
const comment = createMemo(() => props.comment)
|
const comment = createMemo(() => props.comment)
|
||||||
const body = createMemo(() => (comment().body || '').trim())
|
const body = createMemo(() => (comment().body || '').trim())
|
||||||
const remove = () => {
|
const remove = () => {
|
||||||
|
@ -23,19 +29,22 @@ export default (props: {
|
||||||
deleteReaction(comment().id)
|
deleteReaction(comment().id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const formattedDate = createMemo(() =>
|
||||||
|
formatDate(new Date(comment()?.createdAt), { hour: 'numeric', minute: 'numeric' })
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={clsx('comment', { [`comment--level-${props.level}`]: Boolean(props.level) })}>
|
<div class={clsx(styles.comment, { [styles[`commentLevel${props.level}`]]: Boolean(props.level) })}>
|
||||||
<Show when={!!body()}>
|
<Show when={!!body()}>
|
||||||
<div class="comment__content">
|
<div class={styles.commentContent}>
|
||||||
<Show
|
<Show
|
||||||
when={!props.compact}
|
when={!props.compact}
|
||||||
fallback={
|
fallback={
|
||||||
<div class="comment__details">
|
<div class={styles.commentDetails}>
|
||||||
<a href={`/author/${comment()?.createdBy?.slug}`}>
|
<a href={`/author/${comment()?.createdBy?.slug}`}>
|
||||||
@{(comment()?.createdBy || { name: 'anonymous' }).name}
|
@{(comment()?.createdBy || { name: 'anonymous' }).name}
|
||||||
</a>
|
</a>
|
||||||
<div class="comment__article">
|
<div class={styles.commentArticle}>
|
||||||
<Icon name="reply-arrow" />
|
<Icon name="reply-arrow" />
|
||||||
<a href={`#comment-${comment()?.id}`}>
|
<a href={`#comment-${comment()?.id}`}>
|
||||||
#{(comment()?.shout || { title: 'Lorem ipsum titled' }).title}
|
#{(comment()?.shout || { title: 'Lorem ipsum titled' }).title}
|
||||||
|
@ -44,60 +53,100 @@ export default (props: {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div class="comment__details">
|
<div class={styles.commentDetails}>
|
||||||
<div class="comment-author">
|
<div class={styles.commentAuthor}>
|
||||||
<AuthorCard
|
<AuthorCard
|
||||||
author={comment()?.createdBy as Author}
|
author={comment()?.createdBy as Author}
|
||||||
hideDescription={true}
|
hideDescription={true}
|
||||||
hideFollow={true}
|
hideFollow={true}
|
||||||
|
isComments={true}
|
||||||
|
hasLink={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="comment-date">{comment()?.createdAt}</div>
|
<div class={styles.commentDate}>{formattedDate()}</div>
|
||||||
<div class="comment-rating">{comment().stat?.rating || 0}</div>
|
<div
|
||||||
|
class={styles.commentRating}
|
||||||
|
classList={{
|
||||||
|
[styles.commentRatingPositive]: comment().stat?.rating > 0,
|
||||||
|
[styles.commentRatingNegative]: comment().stat?.rating < 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button class={clsx(styles.commentRatingControl, styles.commentRatingControlUp)}></button>
|
||||||
|
<div class={styles.commentRatingValue}>{comment().stat?.rating || 0}</div>
|
||||||
|
<button class={clsx(styles.commentRatingControl, styles.commentRatingControlDown)}></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<div class="comment-body" contenteditable={props.canEdit} id={'comment-' + (comment().id || '')}>
|
<div
|
||||||
|
class={styles.commentBody}
|
||||||
|
contenteditable={props.canEdit}
|
||||||
|
id={'comment-' + (comment().id || '')}
|
||||||
|
>
|
||||||
<MD body={body()} />
|
<MD body={body()} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={!props.compact}>
|
<Show when={!props.compact}>
|
||||||
<div class="comment-controls">
|
<div class={styles.commentControls}>
|
||||||
<button class="comment-control comment-control--reply">
|
<button
|
||||||
<Icon name="reply" />
|
class={clsx(styles.commentControl, styles.commentControlReply)}
|
||||||
|
onClick={() => setIsReplyVisible(!isReplyVisible())}
|
||||||
|
>
|
||||||
|
<Icon name="reply" class={styles.icon} />
|
||||||
{t('Reply')}
|
{t('Reply')}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Show when={props.canEdit}>
|
<Show when={props.canEdit}>
|
||||||
{/*FIXME implement edit comment modal*/}
|
{/*FIXME implement edit comment modal*/}
|
||||||
{/*<button*/}
|
{/*<button*/}
|
||||||
{/* class="comment-control comment-control--edit"*/}
|
{/* class={clsx(styles.commentControl, styles.commentControlEdit)}*/}
|
||||||
{/* onClick={() => showModal('editComment')}*/}
|
{/* onClick={() => showModal('editComment')}*/}
|
||||||
{/*>*/}
|
{/*>*/}
|
||||||
{/* <Icon name="edit" />*/}
|
{/* <Icon name="edit" class={styles.icon} />*/}
|
||||||
{/* {t('Edit')}*/}
|
{/* {t('Edit')}*/}
|
||||||
{/*</button>*/}
|
{/*</button>*/}
|
||||||
<button class="comment-control comment-control--delete" onClick={() => remove()}>
|
<button
|
||||||
<Icon name="delete" />
|
class={clsx(styles.commentControl, styles.commentControlDelete)}
|
||||||
|
onClick={() => remove()}
|
||||||
|
>
|
||||||
|
<Icon name="delete" class={styles.icon} />
|
||||||
{t('Delete')}
|
{t('Delete')}
|
||||||
</button>
|
</button>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
{/*FIXME implement modals */}
|
<SharePopup
|
||||||
|
onVisibilityChange={(isVisible) => {
|
||||||
|
setIsSharePopupVisible(isVisible)
|
||||||
|
}}
|
||||||
|
containerCssClass={stylesHeader.control}
|
||||||
|
trigger={
|
||||||
|
<button class={clsx(styles.commentControl, styles.commentControlShare)}>
|
||||||
|
<Icon name="share" class={styles.icon} />
|
||||||
|
{t('Share')}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{/*<button*/}
|
{/*<button*/}
|
||||||
{/* class="comment-control comment-control--share"*/}
|
{/* class={clsx(styles.commentControl, styles.commentControlComplain)}*/}
|
||||||
{/* onClick={() => showModal('shareComment')}*/}
|
|
||||||
{/*>*/}
|
|
||||||
{/* {t('Share')}*/}
|
|
||||||
{/*</button>*/}
|
|
||||||
{/*<button*/}
|
|
||||||
{/* class="comment-control comment-control--complain"*/}
|
|
||||||
{/* onClick={() => showModal('reportComment')}*/}
|
{/* onClick={() => showModal('reportComment')}*/}
|
||||||
{/*>*/}
|
{/*>*/}
|
||||||
{/* {t('Report')}*/}
|
{/* {t('Report')}*/}
|
||||||
{/*</button>*/}
|
{/*</button>*/}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Show when={isReplyVisible()}>
|
||||||
|
<form class={styles.replyForm}>
|
||||||
|
<textarea name="reply" id="reply" rows="5"></textarea>
|
||||||
|
<div class={styles.replyFormControls}>
|
||||||
|
<button class="button button--light" onClick={() => setIsReplyVisible(false)}>
|
||||||
|
Отмена
|
||||||
|
</button>
|
||||||
|
<button class="button">Отправить</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import styles from '../../styles/Article.module.scss'
|
||||||
import { useReactionsStore } from '../../stores/zine/reactions'
|
import { useReactionsStore } from '../../stores/zine/reactions'
|
||||||
import { createEffect, createMemo, createSignal, onMount, Suspense } from 'solid-js'
|
import { createEffect, createMemo, createSignal, onMount, Suspense } from 'solid-js'
|
||||||
import type { Reaction } from '../../graphql/types.gen'
|
import type { Reaction } from '../../graphql/types.gen'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
const ARTICLE_COMMENTS_PAGE_SIZE = 50
|
const ARTICLE_COMMENTS_PAGE_SIZE = 50
|
||||||
const MAX_COMMENT_LEVEL = 6
|
const MAX_COMMENT_LEVEL = 6
|
||||||
|
@ -44,10 +45,28 @@ export const CommentsTree = (props: { shout: string; reactions?: Reaction[] }) =
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Show when={reactions()}>
|
<Show when={reactions()}>
|
||||||
<h2 id="comments">
|
<div class={styles.commentsHeaderWrapper}>
|
||||||
|
<h2 id="comments" class={styles.commentsHeader}>
|
||||||
{t('Comments')} {reactions().length.toString() || ''}
|
{t('Comments')} {reactions().length.toString() || ''}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
<ul class={clsx(styles.commentsViewSwitcher, 'view-switcher')}>
|
||||||
|
<li class="selected">
|
||||||
|
<a href="#">По порядку</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">По рейтингу</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class={styles.commentForm}>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<input type="text" id="new-comment" placeholder="Коментарий" />
|
||||||
|
<label for="new-comment">Коментарий</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<For each={reactions()}>
|
<For each={reactions()}>
|
||||||
{(reaction: Reaction) => (
|
{(reaction: Reaction) => (
|
||||||
<Comment
|
<Comment
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { capitalize } from '../../utils'
|
import { capitalize, formatDate } from '../../utils'
|
||||||
import './Full.scss'
|
import './Full.scss'
|
||||||
import { Icon } from '../_shared/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
|
@ -16,16 +16,6 @@ interface ArticleProps {
|
||||||
article: Shout
|
article: Shout
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatDate = (date: Date) => {
|
|
||||||
return date
|
|
||||||
.toLocaleDateString('ru', {
|
|
||||||
month: 'long',
|
|
||||||
day: 'numeric',
|
|
||||||
year: 'numeric'
|
|
||||||
})
|
|
||||||
.replace(' г.', '')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FullArticle = (props: ArticleProps) => {
|
export const FullArticle = (props: ArticleProps) => {
|
||||||
const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt)))
|
const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt)))
|
||||||
const [isSharePopupVisible, setIsSharePopupVisible] = createSignal(false)
|
const [isSharePopupVisible, setIsSharePopupVisible] = createSignal(false)
|
||||||
|
@ -90,7 +80,7 @@ export const FullArticle = (props: ArticleProps) => {
|
||||||
<div class="col-md-8 shift-content">
|
<div class="col-md-8 shift-content">
|
||||||
<div class={styles.shoutStats}>
|
<div class={styles.shoutStats}>
|
||||||
<div class={styles.shoutStatsItem}>
|
<div class={styles.shoutStatsItem}>
|
||||||
<RatingControl rating={props.article.stat?.rating} />
|
<RatingControl rating={props.article.stat?.rating} class={styles.ratingControl} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={styles.shoutStatsItem}>
|
<div class={styles.shoutStatsItem}>
|
||||||
|
@ -139,6 +129,11 @@ export const FullArticle = (props: ArticleProps) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class={styles.help}>
|
||||||
|
<button class="button">Соучаствовать</button>
|
||||||
|
<button class="button button--light">Пригласить к участию</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class={styles.topicsList}>
|
<div class={styles.topicsList}>
|
||||||
<For each={props.article.topics}>
|
<For each={props.article.topics}>
|
||||||
{(topic) => (
|
{(topic) => (
|
||||||
|
@ -155,7 +150,7 @@ export const FullArticle = (props: ArticleProps) => {
|
||||||
</Show>
|
</Show>
|
||||||
<For each={props.article?.authors}>
|
<For each={props.article?.authors}>
|
||||||
{(a: Author) => (
|
{(a: Author) => (
|
||||||
<div class="col-md-6">
|
<div class="col-xl-6">
|
||||||
<AuthorCard author={a} compact={false} hasLink={true} liteButtons={true} />
|
<AuthorCard author={a} compact={false} hasLink={true} liteButtons={true} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -49,7 +49,10 @@
|
||||||
.authorSubscribe {
|
.authorSubscribe {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(sm) {
|
||||||
padding: 0 0 0 42px;
|
padding: 0 0 0 42px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
background: #f7f7f7;
|
background: #f7f7f7;
|
||||||
|
@ -256,3 +259,14 @@
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.authorComments {
|
||||||
|
.authorName {
|
||||||
|
@include font-size(1.2rem);
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circlewrap {
|
||||||
|
margin-top: -0.6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ interface AuthorCardProps {
|
||||||
isAuthorsList?: boolean
|
isAuthorsList?: boolean
|
||||||
truncateBio?: boolean
|
truncateBio?: boolean
|
||||||
liteButtons?: boolean
|
liteButtons?: boolean
|
||||||
|
isComments?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AuthorCard = (props: AuthorCardProps) => {
|
export const AuthorCard = (props: AuthorCardProps) => {
|
||||||
|
@ -47,6 +48,7 @@ export const AuthorCard = (props: AuthorCardProps) => {
|
||||||
class={clsx(styles.author)}
|
class={clsx(styles.author)}
|
||||||
classList={{
|
classList={{
|
||||||
[styles.authorPage]: props.isAuthorPage,
|
[styles.authorPage]: props.isAuthorPage,
|
||||||
|
[styles.authorComments]: props.isComments,
|
||||||
[styles.authorsListItem]: props.isAuthorsList
|
[styles.authorsListItem]: props.isAuthorsList
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -55,6 +57,7 @@ export const AuthorCard = (props: AuthorCardProps) => {
|
||||||
hasLink={props.hasLink}
|
hasLink={props.hasLink}
|
||||||
isBig={props.isAuthorPage}
|
isBig={props.isAuthorPage}
|
||||||
isAuthorsList={props.isAuthorsList}
|
isAuthorsList={props.isAuthorsList}
|
||||||
|
class={styles.circlewrap}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class={styles.authorDetails}>
|
<div class={styles.authorDetails}>
|
||||||
|
|
|
@ -77,7 +77,9 @@ img {
|
||||||
}
|
}
|
||||||
|
|
||||||
.shoutAuthorsList {
|
.shoutAuthorsList {
|
||||||
margin-top: 2em;
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
margin: 2em 0;
|
||||||
|
padding-bottom: 2em;
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
color: #696969;
|
color: #696969;
|
||||||
|
@ -118,15 +120,18 @@ img {
|
||||||
}
|
}
|
||||||
|
|
||||||
.shoutStats {
|
.shoutStats {
|
||||||
border-bottom: 1px solid #e8e8e8;
|
|
||||||
border-top: 4px solid #000;
|
border-top: 4px solid #000;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
padding: 3.2rem 0;
|
padding: 3rem 0 0;
|
||||||
|
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.shoutStatsItem {
|
.shoutStatsItem {
|
||||||
@include font-size(1.7rem);
|
@include font-size(1.5rem);
|
||||||
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -179,18 +184,32 @@ img {
|
||||||
.icon {
|
.icon {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
flex: 1 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.shoutStatsItemAdditionalDataItem {
|
.shoutStatsItemAdditionalDataItem {
|
||||||
|
font-weight: normal;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 2rem;
|
margin-left: 2rem;
|
||||||
|
margin-right: 0;
|
||||||
|
|
||||||
|
@include media-breakpoint-down(sm) {
|
||||||
|
&:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.topicsList {
|
.topicsList {
|
||||||
@include font-size(1.2rem);
|
@include font-size(1.2rem);
|
||||||
|
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
margin: 1.6rem 0;
|
margin-top: 1.6rem;
|
||||||
|
padding-bottom: 1.6rem;
|
||||||
|
|
||||||
.shoutTopic {
|
.shoutTopic {
|
||||||
display: inline-block;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -67,3 +67,17 @@ export const snake2camel = (s: string) =>
|
||||||
.split(/(?=[A-Z])/)
|
.split(/(?=[A-Z])/)
|
||||||
.join('-')
|
.join('-')
|
||||||
.toLowerCase()
|
.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(' г.', '')
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user