On the article page make article footer same as on the feed page

This commit is contained in:
kvakazyambra 2023-11-06 22:15:13 +03:00
parent 38ab3ddc4b
commit 2a29e69d21
9 changed files with 136 additions and 54 deletions

View File

@ -299,8 +299,9 @@ img {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
padding: 3rem 0 0; padding: 3rem 0 0;
position: relative;
@include media-breakpoint-down(sm) { @include media-breakpoint-down(lg) {
flex-wrap: wrap; flex-wrap: wrap;
} }
} }
@ -311,11 +312,11 @@ img {
align-items: center; align-items: center;
font-weight: 500; font-weight: 500;
display: flex; display: flex;
margin: 0 6% 1em 0; margin: 0 2rem 1em 0;
vertical-align: baseline; vertical-align: baseline;
cursor: pointer; cursor: pointer;
@include media-breakpoint-up(sm) { @include media-breakpoint-up(xl) {
margin-right: 3.2rem; margin-right: 3.2rem;
} }
@ -359,6 +360,14 @@ img {
} }
} }
.shoutStatsItemBookmarks {
margin-left: auto;
@include media-breakpoint-up(lg) {
margin-left: 0;
}
}
.shoutStatsItemInner { .shoutStatsItemInner {
cursor: pointer; cursor: pointer;
@ -382,32 +391,41 @@ img {
.shoutStatsItemAdditionalData { .shoutStatsItemAdditionalData {
color: rgb(0 0 0 / 40%); color: rgb(0 0 0 / 40%);
cursor: default;
font-weight: normal; font-weight: normal;
justify-self: flex-end; justify-self: flex-end;
margin-right: 0;
margin-left: auto;
white-space: nowrap; white-space: nowrap;
cursor: default;
.icon { .icon {
opacity: 0.4; opacity: 0.4;
height: 2rem; height: 2rem;
} }
@include media-breakpoint-down(sm) { @include media-breakpoint-down(lg) {
flex: 1 40%; flex: 1 100%;
order: 9;
.shoutStatsItemAdditionalDataItem {
margin-left: 0;
}
} }
} }
.shoutStatsItemViews { .shoutStatsItemViews {
color: rgb(0 0 0 / 0.4);
cursor: default; cursor: default;
font-weight: normal;
margin-left: auto;
white-space: nowrap;
@include media-breakpoint-down(sm) { @include media-breakpoint-down(lg) {
color: rgb(0 0 0 / 40%); bottom: 0;
flex: 1 40%; flex: 1 40%;
justify-content: end; justify-content: end;
margin-right: 0; margin-right: 0;
order: 10; order: 10;
position: absolute;
right: 0;
.icon { .icon {
display: none !important; display: none !important;
@ -416,15 +434,20 @@ img {
} }
.shoutStatsItemLabel { .shoutStatsItemLabel {
font-weight: normal;
margin-left: 0.3em; margin-left: 0.3em;
}
.commentsTextLabel {
display: none;
@include media-breakpoint-up(sm) { @include media-breakpoint-up(sm) {
display: none; display: block;
} }
} }
.shoutStatsItemCount { .shoutStatsItemCount {
@include media-breakpoint-down(sm) { @include media-breakpoint-down(lg) {
display: none; display: none;
} }
} }
@ -432,7 +455,7 @@ img {
.shoutStatsItemAdditionalDataItem { .shoutStatsItemAdditionalDataItem {
font-weight: normal; font-weight: normal;
display: inline-block; display: inline-block;
margin-left: 2rem; //margin-left: 2rem;
margin-right: 0; margin-right: 0;
margin-bottom: 0; margin-bottom: 0;
cursor: default; cursor: default;
@ -486,8 +509,10 @@ img {
} }
.commentsHeaderWrapper { .commentsHeaderWrapper {
@include media-breakpoint-up(sm) {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
}
} }
.commentsHeader { .commentsHeader {
@ -623,3 +648,19 @@ a[data-toggle='tooltip'] {
font-weight: 700; font-weight: 700;
} }
} }
.articlePopupOpener {
.iconHover {
display: none;
}
&:hover {
.icon {
display: none;
}
.iconHover {
display: inline-block;
}
}
}

View File

@ -4,16 +4,17 @@
transition: background-color 0.3s; transition: background-color 0.3s;
position: relative; position: relative;
list-style: none; list-style: none;
background: rgb(0 0 0 / 0.1);
@include media-breakpoint-down(sm) {
padding-right: 0;
}
&.isNew { &.isNew {
border-radius: 6px; border-radius: 6px;
background: rgb(38 56 217 / 5%); background: rgb(38 56 217 / 5%);
} }
@include media-breakpoint-down(sm) {
margin-right: -1.2rem;
}
.comment { .comment {
margin-right: -1rem; margin-right: -1rem;
@ -66,6 +67,8 @@
} }
.commentContent { .commentContent {
padding: 0 1rem 1rem 0;
&:hover { &:hover {
.commentControlReply, .commentControlReply,
.commentControlShare, .commentControlShare,
@ -76,6 +79,10 @@
opacity: 1; opacity: 1;
} }
} }
p:last-child {
margin-bottom: 0;
}
} }
.commentControlReply, .commentControlReply,

View File

@ -12,7 +12,7 @@ import { DEFAULT_HEADER_OFFSET, router, useRouter } from '../../stores/router'
import { getDescription } from '../../utils/meta' import { getDescription } from '../../utils/meta'
import { TableOfContents } from '../TableOfContents' import { TableOfContents } from '../TableOfContents'
import { AudioPlayer } from './AudioPlayer' import { AudioPlayer } from './AudioPlayer'
import { SharePopup } from './SharePopup' import { getShareUrl, SharePopup } from './SharePopup'
import { ShoutRatingControl } from './ShoutRatingControl' import { ShoutRatingControl } from './ShoutRatingControl'
import { CommentsTree } from './CommentsTree' import { CommentsTree } from './CommentsTree'
import stylesHeader from '../Nav/Header/Header.module.scss' import stylesHeader from '../Nav/Header/Header.module.scss'
@ -26,6 +26,7 @@ import { CardTopic } from '../Feed/CardTopic'
import { createPopper } from '@popperjs/core' import { createPopper } from '@popperjs/core'
import { AuthorBadge } from '../Author/AuthorBadge' import { AuthorBadge } from '../Author/AuthorBadge'
import { getImageUrl } from '../../utils/getImageUrl' import { getImageUrl } from '../../utils/getImageUrl'
import { FeedArticlePopup } from '../Feed/FeedArticlePopup'
type Props = { type Props = {
article: Shout article: Shout
@ -229,6 +230,8 @@ export const FullArticle = (props: Props) => {
}) })
}) })
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
return ( return (
<> <>
<Title>{props.article.title}</Title> <Title>{props.article.title}</Title>
@ -348,25 +351,43 @@ export const FullArticle = (props: Props) => {
<Popover content={t('Comment')}> <Popover content={t('Comment')}>
{(triggerRef: (el) => void) => ( {(triggerRef: (el) => void) => (
<div class={styles.shoutStatsItem} ref={triggerRef} onClick={scrollToComments}> <div class={clsx(styles.shoutStatsItem)} ref={triggerRef} onClick={scrollToComments}>
<Icon name="comment" class={styles.icon} /> <Icon name="comment" class={styles.icon} />
<Icon name="comment-hover" class={clsx(styles.icon, styles.iconHover)} /> <Icon name="comment-hover" class={clsx(styles.icon, styles.iconHover)} />
{props.article.stat?.commented ?? ''} <span class={styles.commentsTextLabel}>
{props.article.stat?.commented || t('Add' + ' comment')}
</span>
</div> </div>
)} )}
</Popover> </Popover>
<Show when={props.article.stat?.viewed}> <Show when={props.article.stat?.viewed}>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemViews)}> <div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemViews)}>
<Icon name="eye" class={styles.icon} />
<Icon name="eye" class={clsx(styles.icon, styles.iconHover)} />
<span class={styles.shoutStatsItemCount}>{props.article.stat?.viewed}</span>
<span class={styles.shoutStatsItemLabel}>
{t('viewsWithCount', { count: props.article.stat?.viewed })} {t('viewsWithCount', { count: props.article.stat?.viewed })}
</span>
</div> </div>
</Show> </Show>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalData)}>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalDataItem)}>
{formattedDate()}
</div>
</div>
<Popover content={t('Add to bookmarks')}>
{(triggerRef: (el) => void) => (
<div
class={clsx(styles.shoutStatsItem, styles.shoutStatsItemBookmarks)}
ref={triggerRef}
onClick={handleBookmarkButtonClick}
>
<div class={styles.shoutStatsItemInner}>
<Icon name="bookmark" class={styles.icon} />
<Icon name="bookmark-hover" class={clsx(styles.icon, styles.iconHover)} />
</div>
</div>
)}
</Popover>
<Popover content={t('Share')}> <Popover content={t('Share')}>
{(triggerRef: (el) => void) => ( {(triggerRef: (el) => void) => (
<div class={styles.shoutStatsItem} ref={triggerRef}> <div class={styles.shoutStatsItem} ref={triggerRef}>
@ -385,16 +406,7 @@ export const FullArticle = (props: Props) => {
</div> </div>
)} )}
</Popover> </Popover>
<Popover content={t('Add to bookmarks')}>
{(triggerRef: (el) => void) => (
<div class={styles.shoutStatsItem} ref={triggerRef} onClick={handleBookmarkButtonClick}>
<div class={styles.shoutStatsItemInner}>
<Icon name="bookmark" class={styles.icon} />
<Icon name="bookmark-hover" class={clsx(styles.icon, styles.iconHover)} />
</div>
</div>
)}
</Popover>
<Show when={canEdit()}> <Show when={canEdit()}>
<Popover content={t('Edit')}> <Popover content={t('Edit')}>
{(triggerRef: (el) => void) => ( {(triggerRef: (el) => void) => (
@ -410,20 +422,34 @@ export const FullArticle = (props: Props) => {
)} )}
</Popover> </Popover>
</Show> </Show>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalData)}>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalDataItem)}> <FeedArticlePopup
{formattedDate()} isOwner={canEdit()}
containerCssClass={clsx(stylesHeader.control, styles.articlePopupOpener)}
title={props.article.title}
description={getDescription(props.article.body)}
imageUrl={props.article.cover}
shareUrl={getShareUrl({ pathname: `/${props.article.slug}` })}
isVisible={(value) => setIsActionPopupActive(value)}
trigger={
<button>
<Icon name="ellipsis" class={clsx(styles.icon)} />
<Icon name="ellipsis" class={clsx(styles.icon, styles.iconHover)} />
</button>
}
/>
</div> </div>
</div>
</div>
<div class={styles.help}>
<Show when={isAuthenticated() && !canEdit()}> <Show when={isAuthenticated() && !canEdit()}>
<div class={styles.help}>
<button class="button">{t('Cooperate')}</button> <button class="button">{t('Cooperate')}</button>
</div>
</Show> </Show>
<Show when={canEdit()}> <Show when={canEdit()}>
<div class={styles.help}>
<button class="button button--light">{t('Invite to collab')}</button> <button class="button button--light">{t('Invite to collab')}</button>
</Show>
</div> </div>
</Show>
<Show when={props.article.topics.length}> <Show when={props.article.topics.length}>
<div class={styles.topicsList}> <div class={styles.topicsList}>

View File

@ -42,7 +42,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
line-height: 1.3; line-height: 1.3;
margin-bottom: 1rem;
@include media-breakpoint-up(sm) { @include media-breakpoint-up(sm) {
flex: 1 100%; flex: 1 100%;

View File

@ -579,6 +579,12 @@
} }
} }
.shoutCardDetailsItemLabel {
@include media-breakpoint-down(sm) {
display: none;
}
}
.shoutCardComments, .shoutCardComments,
.shoutCardDetailsViewed { .shoutCardDetailsViewed {
align-items: center; align-items: center;

View File

@ -230,7 +230,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
name="comment-hover" name="comment-hover"
class={clsx(styles.icon, styles.iconHover, styles.feedControlIcon)} class={clsx(styles.icon, styles.iconHover, styles.feedControlIcon)}
/> />
<span class={styles.shoutCardLinkContainer}> <span class={clsx(styles.shoutCardLinkContainer, styles.shoutCardDetailsItemLabel)}>
{props.article.stat?.commented || t('Add comment')} {props.article.stat?.commented || t('Add comment')}
</span> </span>
</a> </a>

View File

@ -3,8 +3,9 @@
border: 1px solid rgb(0 0 0 / 15%); border: 1px solid rgb(0 0 0 / 15%);
border-radius: 1.6rem; border-radius: 1.6rem;
padding: 1.6rem !important; padding: 1.6rem !important;
text-align: left;
@include media-breakpoint-between(sm, md) { @include media-breakpoint-down(md) {
left: auto !important; left: auto !important;
right: 0; right: 0;
transform: none !important; transform: none !important;
@ -13,6 +14,8 @@
button { button {
font-size: inherit; font-size: inherit;
font-weight: 500; font-weight: 500;
text-align: left;
white-space: nowrap;
&:hover { &:hover {
background: #000; background: #000;

View File

@ -575,7 +575,7 @@
} }
.userControlItemVerbose { .userControlItemVerbose {
margin-left: 1.2em !important; margin-left: 0.9em !important;
&:first-child { &:first-child {
margin-left: 0 !important; margin-left: 0 !important;

View File

@ -73,7 +73,7 @@ export const LocalizeProvider = (props: { children: JSX.Element }) => {
let result = date.toLocaleDateString(lang(), opts) let result = date.toLocaleDateString(lang(), opts)
if (lang() === 'ru') { if (lang() === 'ru') {
result = result.replace(' г.', '') result = result.replace(' г.', '').replace('г.', '')
} }
return result return result