Import repositories from gitlab
This commit is contained in:
20
next/components/articleBlock/index.jsx
Normal file
20
next/components/articleBlock/index.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { BlocksRenderer } from "@strapi/blocks-react-renderer";
|
||||
import Button from "../button";
|
||||
import styles from "./style.module.scss";
|
||||
|
||||
export default function ArticleBlock({title, content, link, className, ...props}) {
|
||||
return (
|
||||
<article {...props} className={`${styles.articleBlock} ${className ?? ''}`}>
|
||||
<h4>{title}</h4>
|
||||
{content ? <BlocksRenderer content={content}/> : null}
|
||||
<Button
|
||||
type="secondary"
|
||||
as="a"
|
||||
href={link}
|
||||
target="_blank"
|
||||
>
|
||||
Lire la suite
|
||||
</Button>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
17
next/components/articleBlock/style.module.scss
Normal file
17
next/components/articleBlock/style.module.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
.articleBlock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
h4 {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-top: 1rem;
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
28
next/components/button/index.jsx
Normal file
28
next/components/button/index.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import styles from './style.module.scss';
|
||||
|
||||
export default function Button({as, type, children, ...props}) {
|
||||
switch (as ?? 'a') {
|
||||
case 'a':
|
||||
return (
|
||||
<a
|
||||
tabIndex="0"
|
||||
{...props}
|
||||
className={`${styles[type ?? 'secondary']} ${styles.button} not-a-link ${props.className}`}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
case 'button':
|
||||
return (
|
||||
<button
|
||||
tabIndex="0"
|
||||
{...props}
|
||||
className={`${styles[type ?? 'secondary']} ${styles.button} not-a-link ${props.className}`}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
}
|
||||
return (null);
|
||||
}
|
||||
47
next/components/button/style.module.scss
Normal file
47
next/components/button/style.module.scss
Normal file
@@ -0,0 +1,47 @@
|
||||
.button {
|
||||
appearance: none;
|
||||
font-size: unset;
|
||||
background: unset;
|
||||
font-family: unset;
|
||||
display: inline-block;
|
||||
padding: 1.25rem 1.5rem;
|
||||
border-radius: 2.25rem;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s ease;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
color: unset;
|
||||
border: solid 1px var(--fg-2);
|
||||
|
||||
&:hover, &:focus {
|
||||
background: var(--fg-3);
|
||||
border: solid 1px var(--fg-3);
|
||||
box-shadow: 0 0.25rem 0.35rem rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: white;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.25);
|
||||
text-decoration-color: transparent;
|
||||
background: linear-gradient(175deg, var(--fefan-1) 0%, var(--fefan-1) 45%, var(--fefan-2) 55%, var(--fefan-3) 70%, var(--fefan-4) 85%);
|
||||
background-size: 250% 100%;
|
||||
transition: all ease var(--transition-time);
|
||||
|
||||
&:hover, &:focus {
|
||||
text-decoration-color: var(--fefan-1);
|
||||
background-position-x: 100%;
|
||||
box-shadow: 0 0.25rem 0.35rem rgba(0, 0, 0, 0.45);
|
||||
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
32
next/components/editionElement/editionBlock/index.jsx
Normal file
32
next/components/editionElement/editionBlock/index.jsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import ImageBlock from "@/components/imageBlock";
|
||||
import StatBlock from "@/components/statBlock";
|
||||
import VideoBlock from "@/components/videoBlock";
|
||||
|
||||
import styles from './style.module.scss';
|
||||
|
||||
export default async function EditionBlock({type, ...props}) {
|
||||
|
||||
switch (type) {
|
||||
case 'stat':
|
||||
return (
|
||||
<article className={styles.editionBlock}>
|
||||
<StatBlock {...props} />
|
||||
</article>
|
||||
);
|
||||
|
||||
case 'image':
|
||||
return (
|
||||
<article className={styles.editionBlock}>
|
||||
<ImageBlock {...props} src={props.value} alt={props.title}/>
|
||||
</article>
|
||||
);
|
||||
|
||||
case 'video':
|
||||
return (
|
||||
<article className={styles.editionBlock}>
|
||||
<VideoBlock {...props} src={props.value} title={props.title}/>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.editionBlock {
|
||||
overflow: hidden;
|
||||
}
|
||||
31
next/components/editionElement/index.jsx
Normal file
31
next/components/editionElement/index.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import ImageBlock from '../imageBlock';
|
||||
import EditionBlock from './editionBlock';
|
||||
import styles from './style.module.scss';
|
||||
|
||||
export default async function EditionElement(props) {
|
||||
|
||||
return (
|
||||
<section className={`${styles.editionSection} ${props.full ? styles.fullViewport : ''}`}>
|
||||
{props.href ?
|
||||
(
|
||||
<a className={styles.image} href={props.href}>
|
||||
<img src={props.flyerImg} alt={props.flyerAlt}/>
|
||||
</a>
|
||||
) : (
|
||||
<ImageBlock className={styles.image} alt={props.flyerAlt} src={props.flyerImg} />
|
||||
)
|
||||
}
|
||||
{props.blocks.slice(0, 6).map(block => (
|
||||
<EditionBlock {...block} key={`${block.type}-${block.id}`} href={props.href} />
|
||||
))}
|
||||
{props.full ? <aside className={styles.callToAction}>
|
||||
<a href={props.href}>
|
||||
<h2>{props.title}</h2>
|
||||
<svg width="40px" height="40px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 12H18M18 12L13 7M18 12L13 17" stroke="var(--fefan-1)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
</a>
|
||||
</aside> : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
157
next/components/editionElement/style.module.scss
Normal file
157
next/components/editionElement/style.module.scss
Normal file
@@ -0,0 +1,157 @@
|
||||
.editionSection {
|
||||
--cell-width: 14.25rem;
|
||||
|
||||
display: grid;
|
||||
scroll-snap-align: center;
|
||||
flex: none;
|
||||
grid-template-columns: repeat(4, max-content);
|
||||
grid-template-rows: repeat(2, max-content);
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
|
||||
&.fullViewport {
|
||||
min-height: calc(2 * var(--cell-width) + 10rem + 4 * var(--border-width));
|
||||
height: calc(100vh - 2 * var(--border-width) - 5rem - 2rem);
|
||||
}
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
grid-row: span 2;
|
||||
margin: 0.5rem;
|
||||
img {
|
||||
display: block;
|
||||
height: calc(2 * var(--cell-width) + 1rem + 4 * var(--border-width));
|
||||
}
|
||||
}
|
||||
|
||||
img.image {
|
||||
display: block;
|
||||
height: calc(2 * var(--cell-width) + 1rem + 4 * var(--border-width));
|
||||
}
|
||||
|
||||
article {
|
||||
border: solid 1px var(--fg-2);
|
||||
width: var(--cell-width);
|
||||
height: var(--cell-width);
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
object-fit: fill;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.callToAction {
|
||||
grid-row: 3;
|
||||
grid-column: span 4;
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& > a {
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.informations {
|
||||
color: var(--fg-0);
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
margin-left: 1rem;
|
||||
text-decoration: none;
|
||||
|
||||
h2 {
|
||||
font-family: var(--font-details);
|
||||
margin: -0.5rem 0px;
|
||||
font-size: 2rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0px;
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.75;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 70rem) {
|
||||
.editionSection {
|
||||
grid-template-columns: repeat(3, max-content);
|
||||
|
||||
.callToAction {
|
||||
grid-column: span 3;
|
||||
}
|
||||
|
||||
article:nth-of-type(n+5) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 60rem) {
|
||||
.editionSection {
|
||||
grid-template-columns: repeat(2, max-content);
|
||||
|
||||
.callToAction {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
article:nth-of-type(n+3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 40rem) and (min-width: 30rem) {
|
||||
.editionSection {
|
||||
grid-template-columns: repeat(1, max-content);
|
||||
|
||||
.callToAction {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
article {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 30rem) {
|
||||
.editionSection {
|
||||
--cell-width: calc(50vw - 5rem);
|
||||
grid-template-columns: repeat(2, max-content);
|
||||
grid-template-rows: repeat(5, max-content);
|
||||
|
||||
&.fullViewport {
|
||||
height: calc(3.5 * var(--cell-width) + 10rem + 4 * var(--border-width));
|
||||
min-height: calc(100vh - 2 * var(--border-width) - 4rem - 2rem);
|
||||
}
|
||||
|
||||
.image {
|
||||
grid-row: 1;
|
||||
grid-column: span 2;
|
||||
img {
|
||||
height: auto;
|
||||
width: calc(2 * var(--cell-width) + 1rem + 4 * var(--border-width));
|
||||
}
|
||||
}
|
||||
|
||||
img.image {
|
||||
height: auto;
|
||||
width: calc(2 * var(--cell-width) + 1rem + 4 * var(--border-width));
|
||||
}
|
||||
|
||||
.callToAction {
|
||||
grid-row: 5;
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
article:nth-of-type(n+3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
next/components/editionGallery/index.jsx
Normal file
17
next/components/editionGallery/index.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import ImageBlock from "../imageBlock";
|
||||
import styles from "./style.module.scss";
|
||||
|
||||
export default function EditionGallery({ images }) {
|
||||
return (
|
||||
<section className={styles.gallery}>
|
||||
{images.map(({ id, attributes: attr }) => (
|
||||
<div className={styles.galleryImage} key={id}>
|
||||
<ImageBlock
|
||||
alt={attr.alternativeText}
|
||||
src={`${process.env.NEXT_PUBLIC_IMG_URI}${attr.url}`}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
34
next/components/editionGallery/style.module.scss
Normal file
34
next/components/editionGallery/style.module.scss
Normal file
@@ -0,0 +1,34 @@
|
||||
.gallery {
|
||||
--gallery-width: 80rem;
|
||||
--gallery-columns: 3;
|
||||
grid-template-columns: repeat(var(--gallery-columns), calc(var(--gallery-width) / var(--gallery-columns)));
|
||||
grid-auto-rows: max-content;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.galleryImage {
|
||||
grid-column: span 1;
|
||||
margin: 0.5rem;
|
||||
max-height: calc(((6.5 / 10) * (var(--gallery-width) / var(--gallery-columns))) - 1rem);
|
||||
overflow: hidden;
|
||||
|
||||
& > img {
|
||||
width: calc((var(--gallery-width) / var(--gallery-columns)) - 1rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 80rem) {
|
||||
.gallery {
|
||||
--gallery-width: 90vw;
|
||||
--gallery-columns: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 50rem) {
|
||||
.gallery {
|
||||
--gallery-width: 90vw;
|
||||
--gallery-columns: 1;
|
||||
}
|
||||
}
|
||||
63
next/components/email/index.jsx
Normal file
63
next/components/email/index.jsx
Normal file
@@ -0,0 +1,63 @@
|
||||
'use client';
|
||||
|
||||
import qs from 'qs';
|
||||
import { useEffect, useState } from "react";
|
||||
import Button from "../button";
|
||||
import styles from "./style.module.scss";
|
||||
|
||||
function fetchEmail(query) {
|
||||
const queryString = qs.stringify(query);
|
||||
return fetch(`${process.env.NEXT_PUBLIC_CONTENT_URI}/site?${queryString}`).then((data) => data.json());
|
||||
}
|
||||
|
||||
export default function Email() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [displayEmail, setDisplayEmail] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const toggleEmail = () => {
|
||||
if (email === "") {
|
||||
fetchEmail({
|
||||
fields: ['contact_mail']
|
||||
}).then((res) => {
|
||||
setEmail(res.data.attributes.contact_mail);
|
||||
setDisplayEmail(true)
|
||||
})
|
||||
return;
|
||||
} else if (displayEmail === false) {
|
||||
setDisplayEmail(true)
|
||||
} else {
|
||||
navigator.clipboard.writeText(email);
|
||||
setCopied(true);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setDisplayEmail(false);
|
||||
}, 5000);
|
||||
}, [displayEmail])
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 2000);
|
||||
}, [copied])
|
||||
|
||||
return (
|
||||
<Button
|
||||
as="button"
|
||||
type="secondary"
|
||||
onClick={toggleEmail}
|
||||
className={styles.emailButton}
|
||||
title={displayEmail ? "Cliquez pour copier le mail" : null}
|
||||
>
|
||||
<label className={displayEmail ? styles.copy : null}>
|
||||
{displayEmail ? email : "Cliquez pour afficher le mail"}
|
||||
</label>
|
||||
<label className={copied ? styles.showLabel : styles.hideLabel}>
|
||||
{copied ? "copié" : null}
|
||||
</label>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
20
next/components/email/style.module.scss
Normal file
20
next/components/email/style.module.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
.emailButton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
label {
|
||||
transition: all ease-in-out var(--transition-time);
|
||||
}
|
||||
}
|
||||
|
||||
.copy {
|
||||
cursor: copy;
|
||||
}
|
||||
|
||||
.showLabel {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.hideLabel {
|
||||
opacity: 0;
|
||||
}
|
||||
15
next/components/empty/index.jsx
Normal file
15
next/components/empty/index.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import Button from "../button";
|
||||
import styles from "./style.module.scss";
|
||||
|
||||
export default function Empty({message, ...props}) {
|
||||
return <section className={styles.empty}>
|
||||
<h2 {...props}>{message ?? "Il n'y a rien ici."}</h2>
|
||||
<Button
|
||||
as="a"
|
||||
type="secondary"
|
||||
href="/"
|
||||
>
|
||||
Retour à la page d'accueil
|
||||
</Button>
|
||||
</section>
|
||||
}
|
||||
9
next/components/empty/style.module.scss
Normal file
9
next/components/empty/style.module.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
.empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
font-family: var(--font-details);
|
||||
}
|
||||
}
|
||||
10
next/components/fanfare/index.jsx
Normal file
10
next/components/fanfare/index.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import styles from './style.module.scss';
|
||||
|
||||
export default async function Fanfare(props) {
|
||||
return (
|
||||
<div className={styles.band}>
|
||||
<h4>{props.name}</h4>
|
||||
<h5>{props.location}</h5>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
22
next/components/fanfare/style.module.scss
Normal file
22
next/components/fanfare/style.module.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
.band {
|
||||
padding: 0.75rem 0.25rem;
|
||||
max-width: 30vw;
|
||||
h4, h5 {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-family: var(--font-body);
|
||||
font-weight: 500;
|
||||
margin: 0px;
|
||||
font-size: 1rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.75;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
14
next/components/footer/index.jsx
Normal file
14
next/components/footer/index.jsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import Link from "next/link";
|
||||
import styles from "./style.module.scss";
|
||||
|
||||
export default async function Footer() {
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
<Link href="/legal">Mentions légales</Link>
|
||||
<span className={styles.separator}> · </span>
|
||||
<Link href="/sponsor">Ils nous soutiennent</Link>
|
||||
<span className={styles.separator}> · </span>
|
||||
<Link href="/contact">Contact</Link>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
13
next/components/footer/style.module.scss
Normal file
13
next/components/footer/style.module.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
.footer {
|
||||
border-top: var(--fg-3) solid var(--border-width);
|
||||
text-align: center;
|
||||
padding: 0.25rem;
|
||||
|
||||
span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
}
|
||||
}
|
||||
34
next/components/header/index.jsx
Normal file
34
next/components/header/index.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import getData from '@/api';
|
||||
import NavigationDrawer from './navigationDrawer';
|
||||
|
||||
export default async function Header() {
|
||||
const site = await getData('site', {
|
||||
populate: {
|
||||
edition: {
|
||||
fields: [],
|
||||
populate: {
|
||||
programs: {
|
||||
fields: ['type', 'title'],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const editionId = site.data?.attributes.edition.data?.id;
|
||||
const programs = editionId ? site.data.attributes.edition.data.attributes.programs.data : null
|
||||
const socialLinkQuery = editionId ? {
|
||||
filters: {
|
||||
$or: [
|
||||
{ edition: { id: { $null: true } } },
|
||||
{ edition: { id: { $eq: editionId } } },
|
||||
]
|
||||
}
|
||||
} : {
|
||||
filters: { edition: { id: { $null: true } } },
|
||||
}
|
||||
const socialLinks = await getData('social-links', socialLinkQuery)
|
||||
const links = socialLinks.data ?? []
|
||||
return (
|
||||
<NavigationDrawer programs={programs} links={links}/>
|
||||
);
|
||||
}
|
||||
46
next/components/header/navigationDrawer/index.jsx
Normal file
46
next/components/header/navigationDrawer/index.jsx
Normal file
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
import SocialIcon from "../social-icon";
|
||||
import { useState } from "react";
|
||||
import styles from "./style.module.scss";
|
||||
|
||||
export default function NavigationDrawer({programs, links , ...props}) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const toggleDrawer = () => {
|
||||
setIsOpen(prev => !prev);
|
||||
}
|
||||
|
||||
return (
|
||||
<header className={`${styles.header} ${isOpen ? styles.drawerOpen : ""}`}>
|
||||
<div className={`${styles.backdrop}`} onClick={toggleDrawer}/>
|
||||
<button className={`${styles.social} ${styles.burgerIcon}`} onClick={toggleDrawer}>
|
||||
<svg className={`feather feather-menu`} xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#1B519E" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
|
||||
</button>
|
||||
<Link href="/" className={`not-a-link ${styles.logo}`}>
|
||||
<img src="/fefan.png" alt="Fefan" />
|
||||
<h1>Festival de fanfare</h1>
|
||||
</Link>
|
||||
<a className={`${styles.social} ${styles.programIcon}`} href="/prog/city-wide">
|
||||
<svg className={`feather feather-calendar`} xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#1B519E" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
|
||||
</a>
|
||||
<nav className={`${styles.navigation}`}>
|
||||
<div className={styles.spacer} />
|
||||
<Link href="/">Accueil</Link>
|
||||
{programs ? programs.map(({ id, attributes: attr }) => (
|
||||
<Link href={`/prog/${attr.type}`} key={id}>{attr.title}</Link>
|
||||
)): null}
|
||||
<Link href="/editions">Éditions précédentes</Link>
|
||||
<Link href="/contact">Contact</Link>
|
||||
<div className={styles.spacer} />
|
||||
<nav className={styles.socialLinks}>
|
||||
{links.map(({ id, attributes: attr }) => (
|
||||
<Link aria-label={attr.type} target="_blank" href={attr.uri} key={id} className={`${styles.social} not-a-link`}>
|
||||
<SocialIcon name={attr.type} />
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
156
next/components/header/navigationDrawer/style.module.scss
Normal file
156
next/components/header/navigationDrawer/style.module.scss
Normal file
@@ -0,0 +1,156 @@
|
||||
.header {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 200;
|
||||
flex-flow: row;
|
||||
border-bottom: var(--fg-3) solid var(--border-width);
|
||||
background: var(--bg-0);
|
||||
}
|
||||
|
||||
.header + * {
|
||||
margin-top: calc(5rem + var(--border-width));
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
margin: 0.5rem 1rem;
|
||||
flex-flow: column;
|
||||
text-decoration: none;
|
||||
text-align: right;
|
||||
color: var(--fefan-4);
|
||||
|
||||
h1 {
|
||||
font-family: var(--font-details);
|
||||
font-weight: 500;
|
||||
font-size: 0.75rem;
|
||||
margin: -0.10rem 0.35rem;
|
||||
}
|
||||
|
||||
img:first-child {
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.social {
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: unset;
|
||||
font-size: unset;
|
||||
margin: 0;
|
||||
border-radius: 100%;
|
||||
padding: 0.5rem;
|
||||
text-decoration: none;
|
||||
color: var(--fefan-1);
|
||||
display: flex;
|
||||
align-self: center;
|
||||
justify-content: center;
|
||||
box-shadow: inset 0 0 6px 3px var(--background);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.spacer:first-of-type {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
a {
|
||||
margin: 0 0.35rem;
|
||||
}
|
||||
|
||||
.socialLinks {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-right: 1rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
content: '';
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 100vh;
|
||||
z-index: 250;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.burgerIcon {
|
||||
display: none;
|
||||
align-self: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.drawerOpen .navigation {
|
||||
left: 0%;
|
||||
}
|
||||
|
||||
.drawerOpen .backdrop {
|
||||
bottom: 0vh;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.programIcon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
.header {
|
||||
justify-content: space-between;
|
||||
padding: 0 1.5rem;
|
||||
}
|
||||
|
||||
.burgerIcon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.programIcon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
padding: 1rem 0;
|
||||
flex-flow: column;
|
||||
position: fixed;
|
||||
z-index: 300;
|
||||
align-items: stretch;
|
||||
top: 0;
|
||||
max-width: 80vw;
|
||||
width: 20rem;
|
||||
left: -100%;
|
||||
bottom: 0;
|
||||
background: var(--bg-0);
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.25);
|
||||
transition: all 0.2s ease;
|
||||
|
||||
a {
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.spacer:first-of-type {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.socialLinks {
|
||||
justify-content: space-around;
|
||||
}
|
||||
}
|
||||
|
||||
.drawerOpen {
|
||||
left: 0%;
|
||||
}
|
||||
}
|
||||
38
next/components/header/social-icon/index.jsx
Normal file
38
next/components/header/social-icon/index.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
export default function SocialIcon(props) {
|
||||
if (props.name === 'youtube')
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="36" height="36" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor"
|
||||
strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"
|
||||
>
|
||||
<path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z" />
|
||||
<polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02" />
|
||||
</svg>
|
||||
);
|
||||
else if (props.name === 'instagram')
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="36" height="36" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor"
|
||||
strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"
|
||||
>
|
||||
<rect x="2" y="2" width="20" height="20" rx="5" ry="5" />
|
||||
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" />
|
||||
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5" />
|
||||
</svg>
|
||||
);
|
||||
else if (props.name === 'facebook')
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="36" height="36" viewBox="0 0 24 24"
|
||||
fill="none" stroke="currentColor"
|
||||
strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"
|
||||
>
|
||||
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
0
next/components/header/style.module.scss
Normal file
0
next/components/header/style.module.scss
Normal file
33
next/components/imageBlock/index.jsx
Normal file
33
next/components/imageBlock/index.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
'use client';
|
||||
import { useState } from 'react';
|
||||
import styles from './style.module.scss';
|
||||
import ImagePopup from '../imagePopup';
|
||||
|
||||
export default function ImageBlock({alt, src, ...props}) {
|
||||
const [showPopup, setShowPopup] = useState(false);
|
||||
|
||||
const toggleShowPopup = () => {
|
||||
setShowPopup(prev => !prev);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<img
|
||||
{...props}
|
||||
onClick={toggleShowPopup}
|
||||
src={src}
|
||||
alt={alt}
|
||||
className={`${styles.imageBlock} ${props.className}`}
|
||||
/>
|
||||
{
|
||||
showPopup ?
|
||||
<ImagePopup
|
||||
toggleShowPopup={toggleShowPopup}
|
||||
alt={alt}
|
||||
src={src}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
4
next/components/imageBlock/style.module.scss
Normal file
4
next/components/imageBlock/style.module.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.imageBlock {
|
||||
cursor: pointer;
|
||||
max-height: 100%;
|
||||
}
|
||||
32
next/components/imagePopup/index.jsx
Normal file
32
next/components/imagePopup/index.jsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import styles from './style.module.scss';
|
||||
|
||||
export default function ImagePopup({toggleShowPopup, src, alt, ...props}) {
|
||||
return (
|
||||
<div className={styles.modal}>
|
||||
<button onClick={toggleShowPopup} className={styles.close}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="feather feather-x"
|
||||
>
|
||||
<line x1="18" y1="6" x2="6" y2="18" />
|
||||
<line x1="6" y1="6" x2="18" y2="18" />
|
||||
</svg>
|
||||
</button>
|
||||
<div onClick={toggleShowPopup} className={styles.imgModal} />
|
||||
<img
|
||||
{...props}
|
||||
src={src}
|
||||
alt={alt}
|
||||
className={styles.popupImage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
92
next/components/imagePopup/style.module.scss
Normal file
92
next/components/imagePopup/style.module.scss
Normal file
@@ -0,0 +1,92 @@
|
||||
.modal {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 220;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: scroll;
|
||||
overscroll-behavior: contain;
|
||||
cursor: pointer;
|
||||
|
||||
& > div {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
background-color: var(--fg-0);
|
||||
opacity: 0.65;
|
||||
z-index: 220;
|
||||
}
|
||||
}
|
||||
|
||||
.popupImage {
|
||||
margin: auto;
|
||||
display: block;
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
animation-name: zoom;
|
||||
animation-duration: 0.6s;
|
||||
animation-fill-mode: both;
|
||||
z-index: 230;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
top: 3.6rem;
|
||||
right: 4rem;
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
padding: 0.25rem;
|
||||
|
||||
appearance: none;
|
||||
border: unset;
|
||||
font-size: unset;
|
||||
font-family: unset;
|
||||
|
||||
color: var(--bg-0);
|
||||
background: var(--fg-0-translucid);
|
||||
font-weight: bold;
|
||||
transition: 0.3s;
|
||||
cursor: pointer;
|
||||
border-radius: 100%;
|
||||
z-index: 230;
|
||||
|
||||
svg {
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
top: 3.2rem;
|
||||
right: 3.6rem;
|
||||
height: 3.8rem;
|
||||
width: 3.8rem;
|
||||
|
||||
svg {
|
||||
height: 3.8rem;
|
||||
width: 3.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoom {
|
||||
from {
|
||||
transform:scale(0);
|
||||
box-shadow: 0 0 0.2rem rgba(0,0,0,0.5);
|
||||
}
|
||||
to {
|
||||
transform:scale(1);
|
||||
box-shadow: 0 0 1rem rgba(0,0,0,0.5);
|
||||
}
|
||||
}
|
||||
25
next/components/pressBlock/index.jsx
Normal file
25
next/components/pressBlock/index.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import ArticleBlock from "../articleBlock";
|
||||
import ImageBlock from "../imageBlock";
|
||||
import styles from "./style.module.scss";
|
||||
|
||||
export default function PressBlock({
|
||||
left: {type: leftType, ...left},
|
||||
right: {type: rightType, ...right},
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<section {...props} className={styles.pressBlock}>
|
||||
{
|
||||
leftType ? leftType === 'article' ?
|
||||
<ArticleBlock {...left} className={styles.pressArticle}/> :
|
||||
<ImageBlock {...left} className={styles.pressImage}/> : null
|
||||
}
|
||||
|
||||
{
|
||||
rightType ? rightType === 'article' ?
|
||||
<ArticleBlock {...right} className={styles.pressArticle}/> :
|
||||
<ImageBlock {...right} className={styles.pressImage}/> : null
|
||||
}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
36
next/components/pressBlock/style.module.scss
Normal file
36
next/components/pressBlock/style.module.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
.pressBlock {
|
||||
--element-width: 30vw;
|
||||
display: flex;
|
||||
|
||||
.pressArticle, .pressImage {
|
||||
width: var(--element-width);
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.pressImage {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
&:nth-of-type(2n+1) {
|
||||
flex-flow: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 80rem) {
|
||||
.pressBlock {
|
||||
--element-width: 45vw;
|
||||
|
||||
.pressImage {
|
||||
width: calc(var(--element-width) - 10vw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 60rem) {
|
||||
.pressBlock {
|
||||
--element-width: 90vw;
|
||||
&, &:nth-child(2n) {
|
||||
flex-flow: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
next/components/statBlock/index.jsx
Normal file
10
next/components/statBlock/index.jsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import styles from './style.module.scss';
|
||||
|
||||
export default async function StatBlock(props) {
|
||||
return (
|
||||
<a href={props.href} className={styles.statBox}>
|
||||
<span>{props.value}</span>
|
||||
<h3>{props.title}</h3>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
33
next/components/statBlock/style.module.scss
Normal file
33
next/components/statBlock/style.module.scss
Normal file
@@ -0,0 +1,33 @@
|
||||
.statBox {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-decoration: none;
|
||||
transition: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 4rem;
|
||||
color: var(--fg-0);
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
color: var(--fg-1);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 30rem) {
|
||||
.statBox {
|
||||
span {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
next/components/timeSlot/index.jsx
Normal file
23
next/components/timeSlot/index.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { BlocksRenderer } from '@strapi/blocks-react-renderer';
|
||||
import styles from './style.module.scss';
|
||||
|
||||
export default async function TimeSlot(props) {
|
||||
const date_begin = new Date(props.start)
|
||||
const date_end = new Date(props.end)
|
||||
const date_format = new Intl.DateTimeFormat('fr', {
|
||||
weekday: 'long',
|
||||
month: "long",
|
||||
day: "numeric"
|
||||
}).format(date_begin)
|
||||
|
||||
return (
|
||||
<article className={styles.timeSlot}>
|
||||
<h4>
|
||||
{date_format} - {date_begin.getHours()}h
|
||||
{date_begin.getMinutes() !== 0 ? date_begin.getMinutes() : ""} à
|
||||
{date_end.getHours()}h{date_end.getMinutes() !== 0 ? date_end.getMinutes() : ""}
|
||||
</h4>
|
||||
<BlocksRenderer content={props.content}/>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
17
next/components/timeSlot/style.module.scss
Normal file
17
next/components/timeSlot/style.module.scss
Normal file
@@ -0,0 +1,17 @@
|
||||
.timeSlot {
|
||||
h4::first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-family: var(--font-details);
|
||||
margin: 0px;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400;
|
||||
color: var(--fefan-3);
|
||||
}
|
||||
|
||||
margin-bottom: 2rem;
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
15
next/components/videoBlock/index.jsx
Normal file
15
next/components/videoBlock/index.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import styles from "./style.module.scss";
|
||||
|
||||
export default async function VideoBlock(props) {
|
||||
return (
|
||||
<iframe
|
||||
className={`${props.className} ${styles.videoBlock}`}
|
||||
width={ props.mode ==='thumbnail' ? "220" : "560" }
|
||||
height={props.mode ==='thumbnail' ? "220" : "315"}
|
||||
src={props.src}
|
||||
title={props.title}
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowFullScreen
|
||||
/>
|
||||
);
|
||||
}
|
||||
3
next/components/videoBlock/style.module.scss
Normal file
3
next/components/videoBlock/style.module.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.videoBlock {
|
||||
max-width: 90vw;
|
||||
}
|
||||
Reference in New Issue
Block a user