Merge remote-tracking branch 'gitlab/dev' into minor-fixes

This commit is contained in:
bniwredyc 2023-02-10 12:15:22 +01:00
commit 20ecc28ad9
3 changed files with 346 additions and 152 deletions

View File

@ -89,153 +89,165 @@ export const FullArticle = (props: ArticleProps) => {
})
return (
<div class="shout wide-container">
<article class="col-md-6 shift-content">
<div class={styles.shoutHeader}>
<div class={styles.shoutTopic}>
<a href={`/topic/${props.article.mainTopic}`} class={styles.mainTopicLink}>
{mainTopic().title}
</a>
</div>
<>
<div class="shout wide-container">
<article class="col-md-6 shift-content">
<div class={styles.shoutHeader}>
<div class={styles.shoutTopic}>
<a
href={getPagePath(router, 'topic', { slug: props.article.mainTopic })}
class={styles.mainTopicLink}
>
{mainTopic().title}
</a>
</div>
<h1>{props.article.title}</h1>
<Show when={props.article.subtitle}>
<h4>{capitalize(props.article.subtitle, false)}</h4>
</Show>
<h1>{props.article.title}</h1>
<Show when={props.article.subtitle}>
<h4>{capitalize(props.article.subtitle, false)}</h4>
</Show>
<div class={styles.shoutAuthor}>
<For each={props.article.authors}>
{(a: Author, index) => (
<>
<Show when={index() > 0}>, </Show>
<a href={getPagePath(router, 'author', { slug: a.slug })}>{a.name}</a>
</>
)}
</For>
</div>
<div class={styles.shoutCover} style={{ 'background-image': `url('${props.article.cover}')` }} />
</div>
<Show
when={media() && props.article.layout !== 'image'}
fallback={
<Slider>
<For each={media() || []}>
{(m: MediaItem) => (
<div class={styles.shoutAuthor}>
<For each={props.article.authors}>
{(a: Author, index) => (
<>
<img src={m.url || m.pic} alt={m.title} />
<div innerHTML={m.body} />
<Show when={index() > 0}>, </Show>
<a href={getPagePath(router, 'author', { slug: a.slug })}>{a.name}</a>
</>
)}
</For>
</Slider>
}
>
<div class="media-items">
<For each={media() || []}>
{(m: MediaItem) => (
<div class={styles.shoutMediaBody}>
<MediaView media={m} kind={props.article.layout} />
<Show when={m?.body}>
<div innerHTML={m.body} />
</Show>
</div>
<div
class={styles.shoutCover}
style={{ 'background-image': `url('${props.article.cover}')` }}
/>
</div>
<Show when={media() && props.article.layout !== 'image'}>
<div class="media-items">
<For each={media() || []}>
{(m: MediaItem) => (
<div class={styles.shoutMediaBody}>
<MediaView media={m} kind={props.article.layout} />
<Show when={m?.body}>
<div innerHTML={m.body} />
</Show>
</div>
)}
</For>
</div>
</Show>
<Show when={body()}>
<div class={styles.shoutBody}>
<Show when={!body().startsWith('<')} fallback={<div innerHTML={body()} />}>
<MD body={body()} />
</Show>
</div>
</Show>
</article>
</div>
<Show when={media() && props.article.layout === 'image'}>
<Slider slidesPerView={1} isPageGallery={true} isCardsWithCover={true} hasThumbs={true}>
<For each={media() || []}>
{(m: MediaItem) => (
<div class="swiper-slide">
<div class="swiper-slide__inner">
<img src={m.url || m.pic} alt={m.title} loading="lazy" />
<div class="swiper-lazy-preloader swiper-lazy-preloader-white" />
<div class="image-description" innerHTML={m.title} />
</div>
</div>
)}
</For>
</Slider>
</Show>
<div class="shout wide-container">
<div class="col-md-8 shift-content">
<div class={styles.shoutStats}>
<div class={styles.shoutStatsItem}>
<RatingControl rating={props.article.stat?.rating} class={styles.ratingControl} />
</div>
<Show when={props.article.stat?.viewed}>
<div class={clsx(styles.shoutStatsItem)}>
<Icon name="eye" class={clsx(styles.icon, styles.iconEye)} />
{props.article.stat?.viewed}
</div>
</Show>
<div class={styles.shoutStatsItem}>
<Icon name="comment" class={styles.icon} />
{props.article.stat?.commented || ''}
</div>
<div class={styles.shoutStatsItem}>
<SharePopup
title={props.article.title}
description={getDescription(props.article.body)}
imageUrl={props.article.cover}
containerCssClass={stylesHeader.control}
trigger={<Icon name="share-outline" class={styles.icon} />}
/>
</div>
<div class={styles.shoutStatsItem} onClick={bookmark}>
<Icon name="bookmark" class={styles.icon} />
</div>
<Show when={canEdit()}>
<div class={styles.shoutStatsItem}>
<a href="/edit">
<Icon name="edit" />
{t('Edit')}
</a>
</div>
</Show>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalData)}>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalDataItem)}>
{formattedDate()}
</div>
</div>
</div>
<div class={styles.help}>
<Show when={session()?.token}>
<button class="button">{t('Cooperate')}</button>
</Show>
<Show when={canEdit()}>
<button class="button button--light">{t('Invite to collab')}</button>
</Show>
</div>
<div class={styles.topicsList}>
<For each={props.article.topics}>
{(topic) => (
<div class={styles.shoutTopic}>
<a href={getPagePath(router, 'topic', { slug: topic.slug })}>{topic.title}</a>
</div>
)}
</For>
</div>
</Show>
<Show when={body()}>
<div class={styles.shoutBody}>
<Show when={!body().startsWith('<')} fallback={<div innerHTML={body()} />}>
<MD body={body()} />
<div class={styles.shoutAuthorsList}>
<Show when={props.article?.authors?.length > 1}>
<h4>{t('Authors')}</h4>
</Show>
<For each={props.article?.authors}>
{(a: Author) => (
<div class="col-xl-6">
<AuthorCard author={a} compact={false} hasLink={true} liteButtons={true} />
</div>
)}
</For>
</div>
</Show>
</article>
<div class="col-md-8 shift-content">
<div class={styles.shoutStats}>
<div class={styles.shoutStatsItem}>
<RatingControl rating={props.article.stat?.rating} class={styles.ratingControl} />
</div>
<Show when={props.article.stat?.viewed}>
<div class={clsx(styles.shoutStatsItem)}>
<Icon name="eye" class={clsx(styles.icon, styles.iconEye)} />
{props.article.stat?.viewed}
</div>
</Show>
<div class={styles.shoutStatsItem}>
<Icon name="comment" class={styles.icon} />
{props.article.stat?.commented || ''}
</div>
<div class={styles.shoutStatsItem}>
<SharePopup
title={props.article.title}
description={getDescription(props.article.body)}
imageUrl={props.article.cover}
containerCssClass={stylesHeader.control}
trigger={<Icon name="share-outline" class={styles.icon} />}
/>
</div>
<div class={styles.shoutStatsItem} onClick={bookmark}>
<Icon name="bookmark" class={styles.icon} />
</div>
<Show when={canEdit()}>
<div class={styles.shoutStatsItem}>
<a href="/edit">
<Icon name="edit" />
{t('Edit')}
</a>
</div>
</Show>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalData)}>
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalDataItem)}>
{formattedDate()}
</div>
</div>
<CommentsTree
shoutId={props.article?.id}
shoutSlug={props.article?.slug}
commentAuthors={props.article?.authors}
/>
</div>
<div class={styles.help}>
<Show when={session()?.token}>
<button class="button">{t('Cooperate')}</button>
</Show>
<Show when={canEdit()}>
<button class="button button--light">{t('Invite to collab')}</button>
</Show>
</div>
<div class={styles.topicsList}>
<For each={props.article.topics}>
{(topic) => (
<div class={styles.shoutTopic}>
<a href={getPagePath(router, 'topic', { slug: topic.slug })}>{topic.title}</a>
</div>
)}
</For>
</div>
<div class={styles.shoutAuthorsList}>
<Show when={props.article?.authors?.length > 1}>
<h4>{t('Authors')}</h4>
</Show>
<For each={props.article?.authors}>
{(a: Author) => (
<div class="col-xl-6">
<AuthorCard author={a} compact={false} hasLink={true} liteButtons={true} />
</div>
)}
</For>
</div>
<CommentsTree
shoutId={props.article?.id}
shoutSlug={props.article?.slug}
commentAuthors={props.article?.authors}
/>
</div>
</div>
</>
)
}

View File

@ -1,6 +1,5 @@
.swiper-slide {
min-height: 0 !important;
margin-bottom: 0 !important;
.cards-with-cover & {
height: 0 !important;
@ -13,6 +12,16 @@
@include media-breakpoint-up(md) {
padding-top: 35% !important;
}
img {
height: 100%;
left: 0;
object-fit: cover;
object-position: center;
position: absolute;
top: 0;
width: 100%;
}
}
}
@ -88,3 +97,117 @@
}
}
}
.swiper--page-gallery {
padding-bottom: 4rem;
.swiper-wrapper {
align-items: center;
}
.swiper-slide {
display: flex;
justify-content: center;
}
.swiper-slide__inner {
display: flex;
flex-direction: column;
justify-content: center;
img {
display: block;
height: auto;
margin: 0 auto;
max-height: 80vh;
width: auto;
position: relative;
z-index: 11;
}
}
.slider-arrow-prev,
.slider-arrow-next {
background: rgb(0 0 0 / 0.2);
width: 5rem;
}
.slider-arrow-next .icon {
margin-right: 2rem;
}
.slider-arrow-prev .icon {
margin-left: 2rem;
}
.swiper-slide-active {
.swiper-lazy-preloader {
display: none;
}
}
}
.thumbs-container {
@include media-breakpoint-up(md) {
padding-left: 3.2rem;
}
}
.swiper--thumbs {
@include media-breakpoint-up(md) {
max-height: 80vh;
min-width: 100px;
padding: 0 !important;
width: 100px !important;
}
.swiper-slide {
cursor: pointer;
height: 80px;
opacity: 0.5;
width: 100px;
@include media-breakpoint-up(md) {
height: 52px;
width: auto;
}
img {
height: 100%;
object-fit: cover;
object-position: center;
width: 100%;
}
}
.swiper-slide-thumb-active {
opacity: 1;
}
.swiper-slide__inner {
height: 100%;
flex: 1;
}
.swiper-lazy-preloader,
.image-description {
display: none;
}
}
.sliders-container {
@include media-breakpoint-up(md) {
display: flex;
}
}
.swiper-pagination {
background: #141414;
bottom: 0;
font-size: 1.2rem;
font-weight: bold;
left: auto;
right: 0;
padding: 1rem;
width: auto;
}

View File

@ -1,40 +1,46 @@
import { Swiper, Navigation, Pagination } from 'swiper'
import { Swiper, Navigation, Pagination, Lazy, Thumbs } from 'swiper'
import type { SwiperOptions } from 'swiper'
import 'swiper/scss'
import 'swiper/scss/navigation'
import 'swiper/scss/pagination'
import 'swiper/scss/lazy'
import 'swiper/scss/thumbs'
import './Slider.scss'
import { createEffect, createSignal, JSX } from 'solid-js'
import { createEffect, createSignal, JSX, Show } from 'solid-js'
import { Icon } from './Icon'
import { clsx } from 'clsx'
interface SliderProps {
title?: string
slidesPerView?: number
isCardsWithCover?: boolean
children?: JSX.Element
class?: string
isPageGallery?: boolean
hasThumbs?: boolean
}
export default (props: SliderProps) => {
let el: HTMLDivElement | undefined
let thumbsEl: HTMLDivElement | undefined
let pagEl: HTMLDivElement | undefined
let nextEl: HTMLDivElement | undefined
let prevEl: HTMLDivElement | undefined
const isCardsWithCover = typeof props.isCardsWithCover === 'boolean' ? props.isCardsWithCover : true
const [swiper, setSwiper] = createSignal<Swiper>()
const [swiperThumbs, setSwiperThumbs] = createSignal<Swiper>()
const opts: SwiperOptions = {
lazy: true,
roundLengths: true,
loop: true,
centeredSlides: true,
slidesPerView: 1,
modules: [Navigation, Pagination],
modules: [Navigation, Pagination, Lazy, Thumbs],
speed: 500,
navigation: { nextEl, prevEl },
pagination: {
el: pagEl,
type: 'bullets',
clickable: true
},
breakpoints: {
768: {
slidesPerView: props.slidesPerView > 0 ? props.slidesPerView : 1.66666,
@ -44,13 +50,48 @@ export default (props: SliderProps) => {
slidesPerView: props.slidesPerView > 0 ? props.slidesPerView : 1.66666,
spaceBetween: isCardsWithCover ? 8 : 52
}
},
thumbs: {
swiper: swiperThumbs()
}
}
const [swiper, setSwiper] = createSignal<Swiper>()
createEffect(() => {
if (props.hasThumbs && !!thumbsEl) {
setTimeout(() => {
setSwiperThumbs(
new Swiper(thumbsEl, {
slidesPerView: 'auto',
modules: [Lazy, Thumbs],
lazy: true,
roundLengths: true,
spaceBetween: 20,
freeMode: true,
breakpoints: {
768: {
direction: 'vertical'
}
}
})
)
}, 500)
}
})
createEffect(() => {
if (!swiper() && !!el) {
setTimeout(() => {
if (swiperThumbs()) {
opts.thumbs = {
swiper: swiperThumbs()
}
opts.pagination = {
el: '.swiper-pagination',
type: 'fraction'
}
}
setSwiper(new Swiper(el, opts))
}, 500)
}
@ -61,15 +102,33 @@ export default (props: SliderProps) => {
<div class="wide-container">
<div class="row">
<h2 class="col-12">{props.title}</h2>
<div class="swiper" classList={{ 'cards-with-cover': isCardsWithCover }} ref={el}>
<div class="swiper-wrapper">{props.children}</div>
<div class="slider-arrow-next" ref={nextEl} onClick={() => swiper()?.slideNext()}>
<Icon name="slider-arrow" class={'icon'} />
<div class="sliders-container">
<div
class={clsx('swiper', props.class)}
classList={{
'cards-with-cover': isCardsWithCover,
'swiper--page-gallery': props.isPageGallery
}}
ref={el}
>
<div class="swiper-wrapper">{props.children}</div>
<div class="slider-arrow-next" ref={nextEl} onClick={() => swiper()?.slideNext()}>
<Icon name="slider-arrow" class={'icon'} />
</div>
<div class="slider-arrow-prev" ref={prevEl} onClick={() => swiper()?.slidePrev()}>
<Icon name="slider-arrow" class={'icon'} />
</div>
<div class="swiper-pagination" ref={pagEl} />
</div>
<div class="slider-arrow-prev" ref={prevEl} onClick={() => swiper()?.slidePrev()}>
<Icon name="slider-arrow" class={'icon'} />
</div>
<div class="slider-pagination" ref={pagEl} />
<Show when={props.hasThumbs}>
<div class="thumbs-container">
<div class="swiper swiper--thumbs" ref={thumbsEl}>
<div class="swiper-wrapper">{props.children}</div>
</div>
</div>
</Show>
</div>
</div>
</div>