@samuelgomez/astro 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/package.json +42 -0
- package/src/components/Accordion/index.astro +31 -0
- package/src/components/Alert/AlertDanger.astro +12 -0
- package/src/components/Alert/AlertInfo.astro +12 -0
- package/src/components/Alert/AlertSuccess.astro +12 -0
- package/src/components/Alert/AlertWarning.astro +12 -0
- package/src/components/Alert/index.astro +10 -0
- package/src/components/Badge/BadgeDanger.astro +15 -0
- package/src/components/Badge/BadgeInfo.astro +15 -0
- package/src/components/Badge/BadgeSuccess.astro +15 -0
- package/src/components/Badge/BadgeWarning.astro +15 -0
- package/src/components/Badge/index.astro +9 -0
- package/src/components/Box/index.astro +45 -0
- package/src/components/BoxPost/index.astro +66 -0
- package/src/components/Button/index.astro +35 -0
- package/src/components/Caniuse/index.astro +24 -0
- package/src/components/Codepen/index.astro +25 -0
- package/src/components/DebugGrid/index.astro +100 -0
- package/src/components/Demo/Demoui.astro +13 -0
- package/src/components/Demo/index.astro +105 -0
- package/src/components/Dialog/index.astro +74 -0
- package/src/components/FormattedDate/index.astro +17 -0
- package/src/components/GithubContributions/index.astro +88 -0
- package/src/components/Home/CardsBlog.astro +41 -0
- package/src/components/Home/CardsCodepen.astro +53 -0
- package/src/components/Home/CardsGithub.astro +44 -0
- package/src/components/Img/index.astro +28 -0
- package/src/components/Link/index.astro +33 -0
- package/src/components/Nav/NavToggle.astro +40 -0
- package/src/components/Nav/index.astro +71 -0
- package/src/components/Pagination/PaginationItem.astro +51 -0
- package/src/components/Pagination/index.astro +39 -0
- package/src/components/ScrollWatcher/index.astro +11 -0
- package/src/components/Social/index.astro +22 -0
- package/src/components/Support/index.astro +24 -0
- package/src/components/Svg/index.astro +30 -0
- package/src/components/SwitchTheme.astro +45 -0
- package/src/components/Table/Table.astro +15 -0
- package/src/components/Table/TableBody.astro +21 -0
- package/src/components/Table/TableHeader.astro +19 -0
- package/src/components/Table/index.astro +21 -0
- package/src/components/TableOfContent/index.astro +117 -0
- package/src/components/Tabs/TabList.astro +23 -0
- package/src/components/Tabs/TabListItem.astro +22 -0
- package/src/components/Tabs/TabPanel.astro +19 -0
- package/src/components/Tabs/TabsContainer.astro +174 -0
- package/src/components/Tabs/index.astro +46 -0
- package/src/components/Title/h1.astro +12 -0
- package/src/components/Title/h2.astro +12 -0
- package/src/components/Title/h3.astro +12 -0
- package/src/components/Title/h4.astro +12 -0
- package/src/components/Title/h5.astro +12 -0
- package/src/components/Title/h6.astro +12 -0
- package/src/components/Title/heading.astro +8 -0
- package/src/components/Title/index.astro +39 -0
- package/src/components/WrapperToHtml/index.astro +8 -0
- package/src/components/form/Field/index.astro +43 -0
- package/src/components/form/Fieldset/index.astro +14 -0
- package/src/components/form/Form/FieldDate.astro +32 -0
- package/src/components/form/Form/FieldEmail.astro +32 -0
- package/src/components/form/Form/FieldNumber.astro +35 -0
- package/src/components/form/Form/FieldPassword.astro +32 -0
- package/src/components/form/Form/FieldRadio.astro +32 -0
- package/src/components/form/Form/FieldSelect.astro +26 -0
- package/src/components/form/Form/FieldText.astro +35 -0
- package/src/components/form/Form/FieldTextarea.astro +25 -0
- package/src/components/form/Form/index.astro +5 -0
- package/src/components/form/Input/index.astro +27 -0
- package/src/components/form/InputRadio/index.astro +30 -0
- package/src/components/form/MoreInfo/index.astro +14 -0
- package/src/components/form/Select/index.astro +25 -0
- package/src/components/form/Status/index.astro +34 -0
- package/src/components/form/Textarea/index.astro +26 -0
- package/src/components/helpers/WrapperOrNot/index.astro +23 -0
- package/src/components/layout/Footer.astro +3 -0
- package/src/components/layout/Head.astro +154 -0
- package/src/components/layout/Header.astro +28 -0
- package/src/components/old/Grid.astro +18 -0
- package/src/components/old/Section.astro +19 -0
- package/src/components/old/SwitchTheme.astro +66 -0
- package/src/components/old/index-webco.astro +55 -0
- package/src/components/old/send.astro +28 -0
- package/src/helpers/dom.ts +19 -0
- package/src/helpers/isEmptyOrNull.test.ts +58 -0
- package/src/helpers/isEmptyOrNull.ts +6 -0
- package/src/helpers/setSlug.test.ts +20 -0
- package/src/helpers/setSlug.ts +7 -0
- package/src/helpers/setTocTitle.ts +2 -0
- package/src/helpers/setVariants.test.ts +26 -0
- package/src/helpers/setVariants.ts +18 -0
- package/src/icons/Add.astro +18 -0
- package/src/icons/Anchor.astro +22 -0
- package/src/icons/Arrow.astro +11 -0
- package/src/icons/AstroLogo.astro +35 -0
- package/src/icons/Check.astro +11 -0
- package/src/icons/ChevronDown.astro +11 -0
- package/src/icons/ChevronLeft.astro +11 -0
- package/src/icons/ChevronRight.astro +11 -0
- package/src/icons/Codepen.astro +36 -0
- package/src/icons/Cross.astro +11 -0
- package/src/icons/ExternalLink.astro +19 -0
- package/src/icons/Github.astro +36 -0
- package/src/icons/Github2.astro +11 -0
- package/src/icons/Grid.astro +19 -0
- package/src/icons/Info.astro +11 -0
- package/src/icons/Instagram.astro +11 -0
- package/src/icons/Linkedin.astro +11 -0
- package/src/icons/LogoSG.astro +66 -0
- package/src/icons/MoonSun.astro +17 -0
- package/src/icons/Send.astro +11 -0
- package/src/icons/Slash.astro +14 -0
- package/src/icons/Trash.astro +19 -0
- package/src/icons/Twitter.astro +14 -0
- package/src/types/Permutations.d.ts +3 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
import TabContainer from '@samuelgomez/astro/components/Tabs/TabsContainer.astro';
|
|
3
|
+
import TabPanel from '@samuelgomez/astro/components/Tabs/TabPanel.astro';
|
|
4
|
+
import TabList from '@samuelgomez/astro/components/Tabs/TabList.astro';
|
|
5
|
+
import TabListItem from '@samuelgomez/astro/components/Tabs/TabListItem.astro';
|
|
6
|
+
import WrapperOrNot from '@samuelgomez/astro/components/helpers/WrapperOrNot/index.astro';
|
|
7
|
+
import { Code } from 'astro/components';
|
|
8
|
+
import { setSlug } from '@samuelgomez/astro/helpers/setSlug';
|
|
9
|
+
/* import type { HTMLTag } from 'astro/types'; */
|
|
10
|
+
|
|
11
|
+
const html = await Astro.slots.render('html');
|
|
12
|
+
const css = await Astro.slots.render('css');
|
|
13
|
+
const js = await Astro.slots.render('js');
|
|
14
|
+
const jsx = await Astro.slots.render('code');
|
|
15
|
+
|
|
16
|
+
const { title, showJs, wrapperTag = 'span' } = Astro.props;
|
|
17
|
+
const idTab = title ? setSlug(title) : 'demo-code';
|
|
18
|
+
|
|
19
|
+
let scopedStyle;
|
|
20
|
+
let className = undefined;
|
|
21
|
+
let WrapperDemo = wrapperTag;
|
|
22
|
+
|
|
23
|
+
if (Boolean(css)) {
|
|
24
|
+
const style = css
|
|
25
|
+
?.replace('<code>', '')
|
|
26
|
+
?.replace('</code>', '')
|
|
27
|
+
?.replace('{', '{\r')
|
|
28
|
+
?.replace('}', '\r}')
|
|
29
|
+
?.replace(';', ';\r')
|
|
30
|
+
?.trim?.();
|
|
31
|
+
|
|
32
|
+
className = `demo-${Date.now() + '_' + Math.floor(Math.random() * 10000)}`;
|
|
33
|
+
scopedStyle = `.${className} { * > * { font-size: revert; } ${style} }`;
|
|
34
|
+
WrapperDemo = 'div';
|
|
35
|
+
}
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
<TabContainer
|
|
39
|
+
id={idTab}
|
|
40
|
+
idTitle={`title-${idTab}`}
|
|
41
|
+
title={title}
|
|
42
|
+
>
|
|
43
|
+
<TabList idTitle={`title-${idTab}`}>
|
|
44
|
+
<TabListItem id={idTab} index={0}>Démo</TabListItem>
|
|
45
|
+
<TabListItem id={idTab} index={1}>HTML</TabListItem>
|
|
46
|
+
{
|
|
47
|
+
Boolean(css) && (
|
|
48
|
+
<TabListItem id={idTab} index={2}>
|
|
49
|
+
CSS
|
|
50
|
+
</TabListItem>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
{
|
|
54
|
+
Boolean(js) && showJs && (
|
|
55
|
+
<TabListItem id={idTab} index={3}>
|
|
56
|
+
JS
|
|
57
|
+
</TabListItem>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
{
|
|
61
|
+
Boolean(jsx) && (
|
|
62
|
+
<TabListItem id={idTab} index={4}>
|
|
63
|
+
JSX
|
|
64
|
+
</TabListItem>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
</TabList>
|
|
68
|
+
<TabPanel id={idTab} index={0}>
|
|
69
|
+
<WrapperOrNot
|
|
70
|
+
class={className}
|
|
71
|
+
condition={Boolean(WrapperDemo)}
|
|
72
|
+
wrapper={WrapperDemo}
|
|
73
|
+
>
|
|
74
|
+
<Fragment set:html={html} />
|
|
75
|
+
</WrapperOrNot>
|
|
76
|
+
</TabPanel>
|
|
77
|
+
<TabPanel id={idTab} index={1}>
|
|
78
|
+
<Code code={html} lang="html" />
|
|
79
|
+
</TabPanel>
|
|
80
|
+
{
|
|
81
|
+
Boolean(css) && (
|
|
82
|
+
<TabPanel id={idTab} index={2}>
|
|
83
|
+
<Code code={css} lang="css" />
|
|
84
|
+
</TabPanel>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
{
|
|
88
|
+
Boolean(js) && showJs && (
|
|
89
|
+
<TabPanel id={idTab} index={3}>
|
|
90
|
+
<Code code={js} lang="javascript" />
|
|
91
|
+
</TabPanel>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
{
|
|
95
|
+
Boolean(jsx) && (
|
|
96
|
+
<TabPanel id={idTab} index={4}>
|
|
97
|
+
<Code code={jsx} lang="jsx" />
|
|
98
|
+
</TabPanel>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
</TabContainer>
|
|
102
|
+
|
|
103
|
+
{Boolean(css) && <style set:html={scopedStyle} />}
|
|
104
|
+
|
|
105
|
+
{Boolean(js) && <script is:inline set:html={js} />}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Button, {
|
|
3
|
+
type ButtonProps,
|
|
4
|
+
} from '@samuelgomez/astro/components/Button/index.astro';
|
|
5
|
+
import Title, { type titleLevel } from '@samuelgomez/astro/components/Title/index.astro';
|
|
6
|
+
import Cross from '@samuelgomez/astro/icons/Cross.astro';
|
|
7
|
+
|
|
8
|
+
type Tvariant = 'error' | 'info' | 'success' | 'warning';
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
variant?: Tvariant;
|
|
12
|
+
id: string;
|
|
13
|
+
titleLevel?: titleLevel;
|
|
14
|
+
openerVariant?: ButtonProps['variant'];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
variant = '',
|
|
19
|
+
id,
|
|
20
|
+
titleLevel = 4,
|
|
21
|
+
openerVariant,
|
|
22
|
+
} = Astro.props as Props;
|
|
23
|
+
|
|
24
|
+
const className = Boolean(variant) ? `dialog-${variant}` : null;
|
|
25
|
+
|
|
26
|
+
const footerContent = await Astro.slots.render('footer-content');
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
<Button variant={openerVariant} id={`open-dialog-${id}`}
|
|
30
|
+
><slot name="opener" /></Button
|
|
31
|
+
>
|
|
32
|
+
|
|
33
|
+
<dialog
|
|
34
|
+
id={id}
|
|
35
|
+
class={className}
|
|
36
|
+
onclick="document.querySelector(`#${id}`).close()"
|
|
37
|
+
>
|
|
38
|
+
<div class="dialog-inner">
|
|
39
|
+
<header>
|
|
40
|
+
<Title level={titleLevel}><slot name="title" /></Title>
|
|
41
|
+
<form>
|
|
42
|
+
<Button
|
|
43
|
+
variant="small"
|
|
44
|
+
autofocus
|
|
45
|
+
formmethod="dialog"
|
|
46
|
+
type="submit"
|
|
47
|
+
aria-label="Fermer la boîte de dialogue"><Cross /></Button
|
|
48
|
+
>
|
|
49
|
+
</form>
|
|
50
|
+
</header>
|
|
51
|
+
<slot name="content" />
|
|
52
|
+
{
|
|
53
|
+
Boolean(footerContent) && (
|
|
54
|
+
<footer>
|
|
55
|
+
<slot name="footer-content" />
|
|
56
|
+
</footer>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
</div>
|
|
60
|
+
</dialog>
|
|
61
|
+
|
|
62
|
+
<script is:inline define:vars={{ id }}>
|
|
63
|
+
const dialog = document.querySelector(`#${id}`);
|
|
64
|
+
const openBtn = document.querySelector(`#open-dialog-${id}`);
|
|
65
|
+
const closeBtn = dialog.querySelector("button[data-action='close']");
|
|
66
|
+
const validateBtn = dialog.querySelector("button[data-action='validate']");
|
|
67
|
+
|
|
68
|
+
const innerDialog = dialog.querySelector('.dialog-inner');
|
|
69
|
+
innerDialog?.addEventListener('click', (event) => event.stopPropagation());
|
|
70
|
+
|
|
71
|
+
openBtn?.addEventListener('click', () => dialog.showModal());
|
|
72
|
+
closeBtn?.addEventListener('click', () => dialog.close());
|
|
73
|
+
validateBtn?.addEventListener('click', () => dialog.close());
|
|
74
|
+
</script>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<div class="contribs-wrapper" aria-hidden="true">
|
|
2
|
+
<div class="contribs">
|
|
3
|
+
{[...Array(364)].map(() => <div class="contrib" />)}
|
|
4
|
+
</div>
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--gh-c: hsl(var(--g5) / 20%);
|
|
10
|
+
--gh-size: 15px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.contribs-wrapper {
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
padding: 1rem;
|
|
16
|
+
border: 1px solid hsl(var(--g-h5) / 50%);
|
|
17
|
+
border-radius: 3px;
|
|
18
|
+
background-color: var(--bk);
|
|
19
|
+
width: max-content;
|
|
20
|
+
margin: 0 auto;
|
|
21
|
+
}
|
|
22
|
+
.contribs {
|
|
23
|
+
display: grid;
|
|
24
|
+
grid-template-columns: repeat(var(--gh-cols), var(--gh-size));
|
|
25
|
+
gap: 2px;
|
|
26
|
+
}
|
|
27
|
+
.contrib {
|
|
28
|
+
transition: background-color 0.5s ease-in-out;
|
|
29
|
+
height: var(--gh-size);
|
|
30
|
+
width: var(--gh-size);
|
|
31
|
+
background-color: var(--gh-c);
|
|
32
|
+
border-radius: 3px;
|
|
33
|
+
display: var(--gh-show, none);
|
|
34
|
+
}
|
|
35
|
+
</style>
|
|
36
|
+
|
|
37
|
+
<script>
|
|
38
|
+
function randomIntFromInterval(min: number, max: number) {
|
|
39
|
+
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const grey = 'hsl(var(--g-h5) / 20%)';
|
|
43
|
+
const colors = [
|
|
44
|
+
grey,
|
|
45
|
+
'var(--tk8)',
|
|
46
|
+
grey,
|
|
47
|
+
'var(--tk7)',
|
|
48
|
+
'var(--tk6)',
|
|
49
|
+
grey,
|
|
50
|
+
'var(--tk3)',
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
function setColor(elt: HTMLElement) {
|
|
54
|
+
const randomIndex = randomIntFromInterval(0, colors.length - 1);
|
|
55
|
+
elt.style.setProperty('--gh-c', colors[randomIndex]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const elts = document.querySelectorAll<HTMLElement>('.contrib');
|
|
59
|
+
const wrapper = document.querySelector('.contribs-wrapper');
|
|
60
|
+
|
|
61
|
+
window.addEventListener('load', () => {
|
|
62
|
+
[...elts].forEach(elt => {
|
|
63
|
+
setColor(elt);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
wrapper?.addEventListener('mouseover', startAnimation);
|
|
68
|
+
wrapper?.addEventListener('mouseout', stopAnimation);
|
|
69
|
+
|
|
70
|
+
let intervals = [] as NodeJS.Timeout[];
|
|
71
|
+
|
|
72
|
+
function startAnimation() {
|
|
73
|
+
[...elts].forEach(elt => {
|
|
74
|
+
const randomInterval = randomIntFromInterval(300, 2000);
|
|
75
|
+
const interval = setInterval(() => {
|
|
76
|
+
setColor(elt);
|
|
77
|
+
}, randomInterval);
|
|
78
|
+
intervals.push(interval);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function stopAnimation() {
|
|
83
|
+
intervals.forEach(interval => {
|
|
84
|
+
clearInterval(interval);
|
|
85
|
+
});
|
|
86
|
+
intervals = [];
|
|
87
|
+
}
|
|
88
|
+
</script>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Link from '@samuelgomez/astro/components/Link/index.astro';
|
|
3
|
+
import BoxPost from '@samuelgomez/astro/components/BoxPost/index.astro';
|
|
4
|
+
import ChevronRight from '@samuelgomez/astro/icons/ChevronRight.astro';
|
|
5
|
+
import { getCollection } from 'astro:content';
|
|
6
|
+
import LogoSG from '@samuelgomez/astro/icons/LogoSG.astro';
|
|
7
|
+
import { projectAuth } from '@firebase/config';
|
|
8
|
+
const { currentUser } = projectAuth;
|
|
9
|
+
|
|
10
|
+
let posts = await getCollection('blog');
|
|
11
|
+
|
|
12
|
+
if (!currentUser) {
|
|
13
|
+
posts = posts.filter(({ data }) => data.published);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
posts = posts
|
|
17
|
+
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
|
|
18
|
+
.slice(0, 6);
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
<section>
|
|
22
|
+
<header>
|
|
23
|
+
<LogoSG size="100" />
|
|
24
|
+
<h2>Derniers articles</h2>
|
|
25
|
+
<p>Voici les derniers articles de blog sur le développements Front-end.</p>
|
|
26
|
+
</header>
|
|
27
|
+
<section>
|
|
28
|
+
{
|
|
29
|
+
posts.map(({ slug, data }) => (
|
|
30
|
+
<BoxPost slug={slug} {...data} heroImage={undefined} category="blog">
|
|
31
|
+
{data?.description && <p>{data?.description}</p>}
|
|
32
|
+
</BoxPost>
|
|
33
|
+
))
|
|
34
|
+
}
|
|
35
|
+
</section>
|
|
36
|
+
<footer>
|
|
37
|
+
<Link href="/blog">
|
|
38
|
+
Voir mes articles Front-end<ChevronRight slot="after-link" />
|
|
39
|
+
</Link>
|
|
40
|
+
</footer>
|
|
41
|
+
</section>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Codepen from '@samuelgomez/astro/icons/Codepen.astro';
|
|
3
|
+
import Link from '@samuelgomez/astro/components/Link/index.astro';
|
|
4
|
+
import Box from '@samuelgomez/astro/components/Box/index.astro';
|
|
5
|
+
import ChevronRight from '@samuelgomez/astro/icons/ChevronRight.astro';
|
|
6
|
+
import myImage from '@images/codepen-multiple-gradients.webp';
|
|
7
|
+
import myImage2 from '@images/codepen-button-border-gradient.webp';
|
|
8
|
+
|
|
9
|
+
const cards = [
|
|
10
|
+
{
|
|
11
|
+
src: myImage,
|
|
12
|
+
title: 'Multiple gradients',
|
|
13
|
+
description: 'Utilisation des radial-gradient CSS',
|
|
14
|
+
link: 'https://codepen.io/gsam59/full/vYbbXoa',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
src: myImage2,
|
|
18
|
+
title: 'Button border gradient',
|
|
19
|
+
description: "Création d'un bordure animée avec un dégradé",
|
|
20
|
+
link: 'https://codepen.io/gsam59/full/ExpzOqG',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
src: myImage,
|
|
24
|
+
title: 'Multiple gradients',
|
|
25
|
+
description: 'Utilisation des radial-gradient CSS',
|
|
26
|
+
link: 'https://codepen.io/gsam59/full/vYbbXoa',
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<section>
|
|
32
|
+
<header>
|
|
33
|
+
<Codepen />
|
|
34
|
+
<h2>Derniers Codepen</h2>
|
|
35
|
+
<p>Voici les dernières démos Codepen pour ma veille CSS et HTML</p>
|
|
36
|
+
</header>
|
|
37
|
+
<section>
|
|
38
|
+
{
|
|
39
|
+
cards.map(({ link, ...card }) => (
|
|
40
|
+
<Box {...card} titleLevel={3} variant="center">
|
|
41
|
+
<Link href={link}>
|
|
42
|
+
Voir la démo <ChevronRight slot="after-link" />
|
|
43
|
+
</Link>
|
|
44
|
+
</Box>
|
|
45
|
+
))
|
|
46
|
+
}
|
|
47
|
+
</section>
|
|
48
|
+
<footer>
|
|
49
|
+
<Link href="/about">
|
|
50
|
+
Voir mes Codepens<ChevronRight slot="after-link" />
|
|
51
|
+
</Link>
|
|
52
|
+
</footer>
|
|
53
|
+
</section>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Github from '@samuelgomez/astro/icons/Github.astro';
|
|
3
|
+
import Link from '@samuelgomez/astro/components/Link/index.astro';
|
|
4
|
+
import BoxPost from '@samuelgomez/astro/components/BoxPost/index.astro';
|
|
5
|
+
import ChevronRight from '@samuelgomez/astro/icons/ChevronRight.astro';
|
|
6
|
+
import GithubContributions from '@samuelgomez/astro/components/GithubContributions/index.astro';
|
|
7
|
+
import { getCollection } from 'astro:content';
|
|
8
|
+
import { projectAuth } from '@firebase/config';
|
|
9
|
+
const { currentUser } = projectAuth;
|
|
10
|
+
|
|
11
|
+
let posts = await getCollection('work');
|
|
12
|
+
|
|
13
|
+
if (!currentUser) {
|
|
14
|
+
posts = posts.filter(({ data }) => data.published);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
posts = posts
|
|
18
|
+
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
|
|
19
|
+
.slice(0, 6);
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<section>
|
|
23
|
+
<header>
|
|
24
|
+
<Github />
|
|
25
|
+
<h2>Derniers projets Open Source</h2>
|
|
26
|
+
<p>Voici les derniers projets Open Source sur lequels j'ai travaillé.</p>
|
|
27
|
+
<GithubContributions />
|
|
28
|
+
</header>
|
|
29
|
+
|
|
30
|
+
<section>
|
|
31
|
+
{
|
|
32
|
+
posts.map(({ slug, data }) => (
|
|
33
|
+
<BoxPost slug={slug} {...data} category="work">
|
|
34
|
+
{data?.description && <p>{data?.description}</p>}
|
|
35
|
+
</BoxPost>
|
|
36
|
+
))
|
|
37
|
+
}
|
|
38
|
+
</section>
|
|
39
|
+
<footer>
|
|
40
|
+
<Link href="/work">
|
|
41
|
+
Voir mes projets Open Source<ChevronRight slot="after-link" />
|
|
42
|
+
</Link>
|
|
43
|
+
</footer>
|
|
44
|
+
</section>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { ImageMetadata } from 'astro';
|
|
3
|
+
import { Image } from 'astro:assets';
|
|
4
|
+
import { isEmptyObject } from '@samuelgomez/astro/helpers/isEmptyOrNull';
|
|
5
|
+
|
|
6
|
+
const { src, alt = '' } = Astro.props;
|
|
7
|
+
|
|
8
|
+
let image = {} as ImageMetadata;
|
|
9
|
+
|
|
10
|
+
if (Boolean(src)) {
|
|
11
|
+
const baseImagePath = '/src/assets/images/';
|
|
12
|
+
const imagePath = `${baseImagePath}${src}`;
|
|
13
|
+
const imageGlob = `${baseImagePath}*.{jpeg,jpg,png,gif,webp,svg}`;
|
|
14
|
+
|
|
15
|
+
const images = import.meta.glob<{ default: ImageMetadata }>(
|
|
16
|
+
'/src/assets/images/*.{jpeg,jpg,png,gif,webp,svg}'
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (!images[imagePath]) {
|
|
20
|
+
throw new Error(`"${imagePath}" does not exist in glob: "${imageGlob}"`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { default: imageSrc } = await images[imagePath]();
|
|
24
|
+
image = imageSrc;
|
|
25
|
+
}
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
{!isEmptyObject(image) && <Image src={image} alt={alt} quality="max" />}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
|
|
4
|
+
export type Props = HTMLAttributes<'a'>;
|
|
5
|
+
|
|
6
|
+
const { href = '', ...props } = Astro.props;
|
|
7
|
+
|
|
8
|
+
const url = new URL(href ?? '', Astro.url);
|
|
9
|
+
const isExternal = url.host !== Astro.url.host;
|
|
10
|
+
const pathname = new URL(Astro.request.url).pathname;
|
|
11
|
+
const isCurrent =
|
|
12
|
+
(pathname?.startsWith(href?.toString() ?? '') && href?.toString() !== '/') ||
|
|
13
|
+
(href?.toString() === '/' && pathname === '/');
|
|
14
|
+
|
|
15
|
+
const content = await Astro.slots.render('default');
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<a
|
|
19
|
+
href={href}
|
|
20
|
+
aria-current={isCurrent ? 'page' : null}
|
|
21
|
+
rel={isExternal ? 'noopener' : null}
|
|
22
|
+
{...props}
|
|
23
|
+
>
|
|
24
|
+
<slot name="before-link" />
|
|
25
|
+
{
|
|
26
|
+
Boolean(content) && (
|
|
27
|
+
<span>
|
|
28
|
+
<slot />
|
|
29
|
+
</span>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
<slot name="after-link" /></a
|
|
33
|
+
>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Button from '@samuelgomez/astro/components/Button/index.astro';
|
|
3
|
+
|
|
4
|
+
const { 'aria-controls': ariaControls } = Astro.props;
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<Button
|
|
8
|
+
class="burger"
|
|
9
|
+
variant="ghost"
|
|
10
|
+
aria-label="Toggle Navigation"
|
|
11
|
+
aria-expanded="false"
|
|
12
|
+
aria-controls={ariaControls}
|
|
13
|
+
>
|
|
14
|
+
<div aria-hidden="true"></div>
|
|
15
|
+
<div aria-hidden="true"></div>
|
|
16
|
+
<div aria-hidden="true"></div>
|
|
17
|
+
</Button>
|
|
18
|
+
|
|
19
|
+
<script>
|
|
20
|
+
const main = document.querySelector('main');
|
|
21
|
+
const footer = document.querySelector('footer');
|
|
22
|
+
document?.querySelector('.burger')?.addEventListener('click', (e) => {
|
|
23
|
+
const button = e?.currentTarget as HTMLButtonElement;
|
|
24
|
+
button.setAttribute(
|
|
25
|
+
'aria-expanded',
|
|
26
|
+
`${button?.getAttribute('aria-expanded') === 'false'}`
|
|
27
|
+
);
|
|
28
|
+
main?.toggleAttribute('inert');
|
|
29
|
+
footer?.toggleAttribute('inert');
|
|
30
|
+
});
|
|
31
|
+
document?.querySelector('.burger')?.addEventListener('keydown', (e) => {
|
|
32
|
+
const keyboardEvent = e as KeyboardEvent;
|
|
33
|
+
const button = e?.currentTarget as HTMLButtonElement;
|
|
34
|
+
if (keyboardEvent.key === 'Escape') {
|
|
35
|
+
button.setAttribute('aria-expanded', 'false');
|
|
36
|
+
main?.removeAttribute('inert');
|
|
37
|
+
footer?.removeAttribute('inert');
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import Link from '@samuelgomez/astro/components/Link/index.astro';
|
|
4
|
+
import { setSlug } from '@samuelgomez/astro/helpers/setSlug';
|
|
5
|
+
import H3 from '@samuelgomez/astro/components/Title/h3.astro';
|
|
6
|
+
|
|
7
|
+
type Item = {
|
|
8
|
+
label: string;
|
|
9
|
+
href: string;
|
|
10
|
+
admin?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type NavProps = HTMLAttributes<'nav'> & {
|
|
14
|
+
items?: Item[];
|
|
15
|
+
label?: string;
|
|
16
|
+
class?: string;
|
|
17
|
+
id?: string;
|
|
18
|
+
mode?: 'horizontal' | 'vertical';
|
|
19
|
+
ordered?: boolean;
|
|
20
|
+
title?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
items = [],
|
|
25
|
+
label,
|
|
26
|
+
class: className,
|
|
27
|
+
id,
|
|
28
|
+
mode = 'horizontal',
|
|
29
|
+
ordered = false,
|
|
30
|
+
title = false,
|
|
31
|
+
...navProps
|
|
32
|
+
} = Astro.props as NavProps;
|
|
33
|
+
|
|
34
|
+
const List = ordered ? 'ol' : 'ul';
|
|
35
|
+
|
|
36
|
+
const labelId = label ? setSlug(label) : title ? setSlug(title) : null;
|
|
37
|
+
|
|
38
|
+
const isDev = import.meta.env.DEV;
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
{
|
|
42
|
+
Boolean(items.length) && (
|
|
43
|
+
<>
|
|
44
|
+
{title && <H3 label={title} noLink />}
|
|
45
|
+
<nav
|
|
46
|
+
data-mode={mode}
|
|
47
|
+
id={id}
|
|
48
|
+
class={className}
|
|
49
|
+
aria-labelledby={labelId}
|
|
50
|
+
{...navProps}
|
|
51
|
+
>
|
|
52
|
+
{label && (
|
|
53
|
+
<span id={labelId} class="sr-only">
|
|
54
|
+
{label}
|
|
55
|
+
</span>
|
|
56
|
+
)}
|
|
57
|
+
|
|
58
|
+
<List>
|
|
59
|
+
{items.map(
|
|
60
|
+
({ label, href, admin }) =>
|
|
61
|
+
(!admin || (admin && isDev)) && (
|
|
62
|
+
<li>
|
|
63
|
+
<Link href={href}>{label}</Link>
|
|
64
|
+
</li>
|
|
65
|
+
)
|
|
66
|
+
)}
|
|
67
|
+
</List>
|
|
68
|
+
</nav>
|
|
69
|
+
</>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import Link from '@samuelgomez/astro/components/Link/index.astro';
|
|
4
|
+
import Button from '@samuelgomez/astro/components/Button/index.astro';
|
|
5
|
+
|
|
6
|
+
export const ELLIPSIS = '...';
|
|
7
|
+
|
|
8
|
+
export const itemVariants = {
|
|
9
|
+
link: 'link',
|
|
10
|
+
button: 'button',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type PaginationItemAs = keyof typeof itemVariants;
|
|
14
|
+
|
|
15
|
+
type Props = HTMLAttributes<'li'> & {
|
|
16
|
+
page: number | typeof ELLIPSIS;
|
|
17
|
+
isCurrentPage: boolean;
|
|
18
|
+
asItem?: PaginationItemAs;
|
|
19
|
+
href?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
page,
|
|
24
|
+
isCurrentPage,
|
|
25
|
+
'aria-label': ariaLabel,
|
|
26
|
+
asItem = 'link',
|
|
27
|
+
class: className,
|
|
28
|
+
...restProps
|
|
29
|
+
} = Astro.props;
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
<li>
|
|
33
|
+
{
|
|
34
|
+
page === ELLIPSIS ? (
|
|
35
|
+
<span aria-label={ariaLabel}>{page}</span>
|
|
36
|
+
) : asItem === 'link' ? (
|
|
37
|
+
<Link aria-label={ariaLabel} {...restProps}>
|
|
38
|
+
{page}
|
|
39
|
+
</Link>
|
|
40
|
+
) : (
|
|
41
|
+
<Button
|
|
42
|
+
variant="reverse"
|
|
43
|
+
disabled={isCurrentPage || null}
|
|
44
|
+
aria-label={ariaLabel}
|
|
45
|
+
{...restProps}
|
|
46
|
+
>
|
|
47
|
+
{page}
|
|
48
|
+
</Button>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
</li>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import PaginationItem, { type PaginationItemAs } from './PaginationItem.astro';
|
|
4
|
+
|
|
5
|
+
type Props = HTMLAttributes<'nav'> & {
|
|
6
|
+
numberPages: number;
|
|
7
|
+
currentPage: number;
|
|
8
|
+
asItem?: PaginationItemAs;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const { numberPages, currentPage, asItem = 'link', ...navProps } = Astro.props;
|
|
12
|
+
|
|
13
|
+
const items = Array.from({ length: numberPages }, (_, index) => ({
|
|
14
|
+
page: index + 1,
|
|
15
|
+
isCurrentPage: index + 1 === currentPage,
|
|
16
|
+
}));
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
Boolean(items.length) && (
|
|
21
|
+
<nav {...navProps} class="rounded-list">
|
|
22
|
+
<ol>
|
|
23
|
+
{items.map(({ page }) => (
|
|
24
|
+
<PaginationItem
|
|
25
|
+
page={page}
|
|
26
|
+
isCurrentPage={currentPage === page}
|
|
27
|
+
href={asItem === 'link' ? `?page=${page}` : undefined}
|
|
28
|
+
aria-label={
|
|
29
|
+
currentPage === page
|
|
30
|
+
? `Page ${page}, current page`
|
|
31
|
+
: `Go to page ${page}`
|
|
32
|
+
}
|
|
33
|
+
asItem={asItem}
|
|
34
|
+
/>
|
|
35
|
+
))}
|
|
36
|
+
</ol>
|
|
37
|
+
</nav>
|
|
38
|
+
)
|
|
39
|
+
}
|