feat(components): ✨ refactor and add BlogCard
and BlogPost
components
This commit is contained in:
parent
7cafcb5acd
commit
10f36ff4d3
164
components/BlogBlocks.css
Normal file
164
components/BlogBlocks.css
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
.components__blog_block {
|
||||||
|
min-width: 10rem;
|
||||||
|
aspect-ratio: 3 / 4;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-shadow: 0 0 0.4rem 0.2rem var(--_translucent);
|
||||||
|
border: var(--_border-size) solid transparent;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 80%;
|
||||||
|
background-position: center var(--_gap);
|
||||||
|
backdrop-filter: blur(var(--_blur));
|
||||||
|
background-color: var(--_background-color);
|
||||||
|
|
||||||
|
&:has(a:focus-visible),
|
||||||
|
&:hover {
|
||||||
|
border: var(--_border-size) solid var(--_accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
& h3 {
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--_gap) var(--_gap-half);
|
||||||
|
backdrop-filter: blur(var(--_blur));
|
||||||
|
background-color: var(--_translucent);
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
& a {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block--card {
|
||||||
|
max-width: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block--placeholder {
|
||||||
|
animation: var(--animation-blink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block--fallback {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(.components__blog_block--placeholder, .components__blog_block--fallback) {
|
||||||
|
h3 {
|
||||||
|
flex-grow: 1;
|
||||||
|
border: none;
|
||||||
|
align-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block__spacer {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block--card .components__blog_block__spacer {
|
||||||
|
height: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block__links {
|
||||||
|
height: fit-content;
|
||||||
|
display: flex;
|
||||||
|
gap: var(--_gap-half);
|
||||||
|
justify-content: start;
|
||||||
|
padding: var(--_gap-half);
|
||||||
|
backdrop-filter: blur(var(--_blur));
|
||||||
|
background-color: var(--_translucent);
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
|
||||||
|
& > a::before {
|
||||||
|
content: '🔗';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block__tags {
|
||||||
|
height: fit-content;
|
||||||
|
display: flex;
|
||||||
|
gap: var(--_gap-half);
|
||||||
|
justify-content: start;
|
||||||
|
padding: var(--_gap-half);
|
||||||
|
backdrop-filter: blur(var(--_blur));
|
||||||
|
background-color: var(--_translucent);
|
||||||
|
border-bottom: 1px solid currentColor;
|
||||||
|
|
||||||
|
& > span::before {
|
||||||
|
content: '#';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block__status {
|
||||||
|
display: block;
|
||||||
|
padding: var(--_gap-half);
|
||||||
|
background-color: var(--_translucent);
|
||||||
|
backdrop-filter: blur(var(--_blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block--card .components__blog_block__status {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--_gap-half);
|
||||||
|
left: var(--_gap-half);
|
||||||
|
font-size: larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block__publisher {
|
||||||
|
display: block;
|
||||||
|
padding: var(--_gap-half);
|
||||||
|
background-color: var(--_translucent);
|
||||||
|
backdrop-filter: blur(var(--_blur));
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block--card .components__blog_block__publisher {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--_gap-half);
|
||||||
|
right: var(--_gap-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block__description {
|
||||||
|
text-wrap: balance;
|
||||||
|
flex-grow: 1;
|
||||||
|
backdrop-filter: blur(var(--_blur));
|
||||||
|
padding: var(--_gap-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block--card .components__blog_block__description {
|
||||||
|
min-height: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block__body {
|
||||||
|
text-wrap: balance;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: var(--_gap-half);
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block__footer {
|
||||||
|
height: fit-content;
|
||||||
|
display: flex;
|
||||||
|
gap: var(--_gap);
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: var(--_gap-half);
|
||||||
|
background-color: var(--_background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_post__infos {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--_gap-half);
|
||||||
|
margin-block: var(--_gap-half);
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
border: none;
|
||||||
|
padding: var(--_gap-half);
|
||||||
|
background-color: var(--_translucent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_post__description {
|
||||||
|
margin-block: var(--_gap-half);
|
||||||
|
font-family: var(--_font-family-code);
|
||||||
|
}
|
||||||
|
|
||||||
|
.components__blog_block--post {
|
||||||
|
width: var(--_readable-screen);
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
157
components/BlogBlocks.tsx
Normal file
157
components/BlogBlocks.tsx
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
import { Markdown } from ':components/Markdown.tsx'
|
||||||
|
import { NewsFrontMatter } from ':src/blog/types.ts'
|
||||||
|
|
||||||
|
export type BlogProps = {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
body: string
|
||||||
|
author: string
|
||||||
|
publisher: string
|
||||||
|
lastUpdate: Date
|
||||||
|
name: string
|
||||||
|
url: string
|
||||||
|
hash: string
|
||||||
|
options: NewsFrontMatter['x-cohabit']
|
||||||
|
tags: NewsFrontMatter['tags']
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BlogCard(
|
||||||
|
{ title, description, author, lastUpdate, name, options, tags, publisher }:
|
||||||
|
BlogProps,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class='components__blog_block components__blog_block--card'
|
||||||
|
style={{ backgroundImage: `url(${options.thumbnail})` }}
|
||||||
|
>
|
||||||
|
<div class='components__blog_block__spacer'></div>
|
||||||
|
<h3>
|
||||||
|
<a href={`/blog/${name}`}>{title}</a>
|
||||||
|
</h3>
|
||||||
|
<NewsLinks links={options.links} />
|
||||||
|
<NewsTags tags={tags} />
|
||||||
|
<span class='components__blog_block__publisher'>
|
||||||
|
{`@${publisher}`}
|
||||||
|
</span>
|
||||||
|
<NewsStatus status={options.status} />
|
||||||
|
<div class='components__blog_block__description'>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
<NewsFooter author={author} lastUpdate={lastUpdate} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BlogPost(
|
||||||
|
{
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
author,
|
||||||
|
lastUpdate,
|
||||||
|
body,
|
||||||
|
url,
|
||||||
|
options,
|
||||||
|
tags,
|
||||||
|
publisher,
|
||||||
|
}: BlogProps,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<div class='components__blog_block--post'>
|
||||||
|
<h1>{title}</h1>
|
||||||
|
<div class='components__blog_post__infos'>
|
||||||
|
<span class='components__blog_block__publisher'>
|
||||||
|
{`@${publisher}`}
|
||||||
|
</span>
|
||||||
|
<NewsStatus status={options.status} long />
|
||||||
|
<NewsLinks links={options.links} />
|
||||||
|
<NewsTags tags={tags} />
|
||||||
|
</div>
|
||||||
|
<div class='components__blog_post__infos'>
|
||||||
|
<span>{`Visibilité : ${options.visibility}`}</span>
|
||||||
|
<span>
|
||||||
|
{`Date de délivrance : ${
|
||||||
|
new Date(options.dueDate).toLocaleString()
|
||||||
|
}`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class='components__blog_post__description'>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
<div class='components__blog_post__body'>
|
||||||
|
<Markdown options={{ allowMath: true, baseUrl: url }}>
|
||||||
|
{body}
|
||||||
|
</Markdown>
|
||||||
|
</div>
|
||||||
|
<NewsFooter author={author} lastUpdate={lastUpdate} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NewsTags({ tags }: Pick<BlogProps, 'tags'>) {
|
||||||
|
return (
|
||||||
|
<div class='components__blog_block__tags'>
|
||||||
|
{tags
|
||||||
|
? tags.map((tag) => <span>{tag}</span>)
|
||||||
|
: <span>Aucun tag</span>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NewsFooter(
|
||||||
|
{ author, lastUpdate }: Pick<BlogProps, 'author' | 'lastUpdate'>,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<div class='components__blog_block__footer'>
|
||||||
|
<div>
|
||||||
|
<i class='ri-quill-pen-line'></i>
|
||||||
|
<span>{author}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<i class='ri-refresh-line'></i>
|
||||||
|
<span>{lastUpdate.toLocaleDateString()}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NewsLinks({ links }: Pick<BlogProps['options'], 'links'>) {
|
||||||
|
return (
|
||||||
|
<div class='components__blog_block__links'>
|
||||||
|
{links
|
||||||
|
? links.flatMap(Object.entries).map((
|
||||||
|
[name, link],
|
||||||
|
) => <a href={link} target='_blank' title={name}>{name}</a>)
|
||||||
|
: <span>Aucun lien rapide</span>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NewsStatus(
|
||||||
|
{ status, long = false }: Pick<BlogProps['options'], 'status'> & {
|
||||||
|
long?: boolean
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const title = status === 'canceled'
|
||||||
|
? 'Annulé'
|
||||||
|
: status === 'current'
|
||||||
|
? 'En cours'
|
||||||
|
: status === 'finished'
|
||||||
|
? 'Terminé'
|
||||||
|
: 'Prévu'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
class='components__blog_block__status'
|
||||||
|
title={title}
|
||||||
|
>
|
||||||
|
{status === 'canceled'
|
||||||
|
? <i class='ri-calendar-close-line'></i>
|
||||||
|
: status === 'current'
|
||||||
|
? <i class='ri-calendar-2-line'></i>
|
||||||
|
: status === 'finished'
|
||||||
|
? <i class='ri-calendar-check-line'></i>
|
||||||
|
: <i class='ri-calendar-2-line'></i>}
|
||||||
|
{long ? ` ${title}` : ''}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,41 +0,0 @@
|
||||||
.components__blog_card {
|
|
||||||
min-width: 10rem;
|
|
||||||
aspect-ratio: 3 / 4;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: var(--_gap-half);
|
|
||||||
gap: var(--_gap);
|
|
||||||
box-shadow: 0 0 0.4rem 0.2rem var(--_translucent);
|
|
||||||
border: var(--_border-size) solid transparent;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
|
|
||||||
&:has(a:focus-visible),
|
|
||||||
&:hover {
|
|
||||||
border: var(--_border-size) solid var(--_accent-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
& h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
& a {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.components__blog_card__spacer {
|
|
||||||
height: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.components__blog_card__text {
|
|
||||||
text-wrap: balance;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.components__blog_card__footer {
|
|
||||||
height: fit-content;
|
|
||||||
display: flex;
|
|
||||||
gap: var(--_gap);
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
type BlogCardProps = {
|
|
||||||
img: string
|
|
||||||
title: string
|
|
||||||
text: string
|
|
||||||
author: string
|
|
||||||
lasUpdate: Date
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BlogCard(
|
|
||||||
{ img, title, text, author, lasUpdate, id }: BlogCardProps,
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<div class='components__blog_card' style={{ backgroundImage: img }}>
|
|
||||||
<div class='components__blog_card__spacer'></div>
|
|
||||||
<h3>
|
|
||||||
<a href={`/blog/${id}`}>{title}</a>
|
|
||||||
</h3>
|
|
||||||
<div class='components__blog_card__text'>
|
|
||||||
{`${text.slice(0, 150)} ...`}
|
|
||||||
</div>
|
|
||||||
<div class='components__blog_card__footer'>
|
|
||||||
<div>
|
|
||||||
<i class='ri-quill-pen-line'></i>
|
|
||||||
<span>{author}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<i class='ri-refresh-line'></i>
|
|
||||||
<span>{lasUpdate.toLocaleDateString()}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const text =
|
|
||||||
'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui, perferendis enim blanditiis consequatur at porro quod, eligendi alias recusandae modi aliquam non? Quos voluptates quisquam provident animi nisi in ratione.'
|
|
||||||
|
|
||||||
export const blogMock: BlogCardProps[] = Array(50).fill(undefined).map(
|
|
||||||
(_, index) => {
|
|
||||||
return {
|
|
||||||
author: 'PGP',
|
|
||||||
lasUpdate: randomDate(),
|
|
||||||
title: `Some title here ${index}`,
|
|
||||||
text,
|
|
||||||
img: `url("https://picsum.photos/id/${index}/300/200")`,
|
|
||||||
id: String(index),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
function randomDate() {
|
|
||||||
return new Date(Date.now() - Math.random() * 1e10)
|
|
||||||
}
|
|
|
@ -44,7 +44,10 @@
|
||||||
"@simplewebauthn/server": "npm:@simplewebauthn/server@^10.0.0",
|
"@simplewebauthn/server": "npm:@simplewebauthn/server@^10.0.0",
|
||||||
"@simplewebauthn/types": "npm:@simplewebauthn/types@^10.0.0",
|
"@simplewebauthn/types": "npm:@simplewebauthn/types@^10.0.0",
|
||||||
"@std/encoding": "jsr:@std/encoding@^0.224.3",
|
"@std/encoding": "jsr:@std/encoding@^0.224.3",
|
||||||
|
"@std/front-matter": "jsr:@std/front-matter@^0.224.2",
|
||||||
"@std/http": "jsr:@std/http@^0.224.4",
|
"@std/http": "jsr:@std/http@^0.224.4",
|
||||||
|
"@std/json": "jsr:@std/json@^0.224.1",
|
||||||
|
"@std/streams": "jsr:@std/streams@^0.224.5",
|
||||||
"@univoq/": "https://deno.land/x/univoq@0.2.0/",
|
"@univoq/": "https://deno.land/x/univoq@0.2.0/",
|
||||||
"gfm": "https://deno.land/x/gfm@0.6.0/mod.ts",
|
"gfm": "https://deno.land/x/gfm@0.6.0/mod.ts",
|
||||||
"preact": "https://esm.sh/preact@10.19.6",
|
"preact": "https://esm.sh/preact@10.19.6",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
@import url('../../components/Heros.css');
|
@import url('../../components/Heros.css');
|
||||||
@import url('../../components/SponsorCards.css');
|
@import url('../../components/SponsorCards.css');
|
||||||
@import url('../../components/CohabitInfoTable.css');
|
@import url('../../components/CohabitInfoTable.css');
|
||||||
@import url('../../components/BlogCard.css');
|
@import url('../../components/BlogBlocks.css');
|
||||||
@import url('../../components/MachineCard.css');
|
@import url('../../components/MachineCard.css');
|
||||||
@import url('../../components/ProjectCard.css');
|
@import url('../../components/ProjectCard.css');
|
||||||
@import url('../../components/AutoGrid.css');
|
@import url('../../components/AutoGrid.css');
|
||||||
|
|
Loading…
Reference in a new issue