feat(components): add new Picture component

This commit is contained in:
Julien Oculi 2024-07-03 14:46:04 +02:00
parent a2f872ae9d
commit 28397a383a
5 changed files with 58 additions and 1 deletions

28
components/Picture.tsx Normal file
View file

@ -0,0 +1,28 @@
import { unwrapSignalOrValue } from ':src/utils.ts'
import { Ensure } from ':types'
import { JSX } from 'preact'
export type PictureProps =
& { formats: string[] }
& Ensure<JSX.HTMLAttributes<HTMLImageElement>, 'src' | 'alt' | 'loading'>
export function Picture(
{ formats, src, ...props }: PictureProps,
) {
const groups = unwrapSignalOrValue(src)?.match(/(?<path>.*)(?<ext>\.\w+)/)
?.groups
if (groups === undefined) {
throw new SyntaxError(`unable to parse path of "${src.valueOf()}"`)
}
const { path } = groups
return (
<picture>
{formats.map((format) => (
<source type={`image/${format}`} srcset={`${path}.${format}`} />
))}
<img src={src} {...props} />
</picture>
)
}

View file

@ -1,4 +1,5 @@
import { asset } from '$fresh/runtime.ts'
import { Picture } from ':components/Picture.tsx'
export function SponsorCards() {
return (
@ -13,7 +14,12 @@ function SponsorCard(
) {
return (
<a class='components__sponsor_card' href={href} target='_blank'>
<img src={asset(src)} alt={alt} />
<Picture
src={asset(src)}
alt={alt}
loading={'lazy'}
formats={['avif']}
/>
</a>
)
}

View file

@ -34,6 +34,7 @@
":components/": "./components/",
":islands/": "./islands/",
":src/": "./src/",
":types": "./types.ts",
"@cohabit/cohamail/": "./packages/@cohabit__cohamail@0.2.1/",
"@cohabit/ressources_manager/": "./packages/@cohabit__ressources_manager@0.1.3/",
"@deno/gfm": "jsr:@deno/gfm@^0.8.2",

View file

@ -1,3 +1,4 @@
import { SignalLike } from '$fresh/src/types.ts'
import { JsonValue } from '$std/json/common.ts'
import { decodeBase64 } from '@std/encoding/base64'
import { JsonStringifyStream } from '@std/json'
@ -171,3 +172,22 @@ export function base64ToString(base64: string): string {
const bytes = decodeBase64(base64)
return new TextDecoder().decode(bytes)
}
export function unwrapSignalOrValue<T>(valueOrSignal: T | SignalLike<T>): T {
if (typeof valueOrSignal !== 'object') {
return valueOrSignal
}
if (valueOrSignal === null) {
return valueOrSignal
}
if (
'value' in valueOrSignal && 'peek' in valueOrSignal &&
'subscribe' in valueOrSignal
) {
return valueOrSignal.value
}
return valueOrSignal
}

View file

@ -4,3 +4,5 @@ export interface User {
mail: string
groupes: string[]
}
export type Ensure<T, K extends keyof T> = T & Required<Pick<T, K>>