aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-05-03 16:53:55 +0200
committerArmand Philippot <git@armandphilippot.com>2022-05-03 16:53:55 +0200
commitee04742d1f0645908baa30e47845126c28848f50 (patch)
tree1d80dca17437be6a351e932885e95c940833e571 /src/components
parent83a029084f1bbfd78b7099d9bea3371d4533c6d9 (diff)
chore: add a CV page
Diffstat (limited to 'src/components')
-rw-r--r--src/components/atoms/links/link.module.scss87
-rw-r--r--src/components/atoms/links/link.stories.tsx20
-rw-r--r--src/components/atoms/links/link.tsx18
-rw-r--r--src/components/molecules/images/responsive-image.module.scss11
-rw-r--r--src/components/molecules/images/responsive-image.stories.tsx1
-rw-r--r--src/components/molecules/images/responsive-image.tsx6
-rw-r--r--src/components/organisms/widgets/image-widget.module.scss13
-rw-r--r--src/components/organisms/widgets/image-widget.stories.tsx25
-rw-r--r--src/components/organisms/widgets/image-widget.test.tsx11
-rw-r--r--src/components/organisms/widgets/image-widget.tsx18
10 files changed, 174 insertions, 36 deletions
diff --git a/src/components/atoms/links/link.module.scss b/src/components/atoms/links/link.module.scss
index d23667a..1b89727 100644
--- a/src/components/atoms/links/link.module.scss
+++ b/src/components/atoms/links/link.module.scss
@@ -5,23 +5,64 @@
&[hreflang] {
&::after {
display: inline-block;
+
/* Prettier is removing spacing between content parts. */
+
/* prettier-ignore */
content: "\0000a0[" attr(hreflang) "]";
font-size: var(--font-size-sm);
}
}
+ &--download {
+ &::after {
+ display: inline-block;
+
+ /* Prettier is removing spacing between content parts. */
+
+ /* prettier-ignore */
+ content: "\0000a0" url(
+fun.encode-svg(
+ '<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>'
+));
+ }
+
+ &:focus:not(:active)::after {
+ /* Prettier is removing spacing between content parts. */
+
+ /* prettier-ignore */
+ content: "\0000a0" url(
+fun.encode-svg(
+ '<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_white}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>'
+));
+ }
+
+ &[hreflang] {
+ &::after {
+ /* Prettier is removing spacing between content parts. */
+
+ /* prettier-ignore */
+ content: "\0000a0[" attr(hreflang) "]\0000a0" url(
+fun.encode-svg(
+ '<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>'
+));
+ }
+ }
+ }
+
&--external {
&::after {
display: inline-block;
+
/* Prettier is removing spacing between content parts. */
+
/* prettier-ignore */
content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>'));
}
&:focus:not(:active)::after {
/* Prettier is removing spacing between content parts. */
+
/* prettier-ignore */
content: "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>'));
}
@@ -29,18 +70,56 @@
&[hreflang] {
&::after {
/* Prettier is removing spacing between content parts. */
+
/* prettier-ignore */
- content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg(
+ content: "\0000a0[" attr(hreflang) "]\0000a0" url(
+fun.encode-svg(
'<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>'
- ));
+));
}
&:focus:not(:active)::after {
/* Prettier is removing spacing between content parts. */
+
/* prettier-ignore */
- content: "\0000a0[" attr(hreflang) "]\0000a0" url(fun.encode-svg(
+ content: "\0000a0[" attr(hreflang) "]\0000a0" url(
+fun.encode-svg(
'<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>'
- ));
+));
+ }
+ }
+ }
+
+ &--external#{&}--download {
+ &::after {
+ /* Prettier is removing spacing between content parts. */
+
+ /* prettier-ignore */
+ content: "\0000a0" url(
+fun.encode-svg(
+ '<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>'
+)) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>'));
+ }
+
+ &[hreflang] {
+ &::after {
+ /* Prettier is removing spacing between content parts. */
+
+ /* prettier-ignore */
+ content: "\0000a0[" attr(hreflang) "]\0000a0" url(
+fun.encode-svg(
+ '<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>'
+)) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_blue}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>'));
+ }
+
+ &:focus:not(:active)::after {
+ /* Prettier is removing spacing between content parts. */
+
+ /* prettier-ignore */
+ content: "\0000a0[" attr(hreflang) "]\0000a0" url(
+fun.encode-svg(
+ '<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_blue}" d="m49 80.048-28.445-30.77 19.32 4.095V5.06h18.252v48.313l21.318-4.095z"/><path fill="#{var.$light-theme_blue}" d="M0 67.57v27.37h100V67.57H87.973v15.344H12.027V67.569z"/></svg>'
+)) "\0000a0" url(fun.encode-svg('<svg width="13" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><path fill="#{var.$light-theme_white}" d="M100 0 59.543 5.887l20.8 6.523-51.134 51.134 7.249 7.248L87.59 19.66l6.522 20.798z"/><path fill="#{var.$light-theme_white}" d="M4 10a4 4 0 0 0-4 4v82a4 4 0 0 0 4 4h82a4 4 0 0 0 4-4V62.314h-8V92H8V18h29.686v-8z"/></svg>'));
}
}
}
diff --git a/src/components/atoms/links/link.stories.tsx b/src/components/atoms/links/link.stories.tsx
index c3b3465..420eafe 100644
--- a/src/components/atoms/links/link.stories.tsx
+++ b/src/components/atoms/links/link.stories.tsx
@@ -31,14 +31,29 @@ export default {
required: false,
},
},
- external: {
+ download: {
control: {
type: 'boolean',
},
+ description: 'Determine if the link purpose is to download a file.',
table: {
category: 'Options',
+ defaultValue: { summary: false },
+ },
+ type: {
+ name: 'boolean',
+ required: false,
+ },
+ },
+ external: {
+ control: {
+ type: 'boolean',
},
description: 'Determine if the link is external of the current website.',
+ table: {
+ category: 'Options',
+ defaultValue: { summary: false },
+ },
type: {
name: 'boolean',
required: false,
@@ -79,6 +94,7 @@ export const Default = Template.bind({});
Default.args = {
children: 'A link',
href: '#',
+ download: false,
external: false,
};
@@ -89,6 +105,7 @@ export const External = Template.bind({});
External.args = {
children: 'A link',
href: '#',
+ download: false,
external: true,
};
@@ -99,6 +116,7 @@ export const ExternalWithLang = Template.bind({});
ExternalWithLang.args = {
children: 'A link',
href: '#',
+ download: false,
external: true,
lang: 'en',
};
diff --git a/src/components/atoms/links/link.tsx b/src/components/atoms/links/link.tsx
index 674c07b..c8ba273 100644
--- a/src/components/atoms/links/link.tsx
+++ b/src/components/atoms/links/link.tsx
@@ -12,6 +12,10 @@ export type LinkProps = {
*/
className?: string;
/**
+ * True if it is a download link. Default: false.
+ */
+ download?: boolean;
+ /**
* True if it is an external link. Default: false.
*/
external?: boolean;
@@ -33,21 +37,29 @@ export type LinkProps = {
const Link: FC<LinkProps> = ({
children,
className = '',
+ download = false,
+ external = false,
href,
lang,
- external = false,
}) => {
+ const downloadClass = download ? styles['link--download'] : '';
+
return external ? (
<a
href={href}
hrefLang={lang}
- className={`${styles.link} ${styles['link--external']} ${className}`}
+ className={`${styles.link} ${styles['link--external']} ${downloadClass} ${className}`}
>
{children}
</a>
) : (
<NextLink href={href}>
- <a className={`${styles.link} ${className}`}>{children}</a>
+ <a
+ hrefLang={lang}
+ className={`${styles.link} ${downloadClass} ${className}`}
+ >
+ {children}
+ </a>
</NextLink>
);
};
diff --git a/src/components/molecules/images/responsive-image.module.scss b/src/components/molecules/images/responsive-image.module.scss
index 3566421..3a4f283 100644
--- a/src/components/molecules/images/responsive-image.module.scss
+++ b/src/components/molecules/images/responsive-image.module.scss
@@ -3,8 +3,7 @@
.wrapper {
display: flex;
flex-flow: column;
- width: 100%;
- max-width: max-content;
+ width: fit-content;
margin: 0;
position: relative;
text-align: center;
@@ -14,9 +13,7 @@
margin: 0;
padding: fun.convert-px(4) var(--spacing-2xs);
background: var(--color-bg-secondary);
- border: fun.convert-px(1) solid var(--color-border);
- box-shadow: 0 fun.convert-px(-1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow-light);
+ border: fun.convert-px(1) solid var(--color-border-light);
font-weight: 500;
}
@@ -32,7 +29,7 @@
&:hover,
&:focus {
- transform: scale(1.1);
+ transform: scale(var(--scale-up, 1.05));
}
&:focus {
@@ -43,7 +40,7 @@
}
&:active {
- transform: scale(0.9);
+ transform: scale(var(--scale-down, 0.95));
.caption {
text-decoration: none;
diff --git a/src/components/molecules/images/responsive-image.stories.tsx b/src/components/molecules/images/responsive-image.stories.tsx
index a1f5295..35d9116 100644
--- a/src/components/molecules/images/responsive-image.stories.tsx
+++ b/src/components/molecules/images/responsive-image.stories.tsx
@@ -64,6 +64,7 @@ export default {
required: false,
},
},
+ unoptimized: { table: { disable: true } },
width: {
control: {
type: 'number',
diff --git a/src/components/molecules/images/responsive-image.tsx b/src/components/molecules/images/responsive-image.tsx
index 31cbcd1..d07dd6c 100644
--- a/src/components/molecules/images/responsive-image.tsx
+++ b/src/components/molecules/images/responsive-image.tsx
@@ -1,6 +1,6 @@
import Link, { type LinkProps } from '@components/atoms/links/link';
import Image, { type ImageProps } from 'next/image';
-import { FC } from 'react';
+import { FC, ReactNode } from 'react';
import styles from './responsive-image.module.scss';
export type ResponsiveImageProps = Omit<
@@ -14,7 +14,7 @@ export type ResponsiveImageProps = Omit<
/**
* A figure caption.
*/
- caption?: string;
+ caption?: ReactNode;
/**
* Set additional classnames to the figure wrapper.
*/
@@ -55,6 +55,7 @@ const ResponsiveImage: FC<ResponsiveImageProps> = ({
alt={alt}
layout={layout || 'intrinsic'}
objectFit={objectFit || 'contain'}
+ className={styles.img}
{...props}
/>
{caption && (
@@ -67,6 +68,7 @@ const ResponsiveImage: FC<ResponsiveImageProps> = ({
alt={alt}
layout={layout || 'intrinsic'}
objectFit={objectFit || 'contain'}
+ className={styles.img}
{...props}
/>
{caption && (
diff --git a/src/components/organisms/widgets/image-widget.module.scss b/src/components/organisms/widgets/image-widget.module.scss
index 8e4d0aa..0d69441 100644
--- a/src/components/organisms/widgets/image-widget.module.scss
+++ b/src/components/organisms/widgets/image-widget.module.scss
@@ -1,7 +1,12 @@
@use "@styles/abstracts/functions" as fun;
-.img {
+.figure {
+ --scale-up: 1.02;
+ --scale-down: 0.98;
+
margin: 0;
+ padding: fun.convert-px(5);
+ border: fun.convert-px(1) solid var(--color-border);
}
.txt {
@@ -10,7 +15,7 @@
.widget {
&--left {
- .img {
+ .figure {
margin-right: auto;
}
@@ -20,7 +25,7 @@
}
&--center {
- .img {
+ .figure {
margin-left: auto;
margin-right: auto;
}
@@ -31,7 +36,7 @@
}
&--right {
- .img {
+ .figure {
margin-left: auto;
}
diff --git a/src/components/organisms/widgets/image-widget.stories.tsx b/src/components/organisms/widgets/image-widget.stories.tsx
index 27871ae..3014b36 100644
--- a/src/components/organisms/widgets/image-widget.stories.tsx
+++ b/src/components/organisms/widgets/image-widget.stories.tsx
@@ -50,7 +50,7 @@ export default {
required: true,
},
},
- img: {
+ image: {
description: 'An image object.',
type: {
name: 'object',
@@ -58,6 +58,19 @@ export default {
value: {},
},
},
+ imageClassName: {
+ control: {
+ type: 'text',
+ },
+ description: 'Set additional classnames to the image wrapper.',
+ table: {
+ category: 'Styles',
+ },
+ type: {
+ name: 'string',
+ required: false,
+ },
+ },
level: {
control: {
type: 'number',
@@ -107,7 +120,7 @@ const Template: ComponentStory<typeof ImageWidget> = (args) => (
<ImageWidget {...args} />
);
-const img = {
+const image = {
alt: 'Et perferendis quaerat',
height: 480,
src: 'http://placeimg.com/640/480/nature',
@@ -122,7 +135,7 @@ export const AlignLeft = Template.bind({});
AlignLeft.args = {
alignment: 'left',
expanded: true,
- img,
+ image,
level: 2,
title: 'Quo et totam',
};
@@ -134,7 +147,7 @@ export const AlignCenter = Template.bind({});
AlignCenter.args = {
alignment: 'center',
expanded: true,
- img,
+ image,
level: 2,
title: 'Quo et totam',
};
@@ -146,7 +159,7 @@ export const AlignRight = Template.bind({});
AlignRight.args = {
alignment: 'right',
expanded: true,
- img,
+ image,
level: 2,
title: 'Quo et totam',
};
@@ -158,7 +171,7 @@ export const WithDescription = Template.bind({});
WithDescription.args = {
description: 'Sint enim harum',
expanded: true,
- img,
+ image,
level: 2,
title: 'Quo et totam',
};
diff --git a/src/components/organisms/widgets/image-widget.test.tsx b/src/components/organisms/widgets/image-widget.test.tsx
index 8c24bd9..c6b1a3a 100644
--- a/src/components/organisms/widgets/image-widget.test.tsx
+++ b/src/components/organisms/widgets/image-widget.test.tsx
@@ -18,7 +18,12 @@ const url = '/another-page';
describe('ImageWidget', () => {
it('renders an image', () => {
render(
- <ImageWidget expanded={true} img={img} title={title} level={titleLevel} />
+ <ImageWidget
+ expanded={true}
+ image={img}
+ title={title}
+ level={titleLevel}
+ />
);
expect(screen.getByRole('img', { name: img.alt })).toBeInTheDocument();
});
@@ -27,7 +32,7 @@ describe('ImageWidget', () => {
render(
<ImageWidget
expanded={true}
- img={img}
+ image={img}
title={title}
level={titleLevel}
url={url}
@@ -43,7 +48,7 @@ describe('ImageWidget', () => {
render(
<ImageWidget
expanded={true}
- img={img}
+ image={img}
description={description}
title={title}
level={titleLevel}
diff --git a/src/components/organisms/widgets/image-widget.tsx b/src/components/organisms/widgets/image-widget.tsx
index ba04c6a..873337b 100644
--- a/src/components/organisms/widgets/image-widget.tsx
+++ b/src/components/organisms/widgets/image-widget.tsx
@@ -14,7 +14,7 @@ export type Image = Pick<
export type ImageWidgetProps = Pick<
WidgetProps,
- 'expanded' | 'level' | 'title'
+ 'className' | 'expanded' | 'level' | 'title'
> & {
/**
* The content alignment.
@@ -27,7 +27,11 @@ export type ImageWidgetProps = Pick<
/**
* An object describing the image.
*/
- img: Image;
+ image: Image;
+ /**
+ * Set additional classnames to the image wrapper.
+ */
+ imageClassName?: string;
/**
* Add a link to the image.
*/
@@ -41,20 +45,22 @@ export type ImageWidgetProps = Pick<
*/
const ImageWidget: FC<ImageWidgetProps> = ({
alignment = 'left',
+ className = '',
description,
- img,
+ image,
+ imageClassName = '',
url,
...props
}) => {
const alignmentClass = `widget--${alignment}`;
return (
- <Widget className={styles[alignmentClass]} {...props}>
+ <Widget className={`${styles[alignmentClass]} ${className}`} {...props}>
<ResponsiveImage
target={url}
caption={description}
- className={styles.img}
- {...img}
+ className={`${styles.figure} ${imageClassName}`}
+ {...image}
/>
</Widget>
);