parent
2c7e98acdf
commit
4121d99a73
|
@ -36,6 +36,7 @@ img {
|
||||||
img {
|
img {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote,
|
blockquote,
|
||||||
|
|
|
@ -27,6 +27,7 @@ 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'
|
import { FeedArticlePopup } from '../Feed/FeedArticlePopup'
|
||||||
|
import { Lightbox } from '../_shared/Lightbox'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
article: Shout
|
article: Shout
|
||||||
|
@ -49,6 +50,8 @@ const scrollTo = (el: HTMLElement) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FullArticle = (props: Props) => {
|
export const FullArticle = (props: Props) => {
|
||||||
|
const [selectedImage, setSelectedImage] = createSignal('')
|
||||||
|
|
||||||
const { t, formatDate } = useLocalize()
|
const { t, formatDate } = useLocalize()
|
||||||
const {
|
const {
|
||||||
user,
|
user,
|
||||||
|
@ -169,7 +172,7 @@ export const FullArticle = (props: Props) => {
|
||||||
document.body.appendChild(tooltip)
|
document.body.appendChild(tooltip)
|
||||||
|
|
||||||
if (element.hasAttribute('href')) {
|
if (element.hasAttribute('href')) {
|
||||||
element.setAttribute('href', 'javascript: void(0);')
|
element.setAttribute('href', 'javascript: void(0)')
|
||||||
}
|
}
|
||||||
|
|
||||||
const popperInstance = createPopper(element, tooltip, {
|
const popperInstance = createPopper(element, tooltip, {
|
||||||
|
@ -230,7 +233,19 @@ export const FullArticle = (props: Props) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
|
const openLightbox = (image) => {
|
||||||
|
setSelectedImage(image)
|
||||||
|
}
|
||||||
|
const handleLightboxClose = () => {
|
||||||
|
setSelectedImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleArticleBodyClick = (event) => {
|
||||||
|
if (event.target.tagName === 'IMG') {
|
||||||
|
const src = event.target.src
|
||||||
|
openLightbox(getImageUrl(src))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -313,7 +328,7 @@ export const FullArticle = (props: Props) => {
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={body()}>
|
<Show when={body()}>
|
||||||
<div id="shoutBody" class={styles.shoutBody}>
|
<div id="shoutBody" class={styles.shoutBody} onClick={handleArticleBodyClick}>
|
||||||
<Show when={!body().startsWith('<')} fallback={<div innerHTML={body()} />}>
|
<Show when={!body().startsWith('<')} fallback={<div innerHTML={body()} />}>
|
||||||
<MD body={body()} />
|
<MD body={body()} />
|
||||||
</Show>
|
</Show>
|
||||||
|
@ -433,7 +448,6 @@ export const FullArticle = (props: Props) => {
|
||||||
description={getDescription(props.article.body)}
|
description={getDescription(props.article.body)}
|
||||||
imageUrl={props.article.cover}
|
imageUrl={props.article.cover}
|
||||||
shareUrl={getShareUrl({ pathname: `/${props.article.slug}` })}
|
shareUrl={getShareUrl({ pathname: `/${props.article.slug}` })}
|
||||||
isVisible={(value) => setIsActionPopupActive(value)}
|
|
||||||
trigger={
|
trigger={
|
||||||
<button>
|
<button>
|
||||||
<Icon name="ellipsis" class={clsx(styles.icon)} />
|
<Icon name="ellipsis" class={clsx(styles.icon)} />
|
||||||
|
@ -490,6 +504,9 @@ export const FullArticle = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={selectedImage()}>
|
||||||
|
<Lightbox image={selectedImage()} onClose={handleLightboxClose} />
|
||||||
|
</Show>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
52
src/components/_shared/Lightbox/Lightbox.module.scss
Normal file
52
src/components/_shared/Lightbox/Lightbox.module.scss
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
.Lightbox {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgb(0 0 0 / 80%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10000;
|
||||||
|
|
||||||
|
.image {
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 80%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 40px;
|
||||||
|
font-size: 30px;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zoomControls {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 16px;
|
||||||
|
left: 50%;
|
||||||
|
height: 24px;
|
||||||
|
gap: 1rem;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
.control {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/components/_shared/Lightbox/Lightbox.tsx
Normal file
56
src/components/_shared/Lightbox/Lightbox.tsx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './Lightbox.module.scss'
|
||||||
|
import { createSignal } from 'solid-js'
|
||||||
|
import { Icon } from '../Icon'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
class?: string
|
||||||
|
image: string
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ZOOM_STEP = 1.08
|
||||||
|
export const Lightbox = (props: Props) => {
|
||||||
|
const [zoomLevel, setZoomLevel] = createSignal(1)
|
||||||
|
|
||||||
|
const closeLightbox = () => {
|
||||||
|
props.onClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
const zoomIn = (event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
setZoomLevel(zoomLevel() * ZOOM_STEP)
|
||||||
|
}
|
||||||
|
const zoomOut = (event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
setZoomLevel(zoomLevel() / ZOOM_STEP)
|
||||||
|
}
|
||||||
|
|
||||||
|
const lightboxStyle = () => ({
|
||||||
|
transform: `scale(${zoomLevel()})`,
|
||||||
|
transition: 'transform 0.3s ease'
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={clsx(styles.Lightbox, props.class)} onClick={closeLightbox}>
|
||||||
|
<span class={styles.close} onClick={closeLightbox}>
|
||||||
|
<Icon name="close-white" />
|
||||||
|
</span>
|
||||||
|
<div class={styles.zoomControls}>
|
||||||
|
<button class={styles.control} onClick={(event) => zoomOut(event)}>
|
||||||
|
<b>-</b>
|
||||||
|
</button>
|
||||||
|
<button class={styles.control} onClick={(event) => zoomIn(event)}>
|
||||||
|
<b>+</b>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
class={styles.image}
|
||||||
|
src={props.image}
|
||||||
|
style={lightboxStyle()}
|
||||||
|
alt={''}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
src/components/_shared/Lightbox/index.ts
Normal file
1
src/components/_shared/Lightbox/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { Lightbox } from './Lightbox'
|
|
@ -69,9 +69,6 @@ const useProfileForm = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createEffect(() => {
|
|
||||||
console.log('!!! FL:', form.links)
|
|
||||||
})
|
|
||||||
|
|
||||||
return { form, submit, updateFormField, slugError }
|
return { form, submit, updateFormField, slugError }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user