@samuelgomez/astro 0.1.1 → 0.1.3

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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@samuelgomez/astro",
3
3
  "author": "Samuel Gomez",
4
4
  "type": "module",
5
- "version": "0.1.1",
5
+ "version": "0.1.3",
6
6
  "exports": {
7
7
  "./*": "./src/*"
8
8
  },
@@ -1,29 +1,36 @@
1
1
  ---
2
2
  import type { HTMLAttributes } from 'astro/types';
3
3
 
4
- export type AccordionProps = HTMLAttributes<'details'>;
4
+ export type AccordionProps = HTMLAttributes<'details'> & {
5
+ hasIcon?: boolean;
6
+ };
5
7
 
6
- const props = Astro.props as AccordionProps;
8
+ const { hasIcon = true, ...props } = Astro.props as AccordionProps;
7
9
  ---
8
10
 
9
11
  <details {...props}>
10
- <summary
11
- ><slot name="summary" /><slot name="icon"
12
- ><svg
13
- class="chevron"
14
- xmlns="http://www.w3.org/2000/svg"
15
- width="24"
16
- height="24"
17
- viewBox="0 0 24 24"
18
- fill="none"
19
- stroke="currentColor"
20
- stroke-width="2"
21
- stroke-linecap="round"
22
- stroke-linejoin="round"
23
- class="feather feather-chevron-down"
24
- ><polyline points="6 9 12 15 18 9"></polyline></svg
25
- ></slot
26
- >
12
+ <summary class="summary"
13
+ ><slot name="summary" />{
14
+ hasIcon && (
15
+ <slot name="icon">
16
+ <svg
17
+ class="chevron"
18
+ xmlns="http://www.w3.org/2000/svg"
19
+ width="24"
20
+ height="24"
21
+ viewBox="0 0 24 24"
22
+ fill="none"
23
+ stroke="currentColor"
24
+ stroke-width="2"
25
+ stroke-linecap="round"
26
+ stroke-linejoin="round"
27
+ class="feather feather-chevron-down"
28
+ >
29
+ <polyline points="6 9 12 15 18 9" />
30
+ </svg>
31
+ </slot>
32
+ )
33
+ }
27
34
  </summary>
28
35
  <div>
29
36
  <slot />
@@ -33,13 +33,22 @@ const ariaLabelledBy = title ? setSlug(title) : null;
33
33
  const BoxTag = tag ?? ('article' as HTMLTag);
34
34
  ---
35
35
 
36
- <BoxTag aria-labelledby={ariaLabelledBy} {...props}>
36
+ <BoxTag
37
+ aria-labelledby={ariaLabelledBy}
38
+ itemscope
39
+ itemtype="http://schema.org/Article"
40
+ {...props}
41
+ >
37
42
  <slot name="before-icon" />
38
43
 
39
44
  <WrapperOrNot condition={Astro.slots.has('before-icon') && Boolean(content)}>
40
45
  <slot name="image" />
41
- {title && <Title level={titleLevel} label={title} noLink />}
42
- {pubDate && <FormattedDate date={new Date(pubDate)} />}
43
- {Boolean(content) && <Fragment set:html={content} />}
46
+ {title && <Title level={titleLevel} label={title} noLink itemprop="name" />}
47
+ {
48
+ pubDate && (
49
+ <FormattedDate date={new Date(pubDate)} itemprop="datePublished" />
50
+ )
51
+ }
52
+ {Boolean(content) && <Fragment set:html={content} itemprop="text" />}
44
53
  </WrapperOrNot>
45
54
  </BoxTag>
@@ -49,17 +49,17 @@ import Grid from '@samuelgomez/astro/icons/Grid.astro';
49
49
  height: 100%;
50
50
 
51
51
  main {
52
- --name-xs: 'under SM 640px';
52
+ --name-xs: 'xs : < 40rem (640px)';
53
53
  --color-bg-cols-xs: var(--pp2);
54
- --name-sm: 'sm : 640px to 767px';
54
+ --name-sm: 'sm : >=40rem (640px)';
55
55
  --color-bg-cols-sm: var(--bl2);
56
- --name-md: 'md : 768px to 1023px';
56
+ --name-md: 'md : >= 48rem (768px)';
57
57
  --color-bg-cols-md: var(--og2);
58
- --name-lg: 'lg : 1024px to 1279px';
58
+ --name-lg: 'lg : >= 64rem (1024px)';
59
59
  --color-bg-cols-lg: var(--gn1);
60
- --name-xl: 'xl : 1280px to 1535px';
60
+ --name-xl: 'xl : >= 80rem (1280px)';
61
61
  --color-bg-cols-xl: var(--rd1);
62
- --name-2xl: '2xl : over 1535px';
62
+ --name-2xl: '2xl : >= 96rem (1536px)';
63
63
  --color-bg-cols-2xl: var(--tk6);
64
64
 
65
65
  position: relative;
@@ -1,12 +1,13 @@
1
1
  ---
2
2
  type Props = {
3
3
  date: Date;
4
+ itemprop?: string;
4
5
  };
5
6
 
6
- const { date } = Astro.props;
7
+ const { date, itemprop } = Astro.props;
7
8
  ---
8
9
 
9
- <time datetime={date.toISOString()}>
10
+ <time datetime={date.toISOString()} itemprop={itemprop}>
10
11
  {
11
12
  date.toLocaleDateString('fr-fr', {
12
13
  year: 'numeric',
@@ -1,38 +1,55 @@
1
1
  ---
2
2
  import Link from '@samuelgomez/astro/components/Link/index.astro';
3
- import BoxPost from '@samuelgomez/astro/components/BoxPost/index.astro';
4
3
  import ChevronRight from '@samuelgomez/astro/icons/ChevronRight.astro';
5
- import { getCollection } from 'astro:content';
6
- import LogoSG from '@samuelgomez/astro/icons/LogoSG.astro';
4
+ import { getCollection, type CollectionEntry } from 'astro:content';
5
+ import GithubContributions from '@samuelgomez/astro/components/GithubContributions/index.astro';
6
+ import LastPostAccordion from '@samuelgomez/astro/components/Home/LastPostAccordion.astro';
7
7
  import { projectAuth } from '@firebase/config';
8
+
8
9
  const { currentUser } = projectAuth;
9
10
 
10
- let posts = await getCollection('blog');
11
+ let posts = (await getCollection('blog')) as CollectionEntry<'blog'>[];
11
12
 
12
13
  if (!currentUser) {
13
14
  posts = posts.filter(({ data }) => data.published);
14
15
  }
15
16
 
16
- posts = posts
17
- .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
18
- .slice(0, 6);
17
+ let postsLight = posts.map((post) => ({
18
+ title: post.data.title,
19
+ slug: post.slug,
20
+ pubDate: post.data.pubDate,
21
+ type: post.data.type,
22
+ image: post.data.heroImage,
23
+ description: post.data.description,
24
+ heroImage: post.data.heroImage,
25
+ alt: post.data.alt,
26
+ }));
27
+
28
+ // Grouper les posts par catégorie et récupérer le dernier de chaque
29
+ const postsByCategory = Object.groupBy(
30
+ postsLight,
31
+ ({ type }) => type || 'uncategorized',
32
+ );
33
+
34
+ const latestPostsByCategory = Object.values(postsByCategory)
35
+ .filter((latestsposts) => latestsposts !== undefined)
36
+ .map(
37
+ (latestsposts) =>
38
+ latestsposts.sort((a, b) => b.pubDate.valueOf() - a.pubDate.valueOf())[0],
39
+ );
19
40
  ---
20
41
 
21
42
  <section>
22
43
  <header>
23
- <LogoSG size="100" />
24
44
  <h2>Derniers articles</h2>
45
+ <GithubContributions />
25
46
  <p>Voici les derniers articles de blog sur le développements Front-end.</p>
26
47
  </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>
48
+ <LastPostAccordion
49
+ items={latestPostsByCategory}
50
+ name="lastposts"
51
+ category="blog"
52
+ />
36
53
  <footer>
37
54
  <Link href="/blog">
38
55
  Voir mes articles Front-end<ChevronRight slot="after-link" />
@@ -0,0 +1,54 @@
1
+ ---
2
+ import Accordion from '@samuelgomez/astro/components/Accordion/index.astro';
3
+ import Title from '@samuelgomez/astro/components/Title/index.astro';
4
+ import Link from '@samuelgomez/astro/components/Link/index.astro';
5
+ import ChevronRight from '@samuelgomez/astro/icons/ChevronRight.astro';
6
+ import FormattedDate from '@samuelgomez/astro/components/FormattedDate/index.astro';
7
+
8
+ type AccordionVerticalProps = {
9
+ items: {
10
+ slug: string;
11
+ title: string;
12
+ description?: string;
13
+ pubDate: Date;
14
+ type?: string;
15
+ category?: string;
16
+ }[];
17
+ name: string;
18
+ category: string;
19
+ };
20
+
21
+ const { items, name, category } = Astro.props as AccordionVerticalProps;
22
+ ---
23
+
24
+ <section>
25
+ <div>
26
+ {
27
+ items.map(({ slug, title, description, pubDate, type }) => (
28
+ <Accordion
29
+ name={name}
30
+ open
31
+ hasIcon={false}
32
+ style={`--type-color: var(--${type}-color);`}
33
+ itemscope
34
+ itemtype="http://schema.org/Article"
35
+ >
36
+ <strong slot="summary" itemprop="genre">
37
+ {type}
38
+ </strong>
39
+ {title && <Title level={3} label={title} noLink itemprop="name" />}
40
+ {pubDate && (
41
+ <FormattedDate date={new Date(pubDate)} itemprop="datePublished" />
42
+ )}
43
+ <p itemprop="description">{description}</p>
44
+ {Boolean(slug) && (
45
+ <Link href={`${category ? `/${category}/${slug}` : `/${slug}`}`}>
46
+ Voir {title}
47
+ <ChevronRight slot="after-link" />
48
+ </Link>
49
+ )}
50
+ </Accordion>
51
+ ))
52
+ }
53
+ </div>
54
+ </section>
@@ -18,7 +18,8 @@ const content = await Astro.slots.render('default');
18
18
  <a
19
19
  href={href}
20
20
  aria-current={isCurrent ? 'page' : null}
21
- rel={isExternal ? 'noopener' : null}
21
+ rel={isExternal ? 'nofollow noopener' : null}
22
+ itemprop="url"
22
23
  {...props}
23
24
  >
24
25
  <slot name="before-link" />
@@ -5,7 +5,6 @@ const { 'aria-controls': ariaControls } = Astro.props;
5
5
  ---
6
6
 
7
7
  <Button
8
- class="burger"
9
8
  variant="ghost"
10
9
  aria-label="Toggle Navigation"
11
10
  aria-expanded="false"
@@ -17,9 +16,12 @@ const { 'aria-controls': ariaControls } = Astro.props;
17
16
  </Button>
18
17
 
19
18
  <script>
20
- const main = document.querySelector('main');
21
- const footer = document.querySelector('footer');
22
- document?.querySelector('.burger')?.addEventListener('click', (e) => {
19
+ const main = document.querySelector('body > main');
20
+ const footer = document.querySelector('body > footer');
21
+ const burger = document.querySelector(
22
+ 'body > header > button[aria-expanded]'
23
+ );
24
+ burger?.addEventListener('click', (e) => {
23
25
  const button = e?.currentTarget as HTMLButtonElement;
24
26
  button.setAttribute(
25
27
  'aria-expanded',
@@ -28,7 +30,7 @@ const { 'aria-controls': ariaControls } = Astro.props;
28
30
  main?.toggleAttribute('inert');
29
31
  footer?.toggleAttribute('inert');
30
32
  });
31
- document?.querySelector('.burger')?.addEventListener('keydown', (e) => {
33
+ burger?.addEventListener('keydown', (e) => {
32
34
  const keyboardEvent = e as KeyboardEvent;
33
35
  const button = e?.currentTarget as HTMLButtonElement;
34
36
  if (keyboardEvent.key === 'Escape') {
@@ -4,19 +4,38 @@ import Twitter from '@samuelgomez/astro/icons/Twitter.astro';
4
4
  import Github from '@samuelgomez/astro/icons/Github2.astro';
5
5
  import Instagram from '@samuelgomez/astro/icons/Instagram.astro';
6
6
  import Link from '@samuelgomez/astro/components/Link/index.astro';
7
+
8
+ const {
9
+ github = false,
10
+ linkedin = false,
11
+ twitter = false,
12
+ instagram = false,
13
+ } = Astro.props;
7
14
  ---
8
15
 
9
- <menu>
10
- <Link href="https://github.com/samuel-gomez">
11
- <Github slot="after-link" title="Github de Samuel Gomez" />
12
- </Link>
13
- <Link href="https://www.linkedin.com/in/samuel-gomez-developpeur-web/">
14
- <Linkedin slot="after-link" title="Linkedin de Samuel Gomez" />
15
- </Link>
16
- <Link href="https://twitter.com/gamuez">
17
- <Twitter slot="after-link" title="Twitter de Samuel Gomez" />
18
- </Link>
19
- <Link href="https://www.instagram.com/gamuez_art/">
20
- <Instagram slot="after-link" title="Instagram de Samuel Gomez" />
21
- </Link>
22
- </menu>
16
+ {
17
+ (github || linkedin || twitter || instagram) && (
18
+ <menu>
19
+ {github && (
20
+ <Link href="https://github.com/samuel-gomez">
21
+ <Github slot="after-link" title="Github de Samuel Gomez" />
22
+ </Link>
23
+ )}
24
+ {linkedin && (
25
+ <Link href="https://www.linkedin.com/in/samuel-gomez-developpeur-web/">
26
+ <Linkedin slot="after-link" title="Linkedin de Samuel Gomez" />
27
+ </Link>
28
+ )}
29
+ {twitter && (
30
+ <Link href="https://twitter.com/gamuez">
31
+ <Twitter slot="after-link" title="Twitter de Samuel Gomez" />
32
+ </Link>
33
+ )}
34
+ {instagram && (
35
+ <Link href="https://www.instagram.com/gamuez_art/">
36
+ <Instagram slot="after-link" title="Instagram de Samuel Gomez" />
37
+ </Link>
38
+ )}
39
+ </menu>
40
+ )
41
+ }
@@ -11,9 +11,17 @@ type Props = {
11
11
  level?: titleLevel;
12
12
  noLink?: boolean;
13
13
  id?: string;
14
+ itemprop?: string;
14
15
  };
15
16
 
16
- const { label, level = 1, noLink, id, ...props } = Astro.props;
17
+ const {
18
+ label,
19
+ level = 1,
20
+ noLink,
21
+ id,
22
+ itemprop = 'headline',
23
+ ...props
24
+ } = Astro.props;
17
25
 
18
26
  const htmlLabel = await Astro.slots.render('default');
19
27
 
@@ -24,7 +32,7 @@ const title = label || htmlLabel.toString();
24
32
  const Heading = `h${level}` as HTMLTag;
25
33
  ---
26
34
 
27
- <Heading {...props} id={idHeading}>
35
+ <Heading {...props} id={idHeading} itemprop={itemprop}>
28
36
  {
29
37
  !noLink && idHeading && (
30
38
  <Link href={`#${idHeading}`}>
@@ -1,3 +1,13 @@
1
+ ---
2
+ const datetime = new Date();
3
+ const formattedDate = datetime.toISOString().split('T')[0];
4
+ const year = datetime.getFullYear();
5
+ ---
6
+
1
7
  <footer>
2
- <p>&copy; Samuel Gomez 2024</p>
8
+ <p>
9
+ &copy; Samuel Gomez <time datetime={formattedDate} itemprop="copyrightYear"
10
+ >{year}</time
11
+ >
12
+ </p>
3
13
  </footer>
@@ -8,10 +8,11 @@ import { getEntry } from 'astro:content';
8
8
  import { setSlug } from '@samuelgomez/astro/helpers/setSlug';
9
9
 
10
10
  const mainMenu = await getEntry('menu', 'main');
11
- const { userEmail } = Astro.locals;
12
11
 
13
12
  const LABEL = 'Menu de navigation principal';
14
13
  const ID = `id-${setSlug(LABEL)}`;
14
+
15
+ const { userEmail } = Astro.props;
15
16
  ---
16
17
 
17
18
  <header>
@@ -4,9 +4,20 @@ type Props = {
4
4
  size?: string;
5
5
  fill?: string;
6
6
  opacity?: string;
7
+ startColor?: string;
8
+ endColor?: string;
7
9
  };
10
+
8
11
  const FILL = "url('#logoSG-gradient')";
9
- const { title, size = '40', fill = FILL, opacity = '1' } = Astro.props;
12
+ const {
13
+ title,
14
+ size = '32',
15
+ fill = FILL,
16
+ opacity = '1',
17
+ startColor = '#7A5FFF',
18
+ endColor = '#01FF89',
19
+ } = Astro.props;
20
+
10
21
  const titleId = Boolean(title) ? `${crypto.randomUUID()}` : null;
11
22
  ---
12
23
 
@@ -42,19 +53,19 @@ const titleId = Boolean(title) ? `${crypto.randomUUID()}` : null;
42
53
  x2="50%"
43
54
  y2="100%"
44
55
  >
45
- <stop offset="0%" stop-color="#7A5FFF">
56
+ <stop offset="0%" stop-color={startColor}>
46
57
  <animate
47
58
  attributeName="stop-color"
48
- values="#7A5FFF; #01FF89; #7A5FFF"
59
+ values={`${startColor}; ${endColor}; ${startColor}`}
49
60
  dur="4s"
50
61
  repeatCount="indefinite"
51
62
  />
52
63
  </stop>
53
64
 
54
- <stop offset="100%" stop-color="#01FF89">
65
+ <stop offset="100%" stop-color={endColor}>
55
66
  <animate
56
67
  attributeName="stop-color"
57
- values="#01FF89; #7A5FFF; #01FF89"
68
+ values={`${endColor}; ${startColor}; ${endColor}`}
58
69
  dur="4s"
59
70
  repeatCount="indefinite"
60
71
  />