import photo from '@assets/images/armand-philippot.jpg'; import ButtonLink from '@components/atoms/buttons/button-link'; import Career from '@components/atoms/icons/career'; import CCBySA from '@components/atoms/icons/cc-by-sa'; import ComputerScreen from '@components/atoms/icons/computer-screen'; import Envelop from '@components/atoms/icons/envelop'; import Home from '@components/atoms/icons/home'; import PostsStack from '@components/atoms/icons/posts-stack'; import Main from '@components/atoms/layout/main'; import NoScript from '@components/atoms/layout/no-script'; import Footer, { type FooterProps } from '@components/organisms/layout/footer'; import Header, { type HeaderProps } from '@components/organisms/layout/header'; import { type NextPageWithLayoutOptions } from '@ts/types/app'; import useScrollPosition from '@utils/hooks/use-scroll-position'; import useSettings from '@utils/hooks/use-settings'; import Script from 'next/script'; import { FC, ReactElement, ReactNode, useState } from 'react'; import { useIntl } from 'react-intl'; import { Person, SearchAction, WebSite, WithContext } from 'schema-dts'; import styles from './layout.module.scss'; export type QueryAction = SearchAction & { 'query-input': string; }; export type LayoutProps = Pick & { /** * The layout main content. */ children: ReactNode; /** * Determine if article has a comments section. */ withExtraPadding?: boolean; /** * Determine if article should use grid. Default: false. */ useGrid?: boolean; }; /** * Layout component * * Render the base layout used by all pages. */ const Layout: FC = ({ children, withExtraPadding = false, isHome, useGrid = false, }) => { const intl = useIntl(); const { website } = useSettings(); const { baseline, copyright, locales, name, url } = website; const articleGridClass = useGrid ? 'article--grid' : ''; const articleCommentsClass = withExtraPadding ? 'article--padding' : ''; const skipToContent = intl.formatMessage({ defaultMessage: 'Skip to content', description: 'Layout: Skip to content link', id: 'K4rYdT', }); const noScript = intl.formatMessage({ defaultMessage: 'Warning: If you want to benefit from all features (search for example), please activate Javascript.', description: 'Layout: noscript message', id: '7jVUT6', }); const copyrightData = { dates: { start: copyright.start, end: copyright.end, }, owner: name, icon: , }; const homeLabel = intl.formatMessage({ defaultMessage: 'Home', description: 'Layout: main nav - home link', id: 'bojYF5', }); const blogLabel = intl.formatMessage({ defaultMessage: 'Blog', description: 'Layout: main nav - blog link', id: 'D8vB38', }); const projectsLabel = intl.formatMessage({ defaultMessage: 'Projects', description: 'Layout: main nav - projects link', id: 'qnwsWV', }); const cvLabel = intl.formatMessage({ defaultMessage: 'CV', description: 'Layout: main nav - cv link', id: 'R895yC', }); const contactLabel = intl.formatMessage({ defaultMessage: 'Contact', description: 'Layout: main nav - contact link', id: 'AE4kCD', }); const mainNav: HeaderProps['nav'] = [ { id: 'home', label: homeLabel, href: '/', logo: }, { id: 'blog', label: blogLabel, href: '/blog', logo: }, { id: 'projects', label: projectsLabel, href: '/projets', logo: , }, { id: 'cv', label: cvLabel, href: '/cv', logo: }, { id: 'contact', label: contactLabel, href: '/contact', logo: }, ]; const legalNoticeLabel = intl.formatMessage({ defaultMessage: 'Legal notice', description: 'Layout: Legal notice label', id: 'nwbzKm', }); const footerNav: FooterProps['navItems'] = [ { id: 'legal-notice', label: legalNoticeLabel, href: '/mentions-legales' }, ]; const searchActionSchema: QueryAction = { '@type': 'SearchAction', target: { '@type': 'EntryPoint', urlTemplate: `${url}/recherche?s={search_term_string}`, }, query: 'required', 'query-input': 'required name=search_term_string', }; const schemaJsonLd: WithContext = { '@context': 'https://schema.org', '@id': `${url}`, '@type': 'WebSite', name: name, description: baseline, url: url, author: { '@id': `${url}/#branding` }, copyrightYear: Number(copyright.start), creator: { '@id': `${url}/#branding` }, editor: { '@id': `${url}/#branding` }, inLanguage: locales.default, potentialAction: searchActionSchema, }; const brandingSchema: WithContext = { '@context': 'https://schema.org', '@type': 'Person', '@id': `${url}/#branding`, name: name, url: url, jobTitle: baseline, image: photo.src, subjectOf: { '@id': `${url}` }, }; const [backToTopClassName, setBackToTopClassName] = useState( styles['back-to-top--hidden'] ); const updateBackToTopClassName = () => { setBackToTopClassName( window.scrollY > 300 ? styles['back-to-top--visible'] : styles['back-to-top--hidden'] ); }; useScrollPosition(updateBackToTopClassName); return ( <>