diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-05-03 16:53:55 +0200 | 
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-05-03 16:53:55 +0200 | 
| commit | ee04742d1f0645908baa30e47845126c28848f50 (patch) | |
| tree | 1d80dca17437be6a351e932885e95c940833e571 /src/components | |
| parent | 83a029084f1bbfd78b7099d9bea3371d4533c6d9 (diff) | |
chore: add a CV page
Diffstat (limited to 'src/components')
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>    ); | 
