Skip to content

Commit

Permalink
feat(markdown): images grid
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Jan 13, 2024
1 parent 868dbcb commit 5e08d59
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 45 deletions.
20 changes: 6 additions & 14 deletions src/components/ui/image/ZoomedImage.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
'use client'

import { isServer } from '@tanstack/react-query'
import {
forwardRef,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import { forwardRef, useCallback, useMemo, useRef, useState } from 'react'
import { useIsomorphicLayoutEffect } from 'foxact/use-isomorphic-layout-effect'
import mediumZoom from 'medium-zoom'
import Image from 'next/image'
import { tv } from 'tailwind-variants'
Expand All @@ -17,7 +10,7 @@ import type { FC, ReactNode } from 'react'

import { LazyLoad } from '~/components/common/Lazyload'
import { useIsUnMounted } from '~/hooks/common/use-is-unmounted'
import { isDev } from '~/lib/env'
import { isDev, isServerSide } from '~/lib/env'
import { clsxm } from '~/lib/helper'
import { calculateDimensions } from '~/lib/image'
import { useMarkdownImageRecord } from '~/providers/article/MarkdownImageRecordProvider'
Expand Down Expand Up @@ -69,14 +62,13 @@ export const ImageLazy: Component<TImageProps & BaseImageProps> = ({
height,
width,
}) => {
// @ts-ignore
const [zoomer_] = useState(() => {
if (isServer) return null
if (isServerSide) return null!
if (zoomer) return zoomer
const zoom = mediumZoom(undefined)
zoomer = zoom
return zoom
}) as [Zoom]
})

const figcaption = title || alt
const [imageLoadStatus, setImageLoadStatus] = useState(
Expand All @@ -92,7 +84,7 @@ export const ImageLazy: Component<TImageProps & BaseImageProps> = ({
[isUnmount],
)
const imageRef = useRef<HTMLImageElement>(null)
useEffect(() => {
useIsomorphicLayoutEffect(() => {
if (imageLoadStatus !== ImageLoadStatus.Loaded) {
return
}
Expand Down
39 changes: 30 additions & 9 deletions src/components/ui/markdown/customize.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,9 @@ https://loremflickr.com/640/480/city?3
![](https://loremflickr.com/640/480/city?4 'Image')
:::


### Grid

```md

::: grid {cols=3,gap=4}

Grid 1
Expand All @@ -128,7 +126,6 @@ Grid 3

https://loremflickr.com/640/480/city?1


https://loremflickr.com/640/480/city?2

https://loremflickr.com/640/480/city?3
Expand All @@ -152,19 +149,42 @@ Grid 3

https://loremflickr.com/640/480/city?1


https://loremflickr.com/640/480/city?2

https://loremflickr.com/640/480/city?3

![](https://loremflickr.com/640/480/city?4 'Image')
![](https://loremflickr.com/640/480/city?4)

![](https://loremflickr.com/640/480/city?4 'Image')
![](https://loremflickr.com/640/480/city?6)

![](https://loremflickr.com/640/480/city?4 'Image')
![](https://loremflickr.com/640/480/city?40)

:::

### Images Grid

```md
::: grid {cols=2,rows=2,gap=4,type=images}
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
:::
```

::: grid {cols=3,rows=3,gap=12,type=images}
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
https://loremflickr.com/640/480/city?4
:::


## Rich Link

```
Expand Down Expand Up @@ -215,7 +235,6 @@ https://github.com/Innei/Shiro/pull/129

https://github.com/Innei/Shiro/pull/129


```
https://github.com/Innei/Shiro/commit/6957e011439eb2d3cbf42bfb67ed81b07d4bcc2a
```
Expand Down Expand Up @@ -315,6 +334,7 @@ $ c = \pm\sqrt{a^2 + b^2} $
```
$c = \pm\sqrt{a^2 + b^2}$
```

$c = \pm\sqrt{a^2 + b^2}$

$P(x) = a_nx^n+a_{n-1}x^{n-1} + \dots + a_1x + a_0$
Expand All @@ -335,6 +355,7 @@ $$
P\left(U,T\right)=100\left.\left(0.6\min\left(1,\frac{U-0.70}{0.90-0.70}\right)+0.4\min\left(1,\frac{T-4000}{14000-4000}\right)\right)\right.
$$

-
-
92 changes: 72 additions & 20 deletions src/components/ui/markdown/parsers/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import React, { Fragment } from 'react'
import { Priority } from 'markdown-to-jsx'
import type { MarkdownToJSX } from 'markdown-to-jsx'

import { clsxm } from '~/lib/helper'

import { Banner } from '../../banner/Banner'
import { Gallery } from '../../gallery/Gallery'
import { Markdown } from '../Markdown'
import { GridMarkdownImage, GridMarkdownImages } from '../renderers/image'
import { pickImagesFromMarkdown } from '../utils/image'

const shouldCatchContainerName = [
Expand Down Expand Up @@ -99,27 +102,58 @@ export const ContainerRule: MarkdownToJSX.Rule = {
}

case 'grid': {
// cols=2,gap=4
// cols=2,gap=4,rows=2

const cols = params?.match(/cols=(?<cols>\d+)/)?.groups?.cols || 2
const gap = params?.match(/gap=(?<gap>\d+)/)?.groups?.gap || 4
return (
<div
className="grid w-full"
style={{
gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
gap: `${gap}px`,
}}
key={state?.key}
>
<Markdown
value={content}
allowsScript
removeWrapper
className="w-full [&>p:first-child]:mt-0"
/>
</div>
)
const { cols, gap = 8, rows, type = 'normal' } = parseParams(params)

const Grid: Component = ({ children, className }) => {
return (
<div
className={clsxm('relative grid w-full', className)}
style={{
gridTemplateColumns: cols
? `repeat(${cols}, minmax(0, 1fr))`
: undefined,
gap: `${gap}px`,
gridTemplateRows: rows
? `repeat(${rows}, minmax(0, 1fr))`
: undefined,
}}
>
{children}
</div>
)
}
switch (type) {
case 'normal': {
return (
<Grid key={state?.key}>
<Markdown
overrides={{
img: GridMarkdownImage,
}}
value={content}
allowsScript
removeWrapper
className="w-full [&>p:first-child]:mt-0"
/>
</Grid>
)
}
case 'images': {
const imagesSrc = content.split('\n').filter(Boolean) as string[]

return (
<GridMarkdownImages
key={state.key}
imagesSrc={imagesSrc}
Wrapper={Grid}
/>
)
}
default:
return null
}
}
}

Expand All @@ -140,3 +174,21 @@ export const ContainerRule: MarkdownToJSX.Rule = {
* ![name](url)
* :::
*/

type ParsedResult = {
[key: string]: string
}

function parseParams(input: string): ParsedResult {
const regex = /(\w+)=(\w+)/g
let match: RegExpExecArray | null
const result: ParsedResult = {}

while ((match = regex.exec(input)) !== null) {
const key = match[1]
const value = match[2]
result[key] = value
}

return result
}
82 changes: 80 additions & 2 deletions src/components/ui/markdown/renderers/image.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
'use client'

import React from 'react'
import React, { memo, useRef, useState } from 'react'
import { useIsomorphicLayoutEffect } from 'foxact/use-isomorphic-layout-effect'
import mediumZoom from 'medium-zoom'
import type { Zoom } from 'medium-zoom'
import type { FC } from 'react'

import { isServerSide } from '~/lib/env'
import { isVideoExt } from '~/lib/mine-type'
import { useWrappedElementSize } from '~/providers/shared/WrappedElementProvider'
import { useMarkdownImageRecord } from '~/providers/article/MarkdownImageRecordProvider'
import {
useWrappedElementSize,
WrappedElementProvider,
} from '~/providers/shared/WrappedElementProvider'

import { Divider } from '../../divider/Divider'
import { FixedZoomedImage } from '../../image/ZoomedImage'
Expand Down Expand Up @@ -34,3 +43,72 @@ export const MarkdownImage = (props: any) => {

return <FixedZoomedImage {...nextProps} containerWidth={w} />
}

export const GridMarkdownImage = (props: any) => {
return (
<WrappedElementProvider>
<div className="relative flex min-w-0 flex-grow">
<MarkdownImage {...props} />
</div>
</WrappedElementProvider>
)
}

export const GridMarkdownImages: FC<{
imagesSrc: string[]
Wrapper: Component
}> = ({ imagesSrc, Wrapper }) => {
return (
<div className="relative pb-[100%]">
<Wrapper className="absolute inset-0">
{imagesSrc.map((src) => {
return <GridZoomImage key={src} src={src} />
})}
</Wrapper>
</div>
)
}

let zoomer: Zoom
const GridZoomImage: FC<{ src: string }> = memo(({ src }) => {
const { accent } = useMarkdownImageRecord(src) || {}

const [zoomer_] = useState(() => {
if (isServerSide) return null
if (zoomer) return zoomer
const zoom = mediumZoom(undefined)
zoomer = zoom
return zoom
})

const imageEl = useRef<HTMLImageElement>(null)

useIsomorphicLayoutEffect(() => {
if (!zoomer_) return
const $image = imageEl.current
if (!$image) return

zoomer_.attach($image)
return () => {
zoomer_.detach($image)
}
}, [])

return (
<div
className="relative h-full w-full rounded-md bg-cover bg-center"
style={{
backgroundImage: `url(${src})`,
backgroundColor: accent,
}}
>
<img
src={src}
ref={imageEl}
className="absolute inset-0 z-[1] !mx-0 !my-0 h-full w-full cursor-zoom-in opacity-0"
/>
</div>
)
})

GridZoomImage.displayName = 'GridZoomImage'
2 changes: 2 additions & 0 deletions src/styles/image-zoom.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

.medium-zoom-overlay + .medium-zoom-image {
z-index: 100;
opacity: 1;
transition: all 0.5s ease-in-out;
}

.medium-zoom-image {
Expand Down

1 comment on commit 5e08d59

@vercel
Copy link

@vercel vercel bot commented on 5e08d59 Jan 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

shiro – ./

shiro-innei.vercel.app
shiro-git-main-innei.vercel.app
innei.in
springtide.vercel.app

Please sign in to comment.